void WriteFloat(byte[] source, int sourceStart, DDSFormatDetails sourceFormatDetails, byte[] destination, int destStart)
 {
     byte[] bytes = sourceFormatDetails.ReadFloatAsArray(source, sourceStart);
     destination[destStart]     = bytes[0];
     destination[destStart + 1] = bytes[1];
     destination[destStart + 2] = bytes[2];
     destination[destStart + 3] = bytes[3];
 }
Example #2
0
        internal static int GetCompressedSizeUpToIndex(double mipIndex, DDSFormatDetails destFormatDetails, int baseWidth, int baseHeight)
        {
            /*
             *  Mipmapping halves both dimensions per mip down. Dimensions are then divided by 4 if block compressed as a texel is 4x4 pixels.
             *  e.g. 4096 x 4096 block compressed texture with 8 byte blocks e.g. DXT1
             *  Sizes of mipmaps:
             *      4096 / 4 x 4096 / 4 x 8
             *      (4096 / 4 / 2) x (4096 / 4 / 2) x 8
             *      (4096 / 4 / 2 / 2) x (4096 / 4 / 2 / 2) x 8
             *
             *  Pattern: Each dimension divided by 2 per mip size decreased.
             *  Thus, total is divided by 4.
             *      Size of any mip = Sum(1/4^n) x divWidth x divHeight x blockSize,
             *          where n is the desired mip (0 based),
             *          divWidth and divHeight are the block compress adjusted dimensions (uncompressed textures lead to just original dimensions, block compressed are divided by 4)
             *
             *  Turns out the partial sum of the infinite sum: Sum(1/4^n) = 1/3 x (4 - 4^-n). Who knew right?
             */

            // TODO: DDS going down past 4x4
            bool requiresTinyAdjustment = false;
            int  selectedMipDimensions  = (int)(baseWidth / Math.Pow(2d, mipIndex));

            if (selectedMipDimensions < 4)
            {
                requiresTinyAdjustment = true;
            }

            double divisor = 1;

            if (destFormatDetails.IsBlockCompressed)
            {
                divisor = 4;
            }

            double shift = 1d / (4 << (int)(2 * (mipIndex - 1)));

            if (mipIndex == 0)
            {
                shift = 1d;
            }
            else if (mipIndex == -1)
            {
                shift = 4d;
            }

            double sumPart = mipIndex == -1 ? 0 :
                             (1d / 3d) * (4d - shift); // Shifting represents 4^-mipIndex. Math.Pow seems slow.

            double totalSize = destFormatDetails.HeaderSize + (sumPart * destFormatDetails.BlockSize * (baseWidth / divisor) * (baseHeight / divisor));

            if (requiresTinyAdjustment)
            {
                totalSize += destFormatDetails.BlockSize * 2;
            }

            return((int)totalSize);
        }
Example #3
0
        protected override void _Read(BinaryReader reader)
        {
            long baseOffset = reader.BaseStream.Position;

            byte[] buffer = reader.ReadBytes((int)reader.BaseStream.Length);

            MemoryStream memoryStream = new MemoryStream(buffer, 0, buffer.Length, true, true);
            DDS_Header   header       = new DDS_Header(memoryStream);

            FormatDetails = new DDSFormatDetails(header.Format, header.DX10_DXGI_AdditionalHeader.dxgiFormat);
            MipMaps       = DDSGeneral.LoadDDS(memoryStream, header, 0, FormatDetails);
            Width         = header.Width;
            Height        = header.Height;
            memoryStream.Close();
        }
Example #4
0
        static int WriteCompressedMipMap(byte[] destination, int mipOffset, MipMap mipmap, int blockSize, Action <byte[], int, int, byte[], int, AlphaSettings, DDSFormatDetails> compressor,
                                         AlphaSettings alphaSetting, DDSFormatDetails loadedFormatDetails)
        {
            if (mipmap.Width < 4 || mipmap.Height < 4)
            {
                return(-1);
            }

            int destinationTexelCount = mipmap.Width * mipmap.Height / 16;
            int sourceLineLength      = mipmap.Width * 4 * loadedFormatDetails.ComponentSize;
            int numTexelsInLine       = mipmap.Width / 4;

            var mipWriter = new Action <int>(texelIndex =>
            {
                // Since this is the top corner of the first texel in a line, skip 4 pixel rows (texel = 4x4 pixels) and the number of rows down the bitmap we are already.
                int sourceLineOffset = sourceLineLength * 4 * (texelIndex / numTexelsInLine);                                           // Length in bytes x 3 lines x texel line index (how many texel sized lines down the image are we). Index / width will truncate, so for the first texel line, it'll be < 0. For the second texel line, it'll be < 1 and > 0.

                int sourceTopLeftCorner = ((texelIndex % numTexelsInLine) * 16) * loadedFormatDetails.ComponentSize + sourceLineOffset; // *16 since its 4 pixels with 4 channels each. Index % numTexels will effectively reset each line.
                compressor(mipmap.Pixels, sourceTopLeftCorner, sourceLineLength, destination, mipOffset + texelIndex * blockSize, alphaSetting, loadedFormatDetails);
            });

            // Choose an acceleration method.
            if (EnableThreading)
            {
                Parallel.For(0, destinationTexelCount, new ParallelOptions {
                    MaxDegreeOfParallelism = ThreadCount
                }, (mip, loopState) =>
                {
                    mipWriter(mip);
                });
            }
            else
            {
                for (int i = 0; i < destinationTexelCount; i++)
                {
                    mipWriter(i);
                }
            }

            return(mipOffset + destinationTexelCount * blockSize);
        }
        static RGBColor ReadColorFromTexel(byte[] texel, int i, bool premultiply, DDSFormatDetails formatDetails)
        {
            // Pull out rgb from texel
            // Create current pixel color
            RGBColor current = new RGBColor();

            // Check that texel is big enough
            if (i + 3 >= texel.Length)
            {
                return(current);  // Fully transparent color
            }
            int   componentSize    = formatDetails.ComponentSize;
            float premultiplyValue = (premultiply ? current.a : 1.0f);

            current.a = formatDetails.ReadFloat(texel, i + 3 * componentSize);
            current.r = formatDetails.ReadFloat(texel, i + 2 * componentSize) * premultiplyValue;
            current.g = formatDetails.ReadFloat(texel, i + componentSize) * premultiplyValue;
            current.b = formatDetails.ReadFloat(texel, i) * premultiplyValue;

            return(current);
        }
Example #6
0
        internal static void CompressBC2Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, DDSFormatDetails formatDetails)
        {
            // Compress Alpha
            if (alphaSetting == AlphaSettings.RemoveAlphaChannel)
            {
                // No alpha so fill with opaque alpha - has to be an alpha value, so make it so RGB is 100% visible.
                for (int i = 0; i < 8; i++)
                {
                    destination[destPosition + i] = 0xFF;
                }
            }
            else
            {
                int position = sourcePosition + 3;  // Only want to read alphas
                for (int i = 0; i < 8; i += 2)
                {
                    destination[destPosition + i]     = (byte)((imgData[position + 4] & 0xF0) | (imgData[position] >> 4));
                    destination[destPosition + i + 1] = (byte)((imgData[position + 12] & 0xF0) | (imgData[position + 8] >> 4));

                    position += sourceLineLength;
                }
            }


            // Compress Color
            CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, false, 0f, alphaSetting, formatDetails);
        }
Example #7
0
        static void WriteUncompressedPixel(byte[] source, int sourceStart, int[] sourceInds, DDSFormatDetails sourceFormatDetails, uint[] masks,
                                           byte[] destination, int destStart, int[] destInds, DDSFormatDetails destFormatDetails, bool oneChannel, bool twoChannel, bool requiresSignedAdjust)
        {
            if (twoChannel) // No large components - silly spec...
            {
                byte red   = sourceFormatDetails.ReadByte(source, sourceStart);
                byte alpha = sourceFormatDetails.ReadByte(source, sourceStart + 3 * sourceFormatDetails.ComponentSize);

                destination[destStart]     = masks[3] > masks[2] ? red : alpha;
                destination[destStart + 1] = masks[3] > masks[2] ? alpha : red;
            }
            else if (oneChannel) // No large components - silly spec...
            {
                byte blue  = sourceFormatDetails.ReadByte(source, sourceStart);
                byte green = sourceFormatDetails.ReadByte(source, sourceStart + 1 * sourceFormatDetails.ComponentSize);
                byte red   = sourceFormatDetails.ReadByte(source, sourceStart + 2 * sourceFormatDetails.ComponentSize);
                byte alpha = sourceFormatDetails.ReadByte(source, sourceStart + 3 * sourceFormatDetails.ComponentSize);

                destination[destStart] = (byte)(blue * 0.082 + green * 0.6094 + blue * 0.3086); // Weightings taken from ATI Compressonator. Dunno if this changes things much.
            }
            else
            {
                // Handle weird conditions where array isn't long enough...
                if (sourceInds[3] + sourceStart >= source.Length)
                {
                    return;
                }

                for (int i = 0; i < 4; i++)
                {
                    uint mask = masks[i];
                    if (mask != 0)
                    {
                        destFormatDetails.WriteColor(source, sourceStart + sourceInds[i], sourceFormatDetails, destination, destStart + destInds[i]);
                    }
                }

                // Signed adjustments - Only happens for bytes for now. V8U8
                if (requiresSignedAdjust)
                {
                    destination[destStart + destInds[2]] += 128;
                    destination[destStart + destInds[1]] += 128;
                }
            }
        }
Example #8
0
 internal static void CompressBC1Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, DDSFormatDetails formatDetails)
 {
     CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition, true, (alphaSetting == AlphaSettings.RemoveAlphaChannel ? 0 : DXT1AlphaThreshold), alphaSetting, formatDetails);
 }
        internal static void ReadUncompressed(byte[] source, int sourceStart, byte[] destination, int pixelCount, DDS_Header.DDS_PIXELFORMAT ddspf, DDSFormatDetails formatDetails)
        {
            bool requiresSignedAdjustment = ((ddspf.dwFlags & DDS_Header.DDS_PFdwFlags.DDPF_SIGNED) == DDS_Header.DDS_PFdwFlags.DDPF_SIGNED);
            int  sourceIncrement          = ddspf.dwRGBBitCount / 8; // /8 for bits to bytes conversion
            bool oneChannel = (ddspf.dwFlags & DDS_Header.DDS_PFdwFlags.DDPF_LUMINANCE) == DDS_Header.DDS_PFdwFlags.DDPF_LUMINANCE;
            bool twoChannel = (ddspf.dwFlags & DDS_Header.DDS_PFdwFlags.DDPF_ALPHAPIXELS) == DDS_Header.DDS_PFdwFlags.DDPF_ALPHAPIXELS && oneChannel;

            uint AMask = ddspf.dwABitMask;
            uint RMask = ddspf.dwRBitMask;
            uint GMask = ddspf.dwGBitMask;
            uint BMask = ddspf.dwBBitMask;


            ///// Figure out channel existance and ordering.
            // Setup array that indicates channel offset from pixel start.
            // e.g. Alpha is usually first, and is given offset 0.
            // NOTE: Ordering array is in ARGB order, and the stored indices change depending on detected channel order.
            // A negative index indicates channel doesn't exist in data and sets channel to 0xFF.
            List <uint> maskOrder = new List <uint>(4)
            {
                AMask, RMask, GMask, BMask
            };

            maskOrder.Sort();
            maskOrder.RemoveAll(t => t == 0);  // Required, otherwise indicies get all messed up when there's only two channels, but it's not indicated as such.

            // TODO: Cubemaps and hardcoded format readers for performance
            int AIndex = 0;
            int RIndex = 0;
            int GIndex = 0;
            int BIndex = 0;

            if (twoChannel)  // Note: V8U8 does not come under this one.
            {
                // Intensity is first byte, then the alpha. Set all RGB to intensity for grayscale.
                // Second mask is always RMask as determined by the DDS Spec.
                AIndex = AMask > RMask ? 1 : 0;
                RIndex = AMask > RMask ? 0 : 1;
                GIndex = AMask > RMask ? 0 : 1;
                BIndex = AMask > RMask ? 0 : 1;
            }
            else if (oneChannel)
            {
                // Decide whether it's alpha or not.
                AIndex = AMask == 0 ? -1 : 0;
                RIndex = AMask == 0 ? 0 : -1;
                GIndex = AMask == 0 ? 0 : -1;
                BIndex = AMask == 0 ? 0 : -1;
            }
            else
            {
                // Set default ordering
                AIndex = AMask == 0 ? -1 : maskOrder.IndexOf(AMask) * formatDetails.ComponentSize;
                RIndex = RMask == 0 ? -1 : maskOrder.IndexOf(RMask) * formatDetails.ComponentSize;
                GIndex = GMask == 0 ? -1 : maskOrder.IndexOf(GMask) * formatDetails.ComponentSize;
                BIndex = BMask == 0 ? -1 : maskOrder.IndexOf(BMask) * formatDetails.ComponentSize;
            }

            // Determine order of things
            int destAInd = 3 * formatDetails.ComponentSize;
            int destRInd = 2 * formatDetails.ComponentSize;
            int destGInd = 1 * formatDetails.ComponentSize;
            int destBInd = 0;

            switch (formatDetails.ComponentSize)
            {
            case 1:
                // Check masks fit properly
                if (maskOrder.Count != sourceIncrement)
                {
                    // Determine mask size
                    var lengths = new int[4];
                    lengths[0] = CountSetBits(BMask);
                    lengths[1] = CountSetBits(GMask);
                    lengths[2] = CountSetBits(RMask);
                    lengths[3] = CountSetBits(AMask);

                    ReadBytesLegacy(source, sourceStart, sourceIncrement, lengths, destination, new int[] { destBInd, destGInd, destRInd, destAInd });
                }
                else
                {
                    ReadBytes(source, sourceStart, sourceIncrement, new int[] { BIndex, GIndex, RIndex, AIndex }, destination, new int[] { destBInd, destGInd, destRInd, destAInd });
                }
                break;

            case 2:
                ReadUShorts(source, sourceStart, sourceIncrement, new int[] { BIndex, GIndex, RIndex, AIndex }, destination, new int[] { destBInd, destGInd, destRInd, destAInd });
                break;

            case 4:
                ReadFloats(source, sourceStart, sourceIncrement, new int[] { BIndex, GIndex, RIndex, AIndex }, destination, new int[] { destBInd, destGInd, destRInd, destAInd });
                break;
            }

            if (requiresSignedAdjustment)
            {
                for (int i = 0; i < destination.Length; i += 4)
                {
                    //destination[i] -= 128;  // Don't adjust blue
                    destination[i + 1] -= 128;
                    destination[i + 2] -= 128;

                    // Alpha not adjusted
                }
            }
        }
        internal static void CompressRGBTexel(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, bool isDXT1, double alphaRef, AlphaSettings alphaSetting, DDSFormatDetails formatDetails)
        {
            int uSteps = 4;

            bool premultiply = alphaSetting == AlphaSettings.Premultiply;

            // Read texel
            RGBColor[] sourceTexel = new RGBColor[16];
            int        position    = sourcePosition;
            int        count       = 0;

            for (int i = 1; i <= 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    sourceTexel[count++] = ReadColorFromTexel(imgData, position, premultiply, formatDetails);
                    position            += 4 * formatDetails.ComponentSize;
                }

                position = sourcePosition + sourceLineLength * i;
            }


            // TODO replace RGBColor with a SIMD vector for speed. Test difference between vector4 and vector<T>, might not be better.

            // Determine if texel is fully and entirely transparent. If so, can set to white and continue.
            if (isDXT1)
            {
                uSteps = CheckDXT1TexelFullTransparency(sourceTexel, destination, destPosition, alphaRef);
                if (uSteps == -1)
                {
                    return;
                }
            }

            RGBColor[] Color = new RGBColor[16];

            // Some kind of color adjustment. Not sure what it does, especially if it wasn't dithering...
            DoColorFixErrorCorrection(Color, sourceTexel);


            // Palette colors
            RGBColor ColorA, ColorB, ColorC, ColorD;

            ColorA = new RGBColor();
            ColorB = new RGBColor();
            ColorC = new RGBColor();
            ColorD = new RGBColor();

            // OPTIMISER
            RGBColor[] minmax = OptimiseRGB(Color, uSteps);
            ColorA = minmax[0];
            ColorB = minmax[1];

            // Create interstitial colors?
            ColorC.r = ColorA.r * LuminanceInv.r;
            ColorC.g = ColorA.g * LuminanceInv.g;
            ColorC.b = ColorA.b * LuminanceInv.b;

            ColorD.r = ColorB.r * LuminanceInv.r;
            ColorD.g = ColorB.g * LuminanceInv.g;
            ColorD.b = ColorB.b * LuminanceInv.b;


            // Yeah...dunno
            uint wColorA = Encode565(ColorC);
            uint wColorB = Encode565(ColorD);

            // Min max are equal - only interpolate 4 interstitial colors
            if (uSteps == 4 && wColorA == wColorB)
            {
                var c2 = BitConverter.GetBytes(wColorA);
                var c1 = BitConverter.GetBytes(wColorB);  ///////////////////// MIN MAX

                destination[destPosition]     = c2[0];
                destination[destPosition + 1] = c2[1];

                destination[destPosition + 2] = c1[0];
                destination[destPosition + 3] = c1[1];
                return;
            }

            // Interpolate 6 colors or something
            ColorC = Decode565(wColorA);
            ColorD = Decode565(wColorB);

            ColorA.r = ColorC.r * Luminance.r;
            ColorA.g = ColorC.g * Luminance.g;
            ColorA.b = ColorC.b * Luminance.b;

            ColorB.r = ColorD.r * Luminance.r;
            ColorB.g = ColorD.g * Luminance.g;
            ColorB.b = ColorD.b * Luminance.b;


            var step = DoSomethingWithPalette(uSteps, wColorA, wColorB, ColorA, ColorB);

            // Calculating color direction apparently
            RGBColor Dir = new RGBColor()
            {
                r = step[1].r - step[0].r,
                g = step[1].g - step[0].g,
                b = step[1].b - step[0].b
            };
            float fscale = (wColorA != wColorB) ? ((uSteps - 1) / (Dir.r * Dir.r + Dir.g * Dir.g + Dir.b * Dir.b)) : 0.0f;

            Dir.r *= fscale;
            Dir.g *= fscale;
            Dir.b *= fscale;

            // Encoding colors apparently
            uint dw = DoOtherColorFixErrorCorrection(sourceTexel, uSteps, alphaRef, step, Dir);

            uint Min = (uSteps == 3) == (wColorA <= wColorB) ? wColorA : wColorB;
            uint Max = (uSteps == 3) == (wColorA <= wColorB) ? wColorB : wColorA;

            var color1 = BitConverter.GetBytes(Min);
            var color2 = BitConverter.GetBytes(Max);

            destination[destPosition]     = color1[0];
            destination[destPosition + 1] = color1[1];

            destination[destPosition + 2] = color2[0];
            destination[destPosition + 3] = color2[1];

            var indicies = BitConverter.GetBytes(dw);

            destination[destPosition + 4] = indicies[0];
            destination[destPosition + 5] = indicies[1];
            destination[destPosition + 6] = indicies[2];
            destination[destPosition + 7] = indicies[3];
        }
Example #11
0
 internal static void CompressBC7Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, DDSFormatDetails formatDetails)
 {
     BC7.CompressBC7Block(imgData, sourcePosition, sourceLineLength, destination, destPosition);
 }
Example #12
0
 internal static int GetMipOffset(double mipIndex, DDSFormatDetails destFormatDetails, int baseWidth, int baseHeight)
 {
     // -1 because if we want the offset of the mip, it's the sum of all sizes before it NOT including itself.
     return(GetCompressedSizeUpToIndex(mipIndex - 1, destFormatDetails, baseWidth, baseHeight));
 }
Example #13
0
        /// <summary>
        /// Checks image file size to ensure requested mipmap is present in image.
        /// Header mip count can be incorrect or missing. Use this method to validate the mip you're after.
        /// </summary>
        /// <param name="streamLength">Image file stream length.</param>
        /// <param name="mainWidth">Width of image.</param>
        /// <param name="mainHeight">Height of image.</param>
        /// <param name="desiredMipDimension">Max dimension of desired mip.</param>
        /// <param name="destFormatDetails">Destination format details.</param>
        /// <param name="mipOffset">Offset of desired mipmap in image.</param>
        /// <returns>True if mip in image.</returns>
        public static bool EnsureMipInImage(long streamLength, int mainWidth, int mainHeight, int desiredMipDimension, DDSFormatDetails destFormatDetails, out int mipOffset)
        {
            if (mainWidth <= desiredMipDimension && mainHeight <= desiredMipDimension)
            {
                mipOffset = destFormatDetails.HeaderSize;
                return(true); // One mip only
            }

            int dependentDimension = mainWidth > mainHeight ? mainWidth : mainHeight;
            int mipIndex           = (int)Math.Log((dependentDimension / desiredMipDimension), 2);

            if (mipIndex < -1)
            {
                throw new InvalidDataException($"Invalid dimensions for mipmapping. Got desired: {desiredMipDimension} and dependent: {dependentDimension}");
            }

            int requiredOffset = GetMipOffset(mipIndex, destFormatDetails, mainHeight, mainWidth);

            // KFreon: Something wrong with the count here by 1 i.e. the estimate is 1 more than it should be
            if (destFormatDetails.Format == DDSFormat.DDS_ARGB_8)  // TODO: Might not just be 8 bit, still don't know why it's wrong.
            {
                requiredOffset -= 2;
            }

            mipOffset = requiredOffset;

            // Should only occur when an image has 0 or 1 mipmap.
            //if (streamLength <= (requiredOffset - destFormatDetails.HeaderSize))
            if (streamLength <= requiredOffset)
            {
                return(false);
            }

            return(true);
        }
Example #14
0
 static void WriteUncompressedMipMap(byte[] destination, int mipOffset, MipMap mipmap, DDSFormatDetails loadedFormatDetails, DDSFormatDetails destFormatDetails, DDS_Header.DDS_PIXELFORMAT ddspf)
 {
     DDS_Encoders.WriteUncompressed(mipmap.Pixels, destination, mipOffset, ddspf, loadedFormatDetails, destFormatDetails);
 }
Example #15
0
        internal static byte[] Save(List <MipMap> mipMaps, DDSFormatDetails destFormatDetails, AlphaSettings alphaSetting, MipHandling mipChoice)
        {
            DDSFormatDetails loadedFormatDetails = new DDSFormatDetails(DDSFormat.DDS_ARGB_8);

            if ((destFormatDetails.IsMippable && mipChoice == MipHandling.GenerateNew) || (destFormatDetails.IsMippable && mipMaps.Count == 1 && mipChoice == MipHandling.Default))
            {
                BuildMipMaps(mipMaps);
            }

            // Set compressor for Block Compressed textures
            Action <byte[], int, int, byte[], int, AlphaSettings, DDSFormatDetails> compressor = destFormatDetails.BlockEncoder;

            bool needCheckSize = destFormatDetails.IsBlockCompressed;

            int height = mipMaps[0].Height;
            int width  = mipMaps[0].Width;

            if (needCheckSize && !CheckSize_DXT(width, height))
            {
                throw new InvalidOperationException($"DXT compression formats require dimensions to be multiples of 4. Got: {width}x{height}.");
            }

            // Create header and write to destination
            DDS_Header header = new DDS_Header(mipMaps.Count, height, width, destFormatDetails.Format, destFormatDetails.DX10Format);

            int headerLength = destFormatDetails.HeaderSize;

            int fullSize = GetCompressedSizeOfImage(mipMaps.Count, destFormatDetails, width, height);

            /*if (destFormatDetails.ComponentSize != 1)
             *  fullSize += (fullSize - headerLength) * destFormatDetails.ComponentSize;*/// Size adjustment for destination to allow for different component sizes.

            byte[] destination = new byte[fullSize];
            header.WriteToArray(destination, 0);

            int blockSize = destFormatDetails.BlockSize;

            if (destFormatDetails.IsBlockCompressed)
            {
                int mipOffset = headerLength;
                foreach (MipMap mipmap in mipMaps)
                {
                    var temp = WriteCompressedMipMap(destination, mipOffset, mipmap, blockSize, compressor, alphaSetting, loadedFormatDetails);
                    if (temp != -1)  // When dimensions too low.
                    {
                        mipOffset = temp;
                    }
                }
            }
            else
            {
                // UNCOMPRESSED
                var action = new Action <int>(mipIndex =>
                {
                    if (alphaSetting == AlphaSettings.RemoveAlphaChannel)
                    {
                        // Remove alpha by setting AMask = 0
                        var ddspf        = header.ddspf;
                        ddspf.dwABitMask = 0;
                        header.ddspf     = ddspf;
                    }

                    // Get MipOffset
                    int offset = GetMipOffset(mipIndex, destFormatDetails, width, height);

                    WriteUncompressedMipMap(destination, offset, mipMaps[mipIndex], loadedFormatDetails, destFormatDetails, header.ddspf);
                });

                if (EnableThreading)
                {
                    Parallel.For(0, mipMaps.Count, new ParallelOptions {
                        MaxDegreeOfParallelism = ThreadCount
                    }, (mip, loopState) =>
                    {
                        action(mip);
                    });
                }
                else
                {
                    for (int i = 0; i < mipMaps.Count; i++)
                    {
                        action(i);
                    }
                }
            }
            return(destination);
        }
Example #16
0
        internal static List <MipMap> LoadDDS(MemoryStream compressed, DDS_Header header, int desiredMaxDimension, DDSFormatDetails formatDetails)
        {
            MipMap[] MipMaps = null;

            int       mipWidth  = header.Width;
            int       mipHeight = header.Height;
            DDSFormat format    = header.Format;

            int estimatedMips  = header.dwMipMapCount;
            int mipOffset      = formatDetails.HeaderSize;
            int originalOffset = mipOffset;

            if (!EnsureMipInImage(compressed.Length, mipWidth, mipHeight, 4, formatDetails, out mipOffset))  // Update number of mips too
            {
                estimatedMips = 1;
            }

            if (estimatedMips == 0)
            {
                estimatedMips = EstimateNumMipMaps(mipWidth, mipHeight);
            }

            mipOffset = originalOffset;  // Needs resetting after checking there's mips in this image.

            // Ensure there's at least 1 mipmap
            if (estimatedMips == 0)
            {
                estimatedMips = 1;
            }

            int orig_estimatedMips = estimatedMips; // Store original count for later (uncompressed only I think)

            // KFreon: Decide which mip to start loading at - going to just load a few mipmaps if asked instead of loading all, then choosing later. That's slow.
            if (desiredMaxDimension != 0 && estimatedMips > 1)
            {
                if (!EnsureMipInImage(compressed.Length, mipWidth, mipHeight, desiredMaxDimension, formatDetails, out mipOffset))  // Update number of mips too
                {
                    throw new InvalidDataException($"Requested mipmap does not exist in this image. Top Image Size: {mipWidth}x{mipHeight}, requested mip max dimension: {desiredMaxDimension}.");
                }

                // Not the first mipmap.
                if (mipOffset > formatDetails.HeaderSize)
                {
                    double divisor = mipHeight > mipWidth ? mipHeight / desiredMaxDimension : mipWidth / desiredMaxDimension;
                    mipHeight = (int)(mipHeight / divisor);
                    mipWidth  = (int)(mipWidth / divisor);

                    if (mipWidth == 0 || mipHeight == 0)  // Reset as a dimension is too small to resize
                    {
                        mipHeight = header.Height;
                        mipWidth  = header.Width;
                        mipOffset = formatDetails.HeaderSize;
                    }
                    else
                    {
                        // Update estimated mips due to changing dimensions.
                        estimatedMips = EstimateNumMipMaps(mipWidth, mipHeight);
                    }
                }
                else  // The first mipmap
                {
                    mipOffset = formatDetails.HeaderSize;
                }
            }

            // Move to requested mipmap
            compressed.Position = mipOffset;

            // Block Compressed texture chooser.
            Action <byte[], int, byte[], int, int, bool> DecompressBCBlock = formatDetails.BlockDecoder;

            MipMaps = new MipMap[estimatedMips];
            int blockSize = formatDetails.BlockSize;

            // KFreon: Read mipmaps
            if (formatDetails.IsBlockCompressed)    // Threading done in the decompression, not here.
            {
                for (int m = 0; m < estimatedMips; m++)
                {
                    // KFreon: If mip is too small, skip out. This happens most often with non-square textures. I think it's because the last mipmap is square instead of the same aspect.
                    // Don't do the mip size check here (<4) since we still need to have a MipMap object for those lower than this for an accurate count.
                    if (mipWidth <= 0 || mipHeight <= 0)  // Needed cos it doesn't throw when reading past the end for some reason.
                    {
                        break;
                    }

                    MipMap mipmap = ReadCompressedMipMap(compressed, mipWidth, mipHeight, mipOffset, formatDetails, DecompressBCBlock);
                    MipMaps[m] = mipmap;
                    mipOffset += (int)(mipWidth * mipHeight * (blockSize / 16d)); // Also put the division in brackets cos if the mip dimensions are high enough, the multiplications can exceed int.MaxValue)
                    mipWidth  /= 2;
                    mipHeight /= 2;
                }
            }
            else
            {
                int startMip = orig_estimatedMips - estimatedMips;

                // UNCOMPRESSED - Can't really do threading in "decompression" so do it for the mipmaps.
                var action = new Action <int>(mipIndex =>
                {
                    // Calculate mipOffset and dimensions
                    int offset, width, height;
                    offset = GetMipOffset(mipIndex, formatDetails, header.Width, header.Height);

                    double divisor = mipIndex == 0 ? 1d : 2 << (mipIndex - 1);   // Divisor represents 2^mipIndex - Math.Pow seems very slow.
                    width          = (int)(header.Width / divisor);
                    height         = (int)(header.Height / divisor);

                    MipMap mipmap = null;
                    try
                    {
                        mipmap = ReadUncompressedMipMap(compressed, offset, width, height, header.ddspf, formatDetails);
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine(e.ToString());
                    }

                    MipMaps[mipIndex] = mipmap;
                });

                if (EnableThreading)
                {
                    Parallel.For(startMip, orig_estimatedMips, new ParallelOptions {
                        MaxDegreeOfParallelism = ThreadCount
                    }, (mip, loopState) =>
                    {
                        action(mip);
                    });
                }
                else
                {
                    for (int i = startMip; i < orig_estimatedMips; i++)
                    {
                        action(i);
                    }
                }
            }

            List <MipMap> mips = new List <MipMap>(MipMaps.Where(t => t != null));

            if (mips.Count == 0)
            {
                throw new InvalidOperationException($"No mipmaps loaded. Estimated mips: {estimatedMips}, mip dimensions: {mipWidth}x{mipHeight}");
            }
            return(mips);
        }
Example #17
0
        internal static void CompressBC3Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, DDSFormatDetails formatDetails)
        {
            // Compress Alpha
            if (alphaSetting == AlphaSettings.RemoveAlphaChannel)
            {
                // No alpha so fill with opaque alpha - has to be an alpha value, so make it so RGB is 100% visible.
                for (int i = 0; i < 8; i++)
                {
                    destination[destPosition + i] = 0xFF;
                }
            }
            else
            {
                Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition, 3, false, formatDetails);
            }

            // Compress Color
            CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, false, 0f, alphaSetting, formatDetails);
        }
Example #18
0
        /// <summary>
        /// Internal read implementation of the sub classes.
        /// </summary>
        /// <param name="reader"></param>
        /// <exception cref="Exception">Expected DDS RGB24 or RGBA32 color format!</exception>
        /// <exception cref="NotImplementedException">TODO</exception>
        protected override void _Read(BinaryReader reader)
        {
            long baseOffset = reader.BaseStream.Position;

            int gbixOffset = 0;
            int pvrtOffset = 0;

            uint identifier = reader.ReadUInt32();

            if (identifier == m_gbix) //"GBIX"
            {
                HasGlobalIndex  = true;
                GlobalIndexSize = reader.ReadUInt32();
                GlobalIndex     = reader.ReadBytes((int)GlobalIndexSize);

                reader.BaseStream.Seek(4, SeekOrigin.Current); //Skip "PVRT"
                gbixOffset = 0x00;
                pvrtOffset = 0x08 + (int)GlobalIndexSize;
            }
            else
            {
                identifier = reader.ReadUInt32();
                if (identifier == m_gbix)
                {
                    HasGlobalIndex  = true;
                    GlobalIndexSize = reader.ReadUInt32();
                    GlobalIndex     = reader.ReadBytes((int)GlobalIndexSize);
                    gbixOffset      = 0x04;
                    pvrtOffset      = 0x0C + (int)GlobalIndexSize;
                }
                else if (identifier == m_pvrt)
                {
                    gbixOffset = -1;
                    pvrtOffset = 0x04;
                }
                else
                {
                    gbixOffset = -1;
                    pvrtOffset = 0x00;
                    reader.BaseStream.Seek(-4, SeekOrigin.Current);
                }
            }

            // Read information about the texture
            ContentSize = reader.ReadUInt32();
            PixelFormat = (PvrPixelFormat)reader.ReadByte();
            DataFormat  = (PvrDataFormat)reader.ReadByte();
            reader.BaseStream.Seek(2, SeekOrigin.Current);
            Width  = reader.ReadUInt16();
            Height = reader.ReadUInt16();

            if (DataFormat == PvrDataFormat.DDS || DataFormat == PvrDataFormat.DDS_2)
            {
                if (!(PixelFormat == PvrPixelFormat.DDS_DXT1_RGB24 || PixelFormat == PvrPixelFormat.DDS_DXT3_RGBA32))
                {
                    throw new Exception("Expected DDS RGB24 or RGBA32 color format!");
                }

                long             ddsOffset = reader.BaseStream.Position;
                DDS_Header       header    = new DDS_Header(reader.BaseStream);
                DDSFormatDetails format    = new DDSFormatDetails(header.Format, header.DX10_DXGI_AdditionalHeader.dxgiFormat);
                reader.BaseStream.Seek(ddsOffset, SeekOrigin.Begin);

                byte[] ddsBuffer = reader.ReadBytes(header.dwPitchOrLinearSize + header.dwSize + 128);

                MemoryStream memoryStream = new MemoryStream(ddsBuffer, 0, ddsBuffer.Length, true, true);
                MipMaps = DDSGeneral.LoadDDS(memoryStream, header, 0, format);
                memoryStream.Close();

                Width  = header.Width;
                Height = header.Height;
            }
            else
            {
                // Get the codecs and make sure we can decode using them
                PixelCodec = PvrPixelCodec.GetPixelCodec(PixelFormat);
                DataCodec  = PvrDataCodec.GetDataCodec(DataFormat);
                if (DataCodec != null && PixelCodec != null)
                {
                    DataCodec.PixelCodec = PixelCodec;
                }

                // Set the number of palette entries
                int paletteEntries = DataCodec.PaletteEntries;
                if (DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL || DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL_MIPMAP)
                {
                    if (Width <= 16)
                    {
                        paletteEntries = 64; // Actually 16
                    }
                    else if (Width <= 32)
                    {
                        paletteEntries = 256; // Actually 64
                    }
                    else if (Width <= 64)
                    {
                        paletteEntries = 512; // Actually 128
                    }
                    else
                    {
                        paletteEntries = 1024; // Actually 256
                    }
                }

                // Set the palette and data offsets
                int paletteOffset = 0;
                int dataOffset    = 0;
                if (paletteEntries == 0 || DataCodec.NeedsExternalPalette)
                {
                    paletteOffset = -1;
                    dataOffset    = pvrtOffset + 0x10;
                }
                else
                {
                    paletteOffset = pvrtOffset + 0x10;
                    dataOffset    = paletteOffset + (paletteEntries * (PixelCodec.Bpp >> 3));
                }

                // Get the compression format and determine if we need to decompress this texture
                reader.BaseStream.Seek(baseOffset, SeekOrigin.Begin);
                uint first = reader.ReadUInt32();
                reader.BaseStream.Seek(baseOffset + pvrtOffset + 4, SeekOrigin.Begin);
                uint second = reader.ReadUInt32();
                if (first == second - pvrtOffset + dataOffset + 8)
                {
                    CompressionFormat = PvrCompressionFormat.RLE;
                }
                else
                {
                    CompressionFormat = PvrCompressionFormat.NONE;
                }
                CompressionCodec = PvrCompressionCodec.GetCompressionCodec(CompressionFormat);

                if (CompressionFormat != PvrCompressionFormat.NONE && CompressionCodec != null)
                {
                    //TODO: Convert to stream compatible code
                    throw new NotImplementedException("TODO");
                    //m_encodedData = CompressionCodec.Decompress(m_encodedData, dataOffset, PixelCodec, DataCodec);

                    // Now place the offsets in the appropiate area
                    if (CompressionFormat == PvrCompressionFormat.RLE)
                    {
                        if (gbixOffset != -1)
                        {
                            gbixOffset -= 4;
                        }
                        pvrtOffset -= 4;
                        if (paletteOffset != -1)
                        {
                            paletteOffset -= 4;
                        }
                        dataOffset -= 4;
                    }
                }

                // If the texture contains mipmaps, gets the offsets of them
                int[] mipmapOffsets;
                if (DataCodec.HasMipmaps)
                {
                    int mipmapOffset = 0;
                    mipmapOffsets = new int[(int)Math.Log(Width, 2) + 1];

                    // Calculate the padding for the first mipmap offset
                    if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP)
                    {
                        mipmapOffset = (DataCodec.Bpp) >> 3; // A 1x1 mipmap takes up as much space as a 2x1 mipmap
                    }
                    else if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP_ALT)
                    {
                        mipmapOffset = (3 * DataCodec.Bpp) >> 3; // A 1x1 mipmap takes up as much space as a 2x2 mipmap
                    }
                    for (int i = mipmapOffsets.Length - 1, size = 1; i >= 0; i--, size <<= 1)
                    {
                        mipmapOffsets[i] = mipmapOffset;
                        mipmapOffset    += Math.Max((size * size * DataCodec.Bpp) >> 3, 1);
                    }
                }
                else
                {
                    mipmapOffsets = new int[1] {
                        0
                    };
                }

                //DecodeMipmaps()
                if (paletteOffset != -1) // The texture contains an embedded palette
                {
                    reader.BaseStream.Seek(baseOffset + paletteOffset, SeekOrigin.Begin);
                    DataCodec.SetPalette(reader, paletteEntries);
                }

                MipMaps = new List <MipMap>();
                if (DataCodec.HasMipmaps)
                {
                    for (int i = 0, size = Width; i < mipmapOffsets.Length; i++, size >>= 1)
                    {
                        reader.BaseStream.Seek(baseOffset + dataOffset + mipmapOffsets[i], SeekOrigin.Begin);
                        byte[] pixels = DataCodec.Decode(reader, size, size, PixelCodec);
                        MipMaps.Add(new MipMap(pixels, size, size));
                    }
                }
                else
                {
                    reader.BaseStream.Seek(baseOffset + dataOffset + mipmapOffsets[0], SeekOrigin.Begin);
                    byte[] pixels = DataCodec.Decode(reader, Width, Height, PixelCodec);
                    MipMaps.Add(new MipMap(pixels, Width, Height));
                }
            }
            if (HasGlobalIndex)
            {
                reader.BaseStream.Seek(baseOffset + ContentSize + 0xC, SeekOrigin.Begin);
            }
            else
            {
                reader.BaseStream.Seek(baseOffset + ContentSize, SeekOrigin.Begin);
            }
        }
Example #19
0
        // ATI2 3Dc
        internal static void CompressBC5Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, DDSFormatDetails formatDetails)
        {
            // Green: Channel 1.
            Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition, 1, false, formatDetails);

            // Red: Channel 0, 8 destination offset to be after Green.
            Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, 2, false, formatDetails);
        }
 void WriteByte(byte[] source, int sourceStart, DDSFormatDetails sourceFormatDetails, byte[] destination, int destStart)
 {
     destination[destStart] = sourceFormatDetails.ReadByte(source, sourceStart);
 }
Example #21
0
        internal static void WriteUncompressed(byte[] source, byte[] destination, int destStart, DDS_Header.DDS_PIXELFORMAT dest_ddspf, DDSFormatDetails sourceFormatDetails, DDSFormatDetails destFormatDetails)
        {
            int  byteCount            = dest_ddspf.dwRGBBitCount / 8;
            bool requiresSignedAdjust = (dest_ddspf.dwFlags & DDS_Header.DDS_PFdwFlags.DDPF_SIGNED) == DDS_Header.DDS_PFdwFlags.DDPF_SIGNED;
            bool oneChannel           = (dest_ddspf.dwFlags & DDS_Header.DDS_PFdwFlags.DDPF_LUMINANCE) == DDS_Header.DDS_PFdwFlags.DDPF_LUMINANCE;
            bool twoChannel           = oneChannel && (dest_ddspf.dwFlags & DDS_Header.DDS_PFdwFlags.DDPF_ALPHAPIXELS) == DDS_Header.DDS_PFdwFlags.DDPF_ALPHAPIXELS;

            uint AMask = dest_ddspf.dwABitMask;
            uint RMask = dest_ddspf.dwRBitMask;
            uint GMask = dest_ddspf.dwGBitMask;
            uint BMask = dest_ddspf.dwBBitMask;

            ///// Figure out channel existance and ordering.
            // Setup array that indicates channel offset from pixel start.
            // e.g. Alpha is usually first, and is given offset 0.
            // NOTE: Ordering array is in ARGB order, and the stored indices change depending on detected channel order.
            // A negative index indicates channel doesn't exist in data and sets channel to 0xFF.

            if (destFormatDetails.Format == DDSFormat.DDS_ARGB_32F)
            {
                AMask = 4;
                BMask = 3;
                GMask = 2;
                RMask = 1;
            }

            List <uint> maskOrder = new List <uint>(4)
            {
                AMask, RMask, GMask, BMask
            };

            maskOrder.Sort();
            maskOrder.RemoveAll(t => t == 0);  // Required, otherwise indicies get all messed up when there's only two channels, but it's not indicated as such.

            // Determine channel ordering
            int destAIndex = AMask == 0 ? -1 : maskOrder.IndexOf(AMask) * destFormatDetails.ComponentSize;
            int destRIndex = RMask == 0 ? -1 : maskOrder.IndexOf(RMask) * destFormatDetails.ComponentSize;
            int destGIndex = GMask == 0 ? -1 : maskOrder.IndexOf(GMask) * destFormatDetails.ComponentSize;
            int destBIndex = BMask == 0 ? -1 : maskOrder.IndexOf(BMask) * destFormatDetails.ComponentSize;

            int sourceAInd = 3 * sourceFormatDetails.ComponentSize;
            int sourceRInd = 2 * sourceFormatDetails.ComponentSize;
            int sourceGInd = 1 * sourceFormatDetails.ComponentSize;
            int sourceBInd = 0;


            var sourceInds = new int[] { sourceBInd, sourceGInd, sourceRInd, sourceAInd };
            var destInds   = new int[] { destBIndex, destGIndex, destRIndex, destAIndex };
            var masks      = new uint[] { BMask, GMask, RMask, AMask };

            int sourceIncrement = 4 * sourceFormatDetails.ComponentSize;

            if (EnableThreading)
            {
                Parallel.For(0, source.Length / sourceIncrement, new ParallelOptions {
                    MaxDegreeOfParallelism = ThreadCount
                }, (ind, loopState) =>
                {
                    WriteUncompressedPixel(source, ind * sourceIncrement, sourceInds, sourceFormatDetails, masks, destination, destStart + ind * byteCount, destInds, destFormatDetails, oneChannel, twoChannel, requiresSignedAdjust);
                });
            }
            else
            {
                for (int i = 0; i < source.Length; i += 4 * sourceFormatDetails.ComponentSize, destStart += byteCount)
                {
                    WriteUncompressedPixel(source, i, sourceInds, sourceFormatDetails, masks, destination, destStart, destInds, destFormatDetails, oneChannel, twoChannel, requiresSignedAdjust);
                }
            }
        }
        public static void Compress8BitBlock(byte[] source, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, int channel, bool isSigned, DDSFormatDetails formatDetails)
        {
            // KFreon: Get min and max
            byte min            = 255;
            byte max            = 0;
            int  channelBitSize = channel * formatDetails.ComponentSize;
            int  count          = sourcePosition + channelBitSize;

            byte[] sourceTexel    = new byte[16];
            int    sourceTexelInd = 0;

            for (int i = 1; i <= 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    byte color = formatDetails.ReadByte(source, count);
                    sourceTexel[sourceTexelInd++] = color; // Cache source
                    if (color > max)
                    {
                        max = color;
                    }
                    else if (color < min)
                    {
                        min = color;
                    }

                    count += 4 * formatDetails.ComponentSize; // skip to next entry in channel
                }
                count = sourcePosition + channelBitSize + sourceLineLength * i;
            }

            // Build Palette
            byte[] Colors = Build8BitPalette(min, max, isSigned);

            // Compress Pixels
            ulong line = 0;

            sourceTexelInd = 0;
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    int  ind   = (i << 2) + j;
                    byte color = sourceTexel[sourceTexelInd++];
                    int  index = GetClosestValue(Colors, color);
                    line |= (ulong)index << (ind * 3);
                }
            }

            byte[] compressed = BitConverter.GetBytes(line);
            destination[destPosition]     = min;
            destination[destPosition + 1] = max;
            for (int i = 2; i < 8; i++)
            {
                destination[destPosition + i] = compressed[i - 2];
            }
        }
Example #23
0
        /// <summary>
        /// Internal write implementation of the sub classes.
        /// </summary>
        /// <param name="writer"></param>
        /// <exception cref="Exception">Expected DDS RGB24 or RGBA32 color format!</exception>
        /// <exception cref="InvalidOperationException">
        /// </exception>
        protected override void _Write(BinaryWriter writer)
        {
            long baseOffset = writer.BaseStream.Position;

            Bitmap bmp = CreateBitmap();

            //bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            if (DataFormat == PvrDataFormat.DDS || DataFormat == PvrDataFormat.DDS_2)
            {
                if (!(PixelFormat == PvrPixelFormat.DDS_DXT1_RGB24 || PixelFormat == PvrPixelFormat.DDS_DXT3_RGBA32))
                {
                    throw new Exception("Expected DDS RGB24 or RGBA32 color format!");
                }

                byte[] ddsBuffer = null;
                if (PixelFormat == PvrPixelFormat.DDS_DXT1_RGB24)
                {
                    DDSFormatDetails ddsFormatDetails = new DDSFormatDetails(DDSFormat.DDS_DXT1);
                    ddsBuffer = DDSGeneral.Save(MipMaps, ddsFormatDetails, DDSGeneral.AlphaSettings.KeepAlpha, DDSGeneral.MipHandling.Default);
                }
                else if (PixelFormat == PvrPixelFormat.DDS_DXT3_RGBA32)
                {
                    DDSFormatDetails ddsFormatDetails = new DDSFormatDetails(DDSFormat.DDS_DXT3);
                    ddsBuffer = DDSGeneral.Save(MipMaps, ddsFormatDetails, DDSGeneral.AlphaSettings.KeepAlpha, DDSGeneral.MipHandling.Default);
                }
                if (HasGlobalIndex)
                {
                    writer.Write(m_gbix);
                    writer.Write(GlobalIndexSize);
                    writer.Write(GlobalIndex);
                }

                writer.Write(m_pvrt);
                writer.Write(ddsBuffer.Length + 16);
                writer.Write((byte)PixelFormat);
                writer.Write((byte)DataFormat);
                writer.Write((ushort)0);
                writer.Write((ushort)Width);
                writer.Write((ushort)Height);
                writer.Write(ddsBuffer);
            }
            else
            {
                // Set the data format and pixel format and load the appropiate codecs
                PixelCodec = PvrPixelCodec.GetPixelCodec(PixelFormat);
                DataCodec  = PvrDataCodec.GetDataCodec(DataFormat);

                // Make sure the pixel and data codecs exists and we can encode to it
                if (PixelCodec == null || !PixelCodec.CanEncode)
                {
                    throw new InvalidOperationException();
                }
                if (DataCodec == null || !DataCodec.CanEncode)
                {
                    throw new InvalidOperationException();
                }
                DataCodec.PixelCodec = PixelCodec;

                byte[] decodedData = null;
                if (DataCodec.PaletteEntries != 0)
                {
                    if (DataCodec.VQ)
                    {
                        decodedData = BitmapToRawVQ(bmp, DataCodec.PaletteEntries, out m_texturePalette);
                    }
                    else
                    {
                        // Convert the bitmap to an array containing indicies.
                        decodedData = BitmapToRawIndexed(bmp, DataCodec.PaletteEntries, out m_texturePalette);

                        // If this texture has an external palette file, set up the palette encoder
                        if (DataCodec.NeedsExternalPalette)
                        {
                            PaletteEncoder = new PvpPaletteEncoder(m_texturePalette, (ushort)DataCodec.PaletteEntries, PixelFormat, PixelCodec);
                        }
                    }
                }
                else
                {
                    decodedData = BitmapToRaw(bmp);
                }

                // Calculate what the length of the texture will be
                int textureLength = 16 + (int)(Width * Height * (DataCodec.Bpp / 8.0));
                if (HasGlobalIndex)
                {
                    textureLength += 16;
                }
                if (DataCodec.PaletteEntries != 0 && !DataCodec.NeedsExternalPalette)
                {
                    textureLength += (DataCodec.PaletteEntries * PixelCodec.Bpp / 8);
                }

                // Calculate the mipmap padding (if the texture contains mipmaps)
                int mipmapPadding = 0;
                if (DataCodec.HasMipmaps)
                {
                    if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP)
                    {
                        // A 1x1 mipmap takes up as much space as a 2x1 mipmap
                        // There are also 4 extra bytes at the end of the file
                        mipmapPadding  = (DataCodec.Bpp) >> 3;
                        textureLength += 4;
                    }
                    else if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP_ALT)
                    {
                        // A 1x1 mipmap takes up as much space as a 2x2 mipmap
                        mipmapPadding = (3 * DataCodec.Bpp) >> 3;
                    }

                    textureLength += mipmapPadding;
                    for (int size = 1; size < Width; size <<= 1)
                    {
                        textureLength += Math.Max((size * size * DataCodec.Bpp) >> 3, 1);
                    }
                }

                MemoryStream output       = new MemoryStream(textureLength);
                BinaryWriter outputWriter = new BinaryWriter(output);

                // Write out the GBIX header (if we are including one)
                if (HasGlobalIndex)
                {
                    outputWriter.Write(m_gbix);
                    outputWriter.Write(GlobalIndexSize);
                    outputWriter.Write(GlobalIndex);
                }

                // Write out the PVRT header
                outputWriter.Write(m_pvrt);
                if (HasGlobalIndex)
                {
                    outputWriter.Write(textureLength - 24);
                }
                else
                {
                    outputWriter.Write(textureLength - 8);
                }
                outputWriter.Write((byte)PixelFormat);
                outputWriter.Write((byte)DataFormat);
                outputWriter.Write((ushort)0);
                outputWriter.Write((ushort)Width);
                outputWriter.Write((ushort)Height);

                // If we have an internal palette, write it
                if (DataCodec.PaletteEntries != 0 && !DataCodec.NeedsExternalPalette)
                {
                    byte[] palette = PixelCodec.EncodePalette(m_texturePalette, DataCodec.PaletteEntries);
                    output.Write(palette, 0, palette.Length);
                }

                // Write out any mipmaps
                if (DataCodec.HasMipmaps)
                {
                    // Write out any padding bytes before the 1x1 mipmap
                    for (int i = 0; i < mipmapPadding; i++)
                    {
                        output.WriteByte(0);
                    }
                    for (int size = 1; size < Width; size <<= 1)
                    {
                        byte[] mipmapDecodedData = null;
                        if (DataCodec.NeedsExternalPalette)
                        {
                            if (DataCodec.VQ)
                            {
                                mipmapDecodedData = BitmapToRawVQResized(bmp, size, 1, m_codeBook);
                            }
                            else
                            {
                                mipmapDecodedData = BitmapToRawIndexedResized(bmp, size, 1, m_palette);
                            }
                        }
                        else
                        {
                            mipmapDecodedData = BitmapToRawResized(bmp, size, 1);
                        }
                        byte[] mipmapTextureData = DataCodec.Encode(mipmapDecodedData, 0, size, size);
                        output.Write(mipmapTextureData, 0, mipmapTextureData.Length);
                    }
                }

                // Write the texture data
                byte[] textureData = DataCodec.Encode(decodedData, Width, Height, null);
                output.Write(textureData, 0, textureData.Length);

                // If the data format is square twiddled with mipmaps, write out the extra bytes.
                if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP)
                {
                    output.Write(new byte[] { 0, 0, 0, 0 }, 0, 4);
                }

                // Compress the texture
                if (CompressionFormat != PvrCompressionFormat.NONE)
                {
                    CompressionCodec = PvrCompressionCodec.GetCompressionCodec(CompressionFormat);
                    if (CompressionCodec != null)
                    {
                        // Ok, we need to convert the current stream to an array, compress it, then write it back to a new stream
                        byte[] buffer = output.ToArray();
                        buffer = CompressionCodec.Compress(buffer, (HasGlobalIndex ? 0x20 : 0x10), PixelCodec, DataCodec);
                        writer.Write(buffer);
                    }
                }
                else
                {
                    writer.Write(output.GetBuffer());
                }
            }
        }
Example #24
0
 internal static int GetCompressedSizeOfImage(int mipCount, DDSFormatDetails destFormatDetails, int baseWidth, int baseHeight)
 {
     return(GetCompressedSizeUpToIndex(mipCount, destFormatDetails, baseWidth, baseHeight));
 }
 void WriteUShort(byte[] source, int sourceStart, DDSFormatDetails sourceFormatDetails, byte[] destination, int destStart)
 {
     byte[] bytes = sourceFormatDetails.ReadUShortAsArray(source, sourceStart);
     destination[destStart]     = bytes[0];
     destination[destStart + 1] = bytes[1];
 }
Example #26
0
        private static MipMap ReadUncompressedMipMap(MemoryStream stream, int mipOffset, int mipWidth, int mipHeight, DDS_Header.DDS_PIXELFORMAT ddspf, DDSFormatDetails formatDetails)
        {
            byte[] data   = stream.GetBuffer();
            byte[] mipmap = new byte[mipHeight * mipWidth * 4 * formatDetails.ComponentSize];

            // Smaller sizes breaks things, so just exclude them
            if (mipHeight >= 4 && mipWidth >= 4)
            {
                DDS_Decoders.ReadUncompressed(data, mipOffset, mipmap, mipWidth * mipHeight, ddspf, formatDetails);
            }

            return(new MipMap(mipmap, mipWidth, mipHeight));
        }
Example #27
0
        private static MipMap ReadCompressedMipMap(MemoryStream compressed, int mipWidth, int mipHeight, int mipOffset, DDSFormatDetails formatDetails, Action <byte[], int, byte[], int, int, bool> DecompressBlock)
        {
            // Gets stream as data. Note that this array isn't necessarily the correct size. Likely to have garbage at the end.
            // Don't want to use ToArray as that creates a new array. Don't want that.
            byte[] CompressedData = compressed.GetBuffer();

            byte[] decompressedData      = new byte[4 * mipWidth * mipHeight * formatDetails.ComponentSize];
            int    decompressedRowLength = mipWidth * 4;
            int    texelRowSkip          = decompressedRowLength * 4;

            int texelCount     = (mipWidth * mipHeight) / 16;
            int numTexelsInRow = mipWidth / 4;

            if (numTexelsInRow < 1)
            {
                numTexelsInRow = 1;
            }

            if (texelCount != 0)
            {
                var action = new Action <int>(texelIndex =>
                {
                    int compressedPosition = mipOffset + texelIndex * formatDetails.BlockSize;
                    int decompressedStart  = (int)(texelIndex / numTexelsInRow) * texelRowSkip + (texelIndex % numTexelsInRow) * 16;

                    // Problem with how I handle dimensions (no virtual padding or anything yet)
                    if (!CheckSize_DXT(mipWidth, mipHeight))
                    {
                        return;
                    }

                    try
                    {
                        DecompressBlock(CompressedData, compressedPosition, decompressedData, decompressedStart, decompressedRowLength, formatDetails.IsPremultipliedFormat);
                    }
                    catch (IndexOutOfRangeException e)
                    {
                        throw;
                    }
                });

                // Actually perform decompression using threading, no threading, or GPU.
                if (EnableThreading)
                {
                    Parallel.For(0, texelCount, new ParallelOptions {
                        MaxDegreeOfParallelism = ThreadCount
                    }, (texelIndex, loopstate) =>
                    {
                        action(texelIndex);
                    });
                }
                else
                {
                    for (int texelIndex = 0; texelIndex < texelCount; texelIndex++)
                    {
                        action(texelIndex);
                    }
                }
            }
            return(new MipMap(decompressedData, mipWidth, mipHeight));
        }
Example #28
0
 /// <summary>
 /// Calculates the compressed size of an image with given parameters.
 /// </summary>
 /// <param name="numMipmaps">Number of mipmaps in image. JPG etc only have 1.</param>
 /// <param name="formatDetails">Detailed information about format.</param>
 /// <param name="width">Width of image (top mip if mip-able)</param>
 /// <param name="height">Height of image (top mip if mip-able)</param>
 /// <returns>Size of compressed image.</returns>
 public static int GetCompressedSize(int numMipmaps, DDSFormatDetails formatDetails, int width, int height)
 {
     return(DDSGeneral.GetCompressedSizeOfImage(numMipmaps, formatDetails, width, height));
 }