Skip to main content

tiny_skia/
pixmap.rs

1// Copyright 2006 The Android Open Source Project
2// Copyright 2020 Yevhenii Reizner
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7use alloc::vec;
8use alloc::vec::Vec;
9
10use core::convert::TryFrom;
11use core::num::NonZeroUsize;
12
13use tiny_skia_path::IntSize;
14
15use crate::{Color, IntRect};
16
17use crate::color::PremultipliedColorU8;
18use crate::geom::{IntSizeExt, ScreenIntRect};
19
20#[cfg(feature = "png-format")]
21use crate::color::{premultiply_u8, ALPHA_U8_OPAQUE};
22
23/// Number of bytes per pixel.
24pub const BYTES_PER_PIXEL: usize = 4;
25
26/// A container that owns premultiplied RGBA pixels.
27///
28/// The data is not aligned, therefore width == stride.
29#[derive(Clone, PartialEq)]
30pub struct Pixmap {
31    data: Vec<u8>,
32    size: IntSize,
33}
34
35impl Pixmap {
36    /// Allocates a new pixmap.
37    ///
38    /// A pixmap is filled with transparent black by default, aka (0, 0, 0, 0).
39    ///
40    /// Zero size in an error.
41    ///
42    /// Pixmap's width is limited by i32::MAX/4.
43    pub fn new(width: u32, height: u32) -> Option<Self> {
44        let size = IntSize::from_wh(width, height)?;
45        let data_len = data_len_for_size(size)?;
46
47        // We cannot check that allocation was successful yet.
48        // We have to wait for https://github.com/rust-lang/rust/issues/48043
49
50        Some(Pixmap {
51            data: vec![0; data_len],
52            size,
53        })
54    }
55
56    /// Creates a new pixmap by taking ownership over an image buffer
57    /// (premultiplied RGBA pixels).
58    ///
59    /// The size needs to match the data provided.
60    ///
61    /// Pixmap's width is limited by i32::MAX/4.
62    pub fn from_vec(data: Vec<u8>, size: IntSize) -> Option<Self> {
63        let data_len = data_len_for_size(size)?;
64        if data.len() != data_len {
65            return None;
66        }
67
68        Some(Pixmap { data, size })
69    }
70
71    /// Decodes a PNG data into a `Pixmap`.
72    ///
73    /// Only 8-bit images are supported.
74    /// Index PNGs are not supported.
75    #[cfg(feature = "png-format")]
76    pub fn decode_png(data: &[u8]) -> Result<Self, png::DecodingError> {
77        fn make_custom_png_error(msg: &str) -> png::DecodingError {
78            std::io::Error::new(std::io::ErrorKind::Other, msg).into()
79        }
80
81        let mut decoder = png::Decoder::new(std::io::BufReader::new(std::io::Cursor::new(data)));
82        decoder.set_transformations(png::Transformations::normalize_to_color8());
83        let mut reader = decoder.read_info()?;
84        let output_buffer_size = reader
85            .output_buffer_size()
86            .ok_or(png::DecodingError::LimitsExceeded)?;
87        let mut img_data = vec![0; output_buffer_size];
88        let info = reader.next_frame(&mut img_data)?;
89
90        if info.bit_depth != png::BitDepth::Eight {
91            return Err(make_custom_png_error("unsupported bit depth"));
92        }
93
94        let size = IntSize::from_wh(info.width, info.height)
95            .ok_or_else(|| make_custom_png_error("invalid image size"))?;
96        let data_len =
97            data_len_for_size(size).ok_or_else(|| make_custom_png_error("image is too big"))?;
98
99        img_data = match info.color_type {
100            png::ColorType::Rgb => {
101                let mut rgba_data = Vec::with_capacity(data_len);
102                for rgb in img_data.chunks(3) {
103                    rgba_data.push(rgb[0]);
104                    rgba_data.push(rgb[1]);
105                    rgba_data.push(rgb[2]);
106                    rgba_data.push(ALPHA_U8_OPAQUE);
107                }
108
109                rgba_data
110            }
111            png::ColorType::Rgba => img_data,
112            png::ColorType::Grayscale => {
113                let mut rgba_data = Vec::with_capacity(data_len);
114                for gray in img_data {
115                    rgba_data.push(gray);
116                    rgba_data.push(gray);
117                    rgba_data.push(gray);
118                    rgba_data.push(ALPHA_U8_OPAQUE);
119                }
120
121                rgba_data
122            }
123            png::ColorType::GrayscaleAlpha => {
124                let mut rgba_data = Vec::with_capacity(data_len);
125                for slice in img_data.chunks(2) {
126                    let gray = slice[0];
127                    let alpha = slice[1];
128                    rgba_data.push(gray);
129                    rgba_data.push(gray);
130                    rgba_data.push(gray);
131                    rgba_data.push(alpha);
132                }
133
134                rgba_data
135            }
136            png::ColorType::Indexed => {
137                return Err(make_custom_png_error("indexed PNG is not supported"));
138            }
139        };
140
141        // Premultiply alpha.
142        //
143        // We cannon use RasterPipeline here, which is faster,
144        // because it produces slightly different results.
145        // Seems like Skia does the same.
146        //
147        // Also, in our tests unsafe version (no bound checking)
148        // had roughly the same performance. So we keep the safe one.
149        for pixel in img_data.as_mut_slice().chunks_mut(BYTES_PER_PIXEL) {
150            let a = pixel[3];
151            pixel[0] = premultiply_u8(pixel[0], a);
152            pixel[1] = premultiply_u8(pixel[1], a);
153            pixel[2] = premultiply_u8(pixel[2], a);
154        }
155
156        Pixmap::from_vec(img_data, size)
157            .ok_or_else(|| make_custom_png_error("failed to create a pixmap"))
158    }
159
160    /// Loads a PNG file into a `Pixmap`.
161    ///
162    /// Only 8-bit images are supported.
163    /// Index PNGs are not supported.
164    #[cfg(feature = "png-format")]
165    pub fn load_png<P: AsRef<std::path::Path>>(path: P) -> Result<Self, png::DecodingError> {
166        // `png::Decoder` is generic over input, which means that it will instance
167        // two copies: one for `&[]` and one for `File`. Which will simply bloat the code.
168        // Therefore we're using only one type for input.
169        let data = std::fs::read(path)?;
170        Self::decode_png(&data)
171    }
172
173    /// Encodes pixmap into a PNG data.
174    #[cfg(feature = "png-format")]
175    pub fn encode_png(&self) -> Result<Vec<u8>, png::EncodingError> {
176        self.as_ref().encode_png()
177    }
178
179    /// Saves pixmap as a PNG file.
180    #[cfg(feature = "png-format")]
181    pub fn save_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), png::EncodingError> {
182        self.as_ref().save_png(path)
183    }
184
185    /// Returns a container that references Pixmap's data.
186    pub fn as_ref(&self) -> PixmapRef<'_> {
187        PixmapRef {
188            data: &self.data,
189            size: self.size,
190        }
191    }
192
193    /// Returns a container that references Pixmap's data.
194    pub fn as_mut(&mut self) -> PixmapMut<'_> {
195        PixmapMut {
196            data: &mut self.data,
197            size: self.size,
198        }
199    }
200
201    /// Returns pixmap's width.
202    #[inline]
203    pub fn width(&self) -> u32 {
204        self.size.width()
205    }
206
207    /// Returns pixmap's height.
208    #[inline]
209    pub fn height(&self) -> u32 {
210        self.size.height()
211    }
212
213    /// Returns pixmap's size.
214    #[allow(dead_code)]
215    pub(crate) fn size(&self) -> IntSize {
216        self.size
217    }
218
219    /// Fills the entire pixmap with a specified color.
220    pub fn fill(&mut self, color: Color) {
221        let c = color.premultiply().to_color_u8();
222        for p in self.as_mut().pixels_mut() {
223            *p = c;
224        }
225    }
226
227    /// Returns the internal data.
228    ///
229    /// Byteorder: RGBA
230    pub fn data(&self) -> &[u8] {
231        self.data.as_slice()
232    }
233
234    /// Returns the mutable internal data.
235    ///
236    /// Byteorder: RGBA
237    pub fn data_mut(&mut self) -> &mut [u8] {
238        self.data.as_mut_slice()
239    }
240
241    /// Returns a pixel color.
242    ///
243    /// Returns `None` when position is out of bounds.
244    pub fn pixel(&self, x: u32, y: u32) -> Option<PremultipliedColorU8> {
245        let idx = self.width().checked_mul(y)?.checked_add(x)?;
246        self.pixels().get(idx as usize).cloned()
247    }
248
249    /// Returns a mutable slice of pixels.
250    pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] {
251        bytemuck::cast_slice_mut(self.data_mut())
252    }
253
254    /// Returns a slice of pixels.
255    pub fn pixels(&self) -> &[PremultipliedColorU8] {
256        bytemuck::cast_slice(self.data())
257    }
258
259    /// Consumes the internal data.
260    ///
261    /// Byteorder: RGBA
262    pub fn take(self) -> Vec<u8> {
263        self.data
264    }
265
266    /// Consumes the pixmap and returns the internal data as demultiplied RGBA bytes.
267    ///
268    /// Byteorder: RGBA
269    pub fn take_demultiplied(mut self) -> Vec<u8> {
270        // Demultiply alpha.
271        //
272        // RasterPipeline is 15% faster here, but produces slightly different results
273        // due to rounding. So we stick with this method for now.
274        for pixel in self.pixels_mut() {
275            let c = pixel.demultiply();
276            *pixel =
277                PremultipliedColorU8::from_rgba_unchecked(c.red(), c.green(), c.blue(), c.alpha());
278        }
279        self.data
280    }
281
282    /// Returns a copy of the pixmap that intersects the `rect`.
283    ///
284    /// Returns `None` when `Pixmap`'s rect doesn't contain `rect`.
285    pub fn clone_rect(&self, rect: IntRect) -> Option<Pixmap> {
286        self.as_ref().clone_rect(rect)
287    }
288}
289
290impl core::fmt::Debug for Pixmap {
291    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
292        f.debug_struct("Pixmap")
293            .field("data", &"...")
294            .field("width", &self.size.width())
295            .field("height", &self.size.height())
296            .finish()
297    }
298}
299
300/// A container that references premultiplied RGBA pixels.
301///
302/// Can be created from `Pixmap` or from a user provided data.
303///
304/// The data is not aligned, therefore width == stride.
305#[derive(Clone, Copy, PartialEq)]
306pub struct PixmapRef<'a> {
307    data: &'a [u8],
308    size: IntSize,
309}
310
311impl<'a> PixmapRef<'a> {
312    /// Creates a new `PixmapRef` from bytes.
313    ///
314    /// The size must be at least `size.width() * size.height() * BYTES_PER_PIXEL`.
315    /// Zero size in an error. Width is limited by i32::MAX/4.
316    ///
317    /// The `data` is assumed to have premultiplied RGBA pixels (byteorder: RGBA).
318    pub fn from_bytes(data: &'a [u8], width: u32, height: u32) -> Option<Self> {
319        let size = IntSize::from_wh(width, height)?;
320        let data_len = data_len_for_size(size)?;
321        if data.len() < data_len {
322            return None;
323        }
324
325        Some(PixmapRef { data, size })
326    }
327
328    /// Creates a new `Pixmap` from the current data.
329    ///
330    /// Clones the underlying data.
331    pub fn to_owned(&self) -> Pixmap {
332        Pixmap {
333            data: self.data.to_vec(),
334            size: self.size,
335        }
336    }
337
338    /// Returns pixmap's width.
339    #[inline]
340    pub fn width(&self) -> u32 {
341        self.size.width()
342    }
343
344    /// Returns pixmap's height.
345    #[inline]
346    pub fn height(&self) -> u32 {
347        self.size.height()
348    }
349
350    /// Returns pixmap's size.
351    pub(crate) fn size(&self) -> IntSize {
352        self.size
353    }
354
355    /// Returns pixmap's rect.
356    pub(crate) fn rect(&self) -> ScreenIntRect {
357        self.size.to_screen_int_rect(0, 0)
358    }
359
360    /// Returns the internal data.
361    ///
362    /// Byteorder: RGBA
363    pub fn data(&self) -> &'a [u8] {
364        self.data
365    }
366
367    /// Returns a pixel color.
368    ///
369    /// Returns `None` when position is out of bounds.
370    pub fn pixel(&self, x: u32, y: u32) -> Option<PremultipliedColorU8> {
371        let idx = self.width().checked_mul(y)?.checked_add(x)?;
372        self.pixels().get(idx as usize).cloned()
373    }
374
375    /// Returns a slice of pixels.
376    pub fn pixels(&self) -> &'a [PremultipliedColorU8] {
377        bytemuck::cast_slice(self.data())
378    }
379
380    // TODO: add rows() iterator
381
382    /// Returns a copy of the pixmap that intersects the `rect`.
383    ///
384    /// Returns `None` when `Pixmap`'s rect doesn't contain `rect`.
385    pub fn clone_rect(&self, rect: IntRect) -> Option<Pixmap> {
386        // TODO: to ScreenIntRect?
387
388        let rect = self.rect().to_int_rect().intersect(&rect)?;
389        let mut new = Pixmap::new(rect.width(), rect.height())?;
390        {
391            let old_pixels = self.pixels();
392            let mut new_mut = new.as_mut();
393            let new_pixels = new_mut.pixels_mut();
394
395            // TODO: optimize
396            for y in 0..rect.height() {
397                for x in 0..rect.width() {
398                    let old_idx = (y + rect.y() as u32) * self.width() + (x + rect.x() as u32);
399                    let new_idx = y * rect.width() + x;
400                    new_pixels[new_idx as usize] = old_pixels[old_idx as usize];
401                }
402            }
403        }
404
405        Some(new)
406    }
407
408    /// Encodes pixmap into a PNG data.
409    #[cfg(feature = "png-format")]
410    pub fn encode_png(&self) -> Result<Vec<u8>, png::EncodingError> {
411        // Skia uses skcms here, which is somewhat similar to RasterPipeline.
412
413        // Sadly, we have to copy the pixmap here, because of demultiplication.
414        // Not sure how to avoid this.
415        // TODO: remove allocation
416        let demultiplied_data = self.to_owned().take_demultiplied();
417
418        let mut data = Vec::new();
419        {
420            let mut encoder = png::Encoder::new(&mut data, self.width(), self.height());
421            encoder.set_color(png::ColorType::Rgba);
422            encoder.set_depth(png::BitDepth::Eight);
423            let mut writer = encoder.write_header()?;
424            writer.write_image_data(&demultiplied_data)?;
425        }
426
427        Ok(data)
428    }
429
430    /// Saves pixmap as a PNG file.
431    #[cfg(feature = "png-format")]
432    pub fn save_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), png::EncodingError> {
433        let data = self.encode_png()?;
434        std::fs::write(path, data)?;
435        Ok(())
436    }
437}
438
439impl core::fmt::Debug for PixmapRef<'_> {
440    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
441        f.debug_struct("PixmapRef")
442            .field("data", &"...")
443            .field("width", &self.size.width())
444            .field("height", &self.size.height())
445            .finish()
446    }
447}
448
449/// A container that references mutable premultiplied RGBA pixels.
450///
451/// Can be created from `Pixmap` or from a user provided data.
452///
453/// The data is not aligned, therefore width == stride.
454#[derive(PartialEq)]
455pub struct PixmapMut<'a> {
456    data: &'a mut [u8],
457    size: IntSize,
458}
459
460impl<'a> PixmapMut<'a> {
461    /// Creates a new `PixmapMut` from bytes.
462    ///
463    /// The size must be at least `size.width() * size.height() * BYTES_PER_PIXEL`.
464    /// Zero size in an error. Width is limited by i32::MAX/4.
465    ///
466    /// The `data` is assumed to have premultiplied RGBA pixels (byteorder: RGBA).
467    pub fn from_bytes(data: &'a mut [u8], width: u32, height: u32) -> Option<Self> {
468        let size = IntSize::from_wh(width, height)?;
469        let data_len = data_len_for_size(size)?;
470        if data.len() < data_len {
471            return None;
472        }
473
474        Some(PixmapMut { data, size })
475    }
476
477    /// Creates a new `Pixmap` from the current data.
478    ///
479    /// Clones the underlying data.
480    pub fn to_owned(&self) -> Pixmap {
481        Pixmap {
482            data: self.data.to_vec(),
483            size: self.size,
484        }
485    }
486
487    /// Returns a container that references Pixmap's data.
488    pub fn as_ref(&self) -> PixmapRef<'_> {
489        PixmapRef {
490            data: self.data,
491            size: self.size,
492        }
493    }
494
495    /// Returns pixmap's width.
496    #[inline]
497    pub fn width(&self) -> u32 {
498        self.size.width()
499    }
500
501    /// Returns pixmap's height.
502    #[inline]
503    pub fn height(&self) -> u32 {
504        self.size.height()
505    }
506
507    /// Returns pixmap's size.
508    pub(crate) fn size(&self) -> IntSize {
509        self.size
510    }
511
512    /// Fills the entire pixmap with a specified color.
513    pub fn fill(&mut self, color: Color) {
514        let c = color.premultiply().to_color_u8();
515        for p in self.pixels_mut() {
516            *p = c;
517        }
518    }
519
520    /// Returns the mutable internal data.
521    ///
522    /// Byteorder: RGBA
523    pub fn data_mut(&mut self) -> &mut [u8] {
524        self.data
525    }
526
527    /// Returns a mutable slice of pixels.
528    pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] {
529        bytemuck::cast_slice_mut(self.data_mut())
530    }
531
532    /// Creates `SubPixmapMut` that contains the whole `PixmapMut`.
533    pub(crate) fn as_subpixmap(&mut self) -> SubPixmapMut<'_> {
534        SubPixmapMut {
535            size: self.size(),
536            real_width: self.width() as usize,
537            data: self.data,
538        }
539    }
540
541    /// Returns a mutable reference to the pixmap region that intersects the `rect`.
542    ///
543    /// Returns `None` when `Pixmap`'s rect doesn't contain `rect`.
544    pub(crate) fn subpixmap(&mut self, rect: IntRect) -> Option<SubPixmapMut<'_>> {
545        let rect = self.size.to_int_rect(0, 0).intersect(&rect)?;
546        let row_bytes = self.width() as usize * BYTES_PER_PIXEL;
547        let offset = rect.top() as usize * row_bytes + rect.left() as usize * BYTES_PER_PIXEL;
548
549        Some(SubPixmapMut {
550            size: rect.size(),
551            real_width: self.width() as usize,
552            data: &mut self.data[offset..],
553        })
554    }
555}
556
557impl core::fmt::Debug for PixmapMut<'_> {
558    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
559        f.debug_struct("PixmapMut")
560            .field("data", &"...")
561            .field("width", &self.size.width())
562            .field("height", &self.size.height())
563            .finish()
564    }
565}
566
567/// A `PixmapMut` subregion.
568///
569/// Unlike `PixmapMut`, contains `real_width` which references the parent `PixmapMut` width.
570/// This way we can operate on a `PixmapMut` subregion without reallocations.
571/// Primarily required because of `DrawTiler`.
572///
573/// We cannot implement it in `PixmapMut` directly, because it will brake `fill`, `data_mut`
574/// `pixels_mut` and other similar methods.
575/// This is because `SubPixmapMut.data` references more "data" than it actually allowed to access.
576/// On the other hand, `PixmapMut.data` can access all it's data and it's stored linearly.
577pub struct SubPixmapMut<'a> {
578    pub data: &'a mut [u8],
579    pub size: IntSize,
580    pub real_width: usize,
581}
582
583impl SubPixmapMut<'_> {
584    /// Returns a mutable slice of pixels.
585    pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] {
586        bytemuck::cast_slice_mut(self.data)
587    }
588}
589
590/// Returns minimum bytes per row as usize.
591///
592/// Pixmap's maximum value for row bytes must fit in 31 bits.
593fn min_row_bytes(size: IntSize) -> Option<NonZeroUsize> {
594    let w = i32::try_from(size.width()).ok()?;
595    let w = w.checked_mul(BYTES_PER_PIXEL as i32)?;
596    NonZeroUsize::new(w as usize)
597}
598
599/// Returns storage size required by pixel array.
600fn compute_data_len(size: IntSize, row_bytes: usize) -> Option<usize> {
601    let h = size.height().checked_sub(1)?;
602    let h = (h as usize).checked_mul(row_bytes)?;
603
604    let w = (size.width() as usize).checked_mul(BYTES_PER_PIXEL)?;
605
606    h.checked_add(w)
607}
608
609fn data_len_for_size(size: IntSize) -> Option<usize> {
610    let row_bytes = min_row_bytes(size)?;
611    compute_data_len(size, row_bytes.get())
612}