thorvg 0.5.0

Safe Rust bindings to the ThorVG vector graphics library
Documentation
//! Demonstrates masking: alpha and luma mask methods.
//!
//! Run with: `cargo run --example masking`
//! Output:   `masking.png`

#![allow(clippy::cast_precision_loss)]

mod common;

use thorvg::{Circle, ColorSpace, ColorStop, EngineOption, MaskMethod, Paint, Rect, Rgba, Thorvg};

fn main() {
    let engine = Thorvg::init(0).unwrap();
    let (w, h) = (700u32, 350u32);
    let mut buffer = vec![0u32; (w * h) as usize];
    let mut canvas = engine.sw_canvas(EngineOption::Default).unwrap();
    unsafe { canvas.set_target(&mut buffer, w, w, h, ColorSpace::ABGR8888) }.unwrap();

    // Checkerboard background
    for row in 0..(h / 15) {
        for col in 0..(w / 15) {
            let mut sq = engine.shape().unwrap();
            sq.append_rect(Rect::new(col as f32 * 15.0, row as f32 * 15.0, 15.0, 15.0))
            .unwrap();
            let gray = if (row + col) % 2 == 0 { 180 } else { 210 };
            sq.set_fill_color(Rgba::new(gray, gray, gray, 255)).unwrap();
            canvas.add(sq).unwrap();
        }
    }

    // ── Alpha mask: gradient circle masks a rectangle ───────────────
    let mut rect1 = engine.shape().unwrap();
    rect1
        .append_rect(Rect::new(30.0, 50.0, 200.0, 200.0))
        .unwrap();
    rect1.set_fill_color(Rgba::new(50, 50, 220, 255)).unwrap();

    // Mask target: circle with gradient alpha
    let mut grad = engine.linear_gradient().unwrap();
    grad.set_bounds(30.0, 50.0, 230.0, 250.0).unwrap();
    grad.set_color_stops(&[
        ColorStop { offset: 0.0, color: Rgba::new(255, 255, 255, 255) },
        ColorStop { offset: 1.0, color: Rgba::new(0, 0, 0, 0) },
    ])
    .unwrap();

    let mut mask1 = engine.shape().unwrap();
    mask1
        .append_circle(Circle::new(130.0, 150.0, 90.0))
        .unwrap();
    mask1.set_linear_gradient(grad).unwrap();

    rect1.set_mask(mask1, MaskMethod::Alpha).unwrap();
    canvas.add(rect1).unwrap();

    // ── InvAlpha mask ──────────────────────────────────────────────
    let mut rect2 = engine.shape().unwrap();
    rect2
        .append_rect(Rect::new(260.0, 50.0, 200.0, 200.0))
        .unwrap();
    rect2.set_fill_color(Rgba::new(220, 50, 50, 255)).unwrap();

    let mut mask2 = engine.shape().unwrap();
    mask2
        .append_circle(Circle::new(360.0, 150.0, 60.0))
        .unwrap();
    mask2.set_fill_color(Rgba::new(255, 255, 255, 255)).unwrap();

    rect2.set_mask(mask2, MaskMethod::InvAlpha).unwrap();
    canvas.add(rect2).unwrap();

    // ── Luma mask ──────────────────────────────────────────────────
    let mut rect3 = engine.shape().unwrap();
    rect3
        .append_rect(Rect::new(490.0, 50.0, 180.0, 200.0).corner_radius(15.0))
        .unwrap();
    rect3.set_fill_color(Rgba::new(50, 200, 50, 255)).unwrap();

    let mut grad3 = engine.linear_gradient().unwrap();
    grad3.set_bounds(490.0, 50.0, 670.0, 250.0).unwrap();
    grad3
        .set_color_stops(&[
            ColorStop { offset: 0.0, color: Rgba::new(255, 255, 255, 255) },
            ColorStop { offset: 1.0, color: Rgba::new(0, 0, 0, 255) },
        ])
        .unwrap();

    let mut mask3 = engine.shape().unwrap();
    mask3
        .append_rect(Rect::new(490.0, 50.0, 180.0, 200.0))
        .unwrap();
    mask3.set_linear_gradient(grad3).unwrap();

    rect3.set_mask(mask3, MaskMethod::Luma).unwrap();
    canvas.add(rect3).unwrap();

    canvas.draw(true).unwrap();
    canvas.sync().unwrap();
    common::save_png("masking.png", &buffer, w, h);
}