如何调整一个SoftwareBitmap的大小?

4

SoftwareBitmap是UWP中的新功能。以下是简单介绍:

var softwareBitmap = EXTERNALVALUE;

// do I even need a writeable bitmap?
var writeableBitmap = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
softwareBitmap.CopyToBuffer(writeableBitmap.PixelBuffer);

// maybe use BitmapDecoder?

我感到困惑。谢谢。

请注意,我指的不是 BitmapImage,而是 SoftwareBitmap


那可以是任何语言。调整图像大小并不简单。这里有一些C语言算法:https://github.com/MalcolmMcLean/babyxrc/tree/master/src - Malcolm McLean
我过去使用过ImageMagick来完成这个任务。这是相当简单的 https://magick.codeplex.com/ - big_water
也许可以查看BitmapTransform的ScaledHeight和ScaledWidth方法?https://msdn.microsoft.com/library/windows/apps/br226254#methods 我不是很懂这个领域,只是猜测一下。 - JohnLBevan
请注意,我指的不是BitmapImage,而是SoftwareBitmap - Jerry Nixon
你有看过ScaleEffect吗?我想我在某个地方有一个样例,不过得去找找。 - Romasz
不行,如果你想在内存中调整图像大小,ScaleEffect 似乎无法帮助,但如果你想显示调整大小后的图像,它可能会起作用。保持图像原始大小,然后通过 CanvasControl 显示时进行缩放。 - Romasz
3个回答

6

我尝试使用ScaleEffect,并得出了以下扩展方法。实际上这个方法还需要更多的工作,但也许它能帮助你在某种程度上更进一步。

public static SoftwareBitmap Resize(this SoftwareBitmap softwareBitmap, float newWidth, float newHeight)
{
    using (var resourceCreator = CanvasDevice.GetSharedDevice())
    using (var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(resourceCreator, softwareBitmap))
    using (var canvasRenderTarget = new CanvasRenderTarget(resourceCreator, newWidth, newHeight, canvasBitmap.Dpi))
    using (var drawingSession = canvasRenderTarget.CreateDrawingSession())
    using (var scaleEffect = new ScaleEffect())
    {
        scaleEffect.Source = canvasBitmap;
        scaleEffect.Scale = new System.Numerics.Vector2(newWidth / softwareBitmap.PixelWidth, newHeight / softwareBitmap.PixelHeight);
        drawingSession.DrawImage(scaleEffect);
        drawingSession.Flush();
        return SoftwareBitmap.CreateCopyFromBuffer(canvasRenderTarget.GetPixelBytes().AsBuffer(), BitmapPixelFormat.Bgra8, (int)newWidth, (int)newHeight, BitmapAlphaMode.Premultiplied);
    }
}

3

看起来像是一个支架,但它可能会解决你的问题:

    private async Task<SoftwareBitmap> ResizeSoftwareBitmap(SoftwareBitmap softwareBitmap, double scaleFactor)
    {
        var resourceCreator = CanvasDevice.GetSharedDevice();
        var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(resourceCreator, softwareBitmap);
        var canvasRenderTarget = new CanvasRenderTarget(resourceCreator, (int)(softwareBitmap.PixelWidth * scaleFactor), (int)(softwareBitmap.PixelHeight * scaleFactor), 96);

        using (var cds = canvasRenderTarget.CreateDrawingSession())
        {
            cds.DrawImage(canvasBitmap, canvasRenderTarget.Bounds);
        }

        var pixelBytes = canvasRenderTarget.GetPixelBytes();

        var writeableBitmap = new WriteableBitmap((int)(softwareBitmap.PixelWidth * scaleFactor), (int)(softwareBitmap.PixelHeight * scaleFactor));
        using (var stream = writeableBitmap.PixelBuffer.AsStream())
        {
            await stream.WriteAsync(pixelBytes, 0, pixelBytes.Length);
        }

        var scaledSoftwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8, (int)(softwareBitmap.PixelWidth * scaleFactor), (int)(softwareBitmap.PixelHeight * scaleFactor));
        scaledSoftwareBitmap.CopyFromBuffer(writeableBitmap.PixelBuffer);

        return scaledSoftwareBitmap;
    }

您需要从Nuget获取Win2D包。


1

这里我将一个图像文件缩小为100x100的SoftwareBitmap,并将其作为ImageSource返回。

既然你已经拥有了一个SoftwareBitmap,我认为你的任务会更容易。但希望这能给你一些想法。

我们只需要WritableBitmapPixelBuffer来初始化我们新缩放的SoftwareBitmap实例。如果你可以直接从我们拥有的字节数组像素数据(本地变量pixels)创建一个IBuffer,则可以直接将其提供给SoftwareBitmap.CreateCopyFromBuffer()方法。在那种情况下,就不需要WritableBitmap了。

以下是代码:

private async Task<ImageSource> ProcessImageAsync(StorageFile ImageFile)
    {
        if (ImageFile == null)
            throw new ArgumentNullException("ImageFile cannot be null.");

        //The new size of processed image.
        const int side = 100;

        //Initialize bitmap transformations to be applied to the image.
        var transform = new BitmapTransform() { ScaledWidth = side, ScaledHeight = side, InterpolationMode = BitmapInterpolationMode.Cubic };

        //Get image pixels.
        var stream = await ImageFile.OpenStreamForReadAsync();
        var decoder = await BitmapDecoder.CreateAsync(stream.AsRandomAccessStream());
        var pixelData = await decoder.GetPixelDataAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
        var pixels = pixelData.DetachPixelData();

        //Initialize writable bitmap.
        var wBitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
        await wBitmap.SetSourceAsync(stream.AsRandomAccessStream());

        //Create a software bitmap from the writable bitmap's pixel buffer.
        var sBitmap = SoftwareBitmap.CreateCopyFromBuffer(wBitmap.PixelBuffer, BitmapPixelFormat.Bgra8, side, side, BitmapAlphaMode.Premultiplied);

        //Create software bitmap source.
        var sBitmapSource = new SoftwareBitmapSource();
        await sBitmapSource.SetBitmapAsync(sBitmap);

        return sBitmapSource;
    }

我知道这句话不应该成为答案的一部分,但我必须说,我从你的MVA和Channel9视频中学到了很多关于XAML/C#和开发Windows Store应用程序的知识! :)

这个贵吗? - Jerry Nixon
虽然我最初的做法也行,但后来我发现有更直截了当的方法。我又问了一个关于调整大小和灰度化 SoftwareBitmap 的问题。你可以看看这个(https://stackoverflow.com/a/45393902/4062881)回答,从性能上来说比我的好。 - Vijay Chavda
1
@JerryNixon-MSFT 你也可以使用Lumia Imaging SDK - https://msdn.microsoft.com/zh-cn/library/mt598502.aspx。它很棒! - Vijay Chavda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接