// // Copyright 2013 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //
// loadimage_etc.cpp: Decodes ETC and EAC encoded textures.
template <typename T> static T renormalizeEAC(int value)
{
int upper = 0;
int lower = 0;
int shift = 0;
if (std::is_same<T, int16_t>::value)
{ // The spec states that -1024 invalid and should be clamped to -1023
upper = 1023;
lower = -1023;
shift = 5;
} else if (std::is_same<T, uint16_t>::value)
{
upper = 2047;
lower = 0;
shift = 5;
} else
{ // We currently only support renormalizing int16_t or uint16_t
UNREACHABLE();
}
void decodeTBlock(uint8_t *dest,
size_t x,
size_t y,
size_t w,
size_t h,
size_t destRowPitch, const uint8_t alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const
{ // Table C.8, distance index for T and H modes constauto &block = u.idht.mode.tm;
int r1 = extend_4to8bits(block.TR1a << 2 | block.TR1b);
int g1 = extend_4to8bits(block.TG1);
int b1 = extend_4to8bits(block.TB1);
int r2 = extend_4to8bits(block.TR2);
int g2 = extend_4to8bits(block.TG2);
int b2 = extend_4to8bits(block.TB2);
static int distance[8] = {3, 6, 11, 16, 23, 32, 41, 64}; const int d = distance[block.Tda << 1 | block.Tdb];
uint32_t matchBC1Bits(const int *pixelIndices, const int *pixelIndexCounts, const R8G8B8A8 *subblockColors,
size_t numColors, const R8G8B8A8 &minColor, const R8G8B8A8 &maxColor, bool nonOpaquePunchThroughAlpha) const
{ // Project each pixel on the (maxColor, minColor) line to decide which // BC1 code to assign to it.
int direction[3];
for (int ch = 0; ch < 3; ch++)
{
direction[ch] = decodedColors[0][ch] - decodedColors[1][ch];
}
int stops[2];
for (int i = 0; i < 2; i++)
{
stops[i] = decodedColors[i][0] * direction[0] + decodedColors[i][1] * direction[1] +
decodedColors[i][2] * direction[2];
}
ASSERT(numColors <= kNumPixelsInBlock);
int encodedColors[kNumPixelsInBlock];
if (nonOpaquePunchThroughAlpha)
{
for (size_t i = 0; i < numColors; i++)
{ const int count = pixelIndexCounts[i];
if (count > 0)
{ // In non-opaque mode, 3 is for tranparent pixels.
if (0 == subblockColors[i].A)
{
encodedColors[i] = 3;
} else
{ const R8G8B8A8 &pixel = subblockColors[i]; const int dot = pixel.R * direction[0] + pixel.G * direction[1] +
pixel.B * direction[2]; const int factor = gl::clamp( static_cast<int>(
(static_cast<float>(dot - stops[1]) / (stops[0] - stops[1])) * 2 + 0.5f), 0, 2); switch (factor)
{ case0:
encodedColors[i] = 0; break; case1:
encodedColors[i] = 2; break; case2: default:
encodedColors[i] = 1; break;
}
}
}
}
} else
{
for (size_t i = 0; i < numColors; i++)
{ const int count = pixelIndexCounts[i];
if (count > 0)
{ // In opaque mode, the code is from 0 to 3.
uint32_t bits = 0;
for (int i = kNumPixelsInBlock - 1; i >= 0; i--)
{
bits <<= 2;
bits |= encodedColors[pixelIndices[i]];
}
return bits;
}
void packBC1(void *bc1, const int *pixelIndices, const int *pixelIndexCounts, const R8G8B8A8 *subblockColors,
size_t numColors,
int minColorIndex,
int maxColorIndex, bool nonOpaquePunchThroughAlpha) const
{ const R8G8B8A8 &minColor = subblockColors[minColorIndex]; const R8G8B8A8 &maxColor = subblockColors[maxColorIndex];
uint32_t bits;
uint16_t max16 = RGB8ToRGB565(maxColor);
uint16_t min16 = RGB8ToRGB565(minColor);
if (max16 != min16)
{ // Find the best BC1 code for each pixel
bits = matchBC1Bits(pixelIndices, pixelIndexCounts, subblockColors, numColors, minColor,
maxColor, nonOpaquePunchThroughAlpha);
} else
{ // Same colors, BC1 index 0 is the color in both opaque and transparent mode
bits = 0; // BC1 index 3 is transparent
if (nonOpaquePunchThroughAlpha)
{
for (int i = 0; i < kNumPixelsInBlock; i++)
{
if (0 == subblockColors[pixelIndices[i]].A)
{
bits |= (3 << (i * 2));
}
}
}
}
if (max16 < min16)
{
std::swap(max16, min16);
uint32_t xorMask = 0;
if (nonOpaquePunchThroughAlpha)
{ // In transparent mode switching the colors is doing the // following code swap: 0 <-> 1. 0xA selects the second bit of // each code, bits >> 1 selects the first bit of the code when // the seconds bit is set (case 2 and 3). We invert all the // non-selected bits, that is the first bit when the code is // 0 or 1.
xorMask = ~((bits >> 1) | 0xAAAAAAAA);
} else
{ // In opaque mode switching the two colors is doing the // following code swaps: 0 <-> 1 and 2 <-> 3. This is // equivalent to flipping the first bit of each code // (5 = 0b0101)
xorMask = 0x55555555;
}
bits ^= xorMask;
}
// Encode the opaqueness in the order of the two BC1 colors
BC1Block *dest = reinterpret_cast<BC1Block *>(bc1);
if (nonOpaquePunchThroughAlpha)
{
dest->color0 = min16;
dest->color1 = max16;
} else
{
dest->color0 = max16;
dest->color1 = min16;
}
dest->bits = bits;
}
void transcodeIndividualBlockToBC1(uint8_t *dest,
size_t x,
size_t y,
size_t w,
size_t h, const uint8_t alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const
{ constauto &block = u.idht.mode.idm.colors.indiv;
int r1 = extend_4to8bits(block.R1);
int g1 = extend_4to8bits(block.G1);
int b1 = extend_4to8bits(block.B1);
int r2 = extend_4to8bits(block.R2);
int g2 = extend_4to8bits(block.G2);
int b2 = extend_4to8bits(block.B2);
transcodeIndividualOrDifferentialBlockToBC1(dest, x, y, w, h, r1, g1, b1, r2, g2, b2,
alphaValues, nonOpaquePunchThroughAlpha);
}
void transcodeDifferentialBlockToBC1(uint8_t *dest,
size_t x,
size_t y,
size_t w,
size_t h, const uint8_t alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const
{ constauto &block = u.idht.mode.idm.colors.diff;
int b1 = extend_5to8bits(block.B);
int g1 = extend_5to8bits(block.G);
int r1 = extend_5to8bits(block.R);
int r2 = extend_5to8bits(block.R + block.dR);
int g2 = extend_5to8bits(block.G + block.dG);
int b2 = extend_5to8bits(block.B + block.dB);
transcodeIndividualOrDifferentialBlockToBC1(dest, x, y, w, h, r1, g1, b1, r2, g2, b2,
alphaValues, nonOpaquePunchThroughAlpha);
}
// determine covariance matrix
int cov[6] = {0, 0, 0, 0, 0, 0};
for (size_t i = 0; i < numColors; i++)
{ const int count = pixelIndexCounts[i];
if (count > 0)
{ constauto &pixel = subblockColors[i];
if (pixel.A > 0)
{
int r = pixel.R - mu[0];
int g = pixel.G - mu[1];
int b = pixel.B - mu[2];
cov[0] += r * r * count;
cov[1] += r * g * count;
cov[2] += r * b * count;
cov[3] += g * g * count;
cov[4] += g * b * count;
cov[5] += b * b * count;
}
}
}
// Power iteration algorithm to get the eigenvalues and eigenvector
constexpr size_t kPowerIterations = 4;
for (size_t i = 0; i < kPowerIterations; i++)
{
float r = vfr * cov[0] + vfg * cov[1] + vfb * cov[2];
float g = vfr * cov[1] + vfg * cov[3] + vfb * cov[4];
float b = vfr * cov[2] + vfg * cov[4] + vfb * cov[5];
vfr = r;
vfg = g;
vfb = b;
eigenvalue = sqrt(r * r + g * g + b * b);
if (eigenvalue > 0)
{
float invNorm = 1.0f / eigenvalue;
vfr *= invNorm;
vfg *= invNorm;
vfb *= invNorm;
}
}
int vr, vg, vb;
staticconst float kDefaultLuminanceThreshold = 4.0f * 255; staticconst float kQuantizeRange = 512.0f;
if (eigenvalue < kDefaultLuminanceThreshold) // too small, default to luminance
{ // Luminance weights defined by ITU-R Recommendation BT.601, scaled by 1000
vr = 299;
vg = 587;
vb = 114;
} else
{ // From the eigenvalue and eigenvector, choose the axis to project // colors on. When projecting colors we want to do integer computations // for speed, so we normalize the eigenvector to the [0, 512] range.
float magn = std::max(std::max(std::abs(vfr), std::abs(vfg)), std::abs(vfb));
magn = kQuantizeRange / magn;
vr = static_cast<int>(vfr * magn);
vg = static_cast<int>(vfg * magn);
vb = static_cast<int>(vfb * magn);
}
// Pick colors at extreme points
int minD = INT_MAX;
int maxD = 0;
size_t minIndex = 0;
size_t maxIndex = 0;
for (size_t i = 0; i < numColors; i++)
{ const int count = pixelIndexCounts[i];
if (count > 0)
{ constauto &pixel = subblockColors[i];
if (pixel.A > 0)
{
int dot = pixel.R * vr + pixel.G * vg + pixel.B * vb;
if (dot < minD)
{
minD = dot;
minIndex = i;
}
if (dot > maxD)
{
maxD = dot;
maxIndex = i;
}
}
}
}
void transcodeIndividualOrDifferentialBlockToBC1(uint8_t *dest,
size_t x,
size_t y,
size_t w,
size_t h,
int r1,
int g1,
int b1,
int r2,
int g2,
int b2, const uint8_t alphaValues[4][4], bool nonOpaquePunchThroughAlpha) const
{ // A BC1 block has 2 endpoints, pixels is encoded as linear // interpolations of them. A ETC1/ETC2 individual or differential block // has 2 subblocks. Each subblock has one color and a modifier. We // select axis by principal component analysis (PCA) to use as // our two BC1 endpoints and then map pixels to BC1 by projecting on the // line between the two endpoints and choosing the right fraction.
// The goal of this algorithm is make it faster than decode ETC to RGBs // and then encode to BC. To achieve this, we only extract subblock // colors, pixel indices, and counts of each pixel indices from ETC. // With those information, we can only encode used subblock colors // to BC1, and copy the bits to the right pixels. // Fully decode and encode need to process 16 RGBA pixels. With this // algorithm, it's 8 pixels at maximum for a individual or // differential block. Saves us bandwidth and computations.
// Compute the colors that pixels can have in each subblock both for // the decoding of the RGBA data and BC1 encoding
R8G8B8A8 subblockColors[kNumColors];
for (size_t modifierIdx = 0; modifierIdx < 4; modifierIdx++)
{
if (nonOpaquePunchThroughAlpha && (modifierIdx == 2))
{ // In ETC opaque punch through formats, individual and // differential blocks take index 2 as transparent pixel. // Thus we don't need to compute its color, just assign it // as black.
subblockColors[modifierIdx] = createRGBA(0, 0, 0, 0);
subblockColors[4 + modifierIdx] = createRGBA(0, 0, 0, 0);
} else
{ const int i1 = intensityModifier[u.idht.mode.idm.cw1][modifierIdx];
subblockColors[modifierIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1);
int pixelIndices[kNumPixelsInBlock];
int pixelIndexCounts[kNumColors] = {0}; // Extract pixel indices from a ETC block.
for (size_t blockIdx = 0; blockIdx < 2; blockIdx++)
{
extractPixelIndices(pixelIndices, pixelIndexCounts, x, y, w, h, u.idht.mode.idm.flipbit,
blockIdx);
}
int minColorIndex, maxColorIndex;
selectEndPointPCA(pixelIndexCounts, subblockColors, kNumColors, &minColorIndex,
&maxColorIndex);
// Table C.8, distance index for T and H modes constauto &block = u.idht.mode.tm;
int r1 = extend_4to8bits(block.TR1a << 2 | block.TR1b);
int g1 = extend_4to8bits(block.TG1);
int b1 = extend_4to8bits(block.TB1);
int r2 = extend_4to8bits(block.TR2);
int g2 = extend_4to8bits(block.TG2);
int b2 = extend_4to8bits(block.TB2);
static int distance[8] = {3, 6, 11, 16, 23, 32, 41, 64}; const int d = distance[block.Tda << 1 | block.Tdb];
// In ETC opaque punch through formats, index == 2 means transparent pixel. // Thus we don't need to compute its color, just assign it as black. const R8G8B8A8 paintColors[kNumColors] = {
createRGBA(r1, g1, b1),
createRGBA(r2 + d, g2 + d, b2 + d),
nonOpaquePunchThroughAlpha ? createRGBA(0, 0, 0, 0) : createRGBA(r2, g2, b2),
createRGBA(r2 - d, g2 - d, b2 - d),
};
int pixelIndices[kNumPixelsInBlock];
int pixelIndexCounts[kNumColors] = {0};
for (size_t j = 0; j < 4; j++)
{
int *row = &pixelIndices[j * 4];
for (size_t i = 0; i < 4; i++)
{ const size_t pixelIndex = getIndex(i, j);
row[i] = static_cast<int>(pixelIndex);
pixelIndexCounts[pixelIndex]++;
}
}
int minColorIndex, maxColorIndex;
selectEndPointPCA(pixelIndexCounts, paintColors, kNumColors, &minColorIndex,
&maxColorIndex);
for (size_t z = 0; z < depth; z++)
{
for (size_t y = 0; y < height; y += 4)
{ const ETC2Block *sourceRow =
priv::OffsetDataPointer<ETC2Block>(input, y / 4, z, inputRowPitch, inputDepthPitch);
uint8_t *destRow =
priv::OffsetDataPointer<uint8_t>(output, y, z, outputRowPitch, outputDepthPitch);
for (size_t x = 0; x < width; x += 4)
{ const ETC2Block *sourceBlockAlpha = sourceRow + (x / 2);
sourceBlockAlpha->decodeAsSingleETC2Channel( reinterpret_cast<uint8_t *>(decodedAlphaValues), x, y, width, height, 1, 4,
false);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.