image/encoders/ico/nsICOEncoder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "nsCRT.h"
     6 #include "mozilla/Endian.h"
     7 #include "nsBMPEncoder.h"
     8 #include "nsPNGEncoder.h"
     9 #include "nsICOEncoder.h"
    10 #include "prprf.h"
    11 #include "nsString.h"
    12 #include "nsStreamUtils.h"
    13 #include "nsTArray.h"
    15 using namespace mozilla;
    16 using namespace mozilla::image;
    18 NS_IMPL_ISUPPORTS(nsICOEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
    20 nsICOEncoder::nsICOEncoder() : mImageBufferStart(nullptr),
    21                                mImageBufferCurr(0),
    22                                mImageBufferSize(0), 
    23                                mImageBufferReadPoint(0), 
    24                                mFinished(false),
    25                                mUsePNG(true),
    26                                mNotifyThreshold(0)
    27 {
    28 }
    30 nsICOEncoder::~nsICOEncoder()
    31 {
    32   if (mImageBufferStart) {
    33     moz_free(mImageBufferStart);
    34     mImageBufferStart = nullptr;
    35     mImageBufferCurr = nullptr;
    36   }
    37 }
    39 // nsICOEncoder::InitFromData
    40 // Two output options are supported: format=<png|bmp>;bpp=<bpp_value>
    41 // format specifies whether to use png or bitmap format
    42 // bpp specifies the bits per pixel to use where bpp_value can be 24 or 32
    43 NS_IMETHODIMP nsICOEncoder::InitFromData(const uint8_t* aData,
    44                                          uint32_t aLength,
    45                                          uint32_t aWidth,
    46                                          uint32_t aHeight,
    47                                          uint32_t aStride,
    48                                          uint32_t aInputFormat,
    49                                          const nsAString& aOutputOptions)
    50 {
    51   // validate input format
    52   if (aInputFormat != INPUT_FORMAT_RGB &&
    53       aInputFormat != INPUT_FORMAT_RGBA &&
    54       aInputFormat != INPUT_FORMAT_HOSTARGB) {
    55     return NS_ERROR_INVALID_ARG;
    56   }
    58   // Stride is the padded width of each row, so it better be longer
    59   if ((aInputFormat == INPUT_FORMAT_RGB &&
    60        aStride < aWidth * 3) ||
    61       ((aInputFormat == INPUT_FORMAT_RGBA || aInputFormat == INPUT_FORMAT_HOSTARGB) &&
    62        aStride < aWidth * 4)) {
    63     NS_WARNING("Invalid stride for InitFromData");
    64     return NS_ERROR_INVALID_ARG;
    65   }
    67   nsresult rv;
    68   rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions);
    69   NS_ENSURE_SUCCESS(rv, rv);
    71   rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride,
    72                      aInputFormat, aOutputOptions);
    73   NS_ENSURE_SUCCESS(rv, rv);
    75   rv = EndImageEncode();
    76   return rv;
    77 }
    79 // Returns the number of bytes in the image buffer used
    80 // For an ICO file, this is all bytes in the buffer.
    81 NS_IMETHODIMP 
    82 nsICOEncoder::GetImageBufferUsed(uint32_t *aOutputSize)
    83 {
    84   NS_ENSURE_ARG_POINTER(aOutputSize);
    85   *aOutputSize = mImageBufferSize;
    86   return NS_OK;
    87 }
    89 // Returns a pointer to the start of the image buffer
    90 NS_IMETHODIMP 
    91 nsICOEncoder::GetImageBuffer(char **aOutputBuffer)
    92 {
    93   NS_ENSURE_ARG_POINTER(aOutputBuffer);
    94   *aOutputBuffer = reinterpret_cast<char*>(mImageBufferStart);
    95   return NS_OK;
    96 }
    98 NS_IMETHODIMP 
    99 nsICOEncoder::AddImageFrame(const uint8_t* aData,
   100                             uint32_t aLength,
   101                             uint32_t aWidth,
   102                             uint32_t aHeight,
   103                             uint32_t aStride,
   104                             uint32_t aInputFormat, 
   105                             const nsAString& aFrameOptions)
   106 {
   107   if (mUsePNG) {
   109     mContainedEncoder = new nsPNGEncoder();
   110     nsresult rv;
   111     nsAutoString noParams;
   112     rv = mContainedEncoder->InitFromData(aData, aLength, aWidth, aHeight,
   113                                          aStride, aInputFormat, noParams);
   114     NS_ENSURE_SUCCESS(rv, rv);
   116     uint32_t PNGImageBufferSize;
   117     mContainedEncoder->GetImageBufferUsed(&PNGImageBufferSize);
   118     mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE + 
   119                        PNGImageBufferSize;
   120     mImageBufferStart = static_cast<uint8_t*>(moz_malloc(mImageBufferSize));
   121     if (!mImageBufferStart) {
   122       return NS_ERROR_OUT_OF_MEMORY;
   123     }
   124     mImageBufferCurr = mImageBufferStart;
   125     mICODirEntry.mBytesInRes = PNGImageBufferSize;
   127     EncodeFileHeader();
   128     EncodeInfoHeader();
   130     char *imageBuffer;
   131     rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
   132     NS_ENSURE_SUCCESS(rv, rv);
   133     memcpy(mImageBufferCurr, imageBuffer, PNGImageBufferSize);
   134     mImageBufferCurr += PNGImageBufferSize;
   135   } else {
   136     mContainedEncoder = new nsBMPEncoder();
   137     nsresult rv;
   139     nsAutoString params;
   140     params.AppendLiteral("bpp=");
   141     params.AppendInt(mICODirEntry.mBitCount);
   143     rv = mContainedEncoder->InitFromData(aData, aLength, aWidth, aHeight,
   144                                          aStride, aInputFormat, params);
   145     NS_ENSURE_SUCCESS(rv, rv);
   147     uint32_t andMaskSize = ((GetRealWidth() + 31) / 32) * 4 * // row AND mask
   148                            GetRealHeight(); // num rows
   150     uint32_t BMPImageBufferSize;
   151     mContainedEncoder->GetImageBufferUsed(&BMPImageBufferSize);
   152     mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE + 
   153                        BMPImageBufferSize + andMaskSize;
   154     mImageBufferStart = static_cast<uint8_t*>(moz_malloc(mImageBufferSize));
   155     if (!mImageBufferStart) {
   156       return NS_ERROR_OUT_OF_MEMORY;
   157     }
   158     mImageBufferCurr = mImageBufferStart;
   160     // The icon buffer does not include the BFH at all.
   161     mICODirEntry.mBytesInRes = BMPImageBufferSize - BFH_LENGTH + andMaskSize;
   163     // Encode the icon headers
   164     EncodeFileHeader();
   165     EncodeInfoHeader();
   167     char *imageBuffer;
   168     rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
   169     NS_ENSURE_SUCCESS(rv, rv);
   170     memcpy(mImageBufferCurr, imageBuffer + BFH_LENGTH, 
   171            BMPImageBufferSize - BFH_LENGTH);
   172     // We need to fix the BMP height to be *2 for the AND mask
   173     uint32_t fixedHeight = GetRealHeight() * 2;
   174     NativeEndian::swapToLittleEndianInPlace(&fixedHeight, 1);
   175     // The height is stored at an offset of 8 from the DIB header
   176     memcpy(mImageBufferCurr + 8, &fixedHeight, sizeof(fixedHeight));
   177     mImageBufferCurr += BMPImageBufferSize - BFH_LENGTH;
   179     // Calculate rowsize in DWORD's
   180     uint32_t rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
   181     int32_t currentLine = GetRealHeight();
   183     // Write out the AND mask
   184     while (currentLine > 0) {
   185       currentLine--;
   186       uint8_t* encoded = mImageBufferCurr + currentLine * rowSize;
   187       uint8_t* encodedEnd = encoded + rowSize;
   188       while (encoded != encodedEnd) {
   189         *encoded = 0; // make everything visible
   190         encoded++;
   191       }
   192     }
   194     mImageBufferCurr += andMaskSize;
   195   }
   197   return NS_OK;
   198 }
   200 // See ::InitFromData for other info.
   201 NS_IMETHODIMP nsICOEncoder::StartImageEncode(uint32_t aWidth,
   202                                              uint32_t aHeight,
   203                                              uint32_t aInputFormat,
   204                                              const nsAString& aOutputOptions)
   205 {
   206   // can't initialize more than once
   207   if (mImageBufferStart || mImageBufferCurr) {
   208     return NS_ERROR_ALREADY_INITIALIZED;
   209   }
   211   // validate input format
   212   if (aInputFormat != INPUT_FORMAT_RGB &&
   213       aInputFormat != INPUT_FORMAT_RGBA &&
   214       aInputFormat != INPUT_FORMAT_HOSTARGB) {
   215     return NS_ERROR_INVALID_ARG;
   216   }
   218   // Icons are only 1 byte, so make sure our bitmap is in range
   219   if (aWidth > 256 || aHeight > 256) {
   220     return NS_ERROR_INVALID_ARG;
   221   }
   223   // parse and check any provided output options
   224   uint32_t bpp = 24;
   225   bool usePNG = true;
   226   nsresult rv = ParseOptions(aOutputOptions, &bpp, &usePNG);
   227   NS_ENSURE_SUCCESS(rv, rv);
   229   mUsePNG = usePNG;
   231   InitFileHeader();
   232   // The width and height are stored as 0 when we have a value of 256
   233   InitInfoHeader(bpp, aWidth == 256 ? 0 : (uint8_t)aWidth, 
   234                  aHeight == 256 ? 0 : (uint8_t)aHeight);
   236   return NS_OK;
   237 }
   239 NS_IMETHODIMP nsICOEncoder::EndImageEncode()
   240 {
   241   // must be initialized
   242   if (!mImageBufferStart || !mImageBufferCurr) {
   243     return NS_ERROR_NOT_INITIALIZED;
   244   }
   246   mFinished = true;
   247   NotifyListener();
   249   // if output callback can't get enough memory, it will free our buffer
   250   if (!mImageBufferStart || !mImageBufferCurr) {
   251     return NS_ERROR_OUT_OF_MEMORY;
   252   }
   254   return NS_OK;
   255 }
   257 // Parses the encoder options and sets the bits per pixel to use and PNG or BMP
   258 // See InitFromData for a description of the parse options
   259 nsresult
   260 nsICOEncoder::ParseOptions(const nsAString& aOptions, uint32_t* bpp, 
   261                            bool *usePNG)
   262 {
   263   // If no parsing options just use the default of 24BPP and PNG yes
   264   if (aOptions.Length() == 0) {
   265     if (usePNG) {
   266       *usePNG = true;
   267     }
   268     if (bpp) {
   269       *bpp = 24;
   270     }
   271   }
   273   // Parse the input string into a set of name/value pairs.
   274   // From format: format=<png|bmp>;bpp=<bpp_value>
   275   // to format: [0] = format=<png|bmp>, [1] = bpp=<bpp_value>
   276   nsTArray<nsCString> nameValuePairs;
   277   if (!ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs)) {
   278     return NS_ERROR_INVALID_ARG;
   279   }
   281   // For each name/value pair in the set
   282   for (unsigned i = 0; i < nameValuePairs.Length(); ++i) {
   284     // Split the name value pair [0] = name, [1] = value
   285     nsTArray<nsCString> nameValuePair;
   286     if (!ParseString(nameValuePairs[i], '=', nameValuePair)) {
   287       return NS_ERROR_INVALID_ARG;
   288     }
   289     if (nameValuePair.Length() != 2) {
   290       return NS_ERROR_INVALID_ARG;
   291     }
   293     // Parse the format portion of the string format=<png|bmp>;bpp=<bpp_value>
   294     if (nameValuePair[0].Equals("format", nsCaseInsensitiveCStringComparator())) {
   295       if (nameValuePair[1].Equals("png", nsCaseInsensitiveCStringComparator())) {
   296         *usePNG = true;
   297       }
   298       else if (nameValuePair[1].Equals("bmp", nsCaseInsensitiveCStringComparator())) {
   299         *usePNG = false;
   300       }
   301       else {
   302         return NS_ERROR_INVALID_ARG;
   303       }
   304     }
   306     // Parse the bpp portion of the string format=<png|bmp>;bpp=<bpp_value>
   307     if (nameValuePair[0].Equals("bpp", nsCaseInsensitiveCStringComparator())) {
   308       if (nameValuePair[1].Equals("24")) {
   309         *bpp = 24;
   310       }
   311       else if (nameValuePair[1].Equals("32")) {
   312         *bpp = 32;
   313       }
   314       else {
   315         return NS_ERROR_INVALID_ARG;
   316       }
   317     }
   318   }
   320   return NS_OK;
   321 }
   323 NS_IMETHODIMP nsICOEncoder::Close()
   324 {
   325   if (mImageBufferStart) {
   326     moz_free(mImageBufferStart);
   327     mImageBufferStart = nullptr;
   328     mImageBufferSize = 0;
   329     mImageBufferReadPoint = 0;
   330     mImageBufferCurr = nullptr;
   331   }
   333   return NS_OK;
   334 }
   336 // Obtains the available bytes to read
   337 NS_IMETHODIMP nsICOEncoder::Available(uint64_t *_retval)
   338 {
   339   if (!mImageBufferStart || !mImageBufferCurr) {
   340     return NS_BASE_STREAM_CLOSED;
   341   }
   343   *_retval = GetCurrentImageBufferOffset() - mImageBufferReadPoint;
   344   return NS_OK;
   345 }
   347 // [noscript] Reads bytes which are available
   348 NS_IMETHODIMP nsICOEncoder::Read(char *aBuf, uint32_t aCount,
   349                                  uint32_t *_retval)
   350 {
   351   return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
   352 }
   354 // [noscript] Reads segments
   355 NS_IMETHODIMP nsICOEncoder::ReadSegments(nsWriteSegmentFun aWriter,
   356                                          void *aClosure, uint32_t aCount,
   357                                          uint32_t *_retval)
   358 {
   359   uint32_t maxCount = GetCurrentImageBufferOffset() - mImageBufferReadPoint;
   360   if (maxCount == 0) {
   361     *_retval = 0;
   362     return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
   363   }
   365   if (aCount > maxCount) {
   366     aCount = maxCount;
   367   }
   369   nsresult rv = aWriter(this, aClosure,
   370                         reinterpret_cast<const char*>(mImageBufferStart + 
   371                                                       mImageBufferReadPoint),
   372                         0, aCount, _retval);
   373   if (NS_SUCCEEDED(rv)) {
   374     NS_ASSERTION(*_retval <= aCount, "bad write count");
   375     mImageBufferReadPoint += *_retval;
   376   }
   377   // errors returned from the writer end here!
   378   return NS_OK;
   379 }
   381 NS_IMETHODIMP 
   382 nsICOEncoder::IsNonBlocking(bool *_retval)
   383 {
   384   *_retval = true;
   385   return NS_OK;
   386 }
   388 NS_IMETHODIMP 
   389 nsICOEncoder::AsyncWait(nsIInputStreamCallback *aCallback,
   390                         uint32_t aFlags,
   391                         uint32_t aRequestedCount,
   392                         nsIEventTarget *aTarget)
   393 {
   394   if (aFlags != 0) {
   395     return NS_ERROR_NOT_IMPLEMENTED;
   396   }
   398   if (mCallback || mCallbackTarget) {
   399     return NS_ERROR_UNEXPECTED;
   400   }
   402   mCallbackTarget = aTarget;
   403   // 0 means "any number of bytes except 0"
   404   mNotifyThreshold = aRequestedCount;
   405   if (!aRequestedCount) {
   406     mNotifyThreshold = 1024; // We don't want to notify incessantly
   407   }
   409   // We set the callback absolutely last, because NotifyListener uses it to
   410   // determine if someone needs to be notified.  If we don't set it last,
   411   // NotifyListener might try to fire off a notification to a null target
   412   // which will generally cause non-threadsafe objects to be used off the main thread
   413   mCallback = aCallback;
   415   // What we are being asked for may be present already
   416   NotifyListener();
   417   return NS_OK;
   418 }
   420 NS_IMETHODIMP nsICOEncoder::CloseWithStatus(nsresult aStatus)
   421 {
   422   return Close();
   423 }
   425 void
   426 nsICOEncoder::NotifyListener()
   427 {
   428   if (mCallback &&
   429       (GetCurrentImageBufferOffset() - mImageBufferReadPoint >= mNotifyThreshold ||
   430        mFinished)) {
   431     nsCOMPtr<nsIInputStreamCallback> callback;
   432     if (mCallbackTarget) {
   433       callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
   434     } else {
   435       callback = mCallback;
   436     }
   438     NS_ASSERTION(callback, "Shouldn't fail to make the callback");
   439     // Null the callback first because OnInputStreamReady could reenter
   440     // AsyncWait
   441     mCallback = nullptr;
   442     mCallbackTarget = nullptr;
   443     mNotifyThreshold = 0;
   445     callback->OnInputStreamReady(this);
   446   }
   447 }
   449 // Initializes the icon file header mICOFileHeader
   450 void 
   451 nsICOEncoder::InitFileHeader()
   452 {
   453   memset(&mICOFileHeader, 0, sizeof(mICOFileHeader));
   454   mICOFileHeader.mReserved = 0;
   455   mICOFileHeader.mType = 1;
   456   mICOFileHeader.mCount = 1;
   457 }
   459 // Initializes the icon directory info header mICODirEntry
   460 void 
   461 nsICOEncoder::InitInfoHeader(uint32_t aBPP, uint8_t aWidth, uint8_t aHeight)
   462 {
   463   memset(&mICODirEntry, 0, sizeof(mICODirEntry));
   464   mICODirEntry.mBitCount = aBPP;
   465   mICODirEntry.mBytesInRes = 0;
   466   mICODirEntry.mColorCount = 0;
   467   mICODirEntry.mWidth = aWidth;
   468   mICODirEntry.mHeight = aHeight;
   469   mICODirEntry.mImageOffset = ICONFILEHEADERSIZE + ICODIRENTRYSIZE;
   470   mICODirEntry.mPlanes = 1;
   471   mICODirEntry.mReserved = 0;
   472 }
   474 // Encodes the icon file header mICOFileHeader
   475 void 
   476 nsICOEncoder::EncodeFileHeader() 
   477 {  
   478   IconFileHeader littleEndianIFH = mICOFileHeader;
   479   NativeEndian::swapToLittleEndianInPlace(&littleEndianIFH.mReserved, 1);
   480   NativeEndian::swapToLittleEndianInPlace(&littleEndianIFH.mType, 1);
   481   NativeEndian::swapToLittleEndianInPlace(&littleEndianIFH.mCount, 1);
   483   memcpy(mImageBufferCurr, &littleEndianIFH.mReserved, 
   484          sizeof(littleEndianIFH.mReserved));
   485   mImageBufferCurr += sizeof(littleEndianIFH.mReserved);
   486   memcpy(mImageBufferCurr, &littleEndianIFH.mType, 
   487          sizeof(littleEndianIFH.mType));
   488   mImageBufferCurr += sizeof(littleEndianIFH.mType);
   489   memcpy(mImageBufferCurr, &littleEndianIFH.mCount, 
   490          sizeof(littleEndianIFH.mCount));
   491   mImageBufferCurr += sizeof(littleEndianIFH.mCount);
   492 }
   494 // Encodes the icon directory info header mICODirEntry
   495 void 
   496 nsICOEncoder::EncodeInfoHeader()
   497 {
   498   IconDirEntry littleEndianmIDE = mICODirEntry;
   500   NativeEndian::swapToLittleEndianInPlace(&littleEndianmIDE.mPlanes, 1);
   501   NativeEndian::swapToLittleEndianInPlace(&littleEndianmIDE.mBitCount, 1);
   502   NativeEndian::swapToLittleEndianInPlace(&littleEndianmIDE.mBytesInRes, 1);
   503   NativeEndian::swapToLittleEndianInPlace(&littleEndianmIDE.mImageOffset, 1);
   505   memcpy(mImageBufferCurr, &littleEndianmIDE.mWidth, 
   506          sizeof(littleEndianmIDE.mWidth));
   507   mImageBufferCurr += sizeof(littleEndianmIDE.mWidth);
   508   memcpy(mImageBufferCurr, &littleEndianmIDE.mHeight, 
   509          sizeof(littleEndianmIDE.mHeight));
   510   mImageBufferCurr += sizeof(littleEndianmIDE.mHeight);
   511   memcpy(mImageBufferCurr, &littleEndianmIDE.mColorCount, 
   512          sizeof(littleEndianmIDE.mColorCount));
   513   mImageBufferCurr += sizeof(littleEndianmIDE.mColorCount);
   514   memcpy(mImageBufferCurr, &littleEndianmIDE.mReserved, 
   515          sizeof(littleEndianmIDE.mReserved));
   516   mImageBufferCurr += sizeof(littleEndianmIDE.mReserved);
   517   memcpy(mImageBufferCurr, &littleEndianmIDE.mPlanes, 
   518          sizeof(littleEndianmIDE.mPlanes));
   519   mImageBufferCurr += sizeof(littleEndianmIDE.mPlanes);
   520   memcpy(mImageBufferCurr, &littleEndianmIDE.mBitCount, 
   521          sizeof(littleEndianmIDE.mBitCount));
   522   mImageBufferCurr += sizeof(littleEndianmIDE.mBitCount);
   523   memcpy(mImageBufferCurr, &littleEndianmIDE.mBytesInRes, 
   524          sizeof(littleEndianmIDE.mBytesInRes));
   525   mImageBufferCurr += sizeof(littleEndianmIDE.mBytesInRes);
   526   memcpy(mImageBufferCurr, &littleEndianmIDE.mImageOffset, 
   527          sizeof(littleEndianmIDE.mImageOffset));
   528   mImageBufferCurr += sizeof(littleEndianmIDE.mImageOffset);
   529 }

mercurial