// This code is from BrawlLib: // http://code.google.com/p/brawltools/source/browse/trunk/BrawlLib/Imaging/NVDXT.cs // Modified by Treeki to work within NW4RTools. /* This code was modified from the NVidia Texture Tools source. * http://code.google.com/p/nvidia-texture-tools/ */ // Copyright NVIDIA Corporation 2007 -- Ignacio Castano // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Runtime.InteropServices; namespace NW4RTools.Util { unsafe public static class NVDXT { [StructLayout(LayoutKind.Sequential, Pack = 1)] unsafe public struct Vector2 { public float _x; public float _y; public Vector2(float x, float y) { _x = x; _y = y; } public Vector2(float s) { _x = s; _y = s; } public static Vector2 operator -(Vector2 v) { return new Vector2(-v._x, -v._y); } public static Vector2 operator +(Vector2 v1, Vector2 v2) { return new Vector2(v1._x + v2._x, v1._y + v2._y); } public static Vector2 operator -(Vector2 v1, Vector2 v2) { return new Vector2(v1._x - v2._x, v1._y - v2._y); } public static Vector2 operator -(Vector2 v1, float f) { return new Vector2(v1._x - f, v1._y - f); } public static Vector2 operator *(Vector2 v1, Vector2 v2) { return new Vector2(v1._x * v2._x, v1._y * v2._y); } public static Vector2 operator *(Vector2 v1, float s) { return new Vector2(v1._x * s, v1._y * s); } public static Vector2 operator *(float s, Vector2 v1) { return new Vector2(v1._x * s, v1._y * s); } public static Vector2 operator /(Vector2 v1, Vector2 v2) { return new Vector2(v1._x / v2._x, v1._y / v2._y); } public static Vector2 operator /(Vector2 v1, float s) { return new Vector2(v1._x / s, v1._y / s); } public static Vector2 operator +(Vector2 v1, Vector3 v2) { return new Vector2(v1._x + v2._x, v1._y + v2._y); } public static Vector2 operator +(Vector3 v1, Vector2 v2) { return new Vector2(v1._x + v2._x, v1._y + v2._y); } public static float Dot(Vector2 v1, Vector2 v2) { return (v1._x * v2._x) + (v1._y * v2._y); } public float Dot(Vector2 v) { return (_x * v._x) + (_y * v._y); } public static Vector2 Clamp(Vector2 v1, float min, float max) { v1.Clamp(min, max); return v1; } public void Clamp(float min, float max) { this.Max(min); this.Min(max); } public static Vector2 Min(Vector2 v1, Vector2 v2) { return new Vector2(Math.Min(v1._x, v2._x), Math.Min(v1._y, v2._y)); } public static Vector2 Min(Vector2 v1, float f) { return new Vector2(Math.Min(v1._x, f), Math.Min(v1._y, f)); } public void Min(Vector2 v) { if (_x > v._x) _x = v._x; if (_y > v._y) _y = v._y; } public void Min(float f) { _x = Math.Min(_x, f); _y = Math.Min(_y, f); } public static Vector2 Max(Vector2 v1, Vector2 v2) { return new Vector2(Math.Max(v1._x, v2._x), Math.Max(v1._y, v2._y)); } public static Vector2 Max(Vector2 v1, float f) { return new Vector2(Math.Max(v1._x, f), Math.Max(v1._y, f)); } public void Max(Vector2 v) { if (_x < v._x) _x = v._x; if (_y < v._y) _y = v._y; } public void Max(float f) { _x = Math.Max(_x, f); _y = Math.Max(_y, f); } public float DistanceTo(Vector2 v) { Vector2 v1 = this - v; return Vector2.Dot(v1, v1); } public static Vector2 Lerp(Vector2 v1, Vector2 v2, float median) { return (v1 * median) + (v2 * (1.0f - median)); } public static explicit operator Vector2(Vector3 v) { return new Vector2(v._x, v._y); } public static explicit operator Vector3(Vector2 v) { return new Vector3(v._x, v._y, 0.0f); } public static Vector2 Truncate(Vector2 v) { return new Vector2(v._x > 0.0f ? (float)Math.Floor(v._x) : (float)Math.Ceiling(v._x), v._y > 0.0f ? (float)Math.Floor(v._y) : (float)Math.Ceiling(v._y)); } public override string ToString() { return String.Format("({0},{1})", _x, _y); } public bool Contained(Vector2 start, Vector2 end, float expansion) { return Contained(this, start, end, expansion); } public static bool Contained(Vector2 point, Vector2 start, Vector2 end, float expansion) { float* sPtr = (float*)&point; float* s1 = (float*)&start, s2 = (float*)&end; float* temp; for (int i = 0; i < 2; i++) { if (s1[i] > s2[i]) { temp = s1; s1 = s2; s2 = temp; } if ((sPtr[i] < (s1[i] - expansion)) || (sPtr[i] > (s2[i] + expansion))) return false; } return true; } public float TrueDistance(Vector2 p) { float lenX = Math.Abs(p._x - _x); float lenY = Math.Abs(p._y - _y); if (lenX == 0.0f) return lenY; else if (lenY == 0.0f) return lenX; else return (float)(lenX / Math.Cos(Math.Atan(lenY / lenX))); } } [StructLayout(LayoutKind.Sequential, Pack = 1)] unsafe public struct Vector3 { public float _x; public float _y; public float _z; public Vector3(float x, float y, float z) { _x = x; _y = y; _z = z; } public Vector3(float s) { _x = s; _y = s; _z = s; } private const float _colorFactor = 1.0f / 255.0f; /*public static explicit operator Vector3(Color c) { return new Vector3(c.R * _colorFactor, c.G * _colorFactor, c.B * _colorFactor); } public static explicit operator Color(Vector3 v) { return Color.FromArgb((int)(v._x / _colorFactor), (int)(v._y / _colorFactor), (int)(v._z / _colorFactor)); }*/ public static Vector3 operator -(Vector3 v) { return new Vector3(-v._x, -v._y, -v._z); } public static Vector3 operator +(Vector3 v1, Vector3 v2) { return new Vector3(v1._x + v2._x, v1._y + v2._y, v1._z + v2._z); } public static Vector3 operator -(Vector3 v1, Vector3 v2) { return new Vector3(v1._x - v2._x, v1._y - v2._y, v1._z - v2._z); } public static Vector3 operator -(Vector3 v1, float f) { return new Vector3(v1._x - f, v1._y - f, v1._z - f); } public static Vector3 operator *(Vector3 v1, Vector3 v2) { return new Vector3(v1._x * v2._x, v1._y * v2._y, v1._z * v2._z); } public static Vector3 operator *(Vector3 v1, float s) { return new Vector3(v1._x * s, v1._y * s, v1._z * s); } public static Vector3 operator *(float s, Vector3 v1) { return new Vector3(v1._x * s, v1._y * s, v1._z * s); } public static Vector3 operator /(Vector3 v1, Vector3 v2) { return new Vector3(v1._x / v2._x, v1._y / v2._y, v1._z / v2._z); } public static Vector3 operator /(Vector3 v1, float s) { return new Vector3(v1._x / s, v1._y / s, v1._z / s); } public static bool operator ==(Vector3 v1, Vector3 v2) { return (v1._x == v2._x) && (v1._y == v2._y) && (v1._z == v2._z); } public static bool operator !=(Vector3 v1, Vector3 v2) { return (v1._x != v2._x) || (v1._y != v2._y) || (v1._z != v2._z); } public void Add(Vector3* v) { _x += v->_x; _y += v->_y; _z += v->_z; } public void Add(float v) { _x += v; _y += v; _z += v; } public void Sub(Vector3* v) { _x -= v->_x; _y -= v->_y; _z -= v->_z; } public void Sub(float v) { _x -= v; _y -= v; _z -= v; } public void Multiply(Vector3* v) { _x *= v->_x; _y *= v->_y; _z *= v->_z; } public void Multiply(float v) { _x *= v; _y *= v; _z *= v; } public static float* Mult(float* v1, float* v2) { v1[0] = v1[0] * v2[0]; v1[1] = v1[1] * v2[1]; v1[2] = v1[2] * v2[2]; return v1; } public static float* Mult(float* v1, float v2) { v1[0] = v1[0] * v2; v1[1] = v1[1] * v2; v1[2] = v1[2] * v2; return v1; } public static float* Add(float* v1, float* v2) { v1[0] = v1[0] + v2[0]; v1[1] = v1[1] + v2[1]; v1[2] = v1[2] + v2[2]; return v1; } public static float* Add(float* v1, float v2) { v1[0] = v1[0] + v2; v1[1] = v1[1] + v2; v1[2] = v1[2] + v2; return v1; } public static float* Sub(float* v1, float* v2) { v1[0] = v1[0] - v2[0]; v1[1] = v1[1] - v2[1]; v1[2] = v1[2] - v2[2]; return v1; } public static float* Sub(float* v1, float v2) { v1[0] = v1[0] - v2; v1[1] = v1[1] - v2; v1[2] = v1[2] - v2; return v1; } //public static float* Mult(float* v1, float* v2) { v1[0] = v1[0] * v2[0]; v1[1] = v1[1] * v2[1]; v1[2] = v1[2] * v2[2]; return v1; } //public static float* Mult(float* v1, float v2) { v1[0] *= v2; v1[1] *= v2; v1[2] *= v2; return v1; } //public static float* Add(float* v1, float* v2) { v1[0] += v2[0]; v1[1] += v2[1]; v1[2] += v2[2]; return v1; } //public static float* Add(float* v1, float v2) { v1[0] += v2; v1[1] += v2; v1[2] += v2; return v1; } //public static float* Sub(float* v1, float* v2) { v1[0] -= v2[0]; v1[1] -= v2[1]; v1[2] -= v2[2]; return v1; } //public static float* Sub(float* v1, float v2) { v1[0] -= v2; v1[1] -= v2; v1[2] -= v2; return v1; } public static float Dot(Vector3 v1, Vector3 v2) { return (v1._x * v2._x) + (v1._y * v2._y) + (v1._z * v2._z); } public float Dot(Vector3 v) { return (_x * v._x) + (_y * v._y) + (_z * v._z); } public float Dot(Vector3* v) { return (_x * v->_x) + (_y * v->_y) + (_z * v->_z); } public float Dot() { return (_x * _x) + (_y * _y) + (_z * _z); } public static Vector3 Clamp(Vector3 v1, float min, float max) { v1.Clamp(min, max); return v1; } public void Clamp(float min, float max) { this.Max(min); this.Min(max); } public static Vector3 Min(Vector3 v1, Vector3 v2) { return new Vector3(Math.Min(v1._x, v2._x), Math.Min(v1._y, v2._y), Math.Min(v1._z, v2._z)); } public static Vector3 Min(Vector3 v1, float f) { return new Vector3(Math.Min(v1._x, f), Math.Min(v1._y, f), Math.Min(v1._z, f)); } public void Min(Vector3 v) { _x = Math.Min(_x, v._x); _y = Math.Min(_y, v._y); _z = Math.Min(_z, v._z); } public void Min(Vector3* v) { if (v->_x < _x) _x = v->_x; if (v->_y < _y) _y = v->_y; if (v->_z < _z) _z = v->_z; } public void Min(float f) { _x = Math.Min(_x, f); _y = Math.Min(_y, f); _z = Math.Min(_z, f); } public static Vector3 Max(Vector3 v1, Vector3 v2) { return new Vector3(Math.Max(v1._x, v2._x), Math.Max(v1._y, v2._y), Math.Max(v1._z, v2._z)); } public static Vector3 Max(Vector3 v1, Vector3* v2) { return new Vector3(Math.Max(v1._x, v2->_x), Math.Max(v1._y, v2->_y), Math.Max(v1._z, v2->_z)); } public static Vector3 Max(Vector3 v1, float f) { return new Vector3(Math.Max(v1._x, f), Math.Max(v1._y, f), Math.Max(v1._z, f)); } public void Max(Vector3 v) { _x = Math.Max(_x, v._x); _y = Math.Max(_y, v._y); _z = Math.Max(_z, v._z); } public void Max(Vector3* v) { if (v->_x > _x) _x = v->_x; if (v->_y > _y) _y = v->_y; if (v->_z > _z) _z = v->_z; } public void Max(float f) { _x = Math.Max(_x, f); _y = Math.Max(_y, f); _z = Math.Max(_z, f); } public float DistanceTo(Vector3 v) { return (v - this).Dot(); } public static Vector3 Lerp(Vector3 v1, Vector3 v2, float median) { return (v1 * (1.0f - median)) + (v2 * median); } public static Vector3 Floor(Vector3 v) { return new Vector3((int)v._x, (int)v._y, (int)v._z); } public Vector3 Cross(Vector3 v) { return new Vector3(_y * v._z - v._y * _z, _z * v._x - v._z * _x, _x * v._y - v._x * _y); } public static Vector3 Truncate(Vector3 v) { return new Vector3(v._x > 0.0f ? (float)Math.Floor(v._x) : (float)Math.Ceiling(v._x), v._y > 0.0f ? (float)Math.Floor(v._y) : (float)Math.Ceiling(v._z), v._z > 0.0f ? (float)Math.Floor(v._z) : (float)Math.Ceiling(v._z)); } public override string ToString() { return String.Format("({0},{1},{2})", _x, _y, _z); } public bool Contained(Vector3 start, Vector3 end, float expansion) { return Contained(this, start, end, expansion); } unsafe public static bool Contained(Vector3 point, Vector3 start, Vector3 end, float expansion) { float* sPtr = (float*)&point; float* s1 = (float*)&start, s2 = (float*)&end; float* temp; for (int i = 0; i < 3; i++) { if (s1[i] > s2[i]) { temp = s1; s1 = s2; s2 = temp; } if ((sPtr[i] < (s1[i] - expansion)) || (sPtr[i] > (s2[i] + expansion))) return false; } return true; } public static Vector3 IntersectZ(Vector3 ray1, Vector3 ray2, float z) { float a = ray2._z - ray1._z; float tanX = (ray1._y - ray2._y) / a; float tanY = (ray2._x - ray1._x) / a; a = z - ray1._z; return new Vector3(tanY * a + ray1._x, -tanX * a + ray1._y, z); } public float TrueDistance(Vector3 p) { return (float)Math.Sqrt((p - this).Dot()); } public float TrueDistance() { return (float)Math.Sqrt(Dot()); } public Vector3 Normalize() { return this / TrueDistance(); } public Vector3 Normalize(Vector3 origin) { return (this - origin).Normalize(); } public Vector3 GetAngles() { return new Vector3((float)Math.Atan2(_y, -_z), (float)Math.Atan2(-_z, _x), (float)Math.Atan2(_y, _x)); } public Vector3 GetAngles(Vector3 origin) { return (this - origin).GetAngles(); } public Vector3 LookatAngles() { return new Vector3((float)Math.Atan2(_y, Math.Sqrt(_x * _x + _z * _z)), (float)Math.Atan2(-_x, -_z), 0.0f); } public Vector3 LookatAngles(Vector3 origin) { return (this - origin).LookatAngles(); } public float AngleX() { return (float)Math.Atan2(_y, -_z); } public float AngleY() { return (float)Math.Atan2(-_z, _x); } public float AngleZ() { return (float)Math.Atan2(_y, _x); } public override int GetHashCode() { fixed (Vector3* p = &this) { int* p2 = (int*)p; return p2[0] ^ p2[1] ^ p2[2]; } } public override bool Equals(object obj) { if (obj is Vector3) return this == (Vector3)obj; return false; } } [StructLayout(LayoutKind.Sequential, Pack = 1)] unsafe public struct ARGBPixel { private const float ColorFactor = 1.0f / 255.0f; public byte B, G, R, A; public ARGBPixel(byte a, byte r, byte g, byte b) { A = a; R = r; G = g; B = b; } public ARGBPixel(byte intensity) { A = 255; R = intensity; G = intensity; B = intensity; } /*public int DistanceTo(Color c) { int val = A - c.A; int dist = val * val; val = R - c.R; dist += val * val; val = G - c.G; dist += val * val; val = B - c.B; dist += val * val; return dist; }*/ public int DistanceTo(ARGBPixel p) { int val = A - p.A; int dist = val * val; val = R - p.R; dist += val * val; val = G - p.G; dist += val * val; val = B - p.B; return dist + val; } public float Luminance() { return (0.299f * R) + (0.587f * G) + (0.114f * B); } public bool IsGreyscale() { return (R == G) && (G == B); } public int Greyscale() { return (R + G + B) / 3; } public static explicit operator ARGBPixel(int val) { return *((ARGBPixel*)&val); } public static explicit operator int(ARGBPixel p) { return *((int*)&p); } public static explicit operator ARGBPixel(uint val) { return *((ARGBPixel*)&val); } public static explicit operator uint(ARGBPixel p) { return *((uint*)&p); } /*public static explicit operator ARGBPixel(Color val) { return (ARGBPixel)val.ToArgb(); } public static explicit operator Color(ARGBPixel p) { return Color.FromArgb((int)p); }*/ public static explicit operator Vector3(ARGBPixel p) { return new Vector3(p.R * ColorFactor, p.G * ColorFactor, p.B * ColorFactor); } public ARGBPixel Min(ARGBPixel p) { return new ARGBPixel(Math.Min(A, p.A), Math.Min(R, p.R), Math.Min(G, p.G), Math.Min(B, p.B)); } public ARGBPixel Max(ARGBPixel p) { return new ARGBPixel(Math.Max(A, p.A), Math.Max(R, p.R), Math.Max(G, p.G), Math.Max(B, p.B)); } public static bool operator ==(ARGBPixel p1, ARGBPixel p2) { return *((uint*)(&p1)) == *((uint*)(&p2)); } public static bool operator !=(ARGBPixel p1, ARGBPixel p2) { return *((uint*)(&p1)) != *((uint*)(&p2)); } public override string ToString() { return String.Format("A:{0:X2} R:{1:X2} G:{2:X2} B:{3:X2}", A, R, G, B); } public override int GetHashCode() { return (int)this; } public override bool Equals(object obj) { if (obj is ARGBPixel) return (ARGBPixel)obj == this; return false; } unsafe internal ARGBPixel Inverse() { return new ARGBPixel(A, (byte)(255 - R), (byte)(255 - G), (byte)(255 - B)); } unsafe internal ARGBPixel Lighten(int amount) { return new ARGBPixel(A, (byte)Math.Min(R + amount, 255), (byte)Math.Min(G + amount, 255), (byte)Math.Min(B + amount, 255)); } unsafe internal ARGBPixel Darken(int amount) { return new ARGBPixel(A, (byte)Math.Max(R - amount, 0), (byte)Math.Max(G - amount, 0), (byte)Math.Max(B - amount, 0)); } } [StructLayout(LayoutKind.Sequential, Pack = 1)] unsafe public struct CMPRBlock { public UInt16 Color0, Color1; public UInt32 Lookup; } private static CMPRBlock compressDXT1(ARGBPixel* pBlock) { float* pointData = stackalloc float[48]; Vector3* points = (Vector3*)pointData; extractColorBlockRGB(pBlock, points); // find min and max colors Vector3 maxColor, minColor; findMinMaxColorsBox(points, 16, &maxColor, &minColor); selectDiagonal(points, 16, &maxColor, &minColor); insetBBox(&maxColor, &minColor); ushort color0 = roundAndExpand(&maxColor); ushort color1 = roundAndExpand(&minColor); if (color0 < color1) { Vector3 t = maxColor; maxColor = minColor; minColor = t; ushort swap = color0; color0 = color1; color1 = swap; } CMPRBlock block = new CMPRBlock(); block.Color0 = color0; block.Color1 = color1; block.Lookup = computeIndices4(points, &maxColor, &minColor); optimizeEndPoints4(points, &block); return block; } public static CMPRBlock compressDXT1a(ARGBPixel* img, int imgX, int imgY, int imgW, int imgH) { uint* pData = stackalloc uint[16]; ARGBPixel* pBlock = (ARGBPixel*)pData; bool hasAlpha = false; bool isSingle = true; ARGBPixel p; ARGBPixel* sPtr = img + (imgX + (imgY * imgW)); int index = 0; for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++) { p = sPtr[x + (y * imgW)]; pBlock[index++] = p; if (p != pBlock[0]) isSingle = false; if (p.A < 128) hasAlpha = true; } if (isSingle) return optimalCompressDXT1a(sPtr[0]); if (!hasAlpha) return compressDXT1(pBlock); // @@ Handle single RGB, with varying alpha? We need tables for single color compressor in 3 color mode. //else if (rgba.isSingleColorNoAlpha()) { ... } float* pointData = stackalloc float[48]; Vector3* points = (Vector3*)pointData; // read block //Vector3 block[16]; int num = extractColorBlockRGBA(pBlock, points); // find min and max colors Vector3 maxColor, minColor; findMinMaxColorsBox(points, num, &maxColor, &minColor); selectDiagonal(points, num, &maxColor, &minColor); insetBBox(&maxColor, &minColor); ushort color0 = roundAndExpand(&maxColor); ushort color1 = roundAndExpand(&minColor); if (color0 < color1) { Vector3 t = maxColor; maxColor = minColor; minColor = t; ushort swap = color0; color0 = color1; color1 = swap; } CMPRBlock block = new CMPRBlock(); block.Color0 = color1; block.Color1 = color0; block.Lookup = computeIndices3(pBlock, &maxColor, &minColor); // optimizeEndPoints(block, dxtBlock); return block; } private static CMPRBlock optimalCompressDXT1(ARGBPixel c) { CMPRBlock block = new CMPRBlock(); uint indices = 0xAAAAAAAA; block.Color0 = (ushort)((OMatch5[c.R, 0] << 11) | (OMatch6[c.G, 0] << 5) | OMatch5[c.B, 0]); block.Color1 = (ushort)((OMatch5[c.R, 1] << 11) | (OMatch6[c.G, 1] << 5) | OMatch5[c.B, 1]); if (block.Color0 < block.Color1) { ushort swap = block.Color0; block.Color0 = block.Color1; block.Color1 = swap; indices ^= 0x55555555; } block.Lookup = indices; return block; } public static CMPRBlock optimalCompressDXT1a(ARGBPixel rgba) { if (rgba.A < 128) { CMPRBlock block = new CMPRBlock(); block.Color0 = 0; block.Color1 = 0; block.Lookup = 0xFFFFFFFF; return block; } else { return optimalCompressDXT1(rgba); } } private static int extractColorBlockRGBA(ARGBPixel* colors, Vector3* points) { int num = 0; for (int i = 0; i < 16; i++) { ARGBPixel p = colors[i]; if (p.A > 127) points[num++] = new Vector3(p.R, p.G, p.B); } return num; } private static void extractColorBlockRGB(ARGBPixel* colors, Vector3* points) { for (int i = 0; i < 16; i++) { ARGBPixel p = colors[i]; points[i] = new Vector3(p.R, p.G, p.B); } } private static void findMinMaxColorsBox(Vector3* points, int num, Vector3* maxColor, Vector3* minColor) { *maxColor = new Vector3(); *minColor = new Vector3(255.0f); for (uint i = 0; i < num; i++) { maxColor->Max(&points[i]); minColor->Min(&points[i]); } } private static void selectDiagonal(Vector3* points, int num, Vector3* maxColor, Vector3* minColor) { Vector3 center = (*maxColor + *minColor) * 0.5f; Vector3 t; Vector2* tp = (Vector2*)&t; Vector2 covariance = new Vector2(); for (uint i = 0; i < num; i++) { t = points[i] - center; covariance += *tp * t._z; } float x0 = maxColor->_x; float y0 = maxColor->_y; float x1 = minColor->_x; float y1 = minColor->_y; if (covariance._x < 0) { float swap = x0; x0 = x1; x1 = swap; } if (covariance._y < 0) { float swap = y0; y0 = y1; y1 = swap; } maxColor->_x = x0; maxColor->_y = y0; minColor->_x = x1; minColor->_y = y1; } private static void insetBBox(Vector3* maxColor, Vector3* minColor) { Vector3 inset = (*maxColor - *minColor) / 16.0f - (8.0f / 255.0f) / 16.0f; maxColor->Sub(&inset); maxColor->Clamp(0.0f, 255.0f); minColor->Add(&inset); minColor->Clamp(0.0f, 255.0f); } private static ushort roundAndExpand(Vector3* v) { uint r = (uint)(Math.Min(Math.Max(v->_x * (31.0f / 255.0f), 0.0f), 31.0f) + 0.5f); uint g = (uint)(Math.Min(Math.Max(v->_y * (63.0f / 255.0f), 0.0f), 63.0f) + 0.5f); uint b = (uint)(Math.Min(Math.Max(v->_z * (31.0f / 255.0f), 0.0f), 31.0f) + 0.5f); ushort w = (ushort)((r << 11) | (g << 5) | b); r = (r << 3) | (r >> 2); g = (g << 2) | (g >> 4); b = (b << 3) | (b >> 2); *v = new Vector3(r, g, b); return w; } private static uint computeIndices3(ARGBPixel* colors, Vector3* maxColor, Vector3* minColor) { float* pData = stackalloc float[9]; Vector3* palette = (Vector3*)pData; palette[0] = *minColor; palette[1] = *maxColor; palette[2] = (palette[0] + palette[1]) * 0.5f; uint indices = 0; for (int i = 0; i < 16; i++) { ARGBPixel p = colors[i]; Vector3 color = new Vector3(p.R, p.G, p.B); float d0 = colorDistance(&palette[0], &color); float d1 = colorDistance(&palette[1], &color); float d2 = colorDistance(&palette[2], &color); uint index; if (p.A < 128) index = 3; else if (d0 < d1 && d0 < d2) index = 0; else if (d1 < d2) index = 1; else index = 2; indices <<= 2; indices |= index; } return indices; } private static uint computeIndices4(Vector3* points, Vector3* maxColor, Vector3* minColor) { float* pData = stackalloc float[12]; Vector3* palette = (Vector3*)pData; palette[0] = *maxColor; palette[1] = *minColor; palette[2] = Vector3.Lerp(palette[0], palette[1], 1.0f / 3.0f); palette[3] = Vector3.Lerp(palette[0], palette[1], 2.0f / 3.0f); uint indices = 0; for (int i = 0; i < 16; i++) { Vector3 color = points[i]; float d0 = colorDistance(&palette[0], &color); float d1 = colorDistance(&palette[1], &color); float d2 = colorDistance(&palette[2], &color); float d3 = colorDistance(&palette[3], &color); indices <<= 2; if (d3 < d2 && d3 < d1 && d3 < d0) indices |= 3; else if (d2 < d1 && d2 < d0) indices |= 2; else if (d1 < d0) indices |= 1; //uint b0 = (d0 > d3) ? (uint)1 : 0; //uint b1 = (d1 > d2) ? (uint)1 : 0; //uint b2 = (d0 > d2) ? (uint)1 : 0; //uint b3 = (d1 > d3) ? (uint)1 : 0; //uint b4 = (d2 > d3) ? (uint)1 : 0; //uint x0 = b1 & b2; //uint x1 = b0 & b3; //uint x2 = b0 & b4; //indices <<= 2; //indices |= x2 | ((x0 | x1) << 1); } return indices; } private static void optimizeEndPoints4(Vector3* points, CMPRBlock* block) { float alpha2_sum = 0.0f; float beta2_sum = 0.0f; float alphabeta_sum = 0.0f; Vector3 alphax_sum = new Vector3(); Vector3 betax_sum = new Vector3(); uint indices = block->Lookup; for (int i = 0, bi = 30; i < 16; ++i,bi -= 2) { uint bits = indices >> bi; float beta = bits & 1; if ((bits & 2) != 0) beta = (1 + beta) / 3.0f; float alpha = 1.0f - beta; alpha2_sum += alpha * alpha; beta2_sum += beta * beta; alphabeta_sum += alpha * beta; alphax_sum += alpha * points[i]; betax_sum += beta * points[i]; } float denom = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum; if (Math.Abs(denom) <= 0.0001f) 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.Clamp(0.0f, 255.0f); b.Clamp(0.0f, 255.0f); ushort color0 = roundAndExpand(&a); ushort color1 = roundAndExpand(&b); if (color0 < color1) { Vector3 t = a; a = b; b = t; ushort swap = color0; color0 = color1; color1 = swap; } //CMPRBlock block = new CMPRBlock(); block->Color0 = color0; block->Color1 = color1; block->Lookup = computeIndices4(points, &a, &b); } private static float colorDistance(Vector3* c0, Vector3* c1) { Vector3 v = *c0 - *c1; return Vector3.Dot(v, v); } #region OptTables public static readonly byte[,] OMatch5 = new byte[256, 2] { { 0x00, 0x00 }, { 0x00, 0x00 }, { 0x00, 0x01 }, { 0x00, 0x01 }, { 0x01, 0x00 }, { 0x01, 0x00 }, { 0x01, 0x00 }, { 0x01, 0x01 }, { 0x01, 0x01 }, { 0x01, 0x01 }, { 0x01, 0x02 }, { 0x00, 0x04 }, { 0x02, 0x01 }, { 0x02, 0x01 }, { 0x02, 0x01 }, { 0x02, 0x02 }, { 0x02, 0x02 }, { 0x02, 0x02 }, { 0x02, 0x03 }, { 0x01, 0x05 }, { 0x03, 0x02 }, { 0x03, 0x02 }, { 0x04, 0x00 }, { 0x03, 0x03 }, { 0x03, 0x03 }, { 0x03, 0x03 }, { 0x03, 0x04 }, { 0x03, 0x04 }, { 0x03, 0x04 }, { 0x03, 0x05 }, { 0x04, 0x03 }, { 0x04, 0x03 }, { 0x05, 0x02 }, { 0x04, 0x04 }, { 0x04, 0x04 }, { 0x04, 0x05 }, { 0x04, 0x05 }, { 0x05, 0x04 }, { 0x05, 0x04 }, { 0x05, 0x04 }, { 0x06, 0x03 }, { 0x05, 0x05 }, { 0x05, 0x05 }, { 0x05, 0x06 }, { 0x04, 0x08 }, { 0x06, 0x05 }, { 0x06, 0x05 }, { 0x06, 0x05 }, { 0x06, 0x06 }, { 0x06, 0x06 }, { 0x06, 0x06 }, { 0x06, 0x07 }, { 0x05, 0x09 }, { 0x07, 0x06 }, { 0x07, 0x06 }, { 0x08, 0x04 }, { 0x07, 0x07 }, { 0x07, 0x07 }, { 0x07, 0x07 }, { 0x07, 0x08 }, { 0x07, 0x08 }, { 0x07, 0x08 }, { 0x07, 0x09 }, { 0x08, 0x07 }, { 0x08, 0x07 }, { 0x09, 0x06 }, { 0x08, 0x08 }, { 0x08, 0x08 }, { 0x08, 0x09 }, { 0x08, 0x09 }, { 0x09, 0x08 }, { 0x09, 0x08 }, { 0x09, 0x08 }, { 0x0A, 0x07 }, { 0x09, 0x09 }, { 0x09, 0x09 }, { 0x09, 0x0A }, { 0x08, 0x0C }, { 0x0A, 0x09 }, { 0x0A, 0x09 }, { 0x0A, 0x09 }, { 0x0A, 0x0A }, { 0x0A, 0x0A }, { 0x0A, 0x0A }, { 0x0A, 0x0B }, { 0x09, 0x0D }, { 0x0B, 0x0A }, { 0x0B, 0x0A }, { 0x0C, 0x08 }, { 0x0B, 0x0B }, { 0x0B, 0x0B }, { 0x0B, 0x0B }, { 0x0B, 0x0C }, { 0x0B, 0x0C }, { 0x0B, 0x0C }, { 0x0B, 0x0D }, { 0x0C, 0x0B }, { 0x0C, 0x0B }, { 0x0D, 0x0A }, { 0x0C, 0x0C }, { 0x0C, 0x0C }, { 0x0C, 0x0D }, { 0x0C, 0x0D }, { 0x0D, 0x0C }, { 0x0D, 0x0C }, { 0x0D, 0x0C }, { 0x0E, 0x0B }, { 0x0D, 0x0D }, { 0x0D, 0x0D }, { 0x0D, 0x0E }, { 0x0C, 0x10 }, { 0x0E, 0x0D }, { 0x0E, 0x0D }, { 0x0E, 0x0D }, { 0x0E, 0x0E }, { 0x0E, 0x0E }, { 0x0E, 0x0E }, { 0x0E, 0x0F }, { 0x0D, 0x11 }, { 0x0F, 0x0E }, { 0x0F, 0x0E }, { 0x10, 0x0C }, { 0x0F, 0x0F }, { 0x0F, 0x0F }, { 0x0F, 0x0F }, { 0x0F, 0x10 }, { 0x0F, 0x10 }, { 0x0F, 0x10 }, { 0x0F, 0x11 }, { 0x10, 0x0F }, { 0x10, 0x0F }, { 0x11, 0x0E }, { 0x10, 0x10 }, { 0x10, 0x10 }, { 0x10, 0x11 }, { 0x10, 0x11 }, { 0x11, 0x10 }, { 0x11, 0x10 }, { 0x11, 0x10 }, { 0x12, 0x0F }, { 0x11, 0x11 }, { 0x11, 0x11 }, { 0x11, 0x12 }, { 0x10, 0x14 }, { 0x12, 0x11 }, { 0x12, 0x11 }, { 0x12, 0x11 }, { 0x12, 0x12 }, { 0x12, 0x12 }, { 0x12, 0x12 }, { 0x12, 0x13 }, { 0x11, 0x15 }, { 0x13, 0x12 }, { 0x13, 0x12 }, { 0x14, 0x10 }, { 0x13, 0x13 }, { 0x13, 0x13 }, { 0x13, 0x13 }, { 0x13, 0x14 }, { 0x13, 0x14 }, { 0x13, 0x14 }, { 0x13, 0x15 }, { 0x14, 0x13 }, { 0x14, 0x13 }, { 0x15, 0x12 }, { 0x14, 0x14 }, { 0x14, 0x14 }, { 0x14, 0x15 }, { 0x14, 0x15 }, { 0x15, 0x14 }, { 0x15, 0x14 }, { 0x15, 0x14 }, { 0x16, 0x13 }, { 0x15, 0x15 }, { 0x15, 0x15 }, { 0x15, 0x16 }, { 0x14, 0x18 }, { 0x16, 0x15 }, { 0x16, 0x15 }, { 0x16, 0x15 }, { 0x16, 0x16 }, { 0x16, 0x16 }, { 0x16, 0x16 }, { 0x16, 0x17 }, { 0x15, 0x19 }, { 0x17, 0x16 }, { 0x17, 0x16 }, { 0x18, 0x14 }, { 0x17, 0x17 }, { 0x17, 0x17 }, { 0x17, 0x17 }, { 0x17, 0x18 }, { 0x17, 0x18 }, { 0x17, 0x18 }, { 0x17, 0x19 }, { 0x18, 0x17 }, { 0x18, 0x17 }, { 0x19, 0x16 }, { 0x18, 0x18 }, { 0x18, 0x18 }, { 0x18, 0x19 }, { 0x18, 0x19 }, { 0x19, 0x18 }, { 0x19, 0x18 }, { 0x19, 0x18 }, { 0x1A, 0x17 }, { 0x19, 0x19 }, { 0x19, 0x19 }, { 0x19, 0x1A }, { 0x18, 0x1C }, { 0x1A, 0x19 }, { 0x1A, 0x19 }, { 0x1A, 0x19 }, { 0x1A, 0x1A }, { 0x1A, 0x1A }, { 0x1A, 0x1A }, { 0x1A, 0x1B }, { 0x19, 0x1D }, { 0x1B, 0x1A }, { 0x1B, 0x1A }, { 0x1C, 0x18 }, { 0x1B, 0x1B }, { 0x1B, 0x1B }, { 0x1B, 0x1B }, { 0x1B, 0x1C }, { 0x1B, 0x1C }, { 0x1B, 0x1C }, { 0x1B, 0x1D }, { 0x1C, 0x1B }, { 0x1C, 0x1B }, { 0x1D, 0x1A }, { 0x1C, 0x1C }, { 0x1C, 0x1C }, { 0x1C, 0x1D }, { 0x1C, 0x1D }, { 0x1D, 0x1C }, { 0x1D, 0x1C }, { 0x1D, 0x1C }, { 0x1E, 0x1B }, { 0x1D, 0x1D }, { 0x1D, 0x1D }, { 0x1D, 0x1E }, { 0x1D, 0x1E }, { 0x1E, 0x1D }, { 0x1E, 0x1D }, { 0x1E, 0x1D }, { 0x1E, 0x1E }, { 0x1E, 0x1E }, { 0x1E, 0x1E }, { 0x1E, 0x1F }, { 0x1E, 0x1F }, { 0x1F, 0x1E }, { 0x1F, 0x1E }, { 0x1F, 0x1E }, { 0x1F, 0x1F }, { 0x1F, 0x1F } }; public static readonly byte[,] OMatch6 = new byte[256, 2] { { 0x00, 0x00 }, { 0x00, 0x01 }, { 0x01, 0x00 }, { 0x01, 0x01 }, { 0x01, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x01 }, { 0x02, 0x02 }, { 0x02, 0x02 }, { 0x02, 0x03 }, { 0x03, 0x02 }, { 0x03, 0x03 }, { 0x03, 0x03 }, { 0x03, 0x04 }, { 0x04, 0x03 }, { 0x04, 0x04 }, { 0x04, 0x04 }, { 0x04, 0x05 }, { 0x05, 0x04 }, { 0x05, 0x05 }, { 0x05, 0x05 }, { 0x05, 0x06 }, { 0x06, 0x05 }, { 0x00, 0x11 }, { 0x06, 0x06 }, { 0x06, 0x07 }, { 0x07, 0x06 }, { 0x02, 0x10 }, { 0x07, 0x07 }, { 0x07, 0x08 }, { 0x08, 0x07 }, { 0x03, 0x11 }, { 0x08, 0x08 }, { 0x08, 0x09 }, { 0x09, 0x08 }, { 0x05, 0x10 }, { 0x09, 0x09 }, { 0x09, 0x0A }, { 0x0A, 0x09 }, { 0x06, 0x11 }, { 0x0A, 0x0A }, { 0x0A, 0x0B }, { 0x0B, 0x0A }, { 0x08, 0x10 }, { 0x0B, 0x0B }, { 0x0B, 0x0C }, { 0x0C, 0x0B }, { 0x09, 0x11 }, { 0x0C, 0x0C }, { 0x0C, 0x0D }, { 0x0D, 0x0C }, { 0x0B, 0x10 }, { 0x0D, 0x0D }, { 0x0D, 0x0E }, { 0x0E, 0x0D }, { 0x0C, 0x11 }, { 0x0E, 0x0E }, { 0x0E, 0x0F }, { 0x0F, 0x0E }, { 0x0E, 0x10 }, { 0x0F, 0x0F }, { 0x0F, 0x10 }, { 0x10, 0x0E }, { 0x10, 0x0F }, { 0x11, 0x0E }, { 0x10, 0x10 }, { 0x10, 0x11 }, { 0x11, 0x10 }, { 0x12, 0x0F }, { 0x11, 0x11 }, { 0x11, 0x12 }, { 0x12, 0x11 }, { 0x14, 0x0E }, { 0x12, 0x12 }, { 0x12, 0x13 }, { 0x13, 0x12 }, { 0x15, 0x0F }, { 0x13, 0x13 }, { 0x13, 0x14 }, { 0x14, 0x13 }, { 0x17, 0x0E }, { 0x14, 0x14 }, { 0x14, 0x15 }, { 0x15, 0x14 }, { 0x18, 0x0F }, { 0x15, 0x15 }, { 0x15, 0x16 }, { 0x16, 0x15 }, { 0x1A, 0x0E }, { 0x16, 0x16 }, { 0x16, 0x17 }, { 0x17, 0x16 }, { 0x1B, 0x0F }, { 0x17, 0x17 }, { 0x17, 0x18 }, { 0x18, 0x17 }, { 0x13, 0x21 }, { 0x18, 0x18 }, { 0x18, 0x19 }, { 0x19, 0x18 }, { 0x15, 0x20 }, { 0x19, 0x19 }, { 0x19, 0x1A }, { 0x1A, 0x19 }, { 0x16, 0x21 }, { 0x1A, 0x1A }, { 0x1A, 0x1B }, { 0x1B, 0x1A }, { 0x18, 0x20 }, { 0x1B, 0x1B }, { 0x1B, 0x1C }, { 0x1C, 0x1B }, { 0x19, 0x21 }, { 0x1C, 0x1C }, { 0x1C, 0x1D }, { 0x1D, 0x1C }, { 0x1B, 0x20 }, { 0x1D, 0x1D }, { 0x1D, 0x1E }, { 0x1E, 0x1D }, { 0x1C, 0x21 }, { 0x1E, 0x1E }, { 0x1E, 0x1F }, { 0x1F, 0x1E }, { 0x1E, 0x20 }, { 0x1F, 0x1F }, { 0x1F, 0x20 }, { 0x20, 0x1E }, { 0x20, 0x1F }, { 0x21, 0x1E }, { 0x20, 0x20 }, { 0x20, 0x21 }, { 0x21, 0x20 }, { 0x22, 0x1F }, { 0x21, 0x21 }, { 0x21, 0x22 }, { 0x22, 0x21 }, { 0x24, 0x1E }, { 0x22, 0x22 }, { 0x22, 0x23 }, { 0x23, 0x22 }, { 0x25, 0x1F }, { 0x23, 0x23 }, { 0x23, 0x24 }, { 0x24, 0x23 }, { 0x27, 0x1E }, { 0x24, 0x24 }, { 0x24, 0x25 }, { 0x25, 0x24 }, { 0x28, 0x1F }, { 0x25, 0x25 }, { 0x25, 0x26 }, { 0x26, 0x25 }, { 0x2A, 0x1E }, { 0x26, 0x26 }, { 0x26, 0x27 }, { 0x27, 0x26 }, { 0x2B, 0x1F }, { 0x27, 0x27 }, { 0x27, 0x28 }, { 0x28, 0x27 }, { 0x23, 0x31 }, { 0x28, 0x28 }, { 0x28, 0x29 }, { 0x29, 0x28 }, { 0x25, 0x30 }, { 0x29, 0x29 }, { 0x29, 0x2A }, { 0x2A, 0x29 }, { 0x26, 0x31 }, { 0x2A, 0x2A }, { 0x2A, 0x2B }, { 0x2B, 0x2A }, { 0x28, 0x30 }, { 0x2B, 0x2B }, { 0x2B, 0x2C }, { 0x2C, 0x2B }, { 0x29, 0x31 }, { 0x2C, 0x2C }, { 0x2C, 0x2D }, { 0x2D, 0x2C }, { 0x2B, 0x30 }, { 0x2D, 0x2D }, { 0x2D, 0x2E }, { 0x2E, 0x2D }, { 0x2C, 0x31 }, { 0x2E, 0x2E }, { 0x2E, 0x2F }, { 0x2F, 0x2E }, { 0x2E, 0x30 }, { 0x2F, 0x2F }, { 0x2F, 0x30 }, { 0x30, 0x2E }, { 0x30, 0x2F }, { 0x31, 0x2E }, { 0x30, 0x30 }, { 0x30, 0x31 }, { 0x31, 0x30 }, { 0x32, 0x2F }, { 0x31, 0x31 }, { 0x31, 0x32 }, { 0x32, 0x31 }, { 0x34, 0x2E }, { 0x32, 0x32 }, { 0x32, 0x33 }, { 0x33, 0x32 }, { 0x35, 0x2F }, { 0x33, 0x33 }, { 0x33, 0x34 }, { 0x34, 0x33 }, { 0x37, 0x2E }, { 0x34, 0x34 }, { 0x34, 0x35 }, { 0x35, 0x34 }, { 0x38, 0x2F }, { 0x35, 0x35 }, { 0x35, 0x36 }, { 0x36, 0x35 }, { 0x3A, 0x2E }, { 0x36, 0x36 }, { 0x36, 0x37 }, { 0x37, 0x36 }, { 0x3B, 0x2F }, { 0x37, 0x37 }, { 0x37, 0x38 }, { 0x38, 0x37 }, { 0x3D, 0x2E }, { 0x38, 0x38 }, { 0x38, 0x39 }, { 0x39, 0x38 }, { 0x3E, 0x2F }, { 0x39, 0x39 }, { 0x39, 0x3A }, { 0x3A, 0x39 }, { 0x3A, 0x3A }, { 0x3A, 0x3A }, { 0x3A, 0x3B }, { 0x3B, 0x3A }, { 0x3B, 0x3B }, { 0x3B, 0x3B }, { 0x3B, 0x3C }, { 0x3C, 0x3B }, { 0x3C, 0x3C }, { 0x3C, 0x3C }, { 0x3C, 0x3D }, { 0x3D, 0x3C }, { 0x3D, 0x3D }, { 0x3D, 0x3D }, { 0x3D, 0x3E }, { 0x3E, 0x3D }, { 0x3E, 0x3E }, { 0x3E, 0x3E }, { 0x3E, 0x3F }, { 0x3F, 0x3E }, { 0x3F, 0x3F }, { 0x3F, 0x3F } }; #endregion } }