Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(371)

Unified Diff: src/utils/SkTextureCompressor_ASTC.cpp

Issue 444433002: Initial ASTC decoder -- currently only supports 2D LDR decomrpession modes. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/utils/SkTextureCompressor_ASTC.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/utils/SkTextureCompressor_ASTC.cpp
diff --git a/src/utils/SkTextureCompressor_ASTC.cpp b/src/utils/SkTextureCompressor_ASTC.cpp
index 8efffdfc9e8f04e035009bfc17a207ab8297758c..17269b84b0941267b3088064ab6d558ba15a38c2 100644
--- a/src/utils/SkTextureCompressor_ASTC.cpp
+++ b/src/utils/SkTextureCompressor_ASTC.cpp
@@ -261,6 +261,1653 @@ inline void CompressA8ASTCBlockVertical(uint8_t* dst, const uint8_t* src) {
}
////////////////////////////////////////////////////////////////////////////////
+//
+// ASTC Decoder
+//
+// Full details available in the spec:
+// http://www.khronos.org/registry/gles/extensions/OES/OES_texture_compression_astc.txt
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#define ASSERT_ASTC_DECODE_ERROR 0
+
+// An ASTC block is 128 bits. We represent it as two 64-bit integers in order
+// to efficiently operate on the block using bitwise operations.
robertphillips 2014/08/05 15:29:03 Skia-fy this? Maybe ASTCBlock ?
krajcevski 2014/08/05 20:55:06 Done.
+struct astcBlock_t {
+ uint64_t low;
+ uint64_t high;
+};
+
+// Writes the given color to every pixel in the block. This is used by void-extent
+// blocks (a special constant-color encoding of a block) and by the error function.
+static inline void write_constant_color(uint8_t* dst, int dstRowBytes, SkColor color) {
robertphillips 2014/08/05 15:29:03 12? i,j -> x,y ?
krajcevski 2014/08/05 20:55:05 Done.
+ for (int j = 0; j < 12; ++j) {
+ SkColor *dstColors = reinterpret_cast<SkColor*>(dst + j*dstRowBytes);
+ for (int i = 0; i < 12; ++i) {
+ dstColors[i] = color;
+ }
+ }
+}
+
+// Sets the entire block to the ASTC "error" color, a disgusting magenta
+// that's not supposed to appear in natural images.
+static inline void write_error_color(uint8_t* dst, int dstRowBytes) {
+ static const SkColor kASTCErrorColor = SkColorSetRGB(0xFF, 0, 0xFF);
+
+#if ASSERT_ASTC_DECODE_ERROR
+ SkDEBUGFAIL("ASTC decoding error!\n");
+#endif
+
+ write_constant_color(dst, dstRowBytes, kASTCErrorColor);
+}
+
+// Reads an ASTC block from the given pointer.
+static inline void read_astc_block(astcBlock_t *dst, const uint8_t* src) {
+ const uint64_t* qword = reinterpret_cast<const uint64_t*>(src);
+ dst->low = SkEndian_SwapLE64(qword[0]);
+ dst->high = SkEndian_SwapLE64(qword[1]);
+}
+
+// Reads up to 64 bits of the ASTC block starting from bit
+// 'from' and going up to but not including bit 'to'. 'from' starts
+// counting from the LSB, counting up to the MSB. Returns -1 on
+// error.
+static uint64_t read_astc_bits(const astcBlock_t &block, int from, int to) {
robertphillips 2014/08/05 15:29:02 Assert from & to are >= 0 && < 128 ?
krajcevski 2014/08/05 20:55:05 Done.
+ const int nBits = to - from;
+ if (0 == nBits) {
+ return 0;
+ }
+
+ if (nBits < 0 || 64 <= nBits) {
+ SkDEBUGFAIL("ASTC -- shouldn't read more than 64 bits");
+ return -1;
+ }
+
robertphillips 2014/08/05 15:29:03 I don't think we need these two asserts.
krajcevski 2014/08/05 20:55:07 Done.
+ SkASSERT(to > from);
+ SkASSERT(nBits > 0);
+
+ uint64_t result = 0;
robertphillips 2014/08/05 15:29:04 // remember: the 'to' bit isn't read
krajcevski 2014/08/05 20:55:07 Done.
+ if (to <= 64) {
robertphillips 2014/08/05 15:29:02 // All desired bits are in low ?
krajcevski 2014/08/05 20:55:08 Done.
+ result = (block.low >> from) & ((1ULL << nBits) - 1);
+ } else if (from >= 64) {
robertphillips 2014/08/05 15:29:02 // All desired bits are in high ?
krajcevski 2014/08/05 20:55:06 Done.
+ result = (block.high >> (from - 64)) & ((1ULL << nBits) - 1);
+ } else {
+ // from < 64 && to > 64
+ SkASSERT(nBits > (64 - from));
robertphillips 2014/08/05 15:29:02 It seems like you should compute nLow first - clar
krajcevski 2014/08/05 20:55:08 Done.
+ const int nHigh = nBits - (64 - from);
+ const int nLow = nBits - nHigh;
+ SkASSERT(nLow + nHigh == nBits);
+ result =
+ ((block.low >> from) & ((1ULL << nLow) - 1)) |
+ ((block.high & ((1ULL << nHigh) - 1)) << nLow);
+ }
+
+ return result;
+}
+
+// Reverse 64-bit integer taken from TAOCP 4a, although it's better
+// documented at this site:
+// http://matthewarcus.wordpress.com/2012/11/18/reversing-a-64-bit-word/
+
+template <typename T, T m, int k>
+static inline T swap_bits(T p) {
+ T q = ((p>>k)^p) & m;
+ return p^q^(q<<k);
+}
+
+static inline uint64_t reverse64(uint64_t n) {
+ static const uint64_t m0 = 0x5555555555555555LLU;
+ static const uint64_t m1 = 0x0300c0303030c303LLU;
+ static const uint64_t m2 = 0x00c0300c03f0003fLLU;
+ static const uint64_t m3 = 0x00000ffc00003fffLLU;
+ n = ((n>>1)&m0) | (n&m0)<<1;
+ n = swap_bits<uint64_t, m1, 4>(n);
+ n = swap_bits<uint64_t, m2, 8>(n);
+ n = swap_bits<uint64_t, m3, 20>(n);
+ n = (n >> 34) | (n << 30);
+ return n;
+}
+
robertphillips 2014/08/05 15:29:02 Would this make more sense as a method on astcBloc
krajcevski 2014/08/05 20:55:06 Done.
+// Reverses the bits of an ASTC block, making the LSB of the
+// 128 bit block the MSB.
robertphillips 2014/08/05 15:29:03 x -> block ?
krajcevski 2014/08/05 20:55:05 Done.
+static inline void reverse_block(astcBlock_t *x) {
robertphillips 2014/08/05 15:29:03 uint64_t newLow = reverse64(block->high); block->h
krajcevski 2014/08/05 20:55:08 Done.
+ const astcBlock_t b = { reverse64(x->high), reverse64(x->low) };
+ x->high = b.high;
+ x->low = b.low;
+}
+
robertphillips 2014/08/05 15:29:02 SkIsPow2 in SkMath.h ?
krajcevski 2014/08/05 20:55:06 Done.
+// Helper function that returns true if x is a power of two.
+static inline bool is_power_of_two(int x) {
+ return (x & (x - 1)) == 0;
+}
+
robertphillips 2014/08/05 15:29:04 Could SkNextLog2 serve here ?
krajcevski 2014/08/05 20:55:08 Done.
+// Returns the number of bits needed to represent a number
+// in the given power-of-two range.
+static inline int bits_for_range(int x) {
+ SkASSERT(is_power_of_two(x));
+ SkASSERT(0 != x);
+
+ int cnt = 0;
+ while (x >>= 1) {
+ cnt++;
+ }
+
+ return cnt;
+}
+
robertphillips 2014/08/05 15:29:02 Use SkClampMax ?
krajcevski 2014/08/05 20:55:09 Done.
+// Clamps an integer to the range [0, 255]
+static inline int clamp_byte(int x) {
+ return (x > 255)? 255 : ((x < 0)? 0 : x);
+}
+
+// Helper function defined in the ASTC spec, section C.2.14
robertphillips 2014/08/05 15:29:03 // It ... ?
krajcevski 2014/08/05 20:55:06 Done.
+static inline void bit_transfer_signed(int *a, int *b) {
+ *b >>= 1;
+ *b |= *a & 0x80;
+ *a >>= 1;
+ *a &= 0x3F;
+ if ( (*a & 0x20) != 0 ) {
+ *a -= 0x40;
+ }
+}
+
+// Helper function defined in the ASTC spec, section C.2.14
robertphillips 2014/08/05 15:29:03 // It ... ?
krajcevski 2014/08/05 20:55:07 Done.
+static inline SkColor blue_contract(int a, int r, int g, int b) {
+ return SkColorSetARGB(a, (r + b) >> 1, (g + b) >> 1, b);
+}
+
+// A helper class used to decode bit values from standard integer values.
+// We can't use this class with astcBlock_t because then it would need to
+// handle multi-value ranges, and it's non-trivial to lookup a range of bits
+// that splits across two different ints.
+template <typename T>
+class SkTBits {
+public:
+ SkTBits(const T val) : fVal(val) { }
+
+ // Returns the bit at the given position
+ T operator [](const int idx) const {
+ return (fVal >> idx) & 1;
+ }
+
+ // Returns the bits in the given range, inclusive
robertphillips 2014/08/05 15:29:03 Why not start then end ?
krajcevski 2014/08/05 20:55:05 This way the code below matches the spec better.
+ T operator ()(const int end, const int start) const {
+ SkASSERT(end >= start);
+ return (fVal >> start) & ((1ULL << ((end - start) + 1)) - 1);
+ }
+
+private:
+ const T fVal;
+};
+
+// This algorithm matches the trit block decoding in the spec (Table C.2.14)
+static void decode_trit_block(int* dst, int nBits, const uint64_t &block) {
+
+ SkTBits<uint64_t> blockBits(block);
+
robertphillips 2014/08/05 15:29:03 Is there a better name then 'm'?
krajcevski 2014/08/05 20:55:07 m is chosen to match the naming in the spec.
+ int m[5];
+ if (0 == nBits) {
+ memset(m, 0, sizeof(m));
+ } else {
robertphillips 2014/08/05 15:29:02 // The trits are arranged ...
krajcevski 2014/08/05 20:55:06 Done.
+ m[0] = blockBits(nBits - 1, 0);
+ m[1] = blockBits(2*nBits - 1 + 2, nBits + 2);
+ m[2] = blockBits(3*nBits - 1 + 4, 2*nBits + 4);
+ m[3] = blockBits(4*nBits - 1 + 5, 3*nBits + 5);
+ m[4] = blockBits(5*nBits - 1 + 7, 4*nBits + 7);
+ }
+
robertphillips 2014/08/05 15:29:03 Blarg for the rest of this function
krajcevski 2014/08/05 20:55:09 I tried to match this function to the spec as much
+ int T =
+ blockBits(nBits + 1, nBits) |
+ (blockBits(2*nBits + 2 + 1, 2*nBits + 2) << 2) |
+ (blockBits[3*nBits + 4] << 4) |
+ (blockBits(4*nBits + 5 + 1, 4*nBits + 5) << 5) |
+ (blockBits[5*nBits + 7] << 7);
+
+ int t[5];
+
+ int C;
+ SkTBits<int> Tbits(T);
+ if (0x7 == Tbits(4, 2)) {
+ C = (Tbits(7, 5) << 2) | Tbits(1, 0);
+ t[3] = t[4] = 2;
+ } else {
+ C = Tbits(4, 0);
+ if (Tbits(6, 5) == 0x3) {
+ t[4] = 2; t[3] = Tbits[7];
+ } else {
+ t[4] = Tbits[7]; t[3] = Tbits(6, 5);
+ }
+ }
+
+ SkTBits<int> Cbits(C);
+ if (Cbits(1, 0) == 0x3) {
+ t[2] = 2;
+ t[1] = Cbits[4];
+ t[0] = (Cbits[3] << 1) | (Cbits[2] & (0x1 & ~(Cbits[3])));
+ } else if (Cbits(3, 2) == 0x3) {
+ t[2] = 2;
+ t[1] = 2;
+ t[0] = Cbits(1, 0);
+ } else {
+ t[2] = Cbits[4];
+ t[1] = Cbits(3, 2);
+ t[0] = (Cbits[1] << 1) | (Cbits[0] & (0x1 & ~(Cbits[1])));
+ }
+
+#if SK_DEBUG
+ for (int i = 0; i < 5; ++i) {
+ SkASSERT(t[i] < 3);
+ SkASSERT(m[i] < (1 << nBits));
+ }
+#endif
+
+ for (int i = 0; i < 5; ++i) {
+ *dst = (t[i] << nBits) + m[i];
+ ++dst;
+ }
+}
+
robertphillips 2014/08/05 15:29:02 trit -> quint ?
krajcevski 2014/08/05 20:55:05 Done.
+// This algorithm matches the trit block decoding in the spec (Table C.2.15)
+static void decode_quint_block(int* dst, int nBits, const uint64_t &block) {
+ SkTBits<uint64_t> blockBits(block);
+
+ int m[3];
+ if (0 == nBits) {
+ memset(m, 0, sizeof(m));
+ } else {
robertphillips 2014/08/05 15:29:02 // The quints are arranged ...
krajcevski 2014/08/05 20:55:08 Done.
+ m[0] = blockBits(nBits - 1, 0);
+ m[1] = blockBits(2*nBits - 1 + 3, nBits + 3);
+ m[2] = blockBits(3*nBits - 1 + 5, 2*nBits + 5);
+ }
+
robertphillips 2014/08/05 15:29:03 Blarg
krajcevski 2014/08/05 20:55:07 Blarg. :(
+ int Q =
+ blockBits(nBits + 2, nBits) |
+ (blockBits(2*nBits + 3 + 1, 2*nBits + 3) << 3) |
+ (blockBits(3*nBits + 5 + 1, 3*nBits + 5) << 5);
+
+ int q[3];
+ SkTBits<int> Qbits(Q); // quantum?
+
+ if (Qbits(2, 1) == 0x3 && Qbits(6, 5) == 0) {
+ const int notBitZero = (0x1 & ~(Qbits[0]));
+ q[2] = (Qbits[0] << 2) | ((Qbits[4] & notBitZero) << 1) | (Qbits[3] & notBitZero);
+ q[1] = 4;
+ q[0] = 4;
+ } else {
+ int C;
+ if (Qbits(2, 1) == 0x3) {
+ q[2] = 4;
+ C = (Qbits(4, 3) << 3) | ((0x3 & ~(Qbits(6, 5))) << 1) | Qbits[0];
+ } else {
+ q[2] = Qbits(6, 5);
+ C = Qbits(4, 0);
+ }
+
+ SkTBits<int> Cbits(C);
+ if (Cbits(2, 0) == 0x5) {
+ q[1] = 4;
+ q[0] = Cbits(4, 3);
+ } else {
+ q[1] = Cbits(4, 3);
+ q[0] = Cbits(2, 0);
+ }
+ }
+
+#if SK_DEBUG
+ for (int i = 0; i < 3; ++i) {
+ SkASSERT(q[i] < 5);
+ SkASSERT(m[i] < (1 << nBits));
+ }
+#endif
+
+ for (int i = 0; i < 3; ++i) {
+ *dst = (q[i] << nBits) + m[i];
+ ++dst;
+ }
+}
+
+// Function that decodes a sequence of integers stored as an ISE (Integer
+// Sequence Encoding) bit stream. The full details of this function are outlined
+// in section C.2.12 of the ASTC spec. A brief overview is as follows:
+//
+// - Each integer in the sequence is bounded by a specific range r.
+// - The range of each value determines the way the bit stream is interpreted,
+// - If the range is a power of two, then the sequence is a sequence of bits
+// - If the range is of the form 3*2^n, then the sequence is stored as a
+// sequence of blocks, each block contains 5 trits and 5 bit sequences, which
+// decodes into 5 values.
+// - Similarly, if the range is of the form 5*2^n, then the sequence is stored as a
robertphillips 2014/08/05 15:29:02 trits -> quints ?
krajcevski 2014/08/05 20:55:06 Done.
+// sequence of blocks, each block contains 3 trits and 3 bit sequences, which
robertphillips 2014/08/05 15:29:02 5 -> 3 ?
krajcevski 2014/08/05 20:55:06 Done.
+// decodes into 5 values.
+static bool decode_integer_sequence(
+ int* dst, // The array holding the destination bits
+ int dstSize, // The maximum size of the array
+ int nVals, // The number of values that we'd like to decode
+ const astcBlock_t &block, // The block that we're decoding from
+ int startBit, // The bit from which we're going to do the reading
robertphillips 2014/08/05 15:29:03 inclusive or not ?
krajcevski 2014/08/05 20:55:08 Done.
+ int endBit, // The bit at which we stop reading
+ bool bReadForward, // If true, then read LSB -> MSB, else read MSB -> LSB
+ int nBits, // The number of bits representing this encoding
+ int nTrits, // The number of trits representing this encoding
+ int nQuints // The number of quints representing this encoding
+) {
+ // If we want more values than we have, then fail.
+ if (nVals > dstSize) {
+ return false;
+ }
+
+ astcBlock_t src = block;
+
+ if (!bReadForward) {
+ reverse_block(&src);
+ startBit = 128 - startBit;
+ endBit = 128 - endBit;
+ }
+
+ while (nVals > 0) {
+
+ if (nTrits > 0) {
+ SkASSERT(0 == nQuints);
+
+ int endBlockBit = startBit + 8 + 5*nBits;
+ if (endBlockBit > endBit) {
+ endBlockBit = endBit;
+ }
+
+ decode_trit_block(dst, nBits, read_astc_bits(src, startBit, endBlockBit));
+ dst += 5;
+ nVals -= 5;
+ startBit = endBlockBit;
+
+ } else if (nQuints > 0) {
+ SkASSERT(0 == nTrits);
+
+ int endBlockBit = startBit + 7 + 3*nBits;
+ if (endBlockBit > endBit) {
+ endBlockBit = endBit;
+ }
+
+ decode_quint_block(dst, nBits, read_astc_bits(src, startBit, endBlockBit));
+ dst += 3;
+ nVals -= 3;
+ startBit = endBlockBit;
+
+ } else {
+ // Just read the bits, but don't read more than we have...
+ int endValBit = startBit + nBits;
+ if (endValBit > endBit) {
+ endValBit = endBit;
+ }
+
+ *dst = read_astc_bits(src, startBit, endValBit);
+ ++dst;
+ --nVals;
+ startBit = endValBit;
+ }
+ }
+
+ return true;
+}
+
+// Helper function that unquantizes some (seemingly random) generated
+// numbers... meant to match the ASTC hardware. This function is used
robertphillips 2014/08/05 15:29:02 to unquantize ?
krajcevski 2014/08/05 20:55:08 Done.
+// by unquantizing both colors (Table C.2.16) and weights (Table C.2.26)
robertphillips 2014/08/05 17:30:01 Is templating on a mask really useful? Since its i
krajcevski 2014/08/05 20:55:08 Done.
+template<unsigned mask>
+static inline int unquantize_value(int A, int B, int C, int D) {
+ int T = D * C + B;
+ T = T ^ A;
+ T = (A & mask) | (T >> 2);
+ SkASSERT(T < 256);
+ return T;
+}
+
robertphillips 2014/08/05 15:29:04 replicated -> replicate ?
krajcevski 2014/08/05 20:55:05 Done.
+// Helper function to replicated the bits in x that represents an oldPrec
+// precision integer into a prec precision integer. For example:
+// 255 == replicate_bits(7, 3, 8);
+static inline int replicate_bits(int x, int oldPrec, int prec) {
+ while (oldPrec < prec) {
+ const int toShift = SkMin32(prec-oldPrec, oldPrec);
+ x = (x << toShift) | (x >> (oldPrec - toShift));
+ oldPrec += toShift;
+ }
robertphillips 2014/08/05 15:29:03 Add assert that no bits are set outside of desired
krajcevski 2014/08/05 20:55:05 Done.
+ return x;
+}
+
+// Returns the unquantized value of a color that's represented only as
+// a set of bits.
+static inline int unquantize_bits_color(int val, int nBits) {
+ return replicate_bits(val, nBits, 8);
+}
+
+// Returns the unquantized value of a color that's represented as a
+// trit followed by nBits bits. This algorithm follows the sequence
+// defined in section C.2.13 of the ASTC spec.
+static inline int unquantize_trit_color(int val, int nBits) {
+ const int D = (val >> nBits) & 0x3;
+ SkASSERT(D < 3);
+
+ const int A = -(val & 0x1) & 0x1FF;
+
+ static const int Cvals[6] = { 204, 93, 44, 22, 11, 5 };
robertphillips 2014/08/05 17:30:00 Move these asserts to the start of the function?
krajcevski 2014/08/05 20:55:09 Done.
+ SkASSERT(nBits > 0);
+ SkASSERT(nBits < 7);
+
+ const int C = Cvals[nBits - 1];
+
+ int B = 0;
+ const SkTBits<int> valBits(val);
+ switch (nBits) {
+ case 1:
+ B = 0;
+ break;
+
robertphillips 2014/08/05 17:30:00 In Skia this is formatted as: case kGaussian_
krajcevski 2014/08/05 20:55:06 Done.
+ case 2:
+ {
+ const int b = valBits[1];
+ B = (b << 1) | (b << 2) | (b << 4) | (b << 8);
+ }
+ break;
+
+ case 3:
+ {
+ const int cb = valBits(2, 1);
+ B = cb | (cb << 2) | (cb << 7);
+ }
+ break;
+
+ case 4:
+ {
+ const int dcb = valBits(3, 1);
+ B = dcb | (dcb << 6);
+ }
+ break;
+
+ case 5:
+ {
+ const int edcb = valBits(4, 1);
+ B = (edcb << 5) | (edcb >> 2);
+ }
+ break;
+
+ case 6:
+ {
+ const int fedcb = valBits(5, 1);
+ B = (fedcb << 4) | (fedcb >> 4);
+ }
+ break;
+ }
+
+ return unquantize_value<0x80>(A, B, C, D);
+}
+
+// Returns the unquantized value of a color that's represented as a
+// quint followed by nBits bits. This algorithm follows the sequence
+// defined in section C.2.13 of the ASTC spec.
+static inline int unquantize_quint_color(int val, int nBits) {
+ const int D = (val >> nBits) & 0x7;
+ SkASSERT(D < 5);
+
+ const int A = -(val & 0x1) & 0x1FF;
+
+ static const int Cvals[5] = { 113, 54, 26, 13, 6 };
+ SkASSERT(nBits > 0);
+ SkASSERT(nBits < 6);
+
+ const int C = Cvals[nBits - 1];
+
+ int B = 0;
+ const SkTBits<int> valBits(val);
+ switch (nBits) {
+ case 1:
+ B = 0;
+ break;
+
+ case 2:
+ {
+ const int b = valBits[1];
+ B = (b << 2) | (b << 3) | (b << 8);
+ }
+ break;
+
+ case 3:
+ {
+ const int cb = valBits(2, 1);
+ B = (cb >> 1) | (cb << 1) | (cb << 7);
+ }
+ break;
+
+ case 4:
+ {
+ const int dcb = valBits(3, 1);
+ B = (dcb >> 1) | (dcb << 6);
+ }
+ break;
+
+ case 5:
+ {
+ const int edcb = valBits(4, 1);
+ B = (edcb << 5) | (edcb >> 3);
+ }
+ break;
+ }
+
+ return unquantize_value<0x80>(A, B, C, D);
+}
+
+// This algorithm takes a list of integers, stored in vals, and unquantizes them
+// in place. This follows the algorithm laid out in section C.2.13 of the ASTC spec.
robertphillips 2014/08/05 17:30:00 This seems a bit odd. It seems like you actually w
krajcevski 2014/08/05 20:55:06 nBits is a value that needs to be passed to either
+static void unquantize_colors(int *vals, int nVals, int nBits, int nTrits, int nQuints) {
+ for (int i = 0; i < nVals; ++i) {
+ if (nTrits > 0) {
+ SkASSERT(nQuints == 0);
robertphillips 2014/08/05 17:30:00 extra space after ',' ?
krajcevski 2014/08/05 20:55:07 Done.
+ vals[i] = unquantize_trit_color(vals[i], nBits);
+ } else if (nQuints > 0) {
+ SkASSERT(nTrits == 0);
+ vals[i] = unquantize_quint_color(vals[i], nBits);
+ } else {
+ SkASSERT(nQuints == 0 && nTrits == 0);
+ vals[i] = unquantize_bits_color(vals[i], nBits);
+ }
+ }
+}
+
+// Returns an interpolated value between c0 and c1 based on the weight. This
+// follows the algorithm laid out in section C.2.19 of the ASTC spec.
+static int interpolate_channel(int c0, int c1, int weight) {
+ SkASSERT(0 <= c0 && c0 < 256);
+ SkASSERT(0 <= c1 && c1 < 256);
+
+ c0 = (c0 << 8) | c0;
+ c1 = (c1 << 8) | c1;
+
+ const int result = ((c0*(64 - weight) + c1*weight + 32) / 64) >> 8;
+
+ if (result > 255) {
+ return 255;
+ }
+
+ SkASSERT(result >= 0);
+ return result;
+}
+
+// Returns an interpolated color between the two endpoints based on the weight.
+static SkColor interpolate_endpoints(const SkColor endpoints[2], int weight) {
+ return SkColorSetARGB(
+ interpolate_channel(SkColorGetA(endpoints[0]), SkColorGetA(endpoints[1]), weight),
+ interpolate_channel(SkColorGetR(endpoints[0]), SkColorGetR(endpoints[1]), weight),
+ interpolate_channel(SkColorGetG(endpoints[0]), SkColorGetG(endpoints[1]), weight),
+ interpolate_channel(SkColorGetB(endpoints[0]), SkColorGetB(endpoints[1]), weight));
+}
+
+// Returns an interpolated color between the two endpoints based on the weight.
+// It uses separate weights for the channel depending on the value of the 'plane'
+// variable. By default, all channels will use weight 0, and the value of plane
+// means that weight1 will be used for:
+// 0: red
+// 1: green
+// 2: blue
+// 3: alpha
+static SkColor interpolate_dual_endpoints(
+ const SkColor endpoints[2], int weight0, int weight1, int plane) {
+ int a = interpolate_channel(SkColorGetA(endpoints[0]), SkColorGetA(endpoints[1]), weight0);
+ int r = interpolate_channel(SkColorGetR(endpoints[0]), SkColorGetR(endpoints[1]), weight0);
+ int g = interpolate_channel(SkColorGetG(endpoints[0]), SkColorGetG(endpoints[1]), weight0);
+ int b = interpolate_channel(SkColorGetB(endpoints[0]), SkColorGetB(endpoints[1]), weight0);
+
+ switch (plane) {
+
+ case 0:
+ r = interpolate_channel(
+ SkColorGetR(endpoints[0]), SkColorGetR(endpoints[1]), weight1);
+ break;
+
+ case 1:
+ g = interpolate_channel(
+ SkColorGetG(endpoints[0]), SkColorGetG(endpoints[1]), weight1);
+ break;
+
+ case 2:
+ b = interpolate_channel(
+ SkColorGetB(endpoints[0]), SkColorGetB(endpoints[1]), weight1);
+ break;
+
+ case 3:
+ a = interpolate_channel(
+ SkColorGetA(endpoints[0]), SkColorGetA(endpoints[1]), weight1);
+ break;
+
+ default:
+ SkDEBUGFAIL("Plane should be 0-3");
+ break;
+ }
+
+ return SkColorSetARGB(a, r, g, b);
+}
+
+// A struct of decoded values that we use to carry around information
+// about the block. dimX and dimY are the dimension in texels of the block,
+// for which there is only a limited subset of valid values.
robertphillips 2014/08/05 15:29:02 such as ?
krajcevski 2014/08/05 20:55:05 Done.
+struct ASTCDecompressionData {
+ ASTCDecompressionData(int dimX, int dimY) : fDimX(dimX), fDimY(dimY) { }
+ const int fDimX; // the X dimension of the decompressed block
+ const int fDimY; // the Y dimension of the decompressed block
+ astcBlock_t fBlock; // the block data
+ int fBlockMode; // the block header that contains the block mode.
+
+ bool fDualPlaneEnabled; // is this block compressing dual weight planes?
+ int fDualPlane; // the independent plane in dual plane mode.
+
+ bool fVoidExtent; // is this block a single color?
+ bool fError; // does this block have an error encoding?
+
+ int fWeightDimX; // the x dimension of the weight grid
+ int fWeightDimY; // the y dimension of the weight grid
+
+ int fWeightBits; // the number of bits used for each weight value
+ int fWeightTrits; // the number of trits used for each weight value
+ int fWeightQuints; // the number of quints used for each weight value
+
+ int fPartCount; // the number of partitions in this block
+ int fPartIndex; // the partition index: only relevant if fPartCount > 0
+
+ static const int kMaxPartitions = 4;
robertphillips 2014/08/05 17:30:00 // The possible CEM values are ... ?
krajcevski 2014/08/05 20:55:08 Done.
+ int fCEM[kMaxPartitions]; // the color endpoint modes for this block.
+
+ int fColorStartBit; // The bit position of the first bit of the color data
+ int fColorEndBit; // The bit position of the last *possible* bit of the color data
+
+ // Returns the number of partitions for this block.
+ int numPartitions() const {
+ return fPartCount;
+ }
+
+ // Returns the total number of weight values that are stored in this block
+ int numWeights() const {
robertphillips 2014/08/05 17:30:01 Add a space before the '?' ?
krajcevski 2014/08/05 20:55:07 Done.
+ return fWeightDimX * fWeightDimY * (fDualPlaneEnabled? 2 : 1);
+ }
+
robertphillips 2014/08/05 17:30:00 #ifdef SK_DEBUG ?
krajcevski 2014/08/05 20:55:07 Done.
+ // Returns the maximum value that any weight can take. We really only use
+ // this function for debugging.
+ int maxWeightValue() const {
+ int maxVal = (1 << fWeightBits);
+ if (fWeightTrits > 0) {
+ SkASSERT(0 == fWeightQuints);
+ maxVal *= 3;
+ } else if (fWeightQuints > 0) {
+ SkASSERT(0 == fWeightTrits);
+ maxVal *= 5;
+ }
+ return maxVal - 1;
+ }
robertphillips 2014/08/05 17:30:00 #endif ?
krajcevski 2014/08/05 20:55:05 Done.
+
+ // The number of bits needed to represent the texel weight data. This
+ // comes from the 'data size determination' section of the ASTC spec (C.2.22)
+ int numWeightBits() const {
+ const int nWeights = this->numWeights();
+ return
+ ((nWeights*8*fWeightTrits + 4) / 5) +
+ ((nWeights*7*fWeightQuints + 2) / 3) +
+ (nWeights*fWeightBits);
+ }
+
+ // Returns the number of color values stored in this block. The number of
+ // values stored is directly a function of the color endpoint modes.
+ int numColorValues() const {
+ int numValues = 0;
+ for (int i = 0; i < this->numPartitions(); ++i) {
+ numValues += ((fCEM[i] >> 2) + 1) * 2;
+ }
+
+ return numValues;
+ }
+
+ // Figures out the number of bits available for color values, and fills
+ // in the maximum encoding that will fit the number of color values that
+ // we need. Returns false on error. (See section C.2.22 of the spec)
+ bool getColorValueEncoding(int *nBits, int *nTrits, int *nQuints) const {
+ if (NULL == nBits || NULL == nTrits || NULL == nQuints) {
+ return false;
+ }
+
+ const int nColorVals = this->numColorValues();
+ if (nColorVals <= 0) {
+ return false;
+ }
+
+ const int colorBits = fColorEndBit - fColorStartBit;
+ SkASSERT(colorBits > 0);
+
+ // This is the minimum amount of accuracy required by the spec.
+ if (colorBits < ((13 * nColorVals + 4) / 5)) {
+ return false;
+ }
+
+ // Values can be represented as at most 8-bit values.
+ // !SPEED! place this in a lookup table based on colorBits and nColorVals
+ for (int i = 255; i > 0; --i) {
+ int range = i + 1;
+ int bits = 0, trits = 0, quints = 0;
+ bool valid = false;
+ if (is_power_of_two(range)) {
+ bits = bits_for_range(range);
+ valid = true;
+ } else if ((range % 3) == 0 && is_power_of_two(range/3)) {
+ trits = 1;
+ bits = bits_for_range(range/3);
+ valid = true;
+ } else if ((range % 5) == 0 && is_power_of_two(range/5)) {
+ quints = 1;
+ bits = bits_for_range(range/5);
+ valid = true;
+ }
+
+ if (valid) {
+ const int actualColorBits =
+ ((nColorVals*8*trits + 4) / 5) +
+ ((nColorVals*7*quints + 2) / 3) +
+ (nColorVals*bits);
+ if (actualColorBits <= colorBits) {
+ *nTrits = trits;
+ *nQuints = quints;
+ *nBits = bits;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Converts the sequence of color values into endpoints. The algorithm here
+ // corresponds to the values determined by section C.2.14 of the ASTC spec
+ void colorEndpoints(SkColor endpoints[4][2], const int* colorValues) const {
+ for (int i = 0; i < this->numPartitions(); ++i) {
+ switch (fCEM[i]) {
+ // LDR Luminance, direct
robertphillips 2014/08/05 17:30:00 Can these ints be happy, happy enum values ?
krajcevski 2014/08/05 20:55:06 Done.
+ case 0:
robertphillips 2014/08/05 17:30:00 formatting ?
krajcevski 2014/08/05 20:55:06 Done.
+ {
+ const int* v = colorValues;
+ endpoints[i][0] = SkColorSetARGB(0xFF, v[0], v[0], v[0]);
+ endpoints[i][1] = SkColorSetARGB(0xFF, v[1], v[1], v[1]);
+
+ colorValues += 2;
+ }
+ break;
+
+ // LDR Luminance, base+offset
+ case 1:
+ {
+ const int* v = colorValues;
+ const int L0 = (v[0] >> 2) | (v[1] & 0xC0);
+ const int L1 = clamp_byte(L0 + (v[1] & 0x3F));
+
+ endpoints[i][0] = SkColorSetARGB(0xFF, L0, L0, L0);
+ endpoints[i][1] = SkColorSetARGB(0xFF, L1, L1, L1);
+
+ colorValues += 2;
+ }
+ break;
+
+ // LDR Luminance + Alpha, direct
+ case 4:
+ {
+ const int* v = colorValues;
+
+ endpoints[i][0] = SkColorSetARGB(v[2], v[0], v[0], v[0]);
+ endpoints[i][1] = SkColorSetARGB(v[3], v[1], v[1], v[1]);
+
+ colorValues += 4;
+ }
+ break;
+
+ // LDR Luminance + Alpha, base+offset
+ case 5:
+ {
+ int v0 = colorValues[0];
+ int v1 = colorValues[1];
+ int v2 = colorValues[2];
+ int v3 = colorValues[3];
+
+ bit_transfer_signed(&v1, &v0);
+ bit_transfer_signed(&v3, &v2);
+
+ endpoints[i][0] = SkColorSetARGB(v2, v0, v0, v0);
+ endpoints[i][1] = SkColorSetARGB(
+ clamp_byte(v3+v2),
+ clamp_byte(v1+v0),
+ clamp_byte(v1+v0),
+ clamp_byte(v1+v0));
+
+ colorValues += 4;
+ }
+ break;
+
+ // LDR RGB, base+scale
+ case 6:
+ {
+ const int* v = colorValues;
+
+ endpoints[i][0] = SkColorSetARGB(
+ 0xFF, v[0]*v[3] >> 8, v[1]*v[3] >> 8, v[2]*v[3] >> 8);
+ endpoints[i][1] = SkColorSetARGB(0xFF, v[0], v[1], v[2]);
+
+ colorValues += 4;
+ }
+ break;
+
+ // LDR RGB, direct
+ case 8:
robertphillips 2014/08/05 17:30:00 Could 8 & 12 share a sub-routine ?
krajcevski 2014/08/05 20:55:07 Done.
+ {
+ const int* v = colorValues;
+
+ const int s0 = v[0] + v[2] + v[4];
+ const int s1 = v[1] + v[3] + v[5];
+
+ if (s1 >= s0) {
+ endpoints[i][0] = SkColorSetARGB(0xFF, v[0], v[2], v[4]);
+ endpoints[i][1] = SkColorSetARGB(0xFF, v[1], v[3], v[5]);
+ } else {
+ endpoints[i][0] = blue_contract(0xFF, v[1], v[3], v[5]);
+ endpoints[i][1] = blue_contract(0xFF, v[0], v[2], v[4]);
+ }
+
+ colorValues += 6;
+ }
+ break;
+
+ // LDR RGB, base+offset
+ case 9:
robertphillips 2014/08/05 17:30:00 Could 9 & 13 share a subroutine ?
krajcevski 2014/08/05 20:55:07 Done.
+ {
+ int v0 = colorValues[0];
+ int v1 = colorValues[1];
+ int v2 = colorValues[2];
+ int v3 = colorValues[3];
+ int v4 = colorValues[4];
+ int v5 = colorValues[5];
+
+ bit_transfer_signed(&v1, &v0);
+ bit_transfer_signed(&v3, &v2);
+ bit_transfer_signed(&v5, &v4);
+
+ int c[2][4];
+ if (v1 + v3 + v5 >= 0) {
+ c[0][0] = 0xFF;
+ c[0][1] = v0;
+ c[0][2] = v2;
+ c[0][3] = v4;
+
+ c[1][0] = 0xFF;
+ c[1][1] = v0 + v1;
+ c[1][2] = v2 + v3;
+ c[1][3] = v4 + v5;
+ } else {
+ c[0][0] = 0xFF;
+ c[0][1] = (v0 + v1 + v4 + v5) >> 1;
+ c[0][2] = (v2 + v3 + v4 + v5) >> 1;
+ c[0][3] = v4 + v5;
+
+ c[1][0] = 0xFF;
+ c[1][1] = (v0 + v4) >> 1;
+ c[1][2] = (v2 + v4) >> 1;
+ c[1][3] = v4;
+ }
+
+ endpoints[i][0] = SkColorSetARGB(
robertphillips 2014/08/05 17:30:00 Can we replace clamp_byte(c[0][0]) and clamp_byte(
krajcevski 2014/08/05 20:55:07 Not if they're in their own subroutine. :)
+ clamp_byte(c[0][0]),
+ clamp_byte(c[0][1]),
+ clamp_byte(c[0][2]),
+ clamp_byte(c[0][3]));
+
+ endpoints[i][1] = SkColorSetARGB(
+ clamp_byte(c[1][0]),
+ clamp_byte(c[1][1]),
+ clamp_byte(c[1][2]),
+ clamp_byte(c[1][3]));
+
+ colorValues += 6;
+ }
+ break;
+
+ // LDR RGBA, base+scale and two alpha
+ case 10:
+ {
+ const int* v = colorValues;
+
+ endpoints[i][0] = SkColorSetARGB(v[4],
+ (v[0]*v[3]) >> 8,
+ (v[1]*v[3]) >> 8,
+ (v[2]*v[3]) >> 8);
+ endpoints[i][1] = SkColorSetARGB(v[5], v[0], v[1], v[2]);
+
+ colorValues += 6;
+ }
+ break;
+
+ // LDR RGBA, direct
+ case 12:
+ {
+ const int* v = colorValues;
+
+ if ((v[0] + v[2] + v[4]) >= (v[1] + v[3] + v[5])) {
+ endpoints[i][0] = SkColorSetARGB(v[6], v[0], v[2], v[4]);
+ endpoints[i][1] = SkColorSetARGB(v[7], v[1], v[3], v[5]);
+ } else {
+ endpoints[i][0] = blue_contract(v[7], v[1], v[3], v[5]);
+ endpoints[i][1] = blue_contract(v[6], v[0], v[2], v[4]);
+ }
+
+ colorValues += 8;
+ }
+ break;
+
+ // LDR RGBA, base+offset
+ case 13:
+ {
+ int v0 = colorValues[0];
+ int v1 = colorValues[1];
+ int v2 = colorValues[2];
+ int v3 = colorValues[3];
+ int v4 = colorValues[4];
+ int v5 = colorValues[5];
+ int v6 = colorValues[6];
+ int v7 = colorValues[7];
+
+ bit_transfer_signed(&v1, &v0);
+ bit_transfer_signed(&v3, &v2);
+ bit_transfer_signed(&v5, &v4);
+ bit_transfer_signed(&v7, &v6);
+
+ int c[2][4];
+ if ((v1 + v3 + v5) >= 0) {
+ c[0][0] = v6;
+ c[0][1] = v0;
+ c[0][2] = v2;
+ c[0][3] = v4;
+
+ c[1][0] = v6 + v7;
+ c[1][1] = v0 + v1;
+ c[1][2] = v2 + v3;
+ c[1][3] = v4 + v5;
+ } else {
+ c[0][0] = v6 + v7;
+ c[0][1] = (v0 + v1 + v4 + v5) >> 1;
+ c[0][2] = (v2 + v3 + v4 + v5) >> 1;
+ c[0][3] = v4 + v5;
+
+ c[1][0] = v6;
+ c[1][1] = (v0 + v4) >> 1;
+ c[1][2] = (v2 + v4) >> 1;
+ c[1][3] = v4;
+ }
+
+ endpoints[i][0] = SkColorSetARGB(
+ clamp_byte(c[0][0]),
+ clamp_byte(c[0][1]),
+ clamp_byte(c[0][2]),
+ clamp_byte(c[0][3]));
+
+ endpoints[i][1] = SkColorSetARGB(
+ clamp_byte(c[1][0]),
+ clamp_byte(c[1][1]),
+ clamp_byte(c[1][2]),
+ clamp_byte(c[1][3]));
+
+ colorValues += 8;
+ }
+ break;
+
+ default:
+ SkDEBUGFAIL("HDR mode unsupported! This should be caught sooner.");
+ break;
+ }
+ }
+ }
+
+ // Follows the procedure from section C.2.17 of the ASTC specification
+ int unquantizeWeight(int x) const {
+ SkASSERT(x <= this->maxWeightValue());
+
+ const int D = (x >> fWeightBits) & 0x7;
+ const int A = -(x & 0x1) & 0x7F;
+
+ SkTBits<int> xbits(x);
+
+ int T = 0;
+ if (fWeightTrits > 0) {
+ SkASSERT(0 == fWeightQuints);
+ switch (fWeightBits) {
+ case 0:
+ {
+ // x is a single trit
+ SkASSERT(x < 3);
+
+ static const int kUnquantizationTable[3] = { 0, 32, 63 };
+ T = kUnquantizationTable[x];
+ }
+ break;
+
+ case 1:
+ {
+ const int B = 0;
+ const int C = 50;
+ T = unquantize_value<0x20>(A, B, C, D);
+ }
+ break;
+
+ case 2:
+ {
+ const int b = xbits[1];
+ const int B = b | (b << 2) | (b << 6);
+ const int C = 23;
+ T = unquantize_value<0x20>(A, B, C, D);
+ }
+ break;
+
+ case 3:
+ {
+ const int cb = xbits(2, 1);
+ const int B = cb | (cb << 5);
+ const int C = 11;
+ T = unquantize_value<0x20>(A, B, C, D);
+ }
+ break;
+
+ default:
+ SkDEBUGFAIL("Too many bits for trit encoding");
+ break;
+ }
+
+ } else if (fWeightQuints > 0) {
+ SkASSERT(0 == fWeightTrits);
+ switch (fWeightBits) {
+ case 0:
+ {
+ // x is a single quint
+ SkASSERT(x < 5);
+
+ static const int kUnquantizationTable[5] = { 0, 16, 32, 47, 63 };
+ T = kUnquantizationTable[x];
+ }
+ break;
+
+ case 1:
+ {
+ const int B = 0;
+ const int C = 28;
+ T = unquantize_value<0x20>(A, B, C, D);
+ }
+ break;
+
+ case 2:
+ {
+ const int b = xbits[1];
+ const int B = (b << 1) | (b << 6);
+ const int C = 13;
+ T = unquantize_value<0x20>(A, B, C, D);
+ }
+ break;
+
+ default:
+ SkDEBUGFAIL("Too many bits for quint encoding");
+ break;
+ }
+ } else {
+ SkASSERT(0 == fWeightTrits);
+ SkASSERT(0 == fWeightQuints);
+
+ T = replicate_bits(x, fWeightBits, 6);
+ }
+
+ // This should bring the value within [0, 63]..
+ SkASSERT(T <= 63);
+
+ if (T > 32) {
+ T += 1;
+ }
+
+ SkASSERT(T <= 64);
+
+ return T;
+ }
+
+ // Returns the weight at the associated index. If the index is out of bounds, it
+ // returns zero. It also chooses the weight appropriately based on the given dual
+ // plane.
+ int getWeight(const int* unquantizedWeights, int idx, bool dualPlane) const {
+ const int maxIdx = ((fDualPlaneEnabled)? 2 : 1) * fWeightDimX * fWeightDimY - 1;
+ if (fDualPlaneEnabled) {
robertphillips 2014/08/05 17:30:01 space before '?' ?
krajcevski 2014/08/05 20:55:05 Done.
+ const int effectiveIdx = 2*idx + (dualPlane? 1 : 0);
+ if (effectiveIdx > maxIdx) {
+ return 0;
+ }
+ return unquantizedWeights[effectiveIdx];
+ }
+
+ SkASSERT(!dualPlane);
+
+ if (idx > maxIdx) {
+ return 0;
+ } else {
+ return unquantizedWeights[idx];
+ }
+ }
+
+ // This computes the effective weight at location (s, t) of the block. This
+ // weight is computed by sampling the texel weight grid (it's usually not 1-1), and
+ // then applying a bilerp. The algorithm outlined here follows the algorithm
+ // defined in section C.2.18 of the ASTC spec.
+ int infillWeight(const int* unquantizedValues, int s, int t, bool dualPlane) const {
+ const int Ds = (1024 + fDimX/2) / (fDimX - 1);
+ const int Dt = (1024 + fDimY/2) / (fDimY - 1);
+
+ const int cs = Ds * s;
+ const int ct = Dt * t;
+
+ const int gs = (cs*(fWeightDimX - 1) + 32) >> 6;
+ const int gt = (ct*(fWeightDimY - 1) + 32) >> 6;
+
+ const int js = gs >> 4;
+ const int jt = gt >> 4;
+
+ const int fs = gs & 0xF;
+ const int ft = gt & 0xF;
+
+ const int idx = js + jt*fWeightDimX;
+ const int p00 = this->getWeight(unquantizedValues, idx, dualPlane);
+ const int p01 = this->getWeight(unquantizedValues, idx + 1, dualPlane);
+ const int p10 = this->getWeight(unquantizedValues, idx + fWeightDimX, dualPlane);
+ const int p11 = this->getWeight(unquantizedValues, idx + fWeightDimX + 1, dualPlane);
+
+ const int w11 = (fs*ft + 8) >> 4;
+ const int w10 = ft - w11;
+ const int w01 = fs - w11;
+ const int w00 = 16 - fs - ft + w11;
+
+ const int weight = (p00*w00 + p01*w01 + p10*w10 + p11*w11 + 8) >> 4;
+ SkASSERT(weight <= 64);
+ return weight;
+ }
+
+ // Unquantizes the decoded texel weights as described in section C.2.17 of
+ // the ASTC specification. Additionally, it populates texelWeights with
+ // the expanded weight grid, which is computed according to section C.2.18
+ void texelWeights(int texelWeights[2][12][12], const int* texelValues) const {
+ // Unquantized texel weights...
+ int unquantizedValues[144*2]; // 12x12 blocks with dual plane decoding...
+ SkASSERT(this->numWeights() <= 144*2);
+
+ // Unquantize the weights and cache them
+ for (int j = 0; j < this->numWeights(); ++j) {
+ unquantizedValues[j] = this->unquantizeWeight(texelValues[j]);
+ }
+
+ // Do weight infill...
robertphillips 2014/08/05 20:37:53 Is there a reason to use s,t over x,y ?
krajcevski 2014/08/05 20:55:08 s,t are what's used in the spec. They also map bet
+ for (int t = 0; t < fDimY; ++t) {
+ for (int s = 0; s < fDimX; ++s) {
robertphillips 2014/08/05 20:37:53 Would it be clearer to unroll the plane loop and j
krajcevski 2014/08/05 20:55:07 Yes, I think my brain was fried by this point.
+ for (int i = 0; i < 2; ++i) {
+ bool bUseDualPlane = static_cast<bool>(i);
+ if (bUseDualPlane && !fDualPlaneEnabled) {
+ continue;
+ }
+
+ texelWeights[i][s][t] =
+ this->infillWeight(unquantizedValues, s, t, bUseDualPlane);
+ }
+ }
+ }
+ }
+
+ // Returns the partition for the texel located at position (x, y).
robertphillips 2014/08/05 20:37:53 Adapted from ... ?
krajcevski 2014/08/05 20:55:05 Not sure what you mean? The next comment says wher
robertphillips 2014/08/06 12:31:52 I think we should replace "Copied directly from" w
krajcevski 2014/08/06 14:15:57 Done.
+ // Copied directly from C.2.21 of the ASTC specification
+ int getPartition(int x, int y) const {
robertphillips 2014/08/05 20:37:53 bSmallBlock -> isSmallBlock ?
krajcevski 2014/08/05 20:55:08 Done.
+ bool bSmallBlock = (fDimX * fDimY) < 31;
+ const int partitionCount = this->numPartitions();
+ int seed = fPartIndex;
+ if (bSmallBlock) {
+ x <<= 1;
+ y <<= 1;
+ }
+
+ seed += (partitionCount - 1) * 1024;
+
+ uint32_t p = seed;
+ p ^= p >> 15; p -= p << 17; p += p << 7; p += p << 4;
+ p ^= p >> 5; p += p << 16; p ^= p >> 7; p ^= p >> 3;
+ p ^= p << 6; p ^= p >> 17;
+
+ uint32_t rnum = p;
+ uint8_t seed1 = rnum & 0xF;
+ uint8_t seed2 = (rnum >> 4) & 0xF;
+ uint8_t seed3 = (rnum >> 8) & 0xF;
+ uint8_t seed4 = (rnum >> 12) & 0xF;
+ uint8_t seed5 = (rnum >> 16) & 0xF;
+ uint8_t seed6 = (rnum >> 20) & 0xF;
+ uint8_t seed7 = (rnum >> 24) & 0xF;
+ uint8_t seed8 = (rnum >> 28) & 0xF;
+ uint8_t seed9 = (rnum >> 18) & 0xF;
+ uint8_t seed10 = (rnum >> 22) & 0xF;
+ uint8_t seed11 = (rnum >> 26) & 0xF;
+ uint8_t seed12 = ((rnum >> 30) | (rnum << 2)) & 0xF;
+
+ seed1 *= seed1; seed2 *= seed2;
+ seed3 *= seed3; seed4 *= seed4;
+ seed5 *= seed5; seed6 *= seed6;
+ seed7 *= seed7; seed8 *= seed8;
+ seed9 *= seed9; seed10 *= seed10;
+ seed11 *= seed11; seed12 *= seed12;
+
+ int sh1, sh2, sh3;
+ if (0 != (seed & 1)) {
+ sh1 = (0 != (seed & 2))? 4 : 5;
+ sh2 = (partitionCount == 3)? 6 : 5;
+ } else {
+ sh1 = (partitionCount==3)? 6 : 5;
+ sh2 = (0 != (seed & 2))? 4 : 5;
+ }
+ sh3 = (0 != (seed & 0x10))? sh1 : sh2;
+
+ seed1 >>= sh1; seed2 >>= sh2; seed3 >>= sh1; seed4 >>= sh2;
+ seed5 >>= sh1; seed6 >>= sh2; seed7 >>= sh1; seed8 >>= sh2;
+ seed9 >>= sh3; seed10 >>= sh3; seed11 >>= sh3; seed12 >>= sh3;
+
+ const int z = 0;
+ int a = seed1*x + seed2*y + seed11*z + (rnum >> 14);
+ int b = seed3*x + seed4*y + seed12*z + (rnum >> 10);
+ int c = seed5*x + seed6*y + seed9 *z + (rnum >> 6);
+ int d = seed7*x + seed8*y + seed10*z + (rnum >> 2);
+
+ a &= 0x3F;
+ b &= 0x3F;
+ c &= 0x3F;
+ d &= 0x3F;
+
+ if (partitionCount < 4) {
+ d = 0;
+ }
+
+ if (partitionCount < 3) {
+ c = 0;
+ }
+
+ if (a >= b && a >= c && a >= d) {
+ return 0;
+ } else if (b >= c && b >= d) {
+ return 1;
+ } else if (c >= d) {
+ return 2;
+ } else {
+ return 3;
+ }
+ }
+
+ // Performs the proper interpolation of the texel based on the
+ // endpoints and weights.
+ SkColor getTexel(const SkColor endpoints[4][2],
+ const int weights[2][12][12],
+ int x, int y) const {
+ int part = 0;
+ if (this->numPartitions() > 1) {
+ part = this->getPartition(x, y);
+ }
+
+ SkColor result;
+ if (fDualPlaneEnabled) {
+ result = interpolate_dual_endpoints(
+ endpoints[part], weights[0][x][y], weights[1][x][y], fDualPlane);
+ } else {
+ result = interpolate_endpoints(endpoints[part], weights[0][x][y]);
+ }
+
+#if 1
+ // !FIXME! if we're writing directly to a bitmap, then we don't need
+ // to swap the red and blue channels, but since we're usually being used
+ // by the SkImageDecoder_astc module, the results are expected to be in RGBA.
+ result = SkColorSetARGB(
+ SkColorGetA(result), SkColorGetB(result), SkColorGetG(result), SkColorGetR(result));
+#endif
+
+ return result;
+ }
+
+ void decode() {
+ // First decode the block mode.
+ this->decodeBlockMode();
+
+ // Now we can decode the partition information.
+ fPartIndex = read_astc_bits(fBlock, 11, 23);
+ fPartCount = (fPartIndex & 0x3) + 1;
+ fPartIndex >>= 2;
+
+ // This is illegal
+ if (fDualPlaneEnabled && this->numPartitions() == 4) {
+ fError = true;
robertphillips 2014/08/05 20:37:53 Can we early out here?
krajcevski 2014/08/05 20:55:06 Yes. If fError is true then none of the other valu
+ }
+
+ // Based on the partition info, we can decode the color information.
+ this->decodeColorData();
+ }
+
+ // Decodes the dual plane based on the given bit location. The final
+ // location, if the dual plane is enabled, is also the end of our color data.
+ // This function is only meant to be used from this->decodeColorData()
+ void decodeDualPlane(int bitLoc) {
+ if (fDualPlaneEnabled) {
+ fDualPlane = read_astc_bits(fBlock, bitLoc - 2, bitLoc);
+ fColorEndBit = bitLoc - 2;
+ } else {
+ fColorEndBit = bitLoc;
+ }
+ }
+
+ // Decodes the color information based on the ASTC spec.
+ void decodeColorData() {
+
+ // By default, the last color bit is at the end of the texel weights
+ const int lastWeight = 128 - this->numWeightBits();
+
+ // If we have a dual plane then it will be at this location, too.
+ int dualPlaneBitLoc = lastWeight;
+
+ // If there's only one partition, then our job is (relatively) easy.
+ if (this->numPartitions() == 1) {
+ fCEM[0] = read_astc_bits(fBlock, 13, 17);
+ fColorStartBit = 17;
+
+ // Handle dual plane mode...
+ this->decodeDualPlane(dualPlaneBitLoc);
+
+ return;
+ }
+
+ // If we have more than one partition, then we need to make
+ // room for the partition index.
+ fColorStartBit = 29;
+
+ // Read the base CEM. If it's zero, then we have no additional
+ // CEM data and the endpoints for each partition share the same CEM.
+ const int baseCEM = read_astc_bits(fBlock, 23, 25);
+ if (0 == baseCEM) {
+ const int sameCEM = read_astc_bits(fBlock, 25, 29);
+ for (int i = 0; i < kMaxPartitions; ++i) {
+ fCEM[i] = sameCEM;
+ }
+
+ // Handle dual plane mode...
+ this->decodeDualPlane(dualPlaneBitLoc);
+
+ return;
+ }
+
+ // Move the dual plane selector bits down based on how many
+ // partitions the block contains.
+ switch (this->numPartitions()) {
+ case 2:
+ dualPlaneBitLoc -= 2;
+ break;
+
+ case 3:
+ dualPlaneBitLoc -= 5;
+ break;
+
+ case 4:
+ dualPlaneBitLoc -= 8;
+ break;
+
+ default:
+ SkDEBUGFAIL("Internal ASTC decoding error.");
+ break;
+ }
+
+ // The rest of the CEM config will be between the dual plane bit selector
+ // and the texel weight grid.
+ const int lowCEM = read_astc_bits(fBlock, 23, 29);
+ int fullCEM = read_astc_bits(fBlock, dualPlaneBitLoc, lastWeight);
+
+ // Attach the config at the end of the weight grid to the CEM values
+ // in the beginning of the block.
+ fullCEM = (fullCEM << 6) | lowCEM;
+
+ // Ignore the two least significant bits, since those are our baseCEM above.
+ fullCEM = fullCEM >> 2;
+
+ int C[kMaxPartitions]; // Next, decode C and M from the spec (Table C.2.12)
+ for (int i = 0; i < this->numPartitions(); ++i) {
+ C[i] = fullCEM & 1;
+ fullCEM = fullCEM >> 1;
+ }
+
+ int M[kMaxPartitions];
+ for (int i = 0; i < this->numPartitions(); ++i) {
+ M[i] = fullCEM & 0x3;
+ fullCEM = fullCEM >> 2;
+ }
+
+ // Construct our CEMs..
+ SkASSERT(baseCEM > 0);
+ for (int i = 0; i < this->numPartitions(); ++i) {
+ int cem = (baseCEM - 1) * 4;
+ cem += (0 == C[i])? 0 : 4;
+ cem += M[i];
+
+ SkASSERT(cem < 16);
+ fCEM[i] = cem;
+ }
+
+ // Finally, if we have dual plane mode, then read the plane selector.
+ this->decodeDualPlane(dualPlaneBitLoc);
+ }
+
+ // Decodes the block mode. This function determines whether or not we use
+ // dual plane encoding, the size of the texel weight grid, and the number of
+ // bits, trits and quints that are used to encode it. For more information,
+ // see section C.2.10 of the ASTC spec.
+ void decodeBlockMode() {
+ const int blockMode = read_astc_bits(fBlock, 0, 11);
+
+ // Check for special void extent encoding
+ fVoidExtent = (blockMode & 0x1FF) == 0x1FC;
+
+ // Check for reserved block modes
+ fError = (blockMode & 0x1C3) == 0x1C0;
+
+ // Neither reserved nor void-extent, decode as usual
+ // This code corresponds to table C.2.8 of the ASTC spec
robertphillips 2014/08/05 20:37:53 highPrecision ?
krajcevski 2014/08/05 20:55:06 Done.
+ bool bHighPrecision = false;
+ int R = 0;
+ if ((blockMode & 0x3) == 0) {
+ R = ((0xC & blockMode) >> 1) | ((0x10 & blockMode) >> 4);
+ const int bitsSevenAndEight = (blockMode & 0x180) >> 7;
+ SkASSERT(0 <= bitsSevenAndEight && bitsSevenAndEight < 4);
+
+ const int A = (blockMode >> 5) & 0x3;
+ const int B = (blockMode >> 9) & 0x3;
+
robertphillips 2014/08/05 20:37:53 Would it be any better to define these two as flag
krajcevski 2014/08/05 20:55:06 If life were easy, yes. Some block modes don't enc
+ fDualPlaneEnabled = (blockMode >> 10) & 0x1;
+ bHighPrecision = (blockMode >> 9) & 0x1;
+
+ switch (bitsSevenAndEight) {
+ default:
robertphillips 2014/08/05 20:37:53 Do these 0..4 values have some clear symolic inter
krajcevski 2014/08/05 20:55:07 I'm not sure that there's a meaningful way to comm
+ case 0:
+ fWeightDimX = 12;
+ fWeightDimY = A + 2;
+ break;
+
+ case 1:
+ fWeightDimX = A + 2;
+ fWeightDimY = 12;
+ break;
+
+ case 2:
+ fWeightDimX = A + 6;
+ fWeightDimY = B + 6;
+ fDualPlaneEnabled = false;
+ bHighPrecision = false;
+ break;
+
+ case 3:
+ if (0 == A) {
+ fWeightDimX = 6;
+ fWeightDimY = 10;
+ } else {
+ fWeightDimX = 10;
+ fWeightDimY = 6;
+ }
+ break;
+ }
+ } else { // (blockMode & 0x3) != 0
+ R = ((blockMode & 0x3) << 1) | ((blockMode & 0x10) >> 4);
+
+ const int bitsTwoAndThree = (blockMode >> 2) & 0x3;
+ SkASSERT(0 <= bitsTwoAndThree && bitsTwoAndThree < 4);
+
+ const int A = (blockMode >> 5) & 0x3;
+ const int B = (blockMode >> 7) & 0x3;
+
+ fDualPlaneEnabled = (blockMode >> 10) & 0x1;
+ bHighPrecision = (blockMode >> 9) & 0x1;
+
+ switch (bitsTwoAndThree) {
+ case 0:
+ fWeightDimX = B + 4;
+ fWeightDimY = A + 2;
+ break;
+ case 1:
+ fWeightDimX = B + 8;
+ fWeightDimY = A + 2;
+ break;
+ case 2:
+ fWeightDimX = A + 2;
+ fWeightDimY = B + 8;
+ break;
+ case 3:
+ if ((B & 0x2) == 0) {
+ fWeightDimX = A + 2;
+ fWeightDimY = (B & 1) + 6;
+ } else {
+ fWeightDimX = (B & 1) + 2;
+ fWeightDimY = A + 2;
+ }
+ break;
+ }
+ }
+
+ // We should have set the values of R and bHighPrecision
+ // from decoding the block mode, these are used to determine
+ // the proper dimensions of our weight grid.
+ if ((R & 0x6) == 0) {
+ fError = true;
+ } else {
+ static const int kBitAllocationTable[2][6][3] = {
+ {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 2, 0, 0 },
+ { 0, 0, 1 },
+ { 1, 1, 0 },
+ { 3, 0, 0 }
+ },
+ {
+ { 1, 0, 1 },
+ { 2, 1, 0 },
+ { 4, 0, 0 },
+ { 2, 0, 1 },
+ { 3, 1, 0 },
+ { 5, 0, 0 }
+ }
+ };
+
+ fWeightBits = kBitAllocationTable[bHighPrecision][R - 2][0];
+ fWeightTrits = kBitAllocationTable[bHighPrecision][R - 2][1];
+ fWeightQuints = kBitAllocationTable[bHighPrecision][R - 2][2];
+ }
+ }
+};
+
+// Take a known void-extent block, and write out the values as a constant color.
+static void decompress_void_extent(uint8_t* dst, int dstRowBytes,
+ const ASTCDecompressionData &data) {
robertphillips 2014/08/05 15:29:03 0x100 ?
krajcevski 2014/08/05 20:55:08 I think this is remains of a decompressor past.
+ if ((0x100 & data.fBlockMode) == 0) {
+ write_error_color(dst, dstRowBytes);
+ return;
+ }
+
+ // The top 64 bits contain 4 16-bit RGBA values.
+ int a = (read_astc_bits(data.fBlock, 112, 128) + 255) >> 8;
+ int b = (read_astc_bits(data.fBlock, 96, 112) + 255) >> 8;
+ int g = (read_astc_bits(data.fBlock, 80, 96) + 255) >> 8;
+ int r = (read_astc_bits(data.fBlock, 64, 80) + 255) >> 8;
+
+ write_constant_color(dst, dstRowBytes, SkColorSetARGB(a, r, g, b));
+}
+
+// Decompresses a single ASTC block. It's assumed that data.fDimX and data.fDimY are
+// set and that the block has already been decoded (i.e. data.decode() has been called)
+static void decompress_astc_block(uint8_t* dst, int dstRowBytes,
+ const ASTCDecompressionData &data) {
+ if (data.fError) {
+ write_error_color(dst, dstRowBytes);
+ return;
+ }
+
+ if(data.fVoidExtent) {
+ decompress_void_extent(dst, dstRowBytes, data);
+ return;
+ }
+
+ // Decode the texel weights.
robertphillips 2014/08/05 15:29:03 64?
krajcevski 2014/08/05 20:55:05 Done.
+ int texelValues[64];
+ bool success = decode_integer_sequence(
+ texelValues, 64, data.numWeights(),
robertphillips 2014/08/05 15:29:02 128?
krajcevski 2014/08/05 20:55:07 Done.
+ data.fBlock, 128, 128 - data.numWeightBits(), false,
+ data.fWeightBits, data.fWeightTrits, data.fWeightQuints);
+
+ if (!success) {
+ write_error_color(dst, dstRowBytes);
+ return;
+ }
+
+ // Decode the color endpoints
+ int colorBits, colorTrits, colorQuints;
+ if (!data.getColorValueEncoding(&colorBits, &colorTrits, &colorQuints)) {
+ write_error_color(dst, dstRowBytes);
+ return;
+ }
+
+ int colorValues[18];
+ success = decode_integer_sequence(
+ colorValues, 18, data.numColorValues(),
+ data.fBlock, data.fColorStartBit, data.fColorEndBit, true,
+ colorBits, colorTrits, colorQuints);
+
+ if (!success) {
+ write_error_color(dst, dstRowBytes);
+ return;
+ }
+
+ // Unquantize the color values after they've been decoded.
+ unquantize_colors(colorValues, data.numColorValues(), colorBits, colorTrits, colorQuints);
+
+ // Decode the colors into the appropriate endpoints.
+ SkColor endpoints[4][2];
+ data.colorEndpoints(endpoints, colorValues);
+
+ // Do texel infill and decode the texel values.
+ int texelWeights[2][12][12];
+ data.texelWeights(texelWeights, texelValues);
+
+ // Write the texels by interpolating them based on the information
+ // stored in the block.
+ dst += data.fDimY * dstRowBytes;
robertphillips 2014/08/05 15:29:03 i,j -> x,y ?
krajcevski 2014/08/05 20:55:07 Done.
+ for (int j = 0; j < data.fDimY; ++j) {
+ dst -= dstRowBytes;
+ SkColor* colorPtr = reinterpret_cast<SkColor*>(dst);
+ for (int i = 0; i < data.fDimX; ++i) {
+ colorPtr[i] = data.getTexel(endpoints, texelWeights, i, j);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
namespace SkTextureCompressor {
@@ -285,4 +1932,21 @@ SkBlitter* CreateASTCBlitter(int width, int height, void* outputBuffer) {
(width, height, outputBuffer);
}
+void DecompressASTC(uint8_t* dst, int dstRowBytes, const uint8_t* src,
+ int width, int height, int blockDimX, int blockDimY) {
robertphillips 2014/08/05 15:29:02 // ASTC is encoded upside down so we need to walk
krajcevski 2014/08/05 20:55:06 Done.
+ dst += height * dstRowBytes;
+
+ ASTCDecompressionData data(blockDimX, blockDimY);
robertphillips 2014/08/05 15:29:03 Do we need (or already have) and assert somewhere
krajcevski 2014/08/05 20:55:05 Yes, that should be caught in SkTextureCompressor.
+ for (int j = 0; j < height; j += blockDimY) {
+ dst -= blockDimY * dstRowBytes;
+ SkColor *colorPtr = reinterpret_cast<SkColor*>(dst);
+ for (int i = 0; i < width; i += blockDimX) {
robertphillips 2014/08/05 15:29:02 Should we just pass in data here (rather then data
krajcevski 2014/08/05 20:55:05 Done.
+ read_astc_block(&(data.fBlock), src);
+ data.decode();
+ decompress_astc_block(reinterpret_cast<uint8_t*>(colorPtr + i), dstRowBytes, data);
robertphillips 2014/08/05 15:29:02 16?
krajcevski 2014/08/05 20:55:05 Done.
+ src += 16;
+ }
+ }
+}
+
} // SkTextureCompressor
« no previous file with comments | « src/utils/SkTextureCompressor_ASTC.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698