1use cosmic_text::{FontSystem, SwashCache, SwashContent, SwashImage};
10use crabshell_ui::{
11 Affine, CaptureView, Color, Content, Gradient, GradientShape, IconView, ImageView, InputView,
12 NodeView, Rect, Shadow, Size, Style, TextSystem, TextView, WindowView,
13};
14use tiny_skia::{
15 FillRule, FilterQuality, GradientStop, LinearGradient, Mask, Paint, PathBuilder, Pixmap,
16 PixmapMut, PixmapPaint, RadialGradient, Shader, SpreadMode, Transform,
17};
18
19use crate::damage::{BufferAge, DamageHistory, PixelRect};
20use crate::icons::Icons;
21
22const TILE: u32 = 64;
27
28const GLYPH_CACHE_LIMIT: usize = 2048;
31
32const LAYER_LIMIT: u64 = 1 << 22;
36
37const LAYER_MARGIN: f32 = 2.0;
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum PixelFormat {
45 Argb8888,
47 Abgr8888,
49}
50
51pub struct Canvas<'a> {
53 pub pixels: &'a mut [u8],
55 pub width: u32,
59 pub height: u32,
61 pub scale: f32,
63 pub format: PixelFormat,
65 pub age: &'a mut BufferAge,
67}
68
69pub struct Renderer {
72 glyphs: SwashCache,
73 icons: Icons,
74 scratch: Vec<u8>,
77 work: Work,
78}
79
80#[derive(Clone, Copy, Default)]
83#[cfg_attr(not(test), allow(dead_code, reason = "only tests read it"))]
84struct Work {
85 layer_pixels: u64,
87 nodes: u64,
89}
90
91impl Renderer {
92 pub fn new() -> Renderer {
95 Renderer {
96 glyphs: SwashCache::new(),
97 icons: Icons::new(),
98 scratch: Vec::new(),
99 work: Work::default(),
100 }
101 }
102
103 #[cfg(test)]
105 pub(crate) fn cached_glyphs(&self) -> usize {
106 self.glyphs.image_cache.len()
107 }
108
109 #[cfg(test)]
112 pub(crate) fn work(&self) -> (u64, u64) {
113 (self.work.layer_pixels, self.work.nodes)
114 }
115
116 #[cfg(test)]
119 pub(crate) fn with_icon_dirs(bases: Vec<std::path::PathBuf>) -> Renderer {
120 Renderer {
121 icons: Icons::with_themes(crate::theme::Themes::new(bases)),
122 ..Renderer::new()
123 }
124 }
125
126 pub fn set_icon_theme(&mut self, theme: impl Into<String>) {
128 self.icons.set_theme(theme.into());
129 }
130
131 #[cfg(test)]
133 pub(crate) fn cached_icons(&self) -> usize {
134 self.icons.cached()
135 }
136
137 #[cfg(test)]
139 pub(crate) fn rasterized_icons(&self) -> usize {
140 self.icons.rasterized()
141 }
142
143 pub fn forget_captures(&mut self, gone: &[u64]) {
147 self.icons.forget_captures(gone);
148 }
149
150 #[cfg(test)]
152 pub(crate) fn cached_captures(&self) -> usize {
153 self.icons.captures()
154 }
155
156 pub fn draw(
174 &mut self,
175 view: &WindowView<'_>,
176 damage: &[Rect],
177 text: &mut TextSystem,
178 history: &mut DamageHistory,
179 canvas: Canvas<'_>,
180 ) -> Result<Vec<PixelRect>, CanvasError> {
181 let Canvas {
182 pixels,
183 width,
184 height,
185 scale,
186 format,
187 age,
188 } = canvas;
189 if pixels.len() as u64 != u64::from(width) * u64::from(height) * 4
190 || !(scale.is_finite() && scale > 0.0)
191 {
192 return Err(CanvasError {
193 width,
194 height,
195 scale,
196 bytes: pixels.len(),
197 });
198 }
199 let plan = history.plan(damage, scale, width, height, age);
200 let mut tiles: Vec<(u32, u32)> = Vec::new();
201 for area in plan.repaint.iter().filter(|area| area.area() > 0) {
202 for row in area.y / TILE..=(area.y + area.height - 1) / TILE {
203 tiles
204 .extend((area.x / TILE..=(area.x + area.width - 1) / TILE).map(|column| (row, column)));
205 }
206 }
207 tiles.sort_unstable();
208 tiles.dedup();
209 for (row, column) in tiles {
210 let (x, y) = (column * TILE, row * TILE);
211 let size = (width.saturating_sub(x), height.saturating_sub(y));
212 let area = PixelRect::new(x, y, TILE.min(size.0), TILE.min(size.1));
213 self.scratch.clear();
214 self.scratch.resize(area.area() as usize * 4, 0);
215 let Some(mut pixmap) = PixmapMut::from_bytes(&mut self.scratch, area.width, area.height)
216 else {
217 continue;
218 };
219 let mut painter = Painter {
220 grid: Grid {
221 scale,
222 window: view.size(),
223 width,
224 height,
225 left: area.x as f32,
226 top: area.y as f32,
227 },
228 glyphs: &mut self.glyphs,
229 icons: &mut self.icons,
230 fonts: text.font_system(),
231 work: &mut self.work,
232 };
233 painter.node(&mut pixmap, view.root());
234 copy(&self.scratch, area, pixels, width, format);
235 }
236 self.icons.end_frame();
237 if self.glyphs.image_cache.len() > GLYPH_CACHE_LIMIT {
238 self.glyphs.image_cache.clear();
239 self.glyphs.outline_command_cache.clear();
240 }
241 Ok(plan.report)
242 }
243}
244
245#[derive(Clone, Copy, Debug, PartialEq)]
248pub struct CanvasError {
249 pub width: u32,
251 pub height: u32,
253 pub scale: f32,
255 pub bytes: usize,
257}
258
259impl std::fmt::Display for CanvasError {
260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 write!(
262 f,
263 "a {}x{} canvas at scale {} needs {} bytes, but has {}",
264 self.width,
265 self.height,
266 self.scale,
267 u64::from(self.width) * u64::from(self.height) * 4,
268 self.bytes
269 )
270 }
271}
272
273impl std::error::Error for CanvasError {}
274
275impl Default for Renderer {
276 fn default() -> Self {
277 Self::new()
278 }
279}
280
281fn copy(scratch: &[u8], area: PixelRect, pixels: &mut [u8], width: u32, format: PixelFormat) {
283 let stride = width as usize * 4;
284 let row_bytes = area.width as usize * 4;
285 for (row, source) in scratch.chunks_exact(row_bytes).enumerate() {
286 let start = (area.y as usize + row) * stride + area.x as usize * 4;
287 let target = &mut pixels[start..start + row_bytes];
288 match format {
289 PixelFormat::Abgr8888 => target.copy_from_slice(source),
290 PixelFormat::Argb8888 => {
291 let (to, _) = target.as_chunks_mut::<4>();
292 let (from, _) = source.as_chunks::<4>();
293 for (to, from) in to.iter_mut().zip(from) {
294 *to = [from[2], from[1], from[0], from[3]];
295 }
296 }
297 }
298 }
299}
300
301#[derive(Clone, Copy)]
303struct Grid {
304 scale: f32,
305 window: Size,
307 width: u32,
309 height: u32,
310 left: f32,
312 top: f32,
313}
314
315impl Grid {
316 fn x(&self, x: f32) -> f32 {
319 let pixel = if x >= self.window.width {
320 self.width as f32
321 } else {
322 (x * self.scale).round()
323 };
324 pixel - self.left
325 }
326
327 fn y(&self, y: f32) -> f32 {
329 let pixel = if y >= self.window.height {
330 self.height as f32
331 } else {
332 (y * self.scale).round()
333 };
334 pixel - self.top
335 }
336
337 fn cover(&self, rect: Rect, width: u32, height: u32) -> Option<(u32, u32, u32, u32)> {
341 if !(rect.width > 0.0 && rect.height > 0.0) {
342 return None;
343 }
344 let left = ((rect.x * self.scale).floor() - self.left - 1.0).max(0.0) as u32;
346 let top = ((rect.y * self.scale).floor() - self.top - 1.0).max(0.0) as u32;
347 let right = (((rect.right() * self.scale).ceil() - self.left + 1.0).max(0.0) as u32).min(width);
348 let bottom =
349 (((rect.bottom() * self.scale).ceil() - self.top + 1.0).max(0.0) as u32).min(height);
350 (left < right && top < bottom).then(|| (left, top, right - left, bottom - top))
351 }
352
353 fn moved(&self, x: u32, y: u32) -> Grid {
355 Grid {
356 left: self.left + x as f32,
357 top: self.top + y as f32,
358 ..*self
359 }
360 }
361
362 fn rect(&self, rect: Rect) -> Option<tiny_skia::Rect> {
365 tiny_skia::Rect::from_ltrb(
366 self.x(rect.x),
367 self.y(rect.y),
368 self.x(rect.right()),
369 self.y(rect.bottom()),
370 )
371 .filter(|rect| rect.width() >= 1.0 && rect.height() >= 1.0)
372 }
373}
374
375struct Painter<'a> {
377 grid: Grid,
378 glyphs: &'a mut SwashCache,
379 icons: &'a mut Icons,
380 fonts: &'a mut FontSystem,
381 work: &'a mut Work,
382}
383
384impl Painter<'_> {
385 fn node(&mut self, pixmap: &mut PixmapMut<'_>, node: NodeView<'_>) {
389 let Some((x, y, width, height)) =
391 self
392 .grid
393 .cover(node.bounds(), pixmap.width(), pixmap.height())
394 else {
395 return;
396 };
397 let style = node.style();
398 let opacity = style.opacity.unwrap_or(1.0);
399 let opacity = if opacity.is_nan() {
400 1.0
401 } else {
402 opacity.clamp(0.0, 1.0)
403 };
404 if opacity == 0.0 {
405 return;
406 }
407 if let Some(transform) = node.transform() {
408 self.transformed(pixmap, node, &style, transform, opacity);
409 } else if opacity < 1.0 {
410 let Some(mut layer) = Pixmap::new(width, height) else {
412 return;
413 };
414 self.work.layer_pixels += u64::from(layer.width()) * u64::from(layer.height());
415 let mut inner = Painter {
416 grid: self.grid.moved(x, y),
417 glyphs: &mut *self.glyphs,
418 icons: &mut *self.icons,
419 fonts: &mut *self.fonts,
420 work: &mut *self.work,
421 };
422 inner.node_contents(&mut layer.as_mut(), node, &style);
423 let paint = PixmapPaint {
424 opacity,
425 ..PixmapPaint::default()
426 };
427 let (x, y) = (x as i32, y as i32);
428 pixmap.draw_pixmap(x, y, layer.as_ref(), &paint, Transform::identity(), None);
429 } else {
430 self.node_contents(pixmap, node, &style);
431 }
432 }
433
434 fn transformed(
440 &mut self,
441 pixmap: &mut PixmapMut<'_>,
442 node: NodeView<'_>,
443 style: &Style,
444 transform: Affine,
445 opacity: f32,
446 ) {
447 let Affine {
448 sx,
449 ky,
450 kx,
451 sy,
452 tx,
453 ty,
454 } = transform;
455 let Grid {
456 scale, left, top, ..
457 } = self.grid;
458 let device = Transform::from_scale(scale, scale)
460 .pre_concat(Transform::from_row(sx, ky, kx, sy, tx, ty))
461 .pre_scale(1.0 / scale, 1.0 / scale);
462 let to_pixmap = device.post_translate(-left, -top);
463 let Some(back) = to_pixmap.invert() else {
464 return;
465 };
466 let inner = node.inner_bounds();
467 let shown = tiny_skia::Rect::from_xywh(0.0, 0.0, pixmap.width() as f32, pixmap.height() as f32)
468 .and_then(|rect| rect.transform(back));
469 let drawn = tiny_skia::Rect::from_xywh(
470 inner.x * scale,
471 inner.y * scale,
472 inner.width * scale,
473 inner.height * scale,
474 );
475 let Some(area) = shown
476 .zip(drawn)
477 .and_then(|(shown, drawn)| shown.intersect(&drawn))
478 .and_then(|area| area.outset(LAYER_MARGIN, LAYER_MARGIN))
479 .and_then(|area| area.round_out())
480 else {
481 return;
482 };
483 let pixels = u64::from(area.width()) * u64::from(area.height());
484 if pixels > LAYER_LIMIT {
485 tracing::debug!(
486 "{} is transformed into a layer too large to draw",
487 node.path()
488 );
489 return;
490 }
491 let Some(mut layer) = Pixmap::new(area.width(), area.height()) else {
492 return;
493 };
494 self.work.layer_pixels += pixels;
495 let (x, y) = (area.x() as f32, area.y() as f32);
496 let mut inner = Painter {
497 grid: Grid {
498 left: x,
499 top: y,
500 ..self.grid
501 },
502 glyphs: &mut *self.glyphs,
503 icons: &mut *self.icons,
504 fonts: &mut *self.fonts,
505 work: &mut *self.work,
506 };
507 inner.node_contents(&mut layer.as_mut(), node, style);
508 let paint = PixmapPaint {
509 opacity,
510 quality: FilterQuality::Bilinear,
511 ..PixmapPaint::default()
512 };
513 pixmap.draw_pixmap(
514 0,
515 0,
516 layer.as_ref(),
517 &paint,
518 to_pixmap.pre_translate(x, y),
519 None,
520 );
521 }
522
523 fn node_contents(&mut self, pixmap: &mut PixmapMut<'_>, node: NodeView<'_>, style: &Style) {
524 self.work.nodes += 1;
525 if let Some(shadow) = style.shadow {
526 self.shadow(pixmap, node.rect(), style, &shadow);
527 }
528 self.draw_box(pixmap, node.rect(), style);
529 match node.content() {
530 Content::Text(text) => self.text(pixmap, &text),
531 Content::Icon(icon) => self.icon(pixmap, &icon),
532 Content::Image(image) => self.image(pixmap, &image),
533 Content::Capture(capture) => self.capture(pixmap, &capture),
534 Content::Input(input) => self.input(pixmap, &input),
535 Content::None => {}
536 }
537 if node.clips() {
538 self.clipped_children(pixmap, node, style);
539 } else {
540 for child in node.children() {
541 self.node(pixmap, child);
542 }
543 }
544 }
545
546 fn clipped_children(&mut self, pixmap: &mut PixmapMut<'_>, node: NodeView<'_>, style: &Style) {
551 let Some((area, radius)) = self.box_shape(node.rect(), style) else {
552 return;
553 };
554 let width = self.border_width(style);
555 let Some(inside) = tiny_skia::Rect::from_ltrb(
556 area.left() + width,
557 area.top() + width,
558 area.right() - width,
559 area.bottom() - width,
560 ) else {
561 return;
562 };
563 let radius = (radius - width).max(0.0);
564 self.through_mask(pixmap, node.rect(), (inside, radius), |painter, layer| {
565 for child in node.children() {
566 painter.node(layer, child);
567 }
568 });
569 }
570
571 fn through_mask(
574 &mut self,
575 pixmap: &mut PixmapMut<'_>,
576 cover: Rect,
577 (shape, radius): (tiny_skia::Rect, f32),
578 draw: impl FnOnce(&mut Painter<'_>, &mut PixmapMut<'_>),
579 ) {
580 if !visible(pixmap, shape) {
581 return;
582 }
583 let (Some(mut mask), Some((x, y, layer_width, layer_height))) = (
584 Mask::new(pixmap.width(), pixmap.height()),
585 self.grid.cover(cover, pixmap.width(), pixmap.height()),
586 ) else {
587 return;
588 };
589 let Some(mut layer) = Pixmap::new(layer_width, layer_height) else {
590 return;
591 };
592 self.work.layer_pixels += u64::from(layer_width) * u64::from(layer_height);
593 let mut inner = Painter {
594 grid: self.grid.moved(x, y),
595 glyphs: &mut *self.glyphs,
596 icons: &mut *self.icons,
597 fonts: &mut *self.fonts,
598 work: &mut *self.work,
599 };
600 draw(&mut inner, &mut layer.as_mut());
601 let mut path = PathBuilder::new();
602 rounded_rect(&mut path, shape, radius);
603 let Some(path) = path.finish() else {
604 return;
605 };
606 mask.fill_path(&path, FillRule::Winding, true, Transform::identity());
607 pixmap.draw_pixmap(
608 x as i32,
609 y as i32,
610 layer.as_ref(),
611 &PixmapPaint::default(),
612 Transform::identity(),
613 Some(&mask),
614 );
615 }
616
617 fn box_shape(&self, rect: Rect, style: &Style) -> Option<(tiny_skia::Rect, f32)> {
621 let area = self.grid.rect(rect)?;
622 let radius = (style.radius.unwrap_or(0.0) * self.grid.scale)
623 .clamp(0.0, area.width().min(area.height()) / 2.0);
624 let radius = if radius.is_nan() { 0.0 } else { radius };
625 Some((area, radius))
626 }
627
628 fn border_width(&self, style: &Style) -> f32 {
631 let Some(border) = style.border else {
632 return 0.0;
633 };
634 let width = (border.width * self.grid.scale).floor();
635 if border.width > 0.0 {
636 width.max(1.0)
637 } else {
638 width.max(0.0)
639 }
640 }
641
642 fn draw_box(&self, pixmap: &mut PixmapMut<'_>, rect: Rect, style: &Style) {
644 if style.background.is_none() && style.gradient.is_none() && style.border.is_none() {
645 return;
646 }
647 let Some((area, radius)) = self.box_shape(rect, style) else {
648 return;
649 };
650 if !visible(pixmap, area) {
651 return;
652 }
653
654 if let Some(color) = style.background {
655 let mut path = PathBuilder::new();
656 rounded_rect(&mut path, area, radius);
657 fill(pixmap, path, color, FillRule::Winding);
658 }
659 if let Some(gradient) = style.gradient {
660 let mut path = PathBuilder::new();
661 rounded_rect(&mut path, area, radius);
662 if let (Some(path), Some(shader)) = (path.finish(), self.shader(&gradient, area)) {
663 let paint = Paint {
664 shader,
665 anti_alias: true,
666 ..Paint::default()
667 };
668 pixmap.fill_path(
669 &path,
670 &paint,
671 FillRule::Winding,
672 Transform::identity(),
673 None,
674 );
675 }
676 }
677 if let Some(border) = style.border {
678 let width = self.border_width(style);
679 if width >= 1.0 {
680 let mut path = PathBuilder::new();
682 rounded_rect(&mut path, area, radius);
683 if let Some(inner) = tiny_skia::Rect::from_ltrb(
684 area.left() + width,
685 area.top() + width,
686 area.right() - width,
687 area.bottom() - width,
688 ) {
689 rounded_rect(&mut path, inner, (radius - width).max(0.0));
690 }
691 fill(pixmap, path, border.color, FillRule::EvenOdd);
692 }
693 }
694 }
695
696 fn shadow(&self, pixmap: &mut PixmapMut<'_>, rect: Rect, style: &Style, shadow: &Shadow) {
701 let Grid {
702 scale, left, top, ..
703 } = self.grid;
704 let numbers = [shadow.x, shadow.y, shadow.blur, shadow.spread];
705 if shadow.color.a == 0 || !numbers.iter().all(|n| n.is_finite()) {
706 return;
707 }
708 let Some((area, radius)) = self.box_shape(rect, style) else {
709 return;
710 };
711 let spread = shadow.spread * scale;
713 let Some(shape) = tiny_skia::Rect::from_ltrb(
714 area.left() + left + shadow.x * scale - spread,
715 area.top() + top + shadow.y * scale - spread,
716 area.right() + left + shadow.x * scale + spread,
717 area.bottom() + top + shadow.y * scale + spread,
718 ) else {
719 return;
720 };
721 let radius = if spread > 0.0 && radius < spread {
724 radius + spread * (1.0 + (radius / spread - 1.0).powi(3))
725 } else {
726 radius + spread
727 };
728 let radius = radius.clamp(0.0, shape.width().min(shape.height()) / 2.0);
729 let cut = if shadow.cut_out {
731 self.box_mask(pixmap, rect, style)
732 } else {
733 None
734 };
735 let sigma = shadow.blur.max(0.0) * scale / 2.0;
737 if sigma < 0.25 {
738 let mut path = PathBuilder::new();
739 let Some(shape) = shape.transform(Transform::from_translate(-left, -top)) else {
740 return;
741 };
742 rounded_rect(&mut path, shape, radius);
743 let Some(path) = path.finish() else {
744 return;
745 };
746 let mut paint = Paint::default();
747 let color = shadow.color;
748 paint.set_color_rgba8(color.r, color.g, color.b, color.a);
749 paint.anti_alias = true;
750 let outside = cut.map(|mut cut| {
751 cut.invert();
752 cut
753 });
754 let (rule, at) = (FillRule::Winding, Transform::identity());
755 pixmap.fill_path(&path, &paint, rule, at, outside.as_ref());
756 return;
757 }
758 let reach = 3.0 * sigma;
759 let columns = (shape.left() - reach - left).floor().max(0.0) as u32
760 ..((shape.right() + reach - left).ceil().max(0.0) as u32).min(pixmap.width());
761 let rows = (shape.top() - reach - top).floor().max(0.0) as u32
762 ..((shape.bottom() + reach - top).ceil().max(0.0) as u32).min(pixmap.height());
763 if columns.is_empty() || rows.is_empty() {
764 return;
765 }
766 let blurred = BlurredBox::new(shape, radius, sigma);
767 let mut mask = Vec::with_capacity(columns.len() * rows.len());
768 for row in rows.clone() {
769 let xs = columns.clone().map(|column| left + column as f32 + 0.5);
770 blurred.row(top + row as f32 + 0.5, xs, &mut mask);
771 }
772 if let Some(cut) = cut {
773 let stride = pixmap.width() as usize;
774 let under = rows.clone().flat_map(|row| {
775 columns
776 .clone()
777 .map(move |column| row as usize * stride + column as usize)
778 });
779 for (alpha, at) in mask.iter_mut().zip(under) {
780 *alpha = mul(*alpha, 255 - cut.data()[at]);
781 }
782 }
783 let width = columns.len() as u32;
784 let height = rows.len() as u32;
785 let (x, y) = (columns.start as i32, rows.start as i32);
786 blend_mask(pixmap, x, y, width, height, &mask, shadow.color);
787 }
788
789 fn box_mask(&self, pixmap: &PixmapMut<'_>, rect: Rect, style: &Style) -> Option<Mask> {
792 let (area, radius) = self.box_shape(rect, style)?;
793 let mut path = PathBuilder::new();
794 rounded_rect(&mut path, area, radius);
795 let mut mask = Mask::new(pixmap.width(), pixmap.height())?;
796 let (rule, at) = (FillRule::Winding, Transform::identity());
797 mask.fill_path(&path.finish()?, rule, true, at);
798 Some(mask)
799 }
800
801 fn shader(&self, gradient: &Gradient, area: tiny_skia::Rect) -> Option<Shader<'static>> {
805 let (left, top) = (area.left() + self.grid.left, area.top() + self.grid.top);
806 let (width, height) = (area.width(), area.height());
807 let middle = tiny_skia::Point::from_xy(left + width / 2.0, top + height / 2.0);
808 let given = gradient.stops();
809 let mut stops = Vec::with_capacity(given.len() * 2);
810 let stop = |at: f32, color: Color, alpha: u8| {
811 GradientStop::new(
812 at,
813 tiny_skia::Color::from_rgba8(color.r, color.g, color.b, alpha),
814 )
815 };
816 for (index, &(at, color)) in given.iter().enumerate() {
817 if color.a > 0 {
818 stops.push(stop(at, color, color.a));
819 continue;
820 }
821 for neighbor in [index.checked_sub(1), Some(index + 1)]
825 .into_iter()
826 .flatten()
827 {
828 if let Some(&(_, near)) = given.get(neighbor) {
829 stops.push(stop(at, near, 0));
830 }
831 }
832 }
833 let moved = Transform::from_translate(-self.grid.left, -self.grid.top);
834 match gradient.shape {
835 GradientShape::Linear(angle) => {
836 let (sin, cos) = angle.to_radians().sin_cos();
839 let half = (width * sin.abs() + height * cos.abs()) / 2.0;
840 let (dx, dy) = (sin * half, -cos * half);
841 let start = tiny_skia::Point::from_xy(middle.x - dx, middle.y - dy);
842 let end = tiny_skia::Point::from_xy(middle.x + dx, middle.y + dy);
843 LinearGradient::new(start, end, stops, SpreadMode::Pad, moved)
844 }
845 GradientShape::Radial => {
846 let reach = (width * width + height * height).sqrt() / 2.0;
847 RadialGradient::new(middle, 0.0, middle, reach, stops, SpreadMode::Pad, moved)
848 }
849 }
850 }
851
852 fn text(&mut self, pixmap: &mut PixmapMut<'_>, text: &TextView<'_>) {
856 let color = text.color();
857 if color.a == 0 {
858 return;
859 }
860 let Grid {
861 scale, left, top, ..
862 } = self.grid;
863 let origin = text.origin();
864 let x = origin.x * scale - left;
865 for run in text.runs() {
866 let baseline = ((origin.y + run.line_y) * scale).round() - top;
868 if baseline + run.line_height * scale < 0.0
869 || baseline - run.line_height * scale > pixmap.height() as f32
870 {
871 continue;
872 }
873 for glyph in run.glyphs {
874 let physical = glyph.physical((x, baseline), scale);
875 let Some(image) = self.glyphs.get_image(self.fonts, physical.cache_key) else {
876 continue;
877 };
878 let left = physical.x + image.placement.left;
879 let top = physical.y - image.placement.top;
880 let (width, height) = (image.placement.width, image.placement.height);
881 match image.content {
882 SwashContent::Mask => {
883 blend_mask(pixmap, left, top, width, height, &image.data, color);
884 }
885 SwashContent::Color => {
886 let premultiplied = is_color_outline(image);
887 let rgba = (image.data.as_slice(), premultiplied);
888 blend_rgba(pixmap, left, top, width, height, rgba, color.a);
889 }
890 SwashContent::SubpixelMask => {}
892 }
893 }
894 }
895 }
896
897 fn input(&mut self, pixmap: &mut PixmapMut<'_>, input: &InputView<'_>) {
899 let Some(area) = self.grid.rect(input.area) else {
900 return;
901 };
902 self.through_mask(pixmap, input.area, (area, 0.0), |painter, layer| {
903 painter.input_contents(layer, input);
904 });
905 }
906
907 fn input_contents(&mut self, pixmap: &mut PixmapMut<'_>, input: &InputView<'_>) {
910 let color = input.text.color();
911 for selection in input.selection {
912 let Some(area) = self.grid.rect(*selection) else {
913 continue;
914 };
915 let mut path = PathBuilder::new();
916 path.push_rect(area);
917 let tint = Color::rgba(color.r, color.g, color.b, color.a / 3);
918 fill(pixmap, path, tint, FillRule::Winding);
919 }
920 self.text(pixmap, &input.text);
921 for line in input.underline {
922 if let Some(area) = self.grid.rect(*line) {
923 let mut path = PathBuilder::new();
924 path.push_rect(area);
925 fill(pixmap, path, color, FillRule::Winding);
926 }
927 }
928 if let Some(caret) = input.caret {
929 let left = self.grid.x(caret.x);
931 let right = self.grid.x(caret.right()).max(left + 1.0);
932 if let Some(area) = tiny_skia::Rect::from_ltrb(
933 left,
934 self.grid.y(caret.y),
935 right,
936 self.grid.y(caret.bottom()),
937 ) {
938 let mut path = PathBuilder::new();
939 path.push_rect(area);
940 fill(pixmap, path, color, FillRule::Winding);
941 }
942 }
943 }
944
945 fn image(&mut self, pixmap: &mut PixmapMut<'_>, image: &ImageView<'_>) {
947 let Some((x, y, width, height)) = self.picture_area(pixmap, image.rect) else {
948 return;
949 };
950 if let Some(pixels) = self.icons.image(image.path, width, height, image.fit) {
951 draw_at(pixmap, x, y, pixels);
952 }
953 }
954
955 fn capture(&mut self, pixmap: &mut PixmapMut<'_>, capture: &CaptureView<'_>) {
957 let Some((x, y, width, height)) = self.picture_area(pixmap, capture.rect) else {
958 return;
959 };
960 let frame = capture.frame;
961 if let Some(pixels) = self
962 .icons
963 .capture(capture.id, frame, width, height, capture.fit)
964 {
965 draw_at(pixmap, x, y, pixels);
966 }
967 }
968
969 fn picture_area(&self, pixmap: &PixmapMut<'_>, rect: Rect) -> Option<(i32, i32, u32, u32)> {
974 let Grid {
975 scale, left, top, ..
976 } = self.grid;
977 let (width, height) = ((rect.width * scale).round(), (rect.height * scale).round());
978 let (x, y) = (
979 (rect.x * scale).round() - left,
980 (rect.y * scale).round() - top,
981 );
982 if !(width >= 1.0 && height >= 1.0) {
983 return None;
984 }
985 let area = tiny_skia::Rect::from_xywh(x, y, width, height)?;
986 visible(pixmap, area).then_some((x as i32, y as i32, width as u32, height as u32))
988 }
989
990 fn icon(&mut self, pixmap: &mut PixmapMut<'_>, icon: &IconView<'_>) {
992 let Grid {
993 scale, left, top, ..
994 } = self.grid;
995 let side = (icon.rect.width * scale).round();
996 if side.is_nan() || side < 1.0 {
997 return;
998 }
999 let x = (icon.rect.x * scale).round() - left;
1000 let y = (icon.rect.y * scale).round() - top;
1001 let size = icon.rect.width.round() as u32;
1003 let theme_scale = scale.ceil().max(1.0) as u32;
1004 if let Some(image) = self.icons.get(icon.source, side as u32, size, theme_scale) {
1005 pixmap.draw_pixmap(
1006 x as i32,
1007 y as i32,
1008 image.as_ref(),
1009 &PixmapPaint::default(),
1010 Transform::identity(),
1011 None,
1012 );
1013 }
1014 }
1015}
1016
1017fn draw_at(pixmap: &mut PixmapMut<'_>, x: i32, y: i32, pixels: &Pixmap) {
1019 let paint = PixmapPaint::default();
1020 pixmap.draw_pixmap(x, y, pixels.as_ref(), &paint, Transform::identity(), None);
1021}
1022
1023fn visible(pixmap: &PixmapMut<'_>, area: tiny_skia::Rect) -> bool {
1025 area.right() > 0.0
1026 && area.bottom() > 0.0
1027 && area.left() < pixmap.width() as f32
1028 && area.top() < pixmap.height() as f32
1029}
1030
1031fn fill(pixmap: &mut PixmapMut<'_>, path: PathBuilder, color: Color, rule: FillRule) {
1032 let Some(path) = path.finish() else {
1033 return;
1034 };
1035 let mut paint = Paint::default();
1036 paint.set_color_rgba8(color.r, color.g, color.b, color.a);
1037 paint.anti_alias = true;
1038 pixmap.fill_path(&path, &paint, rule, Transform::identity(), None);
1039}
1040
1041fn rounded_rect(path: &mut PathBuilder, rect: tiny_skia::Rect, radius: f32) {
1043 if radius <= 0.0 {
1044 path.push_rect(rect);
1045 return;
1046 }
1047 const KAPPA: f32 = 0.552_284_8;
1050 let (l, t, r, b) = (rect.left(), rect.top(), rect.right(), rect.bottom());
1051 let k = radius * (1.0 - KAPPA);
1052 path.move_to(l + radius, t);
1053 path.line_to(r - radius, t);
1054 path.cubic_to(r - k, t, r, t + k, r, t + radius);
1055 path.line_to(r, b - radius);
1056 path.cubic_to(r, b - k, r - k, b, r - radius, b);
1057 path.line_to(l + radius, b);
1058 path.cubic_to(l + k, b, l, b - k, l, b - radius);
1059 path.line_to(l, t + radius);
1060 path.cubic_to(l, t + k, l + k, t, l + radius, t);
1061 path.close();
1062}
1063
1064fn mul(a: u8, b: u8) -> u8 {
1066 ((u16::from(a) * u16::from(b) + 127) / 255) as u8
1067}
1068
1069fn over(data: &mut [u8], index: usize, source: [u8; 4]) {
1071 let rest = 255 - source[3];
1072 for (channel, value) in data[index..index + 4].iter_mut().zip(source) {
1073 *channel = value.saturating_add(mul(*channel, rest));
1074 }
1075}
1076
1077fn overlap(
1081 pixmap: &PixmapMut<'_>,
1082 left: i32,
1083 top: i32,
1084 width: u32,
1085 height: u32,
1086) -> Option<(usize, usize, usize, usize)> {
1087 let column_start = (-i64::from(left)).max(0);
1088 let row_start = (-i64::from(top)).max(0);
1089 let column_end = i64::from(width).min(i64::from(pixmap.width()) - i64::from(left));
1090 let row_end = i64::from(height).min(i64::from(pixmap.height()) - i64::from(top));
1091 (column_start < column_end && row_start < row_end).then_some((
1092 column_start as usize,
1093 column_end as usize,
1094 row_start as usize,
1095 row_end as usize,
1096 ))
1097}
1098
1099fn blend_mask(
1101 pixmap: &mut PixmapMut<'_>,
1102 left: i32,
1103 top: i32,
1104 width: u32,
1105 height: u32,
1106 mask: &[u8],
1107 color: Color,
1108) {
1109 if mask.len() < width as usize * height as usize {
1110 return;
1111 }
1112 let Some((columns, columns_end, rows, rows_end)) = overlap(pixmap, left, top, width, height)
1113 else {
1114 return;
1115 };
1116 let stride = pixmap.width() as usize;
1117 let data = pixmap.data_mut();
1118 for row in rows..rows_end {
1119 for column in columns..columns_end {
1120 let coverage = mask[row * width as usize + column];
1121 if coverage == 0 {
1122 continue;
1123 }
1124 let alpha = mul(color.a, coverage);
1125 let source = [
1126 mul(color.r, alpha),
1127 mul(color.g, alpha),
1128 mul(color.b, alpha),
1129 alpha,
1130 ];
1131 let x = (i64::from(left) + column as i64) as usize;
1132 let y = (i64::from(top) + row as i64) as usize;
1133 over(data, (y * stride + x) * 4, source);
1134 }
1135 }
1136}
1137
1138const SAMPLES: usize = 8;
1140
1141struct BlurredBox {
1147 middle: (f32, f32),
1148 half: (f32, f32),
1149 radius: f32,
1150 sigma: f32,
1151}
1152
1153impl BlurredBox {
1154 fn new(shape: tiny_skia::Rect, radius: f32, sigma: f32) -> BlurredBox {
1155 BlurredBox {
1156 middle: (
1157 (shape.left() + shape.right()) / 2.0,
1158 (shape.top() + shape.bottom()) / 2.0,
1159 ),
1160 half: (shape.width() / 2.0, shape.height() / 2.0),
1161 radius,
1162 sigma,
1163 }
1164 }
1165
1166 fn row(&self, y: f32, xs: impl Iterator<Item = f32>, mask: &mut Vec<u8>) {
1169 let y = y - self.middle.1;
1170 let (half_width, half_height) = self.half;
1171 let reach = 3.0 * self.sigma;
1172 let down = self.along(y, half_height);
1173 let clear_of_corners = y.abs() <= half_height - self.radius - reach;
1174 let rows = if clear_of_corners {
1175 [(0.0, 0.0); SAMPLES]
1176 } else {
1177 self.rows_near(y)
1178 };
1179 for x in xs {
1180 let x = x - self.middle.0;
1181 let coverage = if clear_of_corners || x.abs() <= half_width - self.radius - reach {
1182 self.along(x, half_width) * down
1183 } else {
1184 let sum: f32 = rows
1185 .iter()
1186 .map(|&(span, weight)| self.along(x, span) * weight)
1187 .sum();
1188 sum.clamp(0.0, 1.0)
1189 };
1190 mask.push((coverage * 255.0).round() as u8);
1191 }
1192 }
1193
1194 fn along(&self, p: f32, half: f32) -> f32 {
1197 if p.abs() <= half - 3.0 * self.sigma {
1198 return 1.0;
1199 }
1200 let scale = std::f32::consts::FRAC_1_SQRT_2 / self.sigma;
1201 0.5 * (erf((p + half) * scale) - erf((p - half) * scale))
1202 }
1203
1204 fn rows_near(&self, y: f32) -> [(f32, f32); SAMPLES] {
1208 let (half_width, half_height) = self.half;
1209 let reach = 3.0 * self.sigma;
1210 let start = (y - half_height).clamp(-reach, reach);
1211 let end = (y + half_height).clamp(-reach, reach);
1212 let step = (end - start) / SAMPLES as f32;
1213 std::array::from_fn(|sample| {
1214 let offset = start + step * (sample as f32 + 0.5);
1215 let row = y - offset;
1218 let into_corner = (row.abs() - (half_height - self.radius)).max(0.0);
1219 let span = half_width - self.radius
1220 + (self.radius * self.radius - into_corner * into_corner)
1221 .max(0.0)
1222 .sqrt();
1223 (span, gaussian(offset, self.sigma) * step)
1224 })
1225 }
1226}
1227
1228fn gaussian(x: f32, sigma: f32) -> f32 {
1230 let root_two_pi = (2.0 * std::f32::consts::PI).sqrt();
1231 (-(x * x) / (2.0 * sigma * sigma)).exp() / (root_two_pi * sigma)
1232}
1233
1234fn erf(x: f32) -> f32 {
1237 let a = x.abs();
1238 let d = 1.0 + a * (0.278_393 + a * (0.230_389 + a * (0.000_972 + a * 0.078_108)));
1239 let d = d * d;
1240 (1.0 - 1.0 / (d * d)).copysign(x)
1241}
1242
1243fn is_color_outline(image: &SwashImage) -> bool {
1250 format!("{:?}", image.source).starts_with("ColorOutline")
1251}
1252
1253fn blend_rgba(
1256 pixmap: &mut PixmapMut<'_>,
1257 left: i32,
1258 top: i32,
1259 width: u32,
1260 height: u32,
1261 (rgba, premultiplied): (&[u8], bool),
1262 opacity: u8,
1263) {
1264 if rgba.len() < width as usize * height as usize * 4 {
1265 return;
1266 }
1267 let Some((columns, columns_end, rows, rows_end)) = overlap(pixmap, left, top, width, height)
1268 else {
1269 return;
1270 };
1271 let stride = pixmap.width() as usize;
1272 let data = pixmap.data_mut();
1273 for row in rows..rows_end {
1274 for column in columns..columns_end {
1275 let index = (row * width as usize + column) * 4;
1276 let alpha = mul(rgba[index + 3], opacity);
1277 if alpha == 0 {
1278 continue;
1279 }
1280 let factor = if premultiplied { opacity } else { alpha };
1283 let source = [
1284 mul(rgba[index], factor),
1285 mul(rgba[index + 1], factor),
1286 mul(rgba[index + 2], factor),
1287 alpha,
1288 ];
1289 let x = (i64::from(left) + column as i64) as usize;
1290 let y = (i64::from(top) + row as i64) as usize;
1291 over(data, (y * stride + x) * 4, source);
1292 }
1293 }
1294}