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::comp_op::{CompOp, PixfmtRgba32CompOp};
19use agg_rust::conv_curve::ConvCurve;
20use agg_rust::conv_dash::ConvDash;
21use agg_rust::conv_stroke::ConvStroke;
22use agg_rust::conv_transform::ConvTransform;
23use agg_rust::gsv_text::GsvText;
24use agg_rust::math_stroke::{LineCap, LineJoin};
25use agg_rust::path_storage::PathStorage;
26use agg_rust::rasterizer_scanline_aa::RasterizerScanlineAa;
27use agg_rust::renderer_base::RendererBase;
28use agg_rust::renderer_scanline::render_scanlines_aa_solid;
29use agg_rust::rendering_buffer::RowAccessor;
30use agg_rust::rounded_rect::RoundedRect;
31use agg_rust::scanline_u::ScanlineU8;
32use agg_rust::trans_affine::TransAffine;
33
34use crate::color::Color;
35use crate::draw_ctx::{FillRule, LinearGradientPaint, PatternPaint, RadialGradientPaint};
36use crate::framebuffer::Framebuffer;
37use crate::text::{measure_advance, shape_text, Font, TextMetrics};
38
39struct LayerEntry {
45 fb: Framebuffer,
47 saved_state: GfxState,
50 saved_stack: Vec<GfxState>,
52 origin_x: f64,
54 origin_y: f64,
56 alpha: f64,
58}
59
60pub use agg_rust::comp_op::CompOp as BlendMode;
62
63#[derive(Clone)]
65struct GfxState {
66 transform: TransAffine,
67 fill_color: Color,
68 fill_linear_gradient: Option<LinearGradientPaint>,
69 fill_radial_gradient: Option<RadialGradientPaint>,
70 fill_pattern: Option<PatternPaint>,
71 stroke_color: Color,
72 stroke_linear_gradient: Option<LinearGradientPaint>,
73 stroke_radial_gradient: Option<RadialGradientPaint>,
74 stroke_pattern: Option<PatternPaint>,
75 fill_rule: FillRule,
76 line_width: f64,
77 line_join: LineJoin,
78 line_cap: LineCap,
79 miter_limit: f64,
80 line_dash: Vec<f64>,
81 dash_offset: f64,
82 blend_mode: CompOp,
83 clip: Option<(f64, f64, f64, f64)>,
84 global_alpha: f64,
85 font: Option<Arc<Font>>,
86 font_size: f64,
87}
88
89impl Default for GfxState {
90 fn default() -> Self {
91 Self {
92 transform: TransAffine::new(),
93 fill_color: Color::black(),
94 fill_linear_gradient: None,
95 fill_radial_gradient: None,
96 fill_pattern: None,
97 stroke_color: Color::black(),
98 stroke_linear_gradient: None,
99 stroke_radial_gradient: None,
100 stroke_pattern: None,
101 fill_rule: FillRule::NonZero,
102 line_width: 1.0,
103 line_join: LineJoin::Round,
104 line_cap: LineCap::Round,
105 miter_limit: 4.0,
106 line_dash: Vec::new(),
107 dash_offset: 0.0,
108 blend_mode: CompOp::SrcOver,
109 clip: None,
110 global_alpha: 1.0,
111 font: None,
112 font_size: 16.0,
113 }
114 }
115}
116
117pub struct GfxCtx<'a> {
130 base_fb: &'a mut Framebuffer,
131 layer_stack: Vec<LayerEntry>,
133 state: GfxState,
134 state_stack: Vec<GfxState>,
135 path: PathStorage,
137 lcd_mode: bool,
144}
145
146impl<'a> GfxCtx<'a> {
147 pub fn new(fb: &'a mut Framebuffer) -> Self {
149 Self {
150 base_fb: fb,
151 layer_stack: Vec::new(),
152 state: GfxState::default(),
153 state_stack: Vec::new(),
154 path: PathStorage::new(),
155 lcd_mode: false,
156 }
157 }
158
159 pub fn save(&mut self) {
164 self.state_stack.push(self.state.clone());
165 }
166
167 pub fn restore(&mut self) {
168 if let Some(state) = self.state_stack.pop() {
169 self.state = state;
170 }
171 }
172
173 pub fn translate(&mut self, tx: f64, ty: f64) {
179 self.state
180 .transform
181 .premultiply(&TransAffine::new_translation(tx, ty));
182 }
183
184 pub fn rotate(&mut self, radians: f64) {
186 self.state
187 .transform
188 .premultiply(&TransAffine::new_rotation(radians));
189 }
190
191 pub fn scale(&mut self, sx: f64, sy: f64) {
193 self.state
194 .transform
195 .premultiply(&TransAffine::new_scaling(sx, sy));
196 }
197
198 pub fn set_transform(&mut self, m: TransAffine) {
199 self.state.transform = m;
200 }
201 pub fn reset_transform(&mut self) {
202 self.state.transform = TransAffine::new();
203 }
204 pub fn transform(&self) -> TransAffine {
208 self.state.transform
209 }
210
211 pub fn set_fill_color(&mut self, color: Color) {
216 self.state.fill_color = color;
217 self.state.fill_linear_gradient = None;
218 self.state.fill_radial_gradient = None;
219 self.state.fill_pattern = None;
220 }
221 pub fn set_fill_linear_gradient(&mut self, gradient: LinearGradientPaint) {
222 self.state.fill_linear_gradient = Some(gradient);
223 self.state.fill_radial_gradient = None;
224 self.state.fill_pattern = None;
225 }
226 pub fn set_fill_radial_gradient(&mut self, gradient: RadialGradientPaint) {
227 self.state.fill_linear_gradient = None;
228 self.state.fill_radial_gradient = Some(gradient);
229 self.state.fill_pattern = None;
230 }
231 pub fn set_fill_pattern(&mut self, pattern: PatternPaint) {
232 self.state.fill_linear_gradient = None;
233 self.state.fill_radial_gradient = None;
234 self.state.fill_pattern = Some(pattern);
235 }
236 pub fn set_stroke_color(&mut self, color: Color) {
237 self.state.stroke_color = color;
238 self.state.stroke_linear_gradient = None;
239 self.state.stroke_radial_gradient = None;
240 self.state.stroke_pattern = None;
241 }
242 pub fn set_stroke_linear_gradient(&mut self, gradient: LinearGradientPaint) {
243 self.state.stroke_linear_gradient = Some(gradient);
244 self.state.stroke_radial_gradient = None;
245 self.state.stroke_pattern = None;
246 }
247 pub fn set_stroke_radial_gradient(&mut self, gradient: RadialGradientPaint) {
248 self.state.stroke_linear_gradient = None;
249 self.state.stroke_radial_gradient = Some(gradient);
250 self.state.stroke_pattern = None;
251 }
252 pub fn set_stroke_pattern(&mut self, pattern: PatternPaint) {
253 self.state.stroke_linear_gradient = None;
254 self.state.stroke_radial_gradient = None;
255 self.state.stroke_pattern = Some(pattern);
256 }
257 pub fn set_line_width(&mut self, w: f64) {
258 self.state.line_width = w;
259 }
260 pub fn set_line_join(&mut self, join: LineJoin) {
261 self.state.line_join = join;
262 }
263 pub fn set_line_cap(&mut self, cap: LineCap) {
264 self.state.line_cap = cap;
265 }
266 pub fn set_miter_limit(&mut self, limit: f64) {
267 self.state.miter_limit = limit.max(1.0);
268 }
269 pub fn set_line_dash(&mut self, dashes: &[f64], offset: f64) {
270 self.state.line_dash.clear();
271 self.state
272 .line_dash
273 .extend(dashes.iter().copied().filter(|v| *v > 0.0));
274 self.state.dash_offset = offset;
275 }
276 pub fn set_fill_rule(&mut self, rule: FillRule) {
277 self.state.fill_rule = rule;
278 }
279
280 pub fn set_blend_mode(&mut self, mode: CompOp) {
282 self.state.blend_mode = mode;
283 }
284
285 pub fn set_global_alpha(&mut self, alpha: f64) {
287 self.state.global_alpha = alpha.clamp(0.0, 1.0);
288 }
289
290 pub fn set_font(&mut self, font: Arc<Font>) {
296 self.state.font = Some(font);
297 }
298
299 pub fn set_font_size(&mut self, size: f64) {
301 self.state.font_size = size.max(1.0);
302 }
303
304 pub fn set_lcd_mode(&mut self, on: bool) {
310 self.lcd_mode = on;
311 }
312
313 pub fn lcd_mode(&self) -> bool {
315 self.lcd_mode
316 }
317
318 pub fn clip_rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
333 let t = &self.state.transform;
335 let corners = [(x, y), (x + w, y), (x + w, y + h), (x, y + h)];
336 let mut sx_min = f64::INFINITY;
337 let mut sy_min = f64::INFINITY;
338 let mut sx_max = f64::NEG_INFINITY;
339 let mut sy_max = f64::NEG_INFINITY;
340 for (lx, ly) in corners {
341 let mut sx = lx;
342 let mut sy = ly;
343 t.transform(&mut sx, &mut sy);
344 if sx < sx_min {
345 sx_min = sx;
346 }
347 if sx > sx_max {
348 sx_max = sx;
349 }
350 if sy < sy_min {
351 sy_min = sy;
352 }
353 if sy > sy_max {
354 sy_max = sy;
355 }
356 }
357 let sw = (sx_max - sx_min).max(0.0);
358 let sh = (sy_max - sy_min).max(0.0);
359 if let Some((cx, cy, cw, ch)) = self.state.clip {
360 let x1 = sx_min.max(cx);
361 let y1 = sy_min.max(cy);
362 let x2 = sx_max.min(cx + cw);
363 let y2 = sy_max.min(cy + ch);
364 self.state.clip = Some((x1, y1, (x2 - x1).max(0.0), (y2 - y1).max(0.0)));
365 } else {
366 self.state.clip = Some((sx_min, sy_min, sw, sh));
367 }
368 }
369
370 pub fn reset_clip(&mut self) {
371 self.state.clip = None;
372 }
373
374 pub fn clear(&mut self, color: Color) {
380 let rgba = color.to_rgba8();
381 for chunk in active_fb(&mut self.base_fb, &mut self.layer_stack)
382 .pixels_mut()
383 .chunks_exact_mut(4)
384 {
385 chunk[0] = rgba.r as u8;
386 chunk[1] = rgba.g as u8;
387 chunk[2] = rgba.b as u8;
388 chunk[3] = rgba.a as u8;
389 }
390 }
391
392 pub fn begin_path(&mut self) {
397 self.path = PathStorage::new();
398 }
399
400 pub fn move_to(&mut self, x: f64, y: f64) {
401 self.path.move_to(x, y);
402 }
403 pub fn line_to(&mut self, x: f64, y: f64) {
404 self.path.line_to(x, y);
405 }
406
407 pub fn cubic_to(&mut self, cx1: f64, cy1: f64, cx2: f64, cy2: f64, x: f64, y: f64) {
408 self.path.curve4(cx1, cy1, cx2, cy2, x, y);
409 }
410
411 pub fn quad_to(&mut self, cx: f64, cy: f64, x: f64, y: f64) {
412 self.path.curve3(cx, cy, x, y);
413 }
414
415 pub fn arc_to(
416 &mut self,
417 cx: f64,
418 cy: f64,
419 r: f64,
420 start_angle: f64,
421 end_angle: f64,
422 ccw: bool,
423 ) {
424 let mut arc = AggArc::new(cx, cy, r, r, start_angle, end_angle, ccw);
425 self.path.concat_path(&mut arc, 0);
426 }
427
428 pub fn circle(&mut self, cx: f64, cy: f64, r: f64) {
430 self.arc_to(cx, cy, r, 0.0, 2.0 * PI, true);
431 self.path.close_polygon(PATH_FLAGS_NONE);
432 }
433
434 pub fn rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
436 self.path.move_to(x, y);
437 self.path.line_to(x + w, y);
438 self.path.line_to(x + w, y + h);
439 self.path.line_to(x, y + h);
440 self.path.close_polygon(PATH_FLAGS_NONE);
441 }
442
443 pub fn rounded_rect(&mut self, x: f64, y: f64, w: f64, h: f64, r: f64) {
445 let r = r.min(w * 0.5).min(h * 0.5).max(0.0);
446 let mut rr = RoundedRect::new(x, y, x + w, y + h, r);
447 rr.normalize_radius();
448 self.path.concat_path(&mut rr, 0);
449 }
450
451 pub fn close_path(&mut self) {
452 self.path.close_polygon(PATH_FLAGS_NONE);
453 }
454
455 pub fn fill(&mut self) {
461 let mode = self.state.blend_mode;
462 let clip = self.state.clip;
463 let fill_rule = self.state.fill_rule;
464 let transform = self.state.transform.clone();
465 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
466 if let Some(gradient) = self.state.fill_linear_gradient.clone() {
467 sampled::rasterize_linear_gradient_fill(
468 fb,
469 &mut self.path,
470 &gradient,
471 self.state.global_alpha as f32,
472 mode,
473 clip,
474 fill_rule,
475 &transform,
476 );
477 } else if let Some(gradient) = self.state.fill_radial_gradient.clone() {
478 sampled::rasterize_radial_gradient_fill(
479 fb,
480 &mut self.path,
481 &gradient,
482 self.state.global_alpha as f32,
483 mode,
484 clip,
485 fill_rule,
486 &transform,
487 );
488 } else if let Some(pattern) = self.state.fill_pattern.clone() {
489 sampled::rasterize_pattern_fill(
490 fb,
491 &mut self.path,
492 &pattern,
493 self.state.global_alpha as f32,
494 mode,
495 clip,
496 fill_rule,
497 &transform,
498 );
499 } else {
500 let mut color = self.state.fill_color;
501 color.a *= self.state.global_alpha as f32;
502 let rgba = color.to_rgba8();
503 rasterize_fill(fb, &mut self.path, &rgba, mode, clip, fill_rule, &transform);
504 }
505 }
506
507 pub fn stroke(&mut self) {
509 let mut color = self.state.stroke_color;
510 color.a *= self.state.global_alpha as f32;
511 let rgba = color.to_rgba8();
512 let width = self.state.line_width;
513 let join = self.state.line_join;
514 let cap = self.state.line_cap;
515 let miter_limit = self.state.miter_limit;
516 let dashes = self.state.line_dash.clone();
517 let dash_offset = self.state.dash_offset;
518 let mode = self.state.blend_mode;
519 let clip = self.state.clip;
520 let transform = self.state.transform.clone();
521 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
522 if let Some(gradient) = self.state.stroke_linear_gradient.clone() {
523 let mut outline = stroke::materialize_stroke_outline(
524 &mut self.path,
525 width,
526 join,
527 cap,
528 miter_limit,
529 &dashes,
530 dash_offset,
531 );
532 sampled::rasterize_linear_gradient_fill(
533 fb,
534 &mut outline,
535 &gradient,
536 self.state.global_alpha as f32,
537 mode,
538 clip,
539 FillRule::NonZero,
540 &transform,
541 );
542 } else if let Some(gradient) = self.state.stroke_radial_gradient.clone() {
543 let mut outline = stroke::materialize_stroke_outline(
544 &mut self.path,
545 width,
546 join,
547 cap,
548 miter_limit,
549 &dashes,
550 dash_offset,
551 );
552 sampled::rasterize_radial_gradient_fill(
553 fb,
554 &mut outline,
555 &gradient,
556 self.state.global_alpha as f32,
557 mode,
558 clip,
559 FillRule::NonZero,
560 &transform,
561 );
562 } else if let Some(pattern) = self.state.stroke_pattern.clone() {
563 let mut outline = stroke::materialize_stroke_outline(
564 &mut self.path,
565 width,
566 join,
567 cap,
568 miter_limit,
569 &dashes,
570 dash_offset,
571 );
572 sampled::rasterize_pattern_fill(
573 fb,
574 &mut outline,
575 &pattern,
576 self.state.global_alpha as f32,
577 mode,
578 clip,
579 FillRule::NonZero,
580 &transform,
581 );
582 } else {
583 rasterize_stroke(
584 fb,
585 &mut self.path,
586 &rgba,
587 width,
588 join,
589 cap,
590 miter_limit,
591 &dashes,
592 dash_offset,
593 mode,
594 clip,
595 &transform,
596 );
597 }
598 }
599
600 pub fn fill_and_stroke(&mut self) {
602 self.fill();
603 self.stroke();
604 }
605
606 pub fn fill_text(&mut self, text: &str, x: f64, y: f64) {
619 let font = match self.state.font.clone() {
620 Some(f) => f,
621 None => return,
622 };
623 let font_size = self.state.font_size;
624
625 let mut color = self.state.fill_color;
626 color.a *= self.state.global_alpha as f32;
627
628 if self.lcd_mode {
654 let t = &self.state.transform;
655 let ctm_scale = (t.sx * t.sx + t.shy * t.shy).sqrt().max(1e-6);
656 let phys_size = font_size * ctm_scale;
657 let cached = crate::lcd_coverage::rasterize_text_lcd_cached(&font, text, phys_size);
658 let dst_x = x - cached.baseline_x_in_mask / ctm_scale;
662 let dst_y = y - cached.baseline_y_in_mask / ctm_scale;
663 <Self as crate::DrawCtx>::draw_lcd_mask_arc(
664 self,
665 &cached.pixels,
666 cached.width,
667 cached.height,
668 color,
669 dst_x,
670 dst_y,
671 );
672 return;
673 }
674
675 let rgba = color.to_rgba8();
676 let mode = self.state.blend_mode;
677 let clip = self.state.clip;
678 let transform = self.state.transform.clone();
679
680 let (glyph_paths, _) = shape_text(&font, text, font_size, x, y);
682 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
683 for mut path in glyph_paths {
684 rasterize_fill(
685 fb,
686 &mut path,
687 &rgba,
688 mode,
689 clip,
690 FillRule::NonZero,
691 &transform,
692 );
693 }
694 }
695
696 pub fn measure_text(&self, text: &str) -> Option<TextMetrics> {
700 let font = self.state.font.as_ref()?;
701 let size = self.state.font_size;
702 Some(TextMetrics {
703 width: measure_advance(font, text, size),
704 ascent: font.ascender_px(size),
705 descent: font.descender_px(size),
706 line_height: font.line_height_px(size),
707 })
708 }
709
710 pub fn fill_text_gsv(&mut self, text: &str, x: f64, y: f64, size: f64) {
718 let mut color = self.state.fill_color;
719 color.a *= self.state.global_alpha as f32;
720 let rgba = color.to_rgba8();
721 let mode = self.state.blend_mode;
722 let clip = self.state.clip;
723 let transform = self.state.transform.clone();
724
725 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
726 let w = fb.width();
727 let h = fb.height();
728 let stride = (w * 4) as i32;
729 let mut ra = RowAccessor::new();
730 unsafe { ra.attach(fb.pixels_mut().as_mut_ptr(), w, h, stride) };
731 let pf = PixfmtRgba32CompOp::new_with_op(&mut ra, mode);
732 let mut rb = RendererBase::new(pf);
733 apply_clip(&mut rb, clip);
734
735 let mut ras = RasterizerScanlineAa::new();
736 let mut sl = ScanlineU8::new();
737
738 let mut gsv = GsvText::new();
739 gsv.size(size, 0.0);
740 gsv.start_point(x, y);
741 gsv.text(text);
742
743 let mut stroke = ConvStroke::new(&mut gsv);
744 stroke.set_width(size * 0.1);
745 let mut transformed = ConvTransform::new(&mut stroke, transform);
746 ras.add_path(&mut transformed, 0);
747 render_scanlines_aa_solid(&mut ras, &mut sl, &mut rb, &rgba);
748 }
749}
750
751mod draw_impl;
752mod layers;
753mod sampled;
754mod stroke;
755
756use draw_impl::{active_fb, composite_framebuffers};
757pub(crate) use draw_impl::{apply_clip, rasterize_fill, rasterize_stroke};