Coverage Report

Created: 2025-07-01 06:50

/rust/registry/src/index.crates.io-6f17d22bba15001f/png-0.17.16/src/common.rs
Line
Count
Source (jump to first uncovered line)
1
//! Common types shared between the encoder and decoder
2
use crate::text_metadata::{EncodableTextChunk, ITXtChunk, TEXtChunk, ZTXtChunk};
3
use crate::{chunk, encoder};
4
use io::Write;
5
use 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)]
10
pub 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
23
impl ColorType {
24
    /// Returns the number of samples used per pixel encoded in this way.
25
65.7k
    pub fn samples(self) -> usize {
26
65.7k
        self.samples_u8().into()
27
65.7k
    }
28
29
78.4k
    pub(crate) fn samples_u8(self) -> u8 {
30
        use self::ColorType::*;
31
78.4k
        match self {
32
2.84k
            Grayscale | Indexed => 1,
33
13.7k
            Rgb => 3,
34
289
            GrayscaleAlpha => 2,
35
61.5k
            Rgba => 4,
36
        }
37
78.4k
    }
38
39
    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
40
4.25k
    pub fn from_u8(n: u8) -> Option<ColorType> {
41
4.25k
        match n {
42
735
            0 => Some(ColorType::Grayscale),
43
294
            2 => Some(ColorType::Rgb),
44
193
            3 => Some(ColorType::Indexed),
45
87
            4 => Some(ColorType::GrayscaleAlpha),
46
2.94k
            6 => Some(ColorType::Rgba),
47
2
            _ => None,
48
        }
49
4.25k
    }
50
51
12.6k
    pub(crate) fn checked_raw_row_length(self, depth: BitDepth, width: u32) -> Option<usize> {
52
12.6k
        // No overflow can occur in 64 bits, we multiply 32-bit with 5 more bits.
53
12.6k
        let bits = u64::from(width) * u64::from(self.samples_u8()) * u64::from(depth.into_u8());
54
12.6k
        TryFrom::try_from(1 + (bits + 7) / 8).ok()
55
12.6k
    }
56
57
56.0k
    pub(crate) fn raw_row_length_from_width(self, depth: BitDepth, width: u32) -> usize {
58
56.0k
        let samples = width as usize * self.samples();
59
56.0k
        1 + match depth {
60
144
            BitDepth::Sixteen => samples * 2,
61
55.8k
            BitDepth::Eight => samples,
62
57
            subbyte => {
63
57
                let samples_per_byte = 8 / subbyte as usize;
64
57
                let whole = samples / samples_per_byte;
65
57
                let fract = usize::from(samples % samples_per_byte > 0);
66
57
                whole + fract
67
            }
68
        }
69
56.0k
    }
70
71
4.25k
    pub(crate) fn is_combination_invalid(self, bit_depth: BitDepth) -> bool {
72
4.25k
        // Section 11.2.2 of the PNG standard disallows several combinations
73
4.25k
        // of bit depth and color type
74
4.25k
        ((bit_depth == BitDepth::One || bit_depth == BitDepth::Two || bit_depth == BitDepth::Four)
75
590
            && (self == ColorType::Rgb
76
590
                || self == ColorType::GrayscaleAlpha
77
589
                || self == ColorType::Rgba))
78
4.24k
            || (bit_depth == BitDepth::Sixteen && self == ColorType::Indexed)
79
4.25k
    }
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)]
86
pub 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)]
100
pub(crate) enum BytesPerPixel {
101
    One = 1,
102
    Two = 2,
103
    Three = 3,
104
    Four = 4,
105
    Six = 6,
106
    Eight = 8,
107
}
108
109
impl BitDepth {
110
    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
111
44.0k
    pub fn from_u8(n: u8) -> Option<BitDepth> {
112
44.0k
        match n {
113
146
            1 => Some(BitDepth::One),
114
276
            2 => Some(BitDepth::Two),
115
174
            4 => Some(BitDepth::Four),
116
42.9k
            8 => Some(BitDepth::Eight),
117
557
            16 => Some(BitDepth::Sixteen),
118
20
            _ => None,
119
        }
120
44.0k
    }
121
122
12.6k
    pub(crate) fn into_u8(self) -> u8 {
123
12.6k
        self as u8
124
12.6k
    }
125
}
126
127
/// Pixel dimensions information
128
#[derive(Clone, Copy, Debug)]
129
pub 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
141
pub enum Unit {
142
    Unspecified = 0,
143
    Meter = 1,
144
}
145
146
impl Unit {
147
    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
148
3
    pub fn from_u8(n: u8) -> Option<Unit> {
149
3
        match n {
150
2
            0 => Some(Unit::Unspecified),
151
0
            1 => Some(Unit::Meter),
152
1
            _ => None,
153
        }
154
3
    }
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)]
160
pub 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
169
impl DisposeOp {
170
    /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now.
171
175
    pub fn from_u8(n: u8) -> Option<DisposeOp> {
172
175
        match n {
173
163
            0 => Some(DisposeOp::None),
174
3
            1 => Some(DisposeOp::Background),
175
8
            2 => Some(DisposeOp::Previous),
176
1
            _ => None,
177
        }
178
175
    }
179
}
180
181
impl fmt::Display for DisposeOp {
182
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183
0
        let name = match *self {
184
0
            DisposeOp::None => "DISPOSE_OP_NONE",
185
0
            DisposeOp::Background => "DISPOSE_OP_BACKGROUND",
186
0
            DisposeOp::Previous => "DISPOSE_OP_PREVIOUS",
187
        };
188
0
        write!(f, "{}", name)
189
0
    }
190
}
191
192
/// How pixels are written into the buffer.
193
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194
#[repr(u8)]
195
pub 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
202
impl BlendOp {
203
    /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now.
204
173
    pub fn from_u8(n: u8) -> Option<BlendOp> {
205
173
        match n {
206
126
            0 => Some(BlendOp::Source),
207
42
            1 => Some(BlendOp::Over),
208
5
            _ => None,
209
        }
210
173
    }
211
}
212
213
impl fmt::Display for BlendOp {
214
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215
0
        let name = match *self {
216
0
            BlendOp::Source => "BLEND_OP_SOURCE",
217
0
            BlendOp::Over => "BLEND_OP_OVER",
218
        };
219
0
        write!(f, "{}", name)
220
0
    }
221
}
222
223
/// Frame control information
224
#[derive(Clone, Copy, Debug)]
225
pub 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
246
impl Default for FrameControl {
247
0
    fn default() -> FrameControl {
248
0
        FrameControl {
249
0
            sequence_number: 0,
250
0
            width: 0,
251
0
            height: 0,
252
0
            x_offset: 0,
253
0
            y_offset: 0,
254
0
            delay_num: 1,
255
0
            delay_den: 30,
256
0
            dispose_op: DisposeOp::None,
257
0
            blend_op: BlendOp::Source,
258
0
        }
259
0
    }
260
}
261
262
impl FrameControl {
263
0
    pub fn set_seq_num(&mut self, s: u32) {
264
0
        self.sequence_number = s;
265
0
    }
266
267
0
    pub fn inc_seq_num(&mut self, i: u32) {
268
0
        self.sequence_number += i;
269
0
    }
270
271
0
    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
272
0
        let mut data = [0u8; 26];
273
0
        data[..4].copy_from_slice(&self.sequence_number.to_be_bytes());
274
0
        data[4..8].copy_from_slice(&self.width.to_be_bytes());
275
0
        data[8..12].copy_from_slice(&self.height.to_be_bytes());
276
0
        data[12..16].copy_from_slice(&self.x_offset.to_be_bytes());
277
0
        data[16..20].copy_from_slice(&self.y_offset.to_be_bytes());
278
0
        data[20..22].copy_from_slice(&self.delay_num.to_be_bytes());
279
0
        data[22..24].copy_from_slice(&self.delay_den.to_be_bytes());
280
0
        data[24] = self.dispose_op as u8;
281
0
        data[25] = self.blend_op as u8;
282
0
283
0
        encoder::write_chunk(w, chunk::fcTL, &data)
284
0
    }
Unexecuted instantiation: <png::common::FrameControl>::encode::<&mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::FrameControl>::encode::<_>
Unexecuted instantiation: <png::common::FrameControl>::encode::<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
285
}
286
287
/// Animation control information
288
#[derive(Clone, Copy, Debug)]
289
pub 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
296
impl AnimationControl {
297
0
    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
298
0
        let mut data = [0; 8];
299
0
        data[..4].copy_from_slice(&self.num_frames.to_be_bytes());
300
0
        data[4..].copy_from_slice(&self.num_plays.to_be_bytes());
301
0
        encoder::write_chunk(w, chunk::acTL, &data)
302
0
    }
Unexecuted instantiation: <png::common::AnimationControl>::encode::<&mut &mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::AnimationControl>::encode::<_>
Unexecuted instantiation: <png::common::AnimationControl>::encode::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
303
}
304
305
/// The type and strength of applied compression.
306
#[derive(Debug, Clone, Copy)]
307
pub 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
330
impl Default for Compression {
331
0
    fn default() -> Self {
332
0
        Self::Default
333
0
    }
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)]
339
pub struct ScaledFloat(u32);
340
341
impl ScaledFloat {
342
    const SCALING: f32 = 100_000.0;
343
344
    /// Gets whether the value is within the clamped range of this type.
345
0
    pub fn in_range(value: f32) -> bool {
346
0
        value >= 0.0 && (value * Self::SCALING).floor() <= u32::MAX as f32
347
0
    }
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
0
    pub fn exact(value: f32) -> bool {
352
0
        let there = Self::forward(value);
353
0
        let back = Self::reverse(there);
354
0
        value == back
355
0
    }
356
357
0
    fn forward(value: f32) -> u32 {
358
0
        (value.max(0.0) * Self::SCALING).floor() as u32
359
0
    }
360
361
0
    fn reverse(encoded: u32) -> f32 {
362
0
        encoded as f32 / Self::SCALING
363
0
    }
364
365
    /// Slightly inaccurate scaling and quantization.
366
    /// Clamps the value into the representable range if it is negative or too large.
367
0
    pub fn new(value: f32) -> Self {
368
0
        Self(Self::forward(value))
369
0
    }
370
371
    /// Fully accurate construction from a value scaled as per specification.
372
802
    pub fn from_scaled(val: u32) -> Self {
373
802
        Self(val)
374
802
    }
375
376
    /// Get the accurate encoded value.
377
0
    pub fn into_scaled(self) -> u32 {
378
0
        self.0
379
0
    }
380
381
    /// Get the unscaled value as a floating point.
382
0
    pub fn into_value(self) -> f32 {
383
0
        Self::reverse(self.0)
384
0
    }
385
386
0
    pub(crate) fn encode_gama<W: Write>(self, w: &mut W) -> encoder::Result<()> {
387
0
        encoder::write_chunk(w, chunk::gAMA, &self.into_scaled().to_be_bytes())
388
0
    }
Unexecuted instantiation: <png::common::ScaledFloat>::encode_gama::<&mut &mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::ScaledFloat>::encode_gama::<_>
Unexecuted instantiation: <png::common::ScaledFloat>::encode_gama::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
389
}
390
391
/// Chromaticities of the color space primaries
392
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
393
pub struct SourceChromaticities {
394
    pub white: (ScaledFloat, ScaledFloat),
395
    pub red: (ScaledFloat, ScaledFloat),
396
    pub green: (ScaledFloat, ScaledFloat),
397
    pub blue: (ScaledFloat, ScaledFloat),
398
}
399
400
impl SourceChromaticities {
401
0
    pub fn new(white: (f32, f32), red: (f32, f32), green: (f32, f32), blue: (f32, f32)) -> Self {
402
0
        SourceChromaticities {
403
0
            white: (ScaledFloat::new(white.0), ScaledFloat::new(white.1)),
404
0
            red: (ScaledFloat::new(red.0), ScaledFloat::new(red.1)),
405
0
            green: (ScaledFloat::new(green.0), ScaledFloat::new(green.1)),
406
0
            blue: (ScaledFloat::new(blue.0), ScaledFloat::new(blue.1)),
407
0
        }
408
0
    }
409
410
    #[rustfmt::skip]
411
0
    pub fn to_be_bytes(self) -> [u8; 32] {
412
0
        let white_x = self.white.0.into_scaled().to_be_bytes();
413
0
        let white_y = self.white.1.into_scaled().to_be_bytes();
414
0
        let red_x   = self.red.0.into_scaled().to_be_bytes();
415
0
        let red_y   = self.red.1.into_scaled().to_be_bytes();
416
0
        let green_x = self.green.0.into_scaled().to_be_bytes();
417
0
        let green_y = self.green.1.into_scaled().to_be_bytes();
418
0
        let blue_x  = self.blue.0.into_scaled().to_be_bytes();
419
0
        let blue_y  = self.blue.1.into_scaled().to_be_bytes();
420
0
        [
421
0
            white_x[0], white_x[1], white_x[2], white_x[3],
422
0
            white_y[0], white_y[1], white_y[2], white_y[3],
423
0
            red_x[0],   red_x[1],   red_x[2],   red_x[3],
424
0
            red_y[0],   red_y[1],   red_y[2],   red_y[3],
425
0
            green_x[0], green_x[1], green_x[2], green_x[3],
426
0
            green_y[0], green_y[1], green_y[2], green_y[3],
427
0
            blue_x[0],  blue_x[1],  blue_x[2],  blue_x[3],
428
0
            blue_y[0],  blue_y[1],  blue_y[2],  blue_y[3],
429
0
        ]
430
0
    }
431
432
0
    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
433
0
        encoder::write_chunk(w, chunk::cHRM, &self.to_be_bytes())
434
0
    }
Unexecuted instantiation: <png::common::SourceChromaticities>::encode::<&mut &mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::SourceChromaticities>::encode::<_>
Unexecuted instantiation: <png::common::SourceChromaticities>::encode::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
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)]
442
pub 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
453
impl SrgbRenderingIntent {
454
0
    pub(crate) fn into_raw(self) -> u8 {
455
0
        self as u8
456
0
    }
457
458
6
    pub(crate) fn from_raw(raw: u8) -> Option<Self> {
459
6
        match raw {
460
2
            0 => Some(SrgbRenderingIntent::Perceptual),
461
3
            1 => Some(SrgbRenderingIntent::RelativeColorimetric),
462
0
            2 => Some(SrgbRenderingIntent::Saturation),
463
0
            3 => Some(SrgbRenderingIntent::AbsoluteColorimetric),
464
1
            _ => None,
465
        }
466
6
    }
467
468
0
    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
469
0
        encoder::write_chunk(w, chunk::sRGB, &[self.into_raw()])
470
0
    }
Unexecuted instantiation: <png::common::SrgbRenderingIntent>::encode::<&mut &mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::SrgbRenderingIntent>::encode::<_>
Unexecuted instantiation: <png::common::SrgbRenderingIntent>::encode::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
471
}
472
473
/// Coding-independent code points (cICP) specify the color space (primaries),
474
/// transfer function, matrix coefficients and scaling factor of the image using
475
/// the code points specified in [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273).
476
///
477
/// See https://www.w3.org/TR/png-3/#cICP-chunk for more details.
478
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
479
pub struct CodingIndependentCodePoints {
480
    /// Id number of the color primaries defined in
481
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) in "Table 2 -
482
    /// Interpretation of colour primaries (ColourPrimaries) value".
483
    pub color_primaries: u8,
484
485
    /// Id number of the transfer characteristics defined in
486
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) in "Table 3 -
487
    /// Interpretation of transfer characteristics (TransferCharacteristics)
488
    /// value".
489
    pub transfer_function: u8,
490
491
    /// Id number of the matrix coefficients defined in
492
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) in "Table 4 -
493
    /// Interpretation of matrix coefficients (MatrixCoefficients) value".
494
    ///
495
    /// This field is included to faithfully replicate the base
496
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) specification, but matrix coefficients
497
    /// will always be set to 0, because RGB is currently the only supported color mode in PNG.
498
    pub matrix_coefficients: u8,
499
500
    /// Whether the image is
501
    /// [a full range image](https://www.w3.org/TR/png-3/#dfn-full-range-image)
502
    /// or
503
    /// [a narrow range image](https://www.w3.org/TR/png-3/#dfn-narrow-range-image).
504
    ///
505
    /// This field is included to faithfully replicate the base
506
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) specification, but it has limited
507
    /// practical application to PNG images, because narrow-range images are [quite
508
    /// rare](https://github.com/w3c/png/issues/312#issuecomment-2327349614) in practice.
509
    pub is_video_full_range_image: bool,
510
}
511
512
/// Mastering Display Color Volume (mDCV) used at the point of content creation,
513
/// as specified in [SMPTE-ST-2086](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=8353899).
514
///
515
/// See https://www.w3.org/TR/png-3/#mDCV-chunk for more details.
516
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
517
pub struct MasteringDisplayColorVolume {
518
    /// Mastering display chromaticities.
519
    pub chromaticities: SourceChromaticities,
520
521
    /// Mastering display maximum luminance.
522
    ///
523
    /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field
524
    /// is set to `10000000` then it indicates 1000 cd/m^2.
525
    pub max_luminance: u32,
526
527
    /// Mastering display minimum luminance.
528
    ///
529
    /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field
530
    /// is set to `10000000` then it indicates 1000 cd/m^2.
531
    pub min_luminance: u32,
532
}
533
534
/// Content light level information of HDR content.
535
///
536
/// See https://www.w3.org/TR/png-3/#cLLI-chunk for more details.
537
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
538
pub struct ContentLightLevelInfo {
539
    /// Maximum Content Light Level indicates the maximum light level of any
540
    /// single pixel (in cd/m^2, also known as nits) of the entire playback
541
    /// sequence.
542
    ///
543
    /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field
544
    /// is set to `10000000` then it indicates 1000 cd/m^2.
545
    ///
546
    /// A value of zero means that the value is unknown or not currently calculable.
547
    pub max_content_light_level: u32,
548
549
    /// Maximum Frame Average Light Level indicates the maximum value of the
550
    /// frame average light level (in cd/m^2, also known as nits) of the entire
551
    /// playback sequence. It is calculated by first averaging the decoded
552
    /// luminance values of all the pixels in each frame, and then using the
553
    /// value for the frame with the highest value.
554
    ///
555
    /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field
556
    /// is set to `10000000` then it indicates 1000 cd/m^2.
557
    ///
558
    /// A value of zero means that the value is unknown or not currently calculable.
559
    pub max_frame_average_light_level: u32,
560
}
561
562
/// PNG info struct
563
#[derive(Clone, Debug)]
564
#[non_exhaustive]
565
pub struct Info<'a> {
566
    pub width: u32,
567
    pub height: u32,
568
    pub bit_depth: BitDepth,
569
    /// How colors are stored in the image.
570
    pub color_type: ColorType,
571
    pub interlaced: bool,
572
    /// The image's `sBIT` chunk, if present; contains significant bits of the sample.
573
    pub sbit: Option<Cow<'a, [u8]>>,
574
    /// The image's `tRNS` chunk, if present; contains the alpha channel of the image's palette, 1 byte per entry.
575
    pub trns: Option<Cow<'a, [u8]>>,
576
    pub pixel_dims: Option<PixelDimensions>,
577
    /// 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).
578
    pub palette: Option<Cow<'a, [u8]>>,
579
    /// The contents of the image's gAMA chunk, if present.
580
    /// Prefer `source_gamma` to also get the derived replacement gamma from sRGB chunks.
581
    pub gama_chunk: Option<ScaledFloat>,
582
    /// The contents of the image's `cHRM` chunk, if present.
583
    /// Prefer `source_chromaticities` to also get the derived replacements from sRGB chunks.
584
    pub chrm_chunk: Option<SourceChromaticities>,
585
    /// The contents of the image's `bKGD` chunk, if present.
586
    pub bkgd: Option<Cow<'a, [u8]>>,
587
588
    pub frame_control: Option<FrameControl>,
589
    pub animation_control: Option<AnimationControl>,
590
    pub compression: Compression,
591
    /// Gamma of the source system.
592
    /// Set by both `gAMA` as well as to a replacement by `sRGB` chunk.
593
    pub source_gamma: Option<ScaledFloat>,
594
    /// Chromaticities of the source system.
595
    /// Set by both `cHRM` as well as to a replacement by `sRGB` chunk.
596
    pub source_chromaticities: Option<SourceChromaticities>,
597
    /// The rendering intent of an SRGB image.
598
    ///
599
    /// Presence of this value also indicates that the image conforms to the SRGB color space.
600
    pub srgb: Option<SrgbRenderingIntent>,
601
    /// The ICC profile for the image.
602
    pub icc_profile: Option<Cow<'a, [u8]>>,
603
    /// The coding-independent code points for video signal type identification of the image.
604
    pub coding_independent_code_points: Option<CodingIndependentCodePoints>,
605
    /// The mastering display color volume for the image.
606
    pub mastering_display_color_volume: Option<MasteringDisplayColorVolume>,
607
    /// The content light information for the image.
608
    pub content_light_level: Option<ContentLightLevelInfo>,
609
    /// The EXIF metadata for the image.
610
    pub exif_metadata: Option<Cow<'a, [u8]>>,
611
    /// tEXt field
612
    pub uncompressed_latin1_text: Vec<TEXtChunk>,
613
    /// zTXt field
614
    pub compressed_latin1_text: Vec<ZTXtChunk>,
615
    /// iTXt field
616
    pub utf8_text: Vec<ITXtChunk>,
617
}
618
619
impl Default for Info<'_> {
620
4.22k
    fn default() -> Info<'static> {
621
4.22k
        Info {
622
4.22k
            width: 0,
623
4.22k
            height: 0,
624
4.22k
            bit_depth: BitDepth::Eight,
625
4.22k
            color_type: ColorType::Grayscale,
626
4.22k
            interlaced: false,
627
4.22k
            palette: None,
628
4.22k
            sbit: None,
629
4.22k
            trns: None,
630
4.22k
            gama_chunk: None,
631
4.22k
            chrm_chunk: None,
632
4.22k
            bkgd: None,
633
4.22k
            pixel_dims: None,
634
4.22k
            frame_control: None,
635
4.22k
            animation_control: None,
636
4.22k
            // Default to `deflate::Compression::Fast` and `filter::FilterType::Sub`
637
4.22k
            // to maintain backward compatible output.
638
4.22k
            compression: Compression::Fast,
639
4.22k
            source_gamma: None,
640
4.22k
            source_chromaticities: None,
641
4.22k
            srgb: None,
642
4.22k
            icc_profile: None,
643
4.22k
            coding_independent_code_points: None,
644
4.22k
            mastering_display_color_volume: None,
645
4.22k
            content_light_level: None,
646
4.22k
            exif_metadata: None,
647
4.22k
            uncompressed_latin1_text: Vec::new(),
648
4.22k
            compressed_latin1_text: Vec::new(),
649
4.22k
            utf8_text: Vec::new(),
650
4.22k
        }
651
4.22k
    }
652
}
653
654
impl Info<'_> {
655
    /// A utility constructor for a default info with width and height.
656
0
    pub fn with_size(width: u32, height: u32) -> Self {
657
0
        Info {
658
0
            width,
659
0
            height,
660
0
            ..Default::default()
661
0
        }
662
0
    }
663
664
    /// Size of the image, width then height.
665
23.1k
    pub fn size(&self) -> (u32, u32) {
666
23.1k
        (self.width, self.height)
667
23.1k
    }
668
669
    /// Returns true if the image is an APNG image.
670
0
    pub fn is_animated(&self) -> bool {
671
0
        self.frame_control.is_some() && self.animation_control.is_some()
672
0
    }
673
674
    /// Returns the frame control information of the image.
675
0
    pub fn animation_control(&self) -> Option<&AnimationControl> {
676
0
        self.animation_control.as_ref()
677
0
    }
678
679
    /// Returns the frame control information of the current frame
680
0
    pub fn frame_control(&self) -> Option<&FrameControl> {
681
0
        self.frame_control.as_ref()
682
0
    }
683
684
    /// Returns the number of bits per pixel.
685
0
    pub fn bits_per_pixel(&self) -> usize {
686
0
        self.color_type.samples() * self.bit_depth as usize
687
0
    }
688
689
    /// Returns the number of bytes per pixel.
690
2.49k
    pub fn bytes_per_pixel(&self) -> usize {
691
2.49k
        // If adjusting this for expansion or other transformation passes, remember to keep the old
692
2.49k
        // implementation for bpp_in_prediction, which is internal to the png specification.
693
2.49k
        self.color_type.samples() * ((self.bit_depth as usize + 7) >> 3)
694
2.49k
    }
695
696
    /// Return the number of bytes for this pixel used in prediction.
697
    ///
698
    /// Some filters use prediction, over the raw bytes of a scanline. Where a previous pixel is
699
    /// require for such forms the specification instead references previous bytes. That is, for
700
    /// a gray pixel of bit depth 2, the pixel used in prediction is actually 4 pixels prior. This
701
    /// has the consequence that the number of possible values is rather small. To make this fact
702
    /// more obvious in the type system and the optimizer we use an explicit enum here.
703
2.49k
    pub(crate) fn bpp_in_prediction(&self) -> BytesPerPixel {
704
2.49k
        BytesPerPixel::from_usize(self.bytes_per_pixel())
705
2.49k
    }
706
707
    /// Returns the number of bytes needed for one deinterlaced image.
708
0
    pub fn raw_bytes(&self) -> usize {
709
0
        self.height as usize * self.raw_row_length()
710
0
    }
711
712
    /// Returns the number of bytes needed for one deinterlaced row.
713
0
    pub fn raw_row_length(&self) -> usize {
714
0
        self.raw_row_length_from_width(self.width)
715
0
    }
716
717
4.22k
    pub(crate) fn checked_raw_row_length(&self) -> Option<usize> {
718
4.22k
        self.color_type
719
4.22k
            .checked_raw_row_length(self.bit_depth, self.width)
720
4.22k
    }
721
722
    /// Returns the number of bytes needed for one deinterlaced row of width `width`.
723
25.2k
    pub fn raw_row_length_from_width(&self, width: u32) -> usize {
724
25.2k
        self.color_type
725
25.2k
            .raw_row_length_from_width(self.bit_depth, width)
726
25.2k
    }
727
728
    /// Mark the image data as conforming to the SRGB color space with the specified rendering intent.
729
    ///
730
    /// Any ICC profiles will be ignored.
731
    ///
732
    /// Source gamma and chromaticities will be written only if they're set to fallback
733
    /// values specified in [11.3.2.5](https://www.w3.org/TR/png-3/#sRGB-gAMA-cHRM).
734
0
    pub(crate) fn set_source_srgb(&mut self, rendering_intent: SrgbRenderingIntent) {
735
0
        self.srgb = Some(rendering_intent);
736
0
        self.icc_profile = None;
737
0
    }
738
739
    /// Encode this header to the writer.
740
    ///
741
    /// Note that this does _not_ include the PNG signature, it starts with the IHDR chunk and then
742
    /// includes other chunks that were added to the header.
743
    #[deprecated(note = "Use Encoder+Writer instead")]
744
0
    pub fn encode<W: Write>(&self, mut w: W) -> encoder::Result<()> {
745
0
        // Encode the IHDR chunk
746
0
        let mut data = [0; 13];
747
0
        data[..4].copy_from_slice(&self.width.to_be_bytes());
748
0
        data[4..8].copy_from_slice(&self.height.to_be_bytes());
749
0
        data[8] = self.bit_depth as u8;
750
0
        data[9] = self.color_type as u8;
751
0
        data[12] = self.interlaced as u8;
752
0
        encoder::write_chunk(&mut w, chunk::IHDR, &data)?;
753
754
        // Encode the pHYs chunk
755
0
        if let Some(pd) = self.pixel_dims {
756
0
            let mut phys_data = [0; 9];
757
0
            phys_data[0..4].copy_from_slice(&pd.xppu.to_be_bytes());
758
0
            phys_data[4..8].copy_from_slice(&pd.yppu.to_be_bytes());
759
0
            match pd.unit {
760
0
                Unit::Meter => phys_data[8] = 1,
761
0
                Unit::Unspecified => phys_data[8] = 0,
762
            }
763
0
            encoder::write_chunk(&mut w, chunk::pHYs, &phys_data)?;
764
0
        }
765
766
        // If specified, the sRGB information overrides the source gamma and chromaticities.
767
0
        if let Some(srgb) = &self.srgb {
768
0
            srgb.encode(&mut w)?;
769
770
            // gAMA and cHRM are optional, for backwards compatibility
771
0
            let srgb_gamma = crate::srgb::substitute_gamma();
772
0
            if Some(srgb_gamma) == self.source_gamma {
773
0
                srgb_gamma.encode_gama(&mut w)?
774
0
            }
775
0
            let srgb_chromaticities = crate::srgb::substitute_chromaticities();
776
0
            if Some(srgb_chromaticities) == self.source_chromaticities {
777
0
                srgb_chromaticities.encode(&mut w)?;
778
0
            }
779
        } else {
780
0
            if let Some(gma) = self.source_gamma {
781
0
                gma.encode_gama(&mut w)?
782
0
            }
783
0
            if let Some(chrms) = self.source_chromaticities {
784
0
                chrms.encode(&mut w)?;
785
0
            }
786
0
            if let Some(iccp) = &self.icc_profile {
787
0
                encoder::write_iccp_chunk(&mut w, "_", iccp)?
788
0
            }
789
        }
790
791
0
        if let Some(exif) = &self.exif_metadata {
792
0
            encoder::write_chunk(&mut w, chunk::eXIf, exif)?;
793
0
        }
794
795
0
        if let Some(actl) = self.animation_control {
796
0
            actl.encode(&mut w)?;
797
0
        }
798
799
        // The position of the PLTE chunk is important, it must come before the tRNS chunk and after
800
        // many of the other metadata chunks.
801
0
        if let Some(p) = &self.palette {
802
0
            encoder::write_chunk(&mut w, chunk::PLTE, p)?;
803
0
        };
804
805
0
        if let Some(t) = &self.trns {
806
0
            encoder::write_chunk(&mut w, chunk::tRNS, t)?;
807
0
        }
808
809
0
        for text_chunk in &self.uncompressed_latin1_text {
810
0
            text_chunk.encode(&mut w)?;
811
        }
812
813
0
        for text_chunk in &self.compressed_latin1_text {
814
0
            text_chunk.encode(&mut w)?;
815
        }
816
817
0
        for text_chunk in &self.utf8_text {
818
0
            text_chunk.encode(&mut w)?;
819
        }
820
821
0
        Ok(())
822
0
    }
Unexecuted instantiation: <png::common::Info>::encode::<&mut &mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::Info>::encode::<_>
Unexecuted instantiation: <png::common::Info>::encode::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
823
}
824
825
impl BytesPerPixel {
826
2.49k
    pub(crate) fn from_usize(bpp: usize) -> Self {
827
2.49k
        match bpp {
828
67
            1 => BytesPerPixel::One,
829
32
            2 => BytesPerPixel::Two,
830
180
            3 => BytesPerPixel::Three,
831
2.18k
            4 => BytesPerPixel::Four,
832
10
            6 => BytesPerPixel::Six,   // Only rgb×16bit
833
26
            8 => BytesPerPixel::Eight, // Only rgba×16bit
834
0
            _ => unreachable!("Not a possible byte rounded pixel width"),
835
        }
836
2.49k
    }
837
838
0
    pub(crate) fn into_usize(self) -> usize {
839
0
        self as usize
840
0
    }
841
}
842
843
bitflags::bitflags! {
844
    /// Output transformations
845
    ///
846
    /// Many flags from libpng are not yet supported. A PR discussing/adding them would be nice.
847
    ///
848
    #[doc = "
849
    ```c
850
    /// Discard the alpha channel
851
    const STRIP_ALPHA         = 0x0002; // read only
852
    /// Expand 1; 2 and 4-bit samples to bytes
853
    const PACKING             = 0x0004; // read and write
854
    /// Change order of packed pixels to LSB first
855
    const PACKSWAP            = 0x0008; // read and write
856
    /// Invert monochrome images
857
    const INVERT_MONO         = 0x0020; // read and write
858
    /// Normalize pixels to the sBIT depth
859
    const SHIFT               = 0x0040; // read and write
860
    /// Flip RGB to BGR; RGBA to BGRA
861
    const BGR                 = 0x0080; // read and write
862
    /// Flip RGBA to ARGB or GA to AG
863
    const SWAP_ALPHA          = 0x0100; // read and write
864
    /// Byte-swap 16-bit samples
865
    const SWAP_ENDIAN         = 0x0200; // read and write
866
    /// Change alpha from opacity to transparency
867
    const INVERT_ALPHA        = 0x0400; // read and write
868
    const STRIP_FILLER        = 0x0800; // write only
869
    const STRIP_FILLER_BEFORE = 0x0800; // write only
870
    const STRIP_FILLER_AFTER  = 0x1000; // write only
871
    const GRAY_TO_RGB         = 0x2000; // read only
872
    const EXPAND_16           = 0x4000; // read only
873
    /// Similar to STRIP_16 but in libpng considering gamma?
874
    /// Not entirely sure the documentation says it is more
875
    /// accurate but doesn't say precisely how.
876
    const SCALE_16            = 0x8000; // read only
877
    ```
878
    "]
879
    pub struct Transformations: u32 {
880
        /// No transformation
881
        const IDENTITY            = 0x00000; // read and write */
882
        /// Strip 16-bit samples to 8 bits
883
        const STRIP_16            = 0x00001; // read only */
884
        /// Expand paletted images to RGB; expand grayscale images of
885
        /// less than 8-bit depth to 8-bit depth; and expand tRNS chunks
886
        /// to alpha channels.
887
        const EXPAND              = 0x00010; // read only */
888
        /// Expand paletted images to include an alpha channel. Implies `EXPAND`.
889
        const ALPHA               = 0x10000; // read only */
890
    }
891
}
892
893
impl Transformations {
894
    /// Transform every input to 8bit grayscale or color.
895
    ///
896
    /// This sets `EXPAND` and `STRIP_16` which is similar to the default transformation used by
897
    /// this library prior to `0.17`.
898
0
    pub fn normalize_to_color8() -> Transformations {
899
0
        Transformations::EXPAND | Transformations::STRIP_16
900
0
    }
901
}
902
903
/// Instantiate the default transformations, the identity transform.
904
impl Default for Transformations {
905
0
    fn default() -> Transformations {
906
0
        Transformations::IDENTITY
907
0
    }
908
}
909
910
#[derive(Debug)]
911
pub struct ParameterError {
912
    inner: ParameterErrorKind,
913
}
914
915
#[derive(Debug)]
916
pub(crate) enum ParameterErrorKind {
917
    /// A provided buffer must be have the exact size to hold the image data. Where the buffer can
918
    /// be allocated by the caller, they must ensure that it has a minimum size as hinted previously.
919
    /// Even though the size is calculated from image data, this does counts as a parameter error
920
    /// because they must react to a value produced by this library, which can have been subjected
921
    /// to limits.
922
    ImageBufferSize { expected: usize, actual: usize },
923
    /// A bit like return `None` from an iterator.
924
    /// We use it to differentiate between failing to seek to the next image in a sequence and the
925
    /// absence of a next image. This is an error of the caller because they should have checked
926
    /// the number of images by inspecting the header data returned when opening the image. This
927
    /// library will perform the checks necessary to ensure that data was accurate or error with a
928
    /// format error otherwise.
929
    PolledAfterEndOfImage,
930
    /// Attempt to continue decoding after a fatal, non-resumable error was reported (e.g. after
931
    /// [`DecodingError::Format`]).  The only case when it is possible to resume after an error
932
    /// is an `UnexpectedEof` scenario - see [`DecodingError::IoError`].
933
    PolledAfterFatalError,
934
}
935
936
impl From<ParameterErrorKind> for ParameterError {
937
0
    fn from(inner: ParameterErrorKind) -> Self {
938
0
        ParameterError { inner }
939
0
    }
940
}
941
942
impl fmt::Display for ParameterError {
943
0
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
944
        use ParameterErrorKind::*;
945
0
        match self.inner {
946
0
            ImageBufferSize { expected, actual } => {
947
0
                write!(fmt, "wrong data size, expected {} got {}", expected, actual)
948
            }
949
0
            PolledAfterEndOfImage => write!(fmt, "End of image has been reached"),
950
            PolledAfterFatalError => {
951
0
                write!(fmt, "A fatal decoding error has been encounted earlier")
952
            }
953
        }
954
0
    }
955
}