ETC2 decoder
A new ETC2 decoder was added to SwiftShader, based on the
OpenGL ETC2 specification. The decoder is fully standalone
and does not rely on any outside code (there are no files
included in the header and the source file only include the
header file, so it can easily be ported to any other project).
Things to note:
- In Surface.cpp, signed ETC2 images are decoded to full 32FP
images, because of the lack of support for signed 8 bit R
and RG internal formats. This should be fixed as soon as
these formats are made available.
- sRGB conversion is not performed within the decoder, so it has
been added as a loop inside Surface::decodeETC2 after the ETC2
decoding is performed. This is to make sure that there is no
loss of precision, should we choose to do the conversion to a
higher bit precision format. The loop is fairly straightforward
and does the conversion in place, so the impact on performance
compared to doing the sRGB conversion in the decoder should be
minimal.
Change-Id: I3a1af623353344bf35818ba9c9f4cf349b587e2f
Reviewed-on: https://swiftshader-review.googlesource.com/3960
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/Android.mk b/src/Android.mk
index fd96980..aa28463 100644
--- a/src/Android.mk
+++ b/src/Android.mk
@@ -45,6 +45,7 @@
Renderer/Clipper.cpp \
Renderer/Color.cpp \
Renderer/Context.cpp \
+ Renderer/ETC_Decoder.cpp \
Renderer/Matrix.cpp \
Renderer/PixelProcessor.cpp \
Renderer/Plane.cpp \
diff --git a/src/OpenGL/libGLES_CM/libGLES_CM.cbp b/src/OpenGL/libGLES_CM/libGLES_CM.cbp
index 905a884..017955f 100644
--- a/src/OpenGL/libGLES_CM/libGLES_CM.cbp
+++ b/src/OpenGL/libGLES_CM/libGLES_CM.cbp
@@ -199,6 +199,8 @@
<Unit filename="../../Renderer/Color.hpp" />
<Unit filename="../../Renderer/Context.cpp" />
<Unit filename="../../Renderer/Context.hpp" />
+ <Unit filename="../../Renderer/ETC_Decoder.cpp" />
+ <Unit filename="../../Renderer/ETC_Decoder.hpp" />
<Unit filename="../../Renderer/LRUCache.hpp" />
<Unit filename="../../Renderer/Matrix.cpp" />
<Unit filename="../../Renderer/Matrix.hpp" />
diff --git a/src/OpenGL/libGLESv2/libGLESv2.cbp b/src/OpenGL/libGLESv2/libGLESv2.cbp
index cd3d3d1..8f40b12 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.cbp
+++ b/src/OpenGL/libGLESv2/libGLESv2.cbp
@@ -198,6 +198,8 @@
<Unit filename="../../Renderer/Color.hpp" />
<Unit filename="../../Renderer/Context.cpp" />
<Unit filename="../../Renderer/Context.hpp" />
+ <Unit filename="../../Renderer/ETC_Decoder.cpp" />
+ <Unit filename="../../Renderer/ETC_Decoder.hpp" />
<Unit filename="../../Renderer/LRUCache.hpp" />
<Unit filename="../../Renderer/Matrix.cpp" />
<Unit filename="../../Renderer/Matrix.hpp" />
diff --git a/src/Renderer/ETC_Decoder.cpp b/src/Renderer/ETC_Decoder.cpp
new file mode 100644
index 0000000..6d98aa2
--- /dev/null
+++ b/src/Renderer/ETC_Decoder.cpp
@@ -0,0 +1,709 @@
+// SwiftShader Software Renderer
+//
+// Copyright(c) 2015 Google Inc.
+//
+// All rights reserved. No part of this software may be copied, distributed, transmitted,
+// transcribed, stored in a retrieval system, translated into any human or computer
+// language by any means, or disclosed to third parties without the explicit written
+// agreement of Google Inc. Without such an agreement, no rights or licenses, express
+// or implied, including but not limited to any patent rights, are granted to you.
+//
+
+#include "ETC_Decoder.hpp"
+
+namespace
+{
+ inline int clampByte(int value)
+ {
+ return (value < 0) ? 0 : ((value > 255) ? 255 : value);
+ }
+
+ inline int clampSByte(int value)
+ {
+ return (value < -128) ? -128 : ((value > 127) ? 127 : value);
+ }
+
+ struct bgra8
+ {
+ unsigned char b;
+ unsigned char g;
+ unsigned char r;
+ unsigned char a;
+
+ inline bgra8()
+ {
+ }
+
+ inline void set(int red, int green, int blue)
+ {
+ r = static_cast<unsigned char>(clampByte(red));
+ g = static_cast<unsigned char>(clampByte(green));
+ b = static_cast<unsigned char>(clampByte(blue));
+ }
+
+ inline void set(int red, int green, int blue, int alpha)
+ {
+ r = static_cast<unsigned char>(clampByte(red));
+ g = static_cast<unsigned char>(clampByte(green));
+ b = static_cast<unsigned char>(clampByte(blue));
+ a = static_cast<unsigned char>(clampByte(alpha));
+ }
+
+ const bgra8& addA(int alpha)
+ {
+ a = alpha;
+ return *this;
+ }
+ };
+
+ inline int extend_4to8bits(int x)
+ {
+ return (x << 4) | x;
+ }
+
+ inline int extend_5to8bits(int x)
+ {
+ return (x << 3) | (x >> 2);
+ }
+
+ inline int extend_6to8bits(int x)
+ {
+ return (x << 2) | (x >> 4);
+ }
+
+ inline int extend_7to8bits(int x)
+ {
+ return (x << 1) | (x >> 6);
+ }
+
+ struct ETC2
+ {
+ // Decodes unsigned single or dual channel block to bytes
+ static void DecodeBlock(const ETC2** sources, unsigned char *dest, int nbChannels, int x, int y, int w, int h, int pitch, bool isSigned)
+ {
+ if(isSigned)
+ {
+ signed char* sDst = reinterpret_cast<signed char*>(dest);
+ for(int j = 0; j < 4 && (y + j) < h; j++)
+ {
+ for(int i = 0; i < 4 && (x + i) < w; i++)
+ {
+ for(int c = nbChannels - 1; c >= 0; c--)
+ {
+ sDst[i * nbChannels + c] = clampSByte(sources[c]->getSingleChannel(i, j, isSigned));
+ }
+ }
+ sDst += pitch;
+ }
+ }
+ else
+ {
+ for(int j = 0; j < 4 && (y + j) < h; j++)
+ {
+ for(int i = 0; i < 4 && (x + i) < w; i++)
+ {
+ for(int c = nbChannels - 1; c >= 0; c--)
+ {
+ dest[i * nbChannels + c] = clampByte(sources[c]->getSingleChannel(i, j, isSigned));
+ }
+ }
+ dest += pitch;
+ }
+ }
+ }
+
+ // Decodes RGB block to bgra8
+ void decodeBlock(unsigned char *dest, int x, int y, int w, int h, int pitch, unsigned char alphaValues[4][4], bool punchThroughAlpha) const
+ {
+ bool opaqueBit = diffbit;
+ bool nonOpaquePunchThroughAlpha = punchThroughAlpha && !opaqueBit;
+
+ // Select mode
+ if(diffbit || punchThroughAlpha)
+ {
+ int r = (R + dR);
+ int g = (G + dG);
+ int b = (B + dB);
+ if(r < 0 || r > 31)
+ {
+ decodeTBlock(dest, x, y, w, h, pitch, alphaValues, nonOpaquePunchThroughAlpha);
+ }
+ else if(g < 0 || g > 31)
+ {
+ decodeHBlock(dest, x, y, w, h, pitch, alphaValues, nonOpaquePunchThroughAlpha);
+ }
+ else if(b < 0 || b > 31)
+ {
+ decodePlanarBlock(dest, x, y, w, h, pitch, alphaValues);
+ }
+ else
+ {
+ decodeDifferentialBlock(dest, x, y, w, h, pitch, alphaValues, nonOpaquePunchThroughAlpha);
+ }
+ }
+ else
+ {
+ decodeIndividualBlock(dest, x, y, w, h, pitch, alphaValues, nonOpaquePunchThroughAlpha);
+ }
+ }
+
+ private:
+ struct
+ {
+ union
+ {
+ // Individual, differential, H and T modes
+ struct
+ {
+ union
+ {
+ // Individual and differential modes
+ struct
+ {
+ union
+ {
+ struct // Individual colors
+ {
+ unsigned char R2 : 4;
+ unsigned char R1 : 4;
+ unsigned char G2 : 4;
+ unsigned char G1 : 4;
+ unsigned char B2 : 4;
+ unsigned char B1 : 4;
+ };
+
+ struct // Differential colors
+ {
+ signed char dR : 3;
+ unsigned char R : 5;
+ signed char dG : 3;
+ unsigned char G : 5;
+ signed char dB : 3;
+ unsigned char B : 5;
+ };
+ };
+
+ bool flipbit : 1;
+ bool diffbit : 1;
+ unsigned char cw2 : 3;
+ unsigned char cw1 : 3;
+ };
+
+ // T mode
+ struct
+ {
+ // Byte 1
+ unsigned char TR1b : 2;
+ unsigned char TdummyB : 1;
+ unsigned char TR1a : 2;
+ unsigned char TdummyA : 3;
+
+ // Byte 2
+ unsigned char TB1 : 4;
+ unsigned char TG1 : 4;
+
+ // Byte 3
+ unsigned char TG2 : 4;
+ unsigned char TR2 : 4;
+
+ // Byte 4
+ unsigned char Tdb : 1;
+ bool Tflipbit : 1;
+ unsigned char Tda : 2;
+ unsigned char TB2 : 4;
+ };
+
+ // H mode
+ struct
+ {
+ // Byte 1
+ unsigned char HG1a : 3;
+ unsigned char HR1 : 4;
+ unsigned char HdummyA : 1;
+
+ // Byte 2
+ unsigned char HB1b : 2;
+ unsigned char HdummyC : 1;
+ unsigned char HB1a : 1;
+ unsigned char HG1b : 1;
+ unsigned char HdummyB : 3;
+
+ // Byte 3
+ unsigned char HG2a : 3;
+ unsigned char HR2 : 4;
+ unsigned char HB1c : 1;
+
+ // Byte 4
+ unsigned char Hdb : 1;
+ bool Hflipbit : 1;
+ unsigned char Hda : 1;
+ unsigned char HB2 : 4;
+ unsigned char HG2b : 1;
+ };
+ };
+
+ unsigned char pixelIndexMSB[2];
+ unsigned char pixelIndexLSB[2];
+ };
+
+ // planar mode
+ struct
+ {
+ // Byte 1
+ unsigned char GO1 : 1;
+ unsigned char RO : 6;
+ unsigned char PdummyA : 1;
+
+ // Byte 2
+ unsigned char BO1 : 1;
+ unsigned char GO2 : 6;
+ unsigned char PdummyB : 1;
+
+ // Byte 3
+ unsigned char BO3a : 2;
+ unsigned char PdummyD : 1;
+ unsigned char BO2 : 2;
+ unsigned char PdummyC : 3;
+
+ // Byte 4
+ unsigned char RH2 : 1;
+ bool Pflipbit : 1;
+ unsigned char RH1 : 5;
+ unsigned char BO3b : 1;
+
+ // Byte 5
+ unsigned char BHa : 1;
+ unsigned char GH : 7;
+
+ // Byte 6
+ unsigned char RVa : 3;
+ unsigned char BHb : 5;
+
+ // Byte 7
+ unsigned char GVa : 5;
+ unsigned char RVb : 3;
+
+ // Byte 8
+ unsigned char BV : 6;
+ unsigned char GVb : 2;
+ };
+
+ // Single channel block
+ struct
+ {
+ union
+ {
+ unsigned char base_codeword;
+ signed char signed_base_codeword;
+ };
+
+ unsigned char table_index : 4;
+ unsigned char multiplier : 4;
+
+ unsigned char mc1 : 2;
+ unsigned char mb : 3;
+ unsigned char ma : 3;
+
+ unsigned char mf1 : 1;
+ unsigned char me : 3;
+ unsigned char md : 3;
+ unsigned char mc2 : 1;
+
+ unsigned char mh : 3;
+ unsigned char mg : 3;
+ unsigned char mf2 : 2;
+
+ unsigned char mk1 : 2;
+ unsigned char mj : 3;
+ unsigned char mi : 3;
+
+ unsigned char mn1 : 1;
+ unsigned char mm : 3;
+ unsigned char ml : 3;
+ unsigned char mk2 : 1;
+
+ unsigned char mp : 3;
+ unsigned char mo : 3;
+ unsigned char mn2 : 2;
+ };
+ };
+ };
+
+ void decodeIndividualBlock(unsigned char *dest, int x, int y, int w, int h, int pitch, unsigned char alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const
+ {
+ int r1 = extend_4to8bits(R1);
+ int g1 = extend_4to8bits(G1);
+ int b1 = extend_4to8bits(B1);
+
+ int r2 = extend_4to8bits(R2);
+ int g2 = extend_4to8bits(G2);
+ int b2 = extend_4to8bits(B2);
+
+ decodeIndividualOrDifferentialBlock(dest, x, y, w, h, pitch, r1, g1, b1, r2, g2, b2, alphaValues, nonOpaquePunchThroughAlpha);
+ }
+
+ void decodeDifferentialBlock(unsigned char *dest, int x, int y, int w, int h, int pitch, unsigned char alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const
+ {
+ int b1 = extend_5to8bits(B);
+ int g1 = extend_5to8bits(G);
+ int r1 = extend_5to8bits(R);
+
+ int r2 = extend_5to8bits(R + dR);
+ int g2 = extend_5to8bits(G + dG);
+ int b2 = extend_5to8bits(B + dB);
+
+ decodeIndividualOrDifferentialBlock(dest, x, y, w, h, pitch, r1, g1, b1, r2, g2, b2, alphaValues, nonOpaquePunchThroughAlpha);
+ }
+
+ void decodeIndividualOrDifferentialBlock(unsigned char *dest, int x, int y, int w, int h, int pitch, int r1, int g1, int b1, int r2, int g2, int b2, unsigned char alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const
+ {
+ // Table 3.17.2 sorted according to table 3.17.3
+ static const int intensityModifierDefault[8][4] =
+ {
+ { 2, 8, -2, -8 },
+ { 5, 17, -5, -17 },
+ { 9, 29, -9, -29 },
+ { 13, 42, -13, -42 },
+ { 18, 60, -18, -60 },
+ { 24, 80, -24, -80 },
+ { 33, 106, -33, -106 },
+ { 47, 183, -47, -183 }
+ };
+
+ // Table C.12, intensity modifier for non opaque punchthrough alpha
+ static const int intensityModifierNonOpaque[8][4] =
+ {
+ { 0, 8, 0, -8 },
+ { 0, 17, 0, -17 },
+ { 0, 29, 0, -29 },
+ { 0, 42, 0, -42 },
+ { 0, 60, 0, -60 },
+ { 0, 80, 0, -80 },
+ { 0, 106, 0, -106 },
+ { 0, 183, 0, -183 }
+ };
+
+ const int(&intensityModifier)[8][4] = nonOpaquePunchThroughAlpha ? intensityModifierNonOpaque : intensityModifierDefault;
+
+ bgra8 subblockColors0[4];
+ bgra8 subblockColors1[4];
+
+ const int i10 = intensityModifier[cw1][0];
+ const int i11 = intensityModifier[cw1][1];
+ const int i12 = intensityModifier[cw1][2];
+ const int i13 = intensityModifier[cw1][3];
+
+ subblockColors0[0].set(r1 + i10, g1 + i10, b1 + i10);
+ subblockColors0[1].set(r1 + i11, g1 + i11, b1 + i11);
+ subblockColors0[2].set(r1 + i12, g1 + i12, b1 + i12);
+ subblockColors0[3].set(r1 + i13, g1 + i13, b1 + i13);
+
+ const int i20 = intensityModifier[cw2][0];
+ const int i21 = intensityModifier[cw2][1];
+ const int i22 = intensityModifier[cw2][2];
+ const int i23 = intensityModifier[cw2][3];
+
+ subblockColors1[0].set(r2 + i20, g2 + i20, b2 + i20);
+ subblockColors1[1].set(r2 + i21, g2 + i21, b2 + i21);
+ subblockColors1[2].set(r2 + i22, g2 + i22, b2 + i22);
+ subblockColors1[3].set(r2 + i23, g2 + i23, b2 + i23);
+
+ unsigned char* destStart = dest;
+
+ if(flipbit)
+ {
+ for(int j = 0; j < 2 && (y + j) < h; j++)
+ {
+ bgra8* color = (bgra8*)dest;
+ if((x + 0) < w) color[0] = subblockColors0[getIndex(0, j)].addA(alphaValues[j][0]);
+ if((x + 1) < w) color[1] = subblockColors0[getIndex(1, j)].addA(alphaValues[j][1]);
+ if((x + 2) < w) color[2] = subblockColors0[getIndex(2, j)].addA(alphaValues[j][2]);
+ if((x + 3) < w) color[3] = subblockColors0[getIndex(3, j)].addA(alphaValues[j][3]);
+ dest += pitch;
+ }
+
+ for(int j = 2; j < 4 && (y + j) < h; j++)
+ {
+ bgra8* color = (bgra8*)dest;
+ if((x + 0) < w) color[0] = subblockColors1[getIndex(0, j)].addA(alphaValues[j][0]);
+ if((x + 1) < w) color[1] = subblockColors1[getIndex(1, j)].addA(alphaValues[j][1]);
+ if((x + 2) < w) color[2] = subblockColors1[getIndex(2, j)].addA(alphaValues[j][2]);
+ if((x + 3) < w) color[3] = subblockColors1[getIndex(3, j)].addA(alphaValues[j][3]);
+ dest += pitch;
+ }
+ }
+ else
+ {
+ for(int j = 0; j < 4 && (y + j) < h; j++)
+ {
+ bgra8* color = (bgra8*)dest;
+ if((x + 0) < w) color[0] = subblockColors0[getIndex(0, j)].addA(alphaValues[j][0]);
+ if((x + 1) < w) color[1] = subblockColors0[getIndex(1, j)].addA(alphaValues[j][1]);
+ if((x + 2) < w) color[2] = subblockColors1[getIndex(2, j)].addA(alphaValues[j][2]);
+ if((x + 3) < w) color[3] = subblockColors1[getIndex(3, j)].addA(alphaValues[j][3]);
+ dest += pitch;
+ }
+ }
+
+ if(nonOpaquePunchThroughAlpha)
+ {
+ decodePunchThroughAlphaBlock(destStart, x, y, w, h, pitch);
+ }
+ }
+
+ void decodeTBlock(unsigned char *dest, int x, int y, int w, int h, int pitch, unsigned char alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const
+ {
+ // Table C.8, distance index fot T and H modes
+ static const int distance[8] = { 3, 6, 11, 16, 23, 32, 41, 64 };
+
+ bgra8 paintColors[4];
+
+ int r1 = extend_4to8bits(TR1a << 2 | TR1b);
+ int g1 = extend_4to8bits(TG1);
+ int b1 = extend_4to8bits(TB1);
+
+ int r2 = extend_4to8bits(TR2);
+ int g2 = extend_4to8bits(TG2);
+ int b2 = extend_4to8bits(TB2);
+
+ const int d = distance[Tda << 1 | Tdb];
+
+ paintColors[0].set(r1, g1, b1);
+ paintColors[1].set(r2 + d, g2 + d, b2 + d);
+ paintColors[2].set(r2, g2, b2);
+ paintColors[3].set(r2 - d, g2 - d, b2 - d);
+
+ unsigned char* destStart = dest;
+
+ for(int j = 0; j < 4 && (y + j) < h; j++)
+ {
+ bgra8* color = (bgra8*)dest;
+ if((x + 0) < w) color[0] = paintColors[getIndex(0, j)].addA(alphaValues[j][0]);
+ if((x + 1) < w) color[1] = paintColors[getIndex(1, j)].addA(alphaValues[j][1]);
+ if((x + 2) < w) color[2] = paintColors[getIndex(2, j)].addA(alphaValues[j][2]);
+ if((x + 3) < w) color[3] = paintColors[getIndex(3, j)].addA(alphaValues[j][3]);
+ dest += pitch;
+ }
+
+ if(nonOpaquePunchThroughAlpha)
+ {
+ decodePunchThroughAlphaBlock(destStart, x, y, w, h, pitch);
+ }
+ }
+
+ void decodeHBlock(unsigned char *dest, int x, int y, int w, int h, int pitch, unsigned char alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const
+ {
+ // Table C.8, distance index fot T and H modes
+ static const int distance[8] = { 3, 6, 11, 16, 23, 32, 41, 64 };
+
+ bgra8 paintColors[4];
+
+ int r1 = extend_4to8bits(HR1);
+ int g1 = extend_4to8bits(HG1a << 1 | HG1b);
+ int b1 = extend_4to8bits(HB1a << 3 | HB1b << 1 | HB1c);
+
+ int r2 = extend_4to8bits(HR2);
+ int g2 = extend_4to8bits(HG2a << 1 | HG2b);
+ int b2 = extend_4to8bits(HB2);
+
+ const int d = distance[(Hda << 2) | (Hdb << 1) | ((r1 << 16 | g1 << 8 | b1) >= (r2 << 16 | g2 << 8 | b2) ? 1 : 0)];
+
+ paintColors[0].set(r1 + d, g1 + d, b1 + d);
+ paintColors[1].set(r1 - d, g1 - d, b1 - d);
+ paintColors[2].set(r2 + d, g2 + d, b2 + d);
+ paintColors[3].set(r2 - d, g2 - d, b2 - d);
+
+ unsigned char* destStart = dest;
+
+ for(int j = 0; j < 4 && (y + j) < h; j++)
+ {
+ bgra8* color = (bgra8*)dest;
+ if((x + 0) < w) color[0] = paintColors[getIndex(0, j)].addA(alphaValues[j][0]);
+ if((x + 1) < w) color[1] = paintColors[getIndex(1, j)].addA(alphaValues[j][1]);
+ if((x + 2) < w) color[2] = paintColors[getIndex(2, j)].addA(alphaValues[j][2]);
+ if((x + 3) < w) color[3] = paintColors[getIndex(3, j)].addA(alphaValues[j][3]);
+ dest += pitch;
+ }
+
+ if(nonOpaquePunchThroughAlpha)
+ {
+ decodePunchThroughAlphaBlock(destStart, x, y, w, h, pitch);
+ }
+ }
+
+ void decodePlanarBlock(unsigned char *dest, int x, int y, int w, int h, int pitch, unsigned char alphaValues[4][4]) const
+ {
+ int ro = extend_6to8bits(RO);
+ int go = extend_7to8bits(GO1 << 6 | GO2);
+ int bo = extend_6to8bits(BO1 << 5 | BO2 << 3 | BO3a << 1 | BO3b);
+
+ int rh = extend_6to8bits(RH1 << 1 | RH2);
+ int gh = extend_7to8bits(GH);
+ int bh = extend_6to8bits(BHa << 5 | BHb);
+
+ int rv = extend_6to8bits(RVa << 3 | RVb);
+ int gv = extend_7to8bits(GVa << 2 | GVb);
+ int bv = extend_6to8bits(BV);
+
+ for(int j = 0; j < 4 && (y + j) < h; j++)
+ {
+ int ry = j * (rv - ro) + 2;
+ int gy = j * (gv - go) + 2;
+ int by = j * (bv - bo) + 2;
+ for(int i = 0; i < 4 && (x + i) < w; i++)
+ {
+ ((bgra8*)(dest))[i].set(((i * (rh - ro) + ry) >> 2) + ro,
+ ((i * (gh - go) + gy) >> 2) + go,
+ ((i * (bh - bo) + by) >> 2) + bo,
+ alphaValues[j][i]);
+ }
+ dest += pitch;
+ }
+ }
+
+ // Index for individual, differential, H and T modes
+ inline int getIndex(int x, int y) const
+ {
+ int bitIndex = x * 4 + y;
+ int bitOffset = bitIndex & 7;
+ int lsb = (pixelIndexLSB[1 - (bitIndex >> 3)] >> bitOffset) & 1;
+ int msb = (pixelIndexMSB[1 - (bitIndex >> 3)] >> bitOffset) & 1;
+
+ return (msb << 1) | lsb;
+ }
+
+ void decodePunchThroughAlphaBlock(unsigned char *dest, int x, int y, int w, int h, int pitch) const
+ {
+ for(int j = 0; j < 4 && (y + j) < h; j++)
+ {
+ for(int i = 0; i < 4 && (x + i) < w; i++)
+ {
+ if(getIndex(i, j) == 2) // msb == 1 && lsb == 0
+ {
+ ((bgra8*)dest)[i].set(0, 0, 0, 0);
+ }
+ }
+ dest += pitch;
+ }
+ }
+
+ // Single channel utility functions
+ inline int getSingleChannel(int x, int y, bool isSigned) const
+ {
+ int codeword = isSigned ? signed_base_codeword : base_codeword;
+ return codeword + getSingleChannelModifier(x, y) * multiplier;
+ }
+
+ inline int getSingleChannelIndex(int x, int y) const
+ {
+ switch(x * 4 + y)
+ {
+ case 0: return ma;
+ case 1: return mb;
+ case 2: return mc1 << 1 | mc2;
+ case 3: return md;
+ case 4: return me;
+ case 5: return mf1 << 2 | mf2;
+ case 6: return mg;
+ case 7: return mh;
+ case 8: return mi;
+ case 9: return mj;
+ case 10: return mk1 << 1 | mk2;
+ case 11: return ml;
+ case 12: return mm;
+ case 13: return mn1 << 2 | mn2;
+ case 14: return mo;
+ default: return mp; // 15
+ }
+ }
+
+ inline int getSingleChannelModifier(int x, int y) const
+ {
+ static const int modifierTable[16][8] = { { -3, -6, -9, -15, 2, 5, 8, 14 },
+ { -3, -7, -10, -13, 2, 6, 9, 12 },
+ { -2, -5, -8, -13, 1, 4, 7, 12 },
+ { -2, -4, -6, -13, 1, 3, 5, 12 },
+ { -3, -6, -8, -12, 2, 5, 7, 11 },
+ { -3, -7, -9, -11, 2, 6, 8, 10 },
+ { -4, -7, -8, -11, 3, 6, 7, 10 },
+ { -3, -5, -8, -11, 2, 4, 7, 10 },
+ { -2, -6, -8, -10, 1, 5, 7, 9 },
+ { -2, -5, -8, -10, 1, 4, 7, 9 },
+ { -2, -4, -8, -10, 1, 3, 7, 9 },
+ { -2, -5, -7, -10, 1, 4, 6, 9 },
+ { -3, -4, -7, -10, 2, 3, 6, 9 },
+ { -1, -2, -3, -10, 0, 1, 2, 9 },
+ { -4, -6, -8, -9, 3, 5, 7, 8 },
+ { -3, -5, -7, -9, 2, 4, 6, 8 } };
+
+ return modifierTable[table_index][getSingleChannelIndex(x, y)];
+ }
+ };
+}
+
+// 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)
+{
+ const ETC2* sources[2];
+ sources[0] = (const ETC2*)src;
+
+ unsigned char alphaValues[4][4] = { { 255, 255, 255, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } };
+
+ switch(inputType)
+ {
+ case ETC_R_SIGNED:
+ case ETC_R_UNSIGNED:
+ for(int y = 0; y < h; y += 4)
+ {
+ 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);
+ }
+ }
+ break;
+ case ETC_RG_SIGNED:
+ case ETC_RG_UNSIGNED:
+ sources[1] = sources[0] + 1;
+ for(int y = 0; y < h; y += 4)
+ {
+ 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);
+ }
+ }
+ break;
+ case ETC_RGB:
+ case ETC_RGB_PUNCHTHROUGH_ALPHA:
+ for(int y = 0; y < h; y += 4)
+ {
+ 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);
+ }
+ }
+ break;
+ case ETC_RGBA:
+ for(int y = 0; y < h; y += 4)
+ {
+ unsigned char *dstRow = dst + (y * dstPitch);
+ for(int x = 0; x < w; x += 4)
+ {
+ // Decode Alpha
+ ETC2::DecodeBlock(&sources[0], &(alphaValues[0][0]), 1, x, y, dstW, dstH, 4, 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]++;
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/Renderer/ETC_Decoder.hpp b/src/Renderer/ETC_Decoder.hpp
new file mode 100644
index 0000000..11b1395
--- /dev/null
+++ b/src/Renderer/ETC_Decoder.hpp
@@ -0,0 +1,38 @@
+// SwiftShader Software Renderer
+//
+// Copyright(c) 2015 Google Inc.
+//
+// All rights reserved. No part of this software may be copied, distributed, transmitted,
+// transcribed, stored in a retrieval system, translated into any human or computer
+// language by any means, or disclosed to third parties without the explicit written
+// agreement of Google Inc. Without such an agreement, no rights or licenses, express
+// or implied, including but not limited to any patent rights, are granted to you.
+//
+
+class ETC_Decoder
+{
+public:
+ enum InputType
+ {
+ ETC_R_SIGNED,
+ ETC_R_UNSIGNED,
+ ETC_RG_SIGNED,
+ ETC_RG_UNSIGNED,
+ ETC_RGB,
+ ETC_RGB_PUNCHTHROUGH_ALPHA,
+ ETC_RGBA
+ };
+
+ /// 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 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);
+};
diff --git a/src/Renderer/Surface.cpp b/src/Renderer/Surface.cpp
index b84ba1c..b5f244d 100644
--- a/src/Renderer/Surface.cpp
+++ b/src/Renderer/Surface.cpp
@@ -13,6 +13,7 @@
#include "Color.hpp"
#include "Context.hpp"
+#include "ETC_Decoder.hpp"
#include "Renderer.hpp"
#include "Common/Half.hpp"
#include "Common/Memory.hpp"
@@ -1428,11 +1429,11 @@
#endif
case FORMAT_ATI1: decodeATI1(destination, source); break; // FIXME: Check destination format
case FORMAT_ATI2: decodeATI2(destination, source); break; // FIXME: Check destination format
- case FORMAT_ETC1: decodeETC1(destination, source); break; // FIXME: Check destination format
case FORMAT_R11_EAC: decodeEAC(destination, source, 1, false); break; // FIXME: Check destination format
case FORMAT_SIGNED_R11_EAC: decodeEAC(destination, source, 1, true); break; // FIXME: Check destination format
case FORMAT_RG11_EAC: decodeEAC(destination, source, 2, false); break; // FIXME: Check destination format
case FORMAT_SIGNED_RG11_EAC: decodeEAC(destination, source, 2, true); break; // FIXME: Check destination format
+ case FORMAT_ETC1:
case FORMAT_RGB8_ETC2: decodeETC2(destination, source, 0, false); break; // FIXME: Check destination format
case FORMAT_SRGB8_ETC2: decodeETC2(destination, source, 0, true); break; // FIXME: Check destination format
case FORMAT_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: decodeETC2(destination, source, 1, false); break; // FIXME: Check destination format
@@ -2061,205 +2062,71 @@
}
}
- struct bgrx8
+ void Surface::decodeETC2(Buffer &internal, const Buffer &external, int nbAlphaBits, bool isSRGB)
{
- byte b;
- byte g;
- byte r;
- byte x;
+ ETC_Decoder::Decode((const byte*)external.buffer, (byte*)internal.buffer, external.width, external.height, internal.width, internal.height, internal.pitchB, internal.bytes,
+ (nbAlphaBits == 8) ? ETC_Decoder::ETC_RGBA : ((nbAlphaBits == 1) ? ETC_Decoder::ETC_RGB_PUNCHTHROUGH_ALPHA : ETC_Decoder::ETC_RGB));
- inline bgrx8()
+ if(isSRGB)
{
- }
-
- inline void set(int red, int green, int blue)
- {
- r = static_cast<byte>(clamp(red, 0, 255));
- g = static_cast<byte>(clamp(green, 0, 255));
- b = static_cast<byte>(clamp(blue, 0, 255));
- x = 255;
- }
- };
-
- struct ETC1
- {
- struct
- {
- union
+ static byte sRGBtoLinearTable[256];
+ static bool sRGBtoLinearTableDirty = true;
+ if(sRGBtoLinearTableDirty)
{
- struct // Individual colors
+ for(int i = 0; i < 256; i++)
{
- byte R2 : 4;
- byte R1 : 4;
- byte G2 : 4;
- byte G1 : 4;
- byte B2 : 4;
- byte B1 : 4;
- };
-
- struct // Differential colors
- {
- sbyte dR : 3;
- byte R : 5;
- sbyte dG : 3;
- byte G : 5;
- sbyte dB : 3;
- byte B : 5;
- };
- };
-
- bool flipbit : 1;
- bool diffbit : 1;
- byte cw2 : 3;
- byte cw1 : 3;
-
- byte pixelIndexMSB[2];
- byte pixelIndexLSB[2];
- };
-
- inline int getIndex(int x, int y) const
- {
- int bitIndex = x * 4 + y;
- int bitOffset = bitIndex & 7;
- int lsb = (pixelIndexLSB[1 - (bitIndex >> 3)] >> bitOffset) & 1;
- int msb = (pixelIndexMSB[1 - (bitIndex >> 3)] >> bitOffset) & 1;
-
- return (msb << 1) | lsb;
- }
- };
-
- inline int extend_4to8bits(int x)
- {
- return (x << 4) | x;
- }
-
- inline int extend_5to8bits(int x)
- {
- return (x << 3) | (x >> 2);
- }
-
- void Surface::decodeETC1(Buffer &internal, const Buffer &external)
- {
- unsigned int *destSlice = (unsigned int*)internal.buffer;
- const ETC1 *source = (const ETC1*)external.buffer;
-
- for(int z = 0; z < external.depth; z++)
- {
- unsigned int *dest = destSlice;
-
- for(int y = 0; y < external.height; y += 4)
- {
- for(int x = 0; x < external.width; x += 4)
- {
- bgrx8 *color = reinterpret_cast<bgrx8*>(&dest[x + y * internal.pitchP]);
-
- int r1, g1, b1;
- int r2, g2, b2;
-
- if(source->diffbit)
- {
- b1 = extend_5to8bits(source->B);
- g1 = extend_5to8bits(source->G);
- r1 = extend_5to8bits(source->R);
-
- r2 = extend_5to8bits(source->R + source->dR);
- g2 = extend_5to8bits(source->G + source->dG);
- b2 = extend_5to8bits(source->B + source->dB);
- }
- else
- {
- r1 = extend_4to8bits(source->R1);
- g1 = extend_4to8bits(source->G1);
- b1 = extend_4to8bits(source->B1);
-
- r2 = extend_4to8bits(source->R2);
- g2 = extend_4to8bits(source->G2);
- b2 = extend_4to8bits(source->B2);
- }
-
- bgrx8 subblockColors0[4];
- bgrx8 subblockColors1[4];
-
- // Table 3.17.2 sorted according to table 3.17.3
- static const int intensityModifier[8][4] =
- {
- {2, 8, -2, -8},
- {5, 17, -5, -17},
- {9, 29, -9, -29},
- {13, 42, -13, -42},
- {18, 60, -18, -60},
- {24, 80, -24, -80},
- {33, 106, -33, -106},
- {47, 183, -47, -183}
- };
-
- const int i10 = intensityModifier[source->cw1][0];
- const int i11 = intensityModifier[source->cw1][1];
- const int i12 = intensityModifier[source->cw1][2];
- const int i13 = intensityModifier[source->cw1][3];
-
- subblockColors0[0].set(r1 + i10, g1 + i10, b1 + i10);
- subblockColors0[1].set(r1 + i11, g1 + i11, b1 + i11);
- subblockColors0[2].set(r1 + i12, g1 + i12, b1 + i12);
- subblockColors0[3].set(r1 + i13, g1 + i13, b1 + i13);
-
- const int i20 = intensityModifier[source->cw2][0];
- const int i21 = intensityModifier[source->cw2][1];
- const int i22 = intensityModifier[source->cw2][2];
- const int i23 = intensityModifier[source->cw2][3];
-
- subblockColors1[0].set(r2 + i20, g2 + i20, b2 + i20);
- subblockColors1[1].set(r2 + i21, g2 + i21, b2 + i21);
- subblockColors1[2].set(r2 + i22, g2 + i22, b2 + i22);
- subblockColors1[3].set(r2 + i23, g2 + i23, b2 + i23);
-
- if(source->flipbit)
- {
- for(int j = 0; j < 2 && (y + j) < internal.height; j++)
- {
- if((x + 0) < internal.width) color[0] = subblockColors0[source->getIndex(0, j)];
- if((x + 1) < internal.width) color[1] = subblockColors0[source->getIndex(1, j)];
- if((x + 2) < internal.width) color[2] = subblockColors0[source->getIndex(2, j)];
- if((x + 3) < internal.width) color[3] = subblockColors0[source->getIndex(3, j)];
- color += internal.pitchP;
- }
-
- for(int j = 2; j < 4 && (y + j) < internal.height; j++)
- {
- if((x + 0) < internal.width) color[0] = subblockColors1[source->getIndex(0, j)];
- if((x + 1) < internal.width) color[1] = subblockColors1[source->getIndex(1, j)];
- if((x + 2) < internal.width) color[2] = subblockColors1[source->getIndex(2, j)];
- if((x + 3) < internal.width) color[3] = subblockColors1[source->getIndex(3, j)];
- color += internal.pitchP;
- }
- }
- else
- {
- for(int j = 0; j < 4 && (y + j) < internal.height; j++)
- {
- if((x + 0) < internal.width) color[0] = subblockColors0[source->getIndex(0, j)];
- if((x + 1) < internal.width) color[1] = subblockColors0[source->getIndex(1, j)];
- if((x + 2) < internal.width) color[2] = subblockColors1[source->getIndex(2, j)];
- if((x + 3) < internal.width) color[3] = subblockColors1[source->getIndex(3, j)];
- color += internal.pitchP;
- }
- }
-
- source++;
+ sRGBtoLinearTable[i] = static_cast<byte>(sRGBtoLinear(static_cast<float>(i) / 255.0f) * 255.0f + 0.5f);
}
+ sRGBtoLinearTableDirty = false;
}
- (byte*&)destSlice += internal.sliceB;
+ // Perform sRGB conversion in place after decoding
+ byte* src = (byte*)internal.buffer;
+ for(int y = 0; y < internal.height; y++)
+ {
+ byte* srcRow = src + y * internal.pitchB;
+ for(int x = 0; x < internal.width; x++)
+ {
+ byte* srcPix = srcRow + x * internal.bytes;
+ for(int i = 0; i < 3; i++)
+ {
+ srcPix[i] = sRGBtoLinearTable[srcPix[i]];
+ }
+ }
+ }
}
}
void Surface::decodeEAC(Buffer &internal, const Buffer &external, int nbChannels, bool isSigned)
{
- ASSERT((nbChannels == 1) || (nbChannels == 2));
- }
+ ASSERT(nbChannels == 1 || nbChannels == 2);
- void Surface::decodeETC2(Buffer &internal, const Buffer &external, int nbAlphaBits, bool isSRGB)
- {
+ ETC_Decoder::Decode((const byte*)external.buffer, (byte*)internal.buffer, external.width, external.height, internal.width, internal.height, internal.pitchB, internal.bytes,
+ (nbChannels == 1) ? (isSigned ? ETC_Decoder::ETC_R_SIGNED : ETC_Decoder::ETC_R_UNSIGNED) : (isSigned ? ETC_Decoder::ETC_RG_SIGNED : ETC_Decoder::ETC_RG_UNSIGNED));
+
+ // FIXME: We convert signed data to float, until signed integer internal formats are supported
+ // This code can be removed if signed ETC2 images are decoded to internal 8 bit signed R/RG formats
+ if(isSigned)
+ {
+ sbyte* src = (sbyte*)internal.buffer;
+
+ for(int y = 0; y < internal.height; y++)
+ {
+ sbyte* srcRow = src + y * internal.pitchB;
+ for(int x = internal.width - 1; x >= 0; x--)
+ {
+ int dx = x & 0xFFFFFFFC;
+ int mx = x - dx;
+ sbyte* srcPix = srcRow + dx * internal.bytes + mx * nbChannels;
+ float* dstPix = (float*)(srcRow + x * internal.bytes);
+ for(int c = nbChannels - 1; c >= 0; c--)
+ {
+ static const float normalization = 1.0f / 127.875f;
+ dstPix[c] = clamp(static_cast<float>(srcPix[c]) * normalization, -1.0f, 1.0f);
+ }
+ }
+ }
+ }
}
void Surface::decodeASTC(Buffer &internal, const Buffer &external, int xBlockSize, int yBlockSize, int zBlockSize, bool isSRGB)
@@ -3618,12 +3485,14 @@
return FORMAT_A32B32G32R32F; // FIXME: 16FP is probably sufficient, but it's currently unsupported
case FORMAT_ATI1:
case FORMAT_R11_EAC:
- case FORMAT_SIGNED_R11_EAC:
return FORMAT_R8;
+ case FORMAT_SIGNED_R11_EAC:
+ return FORMAT_R32F; // FIXME: Signed 8bit format would be sufficient
case FORMAT_ATI2:
case FORMAT_RG11_EAC:
- case FORMAT_SIGNED_RG11_EAC:
return FORMAT_G8R8;
+ case FORMAT_SIGNED_RG11_EAC:
+ return FORMAT_G32R32F; // FIXME: Signed 8bit format would be sufficient
case FORMAT_ETC1:
case FORMAT_RGB8_ETC2:
case FORMAT_SRGB8_ETC2:
diff --git a/src/Renderer/Surface.hpp b/src/Renderer/Surface.hpp
index 23a392d..fe0817e 100644
--- a/src/Renderer/Surface.hpp
+++ b/src/Renderer/Surface.hpp
@@ -401,7 +401,6 @@
#endif
static void decodeATI1(Buffer &internal, const Buffer &external);
static void decodeATI2(Buffer &internal, const Buffer &external);
- static void decodeETC1(Buffer &internal, const Buffer &external);
static void decodeEAC(Buffer &internal, const Buffer &external, int nbChannels, bool isSigned);
static void decodeETC2(Buffer &internal, const Buffer &external, int nbAlphaBits, bool isSRGB);
static void decodeASTC(Buffer &internal, const Buffer &external, int xSize, int ySize, int zSize, bool isSRGB);
diff --git a/src/SwiftShader/SwiftShader.vcxproj b/src/SwiftShader/SwiftShader.vcxproj
index 5eca09b..3ffcdab 100644
--- a/src/SwiftShader/SwiftShader.vcxproj
+++ b/src/SwiftShader/SwiftShader.vcxproj
@@ -320,6 +320,7 @@
<ClCompile Include="..\Main\crc.cpp" />
<ClCompile Include="..\Main\FrameBufferWin.cpp" />
<ClCompile Include="..\Main\Register.cpp" />
+ <ClCompile Include="..\Renderer\ETC_Decoder.cpp" />
<ClCompile Include="..\Shader\Constants.cpp" />
<ClCompile Include="..\Shader\PixelPipeline.cpp" />
<ClCompile Include="..\Shader\PixelProgram.cpp" />
@@ -391,6 +392,7 @@
<ClInclude Include="..\Common\Version.h" />
<ClInclude Include="..\Main\FrameBufferWin.hpp" />
<ClInclude Include="..\Main\Register.hpp" />
+ <ClInclude Include="..\Renderer\ETC_Decoder.hpp" />
<ClInclude Include="..\Renderer\RoutineCache.hpp" />
<ClInclude Include="..\Shader\PixelPipeline.hpp" />
<ClInclude Include="..\Shader\PixelProgram.hpp" />
diff --git a/src/SwiftShader/SwiftShader.vcxproj.filters b/src/SwiftShader/SwiftShader.vcxproj.filters
index 572441c..27b2dd0 100644
--- a/src/SwiftShader/SwiftShader.vcxproj.filters
+++ b/src/SwiftShader/SwiftShader.vcxproj.filters
@@ -185,6 +185,9 @@
<ClCompile Include="..\Shader\PixelProgram.cpp">
<Filter>Source Files\Shader</Filter>
</ClCompile>
+ <ClCompile Include="..\Renderer\ETC_Decoder.cpp">
+ <Filter>Source Files\Renderer</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="MemoryManager.hpp">
@@ -368,6 +371,9 @@
<ClInclude Include="..\Shader\PixelPipeline.hpp">
<Filter>Header Files\Shader</Filter>
</ClInclude>
+ <ClInclude Include="..\Renderer\ETC_Decoder.hpp">
+ <Filter>Header Files\Renderer</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="SwiftShader.ini" />