1 //! Shared access to Cairo image surfaces.
2 use std::cmp::min;
3 use std::marker::PhantomData;
4 use std::ptr::NonNull;
5 use std::slice;
6 
7 use gdk_pixbuf::{Colorspace, Pixbuf};
8 use nalgebra::{storage::Storage, Dim, Matrix};
9 use rgb::FromSlice;
10 
11 use crate::rect::{IRect, Rect};
12 use crate::surface_utils::srgb;
13 use crate::util::clamp;
14 
15 use super::{
16     iterators::{PixelRectangle, Pixels},
17     AsCairoARGB, CairoARGB, EdgeMode, ImageSurfaceDataExt, Pixel, PixelOps, ToCairoARGB,
18     ToGdkPixbufRGBA, ToPixel,
19 };
20 
21 /// Types of pixel data in a `ImageSurface`.
22 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
23 pub enum SurfaceType {
24     /// The pixel data is in the sRGB color space.
25     SRgb,
26     /// The pixel data is in the linear sRGB color space.
27     LinearRgb,
28     /// The pixel data is alpha-only (contains meaningful data only in the alpha channel).
29     ///
30     /// A number of methods are optimized for alpha-only surfaces. For example, linearization and
31     /// unlinearization have no effect for alpha-only surfaces.
32     AlphaOnly,
33 }
34 
35 impl SurfaceType {
36     /// Combines surface types
37     ///
38     /// If combining two alpha-only surfaces, the result is alpha-only.
39     /// If one is alpha-only, the result is the other.
40     /// If none is alpha-only, the types should be the same.
41     ///
42     /// # Panics
43     /// Panics if the surface types are not alpha-only and differ.
combine(self, other: SurfaceType) -> SurfaceType44     pub fn combine(self, other: SurfaceType) -> SurfaceType {
45         match (self, other) {
46             (SurfaceType::AlphaOnly, t) => t,
47             (t, SurfaceType::AlphaOnly) => t,
48             (t1, t2) if t1 == t2 => t1,
49             _ => panic!(),
50         }
51     }
52 }
53 
54 /// Operators supported by `ImageSurface<Shared>::compose`.
55 pub enum Operator {
56     Over,
57     In,
58     Out,
59     Atop,
60     Xor,
61     Multiply,
62     Screen,
63     Darken,
64     Lighten,
65     Overlay,
66     ColorDodge,
67     ColorBurn,
68     HardLight,
69     SoftLight,
70     Difference,
71     Exclusion,
72     HslHue,
73     HslSaturation,
74     HslColor,
75     HslLuminosity,
76 }
77 
78 /// Wrapper for a Cairo image surface that enforces exclusive access when modifying it.
79 ///
80 /// Shared access to `cairo::ImageSurface` is tricky since a read-only borrowed reference
81 /// can still be cloned and then modified. We can't simply use `cairo::ImageSurface::data()`
82 /// because in the filter code we have surfaces referenced from multiple places and it would
83 /// probably add more complexity to remove that and start passing around references.
84 ///
85 /// This wrapper asserts the uniqueness of its image surface.
86 ///
87 /// It uses the typestate pattern to ensure that the surface can be modified only when
88 /// it is in the `Exclusive` state, while in the `Shared` state it only allows read-only access.
89 #[derive(Debug, Clone)]
90 pub struct ImageSurface<T> {
91     surface: cairo::ImageSurface,
92 
93     data_ptr: NonNull<u8>, // *const.
94     width: i32,
95     height: i32,
96     stride: isize,
97 
98     surface_type: SurfaceType,
99 
100     _state: PhantomData<T>,
101 }
102 
103 #[derive(Debug, Clone)]
104 pub struct Shared;
105 
106 /// Shared state of `ImageSurface`
107 pub type SharedImageSurface = ImageSurface<Shared>;
108 
109 #[derive(Debug, Clone)]
110 pub struct Exclusive;
111 
112 /// Exclusive state of `ImageSurface`
113 pub type ExclusiveImageSurface = ImageSurface<Exclusive>;
114 
115 // The access is read-only, the ref-counting on an `cairo::ImageSurface` is atomic.
116 unsafe impl Sync for SharedImageSurface {}
117 
118 /// A compile-time blur direction variable.
119 pub trait BlurDirection {
120     const IS_VERTICAL: bool;
121 }
122 
123 /// Vertical blur direction.
124 pub enum Vertical {}
125 /// Horizontal blur direction.
126 pub enum Horizontal {}
127 
128 impl BlurDirection for Vertical {
129     const IS_VERTICAL: bool = true;
130 }
131 
132 impl BlurDirection for Horizontal {
133     const IS_VERTICAL: bool = false;
134 }
135 
136 /// A compile-time alpha-only marker variable.
137 pub trait IsAlphaOnly {
138     const IS_ALPHA_ONLY: bool;
139 }
140 
141 /// Alpha-only.
142 pub enum AlphaOnly {}
143 /// Not alpha-only.
144 pub enum NotAlphaOnly {}
145 
146 /// Iterator over the rows of a `SharedImageSurface`.
147 pub struct Rows<'a> {
148     surface: &'a SharedImageSurface,
149     next_row: i32,
150 }
151 
152 /// Iterator over the mutable rows of an `ExclusiveImageSurface`.
153 pub struct RowsMut<'a> {
154     // Keep an ImageSurfaceData here instead of a raw mutable pointer to the bytes,
155     // so that the ImageSurfaceData will mark the surface as dirty when it is dropped.
156     data: cairo::ImageSurfaceData<'a>,
157 
158     width: i32,
159     height: i32,
160     stride: i32,
161 
162     next_row: i32,
163 }
164 
165 impl IsAlphaOnly for AlphaOnly {
166     const IS_ALPHA_ONLY: bool = true;
167 }
168 
169 impl IsAlphaOnly for NotAlphaOnly {
170     const IS_ALPHA_ONLY: bool = false;
171 }
172 
173 impl<T> ImageSurface<T> {
174     /// Returns the surface width.
175     #[inline]
width(&self) -> i32176     pub fn width(&self) -> i32 {
177         self.width
178     }
179 
180     /// Returns the surface height.
181     #[inline]
height(&self) -> i32182     pub fn height(&self) -> i32 {
183         self.height
184     }
185 
186     /// Returns the surface stride.
187     #[inline]
stride(&self) -> isize188     pub fn stride(&self) -> isize {
189         self.stride
190     }
191 }
192 
193 impl ImageSurface<Shared> {
194     /// Creates a `SharedImageSurface` from a unique `cairo::ImageSurface`.
195     ///
196     /// # Panics
197     /// Panics if the surface format isn't `ARgb32` and if the surface is not unique, that is, its
198     /// reference count isn't 1.
199     #[inline]
wrap( surface: cairo::ImageSurface, surface_type: SurfaceType, ) -> Result<SharedImageSurface, cairo::Error>200     pub fn wrap(
201         surface: cairo::ImageSurface,
202         surface_type: SurfaceType,
203     ) -> Result<SharedImageSurface, cairo::Error> {
204         // get_pixel() assumes ARgb32.
205         assert_eq!(surface.format(), cairo::Format::ARgb32);
206 
207         let reference_count =
208             unsafe { cairo::ffi::cairo_surface_get_reference_count(surface.to_raw_none()) };
209         assert_eq!(reference_count, 1);
210 
211         let (width, height) = (surface.width(), surface.height());
212 
213         // Cairo allows zero-sized surfaces, but it does malloc(0), whose result
214         // is implementation-defined.  So, we can't assume NonNull below.  This is
215         // why we disallow zero-sized surfaces here.
216         assert!(width > 0 && height > 0);
217 
218         surface.flush();
219 
220         let data_ptr = NonNull::new(unsafe {
221             cairo::ffi::cairo_image_surface_get_data(surface.to_raw_none())
222         })
223         .unwrap();
224 
225         let stride = surface.stride() as isize;
226 
227         Ok(SharedImageSurface {
228             surface,
229             data_ptr,
230             width,
231             height,
232             stride,
233             surface_type,
234             _state: PhantomData,
235         })
236     }
237 
238     /// Creates a `SharedImageSurface` copying from a `cairo::ImageSurface`, even if it
239     /// does not have a reference count of 1.
240     #[inline]
copy_from_surface(surface: &cairo::ImageSurface) -> Result<Self, cairo::Error>241     pub fn copy_from_surface(surface: &cairo::ImageSurface) -> Result<Self, cairo::Error> {
242         let copy =
243             cairo::ImageSurface::create(cairo::Format::ARgb32, surface.width(), surface.height())?;
244 
245         {
246             let cr = cairo::Context::new(&copy)?;
247             cr.set_source_surface(surface, 0f64, 0f64)?;
248             cr.paint()?;
249         }
250 
251         SharedImageSurface::wrap(copy, SurfaceType::SRgb)
252     }
253 
254     /// Creates an empty `SharedImageSurface` of the given size and `type`.
255     #[inline]
empty(width: i32, height: i32, surface_type: SurfaceType) -> Result<Self, cairo::Error>256     pub fn empty(width: i32, height: i32, surface_type: SurfaceType) -> Result<Self, cairo::Error> {
257         let s = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
258 
259         SharedImageSurface::wrap(s, surface_type)
260     }
261 
262     /// Converts this `SharedImageSurface` back into a Cairo image surface.
263     #[inline]
into_image_surface(self) -> Result<cairo::ImageSurface, cairo::Error>264     pub fn into_image_surface(self) -> Result<cairo::ImageSurface, cairo::Error> {
265         let reference_count =
266             unsafe { cairo::ffi::cairo_surface_get_reference_count(self.surface.to_raw_none()) };
267 
268         if reference_count == 1 {
269             Ok(self.surface)
270         } else {
271             // If there are any other references, copy the underlying surface.
272             self.copy_surface(IRect::from_size(self.width, self.height))
273         }
274     }
275 
from_pixbuf( pixbuf: &Pixbuf, content_type: Option<&str>, mime_data: Option<Vec<u8>>, ) -> Result<SharedImageSurface, cairo::Error>276     pub fn from_pixbuf(
277         pixbuf: &Pixbuf,
278         content_type: Option<&str>,
279         mime_data: Option<Vec<u8>>,
280     ) -> Result<SharedImageSurface, cairo::Error> {
281         assert!(pixbuf.colorspace() == Colorspace::Rgb);
282         assert!(pixbuf.bits_per_sample() == 8);
283 
284         let n_channels = pixbuf.n_channels();
285         assert!(n_channels == 3 || n_channels == 4);
286         let has_alpha = n_channels == 4;
287 
288         let width = pixbuf.width();
289         let height = pixbuf.height();
290         let stride = pixbuf.rowstride() as usize;
291         assert!(width > 0 && height > 0 && stride > 0);
292 
293         let pixbuf_data = unsafe { pixbuf.pixels() };
294 
295         let mut surf = ExclusiveImageSurface::new(width, height, SurfaceType::SRgb)?;
296 
297         // We use chunks(), not chunks_exact(), because gdk-pixbuf tends
298         // to make the last row *not* have the full stride (i.e. it is
299         // only as wide as the pixels in that row).
300         let pixbuf_rows = pixbuf_data.chunks(stride).take(height as usize);
301 
302         if has_alpha {
303             pixbuf_rows
304                 .map(|row| row.as_rgba())
305                 .zip(surf.rows_mut())
306                 .flat_map(|(src_row, dest_row)| src_row.iter().zip(dest_row.iter_mut()))
307                 .for_each(|(src, dest)| *dest = src.to_pixel().premultiply().to_cairo_argb());
308         } else {
309             pixbuf_rows
310                 .map(|row| row.as_rgb())
311                 .zip(surf.rows_mut())
312                 .flat_map(|(src_row, dest_row)| src_row.iter().zip(dest_row.iter_mut()))
313                 .for_each(|(src, dest)| *dest = src.to_pixel().to_cairo_argb());
314         }
315 
316         if let (Some(content_type), Some(bytes)) = (content_type, mime_data) {
317             surf.surface.set_mime_data(content_type, bytes)?;
318         }
319 
320         surf.share()
321     }
322 
to_pixbuf(&self) -> Option<Pixbuf>323     pub fn to_pixbuf(&self) -> Option<Pixbuf> {
324         let width = self.width();
325         let height = self.height();
326 
327         let pixbuf = Pixbuf::new(Colorspace::Rgb, true, 8, width, height)?;
328 
329         assert!(pixbuf.colorspace() == Colorspace::Rgb);
330         assert!(pixbuf.bits_per_sample() == 8);
331         assert!(pixbuf.n_channels() == 4);
332 
333         let pixbuf_data = unsafe { pixbuf.pixels() };
334         let stride = pixbuf.rowstride() as usize;
335 
336         // We use chunks_mut(), not chunks_exact_mut(), because gdk-pixbuf tends
337         // to make the last row *not* have the full stride (i.e. it is
338         // only as wide as the pixels in that row).
339         pixbuf_data
340             .chunks_mut(stride)
341             .take(height as usize)
342             .map(|row| row.as_rgba_mut())
343             .zip(self.rows())
344             .flat_map(|(dest_row, src_row)| src_row.iter().zip(dest_row.iter_mut()))
345             .for_each(|(src, dest)| *dest = src.to_pixel().unpremultiply().to_pixbuf_rgba());
346 
347         Some(pixbuf)
348     }
349 
350     /// Returns `true` if the surface contains meaningful data only in the alpha channel.
351     #[inline]
is_alpha_only(&self) -> bool352     fn is_alpha_only(&self) -> bool {
353         self.surface_type == SurfaceType::AlphaOnly
354     }
355 
356     /// Returns the type of this surface.
357     #[inline]
surface_type(&self) -> SurfaceType358     pub fn surface_type(&self) -> SurfaceType {
359         self.surface_type
360     }
361 
362     /// Retrieves the pixel value at the given coordinates.
363     #[inline]
get_pixel(&self, x: u32, y: u32) -> Pixel364     pub fn get_pixel(&self, x: u32, y: u32) -> Pixel {
365         assert!(x < self.width as u32);
366         assert!(y < self.height as u32);
367 
368         #[allow(clippy::cast_ptr_alignment)]
369         let value = unsafe {
370             *(self
371                 .data_ptr
372                 .as_ptr()
373                 .offset(y as isize * self.stride + x as isize * 4) as *const u32)
374         };
375 
376         Pixel::from_u32(value)
377     }
378 
379     /// Retrieves the pixel value by offset into the pixel data array.
380     #[inline]
get_pixel_by_offset(&self, offset: isize) -> Pixel381     pub fn get_pixel_by_offset(&self, offset: isize) -> Pixel {
382         assert!(offset < self.stride * self.height as isize);
383 
384         #[allow(clippy::cast_ptr_alignment)]
385         let value = unsafe { *(self.data_ptr.as_ptr().offset(offset) as *const u32) };
386         Pixel::from_u32(value)
387     }
388 
389     /// Calls `set_source_surface()` on the given Cairo context.
390     #[inline]
set_as_source_surface( &self, cr: &cairo::Context, x: f64, y: f64, ) -> Result<(), cairo::Error>391     pub fn set_as_source_surface(
392         &self,
393         cr: &cairo::Context,
394         x: f64,
395         y: f64,
396     ) -> Result<(), cairo::Error> {
397         cr.set_source_surface(&self.surface, x, y)
398     }
399 
400     /// Creates a Cairo surface pattern from the surface
to_cairo_pattern(&self) -> cairo::SurfacePattern401     pub fn to_cairo_pattern(&self) -> cairo::SurfacePattern {
402         cairo::SurfacePattern::create(&self.surface)
403     }
404 
405     /// Returns a new `cairo::ImageSurface` with the same contents as the one stored in this
406     /// `SharedImageSurface` within the given bounds.
copy_surface(&self, bounds: IRect) -> Result<cairo::ImageSurface, cairo::Error>407     fn copy_surface(&self, bounds: IRect) -> Result<cairo::ImageSurface, cairo::Error> {
408         let output_surface =
409             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
410 
411         let cr = cairo::Context::new(&output_surface)?;
412         let r = cairo::Rectangle::from(bounds);
413         cr.rectangle(r.x, r.y, r.width, r.height);
414         cr.clip();
415 
416         cr.set_source_surface(&self.surface, 0f64, 0f64)?;
417         cr.paint()?;
418 
419         Ok(output_surface)
420     }
421 
422     /// Scales the given surface by `x` and `y` into a surface `width`×`height` in size, clipped by
423     /// `bounds`.
scale_to( &self, width: i32, height: i32, bounds: IRect, x: f64, y: f64, ) -> Result<SharedImageSurface, cairo::Error>424     pub fn scale_to(
425         &self,
426         width: i32,
427         height: i32,
428         bounds: IRect,
429         x: f64,
430         y: f64,
431     ) -> Result<SharedImageSurface, cairo::Error> {
432         let output_surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
433 
434         {
435             let cr = cairo::Context::new(&output_surface)?;
436             let r = cairo::Rectangle::from(bounds);
437             cr.rectangle(r.x, r.y, r.width, r.height);
438             cr.clip();
439 
440             cr.scale(x, y);
441             self.set_as_source_surface(&cr, 0.0, 0.0)?;
442             cr.paint()?;
443         }
444 
445         SharedImageSurface::wrap(output_surface, self.surface_type)
446     }
447 
448     /// Returns a scaled version of a surface and bounds.
449     #[inline]
scale( &self, bounds: IRect, x: f64, y: f64, ) -> Result<(SharedImageSurface, IRect), cairo::Error>450     pub fn scale(
451         &self,
452         bounds: IRect,
453         x: f64,
454         y: f64,
455     ) -> Result<(SharedImageSurface, IRect), cairo::Error> {
456         let new_width = (f64::from(self.width) * x).ceil() as i32;
457         let new_height = (f64::from(self.height) * y).ceil() as i32;
458         let new_bounds = bounds.scale(x, y);
459 
460         Ok((
461             self.scale_to(new_width, new_height, new_bounds, x, y)?,
462             new_bounds,
463         ))
464     }
465 
466     /// Returns a surface with black background and alpha channel matching this surface.
extract_alpha(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error>467     pub fn extract_alpha(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
468         let mut output_surface =
469             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
470 
471         let output_stride = output_surface.stride() as usize;
472         {
473             let mut output_data = output_surface.data().unwrap();
474 
475             for (x, y, Pixel { a, .. }) in Pixels::within(self, bounds) {
476                 let output_pixel = Pixel {
477                     r: 0,
478                     g: 0,
479                     b: 0,
480                     a,
481                 };
482                 output_data.set_pixel(output_stride, output_pixel, x, y);
483             }
484         }
485 
486         SharedImageSurface::wrap(output_surface, SurfaceType::AlphaOnly)
487     }
488 
489     /// Returns a surface whose alpha channel for each pixel is equal to the
490     /// luminance of that pixel's unpremultiplied RGB values.  The resulting
491     /// surface's RGB values are not meanignful; only the alpha channel has
492     /// useful luminance data.
493     ///
494     /// This is to get a mask suitable for use with cairo_mask_surface().
to_luminance_mask(&self) -> Result<SharedImageSurface, cairo::Error>495     pub fn to_luminance_mask(&self) -> Result<SharedImageSurface, cairo::Error> {
496         let bounds = IRect::from_size(self.width, self.height);
497 
498         let mut output_surface =
499             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
500 
501         let stride = output_surface.stride() as usize;
502         {
503             let mut data = output_surface.data().unwrap();
504 
505             for (x, y, pixel) in Pixels::within(self, bounds) {
506                 data.set_pixel(stride, pixel.to_luminance_mask(), x, y);
507             }
508         }
509 
510         SharedImageSurface::wrap(output_surface, self.surface_type)
511     }
512 
513     /// Returns a surface with pre-multiplication of color values undone.
514     ///
515     /// HACK: this is storing unpremultiplied pixels in an ARGB32 image surface (which is supposed
516     /// to be premultiplied pixels).
unpremultiply(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error>517     pub fn unpremultiply(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
518         // Unpremultiplication doesn't affect the alpha channel.
519         if self.is_alpha_only() {
520             return Ok(self.clone());
521         }
522 
523         let mut output_surface =
524             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
525 
526         let stride = output_surface.stride() as usize;
527         {
528             let mut data = output_surface.data().unwrap();
529 
530             for (x, y, pixel) in Pixels::within(self, bounds) {
531                 data.set_pixel(stride, pixel.unpremultiply(), x, y);
532             }
533         }
534 
535         SharedImageSurface::wrap(output_surface, self.surface_type)
536     }
537 
538     /// Converts the surface to the linear sRGB color space.
539     #[inline]
to_linear_rgb(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error>540     pub fn to_linear_rgb(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
541         match self.surface_type {
542             SurfaceType::LinearRgb | SurfaceType::AlphaOnly => Ok(self.clone()),
543             _ => srgb::linearize_surface(self, bounds),
544         }
545     }
546 
547     /// Converts the surface to the sRGB color space.
548     #[inline]
to_srgb(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error>549     pub fn to_srgb(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
550         match self.surface_type {
551             SurfaceType::SRgb | SurfaceType::AlphaOnly => Ok(self.clone()),
552             _ => srgb::unlinearize_surface(self, bounds),
553         }
554     }
555 
556     /// Performs a convolution.
557     ///
558     /// Note that `kernel` is rotated 180 degrees.
559     ///
560     /// The `target` parameter determines the position of the kernel relative to each pixel of the
561     /// image. The value of `(0, 0)` indicates that the top left pixel of the (180-degrees-rotated)
562     /// kernel corresponds to the current pixel, and the rest of the kernel is to the right and
563     /// bottom of the pixel. The value of `(cols / 2, rows / 2)` centers a kernel with an odd
564     /// number of rows and columns.
565     ///
566     /// # Panics
567     /// Panics if `kernel` has zero rows or columns.
convolve<R: Dim, C: Dim, S: Storage<f64, R, C>>( &self, bounds: IRect, target: (i32, i32), kernel: &Matrix<f64, R, C, S>, edge_mode: EdgeMode, ) -> Result<SharedImageSurface, cairo::Error>568     pub fn convolve<R: Dim, C: Dim, S: Storage<f64, R, C>>(
569         &self,
570         bounds: IRect,
571         target: (i32, i32),
572         kernel: &Matrix<f64, R, C, S>,
573         edge_mode: EdgeMode,
574     ) -> Result<SharedImageSurface, cairo::Error> {
575         assert!(kernel.nrows() >= 1);
576         assert!(kernel.ncols() >= 1);
577 
578         let mut output_surface =
579             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
580 
581         let output_stride = output_surface.stride() as usize;
582         {
583             let mut output_data = output_surface.data().unwrap();
584 
585             if self.is_alpha_only() {
586                 for (x, y, _pixel) in Pixels::within(self, bounds) {
587                     let kernel_bounds = IRect::new(
588                         x as i32 - target.0,
589                         y as i32 - target.1,
590                         x as i32 - target.0 + kernel.ncols() as i32,
591                         y as i32 - target.1 + kernel.nrows() as i32,
592                     );
593 
594                     let mut a = 0.0;
595 
596                     for (x, y, pixel) in
597                         PixelRectangle::within(self, bounds, kernel_bounds, edge_mode)
598                     {
599                         let kernel_x = (kernel_bounds.x1 - x - 1) as usize;
600                         let kernel_y = (kernel_bounds.y1 - y - 1) as usize;
601                         let factor = kernel[(kernel_y, kernel_x)];
602 
603                         a += f64::from(pixel.a) * factor;
604                     }
605 
606                     let convert = |x: f64| (clamp(x, 0.0, 255.0) + 0.5) as u8;
607 
608                     let output_pixel = Pixel {
609                         r: 0,
610                         g: 0,
611                         b: 0,
612                         a: convert(a),
613                     };
614 
615                     output_data.set_pixel(output_stride, output_pixel, x, y);
616                 }
617             } else {
618                 for (x, y, _pixel) in Pixels::within(self, bounds) {
619                     let kernel_bounds = IRect::new(
620                         x as i32 - target.0,
621                         y as i32 - target.1,
622                         x as i32 - target.0 + kernel.ncols() as i32,
623                         y as i32 - target.1 + kernel.nrows() as i32,
624                     );
625 
626                     let mut r = 0.0;
627                     let mut g = 0.0;
628                     let mut b = 0.0;
629                     let mut a = 0.0;
630 
631                     for (x, y, pixel) in
632                         PixelRectangle::within(self, bounds, kernel_bounds, edge_mode)
633                     {
634                         let kernel_x = (kernel_bounds.x1 - x - 1) as usize;
635                         let kernel_y = (kernel_bounds.y1 - y - 1) as usize;
636                         let factor = kernel[(kernel_y, kernel_x)];
637 
638                         r += f64::from(pixel.r) * factor;
639                         g += f64::from(pixel.g) * factor;
640                         b += f64::from(pixel.b) * factor;
641                         a += f64::from(pixel.a) * factor;
642                     }
643 
644                     let convert = |x: f64| (clamp(x, 0.0, 255.0) + 0.5) as u8;
645 
646                     let output_pixel = Pixel {
647                         r: convert(r),
648                         g: convert(g),
649                         b: convert(b),
650                         a: convert(a),
651                     };
652 
653                     output_data.set_pixel(output_stride, output_pixel, x, y);
654                 }
655             }
656         }
657 
658         SharedImageSurface::wrap(output_surface, self.surface_type)
659     }
660 
661     /// Performs a horizontal or vertical box blur.
662     ///
663     /// The `target` parameter determines the position of the kernel relative to each pixel of the
664     /// image. The value of `0` indicates that the first pixel of the kernel corresponds to the
665     /// current pixel, and the rest of the kernel is to the right or bottom of the pixel. The value
666     /// of `kernel_size / 2` centers a kernel with an odd size.
667     ///
668     /// # Panics
669     /// Panics if `kernel_size` is `0` or if `target >= kernel_size`.
670     // This is public (and not inlined into box_blur()) for the purpose of accessing it from the
671     // benchmarks.
box_blur_loop<B: BlurDirection, A: IsAlphaOnly>( &self, output_surface: &mut cairo::ImageSurface, bounds: IRect, kernel_size: usize, target: usize, )672     pub fn box_blur_loop<B: BlurDirection, A: IsAlphaOnly>(
673         &self,
674         output_surface: &mut cairo::ImageSurface,
675         bounds: IRect,
676         kernel_size: usize,
677         target: usize,
678     ) {
679         assert_ne!(kernel_size, 0);
680         assert!(target < kernel_size);
681         assert_eq!(self.is_alpha_only(), A::IS_ALPHA_ONLY);
682 
683         {
684             // The following code is needed for a parallel implementation of the blur loop. The
685             // blurring is done either for each row or for each column of pixels, depending on the
686             // value of `vertical`, independently of the others. Naturally, we want to run the
687             // outer loop on a thread pool.
688             //
689             // The case of `vertical == false` is simple since the input image slice can be
690             // partitioned into chunks for each row of pixels and processed in parallel with rayon.
691             // The case of `vertical == true`, however, is more involved because we can't just make
692             // mutable slices for all pixel columns (they would be overlapping which is forbidden
693             // by the aliasing rules).
694             //
695             // This is where the following struct comes into play: it stores a sub-slice of the
696             // pixel data and can be split at any row or column into two parts (similar to
697             // slice::split_at_mut()).
698             struct UnsafeSendPixelData<'a> {
699                 width: u32,
700                 height: u32,
701                 stride: isize,
702                 ptr: NonNull<u8>,
703                 _marker: PhantomData<&'a mut ()>,
704             }
705 
706             unsafe impl<'a> Send for UnsafeSendPixelData<'a> {}
707 
708             impl<'a> UnsafeSendPixelData<'a> {
709                 /// Creates a new `UnsafeSendPixelData`.
710                 ///
711                 /// # Safety
712                 /// You must call `cairo_surface_mark_dirty()` on the surface once all instances of
713                 /// `UnsafeSendPixelData` are dropped to make sure the pixel changes are committed
714                 /// to Cairo.
715                 #[inline]
716                 unsafe fn new(surface: &mut cairo::ImageSurface) -> Self {
717                     assert_eq!(surface.format(), cairo::Format::ARgb32);
718                     let ptr = surface.data().unwrap().as_mut_ptr();
719 
720                     Self {
721                         width: surface.width() as u32,
722                         height: surface.height() as u32,
723                         stride: surface.stride() as isize,
724                         ptr: NonNull::new(ptr).unwrap(),
725                         _marker: PhantomData,
726                     }
727                 }
728 
729                 /// Sets a pixel value at the given coordinates.
730                 #[inline]
731                 fn set_pixel(&mut self, pixel: Pixel, x: u32, y: u32) {
732                     assert!(x < self.width);
733                     assert!(y < self.height);
734 
735                     let value = pixel.to_u32();
736 
737                     #[allow(clippy::cast_ptr_alignment)]
738                     unsafe {
739                         let ptr = self
740                             .ptr
741                             .as_ptr()
742                             .offset(y as isize * self.stride + x as isize * 4)
743                             as *mut u32;
744                         *ptr = value;
745                     }
746                 }
747 
748                 /// Splits this `UnsafeSendPixelData` into two at the given row.
749                 ///
750                 /// The first one contains rows `0..index` (`index` not included) and the second one
751                 /// contains rows `index..height`.
752                 #[inline]
753                 fn split_at_row(self, index: u32) -> (Self, Self) {
754                     assert!(index <= self.height);
755 
756                     (
757                         UnsafeSendPixelData {
758                             width: self.width,
759                             height: index,
760                             stride: self.stride,
761                             ptr: self.ptr,
762                             _marker: PhantomData,
763                         },
764                         UnsafeSendPixelData {
765                             width: self.width,
766                             height: self.height - index,
767                             stride: self.stride,
768                             ptr: NonNull::new(unsafe {
769                                 self.ptr.as_ptr().offset(index as isize * self.stride)
770                             })
771                             .unwrap(),
772                             _marker: PhantomData,
773                         },
774                     )
775                 }
776 
777                 /// Splits this `UnsafeSendPixelData` into two at the given column.
778                 ///
779                 /// The first one contains columns `0..index` (`index` not included) and the second
780                 /// one contains columns `index..width`.
781                 #[inline]
782                 fn split_at_column(self, index: u32) -> (Self, Self) {
783                     assert!(index <= self.width);
784 
785                     (
786                         UnsafeSendPixelData {
787                             width: index,
788                             height: self.height,
789                             stride: self.stride,
790                             ptr: self.ptr,
791                             _marker: PhantomData,
792                         },
793                         UnsafeSendPixelData {
794                             width: self.width - index,
795                             height: self.height,
796                             stride: self.stride,
797                             ptr: NonNull::new(unsafe {
798                                 self.ptr.as_ptr().offset(index as isize * 4)
799                             })
800                             .unwrap(),
801                             _marker: PhantomData,
802                         },
803                     )
804                 }
805             }
806 
807             let output_data = unsafe { UnsafeSendPixelData::new(output_surface) };
808 
809             // Shift is target into the opposite direction.
810             let shift = (kernel_size - target) as i32;
811             let target = target as i32;
812 
813             // Convert to f64 once since we divide by it.
814             let kernel_size_f64 = kernel_size as f64;
815             let compute = |x: u32| (f64::from(x) / kernel_size_f64 + 0.5) as u8;
816 
817             // Depending on `vertical`, we're blurring either horizontally line-by-line, or
818             // vertically column-by-column. In the code below, the main axis is the axis along
819             // which the blurring happens (so if `vertical` is false, the main axis is the
820             // horizontal axis). The other axis is the outer loop axis. The code uses `i` and `j`
821             // for the other axis and main axis coordinates, respectively.
822             let (main_axis_min, main_axis_max, other_axis_min, other_axis_max) = if B::IS_VERTICAL {
823                 (bounds.y0, bounds.y1, bounds.x0, bounds.x1)
824             } else {
825                 (bounds.x0, bounds.x1, bounds.y0, bounds.y1)
826             };
827 
828             // Helper function for getting the pixels.
829             let pixel = |i, j| {
830                 let (x, y) = if B::IS_VERTICAL { (i, j) } else { (j, i) };
831 
832                 self.get_pixel(x as u32, y as u32)
833             };
834 
835             // The following loop assumes the first row or column of `output_data` is the first row
836             // or column inside `bounds`.
837             let mut output_data = if B::IS_VERTICAL {
838                 output_data.split_at_column(bounds.x0 as u32).1
839             } else {
840                 output_data.split_at_row(bounds.y0 as u32).1
841             };
842 
843             rayon::scope(|s| {
844                 for i in other_axis_min..other_axis_max {
845                     // Split off one row or column and launch its processing on another thread.
846                     // Thanks to the initial split before the loop, there's no special case for the
847                     // very first split.
848                     let (mut current, remaining) = if B::IS_VERTICAL {
849                         output_data.split_at_column(1)
850                     } else {
851                         output_data.split_at_row(1)
852                     };
853 
854                     output_data = remaining;
855 
856                     s.spawn(move |_| {
857                         // Helper function for setting the pixels.
858                         let mut set_pixel = |j, pixel| {
859                             // We're processing rows or columns one-by-one, so the other coordinate
860                             // is always 0.
861                             let (x, y) = if B::IS_VERTICAL { (0, j) } else { (j, 0) };
862                             current.set_pixel(pixel, x, y);
863                         };
864 
865                         // The idea is that since all weights of the box blur kernel are equal, for
866                         // each step along the main axis, instead of recomputing the full sum, we
867                         // can take the previous sum, subtract the "oldest" pixel value and add the
868                         // "newest" pixel value.
869                         //
870                         // The sum is u32 so that it can fit MAXIMUM_KERNEL_SIZE * 255.
871                         let mut sum_r = 0;
872                         let mut sum_g = 0;
873                         let mut sum_b = 0;
874                         let mut sum_a = 0;
875 
876                         // The whole sum needs to be computed for the first pixel. However, we know
877                         // that values outside of bounds are transparent, so the loop starts on the
878                         // first pixel in bounds.
879                         for j in main_axis_min..min(main_axis_max, main_axis_min + shift) {
880                             let Pixel { r, g, b, a } = pixel(i, j);
881 
882                             if !A::IS_ALPHA_ONLY {
883                                 sum_r += u32::from(r);
884                                 sum_g += u32::from(g);
885                                 sum_b += u32::from(b);
886                             }
887 
888                             sum_a += u32::from(a);
889                         }
890 
891                         set_pixel(
892                             main_axis_min as u32,
893                             Pixel {
894                                 r: compute(sum_r),
895                                 g: compute(sum_g),
896                                 b: compute(sum_b),
897                                 a: compute(sum_a),
898                             },
899                         );
900 
901                         // Now, go through all the other pixels.
902                         //
903                         // j - target - 1 >= main_axis_min
904                         // j >= main_axis_min + target + 1
905                         let start_subtracting_at = main_axis_min + target + 1;
906 
907                         // j + shift - 1 < main_axis_max
908                         // j < main_axis_max - shift + 1
909                         let stop_adding_at = main_axis_max - shift + 1;
910 
911                         for j in main_axis_min + 1..main_axis_max {
912                             if j >= start_subtracting_at {
913                                 let old_pixel = pixel(i, j - target - 1);
914 
915                                 if !A::IS_ALPHA_ONLY {
916                                     sum_r -= u32::from(old_pixel.r);
917                                     sum_g -= u32::from(old_pixel.g);
918                                     sum_b -= u32::from(old_pixel.b);
919                                 }
920 
921                                 sum_a -= u32::from(old_pixel.a);
922                             }
923 
924                             if j < stop_adding_at {
925                                 let new_pixel = pixel(i, j + shift - 1);
926 
927                                 if !A::IS_ALPHA_ONLY {
928                                     sum_r += u32::from(new_pixel.r);
929                                     sum_g += u32::from(new_pixel.g);
930                                     sum_b += u32::from(new_pixel.b);
931                                 }
932 
933                                 sum_a += u32::from(new_pixel.a);
934                             }
935 
936                             set_pixel(
937                                 j as u32,
938                                 Pixel {
939                                     r: compute(sum_r),
940                                     g: compute(sum_g),
941                                     b: compute(sum_b),
942                                     a: compute(sum_a),
943                                 },
944                             );
945                         }
946                     });
947                 }
948             });
949         }
950 
951         // Don't forget to manually mark the surface as dirty (due to usage of
952         // `UnsafeSendPixelData`).
953         unsafe { cairo::ffi::cairo_surface_mark_dirty(output_surface.to_raw_none()) }
954     }
955 
956     /// Performs a horizontal or vertical box blur.
957     ///
958     /// The `target` parameter determines the position of the kernel relative to each pixel of the
959     /// image. The value of `0` indicates that the first pixel of the kernel corresponds to the
960     /// current pixel, and the rest of the kernel is to the right or bottom of the pixel. The value
961     /// of `kernel_size / 2` centers a kernel with an odd size.
962     ///
963     /// # Panics
964     /// Panics if `kernel_size` is `0` or if `target >= kernel_size`.
965     #[inline]
box_blur<B: BlurDirection>( &self, bounds: IRect, kernel_size: usize, target: usize, ) -> Result<SharedImageSurface, cairo::Error>966     pub fn box_blur<B: BlurDirection>(
967         &self,
968         bounds: IRect,
969         kernel_size: usize,
970         target: usize,
971     ) -> Result<SharedImageSurface, cairo::Error> {
972         let mut output_surface =
973             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
974 
975         if self.is_alpha_only() {
976             self.box_blur_loop::<B, AlphaOnly>(&mut output_surface, bounds, kernel_size, target);
977         } else {
978             self.box_blur_loop::<B, NotAlphaOnly>(&mut output_surface, bounds, kernel_size, target);
979         }
980 
981         SharedImageSurface::wrap(output_surface, self.surface_type)
982     }
983 
984     /// Fills the with a specified color.
985     #[inline]
flood( &self, bounds: IRect, color: cssparser::RGBA, ) -> Result<SharedImageSurface, cairo::Error>986     pub fn flood(
987         &self,
988         bounds: IRect,
989         color: cssparser::RGBA,
990     ) -> Result<SharedImageSurface, cairo::Error> {
991         let output_surface =
992             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
993 
994         if color.alpha > 0 {
995             let cr = cairo::Context::new(&output_surface)?;
996             let r = cairo::Rectangle::from(bounds);
997             cr.rectangle(r.x, r.y, r.width, r.height);
998             cr.clip();
999 
1000             cr.set_source_rgba(
1001                 f64::from(color.red_f32()),
1002                 f64::from(color.green_f32()),
1003                 f64::from(color.blue_f32()),
1004                 f64::from(color.alpha_f32()),
1005             );
1006             cr.paint()?;
1007         }
1008 
1009         SharedImageSurface::wrap(output_surface, self.surface_type)
1010     }
1011 
1012     /// Offsets the image of the specified amount.
1013     #[inline]
offset( &self, bounds: IRect, dx: f64, dy: f64, ) -> Result<SharedImageSurface, cairo::Error>1014     pub fn offset(
1015         &self,
1016         bounds: IRect,
1017         dx: f64,
1018         dy: f64,
1019     ) -> Result<SharedImageSurface, cairo::Error> {
1020         let output_surface =
1021             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
1022 
1023         // output_bounds contains all pixels within bounds,
1024         // for which (x - ox) and (y - oy) also lie within bounds.
1025         if let Some(output_bounds) = bounds
1026             .translate((dx as i32, dy as i32))
1027             .intersection(&bounds)
1028         {
1029             let cr = cairo::Context::new(&output_surface)?;
1030             let r = cairo::Rectangle::from(output_bounds);
1031             cr.rectangle(r.x, r.y, r.width, r.height);
1032             cr.clip();
1033 
1034             self.set_as_source_surface(&cr, dx, dy)?;
1035             cr.paint()?;
1036         }
1037 
1038         SharedImageSurface::wrap(output_surface, self.surface_type)
1039     }
1040 
1041     /// Returns a new surface of the same size, with the contents of the
1042     /// specified image, optionally transformed to match a given box
1043     #[inline]
paint_image( &self, bounds: Rect, image: &SharedImageSurface, rect: Option<Rect>, ) -> Result<SharedImageSurface, cairo::Error>1044     pub fn paint_image(
1045         &self,
1046         bounds: Rect,
1047         image: &SharedImageSurface,
1048         rect: Option<Rect>,
1049     ) -> Result<SharedImageSurface, cairo::Error> {
1050         let output_surface =
1051             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
1052 
1053         if rect.is_none() || !rect.unwrap().is_empty() {
1054             let cr = cairo::Context::new(&output_surface)?;
1055             let r = cairo::Rectangle::from(bounds);
1056             cr.rectangle(r.x, r.y, r.width, r.height);
1057             cr.clip();
1058 
1059             image.set_as_source_surface(&cr, 0f64, 0f64)?;
1060 
1061             if let Some(rect) = rect {
1062                 let mut matrix = cairo::Matrix::new(
1063                     rect.width() / f64::from(image.width()),
1064                     0.0,
1065                     0.0,
1066                     rect.height() / f64::from(image.height()),
1067                     rect.x0,
1068                     rect.y0,
1069                 );
1070                 matrix.invert();
1071 
1072                 cr.source().set_matrix(matrix);
1073             }
1074 
1075             cr.paint()?;
1076         }
1077 
1078         SharedImageSurface::wrap(output_surface, image.surface_type)
1079     }
1080 
1081     /// Creates a new surface with the size and content specified in `bounds`
1082     #[inline]
tile(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error>1083     pub fn tile(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
1084         let output_surface =
1085             cairo::ImageSurface::create(cairo::Format::ARgb32, bounds.width(), bounds.height())?;
1086 
1087         {
1088             let cr = cairo::Context::new(&output_surface)?;
1089             self.set_as_source_surface(&cr, f64::from(-bounds.x0), f64::from(-bounds.y0))?;
1090             cr.paint()?;
1091         }
1092 
1093         SharedImageSurface::wrap(output_surface, self.surface_type)
1094     }
1095 
1096     /// Returns a new surface of the same size, with the contents of the specified
1097     /// image repeated to fill the bounds and starting from the given position.
1098     #[inline]
paint_image_tiled( &self, bounds: IRect, image: &SharedImageSurface, x: i32, y: i32, ) -> Result<SharedImageSurface, cairo::Error>1099     pub fn paint_image_tiled(
1100         &self,
1101         bounds: IRect,
1102         image: &SharedImageSurface,
1103         x: i32,
1104         y: i32,
1105     ) -> Result<SharedImageSurface, cairo::Error> {
1106         let output_surface =
1107             cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
1108 
1109         {
1110             let cr = cairo::Context::new(&output_surface)?;
1111 
1112             let ptn = image.to_cairo_pattern();
1113             ptn.set_extend(cairo::Extend::Repeat);
1114             let mut mat = cairo::Matrix::identity();
1115             mat.translate(f64::from(-x), f64::from(-y));
1116             ptn.set_matrix(mat);
1117 
1118             let r = cairo::Rectangle::from(bounds);
1119             cr.rectangle(r.x, r.y, r.width, r.height);
1120             cr.clip();
1121 
1122             cr.set_source(&ptn)?;
1123             cr.paint()?;
1124         }
1125 
1126         SharedImageSurface::wrap(output_surface, image.surface_type)
1127     }
1128 
1129     /// Performs the combination of two input surfaces using Porter-Duff
1130     /// compositing operators.
1131     ///
1132     /// # Panics
1133     /// Panics if the two surface types are not compatible.
1134     #[inline]
compose( &self, other: &SharedImageSurface, bounds: IRect, operator: Operator, ) -> Result<SharedImageSurface, cairo::Error>1135     pub fn compose(
1136         &self,
1137         other: &SharedImageSurface,
1138         bounds: IRect,
1139         operator: Operator,
1140     ) -> Result<SharedImageSurface, cairo::Error> {
1141         let output_surface = other.copy_surface(bounds)?;
1142 
1143         {
1144             let cr = cairo::Context::new(&output_surface)?;
1145             let r = cairo::Rectangle::from(bounds);
1146             cr.rectangle(r.x, r.y, r.width, r.height);
1147             cr.clip();
1148 
1149             self.set_as_source_surface(&cr, 0.0, 0.0)?;
1150             cr.set_operator(operator.into());
1151             cr.paint()?;
1152         }
1153 
1154         SharedImageSurface::wrap(
1155             output_surface,
1156             self.surface_type.combine(other.surface_type),
1157         )
1158     }
1159 
1160     /// Performs the combination of two input surfaces.
1161     ///
1162     /// Each pixel of the resulting image is computed using the following formula:
1163     /// `res = k1*i1*i2 + k2*i1 + k3*i2 + k4`
1164     ///
1165     /// # Panics
1166     /// Panics if the two surface types are not compatible.
1167     #[inline]
compose_arithmetic( &self, other: &SharedImageSurface, bounds: IRect, k1: f64, k2: f64, k3: f64, k4: f64, ) -> Result<SharedImageSurface, cairo::Error>1168     pub fn compose_arithmetic(
1169         &self,
1170         other: &SharedImageSurface,
1171         bounds: IRect,
1172         k1: f64,
1173         k2: f64,
1174         k3: f64,
1175         k4: f64,
1176     ) -> Result<SharedImageSurface, cairo::Error> {
1177         let mut output_surface = ExclusiveImageSurface::new(
1178             self.width,
1179             self.height,
1180             self.surface_type.combine(other.surface_type),
1181         )?;
1182 
1183         composite_arithmetic(self, other, &mut output_surface, bounds, k1, k2, k3, k4);
1184 
1185         output_surface.share()
1186     }
1187 
rows(&self) -> Rows<'_>1188     pub fn rows(&self) -> Rows<'_> {
1189         Rows {
1190             surface: self,
1191             next_row: 0,
1192         }
1193     }
1194 }
1195 
1196 impl<'a> Iterator for Rows<'a> {
1197     type Item = &'a [CairoARGB];
1198 
next(&mut self) -> Option<Self::Item>1199     fn next(&mut self) -> Option<Self::Item> {
1200         if self.next_row == self.surface.height {
1201             return None;
1202         }
1203 
1204         let row = self.next_row;
1205 
1206         self.next_row += 1;
1207 
1208         unsafe {
1209             let row_ptr = self
1210                 .surface
1211                 .data_ptr
1212                 .as_ptr()
1213                 .offset(row as isize * self.surface.stride);
1214             let row_of_bytes = slice::from_raw_parts(row_ptr, self.surface.width as usize * 4);
1215             let pixels = row_of_bytes.as_cairo_argb();
1216             assert!(pixels.len() == self.surface.width as usize);
1217             Some(pixels)
1218         }
1219     }
1220 }
1221 
1222 impl<'a> Iterator for RowsMut<'a> {
1223     type Item = &'a mut [CairoARGB];
1224 
next(&mut self) -> Option<Self::Item>1225     fn next(&mut self) -> Option<Self::Item> {
1226         if self.next_row == self.height {
1227             return None;
1228         }
1229 
1230         let row = self.next_row as usize;
1231 
1232         self.next_row += 1;
1233 
1234         unsafe {
1235             // We do this with raw pointers, instead of re-slicing the &mut self.data[....],
1236             // because with the latter we can't synthesize an appropriate lifetime for
1237             // the return value.
1238 
1239             let data_ptr = self.data.as_mut_ptr();
1240             let row_ptr = data_ptr.offset(row as isize * self.stride as isize);
1241             let row_of_bytes = slice::from_raw_parts_mut(row_ptr, self.width as usize * 4);
1242             let pixels = row_of_bytes.as_cairo_argb_mut();
1243             assert!(pixels.len() == self.width as usize);
1244             Some(pixels)
1245         }
1246     }
1247 }
1248 
1249 /// Performs the arithmetic composite operation. Public for benchmarking.
1250 #[inline]
composite_arithmetic( surface1: &SharedImageSurface, surface2: &SharedImageSurface, output_surface: &mut ExclusiveImageSurface, bounds: IRect, k1: f64, k2: f64, k3: f64, k4: f64, )1251 pub fn composite_arithmetic(
1252     surface1: &SharedImageSurface,
1253     surface2: &SharedImageSurface,
1254     output_surface: &mut ExclusiveImageSurface,
1255     bounds: IRect,
1256     k1: f64,
1257     k2: f64,
1258     k3: f64,
1259     k4: f64,
1260 ) {
1261     output_surface.modify(&mut |data, stride| {
1262         for (x, y, pixel, pixel_2) in
1263             Pixels::within(surface1, bounds).map(|(x, y, p)| (x, y, p, surface2.get_pixel(x, y)))
1264         {
1265             let i1a = f64::from(pixel.a) / 255f64;
1266             let i2a = f64::from(pixel_2.a) / 255f64;
1267             let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
1268             let oa = clamp(oa, 0f64, 1f64);
1269 
1270             // Contents of image surfaces are transparent by default, so if the resulting pixel is
1271             // transparent there's no need to do anything.
1272             if oa > 0f64 {
1273                 let compute = |i1, i2| {
1274                     let i1 = f64::from(i1) / 255f64;
1275                     let i2 = f64::from(i2) / 255f64;
1276 
1277                     let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
1278                     let o = clamp(o, 0f64, oa);
1279 
1280                     ((o * 255f64) + 0.5) as u8
1281                 };
1282 
1283                 let output_pixel = Pixel {
1284                     r: compute(pixel.r, pixel_2.r),
1285                     g: compute(pixel.g, pixel_2.g),
1286                     b: compute(pixel.b, pixel_2.b),
1287                     a: ((oa * 255f64) + 0.5) as u8,
1288                 };
1289 
1290                 data.set_pixel(stride, output_pixel, x, y);
1291             }
1292         }
1293     });
1294 }
1295 
1296 impl ImageSurface<Exclusive> {
1297     #[inline]
new( width: i32, height: i32, surface_type: SurfaceType, ) -> Result<ExclusiveImageSurface, cairo::Error>1298     pub fn new(
1299         width: i32,
1300         height: i32,
1301         surface_type: SurfaceType,
1302     ) -> Result<ExclusiveImageSurface, cairo::Error> {
1303         let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
1304 
1305         let (width, height) = (surface.width(), surface.height());
1306 
1307         // Cairo allows zero-sized surfaces, but it does malloc(0), whose result
1308         // is implementation-defined.  So, we can't assume NonNull below.  This is
1309         // why we disallow zero-sized surfaces here.
1310         assert!(width > 0 && height > 0);
1311 
1312         let data_ptr = NonNull::new(unsafe {
1313             cairo::ffi::cairo_image_surface_get_data(surface.to_raw_none())
1314         })
1315         .unwrap();
1316 
1317         let stride = surface.stride() as isize;
1318 
1319         Ok(ExclusiveImageSurface {
1320             surface,
1321             data_ptr,
1322             width,
1323             height,
1324             stride,
1325             surface_type,
1326             _state: PhantomData,
1327         })
1328     }
1329 
1330     #[inline]
share(self) -> Result<SharedImageSurface, cairo::Error>1331     pub fn share(self) -> Result<SharedImageSurface, cairo::Error> {
1332         SharedImageSurface::wrap(self.surface, self.surface_type)
1333     }
1334 
1335     /// Raw access to the image data as a slice
1336     #[inline]
data(&mut self) -> cairo::ImageSurfaceData<'_>1337     pub fn data(&mut self) -> cairo::ImageSurfaceData<'_> {
1338         self.surface.data().unwrap()
1339     }
1340 
1341     /// Modify the image data
1342     #[inline]
modify(&mut self, draw_fn: &mut dyn FnMut(&mut cairo::ImageSurfaceData<'_>, usize))1343     pub fn modify(&mut self, draw_fn: &mut dyn FnMut(&mut cairo::ImageSurfaceData<'_>, usize)) {
1344         let stride = self.stride() as usize;
1345         let mut data = self.data();
1346 
1347         draw_fn(&mut data, stride)
1348     }
1349 
1350     /// Draw on the surface using cairo
1351     #[inline]
draw<E: From<cairo::Error>>( &mut self, draw_fn: &mut dyn FnMut(cairo::Context) -> Result<(), E>, ) -> Result<(), E>1352     pub fn draw<E: From<cairo::Error>>(
1353         &mut self,
1354         draw_fn: &mut dyn FnMut(cairo::Context) -> Result<(), E>,
1355     ) -> Result<(), E> {
1356         let cr = cairo::Context::new(&self.surface)?;
1357         draw_fn(cr)
1358     }
1359 
rows_mut(&mut self) -> RowsMut<'_>1360     pub fn rows_mut(&mut self) -> RowsMut<'_> {
1361         let width = self.surface.width();
1362         let height = self.surface.height();
1363         let stride = self.surface.stride();
1364 
1365         let data = self.surface.data().unwrap();
1366 
1367         RowsMut {
1368             width,
1369             height,
1370             stride,
1371             data,
1372             next_row: 0,
1373         }
1374     }
1375 }
1376 
1377 impl From<Operator> for cairo::Operator {
from(op: Operator) -> cairo::Operator1378     fn from(op: Operator) -> cairo::Operator {
1379         use cairo::Operator as Cairo;
1380         use Operator::*;
1381 
1382         match op {
1383             Over => Cairo::Over,
1384             In => Cairo::In,
1385             Out => Cairo::Out,
1386             Atop => Cairo::Atop,
1387             Xor => Cairo::Xor,
1388             Multiply => Cairo::Multiply,
1389             Screen => Cairo::Screen,
1390             Darken => Cairo::Darken,
1391             Lighten => Cairo::Lighten,
1392             Overlay => Cairo::Overlay,
1393             ColorDodge => Cairo::ColorDodge,
1394             ColorBurn => Cairo::ColorBurn,
1395             HardLight => Cairo::HardLight,
1396             SoftLight => Cairo::SoftLight,
1397             Difference => Cairo::Difference,
1398             Exclusion => Cairo::Exclusion,
1399             HslHue => Cairo::HslHue,
1400             HslSaturation => Cairo::HslSaturation,
1401             HslColor => Cairo::HslColor,
1402             HslLuminosity => Cairo::HslLuminosity,
1403         }
1404     }
1405 }
1406 
1407 #[cfg(test)]
1408 mod tests {
1409     use super::*;
1410     use crate::surface_utils::iterators::Pixels;
1411 
1412     #[test]
test_extract_alpha()1413     fn test_extract_alpha() {
1414         const WIDTH: i32 = 32;
1415         const HEIGHT: i32 = 64;
1416 
1417         let bounds = IRect::new(8, 24, 16, 48);
1418         let full_bounds = IRect::from_size(WIDTH, HEIGHT);
1419 
1420         let mut surface = ExclusiveImageSurface::new(WIDTH, HEIGHT, SurfaceType::SRgb).unwrap();
1421 
1422         // Fill the surface with some data.
1423         {
1424             let mut data = surface.data();
1425 
1426             let mut counter = 0u16;
1427             for x in data.iter_mut() {
1428                 *x = counter as u8;
1429                 counter = (counter + 1) % 256;
1430             }
1431         }
1432 
1433         let surface = surface.share().unwrap();
1434         let alpha = surface.extract_alpha(bounds).unwrap();
1435 
1436         for (x, y, p, pa) in
1437             Pixels::within(&surface, full_bounds).map(|(x, y, p)| (x, y, p, alpha.get_pixel(x, y)))
1438         {
1439             assert_eq!(pa.r, 0);
1440             assert_eq!(pa.g, 0);
1441             assert_eq!(pa.b, 0);
1442 
1443             if !bounds.contains(x as i32, y as i32) {
1444                 assert_eq!(pa.a, 0);
1445             } else {
1446                 assert_eq!(pa.a, p.a);
1447             }
1448         }
1449     }
1450 }
1451