1use std::f64::consts::PI;
37use std::sync::Arc;
38
39use agg_rust::arc::Arc as AggArc;
40use agg_rust::basics::{VertexSource, PATH_FLAGS_NONE};
41use agg_rust::comp_op::CompOp;
42use agg_rust::conv_curve::ConvCurve;
43use agg_rust::conv_dash::ConvDash;
44use agg_rust::conv_stroke::ConvStroke;
45use agg_rust::gsv_text::GsvText;
46use agg_rust::math_stroke::{LineCap, LineJoin};
47use agg_rust::path_storage::PathStorage;
48use agg_rust::rounded_rect::RoundedRect;
49use agg_rust::trans_affine::TransAffine;
50
51use crate::color::Color;
52use crate::draw_ctx::{DrawCtx, FillRule, LinearGradientPaint, PatternPaint, RadialGradientPaint};
53use crate::lcd_coverage::{rasterize_text_lcd_cached, LcdBuffer, LcdMask};
54use crate::text::{measure_text_metrics, Font, TextMetrics};
55
56mod gradient;
57mod image;
58mod stroke;
59
60#[derive(Clone)]
68struct LcdState {
69 transform: TransAffine,
70 fill_color: Color,
71 fill_linear_gradient: Option<LinearGradientPaint>,
72 fill_radial_gradient: Option<RadialGradientPaint>,
73 fill_pattern: Option<PatternPaint>,
74 stroke_color: Color,
75 stroke_linear_gradient: Option<LinearGradientPaint>,
76 stroke_radial_gradient: Option<RadialGradientPaint>,
77 stroke_pattern: Option<PatternPaint>,
78 fill_rule: FillRule,
79 line_width: f64,
80 line_join: LineJoin,
81 line_cap: LineCap,
82 miter_limit: f64,
83 line_dash: Vec<f64>,
84 dash_offset: f64,
85 blend_mode: CompOp,
86 global_alpha: f64,
87 font: Option<Arc<Font>>,
88 font_size: f64,
89 clip: Option<(f64, f64, f64, f64)>,
93}
94
95impl Default for LcdState {
96 fn default() -> Self {
97 Self {
98 transform: TransAffine::new(),
99 fill_color: Color::black(),
100 fill_linear_gradient: None,
101 fill_radial_gradient: None,
102 fill_pattern: None,
103 stroke_color: Color::black(),
104 stroke_linear_gradient: None,
105 stroke_radial_gradient: None,
106 stroke_pattern: None,
107 fill_rule: FillRule::NonZero,
108 line_width: 1.0,
109 line_join: LineJoin::Round,
110 line_cap: LineCap::Round,
111 miter_limit: 4.0,
112 line_dash: Vec::new(),
113 dash_offset: 0.0,
114 blend_mode: CompOp::SrcOver,
115 global_alpha: 1.0,
116 font: None,
117 font_size: 16.0,
118 clip: None,
119 }
120 }
121}
122
123struct LcdLayer {
138 buffer: LcdBuffer,
139 saved_state: LcdState,
143 saved_stack: Vec<LcdState>,
144 origin_x: f64,
147 origin_y: f64,
148}
149
150pub struct LcdGfxCtx<'a> {
157 base_buffer: &'a mut LcdBuffer,
158 layer_stack: Vec<LcdLayer>,
163 state: LcdState,
164 state_stack: Vec<LcdState>,
165 path: PathStorage,
168}
169
170impl<'a> LcdGfxCtx<'a> {
171 pub fn new(buffer: &'a mut LcdBuffer) -> Self {
172 Self {
173 base_buffer: buffer,
174 layer_stack: Vec::new(),
175 state: LcdState::default(),
176 state_stack: Vec::new(),
177 path: PathStorage::new(),
178 }
179 }
180
181 pub fn buffer(&self) -> &LcdBuffer {
186 self.base_buffer
187 }
188
189 fn active_buffer(&mut self) -> &mut LcdBuffer {
193 if let Some(layer) = self.layer_stack.last_mut() {
194 &mut layer.buffer
195 } else {
196 &mut *self.base_buffer
197 }
198 }
199}
200
201impl<'a> DrawCtx for LcdGfxCtx<'a> {
204 fn set_fill_color(&mut self, color: Color) {
206 self.state.fill_color = color;
207 self.state.fill_linear_gradient = None;
208 self.state.fill_radial_gradient = None;
209 self.state.fill_pattern = None;
210 }
211 fn set_fill_linear_gradient(&mut self, gradient: LinearGradientPaint) {
212 self.state.fill_linear_gradient = Some(gradient);
213 self.state.fill_radial_gradient = None;
214 self.state.fill_pattern = None;
215 }
216 fn supports_fill_linear_gradient(&self) -> bool {
217 true
218 }
219 fn set_fill_radial_gradient(&mut self, gradient: RadialGradientPaint) {
220 self.state.fill_linear_gradient = None;
221 self.state.fill_radial_gradient = Some(gradient);
222 self.state.fill_pattern = None;
223 }
224 fn supports_fill_radial_gradient(&self) -> bool {
225 true
226 }
227 fn set_fill_pattern(&mut self, pattern: PatternPaint) {
228 self.state.fill_linear_gradient = None;
229 self.state.fill_radial_gradient = None;
230 self.state.fill_pattern = Some(pattern);
231 }
232 fn supports_fill_pattern(&self) -> bool {
233 true
234 }
235 fn set_stroke_color(&mut self, color: Color) {
236 self.state.stroke_color = color;
237 self.state.stroke_linear_gradient = None;
238 self.state.stroke_radial_gradient = None;
239 self.state.stroke_pattern = None;
240 }
241 fn set_stroke_linear_gradient(&mut self, gradient: LinearGradientPaint) {
242 self.state.stroke_linear_gradient = Some(gradient);
243 self.state.stroke_radial_gradient = None;
244 self.state.stroke_pattern = None;
245 }
246 fn supports_stroke_linear_gradient(&self) -> bool {
247 true
248 }
249 fn set_stroke_radial_gradient(&mut self, gradient: RadialGradientPaint) {
250 self.state.stroke_linear_gradient = None;
251 self.state.stroke_radial_gradient = Some(gradient);
252 self.state.stroke_pattern = None;
253 }
254 fn supports_stroke_radial_gradient(&self) -> bool {
255 true
256 }
257 fn set_stroke_pattern(&mut self, pattern: PatternPaint) {
258 self.state.stroke_linear_gradient = None;
259 self.state.stroke_radial_gradient = None;
260 self.state.stroke_pattern = Some(pattern);
261 }
262 fn supports_stroke_pattern(&self) -> bool {
263 true
264 }
265 fn set_line_width(&mut self, w: f64) {
266 self.state.line_width = w;
267 }
268 fn set_line_join(&mut self, j: LineJoin) {
269 self.state.line_join = j;
270 }
271 fn set_line_cap(&mut self, c: LineCap) {
272 self.state.line_cap = c;
273 }
274 fn set_miter_limit(&mut self, limit: f64) {
275 self.state.miter_limit = limit.max(1.0);
276 }
277 fn set_line_dash(&mut self, dashes: &[f64], offset: f64) {
278 self.state.line_dash.clear();
279 self.state
280 .line_dash
281 .extend(dashes.iter().copied().filter(|v| *v > 0.0));
282 self.state.dash_offset = offset;
283 }
284 fn set_blend_mode(&mut self, m: CompOp) {
285 self.state.blend_mode = m;
286 }
287 fn set_global_alpha(&mut self, a: f64) {
288 self.state.global_alpha = a.clamp(0.0, 1.0);
289 }
290 fn set_fill_rule(&mut self, r: FillRule) {
291 self.state.fill_rule = r;
292 }
293
294 fn set_font(&mut self, f: Arc<Font>) {
296 self.state.font = Some(f);
297 }
298 fn set_font_size(&mut self, s: f64) {
299 self.state.font_size = s.max(1.0);
300 }
301
302 fn clip_rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
304 let t = &self.state.transform;
307 let corners = [(x, y), (x + w, y), (x + w, y + h), (x, y + h)];
308 let mut sx_min = f64::INFINITY;
309 let mut sy_min = f64::INFINITY;
310 let mut sx_max = f64::NEG_INFINITY;
311 let mut sy_max = f64::NEG_INFINITY;
312 for (lx, ly) in corners {
313 let mut sx = lx;
314 let mut sy = ly;
315 t.transform(&mut sx, &mut sy);
316 if sx < sx_min {
317 sx_min = sx;
318 }
319 if sx > sx_max {
320 sx_max = sx;
321 }
322 if sy < sy_min {
323 sy_min = sy;
324 }
325 if sy > sy_max {
326 sy_max = sy;
327 }
328 }
329 let new_clip = (
330 sx_min,
331 sy_min,
332 (sx_max - sx_min).max(0.0),
333 (sy_max - sy_min).max(0.0),
334 );
335 self.state.clip = Some(match self.state.clip {
336 Some((cx, cy, cw, ch)) => {
337 let x1 = sx_min.max(cx);
338 let y1 = sy_min.max(cy);
339 let x2 = (new_clip.0 + new_clip.2).min(cx + cw);
340 let y2 = (new_clip.1 + new_clip.3).min(cy + ch);
341 (x1, y1, (x2 - x1).max(0.0), (y2 - y1).max(0.0))
342 }
343 None => new_clip,
344 });
345 }
346 fn reset_clip(&mut self) {
347 self.state.clip = None;
348 }
349
350 fn clear(&mut self, color: Color) {
352 self.active_buffer().clear(color);
353 }
354
355 fn begin_path(&mut self) {
357 self.path = PathStorage::new();
358 }
359 fn move_to(&mut self, x: f64, y: f64) {
360 self.path.move_to(x, y);
361 }
362 fn line_to(&mut self, x: f64, y: f64) {
363 self.path.line_to(x, y);
364 }
365 fn cubic_to(&mut self, cx1: f64, cy1: f64, cx2: f64, cy2: f64, x: f64, y: f64) {
366 self.path.curve4(cx1, cy1, cx2, cy2, x, y);
367 }
368 fn quad_to(&mut self, cx: f64, cy: f64, x: f64, y: f64) {
369 self.path.curve3(cx, cy, x, y);
370 }
371 fn arc_to(&mut self, cx: f64, cy: f64, r: f64, start_angle: f64, end_angle: f64, ccw: bool) {
372 let mut arc = AggArc::new(cx, cy, r, r, start_angle, end_angle, ccw);
373 self.path.concat_path(&mut arc, 0);
374 }
375 fn circle(&mut self, cx: f64, cy: f64, r: f64) {
376 self.arc_to(cx, cy, r, 0.0, 2.0 * PI, true);
377 self.path.close_polygon(PATH_FLAGS_NONE);
378 }
379 fn rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
380 self.path.move_to(x, y);
381 self.path.line_to(x + w, y);
382 self.path.line_to(x + w, y + h);
383 self.path.line_to(x, y + h);
384 self.path.close_polygon(PATH_FLAGS_NONE);
385 }
386 fn rounded_rect(&mut self, x: f64, y: f64, w: f64, h: f64, r: f64) {
387 let r = r.min(w * 0.5).min(h * 0.5).max(0.0);
388 let mut rr = RoundedRect::new(x, y, x + w, y + h, r);
389 rr.normalize_radius();
390 self.path.concat_path(&mut rr, 0);
391 }
392 fn close_path(&mut self) {
393 self.path.close_polygon(PATH_FLAGS_NONE);
394 }
395
396 fn fill(&mut self) {
398 let xform = self.state.transform;
399 let clip = self.state.clip;
400 let rule = self.state.fill_rule;
401 let mut path = std::mem::replace(&mut self.path, PathStorage::new());
406 if let Some(gradient) = self.state.fill_linear_gradient.clone() {
407 let global_alpha = self.state.global_alpha as f32;
408 gradient::fill_linear_gradient(
409 self.active_buffer(),
410 &mut path,
411 &gradient,
412 global_alpha,
413 &xform,
414 clip,
415 rule,
416 );
417 } else if let Some(gradient) = self.state.fill_radial_gradient.clone() {
418 let global_alpha = self.state.global_alpha as f32;
419 gradient::fill_radial_gradient(
420 self.active_buffer(),
421 &mut path,
422 &gradient,
423 global_alpha,
424 &xform,
425 clip,
426 rule,
427 );
428 } else if let Some(pattern) = self.state.fill_pattern.clone() {
429 let global_alpha = self.state.global_alpha as f32;
430 gradient::fill_pattern(
431 self.active_buffer(),
432 &mut path,
433 &pattern,
434 global_alpha,
435 &xform,
436 clip,
437 rule,
438 );
439 } else {
440 let mut color = self.state.fill_color;
441 color.a *= self.state.global_alpha as f32;
442 self.active_buffer()
443 .fill_path(&mut path, color, &xform, clip, rule);
444 }
445 self.path = path;
446 }
447 fn stroke(&mut self) {
448 stroke::stroke(self);
449 }
450 fn fill_and_stroke(&mut self) {
451 self.fill();
452 self.stroke();
453 }
454
455 fn draw_triangles_aa(
456 &mut self,
457 vertices: &[[f32; 3]],
458 indices: &[u32],
459 color: crate::color::Color,
460 ) {
461 let saved_fill = self.state.fill_color;
465 self.state.fill_color = color;
466 let n = indices.len() / 3;
467 for t in 0..n {
468 let i0 = indices[t * 3] as usize;
469 let i1 = indices[t * 3 + 1] as usize;
470 let i2 = indices[t * 3 + 2] as usize;
471 if i0 >= vertices.len() || i1 >= vertices.len() || i2 >= vertices.len() {
472 continue;
473 }
474 let v0 = vertices[i0];
475 let v1 = vertices[i1];
476 let v2 = vertices[i2];
477 self.begin_path();
478 self.move_to(v0[0] as f64, v0[1] as f64);
479 self.line_to(v1[0] as f64, v1[1] as f64);
480 self.line_to(v2[0] as f64, v2[1] as f64);
481 self.close_path();
482 self.fill();
483 }
484 self.state.fill_color = saved_fill;
485 }
486
487 fn fill_text(&mut self, text: &str, x: f64, y: f64) {
489 let font = match self.state.font.clone() {
490 Some(f) => f,
491 None => return,
492 };
493 let mut color = self.state.fill_color;
494 color.a *= self.state.global_alpha as f32;
495
496 let t = &self.state.transform;
501 let ctm_scale = (t.sx * t.sx + t.shy * t.shy).sqrt().max(1e-6);
502 let phys_size = self.state.font_size * ctm_scale;
503 let cached = rasterize_text_lcd_cached(&font, text, phys_size);
504 let dst_x = x - cached.baseline_x_in_mask / ctm_scale;
511 let dst_y = y - cached.baseline_y_in_mask / ctm_scale;
512 let sx = (dst_x * t.sx + dst_y * t.shx + t.tx).round() as i32;
513 let sy = (dst_x * t.shy + dst_y * t.sy + t.ty).round() as i32;
514
515 let mask = LcdMask {
520 data: (*cached.pixels).clone(),
521 width: cached.width,
522 height: cached.height,
523 };
524 let clip_i = self.state.clip.map(crate::lcd_coverage::rect_to_pixel_clip);
525 self.active_buffer()
526 .composite_mask(&mask, color, sx, sy, clip_i);
527 }
528
529 fn fill_text_gsv(&mut self, text: &str, x: f64, y: f64, size: f64) {
530 let mut color = self.state.fill_color;
536 color.a *= self.state.global_alpha as f32;
537 let mut gsv = GsvText::new();
538 gsv.size(size, 0.0);
539 gsv.start_point(x, y);
540 gsv.text(text);
541 let mut materialized = PathStorage::new();
542 {
543 let mut stroke = ConvStroke::new(&mut gsv);
544 stroke.set_width(size * 0.1);
545 materialized.concat_path(&mut stroke, 0);
546 }
547 let xform = self.state.transform;
548 let clip = self.state.clip;
549 self.active_buffer()
550 .fill_path(&mut materialized, color, &xform, clip, FillRule::NonZero);
551 }
552
553 fn measure_text(&self, text: &str) -> Option<TextMetrics> {
554 let font = self.state.font.as_ref()?;
555 Some(measure_text_metrics(font, text, self.state.font_size))
556 }
557
558 fn transform(&self) -> TransAffine {
560 self.state.transform
561 }
562 fn root_transform(&self) -> TransAffine {
563 let mut t = self.state.transform;
564 for layer in self.layer_stack.iter().rev() {
565 t.premultiply(&TransAffine::new_translation(
566 layer.origin_x,
567 layer.origin_y,
568 ));
569 }
570 t
571 }
572 fn save(&mut self) {
573 self.state_stack.push(self.state.clone());
574 }
575 fn restore(&mut self) {
576 if let Some(s) = self.state_stack.pop() {
577 self.state = s;
578 }
579 }
580 fn translate(&mut self, tx: f64, ty: f64) {
581 self.state
582 .transform
583 .premultiply(&TransAffine::new_translation(tx, ty));
584 }
585 fn rotate(&mut self, radians: f64) {
586 self.state
587 .transform
588 .premultiply(&TransAffine::new_rotation(radians));
589 }
590 fn scale(&mut self, sx: f64, sy: f64) {
591 self.state
592 .transform
593 .premultiply(&TransAffine::new_scaling(sx, sy));
594 }
595 fn set_transform(&mut self, m: TransAffine) {
596 self.state.transform = m;
597 }
598 fn reset_transform(&mut self) {
599 self.state.transform = TransAffine::new();
600 }
601
602 fn push_layer(&mut self, width: f64, height: f64) {
612 let origin_x = self.state.transform.tx;
613 let origin_y = self.state.transform.ty;
614 let lw = width.ceil().max(1.0) as u32;
615 let lh = height.ceil().max(1.0) as u32;
616 let mut layer_buffer = LcdBuffer::new(lw, lh);
617
618 let dx = -(origin_x.round() as i32);
626 let dy = -(origin_y.round() as i32);
627 let parent_ref: &LcdBuffer = if let Some(layer) = self.layer_stack.last() {
628 &layer.buffer
629 } else {
630 &*self.base_buffer
631 };
632 layer_buffer.composite_buffer(parent_ref, dx, dy, None);
633
634 let saved_state = self.state.clone();
635 let saved_stack = std::mem::take(&mut self.state_stack);
636 self.layer_stack.push(LcdLayer {
637 buffer: layer_buffer,
638 saved_state,
639 saved_stack,
640 origin_x,
641 origin_y,
642 });
643 self.state.transform = TransAffine::new();
647 self.state.clip = None;
648 }
649
650 fn pop_layer(&mut self) {
651 let Some(layer) = self.layer_stack.pop() else {
652 return;
653 };
654 self.state = layer.saved_state;
656 self.state_stack = layer.saved_stack;
657 let dst_x = layer.origin_x.round() as i32;
662 let dst_y = layer.origin_y.round() as i32;
663 let clip_i = self.state.clip.map(crate::lcd_coverage::rect_to_pixel_clip);
664 self.active_buffer()
665 .composite_buffer(&layer.buffer, dst_x, dst_y, clip_i);
666 }
667
668 fn draw_lcd_mask(
675 &mut self,
676 mask: &[u8],
677 mask_w: u32,
678 mask_h: u32,
679 src_color: Color,
680 dst_x: f64,
681 dst_y: f64,
682 ) {
683 if mask.len() < (mask_w as usize) * (mask_h as usize) * 3 {
684 return;
685 }
686 let lcd_mask = LcdMask {
687 data: mask.to_vec(),
688 width: mask_w,
689 height: mask_h,
690 };
691 let t = &self.state.transform;
692 let sx = (dst_x * t.sx + dst_y * t.shx + t.tx).round() as i32;
693 let sy = (dst_x * t.shy + dst_y * t.sy + t.ty).round() as i32;
694 let clip_i = self.state.clip.map(crate::lcd_coverage::rect_to_pixel_clip);
695 self.active_buffer()
696 .composite_mask(&lcd_mask, src_color, sx, sy, clip_i);
697 }
698
699 fn has_lcd_mask_composite(&self) -> bool {
700 true
701 }
702
703 fn has_image_blit(&self) -> bool {
715 true
716 }
717
718 fn draw_image_rgba(
719 &mut self,
720 data: &[u8],
721 img_w: u32,
722 img_h: u32,
723 dst_x: f64,
724 dst_y: f64,
725 dst_w: f64,
726 dst_h: f64,
727 ) {
728 let transform = self.state.transform;
729 let global_alpha = self.state.global_alpha as f32;
730 let clip = self.state.clip;
731 image::draw_image_rgba(
732 self.active_buffer(),
733 data,
734 img_w,
735 img_h,
736 dst_x,
737 dst_y,
738 dst_w,
739 dst_h,
740 &transform,
741 global_alpha,
742 clip,
743 );
744 }
745}
746
747fn configure_stroke<VS: VertexSource>(
748 stroke: &mut ConvStroke<VS>,
749 width: f64,
750 join: LineJoin,
751 cap: LineCap,
752 miter_limit: f64,
753) {
754 stroke.set_width(width);
755 stroke.set_line_join(join);
756 stroke.set_line_cap(cap);
757 stroke.set_miter_limit(miter_limit);
758}
759
760fn configure_dashes<VS: VertexSource>(dash: &mut ConvDash<VS>, dashes: &[f64], dash_offset: f64) {
761 let mut chunks = dashes.chunks_exact(2);
762 for pair in &mut chunks {
763 dash.add_dash(pair[0], pair[1]);
764 }
765 if let Some(&last) = chunks.remainder().first() {
766 dash.add_dash(last, last);
767 }
768 dash.dash_start(dash_offset);
769}
770
771#[cfg(test)]
774mod tests;