From b28711d999c86c40a11f61846ab15685c2fc172b Mon Sep 17 00:00:00 2001 From: Treeki Date: Thu, 3 Mar 2011 16:06:06 +0100 Subject: added (untested) CMPR compression based on BrawlLib's code. --- NW4RTools/Util/NVDXT.cs | 1037 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1037 insertions(+) create mode 100644 NW4RTools/Util/NVDXT.cs (limited to 'NW4RTools/Util') diff --git a/NW4RTools/Util/NVDXT.cs b/NW4RTools/Util/NVDXT.cs new file mode 100644 index 0000000..fd7d50a --- /dev/null +++ b/NW4RTools/Util/NVDXT.cs @@ -0,0 +1,1037 @@ +// 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 + } +} -- cgit v1.2.3