OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 17 matching lines...) Expand all Loading... |
28 | 28 |
29 #include "config.h" | 29 #include "config.h" |
30 #include "WEBPImageDecoder.h" | 30 #include "WEBPImageDecoder.h" |
31 | 31 |
32 #if USE(WEBP) | 32 #if USE(WEBP) |
33 | 33 |
34 #include "PlatformInstrumentation.h" | 34 #include "PlatformInstrumentation.h" |
35 | 35 |
36 #ifdef QCMS_WEBP_COLOR_CORRECTION | 36 #ifdef QCMS_WEBP_COLOR_CORRECTION |
37 #include "qcms.h" | 37 #include "qcms.h" |
38 #include "webp/demux.h" | 38 #include "webp/format_constants.h" |
39 #else | 39 #else |
40 #undef ICCP_FLAG | 40 #undef ICCP_FLAG |
41 #define ICCP_FLAG 0 | 41 #define ICCP_FLAG 0 |
42 #endif | 42 #undef ALPHA_FLAG |
| 43 #if (WEBP_DECODER_ABI_VERSION >= 0x0163) // Alpha supported, but need to define
flag. |
| 44 #define ALPHA_FLAG 0x00000010 |
| 45 #else // Versions earlier than 0.1.99 don't support alpha. |
| 46 #define ALPHA_FLAG 0 |
| 47 #endif // WEBP_DECODER_ABI_VERSION >= 0x0163 |
| 48 #endif // QCMS_WEBP_COLOR_CORRECTION |
43 | 49 |
44 // Backward emulation for earlier versions than 0.1.99. | 50 // Backward emulation for earlier versions than 0.1.99. |
45 #if (WEBP_DECODER_ABI_VERSION < 0x0163) | 51 #if (WEBP_DECODER_ABI_VERSION < 0x0163) |
46 #define MODE_rgbA MODE_RGBA | 52 #define MODE_rgbA MODE_RGBA |
47 #define MODE_bgrA MODE_BGRA | 53 #define MODE_bgrA MODE_BGRA |
48 #endif | 54 #endif |
49 | 55 |
50 #if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) | 56 #if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) |
51 inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_rgbA : M
ODE_RGBA; } | 57 inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_rgbA : M
ODE_RGBA; } |
52 #elif USE(SKIA) && SK_B32_SHIFT | 58 #elif USE(SKIA) && SK_B32_SHIFT |
53 inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_rgbA : M
ODE_RGBA; } | 59 inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_rgbA : M
ODE_RGBA; } |
54 #else // LITTLE_ENDIAN, output BGRA pixels. | 60 #else // LITTLE_ENDIAN, output BGRA pixels. |
55 inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_bgrA : M
ODE_BGRA; } | 61 inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_bgrA : M
ODE_BGRA; } |
56 #endif | 62 #endif |
57 | 63 |
58 namespace WebCore { | 64 namespace WebCore { |
59 | 65 |
60 WEBPImageDecoder::WEBPImageDecoder(ImageSource::AlphaOption alphaOption, | 66 WEBPImageDecoder::WEBPImageDecoder(ImageSource::AlphaOption alphaOption, |
61 ImageSource::GammaAndColorProfileOption gamma
AndColorProfileOption) | 67 ImageSource::GammaAndColorProfileOption gamma
AndColorProfileOption) |
62 : ImageDecoder(alphaOption, gammaAndColorProfileOption) | 68 : ImageDecoder(alphaOption, gammaAndColorProfileOption) |
63 , m_decoder(0) | 69 , m_decoder(0) |
64 , m_hasAlpha(false) | |
65 , m_formatFlags(0) | 70 , m_formatFlags(0) |
66 #ifdef QCMS_WEBP_COLOR_CORRECTION | 71 #ifdef QCMS_WEBP_COLOR_CORRECTION |
| 72 , m_decodedHeight(0) |
| 73 , m_haveAlreadyParsedThisData(false) |
| 74 , m_demux(0) |
| 75 , m_demuxState(WEBP_DEMUX_PARSING_HEADER) |
67 , m_haveReadProfile(false) | 76 , m_haveReadProfile(false) |
68 , m_transform(0) | 77 , m_transform(0) |
69 , m_decodedHeight(0) | 78 , m_haveReadAnimParams(false) |
| 79 , m_repetitionCount(cAnimationLoopOnce) |
70 #endif | 80 #endif |
71 { | 81 { |
72 } | 82 } |
73 | 83 |
74 WEBPImageDecoder::~WEBPImageDecoder() | 84 WEBPImageDecoder::~WEBPImageDecoder() |
75 { | 85 { |
76 clear(); | 86 clearAll(); |
77 } | 87 } |
78 | 88 |
79 void WEBPImageDecoder::clear() | 89 void WEBPImageDecoder::clearAll() |
80 { | 90 { |
81 #ifdef QCMS_WEBP_COLOR_CORRECTION | 91 #ifdef QCMS_WEBP_COLOR_CORRECTION |
82 if (m_transform) | 92 if (m_transform) |
83 qcms_transform_release(m_transform); | 93 qcms_transform_release(m_transform); |
84 m_transform = 0; | 94 m_transform = 0; |
85 #endif | 95 WebPDemuxDelete(m_demux); |
86 if (m_decoder) | 96 m_demux = 0; |
87 WebPIDelete(m_decoder); | 97 #endif |
| 98 clearDecoder(); |
| 99 } |
| 100 |
| 101 void WEBPImageDecoder::clearDecoder() |
| 102 { |
| 103 WebPIDelete(m_decoder); |
88 m_decoder = 0; | 104 m_decoder = 0; |
| 105 #ifdef QCMS_WEBP_COLOR_CORRECTION |
| 106 m_decodedHeight = 0; |
| 107 #endif |
89 } | 108 } |
90 | 109 |
91 bool WEBPImageDecoder::isSizeAvailable() | 110 bool WEBPImageDecoder::isSizeAvailable() |
92 { | 111 { |
93 if (!ImageDecoder::isSizeAvailable()) | 112 if (!ImageDecoder::isSizeAvailable()) { |
94 decode(true); | 113 #ifdef QCMS_WEBP_COLOR_CORRECTION |
95 | 114 if (!updateDemuxer()) |
| 115 return 0; |
| 116 #else |
| 117 decode(reinterpret_cast<const uint8_t*>(m_data->data()), m_data->size(),
true, 0); |
| 118 #endif |
| 119 } |
96 return ImageDecoder::isSizeAvailable(); | 120 return ImageDecoder::isSizeAvailable(); |
97 } | 121 } |
98 | 122 |
99 ImageFrame* WEBPImageDecoder::frameBufferAtIndex(size_t index) | 123 size_t WEBPImageDecoder::frameCount() |
100 { | 124 { |
101 if (index) | 125 #ifdef QCMS_WEBP_COLOR_CORRECTION |
| 126 if (!updateDemuxer()) |
102 return 0; | 127 return 0; |
103 | 128 #else |
104 if (m_frameBufferCache.isEmpty()) { | 129 if (m_frameBufferCache.isEmpty()) { |
105 m_frameBufferCache.resize(1); | 130 m_frameBufferCache.resize(1); |
106 m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); | 131 m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); |
107 } | 132 } |
108 | 133 #endif |
109 ImageFrame& frame = m_frameBufferCache[0]; | 134 return m_frameBufferCache.size(); |
| 135 } |
| 136 |
| 137 ImageFrame* WEBPImageDecoder::frameBufferAtIndex(size_t index) |
| 138 { |
| 139 if (index >= frameCount()) |
| 140 return 0; |
| 141 |
| 142 ImageFrame& frame = m_frameBufferCache[index]; |
110 if (frame.status() != ImageFrame::FrameComplete) { | 143 if (frame.status() != ImageFrame::FrameComplete) { |
| 144 #ifdef QCMS_WEBP_COLOR_CORRECTION |
| 145 if (index && (m_frameBufferCache[index - 1].status() != ImageFrame::Fram
eComplete)) |
| 146 return 0; // We haven't fully decoded the previous frame yet. |
| 147 ASSERT(m_demux); |
| 148 WebPIterator fIter; |
| 149 if (!WebPDemuxGetFrame(m_demux, index + 1, &fIter)) |
| 150 return 0; |
| 151 if (m_formatFlags & ANIMATION_FLAG) { |
| 152 if (!initFrameBuffer(fIter, index)) |
| 153 return 0; |
| 154 } |
111 PlatformInstrumentation::willDecodeImage("WEBP"); | 155 PlatformInstrumentation::willDecodeImage("WEBP"); |
112 decode(false); | 156 decode(fIter.fragment.bytes, fIter.fragment.size, false, index); |
113 PlatformInstrumentation::didDecodeImage(); | 157 PlatformInstrumentation::didDecodeImage(); |
| 158 WebPDemuxReleaseIterator(&fIter); |
| 159 #else |
| 160 ASSERT(!index); |
| 161 PlatformInstrumentation::willDecodeImage("WEBP"); |
| 162 decode(reinterpret_cast<const uint8_t*>(m_data->data()), m_data->size(),
false, index); |
| 163 PlatformInstrumentation::didDecodeImage(); |
| 164 #endif |
114 } | 165 } |
115 return &frame; | 166 return &frame; |
116 } | 167 } |
117 | 168 |
118 #ifdef QCMS_WEBP_COLOR_CORRECTION | 169 #ifdef QCMS_WEBP_COLOR_CORRECTION |
119 | 170 |
| 171 void WEBPImageDecoder::setData(SharedBuffer* data, bool allDataReceived) |
| 172 { |
| 173 if (failed()) |
| 174 return; |
| 175 |
| 176 ImageDecoder::setData(data, allDataReceived); |
| 177 |
| 178 // Mark that we have new data. |
| 179 if (m_demuxState != WEBP_DEMUX_DONE) |
| 180 m_haveAlreadyParsedThisData = false; |
| 181 } |
| 182 |
| 183 bool WEBPImageDecoder::updateDemuxer() |
| 184 { |
| 185 if (!m_haveAlreadyParsedThisData) { |
| 186 WebPDemuxDelete(m_demux); |
| 187 const uint8_t* dataBytes = reinterpret_cast<const uint8_t*>(m_data->data
()); |
| 188 const size_t dataSize = m_data->size(); |
| 189 |
| 190 static const size_t minSizeForDemux = RIFF_HEADER_SIZE + CHUNK_HEADER_SI
ZE; |
| 191 if (dataSize < minSizeForDemux) |
| 192 return 0; // Wait for headers so that WebPDemuxPartial doesn't retur
n null. |
| 193 |
| 194 WebPData inputData = { dataBytes, dataSize }; |
| 195 m_demux = WebPDemuxPartial(&inputData, &m_demuxState); |
| 196 if (!m_demux) |
| 197 return setFailed(); // Must be a failure as we have at least 'minSiz
eForDemux' bytes. |
| 198 if (m_demuxState >= WEBP_DEMUX_PARSED_HEADER) { |
| 199 if (!ImageDecoder::isSizeAvailable()) { |
| 200 if (!setSize(WebPDemuxGetI(m_demux, WEBP_FF_CANVAS_WIDTH), WebPD
emuxGetI(m_demux, WEBP_FF_CANVAS_HEIGHT))) |
| 201 setFailed(); |
| 202 m_formatFlags = WebPDemuxGetI(m_demux, WEBP_FF_FORMAT_FLAGS); |
| 203 } |
| 204 ASSERT(ImageDecoder::isSizeAvailable()); |
| 205 const bool hasAnimation = (m_formatFlags & ANIMATION_FLAG); |
| 206 const size_t newFrameCount = WebPDemuxGetI(m_demux, WEBP_FF_FRAME_CO
UNT); |
| 207 if (hasAnimation && !m_haveReadAnimParams && (newFrameCount >= 1)) { |
| 208 // As we have parsed at least one frame (even if partially), |
| 209 // we must already have parsed the animation properties. |
| 210 // This is because ANIM chunk always precedes ANMF chunks. |
| 211 const uint32_t loopCount = WebPDemuxGetI(m_demux, WEBP_FF_LOOP_C
OUNT); |
| 212 // Note: The following casts an 'unsigned int' to 'int'. But tha
t is fine, because loop count is always <= 16 bits. |
| 213 m_repetitionCount = (!loopCount) ? cAnimationLoopInfinite : loop
Count; |
| 214 m_haveReadAnimParams = true; |
| 215 } |
| 216 if (newFrameCount > m_frameBufferCache.size()) { |
| 217 m_frameBufferCache.resize(newFrameCount); |
| 218 for (size_t i = 0; i < newFrameCount; ++i) |
| 219 m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha
); |
| 220 } |
| 221 } |
| 222 m_haveAlreadyParsedThisData = true; |
| 223 } |
| 224 return true; |
| 225 } |
| 226 |
| 227 bool WEBPImageDecoder::initFrameBuffer(const WebPIterator& fIter, size_t frameIn
dex) |
| 228 { |
| 229 ImageFrame& buffer = m_frameBufferCache[frameIndex]; |
| 230 if (buffer.status() != ImageFrame::FrameEmpty) // Already initialized. |
| 231 return true; |
| 232 |
| 233 // Initialize the frame rect in our buffer. |
| 234 IntRect frameRect(fIter.x_offset, fIter.y_offset, fIter.width, fIter.height)
; |
| 235 |
| 236 // Make sure the frameRect doesn't extend outside the buffer. |
| 237 if (frameRect.maxX() > size().width()) |
| 238 frameRect.setWidth(size().width() - fIter.x_offset); |
| 239 if (frameRect.maxY() > size().height()) |
| 240 frameRect.setHeight(size().height() - fIter.y_offset); |
| 241 |
| 242 const int left = upperBoundScaledX(frameRect.x()); |
| 243 const int right = lowerBoundScaledX(frameRect.maxX(), left); |
| 244 const int top = upperBoundScaledY(frameRect.y()); |
| 245 const int bottom = lowerBoundScaledY(frameRect.maxY(), top); |
| 246 buffer.setOriginalFrameRect(IntRect(left, top, right - left, bottom - top)); |
| 247 |
| 248 buffer.setDisposalMethod(fIter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND
? ImageFrame::DisposeOverwriteBgcolor : ImageFrame::DisposeKeep); |
| 249 buffer.setDuration(fIter.duration); |
| 250 buffer.setHasAlpha(m_formatFlags & ALPHA_FLAG); |
| 251 |
| 252 if (!frameIndex) { |
| 253 // This is the first frame, so we're not relying on any previous data. |
| 254 if (!buffer.setSize(scaledSize().width(), scaledSize().height())) |
| 255 return setFailed(); |
| 256 } else { |
| 257 // The starting state for this frame depends on the previous frame's |
| 258 // disposal method. |
| 259 const ImageFrame& prevBuffer = m_frameBufferCache[frameIndex - 1]; |
| 260 ASSERT(prevBuffer.status() == ImageFrame::FrameComplete); |
| 261 const IntRect& prevRect = prevBuffer.originalFrameRect(); |
| 262 const ImageFrame::FrameDisposalMethod prevMethod = prevBuffer.disposalMe
thod(); |
| 263 if ((prevMethod == ImageFrame::DisposeKeep) || (prevMethod == ImageFrame
::DisposeNotSpecified)) { |
| 264 // Preserve the last frame as the starting state for this frame. |
| 265 if (!buffer.copyBitmapData(prevBuffer)) |
| 266 return setFailed(); |
| 267 } else { // prevMethod == ImageFrame::DisposeOverwriteBgcolor |
| 268 // We want to clear the previous frame to transparent, without |
| 269 // affecting pixels in the image outside of the frame. |
| 270 // So, we copy the whole previous buffer, then clear just its frame. |
| 271 if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize(
)))) { |
| 272 // Clearing the first frame, or a frame the size of the whole |
| 273 // image, results in a completely empty image. |
| 274 if (!buffer.setSize(scaledSize().width(), scaledSize().height())
) |
| 275 return setFailed(); |
| 276 } else { |
| 277 // Copy the whole previous buffer, then clear just its frame. |
| 278 if (!buffer.copyBitmapData(prevBuffer)) |
| 279 return setFailed(); |
| 280 for (int y = prevRect.y(); y < prevRect.maxY(); ++y) { |
| 281 for (int x = prevRect.x(); x < prevRect.maxX(); ++x) |
| 282 buffer.setRGBA(x, y, 0, 0, 0, 0); |
| 283 } |
| 284 } |
| 285 } |
| 286 } |
| 287 // Update frame status to be partially complete. |
| 288 buffer.setStatus(ImageFrame::FramePartial); |
| 289 return true; |
| 290 } |
| 291 |
120 void WEBPImageDecoder::createColorTransform(const char* data, size_t size) | 292 void WEBPImageDecoder::createColorTransform(const char* data, size_t size) |
121 { | 293 { |
122 if (m_transform) | 294 if (m_transform) |
123 qcms_transform_release(m_transform); | 295 qcms_transform_release(m_transform); |
124 m_transform = 0; | 296 m_transform = 0; |
125 | 297 |
126 qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); | 298 qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); |
127 if (!deviceProfile) | 299 if (!deviceProfile) |
128 return; | 300 return; |
129 qcms_profile* inputProfile = qcms_profile_from_memory(data, size); | 301 qcms_profile* inputProfile = qcms_profile_from_memory(data, size); |
130 if (!inputProfile) | 302 if (!inputProfile) |
131 return; | 303 return; |
132 | 304 |
133 // We currently only support color profiles for RGB profiled images. | 305 // We currently only support color profiles for RGB profiled images. |
134 ASSERT(icSigRgbData == qcms_profile_get_color_space(inputProfile)); | 306 ASSERT(icSigRgbData == qcms_profile_get_color_space(inputProfile)); |
135 // The input image pixels are RGBA format. | 307 // The input image pixels are RGBA format. |
136 qcms_data_type format = QCMS_DATA_RGBA_8; | 308 qcms_data_type format = QCMS_DATA_RGBA_8; |
137 // FIXME: Don't force perceptual intent if the image profile contains an int
ent. | 309 // FIXME: Don't force perceptual intent if the image profile contains an int
ent. |
138 m_transform = qcms_transform_create(inputProfile, format, deviceProfile, QCM
S_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL); | 310 m_transform = qcms_transform_create(inputProfile, format, deviceProfile, QCM
S_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL); |
139 | 311 |
140 qcms_profile_release(inputProfile); | 312 qcms_profile_release(inputProfile); |
141 } | 313 } |
142 | 314 |
143 void WEBPImageDecoder::readColorProfile(const uint8_t* data, size_t size) | 315 void WEBPImageDecoder::readColorProfile() |
144 { | 316 { |
145 WebPChunkIterator chunkIterator; | 317 WebPChunkIterator chunkIterator; |
146 WebPData inputData = { data, size }; | 318 if (!WebPDemuxGetChunk(m_demux, "ICCP", 1, &chunkIterator)) { |
147 WebPDemuxState state; | |
148 | |
149 WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &state); | |
150 if (!WebPDemuxGetChunk(demuxer, "ICCP", 1, &chunkIterator)) { | |
151 WebPDemuxReleaseChunkIterator(&chunkIterator); | 319 WebPDemuxReleaseChunkIterator(&chunkIterator); |
152 WebPDemuxDelete(demuxer); | |
153 return; | 320 return; |
154 } | 321 } |
155 | 322 |
156 const char* profileData = reinterpret_cast<const char*>(chunkIterator.chunk.
bytes); | 323 const char* profileData = reinterpret_cast<const char*>(chunkIterator.chunk.
bytes); |
157 size_t profileSize = chunkIterator.chunk.size; | 324 size_t profileSize = chunkIterator.chunk.size; |
158 | 325 |
159 // Only accept RGB color profiles from input class devices. | 326 // Only accept RGB color profiles from input class devices. |
160 bool ignoreProfile = false; | 327 bool ignoreProfile = false; |
161 if (profileSize < ImageDecoder::iccColorProfileHeaderLength) | 328 if (profileSize < ImageDecoder::iccColorProfileHeaderLength) |
162 ignoreProfile = true; | 329 ignoreProfile = true; |
163 else if (!ImageDecoder::rgbColorProfile(profileData, profileSize)) | 330 else if (!ImageDecoder::rgbColorProfile(profileData, profileSize)) |
164 ignoreProfile = true; | 331 ignoreProfile = true; |
165 else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileSize)) | 332 else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileSize)) |
166 ignoreProfile = true; | 333 ignoreProfile = true; |
167 | 334 |
168 if (!ignoreProfile) | 335 if (!ignoreProfile) |
169 createColorTransform(profileData, profileSize); | 336 createColorTransform(profileData, profileSize); |
170 | 337 |
171 WebPDemuxReleaseChunkIterator(&chunkIterator); | 338 WebPDemuxReleaseChunkIterator(&chunkIterator); |
172 WebPDemuxDelete(demuxer); | |
173 } | 339 } |
174 | 340 |
175 void WEBPImageDecoder::applyColorProfile(const uint8_t* data, size_t size, Image
Frame& buffer) | 341 void WEBPImageDecoder::applyPostProcessing(size_t frameIndex) |
176 { | 342 { |
| 343 ImageFrame& buffer = m_frameBufferCache[frameIndex]; |
177 int width; | 344 int width; |
| 345 int stride; |
178 int decodedHeight; | 346 int decodedHeight; |
179 if (!WebPIDecGetRGB(m_decoder, &decodedHeight, &width, 0, 0)) | 347 if (!WebPIDecGetRGB(m_decoder, &decodedHeight, &width, 0, &stride)) |
180 return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062 | 348 return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062 |
181 if (decodedHeight <= 0) | 349 if (decodedHeight <= 0) |
182 return; | 350 return; |
| 351 ASSERT(width == scaledSize().width()); |
| 352 ASSERT(decodedHeight <= scaledSize().height()); |
| 353 const int left = buffer.originalFrameRect().x(); |
| 354 const int top = buffer.originalFrameRect().y(); |
183 | 355 |
184 if (!m_haveReadProfile) { | 356 // Color Profile. |
185 readColorProfile(data, size); | 357 if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) { |
186 m_haveReadProfile = true; | 358 if (!m_haveReadProfile) { |
| 359 readColorProfile(); |
| 360 m_haveReadProfile = true; |
| 361 } |
| 362 for (int y = m_decodedHeight; y < decodedHeight; ++y) { |
| 363 const int canvasY = top + y; |
| 364 uint8_t* row = reinterpret_cast<uint8_t*>(buffer.getAddr(left, canva
sY)); |
| 365 if (qcms_transform* transform = colorTransform()) |
| 366 qcms_transform_data_type(transform, row, row, width, QCMS_OUTPUT
_RGBX); |
| 367 uint8_t* pixel = row; |
| 368 for (int x = 0; x < width; ++x, pixel += 4) { |
| 369 const int canvasX = left + x; |
| 370 buffer.setRGBA(canvasX, canvasY, pixel[0], pixel[1], pixel[2], p
ixel[3]); |
| 371 } |
| 372 } |
187 } | 373 } |
188 | 374 |
189 ASSERT(width == scaledSize().width()); | 375 // Frame disposal. |
190 ASSERT(decodedHeight <= scaledSize().height()); | 376 if ((m_formatFlags & ANIMATION_FLAG) && frameIndex) { |
191 | 377 ImageFrame& prevBuffer = m_frameBufferCache[frameIndex - 1]; |
192 for (int y = m_decodedHeight; y < decodedHeight; ++y) { | 378 ImageFrame::FrameDisposalMethod prevMethod = prevBuffer.disposalMethod()
; |
193 uint8_t* row = reinterpret_cast<uint8_t*>(buffer.getAddr(0, y)); | 379 ASSERT(prevBuffer.status() == ImageFrame::FrameComplete); |
194 if (qcms_transform* transform = colorTransform()) | 380 if (prevMethod == ImageFrame::DisposeKeep) { // Restore transparent pixe
ls to pixels in previous canvas. |
195 qcms_transform_data_type(transform, row, row, width, QCMS_OUTPUT_RGB
X); | 381 for (int y = m_decodedHeight; y < decodedHeight; ++y) { |
196 uint8_t* pixel = row; | 382 const int canvasY = top + y; |
197 for (int x = 0; x < width; ++x, pixel += 4) | 383 for (int x = 0; x < width; ++x) { |
198 buffer.setRGBA(x, y, pixel[0], pixel[1], pixel[2], pixel[3]); | 384 const int canvasX = left + x; |
| 385 ImageFrame::PixelData& pixel = *buffer.getAddr(canvasX, canv
asY); |
| 386 // FIXME: Use alpha-blending when alpha is between 0 and 255
. |
| 387 // Alpha-blending is being implemented in: https://bugs.webk
it.org/show_bug.cgi?id=17022 |
| 388 if (!((pixel >> 24) & 0xff)) { // Need to restore. |
| 389 const ImageFrame::PixelData prevPixel = *prevBuffer.getA
ddr(canvasX, canvasY); |
| 390 pixel = prevPixel; |
| 391 } |
| 392 } |
| 393 } |
| 394 } else if (prevMethod == ImageFrame::DisposeOverwriteBgcolor) { |
| 395 const IntRect& prevRect = prevBuffer.originalFrameRect(); |
| 396 // We need to restore transparent pixels to as they were just after
initFrame() call. That is: |
| 397 // * Transparent if it belongs to prevRect <-- This is a no-op. |
| 398 // * Pixel in the previous canvas otherwise <-- Need to restore. |
| 399 for (int y = m_decodedHeight; y < decodedHeight; ++y) { |
| 400 const int canvasY = top + y; |
| 401 for (int x = 0; x < width; ++x) { |
| 402 const int canvasX = left + x; |
| 403 ImageFrame::PixelData& pixel = *buffer.getAddr(canvasX, canv
asY); |
| 404 const ImageFrame::PixelData prevPixel = *prevBuffer.getAddr(
canvasX, canvasY); |
| 405 if (!((pixel >> 24) & 0xff) && !prevRect.contains(IntPoint(c
anvasX, canvasY))) // Need to restore. |
| 406 pixel = prevPixel; |
| 407 } |
| 408 } |
| 409 } |
199 } | 410 } |
200 | 411 |
201 m_decodedHeight = decodedHeight; | 412 m_decodedHeight = decodedHeight; |
202 } | 413 } |
203 | 414 |
| 415 void WEBPImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) |
| 416 { |
| 417 // We always preserve at least one frame. |
| 418 if (m_frameBufferCache.size() <= 1) |
| 419 return; |
| 420 |
| 421 // Find the last frame we need to preserve in the cache to facilitate |
| 422 // the construction of next frames (needed by initFrame() and |
| 423 // applyPostProcessing()) . This frame is either: |
| 424 // * The last decoded frame in cache, OR |
| 425 // * The first frame (if cache doesn't contain any decoded frames). |
| 426 const int lastFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() -
1); |
| 427 Vector<ImageFrame>::iterator i(m_frameBufferCache.begin() + lastFrame); |
| 428 while ((i != m_frameBufferCache.begin()) && (i->status() != ImageFrame::Fram
eComplete)) |
| 429 --i; |
| 430 |
| 431 // Now |i| holds the last frame we need to preserve; clear prior frames. |
| 432 for (Vector<ImageFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j
) { |
| 433 ASSERT(j->status() != ImageFrame::FramePartial); |
| 434 if (j->status() != ImageFrame::FrameEmpty) |
| 435 j->clearPixelData(); |
| 436 } |
| 437 } |
| 438 |
204 #endif // QCMS_WEBP_COLOR_CORRECTION | 439 #endif // QCMS_WEBP_COLOR_CORRECTION |
205 | 440 |
206 bool WEBPImageDecoder::decode(bool onlySize) | 441 bool WEBPImageDecoder::decode(const uint8_t* dataBytes, size_t dataSize, bool on
lySize, size_t frameIndex) |
207 { | 442 { |
208 if (failed()) | 443 if (failed()) |
209 return false; | 444 return false; |
210 | 445 |
211 const uint8_t* dataBytes = reinterpret_cast<const uint8_t*>(m_data->data()); | |
212 const size_t dataSize = m_data->size(); | |
213 | |
214 if (!ImageDecoder::isSizeAvailable()) { | 446 if (!ImageDecoder::isSizeAvailable()) { |
215 static const size_t imageHeaderSize = 30; | 447 static const size_t imageHeaderSize = 30; |
216 if (dataSize < imageHeaderSize) | 448 if (dataSize < imageHeaderSize) |
217 return false; | 449 return false; |
218 int width, height; | 450 int width, height; |
219 #ifdef QCMS_WEBP_COLOR_CORRECTION | 451 #if (WEBP_DECODER_ABI_VERSION >= 0x0163) |
220 WebPData inputData = { dataBytes, dataSize }; | |
221 WebPDemuxState state; | |
222 WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &state); | |
223 if (!demuxer) | |
224 return setFailed(); | |
225 | |
226 width = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); | |
227 height = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); | |
228 m_formatFlags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS); | |
229 m_hasAlpha = !!(m_formatFlags & ALPHA_FLAG); | |
230 | |
231 WebPDemuxDelete(demuxer); | |
232 if (state <= WEBP_DEMUX_PARSING_HEADER) | |
233 return false; | |
234 #elif (WEBP_DECODER_ABI_VERSION >= 0x0163) | |
235 WebPBitstreamFeatures features; | 452 WebPBitstreamFeatures features; |
236 if (WebPGetFeatures(dataBytes, dataSize, &features) != VP8_STATUS_OK) | 453 if (WebPGetFeatures(dataBytes, dataSize, &features) != VP8_STATUS_OK) |
237 return setFailed(); | 454 return setFailed(); |
238 width = features.width; | 455 width = features.width; |
239 height = features.height; | 456 height = features.height; |
240 m_hasAlpha = features.has_alpha; | 457 m_formatFlags = features.has_alpha ? ALPHA_FLAG : 0; |
241 #else | 458 #else |
242 // Earlier version won't be able to display WebP files with alpha. | 459 // Earlier version won't be able to display WebP files with alpha. |
243 if (!WebPGetInfo(dataBytes, dataSize, &width, &height)) | 460 if (!WebPGetInfo(dataBytes, dataSize, &width, &height)) |
244 return setFailed(); | 461 return setFailed(); |
245 m_hasAlpha = false; | |
246 #endif | 462 #endif |
247 if (!setSize(width, height)) | 463 if (!setSize(width, height)) |
248 return setFailed(); | 464 return setFailed(); |
249 } | 465 } |
250 | 466 |
251 ASSERT(ImageDecoder::isSizeAvailable()); | 467 ASSERT(ImageDecoder::isSizeAvailable()); |
252 if (onlySize) | 468 if (onlySize) |
253 return true; | 469 return true; |
254 | 470 |
255 ASSERT(!m_frameBufferCache.isEmpty()); | 471 ASSERT(m_frameBufferCache.size() > frameIndex); |
256 ImageFrame& buffer = m_frameBufferCache[0]; | 472 ImageFrame& buffer = m_frameBufferCache[frameIndex]; |
257 ASSERT(buffer.status() != ImageFrame::FrameComplete); | 473 ASSERT(buffer.status() != ImageFrame::FrameComplete); |
258 | 474 |
259 if (buffer.status() == ImageFrame::FrameEmpty) { | 475 if (buffer.status() == ImageFrame::FrameEmpty) { |
260 if (!buffer.setSize(size().width(), size().height())) | 476 if (!buffer.setSize(scaledSize().width(), scaledSize().height())) |
261 return setFailed(); | 477 return setFailed(); |
262 buffer.setStatus(ImageFrame::FramePartial); | 478 buffer.setStatus(ImageFrame::FramePartial); |
263 buffer.setHasAlpha(m_hasAlpha); | 479 buffer.setHasAlpha(m_formatFlags & ALPHA_FLAG); |
264 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); | 480 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); |
265 } | 481 } |
266 | 482 |
267 if (!m_decoder) { | 483 if (!m_decoder) { |
268 WEBP_CSP_MODE mode = outputMode(m_hasAlpha); | 484 WEBP_CSP_MODE mode = outputMode(m_formatFlags & ALPHA_FLAG); |
269 if (!m_premultiplyAlpha) | 485 if (!m_premultiplyAlpha) |
270 mode = outputMode(false); | 486 mode = outputMode(false); |
271 if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) | 487 if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) |
272 mode = MODE_RGBA; // Decode to RGBA for input to libqcms. | 488 mode = MODE_RGBA; // Decode to RGBA for input to libqcms. |
273 int rowStride = size().width() * sizeof(ImageFrame::PixelData); | 489 int rowStride = size().width() * sizeof(ImageFrame::PixelData); |
274 uint8_t* output = reinterpret_cast<uint8_t*>(buffer.getAddr(0, 0)); | 490 const IntRect& frameRect = buffer.originalFrameRect(); |
275 int outputSize = size().height() * rowStride; | 491 uint8_t* output = reinterpret_cast<uint8_t*>(buffer.getAddr(frameRect.x(
), frameRect.y())); |
| 492 int outputSize = frameRect.height() * rowStride; |
276 m_decoder = WebPINewRGB(mode, output, outputSize, rowStride); | 493 m_decoder = WebPINewRGB(mode, output, outputSize, rowStride); |
277 if (!m_decoder) | 494 if (!m_decoder) |
278 return setFailed(); | 495 return setFailed(); |
279 } | 496 } |
280 | 497 |
281 switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { | 498 switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { |
282 case VP8_STATUS_OK: | 499 case VP8_STATUS_OK: |
283 if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) | 500 applyPostProcessing(frameIndex); |
284 applyColorProfile(dataBytes, dataSize, buffer); | |
285 buffer.setStatus(ImageFrame::FrameComplete); | 501 buffer.setStatus(ImageFrame::FrameComplete); |
286 clear(); | 502 clearDecoder(); |
287 return true; | 503 return true; |
288 case VP8_STATUS_SUSPENDED: | 504 case VP8_STATUS_SUSPENDED: |
289 if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) | 505 applyPostProcessing(frameIndex); |
290 applyColorProfile(dataBytes, dataSize, buffer); | |
291 return false; | 506 return false; |
292 default: | 507 default: |
293 clear(); | 508 clearAll(); |
294 return setFailed(); | 509 return setFailed(); |
295 } | 510 } |
296 } | 511 } |
297 | 512 |
298 } // namespace WebCore | 513 } // namespace WebCore |
299 | 514 |
300 #endif | 515 #endif |
OLD | NEW |