PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/libpr0n/encoders/png/nsPNGEncoder.cpp

https://bitbucket.org/dineshkummarc/mozilla-1.9.0-win64
C++ | 590 lines | 374 code | 98 blank | 118 comment | 167 complexity | 33bf5b0c87bdc7714960cd39dabcf666 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, MIT, BSD-3-Clause, MPL-2.0-no-copyleft-exception, GPL-2.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. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either the GNU General Public License Version 2 or later (the "GPL"), or
  29. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. #include "nsCRT.h"
  41. #include "nsPNGEncoder.h"
  42. #include "prmem.h"
  43. #include "prprf.h"
  44. #include "nsString.h"
  45. #include "nsStreamUtils.h"
  46. // Input streams that do not implement nsIAsyncInputStream should be threadsafe
  47. // so that they may be used with nsIInputStreamPump and nsIInputStreamChannel,
  48. // which read such a stream on a background thread.
  49. NS_IMPL_THREADSAFE_ISUPPORTS2(nsPNGEncoder, imgIEncoder, nsIInputStream)
  50. nsPNGEncoder::nsPNGEncoder() : mPNG(nsnull), mPNGinfo(nsnull),
  51. mIsAnimation(PR_FALSE),
  52. mImageBuffer(nsnull), mImageBufferSize(0),
  53. mImageBufferUsed(0), mImageBufferReadPoint(0)
  54. {
  55. }
  56. nsPNGEncoder::~nsPNGEncoder()
  57. {
  58. if (mImageBuffer) {
  59. PR_Free(mImageBuffer);
  60. mImageBuffer = nsnull;
  61. }
  62. // don't leak if EndImageEncode wasn't called
  63. if (mPNG)
  64. png_destroy_write_struct(&mPNG, &mPNGinfo);
  65. }
  66. // nsPNGEncoder::InitFromData
  67. //
  68. // One output option is supported: "transparency=none" means that the
  69. // output PNG will not have an alpha channel, even if the input does.
  70. //
  71. // Based partially on gfx/cairo/cairo/src/cairo-png.c
  72. // See also modules/libimg/png/libpng.txt
  73. NS_IMETHODIMP nsPNGEncoder::InitFromData(const PRUint8* aData,
  74. PRUint32 aLength, // (unused, req'd by JS)
  75. PRUint32 aWidth,
  76. PRUint32 aHeight,
  77. PRUint32 aStride,
  78. PRUint32 aInputFormat,
  79. const nsAString& aOutputOptions)
  80. {
  81. nsresult rv;
  82. rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions);
  83. if (!NS_SUCCEEDED(rv))
  84. return rv;
  85. rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride, aInputFormat, aOutputOptions);
  86. if (!NS_SUCCEEDED(rv))
  87. return rv;
  88. rv = EndImageEncode();
  89. return rv;
  90. }
  91. // nsPNGEncoder::StartImageEncode
  92. //
  93. //
  94. // See ::InitFromData for other info.
  95. NS_IMETHODIMP nsPNGEncoder::StartImageEncode(PRUint32 aWidth,
  96. PRUint32 aHeight,
  97. PRUint32 aInputFormat,
  98. const nsAString& aOutputOptions)
  99. {
  100. PRBool useTransparency = PR_TRUE, skipFirstFrame = PR_FALSE;
  101. PRUint32 numFrames = 1;
  102. PRUint32 numPlays = 0; // For animations, 0 == forever
  103. // can't initialize more than once
  104. if (mImageBuffer != nsnull)
  105. return NS_ERROR_ALREADY_INITIALIZED;
  106. // validate input format
  107. if (aInputFormat != INPUT_FORMAT_RGB &&
  108. aInputFormat != INPUT_FORMAT_RGBA &&
  109. aInputFormat != INPUT_FORMAT_HOSTARGB)
  110. return NS_ERROR_INVALID_ARG;
  111. // parse and check any provided output options
  112. nsresult rv = ParseOptions(aOutputOptions, &useTransparency, &skipFirstFrame,
  113. &numFrames, &numPlays, nsnull, nsnull,
  114. nsnull, nsnull, nsnull);
  115. if (rv != NS_OK) { return rv; }
  116. if (numFrames > 1) {
  117. mIsAnimation = PR_TRUE;
  118. }
  119. // initialize
  120. mPNG = png_create_write_struct(PNG_LIBPNG_VER_STRING,
  121. nsnull,
  122. ErrorCallback,
  123. ErrorCallback);
  124. if (! mPNG)
  125. return NS_ERROR_OUT_OF_MEMORY;
  126. mPNGinfo = png_create_info_struct(mPNG);
  127. if (! mPNGinfo) {
  128. png_destroy_write_struct(&mPNG, nsnull);
  129. return NS_ERROR_FAILURE;
  130. }
  131. // libpng's error handler jumps back here upon an error.
  132. // Note: It's important that all png_* callers do this, or errors
  133. // will result in a corrupt time-warped stack.
  134. if (setjmp(png_jmpbuf(mPNG))) {
  135. png_destroy_write_struct(&mPNG, &mPNGinfo);
  136. return NS_ERROR_FAILURE;
  137. }
  138. // Set up to read the data into our image buffer, start out with an 8K
  139. // estimated size. Note: we don't have to worry about freeing this data
  140. // in this function. It will be freed on object destruction.
  141. mImageBufferSize = 8192;
  142. mImageBuffer = (PRUint8*)PR_Malloc(mImageBufferSize);
  143. if (!mImageBuffer) {
  144. png_destroy_write_struct(&mPNG, &mPNGinfo);
  145. return NS_ERROR_OUT_OF_MEMORY;
  146. }
  147. mImageBufferUsed = 0;
  148. // set our callback for libpng to give us the data
  149. png_set_write_fn(mPNG, this, WriteCallback, NULL);
  150. // include alpha?
  151. int colorType;
  152. if ((aInputFormat == INPUT_FORMAT_HOSTARGB ||
  153. aInputFormat == INPUT_FORMAT_RGBA) && useTransparency)
  154. colorType = PNG_COLOR_TYPE_RGB_ALPHA;
  155. else
  156. colorType = PNG_COLOR_TYPE_RGB;
  157. png_set_IHDR(mPNG, mPNGinfo, aWidth, aHeight, 8, colorType,
  158. PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
  159. PNG_FILTER_TYPE_DEFAULT);
  160. if (mIsAnimation) {
  161. png_set_first_frame_is_hidden(mPNG, mPNGinfo, skipFirstFrame);
  162. png_set_acTL(mPNG, mPNGinfo, numFrames, numPlays);
  163. }
  164. // XXX: support PLTE, gAMA, tRNS, bKGD?
  165. png_write_info(mPNG, mPNGinfo);
  166. return NS_OK;
  167. }
  168. NS_IMETHODIMP nsPNGEncoder::AddImageFrame(const PRUint8* aData,
  169. PRUint32 aLength, // (unused, req'd by JS)
  170. PRUint32 aWidth,
  171. PRUint32 aHeight,
  172. PRUint32 aStride,
  173. PRUint32 aInputFormat,
  174. const nsAString& aFrameOptions)
  175. {
  176. PRBool useTransparency= PR_TRUE;
  177. PRUint32 delay_ms = 500;
  178. PRUint32 dispose_op = PNG_DISPOSE_OP_NONE;
  179. PRUint32 blend_op = PNG_BLEND_OP_SOURCE;
  180. PRUint32 x_offset = 0, y_offset = 0;
  181. // must be initialized
  182. if (mImageBuffer == nsnull)
  183. return NS_ERROR_NOT_INITIALIZED;
  184. // EndImageEncode was done, or some error occurred earlier
  185. if (!mPNG)
  186. return NS_BASE_STREAM_CLOSED;
  187. // validate input format
  188. if (aInputFormat != INPUT_FORMAT_RGB &&
  189. aInputFormat != INPUT_FORMAT_RGBA &&
  190. aInputFormat != INPUT_FORMAT_HOSTARGB)
  191. return NS_ERROR_INVALID_ARG;
  192. // libpng's error handler jumps back here upon an error.
  193. if (setjmp(png_jmpbuf(mPNG))) {
  194. png_destroy_write_struct(&mPNG, &mPNGinfo);
  195. return NS_ERROR_FAILURE;
  196. }
  197. // parse and check any provided output options
  198. nsresult rv = ParseOptions(aFrameOptions, &useTransparency, nsnull,
  199. nsnull, nsnull, &dispose_op, &blend_op,
  200. &delay_ms, &x_offset, &y_offset);
  201. if (rv != NS_OK) { return rv; }
  202. if (mIsAnimation) {
  203. // XXX the row pointers arg (#3) is unused, can it be removed?
  204. png_write_frame_head(mPNG, mPNGinfo, nsnull,
  205. aWidth, aHeight, x_offset, y_offset,
  206. delay_ms, 1000, dispose_op, blend_op);
  207. }
  208. // Stride is the padded width of each row, so it better be longer (I'm afraid
  209. // people will not understand what stride means, so check it well)
  210. if ((aInputFormat == INPUT_FORMAT_RGB &&
  211. aStride < aWidth * 3) ||
  212. ((aInputFormat == INPUT_FORMAT_RGBA || aInputFormat == INPUT_FORMAT_HOSTARGB) &&
  213. aStride < aWidth * 4)) {
  214. NS_WARNING("Invalid stride for InitFromData/AddImageFrame");
  215. return NS_ERROR_INVALID_ARG;
  216. }
  217. // write each row: if we add more input formats, we may want to
  218. // generalize the conversions
  219. if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
  220. // PNG requires RGBA with post-multiplied alpha, so we need to convert
  221. PRUint8* row = new PRUint8[aWidth * 4];
  222. for (PRUint32 y = 0; y < aHeight; y ++) {
  223. ConvertHostARGBRow(&aData[y * aStride], row, aWidth, useTransparency);
  224. png_write_row(mPNG, row);
  225. }
  226. delete[] row;
  227. } else if (aInputFormat == INPUT_FORMAT_RGBA && ! useTransparency) {
  228. // RBGA, but we need to strip the alpha
  229. PRUint8* row = new PRUint8[aWidth * 4];
  230. for (PRUint32 y = 0; y < aHeight; y ++) {
  231. StripAlpha(&aData[y * aStride], row, aWidth);
  232. png_write_row(mPNG, row);
  233. }
  234. delete[] row;
  235. } else if (aInputFormat == INPUT_FORMAT_RGB ||
  236. aInputFormat == INPUT_FORMAT_RGBA) {
  237. // simple RBG(A), no conversion needed
  238. for (PRUint32 y = 0; y < aHeight; y ++) {
  239. png_write_row(mPNG, (PRUint8*)&aData[y * aStride]);
  240. }
  241. } else {
  242. NS_NOTREACHED("Bad format type");
  243. return NS_ERROR_INVALID_ARG;
  244. }
  245. if (mIsAnimation) {
  246. png_write_frame_tail(mPNG, mPNGinfo);
  247. }
  248. return NS_OK;
  249. }
  250. NS_IMETHODIMP nsPNGEncoder::EndImageEncode()
  251. {
  252. // must be initialized
  253. if (mImageBuffer == nsnull)
  254. return NS_ERROR_NOT_INITIALIZED;
  255. // EndImageEncode has already been called, or some error occurred earlier
  256. if (!mPNG)
  257. return NS_BASE_STREAM_CLOSED;
  258. // libpng's error handler jumps back here upon an error.
  259. if (setjmp(png_jmpbuf(mPNG))) {
  260. png_destroy_write_struct(&mPNG, &mPNGinfo);
  261. return NS_ERROR_FAILURE;
  262. }
  263. png_write_end(mPNG, mPNGinfo);
  264. png_destroy_write_struct(&mPNG, &mPNGinfo);
  265. // if output callback can't get enough memory, it will free our buffer
  266. if (!mImageBuffer)
  267. return NS_ERROR_OUT_OF_MEMORY;
  268. return NS_OK;
  269. }
  270. nsresult
  271. nsPNGEncoder::ParseOptions(const nsAString& aOptions,
  272. PRBool* useTransparency,
  273. PRBool* skipFirstFrame,
  274. PRUint32* numFrames,
  275. PRUint32* numPlays,
  276. PRUint32* frameDispose,
  277. PRUint32* frameBlend,
  278. PRUint32* frameDelay,
  279. PRUint32* offsetX,
  280. PRUint32* offsetY)
  281. {
  282. // Make a copy of aOptions, because strtok() will modify it.
  283. nsCAutoString optionsCopy;
  284. optionsCopy.Assign(NS_ConvertUTF16toUTF8(aOptions));
  285. char* options = optionsCopy.BeginWriting();
  286. while (char* token = nsCRT::strtok(options, ";", &options)) {
  287. // If there's an '=' character, split the token around it.
  288. char* equals = token, *value = nsnull;
  289. while(*equals != '=' && *equals) { ++equals; }
  290. if (*equals == '=') { value = equals + 1; }
  291. if (value) { *equals = '\0'; } // temporary null
  292. // transparency=[yes|no|none]
  293. if (nsCRT::strcmp(token, "transparency") == 0 && useTransparency) {
  294. if (!value) { return NS_ERROR_INVALID_ARG; }
  295. if (nsCRT::strcmp(value, "none") == 0 || nsCRT::strcmp(value, "no") == 0) {
  296. *useTransparency = PR_FALSE;
  297. } else if (nsCRT::strcmp(value, "yes") == 0) {
  298. *useTransparency = PR_TRUE;
  299. } else {
  300. return NS_ERROR_INVALID_ARG;
  301. }
  302. // skipfirstframe=[yes|no]
  303. } else if (nsCRT::strcmp(token, "skipfirstframe") == 0 && skipFirstFrame) {
  304. if (!value) { return NS_ERROR_INVALID_ARG; }
  305. if (nsCRT::strcmp(value, "no") == 0) {
  306. *skipFirstFrame = PR_FALSE;
  307. } else if (nsCRT::strcmp(value, "yes") == 0) {
  308. *skipFirstFrame = PR_TRUE;
  309. } else {
  310. return NS_ERROR_INVALID_ARG;
  311. }
  312. // frames=#
  313. } else if (nsCRT::strcmp(token, "frames") == 0 && numFrames) {
  314. if (!value) { return NS_ERROR_INVALID_ARG; }
  315. if (PR_sscanf(value, "%u", numFrames) != 1) { return NS_ERROR_INVALID_ARG; }
  316. // frames=0 is nonsense.
  317. if (*numFrames == 0) { return NS_ERROR_INVALID_ARG; }
  318. // plays=#
  319. } else if (nsCRT::strcmp(token, "plays") == 0 && numPlays) {
  320. if (!value) { return NS_ERROR_INVALID_ARG; }
  321. // plays=0 to loop forever, otherwise play sequence specified number of times
  322. if (PR_sscanf(value, "%u", numPlays) != 1) { return NS_ERROR_INVALID_ARG; }
  323. // dispose=[none|background|previous]
  324. } else if (nsCRT::strcmp(token, "dispose") == 0 && frameDispose) {
  325. if (!value) { return NS_ERROR_INVALID_ARG; }
  326. if (nsCRT::strcmp(value, "none") == 0) {
  327. *frameDispose = PNG_DISPOSE_OP_NONE;
  328. } else if (nsCRT::strcmp(value, "background") == 0) {
  329. *frameDispose = PNG_DISPOSE_OP_BACKGROUND;
  330. } else if (nsCRT::strcmp(value, "previous") == 0) {
  331. *frameDispose = PNG_DISPOSE_OP_PREVIOUS;
  332. } else {
  333. return NS_ERROR_INVALID_ARG;
  334. }
  335. // blend=[source|over]
  336. } else if (nsCRT::strcmp(token, "blend") == 0 && frameBlend) {
  337. if (!value) { return NS_ERROR_INVALID_ARG; }
  338. if (nsCRT::strcmp(value, "source") == 0) {
  339. *frameBlend = PNG_BLEND_OP_SOURCE;
  340. } else if (nsCRT::strcmp(value, "over") == 0) {
  341. *frameBlend = PNG_BLEND_OP_OVER;
  342. } else {
  343. return NS_ERROR_INVALID_ARG;
  344. }
  345. // delay=# (in ms)
  346. } else if (nsCRT::strcmp(token, "delay") == 0 && frameDelay) {
  347. if (!value) { return NS_ERROR_INVALID_ARG; }
  348. if (PR_sscanf(value, "%u", frameDelay) != 1) { return NS_ERROR_INVALID_ARG; }
  349. // xoffset=#
  350. } else if (nsCRT::strcmp(token, "xoffset") == 0 && offsetX) {
  351. if (!value) { return NS_ERROR_INVALID_ARG; }
  352. if (PR_sscanf(value, "%u", offsetX) != 1) { return NS_ERROR_INVALID_ARG; }
  353. // yoffset=#
  354. } else if (nsCRT::strcmp(token, "yoffset") == 0 && offsetY) {
  355. if (!value) { return NS_ERROR_INVALID_ARG; }
  356. if (PR_sscanf(value, "%u", offsetY) != 1) { return NS_ERROR_INVALID_ARG; }
  357. // unknown token name
  358. } else {
  359. return NS_ERROR_INVALID_ARG;
  360. }
  361. if (value) { *equals = '='; } // restore '=' so strtok doesn't get lost
  362. }
  363. return NS_OK;
  364. }
  365. /* void close (); */
  366. NS_IMETHODIMP nsPNGEncoder::Close()
  367. {
  368. if (mImageBuffer != nsnull) {
  369. PR_Free(mImageBuffer);
  370. mImageBuffer = nsnull;
  371. mImageBufferSize = 0;
  372. mImageBufferUsed = 0;
  373. mImageBufferReadPoint = 0;
  374. }
  375. return NS_OK;
  376. }
  377. /* unsigned long available (); */
  378. NS_IMETHODIMP nsPNGEncoder::Available(PRUint32 *_retval)
  379. {
  380. if (!mImageBuffer)
  381. return NS_BASE_STREAM_CLOSED;
  382. *_retval = mImageBufferUsed - mImageBufferReadPoint;
  383. return NS_OK;
  384. }
  385. /* [noscript] unsigned long read (in charPtr aBuf, in unsigned long aCount); */
  386. NS_IMETHODIMP nsPNGEncoder::Read(char * aBuf, PRUint32 aCount,
  387. PRUint32 *_retval)
  388. {
  389. return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
  390. }
  391. /* [noscript] unsigned long readSegments (in nsWriteSegmentFun aWriter, in voidPtr aClosure, in unsigned long aCount); */
  392. NS_IMETHODIMP nsPNGEncoder::ReadSegments(nsWriteSegmentFun aWriter,
  393. void *aClosure, PRUint32 aCount,
  394. PRUint32 *_retval)
  395. {
  396. PRUint32 maxCount = mImageBufferUsed - mImageBufferReadPoint;
  397. if (maxCount == 0) {
  398. *_retval = 0;
  399. return NS_OK;
  400. }
  401. if (aCount > maxCount)
  402. aCount = maxCount;
  403. nsresult rv = aWriter(this, aClosure,
  404. reinterpret_cast<const char*>(mImageBuffer+mImageBufferReadPoint),
  405. 0, aCount, _retval);
  406. if (NS_SUCCEEDED(rv)) {
  407. NS_ASSERTION(*_retval <= aCount, "bad write count");
  408. mImageBufferReadPoint += *_retval;
  409. }
  410. // errors returned from the writer end here!
  411. return NS_OK;
  412. }
  413. /* boolean isNonBlocking (); */
  414. NS_IMETHODIMP nsPNGEncoder::IsNonBlocking(PRBool *_retval)
  415. {
  416. *_retval = PR_FALSE; // We don't implement nsIAsyncInputStream
  417. return NS_OK;
  418. }
  419. // nsPNGEncoder::ConvertHostARGBRow
  420. //
  421. // Our colors are stored with premultiplied alphas, but PNGs use
  422. // post-multiplied alpha. This swaps to PNG-style alpha.
  423. //
  424. // Copied from gfx/cairo/cairo/src/cairo-png.c
  425. void
  426. nsPNGEncoder::ConvertHostARGBRow(const PRUint8* aSrc, PRUint8* aDest,
  427. PRUint32 aPixelWidth, PRBool aUseTransparency)
  428. {
  429. PRUint32 pixelStride = aUseTransparency ? 4 : 3;
  430. for (PRUint32 x = 0; x < aPixelWidth; x ++) {
  431. const PRUint32& pixelIn = ((const PRUint32*)(aSrc))[x];
  432. PRUint8 *pixelOut = &aDest[x * pixelStride];
  433. PRUint8 alpha = (pixelIn & 0xff000000) >> 24;
  434. if (alpha == 0) {
  435. pixelOut[0] = pixelOut[1] = pixelOut[2] = pixelOut[3] = 0;
  436. } else {
  437. pixelOut[0] = (((pixelIn & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
  438. pixelOut[1] = (((pixelIn & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
  439. pixelOut[2] = (((pixelIn & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
  440. if (aUseTransparency)
  441. pixelOut[3] = alpha;
  442. }
  443. }
  444. }
  445. // nsPNGEncoder::StripAlpha
  446. //
  447. // Input is RGBA, output is RGB
  448. void
  449. nsPNGEncoder::StripAlpha(const PRUint8* aSrc, PRUint8* aDest,
  450. PRUint32 aPixelWidth)
  451. {
  452. for (PRUint32 x = 0; x < aPixelWidth; x ++) {
  453. const PRUint8* pixelIn = &aSrc[x * 4];
  454. PRUint8* pixelOut = &aDest[x * 3];
  455. pixelOut[0] = pixelIn[0];
  456. pixelOut[1] = pixelIn[1];
  457. pixelOut[2] = pixelIn[2];
  458. }
  459. }
  460. // nsPNGEncoder::ErrorCallback
  461. void // static
  462. nsPNGEncoder::ErrorCallback(png_structp png_ptr, png_const_charp warning_msg)
  463. {
  464. #ifdef DEBUG
  465. // XXX: these messages are probably useful callers... use nsIConsoleService?
  466. PR_fprintf(PR_STDERR, "PNG Encoder: %s\n", warning_msg);;
  467. #endif
  468. }
  469. // nsPNGEncoder::WriteCallback
  470. void // static
  471. nsPNGEncoder::WriteCallback(png_structp png, png_bytep data, png_size_t size)
  472. {
  473. nsPNGEncoder* that = static_cast<nsPNGEncoder*>(png_get_io_ptr(png));
  474. if (! that->mImageBuffer)
  475. return;
  476. if (that->mImageBufferUsed + size > that->mImageBufferSize) {
  477. // expand buffer, just double each time
  478. that->mImageBufferSize *= 2;
  479. PRUint8* newBuf = (PRUint8*)PR_Realloc(that->mImageBuffer,
  480. that->mImageBufferSize);
  481. if (! newBuf) {
  482. // can't resize, just zero (this will keep us from writing more)
  483. PR_Free(that->mImageBuffer);
  484. that->mImageBufferSize = 0;
  485. that->mImageBufferUsed = 0;
  486. return;
  487. }
  488. that->mImageBuffer = newBuf;
  489. }
  490. memcpy(&that->mImageBuffer[that->mImageBufferUsed], data, size);
  491. that->mImageBufferUsed += size;
  492. }