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