Exemple #1
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;
            var            targetXRes = GetLevelAdjustedResolutionValue(GetResolution().X, mipLevel);
            var            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 (Header.CompressionType)
            {
            case TextureCompressionType.Palettized:
            {
                map = new Image <Rgba32>((int)targetXRes, (int)targetYRes);
                using (var ms = new MemoryStream(inData))
                {
                    using (var br = new BinaryReader(ms))
                    {
                        // Read colour information
                        for (var y = 0; y < targetYRes; ++y)
                        {
                            for (var x = 0; x < targetXRes; ++x)
                            {
                                var colorIndex   = br.ReadByte();
                                var paletteColor = _palette[colorIndex];
                                map[x, y] = paletteColor;
                            }
                        }

                        // Read Alpha information
                        var alphaValues = new List <byte>();
                        if (GetAlphaBitDepth() > 0)
                        {
                            if (GetAlphaBitDepth() == 1)
                            {
                                var alphaByteCount = (int)Math.Ceiling((double)(targetXRes * targetYRes) / 8);
                                alphaValues = Decode1BitAlpha(br.ReadBytes(alphaByteCount));
                            }
                            else if (GetAlphaBitDepth() == 4)
                            {
                                var alphaByteCount = (int)Math.Ceiling((double)(targetXRes * targetYRes) / 2);
                                alphaValues = Decode4BitAlpha(br.ReadBytes(alphaByteCount));
                            }
                            else if (GetAlphaBitDepth() == 8)
                            {
                                // Directly read the alpha values
                                for (var y = 0; y < targetYRes; ++y)
                                {
                                    for (var x = 0; x < targetXRes; ++x)
                                    {
                                        var alphaValue = br.ReadByte();
                                        alphaValues.Add(alphaValue);
                                    }
                                }
                            }
                        }
                        else
                        {
                            // The map is fully opaque
                            for (var y = 0; y < targetYRes; ++y)
                            {
                                for (var x = 0; x < targetXRes; ++x)
                                {
                                    alphaValues.Add(255);
                                }
                            }
                        }

                        // Build the final map
                        for (var y = 0; y < targetYRes; ++y)
                        {
                            for (var x = 0; x < targetXRes; ++x)
                            {
                                var valueIndex = (int)(x + (targetXRes * y));
                                var alphaValue = alphaValues[valueIndex];

                                var pixelColor = map[x, y];
                                var finalPixel = new Rgba32(pixelColor.R, pixelColor.G, pixelColor.B, alphaValue);

                                map[x, y] = finalPixel;
                            }
                        }
                    }
                }

                break;
            }

            case TextureCompressionType.DXTC:
            {
                var squishOptions = SquishOptions.DXT1;
                if (Header.PixelFormat == BLPPixelFormat.DXT3)
                {
                    squishOptions = SquishOptions.DXT3;
                }
                else if (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 (var ms = new MemoryStream(inData))
                {
                    using (var br = new BinaryReader(ms))
                    {
                        for (var y = 0; y < targetYRes; ++y)
                        {
                            for (var x = 0; x < targetXRes; ++x)
                            {
                                var a = br.ReadByte();
                                var r = br.ReadByte();
                                var g = br.ReadByte();
                                var b = br.ReadByte();

                                var 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
                var jpegImage = new byte[_jpegHeaderSize + inData.Length];
                Buffer.BlockCopy(_jpegHeader, 0, jpegImage, 0, (int)_jpegHeaderSize);
                Buffer.BlockCopy(inData, 0, jpegImage, (int)_jpegHeaderSize, inData.Length);

                using (var ms = new MemoryStream(jpegImage))
                {
                    map = Image.Load <Rgba32>(ms).Clone(cx => cx.Invert());
                }

                break;
            }
            }

            return(map);
        }
Exemple #2
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 <Rgba32> inImage, uint mipLevel)
        {
            var targetXRes = GetLevelAdjustedResolutionValue(GetResolution().X, mipLevel);
            var targetYRes = GetLevelAdjustedResolutionValue(GetResolution().Y, mipLevel);

            var colourData = new List <byte>();
            var alphaData  = new List <byte>();

            using (var resizedImage = ResizeImage(inImage, (int)targetXRes, (int)targetYRes))
            {
                if (Header.CompressionType == TextureCompressionType.Palettized)
                {
                    // Generate the colour data
                    for (var y = 0; y < targetYRes; ++y)
                    {
                        for (var x = 0; x < targetXRes; ++x)
                        {
                            var nearestColor = FindClosestMatchingColor(resizedImage[x, y]);
                            var paletteIndex = (byte)_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 (var y = 0; y < targetYRes; ++y)
                            {
                                for (var 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)
                                    {
                                        var pixelAlpha = resizedImage[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 (var y = 0; y < targetYRes; ++y)
                            {
                                for (var 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
                                        var pixelAlpha = resizedImage[x + i, y].A;

                                        // Map the value to a 4-bit integer
                                        pixelAlpha = (byte)ExtendedMath.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 (var y = 0; y < targetYRes; ++y)
                            {
                                for (var x = 0; x < targetXRes; ++x)
                                {
                                    // The alpha value is stored as a whole byte
                                    var alphaValue = resizedImage[x, y].A;
                                    alphaData.Add(alphaValue);
                                }
                            }
                        }
                    }
                    else
                    {
                        // The map is fully opaque
                        for (var y = 0; y < targetYRes; ++y)
                        {
                            for (var x = 0; x < targetXRes; ++x)
                            {
                                alphaData.Add(255);
                            }
                        }
                    }
                }
                else if (Header.CompressionType == TextureCompressionType.DXTC)
                {
                    using (var rgbaStream = new MemoryStream())
                    {
                        using (var bw = new BinaryWriter(rgbaStream))
                        {
                            for (var y = 0; y < targetYRes; ++y)
                            {
                                for (var x = 0; x < targetXRes; ++x)
                                {
                                    bw.Write(resizedImage[x, y].R);
                                    bw.Write(resizedImage[x, y].G);
                                    bw.Write(resizedImage[x, y].B);
                                    bw.Write(resizedImage[x, y].A);
                                }
                            }

                            // Finish writing the data
                            bw.Flush();

                            var rgbaBytes = rgbaStream.ToArray();

                            var squishOptions = SquishOptions.DXT1;
                            if (Header.PixelFormat == BLPPixelFormat.DXT3)
                            {
                                squishOptions = SquishOptions.DXT3;
                            }
                            else if (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 (Header.CompressionType == TextureCompressionType.Uncompressed)
                {
                    using (var argbStream = new MemoryStream())
                    {
                        using (var bw = new BinaryWriter(argbStream))
                        {
                            for (var y = 0; y < targetYRes; ++y)
                            {
                                for (var x = 0; x < targetXRes; ++x)
                                {
                                    bw.Write(resizedImage[x, y].A);
                                    bw.Write(resizedImage[x, y].R);
                                    bw.Write(resizedImage[x, y].G);
                                    bw.Write(resizedImage[x, y].B);
                                }
                            }

                            // Finish writing the data
                            bw.Flush();

                            var argbBytes = argbStream.ToArray();
                            colourData = new List <byte>(argbBytes);
                        }
                    }
                }
            }

            // After compression of the data, merge the color data and alpha data
            var 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);
        }
Exemple #3
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);
        }