use crate::api::blit;
use crate::api::gradient::Gradient;
use crate::api::image::Image;
use crate::api::matrix::Matrix2D;
use crate::api::path::{Path, Point};
use crate::api::pattern::Pattern;
use crate::api::stroke::{StrokeOptions, StrokeWorkspace, stroke_to_fill_with_workspace};
use crate::api::style::{CompOp, FillRule, Rgba32, StrokeCap, StrokeJoin};
use crate::font::Font;
use crate::pipeline::key::{FetchType, FillType};
use crate::pipeline::runtime::PipelineRuntime;
use crate::pixel::PixelFormat;
use crate::raster::analytic::AnalyticRasterizer;
use crate::raster::edge_builder::EdgeBuilder;
fn alpha_to_u8(a: f64) -> u8 {
if !a.is_finite() || a <= 0.0 {
0
} else if a >= 1.0 {
255
} else {
(a * 255.0 + 0.5) as u8
}
}
fn scale_prgb32(prgb32: u32, alpha: u8) -> u32 {
if alpha == 0xFF {
return prgb32;
}
if alpha == 0 {
return 0;
}
let a = alpha as u32;
let scale_chan = |c: u32| {
let v = c * a + 0x80;
(v + (v >> 8)) >> 8
};
let aa = scale_chan((prgb32 >> 24) & 0xFF);
let r = scale_chan((prgb32 >> 16) & 0xFF);
let g = scale_chan((prgb32 >> 8) & 0xFF);
let b = scale_chan(prgb32 & 0xFF);
(aa << 24) | (r << 16) | (g << 8) | b
}
#[inline]
fn scale_span_prgb32(span: &mut [u32], alpha: u8) {
if alpha == 0xFF || span.is_empty() {
return;
}
if alpha == 0 {
span.fill(0);
return;
}
#[cfg(target_arch = "aarch64")]
unsafe {
scale_span_prgb32_neon(span, alpha);
}
#[cfg(not(target_arch = "aarch64"))]
{
scale_span_prgb32_scalar(span, alpha);
}
}
#[allow(dead_code)]
#[inline]
fn scale_span_prgb32_scalar(span: &mut [u32], alpha: u8) {
for px in span.iter_mut() {
*px = scale_prgb32(*px, alpha);
}
}
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
unsafe fn scale_span_prgb32_neon(span: &mut [u32], alpha: u8) {
use std::arch::aarch64::*;
let a_u8 = vdup_n_u8(alpha);
let c80 = vdupq_n_u16(0x80);
let ptr = span.as_mut_ptr() as *mut u8;
let len = span.len();
let mut i = 0usize;
while i + 4 <= len {
unsafe {
let p = ptr.add(i * 4);
let v = vld1q_u8(p);
let lo = vget_low_u8(v);
let hi = vget_high_u8(v);
let lo16 = vmull_u8(lo, a_u8);
let hi16 = vmull_u8(hi, a_u8);
let lo16 = vaddq_u16(lo16, c80);
let lo16 = vaddq_u16(lo16, vshrq_n_u16(lo16, 8));
let lo_out = vshrn_n_u16(lo16, 8);
let hi16 = vaddq_u16(hi16, c80);
let hi16 = vaddq_u16(hi16, vshrq_n_u16(hi16, 8));
let hi_out = vshrn_n_u16(hi16, 8);
let out = vcombine_u8(lo_out, hi_out);
vst1q_u8(p, out);
}
i += 4;
}
for px in &mut span[i..] {
*px = scale_prgb32(*px, alpha);
}
}
#[derive(Debug, Clone, Copy)]
pub struct Rect {
pub x: f64,
pub y: f64,
pub w: f64,
pub h: f64,
}
impl Rect {
pub fn new(x: f64, y: f64, w: f64, h: f64) -> Self {
Self { x, y, w, h }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Circle {
pub cx: f64,
pub cy: f64,
pub r: f64,
}
impl Circle {
pub fn new(cx: f64, cy: f64, r: f64) -> Self {
Self { cx, cy, r }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Ellipse {
pub cx: f64,
pub cy: f64,
pub rx: f64,
pub ry: f64,
}
impl Ellipse {
pub fn new(cx: f64, cy: f64, rx: f64, ry: f64) -> Self {
Self { cx, cy, rx, ry }
}
}
#[derive(Debug, Clone, Copy)]
pub struct RoundRect {
pub x: f64,
pub y: f64,
pub w: f64,
pub h: f64,
pub rx: f64,
pub ry: f64,
}
impl RoundRect {
pub fn new(x: f64, y: f64, w: f64, h: f64, rx: f64, ry: f64) -> Self {
Self { x, y, w, h, rx, ry }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Triangle {
pub x0: f64,
pub y0: f64,
pub x1: f64,
pub y1: f64,
pub x2: f64,
pub y2: f64,
}
impl Triangle {
pub fn new(x0: f64, y0: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
Self {
x0,
y0,
x1,
y1,
x2,
y2,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Line {
pub x0: f64,
pub y0: f64,
pub x1: f64,
pub y1: f64,
}
impl Line {
pub fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> Self {
Self { x0, y0, x1, y1 }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Arc {
pub cx: f64,
pub cy: f64,
pub rx: f64,
pub ry: f64,
pub start: f64,
pub sweep: f64,
}
impl Arc {
pub fn new(cx: f64, cy: f64, rx: f64, ry: f64, start: f64, sweep: f64) -> Self {
Self {
cx,
cy,
rx,
ry,
start,
sweep,
}
}
}
#[derive(Debug, Clone, Copy)]
struct BoxI {
x0: i32,
y0: i32,
x1: i32,
y1: i32,
}
struct ContextState {
comp_op: CompOp,
fill_rule: FillRule,
fill_color_prgb32: u32,
fill_gradient: Option<Gradient>,
fill_pattern: Option<Pattern>,
stroke_color_prgb32: u32,
stroke_gradient: Option<Gradient>,
stroke_pattern: Option<Pattern>,
stroke_width: f64,
stroke_start_cap: StrokeCap,
stroke_end_cap: StrokeCap,
stroke_join: StrokeJoin,
stroke_miter_limit: f64,
stroke_dash_array: Vec<f64>,
stroke_dash_offset: f64,
matrix: Matrix2D,
global_alpha: f64,
fill_alpha: f64,
stroke_alpha: f64,
clip_box: BoxI,
}
pub struct Context<'a> {
image: &'a mut Image,
runtime: &'a mut PipelineRuntime,
comp_op: CompOp,
fill_rule: FillRule,
fill_color_prgb32: u32,
fill_gradient: Option<Gradient>,
fill_pattern: Option<Pattern>,
stroke_color_prgb32: u32,
stroke_gradient: Option<Gradient>,
stroke_pattern: Option<Pattern>,
stroke_width: f64,
stroke_start_cap: StrokeCap,
stroke_end_cap: StrokeCap,
stroke_join: StrokeJoin,
stroke_miter_limit: f64,
stroke_dash_array: Vec<f64>,
stroke_dash_offset: f64,
matrix: Matrix2D,
global_alpha: f64,
fill_alpha: f64,
stroke_alpha: f64,
meta_clip_box: BoxI,
clip_box: BoxI,
state_stack: Vec<ContextState>,
tmp_path: Path,
stroke_path_buf: Path,
stroke_workspace: StrokeWorkspace,
edge_buf: Vec<(f64, f64, f64, f64)>,
rasterizer: AnalyticRasterizer,
gradient_span_buf: Vec<u32>,
}
impl<'a> Context<'a> {
pub fn new(image: &'a mut Image, runtime: &'a mut PipelineRuntime) -> Self {
let meta_clip_box = BoxI {
x0: 0,
y0: 0,
x1: image.width() as i32,
y1: image.height() as i32,
};
Self {
image,
runtime,
comp_op: CompOp::SrcOver,
fill_rule: FillRule::default(),
fill_color_prgb32: 0,
fill_gradient: None,
fill_pattern: None,
stroke_color_prgb32: 0,
stroke_gradient: None,
stroke_pattern: None,
stroke_width: 1.0,
stroke_start_cap: StrokeCap::default(),
stroke_end_cap: StrokeCap::default(),
stroke_join: StrokeJoin::default(),
stroke_miter_limit: 4.0,
stroke_dash_array: Vec::new(),
stroke_dash_offset: 0.0,
matrix: Matrix2D::IDENTITY,
global_alpha: 1.0,
fill_alpha: 1.0,
stroke_alpha: 1.0,
meta_clip_box,
clip_box: meta_clip_box,
state_stack: Vec::new(),
tmp_path: Path::new(),
stroke_path_buf: Path::new(),
stroke_workspace: StrokeWorkspace::new(),
edge_buf: Vec::new(),
rasterizer: AnalyticRasterizer::new(),
gradient_span_buf: Vec::new(),
}
}
pub fn save(&mut self) {
self.state_stack.push(ContextState {
comp_op: self.comp_op,
fill_rule: self.fill_rule,
fill_color_prgb32: self.fill_color_prgb32,
fill_gradient: self.fill_gradient.clone(),
fill_pattern: self.fill_pattern.clone(),
stroke_color_prgb32: self.stroke_color_prgb32,
stroke_gradient: self.stroke_gradient.clone(),
stroke_pattern: self.stroke_pattern.clone(),
stroke_width: self.stroke_width,
stroke_start_cap: self.stroke_start_cap,
stroke_end_cap: self.stroke_end_cap,
stroke_join: self.stroke_join,
stroke_miter_limit: self.stroke_miter_limit,
stroke_dash_array: self.stroke_dash_array.clone(),
stroke_dash_offset: self.stroke_dash_offset,
matrix: self.matrix,
global_alpha: self.global_alpha,
fill_alpha: self.fill_alpha,
stroke_alpha: self.stroke_alpha,
clip_box: self.clip_box,
});
}
pub fn restore(&mut self) {
if let Some(state) = self.state_stack.pop() {
self.comp_op = state.comp_op;
self.fill_rule = state.fill_rule;
self.fill_color_prgb32 = state.fill_color_prgb32;
self.fill_gradient = state.fill_gradient;
self.fill_pattern = state.fill_pattern;
self.stroke_color_prgb32 = state.stroke_color_prgb32;
self.stroke_gradient = state.stroke_gradient;
self.stroke_pattern = state.stroke_pattern;
self.stroke_width = state.stroke_width;
self.stroke_start_cap = state.stroke_start_cap;
self.stroke_end_cap = state.stroke_end_cap;
self.stroke_join = state.stroke_join;
self.stroke_miter_limit = state.stroke_miter_limit;
self.stroke_dash_array = state.stroke_dash_array;
self.stroke_dash_offset = state.stroke_dash_offset;
self.matrix = state.matrix;
self.global_alpha = state.global_alpha;
self.fill_alpha = state.fill_alpha;
self.stroke_alpha = state.stroke_alpha;
self.clip_box = state.clip_box;
}
}
pub fn set_comp_op(&mut self, op: CompOp) {
self.comp_op = op;
}
pub fn comp_op(&self) -> CompOp {
self.comp_op
}
pub fn set_fill_rule(&mut self, rule: FillRule) {
self.fill_rule = rule;
}
pub fn fill_rule(&self) -> FillRule {
self.fill_rule
}
pub fn fill_color_prgb32(&self) -> u32 {
self.fill_color_prgb32
}
pub fn fill_gradient(&self) -> Option<&Gradient> {
self.fill_gradient.as_ref()
}
pub fn fill_pattern(&self) -> Option<&Pattern> {
self.fill_pattern.as_ref()
}
pub fn stroke_color_prgb32(&self) -> u32 {
self.stroke_color_prgb32
}
pub fn stroke_gradient(&self) -> Option<&Gradient> {
self.stroke_gradient.as_ref()
}
pub fn stroke_pattern(&self) -> Option<&Pattern> {
self.stroke_pattern.as_ref()
}
pub fn stroke_width(&self) -> f64 {
self.stroke_width
}
pub fn stroke_miter_limit(&self) -> f64 {
self.stroke_miter_limit
}
pub fn stroke_join(&self) -> StrokeJoin {
self.stroke_join
}
pub fn stroke_start_cap(&self) -> StrokeCap {
self.stroke_start_cap
}
pub fn stroke_end_cap(&self) -> StrokeCap {
self.stroke_end_cap
}
pub fn stroke_dash_array(&self) -> &[f64] {
&self.stroke_dash_array
}
pub fn stroke_dash_offset(&self) -> f64 {
self.stroke_dash_offset
}
pub fn matrix(&self) -> &Matrix2D {
&self.matrix
}
pub fn global_alpha(&self) -> f64 {
self.global_alpha
}
pub fn set_global_alpha(&mut self, a: f64) {
self.global_alpha = a.clamp(0.0, 1.0);
}
pub fn fill_alpha(&self) -> f64 {
self.fill_alpha
}
pub fn set_fill_alpha(&mut self, a: f64) {
self.fill_alpha = a.clamp(0.0, 1.0);
}
pub fn stroke_alpha(&self) -> f64 {
self.stroke_alpha
}
pub fn set_stroke_alpha(&mut self, a: f64) {
self.stroke_alpha = a.clamp(0.0, 1.0);
}
fn effective_fill_alpha_u8(&self) -> u8 {
alpha_to_u8(self.global_alpha * self.fill_alpha)
}
pub fn set_fill_style(&mut self, color: Rgba32) {
self.fill_color_prgb32 = color.to_prgb32();
self.fill_gradient = None;
self.fill_pattern = None;
}
pub fn set_fill_style_gradient(&mut self, gradient: &Gradient) {
self.fill_gradient = Some(gradient.clone());
self.fill_pattern = None;
}
pub fn set_fill_style_pattern(&mut self, pattern: &Pattern) {
self.fill_pattern = Some(pattern.clone());
self.fill_gradient = None;
}
pub fn set_stroke_style(&mut self, color: Rgba32) {
self.stroke_color_prgb32 = color.to_prgb32();
self.stroke_gradient = None;
self.stroke_pattern = None;
}
pub fn set_stroke_style_gradient(&mut self, gradient: &Gradient) {
self.stroke_gradient = Some(gradient.clone());
self.stroke_pattern = None;
}
pub fn set_stroke_style_pattern(&mut self, pattern: &Pattern) {
self.stroke_pattern = Some(pattern.clone());
self.stroke_gradient = None;
}
pub fn translate(&mut self, tx: f64, ty: f64) {
self.matrix.translate(tx, ty);
}
pub fn scale(&mut self, sx: f64, sy: f64) {
self.matrix.scale(sx, sy);
}
pub fn rotate(&mut self, angle: f64) {
self.matrix.rotate(angle);
}
pub fn apply_matrix(&mut self, m: &Matrix2D) {
self.matrix = self.matrix.multiply(m);
}
pub fn reset_matrix(&mut self) {
self.matrix.reset();
}
pub fn rotate_around(&mut self, angle: f64, cx: f64, cy: f64) {
self.matrix.rotate_around(angle, cx, cy);
}
pub fn skew(&mut self, kx: f64, ky: f64) {
self.matrix.skew(kx, ky);
}
pub fn post_translate(&mut self, tx: f64, ty: f64) {
self.matrix.post_translate(tx, ty);
}
pub fn post_scale(&mut self, sx: f64, sy: f64) {
self.matrix.post_scale(sx, sy);
}
pub fn post_rotate(&mut self, angle: f64) {
self.matrix.post_rotate(angle);
}
pub fn post_skew(&mut self, kx: f64, ky: f64) {
self.matrix.post_skew(kx, ky);
}
pub fn post_transform(&mut self, m: &Matrix2D) {
self.matrix.post_transform(m);
}
pub fn clip_to_rect(&mut self, rect: &Rect) {
let x0 = rect.x.floor() as i32;
let y0 = rect.y.floor() as i32;
let x1 = (rect.x + rect.w).ceil() as i32;
let y1 = (rect.y + rect.h).ceil() as i32;
self.clip_box.x0 = self.clip_box.x0.max(x0);
self.clip_box.y0 = self.clip_box.y0.max(y0);
self.clip_box.x1 = self.clip_box.x1.min(x1);
self.clip_box.y1 = self.clip_box.y1.min(y1);
}
pub fn restore_clipping(&mut self) {
self.clip_box = self.meta_clip_box;
}
pub fn user_to_meta(&mut self) {
self.matrix.reset();
}
pub fn clear_all(&mut self) {
let w = self.image.width() as f64;
let h = self.image.height() as f64;
self.clear_rect(&Rect::new(0.0, 0.0, w, h));
}
pub fn clear_rect(&mut self, rect: &Rect) {
let Some(boxi) = self.clip_rect(rect) else {
return;
};
let bpp = self.image.format().bytes_per_pixel();
let stride = self.image.stride();
let base = self.image.data_ptr_mut();
let row_bytes = (boxi.x1 - boxi.x0) as usize * bpp;
for y in boxi.y0..boxi.y1 {
let offset = y as usize * stride + boxi.x0 as usize * bpp;
unsafe {
std::ptr::write_bytes(base.add(offset), 0, row_bytes);
}
}
}
pub fn fill_rect(&mut self, rect: &Rect) {
let eff_alpha = self.effective_fill_alpha_u8();
if eff_alpha != 0xFF && (self.fill_gradient.is_some() || self.fill_pattern.is_some()) {
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.move_to(rect.x, rect.y);
path.line_to(rect.x + rect.w, rect.y);
path.line_to(rect.x + rect.w, rect.y + rect.h);
path.line_to(rect.x, rect.y + rect.h);
path.close();
self.fill_path(&path);
self.tmp_path = path;
return;
}
if !self.matrix.is_identity() {
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.move_to(rect.x, rect.y);
path.line_to(rect.x + rect.w, rect.y);
path.line_to(rect.x + rect.w, rect.y + rect.h);
path.line_to(rect.x, rect.y + rect.h);
path.close();
self.fill_path(&path);
self.tmp_path = path;
return;
}
if self.image.format() == PixelFormat::A8
&& (self.fill_pattern.is_some() || self.fill_gradient.is_some())
{
panic!("pattern and gradient fills are not supported for A8 destination");
}
let bpp = self.image.format().bytes_per_pixel();
if let Some(ref pattern) = self.fill_pattern {
let Some(boxi) = self.clip_rect(rect) else {
return;
};
let prepared = pattern.prepare(&self.matrix, self.comp_op);
let stride = self.image.stride();
let base = self.image.data_ptr_mut();
let width = (boxi.x1 - boxi.x0) as usize;
let offset = boxi.y0 as usize * stride + boxi.x0 as usize * bpp;
let dst = unsafe { base.add(offset) };
let height = (boxi.y1 - boxi.y0) as usize;
prepared.fill_rect(dst, stride, boxi.x0, boxi.y0, width, height);
return;
}
if let Some(ref gradient) = self.fill_gradient {
let Some(boxi) = self.clip_rect(rect) else {
return;
};
let prepared = gradient.prepare(&self.matrix);
let stride = self.image.stride();
let base = self.image.data_ptr_mut();
let width = (boxi.x1 - boxi.x0) as usize;
let offset = boxi.y0 as usize * stride + boxi.x0 as usize * bpp;
let dst = unsafe { base.add(offset) };
let height = (boxi.y1 - boxi.y0) as usize;
if matches!(
gradient.values(),
crate::api::gradient::GradientValues::Radial(_)
) && prepared.is_opaque()
{
let radial_fn = self.runtime.get_or_compile_radial_row();
prepared
.fill_rect_radial_jit(dst, stride, boxi.x0, boxi.y0, width, height, radial_fn);
return;
}
prepared.fill_rect(dst, stride, boxi.x0, boxi.y0, width, height);
return;
}
let prgb32 = scale_prgb32(self.fill_color_prgb32, eff_alpha);
let alpha = prgb32 >> 24;
if self.comp_op == CompOp::SrcOver && alpha == 0 {
return;
}
let Some(boxi) = self.clip_rect(rect) else {
return;
};
let effective_op = if self.comp_op == CompOp::SrcOver && alpha == 255 {
CompOp::SrcCopy
} else {
self.comp_op
};
let stride = self.image.stride();
let base = self.image.data_ptr_mut();
let width = (boxi.x1 - boxi.x0) as usize;
let height = (boxi.y1 - boxi.y0) as usize;
let offset = boxi.y0 as usize * stride + boxi.x0 as usize * bpp;
let dst = unsafe { base.add(offset) };
if let Some(box_fn) =
self.runtime
.get_or_compile_box(self.image.format(), effective_op, FetchType::Solid)
{
unsafe {
box_fn(dst, prgb32, width, height, stride);
}
return;
}
let pipeline_fn = self.runtime.get_or_compile(
self.image.format(),
effective_op,
FillType::BoxA,
FetchType::Solid,
);
for y in 0..height {
let dst_row = unsafe { dst.add(y * stride) };
unsafe {
pipeline_fn(dst_row, prgb32, width);
}
}
}
pub fn fill_path(&mut self, path: &Path) {
assert_ne!(
self.image.format(),
PixelFormat::A8,
"fill_path is not supported for A8 destination"
);
let bpp = self.image.format().bytes_per_pixel();
let eff_alpha = self.effective_fill_alpha_u8();
if self.comp_op == CompOp::SrcOver && eff_alpha == 0 {
return;
}
if self.fill_gradient.is_none() && self.fill_pattern.is_none() {
if self.comp_op == CompOp::SrcOver && (self.fill_color_prgb32 >> 24) == 0 {
return;
}
}
let points = path.points();
if points.is_empty() {
return;
}
let has_transform = !self.matrix.is_identity();
let mut min_x = f64::INFINITY;
let mut min_y = f64::INFINITY;
let mut max_x = f64::NEG_INFINITY;
let mut max_y = f64::NEG_INFINITY;
if has_transform {
let mut raw_min_x = f64::INFINITY;
let mut raw_min_y = f64::INFINITY;
let mut raw_max_x = f64::NEG_INFINITY;
let mut raw_max_y = f64::NEG_INFINITY;
for p in points {
raw_min_x = raw_min_x.min(p.x);
raw_min_y = raw_min_y.min(p.y);
raw_max_x = raw_max_x.max(p.x);
raw_max_y = raw_max_y.max(p.y);
}
let corners = [
self.matrix.map_point(raw_min_x, raw_min_y),
self.matrix.map_point(raw_max_x, raw_min_y),
self.matrix.map_point(raw_max_x, raw_max_y),
self.matrix.map_point(raw_min_x, raw_max_y),
];
for (cx, cy) in &corners {
min_x = min_x.min(*cx);
min_y = min_y.min(*cy);
max_x = max_x.max(*cx);
max_y = max_y.max(*cy);
}
} else {
for p in points {
min_x = min_x.min(p.x);
min_y = min_y.min(p.y);
max_x = max_x.max(p.x);
max_y = max_y.max(p.y);
}
}
let clip_x0 = (min_x.floor() as i32).max(self.clip_box.x0);
let clip_y0 = (min_y.floor() as i32).max(self.clip_box.y0);
let clip_x1 = (max_x.ceil() as i32 + 1).min(self.clip_box.x1);
let clip_y1 = (max_y.ceil() as i32 + 1).min(self.clip_box.y1);
if clip_x0 >= clip_x1 || clip_y0 >= clip_y1 {
return;
}
let mut edge_buf = std::mem::take(&mut self.edge_buf);
edge_buf.clear();
EdgeBuilder::flatten(path, |x0, y0, x1, y1| {
edge_buf.push((x0, y0, x1, y1));
});
if edge_buf.is_empty() {
self.edge_buf = edge_buf;
return;
}
if has_transform {
let transform_fn = self.runtime.get_or_compile_transform_edges();
let m = &self.matrix;
unsafe {
transform_fn(
edge_buf.as_mut_ptr().cast::<f64>(),
edge_buf.len(),
m.m00,
m.m01,
m.m10,
m.m11,
m.m20,
m.m21,
);
}
}
let sweep_fn = self.runtime.get_or_compile_sweep(self.fill_rule);
let stride = self.image.stride();
let base = self.image.data_ptr_mut();
let prepared_pattern = self
.fill_pattern
.as_ref()
.map(|p| p.prepare(&self.matrix, self.comp_op));
if let Some(ref pattern) = prepared_pattern {
let span_cov_fn = self.runtime.get_or_compile_span_cov(
self.image.format(),
self.comp_op,
FetchType::Solid,
);
let mut span_buf = std::mem::take(&mut self.gradient_span_buf);
self.rasterizer.rasterize(
&edge_buf,
clip_x0,
clip_y0,
clip_x1,
clip_y1,
sweep_fn,
|y, x_start, coverage| {
span_buf.resize(coverage.len(), 0);
pattern.fetch_span(x_start, y, &mut span_buf);
scale_span_prgb32(&mut span_buf, eff_alpha);
let offset = y as usize * stride + x_start as usize * bpp;
let dst_row = unsafe { base.add(offset) };
unsafe {
span_cov_fn(
dst_row,
span_buf.as_ptr(),
coverage.len(),
coverage.as_ptr(),
);
}
},
);
self.gradient_span_buf = span_buf;
self.edge_buf = edge_buf;
return;
}
let prepared_gradient = self.fill_gradient.as_ref().map(|g| g.prepare(&self.matrix));
if let Some(ref gradient) = prepared_gradient {
if eff_alpha == 0xFF && gradient.is_linear_pad() && gradient.is_opaque() {
let linear_cov_fn = self.runtime.get_or_compile_linear_gradient_cov();
gradient.fill_path_linear_jit(
&mut self.rasterizer,
&edge_buf,
clip_x0,
clip_y0,
clip_x1,
clip_y1,
sweep_fn,
stride,
base,
linear_cov_fn,
);
self.edge_buf = edge_buf;
return;
}
let span_cov_fn = self.runtime.get_or_compile_span_cov(
self.image.format(),
self.comp_op,
FetchType::Solid,
);
let mut span_buf = std::mem::take(&mut self.gradient_span_buf);
self.rasterizer.rasterize(
&edge_buf,
clip_x0,
clip_y0,
clip_x1,
clip_y1,
sweep_fn,
|y, x_start, coverage| {
span_buf.resize(coverage.len(), 0);
gradient.fetch_span_linear_fixed(x_start, y, &mut span_buf);
scale_span_prgb32(&mut span_buf, eff_alpha);
let offset = y as usize * stride + x_start as usize * bpp;
let dst_row = unsafe { base.add(offset) };
unsafe {
span_cov_fn(
dst_row,
span_buf.as_ptr(),
coverage.len(),
coverage.as_ptr(),
);
}
},
);
self.gradient_span_buf = span_buf;
} else {
let pipeline_cov_fn = self.runtime.get_or_compile_cov(
self.image.format(),
self.comp_op,
FetchType::Solid,
);
let prgb32 = scale_prgb32(self.fill_color_prgb32, eff_alpha);
self.rasterizer.rasterize(
&edge_buf,
clip_x0,
clip_y0,
clip_x1,
clip_y1,
sweep_fn,
|y, x_start, coverage| {
let offset = y as usize * stride + x_start as usize * bpp;
let dst_row = unsafe { base.add(offset) };
unsafe {
pipeline_cov_fn(dst_row, prgb32, coverage.len(), coverage.as_ptr());
}
},
);
}
self.edge_buf = edge_buf;
}
pub fn fill_all(&mut self) {
let w = self.image.width() as f64;
let h = self.image.height() as f64;
self.fill_rect(&Rect::new(0.0, 0.0, w, h));
}
pub fn fill_pie(&mut self, arc: &Arc) {
if arc.rx <= 0.0 || arc.ry <= 0.0 || arc.sweep == 0.0 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_pie(arc.cx, arc.cy, arc.rx, arc.ry, arc.start, arc.sweep);
self.fill_path(&path);
self.tmp_path = path;
}
pub fn fill_circle(&mut self, circle: &Circle) {
if circle.r <= 0.0 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_circle(circle.cx, circle.cy, circle.r);
self.fill_path(&path);
self.tmp_path = path;
}
pub fn fill_triangle(&mut self, t: &Triangle) {
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_triangle(t.x0, t.y0, t.x1, t.y1, t.x2, t.y2);
self.fill_path(&path);
self.tmp_path = path;
}
pub fn fill_polygon(&mut self, points: &[Point]) {
if points.len() < 3 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_polygon(points);
self.fill_path(&path);
self.tmp_path = path;
}
pub fn fill_round_rect(&mut self, rr: &RoundRect) {
if rr.w <= 0.0 || rr.h <= 0.0 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_round_rect(rr.x, rr.y, rr.w, rr.h, rr.rx, rr.ry);
self.fill_path(&path);
self.tmp_path = path;
}
pub fn fill_ellipse(&mut self, ellipse: &Ellipse) {
if ellipse.rx <= 0.0 || ellipse.ry <= 0.0 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_ellipse(ellipse.cx, ellipse.cy, ellipse.rx, ellipse.ry);
self.fill_path(&path);
self.tmp_path = path;
}
pub fn fill_text(&mut self, x: f64, y: f64, font: &Font, text: &str) {
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
let mut cursor_x = x;
let baseline_y = y;
for ch in text.chars() {
let glyph_id = font.map_char_to_glyph(ch);
if glyph_id == 0 {
cursor_x += font.glyph_advance(glyph_id);
continue;
}
let _ = font.append_glyph_outline(glyph_id, cursor_x, baseline_y, &mut path);
cursor_x += font.glyph_advance(glyph_id);
}
if !path.is_empty() {
self.fill_path(&path);
}
self.tmp_path = path;
}
pub fn set_stroke_width(&mut self, width: f64) {
self.stroke_width = width;
}
pub fn set_stroke_cap(&mut self, cap: StrokeCap) {
self.stroke_start_cap = cap;
self.stroke_end_cap = cap;
}
pub fn set_stroke_start_cap(&mut self, cap: StrokeCap) {
self.stroke_start_cap = cap;
}
pub fn set_stroke_end_cap(&mut self, cap: StrokeCap) {
self.stroke_end_cap = cap;
}
pub fn set_stroke_join(&mut self, join: StrokeJoin) {
self.stroke_join = join;
}
pub fn set_stroke_miter_limit(&mut self, limit: f64) {
self.stroke_miter_limit = limit;
}
pub fn set_stroke_dash_array(&mut self, dash_array: &[f64]) {
self.stroke_dash_array = dash_array.to_vec();
}
pub fn set_stroke_dash_offset(&mut self, offset: f64) {
self.stroke_dash_offset = offset;
}
pub fn stroke_path(&mut self, path: &Path) {
let mut stroke_buf = std::mem::take(&mut self.stroke_path_buf);
stroke_buf.clear();
let options = StrokeOptions {
width: self.stroke_width,
start_cap: self.stroke_start_cap,
end_cap: self.stroke_end_cap,
join: self.stroke_join,
miter_limit: self.stroke_miter_limit,
dash_array: self.stroke_dash_array.clone(),
dash_offset: self.stroke_dash_offset,
};
let mut workspace = std::mem::take(&mut self.stroke_workspace);
stroke_to_fill_with_workspace(path, &options, &mut stroke_buf, &mut workspace);
self.stroke_workspace = workspace;
let saved_fill_color = self.fill_color_prgb32;
let saved_fill_gradient = self.fill_gradient.take();
let saved_fill_pattern = self.fill_pattern.take();
let saved_fill_alpha = self.fill_alpha;
self.fill_color_prgb32 = self.stroke_color_prgb32;
self.fill_gradient = self.stroke_gradient.clone();
self.fill_pattern = self.stroke_pattern.clone();
self.fill_alpha = self.stroke_alpha;
self.fill_path(&stroke_buf);
self.fill_color_prgb32 = saved_fill_color;
self.fill_gradient = saved_fill_gradient;
self.fill_pattern = saved_fill_pattern;
self.fill_alpha = saved_fill_alpha;
self.stroke_path_buf = stroke_buf;
}
pub fn stroke_rect(&mut self, rect: &Rect) {
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.move_to(rect.x, rect.y);
path.line_to(rect.x + rect.w, rect.y);
path.line_to(rect.x + rect.w, rect.y + rect.h);
path.line_to(rect.x, rect.y + rect.h);
path.close();
self.stroke_path(&path);
self.tmp_path = path;
}
pub fn stroke_circle(&mut self, circle: &Circle) {
if circle.r <= 0.0 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_circle(circle.cx, circle.cy, circle.r);
self.stroke_path(&path);
self.tmp_path = path;
}
pub fn stroke_triangle(&mut self, t: &Triangle) {
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_triangle(t.x0, t.y0, t.x1, t.y1, t.x2, t.y2);
self.stroke_path(&path);
self.tmp_path = path;
}
pub fn stroke_polygon(&mut self, points: &[Point]) {
if points.len() < 3 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_polygon(points);
self.stroke_path(&path);
self.tmp_path = path;
}
pub fn stroke_polyline(&mut self, points: &[Point]) {
if points.len() < 2 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_polyline(points);
self.stroke_path(&path);
self.tmp_path = path;
}
pub fn stroke_round_rect(&mut self, rr: &RoundRect) {
if rr.w <= 0.0 || rr.h <= 0.0 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_round_rect(rr.x, rr.y, rr.w, rr.h, rr.rx, rr.ry);
self.stroke_path(&path);
self.tmp_path = path;
}
pub fn stroke_ellipse(&mut self, ellipse: &Ellipse) {
if ellipse.rx <= 0.0 || ellipse.ry <= 0.0 {
return;
}
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.add_ellipse(ellipse.cx, ellipse.cy, ellipse.rx, ellipse.ry);
self.stroke_path(&path);
self.tmp_path = path;
}
pub fn stroke_line(&mut self, line: &Line) {
let mut path = std::mem::take(&mut self.tmp_path);
path.clear();
path.move_to(line.x0, line.y0);
path.line_to(line.x1, line.y1);
self.stroke_path(&path);
self.tmp_path = path;
}
pub fn blit_image_rect(&mut self, dst: &Rect, src: &Image, src_rect: Option<Rect>) {
let corners = [
self.matrix.map_point(dst.x, dst.y),
self.matrix.map_point(dst.x + dst.w, dst.y),
self.matrix.map_point(dst.x + dst.w, dst.y + dst.h),
self.matrix.map_point(dst.x, dst.y + dst.h),
];
let mut min_x = f64::INFINITY;
let mut min_y = f64::INFINITY;
let mut max_x = f64::NEG_INFINITY;
let mut max_y = f64::NEG_INFINITY;
for (x, y) in corners {
min_x = min_x.min(x);
min_y = min_y.min(y);
max_x = max_x.max(x);
max_y = max_y.max(y);
}
let ix0 = (min_x.floor() as i32).max(self.clip_box.x0);
let iy0 = (min_y.floor() as i32).max(self.clip_box.y0);
let ix1 = (max_x.ceil() as i32).min(self.clip_box.x1);
let iy1 = (max_y.ceil() as i32).min(self.clip_box.y1);
if ix0 >= ix1 || iy0 >= iy1 {
return;
}
blit::blit_image_rect_scoped(
self.image,
dst,
src,
src_rect,
&self.matrix,
ix0,
iy0,
ix1,
iy1,
self.comp_op,
);
}
pub fn blit_image_at(&mut self, x: f64, y: f64, src: &Image) {
let r = Rect::new(x, y, src.width() as f64, src.height() as f64);
self.blit_image_rect(&r, src, None);
}
pub fn end(&mut self) {}
fn clip_rect(&self, rect: &Rect) -> Option<BoxI> {
let x0 = rect.x.floor() as i32;
let y0 = rect.y.floor() as i32;
let x1 = (rect.x + rect.w).ceil() as i32;
let y1 = (rect.y + rect.h).ceil() as i32;
let x0 = x0.max(self.clip_box.x0);
let y0 = y0.max(self.clip_box.y0);
let x1 = x1.min(self.clip_box.x1);
let y1 = y1.min(self.clip_box.y1);
if x0 >= x1 || y0 >= y1 {
return None;
}
Some(BoxI { x0, y0, x1, y1 })
}
}