1use std::cmp::min;
3use std::marker::PhantomData;
4use std::ptr::NonNull;
5use std::slice;
6
7use cast::i32;
8use nalgebra::{storage::Storage, Dim, Matrix};
9
10use crate::color::{color_to_rgba, Color};
11use crate::drawing_ctx::set_source_color_on_cairo;
12use crate::error::*;
13use crate::rect::{IRect, Rect};
14use crate::surface_utils::srgb;
15use crate::util::clamp;
16
17use super::{
18 iterators::{PixelRectangle, Pixels},
19 AsCairoARGB, CairoARGB, EdgeMode, ImageSurfaceDataExt, Pixel, PixelOps, ToCairoARGB, ToPixel,
20};
21
22pub enum Interpolation {
31 Nearest,
32 Smooth,
33}
34
35impl From<Interpolation> for cairo::Filter {
36 fn from(i: Interpolation) -> cairo::Filter {
37 match i {
40 Interpolation::Nearest => cairo::Filter::Nearest,
41 Interpolation::Smooth => cairo::Filter::Good,
42 }
43 }
44}
45
46#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
48pub enum SurfaceType {
49 SRgb,
51 LinearRgb,
53 AlphaOnly,
58}
59
60impl SurfaceType {
61 pub fn combine(self, other: SurfaceType) -> SurfaceType {
70 match (self, other) {
71 (SurfaceType::AlphaOnly, t) => t,
72 (t, SurfaceType::AlphaOnly) => t,
73 (t1, t2) if t1 == t2 => t1,
74 _ => panic!(),
75 }
76 }
77}
78
79pub 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#[derive(Debug, Clone)]
115pub struct ImageSurface<T> {
116 surface: cairo::ImageSurface,
117
118 data_ptr: NonNull<u8>, width: i32,
120 height: i32,
121 stride: isize,
122
123 surface_type: SurfaceType,
124
125 _state: PhantomData<T>,
126}
127
128#[derive(Debug, Clone)]
129pub struct Shared;
130
131pub type SharedImageSurface = ImageSurface<Shared>;
133
134#[derive(Debug, Clone)]
135pub struct Exclusive;
136
137pub type ExclusiveImageSurface = ImageSurface<Exclusive>;
139
140unsafe impl Sync for SharedImageSurface {}
142
143pub trait BlurDirection {
145 const IS_VERTICAL: bool;
146}
147
148pub enum Vertical {}
150pub enum Horizontal {}
152
153impl BlurDirection for Vertical {
154 const IS_VERTICAL: bool = true;
155}
156
157impl BlurDirection for Horizontal {
158 const IS_VERTICAL: bool = false;
159}
160
161pub trait IsAlphaOnly {
163 const IS_ALPHA_ONLY: bool;
164}
165
166pub enum AlphaOnly {}
168pub enum NotAlphaOnly {}
170
171pub struct Rows<'a> {
173 surface: &'a SharedImageSurface,
174 next_row: i32,
175}
176
177pub struct RowsMut<'a> {
179 data: cairo::ImageSurfaceData<'a>,
182
183 width: i32,
184 height: i32,
185 stride: i32,
186
187 next_row: i32,
188}
189
190impl IsAlphaOnly for AlphaOnly {
191 const IS_ALPHA_ONLY: bool = true;
192}
193
194impl IsAlphaOnly for NotAlphaOnly {
195 const IS_ALPHA_ONLY: bool = false;
196}
197
198impl<T> ImageSurface<T> {
199 #[inline]
201 pub fn width(&self) -> i32 {
202 self.width
203 }
204
205 #[inline]
207 pub fn height(&self) -> i32 {
208 self.height
209 }
210
211 #[inline]
213 pub fn stride(&self) -> isize {
214 self.stride
215 }
216}
217
218impl ImageSurface<Shared> {
219 #[inline]
225 pub fn wrap(
226 surface: cairo::ImageSurface,
227 surface_type: SurfaceType,
228 ) -> Result<SharedImageSurface, cairo::Error> {
229 assert_eq!(surface.format(), cairo::Format::ARgb32);
231
232 let reference_count =
233 unsafe { cairo::ffi::cairo_surface_get_reference_count(surface.to_raw_none()) };
234 assert_eq!(reference_count, 1);
235
236 let (width, height) = (surface.width(), surface.height());
237
238 if !(width > 0 && height > 0) {
242 return Err(cairo::Error::InvalidSize);
243 }
244
245 surface.flush();
246
247 let data_ptr = NonNull::new(unsafe {
248 cairo::ffi::cairo_image_surface_get_data(surface.to_raw_none())
249 })
250 .unwrap();
251
252 let stride = surface.stride() as isize;
253
254 Ok(SharedImageSurface {
255 surface,
256 data_ptr,
257 width,
258 height,
259 stride,
260 surface_type,
261 _state: PhantomData,
262 })
263 }
264
265 #[inline]
268 pub fn copy_from_surface(surface: &cairo::ImageSurface) -> Result<Self, cairo::Error> {
269 let copy =
270 cairo::ImageSurface::create(cairo::Format::ARgb32, surface.width(), surface.height())?;
271
272 {
273 let cr = cairo::Context::new(©)?;
274 cr.set_source_surface(surface, 0f64, 0f64)?;
275 cr.paint()?;
276 }
277
278 SharedImageSurface::wrap(copy, SurfaceType::SRgb)
279 }
280
281 #[inline]
283 pub fn empty(width: i32, height: i32, surface_type: SurfaceType) -> Result<Self, cairo::Error> {
284 let s = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
285
286 SharedImageSurface::wrap(s, surface_type)
287 }
288
289 #[inline]
291 pub fn into_image_surface(self) -> Result<cairo::ImageSurface, cairo::Error> {
292 let reference_count =
293 unsafe { cairo::ffi::cairo_surface_get_reference_count(self.surface.to_raw_none()) };
294
295 if reference_count == 1 {
296 Ok(self.surface)
297 } else {
298 self.copy_surface(IRect::from_size(self.width, self.height))
300 }
301 }
302
303 pub fn from_image(
304 image: &image::DynamicImage,
305 content_type: Option<&str>,
306 mime_data: Option<Vec<u8>>,
307 ) -> Result<SharedImageSurface, cairo::Error> {
308 let rgba_image = image.to_rgba8();
309
310 let width = i32(rgba_image.width()).map_err(|_| cairo::Error::InvalidSize)?;
311 let height = i32(rgba_image.height()).map_err(|_| cairo::Error::InvalidSize)?;
312
313 let mut surf = ExclusiveImageSurface::new(width, height, SurfaceType::SRgb)?;
314
315 rgba_image
316 .rows()
317 .zip(surf.rows_mut())
318 .flat_map(|(src_row, dest_row)| src_row.zip(dest_row.iter_mut()))
319 .for_each(|(src, dest)| *dest = src.to_pixel().premultiply().to_cairo_argb());
320
321 if let (Some(content_type), Some(bytes)) = (content_type, mime_data) {
322 surf.surface.set_mime_data(content_type, bytes)?;
323 }
324
325 surf.share()
326 }
327
328 #[inline]
330 fn is_alpha_only(&self) -> bool {
331 self.surface_type == SurfaceType::AlphaOnly
332 }
333
334 #[inline]
336 pub fn surface_type(&self) -> SurfaceType {
337 self.surface_type
338 }
339
340 #[inline]
342 pub fn get_pixel(&self, x: u32, y: u32) -> Pixel {
343 assert!(x < self.width as u32);
344 assert!(y < self.height as u32);
345
346 #[allow(clippy::cast_ptr_alignment)]
347 let value = unsafe {
348 *(self
349 .data_ptr
350 .as_ptr()
351 .offset(y as isize * self.stride + x as isize * 4) as *const u32)
352 };
353
354 Pixel::from_u32(value)
355 }
356
357 #[inline]
359 pub fn get_pixel_by_offset(&self, offset: isize) -> Pixel {
360 assert!(offset < self.stride * self.height as isize);
361
362 #[allow(clippy::cast_ptr_alignment)]
363 let value = unsafe { *(self.data_ptr.as_ptr().offset(offset) as *const u32) };
364 Pixel::from_u32(value)
365 }
366
367 #[inline]
369 pub fn set_as_source_surface(
370 &self,
371 cr: &cairo::Context,
372 x: f64,
373 y: f64,
374 ) -> Result<(), cairo::Error> {
375 cr.set_source_surface(&self.surface, x, y)
376 }
377
378 pub fn to_cairo_pattern(&self) -> cairo::SurfacePattern {
380 cairo::SurfacePattern::create(&self.surface)
381 }
382
383 fn copy_surface(&self, bounds: IRect) -> Result<cairo::ImageSurface, cairo::Error> {
386 let output_surface =
387 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
388
389 let cr = cairo::Context::new(&output_surface)?;
390 let r = cairo::Rectangle::from(bounds);
391 cr.rectangle(r.x(), r.y(), r.width(), r.height());
392 cr.clip();
393
394 cr.set_source_surface(&self.surface, 0f64, 0f64)?;
395 cr.paint()?;
396
397 Ok(output_surface)
398 }
399
400 pub fn scale_to(
403 &self,
404 width: i32,
405 height: i32,
406 bounds: IRect,
407 x: f64,
408 y: f64,
409 ) -> Result<SharedImageSurface, cairo::Error> {
410 let output_surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
411
412 {
413 let cr = cairo::Context::new(&output_surface)?;
414 let r = cairo::Rectangle::from(bounds);
415 cr.rectangle(r.x(), r.y(), r.width(), r.height());
416 cr.clip();
417
418 cr.scale(x, y);
419 self.set_as_source_surface(&cr, 0.0, 0.0)?;
420 cr.paint()?;
421 }
422
423 SharedImageSurface::wrap(output_surface, self.surface_type)
424 }
425
426 #[inline]
428 pub fn scale(
429 &self,
430 bounds: IRect,
431 x: f64,
432 y: f64,
433 ) -> Result<(SharedImageSurface, IRect), cairo::Error> {
434 let new_width = (f64::from(self.width) * x).ceil() as i32;
435 let new_height = (f64::from(self.height) * y).ceil() as i32;
436 let new_bounds = bounds.scale(x, y);
437
438 Ok((
439 self.scale_to(new_width, new_height, new_bounds, x, y)?,
440 new_bounds,
441 ))
442 }
443
444 pub fn extract_alpha(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
446 let mut output_surface =
447 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
448
449 let output_stride = output_surface.stride() as usize;
450 {
451 let mut output_data = output_surface.data().unwrap();
452
453 for (x, y, Pixel { a, .. }) in Pixels::within(self, bounds) {
454 let output_pixel = Pixel {
455 r: 0,
456 g: 0,
457 b: 0,
458 a,
459 };
460 output_data.set_pixel(output_stride, output_pixel, x, y);
461 }
462 }
463
464 SharedImageSurface::wrap(output_surface, SurfaceType::AlphaOnly)
465 }
466
467 pub fn to_luminance_mask(&self) -> Result<SharedImageSurface, cairo::Error> {
474 let bounds = IRect::from_size(self.width, self.height);
475
476 let mut output_surface =
477 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
478
479 let stride = output_surface.stride() as usize;
480 {
481 let mut data = output_surface.data().unwrap();
482
483 for (x, y, pixel) in Pixels::within(self, bounds) {
484 data.set_pixel(stride, pixel.to_luminance_mask(), x, y);
485 }
486 }
487
488 SharedImageSurface::wrap(output_surface, self.surface_type)
489 }
490
491 pub fn unpremultiply(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
496 if self.is_alpha_only() {
498 return Ok(self.clone());
499 }
500
501 let mut output_surface =
502 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
503
504 let stride = output_surface.stride() as usize;
505 {
506 let mut data = output_surface.data().unwrap();
507
508 for (x, y, pixel) in Pixels::within(self, bounds) {
509 data.set_pixel(stride, pixel.unpremultiply(), x, y);
510 }
511 }
512
513 SharedImageSurface::wrap(output_surface, self.surface_type)
514 }
515
516 #[inline]
518 pub fn to_linear_rgb(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
519 match self.surface_type {
520 SurfaceType::LinearRgb | SurfaceType::AlphaOnly => Ok(self.clone()),
521 _ => srgb::linearize_surface(self, bounds),
522 }
523 }
524
525 #[inline]
527 pub fn to_srgb(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
528 match self.surface_type {
529 SurfaceType::SRgb | SurfaceType::AlphaOnly => Ok(self.clone()),
530 _ => srgb::unlinearize_surface(self, bounds),
531 }
532 }
533
534 pub fn convolve<R: Dim, C: Dim, S: Storage<f64, R, C>>(
547 &self,
548 bounds: IRect,
549 target: (i32, i32),
550 kernel: &Matrix<f64, R, C, S>,
551 edge_mode: EdgeMode,
552 ) -> Result<SharedImageSurface, cairo::Error> {
553 assert!(kernel.nrows() >= 1);
554 assert!(kernel.ncols() >= 1);
555
556 let mut output_surface =
557 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
558
559 let output_stride = output_surface.stride() as usize;
560 {
561 let mut output_data = output_surface.data().unwrap();
562
563 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 for (x, y, _pixel) in Pixels::within(self, bounds) {
597 let kernel_bounds = IRect::new(
598 x as i32 - target.0,
599 y as i32 - target.1,
600 x as i32 - target.0 + kernel.ncols() as i32,
601 y as i32 - target.1 + kernel.nrows() as i32,
602 );
603
604 let mut r = 0.0;
605 let mut g = 0.0;
606 let mut b = 0.0;
607 let mut a = 0.0;
608
609 for (x, y, pixel) in
610 PixelRectangle::within(self, bounds, kernel_bounds, edge_mode)
611 {
612 let kernel_x = (kernel_bounds.x1 - x - 1) as usize;
613 let kernel_y = (kernel_bounds.y1 - y - 1) as usize;
614 let factor = kernel[(kernel_y, kernel_x)];
615
616 r += f64::from(pixel.r) * factor;
617 g += f64::from(pixel.g) * factor;
618 b += f64::from(pixel.b) * factor;
619 a += f64::from(pixel.a) * factor;
620 }
621
622 let convert = |x: f64| (clamp(x, 0.0, 255.0) + 0.5) as u8;
623
624 let output_pixel = Pixel {
625 r: convert(r),
626 g: convert(g),
627 b: convert(b),
628 a: convert(a),
629 };
630
631 output_data.set_pixel(output_stride, output_pixel, x, y);
632 }
633 }
634 }
635
636 SharedImageSurface::wrap(output_surface, self.surface_type)
637 }
638
639 pub fn box_blur_loop<B: BlurDirection, A: IsAlphaOnly>(
651 &self,
652 output_surface: &mut cairo::ImageSurface,
653 bounds: IRect,
654 kernel_size: usize,
655 target: usize,
656 ) {
657 assert_ne!(kernel_size, 0);
658 assert!(target < kernel_size);
659 assert_eq!(self.is_alpha_only(), A::IS_ALPHA_ONLY);
660
661 {
662 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 #[inline]
694 unsafe fn new(surface: &mut cairo::ImageSurface) -> Self {
695 assert_eq!(surface.format(), cairo::Format::ARgb32);
696 let ptr = surface.data().unwrap().as_mut_ptr();
697
698 Self {
699 width: surface.width() as u32,
700 height: surface.height() as u32,
701 stride: surface.stride() as isize,
702 ptr: NonNull::new(ptr).unwrap(),
703 _marker: PhantomData,
704 }
705 }
706
707 #[inline]
709 fn set_pixel(&mut self, pixel: Pixel, x: u32, y: u32) {
710 assert!(x < self.width);
711 assert!(y < self.height);
712
713 let value = pixel.to_u32();
714
715 #[allow(clippy::cast_ptr_alignment)]
716 unsafe {
717 let ptr = self
718 .ptr
719 .as_ptr()
720 .offset(y as isize * self.stride + x as isize * 4)
721 as *mut u32;
722 *ptr = value;
723 }
724 }
725
726 #[inline]
731 fn split_at_row(self, index: u32) -> (Self, Self) {
732 assert!(index <= self.height);
733
734 (
735 UnsafeSendPixelData {
736 width: self.width,
737 height: index,
738 stride: self.stride,
739 ptr: self.ptr,
740 _marker: PhantomData,
741 },
742 UnsafeSendPixelData {
743 width: self.width,
744 height: self.height - index,
745 stride: self.stride,
746 ptr: NonNull::new(unsafe {
747 self.ptr.as_ptr().offset(index as isize * self.stride)
748 })
749 .unwrap(),
750 _marker: PhantomData,
751 },
752 )
753 }
754
755 #[inline]
760 fn split_at_column(self, index: u32) -> (Self, Self) {
761 assert!(index <= self.width);
762
763 (
764 UnsafeSendPixelData {
765 width: index,
766 height: self.height,
767 stride: self.stride,
768 ptr: self.ptr,
769 _marker: PhantomData,
770 },
771 UnsafeSendPixelData {
772 width: self.width - index,
773 height: self.height,
774 stride: self.stride,
775 ptr: NonNull::new(unsafe {
776 self.ptr.as_ptr().offset(index as isize * 4)
777 })
778 .unwrap(),
779 _marker: PhantomData,
780 },
781 )
782 }
783 }
784
785 let output_data = unsafe { UnsafeSendPixelData::new(output_surface) };
786
787 let shift = (kernel_size - target) as i32;
789 let target = target as i32;
790
791 let kernel_size_f64 = kernel_size as f64;
793 let compute = |x: u32| (f64::from(x) / kernel_size_f64 + 0.5) as u8;
794
795 let (main_axis_min, main_axis_max, other_axis_min, other_axis_max) = if B::IS_VERTICAL {
801 (bounds.y0, bounds.y1, bounds.x0, bounds.x1)
802 } else {
803 (bounds.x0, bounds.x1, bounds.y0, bounds.y1)
804 };
805
806 let pixel = |i, j| {
808 let (x, y) = if B::IS_VERTICAL { (i, j) } else { (j, i) };
809
810 self.get_pixel(x as u32, y as u32)
811 };
812
813 let mut output_data = if B::IS_VERTICAL {
816 output_data.split_at_column(bounds.x0 as u32).1
817 } else {
818 output_data.split_at_row(bounds.y0 as u32).1
819 };
820
821 rayon::scope(|s| {
822 for i in other_axis_min..other_axis_max {
823 let (mut current, remaining) = if B::IS_VERTICAL {
827 output_data.split_at_column(1)
828 } else {
829 output_data.split_at_row(1)
830 };
831
832 output_data = remaining;
833
834 s.spawn(move |_| {
835 let mut set_pixel = |j, pixel| {
837 let (x, y) = if B::IS_VERTICAL { (0, j) } else { (j, 0) };
840 current.set_pixel(pixel, x, y);
841 };
842
843 let mut sum_r = 0;
850 let mut sum_g = 0;
851 let mut sum_b = 0;
852 let mut sum_a = 0;
853
854 for j in main_axis_min..min(main_axis_max, main_axis_min + shift) {
858 let Pixel { r, g, b, a } = pixel(i, j);
859
860 if !A::IS_ALPHA_ONLY {
861 sum_r += u32::from(r);
862 sum_g += u32::from(g);
863 sum_b += u32::from(b);
864 }
865
866 sum_a += u32::from(a);
867 }
868
869 set_pixel(
870 main_axis_min as u32,
871 Pixel {
872 r: compute(sum_r),
873 g: compute(sum_g),
874 b: compute(sum_b),
875 a: compute(sum_a),
876 },
877 );
878
879 let start_subtracting_at = main_axis_min + target + 1;
884
885 let stop_adding_at = main_axis_max - shift + 1;
888
889 for j in main_axis_min + 1..main_axis_max {
890 if j >= start_subtracting_at {
891 let old_pixel = pixel(i, j - target - 1);
892
893 if !A::IS_ALPHA_ONLY {
894 sum_r -= u32::from(old_pixel.r);
895 sum_g -= u32::from(old_pixel.g);
896 sum_b -= u32::from(old_pixel.b);
897 }
898
899 sum_a -= u32::from(old_pixel.a);
900 }
901
902 if j < stop_adding_at {
903 let new_pixel = pixel(i, j + shift - 1);
904
905 if !A::IS_ALPHA_ONLY {
906 sum_r += u32::from(new_pixel.r);
907 sum_g += u32::from(new_pixel.g);
908 sum_b += u32::from(new_pixel.b);
909 }
910
911 sum_a += u32::from(new_pixel.a);
912 }
913
914 set_pixel(
915 j as u32,
916 Pixel {
917 r: compute(sum_r),
918 g: compute(sum_g),
919 b: compute(sum_b),
920 a: compute(sum_a),
921 },
922 );
923 }
924 });
925 }
926 });
927 }
928
929 unsafe { cairo::ffi::cairo_surface_mark_dirty(output_surface.to_raw_none()) }
932 }
933
934 #[inline]
944 pub fn box_blur<B: BlurDirection>(
945 &self,
946 bounds: IRect,
947 kernel_size: usize,
948 target: usize,
949 ) -> Result<SharedImageSurface, cairo::Error> {
950 let mut output_surface =
951 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
952
953 if self.is_alpha_only() {
954 self.box_blur_loop::<B, AlphaOnly>(&mut output_surface, bounds, kernel_size, target);
955 } else {
956 self.box_blur_loop::<B, NotAlphaOnly>(&mut output_surface, bounds, kernel_size, target);
957 }
958
959 SharedImageSurface::wrap(output_surface, self.surface_type)
960 }
961
962 #[inline]
964 pub fn flood(&self, bounds: IRect, color: Color) -> Result<SharedImageSurface, cairo::Error> {
965 let output_surface =
966 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
967
968 let rgba = color_to_rgba(&color);
969
970 if rgba.alpha > 0.0 {
971 let cr = cairo::Context::new(&output_surface)?;
972 let r = cairo::Rectangle::from(bounds);
973 cr.rectangle(r.x(), r.y(), r.width(), r.height());
974 cr.clip();
975
976 set_source_color_on_cairo(&cr, &color);
977 cr.paint()?;
978 }
979
980 SharedImageSurface::wrap(output_surface, self.surface_type)
981 }
982
983 #[inline]
985 pub fn offset(
986 &self,
987 bounds: Rect,
988 dx: f64,
989 dy: f64,
990 ) -> Result<SharedImageSurface, cairo::Error> {
991 let output_surface =
992 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
993
994 if let Some(output_bounds) = bounds.translate((dx, dy)).intersection(&bounds) {
997 let cr = cairo::Context::new(&output_surface)?;
998 let r = cairo::Rectangle::from(output_bounds);
999 cr.rectangle(r.x(), r.y(), r.width(), r.height());
1000 cr.clip();
1001
1002 self.set_as_source_surface(&cr, dx, dy)?;
1003 cr.paint()?;
1004 }
1005
1006 SharedImageSurface::wrap(output_surface, self.surface_type)
1007 }
1008
1009 #[inline]
1012 pub fn paint_image(
1013 &self,
1014 bounds: Rect,
1015 image: &SharedImageSurface,
1016 rect: Option<Rect>,
1017 interpolation: Interpolation,
1018 ) -> Result<SharedImageSurface, cairo::Error> {
1019 let output_surface =
1020 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
1021
1022 if rect.is_none() || !rect.unwrap().is_empty() {
1023 let cr = cairo::Context::new(&output_surface)?;
1024 let r = cairo::Rectangle::from(bounds);
1025 cr.rectangle(r.x(), r.y(), r.width(), r.height());
1026 cr.clip();
1027
1028 image.set_as_source_surface(&cr, 0f64, 0f64)?;
1029
1030 if let Some(rect) = rect {
1031 let mut matrix = cairo::Matrix::new(
1032 rect.width() / f64::from(image.width()),
1033 0.0,
1034 0.0,
1035 rect.height() / f64::from(image.height()),
1036 rect.x0,
1037 rect.y0,
1038 );
1039 matrix.invert();
1040
1041 cr.source().set_matrix(matrix);
1042 cr.source().set_filter(cairo::Filter::from(interpolation));
1043 }
1044
1045 cr.paint()?;
1046 }
1047
1048 SharedImageSurface::wrap(output_surface, image.surface_type)
1049 }
1050
1051 #[inline]
1057 pub fn tile(&self, bounds: IRect) -> Result<SharedImageSurface, cairo::Error> {
1058 assert!(!bounds.is_empty());
1061
1062 let output_surface =
1063 cairo::ImageSurface::create(cairo::Format::ARgb32, bounds.width(), bounds.height())?;
1064
1065 {
1066 let cr = cairo::Context::new(&output_surface)?;
1067 self.set_as_source_surface(&cr, f64::from(-bounds.x0), f64::from(-bounds.y0))?;
1068 cr.paint()?;
1069 }
1070
1071 SharedImageSurface::wrap(output_surface, self.surface_type)
1072 }
1073
1074 #[inline]
1077 pub fn paint_image_tiled(
1078 &self,
1079 bounds: IRect,
1080 image: &SharedImageSurface,
1081 x: i32,
1082 y: i32,
1083 ) -> Result<SharedImageSurface, cairo::Error> {
1084 let output_surface =
1085 cairo::ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;
1086
1087 {
1088 let cr = cairo::Context::new(&output_surface)?;
1089
1090 let ptn = image.to_cairo_pattern();
1091 ptn.set_extend(cairo::Extend::Repeat);
1092 let mut mat = cairo::Matrix::identity();
1093 mat.translate(f64::from(-x), f64::from(-y));
1094 ptn.set_matrix(mat);
1095
1096 let r = cairo::Rectangle::from(bounds);
1097 cr.rectangle(r.x(), r.y(), r.width(), r.height());
1098 cr.clip();
1099
1100 cr.set_source(&ptn)?;
1101 cr.paint()?;
1102 }
1103
1104 SharedImageSurface::wrap(output_surface, image.surface_type)
1105 }
1106
1107 #[inline]
1113 pub fn compose(
1114 &self,
1115 other: &SharedImageSurface,
1116 bounds: IRect,
1117 operator: Operator,
1118 ) -> Result<SharedImageSurface, cairo::Error> {
1119 let output_surface = other.copy_surface(bounds)?;
1120
1121 {
1122 let cr = cairo::Context::new(&output_surface)?;
1123 let r = cairo::Rectangle::from(bounds);
1124 cr.rectangle(r.x(), r.y(), r.width(), r.height());
1125 cr.clip();
1126
1127 self.set_as_source_surface(&cr, 0.0, 0.0)?;
1128 cr.set_operator(operator.into());
1129 cr.paint()?;
1130 }
1131
1132 SharedImageSurface::wrap(
1133 output_surface,
1134 self.surface_type.combine(other.surface_type),
1135 )
1136 }
1137
1138 #[inline]
1146 pub fn compose_arithmetic(
1147 &self,
1148 other: &SharedImageSurface,
1149 bounds: IRect,
1150 k1: f64,
1151 k2: f64,
1152 k3: f64,
1153 k4: f64,
1154 ) -> Result<SharedImageSurface, cairo::Error> {
1155 let mut output_surface = ExclusiveImageSurface::new(
1156 self.width,
1157 self.height,
1158 self.surface_type.combine(other.surface_type),
1159 )?;
1160
1161 composite_arithmetic(self, other, &mut output_surface, bounds, k1, k2, k3, k4);
1162
1163 output_surface.share()
1164 }
1165
1166 pub fn rows(&self) -> Rows<'_> {
1167 Rows {
1168 surface: self,
1169 next_row: 0,
1170 }
1171 }
1172}
1173
1174impl<'a> Iterator for Rows<'a> {
1175 type Item = &'a [CairoARGB];
1176
1177 fn next(&mut self) -> Option<Self::Item> {
1178 if self.next_row == self.surface.height {
1179 return None;
1180 }
1181
1182 let row = self.next_row;
1183
1184 self.next_row += 1;
1185
1186 unsafe {
1195 let row_ptr: *const u8 = self
1196 .surface
1197 .data_ptr
1198 .as_ptr()
1199 .offset(row as isize * self.surface.stride);
1200 let row_of_u32: &[u32] =
1201 slice::from_raw_parts(row_ptr as *const u32, self.surface.width as usize);
1202 let pixels = row_of_u32.as_cairo_argb();
1203 assert!(pixels.len() == self.surface.width as usize);
1204 Some(pixels)
1205 }
1206 }
1207}
1208
1209impl<'a> Iterator for RowsMut<'a> {
1210 type Item = &'a mut [CairoARGB];
1211
1212 fn next(&mut self) -> Option<Self::Item> {
1213 if self.next_row == self.height {
1214 return None;
1215 }
1216
1217 let row = self.next_row as usize;
1218
1219 self.next_row += 1;
1220
1221 unsafe {
1230 let data_ptr = self.data.as_mut_ptr();
1235 let row_ptr: *mut u8 = data_ptr.offset(row as isize * self.stride as isize);
1236 let row_of_u32: &mut [u32] =
1237 slice::from_raw_parts_mut(row_ptr as *mut u32, self.width as usize);
1238 let pixels = row_of_u32.as_cairo_argb_mut();
1239 assert!(pixels.len() == self.width as usize);
1240 Some(pixels)
1241 }
1242 }
1243}
1244
1245#[inline]
1247pub fn composite_arithmetic(
1248 surface1: &SharedImageSurface,
1249 surface2: &SharedImageSurface,
1250 output_surface: &mut ExclusiveImageSurface,
1251 bounds: IRect,
1252 k1: f64,
1253 k2: f64,
1254 k3: f64,
1255 k4: f64,
1256) {
1257 output_surface.modify(&mut |data, stride| {
1258 for (x, y, pixel, pixel_2) in
1259 Pixels::within(surface1, bounds).map(|(x, y, p)| (x, y, p, surface2.get_pixel(x, y)))
1260 {
1261 let i1a = f64::from(pixel.a) / 255f64;
1262 let i2a = f64::from(pixel_2.a) / 255f64;
1263 let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
1264 let oa = clamp(oa, 0f64, 1f64);
1265
1266 if oa > 0f64 {
1269 let compute = |i1, i2| {
1270 let i1 = f64::from(i1) / 255f64;
1271 let i2 = f64::from(i2) / 255f64;
1272
1273 let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
1274 let o = clamp(o, 0f64, oa);
1275
1276 ((o * 255f64) + 0.5) as u8
1277 };
1278
1279 let output_pixel = Pixel {
1280 r: compute(pixel.r, pixel_2.r),
1281 g: compute(pixel.g, pixel_2.g),
1282 b: compute(pixel.b, pixel_2.b),
1283 a: ((oa * 255f64) + 0.5) as u8,
1284 };
1285
1286 data.set_pixel(stride, output_pixel, x, y);
1287 }
1288 }
1289 });
1290}
1291
1292impl ImageSurface<Exclusive> {
1293 #[inline]
1294 pub fn new(
1295 width: i32,
1296 height: i32,
1297 surface_type: SurfaceType,
1298 ) -> Result<ExclusiveImageSurface, cairo::Error> {
1299 let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
1300
1301 let (width, height) = (surface.width(), surface.height());
1302
1303 if !(width > 0 && height > 0) {
1307 return Err(cairo::Error::InvalidSize);
1308 }
1309
1310 let data_ptr = NonNull::new(unsafe {
1311 cairo::ffi::cairo_image_surface_get_data(surface.to_raw_none())
1312 })
1313 .unwrap();
1314
1315 let stride = surface.stride() as isize;
1316
1317 Ok(ExclusiveImageSurface {
1318 surface,
1319 data_ptr,
1320 width,
1321 height,
1322 stride,
1323 surface_type,
1324 _state: PhantomData,
1325 })
1326 }
1327
1328 #[inline]
1329 pub fn share(self) -> Result<SharedImageSurface, cairo::Error> {
1330 SharedImageSurface::wrap(self.surface, self.surface_type)
1331 }
1332
1333 #[inline]
1335 pub fn data(&mut self) -> cairo::ImageSurfaceData<'_> {
1336 self.surface.data().unwrap()
1337 }
1338
1339 #[inline]
1341 pub fn modify(&mut self, draw_fn: &mut dyn FnMut(&mut cairo::ImageSurfaceData<'_>, usize)) {
1342 let stride = self.stride() as usize;
1343 let mut data = self.data();
1344
1345 draw_fn(&mut data, stride)
1346 }
1347
1348 #[inline]
1350 pub fn draw(
1351 &mut self,
1352 draw_fn: &mut dyn FnMut(cairo::Context) -> Result<(), Box<InternalRenderingError>>,
1353 ) -> Result<(), Box<InternalRenderingError>> {
1354 let cr = cairo::Context::new(&self.surface)?;
1355 draw_fn(cr)
1356 }
1357
1358 pub fn rows_mut(&mut self) -> RowsMut<'_> {
1359 let width = self.surface.width();
1360 let height = self.surface.height();
1361 let stride = self.surface.stride();
1362
1363 let data = self.surface.data().unwrap();
1364
1365 RowsMut {
1366 width,
1367 height,
1368 stride,
1369 data,
1370 next_row: 0,
1371 }
1372 }
1373}
1374
1375impl From<Operator> for cairo::Operator {
1376 fn from(op: Operator) -> cairo::Operator {
1377 use cairo::Operator as Cairo;
1378 use Operator::*;
1379
1380 match op {
1381 Over => Cairo::Over,
1382 In => Cairo::In,
1383 Out => Cairo::Out,
1384 Atop => Cairo::Atop,
1385 Xor => Cairo::Xor,
1386 Multiply => Cairo::Multiply,
1387 Screen => Cairo::Screen,
1388 Darken => Cairo::Darken,
1389 Lighten => Cairo::Lighten,
1390 Overlay => Cairo::Overlay,
1391 ColorDodge => Cairo::ColorDodge,
1392 ColorBurn => Cairo::ColorBurn,
1393 HardLight => Cairo::HardLight,
1394 SoftLight => Cairo::SoftLight,
1395 Difference => Cairo::Difference,
1396 Exclusion => Cairo::Exclusion,
1397 HslHue => Cairo::HslHue,
1398 HslSaturation => Cairo::HslSaturation,
1399 HslColor => Cairo::HslColor,
1400 HslLuminosity => Cairo::HslLuminosity,
1401 }
1402 }
1403}
1404
1405#[cfg(test)]
1406mod tests {
1407 use super::*;
1408 use crate::surface_utils::iterators::Pixels;
1409
1410 #[test]
1411 fn test_extract_alpha() {
1412 const WIDTH: i32 = 32;
1413 const HEIGHT: i32 = 64;
1414
1415 let bounds = IRect::new(8, 24, 16, 48);
1416 let full_bounds = IRect::from_size(WIDTH, HEIGHT);
1417
1418 let mut surface = ExclusiveImageSurface::new(WIDTH, HEIGHT, SurfaceType::SRgb).unwrap();
1419
1420 {
1422 let mut data = surface.data();
1423
1424 let mut counter = 0u16;
1425 for x in data.iter_mut() {
1426 *x = counter as u8;
1427 counter = (counter + 1) % 256;
1428 }
1429 }
1430
1431 let surface = surface.share().unwrap();
1432 let alpha = surface.extract_alpha(bounds).unwrap();
1433
1434 for (x, y, p, pa) in
1435 Pixels::within(&surface, full_bounds).map(|(x, y, p)| (x, y, p, alpha.get_pixel(x, y)))
1436 {
1437 assert_eq!(pa.r, 0);
1438 assert_eq!(pa.g, 0);
1439 assert_eq!(pa.b, 0);
1440
1441 if !bounds.contains(x as i32, y as i32) {
1442 assert_eq!(pa.a, 0);
1443 } else {
1444 assert_eq!(pa.a, p.a);
1445 }
1446 }
1447 }
1448}