VkImage: Implement BC7 texture format

Also contains the following changes:
* Replace the use of `unsigned char` with `uint8_t`. `char` is not guaranteed to be 8-bit.
* Remove the `dstW` and `dstH` parameters from `BC_Decoder::Decode` and `ETC_Decoder::Decode`. They're always the same as the `w` and `h` parameters.
* Add BC6 types to various switch cases. The actual decode logic is not implemented for these formats.

Tests: *bc7*
Bug: b/151203718
Change-Id: I7b232b9dc3a9b02d172f87a62c88ce56b2cca956
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/41508
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Device/BC_Decoder.cpp b/src/Device/BC_Decoder.cpp
index f29b839..909ef8c 100644
--- a/src/Device/BC_Decoder.cpp
+++ b/src/Device/BC_Decoder.cpp
@@ -14,13 +14,22 @@
 
 #include "BC_Decoder.hpp"
 
+#include "System/Debug.hpp"
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+
+#include <assert.h>
+#include <stdint.h>
+
 namespace {
 static constexpr int BlockWidth = 4;
 static constexpr int BlockHeight = 4;
 
 struct BC_color
 {
-	void decode(unsigned char *dst, int x, int y, int dstW, int dstH, int dstPitch, int dstBpp, bool hasAlphaChannel, bool hasSeparateAlpha) const
+	void decode(uint8_t *dst, int x, int y, int dstW, int dstH, int dstPitch, int dstBpp, bool hasAlphaChannel, bool hasSeparateAlpha) const
 	{
 		Color c[4];
 		c[0].extract565(c0);
@@ -133,7 +142,7 @@
 
 struct BC_channel
 {
-	void decode(unsigned char *dst, int x, int y, int dstW, int dstH, int dstPitch, int dstBpp, int channel, bool isSigned) const
+	void decode(uint8_t *dst, int x, int y, int dstW, int dstH, int dstPitch, int dstBpp, int channel, bool isSigned) const
 	{
 		int c[8] = { 0 };
 
@@ -144,8 +153,8 @@
 		}
 		else
 		{
-			c[0] = static_cast<unsigned char>(data & 0xFF);
-			c[1] = static_cast<unsigned char>((data & 0xFF00) >> 8);
+			c[0] = static_cast<uint8_t>(data & 0xFF);
+			c[1] = static_cast<uint8_t>((data & 0xFF00) >> 8);
 		}
 
 		if(c[0] > c[1])
@@ -169,29 +178,29 @@
 		{
 			for(int i = 0; i < BlockWidth && (x + i) < dstW; i++)
 			{
-				dst[channel + (i * dstBpp) + (j * dstPitch)] = static_cast<unsigned char>(c[getIdx((j * BlockHeight) + i)]);
+				dst[channel + (i * dstBpp) + (j * dstPitch)] = static_cast<uint8_t>(c[getIdx((j * BlockHeight) + i)]);
 			}
 		}
 	}
 
 private:
-	unsigned char getIdx(int i) const
+	uint8_t getIdx(int i) const
 	{
 		int offset = i * 3 + 16;
-		return static_cast<unsigned char>((data & (0x7ull << offset)) >> offset);
+		return static_cast<uint8_t>((data & (0x7ull << offset)) >> offset);
 	}
 
-	unsigned long long data;
+	uint64_t data;
 };
 
 struct BC_alpha
 {
-	void decode(unsigned char *dst, int x, int y, int dstW, int dstH, int dstPitch, int dstBpp) const
+	void decode(uint8_t *dst, int x, int y, int dstW, int dstH, int dstPitch, int dstBpp) const
 	{
 		dst += 3;  // Write only to alpha (channel 3)
 		for(int j = 0; j < BlockHeight && (y + j) < dstH; j++, dst += dstPitch)
 		{
-			unsigned char *dstRow = dst;
+			uint8_t *dstRow = dst;
 			for(int i = 0; i < BlockWidth && (x + i) < dstW; i++, dstRow += dstBpp)
 			{
 				*dstRow = getAlpha(j * BlockHeight + i);
@@ -200,19 +209,587 @@
 	}
 
 private:
-	unsigned char getAlpha(int i) const
+	uint8_t getAlpha(int i) const
 	{
 		int offset = i << 2;
 		int alpha = (data & (0xFull << offset)) >> offset;
-		return static_cast<unsigned char>(alpha | (alpha << 4));
+		return static_cast<uint8_t>(alpha | (alpha << 4));
 	}
 
-	unsigned long long data;
+	uint64_t data;
 };
-}  // end namespace
+
+namespace BC7 {
+// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression_bptc.txt
+// https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc7-format
+
+struct Bitfield
+{
+	int offset;
+	int count;
+	constexpr Bitfield Then(const int bits) { return { offset + count, bits }; }
+	constexpr bool operator==(const Bitfield &rhs)
+	{
+		return offset == rhs.offset && count == rhs.count;
+	}
+};
+
+struct Mode
+{
+	const int IDX;  // Mode index
+	const int NS;   // Number of subsets in each partition
+	const int PB;   // Partition bits
+	const int RB;   // Rotation bits
+	const int ISB;  // Index selection bits
+	const int CB;   // Color bits
+	const int AB;   // Alpha bits
+	const int EPB;  // Endpoint P-bits
+	const int SPB;  // Shared P-bits
+	const int IB;   // Primary index bits per element
+	const int IBC;  // Primary index bits total
+	const int IB2;  // Secondary index bits per element
+
+	constexpr int NumColors() const { return NS * 2; }
+	constexpr Bitfield Partition() const { return { IDX + 1, PB }; }
+	constexpr Bitfield Rotation() const { return Partition().Then(RB); }
+	constexpr Bitfield IndexSelection() const { return Rotation().Then(ISB); }
+	constexpr Bitfield Red(int idx) const
+	{
+		return IndexSelection().Then(CB * idx).Then(CB);
+	}
+	constexpr Bitfield Green(int idx) const
+	{
+		return Red(NumColors() - 1).Then(CB * idx).Then(CB);
+	}
+	constexpr Bitfield Blue(int idx) const
+	{
+		return Green(NumColors() - 1).Then(CB * idx).Then(CB);
+	}
+	constexpr Bitfield Alpha(int idx) const
+	{
+		return Blue(NumColors() - 1).Then(AB * idx).Then(AB);
+	}
+	constexpr Bitfield EndpointPBit(int idx) const
+	{
+		return Alpha(NumColors() - 1).Then(EPB * idx).Then(EPB);
+	}
+	constexpr Bitfield SharedPBit0() const
+	{
+		return EndpointPBit(NumColors() - 1).Then(SPB);
+	}
+	constexpr Bitfield SharedPBit1() const
+	{
+		return SharedPBit0().Then(SPB);
+	}
+	constexpr Bitfield PrimaryIndex(int offset, int count) const
+	{
+		return SharedPBit1().Then(offset).Then(count);
+	}
+	constexpr Bitfield SecondaryIndex(int offset, int count) const
+	{
+		return SharedPBit1().Then(IBC + offset).Then(count);
+	}
+};
+
+static constexpr Mode Modes[] = {
+	//     IDX  NS   PB   RB   ISB  CB   AB   EPB  SPB  IB   IBC, IB2
+	/**/ { 0x0, 0x3, 0x4, 0x0, 0x0, 0x4, 0x0, 0x1, 0x0, 0x3, 0x2d, 0x0 },
+	/**/ { 0x1, 0x2, 0x6, 0x0, 0x0, 0x6, 0x0, 0x0, 0x1, 0x3, 0x2e, 0x0 },
+	/**/ { 0x2, 0x3, 0x6, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x2, 0x1d, 0x0 },
+	/**/ { 0x3, 0x2, 0x6, 0x0, 0x0, 0x7, 0x0, 0x1, 0x0, 0x2, 0x1e, 0x0 },
+	/**/ { 0x4, 0x1, 0x0, 0x2, 0x1, 0x5, 0x6, 0x0, 0x0, 0x2, 0x1f, 0x3 },
+	/**/ { 0x5, 0x1, 0x0, 0x2, 0x0, 0x7, 0x8, 0x0, 0x0, 0x2, 0x1f, 0x2 },
+	/**/ { 0x6, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x1, 0x0, 0x4, 0x3f, 0x0 },
+	/**/ { 0x7, 0x2, 0x6, 0x0, 0x0, 0x5, 0x5, 0x1, 0x0, 0x2, 0x1e, 0x0 },
+	/**/ { -1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x0 },
+};
+
+static_assert(Modes[0].NumColors() == 6, "BC7 sanity checks failed");
+static_assert(Modes[0].Partition() == Bitfield{ 1, 4 }, "BC7 sanity checks failed");
+static_assert(Modes[0].Red(0) == Bitfield{ 5, 4 }, "BC7 sanity checks failed");
+static_assert(Modes[0].Red(5) == Bitfield{ 25, 4 }, "BC7 sanity checks failed");
+static_assert(Modes[0].Green(0) == Bitfield{ 29, 4 }, "BC7 sanity checks failed");
+static_assert(Modes[0].Green(5) == Bitfield{ 49, 4 }, "BC7 sanity checks failed");
+static_assert(Modes[0].Blue(0) == Bitfield{ 53, 4 }, "BC7 sanity checks failed");
+static_assert(Modes[0].Blue(5) == Bitfield{ 73, 4 }, "BC7 sanity checks failed");
+static_assert(Modes[0].EndpointPBit(0) == Bitfield{ 77, 1 }, "BC7 sanity checks failed");
+static_assert(Modes[0].EndpointPBit(5) == Bitfield{ 82, 1 }, "BC7 sanity checks failed");
+static_assert(Modes[0].PrimaryIndex(0, 2) == Bitfield{ 83, 2 }, "BC7 sanity checks failed");
+static_assert(Modes[0].PrimaryIndex(43, 1) == Bitfield{ 126, 1 }, "BC7 sanity checks failed");
+
+static constexpr int MaxPartitions = 64;
+static constexpr int MaxSubsets = 3;
+
+static constexpr uint8_t PartitionTable2[MaxPartitions][16] = {
+	{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 },
+	{ 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 },
+	{ 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
+	{ 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1 },
+	{ 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
+	{ 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1 },
+	{ 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1 },
+	{ 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
+	{ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 },
+	{ 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1 },
+	{ 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0 },
+	{ 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 },
+	{ 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 },
+	{ 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1 },
+	{ 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 },
+	{ 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0 },
+	{ 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0 },
+	{ 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0 },
+	{ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+	{ 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0 },
+	{ 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0 },
+	{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 },
+	{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 },
+	{ 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0 },
+	{ 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0 },
+	{ 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0 },
+	{ 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0 },
+	{ 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 },
+	{ 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1 },
+	{ 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0 },
+	{ 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 },
+	{ 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0 },
+	{ 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 },
+	{ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 },
+	{ 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
+	{ 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1 },
+	{ 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
+	{ 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0 },
+	{ 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0 },
+	{ 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1 },
+	{ 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1 },
+	{ 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0 },
+	{ 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0 },
+	{ 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1 },
+	{ 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1 },
+	{ 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 },
+	{ 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 },
+	{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 },
+	{ 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+	{ 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0 },
+	{ 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1 },
+};
+
+static constexpr uint8_t PartitionTable3[MaxPartitions][16] = {
+	{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 1, 2, 2, 2, 2 },
+	{ 0, 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1 },
+	{ 0, 0, 0, 0, 2, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1 },
+	{ 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2 },
+	{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 2, 2 },
+	{ 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 },
+	{ 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2 },
+	{ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2 },
+	{ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 },
+	{ 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2 },
+	{ 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2 },
+	{ 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2 },
+	{ 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2 },
+	{ 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0 },
+	{ 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2 },
+	{ 0, 1, 1, 1, 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0 },
+	{ 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2 },
+	{ 0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1 },
+	{ 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2 },
+	{ 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 2, 1 },
+	{ 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2 },
+	{ 0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 1, 0, 2, 2, 1, 0 },
+	{ 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0 },
+	{ 0, 0, 1, 2, 0, 0, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2 },
+	{ 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 1, 0 },
+	{ 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1 },
+	{ 0, 0, 2, 2, 1, 1, 0, 2, 1, 1, 0, 2, 0, 0, 2, 2 },
+	{ 0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 0, 2, 2, 2, 2, 2 },
+	{ 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1 },
+	{ 0, 0, 0, 0, 2, 0, 0, 0, 2, 2, 1, 1, 2, 2, 2, 1 },
+	{ 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 2, 2, 2 },
+	{ 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 2, 0, 0, 1, 1 },
+	{ 0, 0, 1, 1, 0, 0, 1, 2, 0, 0, 2, 2, 0, 2, 2, 2 },
+	{ 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0 },
+	{ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0 },
+	{ 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0 },
+	{ 0, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 0, 1, 2, 0 },
+	{ 0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1 },
+	{ 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1 },
+	{ 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1 },
+	{ 0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2, 1, 1, 2, 2 },
+	{ 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1 },
+	{ 0, 2, 2, 0, 1, 2, 2, 1, 0, 2, 2, 0, 1, 2, 2, 1 },
+	{ 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 1 },
+	{ 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 },
+	{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2 },
+	{ 0, 2, 2, 2, 0, 1, 1, 1, 0, 2, 2, 2, 0, 1, 1, 1 },
+	{ 0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2 },
+	{ 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2 },
+	{ 0, 2, 2, 2, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2 },
+	{ 0, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2 },
+	{ 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2 },
+	{ 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2 },
+	{ 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2 },
+	{ 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2 },
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2 },
+	{ 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1 },
+	{ 0, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2 },
+	{ 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
+	{ 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0 },
+};
+
+static constexpr uint8_t AnchorTable2[MaxPartitions] = {
+	// clang-format off
+	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+	0xf, 0x2, 0x8, 0x2, 0x2, 0x8, 0x8, 0xf,
+	0x2, 0x8, 0x2, 0x2, 0x8, 0x8, 0x2, 0x2,
+	0xf, 0xf, 0x6, 0x8, 0x2, 0x8, 0xf, 0xf,
+	0x2, 0x8, 0x2, 0x2, 0x2, 0xf, 0xf, 0x6,
+	0x6, 0x2, 0x6, 0x8, 0xf, 0xf, 0x2, 0x2,
+	0xf, 0xf, 0xf, 0xf, 0xf, 0x2, 0x2, 0xf,
+	// clang-format on
+};
+
+static constexpr uint8_t AnchorTable3a[MaxPartitions] = {
+	// clang-format off
+	0x3, 0x3, 0xf, 0xf, 0x8, 0x3, 0xf, 0xf,
+	0x8, 0x8, 0x6, 0x6, 0x6, 0x5, 0x3, 0x3,
+	0x3, 0x3, 0x8, 0xf, 0x3, 0x3, 0x6, 0xa,
+	0x5, 0x8, 0x8, 0x6, 0x8, 0x5, 0xf, 0xf,
+	0x8, 0xf, 0x3, 0x5, 0x6, 0xa, 0x8, 0xf,
+	0xf, 0x3, 0xf, 0x5, 0xf, 0xf, 0xf, 0xf,
+	0x3, 0xf, 0x5, 0x5, 0x5, 0x8, 0x5, 0xa,
+	0x5, 0xa, 0x8, 0xd, 0xf, 0xc, 0x3, 0x3,
+	// clang-format on
+};
+
+static constexpr uint8_t AnchorTable3b[MaxPartitions] = {
+	// clang-format off
+	0xf, 0x8, 0x8, 0x3, 0xf, 0xf, 0x3, 0x8,
+	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x8,
+	0xf, 0x8, 0xf, 0x3, 0xf, 0x8, 0xf, 0x8,
+	0x3, 0xf, 0x6, 0xa, 0xf, 0xf, 0xa, 0x8,
+	0xf, 0x3, 0xf, 0xa, 0xa, 0x8, 0x9, 0xa,
+	0x6, 0xf, 0x8, 0xf, 0x3, 0x6, 0x6, 0x8,
+	0xf, 0x3, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
+	0xf, 0xf, 0xf, 0xf, 0x3, 0xf, 0xf, 0x8,
+	// clang-format on
+};
+
+struct Color
+{
+	struct RGB
+	{
+		RGB() = default;
+		RGB(uint8_t r, uint8_t g, uint8_t b)
+		    : b(b)
+		    , g(g)
+		    , r(r)
+		{}
+		RGB(int r, int g, int b)
+		    : b(static_cast<uint8_t>(b))
+		    , g(static_cast<uint8_t>(g))
+		    , r(static_cast<uint8_t>(r))
+		{}
+
+		RGB operator<<(int shift) const { return { r << shift, g << shift, b << shift }; }
+		RGB operator>>(int shift) const { return { r >> shift, g >> shift, b >> shift }; }
+		RGB operator|(int bits) const { return { r | bits, g | bits, b | bits }; }
+		RGB operator|(const RGB &rhs) const { return { r | rhs.r, g | rhs.g, b | rhs.b }; }
+		RGB operator+(const RGB &rhs) const { return { r + rhs.r, g + rhs.g, b + rhs.b }; }
+
+		uint8_t b;
+		uint8_t g;
+		uint8_t r;
+	};
+
+	RGB rgb;
+	uint8_t a;
+};
+
+static_assert(sizeof(Color) == 4, "Color size must be 4 bytes");
+
+struct Block
+{
+	constexpr uint64_t Get(const Bitfield &bf) const
+	{
+		uint64_t mask = (1ULL << bf.count) - 1;
+		if(bf.offset + bf.count <= 64)
+		{
+			return (low >> bf.offset) & mask;
+		}
+		if(bf.offset >= 64)
+		{
+			return (high >> (bf.offset - 64)) & mask;
+		}
+		return ((low >> bf.offset) | (high << (64 - bf.offset))) & mask;
+	}
+
+	const Mode &mode() const
+	{
+		if((low & 0b00000001) != 0) { return Modes[0]; }
+		if((low & 0b00000010) != 0) { return Modes[1]; }
+		if((low & 0b00000100) != 0) { return Modes[2]; }
+		if((low & 0b00001000) != 0) { return Modes[3]; }
+		if((low & 0b00010000) != 0) { return Modes[4]; }
+		if((low & 0b00100000) != 0) { return Modes[5]; }
+		if((low & 0b01000000) != 0) { return Modes[6]; }
+		if((low & 0b10000000) != 0) { return Modes[7]; }
+		return Modes[8];  // Invalid mode
+	}
+
+	struct IndexInfo
+	{
+		uint64_t value;
+		int numBits;
+	};
+
+	uint8_t interpolate(uint8_t e0, uint8_t e1, const IndexInfo &index) const
+	{
+		static constexpr uint16_t weights2[] = { 0, 21, 43, 64 };
+		static constexpr uint16_t weights3[] = { 0, 9, 18, 27, 37, 46, 55, 64 };
+		static constexpr uint16_t weights4[] = { 0, 4, 9, 13, 17, 21, 26, 30,
+			                                     34, 38, 43, 47, 51, 55, 60, 64 };
+		static constexpr uint16_t const *weightsN[] = {
+			nullptr, nullptr, weights2, weights3, weights4
+		};
+		auto weights = weightsN[index.numBits];
+		ASSERT_MSG(weights != nullptr, "Unexpected number of index bits: %d", (int)index.numBits);
+		return (uint8_t)(((64 - weights[index.value]) * uint16_t(e0) + weights[index.value] * uint16_t(e1) + 32) >> 6);
+	}
+
+	void decode(uint8_t *dst, int dstX, int dstY, int dstWidth, int dstHeight, size_t dstPitch) const
+	{
+		auto const &mode = this->mode();
+
+		if(mode.IDX < 0)  // Invalid mode:
+		{
+			for(int y = 0; y < 4 && y + dstY < dstHeight; y++)
+			{
+				for(int x = 0; x < 4 && x + dstX < dstWidth; x++)
+				{
+					auto out = reinterpret_cast<Color *>(dst + sizeof(Color) * x + dstPitch * y);
+					out->rgb = { 0, 0, 0 };
+					out->a = 0;
+				}
+			}
+			return;
+		}
+
+		using Endpoint = std::array<Color, 2>;
+		std::array<Endpoint, MaxSubsets> subsets;
+
+		for(int i = 0; i < mode.NS; i++)
+		{
+			auto &subset = subsets[i];
+			subset[0].rgb.r = Get(mode.Red(i * 2 + 0));
+			subset[0].rgb.g = Get(mode.Green(i * 2 + 0));
+			subset[0].rgb.b = Get(mode.Blue(i * 2 + 0));
+			subset[0].a = (mode.AB > 0) ? Get(mode.Alpha(i * 2 + 0)) : 255;
+
+			subset[1].rgb.r = Get(mode.Red(i * 2 + 1));
+			subset[1].rgb.g = Get(mode.Green(i * 2 + 1));
+			subset[1].rgb.b = Get(mode.Blue(i * 2 + 1));
+			subset[1].a = (mode.AB > 0) ? Get(mode.Alpha(i * 2 + 1)) : 255;
+		}
+
+		if(mode.SPB > 0)
+		{
+			auto pbit0 = Get(mode.SharedPBit0());
+			auto pbit1 = Get(mode.SharedPBit1());
+			subsets[0][0].rgb = (subsets[0][0].rgb << 1) | pbit0;
+			subsets[0][1].rgb = (subsets[0][1].rgb << 1) | pbit0;
+			subsets[1][0].rgb = (subsets[1][0].rgb << 1) | pbit1;
+			subsets[1][1].rgb = (subsets[1][1].rgb << 1) | pbit1;
+		}
+
+		if(mode.EPB > 0)
+		{
+			for(int i = 0; i < mode.NS; i++)
+			{
+				auto &subset = subsets[i];
+				auto pbit0 = Get(mode.EndpointPBit(i * 2 + 0));
+				auto pbit1 = Get(mode.EndpointPBit(i * 2 + 1));
+				subset[0].rgb = (subset[0].rgb << 1) | pbit0;
+				subset[1].rgb = (subset[1].rgb << 1) | pbit1;
+				if(mode.AB > 0)
+				{
+					subset[0].a = (subset[0].a << 1) | pbit0;
+					subset[1].a = (subset[1].a << 1) | pbit1;
+				}
+			}
+		}
+
+		auto const colorBits = mode.CB + mode.SPB + mode.EPB;
+		auto const alphaBits = mode.AB + mode.SPB + mode.EPB;
+
+		for(int i = 0; i < mode.NS; i++)
+		{
+			auto &subset = subsets[i];
+			subset[0].rgb = subset[0].rgb << (8 - colorBits);
+			subset[1].rgb = subset[1].rgb << (8 - colorBits);
+			subset[0].rgb = subset[0].rgb | (subset[0].rgb >> colorBits);
+			subset[1].rgb = subset[1].rgb | (subset[1].rgb >> colorBits);
+
+			if(mode.AB > 0)
+			{
+				subset[0].a = subset[0].a << (8 - alphaBits);
+				subset[1].a = subset[1].a << (8 - alphaBits);
+				subset[0].a = subset[0].a | (subset[0].a >> alphaBits);
+				subset[1].a = subset[1].a | (subset[1].a >> alphaBits);
+			}
+		}
+
+		int colorIndexBitOffset = 0;
+		int alphaIndexBitOffset = 0;
+		for(int y = 0; y < 4; y++)
+		{
+			for(int x = 0; x < 4; x++)
+			{
+				auto texelIdx = y * 4 + x;
+				auto partitionIdx = Get(mode.Partition());
+				ASSERT(partitionIdx < MaxPartitions);
+				auto subsetIdx = subsetIndex(mode, partitionIdx, texelIdx);
+				ASSERT(subsetIdx < MaxSubsets);
+				auto const &subset = subsets[subsetIdx];
+
+				auto anchorIdx = anchorIndex(mode, partitionIdx, subsetIdx);
+				auto isAnchor = anchorIdx == texelIdx;
+				auto colorIdx = colorIndex(mode, isAnchor, colorIndexBitOffset);
+				auto alphaIdx = alphaIndex(mode, isAnchor, alphaIndexBitOffset);
+
+				if(y + dstY >= dstHeight || x + dstX >= dstWidth)
+				{
+					// Don't be tempted to skip early at the loops:
+					// The calls to colorIndex() and alphaIndex() adjust bit
+					// offsets that need to be carefully tracked.
+					continue;
+				}
+
+				Color output;
+				output.rgb.r = interpolate(subset[0].rgb.r, subset[1].rgb.r, colorIdx);
+				output.rgb.g = interpolate(subset[0].rgb.g, subset[1].rgb.g, colorIdx);
+				output.rgb.b = interpolate(subset[0].rgb.b, subset[1].rgb.b, colorIdx);
+				output.a = interpolate(subset[0].a, subset[1].a, alphaIdx);
+
+				switch(Get(mode.Rotation()))
+				{
+					default:
+						break;
+					case 1:
+						std::swap(output.a, output.rgb.r);
+						break;
+					case 2:
+						std::swap(output.a, output.rgb.g);
+						break;
+					case 3:
+						std::swap(output.a, output.rgb.b);
+						break;
+				}
+
+				auto out = reinterpret_cast<Color *>(dst + sizeof(Color) * x + dstPitch * y);
+				*out = output;
+			}
+		}
+	}
+
+	int subsetIndex(const Mode &mode, int partitionIdx, int texelIndex) const
+	{
+		switch(mode.NS)
+		{
+			default:
+				return 0;
+			case 2:
+				return PartitionTable2[partitionIdx][texelIndex];
+			case 3:
+				return PartitionTable3[partitionIdx][texelIndex];
+		}
+	}
+
+	int anchorIndex(const Mode &mode, int partitionIdx, int subsetIdx) const
+	{
+		// ARB_texture_compression_bptc states:
+		// "In partition zero, the anchor index is always index zero.
+		// In other partitions, the anchor index is specified by tables
+		// Table.A2 and Table.A3.""
+		// Note: This is really confusing - I believe they meant subset instead
+		// of partition here.
+		switch(subsetIdx)
+		{
+			default:
+				return 0;
+			case 1:
+				return mode.NS == 2 ? AnchorTable2[partitionIdx] : AnchorTable3a[partitionIdx];
+			case 2:
+				return AnchorTable3b[partitionIdx];
+		}
+	}
+
+	IndexInfo colorIndex(const Mode &mode, bool isAnchor,
+	                     int &indexBitOffset) const
+	{
+		// ARB_texture_compression_bptc states:
+		// "The index value for interpolating color comes from the secondary
+		// index for the texel if the format has an index selection bit and its
+		// value is one and from the primary index otherwise.""
+		auto idx = Get(mode.IndexSelection());
+		ASSERT(idx <= 1);
+		bool secondary = idx == 1;
+		auto numBits = secondary ? mode.IB2 : mode.IB;
+		auto numReadBits = numBits - (isAnchor ? 1 : 0);
+		auto index =
+		    Get(secondary ? mode.SecondaryIndex(indexBitOffset, numReadBits)
+		                  : mode.PrimaryIndex(indexBitOffset, numReadBits));
+		indexBitOffset += numReadBits;
+		return { index, numBits };
+	}
+
+	IndexInfo alphaIndex(const Mode &mode, bool isAnchor,
+	                     int &indexBitOffset) const
+	{
+		// ARB_texture_compression_bptc states:
+		// "The alpha index comes from the secondary index if the block has a
+		// secondary index and the block either doesn't have an index selection
+		// bit or that bit is zero and the primary index otherwise."
+		auto idx = Get(mode.IndexSelection());
+		ASSERT(idx <= 1);
+		bool secondary = (mode.IB2 != 0) && (idx == 0);
+		auto numBits = secondary ? mode.IB2 : mode.IB;
+		auto numReadBits = numBits - (isAnchor ? 1 : 0);
+		auto index =
+		    Get(secondary ? mode.SecondaryIndex(indexBitOffset, numReadBits)
+		                  : mode.PrimaryIndex(indexBitOffset, numReadBits));
+		indexBitOffset += numReadBits;
+		return { index, numBits };
+	}
+
+	// Assumes little-endian
+	uint64_t low;
+	uint64_t high;
+};
+
+}  // namespace BC7
+}  // anonymous namespace
 
 // Decodes 1 to 4 channel images to 8 bit output
-bool BC_Decoder::Decode(const unsigned char *src, unsigned char *dst, int w, int h, int dstW, int dstH, int dstPitch, int dstBpp, int n, bool isNoAlphaU)
+bool BC_Decoder::Decode(const uint8_t *src, uint8_t *dst, int w, int h, int dstPitch, int dstBpp, int n, bool isNoAlphaU)
 {
 	static_assert(sizeof(BC_color) == 8, "BC_color must be 8 bytes");
 	static_assert(sizeof(BC_channel) == 8, "BC_channel must be 8 bytes");
@@ -230,10 +807,10 @@
 			const BC_color *color = reinterpret_cast<const BC_color *>(src);
 			for(int y = 0; y < h; y += BlockHeight, dst += dy)
 			{
-				unsigned char *dstRow = dst;
+				uint8_t *dstRow = dst;
 				for(int x = 0; x < w; x += BlockWidth, ++color, dstRow += dx)
 				{
-					color->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, isAlpha, false);
+					color->decode(dstRow, x, y, w, h, dstPitch, dstBpp, isAlpha, false);
 				}
 			}
 		}
@@ -244,11 +821,11 @@
 			const BC_color *color = reinterpret_cast<const BC_color *>(src + 8);
 			for(int y = 0; y < h; y += BlockHeight, dst += dy)
 			{
-				unsigned char *dstRow = dst;
+				uint8_t *dstRow = dst;
 				for(int x = 0; x < w; x += BlockWidth, alpha += 2, color += 2, dstRow += dx)
 				{
-					color->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, isAlpha, true);
-					alpha->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp);
+					color->decode(dstRow, x, y, w, h, dstPitch, dstBpp, isAlpha, true);
+					alpha->decode(dstRow, x, y, w, h, dstPitch, dstBpp);
 				}
 			}
 		}
@@ -259,11 +836,11 @@
 			const BC_color *color = reinterpret_cast<const BC_color *>(src + 8);
 			for(int y = 0; y < h; y += BlockHeight, dst += dy)
 			{
-				unsigned char *dstRow = dst;
+				uint8_t *dstRow = dst;
 				for(int x = 0; x < w; x += BlockWidth, alpha += 2, color += 2, dstRow += dx)
 				{
-					color->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, isAlpha, true);
-					alpha->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, 3, isSigned);
+					color->decode(dstRow, x, y, w, h, dstPitch, dstBpp, isAlpha, true);
+					alpha->decode(dstRow, x, y, w, h, dstPitch, dstBpp, 3, isSigned);
 				}
 			}
 		}
@@ -273,10 +850,10 @@
 			const BC_channel *red = reinterpret_cast<const BC_channel *>(src);
 			for(int y = 0; y < h; y += BlockHeight, dst += dy)
 			{
-				unsigned char *dstRow = dst;
+				uint8_t *dstRow = dst;
 				for(int x = 0; x < w; x += BlockWidth, ++red, dstRow += dx)
 				{
-					red->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, 0, isSigned);
+					red->decode(dstRow, x, y, w, h, dstPitch, dstBpp, 0, isSigned);
 				}
 			}
 		}
@@ -287,11 +864,24 @@
 			const BC_channel *green = reinterpret_cast<const BC_channel *>(src + 8);
 			for(int y = 0; y < h; y += BlockHeight, dst += dy)
 			{
-				unsigned char *dstRow = dst;
+				uint8_t *dstRow = dst;
 				for(int x = 0; x < w; x += BlockWidth, red += 2, green += 2, dstRow += dx)
 				{
-					red->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, 0, isSigned);
-					green->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, 1, isSigned);
+					red->decode(dstRow, x, y, w, h, dstPitch, dstBpp, 0, isSigned);
+					green->decode(dstRow, x, y, w, h, dstPitch, dstBpp, 1, isSigned);
+				}
+			}
+		}
+		break;
+		case 7:  // BC7
+		{
+			const BC7::Block *block = reinterpret_cast<const BC7::Block *>(src);
+			for(int y = 0; y < h; y += BlockHeight, dst += dy)
+			{
+				uint8_t *dstRow = dst;
+				for(int x = 0; x < w; x += BlockWidth, ++block, dstRow += dx)
+				{
+					block->decode(dstRow, x, y, w, h, dstPitch);
 				}
 			}
 		}
diff --git a/src/Device/BC_Decoder.hpp b/src/Device/BC_Decoder.hpp
index 8ebbb60..2b58349 100644
--- a/src/Device/BC_Decoder.hpp
+++ b/src/Device/BC_Decoder.hpp
@@ -18,15 +18,13 @@
 	/// BCn_Decoder::Decode - Decodes 1 to 4 channel images to 8 bit output
 	/// @param src            Pointer to BCn encoded image
 	/// @param dst            Pointer to decoded output image
-	/// @param w              src image width
-	/// @param h              src image height
-	/// @param dstW           dst image width
-	/// @param dstH           dst image height
+	/// @param w              image width
+	/// @param h              image height
 	/// @param dstPitch       dst image pitch (bytes per row)
 	/// @param dstBpp         dst image bytes per pixel
 	/// @param n              n in BCn format
 	/// @param isNoAlphaU     BC1: true if RGB, BC2/BC3: unused, BC4/BC5: true if unsigned
 	/// @return               true if the decoding was performed
 
-	static bool Decode(const unsigned char *src, unsigned char *dst, int w, int h, int dstW, int dstH, int dstPitch, int dstBpp, int n, bool isNoAlphaU);
+	static bool Decode(const unsigned char *src, unsigned char *dst, int w, int h, int dstPitch, int dstBpp, int n, bool isNoAlphaU);
 };
diff --git a/src/Device/ETC_Decoder.cpp b/src/Device/ETC_Decoder.cpp
index d3fcf6e..0f8ecaa 100644
--- a/src/Device/ETC_Decoder.cpp
+++ b/src/Device/ETC_Decoder.cpp
@@ -667,7 +667,7 @@
 }  // namespace
 
 // Decodes 1 to 4 channel images to 8 bit output
-bool ETC_Decoder::Decode(const unsigned char *src, unsigned char *dst, int w, int h, int dstW, int dstH, int dstPitch, int dstBpp, InputType inputType)
+bool ETC_Decoder::Decode(const unsigned char *src, unsigned char *dst, int w, int h, int dstPitch, int dstBpp, InputType inputType)
 {
 	const ETC2 *sources[2];
 	sources[0] = (const ETC2 *)src;
@@ -683,7 +683,7 @@
 				unsigned char *dstRow = dst + (y * dstPitch);
 				for(int x = 0; x < w; x += 4, sources[0]++)
 				{
-					ETC2::DecodeBlock(sources, dstRow + (x * dstBpp), 1, x, y, dstW, dstH, dstPitch, inputType == ETC_R_SIGNED, true);
+					ETC2::DecodeBlock(sources, dstRow + (x * dstBpp), 1, x, y, w, h, dstPitch, inputType == ETC_R_SIGNED, true);
 				}
 			}
 			break;
@@ -695,7 +695,7 @@
 				unsigned char *dstRow = dst + (y * dstPitch);
 				for(int x = 0; x < w; x += 4, sources[0] += 2, sources[1] += 2)
 				{
-					ETC2::DecodeBlock(sources, dstRow + (x * dstBpp), 2, x, y, dstW, dstH, dstPitch, inputType == ETC_RG_SIGNED, true);
+					ETC2::DecodeBlock(sources, dstRow + (x * dstBpp), 2, x, y, w, h, dstPitch, inputType == ETC_RG_SIGNED, true);
 				}
 			}
 			break;
@@ -706,7 +706,7 @@
 				unsigned char *dstRow = dst + (y * dstPitch);
 				for(int x = 0; x < w; x += 4, sources[0]++)
 				{
-					sources[0]->decodeBlock(dstRow + (x * dstBpp), x, y, dstW, dstH, dstPitch, alphaValues, inputType == ETC_RGB_PUNCHTHROUGH_ALPHA);
+					sources[0]->decodeBlock(dstRow + (x * dstBpp), x, y, w, h, dstPitch, alphaValues, inputType == ETC_RGB_PUNCHTHROUGH_ALPHA);
 				}
 			}
 			break;
@@ -717,11 +717,11 @@
 				for(int x = 0; x < w; x += 4)
 				{
 					// Decode Alpha
-					ETC2::DecodeBlock(&sources[0], &(alphaValues[0][0]), 1, x, y, dstW, dstH, 4, false, false);
+					ETC2::DecodeBlock(&sources[0], &(alphaValues[0][0]), 1, x, y, w, h, 4, false, false);
 					sources[0]++;  // RGBA packets are 128 bits, so move on to the next 64 bit packet to decode the RGB color
 
 					// Decode RGB
-					sources[0]->decodeBlock(dstRow + (x * dstBpp), x, y, dstW, dstH, dstPitch, alphaValues, false);
+					sources[0]->decodeBlock(dstRow + (x * dstBpp), x, y, w, h, dstPitch, alphaValues, false);
 					sources[0]++;
 				}
 			}
diff --git a/src/Device/ETC_Decoder.hpp b/src/Device/ETC_Decoder.hpp
index f202bec..f606753 100644
--- a/src/Device/ETC_Decoder.hpp
+++ b/src/Device/ETC_Decoder.hpp
@@ -29,13 +29,11 @@
 	/// ETC_Decoder::Decode - Decodes 1 to 4 channel images to 8 bit output
 	/// @param src            Pointer to ETC2 encoded image
 	/// @param dst            Pointer to BGRA, 8 bit output
-	/// @param w              src image width
-	/// @param h              src image height
-	/// @param dstW           dst image width
-	/// @param dstH           dst image height
+	/// @param w              image width
+	/// @param h              image height
 	/// @param dstPitch       dst image pitch (bytes per row)
 	/// @param dstBpp         dst image bytes per pixel
 	/// @param inputType      src's format
 	/// @return               true if the decoding was performed
-	static bool Decode(const unsigned char *src, unsigned char *dst, int w, int h, int dstW, int dstH, int dstPitch, int dstBpp, InputType inputType);
+	static bool Decode(const unsigned char *src, unsigned char *dst, int w, int h, int dstPitch, int dstBpp, InputType inputType);
 };
diff --git a/src/Vulkan/VkFormat.cpp b/src/Vulkan/VkFormat.cpp
index 72cef97..2638439 100644
--- a/src/Vulkan/VkFormat.cpp
+++ b/src/Vulkan/VkFormat.cpp
@@ -592,11 +592,13 @@
 		case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
 		case VK_FORMAT_BC2_UNORM_BLOCK:
 		case VK_FORMAT_BC3_UNORM_BLOCK:
+		case VK_FORMAT_BC7_UNORM_BLOCK:
 			return VK_FORMAT_B8G8R8A8_UNORM;
 		case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
 		case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
 		case VK_FORMAT_BC2_SRGB_BLOCK:
 		case VK_FORMAT_BC3_SRGB_BLOCK:
+		case VK_FORMAT_BC7_SRGB_BLOCK:
 			return VK_FORMAT_B8G8R8A8_SRGB;
 		case VK_FORMAT_BC4_UNORM_BLOCK:
 			return VK_FORMAT_R8_UNORM;
@@ -636,6 +638,8 @@
 		case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
 		case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
 			return VK_FORMAT_R8G8B8A8_SRGB;
+		case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+		case VK_FORMAT_BC6H_SFLOAT_BLOCK:
 		case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT:
 		case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT:
 		case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT:
@@ -1316,6 +1320,8 @@
 		case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
 		case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
 		case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+		case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+		case VK_FORMAT_BC6H_SFLOAT_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
 			return 3;
@@ -1376,6 +1382,8 @@
 		case VK_FORMAT_BC2_SRGB_BLOCK:
 		case VK_FORMAT_BC3_UNORM_BLOCK:
 		case VK_FORMAT_BC3_SRGB_BLOCK:
+		case VK_FORMAT_BC7_UNORM_BLOCK:
+		case VK_FORMAT_BC7_SRGB_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
@@ -1515,6 +1523,9 @@
 		case VK_FORMAT_BC3_SRGB_BLOCK:
 		case VK_FORMAT_BC4_UNORM_BLOCK:
 		case VK_FORMAT_BC5_UNORM_BLOCK:
+		case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+		case VK_FORMAT_BC7_UNORM_BLOCK:
+		case VK_FORMAT_BC7_SRGB_BLOCK:
 		case VK_FORMAT_EAC_R11_UNORM_BLOCK:
 		case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
@@ -1577,6 +1588,7 @@
 		case VK_FORMAT_R64G64B64A64_SFLOAT:
 		case VK_FORMAT_BC4_SNORM_BLOCK:
 		case VK_FORMAT_BC5_SNORM_BLOCK:
+		case VK_FORMAT_BC6H_SFLOAT_BLOCK:
 		case VK_FORMAT_EAC_R11_SNORM_BLOCK:
 		case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
 		case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT:
@@ -1916,6 +1928,10 @@
 		case VK_FORMAT_BC3_SRGB_BLOCK:
 		case VK_FORMAT_BC5_UNORM_BLOCK:
 		case VK_FORMAT_BC5_SNORM_BLOCK:
+		case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+		case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+		case VK_FORMAT_BC7_UNORM_BLOCK:
+		case VK_FORMAT_BC7_SRGB_BLOCK:
 		case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
 		case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 08e3d8e..4c6e110 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -78,6 +78,12 @@
 		case VK_FORMAT_BC5_UNORM_BLOCK:
 		case VK_FORMAT_BC5_SNORM_BLOCK:
 			return 5;
+		case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+		case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+			return 6;
+		case VK_FORMAT_BC7_UNORM_BLOCK:
+		case VK_FORMAT_BC7_SRGB_BLOCK:
+			return 7;
 		default:
 			UNSUPPORTED("format: %d", int(format));
 			return 0;
@@ -86,7 +92,7 @@
 
 // Returns true for BC1 if we have an RGB format, false for RGBA
 // Returns true for BC4 and BC5 if we have an unsigned format, false for signed
-// Ignored by BC2 and BC3
+// Ignored by BC2, BC3, BC6 and BC7
 bool GetNoAlphaOrUnsigned(const vk::Format &format)
 {
 	switch(format)
@@ -104,6 +110,10 @@
 		case VK_FORMAT_BC3_SRGB_BLOCK:
 		case VK_FORMAT_BC4_SNORM_BLOCK:
 		case VK_FORMAT_BC5_SNORM_BLOCK:
+		case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+		case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+		case VK_FORMAT_BC7_SRGB_BLOCK:
+		case VK_FORMAT_BC7_UNORM_BLOCK:
 			return false;
 		default:
 			UNSUPPORTED("format: %d", int(format));
@@ -986,6 +996,10 @@
 			case VK_FORMAT_BC4_SNORM_BLOCK:
 			case VK_FORMAT_BC5_UNORM_BLOCK:
 			case VK_FORMAT_BC5_SNORM_BLOCK:
+			case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+			case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+			case VK_FORMAT_BC7_UNORM_BLOCK:
+			case VK_FORMAT_BC7_SRGB_BLOCK:
 				decodeBC(subresourceRange);
 				break;
 			case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
@@ -1103,7 +1117,7 @@
 				}
 
 				ETC_Decoder::Decode(source, dest, mipLevelExtent.width, mipLevelExtent.height,
-				                    mipLevelExtent.width, mipLevelExtent.height, pitchB, bytes, inputType);
+				                    pitchB, bytes, inputType);
 			}
 		}
 	}
@@ -1136,7 +1150,7 @@
 				uint8_t *dest = static_cast<uint8_t *>(decompressedImage->getTexelPointer({ 0, 0, depth }, subresourceLayers));
 
 				BC_Decoder::Decode(source, dest, mipLevelExtent.width, mipLevelExtent.height,
-				                   mipLevelExtent.width, mipLevelExtent.height, pitchB, bytes, n, noAlphaU);
+				                   pitchB, bytes, n, noAlphaU);
 			}
 		}
 	}
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 3cd4561..e50654b 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -521,6 +521,8 @@
 		case VK_FORMAT_BC4_SNORM_BLOCK:
 		case VK_FORMAT_BC5_UNORM_BLOCK:
 		case VK_FORMAT_BC5_SNORM_BLOCK:
+		case VK_FORMAT_BC7_UNORM_BLOCK:
+		case VK_FORMAT_BC7_SRGB_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
 		case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: