File: SystemDrawingExtension.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\Extensions\PresentationFramework-SystemDrawing\PresentationFramework-SystemDrawing.csproj (PresentationFramework-SystemDrawing)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable enable
 
// Description: Helper methods for code that uses types from System.Drawing.
 
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
 
using System.Windows;
using System.Windows.Media.Imaging;
 
namespace MS.Internal
{
    //FxCop can't tell that this class is instantiated via reflection, so suppress the FxCop warning.
    [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
    internal class SystemDrawingExtension : SystemDrawingExtensionMethods
    {
        internal override bool IsBitmap(object? data) => data is Bitmap;
 
        internal override bool IsImage(object? data) => data is Image;
 
        internal override bool IsMetafile(object? data) => data is Metafile;
 
        internal override nint GetHandleFromMetafile(object? data) => data switch
        {
            Metafile metafile => metafile.GetHenhmetafile(),
            _ => 0
        };
 
        internal override object GetMetafileFromHemf(nint hMetafile) => new Metafile(hMetafile, deleteEmf: false);
 
        internal override object? GetBitmap(object? data) => GetBitmapImpl(data);
 
        internal override nint GetHBitmap(object? data, out int width, out int height)
        {
            Bitmap? bitmapData = GetBitmapImpl(data);
 
            if (bitmapData is null)
            {
                width = height = 0;
                return IntPtr.Zero;
            }
 
            // GDI+ returns a DIBSECTION based HBITMAP. The clipboard deals well
            // only with bitmaps created using CreateCompatibleBitmap(). So, we
            // convert the DIBSECTION into a compatible bitmap.
            width = bitmapData.Size.Width;
            height = bitmapData.Size.Height;
            return bitmapData.GetHbitmap();
        }
 
        internal override nint GetHBitmapFromBitmap(object? data) => data is Bitmap bitmap ? bitmap.GetHbitmap() : 0;
 
        internal override nint ConvertMetafileToHBitmap(nint handle)
        {
            Metafile metafile = new(handle, deleteEmf: false);
 
            // Initialize the bitmap size to render the metafile.
            int bitmapheight = metafile.Size.Height;
            int bitmapwidth = metafile.Size.Width;
 
            // We use System.Drawing to render metafile into the bitmap.
            Bitmap bmp = new Bitmap(bitmapwidth, bitmapheight);
            Graphics graphics = Graphics.FromImage(bmp);
            graphics.DrawImage(metafile, 0, 0, bitmapwidth, bitmapheight);
 
            return bmp.GetHbitmap();
        }
 
        internal override Stream GetCommentFromGifStream(Stream stream)
        {
            // Read the GIF header
            Bitmap bitmap = new(stream);
 
            // Read the comment as that is where the ISF is stored.
            // For reference the tag is PropertyTagExifUserComment [0x9286] or 37510 (int)
            PropertyItem? piComment = bitmap.GetPropertyItem(37510);
 
            // Want to make this throw in the same way it used to for now. It would be good
            // to redo this to return null and handle the null case in the various callers.
            return new MemoryStream(piComment!.Value!);
        }
 
        internal override void SaveMetafileToImageStream(MemoryStream metafileStream, Stream imageStream)
        {
            Metafile metafile = new(metafileStream);
            metafile.Save(imageStream, ImageFormat.Png);
        }
 
        // Get a bitmap from the given data (either BitmapSource or Bitmap)
        private static Bitmap? GetBitmapImpl(object? data)
        {
            if (data is BitmapSource bitmapSource)
            {
                // Convert BitmapSource to System.Drawing.Bitmap to get Win32 HBITMAP.
                BitmapEncoder bitmapEncoder;
                Stream bitmapStream;
 
                bitmapEncoder = new BmpBitmapEncoder();
                bitmapEncoder.Frames.Add(BitmapFrame.Create(bitmapSource));
 
                bitmapStream = new MemoryStream();
                bitmapEncoder.Save(bitmapStream);
 
                return new Bitmap(bitmapStream);
            }
            else
            {
                // Get Bitmap data from data object.
                return data as Bitmap;
            }
        }
 
        internal override object GetBitmapFromBitmapSource(object source)
        {
            BitmapSource contentImage = (BitmapSource)source;
            int imageWidth = (int)contentImage.Width;
            int imageHeight = (int)contentImage.Height;
 
            Bitmap bitmapFinal = new(imageWidth, imageHeight, PixelFormat.Format32bppRgb);
 
            BitmapData bmData = bitmapFinal.LockBits(
                new Rectangle(0, 0, imageWidth, imageHeight),
                ImageLockMode.WriteOnly,
                PixelFormat.Format32bppRgb);
 
            FormatConvertedBitmap formatConverter = new FormatConvertedBitmap();
            formatConverter.BeginInit();
            formatConverter.Source = contentImage;
            formatConverter.DestinationFormat = System.Windows.Media.PixelFormats.Bgr32;
            formatConverter.EndInit();
 
            formatConverter.CopyPixels(
                new Int32Rect(0, 0, imageWidth, imageHeight),
                bmData.Scan0,
                bmData.Stride * (bmData.Height - 1) + (bmData.Width * 4),
                bmData.Stride);
 
            bitmapFinal.UnlockBits(bmData);
 
            return bitmapFinal;
        }
 
#if WindowsMetaFile
        // Convert the bitmap to the windows metafile to write it as "\wmetafile" control on rtf content
        internal override string ConvertToMetafileHexDataString(Stream imageStream)
        {
            MemoryStream metafileStream = new MemoryStream();
 
            // Get the graphics to write image on the metafile
            Graphics graphics = Graphics.FromHwnd(IntPtr.Zero);
 
            // Create the empty metafile
            Metafile metafile = new Metafile(metafileStream, graphics.GetHdc(), EmfType.EmfOnly);
 
            // Release HDC
            graphics.ReleaseHdc();
 
            // Create the graphics for metafile destination
            Graphics graphicsTarget = Graphics.FromImage(metafile);
 
            // Create bitmap from the image source stream
            Bitmap bitmap = new Bitmap(imageStream);
 
            // Draw the bitmap image to the metafile
            graphicsTarget.DrawImage(bitmap, Point.Empty);
 
            // Dispose graphics target
            graphicsTarget.Dispose();
 
            // Move to the start position
            metafileStream.Position = 0;
 
            // Create the enhance metafile from metafile stream what we rendered from the bitmap image
            Metafile enhanceMetafile = new Metafile(metafileStream);
 
            // Get the enhance metafile handle from the enhance metafile
            IntPtr hdc = UnsafeNativeMethods.CreateCompatibleDC(new HandleRef(this, IntPtr.Zero));
            IntPtr hEnhanceMetafile = enhanceMetafile.GetHenhmetafile();
 
            // Convert the enhance metafile to the windows metafile with Win32 GDI
            uint windowsMetafileSize = UnsafeNativeMethods.GetWinMetaFileBits(hEnhanceMetafile, 0, null, /*MapMode:MM_ANISOTROPIC*/8, hdc);
            Byte[] windowsMetafileBytes = new Byte[windowsMetafileSize];
            UnsafeNativeMethods.GetWinMetaFileBits(hEnhanceMetafile, windowsMetafileSize, windowsMetafileBytes, /*MapMode:MM_ANISOTROPIC*/8, hdc);
 
            // Dispose metafile and metafile stream
            metafile.Dispose();
            metafileStream.Dispose();
 
            // return the windows metafile hex data string
            return ConvertToImageHexDataString(windowsMetafileBytes);
        }
#endif
    }
}