blob: b2e1e648420ee959ec7b85fde7860dd1836c7116 [file] [log] [blame]
scroggo6f5e6192015-06-18 12:53:43 -07001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Leon Scroggins III557fbbe2017-05-23 09:37:21 -04008#include "SkBitmap.h"
9#include "SkCanvas.h"
scroggocc2feb12015-08-14 08:32:46 -070010#include "SkCodecPriv.h"
msarette99883f2016-09-08 06:05:35 -070011#include "SkColorSpaceXform.h"
Leon Scroggins III557fbbe2017-05-23 09:37:21 -040012#include "SkRasterPipeline.h"
Matt Sarett5c496172017-02-07 17:01:16 -050013#include "SkSampler.h"
msarettff2a6c82016-09-07 11:23:28 -070014#include "SkStreamPriv.h"
scroggo6f5e6192015-06-18 12:53:43 -070015#include "SkTemplates.h"
Matt Sarett5c496172017-02-07 17:01:16 -050016#include "SkWebpCodec.h"
scroggo6f5e6192015-06-18 12:53:43 -070017
18// A WebP decoder on top of (subset of) libwebp
19// For more information on WebP image format, and libwebp library, see:
20// https://code.google.com/speed/webp/
21// http://www.webmproject.org/code/#libwebp-webp-image-library
22// https://chromium.googlesource.com/webm/libwebp
23
24// If moving libwebp out of skia source tree, path for webp headers must be
25// updated accordingly. Here, we enforce using local copy in webp sub-directory.
26#include "webp/decode.h"
msarett9d15dab2016-08-24 07:36:06 -070027#include "webp/demux.h"
scroggo6f5e6192015-06-18 12:53:43 -070028#include "webp/encode.h"
29
scroggodb30be22015-12-08 18:54:13 -080030bool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) {
scroggo6f5e6192015-06-18 12:53:43 -070031 // WEBP starts with the following:
32 // RIFFXXXXWEBPVP
33 // Where XXXX is unspecified.
scroggodb30be22015-12-08 18:54:13 -080034 const char* bytes = static_cast<const char*>(buf);
35 return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6);
scroggo6f5e6192015-06-18 12:53:43 -070036}
37
Leon Scroggins III557fbbe2017-05-23 09:37:21 -040038static SkAlphaType alpha_type(bool hasAlpha) {
39 return hasAlpha ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
40}
41
scroggo6f5e6192015-06-18 12:53:43 -070042// Parse headers of RIFF container, and check for valid Webp (VP8) content.
Leon Scroggins III557fbbe2017-05-23 09:37:21 -040043// Returns an SkWebpCodec on success
msarettac6c7502016-04-25 09:30:24 -070044SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
Ben Wagner145dbcd2016-11-03 14:40:50 -040045 std::unique_ptr<SkStream> streamDeleter(stream);
msarettac6c7502016-04-25 09:30:24 -070046
msarettff2a6c82016-09-07 11:23:28 -070047 // Webp demux needs a contiguous data buffer.
48 sk_sp<SkData> data = nullptr;
49 if (stream->getMemoryBase()) {
50 // It is safe to make without copy because we'll hold onto the stream.
51 data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength());
52 } else {
53 data = SkCopyStreamToData(stream);
scroggodb30be22015-12-08 18:54:13 -080054
msarettff2a6c82016-09-07 11:23:28 -070055 // If we are forced to copy the stream to a data, we can go ahead and delete the stream.
56 streamDeleter.reset(nullptr);
57 }
58
59 // It's a little strange that the |demux| will outlive |webpData|, though it needs the
60 // pointer in |webpData| to remain valid. This works because the pointer remains valid
61 // until the SkData is freed.
62 WebPData webpData = { data->bytes(), data->size() };
63 SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&webpData, nullptr));
64 if (nullptr == demux) {
msarettac6c7502016-04-25 09:30:24 -070065 return nullptr;
scroggo6f5e6192015-06-18 12:53:43 -070066 }
67
Matt Sarett5c496172017-02-07 17:01:16 -050068 const int width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
69 const int height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
70
71 // Sanity check for image size that's about to be decoded.
72 {
73 const int64_t size = sk_64_mul(width, height);
74 if (!sk_64_isS32(size)) {
75 return nullptr;
76 }
77 // now check that if we are 4-bytes per pixel, we also don't overflow
78 if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) {
79 return nullptr;
80 }
81 }
82
msarettff2a6c82016-09-07 11:23:28 -070083 WebPChunkIterator chunkIterator;
84 SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator);
85 sk_sp<SkColorSpace> colorSpace = nullptr;
raftiasd737bee2016-12-08 10:53:24 -050086 bool unsupportedICC = false;
msarettff2a6c82016-09-07 11:23:28 -070087 if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) {
Brian Osman526972e2016-10-24 09:24:02 -040088 colorSpace = SkColorSpace::MakeICC(chunkIterator.chunk.bytes, chunkIterator.chunk.size);
raftiasd737bee2016-12-08 10:53:24 -050089 if (!colorSpace) {
90 unsupportedICC = true;
91 }
scroggo6f5e6192015-06-18 12:53:43 -070092 }
msarettff2a6c82016-09-07 11:23:28 -070093 if (!colorSpace) {
Matt Sarett77a7a1b2017-02-07 13:56:11 -050094 colorSpace = SkColorSpace::MakeSRGB();
msarettff2a6c82016-09-07 11:23:28 -070095 }
96
Matt Sarett5c496172017-02-07 17:01:16 -050097 // Get the first frame and its "features" to determine the color and alpha types.
msarettff2a6c82016-09-07 11:23:28 -070098 WebPIterator frame;
99 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
100 if (!WebPDemuxGetFrame(demux, 1, &frame)) {
101 return nullptr;
102 }
103
msarettff2a6c82016-09-07 11:23:28 -0700104 WebPBitstreamFeatures features;
105 VP8StatusCode status = WebPGetFeatures(frame.fragment.bytes, frame.fragment.size, &features);
106 if (VP8_STATUS_OK != status) {
107 return nullptr;
108 }
109
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400110 const bool hasAlpha = SkToBool(frame.has_alpha)
111 || frame.width != width || frame.height != height;
msarettac6c7502016-04-25 09:30:24 -0700112 SkEncodedInfo::Color color;
113 SkEncodedInfo::Alpha alpha;
114 switch (features.format) {
115 case 0:
Matt Sarett5c496172017-02-07 17:01:16 -0500116 // This indicates a "mixed" format. We could see this for
117 // animated webps (multiple fragments).
msarettac6c7502016-04-25 09:30:24 -0700118 // We could also guess kYUV here, but I think it makes more
119 // sense to guess kBGRA which is likely closer to the final
120 // output. Otherwise, we might end up converting
121 // BGRA->YUVA->BGRA.
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400122 // Fallthrough:
123 case 2:
124 // This is the lossless format (BGRA).
125 if (hasAlpha) {
126 color = SkEncodedInfo::kBGRA_Color;
127 alpha = SkEncodedInfo::kUnpremul_Alpha;
128 } else {
129 color = SkEncodedInfo::kBGRX_Color;
130 alpha = SkEncodedInfo::kOpaque_Alpha;
131 }
msarettac6c7502016-04-25 09:30:24 -0700132 break;
133 case 1:
134 // This is the lossy format (YUV).
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400135 if (hasAlpha) {
msarettac6c7502016-04-25 09:30:24 -0700136 color = SkEncodedInfo::kYUVA_Color;
msarettc30c4182016-04-20 11:53:35 -0700137 alpha = SkEncodedInfo::kUnpremul_Alpha;
msarettac6c7502016-04-25 09:30:24 -0700138 } else {
139 color = SkEncodedInfo::kYUV_Color;
140 alpha = SkEncodedInfo::kOpaque_Alpha;
141 }
142 break;
msarettac6c7502016-04-25 09:30:24 -0700143 default:
144 return nullptr;
scroggo6f5e6192015-06-18 12:53:43 -0700145 }
scroggo6f5e6192015-06-18 12:53:43 -0700146
msarettac6c7502016-04-25 09:30:24 -0700147 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
Matt Sarett5c496172017-02-07 17:01:16 -0500148 SkWebpCodec* codecOut = new SkWebpCodec(width, height, info, std::move(colorSpace),
149 streamDeleter.release(), demux.release(),
150 std::move(data));
raftiasd737bee2016-12-08 10:53:24 -0500151 codecOut->setUnsupportedICC(unsupportedICC);
152 return codecOut;
scroggo6f5e6192015-06-18 12:53:43 -0700153}
154
scroggo6f5e6192015-06-18 12:53:43 -0700155SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const {
156 SkISize dim = this->getInfo().dimensions();
msaretta0c414d2015-06-19 07:34:30 -0700157 // SkCodec treats zero dimensional images as errors, so the minimum size
158 // that we will recommend is 1x1.
159 dim.fWidth = SkTMax(1, SkScalarRoundToInt(desiredScale * dim.fWidth));
160 dim.fHeight = SkTMax(1, SkScalarRoundToInt(desiredScale * dim.fHeight));
scroggo6f5e6192015-06-18 12:53:43 -0700161 return dim;
162}
163
scroggoe7fc14b2015-10-02 13:14:46 -0700164bool SkWebpCodec::onDimensionsSupported(const SkISize& dim) {
165 const SkImageInfo& info = this->getInfo();
166 return dim.width() >= 1 && dim.width() <= info.width()
167 && dim.height() >= 1 && dim.height() <= info.height();
168}
169
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400170static WEBP_CSP_MODE webp_decode_mode(const SkImageInfo& info) {
171 const bool premultiply = info.alphaType() == kPremul_SkAlphaType;
172 switch (info.colorType()) {
scroggo6f5e6192015-06-18 12:53:43 -0700173 case kBGRA_8888_SkColorType:
174 return premultiply ? MODE_bgrA : MODE_BGRA;
175 case kRGBA_8888_SkColorType:
176 return premultiply ? MODE_rgbA : MODE_RGBA;
scroggo74992b52015-08-06 13:50:15 -0700177 case kRGB_565_SkColorType:
178 return MODE_RGB_565;
scroggo6f5e6192015-06-18 12:53:43 -0700179 default:
180 return MODE_LAST;
181 }
182}
183
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400184SkWebpCodec::Frame* SkWebpCodec::FrameHolder::appendNewFrame(bool hasAlpha) {
185 const int i = this->size();
186 fFrames.emplace_back(i, hasAlpha);
187 return &fFrames[i];
188}
189
scroggob636b452015-07-22 07:16:20 -0700190bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const {
191 if (!desiredSubset) {
192 return false;
193 }
194
msarettfdb47572015-10-13 12:50:14 -0700195 SkIRect dimensions = SkIRect::MakeSize(this->getInfo().dimensions());
196 if (!dimensions.contains(*desiredSubset)) {
scroggob636b452015-07-22 07:16:20 -0700197 return false;
198 }
199
200 // As stated below, libwebp snaps to even left and top. Make sure top and left are even, so we
201 // decode this exact subset.
202 // Leave right and bottom unmodified, so we suggest a slightly larger subset than requested.
203 desiredSubset->fLeft = (desiredSubset->fLeft >> 1) << 1;
204 desiredSubset->fTop = (desiredSubset->fTop >> 1) << 1;
205 return true;
206}
207
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400208int SkWebpCodec::onGetRepetitionCount() {
209 auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS);
210 if (!(flags & ANIMATION_FLAG)) {
211 return 0;
212 }
213
214 const int repCount = WebPDemuxGetI(fDemux.get(), WEBP_FF_LOOP_COUNT);
215 if (0 == repCount) {
216 return kRepetitionCountInfinite;
217 }
218
219 return repCount;
220}
221
222int SkWebpCodec::onGetFrameCount() {
223 auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS);
224 if (!(flags & ANIMATION_FLAG)) {
225 return 1;
226 }
227
228 const uint32_t oldFrameCount = fFrameHolder.size();
229 if (fFailed) {
230 return oldFrameCount;
231 }
232
233 const uint32_t frameCount = WebPDemuxGetI(fDemux, WEBP_FF_FRAME_COUNT);
234 if (oldFrameCount == frameCount) {
235 // We have already parsed this.
236 return frameCount;
237 }
238
239 fFrameHolder.reserve(frameCount);
240
241 for (uint32_t i = oldFrameCount; i < frameCount; i++) {
242 WebPIterator iter;
243 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoIter(&iter);
244
245 if (!WebPDemuxGetFrame(fDemux.get(), i + 1, &iter)) {
246 fFailed = true;
247 break;
248 }
249
250 // libwebp only reports complete frames of an animated image.
251 SkASSERT(iter.complete);
252
253 Frame* frame = fFrameHolder.appendNewFrame(iter.has_alpha);
254 frame->setXYWH(iter.x_offset, iter.y_offset, iter.width, iter.height);
255 frame->setDisposalMethod(iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ?
256 SkCodecAnimation::RestoreBGColor_DisposalMethod :
257 SkCodecAnimation::Keep_DisposalMethod);
258 frame->setDuration(iter.duration);
259 if (WEBP_MUX_BLEND != iter.blend_method) {
260 frame->setBlend(SkCodecAnimation::Blend::kBG);
261 }
262 fFrameHolder.setAlphaAndRequiredFrame(frame);
263 }
264
265 return fFrameHolder.size();
266
267}
268
269const SkFrame* SkWebpCodec::FrameHolder::onGetFrame(int i) const {
270 return static_cast<const SkFrame*>(this->frame(i));
271}
272
273const SkWebpCodec::Frame* SkWebpCodec::FrameHolder::frame(int i) const {
274 SkASSERT(i >= 0 && i < this->size());
275 return &fFrames[i];
276}
277
278bool SkWebpCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const {
279 if (i >= fFrameHolder.size()) {
280 return false;
281 }
282
283 const Frame* frame = fFrameHolder.frame(i);
284 if (!frame) {
285 return false;
286 }
287
288 if (frameInfo) {
289 frameInfo->fRequiredFrame = frame->getRequiredFrame();
290 frameInfo->fDuration = frame->getDuration();
291 // libwebp only reports fully received frames for an
292 // animated image.
293 frameInfo->fFullyReceived = true;
294 frameInfo->fAlphaType = alpha_type(frame->hasAlpha());
295 }
296
297 return true;
298}
299
300static bool is_8888(SkColorType colorType) {
301 switch (colorType) {
302 case kRGBA_8888_SkColorType:
303 case kBGRA_8888_SkColorType:
304 return true;
305 default:
306 return false;
307 }
308}
309
310static void pick_memory_stages(SkColorType ct, SkRasterPipeline::StockStage* load,
311 SkRasterPipeline::StockStage* store) {
312 switch(ct) {
313 case kUnknown_SkColorType:
314 case kAlpha_8_SkColorType:
315 case kARGB_4444_SkColorType:
316 case kIndex_8_SkColorType:
317 case kGray_8_SkColorType:
318 SkASSERT(false);
319 break;
320 case kRGB_565_SkColorType:
321 if (load) *load = SkRasterPipeline::load_565;
322 if (store) *store = SkRasterPipeline::store_565;
323 break;
324 case kRGBA_8888_SkColorType:
325 case kBGRA_8888_SkColorType:
326 if (load) *load = SkRasterPipeline::load_8888;
327 if (store) *store = SkRasterPipeline::store_8888;
328 break;
329 case kRGBA_F16_SkColorType:
330 if (load) *load = SkRasterPipeline::load_f16;
331 if (store) *store = SkRasterPipeline::store_f16;
332 break;
333 }
334}
335
336static void blend_line(SkColorType dstCT, void* dst,
337 SkColorType srcCT, void* src,
338 bool needsSrgbToLinear, SkAlphaType at,
339 int width) {
340 // Setup conversion from the source and dest, which will be the same.
341 SkRasterPipeline convert_to_linear_premul;
342 if (needsSrgbToLinear) {
343 convert_to_linear_premul.append_from_srgb(at);
344 }
345 if (kUnpremul_SkAlphaType == at) {
346 // srcover assumes premultiplied inputs.
347 convert_to_linear_premul.append(SkRasterPipeline::premul);
348 }
349
350 SkRasterPipeline p;
351 SkRasterPipeline::StockStage load_dst, store_dst;
352 pick_memory_stages(dstCT, &load_dst, &store_dst);
353
354 // Load the final dst.
355 p.append(load_dst, dst);
356 p.extend(convert_to_linear_premul);
357 p.append(SkRasterPipeline::move_src_dst);
358
359 // Load the src.
360 SkRasterPipeline::StockStage load_src;
361 pick_memory_stages(srcCT, &load_src, nullptr);
362 p.append(load_src, src);
363 if (dstCT != srcCT) {
364 SkASSERT(kBGRA_8888_SkColorType == srcCT);
365 p.append(SkRasterPipeline::swap_rb);
366 }
367 p.extend(convert_to_linear_premul);
368
369 p.append(SkRasterPipeline::srcover);
370
371 // Convert back to dst.
372 if (kUnpremul_SkAlphaType == at) {
373 p.append(SkRasterPipeline::unpremul);
374 }
375 if (needsSrgbToLinear) {
376 p.append(SkRasterPipeline::to_srgb);
377 }
378 p.append(store_dst, dst);
379
380 p.run(0, width);
381}
382
scroggoeb602a52015-07-09 08:16:03 -0700383SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
msarette6dd0042015-10-09 11:07:34 -0700384 const Options& options, SkPMColor*, int*,
msarette99883f2016-09-08 06:05:35 -0700385 int* rowsDecodedPtr) {
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400386 // Ensure that we have parsed this far.
387 const int index = options.fFrameIndex;
388 if (index >= this->onGetFrameCount()) {
389 return kIncompleteInput;
390 }
391
392 const auto& srcInfo = this->getInfo();
Matt Sarettcf3f2342017-03-23 15:32:25 -0400393 {
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400394 auto info = srcInfo;
395 if (index > 0) {
396 auto alphaType = alpha_type(fFrameHolder.frame(index)->hasAlpha());
397 info = info.makeAlphaType(alphaType);
398 }
399 if (!conversion_possible(dstInfo, info) ||
400 !this->initializeColorXform(dstInfo, options.fPremulBehavior))
401 {
402 return kInvalidConversion;
403 }
404 }
405
406 if (index > 0 && (options.fSubset || dstInfo.dimensions() != srcInfo.dimensions())) {
407 // Subsetting and scaling are tricky when asking for frames beyond frame 0. In order to
408 // support it, we'll need to determine the proper rectangle for a
409 // WEBP_MUX_DISPOSE_BACKGROUND required frame before erasing it. (Currently the order
410 // is backwards.) Disable until it becomes clear that supporting it is important.
411 return kUnimplemented;
scroggo6f5e6192015-06-18 12:53:43 -0700412 }
413
414 WebPDecoderConfig config;
415 if (0 == WebPInitDecoderConfig(&config)) {
416 // ABI mismatch.
417 // FIXME: New enum for this?
418 return kInvalidInput;
419 }
420
421 // Free any memory associated with the buffer. Must be called last, so we declare it first.
422 SkAutoTCallVProc<WebPDecBuffer, WebPFreeDecBuffer> autoFree(&(config.output));
423
Matt Sarett5c496172017-02-07 17:01:16 -0500424 WebPIterator frame;
425 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400426 // If this succeeded in onGetFrameCount(), it should succeed again here.
427 SkAssertResult(WebPDemuxGetFrame(fDemux, index + 1, &frame));
Matt Sarett604971e2017-02-06 09:51:48 -0500428
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400429 const int requiredFrame = index == 0 ? kNone : fFrameHolder.frame(index)->getRequiredFrame();
Matt Sarett5c496172017-02-07 17:01:16 -0500430 // Get the frameRect. libwebp will have already signaled an error if this is not fully
431 // contained by the canvas.
432 auto frameRect = SkIRect::MakeXYWH(frame.x_offset, frame.y_offset, frame.width, frame.height);
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400433 SkASSERT(srcInfo.bounds().contains(frameRect));
434 const bool frameIsSubset = frameRect != srcInfo.bounds();
435 if (kNone == requiredFrame) {
436 if (frameIsSubset) {
437 SkSampler::Fill(dstInfo, dst, rowBytes, 0, options.fZeroInitialized);
438 }
439 } else {
440 if (!options.fHasPriorFrame) {
441 Options prevFrameOpts(options);
442 prevFrameOpts.fFrameIndex = requiredFrame;
443 const auto result = this->getPixels(dstInfo, dst, rowBytes, &prevFrameOpts,
444 nullptr, nullptr);
445 switch (result) {
446 case kSuccess:
447 break;
448 case kIncompleteInput:
449 return kInvalidInput;
450 default:
451 return result;
452 }
453 }
454
455 // Dispose bg color
456 const Frame* priorFrame = fFrameHolder.frame(requiredFrame);
457 if (priorFrame->getDisposalMethod() == SkCodecAnimation::RestoreBGColor_DisposalMethod) {
458 // FIXME: If we add support for scaling/subsets, this rectangle needs to be adjusted.
459 const auto priorRect = priorFrame->frameRect();
460 const auto info = dstInfo.makeWH(priorRect.width(), priorRect.height());
461 const size_t bpp = SkColorTypeBytesPerPixel(dstInfo.colorType());
462 const size_t offset = priorRect.x() * bpp + priorRect.y() * rowBytes;
463 auto* eraseDst = SkTAddOffset<void>(dst, offset);
464 SkSampler::Fill(info, eraseDst, rowBytes, 0, kNo_ZeroInitialized);
465 }
Matt Sarett604971e2017-02-06 09:51:48 -0500466 }
467
Matt Sarett5c496172017-02-07 17:01:16 -0500468 int dstX = frameRect.x();
469 int dstY = frameRect.y();
470 int subsetWidth = frameRect.width();
471 int subsetHeight = frameRect.height();
472 if (options.fSubset) {
473 SkIRect subset = *options.fSubset;
474 SkASSERT(this->getInfo().bounds().contains(subset));
475 SkASSERT(SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
476 SkASSERT(this->getValidSubset(&subset) && subset == *options.fSubset);
477
478 if (!SkIRect::IntersectsNoEmptyCheck(subset, frameRect)) {
479 return kSuccess;
480 }
481
482 int minXOffset = SkTMin(dstX, subset.x());
483 int minYOffset = SkTMin(dstY, subset.y());
484 dstX -= minXOffset;
485 dstY -= minYOffset;
486 frameRect.offset(-minXOffset, -minYOffset);
487 subset.offset(-minXOffset, -minYOffset);
488
489 // Just like we require that the requested subset x and y offset are even, libwebp
490 // guarantees that the frame x and y offset are even (it's actually impossible to specify
491 // an odd frame offset). So we can still guarantee that the adjusted offsets are even.
492 SkASSERT(SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
493
494 SkIRect intersection;
495 SkAssertResult(intersection.intersect(frameRect, subset));
496 subsetWidth = intersection.width();
497 subsetHeight = intersection.height();
498
499 config.options.use_cropping = 1;
500 config.options.crop_left = subset.x();
501 config.options.crop_top = subset.y();
502 config.options.crop_width = subsetWidth;
503 config.options.crop_height = subsetHeight;
504 }
505
506 // Ignore the frame size and offset when determining if scaling is necessary.
507 int scaledWidth = subsetWidth;
508 int scaledHeight = subsetHeight;
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400509 SkISize srcSize = options.fSubset ? options.fSubset->size() : srcInfo.dimensions();
Matt Sarett5c496172017-02-07 17:01:16 -0500510 if (srcSize != dstInfo.dimensions()) {
scroggo6f5e6192015-06-18 12:53:43 -0700511 config.options.use_scaling = 1;
Matt Sarett5c496172017-02-07 17:01:16 -0500512
513 if (frameIsSubset) {
514 float scaleX = ((float) dstInfo.width()) / srcSize.width();
515 float scaleY = ((float) dstInfo.height()) / srcSize.height();
516
517 // We need to be conservative here and floor rather than round.
518 // Otherwise, we may find ourselves decoding off the end of memory.
519 dstX = scaleX * dstX;
520 scaledWidth = scaleX * scaledWidth;
521 dstY = scaleY * dstY;
522 scaledHeight = scaleY * scaledHeight;
523 if (0 == scaledWidth || 0 == scaledHeight) {
524 return kSuccess;
525 }
526 } else {
527 scaledWidth = dstInfo.width();
528 scaledHeight = dstInfo.height();
529 }
530
531 config.options.scaled_width = scaledWidth;
532 config.options.scaled_height = scaledHeight;
scroggo6f5e6192015-06-18 12:53:43 -0700533 }
534
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400535 const bool blendWithPrevFrame = requiredFrame != kNone && frame.blend_method == WEBP_MUX_BLEND
536 && frame.has_alpha;
537 if (blendWithPrevFrame && options.fPremulBehavior == SkTransferFunctionBehavior::kRespect) {
538 // Blending is done with SkRasterPipeline, which requires a color space that is valid for
539 // rendering.
540 const auto* cs = dstInfo.colorSpace();
541 if (!cs || (!cs->gammaCloseToSRGB() && !cs->gammaIsLinear())) {
542 return kInvalidConversion;
543 }
544 }
545
546 SkBitmap webpDst;
Leon Scroggins IIIee92f132017-05-23 15:28:46 -0400547 auto webpInfo = dstInfo;
548 if (!frame.has_alpha) {
549 webpInfo = webpInfo.makeAlphaType(kOpaque_SkAlphaType);
550 }
551 if (this->colorXform()) {
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400552 // Swizzling between RGBA and BGRA is zero cost in a color transform. So when we have a
553 // color transform, we should decode to whatever is easiest for libwebp, and then let the
554 // color transform swizzle if necessary.
555 // Lossy webp is encoded as YUV (so RGBA and BGRA are the same cost). Lossless webp is
556 // encoded as BGRA. This means decoding to BGRA is either faster or the same cost as RGBA.
Leon Scroggins IIIee92f132017-05-23 15:28:46 -0400557 webpInfo = webpInfo.makeColorType(kBGRA_8888_SkColorType);
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400558
Leon Scroggins IIIee92f132017-05-23 15:28:46 -0400559 if (webpInfo.alphaType() == kPremul_SkAlphaType) {
560 webpInfo = webpInfo.makeAlphaType(kUnpremul_SkAlphaType);
561 }
562 }
563
564 if ((this->colorXform() && !is_8888(dstInfo.colorType())) || blendWithPrevFrame) {
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400565 // We will decode the entire image and then perform the color transform. libwebp
566 // does not provide a row-by-row API. This is a shame particularly when we do not want
567 // 8888, since we will need to create another image sized buffer.
Leon Scroggins IIIee92f132017-05-23 15:28:46 -0400568 webpDst.allocPixels(webpInfo);
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400569 } else {
570 // libwebp can decode directly into the output memory.
Leon Scroggins IIIee92f132017-05-23 15:28:46 -0400571 webpDst.installPixels(webpInfo, dst, rowBytes);
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400572 }
573
Leon Scroggins IIIee92f132017-05-23 15:28:46 -0400574 config.output.colorspace = webp_decode_mode(webpInfo);
scroggo6f5e6192015-06-18 12:53:43 -0700575 config.output.is_external_memory = 1;
576
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400577 config.output.u.RGBA.rgba = reinterpret_cast<uint8_t*>(webpDst.getAddr(dstX, dstY));
578 config.output.u.RGBA.stride = static_cast<int>(webpDst.rowBytes());
579 config.output.u.RGBA.size = webpDst.getSafeSize();
msarettff2a6c82016-09-07 11:23:28 -0700580
halcanary96fcdcc2015-08-27 07:41:13 -0700581 SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &config));
scroggo6f5e6192015-06-18 12:53:43 -0700582 if (!idec) {
583 return kInvalidInput;
584 }
585
msarette99883f2016-09-08 06:05:35 -0700586 int rowsDecoded;
587 SkCodec::Result result;
msarettff2a6c82016-09-07 11:23:28 -0700588 switch (WebPIUpdate(idec, frame.fragment.bytes, frame.fragment.size)) {
589 case VP8_STATUS_OK:
Matt Sarett5c496172017-02-07 17:01:16 -0500590 rowsDecoded = scaledHeight;
msarette99883f2016-09-08 06:05:35 -0700591 result = kSuccess;
592 break;
msarettff2a6c82016-09-07 11:23:28 -0700593 case VP8_STATUS_SUSPENDED:
Matt Sarett5c496172017-02-07 17:01:16 -0500594 WebPIDecGetRGB(idec, &rowsDecoded, nullptr, nullptr, nullptr);
595 *rowsDecodedPtr = rowsDecoded + dstY;
msarette99883f2016-09-08 06:05:35 -0700596 result = kIncompleteInput;
597 break;
msarettff2a6c82016-09-07 11:23:28 -0700598 default:
599 return kInvalidInput;
scroggo6f5e6192015-06-18 12:53:43 -0700600 }
msarette99883f2016-09-08 06:05:35 -0700601
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400602 // We're only transforming the new part of the frame, so no need to worry about the
603 // final composited alpha.
604 const auto srcAlpha = 0 == index ? srcInfo.alphaType() : alpha_type(frame.has_alpha);
605 const auto xformAlphaType = select_xform_alpha(dstInfo.alphaType(), srcAlpha);
606 const bool needsSrgbToLinear = dstInfo.gammaCloseToSRGB() &&
607 options.fPremulBehavior == SkTransferFunctionBehavior::kRespect;
608
609 const size_t dstBpp = SkColorTypeBytesPerPixel(dstInfo.colorType());
610 dst = SkTAddOffset<void>(dst, dstBpp * dstX + rowBytes * dstY);
611 const size_t srcRowBytes = config.output.u.RGBA.stride;
612
613 const auto dstCT = dstInfo.colorType();
Matt Sarett313c4632016-10-20 12:35:23 -0400614 if (this->colorXform()) {
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400615 const auto dstColorFormat = select_xform_format(dstInfo.colorType());
616 const auto srcColorFormat = SkColorSpaceXform::kBGRA_8888_ColorFormat;
617 SkASSERT(select_xform_format(webpDst.colorType()) == srcColorFormat);
msarette99883f2016-09-08 06:05:35 -0700618
Matt Sarett5c496172017-02-07 17:01:16 -0500619 uint32_t* xformSrc = (uint32_t*) config.output.u.RGBA.rgba;
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400620 SkBitmap tmp;
621 void* xformDst;
622
623 if (blendWithPrevFrame) {
624 // Xform into temporary bitmap big enough for one row.
625 tmp.allocPixels(dstInfo.makeWH(scaledWidth, 1));
626 xformDst = tmp.getPixels();
627 } else {
628 xformDst = dst;
629 }
Robert Phillipsb3050b92017-02-06 13:12:18 +0000630 for (int y = 0; y < rowsDecoded; y++) {
Matt Sarett5c496172017-02-07 17:01:16 -0500631 SkAssertResult(this->colorXform()->apply(dstColorFormat, xformDst,
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400632 srcColorFormat, xformSrc, scaledWidth, xformAlphaType));
633 if (blendWithPrevFrame) {
634 blend_line(dstCT, &dst, dstCT, &xformDst, needsSrgbToLinear, xformAlphaType,
635 scaledWidth);
636 dst = SkTAddOffset<void>(dst, rowBytes);
637 } else {
638 xformDst = SkTAddOffset<void>(xformDst, rowBytes);
639 }
Matt Sarett5c496172017-02-07 17:01:16 -0500640 xformSrc = SkTAddOffset<uint32_t>(xformSrc, srcRowBytes);
msarette99883f2016-09-08 06:05:35 -0700641 }
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400642 } else if (blendWithPrevFrame) {
643 const uint8_t* src = config.output.u.RGBA.rgba;
644
645 for (int y = 0; y < rowsDecoded; y++) {
646 blend_line(dstCT, &dst, webpDst.colorType(), &src, needsSrgbToLinear,
647 xformAlphaType, scaledWidth);
648 src = SkTAddOffset<const uint8_t>(src, srcRowBytes);
649 dst = SkTAddOffset<void>(dst, rowBytes);
650 }
msarette99883f2016-09-08 06:05:35 -0700651 }
652
653 return result;
scroggo6f5e6192015-06-18 12:53:43 -0700654}
655
msarett9d15dab2016-08-24 07:36:06 -0700656SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info,
msarettff2a6c82016-09-07 11:23:28 -0700657 sk_sp<SkColorSpace> colorSpace, SkStream* stream, WebPDemuxer* demux,
658 sk_sp<SkData> data)
659 : INHERITED(width, height, info, stream, std::move(colorSpace))
660 , fDemux(demux)
661 , fData(std::move(data))
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400662 , fFailed(false)
663{
664 fFrameHolder.setScreenSize(width, height);
665}