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 "include/codec/SkCodec.h"
9
10#include "include/codec/SkCodecAnimation.h"
11#include "include/core/SkAlphaType.h"
12#include "include/core/SkBitmap.h"
13#include "include/core/SkColorPriv.h"
14#include "include/core/SkColorSpace.h"
15#include "include/core/SkColorType.h"
16#include "include/core/SkData.h"
17#include "include/core/SkImage.h" // IWYU pragma: keep
18#include "include/core/SkMatrix.h"
19#include "include/core/SkStream.h"
20#include "include/private/base/SkTemplates.h"
21#include "modules/skcms/skcms.h"
22#include "src/base/SkNoDestructor.h"
23#include "src/codec/SkCodecPriv.h"
24#include "src/codec/SkFrameHolder.h"
25#include "src/codec/SkSampler.h"
26
27#include <string_view>
28#include <utility>
29
30#if !defined(SK_DISABLE_LEGACY_INIT_DECODERS)
31#include "include/codec/SkBmpDecoder.h"
32#include "include/codec/SkWbmpDecoder.h"
33#include "include/private/base/SkOnce.h"
34
35#ifdef SK_CODEC_DECODES_AVIF
36#include "include/codec/SkAvifDecoder.h"
37#endif
38
39#ifdef SK_HAS_WUFFS_LIBRARY
40#include "include/codec/SkGifDecoder.h"
41#endif
42
43#ifdef SK_HAS_HEIF_LIBRARY
44#include "include/android/SkHeifDecoder.h"
45#endif
46
47#ifdef SK_CODEC_DECODES_JPEG
48#include "include/codec/SkJpegDecoder.h"
49#endif
50
51#ifdef SK_CODEC_DECODES_JPEGXL
52#include "include/codec/SkJpegxlDecoder.h"
53#endif
54
55#ifdef SK_CODEC_DECODES_PNG
56#include "include/codec/SkIcoDecoder.h"
57#include "include/codec/SkPngDecoder.h"
58#endif
59
60#ifdef SK_CODEC_DECODES_RAW
61#include "include/codec/SkRawDecoder.h"
62#endif
63
64#ifdef SK_CODEC_DECODES_WEBP
65#include "include/codec/SkWebpDecoder.h"
66#endif
67#endif // !defined(SK_DISABLE_LEGACY_INIT_DECODERS)
68
69namespace SkCodecs {
70// A static variable inside a function avoids a static initializer.
71// https://chromium.googlesource.com/chromium/src/+/HEAD/docs/static_initializers.md#removing-static-initializers
72static std::vector<Decoder>* get_decoders_for_editing() {
73 static SkNoDestructor<std::vector<Decoder>> decoders;
74#if !defined(SK_DISABLE_LEGACY_INIT_DECODERS)
75 static SkOnce once;
76 once([] {
77 if (decoders->empty()) {
78#ifdef SK_CODEC_DECODES_PNG
79 decoders->push_back(x: SkPngDecoder::Decoder());
80#endif
81#ifdef SK_CODEC_DECODES_JPEG
82 decoders->push_back(x: SkJpegDecoder::Decoder());
83#endif
84#ifdef SK_CODEC_DECODES_WEBP
85 decoders->push_back(x: SkWebpDecoder::Decoder());
86#endif
87#ifdef SK_HAS_WUFFS_LIBRARY
88 decoders->push_back(x: SkGifDecoder::Decoder());
89#endif
90#ifdef SK_CODEC_DECODES_PNG
91 decoders->push_back(x: SkIcoDecoder::Decoder());
92#endif
93 decoders->push_back(x: SkBmpDecoder::Decoder());
94 decoders->push_back(x: SkWbmpDecoder::Decoder());
95#ifdef SK_CODEC_DECODES_AVIF
96 decoders->push_back(SkAvifDecoder::Decoder());
97#endif
98#ifdef SK_CODEC_DECODES_JPEGXL
99 decoders->push_back(SkJpegxlDecoder::Decoder());
100#endif
101#ifdef SK_HAS_HEIF_LIBRARY
102 decoders->push_back(SkHeifDecoder::Decoder());
103#endif
104#ifdef SK_CODEC_DECODES_RAW
105 decoders->push_back(SkRawDecoder::Decoder());
106#endif
107 }
108 });
109#endif // !defined(SK_DISABLE_LEGACY_INIT_DECODERS)
110 return decoders.get();
111}
112
113const std::vector<Decoder>& get_decoders() {
114 auto decoders = get_decoders_for_editing();
115 return *decoders;
116}
117
118void Register(Decoder d) {
119 auto decoders = get_decoders_for_editing();
120 for (size_t i = 0; i < decoders->size(); i++) {
121 if ((*decoders)[i].id == d.id) {
122 (*decoders)[i] = d;
123 return;
124 }
125 }
126 decoders->push_back(x: d);
127}
128
129bool HasDecoder(std::string_view id) {
130 for (const SkCodecs::Decoder& decoder : get_decoders()) {
131 if (decoder.id == id) {
132 return true;
133 }
134 }
135 return false;
136}
137
138} // namespace SkCodecs
139
140std::unique_ptr<SkCodec> SkCodec::MakeFromStream(
141 std::unique_ptr<SkStream> stream, Result* outResult,
142 SkPngChunkReader* chunkReader, SelectionPolicy selectionPolicy) {
143 Result resultStorage;
144 if (!outResult) {
145 outResult = &resultStorage;
146 }
147
148 if (!stream) {
149 *outResult = kInvalidInput;
150 return nullptr;
151 }
152
153 if (selectionPolicy != SelectionPolicy::kPreferStillImage
154 && selectionPolicy != SelectionPolicy::kPreferAnimation) {
155 *outResult = kInvalidParameters;
156 return nullptr;
157 }
158
159 constexpr size_t bytesToRead = MinBufferedBytesNeeded();
160
161 char buffer[bytesToRead];
162 size_t bytesRead = stream->peek(buffer, bytesToRead);
163
164 // It is also possible to have a complete image less than bytesToRead bytes
165 // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead.
166 // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter
167 // than bytesToRead, so pass that directly to the decoder.
168 // It also is possible the stream uses too small a buffer for peeking, but
169 // we trust the caller to use a large enough buffer.
170
171 if (0 == bytesRead) {
172 // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this
173 // printf could be useful to notice failures.
174 // SkCodecPrintf("Encoded image data failed to peek!\n");
175
176 // It is possible the stream does not support peeking, but does support
177 // rewinding.
178 // Attempt to read() and pass the actual amount read to the decoder.
179 bytesRead = stream->read(buffer, size: bytesToRead);
180 if (!stream->rewind()) {
181 SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
182 *outResult = kCouldNotRewind;
183 return nullptr;
184 }
185 }
186
187 SkCodecs::MakeFromStreamCallback rawFallback = nullptr;
188 auto decoders = SkCodecs::get_decoders();
189 for (const SkCodecs::Decoder& proc : decoders) {
190 if (proc.isFormat(buffer, bytesRead)) {
191 // png and heif are special, since we want to be able to supply a SkPngChunkReader
192 // or SelectionPolicy respectively
193 if (proc.id == "png") {
194 return proc.makeFromStream(std::move(stream), outResult, chunkReader);
195 } else if (proc.id == "heif") {
196 return proc.makeFromStream(std::move(stream), outResult, &selectionPolicy);
197 } else if (proc.id == "raw") {
198 rawFallback = proc.makeFromStream;
199 continue;
200 }
201 return proc.makeFromStream(std::move(stream), outResult, nullptr);
202 }
203 }
204 if (rawFallback != nullptr) {
205 // Fallback to raw.
206 return rawFallback(std::move(stream), outResult, nullptr);
207 }
208
209 if (bytesRead < bytesToRead) {
210 *outResult = kIncompleteInput;
211 } else {
212 *outResult = kUnimplemented;
213 }
214 return nullptr;
215}
216
217std::unique_ptr<SkCodec> SkCodec::MakeFromData(sk_sp<SkData> data, SkPngChunkReader* reader) {
218 if (!data) {
219 return nullptr;
220 }
221 return MakeFromStream(stream: SkMemoryStream::Make(data: std::move(data)), outResult: nullptr, chunkReader: reader);
222}
223
224SkCodec::SkCodec(SkEncodedInfo&& info,
225 XformFormat srcFormat,
226 std::unique_ptr<SkStream> stream,
227 SkEncodedOrigin origin)
228 : fEncodedInfo(std::move(info))
229 , fSrcXformFormat(srcFormat)
230 , fStream(std::move(stream))
231 , fOrigin(origin)
232 , fDstInfo()
233 , fOptions() {}
234
235SkCodec::~SkCodec() {}
236
237void SkCodec::setSrcXformFormat(XformFormat pixelFormat) {
238 fSrcXformFormat = pixelFormat;
239}
240
241bool SkCodec::queryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& supportedDataTypes,
242 SkYUVAPixmapInfo* yuvaPixmapInfo) const {
243 if (!yuvaPixmapInfo) {
244 return false;
245 }
246 return this->onQueryYUVAInfo(supportedDataTypes, yuvaPixmapInfo) &&
247 yuvaPixmapInfo->isSupported(supportedDataTypes);
248}
249
250SkCodec::Result SkCodec::getYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps) {
251 if (!yuvaPixmaps.isValid()) {
252 return kInvalidInput;
253 }
254 if (!this->rewindIfNeeded()) {
255 return kCouldNotRewind;
256 }
257 return this->onGetYUVAPlanes(yuvaPixmaps);
258}
259
260bool SkCodec::conversionSupported(const SkImageInfo& dst, bool srcIsOpaque, bool needsColorXform) {
261 if (!valid_alpha(dstAlpha: dst.alphaType(), srcIsOpaque)) {
262 return false;
263 }
264
265 switch (dst.colorType()) {
266 case kRGBA_8888_SkColorType:
267 case kBGRA_8888_SkColorType:
268 case kRGBA_F16_SkColorType:
269 return true;
270 case kBGR_101010x_XR_SkColorType:
271 case kRGB_565_SkColorType:
272 return srcIsOpaque;
273 case kGray_8_SkColorType:
274 return SkEncodedInfo::kGray_Color == fEncodedInfo.color() && srcIsOpaque;
275 case kAlpha_8_SkColorType:
276 // conceptually we can convert anything into alpha_8, but we haven't actually coded
277 // all of those other conversions yet.
278 return SkEncodedInfo::kXAlpha_Color == fEncodedInfo.color();
279 default:
280 return false;
281 }
282}
283
284bool SkCodec::rewindIfNeeded() {
285 // Store the value of fNeedsRewind so we can update it. Next read will
286 // require a rewind.
287 const bool needsRewind = fNeedsRewind;
288 fNeedsRewind = true;
289 if (!needsRewind) {
290 return true;
291 }
292
293 // startScanlineDecode will need to be called before decoding scanlines.
294 fCurrScanline = -1;
295 // startIncrementalDecode will need to be called before incrementalDecode.
296 fStartedIncrementalDecode = false;
297
298 // Some codecs do not have a stream. They may hold onto their own data or another codec.
299 // They must handle rewinding themselves.
300 if (fStream && !fStream->rewind()) {
301 return false;
302 }
303
304 return this->onRewind();
305}
306
307static SkIRect frame_rect_on_screen(SkIRect frameRect,
308 const SkIRect& screenRect) {
309 if (!frameRect.intersect(r: screenRect)) {
310 return SkIRect::MakeEmpty();
311 }
312
313 return frameRect;
314}
315
316bool zero_rect(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes,
317 SkISize srcDimensions, SkIRect prevRect) {
318 const auto dimensions = dstInfo.dimensions();
319 if (dimensions != srcDimensions) {
320 SkRect src = SkRect::Make(size: srcDimensions);
321 SkRect dst = SkRect::Make(size: dimensions);
322 SkMatrix map = SkMatrix::RectToRect(src, dst);
323 SkRect asRect = SkRect::Make(irect: prevRect);
324 if (!map.mapRect(rect: &asRect)) {
325 return false;
326 }
327 asRect.roundOut(dst: &prevRect);
328 }
329
330 if (!prevRect.intersect(r: SkIRect::MakeSize(size: dimensions))) {
331 // Nothing to zero, due to scaling or bad frame rect.
332 return true;
333 }
334
335 const SkImageInfo info = dstInfo.makeDimensions(newSize: prevRect.size());
336 const size_t bpp = dstInfo.bytesPerPixel();
337 const size_t offset = prevRect.x() * bpp + prevRect.y() * rowBytes;
338 void* eraseDst = SkTAddOffset<void>(ptr: pixels, byteOffset: offset);
339 SkSampler::Fill(info, dst: eraseDst, rowBytes, zeroInit: SkCodec::kNo_ZeroInitialized);
340 return true;
341}
342
343SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels, size_t rowBytes,
344 const Options& options, GetPixelsCallback getPixelsFn) {
345 if (getPixelsFn) {
346 // If a callback is used, it handles the frame index, so calls from this SkCodec
347 // should always short-circuit in the else case below.
348 fUsingCallbackForHandleFrameIndex = true;
349 } else if (fUsingCallbackForHandleFrameIndex) {
350 return kSuccess;
351 }
352
353 if (!this->rewindIfNeeded()) {
354 return kCouldNotRewind;
355 }
356
357 const int index = options.fFrameIndex;
358 if (0 == index) {
359 return this->initializeColorXform(dstInfo: info, fEncodedInfo.alpha(), srcIsOpaque: fEncodedInfo.opaque())
360 ? kSuccess : kInvalidConversion;
361 }
362
363 if (index < 0) {
364 return kInvalidParameters;
365 }
366
367 if (options.fSubset) {
368 // If we add support for this, we need to update the code that zeroes
369 // a kRestoreBGColor frame.
370 return kInvalidParameters;
371 }
372
373 if (index >= this->onGetFrameCount()) {
374 return kIncompleteInput;
375 }
376
377 const auto* frameHolder = this->getFrameHolder();
378 SkASSERT(frameHolder);
379
380 const auto* frame = frameHolder->getFrame(i: index);
381 SkASSERT(frame);
382
383 const int requiredFrame = frame->getRequiredFrame();
384 if (requiredFrame != kNoFrame) {
385 // Decode earlier frame if necessary
386 const SkFrame* preppedFrame = nullptr;
387 if (options.fPriorFrame == kNoFrame) {
388 Result result = kInternalError;
389 // getPixelsFn will be set when things like SkAndroidCodec are calling this function.
390 // Thus, we call the provided function when recursively decoding previous frames,
391 // but only when necessary (i.e. there is a required frame).
392 if (getPixelsFn) {
393 result = getPixelsFn(info, pixels, rowBytes, options, requiredFrame);
394 } else {
395 Options prevFrameOptions(options);
396 prevFrameOptions.fFrameIndex = requiredFrame;
397 result = this->getPixels(info, pixels, rowBytes, &prevFrameOptions);
398 }
399 if (result != kSuccess) {
400 return result;
401 }
402 preppedFrame = frameHolder->getFrame(i: requiredFrame);
403 } else {
404 // Check for a valid frame as a starting point. Alternatively, we could
405 // treat an invalid frame as not providing one, but rejecting it will
406 // make it easier to catch the mistake.
407 if (options.fPriorFrame < requiredFrame || options.fPriorFrame >= index) {
408 return kInvalidParameters;
409 }
410 preppedFrame = frameHolder->getFrame(i: options.fPriorFrame);
411 }
412
413 SkASSERT(preppedFrame);
414 switch (preppedFrame->getDisposalMethod()) {
415 case SkCodecAnimation::DisposalMethod::kRestorePrevious:
416 SkASSERT(options.fPriorFrame != kNoFrame);
417 return kInvalidParameters;
418 case SkCodecAnimation::DisposalMethod::kRestoreBGColor:
419 // If a frame after the required frame is provided, there is no
420 // need to clear, since it must be covered by the desired frame.
421 // FIXME: If the required frame is kRestoreBGColor, we don't actually need to decode
422 // it, since we'll just clear it to transparent. Instead, we could decode *its*
423 // required frame and then clear.
424 if (preppedFrame->frameId() == requiredFrame) {
425 SkIRect preppedRect = preppedFrame->frameRect();
426 if (!zero_rect(dstInfo: info, pixels, rowBytes, srcDimensions: this->dimensions(), prevRect: preppedRect)) {
427 return kInternalError;
428 }
429 }
430 break;
431 default:
432 break;
433 }
434 }
435
436 return this->initializeColorXform(dstInfo: info, frame->reportedAlpha(), srcIsOpaque: !frame->hasAlpha())
437 ? kSuccess : kInvalidConversion;
438}
439
440SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
441 const Options* options) {
442 if (kUnknown_SkColorType == info.colorType()) {
443 return kInvalidConversion;
444 }
445 if (nullptr == pixels) {
446 return kInvalidParameters;
447 }
448 if (rowBytes < info.minRowBytes()) {
449 return kInvalidParameters;
450 }
451
452 // Default options.
453 Options optsStorage;
454 if (nullptr == options) {
455 options = &optsStorage;
456 } else {
457 if (options->fSubset) {
458 SkIRect subset(*options->fSubset);
459 if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
460 // FIXME: How to differentiate between not supporting subset at all
461 // and not supporting this particular subset?
462 return kUnimplemented;
463 }
464 }
465 }
466
467 const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
468 options: *options);
469 if (frameIndexResult != kSuccess) {
470 return frameIndexResult;
471 }
472
473 // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
474 // because it supports arbitrary scaling/subset combinations.
475 if (!this->dimensionsSupported(dim: info.dimensions())) {
476 return kInvalidScale;
477 }
478
479 fDstInfo = info;
480 fOptions = *options;
481
482 // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
483 // successfully.
484 int rowsDecoded = 0;
485 const Result result = this->onGetPixels(info, pixels, rowBytes, *options, rowsDecoded: &rowsDecoded);
486
487 // A return value of kIncompleteInput indicates a truncated image stream.
488 // In this case, we will fill any uninitialized memory with a default value.
489 // Some subclasses will take care of filling any uninitialized memory on
490 // their own. They indicate that all of the memory has been filled by
491 // setting rowsDecoded equal to the height.
492 if ((kIncompleteInput == result || kErrorInInput == result) && rowsDecoded != info.height()) {
493 // FIXME: (skbug.com/5772) fillIncompleteImage will fill using the swizzler's width, unless
494 // there is a subset. In that case, it will use the width of the subset. From here, the
495 // subset will only be non-null in the case of SkWebpCodec, but it treats the subset
496 // differenty from the other codecs, and it needs to use the width specified by the info.
497 // Set the subset to null so SkWebpCodec uses the correct width.
498 fOptions.fSubset = nullptr;
499 this->fillIncompleteImage(dstInfo: info, dst: pixels, rowBytes, zeroInit: options->fZeroInitialized, linesRequested: info.height(),
500 linesDecoded: rowsDecoded);
501 }
502
503 return result;
504}
505
506std::tuple<sk_sp<SkImage>, SkCodec::Result> SkCodec::getImage(const SkImageInfo& info,
507 const Options* options) {
508 SkBitmap bm;
509 if (!bm.tryAllocPixels(info)) {
510 return {nullptr, kInternalError};
511 }
512
513 Result result = this->getPixels(info, pixels: bm.getPixels(), rowBytes: bm.rowBytes(), options);
514 switch (result) {
515 case kSuccess:
516 case kIncompleteInput:
517 case kErrorInInput:
518 bm.setImmutable();
519 return {bm.asImage(), result};
520
521 default: break;
522 }
523 return {nullptr, result};
524}
525
526std::tuple<sk_sp<SkImage>, SkCodec::Result> SkCodec::getImage() {
527 return this->getImage(info: this->getInfo(), options: nullptr);
528}
529
530SkCodec::Result SkCodec::startIncrementalDecode(const SkImageInfo& info, void* pixels,
531 size_t rowBytes, const SkCodec::Options* options) {
532 fStartedIncrementalDecode = false;
533
534 if (kUnknown_SkColorType == info.colorType()) {
535 return kInvalidConversion;
536 }
537 if (nullptr == pixels) {
538 return kInvalidParameters;
539 }
540
541 // Set options.
542 Options optsStorage;
543 if (nullptr == options) {
544 options = &optsStorage;
545 } else {
546 if (options->fSubset) {
547 SkIRect size = SkIRect::MakeSize(size: info.dimensions());
548 if (!size.contains(r: *options->fSubset)) {
549 return kInvalidParameters;
550 }
551
552 const int top = options->fSubset->top();
553 const int bottom = options->fSubset->bottom();
554 if (top < 0 || top >= info.height() || top >= bottom || bottom > info.height()) {
555 return kInvalidParameters;
556 }
557 }
558 }
559
560 const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
561 options: *options);
562 if (frameIndexResult != kSuccess) {
563 return frameIndexResult;
564 }
565
566 if (!this->dimensionsSupported(dim: info.dimensions())) {
567 return kInvalidScale;
568 }
569
570 fDstInfo = info;
571 fOptions = *options;
572
573 const Result result = this->onStartIncrementalDecode(info, pixels, rowBytes, fOptions);
574 if (kSuccess == result) {
575 fStartedIncrementalDecode = true;
576 } else if (kUnimplemented == result) {
577 // FIXME: This is temporarily necessary, until we transition SkCodec
578 // implementations from scanline decoding to incremental decoding.
579 // SkAndroidCodec will first attempt to use incremental decoding, but
580 // will fall back to scanline decoding if incremental returns
581 // kUnimplemented. rewindIfNeeded(), above, set fNeedsRewind to true
582 // (after potentially rewinding), but we do not want the next call to
583 // startScanlineDecode() to do a rewind.
584 fNeedsRewind = false;
585 }
586 return result;
587}
588
589
590SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info,
591 const SkCodec::Options* options) {
592 // Reset fCurrScanline in case of failure.
593 fCurrScanline = -1;
594
595 // Set options.
596 Options optsStorage;
597 if (nullptr == options) {
598 options = &optsStorage;
599 } else if (options->fSubset) {
600 SkIRect size = SkIRect::MakeSize(size: info.dimensions());
601 if (!size.contains(r: *options->fSubset)) {
602 return kInvalidInput;
603 }
604
605 // We only support subsetting in the x-dimension for scanline decoder.
606 // Subsetting in the y-dimension can be accomplished using skipScanlines().
607 if (options->fSubset->top() != 0 || options->fSubset->height() != info.height()) {
608 return kInvalidInput;
609 }
610 }
611
612 // Scanline decoding only supports decoding the first frame.
613 if (options->fFrameIndex != 0) {
614 return kUnimplemented;
615 }
616
617 // The void* dst and rowbytes in handleFrameIndex or only used for decoding prior
618 // frames, which is not supported here anyway, so it is safe to pass nullptr/0.
619 const Result frameIndexResult = this->handleFrameIndex(info, pixels: nullptr, rowBytes: 0, options: *options);
620 if (frameIndexResult != kSuccess) {
621 return frameIndexResult;
622 }
623
624 // FIXME: Support subsets somehow?
625 if (!this->dimensionsSupported(dim: info.dimensions())) {
626 return kInvalidScale;
627 }
628
629 const Result result = this->onStartScanlineDecode(info, *options);
630 if (result != SkCodec::kSuccess) {
631 return result;
632 }
633
634 // FIXME: See startIncrementalDecode. That method set fNeedsRewind to false
635 // so that when onStartScanlineDecode calls rewindIfNeeded it would not
636 // rewind. But it also relies on that call to rewindIfNeeded to set
637 // fNeedsRewind to true for future decodes. When
638 // fUsingCallbackForHandleFrameIndex is true, that call to rewindIfNeeded is
639 // skipped, so this method sets it back to true.
640 SkASSERT(fUsingCallbackForHandleFrameIndex || fNeedsRewind);
641 fNeedsRewind = true;
642
643 fCurrScanline = 0;
644 fDstInfo = info;
645 fOptions = *options;
646 return kSuccess;
647}
648
649int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
650 if (fCurrScanline < 0) {
651 return 0;
652 }
653
654 SkASSERT(!fDstInfo.isEmpty());
655 if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
656 return 0;
657 }
658
659 const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
660 if (linesDecoded < countLines) {
661 this->fillIncompleteImage(dstInfo: this->dstInfo(), dst, rowBytes, zeroInit: this->options().fZeroInitialized,
662 linesRequested: countLines, linesDecoded);
663 }
664 fCurrScanline += countLines;
665 return linesDecoded;
666}
667
668bool SkCodec::skipScanlines(int countLines) {
669 if (fCurrScanline < 0) {
670 return false;
671 }
672
673 SkASSERT(!fDstInfo.isEmpty());
674 if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) {
675 // Arguably, we could just skip the scanlines which are remaining,
676 // and return true. We choose to return false so the client
677 // can catch their bug.
678 return false;
679 }
680
681 bool result = this->onSkipScanlines(countLines);
682 fCurrScanline += countLines;
683 return result;
684}
685
686int SkCodec::outputScanline(int inputScanline) const {
687 SkASSERT(0 <= inputScanline && inputScanline < fEncodedInfo.height());
688 return this->onOutputScanline(inputScanline);
689}
690
691int SkCodec::onOutputScanline(int inputScanline) const {
692 switch (this->getScanlineOrder()) {
693 case kTopDown_SkScanlineOrder:
694 return inputScanline;
695 case kBottomUp_SkScanlineOrder:
696 return fEncodedInfo.height() - inputScanline - 1;
697 default:
698 // This case indicates an interlaced gif and is implemented by SkGifCodec.
699 SkASSERT(false);
700 return 0;
701 }
702}
703
704void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
705 ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {
706 if (kYes_ZeroInitialized == zeroInit) {
707 return;
708 }
709
710 const int linesRemaining = linesRequested - linesDecoded;
711 SkSampler* sampler = this->getSampler(false);
712
713 const int fillWidth = sampler ? sampler->fillWidth() :
714 fOptions.fSubset ? fOptions.fSubset->width() :
715 info.width() ;
716 void* fillDst = this->getScanlineOrder() == kBottomUp_SkScanlineOrder ? dst :
717 SkTAddOffset<void>(ptr: dst, byteOffset: linesDecoded * rowBytes);
718 const auto fillInfo = info.makeWH(newWidth: fillWidth, newHeight: linesRemaining);
719 SkSampler::Fill(info: fillInfo, dst: fillDst, rowBytes, zeroInit: kNo_ZeroInitialized);
720}
721
722bool sk_select_xform_format(SkColorType colorType, bool forColorTable,
723 skcms_PixelFormat* outFormat) {
724 SkASSERT(outFormat);
725
726 switch (colorType) {
727 case kRGBA_8888_SkColorType:
728 *outFormat = skcms_PixelFormat_RGBA_8888;
729 break;
730 case kBGRA_8888_SkColorType:
731 *outFormat = skcms_PixelFormat_BGRA_8888;
732 break;
733 case kRGB_565_SkColorType:
734 if (forColorTable) {
735#ifdef SK_PMCOLOR_IS_RGBA
736 *outFormat = skcms_PixelFormat_RGBA_8888;
737#else
738 *outFormat = skcms_PixelFormat_BGRA_8888;
739#endif
740 break;
741 }
742 *outFormat = skcms_PixelFormat_BGR_565;
743 break;
744 case kRGBA_F16_SkColorType:
745 *outFormat = skcms_PixelFormat_RGBA_hhhh;
746 break;
747 case kBGR_101010x_XR_SkColorType:
748 *outFormat = skcms_PixelFormat_BGR_101010x_XR;
749 break;
750 case kGray_8_SkColorType:
751 *outFormat = skcms_PixelFormat_G_8;
752 break;
753 default:
754 return false;
755 }
756 return true;
757}
758
759bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha encodedAlpha,
760 bool srcIsOpaque) {
761 fXformTime = kNo_XformTime;
762 bool needsColorXform = false;
763 if (this->usesColorXform()) {
764 if (kRGBA_F16_SkColorType == dstInfo.colorType() ||
765 kBGR_101010x_XR_SkColorType == dstInfo.colorType()) {
766 needsColorXform = true;
767 if (dstInfo.colorSpace()) {
768 dstInfo.colorSpace()->toProfile(&fDstProfile);
769 } else {
770 // Use the srcProfile to avoid conversion.
771 const auto* srcProfile = fEncodedInfo.profile();
772 fDstProfile = srcProfile ? *srcProfile : *skcms_sRGB_profile();
773 }
774 } else if (dstInfo.colorSpace()) {
775 dstInfo.colorSpace()->toProfile(&fDstProfile);
776 const auto* srcProfile = fEncodedInfo.profile();
777 if (!srcProfile) {
778 srcProfile = skcms_sRGB_profile();
779 }
780 if (!skcms_ApproximatelyEqualProfiles(A: srcProfile, B: &fDstProfile) ) {
781 needsColorXform = true;
782 }
783 }
784 }
785
786 if (!this->conversionSupported(dst: dstInfo, srcIsOpaque, needsColorXform)) {
787 return false;
788 }
789
790 if (needsColorXform) {
791 fXformTime = SkEncodedInfo::kPalette_Color != fEncodedInfo.color()
792 || kRGBA_F16_SkColorType == dstInfo.colorType()
793 ? kDecodeRow_XformTime : kPalette_XformTime;
794 if (!sk_select_xform_format(colorType: dstInfo.colorType(), forColorTable: fXformTime == kPalette_XformTime,
795 outFormat: &fDstXformFormat)) {
796 return false;
797 }
798 if (encodedAlpha == SkEncodedInfo::kUnpremul_Alpha
799 && dstInfo.alphaType() == kPremul_SkAlphaType) {
800 fDstXformAlphaFormat = skcms_AlphaFormat_PremulAsEncoded;
801 } else {
802 fDstXformAlphaFormat = skcms_AlphaFormat_Unpremul;
803 }
804 }
805 return true;
806}
807
808void SkCodec::applyColorXform(void* dst, const void* src, int count) const {
809 // It is okay for srcProfile to be null. This will use sRGB.
810 const auto* srcProfile = fEncodedInfo.profile();
811 SkAssertResult(skcms_Transform(src, fSrcXformFormat, skcms_AlphaFormat_Unpremul, srcProfile,
812 dst, fDstXformFormat, fDstXformAlphaFormat, &fDstProfile,
813 count));
814}
815
816std::vector<SkCodec::FrameInfo> SkCodec::getFrameInfo() {
817 const int frameCount = this->getFrameCount();
818 SkASSERT(frameCount >= 0);
819 if (frameCount <= 0) {
820 return std::vector<FrameInfo>{};
821 }
822
823 if (frameCount == 1 && !this->onGetFrameInfo(0, nullptr)) {
824 // Not animated.
825 return std::vector<FrameInfo>{};
826 }
827
828 std::vector<FrameInfo> result(frameCount);
829 for (int i = 0; i < frameCount; ++i) {
830 SkAssertResult(this->onGetFrameInfo(i, &result[i]));
831 }
832 return result;
833}
834
835const char* SkCodec::ResultToString(Result result) {
836 switch (result) {
837 case kSuccess:
838 return "success";
839 case kIncompleteInput:
840 return "incomplete input";
841 case kErrorInInput:
842 return "error in input";
843 case kInvalidConversion:
844 return "invalid conversion";
845 case kInvalidScale:
846 return "invalid scale";
847 case kInvalidParameters:
848 return "invalid parameters";
849 case kInvalidInput:
850 return "invalid input";
851 case kCouldNotRewind:
852 return "could not rewind";
853 case kInternalError:
854 return "internal error";
855 case kUnimplemented:
856 return "unimplemented";
857 default:
858 SkASSERT(false);
859 return "bogus result value";
860 }
861}
862
863void SkFrame::fillIn(SkCodec::FrameInfo* frameInfo, bool fullyReceived) const {
864 SkASSERT(frameInfo);
865
866 frameInfo->fRequiredFrame = fRequiredFrame;
867 frameInfo->fDuration = fDuration;
868 frameInfo->fFullyReceived = fullyReceived;
869 frameInfo->fAlphaType = fHasAlpha ? kUnpremul_SkAlphaType
870 : kOpaque_SkAlphaType;
871 frameInfo->fHasAlphaWithinBounds = this->reportedAlpha() != SkEncodedInfo::kOpaque_Alpha;
872 frameInfo->fDisposalMethod = fDisposalMethod;
873 frameInfo->fBlend = fBlend;
874 frameInfo->fFrameRect = fRect;
875}
876
877static bool independent(const SkFrame& frame) {
878 return frame.getRequiredFrame() == SkCodec::kNoFrame;
879}
880
881static bool restore_bg(const SkFrame& frame) {
882 return frame.getDisposalMethod() == SkCodecAnimation::DisposalMethod::kRestoreBGColor;
883}
884
885// As its name suggests, this method computes a frame's alpha (e.g. completely
886// opaque, unpremul, binary) and its required frame (a preceding frame that
887// this frame depends on, to draw the complete image at this frame's point in
888// the animation stream), and calls this frame's setter methods with that
889// computed information.
890//
891// A required frame of kNoFrame means that this frame is independent: drawing
892// the complete image at this frame's point in the animation stream does not
893// require first preparing the pixel buffer based on another frame. Instead,
894// drawing can start from an uninitialized pixel buffer.
895//
896// "Uninitialized" is from the SkCodec's caller's point of view. In the SkCodec
897// implementation, for independent frames, first party Skia code (in src/codec)
898// will typically fill the buffer with a uniform background color (e.g.
899// transparent black) before calling into third party codec-specific code (e.g.
900// libjpeg or libpng). Pixels outside of the frame's rect will remain this
901// background color after drawing this frame. For incomplete decodes, pixels
902// inside that rect may be (at least temporarily) set to that background color.
903// In an incremental decode, later passes may then overwrite that background
904// color.
905//
906// Determining kNoFrame or otherwise involves testing a number of conditions
907// sequentially. The first satisfied condition results in setting the required
908// frame to kNoFrame (an "INDx" condition) or to a non-negative frame number (a
909// "DEPx" condition), and the function returning early. Those "INDx" and "DEPx"
910// labels also map to comments in the function body.
911//
912// - IND1: this frame is the first frame.
913// - IND2: this frame fills out the whole image, and it is completely opaque
914// or it overwrites (not blends with) the previous frame.
915// - IND3: all preceding frames' disposals are kRestorePrevious.
916// - IND4: the prevFrame's disposal is kRestoreBGColor, and it fills out the
917// whole image or it is itself otherwise independent.
918// - DEP5: this frame reports alpha (it is not completely opaque) and it
919// blends with (not overwrites) the previous frame.
920// - IND6: this frame's rect covers the rects of all preceding frames back to
921// and including the most recent independent frame before this frame.
922// - DEP7: unconditional.
923//
924// The "prevFrame" variable initially points to the previous frame (also known
925// as the prior frame), but that variable may iterate further backwards over
926// the course of this computation.
927void SkFrameHolder::setAlphaAndRequiredFrame(SkFrame* frame) {
928 const bool reportsAlpha = frame->reportedAlpha() != SkEncodedInfo::kOpaque_Alpha;
929 const auto screenRect = SkIRect::MakeWH(w: fScreenWidth, h: fScreenHeight);
930 const auto frameRect = frame_rect_on_screen(frameRect: frame->frameRect(), screenRect);
931
932 const int i = frame->frameId();
933 if (0 == i) {
934 frame->setHasAlpha(reportsAlpha || frameRect != screenRect);
935 frame->setRequiredFrame(SkCodec::kNoFrame); // IND1
936 return;
937 }
938
939
940 const bool blendWithPrevFrame = frame->getBlend() == SkCodecAnimation::Blend::kSrcOver;
941 if ((!reportsAlpha || !blendWithPrevFrame) && frameRect == screenRect) {
942 frame->setHasAlpha(reportsAlpha);
943 frame->setRequiredFrame(SkCodec::kNoFrame); // IND2
944 return;
945 }
946
947 const SkFrame* prevFrame = this->getFrame(i: i-1);
948 while (prevFrame->getDisposalMethod() == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
949 const int prevId = prevFrame->frameId();
950 if (0 == prevId) {
951 frame->setHasAlpha(true);
952 frame->setRequiredFrame(SkCodec::kNoFrame); // IND3
953 return;
954 }
955
956 prevFrame = this->getFrame(i: prevId - 1);
957 }
958
959 const bool clearPrevFrame = restore_bg(frame: *prevFrame);
960 auto prevFrameRect = frame_rect_on_screen(frameRect: prevFrame->frameRect(), screenRect);
961
962 if (clearPrevFrame) {
963 if (prevFrameRect == screenRect || independent(frame: *prevFrame)) {
964 frame->setHasAlpha(true);
965 frame->setRequiredFrame(SkCodec::kNoFrame); // IND4
966 return;
967 }
968 }
969
970 if (reportsAlpha && blendWithPrevFrame) {
971 // Note: We could be more aggressive here. If prevFrame clears
972 // to background color and covers its required frame (and that
973 // frame is independent), prevFrame could be marked independent.
974 // Would this extra complexity be worth it?
975 frame->setRequiredFrame(prevFrame->frameId()); // DEP5
976 frame->setHasAlpha(prevFrame->hasAlpha() || clearPrevFrame);
977 return;
978 }
979
980 while (frameRect.contains(r: prevFrameRect)) {
981 const int prevRequiredFrame = prevFrame->getRequiredFrame();
982 if (prevRequiredFrame == SkCodec::kNoFrame) {
983 frame->setRequiredFrame(SkCodec::kNoFrame); // IND6
984 frame->setHasAlpha(true);
985 return;
986 }
987
988 prevFrame = this->getFrame(i: prevRequiredFrame);
989 prevFrameRect = frame_rect_on_screen(frameRect: prevFrame->frameRect(), screenRect);
990 }
991
992 frame->setRequiredFrame(prevFrame->frameId()); // DEP7
993 if (restore_bg(frame: *prevFrame)) {
994 frame->setHasAlpha(true);
995 return;
996 }
997 SkASSERT(prevFrame->getDisposalMethod() == SkCodecAnimation::DisposalMethod::kKeep);
998 frame->setHasAlpha(prevFrame->hasAlpha() || (reportsAlpha && !blendWithPrevFrame));
999}
1000
1001

source code of flutter_engine/third_party/skia/src/codec/SkCodec.cpp