1use std::f64::consts::PI;
14use std::sync::Arc;
15
16use agg_rust::arc::Arc as AggArc;
17use agg_rust::basics::PATH_FLAGS_NONE;
18use agg_rust::comp_op::{CompOp, PixfmtRgba32CompOp};
19use agg_rust::conv_curve::ConvCurve;
20use agg_rust::conv_stroke::ConvStroke;
21use agg_rust::conv_transform::ConvTransform;
22use agg_rust::gsv_text::GsvText;
23use agg_rust::math_stroke::{LineCap, LineJoin};
24use agg_rust::path_storage::PathStorage;
25use agg_rust::rasterizer_scanline_aa::RasterizerScanlineAa;
26use agg_rust::renderer_base::RendererBase;
27use agg_rust::renderer_scanline::render_scanlines_aa_solid;
28use agg_rust::rendering_buffer::RowAccessor;
29use agg_rust::rounded_rect::RoundedRect;
30use agg_rust::scanline_u::ScanlineU8;
31use agg_rust::trans_affine::TransAffine;
32
33use crate::color::Color;
34use crate::framebuffer::Framebuffer;
35use crate::text::{shape_text, measure_advance, Font, TextMetrics};
36
37struct LayerEntry {
43 fb: Framebuffer,
45 saved_state: GfxState,
48 saved_stack: Vec<GfxState>,
50 origin_x: f64,
52 origin_y: f64,
54}
55
56pub use agg_rust::comp_op::CompOp as BlendMode;
58
59#[derive(Clone)]
61struct GfxState {
62 transform: TransAffine,
63 fill_color: Color,
64 stroke_color: Color,
65 line_width: f64,
66 line_join: LineJoin,
67 line_cap: LineCap,
68 blend_mode: CompOp,
69 clip: Option<(f64, f64, f64, f64)>,
71 global_alpha: f64,
73 font: Option<Arc<Font>>,
75 font_size: f64,
77}
78
79impl Default for GfxState {
80 fn default() -> Self {
81 Self {
82 transform: TransAffine::new(),
83 fill_color: Color::black(),
84 stroke_color: Color::black(),
85 line_width: 1.0,
86 line_join: LineJoin::Round,
87 line_cap: LineCap::Round,
88 blend_mode: CompOp::SrcOver,
89 clip: None,
90 global_alpha: 1.0,
91 font: None,
92 font_size: 16.0,
93 }
94 }
95}
96
97pub struct GfxCtx<'a> {
110 base_fb: &'a mut Framebuffer,
111 layer_stack: Vec<LayerEntry>,
113 state: GfxState,
114 state_stack: Vec<GfxState>,
115 path: PathStorage,
117 lcd_mode: bool,
124}
125
126impl<'a> GfxCtx<'a> {
127 pub fn new(fb: &'a mut Framebuffer) -> Self {
129 Self {
130 base_fb: fb,
131 layer_stack: Vec::new(),
132 state: GfxState::default(),
133 state_stack: Vec::new(),
134 path: PathStorage::new(),
135 lcd_mode: false,
136 }
137 }
138
139 pub fn push_layer(&mut self, width: f64, height: f64) {
149 let origin_x = self.state.transform.tx;
150 let origin_y = self.state.transform.ty;
151 let saved_state = self.state.clone();
152 let saved_stack = std::mem::take(&mut self.state_stack);
153 let layer_fb = Framebuffer::new(width.ceil() as u32, height.ceil() as u32);
154 self.layer_stack.push(LayerEntry {
155 fb: layer_fb,
156 saved_state,
157 saved_stack,
158 origin_x,
159 origin_y,
160 });
161 self.state.transform = TransAffine::new();
163 self.state.clip = None;
164 }
165
166 pub fn pop_layer(&mut self) {
169 let Some(layer) = self.layer_stack.pop() else { return; };
170 let ox = layer.origin_x as i32;
171 let oy = layer.origin_y as i32;
172 self.state = layer.saved_state;
173 self.state_stack = layer.saved_stack;
174 if let Some(top) = self.layer_stack.last_mut() {
176 composite_framebuffers(&mut top.fb, &layer.fb, ox, oy);
177 } else {
178 composite_framebuffers(self.base_fb, &layer.fb, ox, oy);
179 }
180 }
181
182 pub fn save(&mut self) {
187 self.state_stack.push(self.state.clone());
188 }
189
190 pub fn restore(&mut self) {
191 if let Some(state) = self.state_stack.pop() {
192 self.state = state;
193 }
194 }
195
196 pub fn translate(&mut self, tx: f64, ty: f64) {
202 self.state.transform.premultiply(&TransAffine::new_translation(tx, ty));
203 }
204
205 pub fn rotate(&mut self, radians: f64) {
207 self.state.transform.premultiply(&TransAffine::new_rotation(radians));
208 }
209
210 pub fn scale(&mut self, sx: f64, sy: f64) {
212 self.state.transform.premultiply(&TransAffine::new_scaling(sx, sy));
213 }
214
215 pub fn set_transform(&mut self, m: TransAffine) { self.state.transform = m; }
216 pub fn reset_transform(&mut self) { self.state.transform = TransAffine::new(); }
217 pub fn transform(&self) -> TransAffine { self.state.transform }
221
222 pub fn set_fill_color(&mut self, color: Color) { self.state.fill_color = color; }
227 pub fn set_stroke_color(&mut self, color: Color) { self.state.stroke_color = color; }
228 pub fn set_line_width(&mut self, w: f64) { self.state.line_width = w; }
229 pub fn set_line_join(&mut self, join: LineJoin) { self.state.line_join = join; }
230 pub fn set_line_cap(&mut self, cap: LineCap) { self.state.line_cap = cap; }
231
232 pub fn set_blend_mode(&mut self, mode: CompOp) { self.state.blend_mode = mode; }
234
235 pub fn set_global_alpha(&mut self, alpha: f64) {
237 self.state.global_alpha = alpha.clamp(0.0, 1.0);
238 }
239
240 pub fn set_font(&mut self, font: Arc<Font>) {
246 self.state.font = Some(font);
247 }
248
249 pub fn set_font_size(&mut self, size: f64) {
251 self.state.font_size = size.max(1.0);
252 }
253
254 pub fn set_lcd_mode(&mut self, on: bool) { self.lcd_mode = on; }
260
261 pub fn lcd_mode(&self) -> bool { self.lcd_mode }
263
264 pub fn clip_rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
279 let t = &self.state.transform;
281 let corners = [(x, y), (x + w, y), (x + w, y + h), (x, y + h)];
282 let mut sx_min = f64::INFINITY;
283 let mut sy_min = f64::INFINITY;
284 let mut sx_max = f64::NEG_INFINITY;
285 let mut sy_max = f64::NEG_INFINITY;
286 for (lx, ly) in corners {
287 let mut sx = lx;
288 let mut sy = ly;
289 t.transform(&mut sx, &mut sy);
290 if sx < sx_min { sx_min = sx; }
291 if sx > sx_max { sx_max = sx; }
292 if sy < sy_min { sy_min = sy; }
293 if sy > sy_max { sy_max = sy; }
294 }
295 let sw = (sx_max - sx_min).max(0.0);
296 let sh = (sy_max - sy_min).max(0.0);
297 if let Some((cx, cy, cw, ch)) = self.state.clip {
298 let x1 = sx_min.max(cx);
299 let y1 = sy_min.max(cy);
300 let x2 = sx_max.min(cx + cw);
301 let y2 = sy_max.min(cy + ch);
302 self.state.clip = Some((x1, y1, (x2 - x1).max(0.0), (y2 - y1).max(0.0)));
303 } else {
304 self.state.clip = Some((sx_min, sy_min, sw, sh));
305 }
306 }
307
308 pub fn reset_clip(&mut self) { self.state.clip = None; }
309
310 pub fn clear(&mut self, color: Color) {
316 let rgba = color.to_rgba8();
317 for chunk in active_fb(&mut self.base_fb, &mut self.layer_stack).pixels_mut().chunks_exact_mut(4) {
318 chunk[0] = rgba.r as u8;
319 chunk[1] = rgba.g as u8;
320 chunk[2] = rgba.b as u8;
321 chunk[3] = rgba.a as u8;
322 }
323 }
324
325 pub fn begin_path(&mut self) { self.path = PathStorage::new(); }
330
331 pub fn move_to(&mut self, x: f64, y: f64) { self.path.move_to(x, y); }
332 pub fn line_to(&mut self, x: f64, y: f64) { self.path.line_to(x, y); }
333
334 pub fn cubic_to(&mut self, cx1: f64, cy1: f64, cx2: f64, cy2: f64, x: f64, y: f64) {
335 self.path.curve4(cx1, cy1, cx2, cy2, x, y);
336 }
337
338 pub fn quad_to(&mut self, cx: f64, cy: f64, x: f64, y: f64) {
339 self.path.curve3(cx, cy, x, y);
340 }
341
342 pub fn arc_to(&mut self, cx: f64, cy: f64, r: f64, start_angle: f64, end_angle: f64, ccw: bool) {
343 let mut arc = AggArc::new(cx, cy, r, r, start_angle, end_angle, ccw);
344 self.path.concat_path(&mut arc, 0);
345 }
346
347 pub fn circle(&mut self, cx: f64, cy: f64, r: f64) {
349 self.arc_to(cx, cy, r, 0.0, 2.0 * PI, true);
350 self.path.close_polygon(PATH_FLAGS_NONE);
351 }
352
353 pub fn rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
355 self.path.move_to(x, y);
356 self.path.line_to(x + w, y);
357 self.path.line_to(x + w, y + h);
358 self.path.line_to(x, y + h);
359 self.path.close_polygon(PATH_FLAGS_NONE);
360 }
361
362 pub fn rounded_rect(&mut self, x: f64, y: f64, w: f64, h: f64, r: f64) {
364 let r = r.min(w * 0.5).min(h * 0.5).max(0.0);
365 let mut rr = RoundedRect::new(x, y, x + w, y + h, r);
366 rr.normalize_radius();
367 self.path.concat_path(&mut rr, 0);
368 }
369
370 pub fn close_path(&mut self) { self.path.close_polygon(PATH_FLAGS_NONE); }
371
372 pub fn fill(&mut self) {
378 let mut color = self.state.fill_color;
379 color.a *= self.state.global_alpha as f32;
380 let rgba = color.to_rgba8();
381 let mode = self.state.blend_mode;
382 let clip = self.state.clip;
383 let transform = self.state.transform.clone();
384 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
385 rasterize_fill(fb, &mut self.path, &rgba, mode, clip, &transform);
386 }
387
388 pub fn stroke(&mut self) {
390 let mut color = self.state.stroke_color;
391 color.a *= self.state.global_alpha as f32;
392 let rgba = color.to_rgba8();
393 let width = self.state.line_width;
394 let join = self.state.line_join;
395 let cap = self.state.line_cap;
396 let mode = self.state.blend_mode;
397 let clip = self.state.clip;
398 let transform = self.state.transform.clone();
399 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
400 rasterize_stroke(fb, &mut self.path, &rgba, width, join, cap, mode, clip, &transform);
401 }
402
403 pub fn fill_and_stroke(&mut self) {
405 self.fill();
406 self.stroke();
407 }
408
409 pub fn fill_text(&mut self, text: &str, x: f64, y: f64) {
422 let font = match self.state.font.clone() {
423 Some(f) => f,
424 None => return,
425 };
426 let font_size = self.state.font_size;
427
428 let mut color = self.state.fill_color;
429 color.a *= self.state.global_alpha as f32;
430
431 if self.lcd_mode {
457 let t = &self.state.transform;
458 let ctm_scale = (t.sx * t.sx + t.shy * t.shy).sqrt().max(1e-6);
459 let phys_size = font_size * ctm_scale;
460 let cached = crate::lcd_coverage::rasterize_text_lcd_cached(
461 &font, text, phys_size,
462 );
463 let dst_x = x - cached.baseline_x_in_mask / ctm_scale;
467 let dst_y = y - cached.baseline_y_in_mask / ctm_scale;
468 <Self as crate::DrawCtx>::draw_lcd_mask_arc(
469 self,
470 &cached.pixels, cached.width, cached.height,
471 color, dst_x, dst_y,
472 );
473 return;
474 }
475
476 let rgba = color.to_rgba8();
477 let mode = self.state.blend_mode;
478 let clip = self.state.clip;
479 let transform = self.state.transform.clone();
480
481 let (glyph_paths, _) = shape_text(&font, text, font_size, x, y);
483 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
484 for mut path in glyph_paths {
485 rasterize_fill(fb, &mut path, &rgba, mode, clip, &transform);
486 }
487 }
488
489 pub fn measure_text(&self, text: &str) -> Option<TextMetrics> {
493 let font = self.state.font.as_ref()?;
494 let size = self.state.font_size;
495 Some(TextMetrics {
496 width: measure_advance(font, text, size),
497 ascent: font.ascender_px(size),
498 descent: font.descender_px(size),
499 line_height: font.line_height_px(size),
500 })
501 }
502
503 pub fn fill_text_gsv(&mut self, text: &str, x: f64, y: f64, size: f64) {
511 let mut color = self.state.fill_color;
512 color.a *= self.state.global_alpha as f32;
513 let rgba = color.to_rgba8();
514 let mode = self.state.blend_mode;
515 let clip = self.state.clip;
516 let transform = self.state.transform.clone();
517
518 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
519 let w = fb.width();
520 let h = fb.height();
521 let stride = (w * 4) as i32;
522 let mut ra = RowAccessor::new();
523 unsafe { ra.attach(fb.pixels_mut().as_mut_ptr(), w, h, stride) };
524 let pf = PixfmtRgba32CompOp::new_with_op(&mut ra, mode);
525 let mut rb = RendererBase::new(pf);
526 apply_clip(&mut rb, clip);
527
528 let mut ras = RasterizerScanlineAa::new();
529 let mut sl = ScanlineU8::new();
530
531 let mut gsv = GsvText::new();
532 gsv.size(size, 0.0);
533 gsv.start_point(x, y);
534 gsv.text(text);
535
536 let mut stroke = ConvStroke::new(&mut gsv);
537 stroke.set_width(size * 0.1);
538 let mut transformed = ConvTransform::new(&mut stroke, transform);
539 ras.add_path(&mut transformed, 0);
540 render_scanlines_aa_solid(&mut ras, &mut sl, &mut rb, &rgba);
541 }
542}
543
544#[inline]
556fn active_fb<'a>(
557 base_fb: &'a mut Framebuffer,
558 layer_stack: &'a mut Vec<LayerEntry>,
559) -> &'a mut Framebuffer {
560 if let Some(top) = layer_stack.last_mut() {
561 &mut top.fb
562 } else {
563 base_fb
564 }
565}
566
567
568fn composite_framebuffers(dst: &mut Framebuffer, src: &Framebuffer, dest_x: i32, dest_y: i32) {
588 let src_w = src.width() as i32;
589 let src_h = src.height() as i32;
590 let dst_w = dst.width() as i32;
591 let dst_h = dst.height() as i32;
592
593 let src_px = src.pixels();
594 let dst_px = dst.pixels_mut();
595
596 for sy in 0..src_h {
597 let dy = dest_y + sy;
598 if dy < 0 || dy >= dst_h { continue; }
599 for sx in 0..src_w {
600 let dx = dest_x + sx;
601 if dx < 0 || dx >= dst_w { continue; }
602 let si = ((sy * src_w + sx) * 4) as usize;
603 let di = ((dy * dst_w + dx) * 4) as usize;
604 let sa = src_px[si + 3] as f32 / 255.0;
605 if sa < 1e-4 { continue; } let inv_sa = 1.0 - sa;
607 for k in 0..4 {
609 let s = src_px[si + k] as f32;
610 let d = dst_px[di + k] as f32;
611 dst_px[di + k] = (s + d * inv_sa).round().clamp(0.0, 255.0) as u8;
612 }
613 }
614 }
615}
616
617pub(crate) fn rasterize_fill(
624 fb: &mut Framebuffer,
625 path: &mut PathStorage,
626 color: &agg_rust::color::Rgba8,
627 mode: CompOp,
628 clip: Option<(f64, f64, f64, f64)>,
629 transform: &TransAffine,
630) {
631 let w = fb.width();
632 let h = fb.height();
633 let stride = (w * 4) as i32;
634 let mut ra = RowAccessor::new();
635 unsafe { ra.attach(fb.pixels_mut().as_mut_ptr(), w, h, stride) };
636 let pf = PixfmtRgba32CompOp::new_with_op(&mut ra, mode);
637 let mut rb = RendererBase::new(pf);
638 apply_clip(&mut rb, clip);
639
640 let mut ras = RasterizerScanlineAa::new();
641 let mut sl = ScanlineU8::new();
642 let mut curves = ConvCurve::new(path);
643 let mut transformed = ConvTransform::new(&mut curves, transform.clone());
644 ras.add_path(&mut transformed, 0);
645 render_scanlines_aa_solid(&mut ras, &mut sl, &mut rb, color);
646}
647
648pub(crate) fn rasterize_stroke(
649 fb: &mut Framebuffer,
650 path: &mut PathStorage,
651 color: &agg_rust::color::Rgba8,
652 width: f64,
653 join: LineJoin,
654 cap: LineCap,
655 mode: CompOp,
656 clip: Option<(f64, f64, f64, f64)>,
657 transform: &TransAffine,
658) {
659 let w = fb.width();
660 let h = fb.height();
661 let stride = (w * 4) as i32;
662 let mut ra = RowAccessor::new();
663 unsafe { ra.attach(fb.pixels_mut().as_mut_ptr(), w, h, stride) };
664 let pf = PixfmtRgba32CompOp::new_with_op(&mut ra, mode);
665 let mut rb = RendererBase::new(pf);
666 apply_clip(&mut rb, clip);
667
668 let mut ras = RasterizerScanlineAa::new();
669 let mut sl = ScanlineU8::new();
670 let mut curves = ConvCurve::new(path);
671 let mut stroke = ConvStroke::new(&mut curves);
672 stroke.set_width(width);
673 stroke.set_line_join(join);
674 stroke.set_line_cap(cap);
675 let mut transformed = ConvTransform::new(&mut stroke, transform.clone());
676 ras.add_path(&mut transformed, 0);
677 render_scanlines_aa_solid(&mut ras, &mut sl, &mut rb, color);
678}
679
680impl crate::draw_ctx::DrawCtx for GfxCtx<'_> {
685 fn set_fill_color(&mut self, c: crate::color::Color) { self.set_fill_color(c) }
686 fn set_stroke_color(&mut self, c: crate::color::Color) { self.set_stroke_color(c) }
687 fn set_line_width(&mut self, w: f64) { self.set_line_width(w) }
688 fn set_line_join(&mut self, j: agg_rust::math_stroke::LineJoin) { self.set_line_join(j) }
689 fn set_line_cap(&mut self, c: agg_rust::math_stroke::LineCap) { self.set_line_cap(c) }
690 fn set_blend_mode(&mut self, m: agg_rust::comp_op::CompOp) { self.set_blend_mode(m) }
691 fn set_global_alpha(&mut self, a: f64) { self.set_global_alpha(a) }
692 fn set_font(&mut self, f: Arc<crate::text::Font>) { self.set_font(f) }
693 fn set_font_size(&mut self, s: f64) { self.set_font_size(s) }
694 fn clip_rect(&mut self, x: f64, y: f64, w: f64, h: f64) { self.clip_rect(x, y, w, h) }
695 fn reset_clip(&mut self) { self.reset_clip() }
696 fn clear(&mut self, c: crate::color::Color) { self.clear(c) }
697 fn begin_path(&mut self) { self.begin_path() }
698 fn move_to(&mut self, x: f64, y: f64) { self.move_to(x, y) }
699 fn line_to(&mut self, x: f64, y: f64) { self.line_to(x, y) }
700 fn cubic_to(&mut self, cx1: f64, cy1: f64, cx2: f64, cy2: f64, x: f64, y: f64) {
701 self.cubic_to(cx1, cy1, cx2, cy2, x, y)
702 }
703 fn quad_to(&mut self, cx: f64, cy: f64, x: f64, y: f64) { self.quad_to(cx, cy, x, y) }
704 fn arc_to(&mut self, cx: f64, cy: f64, r: f64, a1: f64, a2: f64, ccw: bool) {
705 self.arc_to(cx, cy, r, a1, a2, ccw)
706 }
707 fn circle(&mut self, cx: f64, cy: f64, r: f64) { self.circle(cx, cy, r) }
708 fn rect(&mut self, x: f64, y: f64, w: f64, h: f64) { self.rect(x, y, w, h) }
709 fn rounded_rect(&mut self, x: f64, y: f64, w: f64, h: f64, r: f64) {
710 self.rounded_rect(x, y, w, h, r)
711 }
712 fn close_path(&mut self) { self.close_path() }
713 fn fill(&mut self) { self.fill() }
714 fn stroke(&mut self) { self.stroke() }
715 fn fill_and_stroke(&mut self) { self.fill_and_stroke() }
716
717 fn draw_triangles_aa(
718 &mut self,
719 vertices: &[[f32; 3]],
720 indices: &[u32],
721 color: crate::color::Color,
722 ) {
723 let saved_fill = self.state.fill_color;
731 self.set_fill_color(color);
732 let n_tris = indices.len() / 3;
733 for t in 0..n_tris {
734 let i0 = indices[t * 3 ] as usize;
735 let i1 = indices[t * 3 + 1] as usize;
736 let i2 = indices[t * 3 + 2] as usize;
737 if i0 >= vertices.len() || i1 >= vertices.len() || i2 >= vertices.len() { continue; }
738 let v0 = vertices[i0];
739 let v1 = vertices[i1];
740 let v2 = vertices[i2];
741 self.begin_path();
742 self.move_to(v0[0] as f64, v0[1] as f64);
743 self.line_to(v1[0] as f64, v1[1] as f64);
744 self.line_to(v2[0] as f64, v2[1] as f64);
745 self.close_path();
746 self.fill();
747 }
748 self.set_fill_color(saved_fill);
749 }
750 fn fill_text(&mut self, t: &str, x: f64, y: f64) { self.fill_text(t, x, y) }
751 fn fill_text_gsv(&mut self, t: &str, x: f64, y: f64, s: f64) { self.fill_text_gsv(t, x, y, s) }
752 fn measure_text(&self, t: &str) -> Option<crate::text::TextMetrics> { self.measure_text(t) }
753 fn transform(&self) -> agg_rust::trans_affine::TransAffine { self.transform() }
754 fn save(&mut self) { self.save() }
755 fn restore(&mut self) { self.restore() }
756 fn translate(&mut self, tx: f64, ty: f64) { self.translate(tx, ty) }
757 fn rotate(&mut self, r: f64) { self.rotate(r) }
758 fn scale(&mut self, sx: f64, sy: f64) { self.scale(sx, sy) }
759 fn set_transform(&mut self, m: agg_rust::trans_affine::TransAffine) { self.set_transform(m) }
760 fn reset_transform(&mut self) { self.reset_transform() }
761 fn push_layer(&mut self, w: f64, h: f64) { self.push_layer(w, h) }
762 fn pop_layer(&mut self) { self.pop_layer() }
763
764 fn has_image_blit(&self) -> bool { true }
765
766 fn draw_image_rgba_arc(
767 &mut self,
768 data: &Arc<Vec<u8>>,
769 img_w: u32,
770 img_h: u32,
771 dst_x: f64,
772 dst_y: f64,
773 dst_w: f64,
774 dst_h: f64,
775 ) {
776 self.draw_image_rgba(data.as_slice(), img_w, img_h, dst_x, dst_y, dst_w, dst_h);
779 }
780
781 fn draw_lcd_backbuffer_arc(
782 &mut self,
783 color: &Arc<Vec<u8>>,
784 alpha: &Arc<Vec<u8>>,
785 w: u32,
786 h: u32,
787 dst_x: f64,
788 dst_y: f64,
789 _dst_w: f64,
790 _dst_h: f64,
791 ) {
792 if w == 0 || h == 0 { return; }
801 let w_u = w as usize;
802 let h_u = h as usize;
803 if color.len() < w_u * h_u * 3 || alpha.len() < w_u * h_u * 3 { return; }
804
805 let t = &self.state.transform;
806 let sx = (dst_x * t.sx + dst_y * t.shx + t.tx).round() as i32;
807 let sy = (dst_x * t.shy + dst_y * t.sy + t.ty).round() as i32;
808 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
809 let fw = fb.width() as i32;
810 let fh = fb.height() as i32;
811 let fw_u = fw as usize;
812 let pixels = fb.pixels_mut();
813
814 for src_y in 0..h_u {
815 let dy = sy + (h_u - 1 - src_y) as i32;
818 if dy < 0 || dy >= fh { continue; }
819 let dy_u = dy as usize;
820 for src_x in 0..w_u {
821 let dx = sx + src_x as i32;
822 if dx < 0 || dx >= fw { continue; }
823 let ci = (src_y * w_u + src_x) * 3;
824
825 let sa_r = alpha[ci] as f32 / 255.0;
826 let sa_g = alpha[ci + 1] as f32 / 255.0;
827 let sa_b = alpha[ci + 2] as f32 / 255.0;
828 if sa_r == 0.0 && sa_g == 0.0 && sa_b == 0.0 { continue; }
829
830 let sc_r = color[ci] as f32 / 255.0;
831 let sc_g = color[ci + 1] as f32 / 255.0;
832 let sc_b = color[ci + 2] as f32 / 255.0;
833
834 let di = (dy_u * fw_u + dx as usize) * 4;
835 let dc_r = pixels[di] as f32 / 255.0;
842 let dc_g = pixels[di + 1] as f32 / 255.0;
843 let dc_b = pixels[di + 2] as f32 / 255.0;
844 let da = pixels[di + 3] as f32 / 255.0;
845
846 let rc_r = sc_r + dc_r * (1.0 - sa_r);
847 let rc_g = sc_g + dc_g * (1.0 - sa_g);
848 let rc_b = sc_b + dc_b * (1.0 - sa_b);
849 let src_a_max = sa_r.max(sa_g).max(sa_b);
850 let ra = src_a_max + da * (1.0 - src_a_max);
851
852 pixels[di] = (rc_r * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
853 pixels[di + 1] = (rc_g * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
854 pixels[di + 2] = (rc_b * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
855 pixels[di + 3] = (ra * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
856 }
857 }
858 }
859
860 fn has_lcd_mask_composite(&self) -> bool { true }
861
862 fn draw_lcd_mask(
863 &mut self,
864 mask: &[u8],
865 mask_w: u32,
866 mask_h: u32,
867 src_color: Color,
868 dst_x: f64,
869 dst_y: f64,
870 ) {
871 if mask.len() < (mask_w as usize) * (mask_h as usize) * 3 { return; }
876 let t = &self.state.transform;
877 let sx = dst_x * t.sx + dst_y * t.shx + t.tx;
878 let sy = dst_x * t.shy + dst_y * t.sy + t.ty;
879 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
880 let fw = fb.width();
881 let fh = fb.height();
882 let origin_x = sx.round() as i32;
883 let origin_y = sy.round() as i32;
884
885 let sa = src_color.a.clamp(0.0, 1.0);
886 let sr = src_color.r.clamp(0.0, 1.0);
887 let sg = src_color.g.clamp(0.0, 1.0);
888 let sb = src_color.b.clamp(0.0, 1.0);
889 let fw_i = fw as i32;
890 let fh_i = fh as i32;
891 let mw_i = mask_w as i32;
892 let mh_i = mask_h as i32;
893 let pixels = fb.pixels_mut();
894
895 for my in 0..mh_i {
896 let dy = origin_y + my;
899 if dy < 0 || dy >= fh_i { continue; }
900 for mx in 0..mw_i {
901 let dx = origin_x + mx;
902 if dx < 0 || dx >= fw_i { continue; }
903 let mi = ((my * mw_i + mx) * 3) as usize;
904 let cr = (mask[mi] as f32 / 255.0) * sa;
907 let cg = (mask[mi + 1] as f32 / 255.0) * sa;
908 let cb = (mask[mi + 2] as f32 / 255.0) * sa;
909 if cr == 0.0 && cg == 0.0 && cb == 0.0 { continue; }
910 let di = ((dy * fw_i + dx) * 4) as usize;
911 let dr = pixels[di] as f32 / 255.0;
912 let dg = pixels[di + 1] as f32 / 255.0;
913 let db = pixels[di + 2] as f32 / 255.0;
914 let rr = sr * cr + dr * (1.0 - cr);
915 let rg = sg * cg + dg * (1.0 - cg);
916 let rbb = sb * cb + db * (1.0 - cb);
917 pixels[di] = (rr * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
918 pixels[di + 1] = (rg * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
919 pixels[di + 2] = (rbb * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
920 }
924 }
925 }
926
927 fn draw_image_rgba(
928 &mut self,
929 data: &[u8],
930 img_w: u32,
931 img_h: u32,
932 dst_x: f64,
933 dst_y: f64,
934 dst_w: f64,
935 dst_h: f64,
936 ) {
937 if img_w == 0 || img_h == 0 || dst_w < 1.0 || dst_h < 1.0 { return; }
940
941 let out_w = dst_w.round() as u32;
942 let out_h = dst_h.round() as u32;
943 let mut scaled = crate::framebuffer::Framebuffer::new(out_w, out_h);
944
945 let px = scaled.pixels_mut();
951 for dy in 0..out_h {
952 for dx in 0..out_w {
953 let sx = (dx as f64 / out_w as f64 * img_w as f64) as u32;
954 let sy_img = ((1.0 - (dy as f64 + 0.5) / out_h as f64) * img_h as f64)
956 .floor()
957 .clamp(0.0, (img_h - 1) as f64) as u32;
958 let si = ((sy_img * img_w + sx) * 4) as usize;
959 let di = ((dy * out_w + dx) * 4) as usize;
960 if si + 3 < data.len() && di + 3 < px.len() {
961 let a = data[si + 3] as u32;
962 if a == 255 {
963 px[di] = data[si];
964 px[di + 1] = data[si + 1];
965 px[di + 2] = data[si + 2];
966 px[di + 3] = 255;
967 } else {
968 px[di] = (((data[si] as u32) * a + 127) / 255) as u8;
970 px[di + 1] = (((data[si + 1] as u32) * a + 127) / 255) as u8;
971 px[di + 2] = (((data[si + 2] as u32) * a + 127) / 255) as u8;
972 px[di + 3] = a as u8;
973 }
974 }
975 }
976 }
977
978 let (tx, ty) = { let t = self.transform(); (t.tx, t.ty) };
980 let screen_x = (tx + dst_x).round() as i32;
981 let screen_y = (ty + dst_y).round() as i32;
982 let fb = active_fb(&mut self.base_fb, &mut self.layer_stack);
983 composite_framebuffers(fb, &scaled, screen_x, screen_y);
984 }
985}
986
987pub(crate) fn apply_clip<PF: agg_rust::pixfmt_rgba::PixelFormat>(
989 rb: &mut RendererBase<PF>,
990 clip: Option<(f64, f64, f64, f64)>,
991) {
992 if let Some((x, y, w, h)) = clip {
993 let x1 = x.floor() as i32;
994 let y1 = y.floor() as i32;
995 let x2 = (x + w).ceil() as i32 - 1;
996 let y2 = (y + h).ceil() as i32 - 1;
997 rb.clip_box_i(x1, y1, x2, y2);
998 }
999}