internal static void SplitChannels(MipMap mip, string savePath)
        {
            char[] channels = new char[] { 'B', 'G', 'R', 'A' };
            for (int i = 0; i < 4; i++)
            {
                // Extract channel into grayscale image
                var grayChannel = BuildGrayscaleFromChannel(mip.Pixels, i);

                // Save channel
                var img = UsefulThings.WPF.Images.CreateWriteableBitmap(grayChannel, mip.Width, mip.Height, PixelFormats.Gray8);
                PngBitmapEncoder encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(img));
                byte[] bytes = null;
                using (MemoryStream ms = new MemoryStream(grayChannel.Length))
                {
                    encoder.Save(ms);
                    bytes = ms.ToArray();
                }

                if (bytes == null)
                {
                    throw new InvalidDataException("Failed to save channel. Reason unknown.");
                }

                string tempPath    = Path.GetFileNameWithoutExtension(savePath) + "_" + channels[i] + ".png";
                string channelPath = Path.Combine(Path.GetDirectoryName(savePath), UsefulThings.General.FindValidNewFileName(tempPath));
                File.WriteAllBytes(channelPath, bytes);
            }
        }
        BitmapSource GetWPFBitmap(MipMap mip, int maxDimension, bool ShowAlpha)
        {
            BitmapSource bmp = null;

            if (maxDimension != 0)
            {
                // Choose a mip of the correct size, if available.
                var sizedMip = MipMaps.Where(m => (m.Height <= maxDimension && m.Width <= maxDimension) || (m.Width <= maxDimension && m.Height <= maxDimension));
                if (sizedMip.Any())
                {
                    var mip1 = sizedMip.First();
                    bmp = mip1.ToImage();
                }
                else
                {
                    double scale = (double)maxDimension / (Height > Width ? Height : Width);
                    mip = ImageEngine.Resize(mip, scale);
                    bmp = mip.ToImage();
                }
            }
            else
            {
                bmp = mip.ToImage();
            }

            if (!ShowAlpha)
            {
                bmp = new FormatConvertedBitmap(bmp, System.Windows.Media.PixelFormats.Bgr32, null, 0);
            }

            bmp.Freeze();
            return(bmp);
        }
Example #3
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>
        /// <returns>BGRA Pixel Data as stream.</returns>
        internal static List <MipMap> LoadWithCodecs(Stream stream, int decodeWidth, int decodeHeight, bool isDDS)
        {
            if (!WindowsCodecsAvailable)
            {
                return(null);
            }

            List <MipMap> mipmaps = new List <MipMap>();
            bool          alternateDecodeDimensions = decodeWidth != 0 || decodeHeight != 0;

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

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

                    mipmaps.Add(new MipMap(mipmap));
                }

                if (mipmaps.Count == 0)
                {
                    // KFreon: No mips, so resize largest
                    var mip = new MipMap(decoder.Frames[0]);

                    // KFreon: Keep aspect ratio. Take smallest scaling value.
                    double hScale = decodeHeight != 0 ? decodeHeight * 1f / mip.Height : 1;
                    double wScale = decodeWidth != 0 ? decodeWidth * 1f / mip.Width : 1;

                    double scale = hScale < wScale ? hScale : wScale;

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

                mipmaps.Add(new MipMap(bmp));
            }

            return(mipmaps);
        }
Example #4
0
 /// <summary>
 /// Builds a DDS image from an existing mipmap.
 /// </summary>
 /// <param name="mip">Mip to base image on.</param>
 /// <param name="DDSFormat">Format of mipmap.</param>
 public ImageEngineImage(MipMap mip, ImageEngineFormat DDSFormat)
 {
     Format  = new Format(DDSFormat);
     MipMaps = new List <MipMap>()
     {
         mip
     };
     header = DDSGeneral.Build_DDS_Header(1, mip.Height, mip.Width, DDSFormat);
 }
Example #5
0
        /// <summary>
        /// Creates a WPF Bitmap from largest mipmap.
        /// Does NOT require that image remains alive.
        /// </summary>
        /// <param name="mergeAlpha">Only valid if maxDimension set. True = flattens alpha, directly affecting RGB.</param>
        /// <param name="maxDimension">Resizes image or uses a mipmap if available.</param>
        /// <returns>WPF bitmap of largest mipmap.</returns>
        public BitmapSource GetWPFBitmap(int maxDimension = 0, bool mergeAlpha = false)
        {
            MipMap mip = MipMaps[0];

            if (maxDimension != 0)
            {
                // Choose a mip of the correct size, if available.
                var sizedMip = MipMaps.Where(m => (m.Height == maxDimension && m.Width <= maxDimension) || (m.Width == maxDimension && m.Height <= maxDimension));
                if (sizedMip.Any())
                {
                    mip = sizedMip.First();
                }
                else
                {
                    double scale = maxDimension * 1f / (Height > Width ? Height : Width);
                    mip = ImageEngine.Resize(mip, scale, mergeAlpha);
                }
            }
            mip.BaseImage.Freeze();
            return(mip.BaseImage);
        }
Example #6
0
        /// <summary>
        /// Creates a GDI+ bitmap from largest mipmap.
        /// Does NOT require that image remains alive.
        /// </summary>
        /// <param name="ignoreAlpha">True = Previews image without alpha channel.</param>
        /// <param name="maxDimension">Largest size to display.</param>
        /// <param name="mergeAlpha">ONLY valid when maxDimension is set. True = flattens alpha, directly affecting RGB.</param>
        /// <returns>GDI+ bitmap of largest mipmap.</returns>
        public System.Drawing.Bitmap GetGDIBitmap(bool ignoreAlpha, bool mergeAlpha, int maxDimension = 0)
        {
            MipMap mip = MipMaps[0];

            if (maxDimension != 0)
            {
                // Choose a mip of the correct size, if available.
                var sizedMip = MipMaps.Where(m => (m.Height == maxDimension && m.Width <= maxDimension) || (m.Width == maxDimension && m.Height <= maxDimension));
                if (sizedMip.Any())
                {
                    mip = sizedMip.First();
                }
                else
                {
                    double scale = maxDimension * 1f / (Height > Width ? Height : Width);
                    mip = ImageEngine.Resize(mip, scale, mergeAlpha);
                }
            }

            mip.BaseImage.Freeze();
            return(UsefulThings.WinForms.Imaging.CreateBitmap(mip.BaseImage, ignoreAlpha));
        }
Example #7
0
        /// <summary>
        /// Gets a preview.
        /// </summary>
        /// <param name="ShowAlpha">False = Creates a preview without alpha.</param>
        /// <param name="index">Index of mipmap to preview.</param>
        /// <returns>BitmapImage of image.</returns>
        public BitmapSource GeneratePreview(int index, bool ShowAlpha)
        {
            // KFreon: NOTE: Seems to ignore alpha - pretty much ultra useful since premultiplying alpha often removes most of the image
            MipMap mip = MipMaps[index];

            BitmapSource bmp;

            if (ShowAlpha)
            {
                bmp = mip.BaseImage;
            }
            else
            {
                bmp = new FormatConvertedBitmap(mip.BaseImage, System.Windows.Media.PixelFormats.Bgr32, null, 0);
            }

            if (!bmp.IsFrozen)
            {
                bmp.Freeze();
            }

            return(bmp);
        }
        /// <summary>
        /// Scales top mipmap and DESTROYS ALL OTHERS.
        /// </summary>
        /// <param name="scale">Scaling factor. </param>
        public void Resize(double scale)
        {
            MipMap closestMip  = null;
            double newScale    = 0;
            double desiredSize = MipMaps[0].Width * scale;

            double min = double.MaxValue;

            foreach (var mip in MipMaps)
            {
                double temp = Math.Abs(mip.Width - desiredSize);
                if (temp < min)
                {
                    closestMip = mip;
                    min        = temp;
                }
            }

            newScale = desiredSize / closestMip.Width;

            MipMaps[0] = ImageEngine.Resize(closestMip, newScale);
            MipMaps.RemoveRange(1, NumMipMaps - 1);
        }
Example #9
0
        internal static MipMap Resize(MipMap mipMap, double scale, bool mergeAlpha)
        {
            WriteableBitmap bmp        = mipMap.BaseImage;
            int             origWidth  = bmp.PixelWidth;
            int             origHeight = bmp.PixelHeight;
            int             origStride = origWidth * 4;
            int             newWidth   = (int)(origWidth * scale);
            int             newHeight  = (int)(origHeight * scale);
            int             newStride  = newWidth * 4;



            WriteableBitmap alpha = new WriteableBitmap(origWidth, origHeight, 96, 96, PixelFormats.Bgr32, null);

            if (!mergeAlpha)
            {
                // Pull out alpha since scaling with alpha doesn't work properly for some reason
                try
                {
                    unsafe
                    {
                        int   index    = 3;
                        byte *alphaPtr = (byte *)alpha.BackBuffer.ToPointer();
                        byte *mainPtr  = (byte *)bmp.BackBuffer.ToPointer();
                        for (int i = 0; i < origWidth * origHeight * 4; i += 4)
                        {
                            // Set all pixels in alpha to value of alpha from original image - otherwise scaling will interpolate colours
                            alphaPtr[i]     = mainPtr[index];
                            alphaPtr[i + 1] = mainPtr[index];
                            alphaPtr[i + 2] = mainPtr[index];
                            alphaPtr[i + 3] = mainPtr[index];
                            index          += 4;
                        }
                    }
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.ToString());
                    throw;
                }
            }

            FormatConvertedBitmap main = new FormatConvertedBitmap(bmp, PixelFormats.Bgr32, null, 0);



            // Scale RGB
            ScaleTransform    scaletransform = new ScaleTransform(scale, scale);
            TransformedBitmap scaledMain     = new TransformedBitmap(main, scaletransform);


            // Put alpha back in
            FormatConvertedBitmap newConv = new FormatConvertedBitmap(scaledMain, PixelFormats.Bgra32, null, 0);
            WriteableBitmap       resized = new WriteableBitmap(newConv);

            if (!mergeAlpha)
            {
                TransformedBitmap scaledAlpha = new TransformedBitmap(alpha, scaletransform);
                WriteableBitmap   newAlpha    = new WriteableBitmap(scaledAlpha);

                try
                {
                    unsafe
                    {
                        byte *resizedPtr = (byte *)resized.BackBuffer.ToPointer();
                        byte *alphaPtr   = (byte *)newAlpha.BackBuffer.ToPointer();
                        for (int i = 3; i < newStride * newHeight; i += 4)
                        {
                            resizedPtr[i] = alphaPtr[i];
                        }
                    }
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.ToString());
                    throw;
                }
            }



            return(new MipMap(resized));
        }
Example #10
0
        /// <summary>
        /// Loads image from stream.
        /// </summary>
        /// <param name="stream">Full image stream.</param>
        /// <param name="Format">Detected Format.</param>
        /// <param name="extension">File Extension. Used to determine format more easily.</param>
        /// <param name="maxWidth">Maximum width to allow when loading. Resized if enforceResize = true.</param>
        /// <param name="maxHeight">Maximum height to allow when loading. Resized if enforceResize = true.</param>
        /// <param name="enforceResize">True = Resizes image to match either maxWidth or maxHeight.</param>
        /// <param name="header">DDS header of image.</param>
        /// <param name="mergeAlpha">ONLY valid when enforceResize is true. True = Flattens alpha down, directly affecting RGB.</param>
        /// <returns>List of Mipmaps.</returns>
        internal static List <MipMap> LoadImage(Stream stream, out Format Format, string extension, int maxWidth, int maxHeight, bool enforceResize, out DDSGeneral.DDS_HEADER header, bool mergeAlpha)
        {
            // KFreon: See if image is built-in codec agnostic.
            header = null;
            Format = ImageFormats.ParseFormat(stream, extension, ref header);
            List <MipMap> MipMaps = null;

            switch (Format.SurfaceFormat)
            {
            case ImageEngineFormat.BMP:
            case ImageEngineFormat.JPG:
            case ImageEngineFormat.PNG:
                MipMaps = WIC_Codecs.LoadWithCodecs(stream, maxWidth, maxHeight, false);
                break;

            case ImageEngineFormat.DDS_DXT1:
            case ImageEngineFormat.DDS_DXT2:
            case ImageEngineFormat.DDS_DXT3:
            case ImageEngineFormat.DDS_DXT4:
            case ImageEngineFormat.DDS_DXT5:
                if (WindowsWICCodecsAvailable)
                {
                    MipMaps = WIC_Codecs.LoadWithCodecs(stream, maxWidth, maxHeight, true);
                }
                else
                {
                    MipMaps = DDSGeneral.LoadDDS(stream, header, Format, maxHeight > maxWidth ? maxHeight : maxWidth);
                }
                break;

            case ImageEngineFormat.DDS_ARGB:
            case ImageEngineFormat.DDS_A8L8:
            case ImageEngineFormat.DDS_RGB:
            case ImageEngineFormat.DDS_ATI1:
            case ImageEngineFormat.DDS_ATI2_3Dc:
            case ImageEngineFormat.DDS_G8_L8:
            case ImageEngineFormat.DDS_V8U8:
                MipMaps = DDSGeneral.LoadDDS(stream, header, Format, maxHeight > maxWidth ? maxHeight : maxWidth);
                break;

            case ImageEngineFormat.TGA:
                var             img    = new TargaImage(stream);
                byte[]          pixels = UsefulThings.WinForms.Imaging.GetPixelDataFromBitmap(img.Image);
                WriteableBitmap wbmp   = UsefulThings.WPF.Images.CreateWriteableBitmap(pixels, img.Image.Width, img.Image.Height);
                var             mip1   = new MipMap(wbmp);
                MipMaps = new List <MipMap>()
                {
                    mip1
                };
                img.Dispose();
                break;

            default:
                throw new InvalidDataException("Image format is unknown.");
            }

            if (MipMaps == null || MipMaps.Count == 0)
            {
                throw new InvalidDataException("No mipmaps loaded.");
            }


            // KFreon: No resizing requested
            if (maxHeight == 0 && maxWidth == 0)
            {
                return(MipMaps);
            }

            // KFreon: Test if we need to resize
            var top = MipMaps.First();

            if (top.Width == maxWidth || top.Height == maxHeight)
            {
                return(MipMaps);
            }

            int max = maxWidth > maxHeight ? maxWidth : maxHeight;

            // KFreon: Attempt to resize
            var sizedMips = MipMaps.Where(m => m.Width > m.Height ? m.Width <= max : m.Height <= max);

            if (sizedMips != null && sizedMips.Any())  // KFreon: If there's already a mip, return that.
            {
                MipMaps = sizedMips.ToList();
            }
            else if (enforceResize)
            {
                // Get top mip and clear others.
                var mip = MipMaps[0];
                MipMaps.Clear();
                MipMap output = null;

                int divisor = mip.Width > mip.Height ? mip.Width / max : mip.Height / max;

                output = Resize(mip, 1f / divisor, mergeAlpha);

                MipMaps.Add(output);
            }
            return(MipMaps);
        }
        internal static MipMap Resize(MipMap mipMap, double xScale, double yScale)
        {
            var baseBMP = UsefulThings.WPF.Images.CreateWriteableBitmap(mipMap.Pixels, mipMap.Width, mipMap.Height);

            baseBMP.Freeze();
            //return Resize(baseBMP, xScale, yScale, mipMap.Width, mipMap.Height, mipMap.LoadedFormatDetails);

            #region Old code, but want to keep not only for posterity, but I'm not certain the above works in the context below.
            // KFreon: Only do the alpha bit if there is any alpha. Git #444 (https://github.com/ME3Explorer/ME3Explorer/issues/444) exposed the issue where if there isn't alpha, it overruns the buffer.
            bool alphaPresent = mipMap.IsAlphaPresent;

            WriteableBitmap alpha = new WriteableBitmap(mipMap.Width, mipMap.Height, 96, 96, PixelFormats.Bgr32, null);
            if (alphaPresent)// && !mergeAlpha)
            {
                // Pull out alpha since scaling with alpha doesn't work properly for some reason
                try
                {
                    unsafe
                    {
                        alpha.Lock();
                        int   index    = 3;
                        byte *alphaPtr = (byte *)alpha.BackBuffer.ToPointer();
                        for (int i = 0; i < mipMap.Width * mipMap.Height * 4; i += 4)
                        {
                            // Set all pixels in alpha to value of alpha from original image - otherwise scaling will interpolate colours
                            alphaPtr[i]     = mipMap.Pixels[index];
                            alphaPtr[i + 1] = mipMap.Pixels[index];
                            alphaPtr[i + 2] = mipMap.Pixels[index];
                            alphaPtr[i + 3] = mipMap.Pixels[index];
                            index          += 4;
                        }

                        alpha.Unlock();
                    }
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.ToString());
                    throw;
                }
            }

            var bmp = UsefulThings.WPF.Images.CreateWriteableBitmap(mipMap.Pixels, mipMap.Width, mipMap.Height);
            FormatConvertedBitmap main = new FormatConvertedBitmap(bmp, PixelFormats.Bgr32, null, 0);



            // Scale RGB
            ScaleTransform    scaletransform = new ScaleTransform(xScale, yScale);
            TransformedBitmap scaledMain     = new TransformedBitmap(main, scaletransform);

            int newWidth  = (int)(mipMap.Width * xScale);
            int newHeight = (int)(mipMap.Height * yScale);
            int newStride = (int)(newWidth * 4);

            // Put alpha back in
            FormatConvertedBitmap newConv = new FormatConvertedBitmap(scaledMain, PixelFormats.Bgra32, null, 0);
            WriteableBitmap       resized = new WriteableBitmap(newConv);

            if (alphaPresent)// && !mergeAlpha)
            {
                TransformedBitmap scaledAlpha = new TransformedBitmap(alpha, scaletransform);
                WriteableBitmap   newAlpha    = new WriteableBitmap(scaledAlpha);

                try
                {
                    unsafe
                    {
                        resized.Lock();
                        newAlpha.Lock();
                        byte *resizedPtr = (byte *)resized.BackBuffer.ToPointer();
                        byte *alphaPtr   = (byte *)newAlpha.BackBuffer.ToPointer();
                        for (int i = 3; i < newStride * newHeight; i += 4)
                        {
                            resizedPtr[i] = alphaPtr[i];
                        }

                        resized.Unlock();
                        newAlpha.Unlock();
                    }
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.ToString());
                    throw;
                }
            }

            return(new MipMap(resized.GetPixelsAsBGRA32(), newWidth, newHeight, mipMap.LoadedFormatDetails));

            #endregion Old code
        }
 internal static MipMap Resize(MipMap mipMap, double scale)
 {
     return(Resize(mipMap, scale, scale));  // Could be either scale dimension, doesn't matter.
 }
        /// <summary>
        /// Creates a WPF Bitmap from largest mipmap.
        /// Does NOT require that image remains alive.
        /// </summary>
        /// <param name="ShowAlpha">True = flattens alpha, directly affecting RGB.</param>
        /// <param name="maxDimension">Resizes image or uses a mipmap if available. Overrides mipIndex if specified.</param>
        /// <param name="mipIndex">Index of mipmap to retrieve. Overridden by maxDimension if it's specified.</param>
        /// <returns>WPF bitmap of largest mipmap.</returns>
        public BitmapSource GetWPFBitmap(int maxDimension = 0, bool ShowAlpha = false, int mipIndex = 0)
        {
            MipMap mip = MipMaps[mipIndex];

            return(GetWPFBitmap(mip, maxDimension, ShowAlpha));
        }
Example #14
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);
        }