static RGBColour ReadColourFromTexel(byte[] texel, int i, bool premultiply) { // Pull out rgb from texel // Create current pixel colour RGBColour current = new RGBColour(); // Check that texel is big enough if (i + 3 >= texel.Length) return current; // Fully transparent colour current.a = texel[i + 3] / 255f; current.r = (texel[i + 2] / 255f) * (premultiply ? current.a : 1.0f); current.g = (texel[i + 1] / 255f) * (premultiply ? current.a : 1.0f); current.b = (texel[i] / 255f) * (premultiply ? current.a : 1.0f); return current; }
static uint Encode565(RGBColour colour) { RGBColour temp = new RGBColour() { r = (colour.r < 0f) ? 0f : (colour.r > 1f) ? 1f : colour.r, g = (colour.g < 0f) ? 0f : (colour.g > 1f) ? 1f : colour.g, b = (colour.b < 0f) ? 0f : (colour.b > 1f) ? 1f : colour.b }; return (uint)(temp.r * 31f + 0.5f) << 11 | (uint)(temp.g * 63f + 0.5f) << 5 | (uint)(temp.b * 31f + 0.5f); }
private static RGBColour[] OptimiseRGB(RGBColour[] Colour, int uSteps) { float[] pC = uSteps == 3 ? pC3 : pC4; float[] pD = uSteps == 3 ? pD3 : pD4; // Find min max RGBColour X = Luminance; RGBColour Y = new RGBColour(); for (int i = 0; i < Colour.Length; i++) { RGBColour current = Colour[i]; // X = min, Y = max if (current.r < X.r) X.r = current.r; if (current.g < X.g) X.g = current.g; if (current.b < X.b) X.b = current.b; if (current.r > Y.r) Y.r = current.r; if (current.g > Y.g) Y.g = current.g; if (current.b > Y.b) Y.b = current.b; } // Diagonal axis - starts with difference between min and max RGBColour diag = new RGBColour() { r = Y.r - X.r, g = Y.g - X.g, b = Y.b - X.b }; float fDiag = diag.r * diag.r + diag.g * diag.g + diag.b * diag.b; if (fDiag < 1.175494351e-38F) { RGBColour min1 = new RGBColour() { r = X.r, g = X.g, b = X.b }; RGBColour max1 = new RGBColour() { r = Y.r, g = Y.g, b = Y.b }; return new RGBColour[] { min1, max1 }; } float FdiagInv = 1f / fDiag; RGBColour Dir = new RGBColour() { r = diag.r * FdiagInv, g = diag.g * FdiagInv, b = diag.b * FdiagInv }; RGBColour Mid = new RGBColour() { r = (X.r + Y.r) * .5f, g = (X.g + Y.g) * .5f, b = (X.b + Y.b) * .5f }; float[] fDir = new float[4]; for (int i = 0; i < Colour.Length; i++) { RGBColour pt = new RGBColour() { r = Dir.r * (Colour[i].r - Mid.r), g = Dir.g * (Colour[i].g - Mid.g), b = Dir.b * (Colour[i].b - Mid.b) }; float f = 0; f = pt.r + pt.g + pt.b; fDir[0] += f * f; f = pt.r + pt.g - pt.b; fDir[1] += f * f; f = pt.r - pt.g + pt.b; fDir[2] += f * f; f = pt.r - pt.g - pt.b; fDir[3] += f * f; } float fDirMax = fDir[0]; int iDirMax = 0; for (int iDir = 1; iDir < 4; iDir++) { if (fDir[iDir] > fDirMax) { fDirMax = fDir[iDir]; iDirMax = iDir; } } if ((iDirMax & 2) != 0) { float f = X.g; X.g = Y.g; Y.g = f; } if ((iDirMax & 1) != 0) { float f = X.b; X.b = Y.b; Y.b = f; } if (fDiag < 1f / 4096f) { RGBColour min1 = new RGBColour() { r = X.r, g = X.g, b = X.b }; RGBColour max1 = new RGBColour() { r = Y.r, g = Y.g, b = Y.b }; return new RGBColour[] { min1, max1 }; } // newtons method for local min of sum of squares error. float fsteps = uSteps - 1; for (int iteration = 0; iteration < 8; iteration++) { RGBColour[] pSteps = new RGBColour[4]; for (int iStep = 0; iStep < uSteps; iStep++) { pSteps[iStep].r = X.r * pC[iStep] + Y.r * pD[iStep]; pSteps[iStep].g = X.g * pC[iStep] + Y.g * pD[iStep]; pSteps[iStep].b = X.b * pC[iStep] + Y.b * pD[iStep]; } // colour direction Dir.r = Y.r - X.r; Dir.g = Y.g - X.g; Dir.b = Y.b - X.b; float fLen = Dir.r * Dir.r + Dir.g * Dir.g + Dir.b * Dir.b; if (fLen < (1f / 4096f)) break; float fScale = fsteps / fLen; Dir.r *= fScale; Dir.g *= fScale; Dir.b *= fScale; // Evaluate function and derivatives float d2X = 0, d2Y = 0; RGBColour dX, dY; dX = new RGBColour(); dY = new RGBColour(); for (int i = 0; i < Colour.Length; i++) { RGBColour current = Colour[i]; float fDot = (current.r - X.r) * Dir.r + (current.g - X.g) * Dir.g + (current.b - X.b) * Dir.b; int iStep = 0; if (fDot <= 0) iStep = 0; else if (fDot >= fsteps) iStep = uSteps - 1; else iStep = (int)(fDot + .5f); RGBColour diff = new RGBColour() { r = pSteps[iStep].r - current.r, g = pSteps[iStep].g - current.g, b = pSteps[iStep].b - current.b }; float fC = pC[iStep] * 1f / 8f; float fD = pD[iStep] * 1f / 8f; d2X += fC * pC[iStep]; dX.r += fC * diff.r; dX.g += fC * diff.g; dX.b += fC * diff.b; d2Y += fD * pD[iStep]; dY.r += fD * diff.r; dY.g += fD * diff.g; dY.b += fD * diff.b; } // Move endpoints if (d2X > 0f) { float f = -1f / d2X; X.r += dX.r * f; X.g += dX.g * f; X.b += dX.b * f; } if (d2Y > 0f) { float f = -1f / d2Y; Y.r += dY.r * f; Y.g += dY.g * f; Y.b += dY.b * f; } float fEpsilon = (0.25f / 64.0f) * (0.25f / 64.0f); if ((dX.r * dX.r < fEpsilon) && (dX.g * dX.g < fEpsilon) && (dX.b * dX.b < fEpsilon) && (dY.r * dY.r < fEpsilon) && (dY.g * dY.g < fEpsilon) && (dY.b * dY.b < fEpsilon)) { break; } } RGBColour min = new RGBColour() { r = X.r, g = X.g, b = X.b }; RGBColour max = new RGBColour() { r = Y.r, g = Y.g, b = Y.b }; return new RGBColour[] { min, max }; }
static void DoSomeDithering(RGBColour current, int index, RGBColour[] InnerColour, int InnerIndex, RGBColour[] Error) { if (true) // Dither { // Calculate difference between current pixel colour and adapted pixel colour? RGBColour diff = new RGBColour() { r = current.a * (byte)(current.r - InnerColour[InnerIndex].r), g = current.a * (byte)(current.g - InnerColour[InnerIndex].g), b = current.a * (byte)(current.b - InnerColour[InnerIndex].b) }; // If current pixel is not at the end of a row if ((index & 3) != 3) { Error[index + 1].r += diff.r * (7f / 16f); Error[index + 1].g += diff.g * (7f / 16f); Error[index + 1].b += diff.b * (7f / 16f); } // If current pixel is not in bottom row if (index < 12) { // If current pixel IS at end of row if ((index & 3) != 0) { Error[index + 3].r += diff.r * (3f / 16f); Error[index + 3].g += diff.g * (3f / 16f); Error[index + 3].b += diff.b * (3f / 16f); } Error[index + 4].r += diff.r * (5f / 16f); Error[index + 4].g += diff.g * (5f / 16f); Error[index + 4].b += diff.b * (5f / 16f); // If current pixel is not at end of row if ((index & 3) != 3) { Error[index + 5].r += diff.r * (1f / 16f); Error[index + 5].g += diff.g * (1f / 16f); Error[index + 5].b += diff.b * (1f / 16f); } } } }
static RGBColour[] DoSomethingWithPalette(int uSteps, uint wColourA, uint wColourB, RGBColour ColourA, RGBColour ColourB) { // Create palette colours RGBColour[] step = new RGBColour[4]; if ((uSteps == 3) == (wColourA <= wColourB)) { step[0] = ColourA; step[1] = ColourB; } else { step[0] = ColourB; step[1] = ColourA; } if (uSteps == 3) { step[2].r = step[0].r + (1f / 2f) * (step[1].r - step[0].r); step[2].g = step[0].g + (1f / 2f) * (step[1].g - step[0].g); step[2].b = step[0].b + (1f / 2f) * (step[1].b - step[0].b); } else { // "step" appears to be the palette as this is the interpolation step[2].r = step[0].r + (1f / 3f) * (step[1].r - step[0].r); step[2].g = step[0].g + (1f / 3f) * (step[1].g - step[0].g); step[2].b = step[0].b + (1f / 3f) * (step[1].b - step[0].b); step[3].r = step[0].r + (2f / 3f) * (step[1].r - step[0].r); step[3].g = step[0].g + (2f / 3f) * (step[1].g - step[0].g); step[3].b = step[0].b + (2f / 3f) * (step[1].b - step[0].b); } return step; }
/// <summary> /// Not exactly sure what this does or why. /// </summary> static void DoColourFixErrorCorrection(RGBColour[] Colour, byte[] imgData, int sourcePosition, int sourceLineLength, AlphaSettings alphaSetting) { RGBColour[] Error = new RGBColour[16]; for (int i = 0; i < 4; i++) { int position = sourcePosition + sourceLineLength * i; for (int j = 0; j < 4; j++) { int index = (i << 2) + j; RGBColour current = ReadColourFromTexel(imgData, position, alphaSetting == AlphaSettings.Premultiply); if (true) // Dither { // Adjust for accumulated error // This works by figuring out the error between the current pixel colour and the adjusted colour? Dunno what the adjustment is. Looks like a 5:6:5 range adaptation // Then, this error is distributed across the "next" few pixels and not the previous. current.r += Error[index].r; current.g += Error[index].g; current.b += Error[index].b; } // 5:6:5 range adaptation? Colour[index].r = (int)(current.r * 31f + .5f) * (1f / 31f); Colour[index].g = (int)(current.g * 63f + .5f) * (1f / 63f); Colour[index].b = (int)(current.b * 31f + .5f) * (1f / 31f); DoSomeDithering(current, index, Colour, index, Error); Colour[index].r *= Luminance.r; Colour[index].g *= Luminance.g; Colour[index].b *= Luminance.b; position += 4; } } }
static uint DoOtherColourFixErrorCorrection(byte[] imgData, int sourcePosition, int sourceLineLength, int uSteps, double alphaRef, AlphaSettings alphaSetting, RGBColour[] step, RGBColour Dir) { uint dw = 0; RGBColour[] Error = new RGBColour[16]; uint[] psteps = uSteps == 3 ? psteps3 : psteps4; for (int i = 0; i < 4; i++) { int position = sourcePosition + sourceLineLength * i; for (int j = 0; j < 4; j++) { int index = (i << 2) + j; RGBColour current = ReadColourFromTexel(imgData, position, alphaSetting == AlphaSettings.Premultiply); if ((uSteps == 3) && (current.a < alphaRef)) { dw = (uint)((3 << 30) | (dw >> 2)); continue; } current.r *= Luminance.r; current.g *= Luminance.g; current.b *= Luminance.b; if (true) // dither { // Error again current.r += Error[index].r; current.g += Error[index].g; current.b += Error[index].b; } float fdot = (current.r - step[0].r) * Dir.r + (current.g - step[0].g) * Dir.g + (current.b - step[0].b) * Dir.b; uint iStep = 0; if (fdot <= 0f) iStep = 0; else if (fdot >= (uSteps - 1)) iStep = 1; else iStep = psteps[(int)(fdot + .5f)]; dw = (iStep << 30) | (dw >> 2); // THIS IS THE MAGIC here. This is the "list" of indicies. Somehow... DoSomeDithering(current, index, step, (int)iStep, Error); position += 4; } } return dw; }
static RGBColour Decode565(uint wColour) { RGBColour colour = new RGBColour() { r = ((wColour >> 11) & 31) * (1f / 31f), g = ((wColour >> 5) & 63) * (1f / 63f), b = ((wColour >> 0) & 31) * (1f / 31f), a = 1f }; return colour; }
internal static void CompressRGBTexel(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, bool isDXT1, double alphaRef, AlphaSettings alphaSetting) { int uSteps = 4; // Determine if texel is fully and entirely transparent. If so, can set to white and continue. if (isDXT1) { uSteps = CheckDXT1TexelFullTransparency(imgData, sourcePosition, sourceLineLength, destination, destPosition, alphaSetting, alphaRef); if (uSteps == -1) return; } RGBColour[] Colour = new RGBColour[16]; // Some kind of colour adjustment. Not sure what it does, especially if it wasn't dithering... DoColourFixErrorCorrection(Colour, imgData, sourcePosition, sourceLineLength, alphaSetting); // Palette colours RGBColour ColourA, ColourB, ColourC, ColourD; ColourA = new RGBColour(); ColourB = new RGBColour(); ColourC = new RGBColour(); ColourD = new RGBColour(); // OPTIMISER RGBColour[] minmax = OptimiseRGB(Colour, uSteps); ColourA = minmax[0]; ColourB = minmax[1]; // Create interstitial colours? ColourC.r = ColourA.r * LuminanceInv.r; ColourC.g = ColourA.g * LuminanceInv.g; ColourC.b = ColourA.b * LuminanceInv.b; ColourD.r = ColourB.r * LuminanceInv.r; ColourD.g = ColourB.g * LuminanceInv.g; ColourD.b = ColourB.b * LuminanceInv.b; // Yeah...dunno uint wColourA = Encode565(ColourC); uint wColourB = Encode565(ColourD); // Min max are equal - only interpolate 4 interstitial colours if (uSteps == 4 && wColourA == wColourB) { var c2 = BitConverter.GetBytes(wColourA); var c1 = BitConverter.GetBytes(wColourB); ///////////////////// MIN MAX destination[destPosition] = c2[0]; destination[destPosition + 1] = c2[1]; destination[destPosition + 2] = c1[0]; destination[destPosition + 3] = c1[1]; return; } // Interpolate 6 colours or something ColourC = Decode565(wColourA); ColourD = Decode565(wColourB); ColourA.r = ColourC.r * Luminance.r; ColourA.g = ColourC.g * Luminance.g; ColourA.b = ColourC.b * Luminance.b; ColourB.r = ColourD.r * Luminance.r; ColourB.g = ColourD.g * Luminance.g; ColourB.b = ColourD.b * Luminance.b; var step = DoSomethingWithPalette(uSteps, wColourA, wColourB, ColourA, ColourB); // Calculating colour direction apparently RGBColour Dir = new RGBColour() { r = step[1].r - step[0].r, g = step[1].g - step[0].g, b = step[1].b - step[0].b }; float fscale = (wColourA != wColourB) ? ((uSteps - 1) / (Dir.r * Dir.r + Dir.g * Dir.g + Dir.b * Dir.b)) : 0.0f; Dir.r *= fscale; Dir.g *= fscale; Dir.b *= fscale; // Encoding colours apparently uint dw = DoOtherColourFixErrorCorrection(imgData, sourcePosition, sourceLineLength, uSteps, alphaRef, alphaSetting, step, Dir); uint Min = (uSteps == 3) == (wColourA <= wColourB) ? wColourA : wColourB; uint Max = (uSteps == 3) == (wColourA <= wColourB) ? wColourB : wColourA; var colour1 = BitConverter.GetBytes(Min); var colour2 = BitConverter.GetBytes(Max); destination[destPosition] = colour1[0]; destination[destPosition + 1] = colour1[1]; destination[destPosition + 2] = colour2[0]; destination[destPosition + 3] = colour2[1]; var indicies = BitConverter.GetBytes(dw); destination[destPosition + 4] = indicies[0]; destination[destPosition + 5] = indicies[1]; destination[destPosition + 6] = indicies[2]; destination[destPosition + 7] = indicies[3]; }
private RGBColour ExtractRGBColour(string[] rgb) { return(RGBColour.FromRGB(double.Parse(rgb[0]), double.Parse(rgb[1]), double.Parse(rgb[2]))); }
private void SetPanelBackColor(Panel pnl, RGBColour colour) { pnl.BackColor = Color.FromArgb(255, (int)colour.R, (int)colour.G, (int)colour.B); }
///<exclude/> public bool Equals(RGBColour other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return other._R.Equals(_R) && other._G.Equals(_G) && other._B.Equals(_B); }