Пример #1
0
        /// <summary>
        /// Gets pixels as a BGRA32 array regardless of their original format (float, short)
        /// </summary>
        /// <param name="width">Width of image.</param>
        /// <param name="height">Height of image.</param>
        /// <param name="pixels">Original pixels.</param>
        /// <param name="formatDetails">Details about format pixels array is currently in.</param>
        /// <returns>BGRA32 pixel array.</returns>
        public static byte[] GetPixelsAsBGRA32(int width, int height, byte[] pixels, ImageFormats.ImageEngineFormatDetails formatDetails)
        {
            if (formatDetails.ComponentSize == 1)
            {
                return(pixels);
            }


            byte[] tempPixels = new byte[width * height * 4];

            Action <int> action = new Action <int>(ind => tempPixels[ind] = formatDetails.ReadByte(pixels, ind * formatDetails.ComponentSize));

            if (EnableThreading)
            {
                Parallel.For(0, tempPixels.Length, new ParallelOptions {
                    MaxDegreeOfParallelism = NumThreads
                }, ind => action(ind));
            }
            else
            {
                for (int i = 0; i < tempPixels.Length; i++)
                {
                    action(i);
                }
            }

            return(tempPixels);
        }
        /// <summary>
        /// Saves fully formatted image in specified format to byte array.
        /// </summary>
        /// <param name="destFormatDetails">Details about destination format.</param>
        /// <param name="GenerateMips">Determines how mipmaps are handled during saving.</param>
        /// <param name="desiredMaxDimension">Maximum size for saved image. Resizes if required, but uses mipmaps if available.</param>
        /// <param name="mipToSave">Index of mipmap to save directly.</param>
        /// <param name="removeAlpha">True = Alpha removed. False = Uses threshold value and alpha values to mask RGB FOR DXT1, otherwise completely removed.</param>
        /// <returns></returns>
        public byte[] Save(ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling GenerateMips, int desiredMaxDimension = 0, int mipToSave = 0, bool removeAlpha = true)
        {
            if (destFormatDetails.Format == ImageEngineFormat.Unknown)
            {
                throw new InvalidOperationException("Save format cannot be 'Unknown'");
            }

            AlphaSettings alphaSetting = AlphaSettings.KeepAlpha;

            if (removeAlpha)
            {
                alphaSetting = AlphaSettings.RemoveAlphaChannel;
            }
            else if (destFormatDetails.Format == ImageEngineFormat.DDS_DXT2 || destFormatDetails.Format == ImageEngineFormat.DDS_DXT4)
            {
                alphaSetting = AlphaSettings.Premultiply;
            }

            // If same format and stuff, can just return original data, or chunks of it.
            if (destFormatDetails.Format == Format)
            {
                return(AttemptSaveUsingOriginalData(destFormatDetails, GenerateMips, desiredMaxDimension, mipToSave, alphaSetting));
            }
            else
            {
                return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave));
            }
        }
        /// <summary>
        /// Saves image in specified format to file. If file exists, it will be overwritten.
        /// </summary>
        /// <param name="destination">File to save to.</param>
        /// <param name="destFormatDetails">Details of destination format.</param>
        /// <param name="GenerateMips">Determines how mipmaps are handled during saving.</param>
        /// <param name="desiredMaxDimension">Maximum size for saved image. Resizes if required, but uses mipmaps if available.</param>
        /// <param name="removeAlpha">True = Alpha removed. False = Uses threshold value and alpha values to mask RGB FOR DXT1 ONLY, otherwise removes completely.</param>
        /// <param name="mipToSave">Index of mipmap to save as single image.</param>
        public async Task Save(string destination, ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling GenerateMips, int desiredMaxDimension = 0, int mipToSave = 0, bool removeAlpha = true)
        {
            var data = Save(destFormatDetails, GenerateMips, desiredMaxDimension, mipToSave, removeAlpha);

            using (FileStream fs = new FileStream(destination, FileMode.Create))
                await fs.WriteAsync(data, 0, data.Length);
        }
        void Load(Stream stream, int maxDimension)
        {
            CompressedSize = (int)stream.Length;
            Header         = ImageEngine.LoadHeader(stream);

            // DX10
            var DX10Format = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;

            if (Header is Headers.DDS_Header)
            {
                DX10Format = ((Headers.DDS_Header)Header).DX10_DXGI_AdditionalHeader.dxgiFormat;
            }


            ImageEngineFormat tempFormat = Header.Format;

            if (DX10Format == DDS_Header.DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT)   // Trickses to get around the DX10 float header deal - Apparently float formats should be specified with the DX10 header...
            {
                tempFormat = ImageEngineFormat.DDS_ARGB_32F;
                var tempPF = ((DDS_Header)Header).ddspf;
                tempPF.dwRBitMask          = 1;
                tempPF.dwGBitMask          = 2;
                tempPF.dwBBitMask          = 3;
                tempPF.dwABitMask          = 4;
                ((DDS_Header)Header).ddspf = tempPF;
            }

            FormatDetails = new ImageFormats.ImageEngineFormatDetails(tempFormat, DX10Format);
            MipMaps       = ImageEngine.LoadImage(stream, Header, maxDimension, 0, FormatDetails);

            // Read original data
            OriginalData    = new byte[CompressedSize];
            stream.Position = 0;
            stream.Read(OriginalData, 0, CompressedSize);
        }
Пример #5
0
        /// <summary>
        /// Creates a Mipmap object from a WPF image.
        /// </summary>
        public MipMap(byte[] pixels, int width, int height, ImageFormats.ImageEngineFormatDetails details)
        {
            Pixels = pixels;
            Width  = width;
            Height = height;
            LoadedFormatDetails = details;

            UncompressedSize = ImageFormats.GetUncompressedSize(width, height, details.MaxNumberOfChannels, false);
        }
        void Load(Stream stream, int maxDimension)
        {
            CompressedSize = (int)stream.Length;
            Header         = ImageEngine.LoadHeader(stream);

            // DX10
            var DX10Format = DDS_Header.DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;

            if (Header is Headers.DDS_Header)
            {
                DX10Format = ((Headers.DDS_Header)Header).DX10_DXGI_AdditionalHeader.dxgiFormat;
            }

            FormatDetails = new ImageFormats.ImageEngineFormatDetails(Header.Format, DX10Format);
            MipMaps       = ImageEngine.LoadImage(stream, Header, maxDimension, 0, FormatDetails);

            // Read original data
            OriginalData    = new byte[CompressedSize];
            stream.Position = 0;
            stream.Read(OriginalData, 0, CompressedSize);
        }
Пример #7
0
        internal static void TestDDSMipSize(List <MipMap> newMips, ImageFormats.ImageEngineFormatDetails destFormatDetails, int width, int height, out double fixXScale, out double fixYScale, MipHandling mipChoice)
        {
            fixXScale = 0;
            fixYScale = 0;
            if (destFormatDetails.IsBlockCompressed && (!UsefulThings.General.IsPowerOfTwo(width) || !UsefulThings.General.IsPowerOfTwo(height)))
            {
                // If only keeping top mip, and that mip is divisible by 4, it's ok.
                if ((mipChoice == MipHandling.KeepTopOnly || mipChoice == MipHandling.KeepExisting) &&
                    DDSGeneral.CheckSize_DXT(width, height))
                {
                    return;
                }


                double newWidth  = 0;
                double newHeight = 0;

                // Takes into account aspect ratio (a little bit)
                double aspect = width / height;
                if (aspect > 1)
                {
                    newWidth = UsefulThings.General.RoundToNearestPowerOfTwo(width);
                    var tempScale = newWidth / width;
                    newHeight = UsefulThings.General.RoundToNearestPowerOfTwo((int)(height * tempScale));
                }
                else
                {
                    newHeight = UsefulThings.General.RoundToNearestPowerOfTwo(height);
                    var tempScale = newHeight / height;
                    newWidth = UsefulThings.General.RoundToNearestPowerOfTwo((int)(width * tempScale));
                }


                // Little extra bit to allow integer cast from Double with the correct answer. Occasionally dimensions * scale would be 511.99999999998 instead of 512, so adding a little allows the integer cast to return correct value.
                fixXScale  = 1d * newWidth / width + 0.001;
                fixYScale  = 1d * newHeight / height + 0.001;
                newMips[0] = Resize(newMips[0], fixXScale, fixYScale);
            }
        }
Пример #8
0
        static async Task <ConcurrentBag <string> > DoBulkParallel(IEnumerable <string> files, ImageFormats.ImageEngineFormatDetails destFormatDetails, string saveFolder,
                                                                   MipHandling saveMipType = MipHandling.Default, bool useSourceAsDestination = false, bool removeAlpha = false, IProgress <int> progressReporter = null)
        {
            ConcurrentBag <string> failures = new ConcurrentBag <string>();

            BufferBlock <string> fileNameStore = new BufferBlock <string>();
            int maxParallelism = ImageEngine.NumThreads == 1 ? 1 :
                                 (ImageEngine.NumThreads == -1 ? Environment.ProcessorCount : ImageEngine.NumThreads);


            // Define block to perform each conversion
            var encoder = new TransformBlock <string, Tuple <byte[], string> >(file =>
            {
                byte[] data = null;

                string filename = Path.GetFileNameWithoutExtension(file) + "." + destFormatDetails.Extension;
                string path     = Path.Combine(useSourceAsDestination ? Path.GetDirectoryName(file) : saveFolder, filename);
                path            = UsefulThings.General.FindValidNewFileName(path);


                using (ImageEngineImage img = new ImageEngineImage(file))
                {
                    try
                    {
                        data = img.Save(destFormatDetails, saveMipType, removeAlpha: removeAlpha);
                    }
                    catch (Exception e)
                    {
                        failures.Add(path + "  Reason: " + e.ToString());
                    }
                }

                progressReporter.Report(1);  // Value not relevent.
                return(new Tuple <byte[], string>(data, path));
            }, new ExecutionDataflowBlockOptions {
                MaxDegreeOfParallelism = maxParallelism, BoundedCapacity = maxParallelism
            });

            // Define block to write converted data to disk
            var diskWriter = new ActionBlock <Tuple <byte[], string> >(tuple =>
            {
                string path = UsefulThings.General.FindValidNewFileName(tuple.Item2);

                try
                {
                    File.WriteAllBytes(path, tuple.Item1);
                }
                catch (Exception e)
                {
                    failures.Add(path + "  Reason: " + e.ToString());
                }
            }, new ExecutionDataflowBlockOptions {
                MaxDegreeOfParallelism = 2, BoundedCapacity = maxParallelism
            });                                                                                                      // Limit to 2 disk write operations at a time, but allow many to be stored in it's buffer.


            // Link blocks together
            fileNameStore.LinkTo(encoder, new DataflowLinkOptions {
                PropagateCompletion = true
            });
            encoder.LinkTo(diskWriter, new DataflowLinkOptions {
                PropagateCompletion = true
            });

            // Begin production
            new Action(async() =>
            {
                foreach (var file in files)
                {
                    await fileNameStore.SendAsync(file);
                }

                fileNameStore.Complete();
            }).Invoke();

            await diskWriter.Completion;

            return(failures);
        }
Пример #9
0
        /// <summary>
        /// Performs a bulk conversion of a bunch of images given conversion parameters.
        /// </summary>
        /// <param name="files">List of supported files to be converted.</param>
        /// <param name="saveFolder">Destination folder of all textures. Can be null if <paramref name="useSourceAsDestination"/> is set.</param>
        /// <param name="saveMipType">Determines how to handle mipmaps for converted images.</param>
        /// <param name="useSourceAsDestination">True = Converted images are saved next to the originals.</param>
        /// <param name="removeAlpha">True = Alpha is removed from converted images.</param>
        /// <param name="destFormatDetails">Details about destination format.</param>
        /// <param name="progressReporter">Progress reporting callback.</param>
        /// <param name="useSourceFormat">No format conversion is performed if possible.</param>
        /// <returns>Errors</returns>
        public static async Task <ConcurrentBag <string> > BulkConvert(IEnumerable <string> files, ImageFormats.ImageEngineFormatDetails destFormatDetails, bool useSourceFormat, string saveFolder,
                                                                       MipHandling saveMipType = MipHandling.Default, bool useSourceAsDestination = false, bool removeAlpha = false, IProgress <int> progressReporter = null)
        {
            ConcurrentBag <string> Failures = new ConcurrentBag <string>();


            // Test if can parallelise uncompressed saving
            // Below says: Only formats that don't support mips or do but aren't block compressed - can be parallised. Also don't parallelise if using source formats.
            bool supportsParallel = false;

            if (!useSourceFormat)
            {
                supportsParallel  = !useSourceFormat && !destFormatDetails.IsMippable;
                supportsParallel |= !supportsParallel && !destFormatDetails.IsBlockCompressed;
            }


            if (EnableThreading && supportsParallel)
            {
                Failures = await DoBulkParallel(files, destFormatDetails, saveFolder, saveMipType, useSourceAsDestination, removeAlpha, progressReporter);
            }
            else
            {
                foreach (var file in files)
                {
                    using (ImageEngineImage img = new ImageEngineImage(file))
                    {
                        // Using source format can only come into this leg of the operation.
                        var saveFormatDetails = useSourceFormat ? img.FormatDetails : destFormatDetails;

                        string filename = useSourceFormat ? Path.GetFileName(file) : Path.GetFileNameWithoutExtension(file) + "." + destFormatDetails.Extension;  // This can stay destFormatDetails instead of saveFormatDetails as it shouldn't be able to get here if destFormatDetails not set.
                        string path     = Path.Combine(useSourceAsDestination ? Path.GetDirectoryName(file) : saveFolder, filename);

                        path = UsefulThings.General.FindValidNewFileName(path);

                        try
                        {
                            await img.Save(path, saveFormatDetails, saveMipType, removeAlpha : removeAlpha);
                        }
                        catch (Exception e)
                        {
                            Failures.Add(path + "  Reason: " + e.ToString());
                        }
                    }

                    progressReporter?.Report(1);   // Value not relevent.
                }
            }


            return(Failures);
        }
Пример #10
0
        /// <summary>
        /// Save mipmaps as given format to stream.
        /// </summary>
        /// <param name="MipMaps">List of Mips to save.</param>
        /// <param name="mipChoice">Determines how to handle mipmaps.</param>
        /// <param name="maxDimension">Maximum value for either image dimension.</param>
        /// <param name="alphaSetting">Determines how to handle alpha.</param>
        /// <param name="mipToSave">0 based index on which mipmap to make top of saved image.</param>
        /// <param name="destFormatDetails">Details about the destination format.</param>
        /// <returns>True on success.</returns>
        internal static byte[] Save(List <MipMap> MipMaps, ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling mipChoice, AlphaSettings alphaSetting, int maxDimension = 0, int mipToSave = 0)
        {
            List <MipMap> newMips = new List <MipMap>(MipMaps);

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

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

            // KFreon: Resize if asked
            if (maxDimension != 0 && maxDimension < width && maxDimension < height)
            {
                if (!UsefulThings.General.IsPowerOfTwo(maxDimension))
                {
                    throw new ArgumentException($"{nameof(maxDimension)} must be a power of 2. Got {nameof(maxDimension)} = {maxDimension}");
                }

                // KFreon: Check if there's a mipmap suitable, removes all larger mipmaps
                var validMipmap = newMips.Where(img => (img.Width == maxDimension && img.Height <= maxDimension) || (img.Height == maxDimension && img.Width <= maxDimension));  // Check if a mip dimension is maxDimension and that the other dimension is equal or smaller
                if (validMipmap?.Count() != 0)
                {
                    int index = newMips.IndexOf(validMipmap.First());
                    newMips.RemoveRange(0, index);
                }
                else
                {
                    // KFreon: Get the amount the image needs to be scaled. Find largest dimension and get it's scale.
                    double scale = maxDimension * 1d / (width > height ? width : height);

                    // KFreon: No mip. Resize.
                    newMips[0] = Resize(newMips[0], scale);
                }
            }

            // KFreon: Ensure we have a power of two for dimensions FOR DDS ONLY
            TestDDSMipSize(newMips, destFormatDetails, width, height, out double fixXScale, out double fixYScale, mipChoice);

            if (fixXScale != 0 || fixYScale != 0 || mipChoice == MipHandling.KeepTopOnly)
            {
                DestroyMipMaps(newMips, mipToSave);
            }

            if ((fixXScale != 0 || fixXScale != 0) && destFormatDetails.IsMippable && mipChoice != MipHandling.KeepTopOnly)
            {
                DDSGeneral.BuildMipMaps(newMips);
            }


            byte[] destination = null;
            if (destFormatDetails.IsDDS)
            {
                destination = DDSGeneral.Save(newMips, destFormatDetails, alphaSetting);
            }
            else
            {
                // KFreon: Try saving with built in codecs
                var mip = newMips[0];


                // Fix formatting
                byte[] newPixels = new byte[mip.Width * mip.Height * 4];
                for (int i = 0, j = 0; i < newPixels.Length; i++, j += mip.LoadedFormatDetails.ComponentSize)
                {
                    newPixels[i] = mip.LoadedFormatDetails.ReadByte(mip.Pixels, j);
                }

                destination = WIC_Codecs.SaveWithCodecs(newPixels, destFormatDetails.Format, mip.Width, mip.Height, alphaSetting);
            }

            return(destination);
        }
Пример #11
0
        internal static List <MipMap> LoadImage(Stream imageStream, AbstractHeader header, int maxDimension, double scale, ImageFormats.ImageEngineFormatDetails formatDetails)
        {
            imageStream.Seek(0, SeekOrigin.Begin);
            List <MipMap> MipMaps = null;

            int decodeWidth  = header.Width > header.Height ? maxDimension : 0;
            int decodeHeight = header.Width < header.Height ? maxDimension : 0;

            switch (header.Format)
            {
            case ImageEngineFormat.DDS_DXT1:
            case ImageEngineFormat.DDS_DXT2:
            case ImageEngineFormat.DDS_DXT3:
            case ImageEngineFormat.DDS_DXT4:
            case ImageEngineFormat.DDS_DXT5:
                MipMaps = WIC_Codecs.LoadWithCodecs(imageStream, decodeWidth, decodeHeight, scale, true, formatDetails);
                if (MipMaps == null)
                {
                    // Windows codecs unavailable/failed. Load with mine.
                    MipMaps = DDSGeneral.LoadDDS((MemoryStream)imageStream, (DDS_Header)header, maxDimension, formatDetails);
                }
                break;

            case ImageEngineFormat.DDS_G8_L8:
            case ImageEngineFormat.DDS_ARGB_4:
            case ImageEngineFormat.DDS_RGB_8:
            case ImageEngineFormat.DDS_V8U8:
            case ImageEngineFormat.DDS_A8L8:
            case ImageEngineFormat.DDS_ARGB_8:
            case ImageEngineFormat.DDS_ARGB_32F:
            case ImageEngineFormat.DDS_ABGR_8:
            case ImageEngineFormat.DDS_G16_R16:
            case ImageEngineFormat.DDS_R5G6B5:
            case ImageEngineFormat.DDS_ATI1:
            case ImageEngineFormat.DDS_ATI2_3Dc:
            case ImageEngineFormat.DDS_CUSTOM:
            case ImageEngineFormat.DDS_DX10:
                MipMaps = DDSGeneral.LoadDDS((MemoryStream)imageStream, (DDS_Header)header, maxDimension, formatDetails);
                break;

            case ImageEngineFormat.GIF:
            case ImageEngineFormat.JPG:
            case ImageEngineFormat.PNG:
            case ImageEngineFormat.BMP:
            case ImageEngineFormat.TIF:
                MipMaps = WIC_Codecs.LoadWithCodecs(imageStream, decodeWidth, decodeHeight, scale, false, formatDetails);
                break;

            case ImageEngineFormat.TGA:
                using (var tga = new TargaImage(imageStream, ((TGA_Header)header).header))
                    MipMaps = new List <MipMap>()
                    {
                        new MipMap(tga.ImageData, tga.Header.Width, tga.Header.Height, formatDetails)
                    };
                break;

            default:
                throw new FormatException($"Format unknown: {header.Format}.");
            }

            return(MipMaps);
        }
        byte[] AttemptSaveUsingOriginalData(ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling GenerateMips, int desiredMaxDimension, int mipToSave, AlphaSettings alphaSetting)
        {
            int        start      = 0;
            int        destStart  = 0;
            int        length     = OriginalData.Length;
            int        newWidth   = Width;
            int        newHeight  = Height;
            DDS_Header tempHeader = null;

            byte[] data             = null;
            byte[] tempOriginalData = OriginalData;

            if (destFormatDetails.IsDDS)
            {
                destStart = destFormatDetails.HeaderSize;
                start     = destStart;

                int mipCount = 0;

                if (mipToSave != 0)
                {
                    mipCount  = 1;
                    newWidth  = MipMaps[mipToSave].Width;
                    newHeight = MipMaps[mipToSave].Height;

                    start  = ImageFormats.GetCompressedSize(mipToSave, destFormatDetails, Width, Height);
                    length = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight);
                }
                else if (desiredMaxDimension != 0 && desiredMaxDimension < Width && desiredMaxDimension < Height)
                {
                    int index = MipMaps.FindIndex(t => t.Width < desiredMaxDimension && t.Height < desiredMaxDimension);

                    // If none found, do a proper save and see what happens.
                    if (index == -1)
                    {
                        return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave));
                    }

                    mipCount -= index;
                    newWidth  = MipMaps[index].Width;
                    newHeight = MipMaps[index].Height;

                    start  = ImageFormats.GetCompressedSize(index, destFormatDetails, Width, Height);
                    length = ImageFormats.GetCompressedSize(mipCount, destFormatDetails, newWidth, newHeight);
                }
                else
                {
                    if (alphaSetting == AlphaSettings.RemoveAlphaChannel)
                    {
                        // Can't edit alpha directly in premultiplied formats. Not easily anyway.
                        if (destFormatDetails.IsPremultipliedFormat)
                        {
                            return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave));
                        }


                        // DDS Formats only
                        switch (destFormatDetails.Format)
                        {
                        // Excluded cos they have no true alpha
                        case ImageEngineFormat.DDS_A8:
                        case ImageEngineFormat.DDS_A8L8:
                        case ImageEngineFormat.DDS_ATI1:
                        case ImageEngineFormat.DDS_ATI2_3Dc:
                        case ImageEngineFormat.DDS_V8U8:
                        case ImageEngineFormat.DDS_G16_R16:
                        case ImageEngineFormat.DDS_G8_L8:
                        case ImageEngineFormat.DDS_R5G6B5:
                        case ImageEngineFormat.DDS_RGB_8:
                        case ImageEngineFormat.DDS_DXT1:
                            break;

                        // Exluded cos they're alpha isn't easily edited
                        case ImageEngineFormat.DDS_DXT2:
                        case ImageEngineFormat.DDS_DXT4:
                            break;

                        // Excluded cos they're currently unsupported
                        case ImageEngineFormat.DDS_CUSTOM:
                        case ImageEngineFormat.DDS_DX10:
                        case ImageEngineFormat.DDS_ARGB_4:
                            break;

                        case ImageEngineFormat.DDS_ABGR_8:
                        case ImageEngineFormat.DDS_ARGB_32F:
                        case ImageEngineFormat.DDS_ARGB_8:
                        case ImageEngineFormat.DDS_DXT3:
                        case ImageEngineFormat.DDS_DXT5:
                            tempOriginalData = new byte[OriginalData.Length];
                            Array.Copy(OriginalData, tempOriginalData, OriginalData.Length);

                            // Edit alpha values
                            int    alphaStart = 128;
                            int    alphaJump  = 0;
                            byte[] alphaBlock = null;
                            if (destFormatDetails.IsBlockCompressed)
                            {
                                alphaJump  = 16;
                                alphaBlock = new byte[8];
                                for (int i = 0; i < 8; i++)
                                {
                                    alphaBlock[i] = 255;
                                }
                            }
                            else
                            {
                                alphaJump  = destFormatDetails.ComponentSize * 4;
                                alphaBlock = new byte[destFormatDetails.ComponentSize];

                                switch (destFormatDetails.ComponentSize)
                                {
                                case 1:
                                    alphaBlock[0] = 255;
                                    break;

                                case 2:
                                    alphaBlock = BitConverter.GetBytes(ushort.MaxValue);
                                    break;

                                case 4:
                                    alphaBlock = BitConverter.GetBytes(1f);
                                    break;
                                }
                            }

                            for (int i = alphaStart; i < OriginalData.Length; i += alphaJump)
                            {
                                Array.Copy(alphaBlock, 0, tempOriginalData, i, alphaBlock.Length);
                            }

                            break;
                        }
                    }


                    switch (GenerateMips)
                    {
                    case MipHandling.KeepExisting:
                        mipCount = NumMipMaps;
                        break;

                    case MipHandling.Default:
                        if (NumMipMaps > 1)
                        {
                            mipCount = NumMipMaps;
                        }
                        else
                        {
                            goto case MipHandling.GenerateNew;      // Eww goto...
                        }
                        break;

                    case MipHandling.GenerateNew:
                        ImageEngine.DestroyMipMaps(MipMaps);
                        ImageEngine.TestDDSMipSize(MipMaps, destFormatDetails, Width, Height, out double fixXScale, out double fixYScale, GenerateMips);

                        // Wrong sizing, so can't use original data anyway.
                        if (fixXScale != 0 || fixYScale != 0)
                        {
                            return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave));
                        }


                        mipCount = DDSGeneral.BuildMipMaps(MipMaps);

                        // Compress mipmaps excl top
                        byte[] formattedMips = DDSGeneral.Save(MipMaps.GetRange(1, MipMaps.Count - 1), destFormatDetails, alphaSetting);
                        if (formattedMips == null)
                        {
                            return(null);
                        }

                        // Get top mip size and create destination array
                        length = ImageFormats.GetCompressedSize(0, destFormatDetails, newWidth, newHeight);     // Should be the length of the top mipmap.
                        data   = new byte[formattedMips.Length + length];

                        // Copy smaller mips to destination
                        Array.Copy(formattedMips, destFormatDetails.HeaderSize, data, length, formattedMips.Length - destFormatDetails.HeaderSize);
                        break;

                    case MipHandling.KeepTopOnly:
                        mipCount = 1;
                        length   = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight);
                        break;
                    }
                }

                // Header
                tempHeader = new DDS_Header(mipCount, newHeight, newWidth, destFormatDetails.Format, destFormatDetails.DX10Format);
            }

            // Use existing array, otherwise create one.
            data = data ?? new byte[length];
            Array.Copy(tempOriginalData, start, data, destStart, length - destStart);

            // Write header if existing (DDS Only)
            if (tempHeader != null)
            {
                tempHeader.WriteToArray(data, 0);
            }

            return(data);
        }
        /// <summary>
        /// Saves image in specified format to stream.
        /// Stream position not reset before or after.
        /// </summary>
        /// <param name="destination">Stream to write to at current position.</param>
        /// <param name="destFormatDetails">Details of destination format</param>
        /// <param name="GenerateMips">Determines how mipmaps are handled during saving.</param>
        /// <param name="desiredMaxDimension">Maximum dimension of saved image. Keeps aspect.</param>
        /// <param name="mipToSave">Specifies a mipmap to save within the whole.</param>
        /// <param name="removeAlpha">True = removes alpha. False = Uses threshold value and alpha values to mask RGB FOR DXT1 ONLY, otherwise removes completely.</param>
        public void Save(Stream destination, ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling GenerateMips, int desiredMaxDimension = 0, int mipToSave = 0, bool removeAlpha = true)
        {
            var data = Save(destFormatDetails, GenerateMips, desiredMaxDimension, mipToSave, removeAlpha);

            destination.Write(data, 0, data.Length);
        }
Пример #14
0
        internal static MipMap Resize(BitmapSource baseBMP, double xScale, double yScale, int width, int height, ImageFormats.ImageEngineFormatDetails formatDetails)
        {
            int origWidth  = width;
            int origHeight = height;
            int origStride = origWidth * 4;
            int newWidth   = (int)(origWidth * xScale);
            int newHeight  = (int)(origHeight * yScale);
            int newStride  = newWidth * 4;

            var bmp = UsefulThings.WPF.Images.CreateWPFBitmap(baseBMP, newWidth, newHeight);

            bmp.Freeze();

            return(new MipMap(bmp.GetPixelsAsBGRA32(), newWidth, newHeight, formatDetails));
        }
Пример #15
0
        /// <summary>
        /// Loads useful information from image stream using Windows 8.1+ codecs.
        /// </summary>
        /// <param name="stream">Stream containing entire file. NOT just pixels.</param>
        /// <param name="decodeWidth">Width to decode as. Aspect ratio unchanged if decodeHeight = 0.</param>
        /// <param name="decodeHeight">Height to decode as. Aspect ratio unchanged if decodeWidth = 0.</param>
        /// <param name="isDDS">True = image is a DDS.</param>
        /// <param name="scale">DOMINANT. DecodeWidth and DecodeHeight ignored if this is > 0. Amount to scale by. Range 0-1.</param>
        /// <param name="formatDetails">Details about the format being loaded.</param>
        /// <returns>BGRA Pixel Data as stream.</returns>
        internal static List <MipMap> LoadWithCodecs(Stream stream, int decodeWidth, int decodeHeight, double scale, bool isDDS, ImageFormats.ImageEngineFormatDetails formatDetails)
        {
            if (isDDS && !ImageEngine.WindowsWICCodecsAvailable)
            {
                return(null);
            }

            bool alternateDecodeDimensions = decodeHeight != 0 || decodeWidth != 0 || scale != 0;
            int  alternateWidth            = decodeWidth;
            int  alternateHeight           = decodeHeight;

            List <MipMap> mipmaps = new List <MipMap>();

            if (isDDS)
            {
                // KFreon: Attempt to load any mipmaps
                stream.Seek(0, SeekOrigin.Begin);
                var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.OnDemand);

                // Setup alternateDimensions if required
                if (scale != 0)
                {
                    alternateHeight = (int)(decoder.Frames[0].Height * scale);
                    alternateWidth  = (int)(decoder.Frames[0].Width * scale);
                }

                foreach (var mipmap in decoder.Frames)
                {
                    // KFreon: Skip mipmaps that are too big if asked to load a smaller image
                    if (alternateDecodeDimensions)
                    {
                        if ((alternateWidth != 0 && mipmap.Width > alternateWidth) ||
                            (alternateHeight != 0 && mipmap.Height > alternateHeight))
                        {
                            continue;
                        }
                    }

                    mipmaps.Add(new MipMap(mipmap.GetPixelsAsBGRA32(), mipmap.PixelWidth, mipmap.PixelHeight, formatDetails));
                }

                if (mipmaps.Count == 0)
                {
                    // KFreon: Image has no mips, so resize largest
                    var frame = decoder.Frames[0];
                    var mip   = new MipMap(frame.GetPixelsAsBGRA32(), frame.PixelWidth, frame.PixelHeight, formatDetails);

                    // Calculate scale if required
                    if (scale == 0)
                    {
                        double xScale = alternateWidth * 1.0 / frame.PixelWidth;
                        double yScale = alternateHeight * 1.0 / frame.PixelHeight;

                        scale = xScale == 0 ? yScale : xScale;
                    }

                    mip = ImageEngine.Resize(mip, scale);
                    mipmaps.Add(mip);
                }
            }
            else
            {
                // KFreon: No Mipmaps
                BitmapImage bmp = AttemptUsingWindowsCodecs(stream, alternateWidth, alternateHeight);
                if (bmp == null)
                {
                    return(null);
                }
                bmp.Freeze();

                mipmaps.Add(new MipMap(bmp.GetPixelsAsBGRA32(), bmp.PixelWidth, bmp.PixelHeight, formatDetails));
            }

            return(mipmaps);
        }
Пример #16
0
        /// <summary>
        /// Loads useful information from an image file.
        /// </summary>
        /// <param name="imageFile">Path to image file.</param>
        /// <param name="decodeWidth">Width to decode to. Aspect unchanged if decodeHeight = 0.</param>
        /// <param name="decodeHeight">Height to decode to. Aspect unchanged if decodeWidth = 0.</param>
        /// <param name="scale">DOMINANT. decodeWidth and decodeHeight ignored if this is > 0. Amount to scale by. Range 0-1.</param>
        /// <param name="formatDetails">Details about the format being loaded.</param>
        /// <param name="isDDS">True = Image is a DDS.</param>
        /// <returns>BGRA Pixel Data as stream.</returns>
        internal static List <MipMap> LoadWithCodecs(string imageFile, int decodeWidth, int decodeHeight, double scale, bool isDDS, ImageFormats.ImageEngineFormatDetails formatDetails)
        {
            if (isDDS && !ImageEngine.WindowsWICCodecsAvailable)
            {
                return(null);
            }

            using (FileStream fs = new FileStream(imageFile, FileMode.Open, FileAccess.Read, FileShare.Read))
                return(LoadWithCodecs(fs, decodeWidth, decodeHeight, scale, isDDS, formatDetails));
        }