コード例 #1
0
        internal static int GetCompressedSizeUpToIndex(double mipIndex, ImageFormats.ImageEngineFormatDetails 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);
        }
コード例 #2
0
ファイル: Program.cs プロジェクト: Tsohg/texconvert
        /// <summary>
        /// Converts a single .png file to .dds format.
        /// </summary>
        /// <param name="file">Full path of the file.</param>
        /// <param name="inPath">Full input directory path.</param>
        /// <param name="outPath">Full output directory path.</param>
        /// <param name="index">Integer label. Start at i = 1.</param>
        /// <param name="total">Total number of files to be converted.</param>
        public void ConvertPngToDds(string file, string inPath, string outPath, ref int index, int total)
        {
            //get data about file..."D:\\msys2\\home\\Nathan\\dbg\\00004d824c7204b6f7-DDS_ARGB_8.png"
            string name = Path.GetFileName(file);

            string[] details = name.Split('-');
            if (details.Length > 2)
            {
                throw new Exception("Additional details detected.");
            }
            if (details.Length == 1) //no details found
            {
                details = new string[] { name, "" }
            }
            ;

            ImageFormats.ImageEngineFormatDetails imageDetails;
            switch (details[1].Split('.')[0]) //A bit of a complicated way to only look at the part without file extension...
            {
            case "DDS_ARGB_8":
                imageDetails = new ImageFormats.ImageEngineFormatDetails(ImageEngineFormat.DDS_ARGB_8);
                break;

            case "DDS_G16_R16":
                imageDetails = new ImageFormats.ImageEngineFormatDetails(ImageEngineFormat.DDS_G16_R16);
                break;

            case "DDS_DXT1":
                imageDetails = new ImageFormats.ImageEngineFormatDetails(ImageEngineFormat.DDS_DXT1);
                break;

            case "DDS_DXT5":
            default:     //we don't know what to do here...so we just assume DXT5.
                imageDetails = new ImageFormats.ImageEngineFormatDetails(ImageEngineFormat.DDS_DXT5);
                break;
            }

            //we have already stripped metadata in split. Begin to return it to dds with the stripped metadata.
            FileStream       fs  = new FileStream(Path.Combine(outPath, Path.ChangeExtension(details[0], ".dds")), FileMode.Create);
            ImageEngineImage imi = new ImageEngineImage(Path.Combine(inPath, name));

            Console.Out.WriteLine("Converting: " + Path.GetFileName(file) + " " + index + "\\" + total);
            imi.Save(fs, imageDetails, MipHandling.Default, 0, 0, false);
            fs.Close();
        }
    }
コード例 #3
0
        private void SaveDDS(List <string> paths, string outputPath)
        {
            ImageEngineImage outputImage = new ImageEngineImage(paths[0]);

            for (int i = 1; i < paths.Count; i++)
            {
                ImageEngineImage mipImage = new ImageEngineImage(paths[i]);
                MipMap           mip      = new MipMap(mipImage.MipMaps[0].Pixels, mipImage.Width, mipImage.Height, mipImage.FormatDetails);
                outputImage.MipMaps.Add(mip);
            }
            ImageFormats.ImageEngineFormatDetails outputFormat = new ImageFormats.ImageEngineFormatDetails(ImageEngineFormat.DDS_DXT1);
            byte[] data = outputImage.Save(outputFormat, MipHandling.KeepExisting);

            using (var file = File.Create(outputPath))
            {
                file.Write(data, 0, data.Length);
            }
        }
コード例 #4
0
        // ATI2 3Dc
        internal static void CompressBC5Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails 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);
        }
コード例 #5
0
        internal static void CompressBC3Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails 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 Colour
            CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, false, 0f, alphaSetting, formatDetails);
        }
コード例 #6
0
        internal static void CompressBC2Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails 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] & 0xF0) | (imgData[position + 4] >> 4));
                    destination[destPosition + i + 1] = (byte)((imgData[position + 8] & 0xF0) | (imgData[position + 12] >> 4));

                    position += sourceLineLength;
                }
            }


            // Compress Colour
            CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, false, 0f, alphaSetting, formatDetails);
        }
コード例 #7
0
        static void WriteUncompressedPixel(byte[] source, int sourceStart, int[] sourceInds, ImageFormats.ImageEngineFormatDetails sourceFormatDetails, uint[] masks,
                                           byte[] destination, int destStart, int[] destInds, ImageFormats.ImageEngineFormatDetails 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.WriteColour(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;
                }
            }
        }
コード例 #8
0
 internal static void CompressBC1Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails formatDetails)
 {
     CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition, true, (alphaSetting == AlphaSettings.RemoveAlphaChannel ? 0 : DXT1AlphaThreshold), alphaSetting, formatDetails);
 }
コード例 #9
0
        internal static void ReadUncompressed(byte[] source, int sourceStart, byte[] destination, int pixelCount, DDS_Header.DDS_PIXELFORMAT ddspf, ImageFormats.ImageEngineFormatDetails 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
                }
            }
        }
コード例 #10
0
        private static MipMap ReadUncompressedMipMap(MemoryStream stream, int mipOffset, int mipWidth, int mipHeight, DDS_Header.DDS_PIXELFORMAT ddspf, ImageFormats.ImageEngineFormatDetails 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, formatDetails));
        }
コード例 #11
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, ImageFormats.ImageEngineFormatDetails 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 == ImageEngineFormat.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);
        }
コード例 #12
0
 internal static int GetMipOffset(double mipIndex, ImageFormats.ImageEngineFormatDetails 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));
 }
コード例 #13
0
 static void WriteUncompressedMipMap(byte[] destination, int mipOffset, MipMap mipmap, ImageFormats.ImageEngineFormatDetails destFormatDetails, DDS_Header.DDS_PIXELFORMAT ddspf)
 {
     DDS_Encoders.WriteUncompressed(mipmap.Pixels, destination, mipOffset, ddspf, mipmap.LoadedFormatDetails, destFormatDetails);
 }
コード例 #14
0
        private static MipMap ReadCompressedMipMap(MemoryStream compressed, int mipWidth, int mipHeight, int mipOffset, ImageFormats.ImageEngineFormatDetails 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 (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 (ImageEngine.EnableThreading)
                {
                    Parallel.For(0, texelCount, new ParallelOptions {
                        MaxDegreeOfParallelism = ImageEngine.NumThreads
                    }, (texelIndex, loopstate) =>
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            loopstate.Stop();
                        }

                        action(texelIndex);
                    });
                }
                else
                {
                    for (int texelIndex = 0; texelIndex < texelCount; texelIndex++)
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            break;
                        }

                        action(texelIndex);
                    }
                }
            }
            // No else here cos the lack of texels means it's below texel dimensions (4x4). So the resulting block is set to 0. Not ideal, but who sees 2x2 mipmaps?

            if (ImageEngine.IsCancellationRequested)
            {
                return(null);
            }

            return(new MipMap(decompressedData, mipWidth, mipHeight, formatDetails));
        }
コード例 #15
0
        internal static byte[] Save(List <MipMap> mipMaps, ImageFormats.ImageEngineFormatDetails destFormatDetails, AlphaSettings alphaSetting)
        {
            // Set compressor for Block Compressed textures
            Action <byte[], int, int, byte[], int, AlphaSettings, ImageFormats.ImageEngineFormatDetails> 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)
                {
                    if (ImageEngine.IsCancellationRequested)
                    {
                        break;
                    }

                    var temp = WriteCompressedMipMap(destination, mipOffset, mipmap, blockSize, compressor, alphaSetting);
                    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], destFormatDetails, header.ddspf);
                });

                if (ImageEngine.EnableThreading)
                {
                    Parallel.For(0, mipMaps.Count, new ParallelOptions {
                        MaxDegreeOfParallelism = ImageEngine.NumThreads
                    }, (mip, loopState) =>
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            loopState.Stop();
                        }

                        action(mip);
                    });
                }
                else
                {
                    for (int i = 0; i < mipMaps.Count; i++)
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            break;
                        }

                        action(i);
                    }
                }
            }

            return(ImageEngine.IsCancellationRequested ? null : destination);
        }
コード例 #16
0
 internal static void CompressBC7Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails formatDetails)
 {
     BC7.CompressBC7Block(imgData, sourcePosition, sourceLineLength, destination, destPosition);
 }
コード例 #17
0
 internal static int GetCompressedSizeOfImage(int mipCount, ImageFormats.ImageEngineFormatDetails destFormatDetails, int baseWidth, int baseHeight)
 {
     return(GetCompressedSizeUpToIndex(mipCount, destFormatDetails, baseWidth, baseHeight));
 }
コード例 #18
0
        internal static void WriteUncompressed(byte[] source, byte[] destination, int destStart, DDS_Header.DDS_PIXELFORMAT dest_ddspf, ImageFormats.ImageEngineFormatDetails sourceFormatDetails, ImageFormats.ImageEngineFormatDetails 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 == ImageEngineFormat.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 (ImageEngine.EnableThreading)
            {
                Parallel.For(0, source.Length / sourceIncrement, new ParallelOptions {
                    MaxDegreeOfParallelism = ImageEngine.NumThreads
                }, (ind, loopState) =>
                {
                    if (ImageEngine.IsCancellationRequested)
                    {
                        loopState.Stop();
                    }

                    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)
                {
                    if (ImageEngine.IsCancellationRequested)
                    {
                        break;
                    }

                    WriteUncompressedPixel(source, i, sourceInds, sourceFormatDetails, masks, destination, destStart, destInds, destFormatDetails, oneChannel, twoChannel, requiresSignedAdjust);
                }
            }
        }
コード例 #19
0
        internal static List <MipMap> LoadDDS(MemoryStream compressed, DDS_Header header, int desiredMaxDimension, ImageFormats.ImageEngineFormatDetails formatDetails)
        {
            MipMap[] MipMaps = null;

            int mipWidth             = header.Width;
            int mipHeight            = header.Height;
            ImageEngineFormat 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++)
                {
                    if (ImageEngine.IsCancellationRequested)
                    {
                        break;
                    }


                    // 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 (ImageEngine.EnableThreading)
                {
                    Parallel.For(startMip, orig_estimatedMips, new ParallelOptions {
                        MaxDegreeOfParallelism = ImageEngine.NumThreads
                    }, (mip, loopState) =>
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            loopState.Stop();
                        }

                        action(mip);
                    });
                }
                else
                {
                    for (int i = startMip; i < orig_estimatedMips; i++)
                    {
                        if (ImageEngine.IsCancellationRequested)
                        {
                            break;
                        }

                        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);
        }