Coverage Report

Created: 2025-10-12 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/png-0.18.0/src/common.rs
Line
Count
Source
1
//! Common types shared between the encoder and decoder
2
use crate::text_metadata::{ITXtChunk, TEXtChunk, ZTXtChunk};
3
#[allow(unused_imports)] // used by doc comments only
4
use crate::Filter;
5
use crate::{chunk, encoder};
6
use io::Write;
7
use std::{borrow::Cow, convert::TryFrom, fmt, io};
8
9
/// Describes how a pixel is encoded.
10
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11
#[repr(u8)]
12
pub enum ColorType {
13
    /// 1 grayscale sample.
14
    Grayscale = 0,
15
    /// 1 red sample, 1 green sample, 1 blue sample.
16
    Rgb = 2,
17
    /// 1 sample for the palette index.
18
    Indexed = 3,
19
    /// 1 grayscale sample, then 1 alpha sample.
20
    GrayscaleAlpha = 4,
21
    /// 1 red sample, 1 green sample, 1 blue sample, and finally, 1 alpha sample.
22
    Rgba = 6,
23
}
24
25
impl ColorType {
26
    /// Returns the number of samples used per pixel encoded in this way.
27
70.4M
    pub fn samples(self) -> usize {
28
70.4M
        self.samples_u8().into()
29
70.4M
    }
30
31
93.2M
    pub(crate) fn samples_u8(self) -> u8 {
32
        use self::ColorType::*;
33
93.2M
        match self {
34
18.9M
            Grayscale | Indexed => 1,
35
6.80M
            Rgb => 3,
36
714k
            GrayscaleAlpha => 2,
37
66.8M
            Rgba => 4,
38
        }
39
93.2M
    }
40
41
    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
42
12.3k
    pub fn from_u8(n: u8) -> Option<ColorType> {
43
12.3k
        match n {
44
4.41k
            0 => Some(ColorType::Grayscale),
45
1.89k
            2 => Some(ColorType::Rgb),
46
1.00k
            3 => Some(ColorType::Indexed),
47
613
            4 => Some(ColorType::GrayscaleAlpha),
48
4.42k
            6 => Some(ColorType::Rgba),
49
9
            _ => None,
50
        }
51
12.3k
    }
52
53
22.8M
    pub(crate) fn checked_raw_row_length(self, depth: BitDepth, width: u32) -> Option<usize> {
54
        // No overflow can occur in 64 bits, we multiply 32-bit with 5 more bits.
55
22.8M
        let bits = u64::from(width) * u64::from(self.samples_u8()) * u64::from(depth.into_u8());
56
22.8M
        TryFrom::try_from(1 + (bits + 7) / 8).ok()
57
22.8M
    }
58
59
68.2M
    pub(crate) fn raw_row_length_from_width(self, depth: BitDepth, width: u32) -> usize {
60
68.2M
        let samples = width as usize * self.samples();
61
68.2M
        1 + match depth {
62
638k
            BitDepth::Sixteen => samples * 2,
63
65.4M
            BitDepth::Eight => samples,
64
2.20M
            subbyte => {
65
2.20M
                let samples_per_byte = 8 / subbyte as usize;
66
2.20M
                let whole = samples / samples_per_byte;
67
2.20M
                let fract = usize::from(samples % samples_per_byte > 0);
68
2.20M
                whole + fract
69
            }
70
        }
71
68.2M
    }
72
73
12.3k
    pub(crate) fn is_combination_invalid(self, bit_depth: BitDepth) -> bool {
74
        // Section 11.2.2 of the PNG standard disallows several combinations
75
        // of bit depth and color type
76
12.3k
        ((bit_depth == BitDepth::One || bit_depth == BitDepth::Two || bit_depth == BitDepth::Four)
77
3.20k
            && (self == ColorType::Rgb
78
3.20k
                || self == ColorType::GrayscaleAlpha
79
3.20k
                || self == ColorType::Rgba))
80
12.3k
            || (bit_depth == BitDepth::Sixteen && self == ColorType::Indexed)
81
12.3k
    }
82
83
0
    pub(crate) fn bits_per_pixel(&self, bit_depth: BitDepth) -> usize {
84
0
        self.samples() * bit_depth as usize
85
0
    }
86
87
6.88k
    pub(crate) fn bytes_per_pixel(&self, bit_depth: BitDepth) -> usize {
88
        // If adjusting this for expansion or other transformation passes, remember to keep the old
89
        // implementation for bpp_in_prediction, which is internal to the png specification.
90
6.88k
        self.samples() * ((bit_depth as usize + 7) >> 3)
91
6.88k
    }
92
}
93
94
/// Bit depth of the PNG file.
95
/// Specifies the number of bits per sample.
96
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97
#[repr(u8)]
98
pub enum BitDepth {
99
    One = 1,
100
    Two = 2,
101
    Four = 4,
102
    Eight = 8,
103
    Sixteen = 16,
104
}
105
106
/// Internal count of bytes per pixel.
107
/// This is used for filtering which never uses sub-byte units. This essentially reduces the number
108
/// of possible byte chunk lengths to a very small set of values appropriate to be defined as an
109
/// enum.
110
#[derive(Debug, Clone, Copy)]
111
#[repr(u8)]
112
pub(crate) enum BytesPerPixel {
113
    One = 1,
114
    Two = 2,
115
    Three = 3,
116
    Four = 4,
117
    Six = 6,
118
    Eight = 8,
119
}
120
121
impl BitDepth {
122
    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
123
68.3M
    pub fn from_u8(n: u8) -> Option<BitDepth> {
124
68.3M
        match n {
125
1.41k
            1 => Some(BitDepth::One),
126
1.04k
            2 => Some(BitDepth::Two),
127
750
            4 => Some(BitDepth::Four),
128
67.6M
            8 => Some(BitDepth::Eight),
129
645k
            16 => Some(BitDepth::Sixteen),
130
65
            _ => None,
131
        }
132
68.3M
    }
133
134
22.8M
    pub(crate) fn into_u8(self) -> u8 {
135
22.8M
        self as u8
136
22.8M
    }
137
}
138
139
/// Pixel dimensions information
140
#[derive(Clone, Copy, Debug)]
141
pub struct PixelDimensions {
142
    /// Pixels per unit, X axis
143
    pub xppu: u32,
144
    /// Pixels per unit, Y axis
145
    pub yppu: u32,
146
    /// Either *Meter* or *Unspecified*
147
    pub unit: Unit,
148
}
149
150
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151
#[repr(u8)]
152
/// Physical unit of the pixel dimensions
153
pub enum Unit {
154
    Unspecified = 0,
155
    Meter = 1,
156
}
157
158
impl Unit {
159
    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
160
104
    pub fn from_u8(n: u8) -> Option<Unit> {
161
104
        match n {
162
20
            0 => Some(Unit::Unspecified),
163
23
            1 => Some(Unit::Meter),
164
61
            _ => None,
165
        }
166
104
    }
167
}
168
169
/// How to reset buffer of an animated png (APNG) at the end of a frame.
170
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171
#[repr(u8)]
172
pub enum DisposeOp {
173
    /// Leave the buffer unchanged.
174
    None = 0,
175
    /// Clear buffer with the background color.
176
    Background = 1,
177
    /// Reset the buffer to the state before the current frame.
178
    Previous = 2,
179
}
180
181
impl DisposeOp {
182
    /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now.
183
560
    pub fn from_u8(n: u8) -> Option<DisposeOp> {
184
560
        match n {
185
481
            0 => Some(DisposeOp::None),
186
57
            1 => Some(DisposeOp::Background),
187
9
            2 => Some(DisposeOp::Previous),
188
13
            _ => None,
189
        }
190
560
    }
191
}
192
193
impl fmt::Display for DisposeOp {
194
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195
0
        let name = match *self {
196
0
            DisposeOp::None => "DISPOSE_OP_NONE",
197
0
            DisposeOp::Background => "DISPOSE_OP_BACKGROUND",
198
0
            DisposeOp::Previous => "DISPOSE_OP_PREVIOUS",
199
        };
200
0
        write!(f, "{}", name)
201
0
    }
202
}
203
204
/// How pixels are written into the buffer.
205
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206
#[repr(u8)]
207
pub enum BlendOp {
208
    /// Pixels overwrite the value at their position.
209
    Source = 0,
210
    /// The new pixels are blended into the current state based on alpha.
211
    Over = 1,
212
}
213
214
impl BlendOp {
215
    /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now.
216
545
    pub fn from_u8(n: u8) -> Option<BlendOp> {
217
545
        match n {
218
503
            0 => Some(BlendOp::Source),
219
34
            1 => Some(BlendOp::Over),
220
8
            _ => None,
221
        }
222
545
    }
223
}
224
225
impl fmt::Display for BlendOp {
226
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227
0
        let name = match *self {
228
0
            BlendOp::Source => "BLEND_OP_SOURCE",
229
0
            BlendOp::Over => "BLEND_OP_OVER",
230
        };
231
0
        write!(f, "{}", name)
232
0
    }
233
}
234
235
/// Frame control information
236
#[derive(Clone, Copy, Debug)]
237
pub struct FrameControl {
238
    /// Sequence number of the animation chunk, starting from 0
239
    pub sequence_number: u32,
240
    /// Width of the following frame
241
    pub width: u32,
242
    /// Height of the following frame
243
    pub height: u32,
244
    /// X position at which to render the following frame
245
    pub x_offset: u32,
246
    /// Y position at which to render the following frame
247
    pub y_offset: u32,
248
    /// Frame delay fraction numerator
249
    pub delay_num: u16,
250
    /// Frame delay fraction denominator
251
    pub delay_den: u16,
252
    /// Type of frame area disposal to be done after rendering this frame
253
    pub dispose_op: DisposeOp,
254
    /// Type of frame area rendering for this frame
255
    pub blend_op: BlendOp,
256
}
257
258
impl Default for FrameControl {
259
0
    fn default() -> FrameControl {
260
0
        FrameControl {
261
0
            sequence_number: 0,
262
0
            width: 0,
263
0
            height: 0,
264
0
            x_offset: 0,
265
0
            y_offset: 0,
266
0
            delay_num: 1,
267
0
            delay_den: 30,
268
0
            dispose_op: DisposeOp::None,
269
0
            blend_op: BlendOp::Source,
270
0
        }
271
0
    }
272
}
273
274
impl FrameControl {
275
0
    pub fn set_seq_num(&mut self, s: u32) {
276
0
        self.sequence_number = s;
277
0
    }
278
279
0
    pub fn inc_seq_num(&mut self, i: u32) {
280
0
        self.sequence_number += i;
281
0
    }
282
283
0
    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
284
0
        let mut data = [0u8; 26];
285
0
        data[..4].copy_from_slice(&self.sequence_number.to_be_bytes());
286
0
        data[4..8].copy_from_slice(&self.width.to_be_bytes());
287
0
        data[8..12].copy_from_slice(&self.height.to_be_bytes());
288
0
        data[12..16].copy_from_slice(&self.x_offset.to_be_bytes());
289
0
        data[16..20].copy_from_slice(&self.y_offset.to_be_bytes());
290
0
        data[20..22].copy_from_slice(&self.delay_num.to_be_bytes());
291
0
        data[22..24].copy_from_slice(&self.delay_den.to_be_bytes());
292
0
        data[24] = self.dispose_op as u8;
293
0
        data[25] = self.blend_op as u8;
294
295
0
        encoder::write_chunk(w, chunk::fcTL, &data)
296
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>>>
297
}
298
299
/// Animation control information
300
#[derive(Clone, Copy, Debug)]
301
pub struct AnimationControl {
302
    /// Number of frames
303
    pub num_frames: u32,
304
    /// Number of times to loop this APNG.  0 indicates infinite looping.
305
    pub num_plays: u32,
306
}
307
308
impl AnimationControl {
309
0
    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
310
0
        let mut data = [0; 8];
311
0
        data[..4].copy_from_slice(&self.num_frames.to_be_bytes());
312
0
        data[4..].copy_from_slice(&self.num_plays.to_be_bytes());
313
0
        encoder::write_chunk(w, chunk::acTL, &data)
314
0
    }
Unexecuted instantiation: <png::common::AnimationControl>::encode::<&mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::AnimationControl>::encode::<_>
Unexecuted instantiation: <png::common::AnimationControl>::encode::<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
315
}
316
317
/// The type and strength of applied compression.
318
///
319
/// This is a simple, high-level interface that will automatically choose
320
/// the appropriate DEFLATE compression mode and PNG filter.
321
///
322
/// If you need more control over the encoding parameters,
323
/// you can set the [DeflateCompression] and [Filter] manually.
324
#[derive(Debug, Clone, Copy)]
325
#[non_exhaustive]
326
pub enum Compression {
327
    /// No compression whatsoever. Fastest, but results in large files.
328
    NoCompression,
329
    /// Extremely fast but light compression.
330
    ///
331
    /// Note: When used in streaming mode, this compression level can actually result in files
332
    /// *larger* than would be produced by `NoCompression` on incompressible data because
333
    /// it doesn't do any buffering of the output stream to detect whether the data is being compressed or not.
334
    Fastest,
335
    /// Extremely fast compression with a decent compression ratio.
336
    ///
337
    /// Significantly outperforms libpng and other popular encoders by using a [specialized DEFLATE
338
    /// implementation tuned for PNG](https://crates.io/crates/fdeflate), while still providing
339
    /// better compression ratio than the fastest modes of other encoders.
340
    ///
341
    /// Like `Compression::Fast` this can currently produce files larger than `NoCompression` in
342
    /// streaming mode when given incompressible data. This may change in the future.
343
    Fast,
344
    /// Balances encoding speed and compression ratio
345
    Balanced,
346
    /// Spend much more time to produce a slightly smaller file than with `Balanced`.
347
    High,
348
}
349
350
impl Default for Compression {
351
0
    fn default() -> Self {
352
0
        Self::Balanced
353
0
    }
354
}
355
356
/// Advanced compression settings with more customization options than [Compression].
357
///
358
/// Note that this setting only affects DEFLATE compression.
359
/// Another setting that influences the compression ratio and lets you choose
360
/// between encoding speed and compression ratio is the [Filter].
361
///
362
/// ### Stability guarantees
363
///
364
/// The implementation details of DEFLATE compression may evolve over time,
365
/// even without a semver-breaking change to the version of `png` crate.
366
///
367
/// If a certain compression setting is superseded by other options,
368
/// it may be marked deprecated and remapped to a different option.
369
/// You will see a deprecation notice when compiling code relying on such options.
370
#[non_exhaustive]
371
#[derive(Debug, Clone, Copy)]
372
pub enum DeflateCompression {
373
    /// Do not compress the data at all.
374
    ///
375
    /// Useful for incompressible images, or when speed is paramount and you don't care about size
376
    /// at all.
377
    ///
378
    /// This mode also disables filters, forcing [Filter::NoFilter].
379
    NoCompression,
380
381
    /// Excellent for creating lightly compressed PNG images very quickly.
382
    ///
383
    /// Uses the [fdeflate](https://crates.io/crates/fdeflate) crate under the hood to achieve
384
    /// speeds far exceeding what libpng is capable of while still providing a decent compression
385
    /// ratio.
386
    ///
387
    /// Note: When used in streaming mode, this compression level can actually result in files
388
    /// *larger* than would be produced by `NoCompression` because it doesn't do any buffering of
389
    /// the output stream to detect whether the data is being compressed or not.
390
    FdeflateUltraFast,
391
392
    /// Compression level between 1 and 9, where higher values mean better compression at the cost of
393
    /// speed.
394
    ///
395
    /// This is currently implemented via [flate2](https://crates.io/crates/flate2) crate
396
    /// by passing through the [compression level](flate2::Compression::new).
397
    ///
398
    /// The implementation details and the exact meaning of each level may change in the future,
399
    /// including in semver-compatible releases.
400
    Level(u8),
401
    // Other variants can be added in the future
402
}
403
404
impl Default for DeflateCompression {
405
0
    fn default() -> Self {
406
0
        Self::from_simple(Compression::Balanced)
407
0
    }
408
}
409
410
impl DeflateCompression {
411
0
    pub(crate) fn from_simple(value: Compression) -> Self {
412
0
        match value {
413
0
            Compression::NoCompression => Self::NoCompression,
414
0
            Compression::Fastest => Self::FdeflateUltraFast,
415
0
            Compression::Fast => Self::FdeflateUltraFast,
416
0
            Compression::Balanced => Self::Level(flate2::Compression::default().level() as u8),
417
0
            Compression::High => Self::Level(flate2::Compression::best().level() as u8),
418
        }
419
0
    }
420
}
421
422
/// An unsigned integer scaled version of a floating point value,
423
/// equivalent to an integer quotient with fixed denominator (100_000)).
424
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
425
pub struct ScaledFloat(u32);
426
427
impl ScaledFloat {
428
    const SCALING: f32 = 100_000.0;
429
430
    /// Gets whether the value is within the clamped range of this type.
431
0
    pub fn in_range(value: f32) -> bool {
432
0
        value >= 0.0 && (value * Self::SCALING).floor() <= u32::MAX as f32
433
0
    }
434
435
    /// Gets whether the value can be exactly converted in round-trip.
436
    #[allow(clippy::float_cmp)] // Stupid tool, the exact float compare is _the entire point_.
437
0
    pub fn exact(value: f32) -> bool {
438
0
        let there = Self::forward(value);
439
0
        let back = Self::reverse(there);
440
0
        value == back
441
0
    }
442
443
0
    fn forward(value: f32) -> u32 {
444
0
        (value.max(0.0) * Self::SCALING).floor() as u32
445
0
    }
446
447
0
    fn reverse(encoded: u32) -> f32 {
448
0
        encoded as f32 / Self::SCALING
449
0
    }
450
451
    /// Slightly inaccurate scaling and quantization.
452
    /// Clamps the value into the representable range if it is negative or too large.
453
0
    pub fn new(value: f32) -> Self {
454
0
        Self(Self::forward(value))
455
0
    }
456
457
    /// Fully accurate construction from a value scaled as per specification.
458
3.14k
    pub fn from_scaled(val: u32) -> Self {
459
3.14k
        Self(val)
460
3.14k
    }
461
462
    /// Get the accurate encoded value.
463
0
    pub fn into_scaled(self) -> u32 {
464
0
        self.0
465
0
    }
466
467
    /// Get the unscaled value as a floating point.
468
0
    pub fn into_value(self) -> f32 {
469
0
        Self::reverse(self.0)
470
0
    }
471
472
0
    pub(crate) fn encode_gama<W: Write>(self, w: &mut W) -> encoder::Result<()> {
473
0
        encoder::write_chunk(w, chunk::gAMA, &self.into_scaled().to_be_bytes())
474
0
    }
Unexecuted instantiation: <png::common::ScaledFloat>::encode_gama::<&mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::ScaledFloat>::encode_gama::<_>
Unexecuted instantiation: <png::common::ScaledFloat>::encode_gama::<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
475
}
476
477
/// Chromaticities of the color space primaries
478
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
479
pub struct SourceChromaticities {
480
    pub white: (ScaledFloat, ScaledFloat),
481
    pub red: (ScaledFloat, ScaledFloat),
482
    pub green: (ScaledFloat, ScaledFloat),
483
    pub blue: (ScaledFloat, ScaledFloat),
484
}
485
486
impl SourceChromaticities {
487
0
    pub fn new(white: (f32, f32), red: (f32, f32), green: (f32, f32), blue: (f32, f32)) -> Self {
488
0
        SourceChromaticities {
489
0
            white: (ScaledFloat::new(white.0), ScaledFloat::new(white.1)),
490
0
            red: (ScaledFloat::new(red.0), ScaledFloat::new(red.1)),
491
0
            green: (ScaledFloat::new(green.0), ScaledFloat::new(green.1)),
492
0
            blue: (ScaledFloat::new(blue.0), ScaledFloat::new(blue.1)),
493
0
        }
494
0
    }
495
496
    #[rustfmt::skip]
497
0
    pub fn to_be_bytes(self) -> [u8; 32] {
498
0
        let white_x = self.white.0.into_scaled().to_be_bytes();
499
0
        let white_y = self.white.1.into_scaled().to_be_bytes();
500
0
        let red_x   = self.red.0.into_scaled().to_be_bytes();
501
0
        let red_y   = self.red.1.into_scaled().to_be_bytes();
502
0
        let green_x = self.green.0.into_scaled().to_be_bytes();
503
0
        let green_y = self.green.1.into_scaled().to_be_bytes();
504
0
        let blue_x  = self.blue.0.into_scaled().to_be_bytes();
505
0
        let blue_y  = self.blue.1.into_scaled().to_be_bytes();
506
0
        [
507
0
            white_x[0], white_x[1], white_x[2], white_x[3],
508
0
            white_y[0], white_y[1], white_y[2], white_y[3],
509
0
            red_x[0],   red_x[1],   red_x[2],   red_x[3],
510
0
            red_y[0],   red_y[1],   red_y[2],   red_y[3],
511
0
            green_x[0], green_x[1], green_x[2], green_x[3],
512
0
            green_y[0], green_y[1], green_y[2], green_y[3],
513
0
            blue_x[0],  blue_x[1],  blue_x[2],  blue_x[3],
514
0
            blue_y[0],  blue_y[1],  blue_y[2],  blue_y[3],
515
0
        ]
516
0
    }
517
518
0
    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
519
0
        encoder::write_chunk(w, chunk::cHRM, &self.to_be_bytes())
520
0
    }
Unexecuted instantiation: <png::common::SourceChromaticities>::encode::<&mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::SourceChromaticities>::encode::<_>
Unexecuted instantiation: <png::common::SourceChromaticities>::encode::<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
521
}
522
523
/// The rendering intent for an sRGB image.
524
///
525
/// Presence of this data also indicates that the image conforms to the sRGB color space.
526
#[repr(u8)]
527
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
528
pub enum SrgbRenderingIntent {
529
    /// For images preferring good adaptation to the output device gamut at the expense of colorimetric accuracy, such as photographs.
530
    Perceptual = 0,
531
    /// For images requiring colour appearance matching (relative to the output device white point), such as logos.
532
    RelativeColorimetric = 1,
533
    /// For images preferring preservation of saturation at the expense of hue and lightness, such as charts and graphs.
534
    Saturation = 2,
535
    /// For images requiring preservation of absolute colorimetry, such as previews of images destined for a different output device (proofs).
536
    AbsoluteColorimetric = 3,
537
}
538
539
impl SrgbRenderingIntent {
540
0
    pub(crate) fn into_raw(self) -> u8 {
541
0
        self as u8
542
0
    }
543
544
18
    pub(crate) fn from_raw(raw: u8) -> Option<Self> {
545
18
        match raw {
546
6
            0 => Some(SrgbRenderingIntent::Perceptual),
547
1
            1 => Some(SrgbRenderingIntent::RelativeColorimetric),
548
0
            2 => Some(SrgbRenderingIntent::Saturation),
549
0
            3 => Some(SrgbRenderingIntent::AbsoluteColorimetric),
550
11
            _ => None,
551
        }
552
18
    }
553
554
0
    pub fn encode<W: Write>(self, w: &mut W) -> encoder::Result<()> {
555
0
        encoder::write_chunk(w, chunk::sRGB, &[self.into_raw()])
556
0
    }
Unexecuted instantiation: <png::common::SrgbRenderingIntent>::encode::<&mut alloc::vec::Vec<u8>>
Unexecuted instantiation: <png::common::SrgbRenderingIntent>::encode::<_>
Unexecuted instantiation: <png::common::SrgbRenderingIntent>::encode::<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
557
}
558
559
/// Coding-independent code points (cICP) specify the color space (primaries),
560
/// transfer function, matrix coefficients and scaling factor of the image using
561
/// the code points specified in [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273).
562
///
563
/// See https://www.w3.org/TR/png-3/#cICP-chunk for more details.
564
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
565
pub struct CodingIndependentCodePoints {
566
    /// Id number of the color primaries defined in
567
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) in "Table 2 -
568
    /// Interpretation of colour primaries (ColourPrimaries) value".
569
    pub color_primaries: u8,
570
571
    /// Id number of the transfer characteristics defined in
572
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) in "Table 3 -
573
    /// Interpretation of transfer characteristics (TransferCharacteristics)
574
    /// value".
575
    pub transfer_function: u8,
576
577
    /// Id number of the matrix coefficients defined in
578
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) in "Table 4 -
579
    /// Interpretation of matrix coefficients (MatrixCoefficients) value".
580
    ///
581
    /// This field is included to faithfully replicate the base
582
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) specification, but matrix coefficients
583
    /// will always be set to 0, because RGB is currently the only supported color mode in PNG.
584
    pub matrix_coefficients: u8,
585
586
    /// Whether the image is
587
    /// [a full range image](https://www.w3.org/TR/png-3/#dfn-full-range-image)
588
    /// or
589
    /// [a narrow range image](https://www.w3.org/TR/png-3/#dfn-narrow-range-image).
590
    ///
591
    /// This field is included to faithfully replicate the base
592
    /// [ITU-T-H.273](https://www.itu.int/rec/T-REC-H.273) specification, but it has limited
593
    /// practical application to PNG images, because narrow-range images are [quite
594
    /// rare](https://github.com/w3c/png/issues/312#issuecomment-2327349614) in practice.
595
    pub is_video_full_range_image: bool,
596
}
597
598
/// Mastering Display Color Volume (mDCV) used at the point of content creation,
599
/// as specified in [SMPTE-ST-2086](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=8353899).
600
///
601
/// See https://www.w3.org/TR/png-3/#mDCV-chunk for more details.
602
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
603
pub struct MasteringDisplayColorVolume {
604
    /// Mastering display chromaticities.
605
    pub chromaticities: SourceChromaticities,
606
607
    /// Mastering display maximum luminance.
608
    ///
609
    /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field
610
    /// is set to `10000000` then it indicates 1000 cd/m^2.
611
    pub max_luminance: u32,
612
613
    /// Mastering display minimum luminance.
614
    ///
615
    /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field
616
    /// is set to `10000000` then it indicates 1000 cd/m^2.
617
    pub min_luminance: u32,
618
}
619
620
/// Content light level information of HDR content.
621
///
622
/// See https://www.w3.org/TR/png-3/#cLLI-chunk for more details.
623
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
624
pub struct ContentLightLevelInfo {
625
    /// Maximum Content Light Level indicates the maximum light level of any
626
    /// single pixel (in cd/m^2, also known as nits) of the entire playback
627
    /// sequence.
628
    ///
629
    /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field
630
    /// is set to `10000000` then it indicates 1000 cd/m^2.
631
    ///
632
    /// A value of zero means that the value is unknown or not currently calculable.
633
    pub max_content_light_level: u32,
634
635
    /// Maximum Frame Average Light Level indicates the maximum value of the
636
    /// frame average light level (in cd/m^2, also known as nits) of the entire
637
    /// playback sequence. It is calculated by first averaging the decoded
638
    /// luminance values of all the pixels in each frame, and then using the
639
    /// value for the frame with the highest value.
640
    ///
641
    /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field
642
    /// is set to `10000000` then it indicates 1000 cd/m^2.
643
    ///
644
    /// A value of zero means that the value is unknown or not currently calculable.
645
    pub max_frame_average_light_level: u32,
646
}
647
648
/// PNG info struct
649
#[derive(Clone, Debug)]
650
#[non_exhaustive]
651
pub struct Info<'a> {
652
    pub width: u32,
653
    pub height: u32,
654
    pub bit_depth: BitDepth,
655
    /// How colors are stored in the image.
656
    pub color_type: ColorType,
657
    pub interlaced: bool,
658
    /// The image's `sBIT` chunk, if present; contains significant bits of the sample.
659
    pub sbit: Option<Cow<'a, [u8]>>,
660
    /// The image's `tRNS` chunk, if present; contains the alpha channel of the image's palette, 1 byte per entry.
661
    pub trns: Option<Cow<'a, [u8]>>,
662
    pub pixel_dims: Option<PixelDimensions>,
663
    /// 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).
664
    pub palette: Option<Cow<'a, [u8]>>,
665
    /// The contents of the image's gAMA chunk, if present.
666
    /// Prefer `source_gamma` to also get the derived replacement gamma from sRGB chunks.
667
    pub gama_chunk: Option<ScaledFloat>,
668
    /// The contents of the image's `cHRM` chunk, if present.
669
    /// Prefer `source_chromaticities` to also get the derived replacements from sRGB chunks.
670
    pub chrm_chunk: Option<SourceChromaticities>,
671
    /// The contents of the image's `bKGD` chunk, if present.
672
    pub bkgd: Option<Cow<'a, [u8]>>,
673
674
    pub frame_control: Option<FrameControl>,
675
    pub animation_control: Option<AnimationControl>,
676
    /// Gamma of the source system.
677
    /// Set by both `gAMA` as well as to a replacement by `sRGB` chunk.
678
    pub source_gamma: Option<ScaledFloat>,
679
    /// Chromaticities of the source system.
680
    /// Set by both `cHRM` as well as to a replacement by `sRGB` chunk.
681
    pub source_chromaticities: Option<SourceChromaticities>,
682
    /// The rendering intent of an SRGB image.
683
    ///
684
    /// Presence of this value also indicates that the image conforms to the SRGB color space.
685
    pub srgb: Option<SrgbRenderingIntent>,
686
    /// The ICC profile for the image.
687
    pub icc_profile: Option<Cow<'a, [u8]>>,
688
    /// The coding-independent code points for video signal type identification of the image.
689
    pub coding_independent_code_points: Option<CodingIndependentCodePoints>,
690
    /// The mastering display color volume for the image.
691
    pub mastering_display_color_volume: Option<MasteringDisplayColorVolume>,
692
    /// The content light information for the image.
693
    pub content_light_level: Option<ContentLightLevelInfo>,
694
    /// The EXIF metadata for the image.
695
    pub exif_metadata: Option<Cow<'a, [u8]>>,
696
    /// tEXt field
697
    pub uncompressed_latin1_text: Vec<TEXtChunk>,
698
    /// zTXt field
699
    pub compressed_latin1_text: Vec<ZTXtChunk>,
700
    /// iTXt field
701
    pub utf8_text: Vec<ITXtChunk>,
702
}
703
704
impl Default for Info<'_> {
705
12.3k
    fn default() -> Info<'static> {
706
12.3k
        Info {
707
12.3k
            width: 0,
708
12.3k
            height: 0,
709
12.3k
            bit_depth: BitDepth::Eight,
710
12.3k
            color_type: ColorType::Grayscale,
711
12.3k
            interlaced: false,
712
12.3k
            palette: None,
713
12.3k
            sbit: None,
714
12.3k
            trns: None,
715
12.3k
            gama_chunk: None,
716
12.3k
            chrm_chunk: None,
717
12.3k
            bkgd: None,
718
12.3k
            pixel_dims: None,
719
12.3k
            frame_control: None,
720
12.3k
            animation_control: None,
721
12.3k
            source_gamma: None,
722
12.3k
            source_chromaticities: None,
723
12.3k
            srgb: None,
724
12.3k
            icc_profile: None,
725
12.3k
            coding_independent_code_points: None,
726
12.3k
            mastering_display_color_volume: None,
727
12.3k
            content_light_level: None,
728
12.3k
            exif_metadata: None,
729
12.3k
            uncompressed_latin1_text: Vec::new(),
730
12.3k
            compressed_latin1_text: Vec::new(),
731
12.3k
            utf8_text: Vec::new(),
732
12.3k
        }
733
12.3k
    }
734
}
735
736
impl Info<'_> {
737
    /// A utility constructor for a default info with width and height.
738
0
    pub fn with_size(width: u32, height: u32) -> Self {
739
0
        Info {
740
0
            width,
741
0
            height,
742
0
            ..Default::default()
743
0
        }
744
0
    }
745
746
    /// Size of the image, width then height.
747
53.9k
    pub fn size(&self) -> (u32, u32) {
748
53.9k
        (self.width, self.height)
749
53.9k
    }
750
751
    /// Returns true if the image is an APNG image.
752
0
    pub fn is_animated(&self) -> bool {
753
0
        self.frame_control.is_some() && self.animation_control.is_some()
754
0
    }
755
756
    /// Returns the frame control information of the image.
757
0
    pub fn animation_control(&self) -> Option<&AnimationControl> {
758
0
        self.animation_control.as_ref()
759
0
    }
760
761
    /// Returns the frame control information of the current frame
762
0
    pub fn frame_control(&self) -> Option<&FrameControl> {
763
0
        self.frame_control.as_ref()
764
0
    }
765
766
    /// Returns the number of bits per pixel.
767
0
    pub fn bits_per_pixel(&self) -> usize {
768
0
        self.color_type.bits_per_pixel(self.bit_depth)
769
0
    }
770
771
    /// Returns the number of bytes per pixel.
772
6.88k
    pub fn bytes_per_pixel(&self) -> usize {
773
        // If adjusting this for expansion or other transformation passes, remember to keep the old
774
        // implementation for bpp_in_prediction, which is internal to the png specification.
775
6.88k
        self.color_type.bytes_per_pixel(self.bit_depth)
776
6.88k
    }
777
778
    /// Return the number of bytes for this pixel used in prediction.
779
    ///
780
    /// Some filters use prediction, over the raw bytes of a scanline. Where a previous pixel is
781
    /// require for such forms the specification instead references previous bytes. That is, for
782
    /// a gray pixel of bit depth 2, the pixel used in prediction is actually 4 pixels prior. This
783
    /// has the consequence that the number of possible values is rather small. To make this fact
784
    /// more obvious in the type system and the optimizer we use an explicit enum here.
785
6.88k
    pub(crate) fn bpp_in_prediction(&self) -> BytesPerPixel {
786
6.88k
        BytesPerPixel::from_usize(self.bytes_per_pixel())
787
6.88k
    }
788
789
    /// Returns the number of bytes needed for one deinterlaced image.
790
0
    pub fn raw_bytes(&self) -> usize {
791
0
        self.height as usize * self.raw_row_length()
792
0
    }
793
794
    /// Returns the number of bytes needed for one deinterlaced row.
795
0
    pub fn raw_row_length(&self) -> usize {
796
0
        self.raw_row_length_from_width(self.width)
797
0
    }
798
799
36.9k
    pub(crate) fn checked_raw_row_length(&self) -> Option<usize> {
800
36.9k
        self.color_type
801
36.9k
            .checked_raw_row_length(self.bit_depth, self.width)
802
36.9k
    }
803
804
    /// Returns the number of bytes needed for one deinterlaced row of width `width`.
805
22.7M
    pub fn raw_row_length_from_width(&self, width: u32) -> usize {
806
22.7M
        self.color_type
807
22.7M
            .raw_row_length_from_width(self.bit_depth, width)
808
22.7M
    }
809
810
    /// Gamma dependent on sRGB chunk
811
0
    pub fn gamma(&self) -> Option<ScaledFloat> {
812
0
        if self.srgb.is_some() {
813
0
            Some(crate::srgb::substitute_gamma())
814
        } else {
815
0
            self.gama_chunk
816
        }
817
0
    }
818
819
    /// Chromaticities dependent on sRGB chunk
820
0
    pub fn chromaticities(&self) -> Option<SourceChromaticities> {
821
0
        if self.srgb.is_some() {
822
0
            Some(crate::srgb::substitute_chromaticities())
823
        } else {
824
0
            self.chrm_chunk
825
        }
826
0
    }
827
828
    /// Mark the image data as conforming to the SRGB color space with the specified rendering intent.
829
    ///
830
    /// Any ICC profiles will be ignored.
831
    ///
832
    /// Source gamma and chromaticities will be written only if they're set to fallback
833
    /// values specified in [11.3.2.5](https://www.w3.org/TR/png-3/#sRGB-gAMA-cHRM).
834
0
    pub(crate) fn set_source_srgb(&mut self, rendering_intent: SrgbRenderingIntent) {
835
0
        self.srgb = Some(rendering_intent);
836
0
        self.icc_profile = None;
837
0
    }
838
}
839
840
impl BytesPerPixel {
841
6.88k
    pub(crate) fn from_usize(bpp: usize) -> Self {
842
6.88k
        match bpp {
843
1.86k
            1 => BytesPerPixel::One,
844
749
            2 => BytesPerPixel::Two,
845
980
            3 => BytesPerPixel::Three,
846
2.64k
            4 => BytesPerPixel::Four,
847
347
            6 => BytesPerPixel::Six,   // Only rgb×16bit
848
309
            8 => BytesPerPixel::Eight, // Only rgba×16bit
849
0
            _ => unreachable!("Not a possible byte rounded pixel width"),
850
        }
851
6.88k
    }
852
853
0
    pub(crate) fn into_usize(self) -> usize {
854
0
        self as usize
855
0
    }
856
}
857
858
bitflags::bitflags! {
859
    /// Output transformations
860
    ///
861
    /// Many flags from libpng are not yet supported. A PR discussing/adding them would be nice.
862
    ///
863
    #[doc = "
864
    ```c
865
    /// Discard the alpha channel
866
    const STRIP_ALPHA         = 0x0002; // read only
867
    /// Expand 1; 2 and 4-bit samples to bytes
868
    const PACKING             = 0x0004; // read and write
869
    /// Change order of packed pixels to LSB first
870
    const PACKSWAP            = 0x0008; // read and write
871
    /// Invert monochrome images
872
    const INVERT_MONO         = 0x0020; // read and write
873
    /// Normalize pixels to the sBIT depth
874
    const SHIFT               = 0x0040; // read and write
875
    /// Flip RGB to BGR; RGBA to BGRA
876
    const BGR                 = 0x0080; // read and write
877
    /// Flip RGBA to ARGB or GA to AG
878
    const SWAP_ALPHA          = 0x0100; // read and write
879
    /// Byte-swap 16-bit samples
880
    const SWAP_ENDIAN         = 0x0200; // read and write
881
    /// Change alpha from opacity to transparency
882
    const INVERT_ALPHA        = 0x0400; // read and write
883
    const STRIP_FILLER        = 0x0800; // write only
884
    const STRIP_FILLER_BEFORE = 0x0800; // write only
885
    const STRIP_FILLER_AFTER  = 0x1000; // write only
886
    const GRAY_TO_RGB         = 0x2000; // read only
887
    const EXPAND_16           = 0x4000; // read only
888
    /// Similar to STRIP_16 but in libpng considering gamma?
889
    /// Not entirely sure the documentation says it is more
890
    /// accurate but doesn't say precisely how.
891
    const SCALE_16            = 0x8000; // read only
892
    ```
893
    "]
894
    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
895
    pub struct Transformations: u32 {
896
        /// No transformation
897
        const IDENTITY            = 0x00000; // read and write */
898
        /// Strip 16-bit samples to 8 bits
899
        const STRIP_16            = 0x00001; // read only */
900
        /// Expand paletted images to RGB; expand grayscale images of
901
        /// less than 8-bit depth to 8-bit depth; and expand tRNS chunks
902
        /// to alpha channels.
903
        const EXPAND              = 0x00010; // read only */
904
        /// Expand paletted images to include an alpha channel. Implies `EXPAND`.
905
        const ALPHA               = 0x10000; // read only */
906
    }
907
}
908
909
impl Transformations {
910
    /// Transform every input to 8bit grayscale or color.
911
    ///
912
    /// This sets `EXPAND` and `STRIP_16` which is similar to the default transformation used by
913
    /// this library prior to `0.17`.
914
0
    pub fn normalize_to_color8() -> Transformations {
915
0
        Transformations::EXPAND | Transformations::STRIP_16
916
0
    }
917
}
918
919
/// Instantiate the default transformations, the identity transform.
920
impl Default for Transformations {
921
0
    fn default() -> Transformations {
922
0
        Transformations::IDENTITY
923
0
    }
924
}
925
926
#[derive(Debug)]
927
pub struct ParameterError {
928
    inner: ParameterErrorKind,
929
}
930
931
#[derive(Debug)]
932
pub(crate) enum ParameterErrorKind {
933
    /// A provided buffer must be have the exact size to hold the image data. Where the buffer can
934
    /// be allocated by the caller, they must ensure that it has a minimum size as hinted previously.
935
    /// Even though the size is calculated from image data, this does counts as a parameter error
936
    /// because they must react to a value produced by this library, which can have been subjected
937
    /// to limits.
938
    ImageBufferSize { expected: usize, actual: usize },
939
    /// A bit like return `None` from an iterator.
940
    /// We use it to differentiate between failing to seek to the next image in a sequence and the
941
    /// absence of a next image. This is an error of the caller because they should have checked
942
    /// the number of images by inspecting the header data returned when opening the image. This
943
    /// library will perform the checks necessary to ensure that data was accurate or error with a
944
    /// format error otherwise.
945
    PolledAfterEndOfImage,
946
    /// Attempt to continue decoding after a fatal, non-resumable error was reported (e.g. after
947
    /// [`DecodingError::Format`]).  The only case when it is possible to resume after an error
948
    /// is an `UnexpectedEof` scenario - see [`DecodingError::IoError`].
949
    PolledAfterFatalError,
950
}
951
952
impl From<ParameterErrorKind> for ParameterError {
953
0
    fn from(inner: ParameterErrorKind) -> Self {
954
0
        ParameterError { inner }
955
0
    }
956
}
957
958
impl fmt::Display for ParameterError {
959
0
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
960
        use ParameterErrorKind::*;
961
0
        match self.inner {
962
0
            ImageBufferSize { expected, actual } => {
963
0
                write!(fmt, "wrong data size, expected {} got {}", expected, actual)
964
            }
965
0
            PolledAfterEndOfImage => write!(fmt, "End of image has been reached"),
966
            PolledAfterFatalError => {
967
0
                write!(fmt, "A fatal decoding error has been encounted earlier")
968
            }
969
        }
970
0
    }
971
}