Path: blob/main_old/src/image_util/loadimage_etc.cpp
1693 views
//1// Copyright 2013 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//56// loadimage_etc.cpp: Decodes ETC and EAC encoded textures.78#include "image_util/loadimage.h"910#include <type_traits>11#include "common/mathutil.h"1213#include "image_util/imageformats.h"1415namespace angle16{17namespace18{1920using IntensityModifier = const int[4];2122// Table 3.17.2 sorted according to table 3.17.323// clang-format off24static IntensityModifier intensityModifierDefault[] =25{26{ 2, 8, -2, -8 },27{ 5, 17, -5, -17 },28{ 9, 29, -9, -29 },29{ 13, 42, -13, -42 },30{ 18, 60, -18, -60 },31{ 24, 80, -24, -80 },32{ 33, 106, -33, -106 },33{ 47, 183, -47, -183 },34};35// clang-format on3637// Table C.12, intensity modifier for non opaque punchthrough alpha38// clang-format off39static IntensityModifier intensityModifierNonOpaque[] =40{41{ 0, 8, 0, -8 },42{ 0, 17, 0, -17 },43{ 0, 29, 0, -29 },44{ 0, 42, 0, -42 },45{ 0, 60, 0, -60 },46{ 0, 80, 0, -80 },47{ 0, 106, 0, -106 },48{ 0, 183, 0, -183 },49};50// clang-format on5152static const int kNumPixelsInBlock = 16;5354struct ETC2Block55{56// Decodes unsigned single or dual channel ETC2 block to 8-bit color57void decodeAsSingleETC2Channel(uint8_t *dest,58size_t x,59size_t y,60size_t w,61size_t h,62size_t destPixelStride,63size_t destRowPitch,64bool isSigned) const65{66for (size_t j = 0; j < 4 && (y + j) < h; j++)67{68uint8_t *row = dest + (j * destRowPitch);69for (size_t i = 0; i < 4 && (x + i) < w; i++)70{71uint8_t *pixel = row + (i * destPixelStride);72if (isSigned)73{74*pixel = clampSByte(getSingleETC2Channel(i, j, isSigned));75}76else77{78*pixel = clampByte(getSingleETC2Channel(i, j, isSigned));79}80}81}82}8384// Decodes unsigned single or dual channel EAC block to 16-bit color85void decodeAsSingleEACChannel(uint16_t *dest,86size_t x,87size_t y,88size_t w,89size_t h,90size_t destPixelStride,91size_t destRowPitch,92bool isSigned,93bool isFloat) const94{95for (size_t j = 0; j < 4 && (y + j) < h; j++)96{97uint16_t *row = reinterpret_cast<uint16_t *>(reinterpret_cast<uint8_t *>(dest) +98(j * destRowPitch));99for (size_t i = 0; i < 4 && (x + i) < w; i++)100{101uint16_t *pixel = row + (i * destPixelStride);102if (isSigned)103{104int16_t tempPixel =105renormalizeEAC<int16_t>(getSingleEACChannel(i, j, isSigned));106*pixel =107isFloat ? gl::float32ToFloat16(float(gl::normalize(tempPixel))) : tempPixel;108}109else110{111uint16_t tempPixel =112renormalizeEAC<uint16_t>(getSingleEACChannel(i, j, isSigned));113*pixel =114isFloat ? gl::float32ToFloat16(float(gl::normalize(tempPixel))) : tempPixel;115}116}117}118}119120// Decodes RGB block to rgba8121void decodeAsRGB(uint8_t *dest,122size_t x,123size_t y,124size_t w,125size_t h,126size_t destRowPitch,127const uint8_t alphaValues[4][4],128bool punchThroughAlpha) const129{130bool opaqueBit = u.idht.mode.idm.diffbit;131bool nonOpaquePunchThroughAlpha = punchThroughAlpha && !opaqueBit;132// Select mode133if (u.idht.mode.idm.diffbit || punchThroughAlpha)134{135const auto &block = u.idht.mode.idm.colors.diff;136int r = (block.R + block.dR);137int g = (block.G + block.dG);138int b = (block.B + block.dB);139if (r < 0 || r > 31)140{141decodeTBlock(dest, x, y, w, h, destRowPitch, alphaValues,142nonOpaquePunchThroughAlpha);143}144else if (g < 0 || g > 31)145{146decodeHBlock(dest, x, y, w, h, destRowPitch, alphaValues,147nonOpaquePunchThroughAlpha);148}149else if (b < 0 || b > 31)150{151decodePlanarBlock(dest, x, y, w, h, destRowPitch, alphaValues);152}153else154{155decodeDifferentialBlock(dest, x, y, w, h, destRowPitch, alphaValues,156nonOpaquePunchThroughAlpha);157}158}159else160{161decodeIndividualBlock(dest, x, y, w, h, destRowPitch, alphaValues,162nonOpaquePunchThroughAlpha);163}164}165166// Transcodes RGB block to BC1167void transcodeAsBC1(uint8_t *dest,168size_t x,169size_t y,170size_t w,171size_t h,172const uint8_t alphaValues[4][4],173bool punchThroughAlpha) const174{175bool opaqueBit = u.idht.mode.idm.diffbit;176bool nonOpaquePunchThroughAlpha = punchThroughAlpha && !opaqueBit;177// Select mode178if (u.idht.mode.idm.diffbit || punchThroughAlpha)179{180const auto &block = u.idht.mode.idm.colors.diff;181int r = (block.R + block.dR);182int g = (block.G + block.dG);183int b = (block.B + block.dB);184if (r < 0 || r > 31)185{186transcodeTBlockToBC1(dest, x, y, w, h, alphaValues, nonOpaquePunchThroughAlpha);187}188else if (g < 0 || g > 31)189{190transcodeHBlockToBC1(dest, x, y, w, h, alphaValues, nonOpaquePunchThroughAlpha);191}192else if (b < 0 || b > 31)193{194transcodePlanarBlockToBC1(dest, x, y, w, h, alphaValues);195}196else197{198transcodeDifferentialBlockToBC1(dest, x, y, w, h, alphaValues,199nonOpaquePunchThroughAlpha);200}201}202else203{204transcodeIndividualBlockToBC1(dest, x, y, w, h, alphaValues,205nonOpaquePunchThroughAlpha);206}207}208209private:210union211{212// Individual, differential, H and T modes213struct214{215union216{217// Individual and differential modes218struct219{220union221{222struct // Individual colors223{224unsigned char R2 : 4;225unsigned char R1 : 4;226unsigned char G2 : 4;227unsigned char G1 : 4;228unsigned char B2 : 4;229unsigned char B1 : 4;230} indiv;231struct // Differential colors232{233signed char dR : 3;234unsigned char R : 5;235signed char dG : 3;236unsigned char G : 5;237signed char dB : 3;238unsigned char B : 5;239} diff;240} colors;241bool flipbit : 1;242bool diffbit : 1;243unsigned char cw2 : 3;244unsigned char cw1 : 3;245} idm;246// T mode247struct248{249// Byte 1250unsigned char TR1b : 2;251unsigned char TunusedB : 1;252unsigned char TR1a : 2;253unsigned char TunusedA : 3;254// Byte 2255unsigned char TB1 : 4;256unsigned char TG1 : 4;257// Byte 3258unsigned char TG2 : 4;259unsigned char TR2 : 4;260// Byte 4261unsigned char Tdb : 1;262bool Tflipbit : 1;263unsigned char Tda : 2;264unsigned char TB2 : 4;265} tm;266// H mode267struct268{269// Byte 1270unsigned char HG1a : 3;271unsigned char HR1 : 4;272unsigned char HunusedA : 1;273// Byte 2274unsigned char HB1b : 2;275unsigned char HunusedC : 1;276unsigned char HB1a : 1;277unsigned char HG1b : 1;278unsigned char HunusedB : 3;279// Byte 3280unsigned char HG2a : 3;281unsigned char HR2 : 4;282unsigned char HB1c : 1;283// Byte 4284unsigned char Hdb : 1;285bool Hflipbit : 1;286unsigned char Hda : 1;287unsigned char HB2 : 4;288unsigned char HG2b : 1;289} hm;290} mode;291unsigned char pixelIndexMSB[2];292unsigned char pixelIndexLSB[2];293} idht;294// planar mode295struct296{297// Byte 1298unsigned char GO1 : 1;299unsigned char RO : 6;300unsigned char PunusedA : 1;301// Byte 2302unsigned char BO1 : 1;303unsigned char GO2 : 6;304unsigned char PunusedB : 1;305// Byte 3306unsigned char BO3a : 2;307unsigned char PunusedD : 1;308unsigned char BO2 : 2;309unsigned char PunusedC : 3;310// Byte 4311unsigned char RH2 : 1;312bool Pflipbit : 1;313unsigned char RH1 : 5;314unsigned char BO3b : 1;315// Byte 5316unsigned char BHa : 1;317unsigned char GH : 7;318// Byte 6319unsigned char RVa : 3;320unsigned char BHb : 5;321// Byte 7322unsigned char GVa : 5;323unsigned char RVb : 3;324// Byte 8325unsigned char BV : 6;326unsigned char GVb : 2;327} pblk;328// Single channel block329struct330{331union332{333unsigned char us;334signed char s;335} base_codeword;336unsigned char table_index : 4;337unsigned char multiplier : 4;338unsigned char mc1 : 2;339unsigned char mb : 3;340unsigned char ma : 3;341unsigned char mf1 : 1;342unsigned char me : 3;343unsigned char md : 3;344unsigned char mc2 : 1;345unsigned char mh : 3;346unsigned char mg : 3;347unsigned char mf2 : 2;348unsigned char mk1 : 2;349unsigned char mj : 3;350unsigned char mi : 3;351unsigned char mn1 : 1;352unsigned char mm : 3;353unsigned char ml : 3;354unsigned char mk2 : 1;355unsigned char mp : 3;356unsigned char mo : 3;357unsigned char mn2 : 2;358} scblk;359} u;360361static unsigned char clampByte(int value)362{363return static_cast<unsigned char>(gl::clamp(value, 0, 255));364}365366static signed char clampSByte(int value)367{368return static_cast<signed char>(gl::clamp(value, -128, 127));369}370371template <typename T>372static T renormalizeEAC(int value)373{374int upper = 0;375int lower = 0;376int shift = 0;377378if (std::is_same<T, int16_t>::value)379{380// The spec states that -1024 invalid and should be clamped to -1023381upper = 1023;382lower = -1023;383shift = 5;384}385else if (std::is_same<T, uint16_t>::value)386{387upper = 2047;388lower = 0;389shift = 5;390}391else392{393// We currently only support renormalizing int16_t or uint16_t394UNREACHABLE();395}396397return static_cast<T>(gl::clamp(value, lower, upper)) << shift;398}399400static R8G8B8A8 createRGBA(int red, int green, int blue, int alpha)401{402R8G8B8A8 rgba;403rgba.R = clampByte(red);404rgba.G = clampByte(green);405rgba.B = clampByte(blue);406rgba.A = clampByte(alpha);407return rgba;408}409410static R8G8B8A8 createRGBA(int red, int green, int blue)411{412return createRGBA(red, green, blue, 255);413}414415static int extend_4to8bits(int x) { return (x << 4) | x; }416static int extend_5to8bits(int x) { return (x << 3) | (x >> 2); }417static int extend_6to8bits(int x) { return (x << 2) | (x >> 4); }418static int extend_7to8bits(int x) { return (x << 1) | (x >> 6); }419420void decodeIndividualBlock(uint8_t *dest,421size_t x,422size_t y,423size_t w,424size_t h,425size_t destRowPitch,426const uint8_t alphaValues[4][4],427bool nonOpaquePunchThroughAlpha) const428{429const auto &block = u.idht.mode.idm.colors.indiv;430int r1 = extend_4to8bits(block.R1);431int g1 = extend_4to8bits(block.G1);432int b1 = extend_4to8bits(block.B1);433int r2 = extend_4to8bits(block.R2);434int g2 = extend_4to8bits(block.G2);435int b2 = extend_4to8bits(block.B2);436decodeIndividualOrDifferentialBlock(dest, x, y, w, h, destRowPitch, r1, g1, b1, r2, g2, b2,437alphaValues, nonOpaquePunchThroughAlpha);438}439440void decodeDifferentialBlock(uint8_t *dest,441size_t x,442size_t y,443size_t w,444size_t h,445size_t destRowPitch,446const uint8_t alphaValues[4][4],447bool nonOpaquePunchThroughAlpha) const448{449const auto &block = u.idht.mode.idm.colors.diff;450int b1 = extend_5to8bits(block.B);451int g1 = extend_5to8bits(block.G);452int r1 = extend_5to8bits(block.R);453int r2 = extend_5to8bits(block.R + block.dR);454int g2 = extend_5to8bits(block.G + block.dG);455int b2 = extend_5to8bits(block.B + block.dB);456decodeIndividualOrDifferentialBlock(dest, x, y, w, h, destRowPitch, r1, g1, b1, r2, g2, b2,457alphaValues, nonOpaquePunchThroughAlpha);458}459460void decodeIndividualOrDifferentialBlock(uint8_t *dest,461size_t x,462size_t y,463size_t w,464size_t h,465size_t destRowPitch,466int r1,467int g1,468int b1,469int r2,470int g2,471int b2,472const uint8_t alphaValues[4][4],473bool nonOpaquePunchThroughAlpha) const474{475const IntensityModifier *intensityModifier =476nonOpaquePunchThroughAlpha ? intensityModifierNonOpaque : intensityModifierDefault;477478R8G8B8A8 subblockColors0[4];479R8G8B8A8 subblockColors1[4];480for (size_t modifierIdx = 0; modifierIdx < 4; modifierIdx++)481{482const int i1 = intensityModifier[u.idht.mode.idm.cw1][modifierIdx];483subblockColors0[modifierIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1);484485const int i2 = intensityModifier[u.idht.mode.idm.cw2][modifierIdx];486subblockColors1[modifierIdx] = createRGBA(r2 + i2, g2 + i2, b2 + i2);487}488489if (u.idht.mode.idm.flipbit)490{491uint8_t *curPixel = dest;492for (size_t j = 0; j < 2 && (y + j) < h; j++)493{494R8G8B8A8 *row = reinterpret_cast<R8G8B8A8 *>(curPixel);495for (size_t i = 0; i < 4 && (x + i) < w; i++)496{497row[i] = subblockColors0[getIndex(i, j)];498row[i].A = alphaValues[j][i];499}500curPixel += destRowPitch;501}502for (size_t j = 2; j < 4 && (y + j) < h; j++)503{504R8G8B8A8 *row = reinterpret_cast<R8G8B8A8 *>(curPixel);505for (size_t i = 0; i < 4 && (x + i) < w; i++)506{507row[i] = subblockColors1[getIndex(i, j)];508row[i].A = alphaValues[j][i];509}510curPixel += destRowPitch;511}512}513else514{515uint8_t *curPixel = dest;516for (size_t j = 0; j < 4 && (y + j) < h; j++)517{518R8G8B8A8 *row = reinterpret_cast<R8G8B8A8 *>(curPixel);519for (size_t i = 0; i < 2 && (x + i) < w; i++)520{521row[i] = subblockColors0[getIndex(i, j)];522row[i].A = alphaValues[j][i];523}524for (size_t i = 2; i < 4 && (x + i) < w; i++)525{526row[i] = subblockColors1[getIndex(i, j)];527row[i].A = alphaValues[j][i];528}529curPixel += destRowPitch;530}531}532if (nonOpaquePunchThroughAlpha)533{534decodePunchThroughAlphaBlock(dest, x, y, w, h, destRowPitch);535}536}537538void decodeTBlock(uint8_t *dest,539size_t x,540size_t y,541size_t w,542size_t h,543size_t destRowPitch,544const uint8_t alphaValues[4][4],545bool nonOpaquePunchThroughAlpha) const546{547// Table C.8, distance index for T and H modes548const auto &block = u.idht.mode.tm;549550int r1 = extend_4to8bits(block.TR1a << 2 | block.TR1b);551int g1 = extend_4to8bits(block.TG1);552int b1 = extend_4to8bits(block.TB1);553int r2 = extend_4to8bits(block.TR2);554int g2 = extend_4to8bits(block.TG2);555int b2 = extend_4to8bits(block.TB2);556557static int distance[8] = {3, 6, 11, 16, 23, 32, 41, 64};558const int d = distance[block.Tda << 1 | block.Tdb];559560const R8G8B8A8 paintColors[4] = {561createRGBA(r1, g1, b1),562createRGBA(r2 + d, g2 + d, b2 + d),563createRGBA(r2, g2, b2),564createRGBA(r2 - d, g2 - d, b2 - d),565};566567uint8_t *curPixel = dest;568for (size_t j = 0; j < 4 && (y + j) < h; j++)569{570R8G8B8A8 *row = reinterpret_cast<R8G8B8A8 *>(curPixel);571for (size_t i = 0; i < 4 && (x + i) < w; i++)572{573row[i] = paintColors[getIndex(i, j)];574row[i].A = alphaValues[j][i];575}576curPixel += destRowPitch;577}578579if (nonOpaquePunchThroughAlpha)580{581decodePunchThroughAlphaBlock(dest, x, y, w, h, destRowPitch);582}583}584585void decodeHBlock(uint8_t *dest,586size_t x,587size_t y,588size_t w,589size_t h,590size_t destRowPitch,591const uint8_t alphaValues[4][4],592bool nonOpaquePunchThroughAlpha) const593{594// Table C.8, distance index for T and H modes595const auto &block = u.idht.mode.hm;596597int r1 = extend_4to8bits(block.HR1);598int g1 = extend_4to8bits(block.HG1a << 1 | block.HG1b);599int b1 = extend_4to8bits(block.HB1a << 3 | block.HB1b << 1 | block.HB1c);600int r2 = extend_4to8bits(block.HR2);601int g2 = extend_4to8bits(block.HG2a << 1 | block.HG2b);602int b2 = extend_4to8bits(block.HB2);603604static const int distance[8] = {3, 6, 11, 16, 23, 32, 41, 64};605const int orderingTrickBit =606((r1 << 16 | g1 << 8 | b1) >= (r2 << 16 | g2 << 8 | b2) ? 1 : 0);607const int d = distance[(block.Hda << 2) | (block.Hdb << 1) | orderingTrickBit];608609const R8G8B8A8 paintColors[4] = {610createRGBA(r1 + d, g1 + d, b1 + d),611createRGBA(r1 - d, g1 - d, b1 - d),612createRGBA(r2 + d, g2 + d, b2 + d),613createRGBA(r2 - d, g2 - d, b2 - d),614};615616uint8_t *curPixel = dest;617for (size_t j = 0; j < 4 && (y + j) < h; j++)618{619R8G8B8A8 *row = reinterpret_cast<R8G8B8A8 *>(curPixel);620for (size_t i = 0; i < 4 && (x + i) < w; i++)621{622row[i] = paintColors[getIndex(i, j)];623row[i].A = alphaValues[j][i];624}625curPixel += destRowPitch;626}627628if (nonOpaquePunchThroughAlpha)629{630decodePunchThroughAlphaBlock(dest, x, y, w, h, destRowPitch);631}632}633634void decodePlanarBlock(uint8_t *dest,635size_t x,636size_t y,637size_t w,638size_t h,639size_t pitch,640const uint8_t alphaValues[4][4]) const641{642int ro = extend_6to8bits(u.pblk.RO);643int go = extend_7to8bits(u.pblk.GO1 << 6 | u.pblk.GO2);644int bo =645extend_6to8bits(u.pblk.BO1 << 5 | u.pblk.BO2 << 3 | u.pblk.BO3a << 1 | u.pblk.BO3b);646int rh = extend_6to8bits(u.pblk.RH1 << 1 | u.pblk.RH2);647int gh = extend_7to8bits(u.pblk.GH);648int bh = extend_6to8bits(u.pblk.BHa << 5 | u.pblk.BHb);649int rv = extend_6to8bits(u.pblk.RVa << 3 | u.pblk.RVb);650int gv = extend_7to8bits(u.pblk.GVa << 2 | u.pblk.GVb);651int bv = extend_6to8bits(u.pblk.BV);652653uint8_t *curPixel = dest;654for (size_t j = 0; j < 4 && (y + j) < h; j++)655{656R8G8B8A8 *row = reinterpret_cast<R8G8B8A8 *>(curPixel);657658int ry = static_cast<int>(j) * (rv - ro) + 2;659int gy = static_cast<int>(j) * (gv - go) + 2;660int by = static_cast<int>(j) * (bv - bo) + 2;661for (size_t i = 0; i < 4 && (x + i) < w; i++)662{663row[i] = createRGBA(((static_cast<int>(i) * (rh - ro) + ry) >> 2) + ro,664((static_cast<int>(i) * (gh - go) + gy) >> 2) + go,665((static_cast<int>(i) * (bh - bo) + by) >> 2) + bo,666alphaValues[j][i]);667}668curPixel += pitch;669}670}671672// Index for individual, differential, H and T modes673size_t getIndex(size_t x, size_t y) const674{675size_t bitIndex = x * 4 + y;676size_t bitOffset = bitIndex & 7;677size_t lsb = (u.idht.pixelIndexLSB[1 - (bitIndex >> 3)] >> bitOffset) & 1;678size_t msb = (u.idht.pixelIndexMSB[1 - (bitIndex >> 3)] >> bitOffset) & 1;679return (msb << 1) | lsb;680}681682void decodePunchThroughAlphaBlock(uint8_t *dest,683size_t x,684size_t y,685size_t w,686size_t h,687size_t destRowPitch) const688{689uint8_t *curPixel = dest;690for (size_t j = 0; j < 4 && (y + j) < h; j++)691{692R8G8B8A8 *row = reinterpret_cast<R8G8B8A8 *>(curPixel);693for (size_t i = 0; i < 4 && (x + i) < w; i++)694{695if (getIndex(i, j) == 2) // msb == 1 && lsb == 0696{697row[i] = createRGBA(0, 0, 0, 0);698}699}700curPixel += destRowPitch;701}702}703704uint16_t RGB8ToRGB565(const R8G8B8A8 &rgba) const705{706return (static_cast<uint16_t>(rgba.R >> 3) << 11) |707(static_cast<uint16_t>(rgba.G >> 2) << 5) |708(static_cast<uint16_t>(rgba.B >> 3) << 0);709}710711uint32_t matchBC1Bits(const int *pixelIndices,712const int *pixelIndexCounts,713const R8G8B8A8 *subblockColors,714size_t numColors,715const R8G8B8A8 &minColor,716const R8G8B8A8 &maxColor,717bool nonOpaquePunchThroughAlpha) const718{719// Project each pixel on the (maxColor, minColor) line to decide which720// BC1 code to assign to it.721722uint8_t decodedColors[2][3] = {{maxColor.R, maxColor.G, maxColor.B},723{minColor.R, minColor.G, minColor.B}};724725int direction[3];726for (int ch = 0; ch < 3; ch++)727{728direction[ch] = decodedColors[0][ch] - decodedColors[1][ch];729}730731int stops[2];732for (int i = 0; i < 2; i++)733{734stops[i] = decodedColors[i][0] * direction[0] + decodedColors[i][1] * direction[1] +735decodedColors[i][2] * direction[2];736}737738ASSERT(numColors <= kNumPixelsInBlock);739740int encodedColors[kNumPixelsInBlock];741if (nonOpaquePunchThroughAlpha)742{743for (size_t i = 0; i < numColors; i++)744{745const int count = pixelIndexCounts[i];746if (count > 0)747{748// In non-opaque mode, 3 is for tranparent pixels.749750if (0 == subblockColors[i].A)751{752encodedColors[i] = 3;753}754else755{756const R8G8B8A8 &pixel = subblockColors[i];757const int dot = pixel.R * direction[0] + pixel.G * direction[1] +758pixel.B * direction[2];759const int factor = gl::clamp(760static_cast<int>(761(static_cast<float>(dot - stops[1]) / (stops[0] - stops[1])) * 2 +7620.5f),7630, 2);764switch (factor)765{766case 0:767encodedColors[i] = 0;768break;769case 1:770encodedColors[i] = 2;771break;772case 2:773default:774encodedColors[i] = 1;775break;776}777}778}779}780}781else782{783for (size_t i = 0; i < numColors; i++)784{785const int count = pixelIndexCounts[i];786if (count > 0)787{788// In opaque mode, the code is from 0 to 3.789790const R8G8B8A8 &pixel = subblockColors[i];791const int dot =792pixel.R * direction[0] + pixel.G * direction[1] + pixel.B * direction[2];793const int factor = gl::clamp(794static_cast<int>(795(static_cast<float>(dot - stops[1]) / (stops[0] - stops[1])) * 3 +7960.5f),7970, 3);798switch (factor)799{800case 0:801encodedColors[i] = 1;802break;803case 1:804encodedColors[i] = 3;805break;806case 2:807encodedColors[i] = 2;808break;809case 3:810default:811encodedColors[i] = 0;812break;813}814}815}816}817818uint32_t bits = 0;819for (int i = kNumPixelsInBlock - 1; i >= 0; i--)820{821bits <<= 2;822bits |= encodedColors[pixelIndices[i]];823}824825return bits;826}827828void packBC1(void *bc1,829const int *pixelIndices,830const int *pixelIndexCounts,831const R8G8B8A8 *subblockColors,832size_t numColors,833int minColorIndex,834int maxColorIndex,835bool nonOpaquePunchThroughAlpha) const836{837const R8G8B8A8 &minColor = subblockColors[minColorIndex];838const R8G8B8A8 &maxColor = subblockColors[maxColorIndex];839840uint32_t bits;841uint16_t max16 = RGB8ToRGB565(maxColor);842uint16_t min16 = RGB8ToRGB565(minColor);843if (max16 != min16)844{845// Find the best BC1 code for each pixel846bits = matchBC1Bits(pixelIndices, pixelIndexCounts, subblockColors, numColors, minColor,847maxColor, nonOpaquePunchThroughAlpha);848}849else850{851// Same colors, BC1 index 0 is the color in both opaque and transparent mode852bits = 0;853// BC1 index 3 is transparent854if (nonOpaquePunchThroughAlpha)855{856for (int i = 0; i < kNumPixelsInBlock; i++)857{858if (0 == subblockColors[pixelIndices[i]].A)859{860bits |= (3 << (i * 2));861}862}863}864}865866if (max16 < min16)867{868std::swap(max16, min16);869870uint32_t xorMask = 0;871if (nonOpaquePunchThroughAlpha)872{873// In transparent mode switching the colors is doing the874// following code swap: 0 <-> 1. 0xA selects the second bit of875// each code, bits >> 1 selects the first bit of the code when876// the seconds bit is set (case 2 and 3). We invert all the877// non-selected bits, that is the first bit when the code is878// 0 or 1.879xorMask = ~((bits >> 1) | 0xAAAAAAAA);880}881else882{883// In opaque mode switching the two colors is doing the884// following code swaps: 0 <-> 1 and 2 <-> 3. This is885// equivalent to flipping the first bit of each code886// (5 = 0b0101)887xorMask = 0x55555555;888}889bits ^= xorMask;890}891892struct BC1Block893{894uint16_t color0;895uint16_t color1;896uint32_t bits;897};898899// Encode the opaqueness in the order of the two BC1 colors900BC1Block *dest = reinterpret_cast<BC1Block *>(bc1);901if (nonOpaquePunchThroughAlpha)902{903dest->color0 = min16;904dest->color1 = max16;905}906else907{908dest->color0 = max16;909dest->color1 = min16;910}911dest->bits = bits;912}913914void transcodeIndividualBlockToBC1(uint8_t *dest,915size_t x,916size_t y,917size_t w,918size_t h,919const uint8_t alphaValues[4][4],920bool nonOpaquePunchThroughAlpha) const921{922const auto &block = u.idht.mode.idm.colors.indiv;923int r1 = extend_4to8bits(block.R1);924int g1 = extend_4to8bits(block.G1);925int b1 = extend_4to8bits(block.B1);926int r2 = extend_4to8bits(block.R2);927int g2 = extend_4to8bits(block.G2);928int b2 = extend_4to8bits(block.B2);929transcodeIndividualOrDifferentialBlockToBC1(dest, x, y, w, h, r1, g1, b1, r2, g2, b2,930alphaValues, nonOpaquePunchThroughAlpha);931}932933void transcodeDifferentialBlockToBC1(uint8_t *dest,934size_t x,935size_t y,936size_t w,937size_t h,938const uint8_t alphaValues[4][4],939bool nonOpaquePunchThroughAlpha) const940{941const auto &block = u.idht.mode.idm.colors.diff;942int b1 = extend_5to8bits(block.B);943int g1 = extend_5to8bits(block.G);944int r1 = extend_5to8bits(block.R);945int r2 = extend_5to8bits(block.R + block.dR);946int g2 = extend_5to8bits(block.G + block.dG);947int b2 = extend_5to8bits(block.B + block.dB);948transcodeIndividualOrDifferentialBlockToBC1(dest, x, y, w, h, r1, g1, b1, r2, g2, b2,949alphaValues, nonOpaquePunchThroughAlpha);950}951952void extractPixelIndices(int *pixelIndices,953int *pixelIndicesCounts,954size_t x,955size_t y,956size_t w,957size_t h,958bool flipbit,959size_t subblockIdx) const960{961size_t dxBegin = 0;962size_t dxEnd = 4;963size_t dyBegin = subblockIdx * 2;964size_t dyEnd = dyBegin + 2;965if (!flipbit)966{967std::swap(dxBegin, dyBegin);968std::swap(dxEnd, dyEnd);969}970971for (size_t j = dyBegin; j < dyEnd; j++)972{973int *row = &pixelIndices[j * 4];974for (size_t i = dxBegin; i < dxEnd; i++)975{976const size_t pixelIndex = subblockIdx * 4 + getIndex(i, j);977row[i] = static_cast<int>(pixelIndex);978pixelIndicesCounts[pixelIndex]++;979}980}981}982983void selectEndPointPCA(const int *pixelIndexCounts,984const R8G8B8A8 *subblockColors,985size_t numColors,986int *minColorIndex,987int *maxColorIndex) const988{989// determine color distribution990int mu[3], min[3], max[3];991for (int ch = 0; ch < 3; ch++)992{993int muv = 0;994int minv = 255;995int maxv = 0;996for (size_t i = 0; i < numColors; i++)997{998const int count = pixelIndexCounts[i];999if (count > 0)1000{1001const auto &pixel = subblockColors[i];1002if (pixel.A > 0)1003{1004// Non-transparent pixels1005muv += (&pixel.R)[ch] * count;1006minv = std::min<int>(minv, (&pixel.R)[ch]);1007maxv = std::max<int>(maxv, (&pixel.R)[ch]);1008}1009}1010}10111012mu[ch] = (muv + kNumPixelsInBlock / 2) / kNumPixelsInBlock;1013min[ch] = minv;1014max[ch] = maxv;1015}10161017// determine covariance matrix1018int cov[6] = {0, 0, 0, 0, 0, 0};1019for (size_t i = 0; i < numColors; i++)1020{1021const int count = pixelIndexCounts[i];1022if (count > 0)1023{1024const auto &pixel = subblockColors[i];1025if (pixel.A > 0)1026{1027int r = pixel.R - mu[0];1028int g = pixel.G - mu[1];1029int b = pixel.B - mu[2];10301031cov[0] += r * r * count;1032cov[1] += r * g * count;1033cov[2] += r * b * count;1034cov[3] += g * g * count;1035cov[4] += g * b * count;1036cov[5] += b * b * count;1037}1038}1039}10401041// Power iteration algorithm to get the eigenvalues and eigenvector10421043// Starts with diagonal vector1044float vfr = static_cast<float>(max[0] - min[0]);1045float vfg = static_cast<float>(max[1] - min[1]);1046float vfb = static_cast<float>(max[2] - min[2]);1047float eigenvalue = 0.0f;10481049constexpr size_t kPowerIterations = 4;1050for (size_t i = 0; i < kPowerIterations; i++)1051{1052float r = vfr * cov[0] + vfg * cov[1] + vfb * cov[2];1053float g = vfr * cov[1] + vfg * cov[3] + vfb * cov[4];1054float b = vfr * cov[2] + vfg * cov[4] + vfb * cov[5];10551056vfr = r;1057vfg = g;1058vfb = b;10591060eigenvalue = sqrt(r * r + g * g + b * b);1061if (eigenvalue > 0)1062{1063float invNorm = 1.0f / eigenvalue;1064vfr *= invNorm;1065vfg *= invNorm;1066vfb *= invNorm;1067}1068}10691070int vr, vg, vb;10711072static const float kDefaultLuminanceThreshold = 4.0f * 255;1073static const float kQuantizeRange = 512.0f;1074if (eigenvalue < kDefaultLuminanceThreshold) // too small, default to luminance1075{1076// Luminance weights defined by ITU-R Recommendation BT.601, scaled by 10001077vr = 299;1078vg = 587;1079vb = 114;1080}1081else1082{1083// From the eigenvalue and eigenvector, choose the axis to project1084// colors on. When projecting colors we want to do integer computations1085// for speed, so we normalize the eigenvector to the [0, 512] range.1086float magn = std::max(std::max(std::abs(vfr), std::abs(vfg)), std::abs(vfb));1087magn = kQuantizeRange / magn;1088vr = static_cast<int>(vfr * magn);1089vg = static_cast<int>(vfg * magn);1090vb = static_cast<int>(vfb * magn);1091}10921093// Pick colors at extreme points1094int minD = INT_MAX;1095int maxD = 0;1096size_t minIndex = 0;1097size_t maxIndex = 0;1098for (size_t i = 0; i < numColors; i++)1099{1100const int count = pixelIndexCounts[i];1101if (count > 0)1102{1103const auto &pixel = subblockColors[i];1104if (pixel.A > 0)1105{1106int dot = pixel.R * vr + pixel.G * vg + pixel.B * vb;1107if (dot < minD)1108{1109minD = dot;1110minIndex = i;1111}1112if (dot > maxD)1113{1114maxD = dot;1115maxIndex = i;1116}1117}1118}1119}11201121*minColorIndex = static_cast<int>(minIndex);1122*maxColorIndex = static_cast<int>(maxIndex);1123}11241125void transcodeIndividualOrDifferentialBlockToBC1(uint8_t *dest,1126size_t x,1127size_t y,1128size_t w,1129size_t h,1130int r1,1131int g1,1132int b1,1133int r2,1134int g2,1135int b2,1136const uint8_t alphaValues[4][4],1137bool nonOpaquePunchThroughAlpha) const1138{1139// A BC1 block has 2 endpoints, pixels is encoded as linear1140// interpolations of them. A ETC1/ETC2 individual or differential block1141// has 2 subblocks. Each subblock has one color and a modifier. We1142// select axis by principal component analysis (PCA) to use as1143// our two BC1 endpoints and then map pixels to BC1 by projecting on the1144// line between the two endpoints and choosing the right fraction.11451146// The goal of this algorithm is make it faster than decode ETC to RGBs1147// and then encode to BC. To achieve this, we only extract subblock1148// colors, pixel indices, and counts of each pixel indices from ETC.1149// With those information, we can only encode used subblock colors1150// to BC1, and copy the bits to the right pixels.1151// Fully decode and encode need to process 16 RGBA pixels. With this1152// algorithm, it's 8 pixels at maximum for a individual or1153// differential block. Saves us bandwidth and computations.11541155static const size_t kNumColors = 8;11561157const IntensityModifier *intensityModifier =1158nonOpaquePunchThroughAlpha ? intensityModifierNonOpaque : intensityModifierDefault;11591160// Compute the colors that pixels can have in each subblock both for1161// the decoding of the RGBA data and BC1 encoding1162R8G8B8A8 subblockColors[kNumColors];1163for (size_t modifierIdx = 0; modifierIdx < 4; modifierIdx++)1164{1165if (nonOpaquePunchThroughAlpha && (modifierIdx == 2))1166{1167// In ETC opaque punch through formats, individual and1168// differential blocks take index 2 as transparent pixel.1169// Thus we don't need to compute its color, just assign it1170// as black.1171subblockColors[modifierIdx] = createRGBA(0, 0, 0, 0);1172subblockColors[4 + modifierIdx] = createRGBA(0, 0, 0, 0);1173}1174else1175{1176const int i1 = intensityModifier[u.idht.mode.idm.cw1][modifierIdx];1177subblockColors[modifierIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1);11781179const int i2 = intensityModifier[u.idht.mode.idm.cw2][modifierIdx];1180subblockColors[4 + modifierIdx] = createRGBA(r2 + i2, g2 + i2, b2 + i2);1181}1182}11831184int pixelIndices[kNumPixelsInBlock];1185int pixelIndexCounts[kNumColors] = {0};1186// Extract pixel indices from a ETC block.1187for (size_t blockIdx = 0; blockIdx < 2; blockIdx++)1188{1189extractPixelIndices(pixelIndices, pixelIndexCounts, x, y, w, h, u.idht.mode.idm.flipbit,1190blockIdx);1191}11921193int minColorIndex, maxColorIndex;1194selectEndPointPCA(pixelIndexCounts, subblockColors, kNumColors, &minColorIndex,1195&maxColorIndex);11961197packBC1(dest, pixelIndices, pixelIndexCounts, subblockColors, kNumColors, minColorIndex,1198maxColorIndex, nonOpaquePunchThroughAlpha);1199}12001201void transcodeTBlockToBC1(uint8_t *dest,1202size_t x,1203size_t y,1204size_t w,1205size_t h,1206const uint8_t alphaValues[4][4],1207bool nonOpaquePunchThroughAlpha) const1208{1209static const size_t kNumColors = 4;12101211// Table C.8, distance index for T and H modes1212const auto &block = u.idht.mode.tm;12131214int r1 = extend_4to8bits(block.TR1a << 2 | block.TR1b);1215int g1 = extend_4to8bits(block.TG1);1216int b1 = extend_4to8bits(block.TB1);1217int r2 = extend_4to8bits(block.TR2);1218int g2 = extend_4to8bits(block.TG2);1219int b2 = extend_4to8bits(block.TB2);12201221static int distance[8] = {3, 6, 11, 16, 23, 32, 41, 64};1222const int d = distance[block.Tda << 1 | block.Tdb];12231224// In ETC opaque punch through formats, index == 2 means transparent pixel.1225// Thus we don't need to compute its color, just assign it as black.1226const R8G8B8A8 paintColors[kNumColors] = {1227createRGBA(r1, g1, b1),1228createRGBA(r2 + d, g2 + d, b2 + d),1229nonOpaquePunchThroughAlpha ? createRGBA(0, 0, 0, 0) : createRGBA(r2, g2, b2),1230createRGBA(r2 - d, g2 - d, b2 - d),1231};12321233int pixelIndices[kNumPixelsInBlock];1234int pixelIndexCounts[kNumColors] = {0};1235for (size_t j = 0; j < 4; j++)1236{1237int *row = &pixelIndices[j * 4];1238for (size_t i = 0; i < 4; i++)1239{1240const size_t pixelIndex = getIndex(i, j);1241row[i] = static_cast<int>(pixelIndex);1242pixelIndexCounts[pixelIndex]++;1243}1244}12451246int minColorIndex, maxColorIndex;1247selectEndPointPCA(pixelIndexCounts, paintColors, kNumColors, &minColorIndex,1248&maxColorIndex);12491250packBC1(dest, pixelIndices, pixelIndexCounts, paintColors, kNumColors, minColorIndex,1251maxColorIndex, nonOpaquePunchThroughAlpha);1252}12531254void transcodeHBlockToBC1(uint8_t *dest,1255size_t x,1256size_t y,1257size_t w,1258size_t h,1259const uint8_t alphaValues[4][4],1260bool nonOpaquePunchThroughAlpha) const1261{1262static const size_t kNumColors = 4;12631264// Table C.8, distance index for T and H modes1265const auto &block = u.idht.mode.hm;12661267int r1 = extend_4to8bits(block.HR1);1268int g1 = extend_4to8bits(block.HG1a << 1 | block.HG1b);1269int b1 = extend_4to8bits(block.HB1a << 3 | block.HB1b << 1 | block.HB1c);1270int r2 = extend_4to8bits(block.HR2);1271int g2 = extend_4to8bits(block.HG2a << 1 | block.HG2b);1272int b2 = extend_4to8bits(block.HB2);12731274static const int distance[8] = {3, 6, 11, 16, 23, 32, 41, 64};1275const int orderingTrickBit =1276((r1 << 16 | g1 << 8 | b1) >= (r2 << 16 | g2 << 8 | b2) ? 1 : 0);1277const int d = distance[(block.Hda << 2) | (block.Hdb << 1) | orderingTrickBit];12781279// In ETC opaque punch through formats, index == 2 means transparent pixel.1280// Thus we don't need to compute its color, just assign it as black.1281const R8G8B8A8 paintColors[kNumColors] = {1282createRGBA(r1 + d, g1 + d, b1 + d),1283createRGBA(r1 - d, g1 - d, b1 - d),1284nonOpaquePunchThroughAlpha ? createRGBA(0, 0, 0, 0)1285: createRGBA(r2 + d, g2 + d, b2 + d),1286createRGBA(r2 - d, g2 - d, b2 - d),1287};12881289int pixelIndices[kNumPixelsInBlock];1290int pixelIndexCounts[kNumColors] = {0};1291for (size_t j = 0; j < 4; j++)1292{1293int *row = &pixelIndices[j * 4];1294for (size_t i = 0; i < 4; i++)1295{1296const size_t pixelIndex = getIndex(i, j);1297row[i] = static_cast<int>(pixelIndex);1298pixelIndexCounts[pixelIndex]++;1299}1300}13011302int minColorIndex, maxColorIndex;1303selectEndPointPCA(pixelIndexCounts, paintColors, kNumColors, &minColorIndex,1304&maxColorIndex);13051306packBC1(dest, pixelIndices, pixelIndexCounts, paintColors, kNumColors, minColorIndex,1307maxColorIndex, nonOpaquePunchThroughAlpha);1308}13091310void transcodePlanarBlockToBC1(uint8_t *dest,1311size_t x,1312size_t y,1313size_t w,1314size_t h,1315const uint8_t alphaValues[4][4]) const1316{1317static const size_t kNumColors = kNumPixelsInBlock;13181319R8G8B8A8 rgbaBlock[kNumColors];1320decodePlanarBlock(reinterpret_cast<uint8_t *>(rgbaBlock), x, y, w, h, sizeof(R8G8B8A8) * 4,1321alphaValues);13221323// Planar block doesn't have a color table, fill indices as full1324int pixelIndices[kNumPixelsInBlock] = {0, 1, 2, 3, 4, 5, 6, 7,13258, 9, 10, 11, 12, 13, 14, 15};1326int pixelIndexCounts[kNumColors] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};13271328int minColorIndex, maxColorIndex;1329selectEndPointPCA(pixelIndexCounts, rgbaBlock, kNumColors, &minColorIndex, &maxColorIndex);13301331packBC1(dest, pixelIndices, pixelIndexCounts, rgbaBlock, kNumColors, minColorIndex,1332maxColorIndex, false);1333}13341335// Single channel utility functions1336int getSingleEACChannel(size_t x, size_t y, bool isSigned) const1337{1338int codeword = isSigned ? u.scblk.base_codeword.s : u.scblk.base_codeword.us;1339int multiplier = (u.scblk.multiplier == 0) ? 1 : u.scblk.multiplier * 8;1340return codeword * 8 + 4 + getSingleChannelModifier(x, y) * multiplier;1341}13421343int getSingleETC2Channel(size_t x, size_t y, bool isSigned) const1344{1345int codeword = isSigned ? u.scblk.base_codeword.s : u.scblk.base_codeword.us;1346return codeword + getSingleChannelModifier(x, y) * u.scblk.multiplier;1347}13481349int getSingleChannelIndex(size_t x, size_t y) const1350{1351ASSERT(x < 4 && y < 4);13521353// clang-format off1354switch (x * 4 + y)1355{1356case 0: return u.scblk.ma;1357case 1: return u.scblk.mb;1358case 2: return u.scblk.mc1 << 1 | u.scblk.mc2;1359case 3: return u.scblk.md;1360case 4: return u.scblk.me;1361case 5: return u.scblk.mf1 << 2 | u.scblk.mf2;1362case 6: return u.scblk.mg;1363case 7: return u.scblk.mh;1364case 8: return u.scblk.mi;1365case 9: return u.scblk.mj;1366case 10: return u.scblk.mk1 << 1 | u.scblk.mk2;1367case 11: return u.scblk.ml;1368case 12: return u.scblk.mm;1369case 13: return u.scblk.mn1 << 2 | u.scblk.mn2;1370case 14: return u.scblk.mo;1371case 15: return u.scblk.mp;1372default: UNREACHABLE(); return 0;1373}1374// clang-format on1375}13761377int getSingleChannelModifier(size_t x, size_t y) const1378{1379// clang-format off1380static const int modifierTable[16][8] =1381{1382{ -3, -6, -9, -15, 2, 5, 8, 14 },1383{ -3, -7, -10, -13, 2, 6, 9, 12 },1384{ -2, -5, -8, -13, 1, 4, 7, 12 },1385{ -2, -4, -6, -13, 1, 3, 5, 12 },1386{ -3, -6, -8, -12, 2, 5, 7, 11 },1387{ -3, -7, -9, -11, 2, 6, 8, 10 },1388{ -4, -7, -8, -11, 3, 6, 7, 10 },1389{ -3, -5, -8, -11, 2, 4, 7, 10 },1390{ -2, -6, -8, -10, 1, 5, 7, 9 },1391{ -2, -5, -8, -10, 1, 4, 7, 9 },1392{ -2, -4, -8, -10, 1, 3, 7, 9 },1393{ -2, -5, -7, -10, 1, 4, 6, 9 },1394{ -3, -4, -7, -10, 2, 3, 6, 9 },1395{ -1, -2, -3, -10, 0, 1, 2, 9 },1396{ -4, -6, -8, -9, 3, 5, 7, 8 },1397{ -3, -5, -7, -9, 2, 4, 6, 8 }1398};1399// clang-format on14001401return modifierTable[u.scblk.table_index][getSingleChannelIndex(x, y)];1402}1403};14041405// clang-format off1406static const uint8_t DefaultETCAlphaValues[4][4] =1407{1408{ 255, 255, 255, 255 },1409{ 255, 255, 255, 255 },1410{ 255, 255, 255, 255 },1411{ 255, 255, 255, 255 },1412};14131414// clang-format on1415void LoadR11EACToR8(size_t width,1416size_t height,1417size_t depth,1418const uint8_t *input,1419size_t inputRowPitch,1420size_t inputDepthPitch,1421uint8_t *output,1422size_t outputRowPitch,1423size_t outputDepthPitch,1424bool isSigned)1425{1426for (size_t z = 0; z < depth; z++)1427{1428for (size_t y = 0; y < height; y += 4)1429{1430const ETC2Block *sourceRow =1431priv::OffsetDataPointer<ETC2Block>(input, y / 4, z, inputRowPitch, inputDepthPitch);1432uint8_t *destRow =1433priv::OffsetDataPointer<uint8_t>(output, y, z, outputRowPitch, outputDepthPitch);14341435for (size_t x = 0; x < width; x += 4)1436{1437const ETC2Block *sourceBlock = sourceRow + (x / 4);1438uint8_t *destPixels = destRow + x;14391440sourceBlock->decodeAsSingleETC2Channel(destPixels, x, y, width, height, 1,1441outputRowPitch, isSigned);1442}1443}1444}1445}14461447void LoadRG11EACToRG8(size_t width,1448size_t height,1449size_t depth,1450const uint8_t *input,1451size_t inputRowPitch,1452size_t inputDepthPitch,1453uint8_t *output,1454size_t outputRowPitch,1455size_t outputDepthPitch,1456bool isSigned)1457{1458for (size_t z = 0; z < depth; z++)1459{1460for (size_t y = 0; y < height; y += 4)1461{1462const ETC2Block *sourceRow =1463priv::OffsetDataPointer<ETC2Block>(input, y / 4, z, inputRowPitch, inputDepthPitch);1464uint8_t *destRow =1465priv::OffsetDataPointer<uint8_t>(output, y, z, outputRowPitch, outputDepthPitch);14661467for (size_t x = 0; x < width; x += 4)1468{1469uint8_t *destPixelsRed = destRow + (x * 2);1470const ETC2Block *sourceBlockRed = sourceRow + (x / 2);1471sourceBlockRed->decodeAsSingleETC2Channel(destPixelsRed, x, y, width, height, 2,1472outputRowPitch, isSigned);14731474uint8_t *destPixelsGreen = destPixelsRed + 1;1475const ETC2Block *sourceBlockGreen = sourceBlockRed + 1;1476sourceBlockGreen->decodeAsSingleETC2Channel(destPixelsGreen, x, y, width, height, 2,1477outputRowPitch, isSigned);1478}1479}1480}1481}14821483void LoadR11EACToR16(size_t width,1484size_t height,1485size_t depth,1486const uint8_t *input,1487size_t inputRowPitch,1488size_t inputDepthPitch,1489uint8_t *output,1490size_t outputRowPitch,1491size_t outputDepthPitch,1492bool isSigned,1493bool isFloat)1494{1495for (size_t z = 0; z < depth; z++)1496{1497for (size_t y = 0; y < height; y += 4)1498{1499const ETC2Block *sourceRow =1500priv::OffsetDataPointer<ETC2Block>(input, y / 4, z, inputRowPitch, inputDepthPitch);1501uint16_t *destRow =1502priv::OffsetDataPointer<uint16_t>(output, y, z, outputRowPitch, outputDepthPitch);15031504for (size_t x = 0; x < width; x += 4)1505{1506const ETC2Block *sourceBlock = sourceRow + (x / 4);1507uint16_t *destPixels = destRow + x;15081509sourceBlock->decodeAsSingleEACChannel(destPixels, x, y, width, height, 1,1510outputRowPitch, isSigned, isFloat);1511}1512}1513}1514}15151516void LoadRG11EACToRG16(size_t width,1517size_t height,1518size_t depth,1519const uint8_t *input,1520size_t inputRowPitch,1521size_t inputDepthPitch,1522uint8_t *output,1523size_t outputRowPitch,1524size_t outputDepthPitch,1525bool isSigned,1526bool isFloat)1527{1528for (size_t z = 0; z < depth; z++)1529{1530for (size_t y = 0; y < height; y += 4)1531{1532const ETC2Block *sourceRow =1533priv::OffsetDataPointer<ETC2Block>(input, y / 4, z, inputRowPitch, inputDepthPitch);1534uint16_t *destRow =1535priv::OffsetDataPointer<uint16_t>(output, y, z, outputRowPitch, outputDepthPitch);15361537for (size_t x = 0; x < width; x += 4)1538{1539uint16_t *destPixelsRed = destRow + (x * 2);1540const ETC2Block *sourceBlockRed = sourceRow + (x / 2);1541sourceBlockRed->decodeAsSingleEACChannel(destPixelsRed, x, y, width, height, 2,1542outputRowPitch, isSigned, isFloat);15431544uint16_t *destPixelsGreen = destPixelsRed + 1;1545const ETC2Block *sourceBlockGreen = sourceBlockRed + 1;1546sourceBlockGreen->decodeAsSingleEACChannel(destPixelsGreen, x, y, width, height, 2,1547outputRowPitch, isSigned, isFloat);1548}1549}1550}1551}15521553void LoadETC2RGB8ToRGBA8(size_t width,1554size_t height,1555size_t depth,1556const uint8_t *input,1557size_t inputRowPitch,1558size_t inputDepthPitch,1559uint8_t *output,1560size_t outputRowPitch,1561size_t outputDepthPitch,1562bool punchthroughAlpha)1563{1564for (size_t z = 0; z < depth; z++)1565{1566for (size_t y = 0; y < height; y += 4)1567{1568const ETC2Block *sourceRow =1569priv::OffsetDataPointer<ETC2Block>(input, y / 4, z, inputRowPitch, inputDepthPitch);1570uint8_t *destRow =1571priv::OffsetDataPointer<uint8_t>(output, y, z, outputRowPitch, outputDepthPitch);15721573for (size_t x = 0; x < width; x += 4)1574{1575const ETC2Block *sourceBlock = sourceRow + (x / 4);1576uint8_t *destPixels = destRow + (x * 4);15771578sourceBlock->decodeAsRGB(destPixels, x, y, width, height, outputRowPitch,1579DefaultETCAlphaValues, punchthroughAlpha);1580}1581}1582}1583}15841585void LoadETC2RGB8ToBC1(size_t width,1586size_t height,1587size_t depth,1588const uint8_t *input,1589size_t inputRowPitch,1590size_t inputDepthPitch,1591uint8_t *output,1592size_t outputRowPitch,1593size_t outputDepthPitch,1594bool punchthroughAlpha)1595{1596for (size_t z = 0; z < depth; z++)1597{1598for (size_t y = 0; y < height; y += 4)1599{1600const ETC2Block *sourceRow =1601priv::OffsetDataPointer<ETC2Block>(input, y / 4, z, inputRowPitch, inputDepthPitch);1602uint8_t *destRow = priv::OffsetDataPointer<uint8_t>(output, y / 4, z, outputRowPitch,1603outputDepthPitch);16041605for (size_t x = 0; x < width; x += 4)1606{1607const ETC2Block *sourceBlock = sourceRow + (x / 4);1608uint8_t *destPixels = destRow + (x * 2);16091610sourceBlock->transcodeAsBC1(destPixels, x, y, width, height, DefaultETCAlphaValues,1611punchthroughAlpha);1612}1613}1614}1615}16161617void LoadETC2RGBA8ToRGBA8(size_t width,1618size_t height,1619size_t depth,1620const uint8_t *input,1621size_t inputRowPitch,1622size_t inputDepthPitch,1623uint8_t *output,1624size_t outputRowPitch,1625size_t outputDepthPitch,1626bool srgb)1627{1628uint8_t decodedAlphaValues[4][4];16291630for (size_t z = 0; z < depth; z++)1631{1632for (size_t y = 0; y < height; y += 4)1633{1634const ETC2Block *sourceRow =1635priv::OffsetDataPointer<ETC2Block>(input, y / 4, z, inputRowPitch, inputDepthPitch);1636uint8_t *destRow =1637priv::OffsetDataPointer<uint8_t>(output, y, z, outputRowPitch, outputDepthPitch);16381639for (size_t x = 0; x < width; x += 4)1640{1641const ETC2Block *sourceBlockAlpha = sourceRow + (x / 2);1642sourceBlockAlpha->decodeAsSingleETC2Channel(1643reinterpret_cast<uint8_t *>(decodedAlphaValues), x, y, width, height, 1, 4,1644false);16451646uint8_t *destPixels = destRow + (x * 4);1647const ETC2Block *sourceBlockRGB = sourceBlockAlpha + 1;1648sourceBlockRGB->decodeAsRGB(destPixels, x, y, width, height, outputRowPitch,1649decodedAlphaValues, false);1650}1651}1652}1653}16541655} // anonymous namespace16561657void LoadETC1RGB8ToRGBA8(size_t width,1658size_t height,1659size_t depth,1660const uint8_t *input,1661size_t inputRowPitch,1662size_t inputDepthPitch,1663uint8_t *output,1664size_t outputRowPitch,1665size_t outputDepthPitch)1666{1667LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1668outputRowPitch, outputDepthPitch, false);1669}16701671void LoadETC1RGB8ToBC1(size_t width,1672size_t height,1673size_t depth,1674const uint8_t *input,1675size_t inputRowPitch,1676size_t inputDepthPitch,1677uint8_t *output,1678size_t outputRowPitch,1679size_t outputDepthPitch)1680{1681LoadETC2RGB8ToBC1(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1682outputRowPitch, outputDepthPitch, false);1683}16841685void LoadEACR11ToR8(size_t width,1686size_t height,1687size_t depth,1688const uint8_t *input,1689size_t inputRowPitch,1690size_t inputDepthPitch,1691uint8_t *output,1692size_t outputRowPitch,1693size_t outputDepthPitch)1694{1695LoadR11EACToR8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1696outputRowPitch, outputDepthPitch, false);1697}16981699void LoadEACR11SToR8(size_t width,1700size_t height,1701size_t depth,1702const uint8_t *input,1703size_t inputRowPitch,1704size_t inputDepthPitch,1705uint8_t *output,1706size_t outputRowPitch,1707size_t outputDepthPitch)1708{1709LoadR11EACToR8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1710outputRowPitch, outputDepthPitch, true);1711}17121713void LoadEACRG11ToRG8(size_t width,1714size_t height,1715size_t depth,1716const uint8_t *input,1717size_t inputRowPitch,1718size_t inputDepthPitch,1719uint8_t *output,1720size_t outputRowPitch,1721size_t outputDepthPitch)1722{1723LoadRG11EACToRG8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1724outputRowPitch, outputDepthPitch, false);1725}17261727void LoadEACRG11SToRG8(size_t width,1728size_t height,1729size_t depth,1730const uint8_t *input,1731size_t inputRowPitch,1732size_t inputDepthPitch,1733uint8_t *output,1734size_t outputRowPitch,1735size_t outputDepthPitch)1736{1737LoadRG11EACToRG8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1738outputRowPitch, outputDepthPitch, true);1739}17401741void LoadEACR11ToR16(size_t width,1742size_t height,1743size_t depth,1744const uint8_t *input,1745size_t inputRowPitch,1746size_t inputDepthPitch,1747uint8_t *output,1748size_t outputRowPitch,1749size_t outputDepthPitch)1750{1751LoadR11EACToR16(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1752outputRowPitch, outputDepthPitch, false, false);1753}17541755void LoadEACR11SToR16(size_t width,1756size_t height,1757size_t depth,1758const uint8_t *input,1759size_t inputRowPitch,1760size_t inputDepthPitch,1761uint8_t *output,1762size_t outputRowPitch,1763size_t outputDepthPitch)1764{1765LoadR11EACToR16(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1766outputRowPitch, outputDepthPitch, true, false);1767}17681769void LoadEACRG11ToRG16(size_t width,1770size_t height,1771size_t depth,1772const uint8_t *input,1773size_t inputRowPitch,1774size_t inputDepthPitch,1775uint8_t *output,1776size_t outputRowPitch,1777size_t outputDepthPitch)1778{1779LoadRG11EACToRG16(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1780outputRowPitch, outputDepthPitch, false, false);1781}17821783void LoadEACRG11SToRG16(size_t width,1784size_t height,1785size_t depth,1786const uint8_t *input,1787size_t inputRowPitch,1788size_t inputDepthPitch,1789uint8_t *output,1790size_t outputRowPitch,1791size_t outputDepthPitch)1792{1793LoadRG11EACToRG16(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1794outputRowPitch, outputDepthPitch, true, false);1795}17961797void LoadEACR11ToR16F(size_t width,1798size_t height,1799size_t depth,1800const uint8_t *input,1801size_t inputRowPitch,1802size_t inputDepthPitch,1803uint8_t *output,1804size_t outputRowPitch,1805size_t outputDepthPitch)1806{1807LoadR11EACToR16(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1808outputRowPitch, outputDepthPitch, false, true);1809}18101811void LoadEACR11SToR16F(size_t width,1812size_t height,1813size_t depth,1814const uint8_t *input,1815size_t inputRowPitch,1816size_t inputDepthPitch,1817uint8_t *output,1818size_t outputRowPitch,1819size_t outputDepthPitch)1820{1821LoadR11EACToR16(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1822outputRowPitch, outputDepthPitch, true, true);1823}18241825void LoadEACRG11ToRG16F(size_t width,1826size_t height,1827size_t depth,1828const uint8_t *input,1829size_t inputRowPitch,1830size_t inputDepthPitch,1831uint8_t *output,1832size_t outputRowPitch,1833size_t outputDepthPitch)1834{1835LoadRG11EACToRG16(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1836outputRowPitch, outputDepthPitch, false, true);1837}18381839void LoadEACRG11SToRG16F(size_t width,1840size_t height,1841size_t depth,1842const uint8_t *input,1843size_t inputRowPitch,1844size_t inputDepthPitch,1845uint8_t *output,1846size_t outputRowPitch,1847size_t outputDepthPitch)1848{1849LoadRG11EACToRG16(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1850outputRowPitch, outputDepthPitch, true, true);1851}18521853void LoadETC2RGB8ToRGBA8(size_t width,1854size_t height,1855size_t depth,1856const uint8_t *input,1857size_t inputRowPitch,1858size_t inputDepthPitch,1859uint8_t *output,1860size_t outputRowPitch,1861size_t outputDepthPitch)1862{1863LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1864outputRowPitch, outputDepthPitch, false);1865}18661867void LoadETC2RGB8ToBC1(size_t width,1868size_t height,1869size_t depth,1870const uint8_t *input,1871size_t inputRowPitch,1872size_t inputDepthPitch,1873uint8_t *output,1874size_t outputRowPitch,1875size_t outputDepthPitch)1876{1877LoadETC2RGB8ToBC1(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1878outputRowPitch, outputDepthPitch, false);1879}18801881void LoadETC2SRGB8ToRGBA8(size_t width,1882size_t height,1883size_t depth,1884const uint8_t *input,1885size_t inputRowPitch,1886size_t inputDepthPitch,1887uint8_t *output,1888size_t outputRowPitch,1889size_t outputDepthPitch)1890{1891LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1892outputRowPitch, outputDepthPitch, false);1893}18941895void LoadETC2SRGB8ToBC1(size_t width,1896size_t height,1897size_t depth,1898const uint8_t *input,1899size_t inputRowPitch,1900size_t inputDepthPitch,1901uint8_t *output,1902size_t outputRowPitch,1903size_t outputDepthPitch)1904{1905LoadETC2RGB8ToBC1(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1906outputRowPitch, outputDepthPitch, false);1907}19081909void LoadETC2RGB8A1ToRGBA8(size_t width,1910size_t height,1911size_t depth,1912const uint8_t *input,1913size_t inputRowPitch,1914size_t inputDepthPitch,1915uint8_t *output,1916size_t outputRowPitch,1917size_t outputDepthPitch)1918{1919LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1920outputRowPitch, outputDepthPitch, true);1921}19221923void LoadETC2RGB8A1ToBC1(size_t width,1924size_t height,1925size_t depth,1926const uint8_t *input,1927size_t inputRowPitch,1928size_t inputDepthPitch,1929uint8_t *output,1930size_t outputRowPitch,1931size_t outputDepthPitch)1932{1933LoadETC2RGB8ToBC1(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1934outputRowPitch, outputDepthPitch, true);1935}19361937void LoadETC2SRGB8A1ToRGBA8(size_t width,1938size_t height,1939size_t depth,1940const uint8_t *input,1941size_t inputRowPitch,1942size_t inputDepthPitch,1943uint8_t *output,1944size_t outputRowPitch,1945size_t outputDepthPitch)1946{1947LoadETC2RGB8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1948outputRowPitch, outputDepthPitch, true);1949}19501951void LoadETC2SRGB8A1ToBC1(size_t width,1952size_t height,1953size_t depth,1954const uint8_t *input,1955size_t inputRowPitch,1956size_t inputDepthPitch,1957uint8_t *output,1958size_t outputRowPitch,1959size_t outputDepthPitch)1960{1961LoadETC2RGB8ToBC1(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1962outputRowPitch, outputDepthPitch, true);1963}19641965void LoadETC2RGBA8ToRGBA8(size_t width,1966size_t height,1967size_t depth,1968const uint8_t *input,1969size_t inputRowPitch,1970size_t inputDepthPitch,1971uint8_t *output,1972size_t outputRowPitch,1973size_t outputDepthPitch)1974{1975LoadETC2RGBA8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1976outputRowPitch, outputDepthPitch, false);1977}19781979void LoadETC2SRGBA8ToSRGBA8(size_t width,1980size_t height,1981size_t depth,1982const uint8_t *input,1983size_t inputRowPitch,1984size_t inputDepthPitch,1985uint8_t *output,1986size_t outputRowPitch,1987size_t outputDepthPitch)1988{1989LoadETC2RGBA8ToRGBA8(width, height, depth, input, inputRowPitch, inputDepthPitch, output,1990outputRowPitch, outputDepthPitch, true);1991}19921993} // namespace angle199419951996