image/encoders/png/nsPNGEncoder.cpp

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

     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/. */
     6 #include "nsCRT.h"
     7 #include "nsPNGEncoder.h"
     8 #include "prprf.h"
     9 #include "nsString.h"
    10 #include "nsStreamUtils.h"
    12 using namespace mozilla;
    14 NS_IMPL_ISUPPORTS(nsPNGEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
    16 nsPNGEncoder::nsPNGEncoder() : mPNG(nullptr), mPNGinfo(nullptr),
    17                                mIsAnimation(false),
    18                                mFinished(false),
    19                                mImageBuffer(nullptr), mImageBufferSize(0),
    20                                mImageBufferUsed(0), mImageBufferReadPoint(0),
    21                                mCallback(nullptr),
    22                                mCallbackTarget(nullptr), mNotifyThreshold(0),
    23                                mReentrantMonitor("nsPNGEncoder.mReentrantMonitor")
    24 {
    25 }
    27 nsPNGEncoder::~nsPNGEncoder()
    28 {
    29   if (mImageBuffer) {
    30     moz_free(mImageBuffer);
    31     mImageBuffer = nullptr;
    32   }
    33   // don't leak if EndImageEncode wasn't called
    34   if (mPNG)
    35     png_destroy_write_struct(&mPNG, &mPNGinfo);
    36 }
    38 // nsPNGEncoder::InitFromData
    39 //
    40 //    One output option is supported: "transparency=none" means that the
    41 //    output PNG will not have an alpha channel, even if the input does.
    42 //
    43 //    Based partially on gfx/cairo/cairo/src/cairo-png.c
    44 //    See also modules/libimg/png/libpng.txt
    46 NS_IMETHODIMP nsPNGEncoder::InitFromData(const uint8_t* aData,
    47                                          uint32_t aLength, // (unused,
    48                                                            // 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   NS_ENSURE_ARG(aData);
    56   nsresult rv;
    58   rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions);
    59   if (!NS_SUCCEEDED(rv))
    60     return rv;
    62   rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride,
    63                      aInputFormat, aOutputOptions);
    64   if (!NS_SUCCEEDED(rv))
    65     return rv;
    67   rv = EndImageEncode();
    69   return rv;
    70 }
    73 // nsPNGEncoder::StartImageEncode
    74 //
    75 // 
    76 // See ::InitFromData for other info.
    77 NS_IMETHODIMP nsPNGEncoder::StartImageEncode(uint32_t aWidth,
    78                                              uint32_t aHeight,
    79                                              uint32_t aInputFormat,
    80                                              const nsAString& aOutputOptions)
    81 {
    82   bool useTransparency = true, skipFirstFrame = false;
    83   uint32_t numFrames = 1;
    84   uint32_t numPlays = 0; // For animations, 0 == forever
    86   // can't initialize more than once
    87   if (mImageBuffer != nullptr)
    88     return NS_ERROR_ALREADY_INITIALIZED;
    90   // validate input format
    91   if (aInputFormat != INPUT_FORMAT_RGB &&
    92       aInputFormat != INPUT_FORMAT_RGBA &&
    93       aInputFormat != INPUT_FORMAT_HOSTARGB)
    94     return NS_ERROR_INVALID_ARG;
    96   // parse and check any provided output options
    97   nsresult rv = ParseOptions(aOutputOptions, &useTransparency, &skipFirstFrame,
    98                              &numFrames, &numPlays, nullptr, nullptr,
    99                              nullptr, nullptr, nullptr);
   100   if (rv != NS_OK)
   101     return rv;
   103 #ifdef PNG_APNG_SUPPORTED
   104   if (numFrames > 1)
   105     mIsAnimation = true;
   107 #endif
   109   // initialize
   110   mPNG = png_create_write_struct(PNG_LIBPNG_VER_STRING,
   111                                  nullptr,
   112                                  ErrorCallback,
   113                                  WarningCallback);
   114   if (! mPNG)
   115     return NS_ERROR_OUT_OF_MEMORY;
   117   mPNGinfo = png_create_info_struct(mPNG);
   118   if (! mPNGinfo) {
   119     png_destroy_write_struct(&mPNG, nullptr);
   120     return NS_ERROR_FAILURE;
   121   }
   123   // libpng's error handler jumps back here upon an error.
   124   // Note: It's important that all png_* callers do this, or errors
   125   // will result in a corrupt time-warped stack.
   126   if (setjmp(png_jmpbuf(mPNG))) {
   127     png_destroy_write_struct(&mPNG, &mPNGinfo);
   128     return NS_ERROR_FAILURE;
   129   }
   131   // Set up to read the data into our image buffer, start out with an 8K
   132   // estimated size. Note: we don't have to worry about freeing this data
   133   // in this function. It will be freed on object destruction.
   134   mImageBufferSize = 8192;
   135   mImageBuffer = (uint8_t*)moz_malloc(mImageBufferSize);
   136   if (!mImageBuffer) {
   137     png_destroy_write_struct(&mPNG, &mPNGinfo);
   138     return NS_ERROR_OUT_OF_MEMORY;
   139   }
   140   mImageBufferUsed = 0;
   142   // set our callback for libpng to give us the data
   143   png_set_write_fn(mPNG, this, WriteCallback, nullptr);
   145   // include alpha?
   146   int colorType;
   147   if ((aInputFormat == INPUT_FORMAT_HOSTARGB ||
   148        aInputFormat == INPUT_FORMAT_RGBA)  &&
   149        useTransparency)
   150     colorType = PNG_COLOR_TYPE_RGB_ALPHA;
   151   else
   152     colorType = PNG_COLOR_TYPE_RGB;
   154   png_set_IHDR(mPNG, mPNGinfo, aWidth, aHeight, 8, colorType,
   155                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
   156                PNG_FILTER_TYPE_DEFAULT);
   158 #ifdef PNG_APNG_SUPPORTED
   159   if (mIsAnimation) {
   160     png_set_first_frame_is_hidden(mPNG, mPNGinfo, skipFirstFrame);
   161     png_set_acTL(mPNG, mPNGinfo, numFrames, numPlays);
   162   }
   163 #endif
   165   // XXX: support PLTE, gAMA, tRNS, bKGD?
   167   png_write_info(mPNG, mPNGinfo);
   169   return NS_OK;
   170 }
   172 // Returns the number of bytes in the image buffer used.
   173 NS_IMETHODIMP nsPNGEncoder::GetImageBufferUsed(uint32_t *aOutputSize)
   174 {
   175   NS_ENSURE_ARG_POINTER(aOutputSize);
   176   *aOutputSize = mImageBufferUsed;
   177   return NS_OK;
   178 }
   180 // Returns a pointer to the start of the image buffer
   181 NS_IMETHODIMP nsPNGEncoder::GetImageBuffer(char **aOutputBuffer)
   182 {
   183   NS_ENSURE_ARG_POINTER(aOutputBuffer);
   184   *aOutputBuffer = reinterpret_cast<char*>(mImageBuffer);
   185   return NS_OK;
   186 }
   188 NS_IMETHODIMP nsPNGEncoder::AddImageFrame(const uint8_t* aData,
   189                                           uint32_t aLength, // (unused,
   190                                                             // req'd by JS)
   191                                           uint32_t aWidth,
   192                                           uint32_t aHeight,
   193                                           uint32_t aStride,
   194                                           uint32_t aInputFormat,
   195                                           const nsAString& aFrameOptions)
   196 {
   197   bool useTransparency= true;
   198   uint32_t delay_ms = 500;
   199 #ifdef PNG_APNG_SUPPORTED
   200   uint32_t dispose_op = PNG_DISPOSE_OP_NONE;
   201   uint32_t blend_op = PNG_BLEND_OP_SOURCE;
   202 #else
   203   uint32_t dispose_op;
   204   uint32_t blend_op;
   205 #endif
   206   uint32_t x_offset = 0, y_offset = 0;
   208   // must be initialized
   209   if (mImageBuffer == nullptr)
   210     return NS_ERROR_NOT_INITIALIZED;
   212   // EndImageEncode was done, or some error occurred earlier
   213   if (!mPNG)
   214     return NS_BASE_STREAM_CLOSED;
   216   // validate input format
   217   if (aInputFormat != INPUT_FORMAT_RGB &&
   218       aInputFormat != INPUT_FORMAT_RGBA &&
   219       aInputFormat != INPUT_FORMAT_HOSTARGB)
   220     return NS_ERROR_INVALID_ARG;
   222   // libpng's error handler jumps back here upon an error.
   223   if (setjmp(png_jmpbuf(mPNG))) {
   224     png_destroy_write_struct(&mPNG, &mPNGinfo);
   225     return NS_ERROR_FAILURE;
   226   }
   228   // parse and check any provided output options
   229   nsresult rv = ParseOptions(aFrameOptions, &useTransparency, nullptr,
   230                              nullptr, nullptr, &dispose_op, &blend_op,
   231                              &delay_ms, &x_offset, &y_offset);
   232   if (rv != NS_OK)
   233     return rv;
   235 #ifdef PNG_APNG_SUPPORTED
   236   if (mIsAnimation) {
   237     // XXX the row pointers arg (#3) is unused, can it be removed?
   238     png_write_frame_head(mPNG, mPNGinfo, nullptr,
   239                          aWidth, aHeight, x_offset, y_offset,
   240                          delay_ms, 1000, dispose_op, blend_op);
   241   }
   242 #endif
   244   // Stride is the padded width of each row, so it better be longer 
   245   // (I'm afraid people will not understand what stride means, so
   246   // check it well)
   247   if ((aInputFormat == INPUT_FORMAT_RGB &&
   248       aStride < aWidth * 3) ||
   249       ((aInputFormat == INPUT_FORMAT_RGBA ||
   250       aInputFormat == INPUT_FORMAT_HOSTARGB) &&
   251       aStride < aWidth * 4)) {
   252     NS_WARNING("Invalid stride for InitFromData/AddImageFrame");
   253     return NS_ERROR_INVALID_ARG;
   254   }
   256 #ifdef PNG_WRITE_FILTER_SUPPORTED
   257   png_set_filter(mPNG, PNG_FILTER_TYPE_BASE, PNG_FILTER_VALUE_NONE);
   258 #endif
   260   // write each row: if we add more input formats, we may want to
   261   // generalize the conversions
   262   if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
   263     // PNG requires RGBA with post-multiplied alpha, so we need to
   264     // convert
   265     uint8_t* row = new uint8_t[aWidth * 4];
   266     for (uint32_t y = 0; y < aHeight; y ++) {
   267       ConvertHostARGBRow(&aData[y * aStride], row, aWidth, useTransparency);
   268       png_write_row(mPNG, row);
   269     }
   270     delete[] row;
   272   } else if (aInputFormat == INPUT_FORMAT_RGBA && ! useTransparency) {
   273     // RBGA, but we need to strip the alpha
   274     uint8_t* row = new uint8_t[aWidth * 4];
   275     for (uint32_t y = 0; y < aHeight; y ++) {
   276       StripAlpha(&aData[y * aStride], row, aWidth);
   277       png_write_row(mPNG, row);
   278     }
   279     delete[] row;
   281   } else if (aInputFormat == INPUT_FORMAT_RGB ||
   282              aInputFormat == INPUT_FORMAT_RGBA) {
   283     // simple RBG(A), no conversion needed
   284     for (uint32_t y = 0; y < aHeight; y ++) {
   285       png_write_row(mPNG, (uint8_t*)&aData[y * aStride]);
   286     }
   288   } else {
   289     NS_NOTREACHED("Bad format type");
   290     return NS_ERROR_INVALID_ARG;
   291   }
   293 #ifdef PNG_APNG_SUPPORTED
   294   if (mIsAnimation) {
   295     png_write_frame_tail(mPNG, mPNGinfo);
   296   }
   297 #endif
   299   return NS_OK;
   300 }
   303 NS_IMETHODIMP nsPNGEncoder::EndImageEncode()
   304 {
   305   // must be initialized
   306   if (mImageBuffer == nullptr)
   307     return NS_ERROR_NOT_INITIALIZED;
   309   // EndImageEncode has already been called, or some error
   310   // occurred earlier
   311   if (!mPNG)
   312     return NS_BASE_STREAM_CLOSED;
   314   // libpng's error handler jumps back here upon an error.
   315   if (setjmp(png_jmpbuf(mPNG))) {
   316     png_destroy_write_struct(&mPNG, &mPNGinfo);
   317     return NS_ERROR_FAILURE;
   318   }
   320   png_write_end(mPNG, mPNGinfo);
   321   png_destroy_write_struct(&mPNG, &mPNGinfo);
   323   mFinished = true;
   324   NotifyListener();
   326   // if output callback can't get enough memory, it will free our buffer
   327   if (!mImageBuffer)
   328     return NS_ERROR_OUT_OF_MEMORY;
   330   return NS_OK;
   331 }
   334 nsresult
   335 nsPNGEncoder::ParseOptions(const nsAString& aOptions,
   336                            bool* useTransparency,
   337                            bool* skipFirstFrame,
   338                            uint32_t* numFrames,
   339                            uint32_t* numPlays,
   340                            uint32_t* frameDispose,
   341                            uint32_t* frameBlend,
   342                            uint32_t* frameDelay,
   343                            uint32_t* offsetX,
   344                            uint32_t* offsetY)
   345 {
   346 #ifdef PNG_APNG_SUPPORTED
   347   // Make a copy of aOptions, because strtok() will modify it.
   348   nsAutoCString optionsCopy;
   349   optionsCopy.Assign(NS_ConvertUTF16toUTF8(aOptions));
   350   char* options = optionsCopy.BeginWriting();
   352   while (char* token = nsCRT::strtok(options, ";", &options)) {
   353     // If there's an '=' character, split the token around it.
   354     char* equals = token, *value = nullptr;
   355     while(*equals != '=' && *equals) {
   356       ++equals;
   357     }
   358     if (*equals == '=')
   359       value = equals + 1;
   361     if (value)
   362       *equals = '\0'; // temporary null
   364     // transparency=[yes|no|none]
   365     if (nsCRT::strcmp(token, "transparency") == 0 && useTransparency) {
   366       if (!value)
   367         return NS_ERROR_INVALID_ARG;
   369       if (nsCRT::strcmp(value, "none") == 0 ||
   370           nsCRT::strcmp(value, "no") == 0) {
   371         *useTransparency = false;
   372       } else if (nsCRT::strcmp(value, "yes") == 0) {
   373         *useTransparency = true;
   374       } else {
   375         return NS_ERROR_INVALID_ARG;
   376       }
   378     // skipfirstframe=[yes|no]
   379     } else if (nsCRT::strcmp(token, "skipfirstframe") == 0 &&
   380                skipFirstFrame) {
   381       if (!value)
   382         return NS_ERROR_INVALID_ARG;
   384       if (nsCRT::strcmp(value, "no") == 0) {
   385         *skipFirstFrame = false;
   386       } else if (nsCRT::strcmp(value, "yes") == 0) {
   387         *skipFirstFrame = true;
   388       } else {
   389         return NS_ERROR_INVALID_ARG;
   390       }
   392     // frames=#
   393     } else if (nsCRT::strcmp(token, "frames") == 0 && numFrames) {
   394       if (!value)
   395         return NS_ERROR_INVALID_ARG;
   397       if (PR_sscanf(value, "%u", numFrames) != 1) {
   398         return NS_ERROR_INVALID_ARG;
   399       }
   401       // frames=0 is nonsense.
   402       if (*numFrames == 0)
   403         return NS_ERROR_INVALID_ARG;
   405     // plays=#
   406     } else if (nsCRT::strcmp(token, "plays") == 0 && numPlays) {
   407       if (!value)
   408         return NS_ERROR_INVALID_ARG;
   410       // plays=0 to loop forever, otherwise play sequence specified
   411       // number of times
   412       if (PR_sscanf(value, "%u", numPlays) != 1)
   413         return NS_ERROR_INVALID_ARG;
   415     // dispose=[none|background|previous]
   416     } else if (nsCRT::strcmp(token, "dispose") == 0 && frameDispose) {
   417       if (!value)
   418         return NS_ERROR_INVALID_ARG;
   420       if (nsCRT::strcmp(value, "none") == 0) {
   421         *frameDispose = PNG_DISPOSE_OP_NONE;
   422       } else if (nsCRT::strcmp(value, "background") == 0) {
   423         *frameDispose = PNG_DISPOSE_OP_BACKGROUND;
   424       } else if (nsCRT::strcmp(value, "previous") == 0) {
   425         *frameDispose = PNG_DISPOSE_OP_PREVIOUS;
   426       } else {
   427         return NS_ERROR_INVALID_ARG;
   428       }
   430     // blend=[source|over]
   431     } else if (nsCRT::strcmp(token, "blend") == 0 && frameBlend) {
   432       if (!value)
   433         return NS_ERROR_INVALID_ARG;
   435       if (nsCRT::strcmp(value, "source") == 0) {
   436         *frameBlend = PNG_BLEND_OP_SOURCE;
   437       } else if (nsCRT::strcmp(value, "over") == 0) {
   438         *frameBlend = PNG_BLEND_OP_OVER;
   439       } else {
   440         return NS_ERROR_INVALID_ARG;
   441       }
   443     // delay=# (in ms)
   444     } else if (nsCRT::strcmp(token, "delay") == 0 && frameDelay) {
   445       if (!value)
   446         return NS_ERROR_INVALID_ARG;
   448       if (PR_sscanf(value, "%u", frameDelay) != 1)
   449         return NS_ERROR_INVALID_ARG;
   451     // xoffset=#
   452     } else if (nsCRT::strcmp(token, "xoffset") == 0 && offsetX) {
   453       if (!value)
   454         return NS_ERROR_INVALID_ARG;
   456       if (PR_sscanf(value, "%u", offsetX) != 1)
   457         return NS_ERROR_INVALID_ARG;
   459     // yoffset=#
   460     } else if (nsCRT::strcmp(token, "yoffset") == 0 && offsetY) {
   461       if (!value)
   462         return NS_ERROR_INVALID_ARG;
   464       if (PR_sscanf(value, "%u", offsetY) != 1)
   465         return NS_ERROR_INVALID_ARG;
   467     // unknown token name
   468     } else
   469       return NS_ERROR_INVALID_ARG;
   471     if (value)
   472       *equals = '='; // restore '=' so strtok doesn't get lost
   473   }
   475 #endif
   476   return NS_OK;
   477 }
   480 /* void close (); */
   481 NS_IMETHODIMP nsPNGEncoder::Close()
   482 {
   483   if (mImageBuffer != nullptr) {
   484     moz_free(mImageBuffer);
   485     mImageBuffer = nullptr;
   486     mImageBufferSize = 0;
   487     mImageBufferUsed = 0;
   488     mImageBufferReadPoint = 0;
   489   }
   490   return NS_OK;
   491 }
   493 /* unsigned long available (); */
   494 NS_IMETHODIMP nsPNGEncoder::Available(uint64_t *_retval)
   495 {
   496   if (!mImageBuffer)
   497     return NS_BASE_STREAM_CLOSED;
   499   *_retval = mImageBufferUsed - mImageBufferReadPoint;
   500   return NS_OK;
   501 }
   503 /* [noscript] unsigned long read (in charPtr aBuf,
   504                                   in unsigned long aCount); */
   505 NS_IMETHODIMP nsPNGEncoder::Read(char * aBuf, uint32_t aCount,
   506                                  uint32_t *_retval)
   507 {
   508   return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
   509 }
   511 /* [noscript] unsigned long readSegments (in nsWriteSegmentFun aWriter,
   512                                           in voidPtr aClosure,
   513                                           in unsigned long aCount); */
   514 NS_IMETHODIMP nsPNGEncoder::ReadSegments(nsWriteSegmentFun aWriter,
   515                                          void *aClosure, uint32_t aCount,
   516                                          uint32_t *_retval)
   517 {
   518   // Avoid another thread reallocing the buffer underneath us
   519   ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);
   521   uint32_t maxCount = mImageBufferUsed - mImageBufferReadPoint;
   522   if (maxCount == 0) {
   523     *_retval = 0;
   524     return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
   525   }
   527   if (aCount > maxCount)
   528     aCount = maxCount;
   529   nsresult rv =
   530       aWriter(this, aClosure,
   531               reinterpret_cast<const char*>(mImageBuffer+mImageBufferReadPoint),
   532               0, aCount, _retval);
   533   if (NS_SUCCEEDED(rv)) {
   534     NS_ASSERTION(*_retval <= aCount, "bad write count");
   535     mImageBufferReadPoint += *_retval;
   536   }
   538   // errors returned from the writer end here!
   539   return NS_OK;
   540 }
   542 /* boolean isNonBlocking (); */
   543 NS_IMETHODIMP nsPNGEncoder::IsNonBlocking(bool *_retval)
   544 {
   545   *_retval = true;
   546   return NS_OK;
   547 }
   549 NS_IMETHODIMP nsPNGEncoder::AsyncWait(nsIInputStreamCallback *aCallback,
   550                                       uint32_t aFlags,
   551                                       uint32_t aRequestedCount,
   552                                       nsIEventTarget *aTarget)
   553 {
   554   if (aFlags != 0)
   555     return NS_ERROR_NOT_IMPLEMENTED;
   557   if (mCallback || mCallbackTarget)
   558     return NS_ERROR_UNEXPECTED;
   560   mCallbackTarget = aTarget;
   561   // 0 means "any number of bytes except 0"
   562   mNotifyThreshold = aRequestedCount;
   563   if (!aRequestedCount)
   564     mNotifyThreshold = 1024; // We don't want to notify incessantly
   566   // We set the callback absolutely last, because NotifyListener uses it to
   567   // determine if someone needs to be notified.  If we don't set it last,
   568   // NotifyListener might try to fire off a notification to a null target
   569   // which will generally cause non-threadsafe objects to be used off the main thread
   570   mCallback = aCallback;
   572   // What we are being asked for may be present already
   573   NotifyListener();
   574   return NS_OK;
   575 }
   577 NS_IMETHODIMP nsPNGEncoder::CloseWithStatus(nsresult aStatus)
   578 {
   579   return Close();
   580 }
   582 // nsPNGEncoder::ConvertHostARGBRow
   583 //
   584 //    Our colors are stored with premultiplied alphas, but PNGs use
   585 //    post-multiplied alpha. This swaps to PNG-style alpha.
   586 //
   587 //    Copied from gfx/cairo/cairo/src/cairo-png.c
   589 void
   590 nsPNGEncoder::ConvertHostARGBRow(const uint8_t* aSrc, uint8_t* aDest,
   591                                  uint32_t aPixelWidth,
   592                                  bool aUseTransparency)
   593 {
   594   uint32_t pixelStride = aUseTransparency ? 4 : 3;
   595   for (uint32_t x = 0; x < aPixelWidth; x ++) {
   596     const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x];
   597     uint8_t *pixelOut = &aDest[x * pixelStride];
   599     uint8_t alpha = (pixelIn & 0xff000000) >> 24;
   600     if (alpha == 0) {
   601       pixelOut[0] = pixelOut[1] = pixelOut[2] = pixelOut[3] = 0;
   602     } else {
   603       pixelOut[0] = (((pixelIn & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
   604       pixelOut[1] = (((pixelIn & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
   605       pixelOut[2] = (((pixelIn & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
   606       if (aUseTransparency)
   607         pixelOut[3] = alpha;
   608     }
   609   }
   610 }
   613 // nsPNGEncoder::StripAlpha
   614 //
   615 //    Input is RGBA, output is RGB
   617 void
   618 nsPNGEncoder::StripAlpha(const uint8_t* aSrc, uint8_t* aDest,
   619                           uint32_t aPixelWidth)
   620 {
   621   for (uint32_t x = 0; x < aPixelWidth; x ++) {
   622     const uint8_t* pixelIn = &aSrc[x * 4];
   623     uint8_t* pixelOut = &aDest[x * 3];
   624     pixelOut[0] = pixelIn[0];
   625     pixelOut[1] = pixelIn[1];
   626     pixelOut[2] = pixelIn[2];
   627   }
   628 }
   631 // nsPNGEncoder::WarningCallback
   633 void // static
   634 nsPNGEncoder::WarningCallback(png_structp png_ptr,
   635                             png_const_charp warning_msg)
   636 {
   637 #ifdef DEBUG
   638 	// XXX: these messages are probably useful callers...
   639         // use nsIConsoleService?
   640 	PR_fprintf(PR_STDERR, "PNG Encoder: %s\n", warning_msg);;
   641 #endif
   642 }
   645 // nsPNGEncoder::ErrorCallback
   647 void // static
   648 nsPNGEncoder::ErrorCallback(png_structp png_ptr,
   649                             png_const_charp error_msg)
   650 {
   651 #ifdef DEBUG
   652 	// XXX: these messages are probably useful callers...
   653         // use nsIConsoleService?
   654 	PR_fprintf(PR_STDERR, "PNG Encoder: %s\n", error_msg);;
   655 #endif
   656 #if PNG_LIBPNG_VER < 10500
   657         longjmp(png_ptr->jmpbuf, 1);
   658 #else
   659         png_longjmp(png_ptr, 1);
   660 #endif
   661 }
   664 // nsPNGEncoder::WriteCallback
   666 void // static
   667 nsPNGEncoder::WriteCallback(png_structp png, png_bytep data,
   668                             png_size_t size)
   669 {
   670   nsPNGEncoder* that = static_cast<nsPNGEncoder*>(png_get_io_ptr(png));
   671   if (! that->mImageBuffer)
   672     return;
   674   if (that->mImageBufferUsed + size > that->mImageBufferSize) {
   675     // When we're reallocing the buffer we need to take the lock to ensure
   676     // that nobody is trying to read from the buffer we are destroying
   677     ReentrantMonitorAutoEnter autoEnter(that->mReentrantMonitor);
   679     // expand buffer, just double each time
   680     that->mImageBufferSize *= 2;
   681     uint8_t* newBuf = (uint8_t*)moz_realloc(that->mImageBuffer,
   682                                             that->mImageBufferSize);
   683     if (! newBuf) {
   684       // can't resize, just zero (this will keep us from writing more)
   685       moz_free(that->mImageBuffer);
   686       that->mImageBuffer = nullptr;
   687       that->mImageBufferSize = 0;
   688       that->mImageBufferUsed = 0;
   689       return;
   690     }
   691     that->mImageBuffer = newBuf;
   692   }
   693   memcpy(&that->mImageBuffer[that->mImageBufferUsed], data, size);
   694   that->mImageBufferUsed += size;
   695   that->NotifyListener();
   696 }
   698 void
   699 nsPNGEncoder::NotifyListener()
   700 {
   701   // We might call this function on multiple threads (any threads that call
   702   // AsyncWait and any that do encoding) so we lock to avoid notifying the
   703   // listener twice about the same data (which generally leads to a truncated
   704   // image).
   705   ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);
   707   if (mCallback &&
   708       (mImageBufferUsed - mImageBufferReadPoint >= mNotifyThreshold ||
   709        mFinished)) {
   710     nsCOMPtr<nsIInputStreamCallback> callback;
   711     if (mCallbackTarget) {
   712       callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
   713     } else {
   714       callback = mCallback;
   715     }
   717     NS_ASSERTION(callback, "Shouldn't fail to make the callback");
   718     // Null the callback first because OnInputStreamReady could reenter
   719     // AsyncWait
   720     mCallback = nullptr;
   721     mCallbackTarget = nullptr;
   722     mNotifyThreshold = 0;
   724     callback->OnInputStreamReady(this);
   725   }
   726 }

mercurial