PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/image/encoders/png/nsPNGEncoder.cpp

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