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 nalgebra::{Dim, Matrix, storage::Storage};
9

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

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

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

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

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

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

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

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

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

            
123
    surface_type: SurfaceType,
124

            
125
    _state: PhantomData<T>,
126
}
127

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
187
    next_row: i32,
188
}
189

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

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

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

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

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

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

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

            
236
71498
        let (width, height) = (surface.width(), surface.height());
237

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

            
245
71498
        surface.flush();
246

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

            
252
71498
        let stride = surface.stride() as isize;
253

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

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

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

            
278
6160
        SharedImageSurface::wrap(copy, SurfaceType::SRgb)
279
6160
    }
280

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

            
286
184
        SharedImageSurface::wrap(s, surface_type)
287
184
    }
288

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

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

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

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

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

            
315
600
        rgba_image
316
600
            .rows()
317
600
            .zip(surf.rows_mut())
318
82940
            .flat_map(|(src_row, dest_row)| src_row.zip(dest_row.iter_mut()))
319
28092220
            .for_each(|(src, dest)| *dest = src.to_pixel().premultiply().to_cairo_argb());
320

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

            
325
600
        surf.share()
326
600
    }
327

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

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

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

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

            
354
447510804
        Pixel::from_u32(value)
355
447510804
    }
356

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

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

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

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

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

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

            
394
1240
        cr.set_source_surface(&self.surface, 0f64, 0f64)?;
395
1240
        cr.paint()?;
396

            
397
1240
        Ok(output_surface)
398
1240
    }
399

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

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

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

            
423
200
        SharedImageSurface::wrap(output_surface, self.surface_type)
424
200
    }
425

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

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

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

            
449
322
        let output_stride = output_surface.stride() as usize;
450
        {
451
322
            let mut output_data = output_surface.data().unwrap();
452

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

            
464
322
        SharedImageSurface::wrap(output_surface, SurfaceType::AlphaOnly)
465
322
    }
466

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

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

            
479
1140
        let stride = output_surface.stride() as usize;
480
        {
481
1140
            let mut data = output_surface.data().unwrap();
482

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

            
488
1140
        SharedImageSurface::wrap(output_surface, self.surface_type)
489
1140
    }
490

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

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

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

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

            
513
120
        SharedImageSurface::wrap(output_surface, self.surface_type)
514
140
    }
515

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

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

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

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

            
559
180
        let output_stride = output_surface.stride() as usize;
560
        {
561
180
            let mut output_data = output_surface.data().unwrap();
562

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

            
572
                    let mut a = 0.0;
573

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

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

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

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

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

            
604
2331380
                    let mut r = 0.0;
605
2331380
                    let mut g = 0.0;
606
2331380
                    let mut b = 0.0;
607
2331380
                    let mut a = 0.0;
608

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

            
616
10415660
                        r += f64::from(pixel.r) * factor;
617
10415660
                        g += f64::from(pixel.g) * factor;
618
10415660
                        b += f64::from(pixel.b) * factor;
619
10415660
                        a += f64::from(pixel.a) * factor;
620
10415660
                    }
621

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

            
624
2331380
                    let output_pixel = Pixel {
625
2331380
                        r: convert(r),
626
2331380
                        g: convert(g),
627
2331380
                        b: convert(b),
628
2331380
                        a: convert(a),
629
2331380
                    };
630

            
631
2331380
                    output_data.set_pixel(output_stride, output_pixel, x, y);
632
                }
633
            }
634
        }
635

            
636
180
        SharedImageSurface::wrap(output_surface, self.surface_type)
637
180
    }
638

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

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

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

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

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

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

            
713
166254300
                    let value = pixel.to_u32();
714

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

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

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

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

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

            
785
4560
            let output_data = unsafe { UnsafeSendPixelData::new(output_surface) };
786

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

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

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

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

            
810
328118940
                self.get_pixel(x as u32, y as u32)
811
328118940
            };
812

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

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

            
832
775020
                    output_data = remaining;
833

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

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

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

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

            
866
4389660
                            sum_a += u32::from(a);
867
                        }
868

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

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

            
885
                        // j + shift - 1 < main_axis_max
886
                        // j < main_axis_max - shift + 1
887
775020
                        let stop_adding_at = main_axis_max - shift + 1;
888

            
889
165479280
                        for j in main_axis_min + 1..main_axis_max {
890
165479280
                            if j >= start_subtracting_at {
891
161864640
                                let old_pixel = pixel(i, j - target - 1);
892

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

            
899
161864640
                                sum_a -= u32::from(old_pixel.a);
900
3614640
                            }
901

            
902
165479280
                            if j < stop_adding_at {
903
161864640
                                let new_pixel = pixel(i, j + shift - 1);
904

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

            
911
161864640
                                sum_a += u32::from(new_pixel.a);
912
3614640
                            }
913

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

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

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

            
953
4560
        if self.is_alpha_only() {
954
1380
            self.box_blur_loop::<B, AlphaOnly>(&mut output_surface, bounds, kernel_size, target);
955
3180
        } else {
956
3180
            self.box_blur_loop::<B, NotAlphaOnly>(&mut output_surface, bounds, kernel_size, target);
957
3180
        }
958

            
959
4560
        SharedImageSurface::wrap(output_surface, self.surface_type)
960
4560
    }
961

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

            
968
680
        let rgba = color_to_rgba(&color);
969

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

            
976
680
            set_source_color_on_cairo(&cr, &color);
977
680
            cr.paint()?;
978
        }
979

            
980
680
        SharedImageSurface::wrap(output_surface, self.surface_type)
981
680
    }
982

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

            
994
        // output_bounds contains all pixels within bounds,
995
        // for which (x - ox) and (y - oy) also lie within bounds.
996
480
        if let Some(output_bounds) = bounds.translate((dx, dy)).intersection(&bounds) {
997
440
            let cr = cairo::Context::new(&output_surface)?;
998
440
            let r = cairo::Rectangle::from(output_bounds);
999
440
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
440
            cr.clip();
440
            self.set_as_source_surface(&cr, dx, dy)?;
440
            cr.paint()?;
40
        }
480
        SharedImageSurface::wrap(output_surface, self.surface_type)
480
    }
    /// Returns a new surface of the same size, with the contents of the
    /// specified image, optionally transformed to match a given box
    #[inline]
1340
    pub fn paint_image(
1340
        &self,
1340
        bounds: Rect,
1340
        image: &SharedImageSurface,
1340
        rect: Option<Rect>,
1340
        interpolation: Interpolation,
1340
    ) -> Result<SharedImageSurface, cairo::Error> {
1340
        let output_surface =
1340
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
1340
        if rect.is_none() || !rect.unwrap().is_empty() {
1340
            let cr = cairo::Context::new(&output_surface)?;
1340
            let r = cairo::Rectangle::from(bounds);
1340
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
1340
            cr.clip();
1340
            image.set_as_source_surface(&cr, 0f64, 0f64)?;
1340
            if let Some(rect) = rect {
840
                let mut matrix = cairo::Matrix::new(
840
                    rect.width() / f64::from(image.width()),
840
                    0.0,
840
                    0.0,
840
                    rect.height() / f64::from(image.height()),
840
                    rect.x0,
840
                    rect.y0,
840
                );
840
                matrix.invert();
840

            
840
                cr.source().set_matrix(matrix);
840
                cr.source().set_filter(cairo::Filter::from(interpolation));
840
            }
1340
            cr.paint()?;
        }
1340
        SharedImageSurface::wrap(output_surface, image.surface_type)
1340
    }
    /// 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]
20
    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.
20
        assert!(!bounds.is_empty());
20
        let output_surface =
20
            cairo::ImageSurface::create(cairo::Format::ARgb32, bounds.width(), bounds.height())?;
        {
20
            let cr = cairo::Context::new(&output_surface)?;
20
            self.set_as_source_surface(&cr, f64::from(-bounds.x0), f64::from(-bounds.y0))?;
20
            cr.paint()?;
        }
20
        SharedImageSurface::wrap(output_surface, self.surface_type)
20
    }
    /// 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]
20
    pub fn paint_image_tiled(
20
        &self,
20
        bounds: IRect,
20
        image: &SharedImageSurface,
20
        x: i32,
20
        y: i32,
20
    ) -> Result<SharedImageSurface, cairo::Error> {
20
        let output_surface =
20
            cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
        {
20
            let cr = cairo::Context::new(&output_surface)?;
20
            let ptn = image.to_cairo_pattern();
20
            ptn.set_extend(cairo::Extend::Repeat);
20
            let mut mat = cairo::Matrix::identity();
20
            mat.translate(f64::from(-x), f64::from(-y));
20
            ptn.set_matrix(mat);
20
            let r = cairo::Rectangle::from(bounds);
20
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
20
            cr.clip();
20
            cr.set_source(&ptn)?;
20
            cr.paint()?;
        }
20
        SharedImageSurface::wrap(output_surface, image.surface_type)
20
    }
    /// Performs the combination of two input surfaces using Porter-Duff
    /// compositing operators.
    ///
    /// # Panics
    /// Panics if the two surface types are not compatible.
    #[inline]
1200
    pub fn compose(
1200
        &self,
1200
        other: &SharedImageSurface,
1200
        bounds: IRect,
1200
        operator: Operator,
1200
    ) -> Result<SharedImageSurface, cairo::Error> {
1200
        let output_surface = other.copy_surface(bounds)?;
        {
1200
            let cr = cairo::Context::new(&output_surface)?;
1200
            let r = cairo::Rectangle::from(bounds);
1200
            cr.rectangle(r.x(), r.y(), r.width(), r.height());
1200
            cr.clip();
1200
            self.set_as_source_surface(&cr, 0.0, 0.0)?;
1200
            cr.set_operator(operator.into());
1200
            cr.paint()?;
        }
1200
        SharedImageSurface::wrap(
1200
            output_surface,
1200
            self.surface_type.combine(other.surface_type),
        )
1200
    }
    /// 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]
240
    pub fn compose_arithmetic(
240
        &self,
240
        other: &SharedImageSurface,
240
        bounds: IRect,
240
        k1: f64,
240
        k2: f64,
240
        k3: f64,
240
        k4: f64,
240
    ) -> Result<SharedImageSurface, cairo::Error> {
240
        let mut output_surface = ExclusiveImageSurface::new(
240
            self.width,
240
            self.height,
240
            self.surface_type.combine(other.surface_type),
        )?;
240
        composite_arithmetic(self, other, &mut output_surface, bounds, k1, k2, k3, k4);
240
        output_surface.share()
240
    }
    pub fn rows(&self) -> Rows<'_> {
        Rows {
            surface: self,
            next_row: 0,
        }
    }
}
impl<'a> Iterator for Rows<'a> {
    type Item = &'a [CairoARGB];
    fn next(&mut self) -> Option<Self::Item> {
        if self.next_row == self.surface.height {
            return None;
        }
        let row = self.next_row;
        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 {
            let row_ptr: *const u8 = self
                .surface
                .data_ptr
                .as_ptr()
                .offset(row as isize * self.surface.stride);
            let row_of_u32: &[u32] =
                slice::from_raw_parts(row_ptr as *const u32, self.surface.width as usize);
            let pixels = row_of_u32.as_cairo_argb();
            assert!(pixels.len() == self.surface.width as usize);
            Some(pixels)
        }
    }
}
impl<'a> Iterator for RowsMut<'a> {
    type Item = &'a mut [CairoARGB];
82940
    fn next(&mut self) -> Option<Self::Item> {
82940
        if self.next_row == self.height {
            return None;
82940
        }
82940
        let row = self.next_row as usize;
82940
        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.
82940
            let data_ptr = self.data.as_mut_ptr();
82940
            let row_ptr: *mut u8 = data_ptr.offset(row as isize * self.stride as isize);
82940
            let row_of_u32: &mut [u32] =
82940
                slice::from_raw_parts_mut(row_ptr as *mut u32, self.width as usize);
82940
            let pixels = row_of_u32.as_cairo_argb_mut();
82940
            assert!(pixels.len() == self.width as usize);
82940
            Some(pixels)
        }
82940
    }
}
/// Performs the arithmetic composite operation. Public for benchmarking.
#[inline]
240
pub fn composite_arithmetic(
240
    surface1: &SharedImageSurface,
240
    surface2: &SharedImageSurface,
240
    output_surface: &mut ExclusiveImageSurface,
240
    bounds: IRect,
240
    k1: f64,
240
    k2: f64,
240
    k3: f64,
240
    k4: f64,
240
) {
240
    output_surface.modify(&mut |data, stride| {
2783280
        for (x, y, pixel, pixel_2) in
2783280
            Pixels::within(surface1, bounds).map(|(x, y, p)| (x, y, p, surface2.get_pixel(x, y)))
        {
2783280
            let i1a = f64::from(pixel.a) / 255f64;
2783280
            let i2a = f64::from(pixel_2.a) / 255f64;
2783280
            let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
2783280
            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.
2783280
            if oa > 0f64 {
5533740
                let compute = |i1, i2| {
5533740
                    let i1 = f64::from(i1) / 255f64;
5533740
                    let i2 = f64::from(i2) / 255f64;
5533740
                    let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
5533740
                    let o = clamp(o, 0f64, oa);
5533740
                    ((o * 255f64) + 0.5) as u8
5533740
                };
1844580
                let output_pixel = Pixel {
1844580
                    r: compute(pixel.r, pixel_2.r),
1844580
                    g: compute(pixel.g, pixel_2.g),
1844580
                    b: compute(pixel.b, pixel_2.b),
1844580
                    a: ((oa * 255f64) + 0.5) as u8,
1844580
                };
1844580
                data.set_pixel(stride, output_pixel, x, y);
938700
            }
        }
240
    });
240
}
impl ImageSurface<Exclusive> {
    #[inline]
15262
    pub fn new(
15262
        width: i32,
15262
        height: i32,
15262
        surface_type: SurfaceType,
15262
    ) -> Result<ExclusiveImageSurface, cairo::Error> {
15262
        let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
15262
        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.
15262
        if !(width > 0 && height > 0) {
            return Err(cairo::Error::InvalidSize);
15262
        }
15262
        let data_ptr = NonNull::new(unsafe {
15262
            cairo::ffi::cairo_image_surface_get_data(surface.to_raw_none())
        })
15262
        .unwrap();
15262
        let stride = surface.stride() as isize;
15262
        Ok(ExclusiveImageSurface {
15262
            surface,
15262
            data_ptr,
15262
            width,
15262
            height,
15262
            stride,
15262
            surface_type,
15262
            _state: PhantomData,
15262
        })
15262
    }
    #[inline]
15262
    pub fn share(self) -> Result<SharedImageSurface, cairo::Error> {
15262
        SharedImageSurface::wrap(self.surface, self.surface_type)
15262
    }
    /// Raw access to the image data as a slice
    #[inline]
14142
    pub fn data(&mut self) -> cairo::ImageSurfaceData<'_> {
14142
        self.surface.data().unwrap()
14142
    }
    /// Modify the image data
    #[inline]
12400
    pub fn modify(&mut self, draw_fn: &mut dyn FnMut(&mut cairo::ImageSurfaceData<'_>, usize)) {
12400
        let stride = self.stride() as usize;
12400
        let mut data = self.data();
12400
        draw_fn(&mut data, stride)
12400
    }
    /// Draw on the surface using cairo
    #[inline]
520
    pub fn draw(
520
        &mut self,
520
        draw_fn: &mut dyn FnMut(cairo::Context) -> Result<(), Box<InternalRenderingError>>,
520
    ) -> Result<(), Box<InternalRenderingError>> {
520
        let cr = cairo::Context::new(&self.surface)?;
520
        draw_fn(cr)
520
    }
600
    pub fn rows_mut(&mut self) -> RowsMut<'_> {
600
        let width = self.surface.width();
600
        let height = self.surface.height();
600
        let stride = self.surface.stride();
600
        let data = self.surface.data().unwrap();
600
        RowsMut {
600
            width,
600
            height,
600
            stride,
600
            data,
600
            next_row: 0,
600
        }
600
    }
}
impl From<Operator> for cairo::Operator {
1200
    fn from(op: Operator) -> cairo::Operator {
        use Operator::*;
        use cairo::Operator as Cairo;
1200
        match op {
580
            Over => Cairo::Over,
360
            In => Cairo::In,
60
            Out => Cairo::Out,
40
            Atop => Cairo::Atop,
40
            Xor => Cairo::Xor,
60
            Multiply => Cairo::Multiply,
20
            Screen => Cairo::Screen,
20
            Darken => Cairo::Darken,
20
            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,
        }
1200
    }
}
#[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;
2
        let bounds = IRect::new(8, 24, 16, 48);
2
        let full_bounds = IRect::from_size(WIDTH, HEIGHT);
2
        let mut surface = ExclusiveImageSurface::new(WIDTH, HEIGHT, SurfaceType::SRgb).unwrap();
        // Fill the surface with some data.
        {
2
            let mut data = surface.data();
2
            let mut counter = 0u16;
16384
            for x in data.iter_mut() {
16384
                *x = counter as u8;
16384
                counter = (counter + 1) % 256;
16384
            }
        }
2
        let surface = surface.share().unwrap();
2
        let alpha = surface.extract_alpha(bounds).unwrap();
4096
        for (x, y, p, pa) in
4096
            Pixels::within(&surface, full_bounds).map(|(x, y, p)| (x, y, p, alpha.get_pixel(x, y)))
        {
4096
            assert_eq!(pa.r, 0);
4096
            assert_eq!(pa.g, 0);
4096
            assert_eq!(pa.b, 0);
4096
            if !bounds.contains(x as i32, y as i32) {
3712
                assert_eq!(pa.a, 0);
            } else {
384
                assert_eq!(pa.a, p.a);
            }
        }
2
    }
}