WPF Image и Windows Bitmaps преобразуют размеры изображения

У меня есть набор методов в моем служебном классе.

Они должны быть конвертированы между типами изображений WPF и Windows, но когда я их использую, мое изображение сильно уменьшается (при просмотре кода его размер должен изменяться только на 1 пиксель или около того на обеих осях из-за двойных преобразований в int, но я явно чего то не хватает)

Я прошу прощения за гигантский пост, я просто не могу найти проблему.

Методы из ImageSource (WPF) в Bitmap (Windows):

public Bitmap ImageSourceToBitmap(Image source)
{
    var targetBitmap = new RenderTargetBitmap(
        (int) source.Source.Width,
        (int) source.Source.Height,
        96d, 96d,
        PixelFormats.Pbgra32);

    targetBitmap.Render(source);

    var memoryStream = new MemoryStream();
    var bitmapEncoder = new BmpBitmapEncoder();
    bitmapEncoder.Frames.Add(BitmapFrame.Create(targetBitmap));
    bitmapEncoder.Save(memoryStream);

    memoryStream.Position = 0;
    var bitmapImage = new BitmapImage();
    bitmapImage.BeginInit();
    bitmapImage.StreamSource = memoryStream;
    bitmapImage.EndInit();

    var resultBitmap = BitmapSourceToBitmap(bitmapImage);

    return resultBitmap;
}

public Bitmap BitmapSourceToBitmap(BitmapSource source)
{
    var width = source.PixelWidth;
    var height = source.PixelHeight;
    var stride = width * ((source.Format.BitsPerPixel + 7) / 8);
    var ptr = IntPtr.Zero;
    try
    {
        ptr = Marshal.AllocHGlobal(height * stride);
        source.CopyPixels(new Int32Rect(0, 0, width, height), ptr, height * stride, stride);
        using (var bitmap = new Bitmap(width, height, stride, 
        PixelFormat.Format32bppPArgb, ptr))
        {
            return new Bitmap(bitmap);
        }
    }
    finally
    {
        if (ptr != IntPtr.Zero)
            Marshal.FreeHGlobal(ptr);
    }
}

Метод растрового изображения (Windows) в BitmapSource(WPF):

public BitmapSource BitmapToBitmapSource(Bitmap source)
{
    var bitmapData = source.LockBits( 
        new Rectangle( 0, 0,source.Width, source.Height ),
        ImageLockMode.ReadOnly,
        source.PixelFormat );

    //Names might be a bit confusing but I didn't have time to refactor
    var bitmapSource = BitmapSource.Create( 
        bitmapData.Width,
        bitmapData.Height,
        source.HorizontalResolution,
        source.VerticalResolution,
        PixelFormats.Pbgra32,
        null,
        bitmapData.Scan0,
        bitmapData.Stride * bitmapData.Height,
        bitmapData.Stride );

    source.UnlockBits(bitmapData);
    return bitmapSource;
}

XAML моего Image Control:

<Border Name="CurrentImageGridBorder" Grid.Column="0" Grid.Row="2" Margin="10" BorderThickness="1" Width="Auto" Height="Auto">
    <Border.BorderBrush>
        <SolidColorBrush Color="{DynamicResource BorderColor}"/>
    </Border.BorderBrush>
    <Grid x:Name="CurrentImageGrid" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Image x:Name="CurrentImage" 
               RenderOptions.BitmapScalingMode="HighQuality"
               UseLayoutRounding="True"
               SnapsToDevicePixels="True"/>
    </Grid>
</Border>

WPF Control, который запускает мой метод GetImageSource

<Button x:Name="OpenButton"
        Content="Open" 
        Style="{DynamicResource {x:Type Button}}"
        HorizontalAlignment="Left" Margin="20" VerticalAlignment="Top" Width="75" Height="20" 
        Click="GetImageSource"/>

Метод GetImageSource:

private void GetImageSource(object sender, RoutedEventArgs e)
{ 
    var openFileDialog = new OpenFileDialog
    {
        Title = "Select an Image",
        Filter = "Image Files (*.jpg;*.jpeg;*.png;*.bmp)|*.jpg;*.jpeg;*.png;*.bmp|" +
                 "JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
                 "Portable Network Graphic|*.png",
        ValidateNames = true,
        Multiselect = false
    };

    if (openFileDialog.ShowDialog() != true) return;
    CurrentImage.Source = new BitmapImage(new Uri(openFileDialog.FileName));
    CurrentImage.Stretch = Stretch.None;
    if (!(CurrentImage.Source.Width > CurrentImageGridBorder.ActualWidth) &&
        !(CurrentImage.Source.Height > CurrentImageGridBorder.ActualHeight)) return;
    CurrentImage.StretchDirection = StretchDirection.Both;
    CurrentImage.Stretch = Stretch.Uniform;

}

1 ответ

РЕДАКТИРОВАТЬ: поскольку вы имеете дело только с растровыми изображениями (которые загружаются из файлов или других потоков, или создаются из сырых пиксельных массивов), вам не нужно RenderTargetBitmap совсем. Source Свойство ваших элементов Image всегда содержит ImageSource, который уже BitmapSource,

Следовательно, вы можете безопасно всегда использовать ImageSource в BitmapSource:

public System.Drawing.Bitmap ImageToBitmap(Image image)
{
    return BitmapSourceToBitmap((BitmapSource) image.Source));
}

Используя RenderTargetBitmap только по необходимости, когда изображение Source это уже не BitmapSource (например, когда это DrawingImage). Тогда все равно нет необходимости делать Image элемент, ни для кодирования и декодирования RenderTargetBitmap в / из MemoryStream,

Просто используйте DrawingVisual и напрямую использовать RenderTargetBitmap как BitmapSource:

public BitmapSource ImageSourceToBitmapSource(ImageSource imageSource)
{
    var bitmapSource = imageSource as BitmapSource;

    if (bitmapSource == null)
    {
        // This part is only necessary if an ImageSource is not a BitmapSource,
        // which may be the case when it is a DrawingImage or a D3DImage.
        // ImageSource instances loaded from files or streams are always BitmapSources.
        //
        var rect = new Rect(0, 0, imageSource.Width, imageSource.Height);

        var renderTargetBitmap = new RenderTargetBitmap(
            (int)Math.Ceiling(rect.Width),
            (int)Math.Ceiling(rect.Height),
            96d, 96d, PixelFormats.Pbgra32);

        var drawingVisual = new DrawingVisual();

        using (var drawingContext = drawingVisual.RenderOpen())
        {
            drawingContext.DrawImage(imageSource, rect);
        }

        renderTargetBitmap.Render(drawingVisual);
        bitmapSource = renderTargetBitmap;
    }

    return bitmapSource;
}

public System.Drawing.Bitmap ImageSourceToBitmap(ImageSource imageSource)
{
    return BitmapSourceToBitmap(ImageSourceToBitmapSource(imageSource));
}

public System.Drawing.Bitmap ImageToBitmap(Image image)
{
    return ImageSourceToBitmap(image.Source));
}
Другие вопросы по тегам