/image/decoders/nsPNGDecoder.cpp

http://github.com/zpao/v8monkey · C++ · 891 lines · 590 code · 139 blank · 162 comment · 163 complexity · 2bfe382a8670c9403e1cf545275fbc1c MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2. *
  3. * ***** BEGIN LICENSE BLOCK *****
  4. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5. *
  6. * The contents of this file are subject to the Mozilla Public License Version
  7. * 1.1 (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. * http://www.mozilla.org/MPL/
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. *
  16. * The Original Code is mozilla.org code.
  17. *
  18. * The Initial Developer of the Original Code is
  19. * Netscape Communications Corporation.
  20. * Portions created by the Initial Developer are Copyright (C) 2001
  21. * the Initial Developer. All Rights Reserved.
  22. *
  23. * Contributor(s):
  24. * Stuart Parmenter <stuart@mozilla.com>
  25. * Andrew Smith
  26. * Federico Mena-Quintero <federico@novell.com>
  27. * Bobby Holley <bobbyholley@gmail.com>
  28. * Glenn Randers-Pehrson <glennrp@gmail.com>
  29. *
  30. * Alternatively, the contents of this file may be used under the terms of
  31. * either the GNU General Public License Version 2 or later (the "GPL"), or
  32. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  33. * in which case the provisions of the GPL or the LGPL are applicable instead
  34. * of those above. If you wish to allow use of your version of this file only
  35. * under the terms of either the GPL or the LGPL, and not to allow others to
  36. * use your version of this file under the terms of the MPL, indicate your
  37. * decision by deleting the provisions above and replace them with the notice
  38. * and other provisions required by the GPL or the LGPL. If you do not delete
  39. * the provisions above, a recipient may use your version of this file under
  40. * the terms of any one of the MPL, the GPL or the LGPL.
  41. *
  42. * ***** END LICENSE BLOCK ***** */
  43. #include "nsPNGDecoder.h"
  44. #include "ImageLogging.h"
  45. #include "nsMemory.h"
  46. #include "nsRect.h"
  47. #include "nsIInputStream.h"
  48. #include "RasterImage.h"
  49. #include "imgIContainerObserver.h"
  50. #include "gfxColor.h"
  51. #include "nsColor.h"
  52. #include "nspr.h"
  53. #include "png.h"
  54. #include "gfxPlatform.h"
  55. namespace mozilla {
  56. namespace image {
  57. #ifdef PR_LOGGING
  58. static PRLogModuleInfo *gPNGLog = PR_NewLogModule("PNGDecoder");
  59. static PRLogModuleInfo *gPNGDecoderAccountingLog =
  60. PR_NewLogModule("PNGDecoderAccounting");
  61. #endif
  62. /* limit image dimensions (bug #251381) */
  63. #define MOZ_PNG_MAX_DIMENSION 1000000L
  64. // For size decodes
  65. #define WIDTH_OFFSET 16
  66. #define HEIGHT_OFFSET (WIDTH_OFFSET + 4)
  67. #define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4)
  68. // First 8 bytes of a PNG file
  69. const PRUint8
  70. nsPNGDecoder::pngSignatureBytes[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
  71. nsPNGDecoder::nsPNGDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver)
  72. : Decoder(aImage, aObserver),
  73. mPNG(nsnull), mInfo(nsnull),
  74. mCMSLine(nsnull), interlacebuf(nsnull),
  75. mInProfile(nsnull), mTransform(nsnull),
  76. mHeaderBuf(nsnull), mHeaderBytesRead(0),
  77. mChannels(0), mFrameIsHidden(false),
  78. mCMSMode(0), mDisablePremultipliedAlpha(false)
  79. {
  80. }
  81. nsPNGDecoder::~nsPNGDecoder()
  82. {
  83. if (mPNG)
  84. png_destroy_read_struct(&mPNG, mInfo ? &mInfo : NULL, NULL);
  85. if (mCMSLine)
  86. nsMemory::Free(mCMSLine);
  87. if (interlacebuf)
  88. nsMemory::Free(interlacebuf);
  89. if (mInProfile) {
  90. qcms_profile_release(mInProfile);
  91. /* mTransform belongs to us only if mInProfile is non-null */
  92. if (mTransform)
  93. qcms_transform_release(mTransform);
  94. }
  95. if (mHeaderBuf)
  96. nsMemory::Free(mHeaderBuf);
  97. }
  98. // CreateFrame() is used for both simple and animated images
  99. void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
  100. PRInt32 width, PRInt32 height,
  101. gfxASurface::gfxImageFormat format)
  102. {
  103. PRUint32 imageDataLength;
  104. nsresult rv = mImage.EnsureFrame(GetFrameCount(), x_offset, y_offset,
  105. width, height, format,
  106. &mImageData, &imageDataLength);
  107. if (NS_FAILED(rv))
  108. longjmp(png_jmpbuf(mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
  109. mFrameRect.x = x_offset;
  110. mFrameRect.y = y_offset;
  111. mFrameRect.width = width;
  112. mFrameRect.height = height;
  113. #ifdef PNG_APNG_SUPPORTED
  114. if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL))
  115. SetAnimFrameInfo();
  116. #endif
  117. // Tell the superclass we're starting a frame
  118. PostFrameStart();
  119. PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
  120. ("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created "
  121. "image frame with %dx%d pixels in container %p",
  122. width, height,
  123. &mImage));
  124. mFrameHasNoAlpha = true;
  125. }
  126. #ifdef PNG_APNG_SUPPORTED
  127. // set timeout and frame disposal method for the current frame
  128. void nsPNGDecoder::SetAnimFrameInfo()
  129. {
  130. png_uint_16 delay_num, delay_den;
  131. /* delay, in seconds is delay_num/delay_den */
  132. png_byte dispose_op;
  133. png_byte blend_op;
  134. PRInt32 timeout; /* in milliseconds */
  135. delay_num = png_get_next_frame_delay_num(mPNG, mInfo);
  136. delay_den = png_get_next_frame_delay_den(mPNG, mInfo);
  137. dispose_op = png_get_next_frame_dispose_op(mPNG, mInfo);
  138. blend_op = png_get_next_frame_blend_op(mPNG, mInfo);
  139. if (delay_num == 0) {
  140. timeout = 0; // SetFrameTimeout() will set to a minimum
  141. } else {
  142. if (delay_den == 0)
  143. delay_den = 100; // so says the APNG spec
  144. // Need to cast delay_num to float to have a proper division and
  145. // the result to int to avoid compiler warning
  146. timeout = static_cast<PRInt32>
  147. (static_cast<PRFloat64>(delay_num) * 1000 / delay_den);
  148. }
  149. PRUint32 numFrames = mImage.GetNumFrames();
  150. mImage.SetFrameTimeout(numFrames - 1, timeout);
  151. if (dispose_op == PNG_DISPOSE_OP_PREVIOUS)
  152. mImage.SetFrameDisposalMethod(numFrames - 1,
  153. RasterImage::kDisposeRestorePrevious);
  154. else if (dispose_op == PNG_DISPOSE_OP_BACKGROUND)
  155. mImage.SetFrameDisposalMethod(numFrames - 1,
  156. RasterImage::kDisposeClear);
  157. else
  158. mImage.SetFrameDisposalMethod(numFrames - 1,
  159. RasterImage::kDisposeKeep);
  160. if (blend_op == PNG_BLEND_OP_SOURCE)
  161. mImage.SetFrameBlendMethod(numFrames - 1, RasterImage::kBlendSource);
  162. /*else // 'over' is the default
  163. mImage.SetFrameBlendMethod(numFrames - 1, RasterImage::kBlendOver); */
  164. }
  165. #endif
  166. // set timeout and frame disposal method for the current frame
  167. void nsPNGDecoder::EndImageFrame()
  168. {
  169. if (mFrameIsHidden)
  170. return;
  171. PRUint32 numFrames = 1;
  172. #ifdef PNG_APNG_SUPPORTED
  173. numFrames = mImage.GetNumFrames();
  174. // We can't use mPNG->num_frames_read as it may be one ahead.
  175. if (numFrames > 1) {
  176. // Tell the image renderer that the frame is complete
  177. if (mFrameHasNoAlpha)
  178. mImage.SetFrameHasNoAlpha(numFrames - 1);
  179. PostInvalidation(mFrameRect);
  180. }
  181. #endif
  182. PostFrameStop();
  183. }
  184. void
  185. nsPNGDecoder::InitInternal()
  186. {
  187. mCMSMode = gfxPlatform::GetCMSMode();
  188. if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0)
  189. mCMSMode = eCMSMode_Off;
  190. mDisablePremultipliedAlpha = (mDecodeFlags & DECODER_NO_PREMULTIPLY_ALPHA) != 0;
  191. #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
  192. static png_byte color_chunks[]=
  193. { 99, 72, 82, 77, '\0', /* cHRM */
  194. 105, 67, 67, 80, '\0'}; /* iCCP */
  195. static png_byte unused_chunks[]=
  196. { 98, 75, 71, 68, '\0', /* bKGD */
  197. 104, 73, 83, 84, '\0', /* hIST */
  198. 105, 84, 88, 116, '\0', /* iTXt */
  199. 111, 70, 70, 115, '\0', /* oFFs */
  200. 112, 67, 65, 76, '\0', /* pCAL */
  201. 115, 67, 65, 76, '\0', /* sCAL */
  202. 112, 72, 89, 115, '\0', /* pHYs */
  203. 115, 66, 73, 84, '\0', /* sBIT */
  204. 115, 80, 76, 84, '\0', /* sPLT */
  205. 116, 69, 88, 116, '\0', /* tEXt */
  206. 116, 73, 77, 69, '\0', /* tIME */
  207. 122, 84, 88, 116, '\0'}; /* zTXt */
  208. #endif
  209. // For size decodes, we only need a small buffer
  210. if (IsSizeDecode()) {
  211. mHeaderBuf = (PRUint8 *)moz_xmalloc(BYTES_NEEDED_FOR_DIMENSIONS);
  212. return;
  213. }
  214. /* For full decodes, do png init stuff */
  215. /* Initialize the container's source image header. */
  216. /* Always decode to 24 bit pixdepth */
  217. mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING,
  218. NULL, nsPNGDecoder::error_callback,
  219. nsPNGDecoder::warning_callback);
  220. if (!mPNG) {
  221. PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
  222. return;
  223. }
  224. mInfo = png_create_info_struct(mPNG);
  225. if (!mInfo) {
  226. PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
  227. png_destroy_read_struct(&mPNG, NULL, NULL);
  228. return;
  229. }
  230. #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
  231. /* Ignore unused chunks */
  232. if (mCMSMode == eCMSMode_Off)
  233. png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2);
  234. png_set_keep_unknown_chunks(mPNG, 1, unused_chunks,
  235. (int)sizeof(unused_chunks)/5);
  236. #endif
  237. #ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
  238. if (mCMSMode != eCMSMode_Off)
  239. png_set_chunk_malloc_max(mPNG, 4000000L);
  240. #endif
  241. /* use this as libpng "progressive pointer" (retrieve in callbacks) */
  242. png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this),
  243. nsPNGDecoder::info_callback,
  244. nsPNGDecoder::row_callback,
  245. nsPNGDecoder::end_callback);
  246. }
  247. void
  248. nsPNGDecoder::WriteInternal(const char *aBuffer, PRUint32 aCount)
  249. {
  250. // We use gotos, so we need to declare variables here
  251. PRUint32 width = 0;
  252. PRUint32 height = 0;
  253. NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
  254. // If we only want width/height, we don't need to go through libpng
  255. if (IsSizeDecode()) {
  256. // Are we done?
  257. if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS)
  258. return;
  259. // Read data into our header buffer
  260. PRUint32 bytesToRead = NS_MIN(aCount, BYTES_NEEDED_FOR_DIMENSIONS -
  261. mHeaderBytesRead);
  262. memcpy(mHeaderBuf + mHeaderBytesRead, aBuffer, bytesToRead);
  263. mHeaderBytesRead += bytesToRead;
  264. // If we're done now, verify the data and set up the container
  265. if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS) {
  266. // Check that the signature bytes are right
  267. if (memcmp(mHeaderBuf, nsPNGDecoder::pngSignatureBytes,
  268. sizeof(pngSignatureBytes))) {
  269. PostDataError();
  270. return;
  271. }
  272. // Grab the width and height, accounting for endianness (thanks libpng!)
  273. width = png_get_uint_32(mHeaderBuf + WIDTH_OFFSET);
  274. height = png_get_uint_32(mHeaderBuf + HEIGHT_OFFSET);
  275. // Too big?
  276. if ((width > MOZ_PNG_MAX_DIMENSION) || (height > MOZ_PNG_MAX_DIMENSION)) {
  277. PostDataError();
  278. return;
  279. }
  280. // Post our size to the superclass
  281. PostSize(width, height);
  282. }
  283. }
  284. // Otherwise, we're doing a standard decode
  285. else {
  286. // libpng uses setjmp/longjmp for error handling - set the buffer
  287. if (setjmp(png_jmpbuf(mPNG))) {
  288. // We might not really know what caused the error, but it makes more
  289. // sense to blame the data.
  290. if (!HasError())
  291. PostDataError();
  292. png_destroy_read_struct(&mPNG, &mInfo, NULL);
  293. return;
  294. }
  295. // Pass the data off to libpng
  296. png_process_data(mPNG, mInfo, (unsigned char *)aBuffer, aCount);
  297. }
  298. }
  299. // Sets up gamma pre-correction in libpng before our callback gets called.
  300. // We need to do this if we don't end up with a CMS profile.
  301. static void
  302. PNGDoGammaCorrection(png_structp png_ptr, png_infop info_ptr)
  303. {
  304. double aGamma;
  305. if (png_get_gAMA(png_ptr, info_ptr, &aGamma)) {
  306. if ((aGamma <= 0.0) || (aGamma > 21474.83)) {
  307. aGamma = 0.45455;
  308. png_set_gAMA(png_ptr, info_ptr, aGamma);
  309. }
  310. png_set_gamma(png_ptr, 2.2, aGamma);
  311. }
  312. else
  313. png_set_gamma(png_ptr, 2.2, 0.45455);
  314. }
  315. // Adapted from http://www.littlecms.com/pngchrm.c example code
  316. static qcms_profile *
  317. PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr,
  318. int color_type, qcms_data_type *inType, PRUint32 *intent)
  319. {
  320. qcms_profile *profile = nsnull;
  321. *intent = QCMS_INTENT_PERCEPTUAL; // Our default
  322. // First try to see if iCCP chunk is present
  323. if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
  324. png_uint_32 profileLen;
  325. #if (PNG_LIBPNG_VER < 10500)
  326. char *profileData, *profileName;
  327. #else
  328. png_bytep profileData;
  329. png_charp profileName;
  330. #endif
  331. int compression;
  332. png_get_iCCP(png_ptr, info_ptr, &profileName, &compression,
  333. &profileData, &profileLen);
  334. profile = qcms_profile_from_memory(
  335. #if (PNG_LIBPNG_VER < 10500)
  336. profileData,
  337. #else
  338. (char *)profileData,
  339. #endif
  340. profileLen);
  341. if (profile) {
  342. PRUint32 profileSpace = qcms_profile_get_color_space(profile);
  343. bool mismatch = false;
  344. if (color_type & PNG_COLOR_MASK_COLOR) {
  345. if (profileSpace != icSigRgbData)
  346. mismatch = true;
  347. } else {
  348. if (profileSpace == icSigRgbData)
  349. png_set_gray_to_rgb(png_ptr);
  350. else if (profileSpace != icSigGrayData)
  351. mismatch = true;
  352. }
  353. if (mismatch) {
  354. qcms_profile_release(profile);
  355. profile = nsnull;
  356. } else {
  357. *intent = qcms_profile_get_rendering_intent(profile);
  358. }
  359. }
  360. }
  361. // Check sRGB chunk
  362. if (!profile && png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
  363. profile = qcms_profile_sRGB();
  364. if (profile) {
  365. int fileIntent;
  366. png_set_gray_to_rgb(png_ptr);
  367. png_get_sRGB(png_ptr, info_ptr, &fileIntent);
  368. PRUint32 map[] = { QCMS_INTENT_PERCEPTUAL,
  369. QCMS_INTENT_RELATIVE_COLORIMETRIC,
  370. QCMS_INTENT_SATURATION,
  371. QCMS_INTENT_ABSOLUTE_COLORIMETRIC };
  372. *intent = map[fileIntent];
  373. }
  374. }
  375. // Check gAMA/cHRM chunks
  376. if (!profile &&
  377. png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) &&
  378. png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) {
  379. qcms_CIE_xyYTRIPLE primaries;
  380. qcms_CIE_xyY whitePoint;
  381. png_get_cHRM(png_ptr, info_ptr,
  382. &whitePoint.x, &whitePoint.y,
  383. &primaries.red.x, &primaries.red.y,
  384. &primaries.green.x, &primaries.green.y,
  385. &primaries.blue.x, &primaries.blue.y);
  386. whitePoint.Y =
  387. primaries.red.Y = primaries.green.Y = primaries.blue.Y = 1.0;
  388. double gammaOfFile;
  389. png_get_gAMA(png_ptr, info_ptr, &gammaOfFile);
  390. profile = qcms_profile_create_rgb_with_gamma(whitePoint, primaries,
  391. 1.0/gammaOfFile);
  392. if (profile)
  393. png_set_gray_to_rgb(png_ptr);
  394. }
  395. if (profile) {
  396. PRUint32 profileSpace = qcms_profile_get_color_space(profile);
  397. if (profileSpace == icSigGrayData) {
  398. if (color_type & PNG_COLOR_MASK_ALPHA)
  399. *inType = QCMS_DATA_GRAYA_8;
  400. else
  401. *inType = QCMS_DATA_GRAY_8;
  402. } else {
  403. if (color_type & PNG_COLOR_MASK_ALPHA ||
  404. png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
  405. *inType = QCMS_DATA_RGBA_8;
  406. else
  407. *inType = QCMS_DATA_RGB_8;
  408. }
  409. }
  410. return profile;
  411. }
  412. void
  413. nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
  414. {
  415. /* int number_passes; NOT USED */
  416. png_uint_32 width, height;
  417. int bit_depth, color_type, interlace_type, compression_type, filter_type;
  418. unsigned int channels;
  419. png_bytep trans = NULL;
  420. int num_trans = 0;
  421. nsPNGDecoder *decoder =
  422. static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
  423. /* always decode to 24-bit RGB or 32-bit RGBA */
  424. png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
  425. &interlace_type, &compression_type, &filter_type);
  426. /* Are we too big? */
  427. if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION)
  428. longjmp(png_jmpbuf(decoder->mPNG), 1);
  429. // Post our size to the superclass
  430. decoder->PostSize(width, height);
  431. if (decoder->HasError()) {
  432. // Setting the size lead to an error; this can happen when for example
  433. // a multipart channel sends an image of a different size.
  434. longjmp(png_jmpbuf(decoder->mPNG), 1);
  435. }
  436. if (color_type == PNG_COLOR_TYPE_PALETTE)
  437. png_set_expand(png_ptr);
  438. if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
  439. png_set_expand(png_ptr);
  440. if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
  441. int sample_max = (1 << bit_depth);
  442. png_color_16p trans_values;
  443. png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
  444. /* libpng doesn't reject a tRNS chunk with out-of-range samples
  445. so we check it here to avoid setting up a useless opacity
  446. channel or producing unexpected transparent pixels when using
  447. libpng-1.2.19 through 1.2.26 (bug #428045) */
  448. if ((color_type == PNG_COLOR_TYPE_GRAY &&
  449. (int)trans_values->gray > sample_max) ||
  450. (color_type == PNG_COLOR_TYPE_RGB &&
  451. ((int)trans_values->red > sample_max ||
  452. (int)trans_values->green > sample_max ||
  453. (int)trans_values->blue > sample_max)))
  454. {
  455. /* clear the tRNS valid flag and release tRNS memory */
  456. png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
  457. }
  458. else
  459. png_set_expand(png_ptr);
  460. }
  461. if (bit_depth == 16)
  462. png_set_strip_16(png_ptr);
  463. qcms_data_type inType;
  464. PRUint32 intent = -1;
  465. PRUint32 pIntent;
  466. if (decoder->mCMSMode != eCMSMode_Off) {
  467. intent = gfxPlatform::GetRenderingIntent();
  468. decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr,
  469. color_type, &inType, &pIntent);
  470. /* If we're not mandating an intent, use the one from the image. */
  471. if (intent == PRUint32(-1))
  472. intent = pIntent;
  473. }
  474. if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) {
  475. qcms_data_type outType;
  476. if (color_type & PNG_COLOR_MASK_ALPHA || num_trans)
  477. outType = QCMS_DATA_RGBA_8;
  478. else
  479. outType = QCMS_DATA_RGB_8;
  480. decoder->mTransform = qcms_transform_create(decoder->mInProfile,
  481. inType,
  482. gfxPlatform::GetCMSOutputProfile(),
  483. outType,
  484. (qcms_intent)intent);
  485. } else {
  486. png_set_gray_to_rgb(png_ptr);
  487. // only do gamma correction if CMS isn't entirely disabled
  488. if (decoder->mCMSMode != eCMSMode_Off)
  489. PNGDoGammaCorrection(png_ptr, info_ptr);
  490. if (decoder->mCMSMode == eCMSMode_All) {
  491. if (color_type & PNG_COLOR_MASK_ALPHA || num_trans)
  492. decoder->mTransform = gfxPlatform::GetCMSRGBATransform();
  493. else
  494. decoder->mTransform = gfxPlatform::GetCMSRGBTransform();
  495. }
  496. }
  497. /* let libpng expand interlaced images */
  498. if (interlace_type == PNG_INTERLACE_ADAM7) {
  499. /* number_passes = */
  500. png_set_interlace_handling(png_ptr);
  501. }
  502. /* now all of those things we set above are used to update various struct
  503. * members and whatnot, after which we can get channels, rowbytes, etc. */
  504. png_read_update_info(png_ptr, info_ptr);
  505. decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr);
  506. /*---------------------------------------------------------------*/
  507. /* copy PNG info into imagelib structs (formerly png_set_dims()) */
  508. /*---------------------------------------------------------------*/
  509. PRInt32 alpha_bits = 1;
  510. if (channels == 2 || channels == 4) {
  511. /* check if alpha is coming from a tRNS chunk and is binary */
  512. if (num_trans) {
  513. /* if it's not an indexed color image, tRNS means binary */
  514. if (color_type == PNG_COLOR_TYPE_PALETTE) {
  515. for (int i=0; i<num_trans; i++) {
  516. if ((trans[i] != 0) && (trans[i] != 255)) {
  517. alpha_bits = 8;
  518. break;
  519. }
  520. }
  521. }
  522. } else {
  523. alpha_bits = 8;
  524. }
  525. }
  526. if (channels == 1 || channels == 3)
  527. decoder->format = gfxASurface::ImageFormatRGB24;
  528. else if (channels == 2 || channels == 4)
  529. decoder->format = gfxASurface::ImageFormatARGB32;
  530. #ifdef PNG_APNG_SUPPORTED
  531. if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL))
  532. png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback, NULL);
  533. if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) {
  534. decoder->mFrameIsHidden = true;
  535. } else {
  536. #endif
  537. decoder->CreateFrame(0, 0, width, height, decoder->format);
  538. #ifdef PNG_APNG_SUPPORTED
  539. }
  540. #endif
  541. if (decoder->mTransform &&
  542. (channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) {
  543. PRUint32 bpp[] = { 0, 3, 4, 3, 4 };
  544. decoder->mCMSLine =
  545. (PRUint8 *)moz_malloc(bpp[channels] * width);
  546. if (!decoder->mCMSLine) {
  547. longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
  548. }
  549. }
  550. if (interlace_type == PNG_INTERLACE_ADAM7) {
  551. if (height < PR_INT32_MAX / (width * channels))
  552. decoder->interlacebuf = (PRUint8 *)moz_malloc(channels * width * height);
  553. if (!decoder->interlacebuf) {
  554. longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
  555. }
  556. }
  557. /* Reject any ancillary chunk after IDAT with a bad CRC (bug #397593).
  558. * It would be better to show the default frame (if one has already been
  559. * successfully decoded) before bailing, but it's simpler to just bail
  560. * out with an error message.
  561. */
  562. png_set_crc_action(png_ptr, PNG_CRC_NO_CHANGE, PNG_CRC_ERROR_QUIT);
  563. return;
  564. }
  565. void
  566. nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
  567. png_uint_32 row_num, int pass)
  568. {
  569. /* libpng comments:
  570. *
  571. * this function is called for every row in the image. If the
  572. * image is interlacing, and you turned on the interlace handler,
  573. * this function will be called for every row in every pass.
  574. * Some of these rows will not be changed from the previous pass.
  575. * When the row is not changed, the new_row variable will be NULL.
  576. * The rows and passes are called in order, so you don't really
  577. * need the row_num and pass, but I'm supplying them because it
  578. * may make your life easier.
  579. *
  580. * For the non-NULL rows of interlaced images, you must call
  581. * png_progressive_combine_row() passing in the row and the
  582. * old row. You can call this function for NULL rows (it will
  583. * just return) and for non-interlaced images (it just does the
  584. * memcpy for you) if it will make the code easier. Thus, you
  585. * can just do this for all cases:
  586. *
  587. * png_progressive_combine_row(png_ptr, old_row, new_row);
  588. *
  589. * where old_row is what was displayed for previous rows. Note
  590. * that the first pass (pass == 0 really) will completely cover
  591. * the old row, so the rows do not have to be initialized. After
  592. * the first pass (and only for interlaced images), you will have
  593. * to pass the current row, and the function will combine the
  594. * old row and the new row.
  595. */
  596. nsPNGDecoder *decoder =
  597. static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
  598. // skip this frame
  599. if (decoder->mFrameIsHidden)
  600. return;
  601. if (row_num >= (png_uint_32) decoder->mFrameRect.height)
  602. return;
  603. if (new_row) {
  604. PRInt32 width = decoder->mFrameRect.width;
  605. PRUint32 iwidth = decoder->mFrameRect.width;
  606. png_bytep line = new_row;
  607. if (decoder->interlacebuf) {
  608. line = decoder->interlacebuf + (row_num * decoder->mChannels * width);
  609. png_progressive_combine_row(png_ptr, line, new_row);
  610. }
  611. PRUint32 bpr = width * sizeof(PRUint32);
  612. PRUint32 *cptr32 = (PRUint32*)(decoder->mImageData + (row_num*bpr));
  613. bool rowHasNoAlpha = true;
  614. if (decoder->mTransform) {
  615. if (decoder->mCMSLine) {
  616. qcms_transform_data(decoder->mTransform, line, decoder->mCMSLine,
  617. iwidth);
  618. /* copy alpha over */
  619. PRUint32 channels = decoder->mChannels;
  620. if (channels == 2 || channels == 4) {
  621. for (PRUint32 i = 0; i < iwidth; i++)
  622. decoder->mCMSLine[4 * i + 3] = line[channels * i + channels - 1];
  623. }
  624. line = decoder->mCMSLine;
  625. } else {
  626. qcms_transform_data(decoder->mTransform, line, line, iwidth);
  627. }
  628. }
  629. switch (decoder->format) {
  630. case gfxASurface::ImageFormatRGB24:
  631. {
  632. // counter for while() loops below
  633. PRUint32 idx = iwidth;
  634. // copy as bytes until source pointer is 32-bit-aligned
  635. for (; (NS_PTR_TO_UINT32(line) & 0x3) && idx; --idx) {
  636. *cptr32++ = GFX_PACKED_PIXEL(0xFF, line[0], line[1], line[2]);
  637. line += 3;
  638. }
  639. // copy pixels in blocks of 4
  640. while (idx >= 4) {
  641. GFX_BLOCK_RGB_TO_FRGB(line, cptr32);
  642. idx -= 4;
  643. line += 12;
  644. cptr32 += 4;
  645. }
  646. // copy remaining pixel(s)
  647. while (idx--) {
  648. // 32-bit read of final pixel will exceed buffer, so read bytes
  649. *cptr32++ = GFX_PACKED_PIXEL(0xFF, line[0], line[1], line[2]);
  650. line += 3;
  651. }
  652. }
  653. break;
  654. case gfxASurface::ImageFormatARGB32:
  655. {
  656. if (!decoder->mDisablePremultipliedAlpha) {
  657. for (PRUint32 x=width; x>0; --x) {
  658. *cptr32++ = GFX_PACKED_PIXEL(line[3], line[0], line[1], line[2]);
  659. if (line[3] != 0xff)
  660. rowHasNoAlpha = false;
  661. line += 4;
  662. }
  663. } else {
  664. for (PRUint32 x=width; x>0; --x) {
  665. *cptr32++ = GFX_PACKED_PIXEL_NO_PREMULTIPLY(line[3], line[0], line[1], line[2]);
  666. if (line[3] != 0xff)
  667. rowHasNoAlpha = false;
  668. line += 4;
  669. }
  670. }
  671. }
  672. break;
  673. default:
  674. longjmp(png_jmpbuf(decoder->mPNG), 1);
  675. }
  676. if (!rowHasNoAlpha)
  677. decoder->mFrameHasNoAlpha = false;
  678. PRUint32 numFrames = decoder->mImage.GetNumFrames();
  679. if (numFrames <= 1) {
  680. // Only do incremental image display for the first frame
  681. // XXXbholley - this check should be handled in the superclass
  682. nsIntRect r(0, row_num, width, 1);
  683. decoder->PostInvalidation(r);
  684. }
  685. }
  686. }
  687. // got the header of a new frame that's coming
  688. void
  689. nsPNGDecoder::frame_info_callback(png_structp png_ptr, png_uint_32 frame_num)
  690. {
  691. #ifdef PNG_APNG_SUPPORTED
  692. png_uint_32 x_offset, y_offset;
  693. PRInt32 width, height;
  694. nsPNGDecoder *decoder =
  695. static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
  696. // old frame is done
  697. decoder->EndImageFrame();
  698. // Only the first frame can be hidden, so unhide unconditionally here.
  699. decoder->mFrameIsHidden = false;
  700. x_offset = png_get_next_frame_x_offset(png_ptr, decoder->mInfo);
  701. y_offset = png_get_next_frame_y_offset(png_ptr, decoder->mInfo);
  702. width = png_get_next_frame_width(png_ptr, decoder->mInfo);
  703. height = png_get_next_frame_height(png_ptr, decoder->mInfo);
  704. decoder->CreateFrame(x_offset, y_offset, width, height, decoder->format);
  705. #endif
  706. }
  707. void
  708. nsPNGDecoder::end_callback(png_structp png_ptr, png_infop info_ptr)
  709. {
  710. /* libpng comments:
  711. *
  712. * this function is called when the whole image has been read,
  713. * including any chunks after the image (up to and including
  714. * the IEND). You will usually have the same info chunk as you
  715. * had in the header, although some data may have been added
  716. * to the comments and time fields.
  717. *
  718. * Most people won't do much here, perhaps setting a flag that
  719. * marks the image as finished.
  720. */
  721. nsPNGDecoder *decoder =
  722. static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
  723. // We shouldn't get here if we've hit an error
  724. NS_ABORT_IF_FALSE(!decoder->HasError(), "Finishing up PNG but hit error!");
  725. #ifdef PNG_APNG_SUPPORTED
  726. if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
  727. PRInt32 num_plays = png_get_num_plays(png_ptr, info_ptr);
  728. decoder->mImage.SetLoopCount(num_plays - 1);
  729. }
  730. #endif
  731. // Send final notifications
  732. decoder->EndImageFrame();
  733. decoder->PostDecodeDone();
  734. }
  735. void
  736. nsPNGDecoder::error_callback(png_structp png_ptr, png_const_charp error_msg)
  737. {
  738. PR_LOG(gPNGLog, PR_LOG_ERROR, ("libpng error: %s\n", error_msg));
  739. longjmp(png_jmpbuf(png_ptr), 1);
  740. }
  741. void
  742. nsPNGDecoder::warning_callback(png_structp png_ptr, png_const_charp warning_msg)
  743. {
  744. PR_LOG(gPNGLog, PR_LOG_WARNING, ("libpng warning: %s\n", warning_msg));
  745. }
  746. Telemetry::ID
  747. nsPNGDecoder::SpeedHistogram()
  748. {
  749. return Telemetry::IMAGE_DECODE_SPEED_PNG;
  750. }
  751. } // namespace image
  752. } // namespace mozilla