public static int GetStorageRequirements(int width, int height, SquishOptions flags) { var blockCount = ((width + 3) / 4) * ((height + 3) / 4); var blockSize = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16; return(blockCount * blockSize); }
public static byte[] DecompressBlock(byte[] block, int blockOffset, SquishOptions flags) { // Get the block locations var colOff = blockOffset; var alphaOff = blockOffset; if ((flags & (SquishOptions.DXT3 | SquishOptions.DXT5)) != 0) { colOff += 8; } // Decompress colour. var rgba = ColourBlock.DecompressColour(block, colOff, flags.HasFlag(SquishOptions.DXT1)); // Decompress alpha seperately if necessary. if (flags.HasFlag(SquishOptions.DXT3)) { Alpha.DecompressAlphaDxt3(block, alphaOff, rgba, 0); } else if (flags.HasFlag(SquishOptions.DXT5)) { Alpha.DecompressAlphaDxt5(block, alphaOff, rgba, 0); } return(rgba); }
public static byte[] CompressBlockMasked(byte[] rgba, int mask, SquishOptions flags) { flags = flags.FixFlags(); ColourSet colours = new ColourSet(rgba, mask, flags); if (colours.Count == 1) { // Always do a single colour fit //SingleColourFit fit = new SingleColourFit(ref colours, flags); } else if (flags.HasFlag(SquishOptions.ColourRangeFit) || colours.Count == 0) { // Do a range fit //RangeFit fit = new RangeFit(ref colours, flags, metric); } else { // Default to a cluster fit (could be iterative or not) //ClusterFit fit = new ClusterFit(ref colours, flags, metric); } // Compress alpha separately if needed if (flags.HasFlag(SquishOptions.DXT3)) { //Alpha.CompressAlphaDxt3(rgba, mask, alphaBlock); } else if (flags.HasFlag(SquishOptions.DXT5)) { //Alpha.CompressAlphaDxt5(rgba, mask, alphaBlock); } throw new NotImplementedException(); }
protected ClusterFit(ColourSet colours, SquishOptions flags) : base(colours, flags) { // Set the iteration count. this._IterationCount = flags.HasFlag(SquishOptions.ColourIterativeClusterFit) ? MaxIterations : 1; // Initialise the best error. this._BestError = new Vector4(float.MaxValue); // Initialize the metric var perceptual = flags.HasFlag(SquishOptions.ColourMetricPerceptual); if (perceptual) { this._Metric = new Vector4(0.2126f, 0.7152f, 0.0722f, 0.0f); } else { this._Metric = new Vector4(1.0f); } // Get the covariance matrix. var covariance = Sym3x3.ComputeWeightedCovariance(colours.Count, colours.Points, colours.Weights); // Compute the principle component this._Principle = Sym3x3.ComputePrincipledComponent(covariance); }
public static byte[] DecompressBlock(byte[] block, int blockOffset, SquishOptions flags) { // Get the block locations var colOff = blockOffset; var alphaOff = blockOffset; if ((flags & (SquishOptions.DXT3 | SquishOptions.DXT5)) != 0) { colOff += 8; } // Decompress colour. var rgba = ColourBlock.DecompressColour(block, colOff, flags.HasFlag(SquishOptions.DXT1)); // Decompress alpha seperately if necessary. if (flags.HasFlag(SquishOptions.DXT3)) { Alpha.DecompressAlphaDxt3(block, alphaOff, rgba, 0); } else if (flags.HasFlag(SquishOptions.DXT5)) { Alpha.DecompressAlphaDxt5(block, alphaOff, rgba, 0); } return rgba; }
private SingleColourFit(ColourSet colours, SquishOptions flags) : base(colours, flags) { Vector3 values = this._Colours.Points[0]; this.colour[0] = (byte)FloatToInt(255.0f * values.X, 255); this.colour[1] = (byte)FloatToInt(255.0f * values.Y, 255); this.colour[2] = (byte)FloatToInt(255.0f * values.Z, 255); this.bestError = int.MaxValue; }
public static SquishOptions FixFlags(this SquishOptions flags) { // grab the flag bits var method = (int)(flags & (SquishOptions.DXT1 | SquishOptions.DXT3 | SquishOptions.DXT5)); var fit = (int)(flags & (SquishOptions.ColourIterativeClusterFit | SquishOptions.ColourClusterFit | SquishOptions.ColourRangeFit)); var extra = (int)(flags & SquishOptions.WeightColourByAlpha); // set defaults if (method != (int)SquishOptions.DXT3 && method != (int)SquishOptions.DXT5) { method = (int)SquishOptions.DXT5; } if (fit != (int)SquishOptions.ColourRangeFit && fit != (int)SquishOptions.ColourIterativeClusterFit) { fit = (int)SquishOptions.ColourClusterFit; } // done return((SquishOptions)(method | fit | extra)); }
public static byte[] DecompressImage(byte[] blocks, int offset, int width, int height, SquishOptions flags) { var argb = new byte[4 * width * height]; var bytesPerBlock = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16; var blockOffset = offset; // Loop over blocks. for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { // Decompress the block. var targetRgba = DecompressBlock(blocks, blockOffset, flags); // Write the decompressed pixels to the correct image locations. var sourcePixelOffset = 0; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // Get the target location. var sx = x + px; var sy = y + py; if (sx < width && sy < height) { var targetPixelOffset = 4 * ((width * sy) + sx); // Copy the rgba value argb[targetPixelOffset + 0] = targetRgba[sourcePixelOffset + 2]; argb[targetPixelOffset + 1] = targetRgba[sourcePixelOffset + 1]; argb[targetPixelOffset + 2] = targetRgba[sourcePixelOffset + 0]; argb[targetPixelOffset + 3] = targetRgba[sourcePixelOffset + 3]; } sourcePixelOffset += 4; } } // advance blockOffset += bytesPerBlock; } } return argb; }
protected ColourFit(ColourSet colours, SquishOptions flags) { this._Colours = colours; this._Flags = flags; }
public static SquishOptions GetMetric(this SquishOptions self) { return(self & (SquishOptions.ColourMetricPerceptual | SquishOptions.ColourMetricUniform)); }
public static SquishOptions GetMethod(this SquishOptions self) { return(self & (SquishOptions.DXT1 | SquishOptions.DXT3 | SquishOptions.DXT5)); }
public ColourSet(byte[] rgba, int mask, SquishOptions flags) { // Check the compression mode. var isDxt1 = flags.HasFlag(SquishOptions.DXT1); var weightByAlpha = flags.HasFlag(SquishOptions.WeightColourByAlpha); // Create he minimal set. for (int i = 0; i < 16; ++i) { // Check this pixel is enabled. int bit = 1 << i; if ((mask & bit) == 0) { this._Remap[i] = -1; continue; } // Check for transparent pixels when using DXT1. if (isDxt1 && rgba[4 * i + 3] < 128) { this._Remap[i] = -1; this._IsTransparent = true; } // Loop over previous points for a match. for (int j = 0;; ++j) { // Allocate a new point. if (j == i) { // Normalise coordinates to [0,1]. var x = rgba[4 * i] / 255f; var y = rgba[4 * i + 1] / 255f; var z = rgba[4 * i + 2] / 255f; // Ensure there is always a non-zero weight even for zero alpha. var w = (rgba[4 * i + 3] + 1) / 256f; // Add the point. this._Points[this._Count] = new Vector3(x, y, z); this._Weights[this._Count] = w; this._Remap[i] = this._Count; // Advance. ++this._Count; break; } // Check for a match. int oldBit = 1 << j; var match = ((mask & oldBit) != 0) && (rgba[4 * i] == rgba[4 * j]) && (rgba[4 * i + 1] == rgba[4 * j + 1]) && (rgba[4 * i + 3] == rgba[4 * j + 2]) && (rgba[4 * j + 3] >= 128 || !isDxt1); if (match) { // Get index of the match. var index = this._Remap[j]; // Ensure there is always a non-zero weight even for zero alpha. var w = (rgba[4 * i + 3] + 1) / 256f; // Map this point and increase the weight. this._Weights[index] += (weightByAlpha ? w : 1f); this._Remap[i] = index; break; } } } // Square root the weights. for (int i = 0; i < this._Count; ++i) { this._Weights[i] = (float)Math.Sqrt(this._Weights[i]); } }
public static byte[] CompressImage(byte[] rgba, int width, int height, SquishOptions flags) { throw new NotImplementedException(); }
public static int GetStorageRequirements(int width, int height, SquishOptions flags) { var blockCount = ((width + 3) / 4) * ((height + 3) / 4); var blockSize = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16; return blockCount * blockSize; }
/// <summary> /// Compresses in input bitmap into a single mipmap at the specified mipmap level, where a mip level is a bisection of the resolution. /// For instance, a mip level of 2 applied to a 64x64 image would produce an image with a resolution of 16x16. /// This function expects the mipmap level to be reasonable (i.e, not a level which would produce a mip smaller than 1x1) /// </summary> /// <returns>The image.</returns> /// <param name="inImage">Image.</param> /// <param name="mipLevel">Mip level.</param> private byte[] CompressImage(Image inImage, uint mipLevel) { uint targetXRes = GetLevelAdjustedResolutionValue(GetResolution().X, mipLevel); uint targetYRes = GetLevelAdjustedResolutionValue(GetResolution().Y, mipLevel); List <byte> colourData = new List <byte>(); List <byte> alphaData = new List <byte>(); using (Bitmap resizedImage = ResizeImage(inImage, (int)targetXRes, (int)targetYRes)) { if (this.Header.CompressionType == TextureCompressionType.Palettized) { // Generate the colour data for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { Color nearestColor = FindClosestMatchingColor(resizedImage.GetPixel(x, y)); byte paletteIndex = (byte)this.Palette.IndexOf(nearestColor); colourData.Add(paletteIndex); } } // Generate the alpha data if (GetAlphaBitDepth() > 0) { if (GetAlphaBitDepth() == 1) { // We're going to be attempting to map 8 pixels on each X iteration for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; x += 8) { // The alpha value is stored per-bit in the byte (8 alpha values per byte) byte alphaByte = 0; for (byte i = 0; (i < 8) && (i < targetXRes); ++i) { byte pixelAlpha = resizedImage.GetPixel(x + i, y).A; if (pixelAlpha > 0) { pixelAlpha = 1; } // Shift the value into the correct position in the byte pixelAlpha = (byte)(pixelAlpha << 7 - i); alphaByte = (byte)(alphaByte | pixelAlpha); } alphaData.Add(alphaByte); } } } else if (GetAlphaBitDepth() == 4) { // We're going to be attempting to map 2 pixels on each X iteration for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; x += 2) { // The alpha value is stored as half a byte (2 alpha values per byte) // Extract these two values and map them to a byte size (4 bits can hold 0 - 15 alpha) byte alphaByte = 0; for (byte i = 0; (i < 2) && (i < targetXRes); ++i) { // Get the value from the image byte pixelAlpha = resizedImage.GetPixel(x + i, y).A; // Map the value to a 4-bit integer pixelAlpha = (byte)ExtensionMethods.Map(pixelAlpha, 0, 255, 0, 15); // Shift the value to the upper bits on the first iteration, and leave it where it is // on the second one pixelAlpha = (byte)(pixelAlpha << 4 * (1 - i)); alphaByte = (byte)(alphaByte | pixelAlpha); } alphaData.Add(alphaByte); } } } else if (GetAlphaBitDepth() == 8) { for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { // The alpha value is stored as a whole byte byte alphaValue = resizedImage.GetPixel(x, y).A; alphaData.Add(alphaValue); } } } } else { // The map is fully opaque for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { alphaData.Add(255); } } } } else if (this.Header.CompressionType == TextureCompressionType.DXTC) { using (MemoryStream rgbaStream = new MemoryStream()) { using (BinaryWriter bw = new BinaryWriter(rgbaStream)) { for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { bw.Write(resizedImage.GetPixel(x, y).R); bw.Write(resizedImage.GetPixel(x, y).G); bw.Write(resizedImage.GetPixel(x, y).B); bw.Write(resizedImage.GetPixel(x, y).A); } } // Finish writing the data bw.Flush(); byte[] rgbaBytes = rgbaStream.ToArray(); SquishOptions squishOptions = SquishOptions.DXT1; if (this.Header.PixelFormat == BLPPixelFormat.DXT3) { squishOptions = SquishOptions.DXT3; } else if (this.Header.PixelFormat == BLPPixelFormat.DXT5) { squishOptions = SquishOptions.DXT5; } // TODO: Implement squish compression colourData = new List <byte>(SquishCompression.CompressImage(rgbaBytes, (int)targetXRes, (int)targetYRes, squishOptions)); } } } else if (this.Header.CompressionType == TextureCompressionType.Uncompressed) { using (MemoryStream argbStream = new MemoryStream()) { using (BinaryWriter bw = new BinaryWriter(argbStream)) { for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { bw.Write(resizedImage.GetPixel(x, y).A); bw.Write(resizedImage.GetPixel(x, y).R); bw.Write(resizedImage.GetPixel(x, y).G); bw.Write(resizedImage.GetPixel(x, y).B); } } // Finish writing the data bw.Flush(); byte[] argbBytes = argbStream.ToArray(); colourData = new List <byte>(argbBytes); } } } } // After compression of the data, merge the color data and alpha data byte[] compressedMipMap = new byte[colourData.Count + alphaData.Count]; Buffer.BlockCopy(colourData.ToArray(), 0, compressedMipMap, 0, colourData.ToArray().Length); Buffer.BlockCopy(alphaData.ToArray(), 0, compressedMipMap, colourData.ToArray().Length, alphaData.ToArray().Length); return(compressedMipMap); }
public static Image <Rgba32> DecompressToImage(byte[] blocks, int offset, int width, int height, SquishOptions flags) { var result = new Image <Rgba32>(width, height); var bufferOffset = 0; var bytesPerBlock = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16; var blockOffset = offset; // Loop over blocks. for (var y = 0; y < height; y += 4) { for (var x = 0; x < width; x += 4) { // Decompress the block. var targetRgba = DecompressBlock(blocks, blockOffset, flags); // Write the decompressed pixels to the correct image locations. var sourcePixelOffset = 0; for (var py = 0; py < 4; ++py) { for (var px = 0; px < 4; ++px) { // Get the target location. var sx = x + px; var sy = y + py; if (sx < width && sy < height) { var sourceColour = new Rgba32 ( targetRgba[sourcePixelOffset + 0], targetRgba[sourcePixelOffset + 1], targetRgba[sourcePixelOffset + 2], targetRgba[sourcePixelOffset + 3] ); result[sx, sy] = sourceColour; /* * var i = 4 * (sx + (sy * width)); * fullBuffer[bufferOffset + i + 0] = targetRgba[sourcePixelOffset + 2]; * fullBuffer[bufferOffset + i + 1] = targetRgba[sourcePixelOffset + 1]; * fullBuffer[bufferOffset + i + 2] = targetRgba[sourcePixelOffset + 0]; * fullBuffer[bufferOffset + i + 3] = targetRgba[sourcePixelOffset + 3]; */ } sourcePixelOffset += 4; // Skip this pixel as it is outside the image. } } // advance blockOffset += bytesPerBlock; } } return(result); }
public static byte[] CompressBlockMasked(byte[] rgba, int mask, SquishOptions flags) { throw new NotImplementedException(); }
public static void DecompressImage(Stream input, Stream output, int width, int height, SquishOptions flags) { throw new NotImplementedException(); }
public static void DecompressBlock(Stream input, Stream output, SquishOptions flags) { throw new NotImplementedException(); }
public static void CompressBlockMasked(Stream input, Stream output, int mask, SquishOptions flags) { throw new NotImplementedException(); }
public static void CompressBlock(Stream input, Stream output, SquishOptions flags) { CompressBlockMasked(input, output, 0xFFFF, flags); }
public static byte[] CompressBlock(byte[] rgba, SquishOptions flags) { return(CompressBlockMasked(rgba, 0xFFFF, flags)); }
/// <summary> /// Decompresses a mipmap in the file at the specified level from the specified data. /// </summary> /// <returns>The mipmap.</returns> /// <param name="inData">Data containing the mipmap level.</param> /// <param name="mipLevel">The mipmap level of the data</param> private Bitmap DecompressMipMap(byte[] inData, uint mipLevel) { Bitmap map = null; uint targetXRes = GetLevelAdjustedResolutionValue(GetResolution().X, mipLevel); uint targetYRes = GetLevelAdjustedResolutionValue(GetResolution().Y, mipLevel); if (inData.Length > 0 && targetXRes > 0 && targetYRes > 0) { if (this.Header.CompressionType == TextureCompressionType.Palettized) { map = new Bitmap((int)targetXRes, (int)targetYRes, PixelFormat.Format32bppArgb); using (MemoryStream ms = new MemoryStream(inData)) { using (BinaryReader br = new BinaryReader(ms)) { // Read colour information for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { byte colorIndex = br.ReadByte(); Color paletteColor = this.Palette[colorIndex]; map.SetPixel(x, y, paletteColor); } } // Read Alpha information List <byte> alphaValues = new List <byte>(); if (GetAlphaBitDepth() > 0) { if (GetAlphaBitDepth() == 1) { int alphaByteCount = (int)Math.Ceiling(((double)(targetXRes * targetYRes) / 8)); alphaValues = Decode1BitAlpha(br.ReadBytes(alphaByteCount)); } else if (GetAlphaBitDepth() == 4) { int alphaByteCount = (int)Math.Ceiling(((double)(targetXRes * targetYRes) / 2)); alphaValues = Decode4BitAlpha(br.ReadBytes(alphaByteCount)); } else if (GetAlphaBitDepth() == 8) { // Directly read the alpha values for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { byte alphaValue = br.ReadByte(); alphaValues.Add(alphaValue); } } } } else { // The map is fully opaque for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { alphaValues.Add(255); } } } // Build the final map for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { int valueIndex = (int)(x + (targetXRes * y)); byte alphaValue = alphaValues[valueIndex]; Color pixelColor = map.GetPixel(x, y); Color finalPixel = Color.FromArgb(alphaValue, pixelColor.R, pixelColor.G, pixelColor.B); map.SetPixel(x, y, finalPixel); } } } } } else if (this.Header.CompressionType == TextureCompressionType.DXTC) { SquishOptions squishOptions = SquishOptions.DXT1; if (this.Header.PixelFormat == BLPPixelFormat.DXT3) { squishOptions = SquishOptions.DXT3; } else if (this.Header.PixelFormat == BLPPixelFormat.DXT5) { squishOptions = SquishOptions.DXT5; } map = (Bitmap)SquishCompression.DecompressToBitmap(inData, (int)targetXRes, (int)targetYRes, squishOptions); } else if (this.Header.CompressionType == TextureCompressionType.Uncompressed) { map = new Bitmap((int)targetXRes, (int)targetYRes, PixelFormat.Format32bppArgb); using (MemoryStream ms = new MemoryStream(inData)) { using (BinaryReader br = new BinaryReader(ms)) { for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { byte a = br.ReadByte(); byte r = br.ReadByte(); byte g = br.ReadByte(); byte b = br.ReadByte(); Color pixelColor = Color.FromArgb(a, r, g, b); map.SetPixel(x, y, pixelColor); } } } } } else if (this.Header.CompressionType == TextureCompressionType.JPEG) { // Merge the JPEG header with the data in the mipmap byte[] jpegImage = new byte[this.JPEGHeaderSize + inData.Length]; Buffer.BlockCopy(this.JPEGHeader, 0, jpegImage, 0, (int)this.JPEGHeaderSize); Buffer.BlockCopy(inData, 0, jpegImage, (int)this.JPEGHeaderSize, inData.Length); using (MemoryStream ms = new MemoryStream(jpegImage)) { map = new Bitmap(ms).Invert(); } } } return(map); }
public static byte[] DecompressImage(byte[] blocks, int width, int height, SquishOptions flags) { return(DecompressImage(blocks, 0, width, height, flags)); }
public static void CompressImage(Stream input, Stream output, int width, int height, SquishOptions flags) { throw new NotImplementedException(); }
public static byte[] DecompressImage(byte[] blocks, int offset, int width, int height, SquishOptions flags) { var argb = new byte[4 * width * height]; var bytesPerBlock = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16; var blockOffset = offset; // Loop over blocks. for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { // Decompress the block. var targetRgba = DecompressBlock(blocks, blockOffset, flags); // Write the decompressed pixels to the correct image locations. var sourcePixelOffset = 0; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // Get the target location. var sx = x + px; var sy = y + py; if (sx < width && sy < height) { var targetPixelOffset = 4 * ((width * sy) + sx); // Copy the rgba value argb[targetPixelOffset + 0] = targetRgba[sourcePixelOffset + 2]; argb[targetPixelOffset + 1] = targetRgba[sourcePixelOffset + 1]; argb[targetPixelOffset + 2] = targetRgba[sourcePixelOffset + 0]; argb[targetPixelOffset + 3] = targetRgba[sourcePixelOffset + 3]; } sourcePixelOffset += 4; } } // advance blockOffset += bytesPerBlock; } } return(argb); }
public static SquishOptions GetFit(this SquishOptions self) { return(self & (SquishOptions.ColourIterativeClusterFit | SquishOptions.ColourClusterFit | SquishOptions.ColourRangeFit)); }
public static SquishOptions GetExtra(this SquishOptions self) { return(self & (SquishOptions.WeightColourByAlpha)); }
public static Image DecompressToBitmap(byte[] blocks, int width, int height, SquishOptions flags) { return DecompressToBitmap(blocks, 0, width, height, flags); }
public static unsafe Image DecompressToBitmap(byte[] blocks, int offset, int width, int height, SquishOptions flags) { var fullBuffer = new byte[4 * width * height]; var bufferOffset = 0; var bytesPerBlock = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16; var blockOffset = offset; // Loop over blocks. for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { // Decompress the block. var targetRgba = DecompressBlock(blocks, blockOffset, flags); // Write the decompressed pixels to the correct image locations. var sourcePixelOffset = 0; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // Get the target location. var sx = x + px; var sy = y + py; if (sx < width && sy < height) { var i = 4 * (sx + (sy * width)); fullBuffer[bufferOffset + i + 0] = targetRgba[sourcePixelOffset + 2]; fullBuffer[bufferOffset + i + 1] = targetRgba[sourcePixelOffset + 1]; fullBuffer[bufferOffset + i + 2] = targetRgba[sourcePixelOffset + 0]; fullBuffer[bufferOffset + i + 3] = targetRgba[sourcePixelOffset + 3]; } sourcePixelOffset += 4; // Skip this pixel as it is outside the image. } } // advance blockOffset += bytesPerBlock; } } Image ret; fixed (byte* p = fullBuffer) { var ptr = (IntPtr)p; var tempImage = new Bitmap(width, height, 4 * width, System.Drawing.Imaging.PixelFormat.Format32bppArgb, ptr); ret = new Bitmap(tempImage); } return ret; }
public static unsafe Image DecompressToBitmap(byte[] blocks, int offset, int width, int height, SquishOptions flags) { var fullBuffer = new byte[4 * width * height]; var bufferOffset = 0; var bytesPerBlock = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16; var blockOffset = offset; // Loop over blocks. for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { // Decompress the block. var targetRgba = DecompressBlock(blocks, blockOffset, flags); // Write the decompressed pixels to the correct image locations. var sourcePixelOffset = 0; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // Get the target location. var sx = x + px; var sy = y + py; if (sx < width && sy < height) { var i = 4 * (sx + (sy * width)); fullBuffer[bufferOffset + i + 0] = targetRgba[sourcePixelOffset + 2]; fullBuffer[bufferOffset + i + 1] = targetRgba[sourcePixelOffset + 1]; fullBuffer[bufferOffset + i + 2] = targetRgba[sourcePixelOffset + 0]; fullBuffer[bufferOffset + i + 3] = targetRgba[sourcePixelOffset + 3]; } sourcePixelOffset += 4; // Skip this pixel as it is outside the image. } } // advance blockOffset += bytesPerBlock; } } Image ret; fixed(byte *p = fullBuffer) { var ptr = (IntPtr)p; var tempImage = new Bitmap(width, height, 4 * width, System.Drawing.Imaging.PixelFormat.Format32bppArgb, ptr); ret = new Bitmap(tempImage); } return(ret); }
public static byte[] CompressBlock(byte[] rgba, SquishOptions flags) { return CompressBlockMasked(rgba, 0xFFFF, flags); }
/// <summary> /// Decompresses a mipmap in the file at the specified level from the specified data. /// </summary> /// <returns>The mipmap.</returns> /// <param name="inData">ExtendedData containing the mipmap level.</param> /// <param name="mipLevel">The mipmap level of the data</param> private Image <Rgba32> DecompressMipMap(byte[] inData, uint mipLevel) { if (inData == null || inData.Length <= 0) { throw new ArgumentException("No image data provided.", nameof(inData)); } Image <Rgba32> map = null; uint targetXRes = GetLevelAdjustedResolutionValue(GetResolution().X, mipLevel); uint targetYRes = GetLevelAdjustedResolutionValue(GetResolution().Y, mipLevel); if (targetXRes <= 0 || targetYRes <= 0) { throw new ArgumentException($"The input mipmap level produced an invalid resolution: {mipLevel}", nameof(mipLevel)); } switch (this.Header.CompressionType) { case TextureCompressionType.Palettized: { map = new Image <Rgba32>((int)targetXRes, (int)targetYRes); using (MemoryStream ms = new MemoryStream(inData)) { using (BinaryReader br = new BinaryReader(ms)) { // Read colour information for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { byte colorIndex = br.ReadByte(); Rgba32 paletteColor = this.Palette[colorIndex]; map[x, y] = paletteColor; } } // Read Alpha information List <byte> alphaValues = new List <byte>(); if (GetAlphaBitDepth() > 0) { if (GetAlphaBitDepth() == 1) { int alphaByteCount = (int)Math.Ceiling(((double)(targetXRes * targetYRes) / 8)); alphaValues = Decode1BitAlpha(br.ReadBytes(alphaByteCount)); } else if (GetAlphaBitDepth() == 4) { int alphaByteCount = (int)Math.Ceiling(((double)(targetXRes * targetYRes) / 2)); alphaValues = Decode4BitAlpha(br.ReadBytes(alphaByteCount)); } else if (GetAlphaBitDepth() == 8) { // Directly read the alpha values for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { byte alphaValue = br.ReadByte(); alphaValues.Add(alphaValue); } } } } else { // The map is fully opaque for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { alphaValues.Add(255); } } } // Build the final map for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { int valueIndex = (int)(x + (targetXRes * y)); byte alphaValue = alphaValues[valueIndex]; Rgba32 pixelColor = map[x, y]; Rgba32 finalPixel = new Rgba32(pixelColor.R, pixelColor.G, pixelColor.B, alphaValue); map[x, y] = finalPixel; } } } } break; } case TextureCompressionType.DXTC: { SquishOptions squishOptions = SquishOptions.DXT1; if (this.Header.PixelFormat == BLPPixelFormat.DXT3) { squishOptions = SquishOptions.DXT3; } else if (this.Header.PixelFormat == BLPPixelFormat.DXT5) { squishOptions = SquishOptions.DXT5; } map = SquishCompression.DecompressToImage(inData, (int)targetXRes, (int)targetYRes, squishOptions); break; } case TextureCompressionType.Uncompressed: { map = new Image <Rgba32>((int)targetXRes, (int)targetYRes); using (MemoryStream ms = new MemoryStream(inData)) { using (BinaryReader br = new BinaryReader(ms)) { for (int y = 0; y < targetYRes; ++y) { for (int x = 0; x < targetXRes; ++x) { byte a = br.ReadByte(); byte r = br.ReadByte(); byte g = br.ReadByte(); byte b = br.ReadByte(); Rgba32 pixelColor = new Rgba32(r, g, b, a); map[x, y] = pixelColor; } } } } break; } case TextureCompressionType.JPEG: { // Merge the JPEG header with the data in the mipmap byte[] jpegImage = new byte[this.JPEGHeaderSize + inData.Length]; Buffer.BlockCopy(this.JPEGHeader, 0, jpegImage, 0, (int)this.JPEGHeaderSize); Buffer.BlockCopy(inData, 0, jpegImage, (int)this.JPEGHeaderSize, inData.Length); using (MemoryStream ms = new MemoryStream(jpegImage)) { map = Image.Load <Rgba32>(ms).Invert(); } break; } } return(map); }