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 cast::i32;
8
use cssparser::Color;
9
use nalgebra::{storage::Storage, Dim, Matrix};
10

            
11
use crate::color::color_to_rgba;
12
use crate::drawing_ctx::set_source_color_on_cairo;
13
use crate::error::*;
14
use crate::rect::{IRect, Rect};
15
use crate::surface_utils::srgb;
16
use crate::util::clamp;
17

            
18
use super::{
19
    iterators::{PixelRectangle, Pixels},
20
    AsCairoARGB, CairoARGB, EdgeMode, ImageSurfaceDataExt, Pixel, PixelOps, ToCairoARGB, ToPixel,
21
};
22

            
23
/// Interpolation when scaling images.
24
///
25
/// This is meant to be translated from the `ImageRendering` property.  We don't use
26
/// `ImageRendering` directly here, because this module is supposed to be lower-level
27
/// than the main part of librsvg.  Here, we take `Interpolation` and translate it
28
/// to Cairo's own values for pattern filtering.
29
///
30
/// This enum can be expanded to use more of Cairo's filtering modes.
31
pub enum Interpolation {
32
    Nearest,
33
    Smooth,
34
}
35

            
36
impl From<Interpolation> for cairo::Filter {
37
150
    fn from(i: Interpolation) -> cairo::Filter {
38
        // Cairo's default for interpolation is CAIRO_FILTER_GOOD.  This happens in Cairo's internals, as
39
        // CAIRO_FILTER_DEFAULT is an internal macro that expands to CAIRO_FILTER_GOOD.
40
150
        match i {
41
1
            Interpolation::Nearest => cairo::Filter::Nearest,
42
149
            Interpolation::Smooth => cairo::Filter::Good,
43
        }
44
150
    }
45
}
46

            
47
/// Types of pixel data in a `ImageSurface`.
48
2471
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
49
pub enum SurfaceType {
50
    /// The pixel data is in the sRGB color space.
51
    SRgb,
52
    /// The pixel data is in the linear sRGB color space.
53
    LinearRgb,
54
    /// The pixel data is alpha-only (contains meaningful data only in the alpha channel).
55
    ///
56
    /// A number of methods are optimized for alpha-only surfaces. For example, linearization and
57
    /// unlinearization have no effect for alpha-only surfaces.
58
    AlphaOnly,
59
}
60

            
61
impl SurfaceType {
62
    /// Combines surface types
63
    ///
64
    /// If combining two alpha-only surfaces, the result is alpha-only.
65
    /// If one is alpha-only, the result is the other.
66
    /// If none is alpha-only, the types should be the same.
67
    ///
68
    /// # Panics
69
    /// Panics if the surface types are not alpha-only and differ.
70
72
    pub fn combine(self, other: SurfaceType) -> SurfaceType {
71
72
        match (self, other) {
72
4
            (SurfaceType::AlphaOnly, t) => t,
73
6
            (t, SurfaceType::AlphaOnly) => t,
74
62
            (t1, t2) if t1 == t2 => t1,
75
            _ => panic!(),
76
        }
77
72
    }
78
}
79

            
80
/// Operators supported by `ImageSurface<Shared>::compose`.
81
pub enum Operator {
82
    Over,
83
    In,
84
    Out,
85
    Atop,
86
    Xor,
87
    Multiply,
88
    Screen,
89
    Darken,
90
    Lighten,
91
    Overlay,
92
    ColorDodge,
93
    ColorBurn,
94
    HardLight,
95
    SoftLight,
96
    Difference,
97
    Exclusion,
98
    HslHue,
99
    HslSaturation,
100
    HslColor,
101
    HslLuminosity,
102
}
103

            
104
/// Wrapper for a Cairo image surface that enforces exclusive access when modifying it.
105
///
106
/// Shared access to `cairo::ImageSurface` is tricky since a read-only borrowed reference
107
/// can still be cloned and then modified. We can't simply use `cairo::ImageSurface::data()`
108
/// because in the filter code we have surfaces referenced from multiple places and it would
109
/// probably add more complexity to remove that and start passing around references.
110
///
111
/// This wrapper asserts the uniqueness of its image surface.
112
///
113
/// It uses the typestate pattern to ensure that the surface can be modified only when
114
/// it is in the `Exclusive` state, while in the `Shared` state it only allows read-only access.
115
2856
#[derive(Debug, Clone)]
116
pub struct ImageSurface<T> {
117
1428
    surface: cairo::ImageSurface,
118

            
119
1428
    data_ptr: NonNull<u8>, // *const.
120
1428
    width: i32,
121
1428
    height: i32,
122
1428
    stride: isize,
123

            
124
1428
    surface_type: SurfaceType,
125

            
126
1428
    _state: PhantomData<T>,
127
}
128

            
129
#[derive(Debug, Clone)]
130
pub struct Shared;
131

            
132
/// Shared state of `ImageSurface`
133
pub type SharedImageSurface = ImageSurface<Shared>;
134

            
135
#[derive(Debug, Clone)]
136
pub struct Exclusive;
137

            
138
/// Exclusive state of `ImageSurface`
139
pub type ExclusiveImageSurface = ImageSurface<Exclusive>;
140

            
141
// The access is read-only, the ref-counting on an `cairo::ImageSurface` is atomic.
142
unsafe impl Sync for SharedImageSurface {}
143

            
144
/// A compile-time blur direction variable.
145
pub trait BlurDirection {
146
    const IS_VERTICAL: bool;
147
}
148

            
149
/// Vertical blur direction.
150
pub enum Vertical {}
151
/// Horizontal blur direction.
152
pub enum Horizontal {}
153

            
154
impl BlurDirection for Vertical {
155
    const IS_VERTICAL: bool = true;
156
}
157

            
158
impl BlurDirection for Horizontal {
159
    const IS_VERTICAL: bool = false;
160
}
161

            
162
/// A compile-time alpha-only marker variable.
163
pub trait IsAlphaOnly {
164
    const IS_ALPHA_ONLY: bool;
165
}
166

            
167
/// Alpha-only.
168
pub enum AlphaOnly {}
169
/// Not alpha-only.
170
pub enum NotAlphaOnly {}
171

            
172
/// Iterator over the rows of a `SharedImageSurface`.
173
pub struct Rows<'a> {
174
    surface: &'a SharedImageSurface,
175
    next_row: i32,
176
}
177

            
178
/// Iterator over the mutable rows of an `ExclusiveImageSurface`.
179
pub struct RowsMut<'a> {
180
    // Keep an ImageSurfaceData here instead of a raw mutable pointer to the bytes,
181
    // so that the ImageSurfaceData will mark the surface as dirty when it is dropped.
182
    data: cairo::ImageSurfaceData<'a>,
183

            
184
    width: i32,
185
    height: i32,
186
    stride: i32,
187

            
188
    next_row: i32,
189
}
190

            
191
impl IsAlphaOnly for AlphaOnly {
192
    const IS_ALPHA_ONLY: bool = true;
193
}
194

            
195
impl IsAlphaOnly for NotAlphaOnly {
196
    const IS_ALPHA_ONLY: bool = false;
197
}
198

            
199
impl<T> ImageSurface<T> {
200
    /// Returns the surface width.
201
    #[inline]
202
769801
    pub fn width(&self) -> i32 {
203
769801
        self.width
204
769801
    }
205

            
206
    /// Returns the surface height.
207
    #[inline]
208
769803
    pub fn height(&self) -> i32 {
209
769803
        self.height
210
769803
    }
211

            
212
    /// Returns the surface stride.
213
    #[inline]
214
326449
    pub fn stride(&self) -> isize {
215
326449
        self.stride
216
326449
    }
217
}
218

            
219
impl ImageSurface<Shared> {
220
    /// Creates a `SharedImageSurface` from a unique `cairo::ImageSurface`.
221
    ///
222
    /// # Panics
223
    /// Panics if the surface format isn't `ARgb32` and if the surface is not unique, that is, its
224
    /// reference count isn't 1.
225
    #[inline]
226
4183
    pub fn wrap(
227
        surface: cairo::ImageSurface,
228
        surface_type: SurfaceType,
229
    ) -> Result<SharedImageSurface, cairo::Error> {
230
        // get_pixel() assumes ARgb32.
231
4183
        assert_eq!(surface.format(), cairo::Format::ARgb32);
232

            
233
        let reference_count =
234
4183
            unsafe { cairo::ffi::cairo_surface_get_reference_count(surface.to_raw_none()) };
235
4183
        assert_eq!(reference_count, 1);
236

            
237
4183
        let (width, height) = (surface.width(), surface.height());
238

            
239
        // Cairo allows zero-sized surfaces, but it does malloc(0), whose result
240
        // is implementation-defined.  So, we can't assume NonNull below.  This is
241
        // why we disallow zero-sized surfaces here.
242
4183
        if !(width > 0 && height > 0) {
243
            return Err(cairo::Error::InvalidSize);
244
        }
245

            
246
4183
        surface.flush();
247

            
248
4183
        let data_ptr = NonNull::new(unsafe {
249
4183
            cairo::ffi::cairo_image_surface_get_data(surface.to_raw_none())
250
        })
251
        .unwrap();
252

            
253
4183
        let stride = surface.stride() as isize;
254

            
255
4183
        Ok(SharedImageSurface {
256
4183
            surface,
257
            data_ptr,
258
            width,
259
            height,
260
            stride,
261
            surface_type,
262
            _state: PhantomData,
263
        })
264
4183
    }
265

            
266
    /// Creates a `SharedImageSurface` copying from a `cairo::ImageSurface`, even if it
267
    /// does not have a reference count of 1.
268
    #[inline]
269
303
    pub fn copy_from_surface(surface: &cairo::ImageSurface) -> Result<Self, cairo::Error> {
270
303
        let copy =
271
303
            cairo::ImageSurface::create(cairo::Format::ARgb32, surface.width(), surface.height())?;
272

            
273
        {
274
303
            let cr = cairo::Context::new(&copy)?;
275
303
            cr.set_source_surface(surface, 0f64, 0f64)?;
276
606
            cr.paint()?;
277
303
        }
278

            
279
303
        SharedImageSurface::wrap(copy, SurfaceType::SRgb)
280
303
    }
281

            
282
    /// Creates an empty `SharedImageSurface` of the given size and `type`.
283
    #[inline]
284
9
    pub fn empty(width: i32, height: i32, surface_type: SurfaceType) -> Result<Self, cairo::Error> {
285
9
        let s = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
286

            
287
9
        SharedImageSurface::wrap(s, surface_type)
288
9
    }
289

            
290
    /// Converts this `SharedImageSurface` back into a Cairo image surface.
291
    #[inline]
292
419
    pub fn into_image_surface(self) -> Result<cairo::ImageSurface, cairo::Error> {
293
419
        let reference_count =
294
419
            unsafe { cairo::ffi::cairo_surface_get_reference_count(self.surface.to_raw_none()) };
295

            
296
419
        if reference_count == 1 {
297
413
            Ok(self.surface)
298
        } else {
299
            // If there are any other references, copy the underlying surface.
300
6
            self.copy_surface(IRect::from_size(self.width, self.height))
301
        }
302
419
    }
303

            
304
30
    pub fn from_image(
305
        image: &image::DynamicImage,
306
        content_type: Option<&str>,
307
        mime_data: Option<Vec<u8>>,
308
    ) -> Result<SharedImageSurface, cairo::Error> {
309
30
        let rgba_image = image.to_rgba8();
310

            
311
30
        let width = i32(rgba_image.width()).map_err(|_| cairo::Error::InvalidSize)?;
312
30
        let height = i32(rgba_image.height()).map_err(|_| cairo::Error::InvalidSize)?;
313

            
314
30
        let mut surf = ExclusiveImageSurface::new(width, height, SurfaceType::SRgb)?;
315

            
316
60
        rgba_image
317
            .rows()
318
30
            .zip(surf.rows_mut())
319
4147
            .flat_map(|(src_row, dest_row)| src_row.zip(dest_row.iter_mut()))
320
1398478
            .for_each(|(src, dest)| *dest = src.to_pixel().premultiply().to_cairo_argb());
321

            
322
30
        if let (Some(content_type), Some(bytes)) = (content_type, mime_data) {
323
30
            surf.surface.set_mime_data(content_type, bytes)?;
324
30
        }
325

            
326
30
        surf.share()
327
30
    }
328

            
329
    /// Returns `true` if the surface contains meaningful data only in the alpha channel.
330
    #[inline]
331
472
    fn is_alpha_only(&self) -> bool {
332
472
        self.surface_type == SurfaceType::AlphaOnly
333
472
    }
334

            
335
    /// Returns the type of this surface.
336
    #[inline]
337
582
    pub fn surface_type(&self) -> SurfaceType {
338
582
        self.surface_type
339
582
    }
340

            
341
    /// Retrieves the pixel value at the given coordinates.
342
    #[inline]
343
12969123
    pub fn get_pixel(&self, x: u32, y: u32) -> Pixel {
344
12969123
        assert!(x < self.width as u32);
345
12969123
        assert!(y < self.height as u32);
346

            
347
        #[allow(clippy::cast_ptr_alignment)]
348
        let value = unsafe {
349
25938246
            *(self
350
                .data_ptr
351
                .as_ptr()
352
12969123
                .offset(y as isize * self.stride + x as isize * 4) as *const u32)
353
        };
354

            
355
12969123
        Pixel::from_u32(value)
356
12969123
    }
357

            
358
    /// Retrieves the pixel value by offset into the pixel data array.
359
    #[inline]
360
82144792
    pub fn get_pixel_by_offset(&self, offset: isize) -> Pixel {
361
82144792
        assert!(offset < self.stride * self.height as isize);
362

            
363
        #[allow(clippy::cast_ptr_alignment)]
364
82144792
        let value = unsafe { *(self.data_ptr.as_ptr().offset(offset) as *const u32) };
365
82144792
        Pixel::from_u32(value)
366
82144792
    }
367

            
368
    /// Calls `set_source_surface()` on the given Cairo context.
369
    #[inline]
370
109719
    pub fn set_as_source_surface(
371
        &self,
372
        cr: &cairo::Context,
373
        x: f64,
374
        y: f64,
375
    ) -> Result<(), cairo::Error> {
376
109719
        cr.set_source_surface(&self.surface, x, y)
377
109719
    }
378

            
379
    /// Creates a Cairo surface pattern from the surface
380
109
    pub fn to_cairo_pattern(&self) -> cairo::SurfacePattern {
381
109
        cairo::SurfacePattern::create(&self.surface)
382
109
    }
383

            
384
    /// Returns a new `cairo::ImageSurface` with the same contents as the one stored in this
385
    /// `SharedImageSurface` within the given bounds.
386
66
    fn copy_surface(&self, bounds: IRect) -> Result<cairo::ImageSurface, cairo::Error> {
387
66
        let output_surface =
388
66
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
389

            
390
66
        let cr = cairo::Context::new(&output_surface)?;
391
66
        let r = cairo::Rectangle::from(bounds);
392
66
        cr.rectangle(r.x(), r.y(), r.width(), r.height());
393
66
        cr.clip();
394

            
395
66
        cr.set_source_surface(&self.surface, 0f64, 0f64)?;
396
132
        cr.paint()?;
397

            
398
66
        Ok(output_surface)
399
66
    }
400

            
401
    /// Scales the given surface by `x` and `y` into a surface `width`×`height` in size, clipped by
402
    /// `bounds`.
403
10
    pub fn scale_to(
404
        &self,
405
        width: i32,
406
        height: i32,
407
        bounds: IRect,
408
        x: f64,
409
        y: f64,
410
    ) -> Result<SharedImageSurface, cairo::Error> {
411
10
        let output_surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
412

            
413
        {
414
10
            let cr = cairo::Context::new(&output_surface)?;
415
10
            let r = cairo::Rectangle::from(bounds);
416
10
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
417
10
            cr.clip();
418

            
419
10
            cr.scale(x, y);
420
10
            self.set_as_source_surface(&cr, 0.0, 0.0)?;
421
20
            cr.paint()?;
422
10
        }
423

            
424
10
        SharedImageSurface::wrap(output_surface, self.surface_type)
425
10
    }
426

            
427
    /// Returns a scaled version of a surface and bounds.
428
    #[inline]
429
5
    pub fn scale(
430
        &self,
431
        bounds: IRect,
432
        x: f64,
433
        y: f64,
434
    ) -> Result<(SharedImageSurface, IRect), cairo::Error> {
435
5
        let new_width = (f64::from(self.width) * x).ceil() as i32;
436
5
        let new_height = (f64::from(self.height) * y).ceil() as i32;
437
5
        let new_bounds = bounds.scale(x, y);
438

            
439
5
        Ok((
440
5
            self.scale_to(new_width, new_height, new_bounds, x, y)?,
441
            new_bounds,
442
        ))
443
5
    }
444

            
445
    /// Returns a surface with black background and alpha channel matching this surface.
446
15289
    pub fn extract_alpha(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
447
15289
        let mut output_surface =
448
15289
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
449

            
450
15289
        let output_stride = output_surface.stride() as usize;
451
        {
452
17
            let mut output_data = output_surface.data().unwrap();
453

            
454
504639
            for (x, y, Pixel { a, .. }) in Pixels::within(self, bounds) {
455
496986
                let output_pixel = Pixel {
456
                    r: 0,
457
                    g: 0,
458
                    b: 0,
459
                    a,
460
                };
461
496986
                output_data.set_pixel(output_stride, output_pixel, x, y);
462
            }
463
17
        }
464

            
465
17
        SharedImageSurface::wrap(output_surface, SurfaceType::AlphaOnly)
466
17
    }
467

            
468
    /// Returns a surface whose alpha channel for each pixel is equal to the
469
    /// luminance of that pixel's unpremultiplied RGB values.  The resulting
470
    /// surface's RGB values are not meanignful; only the alpha channel has
471
    /// useful luminance data.
472
    ///
473
    /// This is to get a mask suitable for use with cairo_mask_surface().
474
413993
    pub fn to_luminance_mask(&self) -> Result<SharedImageSurface, cairo::Error> {
475
413993
        let bounds = IRect::from_size(self.width, self.height);
476

            
477
        let mut output_surface =
478
413993
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
479

            
480
413993
        let stride = output_surface.stride() as usize;
481
        {
482
57
            let mut data = output_surface.data().unwrap();
483

            
484
32845781
            for (x, y, pixel) in Pixels::within(self, bounds) {
485
32634225
                data.set_pixel(stride, pixel.to_luminance_mask(), x, y);
486
            }
487
57
        }
488

            
489
57
        SharedImageSurface::wrap(output_surface, self.surface_type)
490
57
    }
491

            
492
    /// Returns a surface with pre-multiplication of color values undone.
493
    ///
494
    /// HACK: this is storing unpremultiplied pixels in an ARGB32 image surface (which is supposed
495
    /// to be premultiplied pixels).
496
7
    pub fn unpremultiply(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
497
        // Unpremultiplication doesn't affect the alpha channel.
498
7
        if self.is_alpha_only() {
499
1
            return Ok(self.clone());
500
        }
501

            
502
        let mut output_surface =
503
6
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
504

            
505
6
        let stride = output_surface.stride() as usize;
506
        {
507
6
            let mut data = output_surface.data().unwrap();
508

            
509
93686
            for (x, y, pixel) in Pixels::within(self, bounds) {
510
93680
                data.set_pixel(stride, pixel.unpremultiply(), x, y);
511
            }
512
6
        }
513

            
514
6
        SharedImageSurface::wrap(output_surface, self.surface_type)
515
7
    }
516

            
517
    /// Converts the surface to the linear sRGB color space.
518
    #[inline]
519
373
    pub fn to_linear_rgb(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
520
373
        match self.surface_type {
521
92
            SurfaceType::LinearRgb | SurfaceType::AlphaOnly => Ok(self.clone()),
522
281
            _ => srgb::linearize_surface(self, bounds),
523
        }
524
373
    }
525

            
526
    /// Converts the surface to the sRGB color space.
527
    #[inline]
528
312
    pub fn to_srgb(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
529
312
        match self.surface_type {
530
84
            SurfaceType::SRgb | SurfaceType::AlphaOnly => Ok(self.clone()),
531
228
            _ => srgb::unlinearize_surface(self, bounds),
532
        }
533
312
    }
534

            
535
    /// Performs a convolution.
536
    ///
537
    /// Note that `kernel` is rotated 180 degrees.
538
    ///
539
    /// The `target` parameter determines the position of the kernel relative to each pixel of the
540
    /// image. The value of `(0, 0)` indicates that the top left pixel of the (180-degrees-rotated)
541
    /// kernel corresponds to the current pixel, and the rest of the kernel is to the right and
542
    /// bottom of the pixel. The value of `(cols / 2, rows / 2)` centers a kernel with an odd
543
    /// number of rows and columns.
544
    ///
545
    /// # Panics
546
    /// Panics if `kernel` has zero rows or columns.
547
24377
    pub fn convolve<R: Dim, C: Dim, S: Storage<f64, R, C>>(
548
        &self,
549
        bounds: IRect,
550
        target: (i32, i32),
551
        kernel: &Matrix<f64, R, C, S>,
552
        edge_mode: EdgeMode,
553
    ) -> Result<SharedImageSurface, cairo::Error> {
554
24377
        assert!(kernel.nrows() >= 1);
555
24377
        assert!(kernel.ncols() >= 1);
556

            
557
        let mut output_surface =
558
24377
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
559

            
560
24377
        let output_stride = output_surface.stride() as usize;
561
        {
562
9
            let mut output_data = output_surface.data().unwrap();
563

            
564
9
            if self.is_alpha_only() {
565
                for (x, y, _pixel) in Pixels::within(self, bounds) {
566
                    let kernel_bounds = IRect::new(
567
                        x as i32 - target.0,
568
                        y as i32 - target.1,
569
                        x as i32 - target.0 + kernel.ncols() as i32,
570
                        y as i32 - target.1 + kernel.nrows() as i32,
571
                    );
572

            
573
                    let mut a = 0.0;
574

            
575
                    for (x, y, pixel) in
576
                        PixelRectangle::within(self, bounds, kernel_bounds, edge_mode)
577
                    {
578
                        let kernel_x = (kernel_bounds.x1 - x - 1) as usize;
579
                        let kernel_y = (kernel_bounds.y1 - y - 1) as usize;
580
                        let factor = kernel[(kernel_y, kernel_x)];
581

            
582
                        a += f64::from(pixel.a) * factor;
583
                    }
584

            
585
                    let convert = |x: f64| (clamp(x, 0.0, 255.0) + 0.5) as u8;
586

            
587
                    let output_pixel = Pixel {
588
                        r: 0,
589
                        g: 0,
590
                        b: 0,
591
                        a: convert(a),
592
                    };
593

            
594
                    output_data.set_pixel(output_stride, output_pixel, x, y);
595
                }
596
            } else {
597
115745
                for (x, y, _pixel) in Pixels::within(self, bounds) {
598
116323
                    let kernel_bounds = IRect::new(
599
116491
                        x as i32 - target.0,
600
116491
                        y as i32 - target.1,
601
116491
                        x as i32 - target.0 + kernel.ncols() as i32,
602
116426
                        y as i32 - target.1 + kernel.nrows() as i32,
603
                    );
604

            
605
116523
                    let mut r = 0.0;
606
116523
                    let mut g = 0.0;
607
116523
                    let mut b = 0.0;
608
116523
                    let mut a = 0.0;
609

            
610
1142821
                    for (x, y, pixel) in
611
623794
                        PixelRectangle::within(self, bounds, kernel_bounds, edge_mode)
612
                    {
613
518832
                        let kernel_x = (kernel_bounds.x1 - x - 1) as usize;
614
518832
                        let kernel_y = (kernel_bounds.y1 - y - 1) as usize;
615
518832
                        let factor = kernel[(kernel_y, kernel_x)];
616

            
617
511467
                        r += f64::from(pixel.r) * factor;
618
511612
                        g += f64::from(pixel.g) * factor;
619
509851
                        b += f64::from(pixel.b) * factor;
620
508940
                        a += f64::from(pixel.a) * factor;
621
                    }
622

            
623
487104
                    let convert = |x: f64| (clamp(x, 0.0, 255.0) + 0.5) as u8;
624

            
625
116211
                    let output_pixel = Pixel {
626
115960
                        r: convert(r),
627
116102
                        g: convert(g),
628
116485
                        b: convert(b),
629
116249
                        a: convert(a),
630
                    };
631

            
632
116211
                    output_data.set_pixel(output_stride, output_pixel, x, y);
633
                }
634
            }
635
24377
        }
636

            
637
9
        SharedImageSurface::wrap(output_surface, self.surface_type)
638
9
    }
639

            
640
    /// Performs a horizontal or vertical box blur.
641
    ///
642
    /// The `target` parameter determines the position of the kernel relative to each pixel of the
643
    /// image. The value of `0` indicates that the first pixel of the kernel corresponds to the
644
    /// current pixel, and the rest of the kernel is to the right or bottom of the pixel. The value
645
    /// of `kernel_size / 2` centers a kernel with an odd size.
646
    ///
647
    /// # Panics
648
    /// Panics if `kernel_size` is `0` or if `target >= kernel_size`.
649
    // This is public (and not inlined into box_blur()) for the purpose of accessing it from the
650
    // benchmarks.
651
228
    pub fn box_blur_loop<B: BlurDirection, A: IsAlphaOnly>(
652
        &self,
653
        output_surface: &mut cairo::ImageSurface,
654
        bounds: IRect,
655
        kernel_size: usize,
656
        target: usize,
657
    ) {
658
228
        assert_ne!(kernel_size, 0);
659
228
        assert!(target < kernel_size);
660
228
        assert_eq!(self.is_alpha_only(), A::IS_ALPHA_ONLY);
661

            
662
        {
663
            // The following code is needed for a parallel implementation of the blur loop. The
664
            // blurring is done either for each row or for each column of pixels, depending on the
665
            // value of `vertical`, independently of the others. Naturally, we want to run the
666
            // outer loop on a thread pool.
667
            //
668
            // The case of `vertical == false` is simple since the input image slice can be
669
            // partitioned into chunks for each row of pixels and processed in parallel with rayon.
670
            // The case of `vertical == true`, however, is more involved because we can't just make
671
            // mutable slices for all pixel columns (they would be overlapping which is forbidden
672
            // by the aliasing rules).
673
            //
674
            // This is where the following struct comes into play: it stores a sub-slice of the
675
            // pixel data and can be split at any row or column into two parts (similar to
676
            // slice::split_at_mut()).
677
            struct UnsafeSendPixelData<'a> {
678
                width: u32,
679
                height: u32,
680
                stride: isize,
681
                ptr: NonNull<u8>,
682
                _marker: PhantomData<&'a mut ()>,
683
            }
684

            
685
            unsafe impl<'a> Send for UnsafeSendPixelData<'a> {}
686

            
687
            impl<'a> UnsafeSendPixelData<'a> {
688
                /// Creates a new `UnsafeSendPixelData`.
689
                ///
690
                /// # Safety
691
                /// You must call `cairo_surface_mark_dirty()` on the surface once all instances of
692
                /// `UnsafeSendPixelData` are dropped to make sure the pixel changes are committed
693
                /// to Cairo.
694
                #[inline]
695
228
                unsafe fn new(surface: &mut cairo::ImageSurface) -> Self {
696
228
                    assert_eq!(surface.format(), cairo::Format::ARgb32);
697
228
                    let ptr = surface.data().unwrap().as_mut_ptr();
698

            
699
228
                    Self {
700
228
                        width: surface.width() as u32,
701
228
                        height: surface.height() as u32,
702
228
                        stride: surface.stride() as isize,
703
228
                        ptr: NonNull::new(ptr).unwrap(),
704
                        _marker: PhantomData,
705
                    }
706
228
                }
707

            
708
                /// Sets a pixel value at the given coordinates.
709
                #[inline]
710
7672920
                fn set_pixel(&mut self, pixel: Pixel, x: u32, y: u32) {
711
7672920
                    assert!(x < self.width);
712
7672920
                    assert!(y < self.height);
713

            
714
7672920
                    let value = pixel.to_u32();
715

            
716
                    #[allow(clippy::cast_ptr_alignment)]
717
                    unsafe {
718
15345840
                        let ptr = self
719
                            .ptr
720
                            .as_ptr()
721
7672920
                            .offset(y as isize * self.stride + x as isize * 4)
722
                            as *mut u32;
723
7672920
                        *ptr = value;
724
                    }
725
7672920
                }
726

            
727
                /// Splits this `UnsafeSendPixelData` into two at the given row.
728
                ///
729
                /// The first one contains rows `0..index` (`index` not included) and the second one
730
                /// contains rows `index..height`.
731
                #[inline]
732
18571
                fn split_at_row(self, index: u32) -> (Self, Self) {
733
18571
                    assert!(index <= self.height);
734

            
735
18571
                    (
736
18571
                        UnsafeSendPixelData {
737
18571
                            width: self.width,
738
                            height: index,
739
18571
                            stride: self.stride,
740
18571
                            ptr: self.ptr,
741
                            _marker: PhantomData,
742
                        },
743
18571
                        UnsafeSendPixelData {
744
18571
                            width: self.width,
745
18571
                            height: self.height - index,
746
18571
                            stride: self.stride,
747
18571
                            ptr: NonNull::new(unsafe {
748
18571
                                self.ptr.as_ptr().offset(index as isize * self.stride)
749
                            })
750
                            .unwrap(),
751
                            _marker: PhantomData,
752
                        },
753
                    )
754
18571
                }
755

            
756
                /// Splits this `UnsafeSendPixelData` into two at the given column.
757
                ///
758
                /// The first one contains columns `0..index` (`index` not included) and the second
759
                /// one contains columns `index..width`.
760
                #[inline]
761
20388
                fn split_at_column(self, index: u32) -> (Self, Self) {
762
20388
                    assert!(index <= self.width);
763

            
764
20388
                    (
765
20388
                        UnsafeSendPixelData {
766
                            width: index,
767
20388
                            height: self.height,
768
20388
                            stride: self.stride,
769
20388
                            ptr: self.ptr,
770
                            _marker: PhantomData,
771
                        },
772
20388
                        UnsafeSendPixelData {
773
20388
                            width: self.width - index,
774
20388
                            height: self.height,
775
20388
                            stride: self.stride,
776
20388
                            ptr: NonNull::new(unsafe {
777
20388
                                self.ptr.as_ptr().offset(index as isize * 4)
778
                            })
779
                            .unwrap(),
780
                            _marker: PhantomData,
781
                        },
782
                    )
783
20388
                }
784
            }
785

            
786
228
            let output_data = unsafe { UnsafeSendPixelData::new(output_surface) };
787

            
788
            // Shift is target into the opposite direction.
789
228
            let shift = (kernel_size - target) as i32;
790
228
            let target = target as i32;
791

            
792
            // Convert to f64 once since we divide by it.
793
228
            let kernel_size_f64 = kernel_size as f64;
794
15109566
            let compute = |x: u32| (f64::from(x) / kernel_size_f64 + 0.5) as u8;
795

            
796
            // Depending on `vertical`, we're blurring either horizontally line-by-line, or
797
            // vertically column-by-column. In the code below, the main axis is the axis along
798
            // which the blurring happens (so if `vertical` is false, the main axis is the
799
            // horizontal axis). The other axis is the outer loop axis. The code uses `i` and `j`
800
            // for the other axis and main axis coordinates, respectively.
801
228
            let (main_axis_min, main_axis_max, other_axis_min, other_axis_max) = if B::IS_VERTICAL {
802
111
                (bounds.y0, bounds.y1, bounds.x0, bounds.x1)
803
            } else {
804
117
                (bounds.x0, bounds.x1, bounds.y0, bounds.y1)
805
            };
806

            
807
            // Helper function for getting the pixels.
808
7822761
            let pixel = |i, j| {
809
7822533
                let (x, y) = if B::IS_VERTICAL { (i, j) } else { (j, i) };
810

            
811
7822533
                self.get_pixel(x as u32, y as u32)
812
7822533
            };
813

            
814
            // The following loop assumes the first row or column of `output_data` is the first row
815
            // or column inside `bounds`.
816
228
            let mut output_data = if B::IS_VERTICAL {
817
111
                output_data.split_at_column(bounds.x0 as u32).1
818
            } else {
819
117
                output_data.split_at_row(bounds.y0 as u32).1
820
            };
821

            
822
456
            rayon::scope(|s| {
823
38979
                for i in other_axis_min..other_axis_max {
824
                    // Split off one row or column and launch its processing on another thread.
825
                    // Thanks to the initial split before the loop, there's no special case for the
826
                    // very first split.
827
77502
                    let (mut current, remaining) = if B::IS_VERTICAL {
828
20277
                        output_data.split_at_column(1)
829
                    } else {
830
18474
                        output_data.split_at_row(1)
831
                    };
832

            
833
38751
                    output_data = remaining;
834

            
835
77419
                    s.spawn(move |_| {
836
                        // Helper function for setting the pixels.
837
7607301
                        let mut set_pixel = |j, pixel| {
838
                            // We're processing rows or columns one-by-one, so the other coordinate
839
                            // is always 0.
840
7568633
                            let (x, y) = if B::IS_VERTICAL { (0, j) } else { (j, 0) };
841
7568633
                            current.set_pixel(pixel, x, y);
842
7568633
                        };
843

            
844
                        // The idea is that since all weights of the box blur kernel are equal, for
845
                        // each step along the main axis, instead of recomputing the full sum, we
846
                        // can take the previous sum, subtract the "oldest" pixel value and add the
847
                        // "newest" pixel value.
848
                        //
849
                        // The sum is u32 so that it can fit MAXIMUM_KERNEL_SIZE * 255.
850
38668
                        let mut sum_r = 0;
851
38668
                        let mut sum_g = 0;
852
38668
                        let mut sum_b = 0;
853
38668
                        let mut sum_a = 0;
854

            
855
                        // The whole sum needs to be computed for the first pixel. However, we know
856
                        // that values outside of bounds are transparent, so the loop starts on the
857
                        // first pixel in bounds.
858
256886
                        for j in main_axis_min..min(main_axis_max, main_axis_min + shift) {
859
218218
                            let Pixel { r, g, b, a } = pixel(i, j);
860

            
861
218218
                            if !A::IS_ALPHA_ONLY {
862
158988
                                sum_r += u32::from(r);
863
158988
                                sum_g += u32::from(g);
864
158988
                                sum_b += u32::from(b);
865
                            }
866

            
867
218218
                            sum_a += u32::from(a);
868
                        }
869

            
870
38668
                        set_pixel(
871
38668
                            main_axis_min as u32,
872
38668
                            Pixel {
873
38668
                                r: compute(sum_r),
874
38668
                                g: compute(sum_g),
875
38668
                                b: compute(sum_b),
876
38668
                                a: compute(sum_a),
877
                            },
878
                        );
879

            
880
                        // Now, go through all the other pixels.
881
                        //
882
                        // j - target - 1 >= main_axis_min
883
                        // j >= main_axis_min + target + 1
884
38668
                        let start_subtracting_at = main_axis_min + target + 1;
885

            
886
                        // j + shift - 1 < main_axis_max
887
                        // j < main_axis_max - shift + 1
888
38668
                        let stop_adding_at = main_axis_max - shift + 1;
889

            
890
5976248
                        for j in main_axis_min + 1..main_axis_max {
891
14667217
                            if j >= start_subtracting_at {
892
6945275
                                let old_pixel = pixel(i, j - target - 1);
893

            
894
6945275
                                if !A::IS_ALPHA_ONLY {
895
4817291
                                    sum_r -= u32::from(old_pixel.r);
896
4817291
                                    sum_g -= u32::from(old_pixel.g);
897
4817291
                                    sum_b -= u32::from(old_pixel.b);
898
                                }
899

            
900
6945275
                                sum_a -= u32::from(old_pixel.a);
901
                            }
902

            
903
14551703
                            if j < stop_adding_at {
904
6829761
                                let new_pixel = pixel(i, j + shift - 1);
905

            
906
6829761
                                if !A::IS_ALPHA_ONLY {
907
4746079
                                    sum_r += u32::from(new_pixel.r);
908
4746079
                                    sum_g += u32::from(new_pixel.g);
909
4746079
                                    sum_b += u32::from(new_pixel.b);
910
                                }
911

            
912
6829761
                                sum_a += u32::from(new_pixel.a);
913
                            }
914

            
915
5937580
                            set_pixel(
916
                                j as u32,
917
5937580
                                Pixel {
918
5937580
                                    r: compute(sum_r),
919
5937580
                                    g: compute(sum_g),
920
5937580
                                    b: compute(sum_b),
921
5937580
                                    a: compute(sum_a),
922
                                },
923
                            );
924
                        }
925
1745694
                    });
926
                }
927
228
            });
928
        }
929

            
930
        // Don't forget to manually mark the surface as dirty (due to usage of
931
        // `UnsafeSendPixelData`).
932
228
        unsafe { cairo::ffi::cairo_surface_mark_dirty(output_surface.to_raw_none()) }
933
228
    }
934

            
935
    /// Performs a horizontal or vertical box blur.
936
    ///
937
    /// The `target` parameter determines the position of the kernel relative to each pixel of the
938
    /// image. The value of `0` indicates that the first pixel of the kernel corresponds to the
939
    /// current pixel, and the rest of the kernel is to the right or bottom of the pixel. The value
940
    /// of `kernel_size / 2` centers a kernel with an odd size.
941
    ///
942
    /// # Panics
943
    /// Panics if `kernel_size` is `0` or if `target >= kernel_size`.
944
    #[inline]
945
228
    pub fn box_blur<B: BlurDirection>(
946
        &self,
947
        bounds: IRect,
948
        kernel_size: usize,
949
        target: usize,
950
    ) -> Result<SharedImageSurface, cairo::Error> {
951
228
        let mut output_surface =
952
228
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
953

            
954
228
        if self.is_alpha_only() {
955
69
            self.box_blur_loop::<B, AlphaOnly>(&mut output_surface, bounds, kernel_size, target);
956
        } else {
957
159
            self.box_blur_loop::<B, NotAlphaOnly>(&mut output_surface, bounds, kernel_size, target);
958
        }
959

            
960
228
        SharedImageSurface::wrap(output_surface, self.surface_type)
961
228
    }
962

            
963
    /// Fills the with a specified color.
964
    #[inline]
965
102
    pub fn flood(&self, bounds: IRect, color: Color) -> Result<SharedImageSurface, cairo::Error> {
966
102
        let output_surface =
967
102
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
968

            
969
102
        let rgba = color_to_rgba(&color);
970

            
971
34
        if rgba.alpha.unwrap_or(0.0) > 0.0 {
972
34
            let cr = cairo::Context::new(&output_surface)?;
973
34
            let r = cairo::Rectangle::from(bounds);
974
34
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
975
34
            cr.clip();
976

            
977
34
            set_source_color_on_cairo(&cr, &color);
978
136
            cr.paint()?;
979
34
        }
980

            
981
68
        SharedImageSurface::wrap(output_surface, self.surface_type)
982
34
    }
983

            
984
    /// Offsets the image of the specified amount.
985
    #[inline]
986
68
    pub fn offset(
987
        &self,
988
        bounds: Rect,
989
        dx: f64,
990
        dy: f64,
991
    ) -> Result<SharedImageSurface, cairo::Error> {
992
68
        let output_surface =
993
68
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
994

            
995
        // output_bounds contains all pixels within bounds,
996
        // for which (x - ox) and (y - oy) also lie within bounds.
997
68
        if let Some(output_bounds) = bounds.translate((dx, dy)).intersection(&bounds) {
998
22
            let cr = cairo::Context::new(&output_surface)?;
999
22
            let r = cairo::Rectangle::from(output_bounds);
22
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
22
            cr.clip();
22
            self.set_as_source_surface(&cr, dx, dy)?;
90
            cr.paint()?;
22
        }
46
        SharedImageSurface::wrap(output_surface, self.surface_type)
24
    }
    /// Returns a new surface of the same size, with the contents of the
    /// specified image, optionally transformed to match a given box
    #[inline]
285
    pub fn paint_image(
        &self,
        bounds: Rect,
        image: &SharedImageSurface,
        rect: Option<Rect>,
        interpolation: Interpolation,
    ) -> Result<SharedImageSurface, cairo::Error> {
285
        let output_surface =
285
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
285
        if rect.is_none() || !rect.unwrap().is_empty() {
25
            let cr = cairo::Context::new(&output_surface)?;
151
            let r = cairo::Rectangle::from(bounds);
67
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
67
            cr.clip();
67
            image.set_as_source_surface(&cr, 0f64, 0f64)?;
67
            if let Some(rect) = rect {
42
                let mut matrix = cairo::Matrix::new(
42
                    rect.width() / f64::from(image.width()),
                    0.0,
                    0.0,
42
                    rect.height() / f64::from(image.height()),
42
                    rect.x0,
42
                    rect.y0,
                );
42
                matrix.invert();
42
                cr.source().set_matrix(matrix);
42
                cr.source().set_filter(cairo::Filter::from(interpolation));
            }
310
            cr.paint()?;
67
        }
92
        SharedImageSurface::wrap(output_surface, image.surface_type)
67
    }
    /// Creates a new surface with the size and content specified in `bounds`
    ///
    /// # Panics
    /// Panics if `bounds` is an empty rectangle, since `SharedImageSurface` cannot
    /// represent zero-sized images.
    #[inline]
1
    pub fn tile(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
        // Cairo lets us create zero-sized surfaces, but the call to SharedImageSurface::wrap()
        // below will panic in that case.  So, disallow requesting a zero-sized subregion.
1
        assert!(!bounds.is_empty());
        let output_surface =
1
            cairo::ImageSurface::create(cairo::Format::ARgb32, bounds.width(), bounds.height())?;
        {
1
            let cr = cairo::Context::new(&output_surface)?;
1
            self.set_as_source_surface(&cr, f64::from(-bounds.x0), f64::from(-bounds.y0))?;
2
            cr.paint()?;
1
        }
1
        SharedImageSurface::wrap(output_surface, self.surface_type)
1
    }
    /// Returns a new surface of the same size, with the contents of the specified
    /// image repeated to fill the bounds and starting from the given position.
    #[inline]
1
    pub fn paint_image_tiled(
        &self,
        bounds: IRect,
        image: &SharedImageSurface,
        x: i32,
        y: i32,
    ) -> Result<SharedImageSurface, cairo::Error> {
1
        let output_surface =
1
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
        {
1
            let cr = cairo::Context::new(&output_surface)?;
1
            let ptn = image.to_cairo_pattern();
1
            ptn.set_extend(cairo::Extend::Repeat);
1
            let mut mat = cairo::Matrix::identity();
1
            mat.translate(f64::from(-x), f64::from(-y));
1
            ptn.set_matrix(mat);
1
            let r = cairo::Rectangle::from(bounds);
1
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
1
            cr.clip();
1
            cr.set_source(&ptn)?;
2
            cr.paint()?;
1
        }
1
        SharedImageSurface::wrap(output_surface, image.surface_type)
1
    }
    /// Performs the combination of two input surfaces using Porter-Duff
    /// compositing operators.
    ///
    /// # Panics
    /// Panics if the two surface types are not compatible.
    #[inline]
60
    pub fn compose(
        &self,
        other: &SharedImageSurface,
        bounds: IRect,
        operator: Operator,
    ) -> Result<SharedImageSurface, cairo::Error> {
60
        let output_surface = other.copy_surface(bounds)?;
        {
60
            let cr = cairo::Context::new(&output_surface)?;
60
            let r = cairo::Rectangle::from(bounds);
60
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
60
            cr.clip();
60
            self.set_as_source_surface(&cr, 0.0, 0.0)?;
60
            cr.set_operator(operator.into());
120
            cr.paint()?;
60
        }
60
        SharedImageSurface::wrap(
60
            output_surface,
60
            self.surface_type.combine(other.surface_type),
60
        )
60
    }
    /// Performs the combination of two input surfaces.
    ///
    /// Each pixel of the resulting image is computed using the following formula:
    /// `res = k1*i1*i2 + k2*i1 + k3*i2 + k4`
    ///
    /// # Panics
    /// Panics if the two surface types are not compatible.
    #[inline]
12
    pub fn compose_arithmetic(
        &self,
        other: &SharedImageSurface,
        bounds: IRect,
        k1: f64,
        k2: f64,
        k3: f64,
        k4: f64,
    ) -> Result<SharedImageSurface, cairo::Error> {
24
        let mut output_surface = ExclusiveImageSurface::new(
12
            self.width,
12
            self.height,
12
            self.surface_type.combine(other.surface_type),
        )?;
12
        composite_arithmetic(self, other, &mut output_surface, bounds, k1, k2, k3, k4);
12
        output_surface.share()
12
    }
9
    pub fn rows(&self) -> Rows<'_> {
        Rows {
            surface: self,
            next_row: 0,
        }
9
    }
}
impl<'a> Iterator for Rows<'a> {
    type Item = &'a [CairoARGB];
4340
    fn next(&mut self) -> Option<Self::Item> {
4340
        if self.next_row == self.surface.height {
            return None;
        }
4340
        let row = self.next_row;
4340
        self.next_row += 1;
        // SAFETY: this code assumes that cairo image surface data is correctly
        // aligned for u32. This assumption is justified by the Cairo docs,
        // which say this:
        //
        // https://cairographics.org/manual/cairo-Image-Surfaces.html#cairo-image-surface-create-for-data
        //
        // > This pointer must be suitably aligned for any kind of variable,
        // > (for example, a pointer returned by malloc).
        unsafe {
8680
            let row_ptr: *const u8 = self
                .surface
                .data_ptr
                .as_ptr()
4340
                .offset(row as isize * self.surface.stride);
            let row_of_u32: &[u32] =
4340
                slice::from_raw_parts(row_ptr as *const u32, self.surface.width as usize);
4340
            let pixels = row_of_u32.as_cairo_argb();
4340
            assert!(pixels.len() == self.surface.width as usize);
4340
            Some(pixels)
        }
4340
    }
}
impl<'a> Iterator for RowsMut<'a> {
    type Item = &'a mut [CairoARGB];
4147
    fn next(&mut self) -> Option<Self::Item> {
4147
        if self.next_row == self.height {
            return None;
        }
4147
        let row = self.next_row as usize;
4147
        self.next_row += 1;
        // SAFETY: this code assumes that cairo image surface data is correctly
        // aligned for u32. This assumption is justified by the Cairo docs,
        // which say this:
        //
        // https://cairographics.org/manual/cairo-Image-Surfaces.html#cairo-image-surface-create-for-data
        //
        // > This pointer must be suitably aligned for any kind of variable,
        // > (for example, a pointer returned by malloc).
        unsafe {
            // We do this with raw pointers, instead of re-slicing the &mut self.data[....],
            // because with the latter we can't synthesize an appropriate lifetime for
            // the return value.
4147
            let data_ptr = self.data.as_mut_ptr();
4147
            let row_ptr: *mut u8 = data_ptr.offset(row as isize * self.stride as isize);
            let row_of_u32: &mut [u32] =
4147
                slice::from_raw_parts_mut(row_ptr as *mut u32, self.width as usize);
4147
            let pixels = row_of_u32.as_cairo_argb_mut();
4147
            assert!(pixels.len() == self.width as usize);
4147
            Some(pixels)
        }
4147
    }
}
/// Performs the arithmetic composite operation. Public for benchmarking.
#[inline]
12
pub fn composite_arithmetic(
    surface1: &SharedImageSurface,
    surface2: &SharedImageSurface,
    output_surface: &mut ExclusiveImageSurface,
    bounds: IRect,
    k1: f64,
    k2: f64,
    k3: f64,
    k4: f64,
) {
24
    output_surface.modify(&mut |data, stride| {
139020
        for (x, y, pixel, pixel_2) in
278065
            Pixels::within(surface1, bounds).map(|(x, y, p)| (x, y, p, surface2.get_pixel(x, y)))
        {
139008
            let i1a = f64::from(pixel.a) / 255f64;
139008
            let i2a = f64::from(pixel_2.a) / 255f64;
139008
            let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
139008
            let oa = clamp(oa, 0f64, 1f64);
            // Contents of image surfaces are transparent by default, so if the resulting pixel is
            // transparent there's no need to do anything.
139008
            if oa > 0f64 {
365292
                let compute = |i1, i2| {
273377
                    let i1 = f64::from(i1) / 255f64;
273377
                    let i2 = f64::from(i2) / 255f64;
273377
                    let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
273377
                    let o = clamp(o, 0f64, oa);
273377
                    ((o * 255f64) + 0.5) as u8
273377
                };
91915
                let output_pixel = Pixel {
91915
                    r: compute(pixel.r, pixel_2.r),
91915
                    g: compute(pixel.g, pixel_2.g),
91915
                    b: compute(pixel.b, pixel_2.b),
91915
                    a: ((oa * 255f64) + 0.5) as u8,
                };
91915
                data.set_pixel(stride, output_pixel, x, y);
            }
        }
12
    });
12
}
impl ImageSurface<Exclusive> {
    #[inline]
755
    pub fn new(
        width: i32,
        height: i32,
        surface_type: SurfaceType,
    ) -> Result<ExclusiveImageSurface, cairo::Error> {
755
        let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
755
        let (width, height) = (surface.width(), surface.height());
        // Cairo allows zero-sized surfaces, but it does malloc(0), whose result
        // is implementation-defined.  So, we can't assume NonNull below.  This is
        // why we disallow zero-sized surfaces here.
755
        if !(width > 0 && height > 0) {
            return Err(cairo::Error::InvalidSize);
        }
755
        let data_ptr = NonNull::new(unsafe {
755
            cairo::ffi::cairo_image_surface_get_data(surface.to_raw_none())
        })
        .unwrap();
755
        let stride = surface.stride() as isize;
755
        Ok(ExclusiveImageSurface {
755
            surface,
            data_ptr,
            width,
            height,
            stride,
            surface_type,
            _state: PhantomData,
        })
755
    }
    #[inline]
755
    pub fn share(self) -> Result<SharedImageSurface, cairo::Error> {
755
        SharedImageSurface::wrap(self.surface, self.surface_type)
755
    }
    /// Raw access to the image data as a slice
    #[inline]
700
    pub fn data(&mut self) -> cairo::ImageSurfaceData<'_> {
700
        self.surface.data().unwrap()
700
    }
    /// Modify the image data
    #[inline]
612
    pub fn modify(&mut self, draw_fn: &mut dyn FnMut(&mut cairo::ImageSurfaceData<'_>, usize)) {
612
        let stride = self.stride() as usize;
612
        let mut data = self.data();
612
        draw_fn(&mut data, stride)
612
    }
    /// Draw on the surface using cairo
    #[inline]
25
    pub fn draw(
        &mut self,
        draw_fn: &mut dyn FnMut(cairo::Context) -> Result<(), InternalRenderingError>,
    ) -> Result<(), InternalRenderingError> {
25
        let cr = cairo::Context::new(&self.surface)?;
25
        draw_fn(cr)
25
    }
30
    pub fn rows_mut(&mut self) -> RowsMut<'_> {
30
        let width = self.surface.width();
30
        let height = self.surface.height();
30
        let stride = self.surface.stride();
30
        let data = self.surface.data().unwrap();
30
        RowsMut {
            width,
            height,
            stride,
            data,
            next_row: 0,
        }
30
    }
}
impl From<Operator> for cairo::Operator {
60
    fn from(op: Operator) -> cairo::Operator {
        use cairo::Operator as Cairo;
        use Operator::*;
60
        match op {
29
            Over => Cairo::Over,
18
            In => Cairo::In,
3
            Out => Cairo::Out,
2
            Atop => Cairo::Atop,
2
            Xor => Cairo::Xor,
3
            Multiply => Cairo::Multiply,
1
            Screen => Cairo::Screen,
1
            Darken => Cairo::Darken,
1
            Lighten => Cairo::Lighten,
            Overlay => Cairo::Overlay,
            ColorDodge => Cairo::ColorDodge,
            ColorBurn => Cairo::ColorBurn,
            HardLight => Cairo::HardLight,
            SoftLight => Cairo::SoftLight,
            Difference => Cairo::Difference,
            Exclusion => Cairo::Exclusion,
            HslHue => Cairo::HslHue,
            HslSaturation => Cairo::HslSaturation,
            HslColor => Cairo::HslColor,
            HslLuminosity => Cairo::HslLuminosity,
        }
60
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::surface_utils::iterators::Pixels;
    #[test]
2
    fn test_extract_alpha() {
        const WIDTH: i32 = 32;
        const HEIGHT: i32 = 64;
1
        let bounds = IRect::new(8, 24, 16, 48);
1
        let full_bounds = IRect::from_size(WIDTH, HEIGHT);
1
        let mut surface = ExclusiveImageSurface::new(WIDTH, HEIGHT, SurfaceType::SRgb).unwrap();
        // Fill the surface with some data.
        {
1
            let mut data = surface.data();
1
            let mut counter = 0u16;
8193
            for x in data.iter_mut() {
8192
                *x = counter as u8;
8192
                counter = (counter + 1) % 256;
            }
1
        }
1
        let surface = surface.share().unwrap();
1
        let alpha = surface.extract_alpha(bounds).unwrap();
2049
        for (x, y, p, pa) in
4097
            Pixels::within(&surface, full_bounds).map(|(x, y, p)| (x, y, p, alpha.get_pixel(x, y)))
        {
2048
            assert_eq!(pa.r, 0);
2048
            assert_eq!(pa.g, 0);
2048
            assert_eq!(pa.b, 0);
2048
            if !bounds.contains(x as i32, y as i32) {
1856
                assert_eq!(pa.a, 0);
            } else {
192
                assert_eq!(pa.a, p.a);
            }
        }
2
    }
}