You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SkiaSharp绘制Bitmap仅显示空白的问题求助

问题

我想从SKBitmap中裁剪一部分内容绘制到新Bitmap中,再转成byte[]发送给后端。参考了微软SkiaSharp教程,但不需要显示Canvas,只需要获取新Bitmap。直接转换原图片为byte[]能正常得到可上传的图片,但用Canvas绘制后得到的是空白内容,就算复制原Bitmap再绘制也还是空白。相关代码如下:

C#逻辑代码

public async void UploadImage()
    {
        FileResult im = await FilePicker.PickAsync(new PickOptions()
        {
            FileTypes = FilePickerFileType.Images,
        });
        byte[] bytes = null;
        using (Stream ms = await im.OpenReadAsync())
        {
            bytes = new byte[ms.Length];
            ms.Read(bytes, 0, bytes.Length);
        }

        SKBitmap sourceMap = null;
        using (MemoryStream stream = new MemoryStream(bytes))
        {
            sourceMap = SKBitmap.Decode(stream);
        }
        //SKBitmap destination = new SKBitmap(sourceMap.Width,sourceMap.Height);
        SKBitmap destination = sourceMap.Copy();
        SKRect dest = new SKRect(sourceMap.Height / 4, sourceMap.Height / 4, sourceMap.Width / 4, -sourceMap.Height / 4);
        SKRect source = new SKRect(sourceMap.Height/4,sourceMap.Height/4,sourceMap.Width/4,-sourceMap.Height/4);
        
        using (SKCanvas canvas = new SKCanvas(destination))
        {
            canvas.Clear();
            canvas.DrawBitmap(sourceMap, source, dest);
        }
        SKImage skImage = SKImage.FromBitmap(destination);
        SKData encoded = skImage.Encode();
        using(MemoryStream memory = new MemoryStream())
        {
            encoded.AsStream().CopyTo(memory);
            Image = memory.ToArray();
        }
    }

视图代码

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
         xmlns:ImageCroppingTry2="clr-namespace:ImageCroppingTry2"
         x:DataType="ImageCroppingTry2:MainPageViewModel"
         x:Class="ImageCroppingTry2.MainPage">

<ContentPage.Resources>
    <ResourceDictionary>
        <toolkit:ByteArrayToImageSourceConverter x:Key="ByteArrayToImageSourceConverter" />
    </ResourceDictionary>
</ContentPage.Resources>

<VerticalStackLayout>
    <Image x:Name="displayImage" Source="{Binding Image, Converter={StaticResource ByteArrayToImageSourceConverter}}"/>
    <Button Command="{Binding UploadImageCommand}"></Button>
</VerticalStackLayout>
解决方案

你的问题核心是SKRect参数定义错误,以及目标Bitmap的使用逻辑不合理,以下是具体修复方案:

问题分析

  1. SKRect构造顺序错误:SKRect的正确参数顺序是(left, top, right, bottom),要求right > left且bottom > top。你定义的source和dest矩形中,right小于left、bottom小于top,导致矩形无效,SkiaSharp会直接跳过绘制操作。
  2. 目标Bitmap尺寸不匹配:如果是要生成裁剪后的小图,目标Bitmap应该对应裁剪区域的尺寸;如果要在原尺寸画布上绘制,也必须保证矩形参数有效。

修正后的代码

C#逻辑代码

public async void UploadImage()
{
    FileResult im = await FilePicker.PickAsync(new PickOptions()
    {
        FileTypes = FilePickerFileType.Images,
    });
    byte[] bytes = null;
    using (Stream ms = await im.OpenReadAsync())
    {
        bytes = new byte[ms.Length];
        await ms.ReadAsync(bytes, 0, bytes.Length); // 改用异步读取更稳妥
    }

    SKBitmap sourceMap = null;
    using (MemoryStream stream = new MemoryStream(bytes))
    {
        sourceMap = SKBitmap.Decode(stream);
    }

    // 定义有效裁剪区域:从原图1/4位置开始,取1/2宽高的区域
    float cropLeft = sourceMap.Width / 4f;
    float cropTop = sourceMap.Height / 4f;
    float cropRight = sourceMap.Width * 3 / 4f;
    float cropBottom = sourceMap.Height * 3 / 4f;
    SKRect sourceRect = new SKRect(cropLeft, cropTop, cropRight, cropBottom);

    // 根据裁剪区域尺寸创建目标Bitmap
    int destWidth = (int)(cropRight - cropLeft);
    int destHeight = (int)(cropBottom - cropTop);
    SKBitmap destination = new SKBitmap(destWidth, destHeight);

    using (SKCanvas canvas = new SKCanvas(destination))
    {
        canvas.Clear(SKColors.Transparent); // 清空画布为透明(可按需改背景色)
        // 将裁剪区域绘制到目标Bitmap,填满整个新画布
        canvas.DrawBitmap(sourceMap, sourceRect, new SKRect(0, 0, destWidth, destHeight));
    }

    SKImage skImage = SKImage.FromBitmap(destination);
    SKData encoded = skImage.Encode(SKEncodedImageFormat.Png, 100); // 指定编码格式和质量
    using (MemoryStream memory = new MemoryStream())
    {
        encoded.AsStream().CopyTo(memory);
        Image = memory.ToArray();
    }

    // 手动释放Bitmap资源,避免内存泄漏
    sourceMap.Dispose();
    destination.Dispose();
}

关键修改说明

  • 修正SKRect参数,确保left < right、top < bottom,定义有效的裁剪区域。
  • 根据裁剪区域尺寸创建目标Bitmap,减少不必要的内存占用。
  • 改用异步流读取,符合MAUI异步编程规范。
  • 明确指定图片编码格式和质量,避免默认格式的不确定性。
  • 添加资源释放逻辑,防止内存泄漏。

视图代码(仅优化按钮文本)

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
         xmlns:ImageCroppingTry2="clr-namespace:ImageCroppingTry2"
         x:DataType="ImageCroppingTry2:MainPageViewModel"
         x:Class="ImageCroppingTry2.MainPage">

<ContentPage.Resources>
    <ResourceDictionary>
        <toolkit:ByteArrayToImageSourceConverter x:Key="ByteArrayToImageSourceConverter" />
    </ResourceDictionary>
</ContentPage.Resources>

<VerticalStackLayout>
    <Image x:Name="displayImage" Source="{Binding Image, Converter={StaticResource ByteArrayToImageSourceConverter}}"/>
    <Button Command="{Binding UploadImageCommand}" Text="选择并裁剪图片"></Button>
</VerticalStackLayout>

内容的提问来源于stack exchange,提问作者WoistdasNiveau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 20:07:06