public DDS(D3DFormat Format, Bitmap bitmap) { SquishFlags flags = SquishFlags.kDxt1; bool bCompressed = true; switch (Format) { case D3DFormat.DXT1: flags = SquishFlags.kDxt1; break; case D3DFormat.DXT3: flags = SquishFlags.kDxt3; break; case D3DFormat.DXT5: flags = SquishFlags.kDxt5; break; default: bCompressed = false; break; } format = Format; width = bitmap.Width; height = bitmap.Height; var mip = new MipMap(); mip.Width = width; mip.Height = height; byte[] data = new byte[mip.Width * mip.Height * 4]; BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, mip.Width, mip.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); Marshal.Copy(bmpdata.Scan0, data, 0, bmpdata.Stride * bmpdata.Height); bitmap.UnlockBits(bmpdata); if (bCompressed) { for (uint i = 0; i < data.Length - 4; i += 4) { byte r = data[i + 0]; data[i + 0] = data[i + 2]; data[i + 2] = r; } byte[] dest = new byte[Squish.Squish.GetStorageRequirements(mip.Width, mip.Height, flags | SquishFlags.kColourIterativeClusterFit | SquishFlags.kWeightColourByAlpha)]; Squish.Squish.CompressImage(data, mip.Width, mip.Height, ref dest, flags | SquishFlags.kColourIterativeClusterFit | SquishFlags.kWeightColourByAlpha); mip.Data = dest; } else { mip.Data = data; } mipMaps.Add(mip); }
public ClusterFit(ColourSet colours, SquishFlags flags) : base(colours, flags) { // set the iteration count m_iterationCount = ((m_flags & SquishFlags.kColourIterativeClusterFit) != 0) ? kMaxIterations : 1; // initialise the best error m_besterror = Vector4.one * float.MaxValue; // initialise the metric bool perceptual = ((m_flags & SquishFlags.kColourMetricPerceptual) != 0); if (perceptual) { m_metric = new Vector4(0.2126f, 0.7152f, 0.0722f, 0.0f); } else { m_metric = Vector4.one; } // cache some values int count = m_colours.GetCount(); Vector3[] values = m_colours.GetPoints(); // get the covariance matrix Sym3x3 covariance = math.ComputeWeightedCovariance(count, values, m_colours.GetWeights()); // compute the principle component m_principle = math.ComputePrincipleComponent(covariance); }
public ClusterFit(ColourSet colours, SquishFlags flags, float?metric) : base(colours, flags) { // set the iteration count _mIterationCount = (MFlags & SquishFlags.KColourIterativeClusterFit) != 0 ? 8 : 1; // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f) if (metric != null) { //m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f ); } else { _mMetric = new Vector4(1.0f); } // initialise the best error _mBesterror = new Vector4(float.MaxValue); // cache some values var count = MColours.Count; var values = MColours.Points; // get the covariance matrix var covariance = Sym3X3.ComputeWeightedCovariance(count, values, MColours.Weights); // compute the principle component _mPrinciple = Sym3X3.ComputePrincipleComponent(covariance); }
public ClusterFit(ColourSet colours, SquishFlags flags, float? metric) : base(colours, flags) { // set the iteration count m_iterationCount = ((m_flags & SquishFlags.kColourIterativeClusterFit) != 0 ? 8 : 1); // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f) if (metric != null) { //m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f ); } else { m_metric = new Vector4(1.0f); } // initialise the best error m_besterror = new Vector4(float.MaxValue); // cache some values int count = m_colours.Count; Vector3[] values = m_colours.Points; // get the covariance matrix Sym3x3 covariance = Sym3x3.ComputeWeightedCovariance(count, values, m_colours.Weights); // compute the principle component m_principle = Sym3x3.ComputePrincipleComponent(covariance); }
public Bitmap Decompress(int mipLevel = 0, bool suppressAlpha = false) { MipMap mip = MipMaps[mipLevel]; Bitmap b = new Bitmap(mip.Width, mip.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); SquishFlags flags = 0; bool notCompressed = false; switch (Format) { case D3DFormat.DXT1: flags = SquishFlags.kDxt1; break; case D3DFormat.DXT5: flags = SquishFlags.kDxt5; break; case D3DFormat.A8R8G8B8: notCompressed = true; break; default: throw new NotImplementedException($"Can't decompress: {Format}"); } byte[] dest = new byte[mip.Width * mip.Height * 4]; byte[] data = mip.Data; if (notCompressed) { for (uint i = 0; i < data.Length - 4; i += 4) { uint colour = (uint)((data[i + 3] << 24) | (data[i + 2] << 16) | (data[i + 1] << 8) | (data[i + 0] << 0)); dest[i + 0] = (byte)((colour & PixelFormat.BBitMask) >> 0); dest[i + 1] = (byte)((colour & PixelFormat.GBitMask) >> 8); dest[i + 2] = (byte)((colour & PixelFormat.RBitMask) >> 16); dest[i + 3] = (byte)((colour & PixelFormat.ABitMask) >> 24); } } else { Squish.Squish.DecompressImage(dest, mip.Width, mip.Height, ref data, flags); for (uint i = 0; i < dest.Length - 4; i += 4) { byte r = dest[i + 0]; dest[i + 0] = dest[i + 2]; dest[i + 2] = r; } } BitmapData bmpdata = b.LockBits(new Rectangle(0, 0, mip.Width, mip.Height), ImageLockMode.ReadWrite, (suppressAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppRgb : b.PixelFormat)); Marshal.Copy(dest, 0, bmpdata.Scan0, dest.Length); b.UnlockBits(bmpdata); return(b); }
public ClusterFit(ColourSet colours, SquishFlags flags, float?metric) : base(colours, flags) { // set the iteration count m_iterationCount = ((m_flags & SquishFlags.kColourIterativeClusterFit) != 0 ? 8 : 1); // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f) if (metric != null) { //m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f ); } else { m_metric = new Vector4(1.0f); } // initialise the best error m_besterror = new Vector4(float.MaxValue); // cache some values int count = m_colours.Count; Vector3[] values = m_colours.Points; // get the covariance matrix Sym3x3 covariance = Sym3x3.ComputeWeightedCovariance(count, values, m_colours.Weights); // compute the principle component m_principle = Sym3x3.ComputePrincipleComponent(covariance); }
private static void Decompress(byte[] rgba, ref byte[] block, int offset, SquishFlags flags) { // fix any bad flags flags = FixFlags(flags); // get the block locations var colourBlock = offset; var alphaBlock = offset; if ((flags & (SquishFlags.KDxt3 | SquishFlags.KDxt5)) != 0) { colourBlock += 8; } // decompress colour ColourBlock.DecompressColour(rgba, ref block, colourBlock, (flags & SquishFlags.KDxt1) != 0); // decompress alpha separately if necessary if ((flags & SquishFlags.KDxt3) != 0) { throw new NotImplementedException("Squish.DecompressAlphaDxt3"); //DecompressAlphaDxt3(rgba, alphaBlock); } if ((flags & SquishFlags.KDxt5) != 0) { DecompressAlphaDxt5(rgba, ref block, alphaBlock); } }
public ClusterFit(ColourSet colours, SquishFlags flags) : base(colours, flags) { // set the iteration count m_iterationCount = ((m_flags & SquishFlags.ColourIterativeClusterFit) != 0) ? kMaxIterations : 1; // initialise the best error m_besterror = new Vec4(float.MaxValue); // initialise the metric bool perceptual = ((m_flags & SquishFlags.ColourMetricPerceptual) != 0); if (perceptual) m_metric = new Vec4(0.2126f, 0.7152f, 0.0722f, 0.0f); else m_metric = new Vec4(1.0f); // cache some values int count = m_colours.GetCount(); Vec3[] values = m_colours.GetPoints(); // get the covariance matrix Sym3x3 covariance = Sym3x3.ComputeWeightedCovariance(count, values, m_colours.GetWeights()); // compute the principle component m_principle = Sym3x3.ComputePrincipleComponent(covariance); }
private static SquishFlags fixFlags(SquishFlags flags) { // // grab the flag bits // SquishFlags method = flags & (SquishFlags.Dxt1 | SquishFlags.Dxt3 | SquishFlags.Dxt5); SquishFlags fit = flags & (SquishFlags.ColourIterativeClusterFit | SquishFlags.ColourClusterFit | SquishFlags.ColourRangeFit); SquishFlags metric = flags & (SquishFlags.ColourMetricPerceptual | SquishFlags.ColourMetricUniform); SquishFlags extra = flags & SquishFlags.WeightColourByAlpha; // // set defaults // if (method != SquishFlags.Dxt3 && method != SquishFlags.Dxt5) { method = SquishFlags.Dxt1; } if (fit != SquishFlags.ColourRangeFit && fit != SquishFlags.ColourIterativeClusterFit) { fit = SquishFlags.ColourClusterFit; } if (metric != SquishFlags.ColourMetricUniform) { metric = SquishFlags.ColourMetricPerceptual; } // // done // return(method | fit | metric | extra); }
private static unsafe void decompress(byte *rgba, byte *block, SquishFlags flags) { // // get the block locations // byte *colourBlock = block; byte *alphaBock = block; if ((flags & (SquishFlags.Dxt3 | SquishFlags.Dxt5)) != 0) { colourBlock = block + 8; } // // decompress colour // decompressColour(rgba, colourBlock, (flags & SquishFlags.Dxt1) != 0); // // decompress alpha separately if necessary // if ((flags & SquishFlags.Dxt3) != 0) { decompressAlphaDxt3(rgba, alphaBock); } else if ((flags & SquishFlags.Dxt5) != 0) { decompressAlphaDxt5(rgba, alphaBock); } }
public RangeFit( ColourSet colours, SquishFlags flags ) : base(colours, flags) { // initialise the metric bool perceptual = ( ( m_flags & SquishFlags.kColourMetricPerceptual ) != 0 ); if( perceptual ) m_metric = new Vector3( 0.2126f, 0.7152f, 0.0722f ); else m_metric = Vector3.one; // initialise the best error m_besterror = float.MaxValue; // cache some values int count = m_colours.GetCount(); Vector3[] values = m_colours.GetPoints(); float[] weights = m_colours.GetWeights(); // get the covariance matrix Sym3x3 covariance = math.ComputeWeightedCovariance( count, values, weights ); // compute the principle component Vector3 principle = math.ComputePrincipleComponent( covariance ); // get the min and max range as the codebook endpoints Vector3 start = Vector3.zero; Vector3 end = Vector3.zero; if( count > 0 ) { float min, max; // compute the range start = end = values[0]; min = max = Vector3.Dot( values[0], principle ); for( int i = 1; i < count; ++i ) { float val = Vector3.Dot( values[i], principle ); if( val < min ) { start = values[i]; min = val; } else if( val > max ) { end = values[i]; max = val; } } } // clamp the output to [0, 1] start = Vector3.Min( Vector3.one,Vector3.Max( Vector3.zero, start ) ); end = Vector3.Min( Vector3.one, Vector3.Max( Vector3.zero, end ) ); // clamp to the grid and save Vector3 half = Vector3.one*( 0.5f ); m_start = Vector3.Scale(math.Truncate( Vector3.Scale(grid,start) + half ),gridrcp); m_end = Vector3.Scale(math.Truncate( Vector3.Scale(grid,end) + half ),gridrcp); }
internal static bool WriteTo(Texture2D cacheTexture, string cacheFile, bool compress) { String directory = Path.GetDirectoryName(cacheFile + ".none"); if (File.Exists(directory)) { File.Delete(directory); } Directory.CreateDirectory(directory); FileStream imgStream = new FileStream(cacheFile, FileMode.Create, FileAccess.Write); imgStream.Position = 0; //byte[] png = cacheTexture.EncodeToPNG(); byte[] img = cacheTexture.bytes(0); SquishFlags compression = SquishFlags.kDxt5; TextureFormat format = TextureFormat.DXT5; bool hasAlpha = texHasAlpha(img); if (!hasAlpha) { compression = SquishFlags.kDxt1; format = TextureFormat.DXT1; } for (int i = 0; i < cacheTexture.mipmapCount; i++) { int width = Math.Max(1, cacheTexture.width >> i); int height = Math.Max(1, cacheTexture.height >> i); if (compress) { if (i != 0) { img = cacheTexture.bytes(i); } int size = squish.GetStorageRequirements(width, height, compression); if (DatabaseLoaderTexture_ATM.UseSquish) { squish.CompressImage(img, width, height, imageBuffer, compression | SquishFlags.kColourIterativeClusterFit | SquishFlags.kWeightColourByAlpha); } else { TextureToolsDXT.GetDXT(cacheTexture, i, imageBuffer, format); } imgStream.Write(imageBuffer, 0, size); } else { img = cacheTexture.bytes(i, hasAlpha); imgStream.Write(img, 0, img.Length); } if (width == 1 || height == 1) { break; } } imgStream.Close(); return(hasAlpha); }
private static unsafe void compressMasked(byte *rgba, int mask, byte *block, SquishFlags flags) { // // get the block locations // byte *colourBlock = block; byte *alphaBlock = block; if ((flags & (SquishFlags.Dxt3 | SquishFlags.Dxt5)) != 0) { colourBlock = block + 8; } // // create the minimal point set // ColourSet colours = new ColourSet(rgba, mask, flags); // // check the compression type and compress colour // if (colours.Count == 1) { // // always do a single colour fit // SingleColourFit fit = new SingleColourFit(colours, flags); fit.Compress(colourBlock); } else if ((flags & SquishFlags.ColourRangeFit) != 0 || colours.Count == 0) { // // do a range fit // RangeFit fit = new RangeFit(colours, flags); fit.Compress(colourBlock); } else { // // default to a cluster fit (could be iterative or not) // ClusterFit fit = new ClusterFit(colours, flags); fit.Compress(colourBlock); } // // compress alpha separately if necessary // if ((flags & SquishFlags.Dxt3) != 0) { compressAlphaDxt3(rgba, mask, alphaBlock); } else if ((flags & SquishFlags.Dxt5) != 0) { compressAlphaDxt5(rgba, mask, alphaBlock); } }
public static void CompressImage(byte[] rgba, int width, int height, ref byte[] blocks, SquishFlags flags) { // fix any bad flags flags = FixFlags(flags); // initialise the block output int targetBlock = 0; int bytesPerBlock = ((flags & SquishFlags.kDxt1) != 0) ? 8 : 16; // loop over blocks for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { // build the 4x4 block of pixels byte[] sourceRgba = new byte[16 * 4]; byte targetPixel = 0; int mask = 0; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // get the source pixel in the image int sx = x + px; int sy = y + py; // enable if we're in the image if (sx < width && sy < height) { // copy the rgba value for (int i = 0; i < 4; ++i) { sourceRgba[targetPixel] = rgba[i + 4 * (width * sy + sx)]; targetPixel++; } // enable this pixel mask |= (1 << (4 * py + px)); } else { // skip this pixel as its outside the image targetPixel += 4; } } } // compress it into the output CompressMasked(sourceRgba, mask, ref blocks, targetBlock, flags, null); // advance targetBlock += bytesPerBlock; } } }
public static int GetStorageRequirements(int width, int height, SquishFlags flags) { // fix any bad flags flags = FixFlags(flags); // compute the storage requirements var blockcount = (width + 3) / 4 * ((height + 3) / 4); var blocksize = (flags & SquishFlags.KDxt1) != 0 ? 8 : 16; return(blockcount * blocksize); }
/// <summary> /// Decompresses a 4x4 block of pixels. /// </summary> /// <param name="block">The compressed DXT block.</param> /// <param name="width">The width of the source image.</param> /// <param name="height">The height of the source image.</param> /// <param name="flags">Compression flags.</param> /// <returns>Byte array containing the 16 decompressed pixels.</returns> /// <remarks>The decompressed pixels will be written as a contiguous array of 16 rgba /// values, with each component as 1 byte each. In memory this is: /// <code>{ r1, g1, b1, a1, .... , r16, g16, b16, a16 }</code> /// The <paramref name="flags"/> parameter should specify either <c>kDxt1</c>, <c>kDxt3</c> or <c>kDxt5</c> compression, /// however, DXT1 will be used by default if none is specified. All other flags are ignored.</remarks> public static byte[] Decompress(byte[] block, SquishFlags flags) { // Allocate room for decompressed output byte[] pixelOutput = new byte[16]; // Invoke squish::Decompress() with the required parameters SquishDecompress(pixelOutput, block, flags); // Return our pixel data to caller.. return(pixelOutput); }
/// <summary> /// Decompresses an image in memory. /// </summary> /// <param name="blocks">The compressed DXT blocks.</param> /// <param name="width">The width of the source image.</param> /// <param name="height">The height of the source image.</param> /// <param name="flags">Compression flags.</param> /// <returns>Byte array containing the decompressed pixels.</returns> /// <remarks>The decompressed pixels will be written as a contiguous array of width*height /// 16 rgba values, with each component as 1 byte each. In memory this is: /// <code>{ r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height</code> /// The <paramref name="flags"/> parameter should specify either <c>kDxt1</c>, <c>kDxt3</c> or <c>kDxt5</c> compression, /// however, DXT1 will be used by default if none is specified. All other flags are ignored.</remarks> public static byte[] DecompressImage(byte[] blocks, int width, int height, SquishFlags flags) { // Allocate room for decompressed output byte[] pixelOutput = new byte[width * height * 4]; // Invoke squish::DecompressImage() with the required parameters SquishDecompressImage(pixelOutput, width, height, blocks, flags); // Return our pixel data to caller.. return(pixelOutput); }
public SingleColourFit(ColourSet colours, SquishFlags flags) : base(colours, flags) { // grab the single colour Vector3[] values = m_colours.Points; m_colour[0] = (byte)ColourBlock.FloatToInt(255.0f * values[0].X, 255); m_colour[1] = (byte)ColourBlock.FloatToInt(255.0f * values[0].Y, 255); m_colour[2] = (byte)ColourBlock.FloatToInt(255.0f * values[0].Z, 255); // initialise the best error m_besterror = int.MaxValue; }
/// <summary> /// Compresses a 4x4 block of pixels. /// </summary> /// <param name="rgba">The rgba values of the 16 source pixels.</param> /// <param name="flags">Compression flags.</param> /// <returns>Return the Compressed Block Data.</returns> public static byte[] Compress(byte[] rgba, SquishFlags flags) { int bytesPerBlock = ((flags & SquishFlags.Dxt1) != 0) ? 8 : 16; byte[] output = new byte[bytesPerBlock]; unsafe { fixed (byte* lpIn = rgba, lpOut = output) { Compress(lpIn, lpOut, flags); } } return output; }
/// <summary> /// Compresses a 4x4 block of pixels. /// </summary> /// <param name="pixelInput">The rgba values of the 16 source pixels.</param> /// <param name="flags">Compression flags.</param> /// <param name="metric">An optional perceptual metric.</param> /// <returns>Byte array containing the compressed DXT block.</returns> /// <remarks> /// <para>The source pixels should be presented as a contiguous array of 16 rgba /// values, with each component as 1 byte each. In memory this should be: /// rgba values, with each component as 1 byte each. In memory this should be: /// <c>{ r1, g1, b1, a1, .... , rn, gn, bn, an }</c> for n = width*height /// </para> /// <para> /// The <paramref name="flags"/> parameter should specify either <c>kDxt1</c>, <c>kDxt3</c> or <c>kDxt5</c> compression, /// however, DXT1 will be used by default if none is specified. When using DXT1 /// compression, 8 bytes of storage are required for each compressed DXT block. /// DXT3 and DXT5 compression require 16 bytes of storage per block. /// </para> /// <para> /// The <paramref name="flags"/> parameter can also specify a preferred colour compressor to use /// when fitting the RGB components of the data. Possible colour compressors /// are: <c>kColourClusterFit</c> (the default), <c>kColourRangeFit</c> (very fast, low /// quality) or <c>kColourIterativeClusterFit</c> (slowest, best quality). /// </para> /// <para> /// When using <c>kColourClusterFit</c> or <c>kColourIterativeClusterFit</c>, an additional /// flag can be specified to weight the importance of each pixel by its alpha /// value. For images that are rendered using alpha blending, this can /// significantly increase the perceived quality.</para> /// <para> /// The <paramref name="metric"/> parameter can be used to weight the relative importance of each /// colour channel, or pass NULL to use the default uniform weight of /// <c>{ 1.0f, 1.0f, 1.0f }</c>. This replaces the previous flag-based control that /// allowed either uniform or "perceptual" weights with the fixed values /// <c>{ 0.2126f, 0.7152f, 0.0722f }</c>. If non-NULL, the metric should point to a /// contiguous array of 3 floats. /// </para> /// </remarks> public static byte[] Compress(byte[] pixelInput, SquishFlags flags, float[] metric = null) { if (metric != null && metric.Length != 3) { throw new ArgumentException("Non-null metric must reference an array of three floats."); } byte[] block = new byte[(flags & SquishFlags.kDxt1) != 0 ? 8 : 16]; SquishCompress(pixelInput, block, flags, metric); return(block); }
private static unsafe void CompressImageBlockLine(int width, int height, SquishFlags flags, int y, byte *prgba, byte *targetBlockPtr, int bytesPerBlock) { byte *targetBlock = targetBlockPtr; for (int x = 0; x < width; x += 4) { // build the 4x4 block of pixels byte[] sourceRgba = new byte[16 * 4]; fixed(byte *psourceRgba = sourceRgba) { byte *targetPixel = psourceRgba; int mask = 0; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // get the source pixel in the image int sx = x + px; int sy = y + py; // enable if we're in the image if (sx < width && sy < height) { // copy the rgba value byte *sourcePixel = prgba + 4 * (width * sy + sx); for (int i = 0; i < 4; ++i) { *targetPixel++ = *sourcePixel++; } // enable this pixel mask |= (1 << (4 * py + px)); } else { // skip this pixel as its outside the image targetPixel += 4; } } } // compress it into the output CompressMasked(sourceRgba, mask, targetBlock, flags); } // advance targetBlock += bytesPerBlock; } }
public SingleColourFit(ColourSet colours, SquishFlags flags) : base(colours, flags) { // // grab the single colour // Vec3[] values = colours.Points; colour[0] = (byte)ColourBlock.FloatToInt(255.0f * values[0].X, 255); colour[1] = (byte)ColourBlock.FloatToInt(255.0f * values[0].Y, 255); colour[2] = (byte)ColourBlock.FloatToInt(255.0f * values[0].Z, 255); // // initialise the best error // bestError = Int32.MaxValue; }
/// <summary> /// Compress a 4x4 pixel block using the parameters specified in <paramref name="flags"/>. The <paramref name="mask"/> parameter is a used as /// a bit mask to specifify what pixels are valid for compression, corresponding the lowest bit to the first pixel. /// </summary> /// <param name="rgba">Source RGBA block.</param> /// <param name="mask">Pixel bit mask.</param> /// <param name="flags">Compression flags.</param> /// <returns>Output DXT compressed block.</returns> public static byte[] CompressMasked(byte[] rgba, int mask, SquishFlags flags) { byte[] compressedData = new byte[GetStorageRequirements(4, 4, flags)]; GCHandle pinnedData = GCHandle.Alloc(compressedData, GCHandleType.Pinned); GCHandle pinnedRgba = GCHandle.Alloc(rgba, GCHandleType.Pinned); CompressMaskedFunction(pinnedRgba.AddrOfPinnedObject(), mask, pinnedData.AddrOfPinnedObject(), (int)flags); pinnedRgba.Free(); pinnedData.Free(); return(compressedData); }
/// <summary> /// Decompresses a 4x4 pixel block using the parameters specified in <paramref name="flags"/>. /// </summary> /// <param name="block">Source DXT block.</param> /// <param name="flags">Decompression flags.</param> /// <returns>Output RGBA decompressed block.</returns> public static byte[] Decompress(byte[] block, SquishFlags flags) { byte[] decompressedData = new byte[4 * 4 * 4]; GCHandle pinnedData = GCHandle.Alloc(decompressedData, GCHandleType.Pinned); GCHandle pinnedBlock = GCHandle.Alloc(block, GCHandleType.Pinned); DecompressFunction(pinnedData.AddrOfPinnedObject(), pinnedBlock.AddrOfPinnedObject(), (int)flags); pinnedBlock.Free(); pinnedData.Free(); return(decompressedData); }
/// <summary> /// Compresses an image using the parameters specified in <paramref name="flags"/>. /// </summary> /// <param name="rgba">Source RGBA image.</param> /// <param name="width">Width of the image.</param> /// <param name="height">Height of the image.</param> /// <param name="flags">Compression flags.</param> /// <returns>Output DXT compressed image.</returns> public static byte[] CompressImage(byte[] rgba, int width, int height, SquishFlags flags) { byte[] compressedData = new byte[GetStorageRequirements(width, height, flags)]; GCHandle pinnedData = GCHandle.Alloc(compressedData, GCHandleType.Pinned); GCHandle pinnedRgba = GCHandle.Alloc(rgba, GCHandleType.Pinned); CompressImageFunction(pinnedRgba.AddrOfPinnedObject(), width, height, pinnedData.AddrOfPinnedObject(), (int)flags); pinnedRgba.Free(); pinnedData.Free(); return(compressedData); }
/// <summary> /// Decompresses an image using the parameters specified in <paramref name="flags"/>. /// </summary> /// <param name="blocks">Source DXT compressed image.</param> /// <param name="width">Width of the image.</param> /// <param name="height">Height of the image.</param> /// <param name="flags">Decompression flags.</param> /// <returns>Output RGBA decompressed image.</returns> public static byte[] DecompressImage(byte[] blocks, int width, int height, SquishFlags flags) { byte[] decompressedData = new byte[width * height * 4]; GCHandle pinnedData = GCHandle.Alloc(decompressedData, GCHandleType.Pinned); GCHandle pinnedBlocks = GCHandle.Alloc(blocks, GCHandleType.Pinned); DecompressImageFunction(pinnedData.AddrOfPinnedObject(), width, height, pinnedBlocks.AddrOfPinnedObject(), (int)flags); pinnedBlocks.Free(); pinnedData.Free(); return(decompressedData); }
private static unsafe void SquishDecompress(byte[] rgba, byte[] block, SquishFlags flags) { fixed(byte *pRGBA = rgba) fixed(byte *pBlock = block) { if (Is64Bit()) { SquishInterface_64.SquishDecompress(pRGBA, pBlock, (int)flags); } else { SquishInterface_32.SquishDecompress(pRGBA, pBlock, (int)flags); } } }
/// <summary> /// Compresses an image in memory. /// </summary> /// <param name="pixelInput">The pixels of the source.</param> /// <param name="width">The width of the source image.</param> /// <param name="height">The height of the source image.</param> /// <param name="flags">Compression flags.</param> /// <param name="metric">An optional perceptual metric.</param> /// <returns>Byte array containing the compressed output.</returns> /// <remarks> /// <para> /// The source pixels should be presented as a contiguous array of width*height /// rgba values, with each component as 1 byte each. In memory this should be: /// <c>{ r1, g1, b1, a1, .... , rn, gn, bn, an }</c> for n = width*height /// </para> /// <para> /// The <paramref name="flags"/> parameter should specify either <c>kDxt1</c>, <c>kDxt3</c> or <c>kDxt5</c> compression, /// however, DXT1 will be used by default if none is specified. When using DXT1 /// compression, 8 bytes of storage are required for each compressed DXT block. /// DXT3 and DXT5 compression require 16 bytes of storage per block. /// </para> /// <para> /// The <paramref name="flags"/> parameter can also specify a preferred colour compressor to use /// when fitting the RGB components of the data. Possible colour compressors /// are: <c>kColourClusterFit</c> (the default), <c>kColourRangeFit</c> (very fast, low /// quality) or <c>kColourIterativeClusterFit</c> (slowest, best quality). /// </para> /// <para> /// When using <c>kColourClusterFit</c> or <c>kColourIterativeClusterFit</c>, an additional /// flag can be specified to weight the importance of each pixel by its alpha /// value. For images that are rendered using alpha blending, this can /// significantly increase the perceived quality.</para> /// <para> /// The <paramref name="metric"/> parameter can be used to weight the relative importance of each /// colour channel, or pass NULL to use the default uniform weight of /// <c>{ 1.0f, 1.0f, 1.0f }</c>. This replaces the previous flag-based control that /// allowed either uniform or "perceptual" weights with the fixed values /// <c>{ 0.2126f, 0.7152f, 0.0722f }</c>. If non-NULL, the metric should point to a /// contiguous array of 3 floats. /// </para> /// </remarks> public static byte[] CompressImage(byte[] pixelInput, int width, int height, SquishFlags flags, float[] metric = null) { if (metric != null && metric.Length != 3) { throw new ArgumentException("Non-null metric must reference an array of three floats."); } //int storageRequirements = GetStorageRequirements(width, height, flags); byte[] blocks = new byte[((width + 3) >> ((flags & DdsSquish.SquishFlags.kDxt1) == 0 ? 2 : 3)) * 4 * height]; // Invoke squish::CompressImage() with the required parameters SquishCompressImage(pixelInput, width, height, blocks, flags, metric); return(blocks); }
static unsafe void CompressMasked(byte[] rgba, int mask, byte *pBlock, SquishFlags flags) { // fix any bad flags flags = FixFlags(flags); // get the block locations byte *colourBlock = pBlock; byte *alphaBock = pBlock; if ((flags & (SquishFlags.kDxt3 | SquishFlags.kDxt5)) != 0) { colourBlock = pBlock + 8; } // create the minimal point set ColourSet colours = new ColourSet(rgba, mask, flags); // check the compression type and compress colour if (colours.GetCount() == 1) { // always do a single colour fit SingleColourFit fit = new SingleColourFit(colours, flags); fit.Compress(colourBlock); } else if ((flags & SquishFlags.kColourRangeFit) != 0 || colours.GetCount() == 0) { // do a range fit RangeFit fit = new RangeFit(colours, flags); fit.Compress(colourBlock); } else { // default to a cluster fit (could be iterative or not) ClusterFit fit = new ClusterFit(colours, flags); fit.Compress(colourBlock); } // compress alpha separately if necessary if ((flags & SquishFlags.kDxt3) != 0) { alpha.CompressAlphaDxt3(rgba, mask, alphaBock); } else if ((flags & SquishFlags.kDxt5) != 0) { alpha.CompressAlphaDxt5(rgba, mask, alphaBock); } }
private static unsafe void SquishCompress(byte[] rgba, byte[] block, SquishFlags flags, float[] metric) { fixed(byte *_rgba = rgba) fixed(byte *_block = block) fixed(float *_metric = metric) { if (Is64Bit()) { SquishInterface_64.SquishCompress(_rgba, _block, (int)flags, _metric); } else { SquishInterface_32.SquishCompress(_rgba, _block, (int)flags, _metric); } } }
public CompressImageBlockLineArgs( int width, int height, SquishFlags flags, int y, byte *prgba, byte *targetBlock, int bytesPerBlock, ManualResetEvent doneEvent) { this.width = width; this.height = height; this.flags = flags; this.y = y; this.prgba = prgba; this.targetBlock = targetBlock; this.bytesPerBlock = bytesPerBlock; this.doneEvent = doneEvent; }
private static SquishFlags FixFlags(SquishFlags flags) { // grab the flag bits var method = flags & (SquishFlags.KDxt1 | SquishFlags.KDxt3 | SquishFlags.KDxt5); var fit = flags & (SquishFlags.KColourIterativeClusterFit | SquishFlags.KColourClusterFit | SquishFlags.KColourRangeFit); var extra = flags & SquishFlags.KWeightColourByAlpha; // set defaults if (method != SquishFlags.KDxt3 && method != SquishFlags.KDxt5) { method = SquishFlags.KDxt1; } if (fit != SquishFlags.KColourRangeFit && fit != SquishFlags.KColourIterativeClusterFit) { fit = SquishFlags.KColourClusterFit; } // done return(method | fit | extra); }
public static unsafe void CompressImage(byte[] rgba, int width, int height, byte[] blocks, SquishFlags flags, bool waitOnDone) { // fix any bad flags flags = FixFlags(flags); // holds the handles to know which thread are free. // No more than 50 or Mono commplains. // On my machine Environment.ProcessorCount give me a nice 98% CPU ManualResetEvent[] doneEvents = new ManualResetEvent[Environment.ProcessorCount]; for (int i = 0; i < doneEvents.Length; i++) { doneEvents[i] = new ManualResetEvent(true); } // initialise the block output fixed (byte* pblocks = blocks, prgba = rgba) { int bytesPerBlock = ((flags & SquishFlags.kDxt1) != 0) ? 8 : 16; byte* targetBlock = pblocks; // loop over blocks for (int y = 0; y < height; y += 4) { int threadIdx = GetFreeThreadIdx(doneEvents); doneEvents[threadIdx] = new ManualResetEvent(false); CompressImageBlockLineArgs args = new CompressImageBlockLineArgs(width, height, flags, y, prgba, targetBlock, bytesPerBlock, doneEvents[threadIdx]); targetBlock += bytesPerBlock * ((width >> 2) + ((width & 0x3) != 0 ? 1 : 0)); // To debug un comment this line and comment the next one // Threads catch the exceptions (add a try catch ? ) //CompressImageBlockLineThread(args); if (waitOnDone && y > (4*Environment.ProcessorCount)) { WaitHandle.WaitAny(doneEvents); } ThreadPool.QueueUserWorkItem(CompressImageBlockLineThread, args); } WaitHandle.WaitAll(doneEvents); } }
/// <summary> /// Compresses a 4x4 block of pixels in BCn format. /// </summary> /// <param name="rgba">The rgba values of the 16 source pixels.</param> /// <param name="mask">The valid pixel mask.</param> /// <param name="block">Storage for the compressed BCn block.</param> /// <param name="flags">Compression flags.</param> public static unsafe void CompressMaskedBC(byte* rgba, int mask, byte* block, SquishFlags flags) { if ((flags & SquishFlags.BC1) != 0) { SquishFlags cflags = flags; cflags = cflags & ~SquishFlags.BC1; cflags |= SquishFlags.Dxt1; CompressMasked(rgba, mask, block, cflags); } else if ((flags & SquishFlags.BC2) != 0) { SquishFlags cflags = flags; cflags = cflags & ~SquishFlags.BC2; cflags |= SquishFlags.Dxt3; CompressMasked(rgba, mask, block, cflags); } else if ((flags & SquishFlags.BC3) != 0) { SquishFlags cflags = flags; cflags = cflags & ~SquishFlags.BC3; cflags |= SquishFlags.Dxt5; CompressMasked(rgba, mask, block, cflags); } else if ((flags & SquishFlags.BC4) != 0) { AlphaBlock.CompressAlphaDxt5(rgba, mask, block); } else if ((flags & SquishFlags.BC5) != 0) { byte* xBlock = rgba; byte* yBlock = rgba; ColourBlock.SplatX(xBlock); ColourBlock.SplatY(yBlock); byte* outBlockX = block; byte* outBlockY = block + 8; AlphaBlock.CompressAlphaDxt5(xBlock, mask, outBlockX); AlphaBlock.CompressAlphaDxt5(yBlock, mask, outBlockY); } }
/// <summary> /// Compresses an image in memory. /// </summary> /// <param name="rgba">The pixels of the source.</param> /// <param name="width">The width of the source image.</param> /// <param name="height">The height of the source image.</param> /// <param name="flags">Compression flags.</param> /// <returns>Return the Compressed Image Data.</returns> public static byte[] CompressImage(byte[] rgba, int width, int height, SquishFlags flags) { byte[] output = new byte[GetStorageRequirements(width, height, flags)]; unsafe { fixed (byte* lpIn = rgba, lpOut = output) { CompressImage(lpIn, width, height, lpOut, flags); } } return output; }
/// <summary> /// Compresses an image in memory. /// </summary> /// <param name="rgba">The pixels of the source.</param> /// <param name="width">The width of the source image.</param> /// <param name="height">The height of the source image.</param> /// <param name="blocks">Storage for the compressed output.</param> /// <param name="flags">Compression flags.</param> public static unsafe void CompressImage(byte* rgba, int width, int height, byte* blocks, SquishFlags flags) { bool b_cancel = false; // fix any bad flags flags = FixFlags(flags); // initialise the block output byte* targetBlock = blocks; int bytesPerBlock = BytesPerBlock(flags); int current = 0, blocksCount = GetBlocksCount(width, height); // loop over blocks for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { // build the 4x4 block of pixels byte[] sourceRgba = new byte[16 * 4]; fixed (byte* fixSourceRgba = sourceRgba) { byte* targetPixel = fixSourceRgba; //byte* targetPixel = sourceRgba; int mask = 0; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // get the source pixel in the image int sx = x + px; int sy = y + py; // enable if we're in the image if (sx < width && sy < height) { // copy the rgba value byte* sourcePixel = rgba + 4 * (width * sy + sx); for (int i = 0; i < 4; ++i) *targetPixel++ = *sourcePixel++; // enable this pixel mask |= (1 << (4 * py + px)); } else { // skip this pixel as its outside the image targetPixel += 4; } } } // compress it into the output CompressMasked(fixSourceRgba, mask, targetBlock, flags); } if (BlockAdvance != null) { SharpSquishBlockAdvanceEventArgs e = new SharpSquishBlockAdvanceEventArgs(); e.Current = current; e.Blocks = blocksCount; BlockAdvance(e); if (e.Cancel) { b_cancel = true; break; } } // advance targetBlock += bytesPerBlock; current++; } if (b_cancel) break; } }
private static SquishFlags FixFlags(SquishFlags flags) { // grab the flag bits SquishFlags method = flags & (SquishFlags.Dxt1 | SquishFlags.Dxt3 | SquishFlags.Dxt5 | SquishFlags.BC1 | SquishFlags.BC2 | SquishFlags.BC3 | SquishFlags.BC4 | SquishFlags.BC5); SquishFlags fit = flags & (SquishFlags.ColourIterativeClusterFit | SquishFlags.ColourClusterFit | SquishFlags.ColourRangeFit); SquishFlags metric = flags & (SquishFlags.ColourMetricPerceptual | SquishFlags.ColourMetricUniform); SquishFlags extra = flags & SquishFlags.WeightColourByAlpha; // set defaults if (method != SquishFlags.Dxt3 && method != SquishFlags.Dxt5 && method != SquishFlags.BC1 && method != SquishFlags.BC2 && method != SquishFlags.BC3 && method != SquishFlags.BC4 && method != SquishFlags.BC5) method = SquishFlags.Dxt1; if (fit != SquishFlags.ColourRangeFit) fit = SquishFlags.ColourClusterFit; if (metric != SquishFlags.ColourMetricUniform) metric = SquishFlags.ColourMetricPerceptual; // done return method | fit | metric | extra; }
public static int BytesPerBlock(SquishFlags flags) { if ((flags & (SquishFlags.Dxt1 | SquishFlags.BC1 | SquishFlags.BC4)) != 0) return 8; return 16; }
private static unsafe void SquishCompressImage(byte[] rgba, int width, int height, byte[] blocks, SquishFlags flags, float[] metric) { fixed(byte *_rgba = rgba) fixed(byte *_blocks = blocks) fixed(float *_metric = metric) { if (Is64Bit()) { SquishInterface_64.SquishCompressImage(_rgba, width, height, _blocks, (int)flags, _metric); } else { SquishInterface_32.SquishCompressImage(_rgba, width, height, _blocks, (int)flags, _metric); } } }
private static unsafe void compressMasked(byte *rgba, int mask, byte *block, SquishFlags flags) { // // get the block locations // byte *colourBlock = block; byte *alphaBlock = block; if ((flags & (SquishFlags.Dxt3 | SquishFlags.Dxt5)) != 0) colourBlock = block + 8; // // create the minimal point set // ColourSet colours = new ColourSet(rgba, mask, flags); // // check the compression type and compress colour // if (colours.Count == 1) { // // always do a single colour fit // SingleColourFit fit = new SingleColourFit(colours, flags); fit.Compress(colourBlock); } else if ((flags & SquishFlags.ColourRangeFit) != 0 || colours.Count == 0) { // // do a range fit // RangeFit fit = new RangeFit(colours, flags); fit.Compress(colourBlock); } else { // // default to a cluster fit (could be iterative or not) // ClusterFit fit = new ClusterFit(colours, flags); fit.Compress(colourBlock); } // // compress alpha separately if necessary // if ((flags & SquishFlags.Dxt3) != 0) compressAlphaDxt3(rgba, mask, alphaBlock); else if ((flags & SquishFlags.Dxt5) != 0) compressAlphaDxt5(rgba, mask, alphaBlock); }
public unsafe ColourSet(byte *rgba, int mask, SquishFlags flags) { Count = 0; IsTransparent = false; // // check the compression mode for dxt1 // bool isDxt1 = (flags & SquishFlags.Dxt1) != 0; bool weightByAlpha = (flags & SquishFlags.WeightColourByAlpha) != 0; // // create the minimal set // for (int i = 0; i < 16; ++i) { // // check this pixel is enabled // int bit = 1 << i; if ((mask & bit) == 0) { remap[i] = -1; continue; } // // check for transparent pixels when using dxt1 // if (isDxt1 && rgba[4 * i + 3] < 128) { remap[i] = -1; IsTransparent = true; continue; } // // loop over previous points for a match // for(int j = 0; ; ++j) { // // allocate a new point // if (j == i) { // // normalise coordinates to [0,1] // float x = rgba[4 * i + 0] / 255.0f; float y = rgba[4 * i + 1] / 255.0f; float z = rgba[4 * i + 2] / 255.0f; // // ensure there is always non-zero weight even for zero alpha // float w = (rgba[4 * i + 3] + 1) / 256.0f; // // add the point // Points[Count] = new Vec3(x, y, z); Weights[Count] = weightByAlpha ? w : 1.0f; remap[i] = Count; // // advance // ++Count; break; } // // check for a match // int oldbit = 1 << j; bool match = ((mask & oldbit) != 0) && (rgba[4 * i + 0] == rgba[4 * j]) && (rgba[4 * i + 1] == rgba[4 * j + 1]) && (rgba[4 * i + 2] == rgba[4 * j + 2]) && (rgba[4 * j + 3] >= 128 || !isDxt1); if (match) { // // get the index of the match // int index = remap[j]; // // ensure there is always non-zero weight even for zero alpha // float w = (rgba[4 * i + 3] + 1) / 256.0f; // // map to this point and increase the weight // Weights[index] += weightByAlpha ? w : 1.0f; remap[i] = index; break; } } } // // square root the weights // for (int i = 0; i < Count; ++i) Weights[i] = (float)Math.Sqrt(Weights[i]); }
/// <summary> /// Compress a 4x4 pixel block using the parameters specified in <paramref name="flags"/>. /// </summary> /// <param name="rgba">Source RGBA block.</param> /// <param name="block">Output DXT compressed block.</param> /// <param name="flags">Compression flags.</param> public static void Compress(IntPtr rgba, IntPtr block, SquishFlags flags) { CompressFunction(rgba, block, (int)flags); }
/// <summary> /// Decompresses a 4x4 block of pixels. /// </summary> /// <param name="data">The compressed DXT block.</param> /// <param name="flags">Compression flags: /// /// The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, /// however, DXT1 will be used by default if none is specified. All other flags /// are ignored.</param> /// <returns>Return the decompressed block data.</returns> public static byte[] Decompress(byte[] data, SquishFlags flags) { byte[] output = new byte[16 * 4]; unsafe { fixed (byte* lpIn = data, lpOut = output) { Decompress(lpOut, lpIn, flags); } } return output; }
public ColourFit(ColourSet colours, SquishFlags flags) { m_colours = colours; m_flags = flags; }
/// <summary> /// Decompresses an image in memory. /// </summary> /// <param name="rgba">Storage for the decompressed pixels.</param> /// <param name="width">The width of the source image.</param> /// <param name="height">The height of the source image.</param> /// <param name="blocks">The compressed DXT blocks.</param> /// <param name="flags">Compression flags: /// /// The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, /// however, DXT1 will be used by default if none is specified. All other flags /// are ignored.</param> public static unsafe void DecompressImage(byte* rgba, int width, int height, byte* blocks, SquishFlags flags) { // fix any bad flags flags = FixFlags(flags); // initialise the block input byte* sourceBlock = blocks; int bytesPerBlock = BytesPerBlock(flags); // loop over blocks for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { // decompress the block byte[] targetRgba = new byte[4 * 16]; fixed (byte* fixTargetRgba = targetRgba) { Decompress(fixTargetRgba, sourceBlock, flags); // write the decompressed pixels to the correct image locations byte* sourcePixel = fixTargetRgba; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // get the target location int sx = x + px; int sy = y + py; if (sx < width && sy < height) { byte* targetPixel = rgba + 4 * (width * sy + sx); // copy the rgba value for (int i = 0; i < 4; ++i) *targetPixel++ = *sourcePixel++; } else { // skip this pixel as its outside the image sourcePixel += 4; } } } } // advance sourceBlock += bytesPerBlock; } } }
/// <summary> /// Compresses a 4x4 block of pixels in BCn format. /// </summary> /// <param name="rgba">The rgba values of the 16 source pixels.</param> /// <param name="block">Storage for the compressed BCn block.</param> /// <param name="flags">Compression flags.</param> public static unsafe void CompressBC(byte* rgba, byte* block, SquishFlags flags) { // compress with full mask CompressMaskedBC(rgba, 0xffff, block, flags); }
/// <summary> /// Returns the final size in bytes of DXT data compressed with the parameters specified in <paramref name="flags"/>. /// </summary> /// <param name="width">Source image width.</param> /// <param name="height">Source image height.</param> /// <param name="flags">Compression parameters.</param> /// <returns>Size in bytes of the DXT data.</returns> public static int GetStorageRequirements(int width, int height, SquishFlags flags) { return GetStorageRequirementsFunction(width, height, (int)flags); }
/// <summary> /// Compresses a 4x4 block of pixels. /// </summary> /// <param name="rgba">The rgba values of the 16 source pixels.</param> /// <param name="mask">The valid pixel mask.</param> /// <param name="block">Storage for the compressed DXT block.</param> /// <param name="flags">Compression flags.</param> public static unsafe void CompressMasked(byte* rgba, int mask, byte* block, SquishFlags flags) { // fix any bad flags flags = FixFlags(flags); if ((flags & (SquishFlags.BC1 | SquishFlags.BC2 | SquishFlags.BC3 | SquishFlags.BC4 | SquishFlags.BC5)) != 0) { CompressMaskedBC(rgba, mask, block, flags); return; } // get the block locations byte* colourBlock = block; byte* alphaBock = block; if ((flags & (SquishFlags.Dxt3 | SquishFlags.Dxt5)) != 0) colourBlock = block + 8; // create the minimal point set ColourSet colours = new ColourSet(rgba, mask, flags); // check the compression type and compress colour if (colours.GetCount() == 1) { // always do a single colour fit SingleColourFit fit = new SingleColourFit(colours, flags); fit.Compress(colourBlock); } else if ((flags & SquishFlags.ColourRangeFit) != 0 || colours.GetCount() == 0) { // do a range fit RangeFit fit = new RangeFit(colours, flags); fit.Compress(colourBlock); } else { // default to a cluster fit (could be iterative or not) ClusterFit fit = new ClusterFit(colours, flags); fit.Compress(colourBlock); } // compress alpha separately if necessary if ((flags & SquishFlags.Dxt3) != 0) AlphaBlock.CompressAlphaDxt3(rgba, mask, alphaBock); else if ((flags & SquishFlags.Dxt5) != 0) AlphaBlock.CompressAlphaDxt5(rgba, mask, alphaBock); }
public ClusterFit(ColourSet Colours, SquishFlags Flags) : base(Colours, Flags) { // // initialise the metric // bool isPerceptual = (flags & SquishFlags.ColourMetricPerceptual) != 0; metric = isPerceptual ? new Vec3(0.2126f, 0.7152f, 0.0722f) : new Vec3(1.0f); // // initialise the best error // bestError = Single.MaxValue; // // cache some values // int count = colours.Count; Vec3[] values = colours.Points; float[] weights = colours.Weights; // // get the covariance matrix // Sym3x3 covariance = Maths.ComputeWeightedCovariance(count, values, weights); // // compute the principle component // Vec3 principle = Maths.ComputePrincipleComponent(covariance); // // get the min and max range as the codebook endpoints // Vec3 startTemp = new Vec3(0.0f); Vec3 endTemp = new Vec3(0.0f); if (count > 0) { // // compute the range // startTemp = endTemp = values[0]; float min = Vec3.Dot(values[0], principle); float max = min; for(int i = 1; i < count; ++i) { float val = Vec3.Dot(values[i], principle); if (val < min) { startTemp = values[i]; min = val; } else { if (val > max) { endTemp = values[i]; max = val; } } } } // // clamp the output to [0, 1] // Vec3 one = new Vec3(1.0f); Vec3 zero = new Vec3(0.0f); startTemp = Vec3.Min(one, Vec3.Max(zero, startTemp)); endTemp = Vec3.Min(one, Vec3.Max(zero, endTemp)); // // clamp to the grid and save // Vec3 grid = new Vec3(31.0f, 63.0f, 31.0f); Vec3 gridrcp = new Vec3(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f); Vec3 half = new Vec3(0.5f); start = Vec3.Truncate(grid * startTemp + half) * gridrcp; end = Vec3.Truncate(grid * endTemp + half) * gridrcp; }
public RangeFit(ColourSet colours, SquishFlags flags, float? metric) : base(colours, flags) { // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f) if (metric != null) { //m_metric = new Vector3( metric[0], metric[1], metric[2] ); } else { m_metric = new Vector3(1.0f); } // initialise the best error m_besterror = float.MaxValue; // cache some values int count = m_colours.Count; Vector3[] values = m_colours.Points; float[] weights = m_colours.Weights; // get the covariance matrix Sym3x3 covariance = Sym3x3.ComputeWeightedCovariance(count, values, weights); // compute the principle component Vector3 principle = Sym3x3.ComputePrincipleComponent(covariance); // get the min and max range as the codebook endpoints Vector3 start = new Vector3(0.0f); Vector3 end = new Vector3(0.0f); if (count > 0) { float min, max; // compute the range start = end = values[0]; min = max = Vector3.Dot(values[0], principle); for (int i = 1; i < count; ++i) { float val = Vector3.Dot(values[i], principle); if (val < min) { start = values[i]; min = val; } else if (val > max) { end = values[i]; max = val; } } } // clamp the output to [0, 1] Vector3 one = new Vector3(1.0f); Vector3 zero = new Vector3(0.0f); start = Vector3.Min(one, Vector3.Max(zero, start)); end = Vector3.Min(one, Vector3.Max(zero, end)); // clamp to the grid and save Vector3 grid = new Vector3(31.0f, 63.0f, 31.0f); Vector3 gridrcp = new Vector3(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f); Vector3 half = new Vector3(0.5f); m_start = Helpers.Truncate(grid * start + half) * gridrcp; m_end = Helpers.Truncate(grid * end + half) * gridrcp; }
/// <summary> /// Decompresses a 4x4 block of pixels in BCn format. /// </summary> /// <param name="rgba">Storage for the 16 decompressed pixels.</param> /// <param name="block">The compressed BCn block.</param> /// <param name="flags">Compression flags: /// /// The flags parameter should specify either BC1, BC2, BC3, BC4 or BC5 compression, /// however. All other flags are ignored.</param> public static unsafe void DecompressBC(byte* rgba, byte* block, SquishFlags flags) { if ((flags & SquishFlags.BC1) != 0) { SquishFlags cflags = flags; cflags = cflags & ~SquishFlags.BC1; cflags |= SquishFlags.Dxt1; Decompress(rgba, block, cflags); } else if ((flags & SquishFlags.BC2) != 0) { SquishFlags cflags = flags; cflags = cflags & ~SquishFlags.BC2; cflags |= SquishFlags.Dxt3; Decompress(rgba, block, cflags); } else if ((flags & SquishFlags.BC3) != 0) { SquishFlags cflags = flags; cflags = cflags & ~SquishFlags.BC3; cflags |= SquishFlags.Dxt5; Decompress(rgba, block, cflags); } else if ((flags & SquishFlags.BC4) != 0) { byte[] alpha_array = new byte[8]; byte[] index_array = new byte[16]; fixed (byte* fix_alpha = alpha_array, fix_index = index_array) { AlphaBlock.DecodeDXT5AlphaBlock(block, fix_alpha); AlphaBlock.DecodeDXT5AlphaIndices(block, fix_index); for (int i = 0, j = 0; i < 16; i++) { rgba[j] = fix_alpha[fix_index[i]]; rgba[j + 1] = fix_alpha[fix_index[i]]; rgba[j + 2] = fix_alpha[fix_index[i]]; rgba[j + 3] = 255; j += 4; } } } else if ((flags & SquishFlags.BC5) != 0) { byte[] alpha_array = new byte[8]; byte[] index_array = new byte[16]; byte[] reds = new byte[16]; fixed (byte* fix_alpha = alpha_array, fix_index = index_array) { AlphaBlock.DecodeDXT5AlphaBlock(block, fix_alpha); AlphaBlock.DecodeDXT5AlphaIndices(block, fix_index); for (int i = 0; i < 16; i++) { reds[i] = fix_alpha[fix_index[i]]; } AlphaBlock.DecodeDXT5AlphaBlock(block, fix_alpha); AlphaBlock.DecodeDXT5AlphaIndices(block, fix_index); for (int i = 0, j = 0; i < 16; i++) { rgba[j] = reds[i]; // red rgba[j + 1] = fix_alpha[fix_index[i]]; // green rgba[j + 2] = 0; // blue rgba[j + 3] = 0xFF; // alpha j += 4; } } } }
public SingleColourFit(ColourSet colours, SquishFlags flags) : base(colours, flags) { Vec3[] values = m_colours.GetPoints(); m_colour[0] = (byte)CMath.FloatToInt(255.0f * values[0].X(), 255); m_colour[1] = (byte)CMath.FloatToInt(255.0f * values[0].Y(), 255); m_colour[2] = (byte)CMath.FloatToInt(255.0f * values[0].Z(), 255); // initialise the best error m_besterror = int.MaxValue; }
/// <summary> /// Decompresses an image in memory. /// </summary> /// <param name="data">The compressed DXT blocks.</param> /// <param name="width">The width of the source image.</param> /// <param name="height">The height of the source image.</param> /// <param name="flags">Compression flags: /// /// The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, /// however, DXT1 will be used by default if none is specified. All other flags /// are ignored.</param> /// <returns>Return the Decompressed Image Data.</returns> public static byte[] DecompressImage(byte[] data, int width, int height, SquishFlags flags) { byte[] output = new byte[width * height*4]; unsafe { fixed (byte* lpIn = data, lpOut=output) { DecompressImage(lpOut, width, height, lpIn, flags); } } return output; }
private static unsafe int SquishGetStorageRequirements(int width, int height, SquishFlags flags) { if (Is64Bit()) { return(SquishInterface_64.SquishGetStorageRequirements(width, height, (int)flags)); } else { return(SquishInterface_32.SquishGetStorageRequirements(width, height, (int)flags)); } }
/// <summary> /// Decompresses an image using the parameters specified in <paramref name="flags"/>. /// </summary> /// <param name="blocks">Source DXT compressed image.</param> /// <param name="width">Width of the image.</param> /// <param name="height">Height of the image.</param> /// <param name="flags">Decompression flags.</param> /// <returns>Output RGBA decompressed image.</returns> public static byte[] DecompressImage(byte[] blocks, int width, int height, SquishFlags flags) { byte[] decompressedData = new byte[width * height * 4]; GCHandle pinnedData = GCHandle.Alloc(decompressedData, GCHandleType.Pinned); GCHandle pinnedBlocks = GCHandle.Alloc(blocks, GCHandleType.Pinned); DecompressImageFunction(pinnedData.AddrOfPinnedObject(), width, height, pinnedBlocks.AddrOfPinnedObject(), (int)flags); pinnedBlocks.Free(); pinnedData.Free(); return decompressedData; }
private static unsafe void SquishDecompressImage(byte[] rgba, int width, int height, byte[] blocks, SquishFlags flags) { fixed(byte *pRGBA = rgba) fixed(byte *pBlocks = blocks) { if (Is64Bit()) { SquishInterface_64.SquishDecompressImage(pRGBA, width, height, pBlocks, (int)flags); } else { SquishInterface_32.SquishDecompressImage(pRGBA, width, height, pBlocks, (int)flags); } } }
/// <summary> /// Computes the amount of compressed storage required. /// </summary> /// <param name="width">The width of the image.</param> /// <param name="height">The height of the image.</param> /// <param name="flags">Compression flags: /// /// The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, /// however, DXT1 will be used by default if none is specified. All other flags /// are ignored.</param> /// <returns></returns> public static int GetStorageRequirements(int width, int height, SquishFlags flags) { // fix any bad flags flags = FixFlags(flags); // compute the storage requirements int blockcount = GetBlocksCount(width, height); int blocksize = BytesPerBlock(flags); return blockcount * blocksize; }
public static unsafe byte[] CompressImage(byte[] rgba, int width, int height, SquishFlags flags, Action<int, int> progressFn) { // // fix any bad flags // flags = fixFlags(flags); // // initialise the block output // int blockCount = (width + 3 ) / 4 * ((height + 3) / 4); int blockSize = (flags & SquishFlags.Dxt1) != 0 ? 8 : 16; byte[] blocks = new byte[blockCount * blockSize]; int progress = 0; progressFn?.Invoke(0, height); // // loop over blocks // fixed(byte *pBlocks = blocks) { byte *targetBlock = pBlocks; fixed(byte *pRgba = rgba) { byte *source = pRgba; Parallel.ForEach(SteppedEnumerable.SteppedRange(0, height, 4), y => { Parallel.ForEach(SteppedEnumerable.SteppedRange(0, width, 4), x => { // // build the 4x4 block of pixels // byte[] sourceRgba = new byte[16 * 4]; int mask = 0; fixed(byte *pSourceRgba = sourceRgba) { byte *targetPixel = pSourceRgba; for(int py = 0; py < 4; ++py) { for(int px = 0; px < 4; ++px) { // // get the source pixel in the image // int sx = x + px; int sy = y + py; // // enable if we're in the image // if (sx < width && sy < height) { // // copy the rgba value // byte *sourcePixel = source + 4 * (width * sy + sx); for(int i = 0; i < 4; ++i) *targetPixel++ = *sourcePixel++; // // enable this pixel // mask |= 1 << (4 * py + px); } else { // // skip this pixel as its outside the image // targetPixel += 4; } } } // // compress it into the output // int blockNum = (width + 3) / 4 * (y / 4) + x / 4; byte *outputBlock = targetBlock + blockSize * blockNum; compressMasked(pSourceRgba, mask, outputBlock, flags); } }); Interlocked.Add(ref progress, 4); progressFn?.Invoke(progress, height); }); } } progressFn?.Invoke(height, height); return blocks; }
/// <summary> /// Decompresses a 4x4 block of pixels. /// </summary> /// <param name="rgba">Storage for the 16 decompressed pixels.</param> /// <param name="block">The compressed DXT block.</param> /// <param name="flags">Compression flags: /// /// The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, /// however, DXT1 will be used by default if none is specified. All other flags /// are ignored.</param> public static unsafe void Decompress(byte* rgba, byte* block, SquishFlags flags) { // fix any bad flags flags = FixFlags(flags); if ((flags & (SquishFlags.BC1 | SquishFlags.BC2 | SquishFlags.BC3 | SquishFlags.BC4 | SquishFlags.BC5)) != 0) { DecompressBC(rgba, block, flags); return; } // get the block locations byte* colourBlock = block; byte* alphaBock = block; if ((flags & (SquishFlags.Dxt3 | SquishFlags.Dxt5)) != 0) colourBlock += 8; // decompress colour ColourBlock.DecompressColour(rgba, colourBlock, (flags & SquishFlags.Dxt1) != 0); // decompress alpha separately if necessary if ((flags & SquishFlags.Dxt3) != 0) AlphaBlock.DecompressAlphaDxt3(rgba, alphaBock); else if ((flags & SquishFlags.Dxt5) != 0) AlphaBlock.DecompressAlphaDxt5(rgba, alphaBock); }