public static void GetDXT(Texture2D texture, int i, byte[] bytes, TextureFormat format) { Color32[] colors = texture.GetPixels32(i); uint w = (uint) texture.width>>i; uint h = (uint) texture.height>>i; ColorBlock rgba = new ColorBlock(); BlockDXT1 block1 = new BlockDXT1(); BlockDXT5 block5 = new BlockDXT5(); int blocksize = format == TextureFormat.DXT1 ? 8 : 16; int index = 0; for (uint y = 0; y < h; y += 4) { for (uint x = 0; x < w; x += 4) { rgba.init(w, h, colors, x, y); if (format == TextureFormat.DXT1) { QuickCompress.compressDXT1(rgba, block1); block1.WriteBytes(bytes, index); } else { QuickCompress.compressDXT5(rgba, block5, 0); block5.WriteBytes(bytes, index); } index += blocksize; } } }
public void init(ColorBlock src, uint channel) { // Colors are in BGRA format. if (channel == 0) channel = 2; else if (channel == 2) channel = 0; for (uint i = 0; i < 16; i++) { alpha[i] = src.color[i].component()[channel]; weights[i] = 1.0f; } }
void compressDXT5A_RGBM(ColorSet src, ColorBlock RGB, AlphaBlockDXT5 dst) { byte mina = 255; byte maxa = 0; byte mina_no01 = 255; byte maxa_no01 = 0; // Get min/max alpha. /*for (uint i = 0; i < 16; i++) { byte alpha = src.alpha[i]; mina = min(mina, alpha); maxa = max(maxa, alpha); if (alpha != 0 && alpha != 255) { mina_no01 = min(mina_no01, alpha); maxa_no01 = max(maxa_no01, alpha); } }*/ mina = 0; maxa = 255; mina_no01 = 0; maxa_no01 = 255; /*if (maxa - mina < 8) { dst.alpha0 = maxa; dst.alpha1 = mina; nvDebugCheck(computeAlphaError(src, dst) == 0); } else if (maxa_no01 - mina_no01 < 6) { dst.alpha0 = mina_no01; dst.alpha1 = maxa_no01; nvDebugCheck(computeAlphaError(src, dst) == 0); } else*/ { float besterror = computeAlphaError_RGBM(src, RGB, dst); byte besta0 = maxa; byte besta1 = mina; // Expand search space a bit. int alphaExpand = 8; mina = (byte)((mina <= alphaExpand) ? 0 : mina - alphaExpand); maxa = (byte)((maxa >= 255 - alphaExpand) ? 255 : maxa + alphaExpand); for (byte a0 = (byte)(mina + 9); a0 < maxa; a0++) { for (byte a1 = mina; a1 < a0 - 8; a1++) { dst.alpha0 = a0; dst.alpha1 = a1; float error = computeAlphaError_RGBM(src, RGB, dst, besterror); if (error < besterror) { besterror = error; besta0 = a0; besta1 = a1; } } } // Try using the 6 step encoding. /*if (mina == 0 || maxa == 255)*/ { // Expand search space a bit. alphaExpand = 6; mina_no01 = (byte)((mina_no01 <= alphaExpand) ? 0 : mina_no01 - alphaExpand); maxa_no01 = (byte)((maxa_no01 >= 255 - alphaExpand) ? 255 : maxa_no01 + alphaExpand); for (byte a0 = (byte)(mina_no01 + 9); a0 < maxa_no01; a0++) { for (byte a1 = mina_no01; a1 < a0 - 8; a1++) { dst.alpha0 = a1; dst.alpha1 = a0; float error = computeAlphaError_RGBM(src, RGB, dst, besterror); if (error < besterror) { besterror = error; besta0 = a1; besta1 = a0; } } } } dst.alpha0 = besta0; dst.alpha1 = besta1; } computeAlphaIndices_RGBM(src, RGB, dst); }
void compressDXT5A(ColorBlock src, AlphaBlockDXT5 dst) { AlphaBlock4x4 tmp = new AlphaBlock4x4(); tmp.init(src, 3); compressDXT5A(tmp, dst); }
/*void OptimalCompress::initLumaTables() { // For all possible color pairs: for (int c0 = 0; c0 < 65536; c0++) { for (int c1 = 0; c1 < 65536; c1++) { // Compute } } for (int r = 0; r < 1<<5; r++) { for (int g = 0; g < 1<<6; g++) { for (int b = 0; b < 1<<5; b++) { } } } }*/ // Brute force Luma compressor void compressDXT1_Luma(ColorBlock rgba, BlockDXT1 block) { // F_YR = 19595/65536.0f, F_YG = 38470/65536.0f, F_YB = 7471/65536.0f; // 195841 //if ( /* byte ming = 63; byte maxg = 0; bool isSingleColor = true; byte singleColor = rgba.color(0).g; // Get min/max green. for (uint i = 0; i < 16; i++) { byte green = (rgba.color[i].g + 1) >> 2; ming = min(ming, green); maxg = max(maxg, green); if (rgba.color[i].g != singleColor) isSingleColor = false; } if (isSingleColor) { compressDXT1G(singleColor, block); return; } block.col0.r = 31; block.col1.r = 31; block.col0.g = maxg; block.col1.g = ming; block.col0.b = 0; block.col1.b = 0; int bestError = computeGreenError(rgba, block); int bestg0 = maxg; int bestg1 = ming; // Expand search space a bit. int greenExpand = 4; ming = (ming <= greenExpand) ? 0 : ming - greenExpand; maxg = (maxg >= 63-greenExpand) ? 63 : maxg + greenExpand; for (int g0 = ming+1; g0 <= maxg; g0++) { for (int g1 = ming; g1 < g0; g1++) { block.col0.g = g0; block.col1.g = g1; int error = computeGreenError(rgba, block, bestError); if (error < bestError) { bestError = error; bestg0 = g0; bestg1 = g1; } } } block.col0.g = bestg0; block.col1.g = bestg1; nvDebugCheck(bestg0 == bestg1 || block.isFourColorMode()); */ Color32[] palette = new Color32[4]; block.evaluatePalette(palette, false); // @@ Use target decoder. block.indices = computeGreenIndices(rgba, palette); }
// Brute force green channel compressor void compressDXT1G(ColorBlock rgba, BlockDXT1 block) { byte ming = 63; byte maxg = 0; bool isSingleColor = true; byte singleColor = rgba.color[0].g; // Get min/max green. for (uint i = 0; i < 16; i++) { byte green = (byte)((rgba.color[i].g + 1) >> 2); ming = Math.Min(ming, green); maxg = Math.Max(maxg, green); if (rgba.color[i].g != singleColor) isSingleColor = false; } if (isSingleColor) { compressDXT1G(singleColor, block); return; } block.col0.r = 31; block.col1.r = 31; block.col0.g = maxg; block.col1.g = ming; block.col0.b = 0; block.col1.b = 0; uint bestError = computeGreenError(rgba, block); byte bestg0 = maxg; byte bestg1 = ming; // Expand search space a bit. int greenExpand = 4; ming = (byte)((ming <= greenExpand) ? 0 : ming - greenExpand); maxg = (byte)((maxg >= 63 - greenExpand) ? 63 : maxg + greenExpand); for (byte g0 = (byte)(ming + 1); g0 <= maxg; g0++) { for (byte g1 = ming; g1 < g0; g1++) { block.col0.g = g0; block.col1.g = g1; uint error = computeGreenError(rgba, block, bestError); if (error < bestError) { bestError = error; bestg0 = g0; bestg1 = g1; } } } block.col0.g = bestg0; block.col1.g = bestg1; Color32[] palette = new Color32[4]; block.evaluatePalette(palette, false); // @@ Use target decoder. block.indices = computeGreenIndices(rgba, palette); }
static uint computeGreenIndices(ColorBlock rgba, Color32[] palette) { int color0 = palette[0].g; int color1 = palette[1].g; int color2 = palette[2].g; int color3 = palette[3].g; uint indices = 0; for (uint i = 0; i < 16; i++) { int color = rgba.color[i].g; uint d0 = greenDistance(color0, color); uint d1 = greenDistance(color1, color); uint d2 = greenDistance(color2, color); uint d3 = greenDistance(color3, color); uint b0 = d0 > d3 ? (uint)1 : (uint)0; uint b1 = d1 > d2 ? (uint)1 : (uint)0; uint b2 = d0 > d2 ? (uint)1 : (uint)0; uint b3 = d1 > d3 ? (uint)1 : (uint)0; uint b4 = d2 > d3 ? (uint)1 : (uint)0; uint x0 = b1 & b2; uint x1 = b0 & b3; uint x2 = b0 & b4; indices |= (x2 | ((x0 | x1) << 1)) << (int)(2 * i); } return indices; }
/*static uint nearestGreen4(uint green, uint maxGreen, uint minGreen) { uint bias = maxGreen + (maxGreen - minGreen) / 6; uint index = 0; if (maxGreen - minGreen != 0) index = clamp(3 * (bias - green) / (maxGreen - minGreen), 0U, 3U); return (index * minGreen + (3 - index) * maxGreen) / 3; }*/ static uint computeGreenError(ColorBlock rgba, BlockDXT1 block, uint bestError = uint.MaxValue) { // uint g0 = (block.col0.g << 2) | (block.col0.g >> 4); // uint g1 = (block.col1.g << 2) | (block.col1.g >> 4); int[] palette = new int[4]; palette[0] = (block.col0.g << 2) | (block.col0.g >> 4); palette[1] = (block.col1.g << 2) | (block.col1.g >> 4); palette[2] = (2 * palette[0] + palette[1]) / 3; palette[3] = (2 * palette[1] + palette[0]) / 3; uint totalError = 0; for (uint i = 0; i < 16; i++) { int green = rgba.color[i].g; uint error = greenDistance(green, palette[0]); error = Math.Min(error, greenDistance(green, palette[1])); error = Math.Min(error, greenDistance(green, palette[2])); error = Math.Min(error, greenDistance(green, palette[3])); totalError += error; // totalError += nearestGreen4(green, g0, g1); if (totalError > bestError) { // early out return totalError; } } return totalError; }
static void computeAlphaIndices_RGBM(ColorSet src, ColorBlock RGB, AlphaBlockDXT5 dst) { byte[] alphas = new byte[8]; dst.evaluatePalette(alphas, /*d3d9=*/false); // @@ Use target decoder. for (uint i = 0; i < 16; i++) { float R = src.color[i].x; float G = src.color[i].y; float B = src.color[i].z; float r = (float)(RGB.color[i].r) / 255.0f; float g = (float)(RGB.color[i].g) / 255.0f; float b = (float)(RGB.color[i].b) / 255.0f; float minDist = float.MaxValue; uint bestIndex = 8; for (uint p = 0; p < 8; p++) { // Compute M. float M = (float)(alphas[p]) / 255.0f * (1 - threshold) + threshold; // Decode color. float fr = r * M; float fg = g * M; float fb = b * M; // Measure error. float error = Mathf.Pow(R - fr, 2) + Mathf.Pow(G - fg, 2) + Mathf.Pow(B - fb, 2); if (error < minDist) { minDist = error; bestIndex = p; } } dst.setIndex(i, bestIndex); } }
static float computeAlphaError_RGBM(ColorSet src, ColorBlock RGB, AlphaBlockDXT5 dst, float bestError = float.MaxValue) { byte[] alphas = new byte[8]; dst.evaluatePalette(alphas, /*d3d9=*/false); // @@ Use target decoder. float totalError = 0; for (uint i = 0; i < 16; i++) { float R = src.color[i].x; float G = src.color[i].y; float B = src.color[i].z; float r = (float)(RGB.color[i].r) / 255.0f; float g = (float)(RGB.color[i].g) / 255.0f; float b = (float)(RGB.color[i].b) / 255.0f; float minDist = float.MaxValue; for (uint p = 0; p < 8; p++) { // Compute M. float M = (float)(alphas[p]) / 255.0f * (1 - threshold) + threshold; // Decode color. float fr = r * M; float fg = g * M; float fb = b * M; // Measure error. float error = Mathf.Pow(R - fr, 2) + Mathf.Pow(G - fg, 2) + Mathf.Pow(B - fb, 2); minDist = Mathf.Min(error, minDist); } totalError += minDist * src.weights[i]; if (totalError > bestError) { // early out return totalError; } } return totalError; }