Coverage Report

Created: 2026-05-16 07:04

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.06k
    pub fn rows(&self) -> u32 {
99
2.06k
        match *self {
100
2.06k
            RowsDecoded::TopDown { rows } | RowsDecoded::BottomUp { rows } => rows,
101
        }
102
2.06k
    }
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
698
    fn parse(buffer: &[u8; 8], spec_strictness: SpecCompliance) -> ImageResult<Self> {
116
698
        let width = i32::from(u16::from_le_bytes(buffer[0..2].try_into().unwrap()));
117
698
        let height = i32::from(u16::from_le_bytes(buffer[2..4].try_into().unwrap()));
118
119
698
        let planes = u16::from_le_bytes(buffer[4..6].try_into().unwrap());
120
698
        if spec_strictness == SpecCompliance::Strict && planes != 1 {
121
0
            return Err(DecoderError::MoreThanOnePlane.into());
122
698
        }
123
124
698
        let bit_count = u16::from_le_bytes(buffer[6..8].try_into().unwrap());
125
698
        let image_type = match bit_count {
126
516
            1 | 4 | 8 => ImageType::Palette,
127
172
            24 => ImageType::RGB24,
128
            _ => {
129
10
                return Err(
130
10
                    DecoderError::InvalidChannelWidth(ChannelWidthError::Rgb, bit_count).into(),
131
10
                )
132
            }
133
        };
134
135
688
        Ok(ParsedCoreHeader {
136
688
            width,
137
688
            height,
138
688
            bit_count,
139
688
            image_type,
140
688
        })
141
698
    }
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.61k
    fn parse(buffer: &[u8; 36], spec_strictness: SpecCompliance) -> ImageResult<Self> {
157
4.61k
        let width = i32::from_le_bytes(buffer[0..4].try_into().unwrap());
158
4.61k
        let mut height = i32::from_le_bytes(buffer[4..8].try_into().unwrap());
159
160
        // Width cannot be negative
161
4.61k
        if width < 0 {
162
70
            return Err(DecoderError::NegativeWidth(width).into());
163
4.54k
        } else if width > MAX_WIDTH_HEIGHT || height > MAX_WIDTH_HEIGHT {
164
58
            return Err(DecoderError::ImageTooLarge(width, height).into());
165
4.49k
        }
166
167
4.49k
        if height == i32::MIN {
168
2
            return Err(DecoderError::InvalidHeight.into());
169
4.48k
        }
170
171
        // A negative height indicates a top-down DIB
172
4.48k
        let top_down = if height < 0 {
173
1.36k
            height = -height;
174
1.36k
            true
175
        } else {
176
3.12k
            false
177
        };
178
179
4.48k
        let planes = u16::from_le_bytes(buffer[8..10].try_into().unwrap());
180
4.48k
        if spec_strictness == SpecCompliance::Strict && planes != 1 {
181
0
            return Err(DecoderError::MoreThanOnePlane.into());
182
4.48k
        }
183
184
4.48k
        let bit_count = u16::from_le_bytes(buffer[10..12].try_into().unwrap());
185
4.48k
        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.48k
        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.48k
        }
197
198
        // Skip size_image (16-19), x_pix_permeter (20-23), y_pix_permeter (24-27)
199
4.48k
        let colors_used = u32::from_le_bytes(buffer[28..32].try_into().unwrap());
200
        // Skip important_colors (32-35)
201
4.48k
        Ok(ParsedInfoHeader {
202
4.48k
            width,
203
4.48k
            height,
204
4.48k
            top_down,
205
4.48k
            bit_count,
206
4.48k
            compression,
207
4.48k
            colors_used,
208
4.48k
        })
209
4.61k
    }
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
649
    fn parse(buffer: &[u8], has_alpha: bool) -> Self {
226
649
        let r_mask = u32::from_le_bytes(buffer[0..4].try_into().unwrap());
227
649
        let g_mask = u32::from_le_bytes(buffer[4..8].try_into().unwrap());
228
649
        let b_mask = u32::from_le_bytes(buffer[8..12].try_into().unwrap());
229
649
        let a_mask = if has_alpha {
230
344
            u32::from_le_bytes(buffer[12..16].try_into().unwrap())
231
        } else {
232
305
            0
233
        };
234
235
649
        ParsedBitfields {
236
649
            r_mask,
237
649
            g_mask,
238
649
            b_mask,
239
649
            a_mask,
240
649
        }
241
649
    }
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
210
    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
210
        let cs_type = u32::from_le_bytes(buffer[52..56].try_into().unwrap());
260
261
        // Only embedded profiles are supported
262
210
        if cs_type != PROFILE_EMBEDDED {
263
0
            return None;
264
210
        }
265
266
        // bV5ProfileData is at offset 112 from header start, which is offset 108 from after size field
267
210
        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
210
        let profile_size = u32::from_le_bytes(buffer[112..116].try_into().unwrap());
271
272
210
        if profile_size == 0 || profile_offset_from_header == 0 {
273
18
            return None;
274
192
        }
275
276
        // Compute the absolute file offset by adding the header's position to the relative offset
277
192
        let profile_offset = bmp_header_offset + u64::from(profile_offset_from_header);
278
279
192
        Some(ParsedIccProfile {
280
192
            profile_offset,
281
192
            profile_size,
282
192
        })
283
210
    }
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
723
    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
723
        let cs_type = u32::from_le_bytes(buffer[52..56].try_into().unwrap());
306
307
212
        match cs_type {
308
            LCS_CALIBRATED_RGB => {
309
954
                let read_u32 = |offset: usize| -> u32 {
310
954
                    u32::from_le_bytes(buffer[offset..offset + 4].try_into().unwrap())
311
954
                };
312
313
                // FXPT2DOT30 (2.30 fixed-point) → f32.
314
636
                let fxpt2dot30 = |val: u32| -> f32 { val as f32 * (1.0 / (1u64 << 30) as f32) };
315
                // FXPT16DOT16 (16.16 fixed-point) → f32.
316
318
                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
106
                let rx = fxpt2dot30(read_u32(56));
324
106
                let ry = fxpt2dot30(read_u32(60));
325
106
                let gx = fxpt2dot30(read_u32(68));
326
106
                let gy = fxpt2dot30(read_u32(72));
327
106
                let bx = fxpt2dot30(read_u32(80));
328
106
                let by = fxpt2dot30(read_u32(84));
329
330
                // Gamma values at offsets 96-107 from header start (92-103 from after size).
331
106
                let gamma_r = fxpt16dot16(read_u32(92));
332
106
                let gamma_g = fxpt16dot16(read_u32(96));
333
106
                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
106
                if ry == 0.0 || gy == 0.0 || by == 0.0 {
338
13
                    return None;
339
93
                }
340
341
93
                Some(ColorSpaceInfo::CalibratedRgb(CalibratedRgb {
342
93
                    rx,
343
93
                    ry,
344
93
                    gx,
345
93
                    gy,
346
93
                    bx,
347
93
                    by,
348
93
                    gamma_r,
349
93
                    gamma_g,
350
93
                    gamma_b,
351
93
                }))
352
            }
353
4
            LCS_SRGB | LCS_WINDOWS_COLOR_SPACE => Some(ColorSpaceInfo::Srgb),
354
212
            PROFILE_EMBEDDED if bmp_header_size >= BITMAPV5HEADER_SIZE => {
355
210
                ParsedIccProfile::parse(buffer, bmp_header_offset).map(ColorSpaceInfo::EmbeddedIcc)
356
            }
357
403
            _ => None,
358
        }
359
723
    }
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
93
    fn to_color_profile(self) -> moxcms::ColorProfile {
393
93
        let primaries = moxcms::ColorPrimaries {
394
93
            red: moxcms::Chromaticity::new(self.rx, self.ry),
395
93
            green: moxcms::Chromaticity::new(self.gx, self.gy),
396
93
            blue: moxcms::Chromaticity::new(self.bx, self.by),
397
93
        };
398
399
93
        let mut profile = moxcms::ColorProfile::new_srgb();
400
93
        profile.update_rgb_colorimetry(moxcms::WHITE_POINT_D65, primaries);
401
402
        // Clear inherited CICP metadata from the sRGB base profile.
403
93
        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
279
        let safe_gamma = |g: f32| if g > 0.0 { g } else { 1.0 };
409
279
        let parametric_trc = |g: f32| moxcms::ToneReprCurve::Parametric(vec![safe_gamma(g)]);
410
93
        profile.red_trc = Some(parametric_trc(self.gamma_r));
411
93
        profile.green_trc = Some(parametric_trc(self.gamma_g));
412
93
        profile.blue_trc = Some(parametric_trc(self.gamma_b));
413
93
        profile
414
93
    }
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.62k
    fn default() -> Self {
504
5.62k
        DecoderState::ReadingMetadata {
505
5.62k
            progress: MetadataProgress::default(),
506
5.62k
        }
507
5.62k
    }
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.16G
    fn next(&mut self) -> Option<&'a mut [u8]> {
552
1.16G
        match self.chunks {
553
1.16G
            Chunker::FromTop(ref mut chunks) => chunks.next(),
554
3.95M
            Chunker::FromBottom(ref mut chunks) => chunks.next(),
555
        }
556
1.16G
    }
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
418
    fn from(e: DecoderError) -> ImageError {
650
418
        ImageError::Decoding(DecodingError::new(ImageFormat::Bmp.into(), e))
651
418
    }
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
620
fn calculate_row_padding(bytes_per_row: usize) -> usize {
686
620
    (4 - (bytes_per_row % 4)) % 4
687
620
}
688
689
/// Allocate a row buffer with OOM protection.
690
1.16k
fn allocate_row_buffer(size: usize) -> ImageResult<Vec<u8>> {
691
1.16k
    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.16k
    buffer.resize(size, 0);
701
1.16k
    Ok(buffer)
702
1.16k
}
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
656k
fn read_scanline(
711
656k
    reader: &mut (impl io::Read + Seek),
712
656k
    buf: &mut [u8],
713
656k
    current_file_row: &mut u32,
714
656k
    last_row: u32,
715
656k
    spec_strictness: SpecCompliance,
716
656k
) -> io::Result<()> {
717
656k
    let is_last_row = *current_file_row == last_row;
718
656k
    *current_file_row += 1;
719
720
656k
    if is_last_row && spec_strictness == SpecCompliance::Lenient {
721
586
        let current_pos = reader.stream_position()?;
722
586
        let end_pos = reader.seek(SeekFrom::End(0))?;
723
586
        reader.seek(SeekFrom::Start(current_pos))?;
724
725
586
        let Some((last, head)) = buf.split_last_mut() else {
726
            // Empty row, nothing to read.
727
0
            return Ok(());
728
        };
729
730
586
        if Ok(head.len()) == usize::try_from(end_pos - current_pos) {
731
30
            reader.read_exact(head)?;
732
30
            *last = b'\0';
733
30
            return Ok(());
734
556
        }
735
656k
    }
736
737
656k
    reader.read_exact(buf)
738
656k
}
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
5.01k
fn check_for_overflow(width: i32, length: i32, channels: usize) -> ImageResult<()> {
743
5.01k
    num_bytes(width, length, channels)
744
5.01k
        .map(|_| ())
745
5.01k
        .ok_or_else(|| {
746
14
            ImageError::Unsupported(UnsupportedError::from_format_and_kind(
747
14
                ImageFormat::Bmp.into(),
748
14
                UnsupportedErrorKind::GenericFeature(format!(
749
14
                    "Image dimensions ({width}x{length} w/{channels} channels) are too large"
750
14
                )),
751
14
            ))
752
14
        })
753
5.01k
}
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
5.01k
fn num_bytes(width: i32, length: i32, channels: usize) -> Option<usize> {
758
5.01k
    if width <= 0 || length <= 0 {
759
14
        None
760
    } else {
761
4.99k
        match channels.checked_mul(width as usize) {
762
4.99k
            Some(n) => n.checked_mul(length as usize),
763
0
            None => None,
764
        }
765
    }
766
5.01k
}
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.06k
fn with_rows_resumable<F>(
776
2.06k
    buffer: &mut [u8],
777
2.06k
    width: i32,
778
2.06k
    height: i32,
779
2.06k
    channels: usize,
780
2.06k
    top_down: bool,
781
2.06k
    start_row: u32,
782
2.06k
    mut func: F,
783
2.06k
) -> Result<u32, (u32, io::Error)>
784
2.06k
where
785
2.06k
    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.06k
    let row_width = channels.checked_mul(width as usize).unwrap();
790
2.06k
    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
656k
    fn output_row_index(file_row: u32, height: u32, top_down: bool) -> usize {
797
656k
        if top_down {
798
23.0k
            file_row as usize
799
        } else {
800
633k
            (height - 1 - file_row) as usize
801
        }
802
656k
    }
803
804
    /// Get a mutable reference to a specific row in the output buffer.
805
    #[inline]
806
656k
    fn get_row_mut(buf: &mut [u8], row_index: usize, row_stride: usize) -> &mut [u8] {
807
656k
        let start = row_index * row_stride;
808
656k
        &mut buf[start..][..row_stride]
809
656k
    }
810
811
656k
    for file_row in start_row..height {
812
656k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
656k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
656k
        if let Err(e) = func(row) {
816
1.79k
            return Err((file_row, e));
817
654k
        }
818
    }
819
271
    Ok(height)
820
2.06k
}
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
316
fn with_rows_resumable<F>(
776
316
    buffer: &mut [u8],
777
316
    width: i32,
778
316
    height: i32,
779
316
    channels: usize,
780
316
    top_down: bool,
781
316
    start_row: u32,
782
316
    mut func: F,
783
316
) -> Result<u32, (u32, io::Error)>
784
316
where
785
316
    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
316
    let row_width = channels.checked_mul(width as usize).unwrap();
790
316
    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
58.7k
    for file_row in start_row..height {
812
58.7k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
58.7k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
58.7k
        if let Err(e) = func(row) {
816
282
            return Err((file_row, e));
817
58.4k
        }
818
    }
819
34
    Ok(height)
820
316
}
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
359
fn with_rows_resumable<F>(
776
359
    buffer: &mut [u8],
777
359
    width: i32,
778
359
    height: i32,
779
359
    channels: usize,
780
359
    top_down: bool,
781
359
    start_row: u32,
782
359
    mut func: F,
783
359
) -> Result<u32, (u32, io::Error)>
784
359
where
785
359
    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
359
    let row_width = channels.checked_mul(width as usize).unwrap();
790
359
    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
8.73k
    for file_row in start_row..height {
812
8.73k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
8.73k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
8.73k
        if let Err(e) = func(row) {
816
334
            return Err((file_row, e));
817
8.40k
        }
818
    }
819
25
    Ok(height)
820
359
}
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
489
fn with_rows_resumable<F>(
776
489
    buffer: &mut [u8],
777
489
    width: i32,
778
489
    height: i32,
779
489
    channels: usize,
780
489
    top_down: bool,
781
489
    start_row: u32,
782
489
    mut func: F,
783
489
) -> Result<u32, (u32, io::Error)>
784
489
where
785
489
    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
489
    let row_width = channels.checked_mul(width as usize).unwrap();
790
489
    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
27.0k
    for file_row in start_row..height {
812
27.0k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
27.0k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
27.0k
        if let Err(e) = func(row) {
816
433
            return Err((file_row, e));
817
26.6k
        }
818
    }
819
56
    Ok(height)
820
489
}
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
904
fn with_rows_resumable<F>(
776
904
    buffer: &mut [u8],
777
904
    width: i32,
778
904
    height: i32,
779
904
    channels: usize,
780
904
    top_down: bool,
781
904
    start_row: u32,
782
904
    mut func: F,
783
904
) -> Result<u32, (u32, io::Error)>
784
904
where
785
904
    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
904
    let row_width = channels.checked_mul(width as usize).unwrap();
790
904
    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
562k
    for file_row in start_row..height {
812
562k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
562k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
562k
        if let Err(e) = func(row) {
816
748
            return Err((file_row, e));
817
561k
        }
818
    }
819
156
    Ok(height)
820
904
}
821
822
106k
fn set_8bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
823
106k
    pixel_iter: &mut ChunksExactMut<u8>,
824
106k
    palette: &[[u8; 3]],
825
106k
    indices: T,
826
106k
    n_pixels: usize,
827
106k
) -> bool {
828
863k
    for idx in indices.take(n_pixels) {
829
863k
        if let Some(pixel) = pixel_iter.next() {
830
862k
            let rgb = palette[*idx as usize];
831
862k
            pixel[0] = rgb[0];
832
862k
            pixel[1] = rgb[1];
833
862k
            pixel[2] = rgb[2];
834
862k
        } else {
835
1.34k
            return false;
836
        }
837
    }
838
105k
    true
839
106k
}
840
841
1.11M
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
842
1.11M
    pixel_iter: &mut ChunksExactMut<u8>,
843
1.11M
    palette: &[[u8; 3]],
844
1.11M
    indices: T,
845
1.11M
    mut n_pixels: usize,
846
1.11M
) -> bool {
847
3.52M
    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
3.52M
        set_pixel!(idx >> 4);
865
2.42M
        set_pixel!(idx & 0xf);
866
    }
867
48.7k
    true
868
1.11M
}
image::codecs::bmp::decoder::set_4bit_pixel_run::<core::slice::iter::Iter<u8>>
Line
Count
Source
841
33.1k
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
842
33.1k
    pixel_iter: &mut ChunksExactMut<u8>,
843
33.1k
    palette: &[[u8; 3]],
844
33.1k
    indices: T,
845
33.1k
    mut n_pixels: usize,
846
33.1k
) -> bool {
847
381k
    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
379k
        set_pixel!(idx >> 4);
865
350k
        set_pixel!(idx & 0xf);
866
    }
867
11.8k
    true
868
33.1k
}
image::codecs::bmp::decoder::set_4bit_pixel_run::<core::iter::sources::repeat::Repeat<&u8>>
Line
Count
Source
841
1.08M
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
842
1.08M
    pixel_iter: &mut ChunksExactMut<u8>,
843
1.08M
    palette: &[[u8; 3]],
844
1.08M
    indices: T,
845
1.08M
    mut n_pixels: usize,
846
1.08M
) -> bool {
847
3.14M
    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
3.14M
        set_pixel!(idx >> 4);
865
2.07M
        set_pixel!(idx & 0xf);
866
    }
867
36.9k
    true
868
1.08M
}
869
870
#[rustfmt::skip]
871
6.14k
fn set_2bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
872
6.14k
    pixel_iter: &mut ChunksExactMut<u8>,
873
6.14k
    palette: &[[u8; 3]],
874
6.14k
    indices: T,
875
6.14k
    mut n_pixels: usize,
876
6.14k
) -> bool {
877
667k
    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
666k
        set_pixel!((idx >> 6) & 0x3u8);
895
666k
        set_pixel!((idx >> 4) & 0x3u8);
896
665k
        set_pixel!((idx >> 2) & 0x3u8);
897
665k
        set_pixel!( idx       & 0x3u8);
898
    }
899
6.14k
    true
900
6.14k
}
901
902
444k
fn set_1bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
903
444k
    pixel_iter: &mut ChunksExactMut<u8>,
904
444k
    palette: &[[u8; 3]],
905
444k
    indices: T,
906
444k
) {
907
2.10M
    for idx in indices {
908
2.10M
        let mut bit = 0x80;
909
        loop {
910
15.6M
            if let Some(pixel) = pixel_iter.next() {
911
15.2M
                let rgb = palette[usize::from((idx & bit) != 0)];
912
15.2M
                pixel[0] = rgb[0];
913
15.2M
                pixel[1] = rgb[1];
914
15.2M
                pixel[2] = rgb[2];
915
15.2M
            } else {
916
439k
                return;
917
            }
918
919
15.2M
            bit >>= 1;
920
15.2M
            if bit == 0 {
921
1.66M
                break;
922
13.5M
            }
923
        }
924
    }
925
444k
}
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.32k
    const fn from_len_shift(len: u32, shift: u32) -> Self {
952
2.32k
        debug_assert!(len <= 8);
953
2.32k
        debug_assert!(shift + len <= 32);
954
2.32k
        Bitfield {
955
2.32k
            shift,
956
2.32k
            len,
957
2.32k
            factor_addend: Self::FACTOR_ADDEND[(len % 8) as usize],
958
2.32k
        }
959
2.32k
    }
960
961
2.40k
    fn from_mask(mask: u32, max_len: u32) -> ImageResult<Bitfield> {
962
2.40k
        if mask == 0 {
963
771
            return Ok(Bitfield::from_len_shift(0, 0));
964
1.63k
        }
965
1.63k
        let mut shift = mask.trailing_zeros();
966
1.63k
        let mut len = (!(mask >> shift)).trailing_zeros();
967
1.63k
        if len != mask.count_ones() {
968
69
            return Err(DecoderError::BitfieldMaskNonContiguous.into());
969
1.56k
        }
970
1.56k
        if len + shift > max_len {
971
15
            return Err(DecoderError::BitfieldMaskInvalid.into());
972
1.55k
        }
973
1.55k
        if len > 8 {
974
615
            shift += len - 8;
975
615
            len = 8;
976
935
        }
977
1.55k
        Ok(Bitfield::from_len_shift(len, shift))
978
2.40k
    }
979
980
    #[inline]
981
4.39M
    fn read(&self, data: u32) -> u8 {
982
4.39M
        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
4.39M
        let (factor, addend) = self.factor_addend;
987
4.39M
        let mask = (1 << self.len) - 1;
988
4.39M
        let data = (data >> self.shift) & mask;
989
4.39M
        ((data * factor + addend) >> 8) as u8
990
4.39M
    }
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
649
    fn from_mask(
1003
649
        r_mask: u32,
1004
649
        g_mask: u32,
1005
649
        b_mask: u32,
1006
649
        a_mask: u32,
1007
649
        max_len: u32,
1008
649
        spec_strictness: SpecCompliance,
1009
649
    ) -> ImageResult<Bitfields> {
1010
565
        let bitfields = Bitfields {
1011
649
            r: Bitfield::from_mask(r_mask, max_len)?,
1012
603
            g: Bitfield::from_mask(g_mask, max_len)?,
1013
586
            b: Bitfield::from_mask(b_mask, max_len)?,
1014
567
            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
565
        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
565
        }
1023
565
        Ok(bitfields)
1024
649
    }
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.71k
    fn new(reader: &'a mut R) -> Self {
1036
1.71k
        Self {
1037
1.71k
            reader,
1038
1.71k
            bytes_read: 0,
1039
1.71k
        }
1040
1.71k
    }
1041
1042
    /// Total bytes consumed since this reader was created.
1043
3.47M
    fn bytes_read(&self) -> u64 {
1044
3.47M
        self.bytes_read
1045
3.47M
    }
1046
1047
14.5M
    fn read_byte(&mut self) -> io::Result<u8> {
1048
14.5M
        let buf = self.reader.fill_buf()?;
1049
14.5M
        if buf.is_empty() {
1050
988
            return Err(io::Error::new(
1051
988
                io::ErrorKind::UnexpectedEof,
1052
988
                "unexpected end of RLE data",
1053
988
            ));
1054
14.5M
        }
1055
14.5M
        let byte = buf[0];
1056
14.5M
        self.reader.consume(1);
1057
14.5M
        self.bytes_read += 1;
1058
14.5M
        Ok(byte)
1059
14.5M
    }
1060
1061
28.8k
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
1062
28.8k
        let mut remaining = buf.len();
1063
28.8k
        let mut offset = 0;
1064
1065
57.7k
        while remaining > 0 {
1066
28.9k
            let available = self.reader.fill_buf()?;
1067
28.9k
            if available.is_empty() {
1068
136
                return Err(io::Error::new(
1069
136
                    io::ErrorKind::UnexpectedEof,
1070
136
                    "unexpected end of RLE data",
1071
136
                ));
1072
28.8k
            }
1073
1074
28.8k
            let to_read = remaining.min(available.len());
1075
28.8k
            buf[offset..offset + to_read].copy_from_slice(&available[..to_read]);
1076
28.8k
            self.reader.consume(to_read);
1077
28.8k
            self.bytes_read += to_read as u64;
1078
28.8k
            offset += to_read;
1079
28.8k
            remaining -= to_read;
1080
        }
1081
1082
28.7k
        Ok(())
1083
28.8k
    }
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.62k
    fn new_decoder(reader: R) -> BmpDecoder<R> {
1114
5.62k
        BmpDecoder {
1115
5.62k
            reader,
1116
5.62k
1117
5.62k
            bmp_header_type: BMPHeaderType::Info,
1118
5.62k
            indexed_color: false,
1119
5.62k
1120
5.62k
            width: 0,
1121
5.62k
            height: 0,
1122
5.62k
            data_offset: 0,
1123
5.62k
            top_down: false,
1124
5.62k
            no_file_header: false,
1125
5.62k
            add_alpha_channel: false,
1126
5.62k
            image_type: ImageType::Palette,
1127
5.62k
1128
5.62k
            bit_count: 0,
1129
5.62k
            colors_used: 0,
1130
5.62k
            palette: None,
1131
5.62k
            bitfields: None,
1132
5.62k
            icc_profile: None,
1133
5.62k
            spec_strictness: SpecCompliance::default(),
1134
5.62k
            state: DecoderState::default(),
1135
5.62k
        }
1136
5.62k
    }
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.84k
    pub(crate) fn new_with_spec_compliance(
1147
2.84k
        reader: R,
1148
2.84k
        spec: SpecCompliance,
1149
2.84k
    ) -> ImageResult<BmpDecoder<R>> {
1150
2.84k
        let mut decoder = Self::new_decoder(reader);
1151
2.84k
        decoder.spec_strictness = spec;
1152
2.84k
        decoder.read_metadata()?;
1153
2.25k
        Ok(decoder)
1154
2.84k
    }
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.78k
    pub(crate) fn new_with_ico_format(reader: R) -> ImageResult<BmpDecoder<R>> {
1222
2.78k
        let mut decoder = Self::new_decoder(reader);
1223
2.78k
        decoder.read_metadata_in_ico_format()?;
1224
2.11k
        Ok(decoder)
1225
2.78k
    }
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
535
    pub(crate) fn reader(&mut self) -> &mut R {
1235
535
        &mut self.reader
1236
535
    }
1237
1238
5.62k
    fn read_file_header(&mut self) -> ImageResult<()> {
1239
5.62k
        if self.no_file_header {
1240
2.78k
            return Ok(());
1241
2.84k
        }
1242
1243
        // Read entire 14-byte file header
1244
2.84k
        let mut buffer = [0u8; FILE_HEADER_SIZE as usize];
1245
2.84k
        self.reader.read_exact(&mut buffer)?;
1246
1247
        // Check signature
1248
2.81k
        if &buffer[0..2] != b"BM" {
1249
27
            return Err(DecoderError::BmpSignatureInvalid.into());
1250
2.79k
        }
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.79k
        let data_offset = u32::from_le_bytes([buffer[10], buffer[11], buffer[12], buffer[13]]);
1255
2.79k
        self.data_offset = u64::from(data_offset);
1256
1257
2.79k
        Ok(())
1258
5.62k
    }
1259
1260
    /// Determine the image type from the compression method, bit count, and header type.
1261
4.48k
    fn image_type_from_compression(
1262
4.48k
        compression: u32,
1263
4.48k
        bit_count: u16,
1264
4.48k
        add_alpha_channel: bool,
1265
4.48k
        header_type: &BMPHeaderType,
1266
4.48k
    ) -> ImageResult<ImageType> {
1267
42
        match compression {
1268
303
            BI_RGB => match bit_count {
1269
696
                1 | 2 | 4 | 8 => Ok(ImageType::Palette),
1270
267
                16 => Ok(ImageType::RGB16),
1271
285
                24 => Ok(ImageType::RGB24),
1272
58
                32 if add_alpha_channel => Ok(ImageType::RGBA32),
1273
245
                32 => Ok(ImageType::RGB32),
1274
                _ => {
1275
49
                    Err(DecoderError::InvalidChannelWidth(ChannelWidthError::Rgb, bit_count).into())
1276
                }
1277
            },
1278
544
            BI_RLE8 => match bit_count {
1279
509
                8 => Ok(ImageType::RLE8),
1280
35
                _ => Err(
1281
35
                    DecoderError::InvalidChannelWidth(ChannelWidthError::Rle8, bit_count).into(),
1282
35
                ),
1283
            },
1284
759
            BI_RLE4 => match bit_count {
1285
740
                4 => Ok(ImageType::RLE4),
1286
19
                _ => Err(
1287
19
                    DecoderError::InvalidChannelWidth(ChannelWidthError::Rle4, bit_count).into(),
1288
19
                ),
1289
            },
1290
671
            BI_BITFIELDS | BI_ALPHABITFIELDS => match bit_count {
1291
145
                16 => Ok(ImageType::Bitfields16),
1292
524
                32 => Ok(ImageType::Bitfields32),
1293
2
                _ => Err(DecoderError::InvalidChannelWidth(
1294
2
                    ChannelWidthError::Bitfields,
1295
2
                    bit_count,
1296
2
                )
1297
2
                .into()),
1298
            },
1299
896
            BI_JPEG if *header_type == BMPHeaderType::Os2V2 && bit_count == 24 => {
1300
854
                Ok(ImageType::RLE24)
1301
            }
1302
42
            BI_JPEG if *header_type == BMPHeaderType::Os2V2 => {
1303
36
                Err(DecoderError::InvalidChannelWidth(ChannelWidthError::Rle24, bit_count).into())
1304
            }
1305
6
            BI_JPEG => Err(ImageError::Unsupported(
1306
6
                UnsupportedError::from_format_and_kind(
1307
6
                    ImageFormat::Bmp.into(),
1308
6
                    UnsupportedErrorKind::GenericFeature("JPEG compression".to_owned()),
1309
6
                ),
1310
6
            )),
1311
3
            BI_PNG => Err(ImageError::Unsupported(
1312
3
                UnsupportedError::from_format_and_kind(
1313
3
                    ImageFormat::Bmp.into(),
1314
3
                    UnsupportedErrorKind::GenericFeature("PNG compression".to_owned()),
1315
3
                ),
1316
3
            )),
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
10
            _ => Err(DecoderError::ImageTypeUnknown(compression).into()),
1324
        }
1325
4.48k
    }
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
705
    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
705
        let mut buffer = [0u8; 8];
1333
705
        self.reader.read_exact(&mut buffer)?;
1334
1335
698
        let parsed = ParsedCoreHeader::parse(&buffer, self.spec_strictness)?;
1336
1337
688
        self.width = parsed.width;
1338
688
        self.height = parsed.height;
1339
688
        self.bit_count = parsed.bit_count;
1340
688
        self.image_type = parsed.image_type;
1341
1342
688
        check_for_overflow(self.width, self.height, self.num_channels())?;
1343
1344
680
        Ok(())
1345
705
    }
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.11k
    fn read_bitmap_os2v2_header(&mut self, header_size: u32) -> ImageResult<()> {
1350
2.11k
        let remaining = (header_size - 4) as usize;
1351
1352
        // Zero-pad to 36 bytes for ParsedInfoHeader::parse.
1353
2.11k
        let mut buffer = [0u8; 36];
1354
2.11k
        let to_read = remaining.min(36);
1355
2.11k
        self.reader.read_exact(&mut buffer[..to_read])?;
1356
1357
        // Skip OS/2-specific fields beyond the BITMAPINFOHEADER portion (max 28 bytes).
1358
2.09k
        if remaining > 36 {
1359
290
            let skip = remaining - 36;
1360
290
            let mut discard = [0u8; 28];
1361
290
            self.reader.read_exact(&mut discard[..skip])?;
1362
1.80k
        }
1363
1364
2.08k
        let parsed = ParsedInfoHeader::parse(&buffer, self.spec_strictness)?;
1365
1366
1.95k
        self.width = parsed.width;
1367
1.95k
        self.height = parsed.height;
1368
1.95k
        self.top_down = parsed.top_down;
1369
1.95k
        self.bit_count = parsed.bit_count;
1370
1.95k
        self.colors_used = parsed.colors_used;
1371
1.95k
        self.image_type = Self::image_type_from_compression(
1372
1.95k
            parsed.compression,
1373
1.95k
            parsed.bit_count,
1374
1.95k
            self.add_alpha_channel,
1375
1.95k
            &self.bmp_header_type,
1376
141
        )?;
1377
1378
1.81k
        check_for_overflow(self.width, self.height, self.num_channels())?;
1379
1380
1.81k
        Ok(())
1381
2.11k
    }
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.56k
    fn read_bitmap_info_header(&mut self) -> ImageResult<BitfieldCompression> {
1388
        // Info header (after size field): 36 bytes minimum
1389
2.56k
        let mut buffer = [0u8; 36];
1390
2.56k
        self.reader.read_exact(&mut buffer)?;
1391
1392
2.53k
        let parsed = ParsedInfoHeader::parse(&buffer, self.spec_strictness)?;
1393
1394
2.53k
        self.width = parsed.width;
1395
2.53k
        self.height = parsed.height;
1396
2.53k
        self.top_down = parsed.top_down;
1397
2.53k
        self.bit_count = parsed.bit_count;
1398
2.53k
        self.colors_used = parsed.colors_used;
1399
2.53k
        self.image_type = Self::image_type_from_compression(
1400
2.53k
            parsed.compression,
1401
2.53k
            parsed.bit_count,
1402
2.53k
            self.add_alpha_channel,
1403
2.53k
            &self.bmp_header_type,
1404
25
        )?;
1405
1406
2.50k
        check_for_overflow(self.width, self.height, self.num_channels())?;
1407
1408
2.50k
        let compression = match parsed.compression {
1409
54
            BI_ALPHABITFIELDS => BitfieldCompression::Rgba,
1410
2.45k
            _ => BitfieldCompression::Rgb,
1411
        };
1412
2.50k
        Ok(compression)
1413
2.56k
    }
1414
1415
668
    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
668
        let has_alpha = matches!(
1420
668
            self.bmp_header_type,
1421
            BMPHeaderType::V3 | BMPHeaderType::V4 | BMPHeaderType::V5
1422
370
        ) || compression == BitfieldCompression::Rgba;
1423
1424
        // Read bitfield masks into buffer
1425
668
        let buffer_size = if has_alpha { 16 } else { 12 };
1426
668
        let mut buffer = vec![0u8; buffer_size];
1427
668
        self.reader.read_exact(&mut buffer)?;
1428
1429
        // Parse masks using shared logic
1430
649
        let parsed = ParsedBitfields::parse(&buffer, has_alpha);
1431
1432
        // Create Bitfields from parsed masks
1433
649
        self.bitfields = match self.image_type {
1434
            ImageType::Bitfields16 | ImageType::Bitfields32 => {
1435
649
                let max_len = match self.image_type {
1436
137
                    ImageType::Bitfields16 => 16,
1437
512
                    ImageType::Bitfields32 => 32,
1438
0
                    _ => unreachable!(),
1439
                };
1440
649
                Some(Bitfields::from_mask(
1441
649
                    parsed.r_mask,
1442
649
                    parsed.g_mask,
1443
649
                    parsed.b_mask,
1444
649
                    parsed.a_mask,
1445
649
                    max_len,
1446
649
                    self.spec_strictness,
1447
84
                )?)
1448
            }
1449
0
            _ => None,
1450
        };
1451
1452
565
        if self.bitfields.is_some() && parsed.a_mask != 0 {
1453
292
            self.add_alpha_channel = true;
1454
292
        }
1455
1456
565
        Ok(())
1457
668
    }
1458
1459
    /// Read ICC profile data from the file.
1460
137
    fn read_icc_profile(&mut self, icc: &ParsedIccProfile) -> ImageResult<()> {
1461
137
        self.reader.seek(SeekFrom::Start(icc.profile_offset))?;
1462
137
        let mut profile_data = vec![0u8; icc.profile_size as usize];
1463
137
        self.reader.read_exact(&mut profile_data)?;
1464
68
        self.icc_profile = Some(profile_data);
1465
68
        Ok(())
1466
137
    }
1467
1468
    /// Read BMP metadata (headers, palette, etc.).
1469
    ///
1470
    /// On `UnexpectedEof`, the decoder can be retried - the implementation tracks
1471
    /// progress and resumes from where it left off. Once successful, subsequent
1472
    /// calls are no-ops.
1473
    ///
1474
    /// Metadata reading is divided into phases:
1475
    /// 1. Headers: File header, DIB header, and bitmasks (~30-150 bytes).
1476
    ///    These are re-read together on retry since they're small.
1477
    /// 2. Palette: Up to 1KB for indexed color images.
1478
    /// 3. ICC profile: Variable size, can be several KB (V5 headers only).
1479
5.62k
    pub fn read_metadata(&mut self) -> ImageResult<()> {
1480
        // Check if we're in a metadata reading state
1481
5.62k
        let DecoderState::ReadingMetadata { progress } = self.state else {
1482
0
            return Ok(()); // Already past metadata phase
1483
        };
1484
1485
5.62k
        match self.read_metadata_impl(progress) {
1486
            Ok(()) => {
1487
                // Transition directly to the appropriate image reading state
1488
4.37k
                self.state = if self.is_rle() {
1489
1.93k
                    DecoderState::ReadingRleData {
1490
1.93k
                        progress: RleProgress::NotStarted,
1491
1.93k
                    }
1492
                } else {
1493
2.44k
                    DecoderState::ReadingRowData { rows_decoded: 0 }
1494
                };
1495
4.37k
                Ok(())
1496
            }
1497
1.25k
            Err(e) => Err(e),
1498
        }
1499
5.62k
    }
1500
1501
    /// Internal implementation of metadata reading with phased resumability.
1502
    ///
1503
    /// Uses recursive calls to progress through phases. Each phase either:
1504
    /// - Succeeds and calls the next phase
1505
    /// - Fails with an error (which may be retryable like UnexpectedEof)
1506
    ///
1507
    /// Recursion depth is bounded (max 4): NotStarted → ReadingMainHeader → ReadingPalette → ReadingIccProfile → Complete
1508
24.9k
    fn read_metadata_impl(&mut self, progress: MetadataProgress) -> ImageResult<()> {
1509
24.9k
        match progress {
1510
            MetadataProgress::NotStarted => {
1511
                // Record current position and transition to ReadingMainHeader
1512
5.62k
                let start_offset = self.reader.stream_position()?;
1513
5.62k
                let next = MetadataProgress::ReadingMainHeader { start_offset };
1514
5.62k
                self.state = DecoderState::ReadingMetadata { progress: next };
1515
5.62k
                self.read_metadata_impl(next)
1516
            }
1517
5.62k
            MetadataProgress::ReadingMainHeader { start_offset } => {
1518
                // Seek to start position (for retry support)
1519
5.62k
                self.reader.seek(SeekFrom::Start(start_offset))?;
1520
1521
                // Read headers and get offsets for subsequent phases
1522
5.62k
                let offsets = self.read_headers()?;
1523
1524
                // Always progress to ReadingPalette next
1525
4.82k
                let next = MetadataProgress::ReadingPalette { offsets };
1526
4.82k
                self.state = DecoderState::ReadingMetadata { progress: next };
1527
4.82k
                self.read_metadata_impl(next)
1528
            }
1529
4.82k
            MetadataProgress::ReadingPalette { offsets } => {
1530
                // Always seek to palette position (this is also where image data starts
1531
                // for non-palette formats)
1532
4.82k
                self.reader.seek(SeekFrom::Start(offsets.palette_offset))?;
1533
1534
                // Read palette if needed for this image type
1535
2.37k
                if matches!(
1536
4.82k
                    self.image_type,
1537
                    ImageType::Palette | ImageType::RLE4 | ImageType::RLE8
1538
                ) {
1539
2.44k
                    self.read_palette()?;
1540
2.37k
                }
1541
1542
                // For no_file_header mode, capture data_offset now (after palette read)
1543
                // before ICC profile reading potentially changes reader position.
1544
                // For normal mode, clamp data_offset if it points into the DIB header
1545
                // (between FILE_HEADER_SIZE and bmp_header_end). Such values are invalid
1546
                // because they overlap with header data.
1547
4.44k
                if self.no_file_header {
1548
2.13k
                    self.data_offset = self.reader.stream_position()?;
1549
2.30k
                } else if self.spec_strictness != SpecCompliance::Strict
1550
2.30k
                    && self.data_offset >= FILE_HEADER_SIZE
1551
895
                    && self.data_offset < offsets.bmp_header_end
1552
182
                {
1553
182
                    self.data_offset = offsets.bmp_header_end;
1554
2.12k
                }
1555
1556
                // Always progress to ReadingIccProfile next
1557
4.44k
                let next = MetadataProgress::ReadingIccProfile { offsets };
1558
4.44k
                self.state = DecoderState::ReadingMetadata { progress: next };
1559
4.44k
                self.read_metadata_impl(next)
1560
            }
1561
4.44k
            MetadataProgress::ReadingIccProfile { offsets } => {
1562
                // Read ICC profile if present
1563
4.44k
                if let Some(ref icc) = offsets.icc_profile {
1564
137
                    self.read_icc_profile(icc)?;
1565
4.30k
                }
1566
1567
                // Always progress to Complete next
1568
4.37k
                self.state = DecoderState::ReadingMetadata {
1569
4.37k
                    progress: MetadataProgress::Complete,
1570
4.37k
                };
1571
4.37k
                self.read_metadata_impl(MetadataProgress::Complete)
1572
            }
1573
4.37k
            MetadataProgress::Complete => Ok(()),
1574
        }
1575
24.9k
    }
1576
1577
    /// Read headers phase: file header, DIB header, and bitmasks.
1578
    /// Returns HeaderOffsets containing positions for subsequent phases.
1579
5.62k
    fn read_headers(&mut self) -> ImageResult<HeaderOffsets> {
1580
5.62k
        self.read_file_header()?;
1581
5.57k
        let bmp_header_offset = self.reader.stream_position()?;
1582
1583
        // Read header size into buffer for consistency with buffer-based pattern
1584
5.57k
        let mut size_buffer = [0u8; 4];
1585
5.57k
        self.reader.read_exact(&mut size_buffer)?;
1586
5.57k
        let bmp_header_size = u32::from_le_bytes(size_buffer);
1587
1588
5.57k
        let bmp_header_end = bmp_header_offset + u64::from(bmp_header_size);
1589
1590
2.29k
        self.bmp_header_type = match bmp_header_size {
1591
705
            BITMAPCOREHEADER_SIZE => BMPHeaderType::Core,
1592
1.37k
            BITMAPINFOHEADER_SIZE => BMPHeaderType::Info,
1593
67
            BITMAPV2HEADER_SIZE => BMPHeaderType::V2,
1594
303
            BITMAPV3HEADER_SIZE => BMPHeaderType::V3,
1595
377
            BITMAPV4HEADER_SIZE => BMPHeaderType::V4,
1596
438
            BITMAPV5HEADER_SIZE => BMPHeaderType::V5,
1597
2.30k
            _ if bmp_header_size < BITMAPCOREHEADER_SIZE => {
1598
                // Size of any valid header types won't be smaller than core header type.
1599
16
                return Err(DecoderError::HeaderTooSmall(bmp_header_size).into());
1600
            }
1601
            // OS/2 BITMAPCOREHEADER2 (OS22XBITMAPHEADER): 16-64 bytes, 4-byte aligned
1602
            // (plus special sizes 42 and 46). Sizes 40/52/56 are caught by exact arms
1603
            // above and decoded as Windows headers (layout-compatible, so this is fine;
1604
            // the only difference is that a 40-byte OS/2 header with RLE24 won't trigger
1605
            // the Os2V2 path, but that combination is effectively nonexistent).
1606
2.29k
            _ if (OS2_V2_MIN_HEADER_SIZE..=OS2_V2_MAX_HEADER_SIZE).contains(&bmp_header_size)
1607
2.12k
                && (bmp_header_size % 4 == 0 || bmp_header_size == 42 || bmp_header_size == 46) =>
1608
            {
1609
2.11k
                BMPHeaderType::Os2V2
1610
            }
1611
            _ => {
1612
173
                return Err(ImageError::Unsupported(
1613
173
                    UnsupportedError::from_format_and_kind(
1614
173
                        ImageFormat::Bmp.into(),
1615
173
                        UnsupportedErrorKind::GenericFeature(format!(
1616
173
                            "Unknown bitmap header type (size={bmp_header_size})"
1617
173
                        )),
1618
173
                    ),
1619
173
                ))
1620
            }
1621
        };
1622
1623
5.38k
        let bitfield_compression = match self.bmp_header_type {
1624
            BMPHeaderType::Core => {
1625
705
                self.read_bitmap_core_header()?;
1626
680
                BitfieldCompression::Rgb
1627
            }
1628
            BMPHeaderType::Os2V2 => {
1629
2.11k
                self.read_bitmap_os2v2_header(bmp_header_size)?;
1630
1.81k
                BitfieldCompression::Rgb
1631
            }
1632
            BMPHeaderType::Info
1633
            | BMPHeaderType::V2
1634
            | BMPHeaderType::V3
1635
            | BMPHeaderType::V4
1636
2.56k
            | BMPHeaderType::V5 => self.read_bitmap_info_header()?,
1637
        };
1638
1639
4.99k
        let mut bitmask_bytes_offset = 0;
1640
4.32k
        if matches!(
1641
4.99k
            self.image_type,
1642
            ImageType::Bitfields16 | ImageType::Bitfields32
1643
        ) {
1644
668
            self.read_bitmasks(bitfield_compression)?;
1645
1646
            // Per https://learn.microsoft.com/en-us/windows/win32/gdi/bitmap-header-types, bitmaps
1647
            // using the `BITMAPINFOHEADER`, `BITMAPV4HEADER`, or `BITMAPV5HEADER` structures with
1648
            // an image type of `BI_BITFIELD` or `BI_ALPHABITFIELDS` contain bitfield masks
1649
            // immediately after the header.
1650
            //
1651
            // `read_bitmasks` correctly reads these from earlier in the header itself but we must
1652
            // ensure the reader starts on the image data itself, not these extra mask bytes.
1653
401
            if matches!(
1654
565
                self.bmp_header_type,
1655
                BMPHeaderType::Info | BMPHeaderType::V4 | BMPHeaderType::V5
1656
            ) {
1657
164
                bitmask_bytes_offset = match bitfield_compression {
1658
27
                    BitfieldCompression::Rgba => 16, // 4 masks * 4 bytes
1659
137
                    BitfieldCompression::Rgb => 12,  // 3 masks * 4 bytes
1660
                };
1661
401
            }
1662
4.32k
        } else if self.image_type == ImageType::RGB32 && bmp_header_size >= BITMAPV4HEADER_SIZE {
1663
            // V4/V5 headers may declare an alpha channel via alpha_mask even under BI_RGB.
1664
172
            let mut masks_buf = [0u8; 16];
1665
172
            self.reader.read_exact(&mut masks_buf)?;
1666
165
            let alpha_mask = u32::from_le_bytes(masks_buf[12..16].try_into().unwrap());
1667
165
            if alpha_mask != 0 {
1668
                // BI_RGB implies fixed BGRA byte layout, so the only spec-valid
1669
                // alpha mask is 0xFF000000. In lenient mode we still treat any
1670
                // non-zero alpha_mask as "alpha present" because some encoders
1671
                // (e.g. older GDI+ versions) write incorrect mask values while
1672
                // still storing alpha in the high byte.
1673
148
                if self.spec_strictness == SpecCompliance::Strict && alpha_mask != 0xFF000000 {
1674
0
                    return Err(DecoderError::BitfieldMaskInvalid.into());
1675
148
                }
1676
148
                self.add_alpha_channel = true;
1677
148
                self.image_type = ImageType::RGBA32;
1678
17
            }
1679
4.15k
        };
1680
1681
        // Parse color space fields from V4/V5 header
1682
4.88k
        let mut icc_profile = None;
1683
4.88k
        if bmp_header_size >= BITMAPV4HEADER_SIZE {
1684
            // Read the header into a buffer for color space parsing.
1685
            // Buffer starts after the 4-byte size field.
1686
789
            let mut header_buffer = vec![0u8; (bmp_header_size - 4) as usize];
1687
789
            let current_pos = self.reader.stream_position()?;
1688
789
            self.reader.seek(SeekFrom::Start(bmp_header_offset + 4))?;
1689
789
            self.reader.read_exact(&mut header_buffer)?;
1690
1691
            // Extract color space info and handle non-Copy variants immediately
1692
723
            match ColorSpaceInfo::parse(&header_buffer, bmp_header_size, bmp_header_offset) {
1693
93
                Some(ColorSpaceInfo::CalibratedRgb(params)) => {
1694
                    // Synthesize an ICC profile from the calibrated RGB parameters
1695
                    // and store it directly — no file read needed.
1696
93
                    if let Ok(encoded) = params.to_color_profile().encode() {
1697
93
                        self.icc_profile = Some(encoded);
1698
93
                    }
1699
                }
1700
192
                Some(ColorSpaceInfo::EmbeddedIcc(icc)) => {
1701
192
                    icc_profile = Some(icc);
1702
192
                }
1703
                // LCS_sRGB / LCS_WINDOWS_COLOR_SPACE: the caller treats
1704
                // "no ICC profile" as sRGB, so nothing to store.
1705
438
                Some(ColorSpaceInfo::Srgb) | None => {}
1706
            }
1707
1708
            // Seek back to where we were
1709
723
            self.reader.seek(SeekFrom::Start(current_pos))?;
1710
4.09k
        }
1711
1712
        // Calculate palette offset (position after headers)
1713
4.82k
        let palette_offset = bmp_header_end + bitmask_bytes_offset;
1714
1715
4.82k
        Ok(HeaderOffsets {
1716
4.82k
            bmp_header_end,
1717
4.82k
            palette_offset,
1718
4.82k
            icc_profile,
1719
4.82k
        })
1720
5.62k
    }
1721
1722
    #[cfg(feature = "ico")]
1723
    #[doc(hidden)]
1724
2.78k
    pub fn read_metadata_in_ico_format(&mut self) -> ImageResult<()> {
1725
2.78k
        self.no_file_header = true;
1726
2.78k
        self.add_alpha_channel = true;
1727
2.78k
        self.read_metadata()?;
1728
1729
        // The height field in an ICO file is doubled to account for the AND mask
1730
        // (whether or not an AND mask is actually present).
1731
2.11k
        self.height /= 2;
1732
2.11k
        Ok(())
1733
2.78k
    }
1734
1735
2.44k
    fn get_palette_size(&mut self) -> ImageResult<usize> {
1736
2.44k
        match self.colors_used {
1737
896
            0 => Ok(1 << self.bit_count),
1738
            _ => {
1739
1.55k
                if self.spec_strictness == SpecCompliance::Strict
1740
0
                    && self.colors_used > 1 << self.bit_count
1741
                {
1742
0
                    return Err(DecoderError::PaletteSizeExceeded {
1743
0
                        colors_used: self.colors_used,
1744
0
                        bit_count: self.bit_count,
1745
0
                    }
1746
0
                    .into());
1747
1.55k
                }
1748
                // In lenient mode, clamp to max palette size for the bit depth
1749
1.55k
                let max_size = 1usize << self.bit_count;
1750
1.55k
                Ok((self.colors_used as usize).min(max_size))
1751
            }
1752
        }
1753
2.44k
    }
1754
1755
2.44k
    fn bytes_per_color(&self) -> usize {
1756
2.44k
        match self.bmp_header_type {
1757
510
            BMPHeaderType::Core => 3,
1758
1.93k
            _ => 4,
1759
        }
1760
2.44k
    }
1761
1762
2.44k
    fn read_palette(&mut self) -> ImageResult<()> {
1763
        const MAX_PALETTE_SIZE: usize = 256; // Palette indices are u8.
1764
1765
2.44k
        let bytes_per_color = self.bytes_per_color();
1766
2.44k
        let palette_size = self.get_palette_size()?;
1767
2.44k
        let max_length = MAX_PALETTE_SIZE * bytes_per_color;
1768
1769
2.44k
        let length = palette_size * bytes_per_color;
1770
2.44k
        let mut buf = vec_try_with_capacity(max_length)?;
1771
1772
        // Resize and read the palette entries to the buffer.
1773
        // We limit the buffer to at most 256 colours to avoid any oom issues as
1774
        // 8-bit images can't reference more than 256 indexes anyhow.
1775
2.44k
        buf.resize(cmp::min(length, max_length), 0);
1776
2.44k
        self.reader.by_ref().read_exact(&mut buf)?;
1777
1778
        // Allocate 256 entries even if palette_size is smaller, to prevent corrupt files from
1779
        // causing an out-of-bounds array access.
1780
2.07k
        match length.cmp(&max_length) {
1781
0
            Ordering::Greater => self.reader.seek_relative((length - max_length) as i64)?,
1782
2.03k
            Ordering::Less => buf.resize(max_length, 0),
1783
41
            Ordering::Equal => (),
1784
        }
1785
1786
2.07k
        let p: Vec<[u8; 3]> = (0..MAX_PALETTE_SIZE)
1787
530k
            .map(|i| {
1788
530k
                let b = buf[bytes_per_color * i];
1789
530k
                let g = buf[bytes_per_color * i + 1];
1790
530k
                let r = buf[bytes_per_color * i + 2];
1791
530k
                [r, g, b]
1792
530k
            })
1793
2.07k
            .collect();
1794
1795
2.07k
        self.palette = Some(p);
1796
1797
2.07k
        Ok(())
1798
2.44k
    }
1799
1800
    /// Get the palette that is embedded in the BMP image, if any.
1801
0
    pub fn get_palette(&self) -> Option<&[[u8; 3]]> {
1802
0
        self.palette.as_ref().map(|vec| &vec[..])
1803
0
    }
1804
1805
10.5k
    fn num_channels(&self) -> usize {
1806
10.5k
        if self.indexed_color {
1807
0
            1
1808
10.5k
        } else if self.add_alpha_channel {
1809
5.74k
            4
1810
        } else {
1811
4.76k
            3
1812
        }
1813
10.5k
    }
1814
1815
1.71k
    fn rows<'a>(&self, pixel_data: &'a mut [u8]) -> RowIterator<'a> {
1816
1.71k
        let stride = self.width as usize * self.num_channels();
1817
1.71k
        if self.top_down {
1818
303
            RowIterator {
1819
303
                chunks: Chunker::FromTop(pixel_data.chunks_exact_mut(stride)),
1820
303
            }
1821
        } else {
1822
1.41k
            RowIterator {
1823
1.41k
                chunks: Chunker::FromBottom(pixel_data.chunks_exact_mut(stride).rev()),
1824
1.41k
            }
1825
        }
1826
1.71k
    }
1827
1828
904
    fn read_palettized_pixel_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
1829
904
        let num_channels = self.num_channels();
1830
904
        let row_byte_length = ((i32::from(self.bit_count) * self.width + 31) / 32 * 4) as usize;
1831
904
        let mut indices = vec![0; row_byte_length];
1832
904
        let palette = self.palette.as_ref().unwrap();
1833
904
        let bit_count = self.bit_count;
1834
904
        let width = self.width as usize;
1835
904
        let skip_palette = self.indexed_color;
1836
1837
904
        let rows_decoded = self.rows_decoded();
1838
904
        let start_row = rows_decoded.rows();
1839
904
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
1840
1841
904
        let file_offset = self.data_offset + (start_row as u64 * row_byte_length as u64);
1842
904
        self.reader.seek(SeekFrom::Start(file_offset))?;
1843
1844
        // Set alpha to opaque for all pixels if needed (only on first call)
1845
904
        if start_row == 0 && num_channels == 4 {
1846
463
            buf.as_chunks_mut::<4>()
1847
463
                .0
1848
463
                .iter_mut()
1849
2.06G
                .for_each(|c| c[3] = ALPHA_OPAQUE);
1850
441
        }
1851
1852
904
        let spec_strictness = self.spec_strictness;
1853
904
        let last_row: u32 = (self.height - 1).try_into().unwrap();
1854
904
        let mut current_file_row = start_row;
1855
904
        let reader = &mut self.reader;
1856
904
        let result = with_rows_resumable(
1857
904
            buf,
1858
904
            self.width,
1859
904
            self.height,
1860
904
            num_channels,
1861
904
            top_down,
1862
904
            start_row,
1863
562k
            |row| {
1864
562k
                read_scanline(
1865
562k
                    reader,
1866
562k
                    &mut indices,
1867
562k
                    &mut current_file_row,
1868
562k
                    last_row,
1869
562k
                    spec_strictness,
1870
748
                )?;
1871
561k
                if skip_palette {
1872
0
                    row.clone_from_slice(&indices[0..width]);
1873
0
                } else {
1874
561k
                    let mut pixel_iter = row.chunks_exact_mut(num_channels);
1875
561k
                    match bit_count {
1876
444k
                        1 => {
1877
444k
                            set_1bit_pixel_run(&mut pixel_iter, palette, indices.iter());
1878
444k
                        }
1879
6.14k
                        2 => {
1880
6.14k
                            set_2bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
1881
6.14k
                        }
1882
9.25k
                        4 => {
1883
9.25k
                            set_4bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
1884
9.25k
                        }
1885
101k
                        8 => {
1886
101k
                            set_8bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
1887
101k
                        }
1888
0
                        _ => panic!(),
1889
                    }
1890
                }
1891
561k
                Ok(())
1892
562k
            },
1893
        );
1894
1895
904
        self.finish_row_decode(result)
1896
904
    }
1897
1898
316
    fn read_16_bit_pixel_data(
1899
316
        &mut self,
1900
316
        buf: &mut [u8],
1901
316
        bitfields: Option<&Bitfields>,
1902
316
    ) -> ImageResult<()> {
1903
316
        let num_channels = self.num_channels();
1904
316
        let bitfields = match bitfields {
1905
224
            Some(b) => b,
1906
92
            None => self.bitfields.as_ref().unwrap(),
1907
        };
1908
1909
316
        let row_data_len = self.width as usize * 2;
1910
316
        let row_padding_len = calculate_row_padding(row_data_len);
1911
316
        let total_row_len = row_data_len + row_padding_len;
1912
1913
316
        let rows_decoded = self.rows_decoded();
1914
316
        let start_row = rows_decoded.rows();
1915
316
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
1916
316
        let width = self.width;
1917
316
        let height = self.height;
1918
1919
316
        let file_offset = self.data_offset + (start_row as u64 * total_row_len as u64);
1920
316
        self.reader.seek(SeekFrom::Start(file_offset))?;
1921
1922
316
        let mut row_buffer = allocate_row_buffer(total_row_len)?;
1923
1924
316
        let spec_strictness = self.spec_strictness;
1925
316
        let last_row: u32 = (height - 1).try_into().unwrap();
1926
316
        let mut current_file_row = start_row;
1927
316
        let reader = &mut self.reader;
1928
316
        let result = with_rows_resumable(
1929
316
            buf,
1930
316
            width,
1931
316
            height,
1932
316
            num_channels,
1933
316
            top_down,
1934
316
            start_row,
1935
58.7k
            |row| {
1936
58.7k
                read_scanline(
1937
58.7k
                    reader,
1938
58.7k
                    &mut row_buffer,
1939
58.7k
                    &mut current_file_row,
1940
58.7k
                    last_row,
1941
58.7k
                    spec_strictness,
1942
282
                )?;
1943
58.4k
                let row_buffer_chunks = row_buffer.as_chunks::<2>().0.iter();
1944
620k
                for (&row_data, pixel) in row_buffer_chunks.zip(row.chunks_exact_mut(num_channels))
1945
                {
1946
620k
                    let data = u32::from(u16::from_le_bytes(row_data));
1947
620k
                    pixel[0] = bitfields.r.read(data);
1948
620k
                    pixel[1] = bitfields.g.read(data);
1949
620k
                    pixel[2] = bitfields.b.read(data);
1950
620k
                    if num_channels == 4 {
1951
573k
                        pixel[3] = if bitfields.a.len != 0 {
1952
490k
                            bitfields.a.read(data)
1953
                        } else {
1954
82.6k
                            ALPHA_OPAQUE
1955
                        };
1956
46.6k
                    }
1957
                }
1958
58.4k
                Ok(())
1959
58.7k
            },
1960
        );
1961
1962
316
        self.finish_row_decode(result)
1963
316
    }
1964
1965
    /// Read image data from a reader in 32-bit formats that use bitfields.
1966
359
    fn read_32_bit_pixel_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
1967
359
        let num_channels = self.num_channels();
1968
359
        let bitfields = self.bitfields.as_ref().unwrap();
1969
1970
359
        let row_data_len = self.width as usize * 4;
1971
1972
359
        let rows_decoded = self.rows_decoded();
1973
359
        let start_row = rows_decoded.rows();
1974
359
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
1975
359
        let width = self.width;
1976
359
        let height = self.height;
1977
1978
359
        let file_offset = self.data_offset + (start_row as u64 * row_data_len as u64);
1979
359
        self.reader.seek(SeekFrom::Start(file_offset))?;
1980
1981
359
        let mut row_buffer = allocate_row_buffer(row_data_len)?;
1982
1983
359
        let spec_strictness = self.spec_strictness;
1984
359
        let last_row: u32 = (height - 1).try_into().unwrap();
1985
359
        let mut current_file_row = start_row;
1986
359
        let reader = &mut self.reader;
1987
359
        let result = with_rows_resumable(
1988
359
            buf,
1989
359
            width,
1990
359
            height,
1991
359
            num_channels,
1992
359
            top_down,
1993
359
            start_row,
1994
8.73k
            |row| {
1995
8.73k
                read_scanline(
1996
8.73k
                    reader,
1997
8.73k
                    &mut row_buffer,
1998
8.73k
                    &mut current_file_row,
1999
8.73k
                    last_row,
2000
8.73k
                    spec_strictness,
2001
334
                )?;
2002
8.40k
                let row_buffer_chunks = row_buffer.as_chunks::<4>().0.iter();
2003
544k
                for (&row_data, pixel) in row_buffer_chunks.zip(row.chunks_exact_mut(num_channels))
2004
                {
2005
544k
                    let data = u32::from_le_bytes(row_data);
2006
544k
                    pixel[0] = bitfields.r.read(data);
2007
544k
                    pixel[1] = bitfields.g.read(data);
2008
544k
                    pixel[2] = bitfields.b.read(data);
2009
544k
                    if num_channels == 4 {
2010
435k
                        pixel[3] = if bitfields.a.len != 0 {
2011
412k
                            bitfields.a.read(data)
2012
                        } else {
2013
22.3k
                            ALPHA_OPAQUE
2014
                        };
2015
109k
                    }
2016
                }
2017
8.40k
                Ok(())
2018
8.73k
            },
2019
        );
2020
2021
359
        self.finish_row_decode(result)
2022
359
    }
2023
2024
    /// Read image data from a reader where the colours are stored as 8-bit values (24 or 32-bit).
2025
489
    fn read_full_byte_pixel_data(
2026
489
        &mut self,
2027
489
        buf: &mut [u8],
2028
489
        format: &FormatFullBytes,
2029
489
    ) -> ImageResult<()> {
2030
489
        let num_channels = self.num_channels();
2031
489
        let row_data_len = match *format {
2032
304
            FormatFullBytes::RGB24 => self.width as usize * 3,
2033
17
            FormatFullBytes::Format888 => self.width as usize * 4,
2034
168
            FormatFullBytes::RGB32 | FormatFullBytes::RGBA32 => self.width as usize * 4,
2035
        };
2036
489
        let row_padding_len = match *format {
2037
304
            FormatFullBytes::RGB24 => calculate_row_padding(row_data_len),
2038
185
            _ => 0,
2039
        };
2040
489
        let total_row_len = row_data_len + row_padding_len;
2041
2042
489
        let rows_decoded = self.rows_decoded();
2043
489
        let start_row = rows_decoded.rows();
2044
489
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
2045
489
        let width = self.width;
2046
489
        let height = self.height;
2047
2048
489
        let file_offset = self.data_offset + (start_row as u64 * total_row_len as u64);
2049
489
        self.reader.seek(SeekFrom::Start(file_offset))?;
2050
2051
489
        let mut row_buffer = allocate_row_buffer(total_row_len)?;
2052
2053
489
        let spec_strictness = self.spec_strictness;
2054
489
        let last_row: u32 = (height - 1).try_into().unwrap();
2055
489
        let mut current_file_row = start_row;
2056
489
        let reader = &mut self.reader;
2057
489
        let result = with_rows_resumable(
2058
489
            buf,
2059
489
            width,
2060
489
            height,
2061
489
            num_channels,
2062
489
            top_down,
2063
489
            start_row,
2064
27.0k
            |row| {
2065
27.0k
                read_scanline(
2066
27.0k
                    reader,
2067
27.0k
                    &mut row_buffer,
2068
27.0k
                    &mut current_file_row,
2069
27.0k
                    last_row,
2070
27.0k
                    spec_strictness,
2071
433
                )?;
2072
2073
825k
                for (i, pixel) in row.chunks_mut(num_channels).enumerate() {
2074
825k
                    let offset = match *format {
2075
396
                        FormatFullBytes::Format888 => i * 4 + 1, // Skip first byte
2076
                        _ => {
2077
825k
                            i * match *format {
2078
519k
                                FormatFullBytes::RGB24 => 3,
2079
306k
                                _ => 4,
2080
                            }
2081
                        }
2082
                    };
2083
2084
                    // Read the colour values (b, g, r) and reverse to (r, g, b)
2085
825k
                    pixel[0..3].copy_from_slice(&row_buffer[offset..offset + 3]);
2086
825k
                    pixel[0..3].reverse();
2087
2088
                    // Read the alpha channel if present
2089
825k
                    if *format == FormatFullBytes::RGBA32 {
2090
239k
                        pixel[3] = row_buffer[offset + 3];
2091
585k
                    } else if num_channels == 4 {
2092
257k
                        pixel[3] = ALPHA_OPAQUE;
2093
328k
                    }
2094
                }
2095
26.6k
                Ok(())
2096
27.0k
            },
2097
        );
2098
2099
489
        self.finish_row_decode(result)
2100
489
    }
2101
2102
1.71k
    fn read_rle_data(&mut self, buf: &mut [u8], image_type: ImageType) -> ImageResult<()> {
2103
1.71k
        let (start_row, start_x, start_pos) = match self.state {
2104
            DecoderState::ReadingRleData {
2105
                progress: RleProgress::NotStarted,
2106
1.71k
            } => (0u32, 0u32, self.data_offset),
2107
            DecoderState::ReadingRleData {
2108
0
                progress: RleProgress::Checkpoint { row, x, stream_pos },
2109
0
            } => (row, x, stream_pos),
2110
0
            _ => unreachable!("read_rle_data called in unexpected state: {:?}", self.state),
2111
        };
2112
2113
1.71k
        self.reader.seek(SeekFrom::Start(start_pos))?;
2114
2115
1.71k
        let num_channels = self.num_channels();
2116
1.71k
        let p = if image_type != ImageType::RLE24 {
2117
949
            Some(self.palette.as_ref().unwrap())
2118
        } else {
2119
769
            None
2120
        };
2121
2122
1.71k
        let mut row_iter = self.rows(buf).skip(start_row as usize);
2123
1.71k
        let mut current_row = start_row;
2124
1.71k
        let mut first_row_iteration = true;
2125
2126
        // Pre-allocate buffer for RLE4/8 absolute mode (max 256 bytes).
2127
        // RLE24 reads inline BGR triples directly, so this buffer is unused.
2128
1.71k
        let mut rle_indices_buffer = [0u8; 256];
2129
2130
1.71k
        let mut rle_reader = RleReader::new(&mut self.reader);
2131
2132
200k
        while let Some(row) = row_iter.next() {
2133
200k
            let mut pixel_iter = row.chunks_exact_mut(num_channels);
2134
2135
            // When resuming mid-row, skip to the saved x position on the first row.
2136
200k
            let mut x = if first_row_iteration && start_x > 0 {
2137
0
                pixel_iter.nth(start_x as usize - 1); // nth(n) consumes n+1 elements
2138
0
                start_x
2139
            } else {
2140
200k
                0
2141
            };
2142
200k
            first_row_iteration = false;
2143
2144
            loop {
2145
3.48M
                let control_byte = rle_reader.read_byte()?;
2146
2147
3.48M
                match control_byte {
2148
                    RLE_ESCAPE => {
2149
301k
                        let op = rle_reader.read_byte()?;
2150
301k
                        match op {
2151
                            RLE_ESCAPE_EOL => {
2152
210M
                                pixel_iter.for_each(|p| p.fill(0));
2153
198k
                                current_row += 1;
2154
198k
                                x = 0;
2155
198k
                                break;
2156
                            }
2157
                            RLE_ESCAPE_EOF => {
2158
3.71M
                                pixel_iter.for_each(|p| p.fill(0));
2159
1.16G
                                row_iter.for_each(|r| r.fill(0));
2160
546
                                return Ok(());
2161
                            }
2162
                            RLE_ESCAPE_DELTA => {
2163
48.9k
                                let x_delta = rle_reader.read_byte()?;
2164
48.9k
                                let y_delta = rle_reader.read_byte()?;
2165
2166
                                // IE and Windows image preview replace skipped pixels
2167
                                // with black, so we stick to that.
2168
48.9k
                                if y_delta > 0 {
2169
24.7M
                                    pixel_iter.for_each(|p| p.fill(0));
2170
2171
16.2k
                                    for _ in 1..y_delta {
2172
266k
                                        if let Some(row) = row_iter.next() {
2173
266k
                                            row.fill(0);
2174
266k
                                        } else if self.spec_strictness == SpecCompliance::Strict {
2175
0
                                            return Err(DecoderError::CorruptRleData.into());
2176
                                        } else {
2177
23
                                            return Ok(());
2178
                                        }
2179
                                    }
2180
2181
16.2k
                                    current_row += y_delta as u32;
2182
16.2k
                                    if let Some(next_row) = row_iter.next() {
2183
16.2k
                                        pixel_iter = next_row.chunks_exact_mut(num_channels);
2184
16.2k
                                    } else if self.spec_strictness == SpecCompliance::Strict {
2185
0
                                        return Err(DecoderError::CorruptRleData.into());
2186
                                    } else {
2187
6
                                        return Ok(());
2188
                                    }
2189
2190
16.2k
                                    for _ in 0..x {
2191
4.12M
                                        if let Some(pixel) = pixel_iter.next() {
2192
4.12M
                                            pixel.fill(0);
2193
4.12M
                                        } else if self.spec_strictness == SpecCompliance::Strict {
2194
0
                                            return Err(DecoderError::CorruptRleData.into());
2195
                                        } else {
2196
3.95k
                                            break;
2197
                                        }
2198
                                    }
2199
32.6k
                                }
2200
2201
48.8k
                                for _ in 0..x_delta {
2202
160k
                                    if let Some(pixel) = pixel_iter.next() {
2203
158k
                                        pixel.fill(0);
2204
158k
                                    } else if self.spec_strictness == SpecCompliance::Strict {
2205
0
                                        return Err(DecoderError::CorruptRleData.into());
2206
                                    } else {
2207
1.87k
                                        break;
2208
                                    }
2209
                                }
2210
48.8k
                                x += x_delta as u32;
2211
                            }
2212
                            _ => {
2213
                                // Absolute mode: pixel data differs by RLE type.
2214
53.2k
                                let count = op as usize;
2215
53.2k
                                match image_type {
2216
                                    ImageType::RLE8 => {
2217
4.89k
                                        let mut length = count;
2218
4.89k
                                        length += length & 1;
2219
4.89k
                                        rle_reader.read_exact(&mut rle_indices_buffer[..length])?;
2220
                                        // Silently truncate if run overflows the row.
2221
4.83k
                                        let success = set_8bit_pixel_run(
2222
4.83k
                                            &mut pixel_iter,
2223
4.83k
                                            p.unwrap(),
2224
4.83k
                                            rle_indices_buffer[..length].iter(),
2225
4.83k
                                            count,
2226
                                        );
2227
4.83k
                                        if self.spec_strictness == SpecCompliance::Strict
2228
0
                                            && !success
2229
                                        {
2230
0
                                            return Err(DecoderError::CorruptRleData.into());
2231
4.83k
                                        }
2232
                                    }
2233
                                    ImageType::RLE4 => {
2234
23.9k
                                        let mut length = count.div_ceil(2);
2235
23.9k
                                        length += length & 1;
2236
23.9k
                                        rle_reader.read_exact(&mut rle_indices_buffer[..length])?;
2237
                                        // Silently truncate if run overflows the row.
2238
23.8k
                                        let success = set_4bit_pixel_run(
2239
23.8k
                                            &mut pixel_iter,
2240
23.8k
                                            p.unwrap(),
2241
23.8k
                                            rle_indices_buffer[..length].iter(),
2242
23.8k
                                            count,
2243
                                        );
2244
23.8k
                                        if self.spec_strictness == SpecCompliance::Strict
2245
0
                                            && !success
2246
                                        {
2247
0
                                            return Err(DecoderError::CorruptRleData.into());
2248
23.8k
                                        }
2249
                                    }
2250
                                    ImageType::RLE24 => {
2251
24.4k
                                        for _ in 0..count {
2252
1.71M
                                            let b = rle_reader.read_byte()?;
2253
1.71M
                                            let g = rle_reader.read_byte()?;
2254
1.71M
                                            let r = rle_reader.read_byte()?;
2255
1.71M
                                            if let Some(pixel) = pixel_iter.next() {
2256
271k
                                                pixel[0] = r;
2257
271k
                                                pixel[1] = g;
2258
271k
                                                pixel[2] = b;
2259
1.44M
                                            }
2260
                                        }
2261
                                        // RLE24 absolute mode pads to word (2-byte) boundary.
2262
24.1k
                                        if !(count * 3).is_multiple_of(2) {
2263
14.1k
                                            rle_reader.read_byte()?;
2264
10.0k
                                        }
2265
                                    }
2266
0
                                    _ => unreachable!(),
2267
                                }
2268
52.8k
                                x += count as u32;
2269
                            }
2270
                        }
2271
                    }
2272
                    _ => {
2273
                        // Encoded run: pixel data differs by RLE type.
2274
3.17M
                        let n_pixels = control_byte as usize;
2275
3.17M
                        match image_type {
2276
                            ImageType::RLE8 => {
2277
                                // Clamp to row length for compat with imagemagick:
2278
                                // https://github.com/image-rs/image/issues/2321
2279
905k
                                let palette_index = rle_reader.read_byte()?;
2280
905k
                                let repeat_pixel: [u8; 3] = p.unwrap()[palette_index as usize];
2281
2.78M
                                (&mut pixel_iter).take(n_pixels).for_each(|p| {
2282
2.78M
                                    p[0] = repeat_pixel[0];
2283
2.78M
                                    p[1] = repeat_pixel[1];
2284
2.78M
                                    p[2] = repeat_pixel[2];
2285
2.78M
                                });
2286
                            }
2287
                            ImageType::RLE4 => {
2288
1.08M
                                let palette_index = rle_reader.read_byte()?;
2289
                                // Silently truncate if run overflows the row
2290
                                // (matches RLE8 encoded run behavior).
2291
1.08M
                                let success = set_4bit_pixel_run(
2292
1.08M
                                    &mut pixel_iter,
2293
1.08M
                                    p.unwrap(),
2294
1.08M
                                    repeat(&palette_index),
2295
1.08M
                                    n_pixels,
2296
                                );
2297
1.08M
                                if self.spec_strictness == SpecCompliance::Strict && !success {
2298
0
                                    return Err(DecoderError::CorruptRleData.into());
2299
1.08M
                                }
2300
                            }
2301
                            ImageType::RLE24 => {
2302
1.18M
                                let b = rle_reader.read_byte()?;
2303
1.18M
                                let g = rle_reader.read_byte()?;
2304
1.18M
                                let r = rle_reader.read_byte()?;
2305
1.18M
                                for _ in 0..n_pixels {
2306
181M
                                    if let Some(pixel) = pixel_iter.next() {
2307
3.88M
                                        pixel[0] = r;
2308
3.88M
                                        pixel[1] = g;
2309
3.88M
                                        pixel[2] = b;
2310
177M
                                    }
2311
                                }
2312
                            }
2313
0
                            _ => unreachable!(),
2314
                        }
2315
3.17M
                        x += n_pixels as u32;
2316
                    }
2317
                }
2318
2319
                // Checkpoint after every instruction to avoid potential quadratic
2320
                // time complexity when the decoder is given data one byte at a time.
2321
3.28M
                self.state = DecoderState::ReadingRleData {
2322
3.28M
                    progress: RleProgress::Checkpoint {
2323
3.28M
                        row: current_row,
2324
3.28M
                        x,
2325
3.28M
                        stream_pos: start_pos + rle_reader.bytes_read(),
2326
3.28M
                    },
2327
3.28M
                };
2328
            }
2329
2330
            // Checkpoint after EndOfRow (which breaks out of the inner loop).
2331
198k
            self.state = DecoderState::ReadingRleData {
2332
198k
                progress: RleProgress::Checkpoint {
2333
198k
                    row: current_row,
2334
198k
                    x,
2335
198k
                    stream_pos: start_pos + rle_reader.bytes_read(),
2336
198k
                },
2337
198k
            };
2338
        }
2339
2340
19
        Ok(())
2341
1.71k
    }
2342
2343
    /// Determine if the current image type is RLE-compressed.
2344
4.37k
    fn is_rle(&self) -> bool {
2345
2.44k
        matches!(
2346
4.37k
            self.image_type,
2347
            ImageType::RLE4 | ImageType::RLE8 | ImageType::RLE24
2348
        )
2349
4.37k
    }
2350
2351
    /// Returns which rows in the output buffer contain valid decoded pixel data.
2352
    ///
2353
    /// See [`RowsDecoded`] for details on how to interpret the result.
2354
2.06k
    pub fn rows_decoded(&self) -> RowsDecoded {
2355
2.06k
        let rows = match self.state {
2356
2.06k
            DecoderState::ReadingRowData { rows_decoded } => rows_decoded,
2357
0
            DecoderState::ReadingRleData { progress } => match progress {
2358
0
                RleProgress::NotStarted => 0,
2359
                // row is 0-indexed current row; rows 0..row are complete
2360
0
                RleProgress::Checkpoint { row, .. } => row,
2361
            },
2362
0
            DecoderState::ImageDecoded => self.height as u32,
2363
0
            DecoderState::ReadingMetadata { .. } => 0,
2364
        };
2365
2.06k
        if self.top_down {
2366
539
            RowsDecoded::TopDown { rows }
2367
        } else {
2368
1.52k
            RowsDecoded::BottomUp { rows }
2369
        }
2370
2.06k
    }
2371
2372
    /// Handle the result of a row-based decode operation, updating state accordingly.
2373
2.06k
    fn finish_row_decode(&mut self, result: Result<u32, (u32, io::Error)>) -> ImageResult<()> {
2374
2.06k
        let (Ok(rows) | Err((rows, _))) = result;
2375
2.06k
        self.state = DecoderState::ReadingRowData { rows_decoded: rows };
2376
2.06k
        match result {
2377
271
            Ok(_) => Ok(()),
2378
1.79k
            Err((_, e)) => Err(e)?,
2379
        }
2380
2.06k
    }
2381
2382
    /// Read the actual pixel data of the image.
2383
    ///
2384
    /// Must be called after `read_metadata()` succeeds. On `UnexpectedEof`, the decoder
2385
    /// can be retried:
2386
    ///
2387
    /// - For non-RLE formats: decoding resumes from the last successfully decoded row.
2388
    ///   Already-decoded rows are preserved in `buf`.
2389
    /// - For RLE formats: decoding resumes from the last checkpoint (completed instruction symbol).
2390
    ///   Rows and pixels completed before the error are preserved in `buf`.
2391
3.78k
    pub fn read_image_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
2392
3.78k
        match self.state {
2393
0
            DecoderState::ImageDecoded => Ok(()),
2394
3.78k
            DecoderState::ReadingRowData { .. } | DecoderState::ReadingRleData { .. } => self
2395
3.78k
                .read_image_data_impl(buf)
2396
3.78k
                .map(|()| self.state = DecoderState::ImageDecoded),
2397
0
            DecoderState::ReadingMetadata { .. } => Err(DecoderError::MetadataNotRead.into()),
2398
        }
2399
3.78k
    }
2400
2401
    /// Internal implementation of image data reading.
2402
3.78k
    fn read_image_data_impl(&mut self, buf: &mut [u8]) -> ImageResult<()> {
2403
3.78k
        match self.image_type {
2404
904
            ImageType::Palette => self.read_palettized_pixel_data(buf),
2405
224
            ImageType::RGB16 => self.read_16_bit_pixel_data(buf, Some(&R5_G5_B5_COLOR_MASK)),
2406
304
            ImageType::RGB24 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGB24),
2407
53
            ImageType::RGB32 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGB32),
2408
86
            ImageType::RGBA32 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGBA32),
2409
378
            ImageType::RLE8 => self.read_rle_data(buf, ImageType::RLE8),
2410
571
            ImageType::RLE4 => self.read_rle_data(buf, ImageType::RLE4),
2411
769
            ImageType::RLE24 => self.read_rle_data(buf, ImageType::RLE24),
2412
92
            ImageType::Bitfields16 => match self.bitfields {
2413
92
                Some(_) => self.read_16_bit_pixel_data(buf, None),
2414
0
                None => Err(DecoderError::BitfieldMasksMissing(16).into()),
2415
            },
2416
405
            ImageType::Bitfields32 => match self.bitfields {
2417
                Some(R8_G8_B8_COLOR_MASK) => {
2418
17
                    self.read_full_byte_pixel_data(buf, &FormatFullBytes::Format888)
2419
                }
2420
                Some(R8_G8_B8_A8_COLOR_MASK) => {
2421
29
                    self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGBA32)
2422
                }
2423
359
                Some(_) => self.read_32_bit_pixel_data(buf),
2424
0
                None => Err(DecoderError::BitfieldMasksMissing(32).into()),
2425
            },
2426
        }
2427
3.78k
    }
2428
}
2429
2430
impl<R: BufRead + Seek> ImageDecoder for BmpDecoder<R> {
2431
16.4k
    fn prepare_image(&mut self) -> ImageResult<DecoderPreparedImage> {
2432
16.4k
        let color = if self.indexed_color {
2433
0
            ColorType::L8
2434
16.4k
        } else if self.add_alpha_channel {
2435
9.34k
            ColorType::Rgba8
2436
        } else {
2437
7.11k
            ColorType::Rgb8
2438
        };
2439
2440
16.4k
        Ok(DecoderPreparedImage::new(
2441
16.4k
            self.width as u32,
2442
16.4k
            self.height as u32,
2443
16.4k
            color,
2444
16.4k
        ))
2445
16.4k
    }
2446
2447
0
    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
2448
0
        Ok(self.icc_profile.clone())
2449
0
    }
2450
2451
1.76k
    fn read_image(&mut self, buf: &mut [u8]) -> ImageResult<DecodedImageAttributes> {
2452
1.76k
        let layout = self.prepare_image()?;
2453
1.76k
        assert_eq!(u64::try_from(buf.len()), Ok(layout.total_bytes()));
2454
1.76k
        self.read_image_data(buf)?;
2455
330
        Ok(DecodedImageAttributes::default())
2456
1.76k
    }
2457
}
2458
2459
#[cfg(test)]
2460
mod test {
2461
    use std::io::{BufRead, BufReader, Cursor, Seek};
2462
2463
    use super::*;
2464
2465
    #[test]
2466
    fn test_bitfield_len() {
2467
        for len in 1..9 {
2468
            let bitfield = Bitfield::from_len_shift(len, 0);
2469
            for i in 0..(1 << len) {
2470
                let read = bitfield.read(i);
2471
                let calc = (f64::from(i) / f64::from((1 << len) - 1) * 255f64).round() as u8;
2472
                if read != calc {
2473
                    println!("len:{len} i:{i} read:{read} calc:{calc}");
2474
                }
2475
                assert_eq!(read, calc);
2476
            }
2477
        }
2478
    }
2479
2480
    #[test]
2481
    fn read_rle_too_short() {
2482
        let data = vec![
2483
            0x42, 0x4d, 0x04, 0xee, 0xfe, 0xff, 0xff, 0x10, 0xff, 0x00, 0x04, 0x00, 0x00, 0x00,
2484
            0x7c, 0x00, 0x00, 0x00, 0x0c, 0x41, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x01, 0x00,
2485
            0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
2486
            0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x21,
2487
            0xff, 0x00, 0x66, 0x61, 0x72, 0x62, 0x66, 0x65, 0x6c, 0x64, 0x00, 0x00, 0x00, 0x00,
2488
            0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2489
            0xff, 0xd8, 0xff, 0x00, 0x00, 0x19, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2490
            0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xff, 0x00, 0x00, 0x00,
2491
            0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
2492
            0x00, 0x00, 0x00, 0x2d, 0x31, 0x31, 0x35, 0x36, 0x00, 0xff, 0x00, 0x00, 0x52, 0x3a,
2493
            0x37, 0x30, 0x7e, 0x71, 0x63, 0x91, 0x5a, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
2494
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
2495
            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x35, 0x37, 0x00, 0xff, 0x00, 0x00, 0x52,
2496
            0x3a, 0x37, 0x30, 0x7e, 0x71, 0x63, 0x91, 0x5a, 0x04, 0x05, 0x3c, 0x00, 0x00, 0x11,
2497
            0x00, 0x5d, 0x7a, 0x82, 0xb7, 0xca, 0x2d, 0x31, 0xff, 0xff, 0xc7, 0x95, 0x33, 0x2e,
2498
            0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00,
2499
            0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x66, 0x00, 0x4d,
2500
            0x4d, 0x00, 0x2a, 0x00,
2501
        ];
2502
2503
        let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
2504
        let layout = decoder.prepare_image().unwrap();
2505
        let mut buf = vec![0; usize::try_from(layout.total_bytes()).unwrap()];
2506
        assert!(decoder.read_image(&mut buf).is_ok());
2507
    }
2508
2509
    #[test]
2510
    fn test_no_header() {
2511
        let tests = [
2512
            "Info_R8_G8_B8.bmp",
2513
            "Info_A8_R8_G8_B8.bmp",
2514
            "Info_8_Bit.bmp",
2515
            "Info_4_Bit.bmp",
2516
            "Info_1_Bit.bmp",
2517
        ];
2518
2519
        for name in &tests {
2520
            let path = format!("tests/images/bmp/images/{name}");
2521
            let ref_img = crate::open(&path).unwrap();
2522
            let mut data = std::fs::read(&path).unwrap();
2523
            // skip the BITMAPFILEHEADER
2524
            let slice = &mut data[14..];
2525
            let decoder = BmpDecoder::new_without_file_header(Cursor::new(slice)).unwrap();
2526
            let no_hdr_img = crate::DynamicImage::from_decoder(decoder).unwrap();
2527
            assert_eq!(ref_img, no_hdr_img);
2528
        }
2529
    }
2530
2531
    /// Validates that the given ICC profile data can be parsed by moxcms and contains
2532
    /// the expected properties for an RGB display profile.
2533
    fn validate_icc_profile(
2534
        profile_data: &[u8],
2535
        source_file: &str,
2536
        expected_color_space: moxcms::DataColorSpace,
2537
        expected_profile_class: moxcms::ProfileClass,
2538
    ) {
2539
        let parsed_profile = moxcms::ColorProfile::new_from_slice(profile_data);
2540
        assert!(
2541
            parsed_profile.is_ok(),
2542
            "ICC profile from {} should be parseable by moxcms: {:?}",
2543
            source_file,
2544
            parsed_profile.err()
2545
        );
2546
        let parsed_profile = parsed_profile.unwrap();
2547
        assert_eq!(
2548
            parsed_profile.color_space, expected_color_space,
2549
            "ICC profile from {} should have RGB color space",
2550
            source_file
2551
        );
2552
        assert_eq!(
2553
            parsed_profile.profile_class, expected_profile_class,
2554
            "ICC profile from {} should be a display/monitor profile",
2555
            source_file
2556
        );
2557
    }
2558
2559
    #[test]
2560
    fn test_icc_profile() {
2561
        // V5 header file without embedded ICC profile
2562
        let f =
2563
            BufReader::new(std::fs::File::open("tests/images/bmp/images/V5_24_Bit.bmp").unwrap());
2564
        let mut decoder = BmpDecoder::new(f).unwrap();
2565
        let profile = decoder.icc_profile().unwrap();
2566
        assert!(profile.is_none());
2567
2568
        // Test files with embedded ICC profiles
2569
        let f =
2570
            BufReader::new(std::fs::File::open("tests/images/bmp/images/rgb24prof.bmp").unwrap());
2571
        let mut decoder = BmpDecoder::new(f).unwrap();
2572
        let profile = decoder.icc_profile().unwrap();
2573
        assert!(profile.is_some());
2574
        let profile_data = profile.unwrap();
2575
        assert_eq!(profile_data.len(), 3048);
2576
        validate_icc_profile(
2577
            &profile_data,
2578
            "rgb24prof.bmp",
2579
            moxcms::DataColorSpace::Rgb,
2580
            moxcms::ProfileClass::DisplayDevice,
2581
        );
2582
2583
        let f =
2584
            BufReader::new(std::fs::File::open("tests/images/bmp/images/rgb24prof2.bmp").unwrap());
2585
        let mut decoder = BmpDecoder::new(f).unwrap();
2586
        let profile = decoder.icc_profile().unwrap();
2587
        assert!(profile.is_some());
2588
        let profile_data = profile.unwrap();
2589
        assert_eq!(profile_data.len(), 540);
2590
        validate_icc_profile(
2591
            &profile_data,
2592
            "rgb24prof2.bmp",
2593
            moxcms::DataColorSpace::Rgb,
2594
            moxcms::ProfileClass::DisplayDevice,
2595
        );
2596
    }
2597
2598
    #[test]
2599
    fn test_calibrated_rgb_icc_profile() {
2600
        // pal8v4.bmp has a V4 header with LCS_CALIBRATED_RGB — should synthesize an ICC profile.
2601
        let data = std::fs::read("tests/images/bmp/images/pal8v4.bmp").unwrap();
2602
        let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
2603
        let profile = decoder.icc_profile().unwrap();
2604
        assert!(
2605
            profile.is_some(),
2606
            "pal8v4: should have a synthesized ICC profile from calibrated RGB parameters"
2607
        );
2608
        validate_icc_profile(
2609
            &profile.unwrap(),
2610
            "pal8v4.bmp",
2611
            moxcms::DataColorSpace::Rgb,
2612
            moxcms::ProfileClass::DisplayDevice,
2613
        );
2614
2615
        // pal8v5.bmp uses LCS_sRGB — no ICC profile needed.
2616
        let data = std::fs::read("tests/images/bmp/images/pal8v5.bmp").unwrap();
2617
        let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
2618
        assert!(
2619
            decoder.icc_profile().unwrap().is_none(),
2620
            "pal8v5: should have no ICC profile (LCS_sRGB)"
2621
        );
2622
    }
2623
2624
    /// A reader that simulates partial data availability for testing resumable decoding.
2625
    /// It wraps a byte slice and limits how many bytes can be read before returning UnexpectedEof.
2626
    struct PartialReader {
2627
        data: Vec<u8>,
2628
        position: u64,
2629
        available_bytes: usize,
2630
    }
2631
2632
    impl PartialReader {
2633
        fn new(data: Vec<u8>) -> Self {
2634
            Self {
2635
                data,
2636
                position: 0,
2637
                available_bytes: 0,
2638
            }
2639
        }
2640
2641
        /// Set the number of bytes available for reading (absolute, not additive).
2642
        fn set_available(&mut self, bytes: usize) {
2643
            self.available_bytes = bytes.min(self.data.len());
2644
        }
2645
    }
2646
2647
    impl io::Read for PartialReader {
2648
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2649
            if self.position as usize >= self.available_bytes {
2650
                return Err(io::Error::new(
2651
                    io::ErrorKind::UnexpectedEof,
2652
                    "simulated partial data",
2653
                ));
2654
            }
2655
2656
            let available = self.available_bytes - self.position as usize;
2657
            let to_read = buf.len().min(available);
2658
            let start = self.position as usize;
2659
            buf[..to_read].copy_from_slice(&self.data[start..start + to_read]);
2660
            self.position += to_read as u64;
2661
            Ok(to_read)
2662
        }
2663
    }
2664
2665
    impl BufRead for PartialReader {
2666
        fn fill_buf(&mut self) -> io::Result<&[u8]> {
2667
            if self.position as usize >= self.available_bytes {
2668
                return Err(io::Error::new(
2669
                    io::ErrorKind::UnexpectedEof,
2670
                    "simulated partial data",
2671
                ));
2672
            }
2673
2674
            let start = self.position as usize;
2675
            Ok(&self.data[start..self.available_bytes])
2676
        }
2677
2678
        fn consume(&mut self, amt: usize) {
2679
            self.position += amt as u64;
2680
        }
2681
    }
2682
2683
    impl Seek for PartialReader {
2684
        fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
2685
            let new_pos = match pos {
2686
                SeekFrom::Start(offset) => offset as i64,
2687
                SeekFrom::End(offset) => self.data.len() as i64 + offset,
2688
                SeekFrom::Current(offset) => self.position as i64 + offset,
2689
            };
2690
2691
            if new_pos < 0 {
2692
                return Err(io::Error::new(
2693
                    io::ErrorKind::InvalidInput,
2694
                    "seek to negative position",
2695
                ));
2696
            }
2697
2698
            self.position = new_pos as u64;
2699
            Ok(self.position)
2700
        }
2701
    }
2702
2703
    /// Helper to check if an error is UnexpectedEof
2704
    fn is_unexpected_eof(err: &ImageError) -> bool {
2705
        matches!(err, ImageError::IoError(e) if e.kind() == io::ErrorKind::UnexpectedEof)
2706
    }
2707
2708
    /// Test resumable decoding with various BMP formats.
2709
    /// Verifies that read_metadata() and read_image_data() can be retried after
2710
    /// UnexpectedEof and produce identical results to normal decoding.
2711
    /// Also verifies metadata phase progress and row-level progress for non-RLE formats.
2712
    #[test]
2713
    fn test_resumable_decoding() {
2714
        use crate::ImageDecoder;
2715
2716
        struct TestCase {
2717
            path: &'static str,
2718
            is_rle: bool,
2719
            has_palette: bool,
2720
            has_icc_profile: bool,
2721
            top_down: bool,
2722
        }
2723
2724
        // Test multiple BMP formats to ensure resumable decoding works across variants
2725
        let test_files = [
2726
            TestCase {
2727
                path: "tests/images/bmp/images/Info_R8_G8_B8.bmp",
2728
                is_rle: false,
2729
                has_palette: false,
2730
                has_icc_profile: false,
2731
                top_down: false,
2732
            },
2733
            TestCase {
2734
                path: "tests/images/bmp/images/Info_A8_R8_G8_B8.bmp",
2735
                is_rle: false,
2736
                has_palette: false,
2737
                has_icc_profile: false,
2738
                top_down: false,
2739
            },
2740
            TestCase {
2741
                path: "tests/images/bmp/images/Info_A8_R8_G8_B8_Top_Down.bmp",
2742
                is_rle: false,
2743
                has_palette: false,
2744
                has_icc_profile: false,
2745
                top_down: true,
2746
            },
2747
            TestCase {
2748
                path: "tests/images/bmp/images/Info_8_Bit.bmp",
2749
                is_rle: false,
2750
                has_palette: true,
2751
                has_icc_profile: false,
2752
                top_down: false,
2753
            },
2754
            TestCase {
2755
                path: "tests/images/bmp/images/Core_8_Bit.bmp",
2756
                is_rle: false,
2757
                has_palette: true,
2758
                has_icc_profile: false,
2759
                top_down: false,
2760
            },
2761
            TestCase {
2762
                path: "tests/images/bmp/images/pal8rle.bmp",
2763
                is_rle: true,
2764
                has_palette: true,
2765
                has_icc_profile: false,
2766
                top_down: false,
2767
            },
2768
            TestCase {
2769
                path: "tests/images/bmp/images/pal4rle.bmp",
2770
                is_rle: true,
2771
                has_palette: true,
2772
                has_icc_profile: false,
2773
                top_down: false,
2774
            },
2775
            TestCase {
2776
                path: "tests/images/bmp/images/rgb24prof.bmp",
2777
                is_rle: false,
2778
                has_palette: false,
2779
                has_icc_profile: true,
2780
                top_down: false,
2781
            },
2782
            TestCase {
2783
                path: "tests/images/bmp/images/rgb24rle24.bmp",
2784
                is_rle: true,
2785
                has_palette: false,
2786
                has_icc_profile: false,
2787
                top_down: false,
2788
            },
2789
        ];
2790
2791
        for TestCase {
2792
            path,
2793
            is_rle,
2794
            has_palette,
2795
            has_icc_profile,
2796
            top_down,
2797
        } in test_files
2798
        {
2799
            let data = std::fs::read(path).unwrap();
2800
            let file_size = data.len();
2801
2802
            // Get reference result from normal decoding
2803
            let mut ref_decoder = BmpDecoder::new(Cursor::new(data.clone())).unwrap();
2804
            let expected_bytes = ref_decoder.prepare_image().unwrap().total_bytes() as usize;
2805
            let mut ref_buf = vec![0u8; expected_bytes];
2806
            let ref_icc_len = ref_decoder.icc_profile().unwrap().map(|p| p.len());
2807
            ref_decoder.read_image(&mut ref_buf).unwrap();
2808
2809
            // Test resumable decoding with simulated streaming
2810
            let reader = PartialReader::new(data);
2811
            let mut decoder = BmpDecoder::new_resumable(reader);
2812
2813
            // Track metadata phase transitions
2814
            let mut saw_reading_palette = false;
2815
            let mut saw_reading_icc = false;
2816
2817
            // Phase 1: Stream bytes until metadata succeeds
2818
            let mut bytes_available = 0;
2819
            loop {
2820
                decoder.reader.set_available(bytes_available);
2821
                match decoder.read_metadata() {
2822
                    Ok(()) => break,
2823
                    Err(ref e) if is_unexpected_eof(e) => {
2824
                        if let DecoderState::ReadingMetadata { progress } = decoder.state {
2825
                            match progress {
2826
                                MetadataProgress::ReadingPalette { .. } => {
2827
                                    saw_reading_palette = true
2828
                                }
2829
                                MetadataProgress::ReadingIccProfile { .. } => {
2830
                                    saw_reading_icc = true
2831
                                }
2832
                                _ => {}
2833
                            }
2834
                        }
2835
2836
                        // Simulate more data arriving (add 10 bytes at a time, capped at file size)
2837
                        bytes_available = (bytes_available + 10).min(file_size);
2838
                        assert!(
2839
                            bytes_available <= file_size,
2840
                            "{path}: metadata should succeed before EOF"
2841
                        );
2842
                    }
2843
                    Err(e) => panic!("{path}: unexpected error during metadata: {e:?}"),
2844
                }
2845
            }
2846
2847
            // Verify metadata phase transitions occurred as expected
2848
            if has_palette {
2849
                assert!(
2850
                    saw_reading_palette,
2851
                    "{path}: should have seen ReadingPalette phase"
2852
                );
2853
            }
2854
            if has_icc_profile {
2855
                assert!(
2856
                    saw_reading_icc,
2857
                    "{path}: should have seen ReadingIccProfile phase"
2858
                );
2859
                let icc = decoder.icc_profile().unwrap();
2860
                assert_eq!(
2861
                    icc.map(|p| p.len()),
2862
                    ref_icc_len,
2863
                    "{path}: ICC profile length mismatch"
2864
                );
2865
            }
2866
2867
            // Verify dimensions are available after metadata
2868
            let layout = decoder.prepare_image().unwrap();
2869
            let (width, height) = layout.layout.dimensions();
2870
            assert!(width > 0 && height > 0, "{path}: invalid dimensions");
2871
            assert_eq!(
2872
                layout.total_bytes() as usize,
2873
                expected_bytes,
2874
                "{path}: total_bytes mismatch"
2875
            );
2876
2877
            // Phase 2: Stream bytes until image data succeeds
2878
            let mut buf = vec![0u8; expected_bytes];
2879
            let mut prev_decoded_rows = 0u32;
2880
            loop {
2881
                decoder.reader.set_available(bytes_available);
2882
                match decoder.read_image_data(&mut buf) {
2883
                    Ok(()) => {
2884
                        // After successful decode, rows_decoded() should return full height
2885
                        let progress = decoder.rows_decoded();
2886
                        assert_eq!(
2887
                            progress.rows(),
2888
                            height,
2889
                            "{path}: rows_decoded() should equal height after complete decode"
2890
                        );
2891
                        if top_down {
2892
                            assert!(
2893
                                matches!(progress, RowsDecoded::TopDown { .. }),
2894
                                "{path}: top-down file should produce TopDown, got {progress:?}"
2895
                            );
2896
                        } else {
2897
                            assert!(
2898
                                matches!(progress, RowsDecoded::BottomUp { .. }),
2899
                                "{path}: bottom-up file should produce BottomUp, got {progress:?}"
2900
                            );
2901
                        }
2902
                        break;
2903
                    }
2904
                    Err(ref e) if is_unexpected_eof(e) => {
2905
                        // Validate rows_decoded() returns correct count and variant
2906
                        let progress = decoder.rows_decoded();
2907
                        let decoded_rows = progress.rows();
2908
                        assert!(
2909
                            decoded_rows <= height,
2910
                            "{path}: rows_decoded() {decoded_rows} exceeds height {height}"
2911
                        );
2912
                        assert!(decoded_rows >= prev_decoded_rows, "{path}: rows_decoded() decreased from {prev_decoded_rows} to {decoded_rows}");
2913
                        prev_decoded_rows = decoded_rows;
2914
2915
                        // Verify state tracks progress appropriately
2916
                        match decoder.state {
2917
                            DecoderState::ReadingRowData { rows_decoded } => {
2918
                                assert!(!is_rle, "{path}: expected ReadingRleData for RLE format");
2919
                                assert!(
2920
                                    rows_decoded < height,
2921
                                    "{path}: rows_decoded {rows_decoded} >= height {height}"
2922
                                );
2923
                                assert_eq!(
2924
                                    decoded_rows, rows_decoded,
2925
                                    "{path}: rows_decoded() mismatch"
2926
                                );
2927
                            }
2928
                            DecoderState::ReadingRleData { progress } => {
2929
                                assert!(
2930
                                    is_rle,
2931
                                    "{path}: expected ReadingRowData for non-RLE format"
2932
                                );
2933
                                match progress {
2934
                                    RleProgress::NotStarted => {
2935
                                        assert_eq!(
2936
                                            decoded_rows, 0,
2937
                                            "{path}: should be 0 for NotStarted"
2938
                                        );
2939
                                    }
2940
                                    RleProgress::Checkpoint { row, .. } => {
2941
                                        assert!(
2942
                                            row < height,
2943
                                            "{path}: RLE row {row} >= height {height}"
2944
                                        );
2945
                                        assert_eq!(
2946
                                            decoded_rows, row,
2947
                                            "{path}: rows_decoded() mismatch with RLE row"
2948
                                        );
2949
                                    }
2950
                                }
2951
                            }
2952
                            _ => panic!("{path}: unexpected state: {:?}", decoder.state),
2953
                        }
2954
2955
                        bytes_available += 100;
2956
                        assert!(
2957
                            bytes_available <= file_size + 100,
2958
                            "{path}: image data should succeed before EOF"
2959
                        );
2960
                    }
2961
                    Err(e) => panic!("{path}: unexpected error during image data: {e:?}"),
2962
                }
2963
            }
2964
2965
            // Verify decoded data matches reference
2966
            assert_eq!(buf, ref_buf, "{path}: decoded data mismatch");
2967
        }
2968
    }
2969
2970
    /// Test that BMP files with known spec violations are accepted by the
2971
    /// decoder (which defaults to lenient mode), and that strict mode still
2972
    /// detects the violations internally.
2973
    ///
2974
    /// These files come from the Chromium BMP test suite ("bad/" category):
2975
    /// - `rletopdown`: RLE compression with top-down orientation (spec forbids this)
2976
    /// - `badplanes`: planes field != 1 (spec requires exactly 1)
2977
    /// - `badpalettesize`: colors_used exceeds max for the bit depth
2978
    /// - `pal8oversizepal`: 8-bit palette with colors_used=300 (max is 256)
2979
    /// - `rgb16-880`: 16-bit bitfields with 8-8-0 channel widths (blue mask is zero)
2980
    #[test]
2981
    fn test_strict_vs_lenient_spec_validation() {
2982
        let questionable_files = [
2983
            (
2984
                "tests/images/bmp/images/lenient/rletopdown.bmp",
2985
                "rletopdown: RLE with top-down should be rejected in strict mode",
2986
            ),
2987
            (
2988
                "tests/images/bmp/images/lenient/badplanes.bmp",
2989
                "badplanes: planes != 1 should be rejected in strict mode",
2990
            ),
2991
            (
2992
                "tests/images/bmp/images/lenient/badpalettesize.bmp",
2993
                "badpalettesize: palette size exceeding bit depth should be rejected in strict mode",
2994
            ),
2995
            (
2996
                "tests/images/bmp/images/lenient/pal8oversizepal.bmp",
2997
                "pal8oversizepal: colors_used=300 exceeds max 256 for 8-bit",
2998
            ),
2999
            (
3000
                "tests/images/bmp/images/lenient/rgb16-880.bmp",
3001
                "rgb16-880: zero blue mask should be rejected in strict mode",
3002
            ),
3003
            (
3004
                "tests/images/bmp/images/lenient/V5_A8_R8_G8_B8_Rgb_BadMask.bmp",
3005
                "V5_A8_R8_G8_B8_Rgb_BadMask: non-standard alpha mask under BI_RGB",
3006
            ),
3007
        ];
3008
3009
        for (path, description) in &questionable_files {
3010
            let data = std::fs::read(path)
3011
                .unwrap_or_else(|e| panic!("{description}: failed to read {path}: {e}"));
3012
3013
            // Default (lenient) mode: these files should be accepted
3014
            let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap_or_else(|e| {
3015
                panic!("{description}: decoding failed: {e:?}");
3016
            });
3017
            let layout = decoder.prepare_image().unwrap_or_else(|e| {
3018
                panic!("{description}: peek_layout failed: {e:?}");
3019
            });
3020
            let mut buf = vec![0u8; layout.total_bytes() as usize];
3021
            decoder.read_image(buf.as_mut_slice()).unwrap_or_else(|e| {
3022
                panic!("{description}: read_image failed: {e:?}");
3023
            });
3024
3025
            // Strict mode: these files should be rejected
3026
            assert!(
3027
                BmpDecoder::new_with_spec_compliance(Cursor::new(&data), SpecCompliance::Strict)
3028
                    .is_err(),
3029
                "{description}: expected error in strict mode, but got Ok"
3030
            );
3031
        }
3032
    }
3033
3034
    /// A BMP with data_offset=34 points into the middle of the DIB header,
3035
    /// which is invalid. The decoder should clamp it to bmp_header_end (54)
3036
    /// and produce the same output as a correctly-formed file.
3037
    #[test]
3038
    fn test_invalid_data_offset_into_dib_header() {
3039
        let data: Vec<u8> = vec![
3040
            0x42, 0x4D, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
3041
            0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00,
3042
            0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3043
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3044
            0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
3045
        ];
3046
3047
        // Same BMP but with the correct data_offset = 54 (0x36)
3048
        let mut reference = data.clone();
3049
        reference[10] = 0x36;
3050
3051
        // Decode both
3052
        let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
3053
        let len = decoder.prepare_image().unwrap().total_bytes();
3054
        let mut buf = vec![0u8; len as usize];
3055
        decoder.read_image(&mut buf).unwrap();
3056
3057
        let mut ref_decoder = BmpDecoder::new(Cursor::new(&reference)).unwrap();
3058
        let len = decoder.prepare_image().unwrap().total_bytes();
3059
        let mut ref_buf = vec![0u8; len as usize];
3060
        ref_decoder.read_image(&mut ref_buf).unwrap();
3061
3062
        assert_eq!(
3063
            buf, ref_buf,
3064
            "BMP with invalid data_offset=34 should decode identically to data_offset=54"
3065
        );
3066
    }
3067
3068
    /// Test that strict mode correctly rejects RLE files with known corruptions.
3069
    ///
3070
    /// - `rle_overflow.bmp`: The image header specifies a width of 2. However, the RLE data
3071
    ///   contains an absolute run of 3 pixels (`00 03 ...`), which overflows the row boundary.
3072
    /// - `badrle.bmp`: The image height is 64. However, the RLE data contains multiple Delta skip
3073
    ///   instructions that move the cursor past the end of the image.
3074
    #[test]
3075
    fn test_strict_mode_fails_on_rle_errors() {
3076
        let test_files = [
3077
            "tests/images/bmp/images/lenient/rle_overflow.bmp",
3078
            "tests/images/bmp/images/lenient/badrle.bmp",
3079
        ];
3080
3081
        for path in &test_files {
3082
            let data = std::fs::read(path).expect("Test image missing");
3083
3084
            // Strict mode must fail on these images during full decode
3085
            let strict_result =
3086
                BmpDecoder::new_with_spec_compliance(Cursor::new(&data), SpecCompliance::Strict)
3087
                    .and_then(|mut d| {
3088
                        let len = d.prepare_image()?.total_bytes();
3089
                        let mut buf = vec![0u8; len as usize];
3090
                        d.read_image(buf.as_mut_slice())
3091
                    });
3092
            assert!(
3093
                strict_result.is_err(),
3094
                "{path}: expected error in strict mode, but got Ok"
3095
            );
3096
        }
3097
    }
3098
3099
    #[test]
3100
    fn test_decode_bmp_rle_overflow() {
3101
        let data = std::fs::read("tests/images/bmp/images/lenient/rle_overflow.bmp")
3102
            .expect("Test image missing");
3103
        let mut decoder = BmpDecoder::new(Cursor::new(data)).unwrap();
3104
        let len = decoder.prepare_image().unwrap().total_bytes();
3105
        let mut buffer = vec![0u8; len as usize];
3106
        let result = decoder.read_image(&mut buffer);
3107
        assert!(result.is_ok());
3108
    }
3109
3110
    #[test]
3111
    fn test_decode_bmp_badrle() {
3112
        let data = std::fs::read("tests/images/bmp/images/lenient/badrle.bmp")
3113
            .expect("Test image missing");
3114
        let mut decoder = BmpDecoder::new(Cursor::new(data)).unwrap();
3115
        let len = decoder.prepare_image().unwrap().total_bytes();
3116
        let mut buffer = vec![0u8; len as usize];
3117
        let result = decoder.read_image(&mut buffer);
3118
        assert!(result.is_ok());
3119
    }
3120
3121
    #[test]
3122
    fn test_decode_truncated_bmp() {
3123
        use std::io::Cursor;
3124
3125
        let data = vec![
3126
            0x42, 0x4D, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
3127
            0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
3128
            0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3129
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00,
3130
            0x00,
3131
        ];
3132
3133
        // Test Lenient mode
3134
        let decoder = BmpDecoder::new(Cursor::new(data.clone())).unwrap();
3135
        let mut decoder = crate::ImageReader::from_decoder(Box::new(decoder));
3136
        let result = decoder.decode();
3137
        assert!(
3138
            result.is_ok(),
3139
            "Expected Ok in lenient mode for truncated file"
3140
        );
3141
3142
        // Test Strict mode
3143
        let strict_decoder =
3144
            BmpDecoder::new_with_spec_compliance(Cursor::new(data), SpecCompliance::Strict)
3145
                .unwrap();
3146
        let mut decoder = crate::ImageReader::from_decoder(Box::new(strict_decoder));
3147
        let result = decoder.decode();
3148
3149
        assert!(
3150
            result.is_err(),
3151
            "Expected error in strict mode for truncated file"
3152
        );
3153
    }
3154
}