Index: third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
index a2ebf6cb967b33593313ca267d227800dbbf3fbe..6deba9ce22adfd250319cac12dada224f706df3d 100644 |
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
@@ -54,58 +54,58 @@ PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, |
PNGImageDecoder::~PNGImageDecoder() {} |
-inline float pngFixedToFloat(png_fixed_point x) { |
- return ((float)x) * 0.00001f; |
-} |
- |
inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) { |
- if (png_get_valid(png, info, PNG_INFO_sRGB)) { |
+ if (png_get_valid(png, info, PNG_INFO_sRGB)) |
return SkColorSpace::MakeSRGB(); |
- } |
- png_charp name = nullptr; |
- int compression = 0; |
- png_bytep profile = nullptr; |
- png_uint_32 length = 0; |
- if (png_get_iCCP(png, info, &name, &compression, &profile, &length)) { |
+ png_charp name; |
+ int compression; |
+ png_bytep profile; |
+ png_uint_32 length; |
+ if (png_get_iCCP(png, info, &name, &compression, &profile, &length)) |
return SkColorSpace::MakeICC(profile, length); |
- } |
png_fixed_point chrm[8]; |
- if (png_get_cHRM_fixed(png, info, &chrm[0], &chrm[1], &chrm[2], &chrm[3], |
- &chrm[4], &chrm[5], &chrm[6], &chrm[7])) { |
- SkColorSpacePrimaries primaries; |
- primaries.fRX = pngFixedToFloat(chrm[2]); |
- primaries.fRY = pngFixedToFloat(chrm[3]); |
- primaries.fGX = pngFixedToFloat(chrm[4]); |
- primaries.fGY = pngFixedToFloat(chrm[5]); |
- primaries.fBX = pngFixedToFloat(chrm[6]); |
- primaries.fBY = pngFixedToFloat(chrm[7]); |
- primaries.fWX = pngFixedToFloat(chrm[0]); |
- primaries.fWY = pngFixedToFloat(chrm[1]); |
- |
- SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); |
- if (primaries.toXYZD50(&toXYZD50)) { |
- png_fixed_point gammaFixed; |
- if (PNG_INFO_gAMA == png_get_gAMA_fixed(png, info, &gammaFixed)) { |
- SkColorSpaceTransferFn fn; |
- fn.fA = 1.0f; |
- fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f; |
- // This is necessary because the gAMA chunk actually stores 1/gamma. |
- fn.fG = 1.0f / pngFixedToFloat(gammaFixed); |
- return SkColorSpace::MakeRGB(fn, toXYZD50); |
- } |
- |
- // Note that we only use the cHRM tag when gAMA is present. The |
- // specification states that the cHRM is valid even without a gAMA |
- // tag, but we cannot apply the cHRM without guessing a transfer |
- // function. It's possible that we should guess sRGB transfer |
- // function, given that unmarked PNGs should be treated as sRGB. |
- // However, the current behavior matches Safari and Firefox. |
- } |
- } |
- |
- return nullptr; |
+ if (!png_get_cHRM_fixed(png, info, &chrm[0], &chrm[1], &chrm[2], &chrm[3], |
+ &chrm[4], &chrm[5], &chrm[6], &chrm[7])) |
+ return nullptr; |
+ |
+ png_fixed_point inverseGamma; |
+ if (!png_get_gAMA_fixed(png, info, &inverseGamma)) |
+ return nullptr; |
+ |
+ // cHRM and gAMA tags are both present. The PNG spec states that cHRM is |
+ // valid even without gAMA but we cannot apply the cHRM without guessing |
+ // a gAMA. Color correction is not a guessing game: match the behavior |
+ // of Safari and Firefox instead (compat). |
+ |
+ struct pngFixedToFloat { |
+ explicit pngFixedToFloat(png_fixed_point value) |
+ : floatValue(.00001f * value) {} |
+ operator float() { return floatValue; } |
+ float floatValue; |
+ }; |
+ |
+ SkColorSpacePrimaries primaries; |
+ primaries.fRX = pngFixedToFloat(chrm[2]); |
+ primaries.fRY = pngFixedToFloat(chrm[3]); |
+ primaries.fGX = pngFixedToFloat(chrm[4]); |
+ primaries.fGY = pngFixedToFloat(chrm[5]); |
+ primaries.fBX = pngFixedToFloat(chrm[6]); |
+ primaries.fBY = pngFixedToFloat(chrm[7]); |
+ primaries.fWX = pngFixedToFloat(chrm[0]); |
+ primaries.fWY = pngFixedToFloat(chrm[1]); |
+ |
+ SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); |
+ if (!primaries.toXYZD50(&toXYZD50)) |
+ return nullptr; |
+ |
+ SkColorSpaceTransferFn fn; |
+ fn.fG = 1.0f / pngFixedToFloat(inverseGamma); |
+ fn.fA = 1.0f; |
+ fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f; |
+ |
+ return SkColorSpace::MakeRGB(fn, toXYZD50); |
} |
void PNGImageDecoder::headerAvailable() { |
@@ -127,9 +127,9 @@ void PNGImageDecoder::headerAvailable() { |
return; |
} |
- int bitDepth, colorType, interlaceType, compressionType, filterType, channels; |
+ int bitDepth, colorType, interlaceType, compressionType; |
png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, |
- &interlaceType, &compressionType, &filterType); |
+ &interlaceType, &compressionType, nullptr); |
// The options we set here match what Mozilla does. |
@@ -138,12 +138,8 @@ void PNGImageDecoder::headerAvailable() { |
(colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) |
png_set_expand(png); |
- png_bytep trns = 0; |
- int trnsCount = 0; |
- if (png_get_valid(png, info, PNG_INFO_tRNS)) { |
- png_get_tRNS(png, info, &trns, &trnsCount, 0); |
+ if (png_get_valid(png, info, PNG_INFO_tRNS)) |
png_set_expand(png); |
- } |
if (bitDepth == 16) |
png_set_strip_16(png); |
@@ -154,27 +150,12 @@ void PNGImageDecoder::headerAvailable() { |
if ((colorType & PNG_COLOR_MASK_COLOR) && !ignoresColorSpace()) { |
// We only support color profiles for color PALETTE and RGB[A] PNG. |
- // Supporting color profiles for gray-scale images is slightly tricky, at |
- // least using the CoreGraphics ICC library, because we expand gray-scale |
- // images to RGB but we do not similarly transform the color profile. We'd |
- // either need to transform the color profile or we'd need to decode into a |
- // gray-scale image buffer and hand that to CoreGraphics. |
- sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info); |
- if (colorSpace) { |
- setEmbeddedColorSpace(colorSpace); |
- } |
+ // TODO(msarret): Add GRAY profile support, block CYMK? |
+ if (sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info)) |
+ setEmbeddedColorSpace(std::move(colorSpace)); |
} |
if (!hasEmbeddedColorSpace()) { |
- // TODO (msarett): |
- // Applying the transfer function (gamma) should be handled by |
- // SkColorSpaceXform. Here we always convert to a transfer function that |
- // is a 2.2 exponential. This is a little strange given that the dst |
- // transfer function is not necessarily a 2.2 exponential. |
- // TODO (msarett): |
- // Often, PNGs that specify their transfer function with the gAMA tag will |
- // also specify their gamut with the cHRM tag. We should read this tag |
- // and do a full color space transformation if it is present. |
const double inverseGamma = 0.45455; |
const double defaultGamma = 2.2; |
double gamma; |
@@ -194,11 +175,11 @@ void PNGImageDecoder::headerAvailable() { |
if (interlaceType == PNG_INTERLACE_ADAM7) |
png_set_interlace_handling(png); |
- // Update our info now. |
+ // Update our info now (so we can get color channel info). |
png_read_update_info(png, info); |
- channels = png_get_channels(png, info); |
- ASSERT(channels == 3 || channels == 4); |
+ int channels = png_get_channels(png, info); |
+ DCHECK(channels == 3 || channels == 4); |
m_reader->setHasAlpha(channels == 4); |
if (m_reader->decodingSizeOnly()) { |
@@ -250,16 +231,16 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
} |
/* libpng comments (here to explain what follows). |
- * |
- * this function is called for every row in the image. If the |
- * image is interlacing, and you turned on the interlace handler, |
- * this function will be called for every row in every pass. |
- * Some of these rows will not be changed from the previous pass. |
- * When the row is not changed, the new_row variable will be NULL. |
- * The rows and passes are called in order, so you don't really |
- * need the row_num and pass, but I'm supplying them because it |
- * may make your life easier. |
- */ |
+ * |
+ * this function is called for every row in the image. If the |
+ * image is interlacing, and you turned on the interlace handler, |
+ * this function will be called for every row in every pass. |
+ * Some of these rows will not be changed from the previous pass. |
+ * When the row is not changed, the new_row variable will be NULL. |
+ * The rows and passes are called in order, so you don't really |
+ * need the row_num and pass, but I'm supplying them because it |
+ * may make your life easier. |
+ */ |
// Nothing to do if the row is unchanged, or the row is outside |
// the image bounds: libpng may send extra rows, ignore them to |
@@ -271,23 +252,23 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
return; |
/* libpng comments (continued). |
- * |
- * For the non-NULL rows of interlaced images, you must call |
- * png_progressive_combine_row() passing in the row and the |
- * old row. You can call this function for NULL rows (it will |
- * just return) and for non-interlaced images (it just does the |
- * memcpy for you) if it will make the code easier. Thus, you |
- * can just do this for all cases: |
- * |
- * png_progressive_combine_row(png_ptr, old_row, new_row); |
- * |
- * where old_row is what was displayed for previous rows. Note |
- * that the first pass (pass == 0 really) will completely cover |
- * the old row, so the rows do not have to be initialized. After |
- * the first pass (and only for interlaced images), you will have |
- * to pass the current row, and the function will combine the |
- * old row and the new row. |
- */ |
+ * |
+ * For the non-NULL rows of interlaced images, you must call |
+ * png_progressive_combine_row() passing in the row and the |
+ * old row. You can call this function for NULL rows (it will |
+ * just return) and for non-interlaced images (it just does the |
+ * memcpy for you) if it will make the code easier. Thus, you |
+ * can just do this for all cases: |
+ * |
+ * png_progressive_combine_row(png_ptr, old_row, new_row); |
+ * |
+ * where old_row is what was displayed for previous rows. Note |
+ * that the first pass (pass == 0 really) will completely cover |
+ * the old row, so the rows do not have to be initialized. After |
+ * the first pass (and only for interlaced images), you will have |
+ * to pass the current row, and the function will combine the |
+ * old row and the new row. |
+ */ |
bool hasAlpha = m_reader->hasAlpha(); |
png_bytep row = rowBuffer; |
@@ -301,7 +282,6 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
// Write the decoded row pixels to the frame buffer. The repetitive |
// form of the row write loops is for speed. |
ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); |
- unsigned alphaMask = 255; |
int width = size().width(); |
png_bytep srcPtr = row; |
@@ -321,26 +301,31 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
SkColorSpaceXform::kRGBA_8888_ColorFormat; |
xform->apply(colorFormat, dstRow, colorFormat, srcPtr, size().width(), |
kUnpremul_SkAlphaType); |
- srcPtr = (png_bytep)dstRow; |
+ srcPtr = png_bytep(dstRow); |
} |
+ unsigned alphaMask = 255; |
if (buffer.premultiplyAlpha()) { |
for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
- dstPixel++, srcPtr += 4) { |
+ srcPtr += 4, ++dstPixel) { |
buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
srcPtr[3]); |
alphaMask &= srcPtr[3]; |
} |
} else { |
for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
- dstPixel++, srcPtr += 4) { |
+ srcPtr += 4, ++dstPixel) { |
buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], srcPtr[3]); |
alphaMask &= srcPtr[3]; |
} |
} |
+ |
+ if (alphaMask != 255 && !buffer.hasAlpha()) |
+ buffer.setHasAlpha(true); |
+ |
} else { |
for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
- dstPixel++, srcPtr += 3) { |
+ srcPtr += 3, ++dstPixel) { |
buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255); |
} |
@@ -353,9 +338,6 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
} |
} |
- if (alphaMask != 255 && !buffer.hasAlpha()) |
- buffer.setHasAlpha(true); |
- |
buffer.setPixelsChanged(true); |
} |