png/
common.rs

1//! Common types shared between the encoder and decoder
2use crate::text_metadata::{EncodableTextChunk, ITXtChunk, TEXtChunk, ZTXtChunk};
3use crate::{chunk, encoder};
4use io::Write;
5use std::{borrow::Cow, convert::TryFrom, fmt, io};
6
7/// Describes how a pixel is encoded.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum ColorType {
11    /// 1 grayscale sample.
12    Grayscale = 0,
13    /// 1 red sample, 1 green sample, 1 blue sample.
14    Rgb = 2,
15    /// 1 sample for the palette index.
16    Indexed = 3,
17    /// 1 grayscale sample, then 1 alpha sample.
18    GrayscaleAlpha = 4,
19    /// 1 red sample, 1 green sample, 1 blue sample, and finally, 1 alpha sample.
20    Rgba = 6,
21}
22
23impl ColorType {
24    /// Returns the number of samples used per pixel encoded in this way.
25    pub fn samples(self) -> usize {
26        self.samples_u8().into()
27    }
28
29    pub(crate) fn samples_u8(self) -> u8 {
30        use self::ColorType::*;
31        match self {
32            Grayscale | Indexed => 1,
33            Rgb => 3,
34            GrayscaleAlpha => 2,
35            Rgba => 4,
36        }
37    }
38
39    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
40    pub fn from_u8(n: u8) -> Option<ColorType> {
41        match n {
42            0 => Some(ColorType::Grayscale),
43            2 => Some(ColorType::Rgb),
44            3 => Some(ColorType::Indexed),
45            4 => Some(ColorType::GrayscaleAlpha),
46            6 => Some(ColorType::Rgba),
47            _ => None,
48        }
49    }
50
51    pub(crate) fn checked_raw_row_length(self, depth: BitDepth, width: u32) -> Option<usize> {
52        // No overflow can occur in 64 bits, we multiply 32-bit with 5 more bits.
53        let bits = u64::from(width) * u64::from(self.samples_u8()) * u64::from(depth.into_u8());
54        TryFrom::try_from(1 + (bits + 7) / 8).ok()
55    }
56
57    pub(crate) fn raw_row_length_from_width(self, depth: BitDepth, width: u32) -> usize {
58        let samples = width as usize * self.samples();
59        1 + match depth {
60            BitDepth::Sixteen => samples * 2,
61            BitDepth::Eight => samples,
62            subbyte => {
63                let samples_per_byte = 8 / subbyte as usize;
64                let whole = samples / samples_per_byte;
65                let fract = usize::from(samples % samples_per_byte > 0);
66                whole + fract
67            }
68        }
69    }
70
71    pub(crate) fn is_combination_invalid(self, bit_depth: BitDepth) -> bool {
72        // Section 11.2.2 of the PNG standard disallows several combinations
73        // of bit depth and color type
74        ((bit_depth == BitDepth::One || bit_depth == BitDepth::Two || bit_depth == BitDepth::Four)
75            && (self == ColorType::Rgb
76                || self == ColorType::GrayscaleAlpha
77                || self == ColorType::Rgba))
78            || (bit_depth == BitDepth::Sixteen && self == ColorType::Indexed)
79    }
80}
81
82/// Bit depth of the PNG file.
83/// Specifies the number of bits per sample.
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85#[repr(u8)]
86pub enum BitDepth {
87    One = 1,
88    Two = 2,
89    Four = 4,
90    Eight = 8,
91    Sixteen = 16,
92}
93
94/// Internal count of bytes per pixel.
95/// This is used for filtering which never uses sub-byte units. This essentially reduces the number
96/// of possible byte chunk lengths to a very small set of values appropriate to be defined as an
97/// enum.
98#[derive(Debug, Clone, Copy)]
99#[repr(u8)]
100pub(crate) enum BytesPerPixel {
101    One = 1,
102    Two = 2,
103    Three = 3,
104    Four = 4,
105    Six = 6,
106    Eight = 8,
107}
108
109impl BitDepth {
110    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
111    pub fn from_u8(n: u8) -> Option<BitDepth> {
112        match n {
113            1 => Some(BitDepth::One),
114            2 => Some(BitDepth::Two),
115            4 => Some(BitDepth::Four),
116            8 => Some(BitDepth::Eight),
117            16 => Some(BitDepth::Sixteen),
118            _ => None,
119        }
120    }
121
122    pub(crate) fn into_u8(self) -> u8 {
123        self as u8
124    }
125}
126
127/// Pixel dimensions information
128#[derive(Clone, Copy, Debug)]
129pub struct PixelDimensions {
130    /// Pixels per unit, X axis
131    pub xppu: u32,
132    /// Pixels per unit, Y axis
133    pub yppu: u32,
134    /// Either *Meter* or *Unspecified*
135    pub unit: Unit,
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139#[repr(u8)]
140/// Physical unit of the pixel dimensions
141pub enum Unit {
142    Unspecified = 0,
143    Meter = 1,
144}
145
146impl Unit {
147    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
148    pub fn from_u8(n: u8) -> Option<Unit> {
149        match n {
150            0 => Some(Unit::Unspecified),
151            1 => Some(Unit::Meter),
152            _ => None,
153        }
154    }
155}
156
157/// How to reset buffer of an animated png (APNG) at the end of a frame.
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159#[repr(u8)]
160pub enum DisposeOp {
161    /// Leave the buffer unchanged.
162    None = 0,
163    /// Clear buffer with the background color.
164    Background = 1,
165    /// Reset the buffer to the state before the current frame.
166    Previous = 2,
167}
168
169impl DisposeOp {
170    /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now.
171    pub fn from_u8(n: u8) -> Option<DisposeOp> {
172        match n {
173            0 => Some(DisposeOp::None),
174            1 => Some(DisposeOp::Background),
175            2 => Some(DisposeOp::Previous),
176            _ => None,
177        }
178    }
179}
180
181impl fmt::Display for DisposeOp {
182    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183        let name = match *self {
184            DisposeOp::None => "DISPOSE_OP_NONE",
185            DisposeOp::Background => "DISPOSE_OP_BACKGROUND",
186            DisposeOp::Previous => "DISPOSE_OP_PREVIOUS",
187        };
188        write!(f, "{}", name)
189    }
190}
191
192/// How pixels are written into the buffer.
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194#[repr(u8)]
195pub enum BlendOp {
196    /// Pixels overwrite the value at their position.
197    Source = 0,
198    /// The new pixels are blended into the current state based on alpha.
199    Over = 1,
200}
201
202impl BlendOp {
203    /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now.
204    pub fn from_u8(n: u8) -> Option<BlendOp> {
205        match n {
206            0 => Some(BlendOp::Source),
207            1 => Some(BlendOp::Over),
208            _ => None,
209        }
210    }
211}
212
213impl fmt::Display for BlendOp {
214    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215        let name = match *self {
216            BlendOp::Source => "BLEND_OP_SOURCE",
217            BlendOp::Over => "BLEND_OP_OVER",
218        };
219        write!(f, "{}", name)
220    }
221}
222
223/// Frame control information
224#[derive(Clone, Copy, Debug)]
225pub struct FrameControl {
226    /// Sequence number of the animation chunk, starting from 0
227    pub sequence_number: u32,
228    /// Width of the following frame
229    pub width: u32,
230    /// Height of the following frame
231    pub height: u32,
232    /// X position at which to render the following frame
233    pub x_offset: u32,
234    /// Y position at which to render the following frame
235    pub y_offset: u32,
236    /// Frame delay fraction numerator
237    pub delay_num: u16,
238    /// Frame delay fraction denominator
239    pub delay_den: u16,
240    /// Type of frame area disposal to be done after rendering this frame
241    pub dispose_op: DisposeOp,
242    /// Type of frame area rendering for this frame
243    pub blend_op: BlendOp,
244}
245
246impl Default for FrameControl {
247    fn default() -> FrameControl {
248        FrameControl {
249            sequence_number: 0,
250            width: 0,
251            height: 0,
252            x_offset: 0,
253            y_offset: 0,
254            delay_num: 1,
255            delay_den: 30,
256            dispose_op: DisposeOp::None,
257            blend_op: BlendOp::Source,
258        }
259    }
260}
261
262impl FrameControl {
263    pub fn set_seq_num(&mut self, s: u32) {
264        self.sequence_number = s;
265    }
266
267    pub fn inc_seq_num(&mut self, i: u32) {
268        self.sequence_number += i;
269    }
270
271    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
272        let mut data = [0u8; 26];
273        data[..4].copy_from_slice(&self.sequence_number.to_be_bytes());
274        data[4..8].copy_from_slice(&self.width.to_be_bytes());
275        data[8..12].copy_from_slice(&self.height.to_be_bytes());
276        data[12..16].copy_from_slice(&self.x_offset.to_be_bytes());
277        data[16..20].copy_from_slice(&self.y_offset.to_be_bytes());
278        data[20..22].copy_from_slice(&self.delay_num.to_be_bytes());
279        data[22..24].copy_from_slice(&self.delay_den.to_be_bytes());
280        data[24] = self.dispose_op as u8;
281        data[25] = self.blend_op as u8;
282
283        encoder::write_chunk(w, chunk::fcTL, &data)
284    }
285}
286
287/// Animation control information
288#[derive(Clone, Copy, Debug)]
289pub struct AnimationControl {
290    /// Number of frames
291    pub num_frames: u32,
292    /// Number of times to loop this APNG.  0 indicates infinite looping.
293    pub num_plays: u32,
294}
295
296impl AnimationControl {
297    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
298        let mut data = [0; 8];
299        data[..4].copy_from_slice(&self.num_frames.to_be_bytes());
300        data[4..].copy_from_slice(&self.num_plays.to_be_bytes());
301        encoder::write_chunk(w, chunk::acTL, &data)
302    }
303}
304
305/// The type and strength of applied compression.
306#[derive(Debug, Clone, Copy)]
307pub enum Compression {
308    /// Default level
309    Default,
310    /// Fast minimal compression
311    Fast,
312    /// Higher compression level
313    ///
314    /// Best in this context isn't actually the highest possible level
315    /// the encoder can do, but is meant to emulate the `Best` setting in the `Flate2`
316    /// library.
317    Best,
318    #[deprecated(
319        since = "0.17.6",
320        note = "use one of the other compression levels instead, such as 'fast'"
321    )]
322    Huffman,
323    #[deprecated(
324        since = "0.17.6",
325        note = "use one of the other compression levels instead, such as 'fast'"
326    )]
327    Rle,
328}
329
330impl Default for Compression {
331    fn default() -> Self {
332        Self::Default
333    }
334}
335
336/// An unsigned integer scaled version of a floating point value,
337/// equivalent to an integer quotient with fixed denominator (100_000)).
338#[derive(Clone, Copy, Debug, PartialEq, Eq)]
339pub struct ScaledFloat(u32);
340
341impl ScaledFloat {
342    const SCALING: f32 = 100_000.0;
343
344    /// Gets whether the value is within the clamped range of this type.
345    pub fn in_range(value: f32) -> bool {
346        value >= 0.0 && (value * Self::SCALING).floor() <= u32::MAX as f32
347    }
348
349    /// Gets whether the value can be exactly converted in round-trip.
350    #[allow(clippy::float_cmp)] // Stupid tool, the exact float compare is _the entire point_.
351    pub fn exact(value: f32) -> bool {
352        let there = Self::forward(value);
353        let back = Self::reverse(there);
354        value == back
355    }
356
357    fn forward(value: f32) -> u32 {
358        (value.max(0.0) * Self::SCALING).floor() as u32
359    }
360
361    fn reverse(encoded: u32) -> f32 {
362        encoded as f32 / Self::SCALING
363    }
364
365    /// Slightly inaccurate scaling and quantization.
366    /// Clamps the value into the representable range if it is negative or too large.
367    pub fn new(value: f32) -> Self {
368        Self(Self::forward(value))
369    }
370
371    /// Fully accurate construction from a value scaled as per specification.
372    pub fn from_scaled(val: u32) -> Self {
373        Self(val)
374    }
375
376    /// Get the accurate encoded value.
377    pub fn into_scaled(self) -> u32 {
378        self.0
379    }
380
381    /// Get the unscaled value as a floating point.
382    pub fn into_value(self) -> f32 {
383        Self::reverse(self.0)
384    }
385
386    pub(crate) fn encode_gama<W: Write>(self, w: &mut W) -> encoder::Result<()> {
387        encoder::write_chunk(w, chunk::gAMA, &self.into_scaled().to_be_bytes())
388    }
389}
390
391/// Chromaticities of the color space primaries
392#[derive(Clone, Copy, Debug, PartialEq, Eq)]
393pub struct SourceChromaticities {
394    pub white: (ScaledFloat, ScaledFloat),
395    pub red: (ScaledFloat, ScaledFloat),
396    pub green: (ScaledFloat, ScaledFloat),
397    pub blue: (ScaledFloat, ScaledFloat),
398}
399
400impl SourceChromaticities {
401    pub fn new(white: (f32, f32), red: (f32, f32), green: (f32, f32), blue: (f32, f32)) -> Self {
402        SourceChromaticities {
403            white: (ScaledFloat::new(white.0), ScaledFloat::new(white.1)),
404            red: (ScaledFloat::new(red.0), ScaledFloat::new(red.1)),
405            green: (ScaledFloat::new(green.0), ScaledFloat::new(green.1)),
406            blue: (ScaledFloat::new(blue.0), ScaledFloat::new(blue.1)),
407        }
408    }
409
410    #[rustfmt::skip]
411    pub fn to_be_bytes(self) -> [u8; 32] {
412        let white_x = self.white.0.into_scaled().to_be_bytes();
413        let white_y = self.white.1.into_scaled().to_be_bytes();
414        let red_x   = self.red.0.into_scaled().to_be_bytes();
415        let red_y   = self.red.1.into_scaled().to_be_bytes();
416        let green_x = self.green.0.into_scaled().to_be_bytes();
417        let green_y = self.green.1.into_scaled().to_be_bytes();
418        let blue_x  = self.blue.0.into_scaled().to_be_bytes();
419        let blue_y  = self.blue.1.into_scaled().to_be_bytes();
420        [
421            white_x[0], white_x[1], white_x[2], white_x[3],
422            white_y[0], white_y[1], white_y[2], white_y[3],
423            red_x[0],   red_x[1],   red_x[2],   red_x[3],
424            red_y[0],   red_y[1],   red_y[2],   red_y[3],
425            green_x[0], green_x[1], green_x[2], green_x[3],
426            green_y[0], green_y[1], green_y[2], green_y[3],
427            blue_x[0],  blue_x[1],  blue_x[2],  blue_x[3],
428            blue_y[0],  blue_y[1],  blue_y[2],  blue_y[3],
429        ]
430    }
431
432    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
433        encoder::write_chunk(w, chunk::cHRM, &self.to_be_bytes())
434    }
435}
436
437/// The rendering intent for an sRGB image.
438///
439/// Presence of this data also indicates that the image conforms to the sRGB color space.
440#[repr(u8)]
441#[derive(Clone, Copy, Debug, PartialEq, Eq)]
442pub enum SrgbRenderingIntent {
443    /// For images preferring good adaptation to the output device gamut at the expense of colorimetric accuracy, such as photographs.
444    Perceptual = 0,
445    /// For images requiring colour appearance matching (relative to the output device white point), such as logos.
446    RelativeColorimetric = 1,
447    /// For images preferring preservation of saturation at the expense of hue and lightness, such as charts and graphs.
448    Saturation = 2,
449    /// For images requiring preservation of absolute colorimetry, such as previews of images destined for a different output device (proofs).
450    AbsoluteColorimetric = 3,
451}
452
453impl SrgbRenderingIntent {
454    pub(crate) fn into_raw(self) -> u8 {
455        self as u8
456    }
457
458    pub(crate) fn from_raw(raw: u8) -> Option<Self> {
459        match raw {
460            0 => Some(SrgbRenderingIntent::Perceptual),
461            1 => Some(SrgbRenderingIntent::RelativeColorimetric),
462            2 => Some(SrgbRenderingIntent::Saturation),
463            3 => Some(SrgbRenderingIntent::AbsoluteColorimetric),
464            _ => None,
465        }
466    }
467
468    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
469        encoder::write_chunk(w, chunk::sRGB, &[self.into_raw()])
470    }
471}
472
473/// PNG info struct
474#[derive(Clone, Debug)]
475#[non_exhaustive]
476pub struct Info<'a> {
477    pub width: u32,
478    pub height: u32,
479    pub bit_depth: BitDepth,
480    /// How colors are stored in the image.
481    pub color_type: ColorType,
482    pub interlaced: bool,
483    /// The image's `tRNS` chunk, if present; contains the alpha channel of the image's palette, 1 byte per entry.
484    pub trns: Option<Cow<'a, [u8]>>,
485    pub pixel_dims: Option<PixelDimensions>,
486    /// The image's `PLTE` chunk, if present; contains the RGB channels (in that order) of the image's palettes, 3 bytes per entry (1 per channel).
487    pub palette: Option<Cow<'a, [u8]>>,
488    /// The contents of the image's gAMA chunk, if present.
489    /// Prefer `source_gamma` to also get the derived replacement gamma from sRGB chunks.
490    pub gama_chunk: Option<ScaledFloat>,
491    /// The contents of the image's `cHRM` chunk, if present.
492    /// Prefer `source_chromaticities` to also get the derived replacements from sRGB chunks.
493    pub chrm_chunk: Option<SourceChromaticities>,
494
495    pub frame_control: Option<FrameControl>,
496    pub animation_control: Option<AnimationControl>,
497    pub compression: Compression,
498    /// Gamma of the source system.
499    /// Set by both `gAMA` as well as to a replacement by `sRGB` chunk.
500    pub source_gamma: Option<ScaledFloat>,
501    /// Chromaticities of the source system.
502    /// Set by both `cHRM` as well as to a replacement by `sRGB` chunk.
503    pub source_chromaticities: Option<SourceChromaticities>,
504    /// The rendering intent of an SRGB image.
505    ///
506    /// Presence of this value also indicates that the image conforms to the SRGB color space.
507    pub srgb: Option<SrgbRenderingIntent>,
508    /// The ICC profile for the image.
509    pub icc_profile: Option<Cow<'a, [u8]>>,
510    /// tEXt field
511    pub uncompressed_latin1_text: Vec<TEXtChunk>,
512    /// zTXt field
513    pub compressed_latin1_text: Vec<ZTXtChunk>,
514    /// iTXt field
515    pub utf8_text: Vec<ITXtChunk>,
516}
517
518impl Default for Info<'_> {
519    fn default() -> Info<'static> {
520        Info {
521            width: 0,
522            height: 0,
523            bit_depth: BitDepth::Eight,
524            color_type: ColorType::Grayscale,
525            interlaced: false,
526            palette: None,
527            trns: None,
528            gama_chunk: None,
529            chrm_chunk: None,
530            pixel_dims: None,
531            frame_control: None,
532            animation_control: None,
533            // Default to `deflate::Compression::Fast` and `filter::FilterType::Sub`
534            // to maintain backward compatible output.
535            compression: Compression::Fast,
536            source_gamma: None,
537            source_chromaticities: None,
538            srgb: None,
539            icc_profile: None,
540            uncompressed_latin1_text: Vec::new(),
541            compressed_latin1_text: Vec::new(),
542            utf8_text: Vec::new(),
543        }
544    }
545}
546
547impl Info<'_> {
548    /// A utility constructor for a default info with width and height.
549    pub fn with_size(width: u32, height: u32) -> Self {
550        Info {
551            width,
552            height,
553            ..Default::default()
554        }
555    }
556
557    /// Size of the image, width then height.
558    pub fn size(&self) -> (u32, u32) {
559        (self.width, self.height)
560    }
561
562    /// Returns true if the image is an APNG image.
563    pub fn is_animated(&self) -> bool {
564        self.frame_control.is_some() && self.animation_control.is_some()
565    }
566
567    /// Returns the frame control information of the image.
568    pub fn animation_control(&self) -> Option<&AnimationControl> {
569        self.animation_control.as_ref()
570    }
571
572    /// Returns the frame control information of the current frame
573    pub fn frame_control(&self) -> Option<&FrameControl> {
574        self.frame_control.as_ref()
575    }
576
577    /// Returns the number of bits per pixel.
578    pub fn bits_per_pixel(&self) -> usize {
579        self.color_type.samples() * self.bit_depth as usize
580    }
581
582    /// Returns the number of bytes per pixel.
583    pub fn bytes_per_pixel(&self) -> usize {
584        // If adjusting this for expansion or other transformation passes, remember to keep the old
585        // implementation for bpp_in_prediction, which is internal to the png specification.
586        self.color_type.samples() * ((self.bit_depth as usize + 7) >> 3)
587    }
588
589    /// Return the number of bytes for this pixel used in prediction.
590    ///
591    /// Some filters use prediction, over the raw bytes of a scanline. Where a previous pixel is
592    /// require for such forms the specification instead references previous bytes. That is, for
593    /// a gray pixel of bit depth 2, the pixel used in prediction is actually 4 pixels prior. This
594    /// has the consequence that the number of possible values is rather small. To make this fact
595    /// more obvious in the type system and the optimizer we use an explicit enum here.
596    pub(crate) fn bpp_in_prediction(&self) -> BytesPerPixel {
597        BytesPerPixel::from_usize(self.bytes_per_pixel())
598    }
599
600    /// Returns the number of bytes needed for one deinterlaced image.
601    pub fn raw_bytes(&self) -> usize {
602        self.height as usize * self.raw_row_length()
603    }
604
605    /// Returns the number of bytes needed for one deinterlaced row.
606    pub fn raw_row_length(&self) -> usize {
607        self.raw_row_length_from_width(self.width)
608    }
609
610    pub(crate) fn checked_raw_row_length(&self) -> Option<usize> {
611        self.color_type
612            .checked_raw_row_length(self.bit_depth, self.width)
613    }
614
615    /// Returns the number of bytes needed for one deinterlaced row of width `width`.
616    pub fn raw_row_length_from_width(&self, width: u32) -> usize {
617        self.color_type
618            .raw_row_length_from_width(self.bit_depth, width)
619    }
620
621    /// Encode this header to the writer.
622    ///
623    /// Note that this does _not_ include the PNG signature, it starts with the IHDR chunk and then
624    /// includes other chunks that were added to the header.
625    pub fn encode<W: Write>(&self, mut w: W) -> encoder::Result<()> {
626        // Encode the IHDR chunk
627        let mut data = [0; 13];
628        data[..4].copy_from_slice(&self.width.to_be_bytes());
629        data[4..8].copy_from_slice(&self.height.to_be_bytes());
630        data[8] = self.bit_depth as u8;
631        data[9] = self.color_type as u8;
632        data[12] = self.interlaced as u8;
633        encoder::write_chunk(&mut w, chunk::IHDR, &data)?;
634        // Encode the pHYs chunk
635        if let Some(pd) = self.pixel_dims {
636            let mut phys_data = [0; 9];
637            phys_data[0..4].copy_from_slice(&pd.xppu.to_be_bytes());
638            phys_data[4..8].copy_from_slice(&pd.yppu.to_be_bytes());
639            match pd.unit {
640                Unit::Meter => phys_data[8] = 1,
641                Unit::Unspecified => phys_data[8] = 0,
642            }
643            encoder::write_chunk(&mut w, chunk::pHYs, &phys_data)?;
644        }
645
646        if let Some(p) = &self.palette {
647            encoder::write_chunk(&mut w, chunk::PLTE, p)?;
648        };
649
650        if let Some(t) = &self.trns {
651            encoder::write_chunk(&mut w, chunk::tRNS, t)?;
652        }
653
654        // If specified, the sRGB information overrides the source gamma and chromaticities.
655        if let Some(srgb) = &self.srgb {
656            let gamma = crate::srgb::substitute_gamma();
657            let chromaticities = crate::srgb::substitute_chromaticities();
658            srgb.encode(&mut w)?;
659            gamma.encode_gama(&mut w)?;
660            chromaticities.encode(&mut w)?;
661        } else {
662            if let Some(gma) = self.source_gamma {
663                gma.encode_gama(&mut w)?
664            }
665            if let Some(chrms) = self.source_chromaticities {
666                chrms.encode(&mut w)?;
667            }
668        }
669        if let Some(actl) = self.animation_control {
670            actl.encode(&mut w)?;
671        }
672
673        for text_chunk in &self.uncompressed_latin1_text {
674            text_chunk.encode(&mut w)?;
675        }
676
677        for text_chunk in &self.compressed_latin1_text {
678            text_chunk.encode(&mut w)?;
679        }
680
681        for text_chunk in &self.utf8_text {
682            text_chunk.encode(&mut w)?;
683        }
684
685        Ok(())
686    }
687}
688
689impl BytesPerPixel {
690    pub(crate) fn from_usize(bpp: usize) -> Self {
691        match bpp {
692            1 => BytesPerPixel::One,
693            2 => BytesPerPixel::Two,
694            3 => BytesPerPixel::Three,
695            4 => BytesPerPixel::Four,
696            6 => BytesPerPixel::Six,   // Only rgb×16bit
697            8 => BytesPerPixel::Eight, // Only rgba×16bit
698            _ => unreachable!("Not a possible byte rounded pixel width"),
699        }
700    }
701
702    pub(crate) fn into_usize(self) -> usize {
703        self as usize
704    }
705}
706
707bitflags::bitflags! {
708    /// Output transformations
709    ///
710    /// Many flags from libpng are not yet supported. A PR discussing/adding them would be nice.
711    ///
712    #[doc = "
713    ```c
714    /// Discard the alpha channel
715    const STRIP_ALPHA         = 0x0002; // read only
716    /// Expand 1; 2 and 4-bit samples to bytes
717    const PACKING             = 0x0004; // read and write
718    /// Change order of packed pixels to LSB first
719    const PACKSWAP            = 0x0008; // read and write
720    /// Invert monochrome images
721    const INVERT_MONO         = 0x0020; // read and write
722    /// Normalize pixels to the sBIT depth
723    const SHIFT               = 0x0040; // read and write
724    /// Flip RGB to BGR; RGBA to BGRA
725    const BGR                 = 0x0080; // read and write
726    /// Flip RGBA to ARGB or GA to AG
727    const SWAP_ALPHA          = 0x0100; // read and write
728    /// Byte-swap 16-bit samples
729    const SWAP_ENDIAN         = 0x0200; // read and write
730    /// Change alpha from opacity to transparency
731    const INVERT_ALPHA        = 0x0400; // read and write
732    const STRIP_FILLER        = 0x0800; // write only
733    const STRIP_FILLER_BEFORE = 0x0800; // write only
734    const STRIP_FILLER_AFTER  = 0x1000; // write only
735    const GRAY_TO_RGB         = 0x2000; // read only
736    const EXPAND_16           = 0x4000; // read only
737    /// Similar to STRIP_16 but in libpng considering gamma?
738    /// Not entirely sure the documentation says it is more
739    /// accurate but doesn't say precisely how.
740    const SCALE_16            = 0x8000; // read only
741    ```
742    "]
743    pub struct Transformations: u32 {
744        /// No transformation
745        const IDENTITY            = 0x00000; // read and write */
746        /// Strip 16-bit samples to 8 bits
747        const STRIP_16            = 0x00001; // read only */
748        /// Expand paletted images to RGB; expand grayscale images of
749        /// less than 8-bit depth to 8-bit depth; and expand tRNS chunks
750        /// to alpha channels.
751        const EXPAND              = 0x00010; // read only */
752        /// Expand paletted images to include an alpha channel. Implies `EXPAND`.
753        const ALPHA               = 0x10000; // read only */
754    }
755}
756
757impl Transformations {
758    /// Transform every input to 8bit grayscale or color.
759    ///
760    /// This sets `EXPAND` and `STRIP_16` which is similar to the default transformation used by
761    /// this library prior to `0.17`.
762    pub fn normalize_to_color8() -> Transformations {
763        Transformations::EXPAND | Transformations::STRIP_16
764    }
765}
766
767/// Instantiate the default transformations, the identity transform.
768impl Default for Transformations {
769    fn default() -> Transformations {
770        Transformations::IDENTITY
771    }
772}
773
774#[derive(Debug)]
775pub struct ParameterError {
776    inner: ParameterErrorKind,
777}
778
779#[derive(Debug)]
780pub(crate) enum ParameterErrorKind {
781    /// A provided buffer must be have the exact size to hold the image data. Where the buffer can
782    /// be allocated by the caller, they must ensure that it has a minimum size as hinted previously.
783    /// Even though the size is calculated from image data, this does counts as a parameter error
784    /// because they must react to a value produced by this library, which can have been subjected
785    /// to limits.
786    ImageBufferSize { expected: usize, actual: usize },
787    /// A bit like return `None` from an iterator.
788    /// We use it to differentiate between failing to seek to the next image in a sequence and the
789    /// absence of a next image. This is an error of the caller because they should have checked
790    /// the number of images by inspecting the header data returned when opening the image. This
791    /// library will perform the checks necessary to ensure that data was accurate or error with a
792    /// format error otherwise.
793    PolledAfterEndOfImage,
794}
795
796impl From<ParameterErrorKind> for ParameterError {
797    fn from(inner: ParameterErrorKind) -> Self {
798        ParameterError { inner }
799    }
800}
801
802impl fmt::Display for ParameterError {
803    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
804        use ParameterErrorKind::*;
805        match self.inner {
806            ImageBufferSize { expected, actual } => {
807                write!(fmt, "wrong data size, expected {} got {}", expected, actual)
808            }
809            PolledAfterEndOfImage => write!(fmt, "End of image has been reached"),
810        }
811    }
812}