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