Coverage Report

Created: 2026-06-10 07:53

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.31k
    pub fn rows(&self) -> u32 {
99
2.31k
        match *self {
100
2.31k
            RowsDecoded::TopDown { rows } | RowsDecoded::BottomUp { rows } => rows,
101
        }
102
2.31k
    }
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
778
    fn parse(buffer: &[u8; 8], spec_strictness: SpecCompliance) -> ImageResult<Self> {
116
778
        let width = i32::from(u16::from_le_bytes(buffer[0..2].try_into().unwrap()));
117
778
        let height = i32::from(u16::from_le_bytes(buffer[2..4].try_into().unwrap()));
118
119
778
        let planes = u16::from_le_bytes(buffer[4..6].try_into().unwrap());
120
778
        if spec_strictness == SpecCompliance::Strict && planes != 1 {
121
0
            return Err(DecoderError::MoreThanOnePlane.into());
122
778
        }
123
124
778
        let bit_count = u16::from_le_bytes(buffer[6..8].try_into().unwrap());
125
778
        let image_type = match bit_count {
126
507
            1 | 4 | 8 => ImageType::Palette,
127
258
            24 => ImageType::RGB24,
128
            _ => {
129
13
                return Err(
130
13
                    DecoderError::InvalidChannelWidth(ChannelWidthError::Rgb, bit_count).into(),
131
13
                )
132
            }
133
        };
134
135
765
        Ok(ParsedCoreHeader {
136
765
            width,
137
765
            height,
138
765
            bit_count,
139
765
            image_type,
140
765
        })
141
778
    }
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.44k
    fn parse(buffer: &[u8; 36], spec_strictness: SpecCompliance) -> ImageResult<Self> {
157
4.44k
        let width = i32::from_le_bytes(buffer[0..4].try_into().unwrap());
158
4.44k
        let mut height = i32::from_le_bytes(buffer[4..8].try_into().unwrap());
159
160
        // Width cannot be negative
161
4.44k
        if width < 0 {
162
74
            return Err(DecoderError::NegativeWidth(width).into());
163
4.36k
        } else if width > MAX_WIDTH_HEIGHT || height > MAX_WIDTH_HEIGHT {
164
61
            return Err(DecoderError::ImageTooLarge(width, height).into());
165
4.30k
        }
166
167
4.30k
        if height == i32::MIN {
168
2
            return Err(DecoderError::InvalidHeight.into());
169
4.30k
        }
170
171
        // A negative height indicates a top-down DIB
172
4.30k
        let top_down = if height < 0 {
173
1.24k
            height = -height;
174
1.24k
            true
175
        } else {
176
3.06k
            false
177
        };
178
179
4.30k
        let planes = u16::from_le_bytes(buffer[8..10].try_into().unwrap());
180
4.30k
        if spec_strictness == SpecCompliance::Strict && planes != 1 {
181
0
            return Err(DecoderError::MoreThanOnePlane.into());
182
4.30k
        }
183
184
4.30k
        let bit_count = u16::from_le_bytes(buffer[10..12].try_into().unwrap());
185
4.30k
        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.30k
        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.30k
        }
197
198
        // Skip size_image (16-19), x_pix_permeter (20-23), y_pix_permeter (24-27)
199
4.30k
        let colors_used = u32::from_le_bytes(buffer[28..32].try_into().unwrap());
200
        // Skip important_colors (32-35)
201
4.30k
        Ok(ParsedInfoHeader {
202
4.30k
            width,
203
4.30k
            height,
204
4.30k
            top_down,
205
4.30k
            bit_count,
206
4.30k
            compression,
207
4.30k
            colors_used,
208
4.30k
        })
209
4.44k
    }
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
629
    fn parse(buffer: &[u8], has_alpha: bool) -> Self {
226
629
        let r_mask = u32::from_le_bytes(buffer[0..4].try_into().unwrap());
227
629
        let g_mask = u32::from_le_bytes(buffer[4..8].try_into().unwrap());
228
629
        let b_mask = u32::from_le_bytes(buffer[8..12].try_into().unwrap());
229
629
        let a_mask = if has_alpha {
230
278
            u32::from_le_bytes(buffer[12..16].try_into().unwrap())
231
        } else {
232
351
            0
233
        };
234
235
629
        ParsedBitfields {
236
629
            r_mask,
237
629
            g_mask,
238
629
            b_mask,
239
629
            a_mask,
240
629
        }
241
629
    }
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
212
    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
212
        let cs_type = u32::from_le_bytes(buffer[52..56].try_into().unwrap());
260
261
        // Only embedded profiles are supported
262
212
        if cs_type != PROFILE_EMBEDDED {
263
0
            return None;
264
212
        }
265
266
        // bV5ProfileData is at offset 112 from header start, which is offset 108 from after size field
267
212
        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
212
        let profile_size = u32::from_le_bytes(buffer[112..116].try_into().unwrap());
271
272
212
        if profile_size == 0 || profile_offset_from_header == 0 {
273
17
            return None;
274
195
        }
275
276
        // Compute the absolute file offset by adding the header's position to the relative offset
277
195
        let profile_offset = bmp_header_offset + u64::from(profile_offset_from_header);
278
279
195
        Some(ParsedIccProfile {
280
195
            profile_offset,
281
195
            profile_size,
282
195
        })
283
212
    }
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
790
    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
790
        let cs_type = u32::from_le_bytes(buffer[52..56].try_into().unwrap());
306
307
214
        match cs_type {
308
            LCS_CALIBRATED_RGB => {
309
1.08k
                let read_u32 = |offset: usize| -> u32 {
310
1.08k
                    u32::from_le_bytes(buffer[offset..offset + 4].try_into().unwrap())
311
1.08k
                };
312
313
                // FXPT2DOT30 (2.30 fixed-point) → f32.
314
720
                let fxpt2dot30 = |val: u32| -> f32 { val as f32 * (1.0 / (1u64 << 30) as f32) };
315
                // FXPT16DOT16 (16.16 fixed-point) → f32.
316
360
                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
120
                let rx = fxpt2dot30(read_u32(56));
324
120
                let ry = fxpt2dot30(read_u32(60));
325
120
                let gx = fxpt2dot30(read_u32(68));
326
120
                let gy = fxpt2dot30(read_u32(72));
327
120
                let bx = fxpt2dot30(read_u32(80));
328
120
                let by = fxpt2dot30(read_u32(84));
329
330
                // Gamma values at offsets 96-107 from header start (92-103 from after size).
331
120
                let gamma_r = fxpt16dot16(read_u32(92));
332
120
                let gamma_g = fxpt16dot16(read_u32(96));
333
120
                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
120
                if ry == 0.0 || gy == 0.0 || by == 0.0 {
338
14
                    return None;
339
106
                }
340
341
106
                Some(ColorSpaceInfo::CalibratedRgb(CalibratedRgb {
342
106
                    rx,
343
106
                    ry,
344
106
                    gx,
345
106
                    gy,
346
106
                    bx,
347
106
                    by,
348
106
                    gamma_r,
349
106
                    gamma_g,
350
106
                    gamma_b,
351
106
                }))
352
            }
353
4
            LCS_SRGB | LCS_WINDOWS_COLOR_SPACE => Some(ColorSpaceInfo::Srgb),
354
214
            PROFILE_EMBEDDED if bmp_header_size >= BITMAPV5HEADER_SIZE => {
355
212
                ParsedIccProfile::parse(buffer, bmp_header_offset).map(ColorSpaceInfo::EmbeddedIcc)
356
            }
357
454
            _ => None,
358
        }
359
790
    }
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
106
    fn to_color_profile(self) -> moxcms::ColorProfile {
393
106
        let primaries = moxcms::ColorPrimaries {
394
106
            red: moxcms::Chromaticity::new(self.rx, self.ry),
395
106
            green: moxcms::Chromaticity::new(self.gx, self.gy),
396
106
            blue: moxcms::Chromaticity::new(self.bx, self.by),
397
106
        };
398
399
106
        let mut profile = moxcms::ColorProfile::new_srgb();
400
106
        profile.update_rgb_colorimetry(moxcms::WHITE_POINT_D65, primaries);
401
402
        // Clear inherited CICP metadata from the sRGB base profile.
403
106
        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
318
        let safe_gamma = |g: f32| if g > 0.0 { g } else { 1.0 };
409
318
        let parametric_trc = |g: f32| moxcms::ToneReprCurve::Parametric(vec![safe_gamma(g)]);
410
106
        profile.red_trc = Some(parametric_trc(self.gamma_r));
411
106
        profile.green_trc = Some(parametric_trc(self.gamma_g));
412
106
        profile.blue_trc = Some(parametric_trc(self.gamma_b));
413
106
        profile
414
106
    }
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.54k
    fn default() -> Self {
504
5.54k
        DecoderState::ReadingMetadata {
505
5.54k
            progress: MetadataProgress::default(),
506
5.54k
        }
507
5.54k
    }
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
971M
    fn next(&mut self) -> Option<&'a mut [u8]> {
552
971M
        match self.chunks {
553
966M
            Chunker::FromTop(ref mut chunks) => chunks.next(),
554
5.15M
            Chunker::FromBottom(ref mut chunks) => chunks.next(),
555
        }
556
971M
    }
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
411
    fn from(e: DecoderError) -> ImageError {
650
411
        ImageError::Decoding(DecodingError::new(ImageFormat::Bmp.into(), e))
651
411
    }
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
711
fn calculate_row_padding(bytes_per_row: usize) -> usize {
686
711
    (4 - (bytes_per_row % 4)) % 4
687
711
}
688
689
/// Allocate a row buffer with OOM protection.
690
1.41k
fn allocate_row_buffer(size: usize) -> ImageResult<Vec<u8>> {
691
1.41k
    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.41k
    buffer.resize(size, 0);
701
1.41k
    Ok(buffer)
702
1.41k
}
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
827k
fn read_scanline(
711
827k
    reader: &mut (impl io::Read + Seek),
712
827k
    buf: &mut [u8],
713
827k
    current_file_row: &mut u32,
714
827k
    last_row: u32,
715
827k
    spec_strictness: SpecCompliance,
716
827k
) -> io::Result<()> {
717
827k
    let is_last_row = *current_file_row == last_row;
718
827k
    *current_file_row += 1;
719
720
827k
    if is_last_row && spec_strictness == SpecCompliance::Lenient {
721
632
        let current_pos = reader.stream_position()?;
722
632
        let end_pos = reader.seek(SeekFrom::End(0))?;
723
632
        reader.seek(SeekFrom::Start(current_pos))?;
724
725
632
        let Some((last, head)) = buf.split_last_mut() else {
726
            // Empty row, nothing to read.
727
0
            return Ok(());
728
        };
729
730
632
        if Ok(head.len()) == usize::try_from(end_pos - current_pos) {
731
52
            reader.read_exact(head)?;
732
52
            *last = b'\0';
733
52
            return Ok(());
734
580
        }
735
827k
    }
736
737
827k
    reader.read_exact(buf)
738
827k
}
739
740
/// Convenience function to check if the combination of width, length and number of
741
/// channels would result in a buffer that would overflow.
742
4.93k
fn check_for_overflow(width: i32, length: i32, channels: usize) -> ImageResult<()> {
743
4.93k
    num_bytes(width, length, channels)
744
4.93k
        .map(|_| ())
745
4.93k
        .ok_or_else(|| {
746
10
            ImageError::Unsupported(UnsupportedError::from_format_and_kind(
747
10
                ImageFormat::Bmp.into(),
748
10
                UnsupportedErrorKind::GenericFeature(format!(
749
10
                    "Image dimensions ({width}x{length} w/{channels} channels) are too large"
750
10
                )),
751
10
            ))
752
10
        })
753
4.93k
}
754
755
/// Calculate how many many bytes a buffer holding a decoded image with these properties would
756
/// require. Returns `None` if the buffer size would overflow or if one of the sizes are negative.
757
4.93k
fn num_bytes(width: i32, length: i32, channels: usize) -> Option<usize> {
758
4.93k
    if width <= 0 || length <= 0 {
759
10
        None
760
    } else {
761
4.92k
        match channels.checked_mul(width as usize) {
762
4.92k
            Some(n) => n.checked_mul(length as usize),
763
0
            None => None,
764
        }
765
    }
766
4.93k
}
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.31k
fn with_rows_resumable<F>(
776
2.31k
    buffer: &mut [u8],
777
2.31k
    width: i32,
778
2.31k
    height: i32,
779
2.31k
    channels: usize,
780
2.31k
    top_down: bool,
781
2.31k
    start_row: u32,
782
2.31k
    mut func: F,
783
2.31k
) -> Result<u32, (u32, io::Error)>
784
2.31k
where
785
2.31k
    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.31k
    let row_width = channels.checked_mul(width as usize).unwrap();
790
2.31k
    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
827k
    fn output_row_index(file_row: u32, height: u32, top_down: bool) -> usize {
797
827k
        if top_down {
798
177k
            file_row as usize
799
        } else {
800
650k
            (height - 1 - file_row) as usize
801
        }
802
827k
    }
803
804
    /// Get a mutable reference to a specific row in the output buffer.
805
    #[inline]
806
827k
    fn get_row_mut(buf: &mut [u8], row_index: usize, row_stride: usize) -> &mut [u8] {
807
827k
        let start = row_index * row_stride;
808
827k
        &mut buf[start..][..row_stride]
809
827k
    }
810
811
827k
    for file_row in start_row..height {
812
827k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
827k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
827k
        if let Err(e) = func(row) {
816
2.03k
            return Err((file_row, e));
817
825k
        }
818
    }
819
288
    Ok(height)
820
2.31k
}
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
349
fn with_rows_resumable<F>(
776
349
    buffer: &mut [u8],
777
349
    width: i32,
778
349
    height: i32,
779
349
    channels: usize,
780
349
    top_down: bool,
781
349
    start_row: u32,
782
349
    mut func: F,
783
349
) -> Result<u32, (u32, io::Error)>
784
349
where
785
349
    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
349
    let row_width = channels.checked_mul(width as usize).unwrap();
790
349
    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
76.4k
    for file_row in start_row..height {
812
76.4k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
76.4k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
76.4k
        if let Err(e) = func(row) {
816
321
            return Err((file_row, e));
817
76.1k
        }
818
    }
819
28
    Ok(height)
820
349
}
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
398
fn with_rows_resumable<F>(
776
398
    buffer: &mut [u8],
777
398
    width: i32,
778
398
    height: i32,
779
398
    channels: usize,
780
398
    top_down: bool,
781
398
    start_row: u32,
782
398
    mut func: F,
783
398
) -> Result<u32, (u32, io::Error)>
784
398
where
785
398
    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
398
    let row_width = channels.checked_mul(width as usize).unwrap();
790
398
    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
10.2k
    for file_row in start_row..height {
812
10.2k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
10.2k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
10.2k
        if let Err(e) = func(row) {
816
374
            return Err((file_row, e));
817
9.83k
        }
818
    }
819
24
    Ok(height)
820
398
}
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
665
fn with_rows_resumable<F>(
776
665
    buffer: &mut [u8],
777
665
    width: i32,
778
665
    height: i32,
779
665
    channels: usize,
780
665
    top_down: bool,
781
665
    start_row: u32,
782
665
    mut func: F,
783
665
) -> Result<u32, (u32, io::Error)>
784
665
where
785
665
    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
665
    let row_width = channels.checked_mul(width as usize).unwrap();
790
665
    let height = height as u32;
791
792
    /// Get the index of a row in the output buffer given the file row index.
793
    /// For top-down images, row 0 in the file is row 0 in the buffer.
794
    /// For bottom-up images, row 0 in the file is the last row in the buffer.
795
    #[inline]
796
    fn output_row_index(file_row: u32, height: u32, top_down: bool) -> usize {
797
        if top_down {
798
            file_row as usize
799
        } else {
800
            (height - 1 - file_row) as usize
801
        }
802
    }
803
804
    /// Get a mutable reference to a specific row in the output buffer.
805
    #[inline]
806
    fn get_row_mut(buf: &mut [u8], row_index: usize, row_stride: usize) -> &mut [u8] {
807
        let start = row_index * row_stride;
808
        &mut buf[start..][..row_stride]
809
    }
810
811
158k
    for file_row in start_row..height {
812
158k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
158k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
158k
        if let Err(e) = func(row) {
816
602
            return Err((file_row, e));
817
157k
        }
818
    }
819
63
    Ok(height)
820
665
}
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
907
fn with_rows_resumable<F>(
776
907
    buffer: &mut [u8],
777
907
    width: i32,
778
907
    height: i32,
779
907
    channels: usize,
780
907
    top_down: bool,
781
907
    start_row: u32,
782
907
    mut func: F,
783
907
) -> Result<u32, (u32, io::Error)>
784
907
where
785
907
    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
907
    let row_width = channels.checked_mul(width as usize).unwrap();
790
907
    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
582k
    for file_row in start_row..height {
812
582k
        let out_row_idx = output_row_index(file_row, height, top_down);
813
582k
        let row = get_row_mut(buffer, out_row_idx, row_width);
814
815
582k
        if let Err(e) = func(row) {
816
734
            return Err((file_row, e));
817
582k
        }
818
    }
819
173
    Ok(height)
820
907
}
821
822
78.9k
fn set_8bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
823
78.9k
    pixel_iter: &mut ChunksExactMut<u8>,
824
78.9k
    palette: &[[u8; 3]],
825
78.9k
    indices: T,
826
78.9k
    n_pixels: usize,
827
78.9k
) -> bool {
828
1.12M
    for idx in indices.take(n_pixels) {
829
1.12M
        if let Some(pixel) = pixel_iter.next() {
830
1.12M
            let rgb = palette[*idx as usize];
831
1.12M
            pixel[0] = rgb[0];
832
1.12M
            pixel[1] = rgb[1];
833
1.12M
            pixel[2] = rgb[2];
834
1.12M
        } else {
835
1.37k
            return false;
836
        }
837
    }
838
77.5k
    true
839
78.9k
}
840
841
1.65M
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
842
1.65M
    pixel_iter: &mut ChunksExactMut<u8>,
843
1.65M
    palette: &[[u8; 3]],
844
1.65M
    indices: T,
845
1.65M
    mut n_pixels: usize,
846
1.65M
) -> bool {
847
6.27M
    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
6.27M
        set_pixel!(idx >> 4);
865
4.67M
        set_pixel!(idx & 0xf);
866
    }
867
103k
    true
868
1.65M
}
image::codecs::bmp::decoder::set_4bit_pixel_run::<core::slice::iter::Iter<u8>>
Line
Count
Source
841
21.1k
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
842
21.1k
    pixel_iter: &mut ChunksExactMut<u8>,
843
21.1k
    palette: &[[u8; 3]],
844
21.1k
    indices: T,
845
21.1k
    mut n_pixels: usize,
846
21.1k
) -> bool {
847
408k
    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
402k
        set_pixel!(idx >> 4);
865
391k
        set_pixel!(idx & 0xf);
866
    }
867
9.98k
    true
868
21.1k
}
image::codecs::bmp::decoder::set_4bit_pixel_run::<core::iter::sources::repeat::Repeat<&u8>>
Line
Count
Source
841
1.63M
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
842
1.63M
    pixel_iter: &mut ChunksExactMut<u8>,
843
1.63M
    palette: &[[u8; 3]],
844
1.63M
    indices: T,
845
1.63M
    mut n_pixels: usize,
846
1.63M
) -> bool {
847
5.87M
    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
5.87M
        set_pixel!(idx >> 4);
865
4.28M
        set_pixel!(idx & 0xf);
866
    }
867
93.6k
    true
868
1.63M
}
869
870
#[rustfmt::skip]
871
45.7k
fn set_2bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
872
45.7k
    pixel_iter: &mut ChunksExactMut<u8>,
873
45.7k
    palette: &[[u8; 3]],
874
45.7k
    indices: T,
875
45.7k
    mut n_pixels: usize,
876
45.7k
) -> bool {
877
362k
    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
361k
        set_pixel!((idx >> 6) & 0x3u8);
895
328k
        set_pixel!((idx >> 4) & 0x3u8);
896
318k
        set_pixel!((idx >> 2) & 0x3u8);
897
317k
        set_pixel!( idx       & 0x3u8);
898
    }
899
45.7k
    true
900
45.7k
}
901
902
464k
fn set_1bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
903
464k
    pixel_iter: &mut ChunksExactMut<u8>,
904
464k
    palette: &[[u8; 3]],
905
464k
    indices: T,
906
464k
) {
907
2.20M
    for idx in indices {
908
2.20M
        let mut bit = 0x80;
909
        loop {
910
16.4M
            if let Some(pixel) = pixel_iter.next() {
911
15.9M
                let rgb = palette[usize::from((idx & bit) != 0)];
912
15.9M
                pixel[0] = rgb[0];
913
15.9M
                pixel[1] = rgb[1];
914
15.9M
                pixel[2] = rgb[2];
915
15.9M
            } else {
916
461k
                return;
917
            }
918
919
15.9M
            bit >>= 1;
920
15.9M
            if bit == 0 {
921
1.74M
                break;
922
14.2M
            }
923
        }
924
    }
925
464k
}
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.20k
    const fn from_len_shift(len: u32, shift: u32) -> Self {
952
2.20k
        debug_assert!(len <= 8);
953
2.20k
        debug_assert!(shift + len <= 32);
954
2.20k
        Bitfield {
955
2.20k
            shift,
956
2.20k
            len,
957
2.20k
            factor_addend: Self::FACTOR_ADDEND[(len % 8) as usize],
958
2.20k
        }
959
2.20k
    }
960
961
2.30k
    fn from_mask(mask: u32, max_len: u32) -> ImageResult<Bitfield> {
962
2.30k
        if mask == 0 {
963
729
            return Ok(Bitfield::from_len_shift(0, 0));
964
1.57k
        }
965
1.57k
        let mut shift = mask.trailing_zeros();
966
1.57k
        let mut len = (!(mask >> shift)).trailing_zeros();
967
1.57k
        if len != mask.count_ones() {
968
79
            return Err(DecoderError::BitfieldMaskNonContiguous.into());
969
1.49k
        }
970
1.49k
        if len + shift > max_len {
971
17
            return Err(DecoderError::BitfieldMaskInvalid.into());
972
1.47k
        }
973
1.47k
        if len > 8 {
974
567
            shift += len - 8;
975
567
            len = 8;
976
910
        }
977
1.47k
        Ok(Bitfield::from_len_shift(len, shift))
978
2.30k
    }
979
980
    #[inline]
981
3.54M
    fn read(&self, data: u32) -> u8 {
982
3.54M
        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
3.54M
        let (factor, addend) = self.factor_addend;
987
3.54M
        let mask = (1 << self.len) - 1;
988
3.54M
        let data = (data >> self.shift) & mask;
989
3.54M
        ((data * factor + addend) >> 8) as u8
990
3.54M
    }
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
629
    fn from_mask(
1003
629
        r_mask: u32,
1004
629
        g_mask: u32,
1005
629
        b_mask: u32,
1006
629
        a_mask: u32,
1007
629
        max_len: u32,
1008
629
        spec_strictness: SpecCompliance,
1009
629
    ) -> ImageResult<Bitfields> {
1010
533
        let bitfields = Bitfields {
1011
629
            r: Bitfield::from_mask(r_mask, max_len)?,
1012
580
            g: Bitfield::from_mask(g_mask, max_len)?,
1013
558
            b: Bitfield::from_mask(b_mask, max_len)?,
1014
535
            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
533
        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
533
        }
1023
533
        Ok(bitfields)
1024
629
    }
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.83k
    fn new(reader: &'a mut R) -> Self {
1036
1.83k
        Self {
1037
1.83k
            reader,
1038
1.83k
            bytes_read: 0,
1039
1.83k
        }
1040
1.83k
    }
1041
1042
    /// Total bytes consumed since this reader was created.
1043
5.85M
    fn bytes_read(&self) -> u64 {
1044
5.85M
        self.bytes_read
1045
5.85M
    }
1046
1047
31.0M
    fn read_byte(&mut self) -> io::Result<u8> {
1048
31.0M
        let buf = self.reader.fill_buf()?;
1049
31.0M
        if buf.is_empty() {
1050
1.09k
            return Err(io::Error::new(
1051
1.09k
                io::ErrorKind::UnexpectedEof,
1052
1.09k
                "unexpected end of RLE data",
1053
1.09k
            ));
1054
31.0M
        }
1055
31.0M
        let byte = buf[0];
1056
31.0M
        self.reader.consume(1);
1057
31.0M
        self.bytes_read += 1;
1058
31.0M
        Ok(byte)
1059
31.0M
    }
1060
1061
27.8k
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
1062
27.8k
        let mut remaining = buf.len();
1063
27.8k
        let mut offset = 0;
1064
1065
55.7k
        while remaining > 0 {
1066
28.0k
            let available = self.reader.fill_buf()?;
1067
28.0k
            if available.is_empty() {
1068
148
                return Err(io::Error::new(
1069
148
                    io::ErrorKind::UnexpectedEof,
1070
148
                    "unexpected end of RLE data",
1071
148
                ));
1072
27.8k
            }
1073
1074
27.8k
            let to_read = remaining.min(available.len());
1075
27.8k
            buf[offset..offset + to_read].copy_from_slice(&available[..to_read]);
1076
27.8k
            self.reader.consume(to_read);
1077
27.8k
            self.bytes_read += to_read as u64;
1078
27.8k
            offset += to_read;
1079
27.8k
            remaining -= to_read;
1080
        }
1081
1082
27.7k
        Ok(())
1083
27.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.54k
    fn new_decoder(reader: R) -> BmpDecoder<R> {
1114
5.54k
        BmpDecoder {
1115
5.54k
            reader,
1116
5.54k
1117
5.54k
            bmp_header_type: BMPHeaderType::Info,
1118
5.54k
            indexed_color: false,
1119
5.54k
1120
5.54k
            width: 0,
1121
5.54k
            height: 0,
1122
5.54k
            data_offset: 0,
1123
5.54k
            top_down: false,
1124
5.54k
            no_file_header: false,
1125
5.54k
            add_alpha_channel: false,
1126
5.54k
            image_type: ImageType::Palette,
1127
5.54k
1128
5.54k
            bit_count: 0,
1129
5.54k
            colors_used: 0,
1130
5.54k
            palette: None,
1131
5.54k
            bitfields: None,
1132
5.54k
            icc_profile: None,
1133
5.54k
            spec_strictness: SpecCompliance::default(),
1134
5.54k
            state: DecoderState::default(),
1135
5.54k
        }
1136
5.54k
    }
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 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.23k
        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.70k
    pub(crate) fn new_with_ico_format(reader: R) -> ImageResult<BmpDecoder<R>> {
1222
2.70k
        let mut decoder = Self::new_decoder(reader);
1223
2.70k
        decoder.read_metadata_in_ico_format()?;
1224
2.07k
        Ok(decoder)
1225
2.70k
    }
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
496
    pub(crate) fn reader(&mut self) -> &mut R {
1235
496
        &mut self.reader
1236
496
    }
1237
1238
5.54k
    fn read_file_header(&mut self) -> ImageResult<()> {
1239
5.54k
        if self.no_file_header {
1240
2.70k
            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
29
            return Err(DecoderError::BmpSignatureInvalid.into());
1250
2.78k
        }
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.78k
        let data_offset = u32::from_le_bytes([buffer[10], buffer[11], buffer[12], buffer[13]]);
1255
2.78k
        self.data_offset = u64::from(data_offset);
1256
1257
2.78k
        Ok(())
1258
5.54k
    }
1259
1260
    /// Determine the image type from the compression method, bit count, and header type.
1261
4.30k
    fn image_type_from_compression(
1262
4.30k
        compression: u32,
1263
4.30k
        bit_count: u16,
1264
4.30k
        add_alpha_channel: bool,
1265
4.30k
        header_type: &BMPHeaderType,
1266
4.30k
    ) -> ImageResult<ImageType> {
1267
43
        match compression {
1268
376
            BI_RGB => match bit_count {
1269
614
                1 | 2 | 4 | 8 => Ok(ImageType::Palette),
1270
304
                16 => Ok(ImageType::RGB16),
1271
191
                24 => Ok(ImageType::RGB24),
1272
64
                32 if add_alpha_channel => Ok(ImageType::RGBA32),
1273
312
                32 => Ok(ImageType::RGB32),
1274
                _ => {
1275
41
                    Err(DecoderError::InvalidChannelWidth(ChannelWidthError::Rgb, bit_count).into())
1276
                }
1277
            },
1278
492
            BI_RLE8 => match bit_count {
1279
471
                8 => Ok(ImageType::RLE8),
1280
21
                _ => Err(
1281
21
                    DecoderError::InvalidChannelWidth(ChannelWidthError::Rle8, bit_count).into(),
1282
21
                ),
1283
            },
1284
685
            BI_RLE4 => match bit_count {
1285
671
                4 => Ok(ImageType::RLE4),
1286
14
                _ => Err(
1287
14
                    DecoderError::InvalidChannelWidth(ChannelWidthError::Rle4, bit_count).into(),
1288
14
                ),
1289
            },
1290
652
            BI_BITFIELDS | BI_ALPHABITFIELDS => match bit_count {
1291
131
                16 => Ok(ImageType::Bitfields16),
1292
517
                32 => Ok(ImageType::Bitfields32),
1293
4
                _ => Err(DecoderError::InvalidChannelWidth(
1294
4
                    ChannelWidthError::Bitfields,
1295
4
                    bit_count,
1296
4
                )
1297
4
                .into()),
1298
            },
1299
935
            BI_JPEG if *header_type == BMPHeaderType::Os2V2 && bit_count == 24 => {
1300
892
                Ok(ImageType::RLE24)
1301
            }
1302
43
            BI_JPEG if *header_type == BMPHeaderType::Os2V2 => {
1303
37
                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
2
            BI_PNG => Err(ImageError::Unsupported(
1312
2
                UnsupportedError::from_format_and_kind(
1313
2
                    ImageFormat::Bmp.into(),
1314
2
                    UnsupportedErrorKind::GenericFeature("PNG compression".to_owned()),
1315
2
                ),
1316
2
            )),
1317
6
            BI_CMYK | BI_CMYKRLE4 | BI_CMYKRLE8 => Err(ImageError::Unsupported(
1318
6
                UnsupportedError::from_format_and_kind(
1319
6
                    ImageFormat::Bmp.into(),
1320
6
                    UnsupportedErrorKind::GenericFeature("CMYK format".to_owned()),
1321
6
                ),
1322
6
            )),
1323
5
            _ => Err(DecoderError::ImageTypeUnknown(compression).into()),
1324
        }
1325
4.30k
    }
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
786
    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
786
        let mut buffer = [0u8; 8];
1333
786
        self.reader.read_exact(&mut buffer)?;
1334
1335
778
        let parsed = ParsedCoreHeader::parse(&buffer, self.spec_strictness)?;
1336
1337
765
        self.width = parsed.width;
1338
765
        self.height = parsed.height;
1339
765
        self.bit_count = parsed.bit_count;
1340
765
        self.image_type = parsed.image_type;
1341
1342
765
        check_for_overflow(self.width, self.height, self.num_channels())?;
1343
1344
761
        Ok(())
1345
786
    }
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.20k
    fn read_bitmap_os2v2_header(&mut self, header_size: u32) -> ImageResult<()> {
1350
2.20k
        let remaining = (header_size - 4) as usize;
1351
1352
        // Zero-pad to 36 bytes for ParsedInfoHeader::parse.
1353
2.20k
        let mut buffer = [0u8; 36];
1354
2.20k
        let to_read = remaining.min(36);
1355
2.20k
        self.reader.read_exact(&mut buffer[..to_read])?;
1356
1357
        // Skip OS/2-specific fields beyond the BITMAPINFOHEADER portion (max 28 bytes).
1358
2.18k
        if remaining > 36 {
1359
286
            let skip = remaining - 36;
1360
286
            let mut discard = [0u8; 28];
1361
286
            self.reader.read_exact(&mut discard[..skip])?;
1362
1.89k
        }
1363
1364
2.16k
        let parsed = ParsedInfoHeader::parse(&buffer, self.spec_strictness)?;
1365
1366
2.03k
        self.width = parsed.width;
1367
2.03k
        self.height = parsed.height;
1368
2.03k
        self.top_down = parsed.top_down;
1369
2.03k
        self.bit_count = parsed.bit_count;
1370
2.03k
        self.colors_used = parsed.colors_used;
1371
2.03k
        self.image_type = Self::image_type_from_compression(
1372
2.03k
            parsed.compression,
1373
2.03k
            parsed.bit_count,
1374
2.03k
            self.add_alpha_channel,
1375
2.03k
            &self.bmp_header_type,
1376
107
        )?;
1377
1378
1.93k
        check_for_overflow(self.width, self.height, self.num_channels())?;
1379
1380
1.92k
        Ok(())
1381
2.20k
    }
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.29k
    fn read_bitmap_info_header(&mut self) -> ImageResult<BitfieldCompression> {
1388
        // Info header (after size field): 36 bytes minimum
1389
2.29k
        let mut buffer = [0u8; 36];
1390
2.29k
        self.reader.read_exact(&mut buffer)?;
1391
1392
2.27k
        let parsed = ParsedInfoHeader::parse(&buffer, self.spec_strictness)?;
1393
1394
2.26k
        self.width = parsed.width;
1395
2.26k
        self.height = parsed.height;
1396
2.26k
        self.top_down = parsed.top_down;
1397
2.26k
        self.bit_count = parsed.bit_count;
1398
2.26k
        self.colors_used = parsed.colors_used;
1399
2.26k
        self.image_type = Self::image_type_from_compression(
1400
2.26k
            parsed.compression,
1401
2.26k
            parsed.bit_count,
1402
2.26k
            self.add_alpha_channel,
1403
2.26k
            &self.bmp_header_type,
1404
29
        )?;
1405
1406
2.23k
        check_for_overflow(self.width, self.height, self.num_channels())?;
1407
1408
2.23k
        let compression = match parsed.compression {
1409
66
            BI_ALPHABITFIELDS => BitfieldCompression::Rgba,
1410
2.16k
            _ => BitfieldCompression::Rgb,
1411
        };
1412
2.23k
        Ok(compression)
1413
2.29k
    }
1414
1415
647
    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
647
        let has_alpha = matches!(
1420
647
            self.bmp_header_type,
1421
            BMPHeaderType::V3 | BMPHeaderType::V4 | BMPHeaderType::V5
1422
414
        ) || compression == BitfieldCompression::Rgba;
1423
1424
        // Read bitfield masks into buffer
1425
647
        let mut buffer = [0u8; 16];
1426
647
        let buffer = &mut buffer[..if has_alpha { 16 } else { 12 }];
1427
647
        self.reader.read_exact(buffer)?;
1428
1429
        // Parse masks using shared logic
1430
629
        let parsed = ParsedBitfields::parse(buffer, has_alpha);
1431
1432
        // Create Bitfields from parsed masks
1433
629
        self.bitfields = match self.image_type {
1434
            ImageType::Bitfields16 | ImageType::Bitfields32 => {
1435
629
                let max_len = match self.image_type {
1436
123
                    ImageType::Bitfields16 => 16,
1437
506
                    ImageType::Bitfields32 => 32,
1438
0
                    _ => unreachable!(),
1439
                };
1440
629
                Some(Bitfields::from_mask(
1441
629
                    parsed.r_mask,
1442
629
                    parsed.g_mask,
1443
629
                    parsed.b_mask,
1444
629
                    parsed.a_mask,
1445
629
                    max_len,
1446
629
                    self.spec_strictness,
1447
96
                )?)
1448
            }
1449
0
            _ => None,
1450
        };
1451
1452
533
        if self.bitfields.is_some() && parsed.a_mask != 0 {
1453
233
            self.add_alpha_channel = true;
1454
300
        }
1455
1456
533
        Ok(())
1457
647
    }
1458
1459
    /// Read ICC profile data from the file.
1460
121
    fn read_icc_profile(&mut self, icc: &ParsedIccProfile) -> ImageResult<()> {
1461
121
        self.reader.seek(SeekFrom::Start(icc.profile_offset))?;
1462
121
        let mut profile_data = vec![0u8; icc.profile_size as usize];
1463
121
        self.reader.read_exact(&mut profile_data)?;
1464
61
        self.icc_profile = Some(profile_data);
1465
61
        Ok(())
1466
121
    }
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.54k
    pub fn read_metadata(&mut self) -> ImageResult<()> {
1480
        // Check if we're in a metadata reading state
1481
5.54k
        let DecoderState::ReadingMetadata { progress } = self.state else {
1482
0
            return Ok(()); // Already past metadata phase
1483
        };
1484
1485
5.54k
        match self.read_metadata_impl(progress) {
1486
            Ok(()) => {
1487
                // Transition directly to the appropriate image reading state
1488
4.30k
                self.state = if self.is_rle() {
1489
1.84k
                    DecoderState::ReadingRleData {
1490
1.84k
                        progress: RleProgress::NotStarted,
1491
1.84k
                    }
1492
                } else {
1493
2.46k
                    DecoderState::ReadingRowData { rows_decoded: 0 }
1494
                };
1495
4.30k
                Ok(())
1496
            }
1497
1.23k
            Err(e) => Err(e),
1498
        }
1499
5.54k
    }
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.5k
    fn read_metadata_impl(&mut self, progress: MetadataProgress) -> ImageResult<()> {
1509
24.5k
        match progress {
1510
            MetadataProgress::NotStarted => {
1511
                // Record current position and transition to ReadingMainHeader
1512
5.54k
                let start_offset = self.reader.stream_position()?;
1513
5.54k
                let next = MetadataProgress::ReadingMainHeader { start_offset };
1514
5.54k
                self.state = DecoderState::ReadingMetadata { progress: next };
1515
5.54k
                self.read_metadata_impl(next)
1516
            }
1517
5.54k
            MetadataProgress::ReadingMainHeader { start_offset } => {
1518
                // Seek to start position (for retry support)
1519
5.54k
                self.reader.seek(SeekFrom::Start(start_offset))?;
1520
1521
                // Read headers and get offsets for subsequent phases
1522
5.54k
                let offsets = self.read_headers()?;
1523
1524
                // Always progress to ReadingPalette next
1525
4.74k
                let next = MetadataProgress::ReadingPalette { offsets };
1526
4.74k
                self.state = DecoderState::ReadingMetadata { progress: next };
1527
4.74k
                self.read_metadata_impl(next)
1528
            }
1529
4.74k
            MetadataProgress::ReadingPalette { offsets } => {
1530
                // Always seek to palette position (this is also where image data starts
1531
                // for non-palette formats)
1532
4.74k
                self.reader.seek(SeekFrom::Start(offsets.palette_offset))?;
1533
1534
                // Read palette if needed for this image type
1535
2.49k
                if matches!(
1536
4.74k
                    self.image_type,
1537
                    ImageType::Palette | ImageType::RLE4 | ImageType::RLE8
1538
                ) {
1539
2.25k
                    self.read_palette()?;
1540
2.49k
                }
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.36k
                if self.no_file_header {
1548
2.09k
                    self.data_offset = self.reader.stream_position()?;
1549
2.27k
                } else if self.spec_strictness != SpecCompliance::Strict
1550
2.27k
                    && self.data_offset >= FILE_HEADER_SIZE
1551
915
                    && self.data_offset < offsets.bmp_header_end
1552
138
                {
1553
138
                    self.data_offset = offsets.bmp_header_end;
1554
2.13k
                }
1555
1556
                // Always progress to ReadingIccProfile next
1557
4.36k
                let next = MetadataProgress::ReadingIccProfile { offsets };
1558
4.36k
                self.state = DecoderState::ReadingMetadata { progress: next };
1559
4.36k
                self.read_metadata_impl(next)
1560
            }
1561
4.36k
            MetadataProgress::ReadingIccProfile { offsets } => {
1562
                // Read ICC profile if present
1563
4.36k
                if let Some(ref icc) = offsets.icc_profile {
1564
121
                    self.read_icc_profile(icc)?;
1565
4.24k
                }
1566
1567
                // Always progress to Complete next
1568
4.30k
                self.state = DecoderState::ReadingMetadata {
1569
4.30k
                    progress: MetadataProgress::Complete,
1570
4.30k
                };
1571
4.30k
                self.read_metadata_impl(MetadataProgress::Complete)
1572
            }
1573
4.30k
            MetadataProgress::Complete => Ok(()),
1574
        }
1575
24.5k
    }
1576
1577
    /// Read headers phase: file header, DIB header, and bitmasks.
1578
    /// Returns HeaderOffsets containing positions for subsequent phases.
1579
5.54k
    fn read_headers(&mut self) -> ImageResult<HeaderOffsets> {
1580
5.54k
        self.read_file_header()?;
1581
5.48k
        let bmp_header_offset = self.reader.stream_position()?;
1582
1583
        // Read header size into buffer for consistency with buffer-based pattern
1584
5.48k
        let mut size_buffer = [0u8; 4];
1585
5.48k
        self.reader.read_exact(&mut size_buffer)?;
1586
5.48k
        let bmp_header_size = u32::from_le_bytes(size_buffer);
1587
1588
5.48k
        let bmp_header_end = bmp_header_offset + u64::from(bmp_header_size);
1589
1590
2.38k
        self.bmp_header_type = match bmp_header_size {
1591
786
            BITMAPCOREHEADER_SIZE => BMPHeaderType::Core,
1592
1.15k
            BITMAPINFOHEADER_SIZE => BMPHeaderType::Info,
1593
60
            BITMAPV2HEADER_SIZE => BMPHeaderType::V2,
1594
208
            BITMAPV3HEADER_SIZE => BMPHeaderType::V3,
1595
530
            BITMAPV4HEADER_SIZE => BMPHeaderType::V4,
1596
341
            BITMAPV5HEADER_SIZE => BMPHeaderType::V5,
1597
2.39k
            _ if bmp_header_size < BITMAPCOREHEADER_SIZE => {
1598
                // Size of any valid header types won't be smaller than core header type.
1599
14
                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.38k
            _ if (OS2_V2_MIN_HEADER_SIZE..=OS2_V2_MAX_HEADER_SIZE).contains(&bmp_header_size)
1607
2.21k
                && (bmp_header_size % 4 == 0 || bmp_header_size == 42 || bmp_header_size == 46) =>
1608
            {
1609
2.20k
                BMPHeaderType::Os2V2
1610
            }
1611
            _ => {
1612
179
                return Err(ImageError::Unsupported(
1613
179
                    UnsupportedError::from_format_and_kind(
1614
179
                        ImageFormat::Bmp.into(),
1615
179
                        UnsupportedErrorKind::GenericFeature(format!(
1616
179
                            "Unknown bitmap header type (size={bmp_header_size})"
1617
179
                        )),
1618
179
                    ),
1619
179
                ))
1620
            }
1621
        };
1622
1623
5.28k
        let bitfield_compression = match self.bmp_header_type {
1624
            BMPHeaderType::Core => {
1625
786
                self.read_bitmap_core_header()?;
1626
761
                BitfieldCompression::Rgb
1627
            }
1628
            BMPHeaderType::Os2V2 => {
1629
2.20k
                self.read_bitmap_os2v2_header(bmp_header_size)?;
1630
1.92k
                BitfieldCompression::Rgb
1631
            }
1632
            BMPHeaderType::Info
1633
            | BMPHeaderType::V2
1634
            | BMPHeaderType::V3
1635
            | BMPHeaderType::V4
1636
2.29k
            | BMPHeaderType::V5 => self.read_bitmap_info_header()?,
1637
        };
1638
1639
4.92k
        let mut bitmask_bytes_offset = 0;
1640
4.27k
        if matches!(
1641
4.92k
            self.image_type,
1642
            ImageType::Bitfields16 | ImageType::Bitfields32
1643
        ) {
1644
647
            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
381
            if matches!(
1654
533
                self.bmp_header_type,
1655
                BMPHeaderType::Info | BMPHeaderType::V4 | BMPHeaderType::V5
1656
            ) {
1657
152
                bitmask_bytes_offset = match bitfield_compression {
1658
40
                    BitfieldCompression::Rgba => 16, // 4 masks * 4 bytes
1659
112
                    BitfieldCompression::Rgb => 12,  // 3 masks * 4 bytes
1660
                };
1661
381
            }
1662
4.27k
        } 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
249
            let mut masks_buf = [0u8; 16];
1665
249
            self.reader.read_exact(&mut masks_buf)?;
1666
245
            let alpha_mask = u32::from_le_bytes(masks_buf[12..16].try_into().unwrap());
1667
245
            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
224
                if self.spec_strictness == SpecCompliance::Strict && alpha_mask != 0xFF000000 {
1674
0
                    return Err(DecoderError::BitfieldMaskInvalid.into());
1675
224
                }
1676
224
                self.add_alpha_channel = true;
1677
224
                self.image_type = ImageType::RGBA32;
1678
21
            }
1679
4.02k
        };
1680
1681
        // Parse color space fields from V4/V5 header
1682
4.80k
        let mut icc_profile = None;
1683
4.80k
        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
846
            let mut header_buffer = vec![0u8; (bmp_header_size - 4) as usize];
1687
846
            let current_pos = self.reader.stream_position()?;
1688
846
            self.reader.seek(SeekFrom::Start(bmp_header_offset + 4))?;
1689
846
            self.reader.read_exact(&mut header_buffer)?;
1690
1691
            // Extract color space info and handle non-Copy variants immediately
1692
790
            match ColorSpaceInfo::parse(&header_buffer, bmp_header_size, bmp_header_offset) {
1693
106
                Some(ColorSpaceInfo::CalibratedRgb(params)) => {
1694
                    // Synthesize an ICC profile from the calibrated RGB parameters
1695
                    // and store it directly — no file read needed.
1696
106
                    if let Ok(encoded) = params.to_color_profile().encode() {
1697
106
                        self.icc_profile = Some(encoded);
1698
106
                    }
1699
                }
1700
195
                Some(ColorSpaceInfo::EmbeddedIcc(icc)) => {
1701
195
                    icc_profile = Some(icc);
1702
195
                }
1703
                // LCS_sRGB / LCS_WINDOWS_COLOR_SPACE: the caller treats
1704
                // "no ICC profile" as sRGB, so nothing to store.
1705
489
                Some(ColorSpaceInfo::Srgb) | None => {}
1706
            }
1707
1708
            // Seek back to where we were
1709
790
            self.reader.seek(SeekFrom::Start(current_pos))?;
1710
3.95k
        }
1711
1712
        // Calculate palette offset (position after headers)
1713
4.74k
        let palette_offset = bmp_header_end + bitmask_bytes_offset;
1714
1715
4.74k
        Ok(HeaderOffsets {
1716
4.74k
            bmp_header_end,
1717
4.74k
            palette_offset,
1718
4.74k
            icc_profile,
1719
4.74k
        })
1720
5.54k
    }
1721
1722
    #[cfg(feature = "ico")]
1723
    #[doc(hidden)]
1724
2.70k
    pub fn read_metadata_in_ico_format(&mut self) -> ImageResult<()> {
1725
2.70k
        self.no_file_header = true;
1726
2.70k
        self.add_alpha_channel = true;
1727
2.70k
        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.07k
        self.height /= 2;
1732
2.07k
        Ok(())
1733
2.70k
    }
1734
1735
2.25k
    fn get_palette_size(&mut self) -> ImageResult<usize> {
1736
2.25k
        match self.colors_used {
1737
832
            0 => Ok(1 << self.bit_count),
1738
            _ => {
1739
1.42k
                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.42k
                }
1748
                // In lenient mode, clamp to max palette size for the bit depth
1749
1.42k
                let max_size = 1usize << self.bit_count;
1750
1.42k
                Ok((self.colors_used as usize).min(max_size))
1751
            }
1752
        }
1753
2.25k
    }
1754
1755
2.25k
    fn bytes_per_color(&self) -> usize {
1756
2.25k
        match self.bmp_header_type {
1757
504
            BMPHeaderType::Core => 3,
1758
1.75k
            _ => 4,
1759
        }
1760
2.25k
    }
1761
1762
2.25k
    fn read_palette(&mut self) -> ImageResult<()> {
1763
        const MAX_PALETTE_SIZE: usize = 256; // Palette indices are u8.
1764
1765
2.25k
        let bytes_per_color = self.bytes_per_color();
1766
2.25k
        let palette_size = self.get_palette_size()?;
1767
2.25k
        let max_length = MAX_PALETTE_SIZE * bytes_per_color;
1768
1769
2.25k
        let length = palette_size * bytes_per_color;
1770
2.25k
        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.25k
        buf.resize(cmp::min(length, max_length), 0);
1776
2.25k
        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
1.87k
        match length.cmp(&max_length) {
1781
0
            Ordering::Greater => self.reader.seek_relative((length - max_length) as i64)?,
1782
1.84k
            Ordering::Less => buf.resize(max_length, 0),
1783
33
            Ordering::Equal => (),
1784
        }
1785
1786
1.87k
        let p: Vec<[u8; 3]> = (0..MAX_PALETTE_SIZE)
1787
480k
            .map(|i| {
1788
480k
                let b = buf[bytes_per_color * i];
1789
480k
                let g = buf[bytes_per_color * i + 1];
1790
480k
                let r = buf[bytes_per_color * i + 2];
1791
480k
                [r, g, b]
1792
480k
            })
1793
1.87k
            .collect();
1794
1795
1.87k
        self.palette = Some(p);
1796
1797
1.87k
        Ok(())
1798
2.25k
    }
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.9k
    fn num_channels(&self) -> usize {
1806
10.9k
        if self.indexed_color {
1807
0
            1
1808
10.9k
        } else if self.add_alpha_channel {
1809
5.74k
            4
1810
        } else {
1811
5.18k
            3
1812
        }
1813
10.9k
    }
1814
1815
1.83k
    fn rows<'a>(&self, pixel_data: &'a mut [u8]) -> RowIterator<'a> {
1816
1.83k
        let stride = self.width as usize * self.num_channels();
1817
1.83k
        if self.top_down {
1818
370
            RowIterator {
1819
370
                chunks: Chunker::FromTop(pixel_data.chunks_exact_mut(stride)),
1820
370
            }
1821
        } else {
1822
1.46k
            RowIterator {
1823
1.46k
                chunks: Chunker::FromBottom(pixel_data.chunks_exact_mut(stride).rev()),
1824
1.46k
            }
1825
        }
1826
1.83k
    }
1827
1828
907
    fn read_palettized_pixel_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
1829
907
        let num_channels = self.num_channels();
1830
907
        let row_byte_length = ((i32::from(self.bit_count) * self.width + 31) / 32 * 4) as usize;
1831
907
        let mut indices = vec![0; row_byte_length];
1832
907
        let palette = self.palette.as_ref().unwrap();
1833
907
        let bit_count = self.bit_count;
1834
907
        let width = self.width as usize;
1835
907
        let skip_palette = self.indexed_color;
1836
1837
907
        let rows_decoded = self.rows_decoded();
1838
907
        let start_row = rows_decoded.rows();
1839
907
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
1840
1841
907
        let file_offset = self.data_offset + (start_row as u64 * row_byte_length as u64);
1842
907
        self.reader.seek(SeekFrom::Start(file_offset))?;
1843
1844
        // Set alpha to opaque for all pixels if needed (only on first call)
1845
907
        if start_row == 0 && num_channels == 4 {
1846
458
            buf.as_chunks_mut::<4>()
1847
458
                .0
1848
458
                .iter_mut()
1849
2.21G
                .for_each(|c| c[3] = ALPHA_OPAQUE);
1850
449
        }
1851
1852
907
        let spec_strictness = self.spec_strictness;
1853
907
        let last_row: u32 = (self.height - 1).try_into().unwrap();
1854
907
        let mut current_file_row = start_row;
1855
907
        let reader = &mut self.reader;
1856
907
        let result = with_rows_resumable(
1857
907
            buf,
1858
907
            self.width,
1859
907
            self.height,
1860
907
            num_channels,
1861
907
            top_down,
1862
907
            start_row,
1863
582k
            |row| {
1864
582k
                read_scanline(
1865
582k
                    reader,
1866
582k
                    &mut indices,
1867
582k
                    &mut current_file_row,
1868
582k
                    last_row,
1869
582k
                    spec_strictness,
1870
734
                )?;
1871
582k
                if skip_palette {
1872
0
                    row.clone_from_slice(&indices[0..width]);
1873
0
                } else {
1874
582k
                    let mut pixel_iter = row.chunks_exact_mut(num_channels);
1875
582k
                    match bit_count {
1876
464k
                        1 => {
1877
464k
                            set_1bit_pixel_run(&mut pixel_iter, palette, indices.iter());
1878
464k
                        }
1879
45.7k
                        2 => {
1880
45.7k
                            set_2bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
1881
45.7k
                        }
1882
1.28k
                        4 => {
1883
1.28k
                            set_4bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
1884
1.28k
                        }
1885
71.0k
                        8 => {
1886
71.0k
                            set_8bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
1887
71.0k
                        }
1888
0
                        _ => panic!(),
1889
                    }
1890
                }
1891
582k
                Ok(())
1892
582k
            },
1893
        );
1894
1895
907
        self.finish_row_decode(result)
1896
907
    }
1897
1898
349
    fn read_16_bit_pixel_data(
1899
349
        &mut self,
1900
349
        buf: &mut [u8],
1901
349
        bitfields: Option<&Bitfields>,
1902
349
    ) -> ImageResult<()> {
1903
349
        let num_channels = self.num_channels();
1904
349
        let bitfields = match bitfields {
1905
279
            Some(b) => b,
1906
70
            None => self.bitfields.as_ref().unwrap(),
1907
        };
1908
1909
349
        let row_data_len = self.width as usize * 2;
1910
349
        let row_padding_len = calculate_row_padding(row_data_len);
1911
349
        let total_row_len = row_data_len + row_padding_len;
1912
1913
349
        let rows_decoded = self.rows_decoded();
1914
349
        let start_row = rows_decoded.rows();
1915
349
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
1916
349
        let width = self.width;
1917
349
        let height = self.height;
1918
1919
349
        let file_offset = self.data_offset + (start_row as u64 * total_row_len as u64);
1920
349
        self.reader.seek(SeekFrom::Start(file_offset))?;
1921
1922
349
        let mut row_buffer = allocate_row_buffer(total_row_len)?;
1923
1924
349
        let spec_strictness = self.spec_strictness;
1925
349
        let last_row: u32 = (height - 1).try_into().unwrap();
1926
349
        let mut current_file_row = start_row;
1927
349
        let reader = &mut self.reader;
1928
349
        let result = with_rows_resumable(
1929
349
            buf,
1930
349
            width,
1931
349
            height,
1932
349
            num_channels,
1933
349
            top_down,
1934
349
            start_row,
1935
76.4k
            |row| {
1936
76.4k
                read_scanline(
1937
76.4k
                    reader,
1938
76.4k
                    &mut row_buffer,
1939
76.4k
                    &mut current_file_row,
1940
76.4k
                    last_row,
1941
76.4k
                    spec_strictness,
1942
321
                )?;
1943
76.1k
                let row_buffer_chunks = row_buffer.as_chunks::<2>().0.iter();
1944
303k
                for (&row_data, pixel) in row_buffer_chunks.zip(row.chunks_exact_mut(num_channels))
1945
                {
1946
303k
                    let data = u32::from(u16::from_le_bytes(row_data));
1947
303k
                    pixel[0] = bitfields.r.read(data);
1948
303k
                    pixel[1] = bitfields.g.read(data);
1949
303k
                    pixel[2] = bitfields.b.read(data);
1950
303k
                    if num_channels == 4 {
1951
230k
                        pixel[3] = if bitfields.a.len != 0 {
1952
76.3k
                            bitfields.a.read(data)
1953
                        } else {
1954
154k
                            ALPHA_OPAQUE
1955
                        };
1956
72.5k
                    }
1957
                }
1958
76.1k
                Ok(())
1959
76.4k
            },
1960
        );
1961
1962
349
        self.finish_row_decode(result)
1963
349
    }
1964
1965
    /// Read image data from a reader in 32-bit formats that use bitfields.
1966
398
    fn read_32_bit_pixel_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
1967
398
        let num_channels = self.num_channels();
1968
398
        let bitfields = self.bitfields.as_ref().unwrap();
1969
1970
398
        let row_data_len = self.width as usize * 4;
1971
1972
398
        let rows_decoded = self.rows_decoded();
1973
398
        let start_row = rows_decoded.rows();
1974
398
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
1975
398
        let width = self.width;
1976
398
        let height = self.height;
1977
1978
398
        let file_offset = self.data_offset + (start_row as u64 * row_data_len as u64);
1979
398
        self.reader.seek(SeekFrom::Start(file_offset))?;
1980
1981
398
        let mut row_buffer = allocate_row_buffer(row_data_len)?;
1982
1983
398
        let spec_strictness = self.spec_strictness;
1984
398
        let last_row: u32 = (height - 1).try_into().unwrap();
1985
398
        let mut current_file_row = start_row;
1986
398
        let reader = &mut self.reader;
1987
398
        let result = with_rows_resumable(
1988
398
            buf,
1989
398
            width,
1990
398
            height,
1991
398
            num_channels,
1992
398
            top_down,
1993
398
            start_row,
1994
10.2k
            |row| {
1995
10.2k
                read_scanline(
1996
10.2k
                    reader,
1997
10.2k
                    &mut row_buffer,
1998
10.2k
                    &mut current_file_row,
1999
10.2k
                    last_row,
2000
10.2k
                    spec_strictness,
2001
374
                )?;
2002
9.83k
                let row_buffer_chunks = row_buffer.as_chunks::<4>().0.iter();
2003
806k
                for (&row_data, pixel) in row_buffer_chunks.zip(row.chunks_exact_mut(num_channels))
2004
                {
2005
806k
                    let data = u32::from_le_bytes(row_data);
2006
806k
                    pixel[0] = bitfields.r.read(data);
2007
806k
                    pixel[1] = bitfields.g.read(data);
2008
806k
                    pixel[2] = bitfields.b.read(data);
2009
806k
                    if num_channels == 4 {
2010
542k
                        pixel[3] = if bitfields.a.len != 0 {
2011
135k
                            bitfields.a.read(data)
2012
                        } else {
2013
407k
                            ALPHA_OPAQUE
2014
                        };
2015
263k
                    }
2016
                }
2017
9.83k
                Ok(())
2018
10.2k
            },
2019
        );
2020
2021
398
        self.finish_row_decode(result)
2022
398
    }
2023
2024
    /// Read image data from a reader where the colours are stored as 8-bit values (24 or 32-bit).
2025
665
    fn read_full_byte_pixel_data(
2026
665
        &mut self,
2027
665
        buf: &mut [u8],
2028
665
        format: &FormatFullBytes,
2029
665
    ) -> ImageResult<()> {
2030
665
        let num_channels = self.num_channels();
2031
665
        let row_data_len = match *format {
2032
362
            FormatFullBytes::RGB24 => self.width as usize * 3,
2033
21
            FormatFullBytes::Format888 => self.width as usize * 4,
2034
282
            FormatFullBytes::RGB32 | FormatFullBytes::RGBA32 => self.width as usize * 4,
2035
        };
2036
665
        let row_padding_len = match *format {
2037
362
            FormatFullBytes::RGB24 => calculate_row_padding(row_data_len),
2038
303
            _ => 0,
2039
        };
2040
665
        let total_row_len = row_data_len + row_padding_len;
2041
2042
665
        let rows_decoded = self.rows_decoded();
2043
665
        let start_row = rows_decoded.rows();
2044
665
        let top_down = matches!(rows_decoded, RowsDecoded::TopDown { .. });
2045
665
        let width = self.width;
2046
665
        let height = self.height;
2047
2048
665
        let file_offset = self.data_offset + (start_row as u64 * total_row_len as u64);
2049
665
        self.reader.seek(SeekFrom::Start(file_offset))?;
2050
2051
665
        let mut row_buffer = allocate_row_buffer(total_row_len)?;
2052
2053
665
        let spec_strictness = self.spec_strictness;
2054
665
        let last_row: u32 = (height - 1).try_into().unwrap();
2055
665
        let mut current_file_row = start_row;
2056
665
        let reader = &mut self.reader;
2057
665
        let result = with_rows_resumable(
2058
665
            buf,
2059
665
            width,
2060
665
            height,
2061
665
            num_channels,
2062
665
            top_down,
2063
665
            start_row,
2064
158k
            |row| {
2065
158k
                read_scanline(
2066
158k
                    reader,
2067
158k
                    &mut row_buffer,
2068
158k
                    &mut current_file_row,
2069
158k
                    last_row,
2070
158k
                    spec_strictness,
2071
602
                )?;
2072
2073
899k
                for (i, pixel) in row.chunks_mut(num_channels).enumerate() {
2074
899k
                    let offset = match *format {
2075
16.7k
                        FormatFullBytes::Format888 => i * 4 + 1, // Skip first byte
2076
                        _ => {
2077
882k
                            i * match *format {
2078
347k
                                FormatFullBytes::RGB24 => 3,
2079
535k
                                _ => 4,
2080
                            }
2081
                        }
2082
                    };
2083
2084
                    // Read the colour values (b, g, r) and reverse to (r, g, b)
2085
899k
                    pixel[0..3].copy_from_slice(&row_buffer[offset..offset + 3]);
2086
899k
                    pixel[0..3].reverse();
2087
2088
                    // Read the alpha channel if present
2089
899k
                    if *format == FormatFullBytes::RGBA32 {
2090
463k
                        pixel[3] = row_buffer[offset + 3];
2091
463k
                    } else if num_channels == 4 {
2092
135k
                        pixel[3] = ALPHA_OPAQUE;
2093
299k
                    }
2094
                }
2095
157k
                Ok(())
2096
158k
            },
2097
        );
2098
2099
665
        self.finish_row_decode(result)
2100
665
    }
2101
2102
1.83k
    fn read_rle_data(&mut self, buf: &mut [u8], image_type: ImageType) -> ImageResult<()> {
2103
1.83k
        let (start_row, start_x, start_pos) = match self.state {
2104
            DecoderState::ReadingRleData {
2105
                progress: RleProgress::NotStarted,
2106
1.83k
            } => (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.83k
        self.reader.seek(SeekFrom::Start(start_pos))?;
2114
2115
1.83k
        let num_channels = self.num_channels();
2116
1.83k
        let p = if image_type != ImageType::RLE24 {
2117
952
            Some(self.palette.as_ref().unwrap())
2118
        } else {
2119
886
            None
2120
        };
2121
2122
1.83k
        let mut row_iter = self.rows(buf).skip(start_row as usize);
2123
1.83k
        let mut current_row = start_row;
2124
1.83k
        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.83k
        let mut rle_indices_buffer = [0u8; 256];
2129
2130
1.83k
        let mut rle_reader = RleReader::new(&mut self.reader);
2131
2132
576k
        while let Some(row) = row_iter.next() {
2133
576k
            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
576k
            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
576k
                0
2141
            };
2142
576k
            first_row_iteration = false;
2143
2144
            loop {
2145
5.85M
                let control_byte = rle_reader.read_byte()?;
2146
2147
5.85M
                match control_byte {
2148
                    RLE_ESCAPE => {
2149
915k
                        let op = rle_reader.read_byte()?;
2150
915k
                        match op {
2151
                            RLE_ESCAPE_EOL => {
2152
312M
                                pixel_iter.for_each(|p| p.fill(0));
2153
574k
                                current_row += 1;
2154
574k
                                x = 0;
2155
574k
                                break;
2156
                            }
2157
                            RLE_ESCAPE_EOF => {
2158
3.75M
                                pixel_iter.for_each(|p| p.fill(0));
2159
970M
                                row_iter.for_each(|r| r.fill(0));
2160
540
                                return Ok(());
2161
                            }
2162
                            RLE_ESCAPE_DELTA => {
2163
212k
                                let x_delta = rle_reader.read_byte()?;
2164
212k
                                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
212k
                                if y_delta > 0 {
2169
34.4M
                                    pixel_iter.for_each(|p| p.fill(0));
2170
2171
64.6k
                                    for _ in 1..y_delta {
2172
646k
                                        if let Some(row) = row_iter.next() {
2173
646k
                                            row.fill(0);
2174
646k
                                        } else if self.spec_strictness == SpecCompliance::Strict {
2175
0
                                            return Err(DecoderError::CorruptRleData.into());
2176
                                        } else {
2177
26
                                            return Ok(());
2178
                                        }
2179
                                    }
2180
2181
64.5k
                                    current_row += y_delta as u32;
2182
64.5k
                                    if let Some(next_row) = row_iter.next() {
2183
64.5k
                                        pixel_iter = next_row.chunks_exact_mut(num_channels);
2184
64.5k
                                    } else if self.spec_strictness == SpecCompliance::Strict {
2185
0
                                        return Err(DecoderError::CorruptRleData.into());
2186
                                    } else {
2187
6
                                        return Ok(());
2188
                                    }
2189
2190
64.5k
                                    for _ in 0..x {
2191
14.6M
                                        if let Some(pixel) = pixel_iter.next() {
2192
14.6M
                                            pixel.fill(0);
2193
14.6M
                                        } else if self.spec_strictness == SpecCompliance::Strict {
2194
0
                                            return Err(DecoderError::CorruptRleData.into());
2195
                                        } else {
2196
19.7k
                                            break;
2197
                                        }
2198
                                    }
2199
147k
                                }
2200
2201
212k
                                for _ in 0..x_delta {
2202
652k
                                    if let Some(pixel) = pixel_iter.next() {
2203
644k
                                        pixel.fill(0);
2204
644k
                                    } else if self.spec_strictness == SpecCompliance::Strict {
2205
0
                                        return Err(DecoderError::CorruptRleData.into());
2206
                                    } else {
2207
7.98k
                                        break;
2208
                                    }
2209
                                }
2210
212k
                                x += x_delta as u32;
2211
                            }
2212
                            _ => {
2213
                                // Absolute mode: pixel data differs by RLE type.
2214
127k
                                let count = op as usize;
2215
127k
                                match image_type {
2216
                                    ImageType::RLE8 => {
2217
7.94k
                                        let mut length = count;
2218
7.94k
                                        length += length & 1;
2219
7.94k
                                        rle_reader.read_exact(&mut rle_indices_buffer[..length])?;
2220
                                        // Silently truncate if run overflows the row.
2221
7.86k
                                        let success = set_8bit_pixel_run(
2222
7.86k
                                            &mut pixel_iter,
2223
7.86k
                                            p.unwrap(),
2224
7.86k
                                            rle_indices_buffer[..length].iter(),
2225
7.86k
                                            count,
2226
                                        );
2227
7.86k
                                        if self.spec_strictness == SpecCompliance::Strict
2228
0
                                            && !success
2229
                                        {
2230
0
                                            return Err(DecoderError::CorruptRleData.into());
2231
7.86k
                                        }
2232
                                    }
2233
                                    ImageType::RLE4 => {
2234
19.9k
                                        let mut length = count.div_ceil(2);
2235
19.9k
                                        length += length & 1;
2236
19.9k
                                        rle_reader.read_exact(&mut rle_indices_buffer[..length])?;
2237
                                        // Silently truncate if run overflows the row.
2238
19.8k
                                        let success = set_4bit_pixel_run(
2239
19.8k
                                            &mut pixel_iter,
2240
19.8k
                                            p.unwrap(),
2241
19.8k
                                            rle_indices_buffer[..length].iter(),
2242
19.8k
                                            count,
2243
                                        );
2244
19.8k
                                        if self.spec_strictness == SpecCompliance::Strict
2245
0
                                            && !success
2246
                                        {
2247
0
                                            return Err(DecoderError::CorruptRleData.into());
2248
19.8k
                                        }
2249
                                    }
2250
                                    ImageType::RLE24 => {
2251
99.8k
                                        for _ in 0..count {
2252
4.65M
                                            let b = rle_reader.read_byte()?;
2253
4.65M
                                            let g = rle_reader.read_byte()?;
2254
4.65M
                                            let r = rle_reader.read_byte()?;
2255
4.65M
                                            if let Some(pixel) = pixel_iter.next() {
2256
1.66M
                                                pixel[0] = r;
2257
1.66M
                                                pixel[1] = g;
2258
1.66M
                                                pixel[2] = b;
2259
2.99M
                                            }
2260
                                        }
2261
                                        // RLE24 absolute mode pads to word (2-byte) boundary.
2262
99.6k
                                        if !(count * 3).is_multiple_of(2) {
2263
58.9k
                                            rle_reader.read_byte()?;
2264
40.7k
                                        }
2265
                                    }
2266
0
                                    _ => unreachable!(),
2267
                                }
2268
127k
                                x += count as u32;
2269
                            }
2270
                        }
2271
                    }
2272
                    _ => {
2273
                        // Encoded run: pixel data differs by RLE type.
2274
4.93M
                        let n_pixels = control_byte as usize;
2275
4.93M
                        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
865k
                                let palette_index = rle_reader.read_byte()?;
2280
865k
                                let repeat_pixel: [u8; 3] = p.unwrap()[palette_index as usize];
2281
5.67M
                                (&mut pixel_iter).take(n_pixels).for_each(|p| {
2282
5.67M
                                    p[0] = repeat_pixel[0];
2283
5.67M
                                    p[1] = repeat_pixel[1];
2284
5.67M
                                    p[2] = repeat_pixel[2];
2285
5.67M
                                });
2286
                            }
2287
                            ImageType::RLE4 => {
2288
1.63M
                                let palette_index = rle_reader.read_byte()?;
2289
                                // Silently truncate if run overflows the row
2290
                                // (matches RLE8 encoded run behavior).
2291
1.63M
                                let success = set_4bit_pixel_run(
2292
1.63M
                                    &mut pixel_iter,
2293
1.63M
                                    p.unwrap(),
2294
1.63M
                                    repeat(&palette_index),
2295
1.63M
                                    n_pixels,
2296
                                );
2297
1.63M
                                if self.spec_strictness == SpecCompliance::Strict && !success {
2298
0
                                    return Err(DecoderError::CorruptRleData.into());
2299
1.63M
                                }
2300
                            }
2301
                            ImageType::RLE24 => {
2302
2.43M
                                let b = rle_reader.read_byte()?;
2303
2.43M
                                let g = rle_reader.read_byte()?;
2304
2.43M
                                let r = rle_reader.read_byte()?;
2305
2.43M
                                for _ in 0..n_pixels {
2306
318M
                                    if let Some(pixel) = pixel_iter.next() {
2307
11.3M
                                        pixel[0] = r;
2308
11.3M
                                        pixel[1] = g;
2309
11.3M
                                        pixel[2] = b;
2310
307M
                                    }
2311
                                }
2312
                            }
2313
0
                            _ => unreachable!(),
2314
                        }
2315
4.93M
                        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
5.27M
                self.state = DecoderState::ReadingRleData {
2322
5.27M
                    progress: RleProgress::Checkpoint {
2323
5.27M
                        row: current_row,
2324
5.27M
                        x,
2325
5.27M
                        stream_pos: start_pos + rle_reader.bytes_read(),
2326
5.27M
                    },
2327
5.27M
                };
2328
            }
2329
2330
            // Checkpoint after EndOfRow (which breaks out of the inner loop).
2331
574k
            self.state = DecoderState::ReadingRleData {
2332
574k
                progress: RleProgress::Checkpoint {
2333
574k
                    row: current_row,
2334
574k
                    x,
2335
574k
                    stream_pos: start_pos + rle_reader.bytes_read(),
2336
574k
                },
2337
574k
            };
2338
        }
2339
2340
25
        Ok(())
2341
1.83k
    }
2342
2343
    /// Determine if the current image type is RLE-compressed.
2344
4.30k
    fn is_rle(&self) -> bool {
2345
2.46k
        matches!(
2346
4.30k
            self.image_type,
2347
            ImageType::RLE4 | ImageType::RLE8 | ImageType::RLE24
2348
        )
2349
4.30k
    }
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.31k
    pub fn rows_decoded(&self) -> RowsDecoded {
2355
2.31k
        let rows = match self.state {
2356
2.31k
            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.31k
        if self.top_down {
2366
580
            RowsDecoded::TopDown { rows }
2367
        } else {
2368
1.73k
            RowsDecoded::BottomUp { rows }
2369
        }
2370
2.31k
    }
2371
2372
    /// Handle the result of a row-based decode operation, updating state accordingly.
2373
2.31k
    fn finish_row_decode(&mut self, result: Result<u32, (u32, io::Error)>) -> ImageResult<()> {
2374
2.31k
        let (Ok(rows) | Err((rows, _))) = result;
2375
2.31k
        self.state = DecoderState::ReadingRowData { rows_decoded: rows };
2376
2.31k
        match result {
2377
288
            Ok(_) => Ok(()),
2378
2.03k
            Err((_, e)) => Err(e)?,
2379
        }
2380
2.31k
    }
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
4.15k
    pub fn read_image_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
2392
4.15k
        match self.state {
2393
0
            DecoderState::ImageDecoded => Ok(()),
2394
4.15k
            DecoderState::ReadingRowData { .. } | DecoderState::ReadingRleData { .. } => self
2395
4.15k
                .read_image_data_impl(buf)
2396
4.15k
                .map(|()| self.state = DecoderState::ImageDecoded),
2397
0
            DecoderState::ReadingMetadata { .. } => Err(DecoderError::MetadataNotRead.into()),
2398
        }
2399
4.15k
    }
2400
2401
    /// Internal implementation of image data reading.
2402
4.15k
    fn read_image_data_impl(&mut self, buf: &mut [u8]) -> ImageResult<()> {
2403
4.15k
        match self.image_type {
2404
907
            ImageType::Palette => self.read_palettized_pixel_data(buf),
2405
279
            ImageType::RGB16 => self.read_16_bit_pixel_data(buf, Some(&R5_G5_B5_COLOR_MASK)),
2406
362
            ImageType::RGB24 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGB24),
2407
68
            ImageType::RGB32 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGB32),
2408
194
            ImageType::RGBA32 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGBA32),
2409
379
            ImageType::RLE8 => self.read_rle_data(buf, ImageType::RLE8),
2410
573
            ImageType::RLE4 => self.read_rle_data(buf, ImageType::RLE4),
2411
886
            ImageType::RLE24 => self.read_rle_data(buf, ImageType::RLE24),
2412
70
            ImageType::Bitfields16 => match self.bitfields {
2413
70
                Some(_) => self.read_16_bit_pixel_data(buf, None),
2414
0
                None => Err(DecoderError::BitfieldMasksMissing(16).into()),
2415
            },
2416
439
            ImageType::Bitfields32 => match self.bitfields {
2417
                Some(R8_G8_B8_COLOR_MASK) => {
2418
21
                    self.read_full_byte_pixel_data(buf, &FormatFullBytes::Format888)
2419
                }
2420
                Some(R8_G8_B8_A8_COLOR_MASK) => {
2421
20
                    self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGBA32)
2422
                }
2423
398
                Some(_) => self.read_32_bit_pixel_data(buf),
2424
0
                None => Err(DecoderError::BitfieldMasksMissing(32).into()),
2425
            },
2426
        }
2427
4.15k
    }
2428
}
2429
2430
impl<R: BufRead + Seek> ImageDecoder for BmpDecoder<R> {
2431
16.9k
    fn prepare_image(&mut self) -> ImageResult<DecoderPreparedImage> {
2432
16.9k
        let color = if self.indexed_color {
2433
0
            ColorType::L8
2434
16.9k
        } else if self.add_alpha_channel {
2435
9.37k
            ColorType::Rgba8
2436
        } else {
2437
7.55k
            ColorType::Rgb8
2438
        };
2439
2440
16.9k
        Ok(DecoderPreparedImage::new(
2441
16.9k
            self.width as u32,
2442
16.9k
            self.height as u32,
2443
16.9k
            color,
2444
16.9k
        ))
2445
16.9k
    }
2446
2447
0
    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
2448
0
        Ok(self.icc_profile.clone())
2449
0
    }
2450
2451
2.10k
    fn read_image(&mut self, buf: &mut [u8]) -> ImageResult<DecodedImageAttributes> {
2452
2.10k
        let layout = self.prepare_image()?;
2453
2.10k
        assert_eq!(u64::try_from(buf.len()), Ok(layout.total_bytes()));
2454
2.10k
        self.read_image_data(buf)?;
2455
389
        Ok(DecodedImageAttributes::default())
2456
2.10k
    }
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::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::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::with_spec_compliance(Cursor::new(data), SpecCompliance::Strict).unwrap();
3145
        let mut decoder = crate::ImageReader::from_decoder(Box::new(strict_decoder));
3146
        let result = decoder.decode();
3147
3148
        assert!(
3149
            result.is_err(),
3150
            "Expected error in strict mode for truncated file"
3151
        );
3152
    }
3153
}