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 "SkBmpCodec.h"
9 #include "SkCodec.h"
10 #include "SkCodecPriv.h"
11 #include "SkColorSpace.h"
12 #include "SkData.h"
13 #include "SkFrameHolder.h"
14 #include "SkHalf.h"
15 #ifdef SK_HAS_HEIF_LIBRARY
16 #include "SkHeifCodec.h"
17 #endif
18 #include "SkIcoCodec.h"
19 #include "SkJpegCodec.h"
20 #ifdef SK_HAS_PNG_LIBRARY
21 #include "SkPngCodec.h"
22 #endif
23 #include "SkRawCodec.h"
24 #include "SkStream.h"
25 #include "SkWbmpCodec.h"
26 #include "SkWebpCodec.h"
27 #ifdef SK_HAS_WUFFS_LIBRARY
28 #include "SkWuffsCodec.h"
29 #else
30 #include "SkGifCodec.h"
31 #endif
32 
33 struct DecoderProc {
34     bool (*IsFormat)(const void*, size_t);
35     std::unique_ptr<SkCodec> (*MakeFromStream)(std::unique_ptr<SkStream>, SkCodec::Result*);
36 };
37 
38 static constexpr DecoderProc gDecoderProcs[] = {
39 #ifdef SK_HAS_JPEG_LIBRARY
40     { SkJpegCodec::IsJpeg, SkJpegCodec::MakeFromStream },
41 #endif
42 #ifdef SK_HAS_WEBP_LIBRARY
43     { SkWebpCodec::IsWebp, SkWebpCodec::MakeFromStream },
44 #endif
45 #ifdef SK_HAS_WUFFS_LIBRARY
46     { SkWuffsCodec_IsFormat, SkWuffsCodec_MakeFromStream },
47 #else
48     { SkGifCodec::IsGif, SkGifCodec::MakeFromStream },
49 #endif
50 #ifdef SK_HAS_PNG_LIBRARY
51     { SkIcoCodec::IsIco, SkIcoCodec::MakeFromStream },
52 #endif
53     { SkBmpCodec::IsBmp, SkBmpCodec::MakeFromStream },
54     { SkWbmpCodec::IsWbmp, SkWbmpCodec::MakeFromStream },
55 #ifdef SK_HAS_HEIF_LIBRARY
56     { SkHeifCodec::IsHeif, SkHeifCodec::MakeFromStream },
57 #endif
58 };
59 
60 std::unique_ptr<SkCodec> SkCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
61                                                  Result* outResult, SkPngChunkReader* chunkReader) {
62     Result resultStorage;
63     if (!outResult) {
64         outResult = &resultStorage;
65     }
66 
67     if (!stream) {
68         *outResult = kInvalidInput;
69         return nullptr;
70     }
71 
72     constexpr size_t bytesToRead = MinBufferedBytesNeeded();
73 
74     char buffer[bytesToRead];
75     size_t bytesRead = stream->peek(buffer, bytesToRead);
76 
77     // It is also possible to have a complete image less than bytesToRead bytes
78     // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead.
79     // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter
80     // than bytesToRead, so pass that directly to the decoder.
81     // It also is possible the stream uses too small a buffer for peeking, but
82     // we trust the caller to use a large enough buffer.
83 
84     if (0 == bytesRead) {
85         // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this
86         // printf could be useful to notice failures.
87         // SkCodecPrintf("Encoded image data failed to peek!\n");
88 
89         // It is possible the stream does not support peeking, but does support
90         // rewinding.
91         // Attempt to read() and pass the actual amount read to the decoder.
92         bytesRead = stream->read(buffer, bytesToRead);
93         if (!stream->rewind()) {
94             SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
95             *outResult = kCouldNotRewind;
96             return nullptr;
97         }
98     }
99 
100     // PNG is special, since we want to be able to supply an SkPngChunkReader.
101     // But this code follows the same pattern as the loop.
102 #ifdef SK_HAS_PNG_LIBRARY
103     if (SkPngCodec::IsPng(buffer, bytesRead)) {
104         return SkPngCodec::MakeFromStream(std::move(stream), outResult, chunkReader);
105     } else
106 #endif
107     {
108         for (DecoderProc proc : gDecoderProcs) {
109             if (proc.IsFormat(buffer, bytesRead)) {
110                 return proc.MakeFromStream(std::move(stream), outResult);
111             }
112         }
113 
114 #ifdef SK_CODEC_DECODES_RAW
115         // Try to treat the input as RAW if all the other checks failed.
116         return SkRawCodec::MakeFromStream(std::move(stream), outResult);
117 #endif
118     }
119 
120     if (bytesRead < bytesToRead) {
121         *outResult = kIncompleteInput;
122     } else {
123         *outResult = kUnimplemented;
124     }
125 
126     return nullptr;
127 }
128 
129 std::unique_ptr<SkCodec> SkCodec::MakeFromData(sk_sp<SkData> data, SkPngChunkReader* reader) {
130     if (!data) {
131         return nullptr;
132     }
133     return MakeFromStream(SkMemoryStream::Make(std::move(data)), nullptr, reader);
134 }
135 
136 SkCodec::SkCodec(SkEncodedInfo&& info, XformFormat srcFormat, std::unique_ptr<SkStream> stream,
137                  SkEncodedOrigin origin)
138     : fEncodedInfo(std::move(info))
139     , fSrcXformFormat(srcFormat)
140     , fStream(std::move(stream))
141     , fNeedsRewind(false)
142     , fOrigin(origin)
143     , fDstInfo()
144     , fOptions()
145     , fCurrScanline(-1)
146     , fStartedIncrementalDecode(false)
147 {}
148 
149 SkCodec::~SkCodec() {}
150 
151 bool SkCodec::conversionSupported(const SkImageInfo& dst, bool srcIsOpaque, bool needsColorXform) {
152     if (!valid_alpha(dst.alphaType(), srcIsOpaque)) {
153         return false;
154     }
155 
156     switch (dst.colorType()) {
157         case kRGBA_8888_SkColorType:
158         case kBGRA_8888_SkColorType:
159             return true;
160         case kRGBA_F16_SkColorType:
161             return dst.colorSpace();
162         case kRGB_565_SkColorType:
163             return srcIsOpaque;
164         case kGray_8_SkColorType:
165             return SkEncodedInfo::kGray_Color == fEncodedInfo.color() && srcIsOpaque;
166         case kAlpha_8_SkColorType:
167             // conceptually we can convert anything into alpha_8, but we haven't actually coded
168             // all of those other conversions yet.
169             return SkEncodedInfo::kXAlpha_Color == fEncodedInfo.color();
170         default:
171             return false;
172     }
173 }
174 
175 bool SkCodec::rewindIfNeeded() {
176     // Store the value of fNeedsRewind so we can update it. Next read will
177     // require a rewind.
178     const bool needsRewind = fNeedsRewind;
179     fNeedsRewind = true;
180     if (!needsRewind) {
181         return true;
182     }
183 
184     // startScanlineDecode will need to be called before decoding scanlines.
185     fCurrScanline = -1;
186     // startIncrementalDecode will need to be called before incrementalDecode.
187     fStartedIncrementalDecode = false;
188 
189     // Some codecs do not have a stream.  They may hold onto their own data or another codec.
190     // They must handle rewinding themselves.
191     if (fStream && !fStream->rewind()) {
192         return false;
193     }
194 
195     return this->onRewind();
196 }
197 
198 bool zero_rect(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes,
199                SkISize srcDimensions, SkIRect prevRect) {
200     const auto dimensions = dstInfo.dimensions();
201     if (dimensions != srcDimensions) {
202         SkRect src = SkRect::Make(srcDimensions);
203         SkRect dst = SkRect::Make(dimensions);
204         SkMatrix map = SkMatrix::MakeRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit);
205         SkRect asRect = SkRect::Make(prevRect);
206         if (!map.mapRect(&asRect)) {
207             return false;
208         }
209         asRect.roundIn(&prevRect);
210         if (prevRect.isEmpty()) {
211             // Down-scaling shrank the empty portion to nothing,
212             // so nothing to zero.
213             return true;
214         }
215     }
216 
217     if (!prevRect.intersect(dstInfo.bounds())) {
218         SkCodecPrintf("rectangles do not intersect!");
219         SkASSERT(false);
220         return true;
221     }
222 
223     const SkImageInfo info = dstInfo.makeWH(prevRect.width(), prevRect.height());
224     const size_t bpp = dstInfo.bytesPerPixel();
225     const size_t offset = prevRect.x() * bpp + prevRect.y() * rowBytes;
226     void* eraseDst = SkTAddOffset<void>(pixels, offset);
227     SkSampler::Fill(info, eraseDst, rowBytes, SkCodec::kNo_ZeroInitialized);
228     return true;
229 }
230 
231 SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels, size_t rowBytes,
232                                           const Options& options) {
233     const int index = options.fFrameIndex;
234     if (0 == index) {
235         return this->initializeColorXform(info, fEncodedInfo.alpha(), fEncodedInfo.opaque())
236             ? kSuccess : kInvalidConversion;
237     }
238 
239     if (index < 0) {
240         return kInvalidParameters;
241     }
242 
243     if (options.fSubset) {
244         // If we add support for this, we need to update the code that zeroes
245         // a kRestoreBGColor frame.
246         return kInvalidParameters;
247     }
248 
249     if (index >= this->onGetFrameCount()) {
250         return kIncompleteInput;
251     }
252 
253     const auto* frameHolder = this->getFrameHolder();
254     SkASSERT(frameHolder);
255 
256     const auto* frame = frameHolder->getFrame(index);
257     SkASSERT(frame);
258 
259     const int requiredFrame = frame->getRequiredFrame();
260     if (requiredFrame != kNoFrame) {
261         if (options.fPriorFrame != kNoFrame) {
262             // Check for a valid frame as a starting point. Alternatively, we could
263             // treat an invalid frame as not providing one, but rejecting it will
264             // make it easier to catch the mistake.
265             if (options.fPriorFrame < requiredFrame || options.fPriorFrame >= index) {
266                 return kInvalidParameters;
267             }
268             const auto* prevFrame = frameHolder->getFrame(options.fPriorFrame);
269             switch (prevFrame->getDisposalMethod()) {
270                 case SkCodecAnimation::DisposalMethod::kRestorePrevious:
271                     return kInvalidParameters;
272                 case SkCodecAnimation::DisposalMethod::kRestoreBGColor:
273                     // If a frame after the required frame is provided, there is no
274                     // need to clear, since it must be covered by the desired frame.
275                     if (options.fPriorFrame == requiredFrame) {
276                         SkIRect prevRect = prevFrame->frameRect();
277                         if (!zero_rect(info, pixels, rowBytes, this->dimensions(), prevRect)) {
278                             return kInternalError;
279                         }
280                     }
281                     break;
282                 default:
283                     break;
284             }
285         } else {
286             Options prevFrameOptions(options);
287             prevFrameOptions.fFrameIndex = requiredFrame;
288             prevFrameOptions.fZeroInitialized = kNo_ZeroInitialized;
289             const Result result = this->getPixels(info, pixels, rowBytes, &prevFrameOptions);
290             if (result != kSuccess) {
291                 return result;
292             }
293             const auto* prevFrame = frameHolder->getFrame(requiredFrame);
294             const auto disposalMethod = prevFrame->getDisposalMethod();
295             if (disposalMethod == SkCodecAnimation::DisposalMethod::kRestoreBGColor) {
296                 auto prevRect = prevFrame->frameRect();
297                 if (!zero_rect(info, pixels, rowBytes, this->dimensions(), prevRect)) {
298                     return kInternalError;
299                 }
300             }
301         }
302     }
303 
304     return this->initializeColorXform(info, frame->reportedAlpha(), !frame->hasAlpha())
305         ? kSuccess : kInvalidConversion;
306 }
307 
308 SkCodec::Result SkCodec::getPixels(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes,
309                                    const Options* options) {
310     SkImageInfo info = dstInfo;
311     if (!info.colorSpace()) {
312         info = info.makeColorSpace(SkColorSpace::MakeSRGB());
313     }
314 
315     if (kUnknown_SkColorType == info.colorType()) {
316         return kInvalidConversion;
317     }
318     if (nullptr == pixels) {
319         return kInvalidParameters;
320     }
321     if (rowBytes < info.minRowBytes()) {
322         return kInvalidParameters;
323     }
324 
325     if (!this->rewindIfNeeded()) {
326         return kCouldNotRewind;
327     }
328 
329     // Default options.
330     Options optsStorage;
331     if (nullptr == options) {
332         options = &optsStorage;
333     } else {
334         if (options->fSubset) {
335             SkIRect subset(*options->fSubset);
336             if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
337                 // FIXME: How to differentiate between not supporting subset at all
338                 // and not supporting this particular subset?
339                 return kUnimplemented;
340             }
341         }
342     }
343 
344     const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
345                                                            *options);
346     if (frameIndexResult != kSuccess) {
347         return frameIndexResult;
348     }
349 
350     // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
351     // because it supports arbitrary scaling/subset combinations.
352     if (!this->dimensionsSupported(info.dimensions())) {
353         return kInvalidScale;
354     }
355 
356     fDstInfo = info;
357     fOptions = *options;
358 
359     // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
360     // successfully.
361     int rowsDecoded = 0;
362     const Result result = this->onGetPixels(info, pixels, rowBytes, *options, &rowsDecoded);
363 
364     // A return value of kIncompleteInput indicates a truncated image stream.
365     // In this case, we will fill any uninitialized memory with a default value.
366     // Some subclasses will take care of filling any uninitialized memory on
367     // their own.  They indicate that all of the memory has been filled by
368     // setting rowsDecoded equal to the height.
369     if ((kIncompleteInput == result || kErrorInInput == result) && rowsDecoded != info.height()) {
370         // FIXME: (skbug.com/5772) fillIncompleteImage will fill using the swizzler's width, unless
371         // there is a subset. In that case, it will use the width of the subset. From here, the
372         // subset will only be non-null in the case of SkWebpCodec, but it treats the subset
373         // differenty from the other codecs, and it needs to use the width specified by the info.
374         // Set the subset to null so SkWebpCodec uses the correct width.
375         fOptions.fSubset = nullptr;
376         this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
377                 rowsDecoded);
378     }
379 
380     return result;
381 }
382 
383 SkCodec::Result SkCodec::startIncrementalDecode(const SkImageInfo& dstInfo, void* pixels,
384         size_t rowBytes, const SkCodec::Options* options) {
385     fStartedIncrementalDecode = false;
386 
387     SkImageInfo info = dstInfo;
388     if (!info.colorSpace()) {
389         info = info.makeColorSpace(SkColorSpace::MakeSRGB());
390     }
391     if (kUnknown_SkColorType == info.colorType()) {
392         return kInvalidConversion;
393     }
394     if (nullptr == pixels) {
395         return kInvalidParameters;
396     }
397 
398     // FIXME: If the rows come after the rows of a previous incremental decode,
399     // we might be able to skip the rewind, but only the implementation knows
400     // that. (e.g. PNG will always need to rewind, since we called longjmp, but
401     // a bottom-up BMP could skip rewinding if the new rows are above the old
402     // rows.)
403     if (!this->rewindIfNeeded()) {
404         return kCouldNotRewind;
405     }
406 
407     // Set options.
408     Options optsStorage;
409     if (nullptr == options) {
410         options = &optsStorage;
411     } else {
412         if (options->fSubset) {
413             SkIRect size = SkIRect::MakeSize(info.dimensions());
414             if (!size.contains(*options->fSubset)) {
415                 return kInvalidParameters;
416             }
417 
418             const int top = options->fSubset->top();
419             const int bottom = options->fSubset->bottom();
420             if (top < 0 || top >= info.height() || top >= bottom || bottom > info.height()) {
421                 return kInvalidParameters;
422             }
423         }
424     }
425 
426     const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
427                                                            *options);
428     if (frameIndexResult != kSuccess) {
429         return frameIndexResult;
430     }
431 
432     if (!this->dimensionsSupported(info.dimensions())) {
433         return kInvalidScale;
434     }
435 
436     fDstInfo = info;
437     fOptions = *options;
438 
439     const Result result = this->onStartIncrementalDecode(info, pixels, rowBytes, fOptions);
440     if (kSuccess == result) {
441         fStartedIncrementalDecode = true;
442     } else if (kUnimplemented == result) {
443         // FIXME: This is temporarily necessary, until we transition SkCodec
444         // implementations from scanline decoding to incremental decoding.
445         // SkAndroidCodec will first attempt to use incremental decoding, but
446         // will fall back to scanline decoding if incremental returns
447         // kUnimplemented. rewindIfNeeded(), above, set fNeedsRewind to true
448         // (after potentially rewinding), but we do not want the next call to
449         // startScanlineDecode() to do a rewind.
450         fNeedsRewind = false;
451     }
452     return result;
453 }
454 
455 
456 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo,
457         const SkCodec::Options* options) {
458     // Reset fCurrScanline in case of failure.
459     fCurrScanline = -1;
460 
461     SkImageInfo info = dstInfo;
462     if (!info.colorSpace()) {
463         info = info.makeColorSpace(SkColorSpace::MakeSRGB());
464     }
465 
466     if (!this->rewindIfNeeded()) {
467         return kCouldNotRewind;
468     }
469 
470     // Set options.
471     Options optsStorage;
472     if (nullptr == options) {
473         options = &optsStorage;
474     } else if (options->fSubset) {
475         SkIRect size = SkIRect::MakeSize(info.dimensions());
476         if (!size.contains(*options->fSubset)) {
477             return kInvalidInput;
478         }
479 
480         // We only support subsetting in the x-dimension for scanline decoder.
481         // Subsetting in the y-dimension can be accomplished using skipScanlines().
482         if (options->fSubset->top() != 0 || options->fSubset->height() != info.height()) {
483             return kInvalidInput;
484         }
485     }
486 
487     // Scanline decoding only supports decoding the first frame.
488     if (options->fFrameIndex != 0) {
489         return kUnimplemented;
490     }
491 
492     // The void* dst and rowbytes in handleFrameIndex or only used for decoding prior
493     // frames, which is not supported here anyway, so it is safe to pass nullptr/0.
494     const Result frameIndexResult = this->handleFrameIndex(info, nullptr, 0, *options);
495     if (frameIndexResult != kSuccess) {
496         return frameIndexResult;
497     }
498 
499     // FIXME: Support subsets somehow?
500     if (!this->dimensionsSupported(info.dimensions())) {
501         return kInvalidScale;
502     }
503 
504     const Result result = this->onStartScanlineDecode(info, *options);
505     if (result != SkCodec::kSuccess) {
506         return result;
507     }
508 
509     fCurrScanline = 0;
510     fDstInfo = info;
511     fOptions = *options;
512     return kSuccess;
513 }
514 
515 int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
516     if (fCurrScanline < 0) {
517         return 0;
518     }
519 
520     SkASSERT(!fDstInfo.isEmpty());
521     if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
522         return 0;
523     }
524 
525     const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
526     if (linesDecoded < countLines) {
527         this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized,
528                 countLines, linesDecoded);
529     }
530     fCurrScanline += countLines;
531     return linesDecoded;
532 }
533 
534 bool SkCodec::skipScanlines(int countLines) {
535     if (fCurrScanline < 0) {
536         return false;
537     }
538 
539     SkASSERT(!fDstInfo.isEmpty());
540     if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) {
541         // Arguably, we could just skip the scanlines which are remaining,
542         // and return true. We choose to return false so the client
543         // can catch their bug.
544         return false;
545     }
546 
547     bool result = this->onSkipScanlines(countLines);
548     fCurrScanline += countLines;
549     return result;
550 }
551 
552 int SkCodec::outputScanline(int inputScanline) const {
553     SkASSERT(0 <= inputScanline && inputScanline < fEncodedInfo.height());
554     return this->onOutputScanline(inputScanline);
555 }
556 
557 int SkCodec::onOutputScanline(int inputScanline) const {
558     switch (this->getScanlineOrder()) {
559         case kTopDown_SkScanlineOrder:
560             return inputScanline;
561         case kBottomUp_SkScanlineOrder:
562             return fEncodedInfo.height() - inputScanline - 1;
563         default:
564             // This case indicates an interlaced gif and is implemented by SkGifCodec.
565             SkASSERT(false);
566             return 0;
567     }
568 }
569 
570 void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
571         ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {
572     if (kYes_ZeroInitialized == zeroInit) {
573         return;
574     }
575 
576     const int linesRemaining = linesRequested - linesDecoded;
577     SkSampler* sampler = this->getSampler(false);
578 
579     const int fillWidth = sampler          ? sampler->fillWidth()      :
580                           fOptions.fSubset ? fOptions.fSubset->width() :
581                                              info.width()              ;
582     void* fillDst = this->getScanlineOrder() == kBottomUp_SkScanlineOrder ? dst :
583                         SkTAddOffset<void>(dst, linesDecoded * rowBytes);
584     const auto fillInfo = info.makeWH(fillWidth, linesRemaining);
585     SkSampler::Fill(fillInfo, fillDst, rowBytes, kNo_ZeroInitialized);
586 }
587 
588 bool sk_select_xform_format(SkColorType colorType, bool forColorTable,
589                             skcms_PixelFormat* outFormat) {
590     SkASSERT(outFormat);
591 
592     switch (colorType) {
593         case kRGBA_8888_SkColorType:
594             *outFormat = skcms_PixelFormat_RGBA_8888;
595             break;
596         case kBGRA_8888_SkColorType:
597             *outFormat = skcms_PixelFormat_BGRA_8888;
598             break;
599         case kRGB_565_SkColorType:
600             if (forColorTable) {
601 #ifdef SK_PMCOLOR_IS_RGBA
602                 *outFormat = skcms_PixelFormat_RGBA_8888;
603 #else
604                 *outFormat = skcms_PixelFormat_BGRA_8888;
605 #endif
606                 break;
607             }
608             *outFormat = skcms_PixelFormat_BGR_565;
609             break;
610         case kRGBA_F16_SkColorType:
611             *outFormat = skcms_PixelFormat_RGBA_hhhh;
612             break;
613         case kGray_8_SkColorType:
614             *outFormat = skcms_PixelFormat_G_8;
615             break;
616         default:
617             return false;
618     }
619     return true;
620 }
621 
622 bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha encodedAlpha,
623                                    bool srcIsOpaque) {
624     fXformTime = kNo_XformTime;
625     bool needsColorXform = false;
626     if (this->usesColorXform() && dstInfo.colorSpace()) {
627         dstInfo.colorSpace()->toProfile(&fDstProfile);
628         if (kRGBA_F16_SkColorType == dstInfo.colorType()) {
629             needsColorXform = true;
630         } else {
631             const auto* srcProfile = fEncodedInfo.profile();
632             if (!srcProfile) {
633                 srcProfile = skcms_sRGB_profile();
634             }
635             if (!skcms_ApproximatelyEqualProfiles(srcProfile, &fDstProfile) ) {
636                 needsColorXform = true;
637             }
638         }
639     }
640 
641     if (!this->conversionSupported(dstInfo, srcIsOpaque, needsColorXform)) {
642         return false;
643     }
644 
645     if (needsColorXform) {
646         fXformTime = SkEncodedInfo::kPalette_Color != fEncodedInfo.color()
647                           || kRGBA_F16_SkColorType == dstInfo.colorType()
648                 ? kDecodeRow_XformTime : kPalette_XformTime;
649         if (!sk_select_xform_format(dstInfo.colorType(), fXformTime == kPalette_XformTime,
650                                     &fDstXformFormat)) {
651             return false;
652         }
653         if (encodedAlpha == SkEncodedInfo::kUnpremul_Alpha
654                 && dstInfo.alphaType() == kPremul_SkAlphaType) {
655             fDstXformAlphaFormat = skcms_AlphaFormat_PremulAsEncoded;
656         } else {
657             fDstXformAlphaFormat = skcms_AlphaFormat_Unpremul;
658         }
659     }
660     return true;
661 }
662 
663 void SkCodec::applyColorXform(void* dst, const void* src, int count) const {
664     // It is okay for srcProfile to be null. This will use sRGB.
665     const auto* srcProfile = fEncodedInfo.profile();
666     SkAssertResult(skcms_Transform(src, fSrcXformFormat, skcms_AlphaFormat_Unpremul, srcProfile,
667                                    dst, fDstXformFormat, fDstXformAlphaFormat, &fDstProfile,
668                                    count));
669 }
670 
671 std::vector<SkCodec::FrameInfo> SkCodec::getFrameInfo() {
672     const int frameCount = this->getFrameCount();
673     SkASSERT(frameCount >= 0);
674     if (frameCount <= 0) {
675         return std::vector<FrameInfo>{};
676     }
677 
678     if (frameCount == 1 && !this->onGetFrameInfo(0, nullptr)) {
679         // Not animated.
680         return std::vector<FrameInfo>{};
681     }
682 
683     std::vector<FrameInfo> result(frameCount);
684     for (int i = 0; i < frameCount; ++i) {
685         SkAssertResult(this->onGetFrameInfo(i, &result[i]));
686     }
687     return result;
688 }
689 
690 const char* SkCodec::ResultToString(Result result) {
691     switch (result) {
692         case kSuccess:
693             return "success";
694         case kIncompleteInput:
695             return "incomplete input";
696         case kErrorInInput:
697             return "error in input";
698         case kInvalidConversion:
699             return "invalid conversion";
700         case kInvalidScale:
701             return "invalid scale";
702         case kInvalidParameters:
703             return "invalid parameters";
704         case kInvalidInput:
705             return "invalid input";
706         case kCouldNotRewind:
707             return "could not rewind";
708         case kInternalError:
709             return "internal error";
710         case kUnimplemented:
711             return "unimplemented";
712         default:
713             SkASSERT(false);
714             return "bogus result value";
715     }
716 }
717 
718 static SkIRect frame_rect_on_screen(SkIRect frameRect,
719                                     const SkIRect& screenRect) {
720     if (!frameRect.intersect(screenRect)) {
721         return SkIRect::MakeEmpty();
722     }
723 
724     return frameRect;
725 }
726 
727 static bool independent(const SkFrame& frame) {
728     return frame.getRequiredFrame() == SkCodec::kNoFrame;
729 }
730 
731 static bool restore_bg(const SkFrame& frame) {
732     return frame.getDisposalMethod() == SkCodecAnimation::DisposalMethod::kRestoreBGColor;
733 }
734 
735 void SkFrameHolder::setAlphaAndRequiredFrame(SkFrame* frame) {
736     const bool reportsAlpha = frame->reportedAlpha() != SkEncodedInfo::kOpaque_Alpha;
737     const auto screenRect = SkIRect::MakeWH(fScreenWidth, fScreenHeight);
738     const auto frameRect = frame_rect_on_screen(frame->frameRect(), screenRect);
739 
740     const int i = frame->frameId();
741     if (0 == i) {
742         frame->setHasAlpha(reportsAlpha || frameRect != screenRect);
743         frame->setRequiredFrame(SkCodec::kNoFrame);
744         return;
745     }
746 
747 
748     const bool blendWithPrevFrame = frame->getBlend() == SkCodecAnimation::Blend::kPriorFrame;
749     if ((!reportsAlpha || !blendWithPrevFrame) && frameRect == screenRect) {
750         frame->setHasAlpha(reportsAlpha);
751         frame->setRequiredFrame(SkCodec::kNoFrame);
752         return;
753     }
754 
755     const SkFrame* prevFrame = this->getFrame(i-1);
756     while (prevFrame->getDisposalMethod() == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
757         const int prevId = prevFrame->frameId();
758         if (0 == prevId) {
759             frame->setHasAlpha(true);
760             frame->setRequiredFrame(SkCodec::kNoFrame);
761             return;
762         }
763 
764         prevFrame = this->getFrame(prevId - 1);
765     }
766 
767     const bool clearPrevFrame = restore_bg(*prevFrame);
768     auto prevFrameRect = frame_rect_on_screen(prevFrame->frameRect(), screenRect);
769 
770     if (clearPrevFrame) {
771         if (prevFrameRect == screenRect || independent(*prevFrame)) {
772             frame->setHasAlpha(true);
773             frame->setRequiredFrame(SkCodec::kNoFrame);
774             return;
775         }
776     }
777 
778     if (reportsAlpha && blendWithPrevFrame) {
779         // Note: We could be more aggressive here. If prevFrame clears
780         // to background color and covers its required frame (and that
781         // frame is independent), prevFrame could be marked independent.
782         // Would this extra complexity be worth it?
783         frame->setRequiredFrame(prevFrame->frameId());
784         frame->setHasAlpha(prevFrame->hasAlpha() || clearPrevFrame);
785         return;
786     }
787 
788     while (frameRect.contains(prevFrameRect)) {
789         const int prevRequiredFrame = prevFrame->getRequiredFrame();
790         if (prevRequiredFrame == SkCodec::kNoFrame) {
791             frame->setRequiredFrame(SkCodec::kNoFrame);
792             frame->setHasAlpha(true);
793             return;
794         }
795 
796         prevFrame = this->getFrame(prevRequiredFrame);
797         prevFrameRect = frame_rect_on_screen(prevFrame->frameRect(), screenRect);
798     }
799 
800     if (restore_bg(*prevFrame)) {
801         frame->setHasAlpha(true);
802         if (prevFrameRect == screenRect || independent(*prevFrame)) {
803             frame->setRequiredFrame(SkCodec::kNoFrame);
804         } else {
805             // Note: As above, frame could still be independent, e.g. if
806             // prevFrame covers its required frame and that frame is
807             // independent.
808             frame->setRequiredFrame(prevFrame->frameId());
809         }
810         return;
811     }
812 
813     SkASSERT(prevFrame->getDisposalMethod() == SkCodecAnimation::DisposalMethod::kKeep);
814     frame->setRequiredFrame(prevFrame->frameId());
815     frame->setHasAlpha(prevFrame->hasAlpha() || (reportsAlpha && !blendWithPrevFrame));
816 }
817 
818