Commit 0d0fdfbd authored by noel.gordon@gmail.com's avatar noel.gordon@gmail.com

Add webp image color profile support

https://bugs.webkit.org/show_bug.cgi?id=113184

Reviewed by Eric Seidel.

Source/WebCore:

Requires libwebp version 0.3.0 (decoder ABI version 0x201). For images
with an ICC color profile chunk, poll the incremental decoder for the
current decoded height with the WebPIDecGetRGB() API and color correct
any newly decoded rows in-situ in the frame buffer.

Note: the ICC chunk appears before the encoded image frame in the webp
encoding (RIFF container) format. When the incremental decoder outputs
decoded pixels, enough encoded data has arrived to read the entire ICC
color profile data chunk.

Tests: fast/images/webp-color-profile-lossless.html
       fast/images/webp-color-profile-lossy-alpha.html
       fast/images/webp-color-profile-lossy.html

* platform/image-decoders/webp/WEBPImageDecoder.cpp:
(WebCore::WEBPImageDecoder::WEBPImageDecoder):
(WebCore::WEBPImageDecoder::~WEBPImageDecoder): Call clear().
(WebCore):
(WebCore::WEBPImageDecoder::clear):
Added. Helper to clean up the webp decoder and color transform members.
(WebCore::WEBPImageDecoder::createColorTransform):
Create m_transform using the supplied profile memory data. Note that
the |deviceProfile| is not owned, but the |inputProfile| temporary is
so release it here with qcms_release_profile().
(WebCore::WEBPImageDecoder::readColorProfile):
Called once only when the decoder begins to output decoded rows of an
image containing an ICC chunk, to read the ICC color profile data from
the encoded data stream, verify it, and use it to create m_transform.
(WebCore::WEBPImageDecoder::applyColorProfile):
Since there is no row callback in libwebp, poll for the decoded height
of the image so far. If new rows are decoded, color correct the pixels
of those new rows and re-write them back into the frame buffer using
buffer.setRGBA() to 1) alpha pre-multiply the pixels if needed, and 2)
shuffle the pixel bytes into the platform's RGBA pixel endian-ness.
(WebCore::WEBPImageDecoder::decode):
If the container format indicates the image has an ICC color profile,
decode the image to RGBA format for subsequent input to the QCMS color
correction library in applyColorProfile().
* platform/image-decoders/webp/WEBPImageDecoder.h:
(WEBPImageDecoder):
(WebCore::WEBPImageDecoder::colorTransform): m_transform getter.

LayoutTests:

* fast/images/resources/webp-color-profile-lossless.webp: Added.
* fast/images/resources/webp-color-profile-lossy-alpha.webp: Added.
* fast/images/resources/webp-color-profile-lossy.webp: Added.
* fast/images/webp-color-profile-lossless-expected.txt: Added.
* fast/images/webp-color-profile-lossless.html: Added.
* fast/images/webp-color-profile-lossy-alpha-expected.txt: Added.
* fast/images/webp-color-profile-lossy-alpha.html: Added.
* fast/images/webp-color-profile-lossy-expected.txt: Added.
* fast/images/webp-color-profile-lossy.html: Added.
* platform/chromium-mac/fast/images/webp-color-profile-lossless-expected.png: Added.
* platform/chromium-mac/fast/images/webp-color-profile-lossy-alpha-expected.png: Added.
* platform/chromium-mac/fast/images/webp-color-profile-lossy-expected.png: Added.
* platform/chromium-win/fast/images/webp-color-profile-lossless-expected.png: Added.
* platform/chromium-win/fast/images/webp-color-profile-lossy-alpha-expected.png: Added.
* platform/chromium-win/fast/images/webp-color-profile-lossy-expected.png: Added.
* platform/efl/TestExpectations:
* platform/gtk/TestExpectations:
* platform/mac/TestExpectations:
* platform/qt/TestExpectations:
* platform/win/TestExpectations:
* platform/wincairo/TestExpectations:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@147048 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent ec47ab66
2013-03-27 Noel Gordon <noel.gordon@gmail.com>
Add webp image color profile support
https://bugs.webkit.org/show_bug.cgi?id=113184
Reviewed by Eric Seidel.
* fast/images/resources/webp-color-profile-lossless.webp: Added.
* fast/images/resources/webp-color-profile-lossy-alpha.webp: Added.
* fast/images/resources/webp-color-profile-lossy.webp: Added.
* fast/images/webp-color-profile-lossless-expected.txt: Added.
* fast/images/webp-color-profile-lossless.html: Added.
* fast/images/webp-color-profile-lossy-alpha-expected.txt: Added.
* fast/images/webp-color-profile-lossy-alpha.html: Added.
* fast/images/webp-color-profile-lossy-expected.txt: Added.
* fast/images/webp-color-profile-lossy.html: Added.
* platform/chromium-mac/fast/images/webp-color-profile-lossless-expected.png: Added.
* platform/chromium-mac/fast/images/webp-color-profile-lossy-alpha-expected.png: Added.
* platform/chromium-mac/fast/images/webp-color-profile-lossy-expected.png: Added.
* platform/chromium-win/fast/images/webp-color-profile-lossless-expected.png: Added.
* platform/chromium-win/fast/images/webp-color-profile-lossy-alpha-expected.png: Added.
* platform/chromium-win/fast/images/webp-color-profile-lossy-expected.png: Added.
* platform/efl/TestExpectations:
* platform/gtk/TestExpectations:
* platform/mac/TestExpectations:
* platform/qt/TestExpectations:
* platform/win/TestExpectations:
* platform/wincairo/TestExpectations:
2013-03-27 Filip Pizlo <fpizlo@apple.com>
JIT and DFG should NaN-check loads from Float32 arrays
<!-- The die at the center of the image should be green. -->
<img src="resources/webp-color-profile-lossless.webp">
<script>
if (window.testRunner)
window.testRunner.dumpAsText(true);
</script>
<!-- The die at the center of the image should be green. -->
<img src="resources/webp-color-profile-lossy-alpha.webp">
<script>
if (window.testRunner)
window.testRunner.dumpAsText(true);
</script>
<!-- The red sector of the image should be at the 12 o'clock position. -->
<img src="resources/webp-color-profile-lossy.webp" width="400px">
<script>
if (window.testRunner)
window.testRunner.dumpAsText(true);
</script>
......@@ -1186,6 +1186,9 @@ webkit.org/b/88045 fast/dom/gc-attribute-node.html [ Failure ]
# Requires WebP support.
Bug(EFL) fast/canvas/canvas-toDataURL-webp.html
Bug(EFL) fast/images/webp-image-decoding.html
Bug(EFL) fast/images/webp-color-profile-lossless.html
Bug(EFL) fast/images/webp-color-profile-lossy-alpha.html
Bug(EFL) fast/images/webp-color-profile-lossy.html
Bug(EFL) http/tests/images/webp-partial-load.html
Bug(EFL) http/tests/images/webp-progressive-load.html
......
......@@ -207,6 +207,10 @@ webkit.org/b/58443 fast/forms/file/input-file-entries.html [ Skip ]
webkit.org/b/98939 fast/canvas/canvas-toDataURL-webp.html [ Skip ]
# Requires WebP 0.2 support.
webkit.org/b/98939 fast/images/webp-image-decoding.html [ Skip ]
# Requires WebP 0.3 and color profile support.
webkit.org/b/98939 fast/images/webp-color-profile-lossless.html [ Skip ]
webkit.org/b/98939 fast/images/webp-color-profile-lossy-alpha.html [ Skip ]
webkit.org/b/98939 fast/images/webp-color-profile-lossy.html [ Skip ]
# DataTransferItems is not yet implemented.
webkit.org/b/98940 editing/pasteboard/data-transfer-items.html [ Skip ]
......
......@@ -354,6 +354,9 @@ plugins/plugin-initiate-popup-window.html
# Requires WebP support.
fast/canvas/canvas-toDataURL-webp.html
fast/images/webp-image-decoding.html
fast/images/webp-color-profile-lossless.html
fast/images/webp-color-profile-lossy-alpha.html
fast/images/webp-color-profile-lossy.html
http/tests/images/webp-partial-load.html
http/tests/images/webp-progressive-load.html
......
......@@ -1810,6 +1810,9 @@ fast/writing-mode/japanese-rl-text.html
# Requires WebP support.
fast/canvas/canvas-toDataURL-webp.html
fast/images/webp-image-decoding.html
fast/images/webp-color-profile-lossless.html
fast/images/webp-color-profile-lossy-alpha.html
fast/images/webp-color-profile-lossy.html
http/tests/images/webp-partial-load.html
http/tests/images/webp-progressive-load.html
......
......@@ -1356,6 +1356,9 @@ plugins/iframe-shims.html
# Requires WebP support.
fast/canvas/canvas-toDataURL-webp.html
fast/images/webp-image-decoding.html
fast/images/webp-color-profile-lossless.html
fast/images/webp-color-profile-lossy-alpha.html
fast/images/webp-color-profile-lossy.html
http/tests/images/webp-partial-load.html
http/tests/images/webp-progressive-load.html
......
......@@ -1876,6 +1876,9 @@ plugins/iframe-shims.html
# Requires WebP support.
fast/canvas/canvas-toDataURL-webp.html
fast/images/webp-image-decoding.html
fast/images/webp-color-profile-lossless.html
fast/images/webp-color-profile-lossy-alpha.html
fast/images/webp-color-profile-lossy.html
http/tests/images/webp-partial-load.html
http/tests/images/webp-progressive-load.html
......
2013-03-27 Noel Gordon <noel.gordon@gmail.com>
Add webp image color profile support
https://bugs.webkit.org/show_bug.cgi?id=113184
Reviewed by Eric Seidel.
Requires libwebp version 0.3.0 (decoder ABI version 0x201). For images
with an ICC color profile chunk, poll the incremental decoder for the
current decoded height with the WebPIDecGetRGB() API and color correct
any newly decoded rows in-situ in the frame buffer.
Note: the ICC chunk appears before the encoded image frame in the webp
encoding (RIFF container) format. When the incremental decoder outputs
decoded pixels, enough encoded data has arrived to read the entire ICC
color profile data chunk.
Tests: fast/images/webp-color-profile-lossless.html
fast/images/webp-color-profile-lossy-alpha.html
fast/images/webp-color-profile-lossy.html
* platform/image-decoders/webp/WEBPImageDecoder.cpp:
(WebCore::WEBPImageDecoder::WEBPImageDecoder):
(WebCore::WEBPImageDecoder::~WEBPImageDecoder): Call clear().
(WebCore):
(WebCore::WEBPImageDecoder::clear):
Added. Helper to clean up the webp decoder and color transform members.
(WebCore::WEBPImageDecoder::createColorTransform):
Create m_transform using the supplied profile memory data. Note that
the |deviceProfile| is not owned, but the |inputProfile| temporary is
so release it here with qcms_release_profile().
(WebCore::WEBPImageDecoder::readColorProfile):
Called once only when the decoder begins to output decoded rows of an
image containing an ICC chunk, to read the ICC color profile data from
the encoded data stream, verify it, and use it to create m_transform.
(WebCore::WEBPImageDecoder::applyColorProfile):
Since there is no row callback in libwebp, poll for the decoded height
of the image so far. If new rows are decoded, color correct the pixels
of those new rows and re-write them back into the frame buffer using
buffer.setRGBA() to 1) alpha pre-multiply the pixels if needed, and 2)
shuffle the pixel bytes into the platform's RGBA pixel endian-ness.
(WebCore::WEBPImageDecoder::decode):
If the container format indicates the image has an ICC color profile,
decode the image to RGBA format for subsequent input to the QCMS color
correction library in applyColorProfile().
* platform/image-decoders/webp/WEBPImageDecoder.h:
(WEBPImageDecoder):
(WebCore::WEBPImageDecoder::colorTransform): m_transform getter.
2013-03-27 Jun Jiang <jun.a.jiang@intel.com>
Refactor validation checks for texture uploads
......@@ -32,9 +32,16 @@
#if USE(WEBP)
#include "PlatformInstrumentation.h"
#include "webp/decode.h"
// backward emulation for earlier versions than 0.1.99
#ifdef QCMS_WEBP_COLOR_CORRECTION
#include "qcms.h"
#include "webp/demux.h"
#else
#undef ICCP_FLAG
#define ICCP_FLAG 0
#endif
// Backward emulation for earlier versions than 0.1.99.
#if (WEBP_DECODER_ABI_VERSION < 0x0163)
#define MODE_rgbA MODE_RGBA
#define MODE_bgrA MODE_BGRA
......@@ -55,11 +62,27 @@ WEBPImageDecoder::WEBPImageDecoder(ImageSource::AlphaOption alphaOption,
: ImageDecoder(alphaOption, gammaAndColorProfileOption)
, m_decoder(0)
, m_hasAlpha(false)
, m_formatFlags(0)
#ifdef QCMS_WEBP_COLOR_CORRECTION
, m_haveReadProfile(false)
, m_transform(0)
, m_decodedHeight(0)
#endif
{
}
WEBPImageDecoder::~WEBPImageDecoder()
{
clear();
}
void WEBPImageDecoder::clear()
{
#ifdef QCMS_WEBP_COLOR_CORRECTION
if (m_transform)
qcms_transform_release(m_transform);
m_transform = 0;
#endif
if (m_decoder)
WebPIDelete(m_decoder);
m_decoder = 0;
......@@ -92,6 +115,94 @@ ImageFrame* WEBPImageDecoder::frameBufferAtIndex(size_t index)
return &frame;
}
#ifdef QCMS_WEBP_COLOR_CORRECTION
void WEBPImageDecoder::createColorTransform(const char* data, size_t size)
{
if (m_transform)
qcms_transform_release(m_transform);
m_transform = 0;
qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile();
if (!deviceProfile)
return;
qcms_profile* inputProfile = qcms_profile_from_memory(data, size);
if (!inputProfile)
return;
// We currently only support color profiles for RGB profiled images.
ASSERT(icSigRgbData == qcms_profile_get_color_space(inputProfile));
// The input image pixels are RGBA format.
qcms_data_type format = QCMS_DATA_RGBA_8;
// FIXME: Don't force perceptual intent if the image profile contains an intent.
m_transform = qcms_transform_create(inputProfile, format, deviceProfile, QCMS_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL);
qcms_profile_release(inputProfile);
}
void WEBPImageDecoder::readColorProfile(const uint8_t* data, size_t size)
{
WebPChunkIterator chunkIterator;
WebPData inputData = { data, size };
WebPDemuxState state;
WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &state);
if (!WebPDemuxGetChunk(demuxer, "ICCP", 1, &chunkIterator)) {
WebPDemuxReleaseChunkIterator(&chunkIterator);
WebPDemuxDelete(demuxer);
return;
}
const char* profileData = reinterpret_cast<const char*>(chunkIterator.chunk.bytes);
size_t profileSize = chunkIterator.chunk.size;
// Only accept RGB color profiles from input class devices.
bool ignoreProfile = false;
if (profileSize < ImageDecoder::iccColorProfileHeaderLength)
ignoreProfile = true;
else if (!ImageDecoder::rgbColorProfile(profileData, profileSize))
ignoreProfile = true;
else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileSize))
ignoreProfile = true;
if (!ignoreProfile)
createColorTransform(profileData, profileSize);
WebPDemuxReleaseChunkIterator(&chunkIterator);
WebPDemuxDelete(demuxer);
}
void WEBPImageDecoder::applyColorProfile(const uint8_t* data, size_t size, ImageFrame& buffer)
{
int width;
int decodedHeight;
if (!WebPIDecGetRGB(m_decoder, &decodedHeight, &width, 0, 0))
return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062
if (decodedHeight <= 0)
return;
if (!m_haveReadProfile) {
readColorProfile(data, size);
m_haveReadProfile = true;
}
ASSERT(width == scaledSize().width());
ASSERT(decodedHeight <= scaledSize().height());
for (int y = m_decodedHeight; y < decodedHeight; ++y) {
uint8_t* row = reinterpret_cast<uint8_t*>(buffer.getAddr(0, y));
if (qcms_transform* transform = colorTransform())
qcms_transform_data_type(transform, row, row, width, QCMS_OUTPUT_RGBX);
uint8_t* pixel = row;
for (int x = 0; x < width; ++x, pixel += 4)
buffer.setRGBA(x, y, pixel[0], pixel[1], pixel[2], pixel[3]);
}
m_decodedHeight = decodedHeight;
}
#endif // QCMS_WEBP_COLOR_CORRECTION
bool WEBPImageDecoder::decode(bool onlySize)
{
if (failed())
......@@ -105,7 +216,22 @@ bool WEBPImageDecoder::decode(bool onlySize)
if (dataSize < imageHeaderSize)
return false;
int width, height;
#if (WEBP_DECODER_ABI_VERSION >= 0x0163)
#ifdef QCMS_WEBP_COLOR_CORRECTION
WebPData inputData = { dataBytes, dataSize };
WebPDemuxState state;
WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &state);
if (!demuxer)
return setFailed();
width = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
height = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
m_formatFlags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
m_hasAlpha = !!(m_formatFlags & ALPHA_FLAG);
WebPDemuxDelete(demuxer);
if (state <= WEBP_DEMUX_PARSING_HEADER)
return false;
#elif (WEBP_DECODER_ABI_VERSION >= 0x0163)
WebPBitstreamFeatures features;
if (WebPGetFeatures(dataBytes, dataSize, &features) != VP8_STATUS_OK)
return setFailed();
......@@ -139,25 +265,32 @@ bool WEBPImageDecoder::decode(bool onlySize)
}
if (!m_decoder) {
WEBP_CSP_MODE mode = outputMode(m_hasAlpha);
if (!m_premultiplyAlpha)
mode = outputMode(false);
if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile())
mode = MODE_RGBA; // Decode to RGBA for input to libqcms.
int rowStride = size().width() * sizeof(ImageFrame::PixelData);
uint8_t* output = reinterpret_cast<uint8_t*>(buffer.getAddr(0, 0));
int outputSize = size().height() * rowStride;
m_decoder = WebPINewRGB(outputMode(m_hasAlpha), output, outputSize, rowStride);
m_decoder = WebPINewRGB(mode, output, outputSize, rowStride);
if (!m_decoder)
return setFailed();
}
switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) {
case VP8_STATUS_OK:
if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile())
applyColorProfile(dataBytes, dataSize, buffer);
buffer.setStatus(ImageFrame::FrameComplete);
WebPIDelete(m_decoder);
m_decoder = 0;
clear();
return true;
case VP8_STATUS_SUSPENDED:
if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile())
applyColorProfile(dataBytes, dataSize, buffer);
return false;
default:
WebPIDelete(m_decoder);
m_decoder = 0;
clear();
return setFailed();
}
}
......
......@@ -33,7 +33,10 @@
#if USE(WEBP)
typedef struct WebPIDecoder WebPIDecoder;
#include "webp/decode.h"
#if USE(QCMSLIB) && (WEBP_DECODER_ABI_VERSION > 0x200)
#define QCMS_WEBP_COLOR_CORRECTION
#endif
namespace WebCore {
......@@ -51,6 +54,21 @@ private:
WebPIDecoder* m_decoder;
bool m_hasAlpha;
int m_formatFlags;
#ifdef QCMS_WEBP_COLOR_CORRECTION
qcms_transform* colorTransform() const { return m_transform; }
void createColorTransform(const char* data, size_t);
void readColorProfile(const uint8_t* data, size_t);
void applyColorProfile(const uint8_t* data, size_t, ImageFrame&);
bool m_haveReadProfile;
qcms_transform* m_transform;
int m_decodedHeight;
#else
void applyColorProfile(const uint8_t*, size_t, ImageFrame&) { };
#endif
void clear();
};
} // namespace WebCore
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment