LCOV - code coverage report
Current view: directory - image/encoders/png - nsPNGEncoder.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 310 213 68.7 %
Date: 2012-06-02 Functions: 25 18 72.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  * ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is PNG Encoding code
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Google Inc.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2005
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Brett Wilson <brettw@gmail.com>
      24                 :  *   Stuart Parmenter <pavlov@pavlov.net>
      25                 :  *   Justin Dolske <dolske@mozilla.com>
      26                 :  *   Glenn Randers-Pehrson <glennrp@gmail.com>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      30                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : #include "nsCRT.h"
      43                 : #include "nsPNGEncoder.h"
      44                 : #include "prmem.h"
      45                 : #include "prprf.h"
      46                 : #include "nsString.h"
      47                 : #include "nsStreamUtils.h"
      48                 : 
      49                 : using namespace mozilla;
      50                 : 
      51            1058 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsPNGEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
      52                 : 
      53              26 : nsPNGEncoder::nsPNGEncoder() : mPNG(nsnull), mPNGinfo(nsnull),
      54                 :                                mIsAnimation(false),
      55                 :                                mFinished(false),
      56                 :                                mImageBuffer(nsnull), mImageBufferSize(0),
      57                 :                                mImageBufferUsed(0), mImageBufferReadPoint(0),
      58                 :                                mCallback(nsnull),
      59                 :                                mCallbackTarget(nsnull), mNotifyThreshold(0),
      60              26 :                                mReentrantMonitor("nsPNGEncoder.mReentrantMonitor")
      61                 : {
      62              26 : }
      63                 : 
      64              52 : nsPNGEncoder::~nsPNGEncoder()
      65                 : {
      66              26 :   if (mImageBuffer) {
      67              26 :     PR_Free(mImageBuffer);
      68              26 :     mImageBuffer = nsnull;
      69                 :   }
      70                 :   // don't leak if EndImageEncode wasn't called
      71              26 :   if (mPNG)
      72               0 :     png_destroy_write_struct(&mPNG, &mPNGinfo);
      73              26 : }
      74                 : 
      75                 : // nsPNGEncoder::InitFromData
      76                 : //
      77                 : //    One output option is supported: "transparency=none" means that the
      78                 : //    output PNG will not have an alpha channel, even if the input does.
      79                 : //
      80                 : //    Based partially on gfx/cairo/cairo/src/cairo-png.c
      81                 : //    See also modules/libimg/png/libpng.txt
      82                 : 
      83              16 : NS_IMETHODIMP nsPNGEncoder::InitFromData(const PRUint8* aData,
      84                 :                                          PRUint32 aLength, // (unused,
      85                 :                                                            // req'd by JS)
      86                 :                                          PRUint32 aWidth,
      87                 :                                          PRUint32 aHeight,
      88                 :                                          PRUint32 aStride,
      89                 :                                          PRUint32 aInputFormat,
      90                 :                                          const nsAString& aOutputOptions)
      91                 : {
      92              16 :   NS_ENSURE_ARG(aData);
      93                 :   nsresult rv;
      94                 : 
      95              16 :   rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions);
      96              16 :   if (!NS_SUCCEEDED(rv))
      97               0 :     return rv;
      98                 : 
      99                 :   rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride,
     100              16 :                      aInputFormat, aOutputOptions);
     101              16 :   if (!NS_SUCCEEDED(rv))
     102               0 :     return rv;
     103                 : 
     104              16 :   rv = EndImageEncode();
     105                 : 
     106              16 :   return rv;
     107                 : }
     108                 : 
     109                 : 
     110                 : // nsPNGEncoder::StartImageEncode
     111                 : //
     112                 : // 
     113                 : // See ::InitFromData for other info.
     114              26 : NS_IMETHODIMP nsPNGEncoder::StartImageEncode(PRUint32 aWidth,
     115                 :                                              PRUint32 aHeight,
     116                 :                                              PRUint32 aInputFormat,
     117                 :                                              const nsAString& aOutputOptions)
     118                 : {
     119              26 :   bool useTransparency = true, skipFirstFrame = false;
     120              26 :   PRUint32 numFrames = 1;
     121              26 :   PRUint32 numPlays = 0; // For animations, 0 == forever
     122                 : 
     123                 :   // can't initialize more than once
     124              26 :   if (mImageBuffer != nsnull)
     125               0 :     return NS_ERROR_ALREADY_INITIALIZED;
     126                 : 
     127                 :   // validate input format
     128              26 :   if (aInputFormat != INPUT_FORMAT_RGB &&
     129                 :       aInputFormat != INPUT_FORMAT_RGBA &&
     130                 :       aInputFormat != INPUT_FORMAT_HOSTARGB)
     131               0 :     return NS_ERROR_INVALID_ARG;
     132                 : 
     133                 :   // parse and check any provided output options
     134                 :   nsresult rv = ParseOptions(aOutputOptions, &useTransparency, &skipFirstFrame,
     135                 :                              &numFrames, &numPlays, nsnull, nsnull,
     136              26 :                              nsnull, nsnull, nsnull);
     137              26 :   if (rv != NS_OK)
     138               0 :     return rv;
     139                 : 
     140                 : #ifdef PNG_APNG_SUPPORTED
     141              26 :   if (numFrames > 1)
     142               6 :     mIsAnimation = true;
     143                 : 
     144                 : #endif
     145                 : 
     146                 :   // initialize
     147                 :   mPNG = png_create_write_struct(PNG_LIBPNG_VER_STRING,
     148                 :                                  nsnull,
     149                 :                                  ErrorCallback,
     150              26 :                                  WarningCallback);
     151              26 :   if (! mPNG)
     152               0 :     return NS_ERROR_OUT_OF_MEMORY;
     153                 : 
     154              26 :   mPNGinfo = png_create_info_struct(mPNG);
     155              26 :   if (! mPNGinfo) {
     156               0 :     png_destroy_write_struct(&mPNG, nsnull);
     157               0 :     return NS_ERROR_FAILURE;
     158                 :   }
     159                 : 
     160                 :   // libpng's error handler jumps back here upon an error.
     161                 :   // Note: It's important that all png_* callers do this, or errors
     162                 :   // will result in a corrupt time-warped stack.
     163              26 :   if (setjmp(png_jmpbuf(mPNG))) {
     164               0 :     png_destroy_write_struct(&mPNG, &mPNGinfo);
     165               0 :     return NS_ERROR_FAILURE;
     166                 :   }
     167                 : 
     168                 :   // Set up to read the data into our image buffer, start out with an 8K
     169                 :   // estimated size. Note: we don't have to worry about freeing this data
     170                 :   // in this function. It will be freed on object destruction.
     171              26 :   mImageBufferSize = 8192;
     172              26 :   mImageBuffer = (PRUint8*)PR_Malloc(mImageBufferSize);
     173              26 :   if (!mImageBuffer) {
     174               0 :     png_destroy_write_struct(&mPNG, &mPNGinfo);
     175               0 :     return NS_ERROR_OUT_OF_MEMORY;
     176                 :   }
     177              26 :   mImageBufferUsed = 0;
     178                 : 
     179                 :   // set our callback for libpng to give us the data
     180              26 :   png_set_write_fn(mPNG, this, WriteCallback, nsnull);
     181                 : 
     182                 :   // include alpha?
     183                 :   int colorType;
     184              26 :   if ((aInputFormat == INPUT_FORMAT_HOSTARGB ||
     185                 :        aInputFormat == INPUT_FORMAT_RGBA)  &&
     186                 :        useTransparency)
     187              19 :     colorType = PNG_COLOR_TYPE_RGB_ALPHA;
     188                 :   else
     189               7 :     colorType = PNG_COLOR_TYPE_RGB;
     190                 : 
     191                 :   png_set_IHDR(mPNG, mPNGinfo, aWidth, aHeight, 8, colorType,
     192                 :                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
     193              26 :                PNG_FILTER_TYPE_DEFAULT);
     194                 : 
     195                 : #ifdef PNG_APNG_SUPPORTED
     196              26 :   if (mIsAnimation) {
     197               6 :     png_set_first_frame_is_hidden(mPNG, mPNGinfo, skipFirstFrame);
     198               6 :     png_set_acTL(mPNG, mPNGinfo, numFrames, numPlays);
     199                 :   }
     200                 : #endif
     201                 : 
     202                 :   // XXX: support PLTE, gAMA, tRNS, bKGD?
     203                 : 
     204              26 :   png_write_info(mPNG, mPNGinfo);
     205                 : 
     206              26 :   return NS_OK;
     207                 : }
     208                 : 
     209                 : // Returns the number of bytes in the image buffer used.
     210               0 : NS_IMETHODIMP nsPNGEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
     211                 : {
     212               0 :   NS_ENSURE_ARG_POINTER(aOutputSize);
     213               0 :   *aOutputSize = mImageBufferUsed;
     214               0 :   return NS_OK;
     215                 : }
     216                 : 
     217                 : // Returns a pointer to the start of the image buffer
     218               0 : NS_IMETHODIMP nsPNGEncoder::GetImageBuffer(char **aOutputBuffer)
     219                 : {
     220               0 :   NS_ENSURE_ARG_POINTER(aOutputBuffer);
     221               0 :   *aOutputBuffer = reinterpret_cast<char*>(mImageBuffer);
     222               0 :   return NS_OK;
     223                 : }
     224                 : 
     225              39 : NS_IMETHODIMP nsPNGEncoder::AddImageFrame(const PRUint8* aData,
     226                 :                                           PRUint32 aLength, // (unused,
     227                 :                                                             // req'd by JS)
     228                 :                                           PRUint32 aWidth,
     229                 :                                           PRUint32 aHeight,
     230                 :                                           PRUint32 aStride,
     231                 :                                           PRUint32 aInputFormat,
     232                 :                                           const nsAString& aFrameOptions)
     233                 : {
     234              39 :   bool useTransparency= true;
     235              39 :   PRUint32 delay_ms = 500;
     236                 : #ifdef PNG_APNG_SUPPORTED
     237              39 :   PRUint32 dispose_op = PNG_DISPOSE_OP_NONE;
     238              39 :   PRUint32 blend_op = PNG_BLEND_OP_SOURCE;
     239                 : #else
     240                 :   PRUint32 dispose_op;
     241                 :   PRUint32 blend_op;
     242                 : #endif
     243              39 :   PRUint32 x_offset = 0, y_offset = 0;
     244                 : 
     245                 :   // must be initialized
     246              39 :   if (mImageBuffer == nsnull)
     247               0 :     return NS_ERROR_NOT_INITIALIZED;
     248                 : 
     249                 :   // EndImageEncode was done, or some error occurred earlier
     250              39 :   if (!mPNG)
     251               0 :     return NS_BASE_STREAM_CLOSED;
     252                 : 
     253                 :   // validate input format
     254              39 :   if (aInputFormat != INPUT_FORMAT_RGB &&
     255                 :       aInputFormat != INPUT_FORMAT_RGBA &&
     256                 :       aInputFormat != INPUT_FORMAT_HOSTARGB)
     257               0 :     return NS_ERROR_INVALID_ARG;
     258                 : 
     259                 :   // libpng's error handler jumps back here upon an error.
     260              39 :   if (setjmp(png_jmpbuf(mPNG))) {
     261               0 :     png_destroy_write_struct(&mPNG, &mPNGinfo);
     262               0 :     return NS_ERROR_FAILURE;
     263                 :   }
     264                 : 
     265                 :   // parse and check any provided output options
     266                 :   nsresult rv = ParseOptions(aFrameOptions, &useTransparency, nsnull,
     267                 :                              nsnull, nsnull, &dispose_op, &blend_op,
     268              39 :                              &delay_ms, &x_offset, &y_offset);
     269              39 :   if (rv != NS_OK)
     270               0 :     return rv;
     271                 : 
     272                 : #ifdef PNG_APNG_SUPPORTED
     273              39 :   if (mIsAnimation) {
     274                 :     // XXX the row pointers arg (#3) is unused, can it be removed?
     275                 :     png_write_frame_head(mPNG, mPNGinfo, nsnull,
     276                 :                          aWidth, aHeight, x_offset, y_offset,
     277              19 :                          delay_ms, 1000, dispose_op, blend_op);
     278                 :   }
     279                 : #endif
     280                 : 
     281                 :   // Stride is the padded width of each row, so it better be longer 
     282                 :   // (I'm afraid people will not understand what stride means, so
     283                 :   // check it well)
     284              39 :   if ((aInputFormat == INPUT_FORMAT_RGB &&
     285                 :       aStride < aWidth * 3) ||
     286                 :       ((aInputFormat == INPUT_FORMAT_RGBA ||
     287                 :       aInputFormat == INPUT_FORMAT_HOSTARGB) &&
     288                 :       aStride < aWidth * 4)) {
     289               0 :     NS_WARNING("Invalid stride for InitFromData/AddImageFrame");
     290               0 :     return NS_ERROR_INVALID_ARG;
     291                 :   }
     292                 : 
     293                 : #ifdef PNG_WRITE_FILTER_SUPPORTED
     294                 :   png_set_filter(mPNG, PNG_FILTER_TYPE_BASE, PNG_FILTER_VALUE_NONE);
     295                 : #endif
     296                 : 
     297                 :   // write each row: if we add more input formats, we may want to
     298                 :   // generalize the conversions
     299              39 :   if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
     300                 :     // PNG requires RGBA with post-multiplied alpha, so we need to
     301                 :     // convert
     302              24 :     PRUint8* row = new PRUint8[aWidth * 4];
     303             236 :     for (PRUint32 y = 0; y < aHeight; y ++) {
     304             224 :       ConvertHostARGBRow(&aData[y * aStride], row, aWidth, useTransparency);
     305             224 :       png_write_row(mPNG, row);
     306                 :     }
     307              12 :     delete[] row;
     308                 : 
     309              27 :   } else if (aInputFormat == INPUT_FORMAT_RGBA && ! useTransparency) {
     310                 :     // RBGA, but we need to strip the alpha
     311               4 :     PRUint8* row = new PRUint8[aWidth * 4];
     312               8 :     for (PRUint32 y = 0; y < aHeight; y ++) {
     313               6 :       StripAlpha(&aData[y * aStride], row, aWidth);
     314               6 :       png_write_row(mPNG, row);
     315                 :     }
     316               4 :     delete[] row;
     317                 : 
     318              25 :   } else if (aInputFormat == INPUT_FORMAT_RGB ||
     319                 :              aInputFormat == INPUT_FORMAT_RGBA) {
     320                 :     // simple RBG(A), no conversion needed
     321              94 :     for (PRUint32 y = 0; y < aHeight; y ++) {
     322              69 :       png_write_row(mPNG, (PRUint8*)&aData[y * aStride]);
     323              25 :     }
     324                 : 
     325                 :   } else {
     326               0 :     NS_NOTREACHED("Bad format type");
     327               0 :     return NS_ERROR_INVALID_ARG;
     328                 :   }
     329                 : 
     330                 : #ifdef PNG_APNG_SUPPORTED
     331              39 :   if (mIsAnimation) {
     332              19 :     png_write_frame_tail(mPNG, mPNGinfo);
     333                 :   }
     334                 : #endif
     335                 : 
     336              39 :   return NS_OK;
     337                 : }
     338                 : 
     339                 : 
     340              26 : NS_IMETHODIMP nsPNGEncoder::EndImageEncode()
     341                 : {
     342                 :   // must be initialized
     343              26 :   if (mImageBuffer == nsnull)
     344               0 :     return NS_ERROR_NOT_INITIALIZED;
     345                 : 
     346                 :   // EndImageEncode has already been called, or some error
     347                 :   // occurred earlier
     348              26 :   if (!mPNG)
     349               0 :     return NS_BASE_STREAM_CLOSED;
     350                 : 
     351                 :   // libpng's error handler jumps back here upon an error.
     352              26 :   if (setjmp(png_jmpbuf(mPNG))) {
     353               0 :     png_destroy_write_struct(&mPNG, &mPNGinfo);
     354               0 :     return NS_ERROR_FAILURE;
     355                 :   }
     356                 : 
     357              26 :   png_write_end(mPNG, mPNGinfo);
     358              26 :   png_destroy_write_struct(&mPNG, &mPNGinfo);
     359                 : 
     360              26 :   mFinished = true;
     361              26 :   NotifyListener();
     362                 : 
     363                 :   // if output callback can't get enough memory, it will free our buffer
     364              26 :   if (!mImageBuffer)
     365               0 :     return NS_ERROR_OUT_OF_MEMORY;
     366                 : 
     367              26 :   return NS_OK;
     368                 : }
     369                 : 
     370                 : 
     371                 : nsresult
     372              65 : nsPNGEncoder::ParseOptions(const nsAString& aOptions,
     373                 :                            bool* useTransparency,
     374                 :                            bool* skipFirstFrame,
     375                 :                            PRUint32* numFrames,
     376                 :                            PRUint32* numPlays,
     377                 :                            PRUint32* frameDispose,
     378                 :                            PRUint32* frameBlend,
     379                 :                            PRUint32* frameDelay,
     380                 :                            PRUint32* offsetX,
     381                 :                            PRUint32* offsetY)
     382                 : {
     383                 : #ifdef PNG_APNG_SUPPORTED
     384                 :   // Make a copy of aOptions, because strtok() will modify it.
     385             130 :   nsCAutoString optionsCopy;
     386              65 :   optionsCopy.Assign(NS_ConvertUTF16toUTF8(aOptions));
     387              65 :   char* options = optionsCopy.BeginWriting();
     388                 : 
     389             239 :   while (char* token = nsCRT::strtok(options, ";", &options)) {
     390                 :     // If there's an '=' character, split the token around it.
     391              87 :     char* equals = token, *value = nsnull;
     392             771 :     while(*equals != '=' && *equals) {
     393             597 :       ++equals;
     394                 :     }
     395              87 :     if (*equals == '=')
     396              87 :       value = equals + 1;
     397                 : 
     398              87 :     if (value)
     399              87 :       *equals = '\0'; // temporary null
     400                 : 
     401                 :     // transparency=[yes|no|none]
     402              87 :     if (nsCRT::strcmp(token, "transparency") == 0 && useTransparency) {
     403               8 :       if (!value)
     404               0 :         return NS_ERROR_INVALID_ARG;
     405                 : 
     406               8 :       if (nsCRT::strcmp(value, "none") == 0 ||
     407               0 :           nsCRT::strcmp(value, "no") == 0) {
     408               8 :         *useTransparency = false;
     409               0 :       } else if (nsCRT::strcmp(value, "yes") == 0) {
     410               0 :         *useTransparency = true;
     411                 :       } else {
     412               0 :         return NS_ERROR_INVALID_ARG;
     413                 :       }
     414                 : 
     415                 :     // skipfirstframe=[yes|no]
     416              79 :     } else if (nsCRT::strcmp(token, "skipfirstframe") == 0 &&
     417                 :                skipFirstFrame) {
     418               6 :       if (!value)
     419               0 :         return NS_ERROR_INVALID_ARG;
     420                 : 
     421               6 :       if (nsCRT::strcmp(value, "no") == 0) {
     422               5 :         *skipFirstFrame = false;
     423               1 :       } else if (nsCRT::strcmp(value, "yes") == 0) {
     424               1 :         *skipFirstFrame = true;
     425                 :       } else {
     426               0 :         return NS_ERROR_INVALID_ARG;
     427                 :       }
     428                 : 
     429                 :     // frames=#
     430              73 :     } else if (nsCRT::strcmp(token, "frames") == 0 && numFrames) {
     431               6 :       if (!value)
     432               0 :         return NS_ERROR_INVALID_ARG;
     433                 : 
     434               6 :       if (PR_sscanf(value, "%u", numFrames) != 1) {
     435               0 :         return NS_ERROR_INVALID_ARG;
     436                 :       }
     437                 : 
     438                 :       // frames=0 is nonsense.
     439               6 :       if (*numFrames == 0)
     440               0 :         return NS_ERROR_INVALID_ARG;
     441                 : 
     442                 :     // plays=#
     443              67 :     } else if (nsCRT::strcmp(token, "plays") == 0 && numPlays) {
     444               6 :       if (!value)
     445               0 :         return NS_ERROR_INVALID_ARG;
     446                 : 
     447                 :       // plays=0 to loop forever, otherwise play sequence specified
     448                 :       // number of times
     449               6 :       if (PR_sscanf(value, "%u", numPlays) != 1)
     450               0 :         return NS_ERROR_INVALID_ARG;
     451                 : 
     452                 :     // dispose=[none|background|previous]
     453              61 :     } else if (nsCRT::strcmp(token, "dispose") == 0 && frameDispose) {
     454              19 :       if (!value)
     455               0 :         return NS_ERROR_INVALID_ARG;
     456                 : 
     457              19 :       if (nsCRT::strcmp(value, "none") == 0) {
     458              16 :         *frameDispose = PNG_DISPOSE_OP_NONE;
     459               3 :       } else if (nsCRT::strcmp(value, "background") == 0) {
     460               3 :         *frameDispose = PNG_DISPOSE_OP_BACKGROUND;
     461               0 :       } else if (nsCRT::strcmp(value, "previous") == 0) {
     462               0 :         *frameDispose = PNG_DISPOSE_OP_PREVIOUS;
     463                 :       } else {
     464               0 :         return NS_ERROR_INVALID_ARG;
     465                 :       }
     466                 : 
     467                 :     // blend=[source|over]
     468              42 :     } else if (nsCRT::strcmp(token, "blend") == 0 && frameBlend) {
     469              19 :       if (!value)
     470               0 :         return NS_ERROR_INVALID_ARG;
     471                 : 
     472              19 :       if (nsCRT::strcmp(value, "source") == 0) {
     473              15 :         *frameBlend = PNG_BLEND_OP_SOURCE;
     474               4 :       } else if (nsCRT::strcmp(value, "over") == 0) {
     475               4 :         *frameBlend = PNG_BLEND_OP_OVER;
     476                 :       } else {
     477               0 :         return NS_ERROR_INVALID_ARG;
     478                 :       }
     479                 : 
     480                 :     // delay=# (in ms)
     481              23 :     } else if (nsCRT::strcmp(token, "delay") == 0 && frameDelay) {
     482              19 :       if (!value)
     483               0 :         return NS_ERROR_INVALID_ARG;
     484                 : 
     485              19 :       if (PR_sscanf(value, "%u", frameDelay) != 1)
     486               0 :         return NS_ERROR_INVALID_ARG;
     487                 : 
     488                 :     // xoffset=#
     489               4 :     } else if (nsCRT::strcmp(token, "xoffset") == 0 && offsetX) {
     490               2 :       if (!value)
     491               0 :         return NS_ERROR_INVALID_ARG;
     492                 : 
     493               2 :       if (PR_sscanf(value, "%u", offsetX) != 1)
     494               0 :         return NS_ERROR_INVALID_ARG;
     495                 : 
     496                 :     // yoffset=#
     497               2 :     } else if (nsCRT::strcmp(token, "yoffset") == 0 && offsetY) {
     498               2 :       if (!value)
     499               0 :         return NS_ERROR_INVALID_ARG;
     500                 : 
     501               2 :       if (PR_sscanf(value, "%u", offsetY) != 1)
     502               0 :         return NS_ERROR_INVALID_ARG;
     503                 : 
     504                 :     // unknown token name
     505                 :     } else
     506               0 :       return NS_ERROR_INVALID_ARG;
     507                 : 
     508              87 :     if (value)
     509              87 :       *equals = '='; // restore '=' so strtok doesn't get lost
     510                 :   }
     511                 : 
     512                 : #endif
     513              65 :   return NS_OK;
     514                 : }
     515                 : 
     516                 : 
     517                 : /* void close (); */
     518               0 : NS_IMETHODIMP nsPNGEncoder::Close()
     519                 : {
     520               0 :   if (mImageBuffer != nsnull) {
     521               0 :     PR_Free(mImageBuffer);
     522               0 :     mImageBuffer = nsnull;
     523               0 :     mImageBufferSize = 0;
     524               0 :     mImageBufferUsed = 0;
     525               0 :     mImageBufferReadPoint = 0;
     526                 :   }
     527               0 :   return NS_OK;
     528                 : }
     529                 : 
     530                 : /* unsigned long available (); */
     531              37 : NS_IMETHODIMP nsPNGEncoder::Available(PRUint32 *_retval)
     532                 : {
     533              37 :   if (!mImageBuffer)
     534               0 :     return NS_BASE_STREAM_CLOSED;
     535                 : 
     536              37 :   *_retval = mImageBufferUsed - mImageBufferReadPoint;
     537              37 :   return NS_OK;
     538                 : }
     539                 : 
     540                 : /* [noscript] unsigned long read (in charPtr aBuf,
     541                 :                                   in unsigned long aCount); */
     542              67 : NS_IMETHODIMP nsPNGEncoder::Read(char * aBuf, PRUint32 aCount,
     543                 :                                  PRUint32 *_retval)
     544                 : {
     545              67 :   return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
     546                 : }
     547                 : 
     548                 : /* [noscript] unsigned long readSegments (in nsWriteSegmentFun aWriter,
     549                 :                                           in voidPtr aClosure,
     550                 :                                           in unsigned long aCount); */
     551              67 : NS_IMETHODIMP nsPNGEncoder::ReadSegments(nsWriteSegmentFun aWriter,
     552                 :                                          void *aClosure, PRUint32 aCount,
     553                 :                                          PRUint32 *_retval)
     554                 : {
     555                 :   // Avoid another thread reallocing the buffer underneath us
     556             134 :   ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);
     557                 : 
     558              67 :   PRUint32 maxCount = mImageBufferUsed - mImageBufferReadPoint;
     559              67 :   if (maxCount == 0) {
     560               0 :     *_retval = 0;
     561               0 :     return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
     562                 :   }
     563                 : 
     564              67 :   if (aCount > maxCount)
     565               0 :     aCount = maxCount;
     566                 :   nsresult rv =
     567                 :       aWriter(this, aClosure,
     568                 :               reinterpret_cast<const char*>(mImageBuffer+mImageBufferReadPoint),
     569              67 :               0, aCount, _retval);
     570              67 :   if (NS_SUCCEEDED(rv)) {
     571              67 :     NS_ASSERTION(*_retval <= aCount, "bad write count");
     572              67 :     mImageBufferReadPoint += *_retval;
     573                 :   }
     574                 : 
     575                 :   // errors returned from the writer end here!
     576              67 :   return NS_OK;
     577                 : }
     578                 : 
     579                 : /* boolean isNonBlocking (); */
     580               0 : NS_IMETHODIMP nsPNGEncoder::IsNonBlocking(bool *_retval)
     581                 : {
     582               0 :   *_retval = true;
     583               0 :   return NS_OK;
     584                 : }
     585                 : 
     586               8 : NS_IMETHODIMP nsPNGEncoder::AsyncWait(nsIInputStreamCallback *aCallback,
     587                 :                                       PRUint32 aFlags,
     588                 :                                       PRUint32 aRequestedCount,
     589                 :                                       nsIEventTarget *aTarget)
     590                 : {
     591               8 :   if (aFlags != 0)
     592               0 :     return NS_ERROR_NOT_IMPLEMENTED;
     593                 : 
     594               8 :   if (mCallback || mCallbackTarget)
     595               0 :     return NS_ERROR_UNEXPECTED;
     596                 : 
     597               8 :   mCallbackTarget = aTarget;
     598                 :   // 0 means "any number of bytes except 0"
     599               8 :   mNotifyThreshold = aRequestedCount;
     600               8 :   if (!aRequestedCount)
     601               8 :     mNotifyThreshold = 1024; // We don't want to notify incessantly
     602                 : 
     603                 :   // We set the callback absolutely last, because NotifyListener uses it to
     604                 :   // determine if someone needs to be notified.  If we don't set it last,
     605                 :   // NotifyListener might try to fire off a notification to a null target
     606                 :   // which will generally cause non-threadsafe objects to be used off the main thread
     607               8 :   mCallback = aCallback;
     608                 : 
     609                 :   // What we are being asked for may be present already
     610               8 :   NotifyListener();
     611               8 :   return NS_OK;
     612                 : }
     613                 : 
     614               0 : NS_IMETHODIMP nsPNGEncoder::CloseWithStatus(nsresult aStatus)
     615                 : {
     616               0 :   return Close();
     617                 : }
     618                 : 
     619                 : // nsPNGEncoder::ConvertHostARGBRow
     620                 : //
     621                 : //    Our colors are stored with premultiplied alphas, but PNGs use
     622                 : //    post-multiplied alpha. This swaps to PNG-style alpha.
     623                 : //
     624                 : //    Copied from gfx/cairo/cairo/src/cairo-png.c
     625                 : 
     626                 : void
     627             224 : nsPNGEncoder::ConvertHostARGBRow(const PRUint8* aSrc, PRUint8* aDest,
     628                 :                                  PRUint32 aPixelWidth,
     629                 :                                  bool aUseTransparency)
     630                 : {
     631             224 :   PRUint32 pixelStride = aUseTransparency ? 4 : 3;
     632            4832 :   for (PRUint32 x = 0; x < aPixelWidth; x ++) {
     633            4608 :     const PRUint32& pixelIn = ((const PRUint32*)(aSrc))[x];
     634            4608 :     PRUint8 *pixelOut = &aDest[x * pixelStride];
     635                 : 
     636            4608 :     PRUint8 alpha = (pixelIn & 0xff000000) >> 24;
     637            4608 :     if (alpha == 0) {
     638             605 :       pixelOut[0] = pixelOut[1] = pixelOut[2] = pixelOut[3] = 0;
     639                 :     } else {
     640            4003 :       pixelOut[0] = (((pixelIn & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
     641            4003 :       pixelOut[1] = (((pixelIn & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
     642            4003 :       pixelOut[2] = (((pixelIn & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
     643            4003 :       if (aUseTransparency)
     644            4003 :         pixelOut[3] = alpha;
     645                 :     }
     646                 :   }
     647             224 : }
     648                 : 
     649                 : 
     650                 : // nsPNGEncoder::StripAlpha
     651                 : //
     652                 : //    Input is RGBA, output is RGB
     653                 : 
     654                 : void
     655               6 : nsPNGEncoder::StripAlpha(const PRUint8* aSrc, PRUint8* aDest,
     656                 :                           PRUint32 aPixelWidth)
     657                 : {
     658              24 :   for (PRUint32 x = 0; x < aPixelWidth; x ++) {
     659              18 :     const PRUint8* pixelIn = &aSrc[x * 4];
     660              18 :     PRUint8* pixelOut = &aDest[x * 3];
     661              18 :     pixelOut[0] = pixelIn[0];
     662              18 :     pixelOut[1] = pixelIn[1];
     663              18 :     pixelOut[2] = pixelIn[2];
     664                 :   }
     665               6 : }
     666                 : 
     667                 : 
     668                 : // nsPNGEncoder::WarningCallback
     669                 : 
     670                 : void // static
     671               0 : nsPNGEncoder::WarningCallback(png_structp png_ptr,
     672                 :                             png_const_charp warning_msg)
     673                 : {
     674                 : #ifdef DEBUG
     675                 :         // XXX: these messages are probably useful callers...
     676                 :         // use nsIConsoleService?
     677               0 :         PR_fprintf(PR_STDERR, "PNG Encoder: %s\n", warning_msg);;
     678                 : #endif
     679               0 : }
     680                 : 
     681                 : 
     682                 : // nsPNGEncoder::ErrorCallback
     683                 : 
     684                 : void // static
     685               0 : nsPNGEncoder::ErrorCallback(png_structp png_ptr,
     686                 :                             png_const_charp error_msg)
     687                 : {
     688                 : #ifdef DEBUG
     689                 :         // XXX: these messages are probably useful callers...
     690                 :         // use nsIConsoleService?
     691               0 :         PR_fprintf(PR_STDERR, "PNG Encoder: %s\n", error_msg);;
     692                 : #endif
     693                 : #if PNG_LIBPNG_VER < 10500
     694                 :         longjmp(png_ptr->jmpbuf, 1);
     695                 : #else
     696               0 :         png_longjmp(png_ptr, 1);
     697                 : #endif
     698                 : }
     699                 : 
     700                 : 
     701                 : // nsPNGEncoder::WriteCallback
     702                 : 
     703                 : void // static
     704             358 : nsPNGEncoder::WriteCallback(png_structp png, png_bytep data,
     705                 :                             png_size_t size)
     706                 : {
     707             358 :   nsPNGEncoder* that = static_cast<nsPNGEncoder*>(png_get_io_ptr(png));
     708             358 :   if (! that->mImageBuffer)
     709               0 :     return;
     710                 : 
     711             358 :   if (that->mImageBufferUsed + size > that->mImageBufferSize) {
     712                 :     // When we're reallocing the buffer we need to take the lock to ensure
     713                 :     // that nobody is trying to read from the buffer we are destroying
     714               0 :     ReentrantMonitorAutoEnter autoEnter(that->mReentrantMonitor);
     715                 : 
     716                 :     // expand buffer, just double each time
     717               0 :     that->mImageBufferSize *= 2;
     718                 :     PRUint8* newBuf = (PRUint8*)PR_Realloc(that->mImageBuffer,
     719               0 :                                            that->mImageBufferSize);
     720               0 :     if (! newBuf) {
     721                 :       // can't resize, just zero (this will keep us from writing more)
     722               0 :       PR_Free(that->mImageBuffer);
     723               0 :       that->mImageBuffer = nsnull;
     724               0 :       that->mImageBufferSize = 0;
     725               0 :       that->mImageBufferUsed = 0;
     726                 :       return;
     727                 :     }
     728               0 :     that->mImageBuffer = newBuf;
     729                 :   }
     730             358 :   memcpy(&that->mImageBuffer[that->mImageBufferUsed], data, size);
     731             358 :   that->mImageBufferUsed += size;
     732             358 :   that->NotifyListener();
     733                 : }
     734                 : 
     735                 : void
     736             392 : nsPNGEncoder::NotifyListener()
     737                 : {
     738                 :   // We might call this function on multiple threads (any threads that call
     739                 :   // AsyncWait and any that do encoding) so we lock to avoid notifying the
     740                 :   // listener twice about the same data (which generally leads to a truncated
     741                 :   // image).
     742             784 :   ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);
     743                 : 
     744             392 :   if (mCallback &&
     745                 :       (mImageBufferUsed - mImageBufferReadPoint >= mNotifyThreshold ||
     746             392 :        mFinished)) {
     747              16 :     nsCOMPtr<nsIInputStreamCallback> callback;
     748               8 :     if (mCallbackTarget) {
     749               8 :       NS_NewInputStreamReadyEvent(getter_AddRefs(callback),
     750                 :                                   mCallback,
     751               8 :                                   mCallbackTarget);
     752                 :     } else {
     753               0 :       callback = mCallback;
     754                 :     }
     755                 : 
     756               8 :     NS_ASSERTION(callback, "Shouldn't fail to make the callback");
     757                 :     // Null the callback first because OnInputStreamReady could reenter
     758                 :     // AsyncWait
     759               8 :     mCallback = nsnull;
     760               8 :     mCallbackTarget = nsnull;
     761               8 :     mNotifyThreshold = 0;
     762                 : 
     763               8 :     callback->OnInputStreamReady(this);
     764                 :   }
     765             392 : }

Generated by: LCOV version 1.7