LCOV - code coverage report
Current view: top level - image/encoders/bmp - nsBMPEncoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 403 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 31 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13