Skip to main content

crabshell_render/
draw.rs

1//! Drawing a window's view into a buffer.
2//!
3//! The areas to repaint are drawn from scratch, one tile of a fixed grid at
4//! a time, into an RGBA pixmap of the tile's size, then copied into the
5//! buffer. The pixmap clips the drawing to the tile, and the copy converts
6//! the pixels to the buffer's byte order, so tiny-skia, glyph masks, resvg
7//! and PNG decoding all work in the RGBA order they share.
8
9use 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
22/// The side of a tile, in buffer pixels. tiny-skia cuts a curve where it
23/// leaves the pixmap, which changes the antialiasing beside the cut, so
24/// every pixel is drawn in the same tile whatever the damage, and a repaint
25/// matches drawing everything.
26const TILE: u32 = 64;
27
28/// How many glyph images cosmic-text's cache may hold after a draw before
29/// it is cleared. A bar needs a few hundred; the cache never shrinks itself.
30const GLYPH_CACHE_LIMIT: usize = 2048;
31
32/// The most pixels a transformed layer may hold, 16 MiB. Only a widget
33/// shrunk to a sliver of its size, so that a tile shows much of it, comes
34/// near; past this it isn't drawn.
35const LAYER_LIMIT: u64 = 1 << 22;
36
37/// Buffer pixels a transformed layer reaches past what it samples, so the
38/// edges where tiny-skia cuts its curves stay out of the picture.
39const LAYER_MARGIN: f32 = 2.0;
40
41/// How a buffer stores each pixel, as `wl_shm` names the formats. Both are
42/// 32 bits per pixel with premultiplied alpha.
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum PixelFormat {
45  /// Bytes B, G, R, A in memory. Every compositor supports it.
46  Argb8888,
47  /// Bytes R, G, B, A in memory, as tiny-skia draws. Copied as is.
48  Abgr8888,
49}
50
51/// A buffer to draw a window into.
52pub struct Canvas<'a> {
53  /// The buffer's memory: `width * height` pixels of 4 bytes, row by row.
54  pub pixels: &'a mut [u8],
55  /// The width in pixels: the window's logical width times `scale`,
56  /// rounded halfway away from zero, as `wp_fractional_scale_v1` says.
57  /// The window's edge is drawn at the buffer's edge either way.
58  pub width: u32,
59  /// The height in pixels, likewise.
60  pub height: u32,
61  /// Physical pixels per logical pixel.
62  pub scale: f32,
63  /// How the buffer stores pixels.
64  pub format: PixelFormat,
65  /// The frame the buffer last held, updated by the draw.
66  pub age: &'a mut BufferAge,
67}
68
69/// Draws windows. One serves every window; it holds the glyph and icon
70/// caches.
71pub struct Renderer {
72  glyphs: SwashCache,
73  icons: Icons,
74  /// Where each tile is drawn before it is copied into the buffer, kept
75  /// between frames.
76  scratch: Vec<u8>,
77  work: Work,
78}
79
80/// What drawing has cost so far, for tests to check that a draw stays
81/// small.
82#[derive(Clone, Copy, Default)]
83#[cfg_attr(not(test), allow(dead_code, reason = "only tests read it"))]
84struct Work {
85  /// Pixels of opacity, clip and transform layers allocated.
86  layer_pixels: u64,
87  /// Nodes drawn.
88  nodes: u64,
89}
90
91impl Renderer {
92  /// A renderer with empty caches, looking icons up in the `hicolor`
93  /// theme.
94  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  /// The number of glyph images cached, for tests.
104  #[cfg(test)]
105  pub(crate) fn cached_glyphs(&self) -> usize {
106    self.glyphs.image_cache.len()
107  }
108
109  /// The pixels of opacity layers allocated and the nodes drawn so far,
110  /// for tests.
111  #[cfg(test)]
112  pub(crate) fn work(&self) -> (u64, u64) {
113    (self.work.layer_pixels, self.work.nodes)
114  }
115
116  /// A renderer that looks icon names up in `bases` instead of the
117  /// environment's directories, for tests.
118  #[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  /// Sets the icon theme names are looked up in.
127  pub fn set_icon_theme(&mut self, theme: impl Into<String>) {
128    self.icons.set_theme(theme.into());
129  }
130
131  /// The number of rasterized icons cached, for tests.
132  #[cfg(test)]
133  pub(crate) fn cached_icons(&self) -> usize {
134    self.icons.cached()
135  }
136
137  /// The number of times an icon was rasterized, for tests.
138  #[cfg(test)]
139  pub(crate) fn rasterized_icons(&self) -> usize {
140    self.icons.rasterized()
141  }
142
143  /// Drops the scaled frames kept for screencopy views `gone`, at once:
144  /// each is as large as its view, and an idle shell may draw nothing
145  /// for a long while.
146  pub fn forget_captures(&mut self, gone: &[u64]) {
147    self.icons.forget_captures(gone);
148  }
149
150  /// The number of screencopy frames kept scaled, for tests.
151  #[cfg(test)]
152  pub(crate) fn cached_captures(&self) -> usize {
153    self.icons.captures()
154  }
155
156  /// Draws the laid-out window `view` into `canvas` and returns the areas
157  /// to report with `wl_surface.damage_buffer`.
158  ///
159  /// `damage` is what the window reported since the last draw, from
160  /// [`Window::take_damage`](crabshell_ui::Window::take_damage). The draw
161  /// repaints that, plus what changed in frames this buffer missed
162  /// according to `history`, or the whole buffer when that is more than
163  /// half of it or the buffer is new. Text and glyphs come from `text`.
164  ///
165  /// Commit the buffer after every successful draw, in order: the history
166  /// counts each one as a frame the compositor showed.
167  ///
168  /// # Errors
169  ///
170  /// If `canvas` holds the wrong number of bytes for its size, or its
171  /// scale isn't a positive number. Nothing is drawn and the history
172  /// doesn't advance.
173  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/// A canvas whose bytes don't fit its size, or whose scale isn't a positive
246/// number.
247#[derive(Clone, Copy, Debug, PartialEq)]
248pub struct CanvasError {
249  /// The canvas's width in pixels.
250  pub width: u32,
251  /// The canvas's height in pixels.
252  pub height: u32,
253  /// The canvas's scale.
254  pub scale: f32,
255  /// How many bytes the canvas held.
256  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
281/// Copies an area drawn in RGBA into the buffer, converting byte order.
282fn 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/// Turns logical coordinates into pixels of the area being drawn.
302#[derive(Clone, Copy)]
303struct Grid {
304  scale: f32,
305  /// The window's logical size.
306  window: Size,
307  /// The buffer's size in pixels.
308  width: u32,
309  height: u32,
310  /// Where the area being drawn starts in the buffer.
311  left: f32,
312  top: f32,
313}
314
315impl Grid {
316  /// A logical x coordinate on the pixel grid. The window's right edge is
317  /// the buffer's, even where rounding would leave a column out.
318  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  /// A logical y coordinate on the pixel grid, likewise.
328  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  /// The pixels of a `width` by `height` area that `rect`, in logical
338  /// pixels, touches: rounded outward, a pixel added around for
339  /// antialiasing, and cut to the area. `None` if it misses the area.
340  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    // `as` saturates, so huge or negative values stay in range.
345    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  /// This grid for a pixmap placed at `x`, `y` within this one.
354  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  /// A logical rectangle on the pixel grid, or `None` if it covers no
363  /// pixel.
364  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
375/// What drawing needs besides the pixmap.
376struct 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  /// Draws `node` and everything inside it. With an opacity below 1, they
386  /// are drawn into a layer first, which is then blended in at that
387  /// opacity, so overlapping children don't show through each other.
388  fn node(&mut self, pixmap: &mut PixmapMut<'_>, node: NodeView<'_>) {
389    // Nothing the node or its descendants paint reaches this area.
390    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      // The layer covers only what the subtree paints in this area.
411      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  /// Draws `node` and everything inside it into a layer in its own
435  /// coordinates, then blends that in through its `transform`, filtered
436  /// bilinearly, at `opacity`: one layer for both. The layer covers only
437  /// what lands in this pixmap, in the node's own buffer pixels, so a
438  /// pixel is always drawn the same way in the same tile.
439  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    // From the node's own buffer pixels to its parent's.
459    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  /// `node`'s children, drawn into a layer that is blended in through a
547  /// mask of the inside of the node's border. The mask is drawn in the
548  /// area's own pixels, on the same fixed grid as everything else, so a
549  /// repaint cuts every edge exactly as a full draw does.
550  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  /// What `draw` paints within `cover`, a logical rectangle, blended in
572  /// through a mask of `shape`, a pixel rectangle with rounded corners.
573  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  /// A widget's rectangle on the pixel grid and its corner radius in
618  /// pixels, clamped to half the shorter side. `None` if it covers no
619  /// pixel.
620  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  /// The border's width in pixels: as in CSS, rounded down to whole
629  /// pixels, but at least one. 0 without a border.
630  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  /// The background and border of a widget's rectangle.
643  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        // A ring: the outer shape with the inner one cut out.
681        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  /// `shadow` of the box `rect` with `style`'s corners. A sharp one is a
697  /// filled shape; a blurred one is worked out pixel by pixel in buffer
698  /// pixels, so every tile agrees, and only within one and a half blurs
699  /// of its shape, past which it fades below a 255th.
700  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    // The shape in buffer pixels.
712    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    // As CSS: a corner smaller than the spread grows by less, so a square
722    // box keeps square corners.
723    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    // A cut-out shadow leaves clear what the box's background covers.
730    let cut = if shadow.cut_out {
731      self.box_mask(pixmap, rect, style)
732    } else {
733      None
734    };
735    // CSS draws a blur as a Gaussian of half its length.
736    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  /// How much of each of this pixmap's pixels the box `rect` with
790  /// `style`'s corners covers, as its background fills it.
791  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  /// `gradient` across `area`, a box in this pixmap's pixels. It is laid
802  /// out in buffer pixels and moved onto the pixmap, so every tile works
803  /// out the same color for a pixel.
804  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      // tiny-skia blends colors before their alpha, so a clear stop
822      // would fade through its own color. As in CSS, each side fades
823      // out in its neighbor's.
824      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        // As CSS: 0 degrees points up, and the line is long enough for
837        // its ends to meet the corners.
838        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  /// Glyphs, drawn as masks in the text's color. The baseline sits on a
853  /// whole pixel so horizontal strokes stay sharp; glyphs keep their
854  /// sub-pixel horizontal position.
855  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      // Rounded in buffer coordinates, so every area drawn agrees.
867      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          // Not requested: there is no subpixel antialiasing.
891          SwashContent::SubpixelMask => {}
892        }
893      }
894    }
895  }
896
897  /// A text field, cut to its content box.
898  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  /// A field's selection in a third of the text's color, the text, and a
908  /// caret in the text's color.
909  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      // At least one pixel wide whatever the scale.
930      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  /// An image, scaled to its box in pixels.
946  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  /// A screencopy view's frame, scaled to its box in pixels.
956  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  /// Where a picture in `rect` goes in the pixmap, and its size in
970  /// pixels; `None` if it covers no pixel of it. Placed and sized apart,
971  /// as an icon is, so a picture past the window's edge is cut there
972  /// instead of squeezed inside it.
973  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    // `as` saturates, so a huge box stays in range.
987    visible(pixmap, area).then_some((x as i32, y as i32, width as u32, height as u32))
988  }
989
990  /// An icon, rasterized at its size in pixels.
991  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    // Theme directories come in whole scales; 1.5 looks for @2 icons.
1002    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
1017/// Draws `pixels` unscaled with its corner at `x`, `y`.
1018fn 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
1023/// Whether any of `area` lies inside the pixmap.
1024fn 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
1041/// Adds a rectangle with circular corners of `radius` to `path`.
1042fn 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  // How far a cubic curve's control points sit along the tangents to
1048  // follow a quarter circle closely.
1049  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
1064/// `a * b / 255`, rounded.
1065fn mul(a: u8, b: u8) -> u8 {
1066  ((u16::from(a) * u16::from(b) + 127) / 255) as u8
1067}
1068
1069/// Blends a premultiplied `source` over the pixel at `index` (in bytes).
1070fn 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
1077/// The part of a `width` by `height` image placed at `left`, `top` that
1078/// lies inside the pixmap: the first and last column and row, as image
1079/// coordinates.
1080fn 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
1099/// Blends a glyph's coverage mask in `color`.
1100fn 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
1138/// How many rows a point near a corner sums.
1139const SAMPLES: usize = 8;
1140
1141/// A rectangle with rounded corners blurred by a Gaussian: how much of it
1142/// covers each point. Away from the corners it is a plain rectangle,
1143/// whose blur is the product of each axis's, from the error function.
1144/// Near a corner, each row's span is blurred across and the rows are
1145/// summed over eight samples of the Gaussian down.
1146struct 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  /// Pushes the coverage at each of `xs` on the row at `y`, out of 255,
1167  /// onto `mask`.
1168  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  /// The blur of the span from `-half` to `half` at `p`. One the blur
1195  /// stays inside of is covered whole.
1196  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  /// The rows of the shape a point at height `y` near a corner sums: for
1205  /// each sample within the shape and the Gaussian's reach, the row's
1206  /// half span and its weight.
1207  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      // How far into a corner's rows this one is, and how much it
1216      // narrows there.
1217      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
1228/// The normal distribution's density at `x`, for a deviation of `sigma`.
1229fn 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
1234/// The error function, within 5e-4, an eighth of a color level, by
1235/// Abramowitz and Stegun's 7.1.27, which needs no exponential.
1236fn 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
1243/// Whether a color glyph came from vector color layers (COLR), which swash
1244/// renders premultiplied, rather than an embedded bitmap, which it hands
1245/// over as stored: straight alpha.
1246///
1247/// cosmic-text doesn't re-export swash's `Source`, and swash isn't a
1248/// dependency of this crate, so the variant is read from its name.
1249fn is_color_outline(image: &SwashImage) -> bool {
1250  format!("{:?}", image.source).starts_with("ColorOutline")
1251}
1252
1253/// Blends a color glyph's RGBA pixels at `opacity` out of 255. `rgba` says
1254/// whether they are premultiplied already.
1255fn 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      // Premultiplied colors only need the opacity; straight ones need
1281      // their own alpha too.
1282      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}