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();
        }
Beispiel #4
0
        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;
        }
        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();
        }
Beispiel #7
0
        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);
        }
        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;
        }
Beispiel #11
0
 protected ColourFit(ColourSet colours, SquishOptions flags)
 {
     this._Colours = colours;
     this._Flags = flags;
 }
Beispiel #12
0
 public static SquishOptions GetMetric(this SquishOptions self)
 {
     return(self & (SquishOptions.ColourMetricPerceptual | SquishOptions.ColourMetricUniform));
 }
Beispiel #13
0
 public static SquishOptions GetMethod(this SquishOptions self)
 {
     return(self & (SquishOptions.DXT1 | SquishOptions.DXT3 | SquishOptions.DXT5));
 }
Beispiel #14
0
        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]);
            }
        }
Beispiel #15
0
 public static byte[] CompressImage(byte[] rgba, int width, int height, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #16
0
 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;
 }
Beispiel #17
0
        /// <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);
        }
Beispiel #18
0
        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);
        }
Beispiel #19
0
 public static byte[] CompressBlockMasked(byte[] rgba, int mask, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #20
0
 public static void DecompressImage(Stream input, Stream output, int width, int height, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #21
0
 public static void DecompressBlock(Stream input, Stream output, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #22
0
 public static void CompressBlockMasked(Stream input, Stream output, int mask, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #23
0
 public static void CompressBlock(Stream input, Stream output, SquishOptions flags)
 {
     CompressBlockMasked(input, output, 0xFFFF, flags);
 }
Beispiel #24
0
 public static byte[] CompressBlock(byte[] rgba, SquishOptions flags)
 {
     return(CompressBlockMasked(rgba, 0xFFFF, flags));
 }
Beispiel #25
0
        /// <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);
        }
Beispiel #26
0
 public static byte[] CompressImage(byte[] rgba, int width, int height, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #27
0
 public static void CompressBlockMasked(Stream input, Stream output, int mask, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #28
0
 public static byte[] DecompressImage(byte[] blocks, int width, int height, SquishOptions flags)
 {
     return(DecompressImage(blocks, 0, width, height, flags));
 }
Beispiel #29
0
 public static void CompressImage(Stream input, Stream output, int width, int height, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #30
0
        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);
        }
Beispiel #31
0
 public static void DecompressBlock(Stream input, Stream output, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #32
0
 protected ColourFit(ColourSet colours, SquishOptions flags)
 {
     this._Colours = colours;
     this._Flags   = flags;
 }
Beispiel #33
0
        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[] CompressBlockMasked(byte[] rgba, int mask, SquishOptions flags)
 {
     throw new NotImplementedException();
 }
Beispiel #35
0
 public static SquishOptions GetFit(this SquishOptions self)
 {
     return(self & (SquishOptions.ColourIterativeClusterFit | SquishOptions.ColourClusterFit | SquishOptions.ColourRangeFit));
 }
Beispiel #36
0
 public static void CompressBlock(Stream input, Stream output, SquishOptions flags)
 {
     CompressBlockMasked(input, output, 0xFFFF, flags);
 }
Beispiel #37
0
 public static SquishOptions GetExtra(this SquishOptions self)
 {
     return(self & (SquishOptions.WeightColourByAlpha));
 }
Beispiel #38
0
 public static Image DecompressToBitmap(byte[] blocks, int width, int height, SquishOptions flags)
 {
     return DecompressToBitmap(blocks, 0, width, height, flags);
 }
Beispiel #39
0
        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;
        }
Beispiel #40
0
        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);
        }
Beispiel #41
0
 public static byte[] CompressBlock(byte[] rgba, SquishOptions flags)
 {
     return CompressBlockMasked(rgba, 0xFFFF, flags);
 }
Beispiel #42
0
        /// <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);
        }