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 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; } } }
void compressDXT1a(Color32 c, uint alphaMask, BlockDXT1 dxtBlock) { if (alphaMask == 0) { compressDXT1(c, dxtBlock); } else { dxtBlock.col0.r = OMatchAlpha5[c.r, 0]; dxtBlock.col0.g = OMatchAlpha6[c.g, 0]; dxtBlock.col0.b = OMatchAlpha5[c.b, 0]; dxtBlock.col1.r = OMatchAlpha5[c.r, 1]; dxtBlock.col1.g = OMatchAlpha6[c.g, 1]; dxtBlock.col1.b = OMatchAlpha5[c.b, 1]; dxtBlock.indices = 0xaaaaaaaa; // 0b1010..1010 if (dxtBlock.col0.u > dxtBlock.col1.u) { ushort u0tmp = dxtBlock.col0.u; dxtBlock.col0.u = dxtBlock.col1.u; dxtBlock.col1.u = dxtBlock.col0.u; } dxtBlock.indices |= alphaMask; } }
/*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 optimizeEndPoints3(Vector3[] block, BlockDXT1 dxtBlock) { float alpha2_sum = 0.0f; float beta2_sum = 0.0f; float alphabeta_sum = 0.0f; Vector3 alphax_sum = Vector3.zero; Vector3 betax_sum = Vector3.zero; for (int i = 0; i < 16; ++i) { uint bits = dxtBlock.indices >> (2 * i); float beta = (float)(bits & 1); if ((bits & 2) != 0) { beta = 0.5f; } float alpha = 1.0f - beta; alpha2_sum += alpha * alpha; beta2_sum += beta * beta; alphabeta_sum += alpha * beta; alphax_sum += alpha * block[i]; betax_sum += beta * block[i]; } float denom = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum; if (Mathf.Approximately(denom, 0.0f)) { return; } float factor = 1.0f / denom; Vector3 a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor; Vector3 b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor; a.x = Mathf.Clamp(a.x, 0, 255); a.y = Mathf.Clamp(a.y, 0, 255); a.z = Mathf.Clamp(a.z, 0, 255); b.x = Mathf.Clamp(b.x, 0, 255); b.y = Mathf.Clamp(b.y, 0, 255); b.z = Mathf.Clamp(b.z, 0, 255); ushort color0 = roundAndExpand(ref a); ushort color1 = roundAndExpand(ref b); if (color0 < color1) { swap(ref a, ref b); swap(ref color0, ref color1); } dxtBlock.col0 = new Color16(color1); dxtBlock.col1 = new Color16(color0); dxtBlock.indices = computeIndices3(block, a, b); }
void compressDXT1a(ColorBlock rgba, BlockDXT1 dxtBlock) { bool hasAlpha = false; for (uint i = 0; i < 16; i++) { if (rgba.color[i].a == 0) { hasAlpha = true; break; } } if (!hasAlpha) { compressDXT1(rgba, dxtBlock); } // @@ Handle single RGB, with varying alpha? We need tables for single color compressor in 3 color mode. //else if (rgba.isSingleColorNoAlpha()) { ... } else { // read block Vector3[] block = new Vector3[16]; uint num = extractColorBlockRGBA(rgba, block); // find min and max colors Vector3 maxColor = Vector3.zero, minColor = Vector3.zero; findMinMaxColorsBox(block, num, ref maxColor, ref minColor); selectDiagonal(block, num, ref maxColor, ref minColor); insetBBox(ref maxColor, ref minColor); ushort color0 = roundAndExpand(ref maxColor); ushort color1 = roundAndExpand(ref minColor); if (color0 < color1) { swap(ref maxColor, ref minColor); swap(ref color0, ref color1); } dxtBlock.col0 = new Color16(color1); dxtBlock.col1 = new Color16(color0); dxtBlock.indices = computeIndices3(block, maxColor, minColor); // optimizeEndPoints(block, dxtBlock); } }
void compressDXT1G(byte g, BlockDXT1 dxtBlock) { dxtBlock.col0.r = 31; dxtBlock.col0.g = OMatch6[g, 0]; dxtBlock.col0.b = 0; dxtBlock.col1.r = 31; dxtBlock.col1.g = OMatch6[g, 1]; dxtBlock.col1.b = 0; dxtBlock.indices = 0xaaaaaaaa; if (dxtBlock.col0.u < dxtBlock.col1.u) { ushort u0tmp = dxtBlock.col0.u; dxtBlock.col0.u = dxtBlock.col1.u; dxtBlock.col1.u = dxtBlock.col0.u; dxtBlock.indices ^= 0x55555555; } }
// Single color compressor, based on: // https://mollyrocket.com/forums/viewtopic.php?t=392 public static void compressDXT1(Color32 c, BlockDXT1 dxtBlock) { initSingleColorLookup(); dxtBlock.col0.r = OMatch5[c.r, 0]; dxtBlock.col0.g = OMatch6[c.g, 0]; dxtBlock.col0.b = OMatch5[c.b, 0]; dxtBlock.col1.r = OMatch5[c.r, 1]; dxtBlock.col1.g = OMatch6[c.g, 1]; dxtBlock.col1.b = OMatch5[c.b, 1]; dxtBlock.indices = 0xaaaaaaaa; if (dxtBlock.col0.u < dxtBlock.col1.u) { ushort u0tmp = dxtBlock.col0.u; dxtBlock.col0.u = dxtBlock.col1.u; dxtBlock.col1.u = dxtBlock.col0.u; dxtBlock.indices ^= 0x55555555; } }
void outputBlock3(ColorSet set, Vector3 start, Vector3 end, BlockDXT1 block) { Vector3 minColor = start * 255.0f; Vector3 maxColor = end * 255.0f; ushort color0 = roundAndExpand(ref minColor); ushort color1 = roundAndExpand(ref maxColor); if (color0 > color1) { swap(ref maxColor, ref minColor); swap(ref color0, ref color1); } block.col0 = new Color16(color0); block.col1 = new Color16(color1); block.indices = computeIndices3(set, maxColor / 255.0f, minColor / 255.0f); //optimizeEndPoints3(set, block); }
/*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); }
/*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 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; }
// 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); }
public static void compressDXT1(ColorBlock rgba, BlockDXT1 dxtBlock) { if (rgba.isSingleColor()) { OptimalCompress.compressDXT1(rgba.color[0], dxtBlock); } else { // read block Vector3[] block = new Vector3[16]; extractColorBlockRGB(rgba, block); #if true // find min and max colors Vector3 maxColor = Vector3.zero, minColor = Vector3.zero; findMinMaxColorsBox(block, 16, ref maxColor, ref minColor); selectDiagonal(block, 16, ref maxColor, ref minColor); insetBBox(ref maxColor, ref minColor); #else float[] weights = new float[16] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; Vector3[] cluster = new Vector3[4]; int count = Fitting.Compute4Means(16, block, weights, Vector3.one, cluster); Vector3 maxColor, minColor; float bestError = FLT_MAX; for (int i = 1; i < 4; i++) { for (int j = 0; j < i; j++) { uint16 color0 = roundAndExpand(&cluster[i]); uint16 color1 = roundAndExpand(&cluster[j]); float error = evaluatePaletteError4(block, cluster[i], cluster[j]); if (error < bestError) { bestError = error; maxColor = cluster[i]; minColor = cluster[j]; } } } #endif ushort color0 = roundAndExpand(ref maxColor); ushort color1 = roundAndExpand(ref minColor); if (color0 < color1) { swap(ref maxColor, ref minColor); swap(ref color0, ref color1); } dxtBlock.col0 = new Color16(color0); dxtBlock.col1 = new Color16(color1); dxtBlock.indices = computeIndices4(block, maxColor, minColor); optimizeEndPoints4(block, dxtBlock); } }