Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/decoders/nsBMPDecoder.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
// This is a cross-platform BMP Decoder, which should work everywhere,
8
// including big-endian machines like the PowerPC.
9
//
10
// BMP is a format that has been extended multiple times. To understand the
11
// decoder you need to understand this history. The summary of the history
12
// below was determined from the following documents.
13
//
14
// - http://www.fileformat.info/format/bmp/egff.htm
15
// - http://www.fileformat.info/format/os2bmp/egff.htm
16
// - http://fileformats.archiveteam.org/wiki/BMP
17
// - http://fileformats.archiveteam.org/wiki/OS/2_BMP
18
// - https://en.wikipedia.org/wiki/BMP_file_format
19
// - https://upload.wikimedia.org/wikipedia/commons/c/c4/BMPfileFormat.png
20
//
21
// WINDOWS VERSIONS OF THE BMP FORMAT
22
// ----------------------------------
23
// WinBMPv1.
24
// - This version is no longer used and can be ignored.
25
//
26
// WinBMPv2.
27
// - First is a 14 byte file header that includes: the magic number ("BM"),
28
//   file size, and offset to the pixel data (|mDataOffset|).
29
// - Next is a 12 byte info header which includes: the info header size
30
//   (mBIHSize), width, height, number of color planes, and bits-per-pixel
31
//   (|mBpp|) which must be 1, 4, 8 or 24.
32
// - Next is the semi-optional color table, which has length 2^|mBpp| and has 3
33
//   bytes per value (BGR). The color table is required if |mBpp| is 1, 4, or 8.
34
// - Next is an optional gap.
35
// - Next is the pixel data, which is pointed to by |mDataOffset|.
36
//
37
// WinBMPv3. This is the most widely used version.
38
// - It changed the info header to 40 bytes by taking the WinBMPv2 info
39
//   header, enlargening its width and height fields, and adding more fields
40
//   including: a compression type (|mCompression|) and number of colors
41
//   (|mNumColors|).
42
// - The semi-optional color table is now 4 bytes per value (BGR0), and its
43
//   length is |mNumColors|, or 2^|mBpp| if |mNumColors| is zero.
44
// - |mCompression| can be RGB (i.e. no compression), RLE4 (if |mBpp|==4) or
45
//   RLE8 (if |mBpp|==8) values.
46
//
47
// WinBMPv3-NT. A variant of WinBMPv3.
48
// - It did not change the info header layout from WinBMPv3.
49
// - |mBpp| can now be 16 or 32, in which case |mCompression| can be RGB or the
50
//   new BITFIELDS value; in the latter case an additional 12 bytes of color
51
//   bitfields follow the info header.
52
//
53
// WinBMPv4.
54
// - It extended the info header to 108 bytes, including the 12 bytes of color
55
//   mask data from WinBMPv3-NT, plus alpha mask data, and also color-space and
56
//   gamma correction fields.
57
//
58
// WinBMPv5.
59
// - It extended the info header to 124 bytes, adding color profile data.
60
// - It also added an optional color profile table after the pixel data (and
61
//   another optional gap).
62
//
63
// WinBMPv3-ICO. This is a variant of WinBMPv3.
64
// - It's the BMP format used for BMP images within ICO files.
65
// - The only difference with WinBMPv3 is that if an image is 32bpp and has no
66
//   compression, then instead of treating the pixel data as 0RGB it is treated
67
//   as ARGB, but only if one or more of the A values are non-zero.
68
//
69
// OS/2 VERSIONS OF THE BMP FORMAT
70
// -------------------------------
71
// OS2-BMPv1.
72
// - Almost identical to WinBMPv2; the differences are basically ignorable.
73
//
74
// OS2-BMPv2.
75
// - Similar to WinBMPv3.
76
// - The info header is 64 bytes but can be reduced to as little as 16; any
77
//   omitted fields are treated as zero. The first 40 bytes of these fields are
78
//   nearly identical to the WinBMPv3 info header; the remaining 24 bytes are
79
//   different.
80
// - Also adds compression types "Huffman 1D" and "RLE24", which we don't
81
//   support.
82
// - We treat OS2-BMPv2 files as if they are WinBMPv3 (i.e. ignore the extra 24
83
//   bytes in the info header), which in practice is good enough.
84
85
#include "ImageLogging.h"
86
#include "nsBMPDecoder.h"
87
88
#include <stdlib.h>
89
90
#include "mozilla/Attributes.h"
91
#include "mozilla/EndianUtils.h"
92
#include "mozilla/Likely.h"
93
94
#include "nsIInputStream.h"
95
#include "RasterImage.h"
96
#include <algorithm>
97
98
using namespace mozilla::gfx;
99
100
namespace mozilla {
101
namespace image {
102
namespace bmp {
103
104
struct Compression {
105
  enum {
106
    RGB = 0,
107
    RLE8 = 1,
108
    RLE4 = 2,
109
    BITFIELDS = 3
110
  };
111
};
112
113
// RLE escape codes and constants.
114
struct RLE {
115
  enum {
116
    ESCAPE = 0,
117
    ESCAPE_EOL = 0,
118
    ESCAPE_EOF = 1,
119
    ESCAPE_DELTA = 2,
120
121
    SEGMENT_LENGTH = 2,
122
    DELTA_LENGTH = 2
123
  };
124
};
125
126
} // namespace bmp
127
128
using namespace bmp;
129
130
/// Sets the pixel data in aDecoded to the given values.
131
/// @param aDecoded pointer to pixel to be set, will be incremented to point to
132
/// the next pixel.
133
static void
134
SetPixel(uint32_t*& aDecoded, uint8_t aRed, uint8_t aGreen,
135
         uint8_t aBlue, uint8_t aAlpha = 0xFF)
136
0
{
137
0
  *aDecoded++ = gfxPackedPixel(aAlpha, aRed, aGreen, aBlue);
138
0
}
139
140
static void
141
SetPixel(uint32_t*& aDecoded, uint8_t idx,
142
         const UniquePtr<ColorTableEntry[]>& aColors)
143
0
{
144
0
  SetPixel(aDecoded,
145
0
           aColors[idx].mRed, aColors[idx].mGreen, aColors[idx].mBlue);
146
0
}
147
148
/// Sets two (or one if aCount = 1) pixels
149
/// @param aDecoded where the data is stored. Will be moved 4 resp 8 bytes
150
/// depending on whether one or two pixels are written.
151
/// @param aData The values for the two pixels
152
/// @param aCount Current count. Is decremented by one or two.
153
static void
154
Set4BitPixel(uint32_t*& aDecoded, uint8_t aData, uint32_t& aCount,
155
             const UniquePtr<ColorTableEntry[]>& aColors)
156
0
{
157
0
  uint8_t idx = aData >> 4;
158
0
  SetPixel(aDecoded, idx, aColors);
159
0
  if (--aCount > 0) {
160
0
    idx = aData & 0xF;
161
0
    SetPixel(aDecoded, idx, aColors);
162
0
    --aCount;
163
0
  }
164
0
}
165
166
static mozilla::LazyLogModule sBMPLog("BMPDecoder");
167
168
// The length of the mBIHSize field in the info header.
169
static const uint32_t BIHSIZE_FIELD_LENGTH = 4;
170
171
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength)
172
  : Decoder(aImage)
173
  , mLexer(Transition::To(aState, aLength), Transition::TerminateSuccess())
174
  , mIsWithinICO(false)
175
  , mMayHaveTransparency(false)
176
  , mDoesHaveTransparency(false)
177
  , mNumColors(0)
178
  , mColors(nullptr)
179
  , mBytesPerColor(0)
180
  , mPreGapLength(0)
181
  , mPixelRowSize(0)
182
  , mCurrentRow(0)
183
  , mCurrentPos(0)
184
  , mAbsoluteModeNumPixels(0)
185
0
{
186
0
}
187
188
// Constructor for normal BMP files.
189
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage)
190
  : nsBMPDecoder(aImage, State::FILE_HEADER, FILE_HEADER_LENGTH)
191
0
{
192
0
}
193
194
// Constructor used for WinBMPv3-ICO files, which lack a file header.
195
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, uint32_t aDataOffset)
196
  : nsBMPDecoder(aImage, State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH)
197
0
{
198
0
  SetIsWithinICO();
199
0
200
0
  // Even though the file header isn't present in this case, the dataOffset
201
0
  // field is set as if it is, and so we must increment mPreGapLength
202
0
  // accordingly.
203
0
  mPreGapLength += FILE_HEADER_LENGTH;
204
0
205
0
  // This is the one piece of data we normally get from a BMP file header, so
206
0
  // it must be provided via an argument.
207
0
  mH.mDataOffset = aDataOffset;
208
0
}
209
210
nsBMPDecoder::~nsBMPDecoder()
211
0
{
212
0
}
213
214
// Obtains the size of the compressed image resource.
215
int32_t
216
nsBMPDecoder::GetCompressedImageSize() const
217
0
{
218
0
  // In the RGB case mImageSize might not be set, so compute it manually.
219
0
  MOZ_ASSERT(mPixelRowSize != 0);
220
0
  return mH.mCompression == Compression::RGB
221
0
       ? mPixelRowSize * AbsoluteHeight()
222
0
       : mH.mImageSize;
223
0
}
224
225
nsresult
226
nsBMPDecoder::BeforeFinishInternal()
227
0
{
228
0
  if (!IsMetadataDecode() && !mImageData) {
229
0
    return NS_ERROR_FAILURE;  // No image; something went wrong.
230
0
  }
231
0
232
0
  return NS_OK;
233
0
}
234
235
nsresult
236
nsBMPDecoder::FinishInternal()
237
0
{
238
0
  // We shouldn't be called in error cases.
239
0
  MOZ_ASSERT(!HasError(), "Can't call FinishInternal on error!");
240
0
241
0
  // We should never make multiple frames.
242
0
  MOZ_ASSERT(GetFrameCount() <= 1, "Multiple BMP frames?");
243
0
244
0
  // Send notifications if appropriate.
245
0
  if (!IsMetadataDecode() && HasSize()) {
246
0
247
0
    // We should have image data.
248
0
    MOZ_ASSERT(mImageData);
249
0
250
0
    // If it was truncated, fill in the missing pixels as black.
251
0
    while (mCurrentRow > 0) {
252
0
      uint32_t* dst = RowBuffer();
253
0
      while (mCurrentPos < mH.mWidth) {
254
0
        SetPixel(dst, 0, 0, 0);
255
0
        mCurrentPos++;
256
0
      }
257
0
      mCurrentPos = 0;
258
0
      FinishRow();
259
0
    }
260
0
261
0
    // Invalidate.
262
0
    nsIntRect r(0, 0, mH.mWidth, AbsoluteHeight());
263
0
    PostInvalidation(r);
264
0
265
0
    MOZ_ASSERT_IF(mDoesHaveTransparency, mMayHaveTransparency);
266
0
267
0
    // We have transparency if we either detected some in the image itself
268
0
    // (i.e., |mDoesHaveTransparency| is true) or we're in an ICO, which could
269
0
    // mean we have an AND mask that provides transparency (i.e., |mIsWithinICO|
270
0
    // is true).
271
0
    // XXX(seth): We can tell when we create the decoder if the AND mask is
272
0
    // present, so we could be more precise about this.
273
0
    const Opacity opacity = mDoesHaveTransparency || mIsWithinICO
274
0
                          ? Opacity::SOME_TRANSPARENCY
275
0
                          : Opacity::FULLY_OPAQUE;
276
0
277
0
    PostFrameStop(opacity);
278
0
    PostDecodeDone();
279
0
  }
280
0
281
0
  return NS_OK;
282
0
}
283
284
// ----------------------------------------
285
// Actual Data Processing
286
// ----------------------------------------
287
288
void
289
BitFields::Value::Set(uint32_t aMask)
290
0
{
291
0
  mMask = aMask;
292
0
293
0
  // Handle this exceptional case first. The chosen values don't matter
294
0
  // (because a mask of zero will always give a value of zero) except that
295
0
  // mBitWidth:
296
0
  // - shouldn't be zero, because that would cause an infinite loop in Get();
297
0
  // - shouldn't be 5 or 8, because that could cause a false positive match in
298
0
  //   IsR5G5B5() or IsR8G8B8().
299
0
  if (mMask == 0x0) {
300
0
    mRightShift = 0;
301
0
    mBitWidth = 1;
302
0
    return;
303
0
  }
304
0
305
0
  // Find the rightmost 1.
306
0
  uint8_t i;
307
0
  for (i = 0; i < 32; i++) {
308
0
    if (mMask & (1 << i)) {
309
0
      break;
310
0
    }
311
0
  }
312
0
  mRightShift = i;
313
0
314
0
  // Now find the leftmost 1 in the same run of 1s. (If there are multiple runs
315
0
  // of 1s -- which isn't valid -- we'll behave as if only the lowest run was
316
0
  // present, which seems reasonable.)
317
0
  for (i = i + 1; i < 32; i++) {
318
0
    if (!(mMask & (1 << i))) {
319
0
      break;
320
0
    }
321
0
  }
322
0
  mBitWidth = i - mRightShift;
323
0
}
324
325
MOZ_ALWAYS_INLINE uint8_t
326
BitFields::Value::Get(uint32_t aValue) const
327
0
{
328
0
  // Extract the unscaled value.
329
0
  uint32_t v = (aValue & mMask) >> mRightShift;
330
0
331
0
  // Idea: to upscale v precisely we need to duplicate its bits, possibly
332
0
  // repeatedly, possibly partially in the last case, from bit 7 down to bit 0
333
0
  // in v2. For example:
334
0
  //
335
0
  // - mBitWidth=1:  v2 = v<<7 | v<<6 | ... | v<<1 | v>>0     k -> kkkkkkkk
336
0
  // - mBitWidth=2:  v2 = v<<6 | v<<4 | v<<2 | v>>0          jk -> jkjkjkjk
337
0
  // - mBitWidth=3:  v2 = v<<5 | v<<2 | v>>1                ijk -> ijkijkij
338
0
  // - mBitWidth=4:  v2 = v<<4 | v>>0                      hijk -> hijkhijk
339
0
  // - mBitWidth=5:  v2 = v<<3 | v>>2                     ghijk -> ghijkghi
340
0
  // - mBitWidth=6:  v2 = v<<2 | v>>4                    fghijk -> fghijkfg
341
0
  // - mBitWidth=7:  v2 = v<<1 | v>>6                   efghijk -> efghijke
342
0
  // - mBitWidth=8:  v2 = v>>0                         defghijk -> defghijk
343
0
  // - mBitWidth=9:  v2 = v>>1                        cdefghijk -> cdefghij
344
0
  // - mBitWidth=10: v2 = v>>2                       bcdefghijk -> bcdefghi
345
0
  // - mBitWidth=11: v2 = v>>3                      abcdefghijk -> abcdefgh
346
0
  // - etc.
347
0
  //
348
0
  uint8_t v2 = 0;
349
0
  int32_t i;      // must be a signed integer
350
0
  for (i = 8 - mBitWidth; i > 0; i -= mBitWidth) {
351
0
    v2 |= v << uint32_t(i);
352
0
  }
353
0
  v2 |= v >> uint32_t(-i);
354
0
  return v2;
355
0
}
356
357
MOZ_ALWAYS_INLINE uint8_t
358
BitFields::Value::GetAlpha(uint32_t aValue, bool& aHasAlphaOut) const
359
0
{
360
0
  if (mMask == 0x0) {
361
0
    return 0xff;
362
0
  }
363
0
  aHasAlphaOut = true;
364
0
  return Get(aValue);
365
0
}
366
367
MOZ_ALWAYS_INLINE uint8_t
368
BitFields::Value::Get5(uint32_t aValue) const
369
0
{
370
0
  MOZ_ASSERT(mBitWidth == 5);
371
0
  uint32_t v = (aValue & mMask) >> mRightShift;
372
0
  return (v << 3u) | (v >> 2u);
373
0
}
374
375
MOZ_ALWAYS_INLINE uint8_t
376
BitFields::Value::Get8(uint32_t aValue) const
377
0
{
378
0
  MOZ_ASSERT(mBitWidth == 8);
379
0
  uint32_t v = (aValue & mMask) >> mRightShift;
380
0
  return v;
381
0
}
382
383
void
384
BitFields::SetR5G5B5()
385
0
{
386
0
  mRed.Set(0x7c00);
387
0
  mGreen.Set(0x03e0);
388
0
  mBlue.Set(0x001f);
389
0
}
390
391
void
392
BitFields::SetR8G8B8()
393
0
{
394
0
  mRed.Set(0xff0000);
395
0
  mGreen.Set(0xff00);
396
0
  mBlue.Set(0x00ff);
397
0
}
398
399
bool
400
BitFields::IsR5G5B5() const
401
0
{
402
0
  return mRed.mBitWidth == 5 &&
403
0
         mGreen.mBitWidth == 5 &&
404
0
         mBlue.mBitWidth == 5 &&
405
0
         mAlpha.mMask == 0x0;
406
0
}
407
408
bool
409
BitFields::IsR8G8B8() const
410
0
{
411
0
  return mRed.mBitWidth == 8 &&
412
0
         mGreen.mBitWidth == 8 &&
413
0
         mBlue.mBitWidth == 8 &&
414
0
         mAlpha.mMask == 0x0;
415
0
}
416
417
uint32_t*
418
nsBMPDecoder::RowBuffer()
419
0
{
420
0
  if (mDownscaler) {
421
0
    return reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer()) + mCurrentPos;
422
0
  }
423
0
424
0
  // Convert from row (1..mHeight) to absolute line (0..mHeight-1).
425
0
  int32_t line = (mH.mHeight < 0)
426
0
               ? -mH.mHeight - mCurrentRow
427
0
               : mCurrentRow - 1;
428
0
  int32_t offset = line * mH.mWidth + mCurrentPos;
429
0
  return reinterpret_cast<uint32_t*>(mImageData) + offset;
430
0
}
431
432
void
433
nsBMPDecoder::FinishRow()
434
0
{
435
0
  if (mDownscaler) {
436
0
    mDownscaler->CommitRow();
437
0
438
0
    if (mDownscaler->HasInvalidation()) {
439
0
      DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
440
0
      PostInvalidation(invalidRect.mOriginalSizeRect,
441
0
                       Some(invalidRect.mTargetSizeRect));
442
0
    }
443
0
  } else {
444
0
    PostInvalidation(IntRect(0, mCurrentRow, mH.mWidth, 1));
445
0
  }
446
0
  mCurrentRow--;
447
0
}
448
449
LexerResult
450
nsBMPDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
451
0
{
452
0
  MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
453
0
454
0
  return mLexer.Lex(aIterator, aOnResume,
455
0
                    [=](State aState, const char* aData, size_t aLength) {
456
0
    switch (aState) {
457
0
      case State::FILE_HEADER:      return ReadFileHeader(aData, aLength);
458
0
      case State::INFO_HEADER_SIZE: return ReadInfoHeaderSize(aData, aLength);
459
0
      case State::INFO_HEADER_REST: return ReadInfoHeaderRest(aData, aLength);
460
0
      case State::BITFIELDS:        return ReadBitfields(aData, aLength);
461
0
      case State::COLOR_TABLE:      return ReadColorTable(aData, aLength);
462
0
      case State::GAP:              return SkipGap();
463
0
      case State::AFTER_GAP:        return AfterGap();
464
0
      case State::PIXEL_ROW:        return ReadPixelRow(aData);
465
0
      case State::RLE_SEGMENT:      return ReadRLESegment(aData);
466
0
      case State::RLE_DELTA:        return ReadRLEDelta(aData);
467
0
      case State::RLE_ABSOLUTE:     return ReadRLEAbsolute(aData, aLength);
468
0
      default:
469
0
        MOZ_CRASH("Unknown State");
470
0
    }
471
0
  });
472
0
}
473
474
LexerTransition<nsBMPDecoder::State>
475
nsBMPDecoder::ReadFileHeader(const char* aData, size_t aLength)
476
0
{
477
0
  mPreGapLength += aLength;
478
0
479
0
  bool signatureOk = aData[0] == 'B' && aData[1] == 'M';
480
0
  if (!signatureOk) {
481
0
    return Transition::TerminateFailure();
482
0
  }
483
0
484
0
  // We ignore the filesize (aData + 2) and reserved (aData + 6) fields.
485
0
486
0
  mH.mDataOffset = LittleEndian::readUint32(aData + 10);
487
0
488
0
  return Transition::To(State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH);
489
0
}
490
491
// We read the info header in two steps: (a) read the mBIHSize field to
492
// determine how long the header is; (b) read the rest of the header.
493
LexerTransition<nsBMPDecoder::State>
494
nsBMPDecoder::ReadInfoHeaderSize(const char* aData, size_t aLength)
495
0
{
496
0
  mPreGapLength += aLength;
497
0
498
0
  mH.mBIHSize = LittleEndian::readUint32(aData);
499
0
500
0
  bool bihSizeOk = mH.mBIHSize == InfoHeaderLength::WIN_V2 ||
501
0
                   mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
502
0
                   mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
503
0
                   mH.mBIHSize == InfoHeaderLength::WIN_V5 ||
504
0
                   (mH.mBIHSize >= InfoHeaderLength::OS2_V2_MIN &&
505
0
                    mH.mBIHSize <= InfoHeaderLength::OS2_V2_MAX);
506
0
  if (!bihSizeOk) {
507
0
    return Transition::TerminateFailure();
508
0
  }
509
0
  // ICO BMPs must have a WinBMPv3 header. nsICODecoder should have already
510
0
  // terminated decoding if this isn't the case.
511
0
  MOZ_ASSERT_IF(mIsWithinICO, mH.mBIHSize == InfoHeaderLength::WIN_V3);
512
0
513
0
  return Transition::To(State::INFO_HEADER_REST,
514
0
                        mH.mBIHSize - BIHSIZE_FIELD_LENGTH);
515
0
}
516
517
LexerTransition<nsBMPDecoder::State>
518
nsBMPDecoder::ReadInfoHeaderRest(const char* aData, size_t aLength)
519
0
{
520
0
  mPreGapLength += aLength;
521
0
522
0
  // |mWidth| and |mHeight| may be signed (Windows) or unsigned (OS/2). We just
523
0
  // read as unsigned because in practice that's good enough.
524
0
  if (mH.mBIHSize == InfoHeaderLength::WIN_V2) {
525
0
    mH.mWidth  = LittleEndian::readUint16(aData + 0);
526
0
    mH.mHeight = LittleEndian::readUint16(aData + 2);
527
0
    // We ignore the planes (aData + 4) field; it should always be 1.
528
0
    mH.mBpp    = LittleEndian::readUint16(aData + 6);
529
0
  } else {
530
0
    mH.mWidth  = LittleEndian::readUint32(aData + 0);
531
0
    mH.mHeight = LittleEndian::readUint32(aData + 4);
532
0
    // We ignore the planes (aData + 4) field; it should always be 1.
533
0
    mH.mBpp    = LittleEndian::readUint16(aData + 10);
534
0
535
0
    // For OS2-BMPv2 the info header may be as little as 16 bytes, so be
536
0
    // careful for these fields.
537
0
    mH.mCompression = aLength >= 16 ? LittleEndian::readUint32(aData + 12) : 0;
538
0
    mH.mImageSize   = aLength >= 20 ? LittleEndian::readUint32(aData + 16) : 0;
539
0
    // We ignore the xppm (aData + 20) and yppm (aData + 24) fields.
540
0
    mH.mNumColors   = aLength >= 32 ? LittleEndian::readUint32(aData + 28) : 0;
541
0
    // We ignore the important_colors (aData + 36) field.
542
0
543
0
    // For WinBMPv4, WinBMPv5 and (possibly) OS2-BMPv2 there are additional
544
0
    // fields in the info header which we ignore, with the possible exception
545
0
    // of the color bitfields (see below).
546
0
  }
547
0
548
0
  // The height for BMPs embedded inside an ICO includes spaces for the AND
549
0
  // mask even if it is not present, thus we need to adjust for that here.
550
0
  if (mIsWithinICO) {
551
0
    // XXX(seth): Should we really be writing the absolute value from
552
0
    // the BIH below? Seems like this could be problematic for inverted BMPs.
553
0
    mH.mHeight = abs(mH.mHeight) / 2;
554
0
  }
555
0
556
0
  // Run with MOZ_LOG=BMPDecoder:5 set to see this output.
557
0
  MOZ_LOG(sBMPLog, LogLevel::Debug,
558
0
          ("BMP: bihsize=%u, %d x %d, bpp=%u, compression=%u, colors=%u\n",
559
0
          mH.mBIHSize, mH.mWidth, mH.mHeight, uint32_t(mH.mBpp),
560
0
          mH.mCompression, mH.mNumColors));
561
0
562
0
  // BMPs with negative width are invalid. Also, reject extremely wide images
563
0
  // to keep the math sane. And reject INT_MIN as a height because you can't
564
0
  // get its absolute value (because -INT_MIN is one more than INT_MAX).
565
0
  const int32_t k64KWidth = 0x0000FFFF;
566
0
  bool sizeOk = 0 <= mH.mWidth && mH.mWidth <= k64KWidth &&
567
0
                mH.mHeight != INT_MIN;
568
0
  if (!sizeOk) {
569
0
    return Transition::TerminateFailure();
570
0
  }
571
0
572
0
  // Check mBpp and mCompression.
573
0
  bool bppCompressionOk =
574
0
    (mH.mCompression == Compression::RGB &&
575
0
      (mH.mBpp ==  1 || mH.mBpp ==  4 || mH.mBpp ==  8 ||
576
0
       mH.mBpp == 16 || mH.mBpp == 24 || mH.mBpp == 32)) ||
577
0
    (mH.mCompression == Compression::RLE8 && mH.mBpp == 8) ||
578
0
    (mH.mCompression == Compression::RLE4 && mH.mBpp == 4) ||
579
0
    (mH.mCompression == Compression::BITFIELDS &&
580
0
      // For BITFIELDS compression we require an exact match for one of the
581
0
      // WinBMP BIH sizes; this clearly isn't an OS2 BMP.
582
0
      (mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
583
0
       mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
584
0
       mH.mBIHSize == InfoHeaderLength::WIN_V5) &&
585
0
      (mH.mBpp == 16 || mH.mBpp == 32));
586
0
  if (!bppCompressionOk) {
587
0
    return Transition::TerminateFailure();
588
0
  }
589
0
590
0
  // Initialize our current row to the top of the image.
591
0
  mCurrentRow = AbsoluteHeight();
592
0
593
0
  // Round it up to the nearest byte count, then pad to 4-byte boundary.
594
0
  // Compute this even for a metadate decode because GetCompressedImageSize()
595
0
  // relies on it.
596
0
  mPixelRowSize = (mH.mBpp * mH.mWidth + 7) / 8;
597
0
  uint32_t surplus = mPixelRowSize % 4;
598
0
  if (surplus != 0) {
599
0
    mPixelRowSize += 4 - surplus;
600
0
  }
601
0
602
0
  size_t bitFieldsLengthStillToRead = 0;
603
0
  if (mH.mCompression == Compression::BITFIELDS) {
604
0
    // Need to read bitfields.
605
0
    if (mH.mBIHSize >= InfoHeaderLength::WIN_V4) {
606
0
      // Bitfields are present in the info header, so we can read them
607
0
      // immediately.
608
0
      mBitFields.ReadFromHeader(aData + 36, /* aReadAlpha = */ true);
609
0
    } else {
610
0
      // Bitfields are present after the info header, so we will read them in
611
0
      // ReadBitfields().
612
0
      bitFieldsLengthStillToRead = BitFields::LENGTH;
613
0
    }
614
0
  } else if (mH.mBpp == 16) {
615
0
    // No bitfields specified; use the default 5-5-5 values.
616
0
    mBitFields.SetR5G5B5();
617
0
  } else if (mH.mBpp == 32) {
618
0
    // No bitfields specified; use the default 8-8-8 values.
619
0
    mBitFields.SetR8G8B8();
620
0
  }
621
0
622
0
  return Transition::To(State::BITFIELDS, bitFieldsLengthStillToRead);
623
0
}
624
625
void
626
BitFields::ReadFromHeader(const char* aData, bool aReadAlpha)
627
0
{
628
0
  mRed.Set  (LittleEndian::readUint32(aData + 0));
629
0
  mGreen.Set(LittleEndian::readUint32(aData + 4));
630
0
  mBlue.Set (LittleEndian::readUint32(aData + 8));
631
0
  if (aReadAlpha) {
632
0
    mAlpha.Set(LittleEndian::readUint32(aData + 12));
633
0
  }
634
0
}
635
636
LexerTransition<nsBMPDecoder::State>
637
nsBMPDecoder::ReadBitfields(const char* aData, size_t aLength)
638
0
{
639
0
  mPreGapLength += aLength;
640
0
641
0
  // If aLength is zero there are no bitfields to read, or we already read them
642
0
  // in ReadInfoHeader().
643
0
  if (aLength != 0) {
644
0
    mBitFields.ReadFromHeader(aData, /* aReadAlpha = */ false);
645
0
  }
646
0
647
0
  // Note that RLE-encoded BMPs might be transparent because the 'delta' mode
648
0
  // can skip pixels and cause implicit transparency.
649
0
  mMayHaveTransparency =
650
0
    mIsWithinICO ||
651
0
    mH.mCompression == Compression::RLE8 ||
652
0
    mH.mCompression == Compression::RLE4 ||
653
0
    (mH.mCompression == Compression::BITFIELDS &&
654
0
     mBitFields.mAlpha.IsPresent());
655
0
  if (mMayHaveTransparency) {
656
0
    PostHasTransparency();
657
0
  }
658
0
659
0
  // Post our size to the superclass.
660
0
  PostSize(mH.mWidth, AbsoluteHeight());
661
0
  if (HasError()) {
662
0
    return Transition::TerminateFailure();
663
0
  }
664
0
665
0
  // We've now read all the headers. If we're doing a metadata decode, we're
666
0
  // done.
667
0
  if (IsMetadataDecode()) {
668
0
    return Transition::TerminateSuccess();
669
0
  }
670
0
671
0
  // Set up the color table, if present; it'll be filled in by ReadColorTable().
672
0
  if (mH.mBpp <= 8) {
673
0
    mNumColors = 1 << mH.mBpp;
674
0
    if (0 < mH.mNumColors && mH.mNumColors < mNumColors) {
675
0
      mNumColors = mH.mNumColors;
676
0
    }
677
0
678
0
    // Always allocate and zero 256 entries, even though mNumColors might be
679
0
    // smaller, because the file might erroneously index past mNumColors.
680
0
    mColors = MakeUnique<ColorTableEntry[]>(256);
681
0
    memset(mColors.get(), 0, 256 * sizeof(ColorTableEntry));
682
0
683
0
    // OS/2 Bitmaps have no padding byte.
684
0
    mBytesPerColor = (mH.mBIHSize == InfoHeaderLength::WIN_V2) ? 3 : 4;
685
0
  }
686
0
687
0
  MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
688
0
  nsresult rv = AllocateFrame(OutputSize(), FullOutputFrame(),
689
0
                              mMayHaveTransparency ? SurfaceFormat::B8G8R8A8
690
0
                                                   : SurfaceFormat::B8G8R8X8);
691
0
  if (NS_FAILED(rv)) {
692
0
    return Transition::TerminateFailure();
693
0
  }
694
0
  MOZ_ASSERT(mImageData, "Should have a buffer now");
695
0
696
0
  if (mDownscaler) {
697
0
    // BMPs store their rows in reverse order, so the downscaler needs to
698
0
    // reverse them again when writing its output. Unless the height is
699
0
    // negative!
700
0
    rv = mDownscaler->BeginFrame(Size(), Nothing(),
701
0
                                 mImageData, mMayHaveTransparency,
702
0
                                 /* aFlipVertically = */ mH.mHeight >= 0);
703
0
    if (NS_FAILED(rv)) {
704
0
      return Transition::TerminateFailure();
705
0
    }
706
0
  }
707
0
708
0
  return Transition::To(State::COLOR_TABLE, mNumColors * mBytesPerColor);
709
0
}
710
711
LexerTransition<nsBMPDecoder::State>
712
nsBMPDecoder::ReadColorTable(const char* aData, size_t aLength)
713
0
{
714
0
  MOZ_ASSERT_IF(aLength != 0, mNumColors > 0 && mColors);
715
0
716
0
  mPreGapLength += aLength;
717
0
718
0
  for (uint32_t i = 0; i < mNumColors; i++) {
719
0
    // The format is BGR or BGR0.
720
0
    mColors[i].mBlue  = uint8_t(aData[0]);
721
0
    mColors[i].mGreen = uint8_t(aData[1]);
722
0
    mColors[i].mRed   = uint8_t(aData[2]);
723
0
    aData += mBytesPerColor;
724
0
  }
725
0
726
0
  // We know how many bytes we've read so far (mPreGapLength) and we know the
727
0
  // offset of the pixel data (mH.mDataOffset), so we can determine the length
728
0
  // of the gap (possibly zero) between the color table and the pixel data.
729
0
  //
730
0
  // If the gap is negative the file must be malformed (e.g. mH.mDataOffset
731
0
  // points into the middle of the color palette instead of past the end) and
732
0
  // we give up.
733
0
  if (mPreGapLength > mH.mDataOffset) {
734
0
    return Transition::TerminateFailure();
735
0
  }
736
0
737
0
  uint32_t gapLength = mH.mDataOffset - mPreGapLength;
738
0
  return Transition::ToUnbuffered(State::AFTER_GAP, State::GAP, gapLength);
739
0
}
740
741
LexerTransition<nsBMPDecoder::State>
742
nsBMPDecoder::SkipGap()
743
0
{
744
0
  return Transition::ContinueUnbuffered(State::GAP);
745
0
}
746
747
LexerTransition<nsBMPDecoder::State>
748
nsBMPDecoder::AfterGap()
749
0
{
750
0
  // If there are no pixels we can stop.
751
0
  //
752
0
  // XXX: normally, if there are no pixels we will have stopped decoding before
753
0
  // now, outside of this decoder. However, if the BMP is within an ICO file,
754
0
  // it's possible that the ICO claimed the image had a non-zero size while the
755
0
  // BMP claims otherwise. This test is to catch that awkward case. If we ever
756
0
  // come up with a more general solution to this ICO-and-BMP-disagree-on-size
757
0
  // problem, this test can be removed.
758
0
  if (mH.mWidth == 0 || mH.mHeight == 0) {
759
0
    return Transition::TerminateSuccess();
760
0
  }
761
0
762
0
  bool hasRLE = mH.mCompression == Compression::RLE8 ||
763
0
                mH.mCompression == Compression::RLE4;
764
0
  return hasRLE
765
0
       ? Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH)
766
0
       : Transition::To(State::PIXEL_ROW, mPixelRowSize);
767
0
}
768
769
LexerTransition<nsBMPDecoder::State>
770
nsBMPDecoder::ReadPixelRow(const char* aData)
771
0
{
772
0
  MOZ_ASSERT(mCurrentRow > 0);
773
0
  MOZ_ASSERT(mCurrentPos == 0);
774
0
775
0
  const uint8_t* src = reinterpret_cast<const uint8_t*>(aData);
776
0
  uint32_t* dst = RowBuffer();
777
0
  uint32_t lpos = mH.mWidth;
778
0
  switch (mH.mBpp) {
779
0
    case 1:
780
0
      while (lpos > 0) {
781
0
        int8_t bit;
782
0
        uint8_t idx;
783
0
        for (bit = 7; bit >= 0 && lpos > 0; bit--) {
784
0
          idx = (*src >> bit) & 1;
785
0
          SetPixel(dst, idx, mColors);
786
0
          --lpos;
787
0
        }
788
0
        ++src;
789
0
      }
790
0
      break;
791
0
792
0
    case 4:
793
0
      while (lpos > 0) {
794
0
        Set4BitPixel(dst, *src, lpos, mColors);
795
0
        ++src;
796
0
      }
797
0
      break;
798
0
799
0
    case 8:
800
0
      while (lpos > 0) {
801
0
        SetPixel(dst, *src, mColors);
802
0
        --lpos;
803
0
        ++src;
804
0
      }
805
0
      break;
806
0
807
0
    case 16:
808
0
      if (mBitFields.IsR5G5B5()) {
809
0
        // Specialize this common case.
810
0
        while (lpos > 0) {
811
0
          uint16_t val = LittleEndian::readUint16(src);
812
0
          SetPixel(dst, mBitFields.mRed.Get5(val),
813
0
                        mBitFields.mGreen.Get5(val),
814
0
                        mBitFields.mBlue.Get5(val));
815
0
          --lpos;
816
0
          src += 2;
817
0
        }
818
0
      } else {
819
0
        bool anyHasAlpha = false;
820
0
        while (lpos > 0) {
821
0
          uint16_t val = LittleEndian::readUint16(src);
822
0
          SetPixel(dst, mBitFields.mRed.Get(val),
823
0
                        mBitFields.mGreen.Get(val),
824
0
                        mBitFields.mBlue.Get(val),
825
0
                        mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
826
0
          --lpos;
827
0
          src += 2;
828
0
        }
829
0
        if (anyHasAlpha) {
830
0
          MOZ_ASSERT(mMayHaveTransparency);
831
0
          mDoesHaveTransparency = true;
832
0
        }
833
0
      }
834
0
      break;
835
0
836
0
    case 24:
837
0
      while (lpos > 0) {
838
0
        SetPixel(dst, src[2], src[1], src[0]);
839
0
        --lpos;
840
0
        src += 3;
841
0
      }
842
0
      break;
843
0
844
0
    case 32:
845
0
      if (mH.mCompression == Compression::RGB && mIsWithinICO &&
846
0
          mH.mBpp == 32) {
847
0
        // This is a special case only used for 32bpp WinBMPv3-ICO files, which
848
0
        // could be in either 0RGB or ARGB format. We start by assuming it's
849
0
        // an 0RGB image. If we hit a non-zero alpha value, then we know it's
850
0
        // actually an ARGB image, and change tack accordingly.
851
0
        // (Note: a fully-transparent ARGB image is indistinguishable from a
852
0
        // 0RGB image, and we will render such an image as a 0RGB image, i.e.
853
0
        // opaquely. This is unlikely to be a problem in practice.)
854
0
        while (lpos > 0) {
855
0
          if (!mDoesHaveTransparency && src[3] != 0) {
856
0
            // Up until now this looked like an 0RGB image, but we now know
857
0
            // it's actually an ARGB image. Which means every pixel we've seen
858
0
            // so far has been fully transparent. So we go back and redo them.
859
0
860
0
            // Tell the Downscaler to go back to the start.
861
0
            if (mDownscaler) {
862
0
              mDownscaler->ResetForNextProgressivePass();
863
0
            }
864
0
865
0
            // Redo the complete rows we've already done.
866
0
            MOZ_ASSERT(mCurrentPos == 0);
867
0
            int32_t currentRow = mCurrentRow;
868
0
            mCurrentRow = AbsoluteHeight();
869
0
            while (mCurrentRow > currentRow) {
870
0
              dst = RowBuffer();
871
0
              for (int32_t i = 0; i < mH.mWidth; i++) {
872
0
                SetPixel(dst, 0, 0, 0, 0);
873
0
              }
874
0
              FinishRow();
875
0
            }
876
0
877
0
            // Redo the part of this row we've already done.
878
0
            dst = RowBuffer();
879
0
            int32_t n = mH.mWidth - lpos;
880
0
            for (int32_t i = 0; i < n; i++) {
881
0
              SetPixel(dst, 0, 0, 0, 0);
882
0
            }
883
0
884
0
            MOZ_ASSERT(mMayHaveTransparency);
885
0
            mDoesHaveTransparency = true;
886
0
          }
887
0
888
0
          // If mDoesHaveTransparency is false, treat this as an 0RGB image.
889
0
          // Otherwise, treat this as an ARGB image.
890
0
          SetPixel(dst, src[2], src[1], src[0],
891
0
                   mDoesHaveTransparency ? src[3] : 0xff);
892
0
          src += 4;
893
0
          --lpos;
894
0
        }
895
0
      } else if (mBitFields.IsR8G8B8()) {
896
0
        // Specialize this common case.
897
0
        while (lpos > 0) {
898
0
          uint32_t val = LittleEndian::readUint32(src);
899
0
          SetPixel(dst, mBitFields.mRed.Get8(val),
900
0
                        mBitFields.mGreen.Get8(val),
901
0
                        mBitFields.mBlue.Get8(val));
902
0
          --lpos;
903
0
          src += 4;
904
0
        }
905
0
      } else {
906
0
        bool anyHasAlpha = false;
907
0
        while (lpos > 0) {
908
0
          uint32_t val = LittleEndian::readUint32(src);
909
0
          SetPixel(dst, mBitFields.mRed.Get(val),
910
0
                        mBitFields.mGreen.Get(val),
911
0
                        mBitFields.mBlue.Get(val),
912
0
                        mBitFields.mAlpha.GetAlpha(val, anyHasAlpha));
913
0
          --lpos;
914
0
          src += 4;
915
0
        }
916
0
        if (anyHasAlpha) {
917
0
          MOZ_ASSERT(mMayHaveTransparency);
918
0
          mDoesHaveTransparency = true;
919
0
        }
920
0
      }
921
0
      break;
922
0
923
0
    default:
924
0
      MOZ_CRASH("Unsupported color depth; earlier check didn't catch it?");
925
0
  }
926
0
927
0
  FinishRow();
928
0
  return mCurrentRow == 0
929
0
       ? Transition::TerminateSuccess()
930
0
       : Transition::To(State::PIXEL_ROW, mPixelRowSize);
931
0
}
932
933
LexerTransition<nsBMPDecoder::State>
934
nsBMPDecoder::ReadRLESegment(const char* aData)
935
0
{
936
0
  if (mCurrentRow == 0) {
937
0
    return Transition::TerminateSuccess();
938
0
  }
939
0
940
0
  uint8_t byte1 = uint8_t(aData[0]);
941
0
  uint8_t byte2 = uint8_t(aData[1]);
942
0
943
0
  if (byte1 != RLE::ESCAPE) {
944
0
    // Encoded mode consists of two bytes: byte1 specifies the number of
945
0
    // consecutive pixels to be drawn using the color index contained in
946
0
    // byte2.
947
0
    //
948
0
    // Work around bitmaps that specify too many pixels.
949
0
    uint32_t pixelsNeeded =
950
0
      std::min<uint32_t>(mH.mWidth - mCurrentPos, byte1);
951
0
    if (pixelsNeeded) {
952
0
      uint32_t* dst = RowBuffer();
953
0
      mCurrentPos += pixelsNeeded;
954
0
      if (mH.mCompression == Compression::RLE8) {
955
0
        do {
956
0
          SetPixel(dst, byte2, mColors);
957
0
          pixelsNeeded --;
958
0
        } while (pixelsNeeded);
959
0
      } else {
960
0
        do {
961
0
          Set4BitPixel(dst, byte2, pixelsNeeded, mColors);
962
0
        } while (pixelsNeeded);
963
0
      }
964
0
    }
965
0
    return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
966
0
  }
967
0
968
0
  if (byte2 == RLE::ESCAPE_EOL) {
969
0
    mCurrentPos = 0;
970
0
    FinishRow();
971
0
    return mCurrentRow == 0
972
0
         ? Transition::TerminateSuccess()
973
0
         : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
974
0
  }
975
0
976
0
  if (byte2 == RLE::ESCAPE_EOF) {
977
0
    return Transition::TerminateSuccess();
978
0
  }
979
0
980
0
  if (byte2 == RLE::ESCAPE_DELTA) {
981
0
    return Transition::To(State::RLE_DELTA, RLE::DELTA_LENGTH);
982
0
  }
983
0
984
0
  // Absolute mode. |byte2| gives the number of pixels. The length depends on
985
0
  // whether it's 4-bit or 8-bit RLE. Also, the length must be even (and zero
986
0
  // padding is used to achieve this when necessary).
987
0
  MOZ_ASSERT(mAbsoluteModeNumPixels == 0);
988
0
  mAbsoluteModeNumPixels = byte2;
989
0
  uint32_t length = byte2;
990
0
  if (mH.mCompression == Compression::RLE4) {
991
0
    length = (length + 1) / 2;    // halve, rounding up
992
0
  }
993
0
  if (length & 1) {
994
0
    length++;
995
0
  }
996
0
  return Transition::To(State::RLE_ABSOLUTE, length);
997
0
}
998
999
LexerTransition<nsBMPDecoder::State>
1000
nsBMPDecoder::ReadRLEDelta(const char* aData)
1001
0
{
1002
0
  // Delta encoding makes it possible to skip pixels making part of the image
1003
0
  // transparent.
1004
0
  MOZ_ASSERT(mMayHaveTransparency);
1005
0
  mDoesHaveTransparency = true;
1006
0
1007
0
  if (mDownscaler) {
1008
0
    // Clear the skipped pixels. (This clears to the end of the row,
1009
0
    // which is perfect if there's a Y delta and harmless if not).
1010
0
    mDownscaler->ClearRestOfRow(/* aStartingAtCol = */ mCurrentPos);
1011
0
  }
1012
0
1013
0
  // Handle the XDelta.
1014
0
  mCurrentPos += uint8_t(aData[0]);
1015
0
  if (mCurrentPos > mH.mWidth) {
1016
0
    mCurrentPos = mH.mWidth;
1017
0
  }
1018
0
1019
0
  // Handle the Y Delta.
1020
0
  int32_t yDelta = std::min<int32_t>(uint8_t(aData[1]), mCurrentRow);
1021
0
  mCurrentRow -= yDelta;
1022
0
1023
0
  if (mDownscaler && yDelta > 0) {
1024
0
    // Commit the current row (the first of the skipped rows).
1025
0
    mDownscaler->CommitRow();
1026
0
1027
0
    // Clear and commit the remaining skipped rows.
1028
0
    for (int32_t line = 1; line < yDelta; line++) {
1029
0
      mDownscaler->ClearRow();
1030
0
      mDownscaler->CommitRow();
1031
0
    }
1032
0
  }
1033
0
1034
0
  return mCurrentRow == 0
1035
0
       ? Transition::TerminateSuccess()
1036
0
       : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1037
0
}
1038
1039
LexerTransition<nsBMPDecoder::State>
1040
nsBMPDecoder::ReadRLEAbsolute(const char* aData, size_t aLength)
1041
0
{
1042
0
  uint32_t n = mAbsoluteModeNumPixels;
1043
0
  mAbsoluteModeNumPixels = 0;
1044
0
1045
0
  if (mCurrentPos + n > uint32_t(mH.mWidth)) {
1046
0
    // Bad data. Stop decoding; at least part of the image may have been
1047
0
    // decoded.
1048
0
    return Transition::TerminateSuccess();
1049
0
  }
1050
0
1051
0
  // In absolute mode, n represents the number of pixels that follow, each of
1052
0
  // which contains the color index of a single pixel.
1053
0
  uint32_t* dst = RowBuffer();
1054
0
  uint32_t iSrc = 0;
1055
0
  uint32_t* oldPos = dst;
1056
0
  if (mH.mCompression == Compression::RLE8) {
1057
0
    while (n > 0) {
1058
0
      SetPixel(dst, aData[iSrc], mColors);
1059
0
      n--;
1060
0
      iSrc++;
1061
0
    }
1062
0
  } else {
1063
0
    while (n > 0) {
1064
0
      Set4BitPixel(dst, aData[iSrc], n, mColors);
1065
0
      iSrc++;
1066
0
    }
1067
0
  }
1068
0
  mCurrentPos += dst - oldPos;
1069
0
1070
0
  // We should read all the data (unless the last byte is zero padding).
1071
0
  MOZ_ASSERT(iSrc == aLength - 1 || iSrc == aLength);
1072
0
1073
0
  return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
1074
0
}
1075
1076
} // namespace image
1077
} // namespace mozilla