Coverage Report

Created: 2026-08-13 08:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/image/src/codecs/bmp/decoder.rs
Line
Count
Source
1
use crate::io::DecoderPreparedImage;
2
use crate::utils::vec_try_with_capacity;
3
use std::cmp::{self, Ordering};
4
use std::io::{self, BufRead, Seek, SeekFrom};
5
use std::iter::{repeat, Rev};
6
use std::slice::ChunksExactMut;
7
use std::{error, fmt};
8
9
use crate::color::ColorType;
10
use crate::error::{
11
    DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
12
};
13
use crate::io::{image_reader_type::SpecCompliance, DecodedImageAttributes};
14
use crate::{ImageDecoder, ImageFormat};
15
16
const BITMAPCOREHEADER_SIZE: u32 = 12;
17
const BITMAPINFOHEADER_SIZE: u32 = 40;
18
const BITMAPV2HEADER_SIZE: u32 = 52;
19
const BITMAPV3HEADER_SIZE: u32 = 56;
20
const BITMAPV4HEADER_SIZE: u32 = 108;
21
const BITMAPV5HEADER_SIZE: u32 = 124;
22
const FILE_HEADER_SIZE: u64 = 14;
23
24
const OS2_V2_MAX_HEADER_SIZE: u32 = 64;
25
const OS2_V2_MIN_HEADER_SIZE: u32 = 16;
26
27
// Compression method constants
28
const BI_RGB: u32 = 0;
29
const BI_RLE8: u32 = 1;
30
const BI_RLE4: u32 = 2;
31
const BI_BITFIELDS: u32 = 3;
32
const BI_JPEG: u32 = 4; // Used in legacy Windows pass-through printing path (not supported) and for RLE24
33
const BI_PNG: u32 = 5; // Used in legacy Windows pass-through printing path - not supported
34
const BI_ALPHABITFIELDS: u32 = 6;
35
const BI_CMYK: u32 = 11;
36
const BI_CMYKRLE8: u32 = 12;
37
const BI_CMYKRLE4: u32 = 13;
38
39
static R5_G5_B5_COLOR_MASK: Bitfields = Bitfields {
40
    r: Bitfield::from_len_shift(5, 10),
41
    g: Bitfield::from_len_shift(5, 5),
42
    b: Bitfield::from_len_shift(5, 0),
43
    a: Bitfield::from_len_shift(0, 0),
44
};
45
const R8_G8_B8_COLOR_MASK: Bitfields = Bitfields {
46
    r: Bitfield::from_len_shift(8, 24),
47
    g: Bitfield::from_len_shift(8, 16),
48
    b: Bitfield::from_len_shift(8, 8),
49
    a: Bitfield::from_len_shift(0, 0),
50
};
51
const R8_G8_B8_A8_COLOR_MASK: Bitfields = Bitfields {
52
    r: Bitfield::from_len_shift(8, 16),
53
    g: Bitfield::from_len_shift(8, 8),
54
    b: Bitfield::from_len_shift(8, 0),
55
    a: Bitfield::from_len_shift(8, 24),
56
};
57
58
const RLE_ESCAPE: u8 = 0;
59
const RLE_ESCAPE_EOL: u8 = 0;
60
const RLE_ESCAPE_EOF: u8 = 1;
61
const RLE_ESCAPE_DELTA: u8 = 2;
62
63
/// Opaque alpha channel value (fully opaque)
64
const ALPHA_OPAQUE: u8 = 0xFF;
65
66
/// The maximum width/height the decoder will process.
67
const MAX_WIDTH_HEIGHT: i32 = 0xFFFF;
68
69
/// The value of the V5 header field indicating an embedded ICC profile.
70
const PROFILE_EMBEDDED: u32 = u32::from_be_bytes(*b"MBED");
71
72
// BMP color space type constants (bV4CSType / bV5CSType).
73
const LCS_CALIBRATED_RGB: u32 = 0x00000000;
74
const LCS_SRGB: u32 = u32::from_be_bytes(*b"sRGB");
75
const LCS_WINDOWS_COLOR_SPACE: u32 = u32::from_be_bytes(*b"Win ");
76
77
/// During progressive decoding, the decoder applies transforms (e.g. a vertical
78
/// flip for bottom-up BMP files) as it writes rows into the output buffer.
79
/// This enum describes which rows contain valid pixel data by indicating the
80
/// transform that was applied.
81
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82
pub enum RowsDecoded {
83
    /// Rows were decoded sequentially from the top of the image.
84
    TopDown {
85
        /// Number of top rows decoded so far.
86
        rows: u32,
87
    },
88
    /// Rows were decoded from the bottom of the image (vertical flip).
89
    BottomUp {
90
        /// Number of bottom rows decoded so far.
91
        rows: u32,
92
    },
93
}
94
95
impl RowsDecoded {
96
    /// Returns the number of decoded rows.
97
    #[inline]
98
2.14k
    pub fn rows(&self) -> u32 {
99
2.14k
        match *self {
100
2.14k
            RowsDecoded::TopDown { rows } | RowsDecoded::BottomUp { rows } => rows,
101
        }
102
2.14k
    }
103
}
104
105
/// Parsed BITMAPCOREHEADER fields (excludes 4-byte size field).
106
struct ParsedCoreHeader {
107
    width: i32,
108
    height: i32,
109
    bit_count: u16,
110
    image_type: ImageType,
111
}
112
113
impl ParsedCoreHeader {
114
    /// Parse BITMAPCOREHEADER fields from an 8-byte buffer.
115
671
    fn parse(buffer: &[u8; 8], spec_strictness: SpecCompliance) -> ImageResult<Self> {
116
671
        let width = i32::from(u16::from_le_bytes(buffer[0..2].try_into().unwrap()));
117
671
        let height = i32::from(u16::from_le_bytes(buffer[2..4].try_into().unwrap()));
118
119
671
        let planes = u16::from_le_bytes(buffer[4..6].try_into().unwrap());
120
671
        if spec_strictness == SpecCompliance::Strict && planes != 1 {
121
0
            return Err(DecoderError::MoreThanOnePlane.into());
122
671
        }
123
124
671
        let bit_count = u16::from_le_bytes(buffer[6..8].try_into().unwrap());
125
671
        let image_type = match bit_count {
126
472
            1 | 4 | 8 => ImageType::Palette,
127
192
            24 => ImageType::RGB24,
128
            _ => {
129
7
                return Err(
130
7
                    DecoderError::InvalidChannelWidth(ChannelWidthError::Rgb, bit_count).into(),
131
7
                )
132
            }
133
        };
134
135
664
        Ok(ParsedCoreHeader {
136
664
            width,
137
664
            height,
138
664
            bit_count,
139
664
            image_type,
140
664
        })
141
671
    }
142
}
143
144
/// Parsed BITMAPINFOHEADER fields (excludes 4-byte size field).
145
struct ParsedInfoHeader {
146
    width: i32,
147
    height: i32,
148
    top_down: bool,
149
    bit_count: u16,
150
    compression: u32,
151
    colors_used: u32,
152
}
153
154
impl ParsedInfoHeader {
155
    /// Parse BITMAPINFOHEADER fields from a 36-byte buffer.
156
4.33k
    fn parse(buffer: &[u8; 36], spec_strictness: SpecCompliance) -> ImageResult<Self> {
157
4.33k
        let width = i32::from_le_bytes(buffer[0..4].try_into().unwrap());
158
4.33k
        let mut height = i32::from_le_bytes(buffer[4..8].try_into().unwrap());
159
160
        // Width cannot be negative
161
4.33k
        if width < 0 {
162
69
            return Err(DecoderError::NegativeWidth(width).into());
163
4.26k
        } else if width > MAX_WIDTH_HEIGHT || height > MAX_WIDTH_HEIGHT {
164
48
            return Err(DecoderError::ImageTooLarge(width, height).into());
165
4.21k
        }
166
167
4.21k
        if height == i32::MIN {
168
3
            return Err(DecoderError::InvalidHeight.into());
169
4.21k
        }
170
171
        // A negative height indicates a top-down DIB
172
4.21k
        let top_down = if height < 0 {
173
1.25k
            height = -height;
174
1.25k
            true
175
        } else {
176
2.95k
            false
177
        };
178
179
4.21k
        let planes = u16::from_le_bytes(buffer[8..10].try_into().unwrap());
180
4.21k
        if spec_strictness == SpecCompliance::Strict && planes != 1 {
181
0
            return Err(DecoderError::MoreThanOnePlane.into());
182
4.21k
        }
183
184
4.21k
        let bit_count = u16::from_le_bytes(buffer[10..12].try_into().unwrap());
185
4.21k
        let compression = u32::from_le_bytes(buffer[12..16].try_into().unwrap());
186
187
        // Top-down DIBs cannot be compressed (per BMP specification).
188
        // In lenient mode, we allow this for compatibility with other decoders.
189
4.21k
        if spec_strictness == SpecCompliance::Strict
190
0
            && top_down
191
0
            && compression != BI_RGB
192
0
            && compression != BI_BITFIELDS
193
0
            && compression != BI_ALPHABITFIELDS
194
        {
195
0
            return Err(DecoderError::ImageTypeInvalidForTopDown(compression).into());
196
4.21k
        }
197
198
        // Skip size_image (16-19), x_pix_permeter (20-23), y_pix_permeter (24-27)
199
4.21k
        let colors_used = u32::from_le_bytes(buffer[28..32].try_into().unwrap());
200
        // Skip important_colors (32-35)
201
4.21k
        Ok(ParsedInfoHeader {
202
4.21k
            width,
203
4.21k
            height,
204
4.21k
            top_down,
205
4.21k
            bit_count,
206
4.21k
            compression,
207
4.21k
            colors_used,
208
4.21k
        })
209
4.33k
    }
210
}
211
212
/// Parsed bitfield masks from DIB header.
213
struct ParsedBitfields {
214
    r_mask: u32,
215
    g_mask: u32,
216
    b_mask: u32,
217
    a_mask: u32,
218
}
219
220
impl ParsedBitfields {
221
    /// Parse bitfield masks from buffer.
222
    /// Caller must ensure buffer has sufficient length; this method does not validate.
223
    /// Note: Caller must ensure buffer has 12 (V2/Core) or 16 (V3/V4/V5) bytes length; this method does not validate.
224
    #[track_caller]
225
597
    fn parse(buffer: &[u8], has_alpha: bool) -> Self {
226
597
        let r_mask = u32::from_le_bytes(buffer[0..4].try_into().unwrap());
227
597
        let g_mask = u32::from_le_bytes(buffer[4..8].try_into().unwrap());
228
597
        let b_mask = u32::from_le_bytes(buffer[8..12].try_into().unwrap());
229
597
        let a_mask = if has_alpha {
230
295
            u32::from_le_bytes(buffer[12..16].try_into().unwrap())
231
        } else {
232
302
            0
233
        };
234
235
597
        ParsedBitfields {
236
597
            r_mask,
237
597
            g_mask,
238
597
            b_mask,
239
597
            a_mask,
240
597
        }
241
597
    }
242
}
243
244
/// Parsed ICC profile metadata from V5 header.
245
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
246
struct ParsedIccProfile {
247
    /// Absolute file offset where the ICC profile data starts.
248
    profile_offset: u64,
249
    profile_size: u32,
250
}
251
252
impl ParsedIccProfile {
253
    /// Parse ICC profile metadata from V5 header buffer.
254
    /// Returns None if no embedded ICC profile is present.
255
    /// Note: Caller must ensure buffer has 116 bytes length; this method does not validate.
256
    #[track_caller]
257
337
    fn parse(buffer: &[u8], bmp_header_offset: u64) -> Option<Self> {
258
        // bV5CSType is at offset 56 from header start, which is offset 52 from after the size field
259
337
        let cs_type = u32::from_le_bytes(buffer[52..56].try_into().unwrap());
260
261
        // Only embedded profiles are supported
262
337
        if cs_type != PROFILE_EMBEDDED {
263
0
            return None;
264
337
        }
265
266
        // bV5ProfileData is at offset 112 from header start, which is offset 108 from after size field
267
337
        let profile_offset_from_header = u32::from_le_bytes(buffer[108..112].try_into().unwrap());
268
269
        // bV5ProfileSize is at offset 116 from header start, which is offset 112 from after size field
270
337
        let profile_size = u32::from_le_bytes(buffer[112..116].try_into().unwrap());
271
272
337
        if profile_size == 0 || profile_offset_from_header == 0 {
273
8
            return None;
274
329
        }
275
276
        // Compute the absolute file offset by adding the header's position to the relative offset
277
329
        let profile_offset = bmp_header_offset + u64::from(profile_offset_from_header);
278
279
329
        Some(ParsedIccProfile {
280
329
            profile_offset,
281
329
            profile_size,
282
329
        })
283
337
    }
284
}
285
286
/// Color space data parsed from V4/V5 BMP headers.
287
#[derive(Debug, Clone)]
288
enum ColorSpaceInfo {
289
    /// LCS_CALIBRATED_RGB: endpoint and gamma values specified in the header.
290
    CalibratedRgb(CalibratedRgb),
291
    /// LCS_sRGB or LCS_WINDOWS_COLOR_SPACE: sRGB color space.
292
    Srgb,
293
    /// PROFILE_EMBEDDED: ICC profile data embedded in the file.
294
    EmbeddedIcc(ParsedIccProfile),
295
}
296
297
impl ColorSpaceInfo {
298
    /// Parse color space information from a V4/V5 header buffer.
299
    /// The buffer should start after the 4-byte size field.
300
    /// Note: Caller must ensure buffer has at least 104 bytes (V4 header minus size field);
301
    /// this method does not validate.
302
    #[track_caller]
303
818
    fn parse(buffer: &[u8], bmp_header_size: u32, bmp_header_offset: u64) -> Option<Self> {
304
        // bV4CSType at offset 56 from header start = offset 52 from after size field.
305
818
        let cs_type = u32::from_le_bytes(buffer[52..56].try_into().unwrap());
306
307
339
        match cs_type {
308
            LCS_CALIBRATED_RGB => {
309
1.05k
                let read_u32 = |offset: usize| -> u32 {
310
1.05k
                    u32::from_le_bytes(buffer[offset..offset + 4].try_into().unwrap())
311
1.05k
                };
312
313
                // FXPT2DOT30 (2.30 fixed-point) → f32.
314
702
                let fxpt2dot30 = |val: u32| -> f32 { val as f32 * (1.0 / (1u64 << 30) as f32) };
315
                // FXPT16DOT16 (16.16 fixed-point) → f32.
316
351
                let fxpt16dot16 = |val: u32| -> f32 { val as f32 / 65536.0 };
317
318
                // CIEXYZTRIPLE: 9 FXPT2DOT30 values at offsets 60-95 from header
319
                // start (56-91 from after size field). Layout:
320
                //   RedX, RedY, RedZ, GreenX, GreenY, GreenZ, BlueX, BlueY, BlueZ
321
                // We read only X and Y per primary (Z is implicit: Z = 1 - X - Y
322
                // for chromaticity, but BMP stores raw CIE XYZ values).
323
117
                let rx = fxpt2dot30(read_u32(56));
324
117
                let ry = fxpt2dot30(read_u32(60));
325
117
                let gx = fxpt2dot30(read_u32(68));
326
117
                let gy = fxpt2dot30(read_u32(72));
327
117
                let bx = fxpt2dot30(read_u32(80));
328
117
                let by = fxpt2dot30(read_u32(84));
329
330
                // Gamma values at offsets 96-107 from header start (92-103 from after size).
331
117
                let gamma_r = fxpt16dot16(read_u32(92));
332
117
                let gamma_g = fxpt16dot16(read_u32(96));
333
117
                let gamma_b = fxpt16dot16(read_u32(100));
334
335
                // Validate: Y values must be non-zero (used as denominators in
336
                // XYZ→chromaticity conversion by color management libraries).
337
117
                if ry == 0.0 || gy == 0.0 || by == 0.0 {
338
18
                    return None;
339
99
                }
340
341
99
                Some(ColorSpaceInfo::CalibratedRgb(CalibratedRgb {
342
99
                    rx,
343
99
                    ry,
344
99
                    gx,
345
99
                    gy,
346
99
                    bx,
347
99
                    by,
348
99
                    gamma_r,
349
99
                    gamma_g,
350
99
                    gamma_b,
351
99
                }))
352
            }
353
4
            LCS_SRGB | LCS_WINDOWS_COLOR_SPACE => Some(ColorSpaceInfo::Srgb),
354
339
            PROFILE_EMBEDDED if bmp_header_size >= BITMAPV5HEADER_SIZE => {
355
337
                ParsedIccProfile::parse(buffer, bmp_header_offset).map(ColorSpaceInfo::EmbeddedIcc)
356
            }
357
360
            _ => None,
358
        }
359
818
    }
360
}
361
362
/// Calibrated RGB color space parameters from a BMP V4/V5 header.
363
///
364
/// When the header's `bV4CSType` is `LCS_CALIBRATED_RGB`, these fields
365
/// carry the CIE XYZ endpoint coordinates for the RGB primaries and
366
/// per-channel gamma values, parsed from the FXPT2DOT30 / FXPT16DOT16
367
/// fixed-point fields in the header.
368
#[derive(Debug, Clone, Copy, PartialEq)]
369
struct CalibratedRgb {
370
    /// Red primary CIE X coordinate (FXPT2DOT30).
371
    rx: f32,
372
    /// Red primary CIE Y coordinate (FXPT2DOT30).
373
    ry: f32,
374
    /// Green primary CIE X coordinate (FXPT2DOT30).
375
    gx: f32,
376
    /// Green primary CIE Y coordinate (FXPT2DOT30).
377
    gy: f32,
378
    /// Blue primary CIE X coordinate (FXPT2DOT30).
379
    bx: f32,
380
    /// Blue primary CIE Y coordinate (FXPT2DOT30).
381
    by: f32,
382
    /// Red channel gamma (FXPT16DOT16).
383
    gamma_r: f32,
384
    /// Green channel gamma (FXPT16DOT16).
385
    gamma_g: f32,
386
    /// Blue channel gamma (FXPT16DOT16).
387
    gamma_b: f32,
388
}
389
390
impl CalibratedRgb {
391
    /// Build a moxcms `ColorProfile` from the calibrated RGB primaries and gamma.
392
99
    fn to_color_profile(self) -> moxcms::ColorProfile {
393
99
        let primaries = moxcms::ColorPrimaries {
394
99
            red: moxcms::Chromaticity::new(self.rx, self.ry),
395
99
            green: moxcms::Chromaticity::new(self.gx, self.gy),
396
99
            blue: moxcms::Chromaticity::new(self.bx, self.by),
397
99
        };
398
399
99
        let mut profile = moxcms::ColorProfile::new_srgb();
400
99
        profile.update_rgb_colorimetry(moxcms::WHITE_POINT_D65, primaries);
401
402
        // Clear inherited CICP metadata from the sRGB base profile.
403
99
        profile.cicp = None;
404
405
        // Use gamma directly as the TRC exponent via a parametric curve
406
        // (ICC type 0: Y = X^gamma).  This preserves full s15Fixed16 precision
407
        // when serialised to ICC bytes.
408
297
        let safe_gamma = |g: f32| if g > 0.0 { g } else { 1.0 };
409
297
        let parametric_trc = |g: f32| moxcms::ToneReprCurve::Parametric(vec![safe_gamma(g)]);
410
99
        profile.red_trc = Some(parametric_trc(self.gamma_r));
411
99
        profile.green_trc = Some(parametric_trc(self.gamma_g));
412
99
        profile.blue_trc = Some(parametric_trc(self.gamma_b));
413
99
        profile
414
99
    }
415
}
416
417
#[derive(PartialEq, Copy, Clone)]
418
enum ImageType {
419
    Palette,
420
    RGB16,
421
    RGB24,
422
    RGB32,
423
    RGBA32,
424
    RLE8,
425
    RLE4,
426
    RLE24,
427
    Bitfields16,
428
    Bitfields32,
429
}
430
431
/// Progress within the metadata reading phase.
432
///
433
/// The metadata is split into phases:
434
/// 1. Headers: File header, DIB header, and bitmasks (~30-150 bytes total).
435
///    These are always re-read together on retry since they're small.
436
/// 2. Optional data: Palette (up to 1KB) and ICC profile (variable, can be several KB).
437
///    These are tracked separately since they can be larger.
438
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
439
enum MetadataProgress {
440
    /// Initial state, nothing read yet.
441
    #[default]
442
    NotStarted,
443
    /// Reading main headers (file header, DIB header, bitmasks).
444
    /// Stores the start offset for seeking on retry.
445
    ReadingMainHeader { start_offset: u64 },
446
    /// Headers have been read; now reading palette.
447
    /// Stores header offsets for subsequent phases.
448
    ReadingPalette { offsets: HeaderOffsets },
449
    /// Headers and palette (if any) have been read; now reading ICC profile.
450
    /// Stores header offsets for the ICC profile read.
451
    ReadingIccProfile { offsets: HeaderOffsets },
452
    /// All metadata has been read successfully.
453
    Complete,
454
}
455
456
/// Offsets and sizes discovered during header parsing.
457
/// Carried through metadata phases to avoid redundant state.
458
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
459
struct HeaderOffsets {
460
    /// Absolute file offset where the DIB header ends (before any extra
461
    /// bitmask bytes or palette). This is the minimum valid data_offset.
462
    bmp_header_end: u64,
463
    /// Offset where palette data starts (after headers).
464
    palette_offset: u64,
465
    /// ICC profile metadata if present.
466
    icc_profile: Option<ParsedIccProfile>,
467
}
468
469
/// Progress within the RLE decoding phase.
470
///
471
/// RLE decoding checkpoints at row boundaries (after EndOfRow markers) and
472
/// after Delta instructions to avoid quadratic time with malformed files.
473
/// On UnexpectedEof, decoding resumes from the last stored checkpoint.
474
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
475
enum RleProgress {
476
    /// Not started yet.
477
    #[default]
478
    NotStarted,
479
    /// Checkpoint at position (row, x) with stream at stream_pos.
480
    /// On resume, decoding continues from this exact pixel position.
481
    Checkpoint { row: u32, x: u32, stream_pos: u64 },
482
}
483
484
/// Decoder state for resumable decoding.
485
///
486
/// This allows the decoder to recover from `UnexpectedEof` errors.
487
/// Decoding can resume from the last successfully decoded row or RLE symbol.
488
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
489
enum DecoderState {
490
    /// Currently reading metadata (headers, palette, ICC profile).
491
    ReadingMetadata { progress: MetadataProgress },
492
    /// Currently reading row-based (non-RLE) image data.
493
    /// Stores the number of rows successfully decoded.
494
    ReadingRowData { rows_decoded: u32 },
495
    /// Currently reading RLE-compressed data.
496
    /// Tracks progress at symbol boundaries for resumability.
497
    ReadingRleData { progress: RleProgress },
498
    /// Image data has been fully decoded.
499
    ImageDecoded,
500
}
501
502
impl Default for DecoderState {
503
5.30k
    fn default() -> Self {
504
5.30k
        DecoderState::ReadingMetadata {
505
5.30k
            progress: MetadataProgress::default(),
506
5.30k
        }
507
5.30k
    }
508
}
509
510
#[derive(PartialEq)]
511
enum BMPHeaderType {
512
    Core,
513
    Info,
514
    V2,
515
    V3,
516
    V4,
517
    V5,
518
    Os2V2,
519
}
520
521
#[derive(PartialEq)]
522
enum FormatFullBytes {
523
    RGB24,
524
    RGB32,
525
    RGBA32,
526
    Format888,
527
}
528
529
/// Compression type for bitfield-based formats.
530
#[derive(PartialEq, Copy, Clone)]
531
enum BitfieldCompression {
532
    /// BI_BITFIELDS: RGB masks only (3 masks, 12 bytes after header).
533
    Rgb,
534
    /// BI_ALPHABITFIELDS: RGBA masks (4 masks, 16 bytes after header).
535
    Rgba,
536
}
537
538
enum Chunker<'a> {
539
    FromTop(ChunksExactMut<'a, u8>),
540
    FromBottom(Rev<ChunksExactMut<'a, u8>>),
541
}
542
543
pub(crate) struct RowIterator<'a> {
544
    chunks: Chunker<'a>,
545
}
546
547
impl<'a> Iterator for RowIterator<'a> {
548
    type Item = &'a mut [u8];
549
550
    #[inline(always)]
551
1.70G
    fn next(&mut self) -> Option<&'a mut [u8]> {
552
1.70G
        match self.chunks {
553
1.69G
            Chunker::FromTop(ref mut chunks) => chunks.next(),
554
6.28M
            Chunker::FromBottom(ref mut chunks) => chunks.next(),
555
        }
556
1.70G
    }
557
}
558
559
/// All errors that can occur when attempting to parse a BMP
560
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
561
enum DecoderError {
562
    /// The bitfield mask interleaves set and unset bits
563
    BitfieldMaskNonContiguous,
564
    /// Bitfield mask invalid (e.g. too long for specified type)
565
    BitfieldMaskInvalid,
566
    /// Bitfield (of the specified width – 16- or 32-bit) mask not present
567
    BitfieldMaskMissing(u32),
568
    /// Bitfield (of the specified width – 16- or 32-bit) masks not present
569
    BitfieldMasksMissing(u32),
570
571
    /// BMP's "BM" signature wrong or missing
572
    BmpSignatureInvalid,
573
    /// More than the exactly one allowed plane specified by the format
574
    MoreThanOnePlane,
575
    /// Invalid amount of bits per channel for the specified image type
576
    InvalidChannelWidth(ChannelWidthError, u16),
577
578
    /// The width is negative
579
    NegativeWidth(i32),
580
    /// One of the dimensions is larger than a soft limit
581
    ImageTooLarge(i32, i32),
582
    /// The height is `i32::min_value()`
583
    ///
584
    /// General negative heights specify top-down DIBs
585
    InvalidHeight,
586
587
    /// Specified image type is invalid for top-down BMPs (i.e. is compressed)
588
    ImageTypeInvalidForTopDown(u32),
589
    /// Image type not currently recognized by the decoder
590
    ImageTypeUnknown(u32),
591
592
    /// Bitmap header smaller than the core header
593
    HeaderTooSmall(u32),
594
595
    /// The palette is bigger than allowed by the bit count of the BMP
596
    PaletteSizeExceeded { colors_used: u32, bit_count: u16 },
597
598
    /// read_image_data was called before read_metadata completed
599
    MetadataNotRead,
600
    /// Corrupt RLE data
601
    CorruptRleData,
602
}
603
604
impl fmt::Display for DecoderError {
605
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606
0
        match self {
607
0
            DecoderError::CorruptRleData => f.write_str("Corrupt RLE data"),
608
0
            DecoderError::BitfieldMaskNonContiguous => f.write_str("Non-contiguous bitfield mask"),
609
0
            DecoderError::BitfieldMaskInvalid => f.write_str("Invalid bitfield mask"),
610
0
            DecoderError::BitfieldMaskMissing(bb) => {
611
0
                f.write_fmt(format_args!("Missing {bb}-bit bitfield mask"))
612
            }
613
0
            DecoderError::BitfieldMasksMissing(bb) => {
614
0
                f.write_fmt(format_args!("Missing {bb}-bit bitfield masks"))
615
            }
616
0
            DecoderError::BmpSignatureInvalid => f.write_str("BMP signature not found"),
617
0
            DecoderError::MoreThanOnePlane => f.write_str("More than one plane"),
618
0
            DecoderError::InvalidChannelWidth(tp, n) => {
619
0
                f.write_fmt(format_args!("Invalid channel bit count for {tp}: {n}"))
620
            }
621
0
            DecoderError::NegativeWidth(w) => f.write_fmt(format_args!("Negative width ({w})")),
622
0
            DecoderError::ImageTooLarge(w, h) => f.write_fmt(format_args!(
623
0
                "Image too large (one of ({w}, {h}) > soft limit of {MAX_WIDTH_HEIGHT})"
624
            )),
625
0
            DecoderError::InvalidHeight => f.write_str("Invalid height"),
626
0
            DecoderError::ImageTypeInvalidForTopDown(tp) => f.write_fmt(format_args!(
627
0
                "Invalid image type {tp} for top-down image."
628
            )),
629
0
            DecoderError::ImageTypeUnknown(tp) => {
630
0
                f.write_fmt(format_args!("Unknown image compression type {tp}"))
631
            }
632
0
            DecoderError::HeaderTooSmall(s) => {
633
0
                f.write_fmt(format_args!("Bitmap header too small ({s} bytes)"))
634
            }
635
            DecoderError::PaletteSizeExceeded {
636
0
                colors_used,
637
0
                bit_count,
638
0
            } => f.write_fmt(format_args!(
639
0
                "Palette size {colors_used} exceeds maximum size for BMP with bit count of {bit_count}"
640
            )),
641
            DecoderError::MetadataNotRead => {
642
0
                f.write_str("read_image_data called before read_metadata completed")
643
            }
644
        }
645
0
    }
646
}
647
648
impl From<DecoderError> for ImageError {
649
391
    fn from(e: DecoderError) -> ImageError {
650
391
        ImageError::Decoding(DecodingError::new(ImageFormat::Bmp.into(), e))
651
391
    }
652
}
653
654
impl error::Error for DecoderError {}
655
656
/// Distinct image types whose saved channel width can be invalid
657
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
658
enum ChannelWidthError {
659
    /// RGB
660
    Rgb,
661
    /// 8-bit run length encoding
662
    Rle8,
663
    /// 4-bit run length encoding
664
    Rle4,
665
    /// 24-bit run length encoding (OS/2)
666
    Rle24,
667
    /// Bitfields (16- or 32-bit)
668
    Bitfields,
669
}
670
671
impl fmt::Display for ChannelWidthError {
672
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
673
0
        f.write_str(match self {
674
0
            ChannelWidthError::Rgb => "RGB",
675
0
            ChannelWidthError::Rle8 => "RLE8",
676
0
            ChannelWidthError::Rle4 => "RLE4",
677
0
            ChannelWidthError::Rle24 => "RLE24",
678
0
            ChannelWidthError::Bitfields => "bitfields",
679
        })
680
0
    }
681
}
682
683
/// BMP rows must be padded to a multiple of 4 bytes.
684
#[inline]
685
666
fn calculate_row_padding(bytes_per_row: usize) -> usize {
686
666
    (4 - (bytes_per_row % 4)) % 4
687
666
}
688
689
/// Allocate a row buffer with OOM protection.
690
1.26k
fn allocate_row_buffer(size: usize) -> ImageResult<Vec<u8>> {
691
1.26k
    let mut buffer = vec_try_with_capacity(size).map_err(|_| {
692
0
        ImageError::Unsupported(UnsupportedError::from_format_and_kind(
693
0
            ImageFormat::Bmp.into(),
694
0
            UnsupportedErrorKind::GenericFeature(format!(
695
0
                "Row buffer allocation ({} bytes) too large",
696
0
                size
697
0
            )),
698
0
        ))
699
0
    })?;
700
1.26k
    buffer.resize(size, 0);
701
1.26k
    Ok(buffer)
702
1.26k
}
703
704
/// Checks if the current scanline is the last one or not. If it is not the
705
/// last one, it performs a normal read. Otherwise, the special case applies:
706
/// Apparently many BMPs are missing the final byte at the end of the file.
707
/// This function checks if the stream is exactly one byte short of the
708
/// required final scanline length. If so, it reads the available bytes and
709
/// explicitly zeroes the missing trailing byte. Otherwise, it performs a normal `read_exact`.
710
326k
fn read_scanline(
711
326k
    reader: &mut (impl io::Read + Seek),
712
326k
    buf: &mut [u8],
713
326k
    current_file_row: &mut u32,
714
326k
    last_row: u32,
715
326k
    spec_strictness: SpecCompliance,
716
326k
) -> io::Result<()> {
717
326k
    let is_last_row = *current_file_row == last_row;
718
326k
    *current_file_row += 1;
719
720
326k
    if is_last_row && spec_strictness == SpecCompliance::Lenient {
721
645
        let current_pos = reader.stream_position()?;
722
645
        let end_pos = reader.seek(SeekFrom::End(0))?;
723
645
        reader.seek(SeekFrom::Start(current_pos))?;
724
725
645
        let Some((last, head)) = buf.split_last_mut() else {
726
            // Empty row, nothing to read.
727
0
            return Ok(());
728
        };
729
730
645
        if Ok(head.len()) == usize::try_from(end_pos - current_pos) {
731
84
            reader.read_exact(head)?;
732
84
            *last = b'\0';
733
84
            return Ok(());
734
561
        }
735
325k
    }
736
737
326k
    reader.read_exact(buf)
738
326k
}
739
740
/// Convenience function to check if the combination of width, length and number of
741
/// channels would result in a buffer that would overflow.
742
4.72k
fn check_for_overflow(width: i32, length: i32, channels: usize) -> ImageResult<()> {
743
4.72k
    num_bytes(width, length, channels)
744
4.72k
        .map(|_| ())
745
4.72k
        .ok_or_else(|| {
746
10
            ImageError::Unsupported(UnsupportedError::from_format_and_kind(
747
10
                ImageFormat::Bmp.into(),
748
10
                UnsupportedErrorKind::GenericFeature(format!(
749
10
                    "Image dimensions ({width}x{length} w/{channels} channels) are too large"
750
10
                )),
751
10
            ))
752
10
        })
753
4.72k
}
754
755
/// Calculate how many many bytes a buffer holding a decoded image with these properties would
756
/// require. Returns `None` if the buffer size would overflow or if one of the sizes are negative.
757
4.72k
fn num_bytes(width: i32, length: i32, channels: usize) -> Option<usize> {
758
4.72k
    if width <= 0 || length <= 0 {
759
10
        None
760
    } else {
761
4.71k
        match channels.checked_mul(width as usize) {
762
4.71k
            Some(n) => n.checked_mul(length as usize),
763
0
            None => None,
764
        }
765
    }
766
4.72k
}
767
768
/// Process rows with resumability support.
769
///
770
/// Calls `func` for each row from `start_row` to `height`, passing the output row slice.
771
/// On success, returns the total number of rows (height).
772
/// On error, returns the number of rows successfully completed before the error.
773
///
774
/// The caller is responsible for seeking to the correct file position before calling.
775
2.14k
fn with_rows_resumable<F>(
776
2.14k
    buffer: &mut [u8],
777
2.14k
    width: i32,
778
2.14k
    height: i32,
779
2.14k
    channels: usize,
780
2.14k
    top_down: bool,
781
2.14k
    start_row: u32,
782
2.14k
    mut func: F,
783
2.14k
) -> Result<u32, (u32, io::Error)>
784
2.14k
where
785
2.14k
    F: FnMut(&mut [u8]) -> io::Result<()>,
786
{
787
    // An overflow should already have been checked for when this is called,
788
    // though we check anyhow, as it somehow seems to increase performance slightly.
789
2.14k
    let row_width = channels.checked_mul(width as usize).unwrap();
790
2.14k
    let height = height as u32;
791
792
    /// Get the index of a row in the output buffer given the file row index.
793
    /// For top-down images, row 0 in the file is row 0 in the buffer.
794
    /// For bottom-up images, row 0 in the file is the last row in the buffer.
795
    #[inline]
796
326k
    fn output_row_index(file_row: u32, height: u32, top_down: bool) -> usize {
797
326k
        if top_down {
798
21.8k
            file_row as usize
799
        } else {
800
304k
            (height - 1 - file_row) as usize
801
        }
802
326k
    }
803
804
    /// Get a mutable reference to a specific row in the output buffer.
805
    #[inline]
806
326k
    fn get_row_mut(buf: &mut [u8], row_index: usize, row_stride: usize) -> &mut [u8] {
807
326k
        let start = row_index * row_stride;
808
326k
        &mut buf[start..][..row_stride]
809
326k
    }
810
811
326k
    for file_row in start_row..height {
812
326k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
326k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
326k
        if let Err(e) = func(row) {
816
1.84k
            return Err((file_row, e));
817
324k
        }
818
    }
819
301
    Ok(height)
820
2.14k
}
image::codecs::bmp::decoder::with_rows_resumable::<<image::codecs::bmp::decoder::BmpDecoder<std::io::cursor::Cursor<&[u8]>>>::read_16_bit_pixel_data::{closure#0}>
Line
Count
Source
775
350
fn with_rows_resumable<F>(
776
350
    buffer: &mut [u8],
777
350
    width: i32,
778
350
    height: i32,
779
350
    channels: usize,
780
350
    top_down: bool,
781
350
    start_row: u32,
782
350
    mut func: F,
783
350
) -> Result<u32, (u32, io::Error)>
784
350
where
785
350
    F: FnMut(&mut [u8]) -> io::Result<()>,
786
{
787
    // An overflow should already have been checked for when this is called,
788
    // though we check anyhow, as it somehow seems to increase performance slightly.
789
350
    let row_width = channels.checked_mul(width as usize).unwrap();
790
350
    let height = height as u32;
791
792
    /// Get the index of a row in the output buffer given the file row index.
793
    /// For top-down images, row 0 in the file is row 0 in the buffer.
794
    /// For bottom-up images, row 0 in the file is the last row in the buffer.
795
    #[inline]
796
    fn output_row_index(file_row: u32, height: u32, top_down: bool) -> usize {
797
        if top_down {
798
            file_row as usize
799
        } else {
800
            (height - 1 - file_row) as usize
801
        }
802
    }
803
804
    /// Get a mutable reference to a specific row in the output buffer.
805
    #[inline]
806
    fn get_row_mut(buf: &mut [u8], row_index: usize, row_stride: usize) -> &mut [u8] {
807
        let start = row_index * row_stride;
808
        &mut buf[start..][..row_stride]
809
    }
810
811
13.3k
    for file_row in start_row..height {
812
13.3k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
13.3k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
13.3k
        if let Err(e) = func(row) {
816
312
            return Err((file_row, e));
817
13.0k
        }
818
    }
819
38
    Ok(height)
820
350
}
image::codecs::bmp::decoder::with_rows_resumable::<<image::codecs::bmp::decoder::BmpDecoder<std::io::cursor::Cursor<&[u8]>>>::read_32_bit_pixel_data::{closure#0}>
Line
Count
Source
775
355
fn with_rows_resumable<F>(
776
355
    buffer: &mut [u8],
777
355
    width: i32,
778
355
    height: i32,
779
355
    channels: usize,
780
355
    top_down: bool,
781
355
    start_row: u32,
782
355
    mut func: F,
783
355
) -> Result<u32, (u32, io::Error)>
784
355
where
785
355
    F: FnMut(&mut [u8]) -> io::Result<()>,
786
{
787
    // An overflow should already have been checked for when this is called,
788
    // though we check anyhow, as it somehow seems to increase performance slightly.
789
355
    let row_width = channels.checked_mul(width as usize).unwrap();
790
355
    let height = height as u32;
791
792
    /// Get the index of a row in the output buffer given the file row index.
793
    /// For top-down images, row 0 in the file is row 0 in the buffer.
794
    /// For bottom-up images, row 0 in the file is the last row in the buffer.
795
    #[inline]
796
    fn output_row_index(file_row: u32, height: u32, top_down: bool) -> usize {
797
        if top_down {
798
            file_row as usize
799
        } else {
800
            (height - 1 - file_row) as usize
801
        }
802
    }
803
804
    /// Get a mutable reference to a specific row in the output buffer.
805
    #[inline]
806
    fn get_row_mut(buf: &mut [u8], row_index: usize, row_stride: usize) -> &mut [u8] {
807
        let start = row_index * row_stride;
808
        &mut buf[start..][..row_stride]
809
    }
810
811
111k
    for file_row in start_row..height {
812
111k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
111k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
111k
        if let Err(e) = func(row) {
816
317
            return Err((file_row, e));
817
111k
        }
818
    }
819
38
    Ok(height)
820
355
}
image::codecs::bmp::decoder::with_rows_resumable::<<image::codecs::bmp::decoder::BmpDecoder<std::io::cursor::Cursor<&[u8]>>>::read_full_byte_pixel_data::{closure#0}>
Line
Count
Source
775
558
fn with_rows_resumable<F>(
776
558
    buffer: &mut [u8],
777
558
    width: i32,
778
558
    height: i32,
779
558
    channels: usize,
780
558
    top_down: bool,
781
558
    start_row: u32,
782
558
    mut func: F,
783
558
) -> Result<u32, (u32, io::Error)>
784
558
where
785
558
    F: FnMut(&mut [u8]) -> io::Result<()>,
786
{
787
    // An overflow should already have been checked for when this is called,
788
    // though we check anyhow, as it somehow seems to increase performance slightly.
789
558
    let row_width = channels.checked_mul(width as usize).unwrap();
790
558
    let height = height as u32;
791
792
    /// Get the index of a row in the output buffer given the file row index.
793
    /// For top-down images, row 0 in the file is row 0 in the buffer.
794
    /// For bottom-up images, row 0 in the file is the last row in the buffer.
795
    #[inline]
796
    fn output_row_index(file_row: u32, height: u32, top_down: bool) -> usize {
797
        if top_down {
798
            file_row as usize
799
        } else {
800
            (height - 1 - file_row) as usize
801
        }
802
    }
803
804
    /// Get a mutable reference to a specific row in the output buffer.
805
    #[inline]
806
    fn get_row_mut(buf: &mut [u8], row_index: usize, row_stride: usize) -> &mut [u8] {
807
        let start = row_index * row_stride;
808
        &mut buf[start..][..row_stride]
809
    }
810
811
42.3k
    for file_row in start_row..height {
812
42.3k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
42.3k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
42.3k
        if let Err(e) = func(row) {
816
487
            return Err((file_row, e));
817
41.8k
        }
818
    }
819
71
    Ok(height)
820
558
}
image::codecs::bmp::decoder::with_rows_resumable::<<image::codecs::bmp::decoder::BmpDecoder<std::io::cursor::Cursor<&[u8]>>>::read_palettized_pixel_data::{closure#1}>
Line
Count
Source
775
878
fn with_rows_resumable<F>(
776
878
    buffer: &mut [u8],
777
878
    width: i32,
778
878
    height: i32,
779
878
    channels: usize,
780
878
    top_down: bool,
781
878
    start_row: u32,
782
878
    mut func: F,
783
878
) -> Result<u32, (u32, io::Error)>
784
878
where
785
878
    F: FnMut(&mut [u8]) -> io::Result<()>,
786
{
787
    // An overflow should already have been checked for when this is called,
788
    // though we check anyhow, as it somehow seems to increase performance slightly.
789
878
    let row_width = channels.checked_mul(width as usize).unwrap();
790
878
    let height = height as u32;
791
792
    /// Get the index of a row in the output buffer given the file row index.
793
    /// For top-down images, row 0 in the file is row 0 in the buffer.
794
    /// For bottom-up images, row 0 in the file is the last row in the buffer.
795
    #[inline]
796
    fn output_row_index(file_row: u32, height: u32, top_down: bool) -> usize {
797
        if top_down {
798
            file_row as usize
799
        } else {
800
            (height - 1 - file_row) as usize
801
        }
802
    }
803
804
    /// Get a mutable reference to a specific row in the output buffer.
805
    #[inline]
806
    fn get_row_mut(buf: &mut [u8], row_index: usize, row_stride: usize) -> &mut [u8] {
807
        let start = row_index * row_stride;
808
        &mut buf[start..][..row_stride]
809
    }
810
811
158k
    for file_row in start_row..height {
812
158k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
158k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
158k
        if let Err(e) = func(row) {
816
724
            return Err((file_row, e));
817
157k
        }
818
    }
819
154
    Ok(height)
820
878
}
821
822
16.6k
fn set_8bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
823
16.6k
    pixel_iter: &mut ChunksExactMut<u8>,
824
16.6k
    palette: &[[u8; 3]],
825
16.6k
    indices: T,
826
16.6k
    n_pixels: usize,
827
16.6k
) -> bool {
828
1.60M
    for idx in indices.take(n_pixels) {
829
1.60M
        if let Some(pixel) = pixel_iter.next() {
830
1.59M
            let rgb = palette[*idx as usize];
831
1.59M
            pixel[0] = rgb[0];
832
1.59M
            pixel[1] = rgb[1];
833
1.59M
            pixel[2] = rgb[2];
834
1.59M
        } else {
835
2.47k
            return false;
836
        }
837
    }
838
14.1k
    true
839
16.6k
}
840
841
2.58M
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
842
2.58M
    pixel_iter: &mut ChunksExactMut<u8>,
843
2.58M
    palette: &[[u8; 3]],
844
2.58M
    indices: T,
845
2.58M
    mut n_pixels: usize,
846
2.58M
) -> bool {
847
4.83M
    for idx in indices {
848
        macro_rules! set_pixel {
849
            ($i:expr) => {
850
                if n_pixels == 0 {
851
                    break;
852
                }
853
                if let Some(pixel) = pixel_iter.next() {
854
                    let rgb = palette[$i as usize];
855
                    pixel[0] = rgb[0];
856
                    pixel[1] = rgb[1];
857
                    pixel[2] = rgb[2];
858
                } else {
859
                    return false;
860
                }
861
                n_pixels -= 1;
862
            };
863
        }
864
4.83M
        set_pixel!(idx >> 4);
865
2.34M
        set_pixel!(idx & 0xf);
866
    }
867
73.6k
    true
868
2.58M
}
image::codecs::bmp::decoder::set_4bit_pixel_run::<core::slice::iter::Iter<u8>>
Line
Count
Source
841
48.5k
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
842
48.5k
    pixel_iter: &mut ChunksExactMut<u8>,
843
48.5k
    palette: &[[u8; 3]],
844
48.5k
    indices: T,
845
48.5k
    mut n_pixels: usize,
846
48.5k
) -> bool {
847
386k
    for idx in indices {
848
        macro_rules! set_pixel {
849
            ($i:expr) => {
850
                if n_pixels == 0 {
851
                    break;
852
                }
853
                if let Some(pixel) = pixel_iter.next() {
854
                    let rgb = palette[$i as usize];
855
                    pixel[0] = rgb[0];
856
                    pixel[1] = rgb[1];
857
                    pixel[2] = rgb[2];
858
                } else {
859
                    return false;
860
                }
861
                n_pixels -= 1;
862
            };
863
        }
864
383k
        set_pixel!(idx >> 4);
865
353k
        set_pixel!(idx & 0xf);
866
    }
867
7.80k
    true
868
48.5k
}
image::codecs::bmp::decoder::set_4bit_pixel_run::<core::iter::sources::repeat::Repeat<&u8>>
Line
Count
Source
841
2.53M
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
842
2.53M
    pixel_iter: &mut ChunksExactMut<u8>,
843
2.53M
    palette: &[[u8; 3]],
844
2.53M
    indices: T,
845
2.53M
    mut n_pixels: usize,
846
2.53M
) -> bool {
847
4.44M
    for idx in indices {
848
        macro_rules! set_pixel {
849
            ($i:expr) => {
850
                if n_pixels == 0 {
851
                    break;
852
                }
853
                if let Some(pixel) = pixel_iter.next() {
854
                    let rgb = palette[$i as usize];
855
                    pixel[0] = rgb[0];
856
                    pixel[1] = rgb[1];
857
                    pixel[2] = rgb[2];
858
                } else {
859
                    return false;
860
                }
861
                n_pixels -= 1;
862
            };
863
        }
864
4.44M
        set_pixel!(idx >> 4);
865
1.99M
        set_pixel!(idx & 0xf);
866
    }
867
65.8k
    true
868
2.53M
}
869
870
#[rustfmt::skip]
871
12.1k
fn set_2bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
872
12.1k
    pixel_iter: &mut ChunksExactMut<u8>,
873
12.1k
    palette: &[[u8; 3]],
874
12.1k
    indices: T,
875
12.1k
    mut n_pixels: usize,
876
12.1k
) -> bool {
877
201k
    for idx in indices {
878
        macro_rules! set_pixel {
879
            ($i:expr) => {
880
                if n_pixels == 0 {
881
                    break;
882
                }
883
                if let Some(pixel) = pixel_iter.next() {
884
                    let rgb = palette[$i as usize];
885
                    pixel[0] = rgb[0];
886
                    pixel[1] = rgb[1];
887
                    pixel[2] = rgb[2];
888
                } else {
889
                    return false;
890
                }
891
                n_pixels -= 1;
892
            };
893
        }
894
201k
        set_pixel!((idx >> 6) & 0x3u8);
895
195k
        set_pixel!((idx >> 4) & 0x3u8);
896
194k
        set_pixel!((idx >> 2) & 0x3u8);
897
189k
        set_pixel!( idx       & 0x3u8);
898
    }
899
12.1k
    true
900
12.1k
}
901
902
136k
fn set_1bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
903
136k
    pixel_iter: &mut ChunksExactMut<u8>,
904
136k
    palette: &[[u8; 3]],
905
136k
    indices: T,
906
136k
) {
907
457k
    for idx in indices {
908
456k
        let mut bit = 0x80;
909
        loop {
910
3.15M
            if let Some(pixel) = pixel_iter.next() {
911
3.01M
                let rgb = palette[usize::from((idx & bit) != 0)];
912
3.01M
                pixel[0] = rgb[0];
913
3.01M
                pixel[1] = rgb[1];
914
3.01M
                pixel[2] = rgb[2];
915
3.01M
            } else {
916
135k
                return;
917
            }
918
919
3.01M
            bit >>= 1;
920
3.01M
            if bit == 0 {
921
320k
                break;
922
2.69M
            }
923
        }
924
    }
925
136k
}
926
927
#[derive(PartialEq, Eq)]
928
struct Bitfield {
929
    shift: u32,
930
    len: u32,
931
    factor_addend: (u32, u32),
932
}
933
934
impl Bitfield {
935
    /// Factors and addends such that `((data * factor + addend) >> 8) as u8`
936
    /// maps the `data` value to the nearest value in the full 0-255 range.
937
    ///
938
    /// All constants come from the following site and were adjusted to use a
939
    /// shift of 8: https://rundevelopment.github.io/blog/fast-unorm-conversions#constants
940
    const FACTOR_ADDEND: [(u32, u32); 8] = [
941
        (0x01_00, 0),    // len=8: round(x * 255 / 255) = (x * 256 + 0) >> 8
942
        (0xff_00, 0),    // len=1: round(x * 255 / 1)   = (x * 65280 + 0) >> 8
943
        (0x55_00, 0),    // len=2: round(x * 255 / 3)   = (x * 21760 + 0) >> 8
944
        (0x24_80, 0),    // len=3: round(x * 255 / 7)   = (x * 9344 + 0) >> 8
945
        (0x11_00, 0),    // len=4: round(x * 255 / 15)  = (x * 4352 + 0) >> 8
946
        (0x08_3c, 0x5C), // len=5: round(x * 255 / 31)  = (x * 2108 + 92) >> 8
947
        (0x04_0c, 0x84), // len=6: round(x * 255 / 63)  = (x * 1036 + 132) >> 8
948
        (0x02_04, 0),    // len=7: round(x * 255 / 127) = (x * 516 + 0) >> 8
949
    ];
950
951
2.10k
    const fn from_len_shift(len: u32, shift: u32) -> Self {
952
2.10k
        debug_assert!(len <= 8);
953
2.10k
        debug_assert!(shift + len <= 32);
954
2.10k
        Bitfield {
955
2.10k
            shift,
956
2.10k
            len,
957
2.10k
            factor_addend: Self::FACTOR_ADDEND[(len % 8) as usize],
958
2.10k
        }
959
2.10k
    }
960
961
2.19k
    fn from_mask(mask: u32, max_len: u32) -> ImageResult<Bitfield> {
962
2.19k
        if mask == 0 {
963
750
            return Ok(Bitfield::from_len_shift(0, 0));
964
1.44k
        }
965
1.44k
        let mut shift = mask.trailing_zeros();
966
1.44k
        let mut len = (!(mask >> shift)).trailing_zeros();
967
1.44k
        if len != mask.count_ones() {
968
78
            return Err(DecoderError::BitfieldMaskNonContiguous.into());
969
1.36k
        }
970
1.36k
        if len + shift > max_len {
971
8
            return Err(DecoderError::BitfieldMaskInvalid.into());
972
1.35k
        }
973
1.35k
        if len > 8 {
974
545
            shift += len - 8;
975
545
            len = 8;
976
813
        }
977
1.35k
        Ok(Bitfield::from_len_shift(len, shift))
978
2.19k
    }
979
980
    #[inline]
981
2.63M
    fn read(&self, data: u32) -> u8 {
982
2.63M
        debug_assert!(self.len <= 8);
983
984
        // This performs branch-less UNORM conversion using the multiply-add
985
        // method. See `FACTOR_ADDEND` above for more information.
986
2.63M
        let (factor, addend) = self.factor_addend;
987
2.63M
        let mask = (1 << self.len) - 1;
988
2.63M
        let data = (data >> self.shift) & mask;
989
2.63M
        ((data * factor + addend) >> 8) as u8
990
2.63M
    }
991
}
992
993
#[derive(PartialEq, Eq)]
994
struct Bitfields {
995
    r: Bitfield,
996
    g: Bitfield,
997
    b: Bitfield,
998
    a: Bitfield,
999
}
1000
1001
impl Bitfields {
1002
597
    fn from_mask(
1003
597
        r_mask: u32,
1004
597
        g_mask: u32,
1005
597
        b_mask: u32,
1006
597
        a_mask: u32,
1007
597
        max_len: u32,
1008
597
        spec_strictness: SpecCompliance,
1009
597
    ) -> ImageResult<Bitfields> {
1010
511
        let bitfields = Bitfields {
1011
597
            r: Bitfield::from_mask(r_mask, max_len)?,
1012
553
            g: Bitfield::from_mask(g_mask, max_len)?,
1013
529
            b: Bitfield::from_mask(b_mask, max_len)?,
1014
515
            a: Bitfield::from_mask(a_mask, max_len)?,
1015
        };
1016
        // In strict mode, all RGB channels must have non-zero masks.
1017
        // In lenient mode, allow zero masks (the channel will read as 0).
1018
511
        if spec_strictness == SpecCompliance::Strict
1019
0
            && (bitfields.r.len == 0 || bitfields.g.len == 0 || bitfields.b.len == 0)
1020
        {
1021
0
            return Err(DecoderError::BitfieldMaskMissing(max_len).into());
1022
511
        }
1023
511
        Ok(bitfields)
1024
597
    }
1025
}
1026
1027
/// Helper to read RLE data using the already-buffered reader.
1028
/// Avoids double-buffering since BmpDecoder already requires BufRead.
1029
struct RleReader<'a, R> {
1030
    reader: &'a mut R,
1031
    bytes_read: u64,
1032
}
1033
1034
impl<'a, R: BufRead> RleReader<'a, R> {
1035
1.78k
    fn new(reader: &'a mut R) -> Self {
1036
1.78k
        Self {
1037
1.78k
            reader,
1038
1.78k
            bytes_read: 0,
1039
1.78k
        }
1040
1.78k
    }
1041
1042
    /// Total bytes consumed since this reader was created.
1043
5.99M
    fn bytes_read(&self) -> u64 {
1044
5.99M
        self.bytes_read
1045
5.99M
    }
1046
1047
26.2M
    fn read_byte(&mut self) -> io::Result<u8> {
1048
26.2M
        let buf = self.reader.fill_buf()?;
1049
26.2M
        if buf.is_empty() {
1050
1.04k
            return Err(io::Error::new(
1051
1.04k
                io::ErrorKind::UnexpectedEof,
1052
1.04k
                "unexpected end of RLE data",
1053
1.04k
            ));
1054
26.2M
        }
1055
26.2M
        let byte = buf[0];
1056
26.2M
        self.reader.consume(1);
1057
26.2M
        self.bytes_read += 1;
1058
26.2M
        Ok(byte)
1059
26.2M
    }
1060
1061
56.0k
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
1062
56.0k
        let mut remaining = buf.len();
1063
56.0k
        let mut offset = 0;
1064
1065
112k
        while remaining > 0 {
1066
56.1k
            let available = self.reader.fill_buf()?;
1067
56.1k
            if available.is_empty() {
1068
128
                return Err(io::Error::new(
1069
128
                    io::ErrorKind::UnexpectedEof,
1070
128
                    "unexpected end of RLE data",
1071
128
                ));
1072
56.0k
            }
1073
1074
56.0k
            let to_read = remaining.min(available.len());
1075
56.0k
            buf[offset..offset + to_read].copy_from_slice(&available[..to_read]);
1076
56.0k
            self.reader.consume(to_read);
1077
56.0k
            self.bytes_read += to_read as u64;
1078
56.0k
            offset += to_read;
1079
56.0k
            remaining -= to_read;
1080
        }
1081
1082
55.9k
        Ok(())
1083
56.0k
    }
1084
}
1085
1086
/// A bmp decoder
1087
pub struct BmpDecoder<R> {
1088
    reader: R,
1089
1090
    bmp_header_type: BMPHeaderType,
1091
    indexed_color: bool,
1092
1093
    width: i32,
1094
    height: i32,
1095
    data_offset: u64,
1096
    top_down: bool,
1097
    no_file_header: bool,
1098
    add_alpha_channel: bool,
1099
    image_type: ImageType,
1100
1101
    bit_count: u16,
1102
    colors_used: u32,
1103
    palette: Option<Vec<[u8; 3]>>,
1104
    bitfields: Option<Bitfields>,
1105
    icc_profile: Option<Vec<u8>>,
1106
    spec_strictness: SpecCompliance,
1107
1108
    /// Current decoder state for resumable decoding.
1109
    state: DecoderState,
1110
}
1111
1112
impl<R: BufRead + Seek> BmpDecoder<R> {
1113
5.30k
    fn new_decoder(reader: R) -> BmpDecoder<R> {
1114
5.30k
        BmpDecoder {
1115
5.30k
            reader,
1116
5.30k
1117
5.30k
            bmp_header_type: BMPHeaderType::Info,
1118
5.30k
            indexed_color: false,
1119
5.30k
1120
5.30k
            width: 0,
1121
5.30k
            height: 0,
1122
5.30k
            data_offset: 0,
1123
5.30k
            top_down: false,
1124
5.30k
            no_file_header: false,
1125
5.30k
            add_alpha_channel: false,
1126
5.30k
            image_type: ImageType::Palette,
1127
5.30k
1128
5.30k
            bit_count: 0,
1129
5.30k
            colors_used: 0,
1130
5.30k
            palette: None,
1131
5.30k
            bitfields: None,
1132
5.30k
            icc_profile: None,
1133
5.30k
            spec_strictness: SpecCompliance::default(),
1134
5.30k
            state: DecoderState::default(),
1135
5.30k
        }
1136
5.30k
    }
1137
1138
    /// Create a new decoder that decodes from the stream ```r```
1139
0
    pub fn new(reader: R) -> ImageResult<BmpDecoder<R>> {
1140
0
        let mut decoder = Self::new_decoder(reader);
1141
0
        decoder.read_metadata()?;
1142
0
        Ok(decoder)
1143
0
    }
1144
1145
    /// Create a new decoder with the given spec compliance mode.
1146
2.61k
    pub(crate) fn with_spec_compliance(
1147
2.61k
        reader: R,
1148
2.61k
        spec: SpecCompliance,
1149
2.61k
    ) -> ImageResult<BmpDecoder<R>> {
1150
2.61k
        let mut decoder = Self::new_decoder(reader);
1151
2.61k
        decoder.spec_strictness = spec;
1152
2.61k
        decoder.read_metadata()?;
1153
1.98k
        Ok(decoder)
1154
2.61k
    }
1155
1156
    /// Create a new decoder that decodes from the stream `r` without reading
1157
    /// metadata immediately. This allows for resumable decoding when the
1158
    /// underlying reader may return `UnexpectedEof`.
1159
    ///
1160
    /// After creating the decoder, call `read_metadata()` to read the BMP
1161
    /// headers. If it returns an `UnexpectedEof` error, you can retry on the
1162
    /// same decoder instance after more data becomes available.
1163
    ///
1164
    /// Once metadata is read, call `read_image_data()` to read the pixel data.
1165
    /// This also supports retrying on `UnexpectedEof`.
1166
    ///
1167
    /// # Example
1168
    ///
1169
    /// ```ignore
1170
    /// use image::codecs::bmp::BmpDecoder;
1171
    /// use image::error::ImageError;
1172
    /// use image::ImageDecoder;
1173
    /// use std::io;
1174
    ///
1175
    /// fn is_unexpected_eof(err: &ImageError) -> bool {
1176
    ///     matches!(err, ImageError::IoError(e) if e.kind() == io::ErrorKind::UnexpectedEof)
1177
    /// }
1178
    ///
1179
    /// let mut decoder = BmpDecoder::new_resumable(reader);
1180
    ///
1181
    /// // Phase 1: Read metadata (with retry on UnexpectedEof)
1182
    /// loop {
1183
    ///     match decoder.read_metadata() {
1184
    ///         Ok(()) => break,
1185
    ///         Err(ref e) if is_unexpected_eof(e) => {
1186
    ///             // Wait for more data and retry on same decoder
1187
    ///             continue;
1188
    ///         }
1189
    ///         Err(e) => return Err(e),
1190
    ///     }
1191
    /// }
1192
    ///
1193
    /// // Phase 2: Read image data (with retry on UnexpectedEof)
1194
    /// let mut buf = vec![0u8; decoder.total_bytes() as usize];
1195
    /// loop {
1196
    ///     match decoder.read_image_data(&mut buf) {
1197
    ///         Ok(()) => break,
1198
    ///         Err(ref e) if is_unexpected_eof(e) => {
1199
    ///             // Wait for more data and retry on same decoder
1200
    ///             continue;
1201
    ///         }
1202
    ///         Err(e) => return Err(e),
1203
    ///     }
1204
    /// }
1205
    /// ```
1206
0
    pub fn new_resumable(reader: R) -> BmpDecoder<R> {
1207
0
        Self::new_decoder(reader)
1208
0
    }
1209
1210
    /// Create a new decoder that decodes from the stream ```r``` without first
1211
    /// reading a BITMAPFILEHEADER. This is useful for decoding the `CF_DIB` format
1212
    /// directly from the Windows clipboard.
1213
0
    pub fn new_without_file_header(reader: R) -> ImageResult<BmpDecoder<R>> {
1214
0
        let mut decoder = Self::new_decoder(reader);
1215
0
        decoder.no_file_header = true;
1216
0
        decoder.read_metadata()?;
1217
0
        Ok(decoder)
1218
0
    }
1219
1220
    #[cfg(feature = "ico")]
1221
2.69k
    pub(crate) fn new_with_ico_format(reader: R) -> ImageResult<BmpDecoder<R>> {
1222
2.69k
        let mut decoder = Self::new_decoder(reader);
1223
2.69k
        decoder.read_metadata_in_ico_format()?;
1224
2.04k
        Ok(decoder)
1225
2.69k
    }
1226
1227
    /// If true, the palette in BMP does not apply to the image even if it is found.
1228
    /// In other words, the output image is the indexed color.
1229
0
    pub fn set_indexed_color(&mut self, indexed_color: bool) {
1230
0
        self.indexed_color = indexed_color;
1231
0
    }
1232
1233
    #[cfg(feature = "ico")]
1234
530
    pub(crate) fn reader(&mut self) -> &mut R {
1235
530
        &mut self.reader
1236
530
    }
1237
1238
5.30k
    fn read_file_header(&mut self) -> ImageResult<()> {
1239
5.30k
        if self.no_file_header {
1240
2.69k
            return Ok(());
1241
2.61k
        }
1242
1243
        // Read entire 14-byte file header
1244
2.61k
        let mut buffer = [0u8; FILE_HEADER_SIZE as usize];
1245
2.61k
        self.reader.read_exact(&mut buffer)?;
1246
1247
        // Check signature
1248
2.58k
        if &buffer[0..2] != b"BM" {
1249
25
            return Err(DecoderError::BmpSignatureInvalid.into());
1250
2.56k
        }
1251
1252
        // Skip file size (4 bytes) and reserved (4 bytes) at offsets 2-9
1253
        // Extract data_offset from bytes 10-13
1254
2.56k
        let data_offset = u32::from_le_bytes([buffer[10], buffer[11], buffer[12], buffer[13]]);
1255
2.56k
        self.data_offset = u64::from(data_offset);
1256
1257
2.56k
        Ok(())
1258
5.30k
    }
1259
1260
    /// Determine the image type from the compression method, bit count, and header type.
1261
4.21k
    fn image_type_from_compression(
1262
4.21k
        compression: u32,
1263
4.21k
        bit_count: u16,
1264
4.21k
        add_alpha_channel: bool,
1265
4.21k
        header_type: &BMPHeaderType,
1266
4.21k
    ) -> ImageResult<ImageType> {
1267
45
        match compression {
1268
314
            BI_RGB => match bit_count {
1269
554
                1 | 2 | 4 | 8 => Ok(ImageType::Palette),
1270
299
                16 => Ok(ImageType::RGB16),
1271
206
                24 => Ok(ImageType::RGB24),
1272
53
                32 if add_alpha_channel => Ok(ImageType::RGBA32),
1273
261
                32 => Ok(ImageType::RGB32),
1274
                _ => {
1275
28
                    Err(DecoderError::InvalidChannelWidth(ChannelWidthError::Rgb, bit_count).into())
1276
                }
1277
            },
1278
579
            BI_RLE8 => match bit_count {
1279
558
                8 => Ok(ImageType::RLE8),
1280
21
                _ => Err(
1281
21
                    DecoderError::InvalidChannelWidth(ChannelWidthError::Rle8, bit_count).into(),
1282
21
                ),
1283
            },
1284
565
            BI_RLE4 => match bit_count {
1285
533
                4 => Ok(ImageType::RLE4),
1286
32
                _ => Err(
1287
32
                    DecoderError::InvalidChannelWidth(ChannelWidthError::Rle4, bit_count).into(),
1288
32
                ),
1289
            },
1290
624
            BI_BITFIELDS | BI_ALPHABITFIELDS => match bit_count {
1291
123
                16 => Ok(ImageType::Bitfields16),
1292
498
                32 => Ok(ImageType::Bitfields32),
1293
3
                _ => Err(DecoderError::InvalidChannelWidth(
1294
3
                    ChannelWidthError::Bitfields,
1295
3
                    bit_count,
1296
3
                )
1297
3
                .into()),
1298
            },
1299
1.02k
            BI_JPEG if *header_type == BMPHeaderType::Os2V2 && bit_count == 24 => {
1300
976
                Ok(ImageType::RLE24)
1301
            }
1302
45
            BI_JPEG if *header_type == BMPHeaderType::Os2V2 => {
1303
40
                Err(DecoderError::InvalidChannelWidth(ChannelWidthError::Rle24, bit_count).into())
1304
            }
1305
5
            BI_JPEG => Err(ImageError::Unsupported(
1306
5
                UnsupportedError::from_format_and_kind(
1307
5
                    ImageFormat::Bmp.into(),
1308
5
                    UnsupportedErrorKind::GenericFeature("JPEG compression".to_owned()),
1309
5
                ),
1310
5
            )),
1311
2
            BI_PNG => Err(ImageError::Unsupported(
1312
2
                UnsupportedError::from_format_and_kind(
1313
2
                    ImageFormat::Bmp.into(),
1314
2
                    UnsupportedErrorKind::GenericFeature("PNG compression".to_owned()),
1315
2
                ),
1316
2
            )),
1317
6
            BI_CMYK | BI_CMYKRLE4 | BI_CMYKRLE8 => Err(ImageError::Unsupported(
1318
6
                UnsupportedError::from_format_and_kind(
1319
6
                    ImageFormat::Bmp.into(),
1320
6
                    UnsupportedErrorKind::GenericFeature("CMYK format".to_owned()),
1321
6
                ),
1322
6
            )),
1323
14
            _ => Err(DecoderError::ImageTypeUnknown(compression).into()),
1324
        }
1325
4.21k
    }
1326
1327
    /// Read BITMAPCOREHEADER <https://msdn.microsoft.com/en-us/library/vs/alm/dd183372(v=vs.85).aspx>
1328
    ///
1329
    /// returns Err if any of the values are invalid.
1330
678
    fn read_bitmap_core_header(&mut self) -> ImageResult<()> {
1331
        // Core header (after size field): width(2), height(2), planes(2), bitcount(2) = 8 bytes
1332
678
        let mut buffer = [0u8; 8];
1333
678
        self.reader.read_exact(&mut buffer)?;
1334
1335
671
        let parsed = ParsedCoreHeader::parse(&buffer, self.spec_strictness)?;
1336
1337
664
        self.width = parsed.width;
1338
664
        self.height = parsed.height;
1339
664
        self.bit_count = parsed.bit_count;
1340
664
        self.image_type = parsed.image_type;
1341
1342
664
        check_for_overflow(self.width, self.height, self.num_channels())?;
1343
1344
660
        Ok(())
1345
678
    }
1346
1347
    /// Read OS/2 BITMAPCOREHEADER2 (variable size 16-64 bytes, layout-compatible
1348
    /// with BITMAPINFOHEADER). Fields beyond the header size default to 0.
1349
2.28k
    fn read_bitmap_os2v2_header(&mut self, header_size: u32) -> ImageResult<()> {
1350
2.28k
        let remaining = (header_size - 4) as usize;
1351
1352
        // Zero-pad to 36 bytes for ParsedInfoHeader::parse.
1353
2.28k
        let mut buffer = [0u8; 36];
1354
2.28k
        let to_read = remaining.min(36);
1355
2.28k
        self.reader.read_exact(&mut buffer[..to_read])?;
1356
1357
        // Skip OS/2-specific fields beyond the BITMAPINFOHEADER portion (max 28 bytes).
1358
2.26k
        if remaining > 36 {
1359
166
            let skip = remaining - 36;
1360
166
            let mut discard = [0u8; 28];
1361
166
            self.reader.read_exact(&mut discard[..skip])?;
1362
2.09k
        }
1363
1364
2.24k
        let parsed = ParsedInfoHeader::parse(&buffer, self.spec_strictness)?;
1365
1366
2.13k
        self.width = parsed.width;
1367
2.13k
        self.height = parsed.height;
1368
2.13k
        self.top_down = parsed.top_down;
1369
2.13k
        self.bit_count = parsed.bit_count;
1370
2.13k
        self.colors_used = parsed.colors_used;
1371
2.13k
        self.image_type = Self::image_type_from_compression(
1372
2.13k
            parsed.compression,
1373
2.13k
            parsed.bit_count,
1374
2.13k
            self.add_alpha_channel,
1375
2.13k
            &self.bmp_header_type,
1376
122
        )?;
1377
1378
2.00k
        check_for_overflow(self.width, self.height, self.num_channels())?;
1379
1380
2.00k
        Ok(())
1381
2.28k
    }
1382
1383
    /// Read BITMAPINFOHEADER <https://msdn.microsoft.com/en-us/library/vs/alm/dd183376(v=vs.85).aspx>
1384
    /// or BITMAPV{2|3|4|5}HEADER.
1385
    ///
1386
    /// Returns the bitfield compression type or Err if any of the values are invalid.
1387
2.10k
    fn read_bitmap_info_header(&mut self) -> ImageResult<BitfieldCompression> {
1388
        // Info header (after size field): 36 bytes minimum
1389
2.10k
        let mut buffer = [0u8; 36];
1390
2.10k
        self.reader.read_exact(&mut buffer)?;
1391
1392
2.08k
        let parsed = ParsedInfoHeader::parse(&buffer, self.spec_strictness)?;
1393
1394
2.08k
        self.width = parsed.width;
1395
2.08k
        self.height = parsed.height;
1396
2.08k
        self.top_down = parsed.top_down;
1397
2.08k
        self.bit_count = parsed.bit_count;
1398
2.08k
        self.colors_used = parsed.colors_used;
1399
2.08k
        self.image_type = Self::image_type_from_compression(
1400
2.08k
            parsed.compression,
1401
2.08k
            parsed.bit_count,
1402
2.08k
            self.add_alpha_channel,
1403
2.08k
            &self.bmp_header_type,
1404
29
        )?;
1405
1406
2.05k
        check_for_overflow(self.width, self.height, self.num_channels())?;
1407
1408
2.05k
        let compression = match parsed.compression {
1409
118
            BI_ALPHABITFIELDS => BitfieldCompression::Rgba,
1410
1.93k
            _ => BitfieldCompression::Rgb,
1411
        };
1412
2.05k
        Ok(compression)
1413
2.10k
    }
1414
1415
620
    fn read_bitmasks(&mut self, compression: BitfieldCompression) -> ImageResult<()> {
1416
        // Determine if we need to read alpha mask:
1417
        // - V3/V4/V5 headers have the alpha mask embedded in the header
1418
        // - BI_ALPHABITFIELDS compression has a 4th mask after the header
1419
620
        let has_alpha = matches!(
1420
620
            self.bmp_header_type,
1421
            BMPHeaderType::V3 | BMPHeaderType::V4 | BMPHeaderType::V5
1422
385
        ) || compression == BitfieldCompression::Rgba;
1423
1424
        // Read bitfield masks into buffer
1425
620
        let mut buffer = [0u8; 16];
1426
620
        let buffer = &mut buffer[..if has_alpha { 16 } else { 12 }];
1427
620
        self.reader.read_exact(buffer)?;
1428
1429
        // Parse masks using shared logic
1430
597
        let parsed = ParsedBitfields::parse(buffer, has_alpha);
1431
1432
        // Create Bitfields from parsed masks
1433
597
        self.bitfields = match self.image_type {
1434
            ImageType::Bitfields16 | ImageType::Bitfields32 => {
1435
597
                let max_len = match self.image_type {
1436
117
                    ImageType::Bitfields16 => 16,
1437
480
                    ImageType::Bitfields32 => 32,
1438
0
                    _ => unreachable!(),
1439
                };
1440
597
                Some(Bitfields::from_mask(
1441
597
                    parsed.r_mask,
1442
597
                    parsed.g_mask,
1443
597
                    parsed.b_mask,
1444
597
                    parsed.a_mask,
1445
597
                    max_len,
1446
597
                    self.spec_strictness,
1447
86
                )?)
1448
            }
1449
0
            _ => None,
1450
        };
1451
1452
511
        if self.bitfields.is_some() && parsed.a_mask != 0 {
1453
243
            self.add_alpha_channel = true;
1454
268
        }
1455
1456
511
        Ok(())
1457
620
    }
1458
1459
    /// Read ICC profile data from the file.
1460
255
    fn read_icc_profile(&mut self, icc: &ParsedIccProfile) -> ImageResult<()> {
1461
255
        let profile_end = icc
1462
255
            .profile_offset
1463
255
            .checked_add(u64::from(icc.profile_size))
1464
255
            .ok_or_else(|| {
1465
0
                io::Error::new(io::ErrorKind::InvalidData, "BMP ICC profile range overflow")
1466
0
            })?;
1467
255
        let stream_len = self.reader.seek(SeekFrom::End(0))?;
1468
255
        if profile_end > stream_len {
1469
165
            return Err(io::Error::new(
1470
165
                io::ErrorKind::UnexpectedEof,
1471
165
                "BMP ICC profile extends beyond file",
1472
165
            )
1473
165
            .into());
1474
90
        }
1475
1476
90
        self.reader.seek(SeekFrom::Start(icc.profile_offset))?;
1477
90
        let profile_size = icc.profile_size as usize;
1478
90
        let mut profile_data = vec_try_with_capacity(profile_size)?;
1479
90
        profile_data.resize(profile_size, 0);
1480
90
        self.reader.read_exact(&mut profile_data)?;
1481
90
        self.icc_profile = Some(profile_data);
1482
90
        Ok(())
1483
255
    }
1484
1485
    /// Read BMP metadata (headers, palette, etc.).
1486
    ///
1487
    /// On `UnexpectedEof`, the decoder can be retried - the implementation tracks
1488
    /// progress and resumes from where it left off. Once successful, subsequent
1489
    /// calls are no-ops.
1490
    ///
1491
    /// Metadata reading is divided into phases:
1492
    /// 1. Headers: File header, DIB header, and bitmasks (~30-150 bytes).
1493
    ///    These are re-read together on retry since they're small.
1494
    /// 2. Palette: Up to 1KB for indexed color images.
1495
    /// 3. ICC profile: Variable size, can be several KB (V5 headers only).
1496
5.30k
    pub fn read_metadata(&mut self) -> ImageResult<()> {
1497
        // Check if we're in a metadata reading state
1498
5.30k
        let DecoderState::ReadingMetadata { progress } = self.state else {
1499
0
            return Ok(()); // Already past metadata phase
1500
        };
1501
1502
5.30k
        match self.read_metadata_impl(progress) {
1503
            Ok(()) => {
1504
                // Transition directly to the appropriate image reading state
1505
4.03k
                self.state = if self.is_rle() {
1506
1.79k
                    DecoderState::ReadingRleData {
1507
1.79k
                        progress: RleProgress::NotStarted,
1508
1.79k
                    }
1509
                } else {
1510
2.24k
                    DecoderState::ReadingRowData { rows_decoded: 0 }
1511
                };
1512
4.03k
                Ok(())
1513
            }
1514
1.26k
            Err(e) => Err(e),
1515
        }
1516
5.30k
    }
1517
1518
    /// Internal implementation of metadata reading with phased resumability.
1519
    ///
1520
    /// Uses recursive calls to progress through phases. Each phase either:
1521
    /// - Succeeds and calls the next phase
1522
    /// - Fails with an error (which may be retryable like UnexpectedEof)
1523
    ///
1524
    /// Recursion depth is bounded (max 4): NotStarted → ReadingMainHeader → ReadingPalette → ReadingIccProfile → Complete
1525
23.3k
    fn read_metadata_impl(&mut self, progress: MetadataProgress) -> ImageResult<()> {
1526
23.3k
        match progress {
1527
            MetadataProgress::NotStarted => {
1528
                // Record current position and transition to ReadingMainHeader
1529
5.30k
                let start_offset = self.reader.stream_position()?;
1530
5.30k
                let next = MetadataProgress::ReadingMainHeader { start_offset };
1531
5.30k
                self.state = DecoderState::ReadingMetadata { progress: next };
1532
5.30k
                self.read_metadata_impl(next)
1533
            }
1534
5.30k
            MetadataProgress::ReadingMainHeader { start_offset } => {
1535
                // Seek to start position (for retry support)
1536
5.30k
                self.reader.seek(SeekFrom::Start(start_offset))?;
1537
1538
                // Read headers and get offsets for subsequent phases
1539
5.30k
                let offsets = self.read_headers()?;
1540
1541
                // Always progress to ReadingPalette next
1542
4.52k
                let next = MetadataProgress::ReadingPalette { offsets };
1543
4.52k
                self.state = DecoderState::ReadingMetadata { progress: next };
1544
4.52k
                self.read_metadata_impl(next)
1545
            }
1546
4.52k
            MetadataProgress::ReadingPalette { offsets } => {
1547
                // Always seek to palette position (this is also where image data starts
1548
                // for non-palette formats)
1549
4.52k
                self.reader.seek(SeekFrom::Start(offsets.palette_offset))?;
1550
1551
                // Read palette if needed for this image type
1552
2.41k
                if matches!(
1553
4.52k
                    self.image_type,
1554
                    ImageType::Palette | ImageType::RLE4 | ImageType::RLE8
1555
                ) {
1556
2.10k
                    self.read_palette()?;
1557
2.41k
                }
1558
1559
                // For no_file_header mode, capture data_offset now (after palette read)
1560
                // before ICC profile reading potentially changes reader position.
1561
                // For normal mode, clamp data_offset if it points into the DIB header
1562
                // (between FILE_HEADER_SIZE and bmp_header_end). Such values are invalid
1563
                // because they overlap with header data.
1564
4.19k
                if self.no_file_header {
1565
2.12k
                    self.data_offset = self.reader.stream_position()?;
1566
2.07k
                } else if self.spec_strictness != SpecCompliance::Strict
1567
2.07k
                    && self.data_offset >= FILE_HEADER_SIZE
1568
794
                    && self.data_offset < offsets.bmp_header_end
1569
150
                {
1570
150
                    self.data_offset = offsets.bmp_header_end;
1571
1.92k
                }
1572
1573
                // Always progress to ReadingIccProfile next
1574
4.19k
                let next = MetadataProgress::ReadingIccProfile { offsets };
1575
4.19k
                self.state = DecoderState::ReadingMetadata { progress: next };
1576
4.19k
                self.read_metadata_impl(next)
1577
            }
1578
4.19k
            MetadataProgress::ReadingIccProfile { offsets } => {
1579
                // Read ICC profile if present
1580
4.19k
                if let Some(ref icc) = offsets.icc_profile {
1581
255
                    self.read_icc_profile(icc)?;
1582
3.94k
                }
1583
1584
                // Always progress to Complete next
1585
4.03k
                self.state = DecoderState::ReadingMetadata {
1586
4.03k
                    progress: MetadataProgress::Complete,
1587
4.03k
                };
1588
4.03k
                self.read_metadata_impl(MetadataProgress::Complete)
1589
            }
1590
4.03k
            MetadataProgress::Complete => Ok(()),
1591
        }
1592
23.3k
    }
1593
1594
    /// Read headers phase: file header, DIB header, and bitmasks.
1595
    /// Returns HeaderOffsets containing positions for subsequent phases.
1596
5.30k
    fn read_headers(&mut self) -> ImageResult<HeaderOffsets> {
1597
5.30k
        self.read_file_header()?;
1598
5.25k
        let bmp_header_offset = self.reader.stream_position()?;
1599
1600
        // Read header size into buffer for consistency with buffer-based pattern
1601
5.25k
        let mut size_buffer = [0u8; 4];
1602
5.25k
        self.reader.read_exact(&mut size_buffer)?;
1603
5.24k
        let bmp_header_size = u32::from_le_bytes(size_buffer);
1604
1605
5.24k
        let bmp_header_end = bmp_header_offset + u64::from(bmp_header_size);
1606
1607
2.44k
        self.bmp_header_type = match bmp_header_size {
1608
678
            BITMAPCOREHEADER_SIZE => BMPHeaderType::Core,
1609
931
            BITMAPINFOHEADER_SIZE => BMPHeaderType::Info,
1610
55
            BITMAPV2HEADER_SIZE => BMPHeaderType::V2,
1611
188
            BITMAPV3HEADER_SIZE => BMPHeaderType::V3,
1612
465
            BITMAPV4HEADER_SIZE => BMPHeaderType::V4,
1613
468
            BITMAPV5HEADER_SIZE => BMPHeaderType::V5,
1614
2.46k
            _ if bmp_header_size < BITMAPCOREHEADER_SIZE => {
1615
                // Size of any valid header types won't be smaller than core header type.
1616
15
                return Err(DecoderError::HeaderTooSmall(bmp_header_size).into());
1617
            }
1618
            // OS/2 BITMAPCOREHEADER2 (OS22XBITMAPHEADER): 16-64 bytes, 4-byte aligned
1619
            // (plus special sizes 42 and 46). Sizes 40/52/56 are caught by exact arms
1620
            // above and decoded as Windows headers (layout-compatible, so this is fine;
1621
            // the only difference is that a 40-byte OS/2 header with RLE24 won't trigger
1622
            // the Os2V2 path, but that combination is effectively nonexistent).
1623
2.44k
            _ if (OS2_V2_MIN_HEADER_SIZE..=OS2_V2_MAX_HEADER_SIZE).contains(&bmp_header_size)
1624
2.28k
                && (bmp_header_size % 4 == 0 || bmp_header_size == 42 || bmp_header_size == 46) =>
1625
            {
1626
2.28k
                BMPHeaderType::Os2V2
1627
            }
1628
            _ => {
1629
168
                return Err(ImageError::Unsupported(
1630
168
                    UnsupportedError::from_format_and_kind(
1631
168
                        ImageFormat::Bmp.into(),
1632
168
                        UnsupportedErrorKind::GenericFeature(format!(
1633
168
                            "Unknown bitmap header type (size={bmp_header_size})"
1634
168
                        )),
1635
168
                    ),
1636
168
                ))
1637
            }
1638
        };
1639
1640
5.06k
        let bitfield_compression = match self.bmp_header_type {
1641
            BMPHeaderType::Core => {
1642
678
                self.read_bitmap_core_header()?;
1643
660
                BitfieldCompression::Rgb
1644
            }
1645
            BMPHeaderType::Os2V2 => {
1646
2.28k
                self.read_bitmap_os2v2_header(bmp_header_size)?;
1647
2.00k
                BitfieldCompression::Rgb
1648
            }
1649
            BMPHeaderType::Info
1650
            | BMPHeaderType::V2
1651
            | BMPHeaderType::V3
1652
            | BMPHeaderType::V4
1653
2.10k
            | BMPHeaderType::V5 => self.read_bitmap_info_header()?,
1654
        };
1655
1656
4.71k
        let mut bitmask_bytes_offset = 0;
1657
4.09k
        if matches!(
1658
4.71k
            self.image_type,
1659
            ImageType::Bitfields16 | ImageType::Bitfields32
1660
        ) {
1661
620
            self.read_bitmasks(bitfield_compression)?;
1662
1663
            // Per https://learn.microsoft.com/en-us/windows/win32/gdi/bitmap-header-types, bitmaps
1664
            // using the `BITMAPINFOHEADER`, `BITMAPV4HEADER`, or `BITMAPV5HEADER` structures with
1665
            // an image type of `BI_BITFIELD` or `BI_ALPHABITFIELDS` contain bitfield masks
1666
            // immediately after the header.
1667
            //
1668
            // `read_bitmasks` correctly reads these from earlier in the header itself but we must
1669
            // ensure the reader starts on the image data itself, not these extra mask bytes.
1670
373
            if matches!(
1671
511
                self.bmp_header_type,
1672
                BMPHeaderType::Info | BMPHeaderType::V4 | BMPHeaderType::V5
1673
            ) {
1674
138
                bitmask_bytes_offset = match bitfield_compression {
1675
54
                    BitfieldCompression::Rgba => 16, // 4 masks * 4 bytes
1676
84
                    BitfieldCompression::Rgb => 12,  // 3 masks * 4 bytes
1677
                };
1678
373
            }
1679
4.09k
        } else if self.image_type == ImageType::RGB32 && bmp_header_size >= BITMAPV4HEADER_SIZE {
1680
            // V4/V5 headers may declare an alpha channel via alpha_mask even under BI_RGB.
1681
213
            let mut masks_buf = [0u8; 16];
1682
213
            self.reader.read_exact(&mut masks_buf)?;
1683
206
            let alpha_mask = u32::from_le_bytes(masks_buf[12..16].try_into().unwrap());
1684
206
            if alpha_mask != 0 {
1685
                // BI_RGB implies fixed BGRA byte layout, so the only spec-valid
1686
                // alpha mask is 0xFF000000. In lenient mode we still treat any
1687
                // non-zero alpha_mask as "alpha present" because some encoders
1688
                // (e.g. older GDI+ versions) write incorrect mask values while
1689
                // still storing alpha in the high byte.
1690
184
                if self.spec_strictness == SpecCompliance::Strict && alpha_mask != 0xFF000000 {
1691
0
                    return Err(DecoderError::BitfieldMaskInvalid.into());
1692
184
                }
1693
184
                self.add_alpha_channel = true;
1694
184
                self.image_type = ImageType::RGBA32;
1695
22
            }
1696
3.88k
        };
1697
1698
        // Parse color space fields from V4/V5 header
1699
4.59k
        let mut icc_profile = None;
1700
4.59k
        if bmp_header_size >= BITMAPV4HEADER_SIZE {
1701
            // Read the header into a buffer for color space parsing.
1702
            // Buffer starts after the 4-byte size field.
1703
895
            let mut header_buffer = vec![0u8; (bmp_header_size - 4) as usize];
1704
895
            let current_pos = self.reader.stream_position()?;
1705
895
            self.reader.seek(SeekFrom::Start(bmp_header_offset + 4))?;
1706
895
            self.reader.read_exact(&mut header_buffer)?;
1707
1708
            // Extract color space info and handle non-Copy variants immediately
1709
818
            match ColorSpaceInfo::parse(&header_buffer, bmp_header_size, bmp_header_offset) {
1710
99
                Some(ColorSpaceInfo::CalibratedRgb(params)) => {
1711
                    // Synthesize an ICC profile from the calibrated RGB parameters
1712
                    // and store it directly — no file read needed.
1713
99
                    if let Ok(encoded) = params.to_color_profile().encode() {
1714
99
                        self.icc_profile = Some(encoded);
1715
99
                    }
1716
                }
1717
329
                Some(ColorSpaceInfo::EmbeddedIcc(icc)) => {
1718
329
                    icc_profile = Some(icc);
1719
329
                }
1720
                // LCS_sRGB / LCS_WINDOWS_COLOR_SPACE: the caller treats
1721
                // "no ICC profile" as sRGB, so nothing to store.
1722
390
                Some(ColorSpaceInfo::Srgb) | None => {}
1723
            }
1724
1725
            // Seek back to where we were
1726
818
            self.reader.seek(SeekFrom::Start(current_pos))?;
1727
3.70k
        }
1728
1729
        // Calculate palette offset (position after headers)
1730
4.52k
        let palette_offset = bmp_header_end + bitmask_bytes_offset;
1731
1732
4.52k
        Ok(HeaderOffsets {
1733
4.52k
            bmp_header_end,
1734
4.52k
            palette_offset,
1735
4.52k
            icc_profile,
1736
4.52k
        })
1737
5.30k
    }
1738
1739
    #[cfg(feature = "ico")]
1740
    #[doc(hidden)]
1741
2.69k
    pub fn read_metadata_in_ico_format(&mut self) -> ImageResult<()> {
1742
2.69k
        self.no_file_header = true;
1743
2.69k
        self.add_alpha_channel = true;
1744
2.69k
        self.read_metadata()?;
1745
1746
        // The height field in an ICO file is doubled to account for the AND mask
1747
        // (whether or not an AND mask is actually present).
1748
2.04k
        self.height /= 2;
1749
2.04k
        Ok(())
1750
2.69k
    }
1751
1752
2.10k
    fn get_palette_size(&mut self) -> ImageResult<usize> {
1753
2.10k
        match self.colors_used {
1754
830
            0 => Ok(1 << self.bit_count),
1755
            _ => {
1756
1.27k
                if self.spec_strictness == SpecCompliance::Strict
1757
0
                    && self.colors_used > 1 << self.bit_count
1758
                {
1759
0
                    return Err(DecoderError::PaletteSizeExceeded {
1760
0
                        colors_used: self.colors_used,
1761
0
                        bit_count: self.bit_count,
1762
0
                    }
1763
0
                    .into());
1764
1.27k
                }
1765
                // In lenient mode, clamp to max palette size for the bit depth
1766
1.27k
                let max_size = 1usize << self.bit_count;
1767
1.27k
                Ok((self.colors_used as usize).min(max_size))
1768
            }
1769
        }
1770
2.10k
    }
1771
1772
2.10k
    fn bytes_per_color(&self) -> usize {
1773
2.10k
        match self.bmp_header_type {
1774
469
            BMPHeaderType::Core => 3,
1775
1.63k
            _ => 4,
1776
        }
1777
2.10k
    }
1778
1779
2.10k
    fn read_palette(&mut self) -> ImageResult<()> {
1780
        const MAX_PALETTE_SIZE: usize = 256; // Palette indices are u8.
1781
1782
2.10k
        let bytes_per_color = self.bytes_per_color();
1783
2.10k
        let palette_size = self.get_palette_size()?;
1784
2.10k
        let max_length = MAX_PALETTE_SIZE * bytes_per_color;
1785
1786
2.10k
        let length = palette_size * bytes_per_color;
1787
2.10k
        let mut buf = vec_try_with_capacity(max_length)?;
1788
1789
        // Resize and read the palette entries to the buffer.
1790
        // We limit the buffer to at most 256 colours to avoid any oom issues as
1791
        // 8-bit images can't reference more than 256 indexes anyhow.
1792
2.10k
        buf.resize(cmp::min(length, max_length), 0);
1793
2.10k
        self.reader.by_ref().read_exact(&mut buf)?;
1794
1795
        // Allocate 256 entries even if palette_size is smaller, to prevent corrupt files from
1796
        // causing an out-of-bounds array access.
1797
1.78k
        match length.cmp(&max_length) {
1798
0
            Ordering::Greater => self.reader.seek_relative((length - max_length) as i64)?,
1799
1.74k
            Ordering::Less => buf.resize(max_length, 0),
1800
40
            Ordering::Equal => (),
1801
        }
1802
1803
1.78k
        let p: Vec<[u8; 3]> = (0..MAX_PALETTE_SIZE)
1804
456k
            .map(|i| {
1805
456k
                let b = buf[bytes_per_color * i];
1806
456k
                let g = buf[bytes_per_color * i + 1];
1807
456k
                let r = buf[bytes_per_color * i + 2];
1808
456k
                [r, g, b]
1809
456k
            })
1810
1.78k
            .collect();
1811
1812
1.78k
        self.palette = Some(p);
1813
1814
1.78k
        Ok(())
1815
2.10k
    }
1816
1817
    /// Get the palette that is embedded in the BMP image, if any.
1818
0
    pub fn get_palette(&self) -> Option<&[[u8; 3]]> {
1819
0
        self.palette.as_ref().map(|vec| &vec[..])
1820
0
    }
1821
1822
10.4k
    fn num_channels(&self) -> usize {
1823
10.4k
        if self.indexed_color {
1824
0
            1
1825
10.4k
        } else if self.add_alpha_channel {
1826
5.65k
            4
1827
        } else {
1828
4.78k
            3
1829
        }
1830
10.4k
    }
1831
1832
1.78k
    fn rows<'a>(&self, pixel_data: &'a mut [u8]) -> RowIterator<'a> {
1833
1.78k
        let stride = self.width as usize * self.num_channels();
1834
1.78k
        if self.top_down {
1835
467
            RowIterator {
1836
467
                chunks: Chunker::FromTop(pixel_data.chunks_exact_mut(stride)),
1837
467
            }
1838
        } else {
1839
1.32k
            RowIterator {
1840
1.32k
                chunks: Chunker::FromBottom(pixel_data.chunks_exact_mut(stride).rev()),
1841
1.32k
            }
1842
        }
1843
1.78k
    }
1844
1845
878
    fn read_palettized_pixel_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
1846
878
        let num_channels = self.num_channels();
1847
878
        let row_byte_length = ((i32::from(self.bit_count) * self.width + 31) / 32 * 4) as usize;
1848
878
        let mut indices = vec![0; row_byte_length];
1849
878
        let palette = self.palette.as_ref().unwrap();
1850
878
        let bit_count = self.bit_count;
1851
878
        let width = self.width as usize;
1852
878
        let skip_palette = self.indexed_color;
1853
1854
878
        let rows_decoded = self.rows_decoded();
1855
878
        let start_row = rows_decoded.rows();
1856
878
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
1857
1858
878
        let file_offset = self.data_offset + (start_row as u64 * row_byte_length as u64);
1859
878
        self.reader.seek(SeekFrom::Start(file_offset))?;
1860
1861
        // Set alpha to opaque for all pixels if needed (only on first call)
1862
878
        if start_row == 0 && num_channels == 4 {
1863
462
            buf.as_chunks_mut::<4>()
1864
462
                .0
1865
462
                .iter_mut()
1866
2.82G
                .for_each(|c| c[3] = ALPHA_OPAQUE);
1867
416
        }
1868
1869
878
        let spec_strictness = self.spec_strictness;
1870
878
        let last_row: u32 = (self.height - 1).try_into().unwrap();
1871
878
        let mut current_file_row = start_row;
1872
878
        let reader = &mut self.reader;
1873
878
        let result = with_rows_resumable(
1874
878
            buf,
1875
878
            self.width,
1876
878
            self.height,
1877
878
            num_channels,
1878
878
            top_down,
1879
878
            start_row,
1880
158k
            |row| {
1881
158k
                read_scanline(
1882
158k
                    reader,
1883
158k
                    &mut indices,
1884
158k
                    &mut current_file_row,
1885
158k
                    last_row,
1886
158k
                    spec_strictness,
1887
724
                )?;
1888
157k
                if skip_palette {
1889
0
                    row.clone_from_slice(&indices[0..width]);
1890
0
                } else {
1891
157k
                    let mut pixel_iter = row.chunks_exact_mut(num_channels);
1892
157k
                    match bit_count {
1893
136k
                        1 => {
1894
136k
                            set_1bit_pixel_run(&mut pixel_iter, palette, indices.iter());
1895
136k
                        }
1896
12.1k
                        2 => {
1897
12.1k
                            set_2bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
1898
12.1k
                        }
1899
2.04k
                        4 => {
1900
2.04k
                            set_4bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
1901
2.04k
                        }
1902
7.26k
                        8 => {
1903
7.26k
                            set_8bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
1904
7.26k
                        }
1905
0
                        _ => panic!(),
1906
                    }
1907
                }
1908
157k
                Ok(())
1909
158k
            },
1910
        );
1911
1912
878
        self.finish_row_decode(result)
1913
878
    }
1914
1915
350
    fn read_16_bit_pixel_data(
1916
350
        &mut self,
1917
350
        buf: &mut [u8],
1918
350
        bitfields: Option<&Bitfields>,
1919
350
    ) -> ImageResult<()> {
1920
350
        let num_channels = self.num_channels();
1921
350
        let bitfields = match bitfields {
1922
261
            Some(b) => b,
1923
89
            None => self.bitfields.as_ref().unwrap(),
1924
        };
1925
1926
350
        let row_data_len = self.width as usize * 2;
1927
350
        let row_padding_len = calculate_row_padding(row_data_len);
1928
350
        let total_row_len = row_data_len + row_padding_len;
1929
1930
350
        let rows_decoded = self.rows_decoded();
1931
350
        let start_row = rows_decoded.rows();
1932
350
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
1933
350
        let width = self.width;
1934
350
        let height = self.height;
1935
1936
350
        let file_offset = self.data_offset + (start_row as u64 * total_row_len as u64);
1937
350
        self.reader.seek(SeekFrom::Start(file_offset))?;
1938
1939
350
        let mut row_buffer = allocate_row_buffer(total_row_len)?;
1940
1941
350
        let spec_strictness = self.spec_strictness;
1942
350
        let last_row: u32 = (height - 1).try_into().unwrap();
1943
350
        let mut current_file_row = start_row;
1944
350
        let reader = &mut self.reader;
1945
350
        let result = with_rows_resumable(
1946
350
            buf,
1947
350
            width,
1948
350
            height,
1949
350
            num_channels,
1950
350
            top_down,
1951
350
            start_row,
1952
13.3k
            |row| {
1953
13.3k
                read_scanline(
1954
13.3k
                    reader,
1955
13.3k
                    &mut row_buffer,
1956
13.3k
                    &mut current_file_row,
1957
13.3k
                    last_row,
1958
13.3k
                    spec_strictness,
1959
312
                )?;
1960
13.0k
                let row_buffer_chunks = row_buffer.as_chunks::<2>().0.iter();
1961
441k
                for (&row_data, pixel) in row_buffer_chunks.zip(row.chunks_exact_mut(num_channels))
1962
                {
1963
441k
                    let data = u32::from(u16::from_le_bytes(row_data));
1964
441k
                    pixel[0] = bitfields.r.read(data);
1965
441k
                    pixel[1] = bitfields.g.read(data);
1966
441k
                    pixel[2] = bitfields.b.read(data);
1967
441k
                    if num_channels == 4 {
1968
222k
                        pixel[3] = if bitfields.a.len != 0 {
1969
207k
                            bitfields.a.read(data)
1970
                        } else {
1971
14.6k
                            ALPHA_OPAQUE
1972
                        };
1973
218k
                    }
1974
                }
1975
13.0k
                Ok(())
1976
13.3k
            },
1977
        );
1978
1979
350
        self.finish_row_decode(result)
1980
350
    }
1981
1982
    /// Read image data from a reader in 32-bit formats that use bitfields.
1983
355
    fn read_32_bit_pixel_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
1984
355
        let num_channels = self.num_channels();
1985
355
        let bitfields = self.bitfields.as_ref().unwrap();
1986
1987
355
        let row_data_len = self.width as usize * 4;
1988
1989
355
        let rows_decoded = self.rows_decoded();
1990
355
        let start_row = rows_decoded.rows();
1991
355
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
1992
355
        let width = self.width;
1993
355
        let height = self.height;
1994
1995
355
        let file_offset = self.data_offset + (start_row as u64 * row_data_len as u64);
1996
355
        self.reader.seek(SeekFrom::Start(file_offset))?;
1997
1998
355
        let mut row_buffer = allocate_row_buffer(row_data_len)?;
1999
2000
355
        let spec_strictness = self.spec_strictness;
2001
355
        let last_row: u32 = (height - 1).try_into().unwrap();
2002
355
        let mut current_file_row = start_row;
2003
355
        let reader = &mut self.reader;
2004
355
        let result = with_rows_resumable(
2005
355
            buf,
2006
355
            width,
2007
355
            height,
2008
355
            num_channels,
2009
355
            top_down,
2010
355
            start_row,
2011
111k
            |row| {
2012
111k
                read_scanline(
2013
111k
                    reader,
2014
111k
                    &mut row_buffer,
2015
111k
                    &mut current_file_row,
2016
111k
                    last_row,
2017
111k
                    spec_strictness,
2018
317
                )?;
2019
111k
                let row_buffer_chunks = row_buffer.as_chunks::<4>().0.iter();
2020
318k
                for (&row_data, pixel) in row_buffer_chunks.zip(row.chunks_exact_mut(num_channels))
2021
                {
2022
318k
                    let data = u32::from_le_bytes(row_data);
2023
318k
                    pixel[0] = bitfields.r.read(data);
2024
318k
                    pixel[1] = bitfields.g.read(data);
2025
318k
                    pixel[2] = bitfields.b.read(data);
2026
318k
                    if num_channels == 4 {
2027
244k
                        pixel[3] = if bitfields.a.len != 0 {
2028
151k
                            bitfields.a.read(data)
2029
                        } else {
2030
92.9k
                            ALPHA_OPAQUE
2031
                        };
2032
74.8k
                    }
2033
                }
2034
111k
                Ok(())
2035
111k
            },
2036
        );
2037
2038
355
        self.finish_row_decode(result)
2039
355
    }
2040
2041
    /// Read image data from a reader where the colours are stored as 8-bit values (24 or 32-bit).
2042
558
    fn read_full_byte_pixel_data(
2043
558
        &mut self,
2044
558
        buf: &mut [u8],
2045
558
        format: &FormatFullBytes,
2046
558
    ) -> ImageResult<()> {
2047
558
        let num_channels = self.num_channels();
2048
558
        let row_data_len = match *format {
2049
316
            FormatFullBytes::RGB24 => self.width as usize * 3,
2050
17
            FormatFullBytes::Format888 => self.width as usize * 4,
2051
225
            FormatFullBytes::RGB32 | FormatFullBytes::RGBA32 => self.width as usize * 4,
2052
        };
2053
558
        let row_padding_len = match *format {
2054
316
            FormatFullBytes::RGB24 => calculate_row_padding(row_data_len),
2055
242
            _ => 0,
2056
        };
2057
558
        let total_row_len = row_data_len + row_padding_len;
2058
2059
558
        let rows_decoded = self.rows_decoded();
2060
558
        let start_row = rows_decoded.rows();
2061
558
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
2062
558
        let width = self.width;
2063
558
        let height = self.height;
2064
2065
558
        let file_offset = self.data_offset + (start_row as u64 * total_row_len as u64);
2066
558
        self.reader.seek(SeekFrom::Start(file_offset))?;
2067
2068
558
        let mut row_buffer = allocate_row_buffer(total_row_len)?;
2069
2070
558
        let spec_strictness = self.spec_strictness;
2071
558
        let last_row: u32 = (height - 1).try_into().unwrap();
2072
558
        let mut current_file_row = start_row;
2073
558
        let reader = &mut self.reader;
2074
558
        let result = with_rows_resumable(
2075
558
            buf,
2076
558
            width,
2077
558
            height,
2078
558
            num_channels,
2079
558
            top_down,
2080
558
            start_row,
2081
42.3k
            |row| {
2082
42.3k
                read_scanline(
2083
42.3k
                    reader,
2084
42.3k
                    &mut row_buffer,
2085
42.3k
                    &mut current_file_row,
2086
42.3k
                    last_row,
2087
42.3k
                    spec_strictness,
2088
487
                )?;
2089
2090
1.30M
                for (i, pixel) in row.chunks_mut(num_channels).enumerate() {
2091
1.30M
                    let offset = match *format {
2092
123k
                        FormatFullBytes::Format888 => i * 4 + 1, // Skip first byte
2093
                        _ => {
2094
1.18M
                            i * match *format {
2095
486k
                                FormatFullBytes::RGB24 => 3,
2096
698k
                                _ => 4,
2097
                            }
2098
                        }
2099
                    };
2100
2101
                    // Read the colour values (b, g, r) and reverse to (r, g, b)
2102
1.30M
                    pixel[0..3].copy_from_slice(&row_buffer[offset..offset + 3]);
2103
1.30M
                    pixel[0..3].reverse();
2104
2105
                    // Read the alpha channel if present
2106
1.30M
                    if *format == FormatFullBytes::RGBA32 {
2107
353k
                        pixel[3] = row_buffer[offset + 3];
2108
954k
                    } else if num_channels == 4 {
2109
130k
                        pixel[3] = ALPHA_OPAQUE;
2110
823k
                    }
2111
                }
2112
41.8k
                Ok(())
2113
42.3k
            },
2114
        );
2115
2116
558
        self.finish_row_decode(result)
2117
558
    }
2118
2119
1.78k
    fn read_rle_data(&mut self, buf: &mut [u8], image_type: ImageType) -> ImageResult<()> {
2120
1.78k
        let (start_row, start_x, start_pos) = match self.state {
2121
            DecoderState::ReadingRleData {
2122
                progress: RleProgress::NotStarted,
2123
1.78k
            } => (0u32, 0u32, self.data_offset),
2124
            DecoderState::ReadingRleData {
2125
0
                progress: RleProgress::Checkpoint { row, x, stream_pos },
2126
0
            } => (row, x, stream_pos),
2127
0
            _ => unreachable!("read_rle_data called in unexpected state: {:?}", self.state),
2128
        };
2129
2130
1.78k
        self.reader.seek(SeekFrom::Start(start_pos))?;
2131
2132
1.78k
        let num_channels = self.num_channels();
2133
1.78k
        let p = if image_type != ImageType::RLE24 {
2134
814
            Some(self.palette.as_ref().unwrap())
2135
        } else {
2136
973
            None
2137
        };
2138
2139
1.78k
        let mut row_iter = self.rows(buf).skip(start_row as usize);
2140
1.78k
        let mut current_row = start_row;
2141
1.78k
        let mut first_row_iteration = true;
2142
2143
        // Pre-allocate buffer for RLE4/8 absolute mode (max 256 bytes).
2144
        // RLE24 reads inline BGR triples directly, so this buffer is unused.
2145
1.78k
        let mut rle_indices_buffer = [0u8; 256];
2146
2147
1.78k
        let mut rle_reader = RleReader::new(&mut self.reader);
2148
2149
599k
        while let Some(row) = row_iter.next() {
2150
599k
            let mut pixel_iter = row.chunks_exact_mut(num_channels);
2151
2152
            // When resuming mid-row, skip to the saved x position on the first row.
2153
599k
            let mut x = if first_row_iteration && start_x > 0 {
2154
0
                pixel_iter.nth(start_x as usize - 1); // nth(n) consumes n+1 elements
2155
0
                start_x
2156
            } else {
2157
599k
                0
2158
            };
2159
599k
            first_row_iteration = false;
2160
2161
            loop {
2162
5.99M
                let control_byte = rle_reader.read_byte()?;
2163
2164
5.99M
                match control_byte {
2165
                    RLE_ESCAPE => {
2166
1.00M
                        let op = rle_reader.read_byte()?;
2167
1.00M
                        match op {
2168
                            RLE_ESCAPE_EOL => {
2169
301M
                                pixel_iter.for_each(|p| p.fill(0));
2170
597k
                                current_row += 1;
2171
597k
                                x = 0;
2172
597k
                                break;
2173
                            }
2174
                            RLE_ESCAPE_EOF => {
2175
3.14M
                                pixel_iter.for_each(|p| p.fill(0));
2176
1.70G
                                row_iter.for_each(|r| r.fill(0));
2177
561
                                return Ok(());
2178
                            }
2179
                            RLE_ESCAPE_DELTA => {
2180
264k
                                let x_delta = rle_reader.read_byte()?;
2181
264k
                                let y_delta = rle_reader.read_byte()?;
2182
2183
                                // IE and Windows image preview replace skipped pixels
2184
                                // with black, so we stick to that.
2185
264k
                                if y_delta > 0 {
2186
30.7M
                                    pixel_iter.for_each(|p| p.fill(0));
2187
2188
78.8k
                                    for _ in 1..y_delta {
2189
725k
                                        if let Some(row) = row_iter.next() {
2190
725k
                                            row.fill(0);
2191
725k
                                        } else if self.spec_strictness == SpecCompliance::Strict {
2192
0
                                            return Err(DecoderError::CorruptRleData.into());
2193
                                        } else {
2194
23
                                            return Ok(());
2195
                                        }
2196
                                    }
2197
2198
78.8k
                                    current_row += y_delta as u32;
2199
78.8k
                                    if let Some(next_row) = row_iter.next() {
2200
78.8k
                                        pixel_iter = next_row.chunks_exact_mut(num_channels);
2201
78.8k
                                    } else if self.spec_strictness == SpecCompliance::Strict {
2202
0
                                        return Err(DecoderError::CorruptRleData.into());
2203
                                    } else {
2204
5
                                        return Ok(());
2205
                                    }
2206
2207
78.8k
                                    for _ in 0..x {
2208
10.5M
                                        if let Some(pixel) = pixel_iter.next() {
2209
10.5M
                                            pixel.fill(0);
2210
10.5M
                                        } else if self.spec_strictness == SpecCompliance::Strict {
2211
0
                                            return Err(DecoderError::CorruptRleData.into());
2212
                                        } else {
2213
28.2k
                                            break;
2214
                                        }
2215
                                    }
2216
185k
                                }
2217
2218
264k
                                for _ in 0..x_delta {
2219
724k
                                    if let Some(pixel) = pixel_iter.next() {
2220
713k
                                        pixel.fill(0);
2221
713k
                                    } else if self.spec_strictness == SpecCompliance::Strict {
2222
0
                                        return Err(DecoderError::CorruptRleData.into());
2223
                                    } else {
2224
11.0k
                                        break;
2225
                                    }
2226
                                }
2227
264k
                                x += x_delta as u32;
2228
                            }
2229
                            _ => {
2230
                                // Absolute mode: pixel data differs by RLE type.
2231
146k
                                let count = op as usize;
2232
146k
                                match image_type {
2233
                                    ImageType::RLE8 => {
2234
9.46k
                                        let mut length = count;
2235
9.46k
                                        length += length & 1;
2236
9.46k
                                        rle_reader.read_exact(&mut rle_indices_buffer[..length])?;
2237
                                        // Silently truncate if run overflows the row.
2238
9.40k
                                        let success = set_8bit_pixel_run(
2239
9.40k
                                            &mut pixel_iter,
2240
9.40k
                                            p.unwrap(),
2241
9.40k
                                            rle_indices_buffer[..length].iter(),
2242
9.40k
                                            count,
2243
                                        );
2244
9.40k
                                        if self.spec_strictness == SpecCompliance::Strict
2245
0
                                            && !success
2246
                                        {
2247
0
                                            return Err(DecoderError::CorruptRleData.into());
2248
9.40k
                                        }
2249
                                    }
2250
                                    ImageType::RLE4 => {
2251
46.5k
                                        let mut length = count.div_ceil(2);
2252
46.5k
                                        length += length & 1;
2253
46.5k
                                        rle_reader.read_exact(&mut rle_indices_buffer[..length])?;
2254
                                        // Silently truncate if run overflows the row.
2255
46.5k
                                        let success = set_4bit_pixel_run(
2256
46.5k
                                            &mut pixel_iter,
2257
46.5k
                                            p.unwrap(),
2258
46.5k
                                            rle_indices_buffer[..length].iter(),
2259
46.5k
                                            count,
2260
                                        );
2261
46.5k
                                        if self.spec_strictness == SpecCompliance::Strict
2262
0
                                            && !success
2263
                                        {
2264
0
                                            return Err(DecoderError::CorruptRleData.into());
2265
46.5k
                                        }
2266
                                    }
2267
                                    ImageType::RLE24 => {
2268
90.1k
                                        for _ in 0..count {
2269
3.61M
                                            let b = rle_reader.read_byte()?;
2270
3.61M
                                            let g = rle_reader.read_byte()?;
2271
3.61M
                                            let r = rle_reader.read_byte()?;
2272
3.61M
                                            if let Some(pixel) = pixel_iter.next() {
2273
843k
                                                pixel[0] = r;
2274
843k
                                                pixel[1] = g;
2275
843k
                                                pixel[2] = b;
2276
2.76M
                                            }
2277
                                        }
2278
                                        // RLE24 absolute mode pads to word (2-byte) boundary.
2279
89.8k
                                        if !(count * 3).is_multiple_of(2) {
2280
49.1k
                                            rle_reader.read_byte()?;
2281
40.7k
                                        }
2282
                                    }
2283
0
                                    _ => unreachable!(),
2284
                                }
2285
145k
                                x += count as u32;
2286
                            }
2287
                        }
2288
                    }
2289
                    _ => {
2290
                        // Encoded run: pixel data differs by RLE type.
2291
4.98M
                        let n_pixels = control_byte as usize;
2292
4.98M
                        match image_type {
2293
                            ImageType::RLE8 => {
2294
                                // Clamp to row length for compat with imagemagick:
2295
                                // https://github.com/image-rs/image/issues/2321
2296
1.00M
                                let palette_index = rle_reader.read_byte()?;
2297
1.00M
                                let repeat_pixel: [u8; 3] = p.unwrap()[palette_index as usize];
2298
5.74M
                                (&mut pixel_iter).take(n_pixels).for_each(|p| {
2299
5.74M
                                    p[0] = repeat_pixel[0];
2300
5.74M
                                    p[1] = repeat_pixel[1];
2301
5.74M
                                    p[2] = repeat_pixel[2];
2302
5.74M
                                });
2303
                            }
2304
                            ImageType::RLE4 => {
2305
2.53M
                                let palette_index = rle_reader.read_byte()?;
2306
                                // Silently truncate if run overflows the row
2307
                                // (matches RLE8 encoded run behavior).
2308
2.53M
                                let success = set_4bit_pixel_run(
2309
2.53M
                                    &mut pixel_iter,
2310
2.53M
                                    p.unwrap(),
2311
2.53M
                                    repeat(&palette_index),
2312
2.53M
                                    n_pixels,
2313
                                );
2314
2.53M
                                if self.spec_strictness == SpecCompliance::Strict && !success {
2315
0
                                    return Err(DecoderError::CorruptRleData.into());
2316
2.53M
                                }
2317
                            }
2318
                            ImageType::RLE24 => {
2319
1.44M
                                let b = rle_reader.read_byte()?;
2320
1.44M
                                let g = rle_reader.read_byte()?;
2321
1.44M
                                let r = rle_reader.read_byte()?;
2322
1.44M
                                for _ in 0..n_pixels {
2323
97.4M
                                    if let Some(pixel) = pixel_iter.next() {
2324
6.08M
                                        pixel[0] = r;
2325
6.08M
                                        pixel[1] = g;
2326
6.08M
                                        pixel[2] = b;
2327
91.3M
                                    }
2328
                                }
2329
                            }
2330
0
                            _ => unreachable!(),
2331
                        }
2332
4.98M
                        x += n_pixels as u32;
2333
                    }
2334
                }
2335
2336
                // Checkpoint after every instruction to avoid potential quadratic
2337
                // time complexity when the decoder is given data one byte at a time.
2338
5.39M
                self.state = DecoderState::ReadingRleData {
2339
5.39M
                    progress: RleProgress::Checkpoint {
2340
5.39M
                        row: current_row,
2341
5.39M
                        x,
2342
5.39M
                        stream_pos: start_pos + rle_reader.bytes_read(),
2343
5.39M
                    },
2344
5.39M
                };
2345
            }
2346
2347
            // Checkpoint after EndOfRow (which breaks out of the inner loop).
2348
597k
            self.state = DecoderState::ReadingRleData {
2349
597k
                progress: RleProgress::Checkpoint {
2350
597k
                    row: current_row,
2351
597k
                    x,
2352
597k
                    stream_pos: start_pos + rle_reader.bytes_read(),
2353
597k
                },
2354
597k
            };
2355
        }
2356
2357
30
        Ok(())
2358
1.78k
    }
2359
2360
    /// Determine if the current image type is RLE-compressed.
2361
4.03k
    fn is_rle(&self) -> bool {
2362
2.24k
        matches!(
2363
4.03k
            self.image_type,
2364
            ImageType::RLE4 | ImageType::RLE8 | ImageType::RLE24
2365
        )
2366
4.03k
    }
2367
2368
    /// Returns which rows in the output buffer contain valid decoded pixel data.
2369
    ///
2370
    /// See [`RowsDecoded`] for details on how to interpret the result.
2371
2.14k
    pub fn rows_decoded(&self) -> RowsDecoded {
2372
2.14k
        let rows = match self.state {
2373
2.14k
            DecoderState::ReadingRowData { rows_decoded } => rows_decoded,
2374
0
            DecoderState::ReadingRleData { progress } => match progress {
2375
0
                RleProgress::NotStarted => 0,
2376
                // row is 0-indexed current row; rows 0..row are complete
2377
0
                RleProgress::Checkpoint { row, .. } => row,
2378
            },
2379
0
            DecoderState::ImageDecoded => self.height as u32,
2380
0
            DecoderState::ReadingMetadata { .. } => 0,
2381
        };
2382
2.14k
        if self.top_down {
2383
492
            RowsDecoded::TopDown { rows }
2384
        } else {
2385
1.64k
            RowsDecoded::BottomUp { rows }
2386
        }
2387
2.14k
    }
2388
2389
    /// Handle the result of a row-based decode operation, updating state accordingly.
2390
2.14k
    fn finish_row_decode(&mut self, result: Result<u32, (u32, io::Error)>) -> ImageResult<()> {
2391
2.14k
        let (Ok(rows) | Err((rows, _))) = result;
2392
2.14k
        self.state = DecoderState::ReadingRowData { rows_decoded: rows };
2393
2.14k
        match result {
2394
301
            Ok(_) => Ok(()),
2395
1.84k
            Err((_, e)) => Err(e)?,
2396
        }
2397
2.14k
    }
2398
2399
    /// Read the actual pixel data of the image.
2400
    ///
2401
    /// Must be called after `read_metadata()` succeeds. On `UnexpectedEof`, the decoder
2402
    /// can be retried:
2403
    ///
2404
    /// - For non-RLE formats: decoding resumes from the last successfully decoded row.
2405
    ///   Already-decoded rows are preserved in `buf`.
2406
    /// - For RLE formats: decoding resumes from the last checkpoint (completed instruction symbol).
2407
    ///   Rows and pixels completed before the error are preserved in `buf`.
2408
3.92k
    pub fn read_image_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
2409
3.92k
        match self.state {
2410
0
            DecoderState::ImageDecoded => Ok(()),
2411
3.92k
            DecoderState::ReadingRowData { .. } | DecoderState::ReadingRleData { .. } => self
2412
3.92k
                .read_image_data_impl(buf)
2413
3.92k
                .map(|()| self.state = DecoderState::ImageDecoded),
2414
0
            DecoderState::ReadingMetadata { .. } => Err(DecoderError::MetadataNotRead.into()),
2415
        }
2416
3.92k
    }
2417
2418
    /// Internal implementation of image data reading.
2419
3.92k
    fn read_image_data_impl(&mut self, buf: &mut [u8]) -> ImageResult<()> {
2420
3.92k
        match self.image_type {
2421
878
            ImageType::Palette => self.read_palettized_pixel_data(buf),
2422
261
            ImageType::RGB16 => self.read_16_bit_pixel_data(buf, Some(&R5_G5_B5_COLOR_MASK)),
2423
316
            ImageType::RGB24 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGB24),
2424
55
            ImageType::RGB32 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGB32),
2425
151
            ImageType::RGBA32 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGBA32),
2426
343
            ImageType::RLE8 => self.read_rle_data(buf, ImageType::RLE8),
2427
471
            ImageType::RLE4 => self.read_rle_data(buf, ImageType::RLE4),
2428
973
            ImageType::RLE24 => self.read_rle_data(buf, ImageType::RLE24),
2429
89
            ImageType::Bitfields16 => match self.bitfields {
2430
89
                Some(_) => self.read_16_bit_pixel_data(buf, None),
2431
0
                None => Err(DecoderError::BitfieldMasksMissing(16).into()),
2432
            },
2433
391
            ImageType::Bitfields32 => match self.bitfields {
2434
                Some(R8_G8_B8_COLOR_MASK) => {
2435
17
                    self.read_full_byte_pixel_data(buf, &FormatFullBytes::Format888)
2436
                }
2437
                Some(R8_G8_B8_A8_COLOR_MASK) => {
2438
19
                    self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGBA32)
2439
                }
2440
355
                Some(_) => self.read_32_bit_pixel_data(buf),
2441
0
                None => Err(DecoderError::BitfieldMasksMissing(32).into()),
2442
            },
2443
        }
2444
3.92k
    }
2445
}
2446
2447
impl<R: BufRead + Seek> ImageDecoder for BmpDecoder<R> {
2448
15.9k
    fn prepare_image(&mut self) -> ImageResult<DecoderPreparedImage> {
2449
15.9k
        let color = if self.indexed_color {
2450
0
            ColorType::L8
2451
15.9k
        } else if self.add_alpha_channel {
2452
9.06k
            ColorType::Rgba8
2453
        } else {
2454
6.85k
            ColorType::Rgb8
2455
        };
2456
2457
15.9k
        Ok(DecoderPreparedImage::new(
2458
15.9k
            self.width as u32,
2459
15.9k
            self.height as u32,
2460
15.9k
            color,
2461
15.9k
        ))
2462
15.9k
    }
2463
2464
0
    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
2465
0
        Ok(self.icc_profile.clone())
2466
0
    }
2467
2468
1.91k
    fn read_image(&mut self, buf: &mut [u8]) -> ImageResult<DecodedImageAttributes> {
2469
1.91k
        let layout = self.prepare_image()?;
2470
1.91k
        assert_eq!(u64::try_from(buf.len()), Ok(layout.total_bytes()));
2471
1.91k
        self.read_image_data(buf)?;
2472
390
        Ok(DecodedImageAttributes::default())
2473
1.91k
    }
2474
}
2475
2476
#[cfg(test)]
2477
mod test {
2478
    use std::io::{BufRead, BufReader, Cursor, Seek};
2479
2480
    use super::*;
2481
2482
    #[test]
2483
    fn test_bitfield_len() {
2484
        for len in 1..9 {
2485
            let bitfield = Bitfield::from_len_shift(len, 0);
2486
            for i in 0..(1 << len) {
2487
                let read = bitfield.read(i);
2488
                let calc = (f64::from(i) / f64::from((1 << len) - 1) * 255f64).round() as u8;
2489
                if read != calc {
2490
                    println!("len:{len} i:{i} read:{read} calc:{calc}");
2491
                }
2492
                assert_eq!(read, calc);
2493
            }
2494
        }
2495
    }
2496
2497
    #[test]
2498
    fn read_rle_too_short() {
2499
        let data = vec![
2500
            0x42, 0x4d, 0x04, 0xee, 0xfe, 0xff, 0xff, 0x10, 0xff, 0x00, 0x04, 0x00, 0x00, 0x00,
2501
            0x7c, 0x00, 0x00, 0x00, 0x0c, 0x41, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x01, 0x00,
2502
            0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
2503
            0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x21,
2504
            0xff, 0x00, 0x66, 0x61, 0x72, 0x62, 0x66, 0x65, 0x6c, 0x64, 0x00, 0x00, 0x00, 0x00,
2505
            0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2506
            0xff, 0xd8, 0xff, 0x00, 0x00, 0x19, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2507
            0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xff, 0x00, 0x00, 0x00,
2508
            0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
2509
            0x00, 0x00, 0x00, 0x2d, 0x31, 0x31, 0x35, 0x36, 0x00, 0xff, 0x00, 0x00, 0x52, 0x3a,
2510
            0x37, 0x30, 0x7e, 0x71, 0x63, 0x91, 0x5a, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
2511
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
2512
            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x35, 0x37, 0x00, 0xff, 0x00, 0x00, 0x52,
2513
            0x3a, 0x37, 0x30, 0x7e, 0x71, 0x63, 0x91, 0x5a, 0x04, 0x05, 0x3c, 0x00, 0x00, 0x11,
2514
            0x00, 0x5d, 0x7a, 0x82, 0xb7, 0xca, 0x2d, 0x31, 0xff, 0xff, 0xc7, 0x95, 0x33, 0x2e,
2515
            0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00,
2516
            0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x66, 0x00, 0x4d,
2517
            0x4d, 0x00, 0x2a, 0x00,
2518
        ];
2519
2520
        let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
2521
        let layout = decoder.prepare_image().unwrap();
2522
        let mut buf = vec![0; usize::try_from(layout.total_bytes()).unwrap()];
2523
        assert!(decoder.read_image(&mut buf).is_ok());
2524
    }
2525
2526
    #[test]
2527
    fn test_no_header() {
2528
        let tests = [
2529
            "Info_R8_G8_B8.bmp",
2530
            "Info_A8_R8_G8_B8.bmp",
2531
            "Info_8_Bit.bmp",
2532
            "Info_4_Bit.bmp",
2533
            "Info_1_Bit.bmp",
2534
        ];
2535
2536
        for name in &tests {
2537
            let path = format!("tests/images/bmp/images/{name}");
2538
            let ref_img = crate::open(&path).unwrap();
2539
            let mut data = std::fs::read(&path).unwrap();
2540
            // skip the BITMAPFILEHEADER
2541
            let slice = &mut data[14..];
2542
            let decoder = BmpDecoder::new_without_file_header(Cursor::new(slice)).unwrap();
2543
            let no_hdr_img = crate::DynamicImage::from_decoder(decoder).unwrap();
2544
            assert_eq!(ref_img, no_hdr_img);
2545
        }
2546
    }
2547
2548
    /// Validates that the given ICC profile data can be parsed by moxcms and contains
2549
    /// the expected properties for an RGB display profile.
2550
    fn validate_icc_profile(
2551
        profile_data: &[u8],
2552
        source_file: &str,
2553
        expected_color_space: moxcms::DataColorSpace,
2554
        expected_profile_class: moxcms::ProfileClass,
2555
    ) {
2556
        let parsed_profile = moxcms::ColorProfile::new_from_slice(profile_data);
2557
        assert!(
2558
            parsed_profile.is_ok(),
2559
            "ICC profile from {} should be parseable by moxcms: {:?}",
2560
            source_file,
2561
            parsed_profile.err()
2562
        );
2563
        let parsed_profile = parsed_profile.unwrap();
2564
        assert_eq!(
2565
            parsed_profile.color_space, expected_color_space,
2566
            "ICC profile from {} should have RGB color space",
2567
            source_file
2568
        );
2569
        assert_eq!(
2570
            parsed_profile.profile_class, expected_profile_class,
2571
            "ICC profile from {} should be a display/monitor profile",
2572
            source_file
2573
        );
2574
    }
2575
2576
    #[test]
2577
    fn test_icc_profile() {
2578
        // V5 header file without embedded ICC profile
2579
        let f =
2580
            BufReader::new(std::fs::File::open("tests/images/bmp/images/V5_24_Bit.bmp").unwrap());
2581
        let mut decoder = BmpDecoder::new(f).unwrap();
2582
        let profile = decoder.icc_profile().unwrap();
2583
        assert!(profile.is_none());
2584
2585
        // Test files with embedded ICC profiles
2586
        let f =
2587
            BufReader::new(std::fs::File::open("tests/images/bmp/images/rgb24prof.bmp").unwrap());
2588
        let mut decoder = BmpDecoder::new(f).unwrap();
2589
        let profile = decoder.icc_profile().unwrap();
2590
        assert!(profile.is_some());
2591
        let profile_data = profile.unwrap();
2592
        assert_eq!(profile_data.len(), 3048);
2593
        validate_icc_profile(
2594
            &profile_data,
2595
            "rgb24prof.bmp",
2596
            moxcms::DataColorSpace::Rgb,
2597
            moxcms::ProfileClass::DisplayDevice,
2598
        );
2599
2600
        let f =
2601
            BufReader::new(std::fs::File::open("tests/images/bmp/images/rgb24prof2.bmp").unwrap());
2602
        let mut decoder = BmpDecoder::new(f).unwrap();
2603
        let profile = decoder.icc_profile().unwrap();
2604
        assert!(profile.is_some());
2605
        let profile_data = profile.unwrap();
2606
        assert_eq!(profile_data.len(), 540);
2607
        validate_icc_profile(
2608
            &profile_data,
2609
            "rgb24prof2.bmp",
2610
            moxcms::DataColorSpace::Rgb,
2611
            moxcms::ProfileClass::DisplayDevice,
2612
        );
2613
    }
2614
2615
    #[test]
2616
    fn test_calibrated_rgb_icc_profile() {
2617
        // pal8v4.bmp has a V4 header with LCS_CALIBRATED_RGB — should synthesize an ICC profile.
2618
        let data = std::fs::read("tests/images/bmp/images/pal8v4.bmp").unwrap();
2619
        let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
2620
        let profile = decoder.icc_profile().unwrap();
2621
        assert!(
2622
            profile.is_some(),
2623
            "pal8v4: should have a synthesized ICC profile from calibrated RGB parameters"
2624
        );
2625
        validate_icc_profile(
2626
            &profile.unwrap(),
2627
            "pal8v4.bmp",
2628
            moxcms::DataColorSpace::Rgb,
2629
            moxcms::ProfileClass::DisplayDevice,
2630
        );
2631
2632
        // pal8v5.bmp uses LCS_sRGB — no ICC profile needed.
2633
        let data = std::fs::read("tests/images/bmp/images/pal8v5.bmp").unwrap();
2634
        let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
2635
        assert!(
2636
            decoder.icc_profile().unwrap().is_none(),
2637
            "pal8v5: should have no ICC profile (LCS_sRGB)"
2638
        );
2639
    }
2640
2641
    /// A reader that simulates partial data availability for testing resumable decoding.
2642
    /// It wraps a byte slice and limits how many bytes can be read before returning UnexpectedEof.
2643
    struct PartialReader {
2644
        data: Vec<u8>,
2645
        position: u64,
2646
        available_bytes: usize,
2647
    }
2648
2649
    impl PartialReader {
2650
        fn new(data: Vec<u8>) -> Self {
2651
            Self {
2652
                data,
2653
                position: 0,
2654
                available_bytes: 0,
2655
            }
2656
        }
2657
2658
        /// Set the number of bytes available for reading (absolute, not additive).
2659
        fn set_available(&mut self, bytes: usize) {
2660
            self.available_bytes = bytes.min(self.data.len());
2661
        }
2662
    }
2663
2664
    impl io::Read for PartialReader {
2665
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2666
            if self.position as usize >= self.available_bytes {
2667
                return Err(io::Error::new(
2668
                    io::ErrorKind::UnexpectedEof,
2669
                    "simulated partial data",
2670
                ));
2671
            }
2672
2673
            let available = self.available_bytes - self.position as usize;
2674
            let to_read = buf.len().min(available);
2675
            let start = self.position as usize;
2676
            buf[..to_read].copy_from_slice(&self.data[start..start + to_read]);
2677
            self.position += to_read as u64;
2678
            Ok(to_read)
2679
        }
2680
    }
2681
2682
    impl BufRead for PartialReader {
2683
        fn fill_buf(&mut self) -> io::Result<&[u8]> {
2684
            if self.position as usize >= self.available_bytes {
2685
                return Err(io::Error::new(
2686
                    io::ErrorKind::UnexpectedEof,
2687
                    "simulated partial data",
2688
                ));
2689
            }
2690
2691
            let start = self.position as usize;
2692
            Ok(&self.data[start..self.available_bytes])
2693
        }
2694
2695
        fn consume(&mut self, amt: usize) {
2696
            self.position += amt as u64;
2697
        }
2698
    }
2699
2700
    impl Seek for PartialReader {
2701
        fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
2702
            let new_pos = match pos {
2703
                SeekFrom::Start(offset) => offset as i64,
2704
                SeekFrom::End(offset) => self.data.len() as i64 + offset,
2705
                SeekFrom::Current(offset) => self.position as i64 + offset,
2706
            };
2707
2708
            if new_pos < 0 {
2709
                return Err(io::Error::new(
2710
                    io::ErrorKind::InvalidInput,
2711
                    "seek to negative position",
2712
                ));
2713
            }
2714
2715
            self.position = new_pos as u64;
2716
            Ok(self.position)
2717
        }
2718
    }
2719
2720
    /// Helper to check if an error is UnexpectedEof
2721
    fn is_unexpected_eof(err: &ImageError) -> bool {
2722
        matches!(err, ImageError::IoError(e) if e.kind() == io::ErrorKind::UnexpectedEof)
2723
    }
2724
2725
    /// Test resumable decoding with various BMP formats.
2726
    /// Verifies that read_metadata() and read_image_data() can be retried after
2727
    /// UnexpectedEof and produce identical results to normal decoding.
2728
    /// Also verifies metadata phase progress and row-level progress for non-RLE formats.
2729
    #[test]
2730
    fn test_resumable_decoding() {
2731
        use crate::ImageDecoder;
2732
2733
        struct TestCase {
2734
            path: &'static str,
2735
            is_rle: bool,
2736
            has_palette: bool,
2737
            has_icc_profile: bool,
2738
            top_down: bool,
2739
        }
2740
2741
        // Test multiple BMP formats to ensure resumable decoding works across variants
2742
        let test_files = [
2743
            TestCase {
2744
                path: "tests/images/bmp/images/Info_R8_G8_B8.bmp",
2745
                is_rle: false,
2746
                has_palette: false,
2747
                has_icc_profile: false,
2748
                top_down: false,
2749
            },
2750
            TestCase {
2751
                path: "tests/images/bmp/images/Info_A8_R8_G8_B8.bmp",
2752
                is_rle: false,
2753
                has_palette: false,
2754
                has_icc_profile: false,
2755
                top_down: false,
2756
            },
2757
            TestCase {
2758
                path: "tests/images/bmp/images/Info_A8_R8_G8_B8_Top_Down.bmp",
2759
                is_rle: false,
2760
                has_palette: false,
2761
                has_icc_profile: false,
2762
                top_down: true,
2763
            },
2764
            TestCase {
2765
                path: "tests/images/bmp/images/Info_8_Bit.bmp",
2766
                is_rle: false,
2767
                has_palette: true,
2768
                has_icc_profile: false,
2769
                top_down: false,
2770
            },
2771
            TestCase {
2772
                path: "tests/images/bmp/images/Core_8_Bit.bmp",
2773
                is_rle: false,
2774
                has_palette: true,
2775
                has_icc_profile: false,
2776
                top_down: false,
2777
            },
2778
            TestCase {
2779
                path: "tests/images/bmp/images/pal8rle.bmp",
2780
                is_rle: true,
2781
                has_palette: true,
2782
                has_icc_profile: false,
2783
                top_down: false,
2784
            },
2785
            TestCase {
2786
                path: "tests/images/bmp/images/pal4rle.bmp",
2787
                is_rle: true,
2788
                has_palette: true,
2789
                has_icc_profile: false,
2790
                top_down: false,
2791
            },
2792
            TestCase {
2793
                path: "tests/images/bmp/images/rgb24prof.bmp",
2794
                is_rle: false,
2795
                has_palette: false,
2796
                has_icc_profile: true,
2797
                top_down: false,
2798
            },
2799
            TestCase {
2800
                path: "tests/images/bmp/images/rgb24rle24.bmp",
2801
                is_rle: true,
2802
                has_palette: false,
2803
                has_icc_profile: false,
2804
                top_down: false,
2805
            },
2806
        ];
2807
2808
        for TestCase {
2809
            path,
2810
            is_rle,
2811
            has_palette,
2812
            has_icc_profile,
2813
            top_down,
2814
        } in test_files
2815
        {
2816
            let data = std::fs::read(path).unwrap();
2817
            let file_size = data.len();
2818
2819
            // Get reference result from normal decoding
2820
            let mut ref_decoder = BmpDecoder::new(Cursor::new(data.clone())).unwrap();
2821
            let expected_bytes = ref_decoder.prepare_image().unwrap().total_bytes() as usize;
2822
            let mut ref_buf = vec![0u8; expected_bytes];
2823
            let ref_icc_len = ref_decoder.icc_profile().unwrap().map(|p| p.len());
2824
            ref_decoder.read_image(&mut ref_buf).unwrap();
2825
2826
            // Test resumable decoding with simulated streaming
2827
            let reader = PartialReader::new(data);
2828
            let mut decoder = BmpDecoder::new_resumable(reader);
2829
2830
            // Track metadata phase transitions
2831
            let mut saw_reading_palette = false;
2832
            let mut saw_reading_icc = false;
2833
2834
            // Phase 1: Stream bytes until metadata succeeds
2835
            let mut bytes_available = 0;
2836
            loop {
2837
                decoder.reader.set_available(bytes_available);
2838
                match decoder.read_metadata() {
2839
                    Ok(()) => break,
2840
                    Err(ref e) if is_unexpected_eof(e) => {
2841
                        if let DecoderState::ReadingMetadata { progress } = decoder.state {
2842
                            match progress {
2843
                                MetadataProgress::ReadingPalette { .. } => {
2844
                                    saw_reading_palette = true
2845
                                }
2846
                                MetadataProgress::ReadingIccProfile { .. } => {
2847
                                    saw_reading_icc = true
2848
                                }
2849
                                _ => {}
2850
                            }
2851
                        }
2852
2853
                        // Simulate more data arriving (add 10 bytes at a time, capped at file size)
2854
                        bytes_available = (bytes_available + 10).min(file_size);
2855
                        assert!(
2856
                            bytes_available <= file_size,
2857
                            "{path}: metadata should succeed before EOF"
2858
                        );
2859
                    }
2860
                    Err(e) => panic!("{path}: unexpected error during metadata: {e:?}"),
2861
                }
2862
            }
2863
2864
            // Verify metadata phase transitions occurred as expected
2865
            if has_palette {
2866
                assert!(
2867
                    saw_reading_palette,
2868
                    "{path}: should have seen ReadingPalette phase"
2869
                );
2870
            }
2871
            if has_icc_profile {
2872
                assert!(
2873
                    saw_reading_icc,
2874
                    "{path}: should have seen ReadingIccProfile phase"
2875
                );
2876
                let icc = decoder.icc_profile().unwrap();
2877
                assert_eq!(
2878
                    icc.map(|p| p.len()),
2879
                    ref_icc_len,
2880
                    "{path}: ICC profile length mismatch"
2881
                );
2882
            }
2883
2884
            // Verify dimensions are available after metadata
2885
            let layout = decoder.prepare_image().unwrap();
2886
            let (width, height) = layout.layout.dimensions();
2887
            assert!(width > 0 && height > 0, "{path}: invalid dimensions");
2888
            assert_eq!(
2889
                layout.total_bytes() as usize,
2890
                expected_bytes,
2891
                "{path}: total_bytes mismatch"
2892
            );
2893
2894
            // Phase 2: Stream bytes until image data succeeds
2895
            let mut buf = vec![0u8; expected_bytes];
2896
            let mut prev_decoded_rows = 0u32;
2897
            loop {
2898
                decoder.reader.set_available(bytes_available);
2899
                match decoder.read_image_data(&mut buf) {
2900
                    Ok(()) => {
2901
                        // After successful decode, rows_decoded() should return full height
2902
                        let progress = decoder.rows_decoded();
2903
                        assert_eq!(
2904
                            progress.rows(),
2905
                            height,
2906
                            "{path}: rows_decoded() should equal height after complete decode"
2907
                        );
2908
                        if top_down {
2909
                            assert!(
2910
                                matches!(progress, RowsDecoded::TopDown { .. }),
2911
                                "{path}: top-down file should produce TopDown, got {progress:?}"
2912
                            );
2913
                        } else {
2914
                            assert!(
2915
                                matches!(progress, RowsDecoded::BottomUp { .. }),
2916
                                "{path}: bottom-up file should produce BottomUp, got {progress:?}"
2917
                            );
2918
                        }
2919
                        break;
2920
                    }
2921
                    Err(ref e) if is_unexpected_eof(e) => {
2922
                        // Validate rows_decoded() returns correct count and variant
2923
                        let progress = decoder.rows_decoded();
2924
                        let decoded_rows = progress.rows();
2925
                        assert!(
2926
                            decoded_rows <= height,
2927
                            "{path}: rows_decoded() {decoded_rows} exceeds height {height}"
2928
                        );
2929
                        assert!(decoded_rows >= prev_decoded_rows, "{path}: rows_decoded() decreased from {prev_decoded_rows} to {decoded_rows}");
2930
                        prev_decoded_rows = decoded_rows;
2931
2932
                        // Verify state tracks progress appropriately
2933
                        match decoder.state {
2934
                            DecoderState::ReadingRowData { rows_decoded } => {
2935
                                assert!(!is_rle, "{path}: expected ReadingRleData for RLE format");
2936
                                assert!(
2937
                                    rows_decoded < height,
2938
                                    "{path}: rows_decoded {rows_decoded} >= height {height}"
2939
                                );
2940
                                assert_eq!(
2941
                                    decoded_rows, rows_decoded,
2942
                                    "{path}: rows_decoded() mismatch"
2943
                                );
2944
                            }
2945
                            DecoderState::ReadingRleData { progress } => {
2946
                                assert!(
2947
                                    is_rle,
2948
                                    "{path}: expected ReadingRowData for non-RLE format"
2949
                                );
2950
                                match progress {
2951
                                    RleProgress::NotStarted => {
2952
                                        assert_eq!(
2953
                                            decoded_rows, 0,
2954
                                            "{path}: should be 0 for NotStarted"
2955
                                        );
2956
                                    }
2957
                                    RleProgress::Checkpoint { row, .. } => {
2958
                                        assert!(
2959
                                            row < height,
2960
                                            "{path}: RLE row {row} >= height {height}"
2961
                                        );
2962
                                        assert_eq!(
2963
                                            decoded_rows, row,
2964
                                            "{path}: rows_decoded() mismatch with RLE row"
2965
                                        );
2966
                                    }
2967
                                }
2968
                            }
2969
                            _ => panic!("{path}: unexpected state: {:?}", decoder.state),
2970
                        }
2971
2972
                        bytes_available += 100;
2973
                        assert!(
2974
                            bytes_available <= file_size + 100,
2975
                            "{path}: image data should succeed before EOF"
2976
                        );
2977
                    }
2978
                    Err(e) => panic!("{path}: unexpected error during image data: {e:?}"),
2979
                }
2980
            }
2981
2982
            // Verify decoded data matches reference
2983
            assert_eq!(buf, ref_buf, "{path}: decoded data mismatch");
2984
        }
2985
    }
2986
2987
    /// Test that BMP files with known spec violations are accepted by the
2988
    /// decoder (which defaults to lenient mode), and that strict mode still
2989
    /// detects the violations internally.
2990
    ///
2991
    /// These files come from the Chromium BMP test suite ("bad/" category):
2992
    /// - `rletopdown`: RLE compression with top-down orientation (spec forbids this)
2993
    /// - `badplanes`: planes field != 1 (spec requires exactly 1)
2994
    /// - `badpalettesize`: colors_used exceeds max for the bit depth
2995
    /// - `pal8oversizepal`: 8-bit palette with colors_used=300 (max is 256)
2996
    /// - `rgb16-880`: 16-bit bitfields with 8-8-0 channel widths (blue mask is zero)
2997
    #[test]
2998
    fn test_strict_vs_lenient_spec_validation() {
2999
        let questionable_files = [
3000
            (
3001
                "tests/images/bmp/images/lenient/rletopdown.bmp",
3002
                "rletopdown: RLE with top-down should be rejected in strict mode",
3003
            ),
3004
            (
3005
                "tests/images/bmp/images/lenient/badplanes.bmp",
3006
                "badplanes: planes != 1 should be rejected in strict mode",
3007
            ),
3008
            (
3009
                "tests/images/bmp/images/lenient/badpalettesize.bmp",
3010
                "badpalettesize: palette size exceeding bit depth should be rejected in strict mode",
3011
            ),
3012
            (
3013
                "tests/images/bmp/images/lenient/pal8oversizepal.bmp",
3014
                "pal8oversizepal: colors_used=300 exceeds max 256 for 8-bit",
3015
            ),
3016
            (
3017
                "tests/images/bmp/images/lenient/rgb16-880.bmp",
3018
                "rgb16-880: zero blue mask should be rejected in strict mode",
3019
            ),
3020
            (
3021
                "tests/images/bmp/images/lenient/V5_A8_R8_G8_B8_Rgb_BadMask.bmp",
3022
                "V5_A8_R8_G8_B8_Rgb_BadMask: non-standard alpha mask under BI_RGB",
3023
            ),
3024
        ];
3025
3026
        for (path, description) in &questionable_files {
3027
            let data = std::fs::read(path)
3028
                .unwrap_or_else(|e| panic!("{description}: failed to read {path}: {e}"));
3029
3030
            // Default (lenient) mode: these files should be accepted
3031
            let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap_or_else(|e| {
3032
                panic!("{description}: decoding failed: {e:?}");
3033
            });
3034
            let layout = decoder.prepare_image().unwrap_or_else(|e| {
3035
                panic!("{description}: peek_layout failed: {e:?}");
3036
            });
3037
            let mut buf = vec![0u8; layout.total_bytes() as usize];
3038
            decoder.read_image(buf.as_mut_slice()).unwrap_or_else(|e| {
3039
                panic!("{description}: read_image failed: {e:?}");
3040
            });
3041
3042
            // Strict mode: these files should be rejected
3043
            assert!(
3044
                BmpDecoder::with_spec_compliance(Cursor::new(&data), SpecCompliance::Strict)
3045
                    .is_err(),
3046
                "{description}: expected error in strict mode, but got Ok"
3047
            );
3048
        }
3049
    }
3050
3051
    /// A BMP with data_offset=34 points into the middle of the DIB header,
3052
    /// which is invalid. The decoder should clamp it to bmp_header_end (54)
3053
    /// and produce the same output as a correctly-formed file.
3054
    #[test]
3055
    fn test_invalid_data_offset_into_dib_header() {
3056
        let data: Vec<u8> = vec![
3057
            0x42, 0x4D, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
3058
            0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00,
3059
            0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3060
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3061
            0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
3062
        ];
3063
3064
        // Same BMP but with the correct data_offset = 54 (0x36)
3065
        let mut reference = data.clone();
3066
        reference[10] = 0x36;
3067
3068
        // Decode both
3069
        let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
3070
        let len = decoder.prepare_image().unwrap().total_bytes();
3071
        let mut buf = vec![0u8; len as usize];
3072
        decoder.read_image(&mut buf).unwrap();
3073
3074
        let mut ref_decoder = BmpDecoder::new(Cursor::new(&reference)).unwrap();
3075
        let len = decoder.prepare_image().unwrap().total_bytes();
3076
        let mut ref_buf = vec![0u8; len as usize];
3077
        ref_decoder.read_image(&mut ref_buf).unwrap();
3078
3079
        assert_eq!(
3080
            buf, ref_buf,
3081
            "BMP with invalid data_offset=34 should decode identically to data_offset=54"
3082
        );
3083
    }
3084
3085
    /// Test that strict mode correctly rejects RLE files with known corruptions.
3086
    ///
3087
    /// - `rle_overflow.bmp`: The image header specifies a width of 2. However, the RLE data
3088
    ///   contains an absolute run of 3 pixels (`00 03 ...`), which overflows the row boundary.
3089
    /// - `badrle.bmp`: The image height is 64. However, the RLE data contains multiple Delta skip
3090
    ///   instructions that move the cursor past the end of the image.
3091
    #[test]
3092
    fn test_strict_mode_fails_on_rle_errors() {
3093
        let test_files = [
3094
            "tests/images/bmp/images/lenient/rle_overflow.bmp",
3095
            "tests/images/bmp/images/lenient/badrle.bmp",
3096
        ];
3097
3098
        for path in &test_files {
3099
            let data = std::fs::read(path).expect("Test image missing");
3100
3101
            // Strict mode must fail on these images during full decode
3102
            let strict_result =
3103
                BmpDecoder::with_spec_compliance(Cursor::new(&data), SpecCompliance::Strict)
3104
                    .and_then(|mut d| {
3105
                        let len = d.prepare_image()?.total_bytes();
3106
                        let mut buf = vec![0u8; len as usize];
3107
                        d.read_image(buf.as_mut_slice())
3108
                    });
3109
            assert!(
3110
                strict_result.is_err(),
3111
                "{path}: expected error in strict mode, but got Ok"
3112
            );
3113
        }
3114
    }
3115
3116
    #[test]
3117
    fn test_decode_bmp_rle_overflow() {
3118
        let data = std::fs::read("tests/images/bmp/images/lenient/rle_overflow.bmp")
3119
            .expect("Test image missing");
3120
        let mut decoder = BmpDecoder::new(Cursor::new(data)).unwrap();
3121
        let len = decoder.prepare_image().unwrap().total_bytes();
3122
        let mut buffer = vec![0u8; len as usize];
3123
        let result = decoder.read_image(&mut buffer);
3124
        assert!(result.is_ok());
3125
    }
3126
3127
    #[test]
3128
    fn test_decode_bmp_badrle() {
3129
        let data = std::fs::read("tests/images/bmp/images/lenient/badrle.bmp")
3130
            .expect("Test image missing");
3131
        let mut decoder = BmpDecoder::new(Cursor::new(data)).unwrap();
3132
        let len = decoder.prepare_image().unwrap().total_bytes();
3133
        let mut buffer = vec![0u8; len as usize];
3134
        let result = decoder.read_image(&mut buffer);
3135
        assert!(result.is_ok());
3136
    }
3137
3138
    #[test]
3139
    fn test_decode_truncated_bmp() {
3140
        use std::io::Cursor;
3141
3142
        let data = vec![
3143
            0x42, 0x4D, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
3144
            0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
3145
            0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3146
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00,
3147
            0x00,
3148
        ];
3149
3150
        // Test Lenient mode
3151
        let decoder = BmpDecoder::new(Cursor::new(data.clone())).unwrap();
3152
        let mut decoder = crate::ImageReader::from_decoder(Box::new(decoder));
3153
        let result = decoder.decode();
3154
        assert!(
3155
            result.is_ok(),
3156
            "Expected Ok in lenient mode for truncated file"
3157
        );
3158
3159
        // Test Strict mode
3160
        let strict_decoder =
3161
            BmpDecoder::with_spec_compliance(Cursor::new(data), SpecCompliance::Strict).unwrap();
3162
        let mut decoder = crate::ImageReader::from_decoder(Box::new(strict_decoder));
3163
        let result = decoder.decode();
3164
3165
        assert!(
3166
            result.is_err(),
3167
            "Expected error in strict mode for truncated file"
3168
        );
3169
    }
3170
}