1use std::f64::consts::PI;
12use std::sync::Arc;
13
14use agg_rust::arc::Arc as AggArc;
15use agg_rust::basics::FillingRule;
16use agg_rust::basics::VertexSource;
17use agg_rust::basics::PATH_FLAGS_NONE;
18use agg_rust::bounding_rect::bounding_rect_single;
19use agg_rust::comp_op::{CompOp, PixfmtRgba32CompOp};
20use agg_rust::conv_curve::ConvCurve;
21use agg_rust::conv_dash::ConvDash;
22use agg_rust::conv_stroke::ConvStroke;
23use agg_rust::conv_transform::ConvTransform;
24use agg_rust::gsv_text::GsvText;
25use agg_rust::math_stroke::{LineCap, LineJoin};
26use agg_rust::path_storage::PathStorage;
27use agg_rust::rasterizer_scanline_aa::RasterizerScanlineAa;
28use agg_rust::renderer_base::RendererBase;
29use agg_rust::renderer_scanline::render_scanlines_aa_solid;
30use agg_rust::rendering_buffer::RowAccessor;
31use agg_rust::rounded_rect::RoundedRect;
32use agg_rust::scanline_u::ScanlineU8;
33use agg_rust::trans_affine::TransAffine;
34
35use crate::color::Color;
36use crate::draw_ctx::{FillRule, LinearGradientPaint, PatternPaint, RadialGradientPaint};
37use crate::framebuffer::Framebuffer;
38use crate::text::{measure_advance, shape_text, Font, TextMetrics};
39
40struct LayerEntry {
46 fb: Framebuffer,
48 saved_state: GfxState,
51 saved_stack: Vec<GfxState>,
53 origin_x: f64,
55 origin_y: f64,
57 alpha: f64,
59 clip_mask: Option<Vec<u8>>,
65 clip_saved_path: Option<PathStorage>,
69}
70
71pub use agg_rust::comp_op::CompOp as BlendMode;
73
74#[derive(Clone)]
76struct GfxState {
77 transform: TransAffine,
78 fill_color: Color,
79 fill_linear_gradient: Option<LinearGradientPaint>,
80 fill_radial_gradient: Option<RadialGradientPaint>,
81 fill_pattern: Option<PatternPaint>,
82 stroke_color: Color,
83 stroke_linear_gradient: Option<LinearGradientPaint>,
84 stroke_radial_gradient: Option<RadialGradientPaint>,
85 stroke_pattern: Option<PatternPaint>,
86 fill_rule: FillRule,
87 line_width: f64,
88 line_join: LineJoin,
89 line_cap: LineCap,
90 miter_limit: f64,
91 line_dash: Vec<f64>,
92 dash_offset: f64,
93 blend_mode: CompOp,
94 clip: Option<(f64, f64, f64, f64)>,
95 global_alpha: f64,
96 font: Option<Arc<Font>>,
97 font_size: f64,
98}
99
100impl Default for GfxState {
101 fn default() -> Self {
102 Self {
103 transform: TransAffine::new(),
104 fill_color: Color::black(),
105 fill_linear_gradient: None,
106 fill_radial_gradient: None,
107 fill_pattern: None,
108 stroke_color: Color::black(),
109 stroke_linear_gradient: None,
110 stroke_radial_gradient: None,
111 stroke_pattern: None,
112 fill_rule: FillRule::NonZero,
113 line_width: 1.0,
114 line_join: LineJoin::Round,
115 line_cap: LineCap::Round,
116 miter_limit: 4.0,
117 line_dash: Vec::new(),
118 dash_offset: 0.0,
119 blend_mode: CompOp::SrcOver,
120 clip: None,
121 global_alpha: 1.0,
122 font: None,
123 font_size: 16.0,
124 }
125 }
126}
127
128pub struct GfxCtx<'a> {
141 base_fb: &'a mut Framebuffer,
142 layer_stack: Vec<LayerEntry>,
144 state: GfxState,
145 state_stack: Vec<GfxState>,
146 path: PathStorage,
148 lcd_mode: bool,
155}
156
157impl<'a> GfxCtx<'a> {
158 pub fn new(fb: &'a mut Framebuffer) -> Self {
160 Self {
161 base_fb: fb,
162 layer_stack: Vec::new(),
163 state: GfxState::default(),
164 state_stack: Vec::new(),
165 path: PathStorage::new(),
166 lcd_mode: false,
167 }
168 }
169
170 pub fn save(&mut self) {
175 self.state_stack.push(self.state.clone());
176 }
177
178 pub fn restore(&mut self) {
185 while self.state_stack.is_empty()
189 && self
190 .layer_stack
191 .last()
192 .is_some_and(|l| l.clip_mask.is_some())
193 {
194 self.pop_layer();
195 }
196 if let Some(state) = self.state_stack.pop() {
197 self.state = state;
198 }
199 }
200
201 pub fn translate(&mut self, tx: f64, ty: f64) {
207 self.state
208 .transform
209 .premultiply(&TransAffine::new_translation(tx, ty));
210 }
211
212 pub fn rotate(&mut self, radians: f64) {
214 self.state
215 .transform
216 .premultiply(&TransAffine::new_rotation(radians));
217 }
218
219 pub fn scale(&mut self, sx: f64, sy: f64) {
221 self.state
222 .transform
223 .premultiply(&TransAffine::new_scaling(sx, sy));
224 }
225
226 pub fn set_transform(&mut self, m: TransAffine) {
227 self.state.transform = m;
228 }
229 pub fn reset_transform(&mut self) {
230 self.state.transform = TransAffine::new();
231 }
232 pub fn transform(&self) -> TransAffine {
236 self.state.transform
237 }
238
239 pub fn set_fill_color(&mut self, color: Color) {
244 self.state.fill_color = color;
245 self.state.fill_linear_gradient = None;
246 self.state.fill_radial_gradient = None;
247 self.state.fill_pattern = None;
248 }
249 pub fn set_fill_linear_gradient(&mut self, gradient: LinearGradientPaint) {
250 self.state.fill_linear_gradient = Some(gradient);
251 self.state.fill_radial_gradient = None;
252 self.state.fill_pattern = None;
253 }
254 pub fn set_fill_radial_gradient(&mut self, gradient: RadialGradientPaint) {
255 self.state.fill_linear_gradient = None;
256 self.state.fill_radial_gradient = Some(gradient);
257 self.state.fill_pattern = None;
258 }
259 pub fn set_fill_pattern(&mut self, pattern: PatternPaint) {
260 self.state.fill_linear_gradient = None;
261 self.state.fill_radial_gradient = None;
262 self.state.fill_pattern = Some(pattern);
263 }
264 pub fn set_stroke_color(&mut self, color: Color) {
265 self.state.stroke_color = color;
266 self.state.stroke_linear_gradient = None;
267 self.state.stroke_radial_gradient = None;
268 self.state.stroke_pattern = None;
269 }
270 pub fn set_stroke_linear_gradient(&mut self, gradient: LinearGradientPaint) {
271 self.state.stroke_linear_gradient = Some(gradient);
272 self.state.stroke_radial_gradient = None;
273 self.state.stroke_pattern = None;
274 }
275 pub fn set_stroke_radial_gradient(&mut self, gradient: RadialGradientPaint) {
276 self.state.stroke_linear_gradient = None;
277 self.state.stroke_radial_gradient = Some(gradient);
278 self.state.stroke_pattern = None;
279 }
280 pub fn set_stroke_pattern(&mut self, pattern: PatternPaint) {
281 self.state.stroke_linear_gradient = None;
282 self.state.stroke_radial_gradient = None;
283 self.state.stroke_pattern = Some(pattern);
284 }
285 pub fn set_line_width(&mut self, w: f64) {
286 self.state.line_width = w;
287 }
288 pub fn set_line_join(&mut self, join: LineJoin) {
289 self.state.line_join = join;
290 }
291 pub fn set_line_cap(&mut self, cap: LineCap) {
292 self.state.line_cap = cap;
293 }
294 pub fn set_miter_limit(&mut self, limit: f64) {
295 self.state.miter_limit = limit.max(1.0);
296 }
297 pub fn set_line_dash(&mut self, dashes: &[f64], offset: f64) {
298 self.state.line_dash.clear();
299 self.state
300 .line_dash
301 .extend(dashes.iter().copied().filter(|v| *v > 0.0));
302 self.state.dash_offset = offset;
303 }
304 pub fn set_fill_rule(&mut self, rule: FillRule) {
305 self.state.fill_rule = rule;
306 }
307
308 pub fn set_blend_mode(&mut self, mode: CompOp) {
310 self.state.blend_mode = mode;
311 }
312
313 pub fn set_global_alpha(&mut self, alpha: f64) {
315 self.state.global_alpha = alpha.clamp(0.0, 1.0);
316 }
317
318 pub fn set_font(&mut self, font: Arc<Font>) {
324 self.state.font = Some(font);
325 }
326
327 pub fn set_font_size(&mut self, size: f64) {
329 self.state.font_size = size.max(1.0);
330 }
331
332 pub fn set_lcd_mode(&mut self, on: bool) {
338 self.lcd_mode = on;
339 }
340
341 pub fn lcd_mode(&self) -> bool {
343 self.lcd_mode
344 }
345
346 pub fn clip_rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
361 let t = &self.state.transform;
363 let corners = [(x, y), (x + w, y), (x + w, y + h), (x, y + h)];
364 let mut sx_min = f64::INFINITY;
365 let mut sy_min = f64::INFINITY;
366 let mut sx_max = f64::NEG_INFINITY;
367 let mut sy_max = f64::NEG_INFINITY;
368 for (lx, ly) in corners {
369 let mut sx = lx;
370 let mut sy = ly;
371 t.transform(&mut sx, &mut sy);
372 if sx < sx_min {
373 sx_min = sx;
374 }
375 if sx > sx_max {
376 sx_max = sx;
377 }
378 if sy < sy_min {
379 sy_min = sy;
380 }
381 if sy > sy_max {
382 sy_max = sy;
383 }
384 }
385 let sw = (sx_max - sx_min).max(0.0);
386 let sh = (sy_max - sy_min).max(0.0);
387 if let Some((cx, cy, cw, ch)) = self.state.clip {
388 let x1 = sx_min.max(cx);
389 let y1 = sy_min.max(cy);
390 let x2 = sx_max.min(cx + cw);
391 let y2 = sy_max.min(cy + ch);
392 self.state.clip = Some((x1, y1, (x2 - x1).max(0.0), (y2 - y1).max(0.0)));
393 } else {
394 self.state.clip = Some((sx_min, sy_min, sw, sh));
395 }
396 }
397
398 pub fn reset_clip(&mut self) {
399 self.state.clip = None;
400 }
401
402 pub fn clear(&mut self, color: Color) {
408 let rgba = color.to_rgba8();
409 for chunk in active_fb(&mut self.base_fb, &mut self.layer_stack)
410 .pixels_mut()
411 .chunks_exact_mut(4)
412 {
413 chunk[0] = rgba.r as u8;
414 chunk[1] = rgba.g as u8;
415 chunk[2] = rgba.b as u8;
416 chunk[3] = rgba.a as u8;
417 }
418 }
419
420 pub fn begin_path(&mut self) {
425 self.path = PathStorage::new();
426 }
427
428 pub fn move_to(&mut self, x: f64, y: f64) {
429 self.path.move_to(x, y);
430 }
431 pub fn line_to(&mut self, x: f64, y: f64) {
432 self.path.line_to(x, y);
433 }
434
435 pub fn cubic_to(&mut self, cx1: f64, cy1: f64, cx2: f64, cy2: f64, x: f64, y: f64) {
436 self.path.curve4(cx1, cy1, cx2, cy2, x, y);
437 }
438
439 pub fn quad_to(&mut self, cx: f64, cy: f64, x: f64, y: f64) {
440 self.path.curve3(cx, cy, x, y);
441 }
442
443 pub fn arc_to(
444 &mut self,
445 cx: f64,
446 cy: f64,
447 r: f64,
448 start_angle: f64,
449 end_angle: f64,
450 ccw: bool,
451 ) {
452 let mut arc = AggArc::new(cx, cy, r, r, start_angle, end_angle, ccw);
453 self.path.concat_path(&mut arc, 0);
454 }
455
456 pub fn circle(&mut self, cx: f64, cy: f64, r: f64) {
458 self.arc_to(cx, cy, r, 0.0, 2.0 * PI, true);
459 self.path.close_polygon(PATH_FLAGS_NONE);
460 }
461
462 pub fn rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
464 self.path.move_to(x, y);
465 self.path.line_to(x + w, y);
466 self.path.line_to(x + w, y + h);
467 self.path.line_to(x, y + h);
468 self.path.close_polygon(PATH_FLAGS_NONE);
469 }
470
471 pub fn rounded_rect(&mut self, x: f64, y: f64, w: f64, h: f64, r: f64) {
473 let r = r.min(w * 0.5).min(h * 0.5).max(0.0);
474 let mut rr = RoundedRect::new(x, y, x + w, y + h, r);
475 rr.normalize_radius();
476 self.path.concat_path(&mut rr, 0);
477 }
478
479 pub fn close_path(&mut self) {
480 self.path.close_polygon(PATH_FLAGS_NONE);
481 }
482
483 pub fn fill(&mut self) {
489 let mode = self.state.blend_mode;
490 let clip = self.state.clip;
491 let fill_rule = self.state.fill_rule;
492 let transform = self.state.transform.clone();
493 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
494 if let Some(gradient) = self.state.fill_linear_gradient.clone() {
495 sampled::rasterize_linear_gradient_fill(
496 fb,
497 &mut self.path,
498 &gradient,
499 self.state.global_alpha as f32,
500 mode,
501 clip,
502 fill_rule,
503 &transform,
504 );
505 } else if let Some(gradient) = self.state.fill_radial_gradient.clone() {
506 sampled::rasterize_radial_gradient_fill(
507 fb,
508 &mut self.path,
509 &gradient,
510 self.state.global_alpha as f32,
511 mode,
512 clip,
513 fill_rule,
514 &transform,
515 );
516 } else if let Some(pattern) = self.state.fill_pattern.clone() {
517 sampled::rasterize_pattern_fill(
518 fb,
519 &mut self.path,
520 &pattern,
521 self.state.global_alpha as f32,
522 mode,
523 clip,
524 fill_rule,
525 &transform,
526 );
527 } else {
528 let mut color = self.state.fill_color;
529 color.a *= self.state.global_alpha as f32;
530 let rgba = color.to_rgba8();
531 rasterize_fill(fb, &mut self.path, &rgba, mode, clip, fill_rule, &transform);
532 }
533 }
534
535 pub fn stroke(&mut self) {
537 let mut color = self.state.stroke_color;
538 color.a *= self.state.global_alpha as f32;
539 let rgba = color.to_rgba8();
540 let width = self.state.line_width;
541 let join = self.state.line_join;
542 let cap = self.state.line_cap;
543 let miter_limit = self.state.miter_limit;
544 let dashes = self.state.line_dash.clone();
545 let dash_offset = self.state.dash_offset;
546 let mode = self.state.blend_mode;
547 let clip = self.state.clip;
548 let transform = self.state.transform.clone();
549 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
550 if let Some(gradient) = self.state.stroke_linear_gradient.clone() {
551 let mut outline = stroke::materialize_stroke_outline(
552 &mut self.path,
553 width,
554 join,
555 cap,
556 miter_limit,
557 &dashes,
558 dash_offset,
559 );
560 sampled::rasterize_linear_gradient_fill(
561 fb,
562 &mut outline,
563 &gradient,
564 self.state.global_alpha as f32,
565 mode,
566 clip,
567 FillRule::NonZero,
568 &transform,
569 );
570 } else if let Some(gradient) = self.state.stroke_radial_gradient.clone() {
571 let mut outline = stroke::materialize_stroke_outline(
572 &mut self.path,
573 width,
574 join,
575 cap,
576 miter_limit,
577 &dashes,
578 dash_offset,
579 );
580 sampled::rasterize_radial_gradient_fill(
581 fb,
582 &mut outline,
583 &gradient,
584 self.state.global_alpha as f32,
585 mode,
586 clip,
587 FillRule::NonZero,
588 &transform,
589 );
590 } else if let Some(pattern) = self.state.stroke_pattern.clone() {
591 let mut outline = stroke::materialize_stroke_outline(
592 &mut self.path,
593 width,
594 join,
595 cap,
596 miter_limit,
597 &dashes,
598 dash_offset,
599 );
600 sampled::rasterize_pattern_fill(
601 fb,
602 &mut outline,
603 &pattern,
604 self.state.global_alpha as f32,
605 mode,
606 clip,
607 FillRule::NonZero,
608 &transform,
609 );
610 } else {
611 rasterize_stroke(
612 fb,
613 &mut self.path,
614 &rgba,
615 width,
616 join,
617 cap,
618 miter_limit,
619 &dashes,
620 dash_offset,
621 mode,
622 clip,
623 &transform,
624 );
625 }
626 }
627
628 pub fn fill_and_stroke(&mut self) {
630 self.fill();
631 self.stroke();
632 }
633
634 pub fn fill_text(&mut self, text: &str, x: f64, y: f64) {
647 let font = match self.state.font.clone() {
648 Some(f) => f,
649 None => return,
650 };
651 let font_size = self.state.font_size;
652
653 let mut color = self.state.fill_color;
654 color.a *= self.state.global_alpha as f32;
655
656 if self.lcd_mode {
682 let t = &self.state.transform;
683 let ctm_scale = (t.sx * t.sx + t.shy * t.shy).sqrt().max(1e-6);
684 let phys_size = font_size * ctm_scale;
685 let cached = crate::lcd_coverage::rasterize_text_lcd_cached(&font, text, phys_size);
686 let dst_x = x - cached.baseline_x_in_mask / ctm_scale;
690 let dst_y = y - cached.baseline_y_in_mask / ctm_scale;
691 <Self as crate::DrawCtx>::draw_lcd_mask_arc(
692 self,
693 &cached.pixels,
694 cached.width,
695 cached.height,
696 color,
697 dst_x,
698 dst_y,
699 );
700 return;
701 }
702
703 let rgba = color.to_rgba8();
704 let mode = self.state.blend_mode;
705 let clip = self.state.clip;
706 let transform = self.state.transform.clone();
707
708 let (glyph_paths, _) = shape_text(&font, text, font_size, x, y);
710 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
711 for mut path in glyph_paths {
712 rasterize_fill(
713 fb,
714 &mut path,
715 &rgba,
716 mode,
717 clip,
718 FillRule::NonZero,
719 &transform,
720 );
721 }
722 }
723
724 pub fn measure_text(&self, text: &str) -> Option<TextMetrics> {
728 let font = self.state.font.as_ref()?;
729 let size = self.state.font_size;
730 Some(TextMetrics {
731 width: measure_advance(font, text, size),
732 ascent: font.ascender_px(size),
733 descent: font.descender_px(size),
734 line_height: font.line_height_px(size),
735 })
736 }
737
738 pub fn fill_text_gsv(&mut self, text: &str, x: f64, y: f64, size: f64) {
746 let mut color = self.state.fill_color;
747 color.a *= self.state.global_alpha as f32;
748 let rgba = color.to_rgba8();
749 let mode = self.state.blend_mode;
750 let clip = self.state.clip;
751 let transform = self.state.transform.clone();
752
753 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
754 let w = fb.width();
755 let h = fb.height();
756 let stride = (w * 4) as i32;
757 let mut ra = RowAccessor::new();
758 unsafe { ra.attach(fb.pixels_mut().as_mut_ptr(), w, h, stride) };
759 let pf = PixfmtRgba32CompOp::new_with_op(&mut ra, mode);
760 let mut rb = RendererBase::new(pf);
761 apply_clip(&mut rb, clip);
762
763 let mut ras = RasterizerScanlineAa::new();
764 let mut sl = ScanlineU8::new();
765
766 let mut gsv = GsvText::new();
767 gsv.size(size, 0.0);
768 gsv.start_point(x, y);
769 gsv.text(text);
770
771 let mut stroke = ConvStroke::new(&mut gsv);
772 stroke.set_width(size * 0.1);
773 let mut transformed = ConvTransform::new(&mut stroke, transform);
774 ras.add_path(&mut transformed, 0);
775 render_scanlines_aa_solid(&mut ras, &mut sl, &mut rb, &rgba);
776 }
777}
778
779mod draw_impl;
780mod layers;
781mod sampled;
782mod stroke;
783
784use draw_impl::{active_fb, composite_framebuffers};
785pub(crate) use draw_impl::{apply_clip, rasterize_fill, rasterize_stroke};