Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/encoders/bmp/nsBMPEncoder.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "nsCRT.h"
7
#include "mozilla/EndianUtils.h"
8
#include "mozilla/UniquePtrExtensions.h"
9
#include "nsBMPEncoder.h"
10
#include "nsString.h"
11
#include "nsStreamUtils.h"
12
#include "nsTArray.h"
13
#include "mozilla/CheckedInt.h"
14
15
using namespace mozilla;
16
using namespace mozilla::image;
17
using namespace mozilla::image::bmp;
18
19
NS_IMPL_ISUPPORTS(nsBMPEncoder, imgIEncoder, nsIInputStream,
20
                  nsIAsyncInputStream)
21
22
nsBMPEncoder::nsBMPEncoder()
23
  : mBMPInfoHeader{}
24
  , mImageBufferStart(nullptr)
25
  , mImageBufferCurr(0)
26
  , mImageBufferSize(0)
27
  , mImageBufferReadPoint(0)
28
  , mFinished(false)
29
  , mCallback(nullptr)
30
  , mCallbackTarget(nullptr)
31
  , mNotifyThreshold(0)
32
0
{
33
0
  this->mBMPFileHeader.filesize = 0;
34
0
  this->mBMPFileHeader.reserved = 0;
35
0
  this->mBMPFileHeader.dataoffset = 0;
36
0
}
37
38
nsBMPEncoder::~nsBMPEncoder()
39
0
{
40
0
  if (mImageBufferStart) {
41
0
    free(mImageBufferStart);
42
0
    mImageBufferStart = nullptr;
43
0
    mImageBufferCurr = nullptr;
44
0
  }
45
0
}
46
47
// nsBMPEncoder::InitFromData
48
//
49
// One output option is supported: bpp=<bpp_value>
50
// bpp specifies the bits per pixel to use where bpp_value can be 24 or 32
51
NS_IMETHODIMP
52
nsBMPEncoder::InitFromData(const uint8_t* aData,
53
                           uint32_t aLength, // (unused, req'd by JS)
54
                           uint32_t aWidth,
55
                           uint32_t aHeight,
56
                           uint32_t aStride,
57
                           uint32_t aInputFormat,
58
                           const nsAString& aOutputOptions)
59
0
{
60
0
  // validate input format
61
0
  if (aInputFormat != INPUT_FORMAT_RGB &&
62
0
      aInputFormat != INPUT_FORMAT_RGBA &&
63
0
      aInputFormat != INPUT_FORMAT_HOSTARGB) {
64
0
    return NS_ERROR_INVALID_ARG;
65
0
  }
66
0
67
0
  CheckedInt32 check = CheckedInt32(aWidth) * 4;
68
0
  if (MOZ_UNLIKELY(!check.isValid())) {
69
0
    return NS_ERROR_INVALID_ARG;
70
0
  }
71
0
72
0
  // Stride is the padded width of each row, so it better be longer
73
0
  if ((aInputFormat == INPUT_FORMAT_RGB &&
74
0
       aStride < aWidth * 3) ||
75
0
      ((aInputFormat == INPUT_FORMAT_RGBA ||
76
0
        aInputFormat == INPUT_FORMAT_HOSTARGB) &&
77
0
       aStride < aWidth * 4)) {
78
0
      NS_WARNING("Invalid stride for InitFromData");
79
0
      return NS_ERROR_INVALID_ARG;
80
0
  }
81
0
82
0
  nsresult rv;
83
0
  rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions);
84
0
  if (NS_FAILED(rv)) {
85
0
    return rv;
86
0
  }
87
0
88
0
  rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride,
89
0
                     aInputFormat, aOutputOptions);
90
0
  if (NS_FAILED(rv)) {
91
0
    return rv;
92
0
  }
93
0
94
0
  rv = EndImageEncode();
95
0
  return rv;
96
0
}
97
98
// Just a helper method to make it explicit in calculations that we are dealing
99
// with bytes and not bits
100
static inline uint16_t
101
BytesPerPixel(uint16_t aBPP)
102
0
{
103
0
  return aBPP / 8;
104
0
}
105
106
// Calculates the number of padding bytes that are needed per row of image data
107
static inline uint32_t
108
PaddingBytes(uint16_t aBPP, uint32_t aWidth)
109
0
{
110
0
  uint32_t rowSize = aWidth * BytesPerPixel(aBPP);
111
0
  uint8_t paddingSize = 0;
112
0
  if (rowSize % 4) {
113
0
    paddingSize = (4 - (rowSize % 4));
114
0
  }
115
0
  return paddingSize;
116
0
}
117
118
// See ::InitFromData for other info.
119
NS_IMETHODIMP
120
nsBMPEncoder::StartImageEncode(uint32_t aWidth,
121
                               uint32_t aHeight,
122
                               uint32_t aInputFormat,
123
                               const nsAString& aOutputOptions)
124
0
{
125
0
  // can't initialize more than once
126
0
  if (mImageBufferStart || mImageBufferCurr) {
127
0
    return NS_ERROR_ALREADY_INITIALIZED;
128
0
  }
129
0
130
0
  // validate input format
131
0
  if (aInputFormat != INPUT_FORMAT_RGB &&
132
0
      aInputFormat != INPUT_FORMAT_RGBA &&
133
0
      aInputFormat != INPUT_FORMAT_HOSTARGB) {
134
0
    return NS_ERROR_INVALID_ARG;
135
0
  }
136
0
137
0
  // parse and check any provided output options
138
0
  Version version;
139
0
  uint16_t bpp;
140
0
  nsresult rv = ParseOptions(aOutputOptions, version, bpp);
141
0
  if (NS_FAILED(rv)) {
142
0
    return rv;
143
0
  }
144
0
  MOZ_ASSERT(bpp <= 32);
145
0
146
0
  rv = InitFileHeader(version, bpp, aWidth, aHeight);
147
0
  if (NS_FAILED(rv)) {
148
0
    return rv;
149
0
  }
150
0
  rv = InitInfoHeader(version, bpp, aWidth, aHeight);
151
0
  if (NS_FAILED(rv)) {
152
0
    return rv;
153
0
  }
154
0
155
0
  mImageBufferSize = mBMPFileHeader.filesize;
156
0
  mImageBufferStart = static_cast<uint8_t*>(malloc(mImageBufferSize));
157
0
  if (!mImageBufferStart) {
158
0
    return NS_ERROR_OUT_OF_MEMORY;
159
0
  }
160
0
  mImageBufferCurr = mImageBufferStart;
161
0
162
0
  EncodeFileHeader();
163
0
  EncodeInfoHeader();
164
0
165
0
  return NS_OK;
166
0
}
167
168
// Returns the number of bytes in the image buffer used.
169
// For a BMP file, this is all bytes in the buffer.
170
NS_IMETHODIMP
171
nsBMPEncoder::GetImageBufferUsed(uint32_t* aOutputSize)
172
0
{
173
0
  NS_ENSURE_ARG_POINTER(aOutputSize);
174
0
  *aOutputSize = mImageBufferSize;
175
0
  return NS_OK;
176
0
}
177
178
// Returns a pointer to the start of the image buffer
179
NS_IMETHODIMP
180
nsBMPEncoder::GetImageBuffer(char** aOutputBuffer)
181
0
{
182
0
  NS_ENSURE_ARG_POINTER(aOutputBuffer);
183
0
  *aOutputBuffer = reinterpret_cast<char*>(mImageBufferStart);
184
0
  return NS_OK;
185
0
}
186
187
NS_IMETHODIMP
188
nsBMPEncoder::AddImageFrame(const uint8_t* aData,
189
                            uint32_t aLength, // (unused, req'd by JS)
190
                            uint32_t aWidth,
191
                            uint32_t aHeight,
192
                            uint32_t aStride,
193
                            uint32_t aInputFormat,
194
                            const nsAString& aFrameOptions)
195
0
{
196
0
  // must be initialized
197
0
  if (!mImageBufferStart || !mImageBufferCurr) {
198
0
    return NS_ERROR_NOT_INITIALIZED;
199
0
  }
200
0
201
0
  // validate input format
202
0
  if (aInputFormat != INPUT_FORMAT_RGB &&
203
0
      aInputFormat != INPUT_FORMAT_RGBA &&
204
0
      aInputFormat != INPUT_FORMAT_HOSTARGB) {
205
0
    return NS_ERROR_INVALID_ARG;
206
0
  }
207
0
208
0
  if (mBMPInfoHeader.width < 0) {
209
0
    return NS_ERROR_ILLEGAL_VALUE;
210
0
  }
211
0
212
0
  CheckedUint32 size =
213
0
    CheckedUint32(mBMPInfoHeader.width) * CheckedUint32(BytesPerPixel(mBMPInfoHeader.bpp));
214
0
  if (MOZ_UNLIKELY(!size.isValid())) {
215
0
    return NS_ERROR_FAILURE;
216
0
  }
217
0
218
0
  auto row = MakeUniqueFallible<uint8_t[]>(size.value());
219
0
  if (!row) {
220
0
    return NS_ERROR_OUT_OF_MEMORY;
221
0
  }
222
0
223
0
  CheckedUint32 check = CheckedUint32(mBMPInfoHeader.height) * aStride;
224
0
  if (MOZ_UNLIKELY(!check.isValid())) {
225
0
    return NS_ERROR_FAILURE;
226
0
  }
227
0
228
0
  // write each row: if we add more input formats, we may want to
229
0
  // generalize the conversions
230
0
  if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
231
0
    // BMP requires RGBA with post-multiplied alpha, so we need to convert
232
0
    for (int32_t y = mBMPInfoHeader.height - 1; y >= 0 ; y --) {
233
0
      ConvertHostARGBRow(&aData[y * aStride], row, mBMPInfoHeader.width);
234
0
      if(mBMPInfoHeader.bpp == 24) {
235
0
        EncodeImageDataRow24(row.get());
236
0
      } else {
237
0
        EncodeImageDataRow32(row.get());
238
0
      }
239
0
    }
240
0
  } else if (aInputFormat == INPUT_FORMAT_RGBA) {
241
0
    // simple RGBA, no conversion needed
242
0
    for (int32_t y = 0; y < mBMPInfoHeader.height; y++) {
243
0
      if (mBMPInfoHeader.bpp == 24) {
244
0
        EncodeImageDataRow24(row.get());
245
0
      } else {
246
0
        EncodeImageDataRow32(row.get());
247
0
      }
248
0
    }
249
0
  } else if (aInputFormat == INPUT_FORMAT_RGB) {
250
0
    // simple RGB, no conversion needed
251
0
    for (int32_t y = 0; y < mBMPInfoHeader.height; y++) {
252
0
      if (mBMPInfoHeader.bpp == 24) {
253
0
        EncodeImageDataRow24(&aData[y * aStride]);
254
0
      } else {
255
0
        EncodeImageDataRow32(&aData[y * aStride]);
256
0
      }
257
0
    }
258
0
  } else {
259
0
    MOZ_ASSERT_UNREACHABLE("Bad format type");
260
0
    return NS_ERROR_INVALID_ARG;
261
0
  }
262
0
263
0
  return NS_OK;
264
0
}
265
266
267
NS_IMETHODIMP
268
nsBMPEncoder::EndImageEncode()
269
0
{
270
0
  // must be initialized
271
0
  if (!mImageBufferStart || !mImageBufferCurr) {
272
0
    return NS_ERROR_NOT_INITIALIZED;
273
0
  }
274
0
275
0
  mFinished = true;
276
0
  NotifyListener();
277
0
278
0
  // if output callback can't get enough memory, it will free our buffer
279
0
  if (!mImageBufferStart || !mImageBufferCurr) {
280
0
    return NS_ERROR_OUT_OF_MEMORY;
281
0
  }
282
0
283
0
  return NS_OK;
284
0
}
285
286
287
// Parses the encoder options and sets the bits per pixel to use
288
// See InitFromData for a description of the parse options
289
nsresult
290
nsBMPEncoder::ParseOptions(const nsAString& aOptions, Version& aVersionOut,
291
                           uint16_t& aBppOut)
292
0
{
293
0
  aVersionOut = VERSION_3;
294
0
  aBppOut = 24;
295
0
296
0
  // Parse the input string into a set of name/value pairs.
297
0
  // From a format like: name=value;bpp=<bpp_value>;name=value
298
0
  // to format: [0] = name=value, [1] = bpp=<bpp_value>, [2] = name=value
299
0
  nsTArray<nsCString> nameValuePairs;
300
0
  if (!ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs)) {
301
0
    return NS_ERROR_INVALID_ARG;
302
0
  }
303
0
304
0
  // For each name/value pair in the set
305
0
  for (uint32_t i = 0; i < nameValuePairs.Length(); ++i) {
306
0
307
0
    // Split the name value pair [0] = name, [1] = value
308
0
    nsTArray<nsCString> nameValuePair;
309
0
    if (!ParseString(nameValuePairs[i], '=', nameValuePair)) {
310
0
      return NS_ERROR_INVALID_ARG;
311
0
    }
312
0
    if (nameValuePair.Length() != 2) {
313
0
      return NS_ERROR_INVALID_ARG;
314
0
    }
315
0
316
0
    // Parse the bpp portion of the string name=value;version=<version_value>;
317
0
    // name=value
318
0
    if (nameValuePair[0].Equals("version",
319
0
                                nsCaseInsensitiveCStringComparator())) {
320
0
      if (nameValuePair[1].EqualsLiteral("3")) {
321
0
        aVersionOut = VERSION_3;
322
0
      } else if (nameValuePair[1].EqualsLiteral("5")) {
323
0
        aVersionOut = VERSION_5;
324
0
      } else {
325
0
        return NS_ERROR_INVALID_ARG;
326
0
      }
327
0
    }
328
0
329
0
    // Parse the bpp portion of the string name=value;bpp=<bpp_value>;name=value
330
0
    if (nameValuePair[0].Equals("bpp", nsCaseInsensitiveCStringComparator())) {
331
0
      if (nameValuePair[1].EqualsLiteral("24")) {
332
0
        aBppOut = 24;
333
0
      } else if (nameValuePair[1].EqualsLiteral("32")) {
334
0
        aBppOut = 32;
335
0
      } else {
336
0
        return NS_ERROR_INVALID_ARG;
337
0
      }
338
0
    }
339
0
  }
340
0
341
0
  return NS_OK;
342
0
}
343
344
NS_IMETHODIMP
345
nsBMPEncoder::Close()
346
0
{
347
0
  if (mImageBufferStart) {
348
0
    free(mImageBufferStart);
349
0
    mImageBufferStart = nullptr;
350
0
    mImageBufferSize = 0;
351
0
    mImageBufferReadPoint = 0;
352
0
    mImageBufferCurr = nullptr;
353
0
  }
354
0
355
0
  return NS_OK;
356
0
}
357
358
// Obtains the available bytes to read
359
NS_IMETHODIMP
360
nsBMPEncoder::Available(uint64_t* _retval)
361
0
{
362
0
  if (!mImageBufferStart || !mImageBufferCurr) {
363
0
    return NS_BASE_STREAM_CLOSED;
364
0
  }
365
0
366
0
  *_retval = GetCurrentImageBufferOffset() - mImageBufferReadPoint;
367
0
  return NS_OK;
368
0
}
369
370
// [noscript] Reads bytes which are available
371
NS_IMETHODIMP
372
nsBMPEncoder::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
373
0
{
374
0
  return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
375
0
}
376
377
// [noscript] Reads segments
378
NS_IMETHODIMP
379
nsBMPEncoder::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
380
                           uint32_t aCount, uint32_t* _retval)
381
0
{
382
0
  uint32_t maxCount = GetCurrentImageBufferOffset() - mImageBufferReadPoint;
383
0
  if (maxCount == 0) {
384
0
    *_retval = 0;
385
0
    return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
386
0
  }
387
0
388
0
  if (aCount > maxCount) {
389
0
    aCount = maxCount;
390
0
  }
391
0
  nsresult rv = aWriter(this, aClosure,
392
0
                        reinterpret_cast<const char*>(mImageBufferStart +
393
0
                                                      mImageBufferReadPoint),
394
0
                        0, aCount, _retval);
395
0
  if (NS_SUCCEEDED(rv)) {
396
0
    NS_ASSERTION(*_retval <= aCount, "bad write count");
397
0
    mImageBufferReadPoint += *_retval;
398
0
  }
399
0
  // errors returned from the writer end here!
400
0
  return NS_OK;
401
0
}
402
403
NS_IMETHODIMP
404
nsBMPEncoder::IsNonBlocking(bool* _retval)
405
0
{
406
0
  *_retval = true;
407
0
  return NS_OK;
408
0
}
409
410
NS_IMETHODIMP
411
nsBMPEncoder::AsyncWait(nsIInputStreamCallback* aCallback,
412
                        uint32_t aFlags,
413
                        uint32_t aRequestedCount,
414
                        nsIEventTarget* aTarget)
415
0
{
416
0
  if (aFlags != 0) {
417
0
    return NS_ERROR_NOT_IMPLEMENTED;
418
0
  }
419
0
420
0
  if (mCallback || mCallbackTarget) {
421
0
    return NS_ERROR_UNEXPECTED;
422
0
  }
423
0
424
0
  mCallbackTarget = aTarget;
425
0
  // 0 means "any number of bytes except 0"
426
0
  mNotifyThreshold = aRequestedCount;
427
0
  if (!aRequestedCount) {
428
0
    mNotifyThreshold = 1024; // We don't want to notify incessantly
429
0
  }
430
0
431
0
  // We set the callback absolutely last, because NotifyListener uses it to
432
0
  // determine if someone needs to be notified.  If we don't set it last,
433
0
  // NotifyListener might try to fire off a notification to a null target
434
0
  // which will generally cause non-threadsafe objects to be used off the
435
0
  // main thread
436
0
  mCallback = aCallback;
437
0
438
0
  // What we are being asked for may be present already
439
0
  NotifyListener();
440
0
  return NS_OK;
441
0
}
442
443
NS_IMETHODIMP
444
nsBMPEncoder::CloseWithStatus(nsresult aStatus)
445
0
{
446
0
  return Close();
447
0
}
448
449
// nsBMPEncoder::ConvertHostARGBRow
450
//
451
//    Our colors are stored with premultiplied alphas, but we need
452
//    an output with no alpha in machine-independent byte order.
453
//
454
void
455
nsBMPEncoder::ConvertHostARGBRow(const uint8_t* aSrc,
456
                                 const UniquePtr<uint8_t[]>& aDest,
457
                                 uint32_t aPixelWidth)
458
0
{
459
0
  uint16_t bytes = BytesPerPixel(mBMPInfoHeader.bpp);
460
0
461
0
  if (mBMPInfoHeader.bpp == 32) {
462
0
    for (uint32_t x = 0; x < aPixelWidth; x++) {
463
0
      const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x];
464
0
      uint8_t* pixelOut = &aDest[x * bytes];
465
0
466
0
      pixelOut[0] = (pixelIn & 0x00ff0000) >> 16;
467
0
      pixelOut[1] = (pixelIn & 0x0000ff00) >>  8;
468
0
      pixelOut[2] = (pixelIn & 0x000000ff) >>  0;
469
0
      pixelOut[3] = (pixelIn & 0xff000000) >> 24;
470
0
    }
471
0
  } else {
472
0
    for (uint32_t x = 0; x < aPixelWidth; x++) {
473
0
      const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x];
474
0
      uint8_t* pixelOut = &aDest[x * bytes];
475
0
476
0
      pixelOut[0] = (pixelIn & 0xff0000) >> 16;
477
0
      pixelOut[1] = (pixelIn & 0x00ff00) >>  8;
478
0
      pixelOut[2] = (pixelIn & 0x0000ff) >>  0;
479
0
    }
480
0
  }
481
0
}
482
483
void
484
nsBMPEncoder::NotifyListener()
485
0
{
486
0
  if (mCallback &&
487
0
      (GetCurrentImageBufferOffset() - mImageBufferReadPoint >=
488
0
       mNotifyThreshold || mFinished)) {
489
0
    nsCOMPtr<nsIInputStreamCallback> callback;
490
0
    if (mCallbackTarget) {
491
0
      callback = NS_NewInputStreamReadyEvent("nsBMPEncoder::NotifyListener",
492
0
                                             mCallback, mCallbackTarget);
493
0
    } else {
494
0
      callback = mCallback;
495
0
    }
496
0
497
0
    NS_ASSERTION(callback, "Shouldn't fail to make the callback");
498
0
    // Null the callback first because OnInputStreamReady could
499
0
    // reenter AsyncWait
500
0
    mCallback = nullptr;
501
0
    mCallbackTarget = nullptr;
502
0
    mNotifyThreshold = 0;
503
0
504
0
    callback->OnInputStreamReady(this);
505
0
  }
506
0
}
507
508
// Initializes the BMP file header mBMPFileHeader to the passed in values
509
nsresult
510
nsBMPEncoder::InitFileHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
511
                             uint32_t aHeight)
512
0
{
513
0
  memset(&mBMPFileHeader, 0, sizeof(mBMPFileHeader));
514
0
  mBMPFileHeader.signature[0] = 'B';
515
0
  mBMPFileHeader.signature[1] = 'M';
516
0
517
0
  if (aVersion == VERSION_3) {
518
0
    mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V3;
519
0
  } else { // aVersion == 5
520
0
    mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V5;
521
0
  }
522
0
523
0
  // The color table is present only if BPP is <= 8
524
0
  if (aBPP <= 8) {
525
0
    uint32_t numColors = 1 << aBPP;
526
0
    mBMPFileHeader.dataoffset += 4 * numColors;
527
0
    CheckedUint32 filesize =
528
0
      CheckedUint32(mBMPFileHeader.dataoffset) + CheckedUint32(aWidth) * aHeight;
529
0
    if (MOZ_UNLIKELY(!filesize.isValid())) {
530
0
      return NS_ERROR_INVALID_ARG;
531
0
    }
532
0
    mBMPFileHeader.filesize = filesize.value();
533
0
  } else {
534
0
    CheckedUint32 filesize =
535
0
      CheckedUint32(mBMPFileHeader.dataoffset) +
536
0
        (CheckedUint32(aWidth) * BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * aHeight;
537
0
    if (MOZ_UNLIKELY(!filesize.isValid())) {
538
0
      return NS_ERROR_INVALID_ARG;
539
0
    }
540
0
    mBMPFileHeader.filesize = filesize.value();
541
0
  }
542
0
543
0
  mBMPFileHeader.reserved = 0;
544
0
545
0
  return NS_OK;
546
0
}
547
548
#define ENCODE(pImageBufferCurr, value) \
549
0
    memcpy(*pImageBufferCurr, &value, sizeof value); \
550
0
    *pImageBufferCurr += sizeof value;
551
552
// Initializes the bitmap info header mBMPInfoHeader to the passed in values
553
nsresult
554
nsBMPEncoder::InitInfoHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
555
                             uint32_t aHeight)
556
0
{
557
0
  memset(&mBMPInfoHeader, 0, sizeof(mBMPInfoHeader));
558
0
  if (aVersion == VERSION_3) {
559
0
    mBMPInfoHeader.bihsize = InfoHeaderLength::WIN_V3;
560
0
  } else {
561
0
    MOZ_ASSERT(aVersion == VERSION_5);
562
0
    mBMPInfoHeader.bihsize = InfoHeaderLength::WIN_V5;
563
0
  }
564
0
565
0
  CheckedInt32 width(aWidth);
566
0
  CheckedInt32 height(aHeight);
567
0
  if (MOZ_UNLIKELY(!width.isValid() || !height.isValid())) {
568
0
    return NS_ERROR_INVALID_ARG;
569
0
  }
570
0
  mBMPInfoHeader.width = width.value();
571
0
  mBMPInfoHeader.height = height.value();
572
0
573
0
  mBMPInfoHeader.planes = 1;
574
0
  mBMPInfoHeader.bpp = aBPP;
575
0
  mBMPInfoHeader.compression = 0;
576
0
  mBMPInfoHeader.colors = 0;
577
0
  mBMPInfoHeader.important_colors = 0;
578
0
579
0
  CheckedUint32 check = CheckedUint32(aWidth) * BytesPerPixel(aBPP);
580
0
  if (MOZ_UNLIKELY(!check.isValid())) {
581
0
    return NS_ERROR_INVALID_ARG;
582
0
  }
583
0
584
0
  if (aBPP <= 8) {
585
0
    CheckedUint32 imagesize = CheckedUint32(aWidth) * aHeight;
586
0
    if (MOZ_UNLIKELY(!imagesize.isValid())) {
587
0
      return NS_ERROR_INVALID_ARG;
588
0
    }
589
0
    mBMPInfoHeader.image_size = imagesize.value();
590
0
  } else {
591
0
    CheckedUint32 imagesize =
592
0
      (CheckedUint32(aWidth) * BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * CheckedUint32(aHeight);
593
0
    if (MOZ_UNLIKELY(!imagesize.isValid())) {
594
0
      return NS_ERROR_INVALID_ARG;
595
0
    }
596
0
    mBMPInfoHeader.image_size = imagesize.value();
597
0
  }
598
0
  mBMPInfoHeader.xppm = 0;
599
0
  mBMPInfoHeader.yppm = 0;
600
0
  if (aVersion >= VERSION_5) {
601
0
      mBMPInfoHeader.red_mask   = 0x000000FF;
602
0
      mBMPInfoHeader.green_mask = 0x0000FF00;
603
0
      mBMPInfoHeader.blue_mask  = 0x00FF0000;
604
0
      mBMPInfoHeader.alpha_mask = 0xFF000000;
605
0
      mBMPInfoHeader.color_space = V5InfoHeader::COLOR_SPACE_LCS_SRGB;
606
0
      mBMPInfoHeader.white_point.r.x = 0;
607
0
      mBMPInfoHeader.white_point.r.y = 0;
608
0
      mBMPInfoHeader.white_point.r.z = 0;
609
0
      mBMPInfoHeader.white_point.g.x = 0;
610
0
      mBMPInfoHeader.white_point.g.y = 0;
611
0
      mBMPInfoHeader.white_point.g.z = 0;
612
0
      mBMPInfoHeader.white_point.b.x = 0;
613
0
      mBMPInfoHeader.white_point.b.y = 0;
614
0
      mBMPInfoHeader.white_point.b.z = 0;
615
0
      mBMPInfoHeader.gamma_red = 0;
616
0
      mBMPInfoHeader.gamma_green = 0;
617
0
      mBMPInfoHeader.gamma_blue = 0;
618
0
      mBMPInfoHeader.intent = 0;
619
0
      mBMPInfoHeader.profile_offset = 0;
620
0
      mBMPInfoHeader.profile_size = 0;
621
0
      mBMPInfoHeader.reserved = 0;
622
0
  }
623
0
624
0
  return NS_OK;
625
0
}
626
627
// Encodes the BMP file header mBMPFileHeader
628
void
629
nsBMPEncoder::EncodeFileHeader()
630
0
{
631
0
  FileHeader littleEndianBFH = mBMPFileHeader;
632
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.filesize, 1);
633
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.reserved, 1);
634
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.dataoffset, 1);
635
0
636
0
  ENCODE(&mImageBufferCurr, littleEndianBFH.signature);
637
0
  ENCODE(&mImageBufferCurr, littleEndianBFH.filesize);
638
0
  ENCODE(&mImageBufferCurr, littleEndianBFH.reserved);
639
0
  ENCODE(&mImageBufferCurr, littleEndianBFH.dataoffset);
640
0
}
641
642
// Encodes the BMP infor header mBMPInfoHeader
643
void
644
nsBMPEncoder::EncodeInfoHeader()
645
0
{
646
0
  V5InfoHeader littleEndianmBIH = mBMPInfoHeader;
647
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.bihsize, 1);
648
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.width, 1);
649
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.height, 1);
650
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.planes, 1);
651
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.bpp, 1);
652
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.compression, 1);
653
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.image_size, 1);
654
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.xppm, 1);
655
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.yppm, 1);
656
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.colors, 1);
657
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.important_colors,
658
0
                                          1);
659
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.red_mask, 1);
660
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.green_mask, 1);
661
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.blue_mask, 1);
662
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.alpha_mask, 1);
663
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.color_space, 1);
664
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.x, 1);
665
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.y, 1);
666
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.z, 1);
667
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.x, 1);
668
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.y, 1);
669
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.z, 1);
670
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.x, 1);
671
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.y, 1);
672
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.z, 1);
673
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_red, 1);
674
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_green, 1);
675
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_blue, 1);
676
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.intent, 1);
677
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_offset, 1);
678
0
  NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_size, 1);
679
0
680
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.bihsize);
681
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.width);
682
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.height);
683
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.planes);
684
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.bpp);
685
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.compression);
686
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.image_size);
687
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.xppm);
688
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.yppm);
689
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.colors);
690
0
  ENCODE(&mImageBufferCurr, littleEndianmBIH.important_colors);
691
0
692
0
  if (mBMPInfoHeader.bihsize > InfoHeaderLength::WIN_V3) {
693
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.red_mask);
694
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.green_mask);
695
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.blue_mask);
696
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.alpha_mask);
697
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.color_space);
698
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.x);
699
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.y);
700
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.z);
701
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.x);
702
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.y);
703
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.z);
704
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.x);
705
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.y);
706
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.z);
707
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_red);
708
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_green);
709
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_blue);
710
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.intent);
711
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_offset);
712
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_size);
713
0
    ENCODE(&mImageBufferCurr, littleEndianmBIH.reserved);
714
0
  }
715
0
}
716
717
// Sets a pixel in the image buffer that doesn't have alpha data
718
static inline void
719
SetPixel24(uint8_t*& imageBufferCurr, uint8_t aRed, uint8_t aGreen,
720
  uint8_t aBlue)
721
0
{
722
0
  *imageBufferCurr = aBlue;
723
0
  *(imageBufferCurr + 1) = aGreen;
724
0
  *(imageBufferCurr + 2) = aRed;
725
0
}
726
727
// Sets a pixel in the image buffer with alpha data
728
static inline void
729
SetPixel32(uint8_t*& imageBufferCurr, uint8_t aRed, uint8_t aGreen,
730
           uint8_t aBlue, uint8_t aAlpha = 0xFF)
731
0
{
732
0
  *imageBufferCurr = aBlue;
733
0
  *(imageBufferCurr + 1) = aGreen;
734
0
  *(imageBufferCurr + 2) = aRed;
735
0
  *(imageBufferCurr + 3) = aAlpha;
736
0
}
737
738
// Encodes a row of image data which does not have alpha data
739
void
740
nsBMPEncoder::EncodeImageDataRow24(const uint8_t* aData)
741
0
{
742
0
  for (int32_t x = 0; x < mBMPInfoHeader.width; x++) {
743
0
    uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp);
744
0
    SetPixel24(mImageBufferCurr, aData[pos], aData[pos + 1], aData[pos + 2]);
745
0
    mImageBufferCurr += BytesPerPixel(mBMPInfoHeader.bpp);
746
0
  }
747
0
748
0
  for (uint32_t x = 0; x < PaddingBytes(mBMPInfoHeader.bpp,
749
0
                                        mBMPInfoHeader.width); x++) {
750
0
    *mImageBufferCurr++ = 0;
751
0
  }
752
0
}
753
754
// Encodes a row of image data which does have alpha data
755
void
756
nsBMPEncoder::EncodeImageDataRow32(const uint8_t* aData)
757
0
{
758
0
  for (int32_t x = 0; x < mBMPInfoHeader.width; x++) {
759
0
    uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp);
760
0
    SetPixel32(mImageBufferCurr, aData[pos], aData[pos + 1],
761
0
               aData[pos + 2], aData[pos + 3]);
762
0
    mImageBufferCurr += 4;
763
0
  }
764
0
765
0
  for (uint32_t x = 0; x < PaddingBytes(mBMPInfoHeader.bpp,
766
0
                                        mBMPInfoHeader.width); x++) {
767
0
    *mImageBufferCurr++ = 0;
768
0
  }
769
0
}