tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsBMPEncoder.cpp (25288B)


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