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

Skia库无法实现Alpha蒙版?求无位图操作的解决方案

Skia实现复杂图形Alpha蒙版的无位图方案

我想用Skia实现两个复杂图形的Alpha蒙版效果:

  • 图形1:包含多次canvas保存与恢复的复杂绘制操作(示例为文本“Skia”)
  • 图形2:蒙版层,同样包含多次canvas保存与恢复的复杂绘制操作(示例为矩形区域)

期望结果:仅绘制两个图形的交集区域,且仅保留图形1的像素。目前我只能通过位图操作实现,请问有没有无需画布快照的有效解决方案?

示例代码

void draw(SkCanvas *canvas)
{
    SkFont font1;
    font1.setSize(100);
    font1.setEdging(SkFont::Edging::kAntiAlias);

    // 清空画布为透明
    canvas->clear(SK_ColorTRANSPARENT);
    sk_sp<SkTextBlob> blob1 = SkTextBlob::MakeFromString("Skia", font1);

    SkPaint paint1;
    paint1.setAntiAlias(true);
    paint1.setColor(SkColorSetARGB(0xFF, 0x42, 0x85, 0xF4));

    // 绘制文本(图形1)
    canvas->drawTextBlob(blob1.get(), 0, 100, paint1);

    SkPaint p;
    p.setColor(SK_ColorRED);
    p.setAntiAlias(true);
    canvas->saveLayer(nullptr, nullptr);
    // 绘制画布边界矩形(400x400)
    canvas->drawRect(SkRect::MakeXYWH(0, 0, 400, 400), p);
    p.setBlendMode(SkBlendMode::kClear);

    // 蒙版的复杂绘制操作(包含多次save/restore和绘制命令)
    canvas->drawRect(SkRect::MakeXYWH(0, 0, 400, 50), p);
    canvas->restore();
}

示例效果

400x400画布的蒙版效果

解决方案

无需位图快照,完全可以通过Skia的图层混合模式和离屏渲染实现,以下是两种可行方案:

方案一:使用带蒙版的saveLayer

核心逻辑是先绘制蒙版到离屏图层,再将该图层作为蒙版应用到图形1的绘制流程:

void drawWithMaskLayer(SkCanvas* canvas) {
    canvas->clear(SK_ColorTRANSPARENT);

    // 1. 创建蒙版图层(执行复杂绘制操作)
    SkPaint maskPaint;
    maskPaint.setAntiAlias(true);
    maskPaint.setBlendMode(SkBlendMode::kSrc);

    sk_sp<SkSurface> maskSurface = SkSurfaces::MakeRasterN32Premul(400, 400);
    SkCanvas* maskCanvas = maskSurface->getCanvas();
    maskCanvas->clear(SK_ColorTRANSPARENT);

    // 蒙版的复杂绘制(示例:保留下方350px区域,清除顶部50px)
    maskCanvas->drawRect(SkRect::MakeXYWH(0, 0, 400, 400), maskPaint);
    maskPaint.setBlendMode(SkBlendMode::kClear);
    maskCanvas->drawRect(SkRect::MakeXYWH(0, 0, 400, 50), maskPaint);

    sk_sp<SkImage> maskImage = maskSurface->makeImageSnapshot();
    SkPaint maskLayerPaint;
    maskLayerPaint.setBlendMode(SkBlendMode::kDstIn); // 仅保留蒙版区域内容

    // 2. 绘制图形1并应用蒙版
    canvas->saveLayer(nullptr, &maskLayerPaint);
    {
        // 图形1的复杂绘制(示例:文本Skia)
        SkFont font1;
        font1.setSize(100);
        font1.setEdging(SkFont::Edging::kAntiAlias);
        sk_sp<SkTextBlob> blob1 = SkTextBlob::MakeFromString("Skia", font1);

        SkPaint paint1;
        paint1.setAntiAlias(true);
        paint1.setColor(SkColorSetARGB(0xFF, 0x42, 0x85, 0xF4));
        canvas->drawTextBlob(blob1.get(), 0, 100, paint1);
    }
    canvas->drawImage(maskImage.get(), 0, 0, &maskLayerPaint);
    canvas->restore();
}

方案二:使用SkBlendMode::kIn分层绘制

利用kIn混合模式的特性:仅保留源(图形1)与目标(蒙版)的交集区域,且仅显示源的像素:

void drawWithBlendMode(SkCanvas* canvas) {
    canvas->clear(SK_ColorTRANSPARENT);

    // 1. 先绘制蒙版到画布(作为目标层)
    SkPaint maskPaint;
    maskPaint.setAntiAlias(true);
    maskPaint.setBlendMode(SkBlendMode::kSrc);

    canvas->saveLayer(nullptr, nullptr);
    {
        // 蒙版的复杂绘制
        canvas->drawRect(SkRect::MakeXYWH(0, 0, 400, 400), maskPaint);
        maskPaint.setBlendMode(SkBlendMode::kClear);
        canvas->drawRect(SkRect::MakeXYWH(0, 0, 400, 50), maskPaint);
    }
    canvas->restore();

    // 2. 绘制图形1,应用kIn混合模式
    SkPaint contentPaint;
    contentPaint.setAntiAlias(true);
    contentPaint.setColor(SkColorSetARGB(0xFF, 0x42, 0x85, 0xF4));
    contentPaint.setBlendMode(SkBlendMode::kIn); // 仅保留与蒙版的交集

    canvas->saveLayer(nullptr, &contentPaint);
    {
        // 图形1的复杂绘制
        SkFont font1;
        font1.setSize(100);
        font1.setEdging(SkFont::Edging::kAntiAlias);
        sk_sp<SkTextBlob> blob1 = SkTextBlob::MakeFromString("Skia", font1);
        canvas->drawTextBlob(blob1.get(), 0, 100, contentPaint);
    }
    canvas->restore();
}

关键说明

  • 两种方案均无需画布快照,完全基于Skia的矢量/图层渲染机制
  • 复杂绘制中的save/restore操作可直接保留在各自的绘制流程中,不影响蒙版逻辑
  • SkBlendMode::kIn是实现交集保留的核心,精准控制像素显示区域

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 00:57:43