SkRasterPipeline in SkArenaAlloc
[platform/upstream/libSkiaSharp.git] / src / codec / SkWebpCodec.cpp
1 /*
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
8 #include "SkBitmap.h"
9 #include "SkCanvas.h"
10 #include "SkCodecPriv.h"
11 #include "SkColorSpaceXform.h"
12 #include "SkRasterPipeline.h"
13 #include "SkSampler.h"
14 #include "SkStreamPriv.h"
15 #include "SkTemplates.h"
16 #include "SkWebpCodec.h"
17
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"
27 #include "webp/demux.h"
28 #include "webp/encode.h"
29
30 bool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) {
31     // WEBP starts with the following:
32     // RIFFXXXXWEBPVP
33     // Where XXXX is unspecified.
34     const char* bytes = static_cast<const char*>(buf);
35     return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6);
36 }
37
38 static SkAlphaType alpha_type(bool hasAlpha) {
39     return hasAlpha ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
40 }
41
42 // Parse headers of RIFF container, and check for valid Webp (VP8) content.
43 // Returns an SkWebpCodec on success
44 SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
45     std::unique_ptr<SkStream> streamDeleter(stream);
46
47     // 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);
54
55         // 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) {
65         return nullptr;
66     }
67
68     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
83     WebPChunkIterator chunkIterator;
84     SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator);
85     sk_sp<SkColorSpace> colorSpace = nullptr;
86     bool unsupportedICC = false;
87     if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) {
88         colorSpace = SkColorSpace::MakeICC(chunkIterator.chunk.bytes, chunkIterator.chunk.size);
89         if (!colorSpace) {
90             unsupportedICC = true;
91         }
92     }
93     if (!colorSpace) {
94         colorSpace = SkColorSpace::MakeSRGB();
95     }
96
97     // Get the first frame and its "features" to determine the color and alpha types.
98     WebPIterator frame;
99     SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
100     if (!WebPDemuxGetFrame(demux, 1, &frame)) {
101         return nullptr;
102     }
103
104     WebPBitstreamFeatures features;
105     VP8StatusCode status = WebPGetFeatures(frame.fragment.bytes, frame.fragment.size, &features);
106     if (VP8_STATUS_OK != status) {
107         return nullptr;
108     }
109
110     const bool hasAlpha = SkToBool(frame.has_alpha)
111             || frame.width != width || frame.height != height;
112     SkEncodedInfo::Color color;
113     SkEncodedInfo::Alpha alpha;
114     switch (features.format) {
115         case 0:
116             // This indicates a "mixed" format.  We could see this for
117             // animated webps (multiple fragments).
118             // 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.
122             // 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             }
132             break;
133         case 1:
134             // This is the lossy format (YUV).
135             if (hasAlpha) {
136                 color = SkEncodedInfo::kYUVA_Color;
137                 alpha = SkEncodedInfo::kUnpremul_Alpha;
138             } else {
139                 color = SkEncodedInfo::kYUV_Color;
140                 alpha = SkEncodedInfo::kOpaque_Alpha;
141             }
142             break;
143         default:
144             return nullptr;
145     }
146
147     SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
148     SkWebpCodec* codecOut = new SkWebpCodec(width, height, info, std::move(colorSpace),
149                                             streamDeleter.release(), demux.release(),
150                                             std::move(data));
151     codecOut->setUnsupportedICC(unsupportedICC);
152     return codecOut;
153 }
154
155 SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const {
156     SkISize dim = this->getInfo().dimensions();
157     // 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));
161     return dim;
162 }
163
164 bool 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
170 static WEBP_CSP_MODE webp_decode_mode(const SkImageInfo& info) {
171     const bool premultiply = info.alphaType() == kPremul_SkAlphaType;
172     switch (info.colorType()) {
173         case kBGRA_8888_SkColorType:
174             return premultiply ? MODE_bgrA : MODE_BGRA;
175         case kRGBA_8888_SkColorType:
176             return premultiply ? MODE_rgbA : MODE_RGBA;
177         case kRGB_565_SkColorType:
178             return MODE_RGB_565;
179         default:
180             return MODE_LAST;
181     }
182 }
183
184 SkWebpCodec::Frame* SkWebpCodec::FrameHolder::appendNewFrame(bool hasAlpha) {
185     const int i = this->size();
186     fFrames.emplace_back(i, hasAlpha);
187     return &fFrames[i];
188 }
189
190 bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const {
191     if (!desiredSubset) {
192         return false;
193     }
194
195     SkIRect dimensions  = SkIRect::MakeSize(this->getInfo().dimensions());
196     if (!dimensions.contains(*desiredSubset)) {
197         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
208 int 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
222 int 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
269 const SkFrame* SkWebpCodec::FrameHolder::onGetFrame(int i) const {
270     return static_cast<const SkFrame*>(this->frame(i));
271 }
272
273 const SkWebpCodec::Frame* SkWebpCodec::FrameHolder::frame(int i) const {
274     SkASSERT(i >= 0 && i < this->size());
275     return &fFrames[i];
276 }
277
278 bool 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
300 static 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
310 static 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
336 static 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_<256> 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_<256> 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
383 SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
384                                          const Options& options, SkPMColor*, int*,
385                                          int* rowsDecodedPtr) {
386     // 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();
393     {
394         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;
412     }
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
424     WebPIterator frame;
425     SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
426     // If this succeeded in onGetFrameCount(), it should succeed again here.
427     SkAssertResult(WebPDemuxGetFrame(fDemux, index + 1, &frame));
428
429     const int requiredFrame = index == 0 ? kNone : fFrameHolder.frame(index)->getRequiredFrame();
430     // 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);
433     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         }
466     }
467
468     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;
509     SkISize srcSize = options.fSubset ? options.fSubset->size() : srcInfo.dimensions();
510     if (srcSize != dstInfo.dimensions()) {
511         config.options.use_scaling = 1;
512
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;
533     }
534
535     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;
547     auto webpInfo = dstInfo;
548     if (!frame.has_alpha) {
549         webpInfo = webpInfo.makeAlphaType(kOpaque_SkAlphaType);
550     }
551     if (this->colorXform()) {
552         // 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.
557         webpInfo = webpInfo.makeColorType(kBGRA_8888_SkColorType);
558
559         if (webpInfo.alphaType() == kPremul_SkAlphaType) {
560             webpInfo = webpInfo.makeAlphaType(kUnpremul_SkAlphaType);
561         }
562     }
563
564     if ((this->colorXform() && !is_8888(dstInfo.colorType())) || blendWithPrevFrame) {
565         // 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.
568         webpDst.allocPixels(webpInfo);
569     } else {
570         // libwebp can decode directly into the output memory.
571         webpDst.installPixels(webpInfo, dst, rowBytes);
572     }
573
574     config.output.colorspace = webp_decode_mode(webpInfo);
575     config.output.is_external_memory = 1;
576
577     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();
580
581     SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &config));
582     if (!idec) {
583         return kInvalidInput;
584     }
585
586     int rowsDecoded;
587     SkCodec::Result result;
588     switch (WebPIUpdate(idec, frame.fragment.bytes, frame.fragment.size)) {
589         case VP8_STATUS_OK:
590             rowsDecoded = scaledHeight;
591             result = kSuccess;
592             break;
593         case VP8_STATUS_SUSPENDED:
594             WebPIDecGetRGB(idec, &rowsDecoded, nullptr, nullptr, nullptr);
595             *rowsDecodedPtr = rowsDecoded + dstY;
596             result = kIncompleteInput;
597             break;
598         default:
599             return kInvalidInput;
600     }
601
602     // 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();
614     if (this->colorXform()) {
615         const auto dstColorFormat = select_xform_format(dstInfo.colorType());
616         const auto srcColorFormat = SkColorSpaceXform::kBGRA_8888_ColorFormat;
617         SkASSERT(select_xform_format(webpDst.colorType()) == srcColorFormat);
618
619         uint32_t* xformSrc = (uint32_t*) config.output.u.RGBA.rgba;
620         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         }
630         for (int y = 0; y < rowsDecoded; y++) {
631             SkAssertResult(this->colorXform()->apply(dstColorFormat, xformDst,
632                     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             }
640             xformSrc = SkTAddOffset<uint32_t>(xformSrc, srcRowBytes);
641         }
642     } 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         }
651     }
652
653     return result;
654 }
655
656 SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info,
657                          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))
662     , fFailed(false)
663 {
664     fFrameHolder.setScreenSize(width, height);
665 }