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 "SkGifCodec.h"
15 #include "SkHalf.h"
16 #include "SkIcoCodec.h"
17 #include "SkJpegCodec.h"
18 #ifdef SK_HAS_PNG_LIBRARY
19 #include "SkPngCodec.h"
20 #endif
21 #include "SkRawCodec.h"
22 #include "SkStream.h"
23 #include "SkWbmpCodec.h"
24 #include "SkWebpCodec.h"
25 
26 struct DecoderProc {
27     bool (*IsFormat)(const void*, size_t);
28     SkCodec* (*NewFromStream)(SkStream*);
29 };
30 
31 static const DecoderProc gDecoderProcs[] = {
32 #ifdef SK_HAS_JPEG_LIBRARY
33     { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream },
34 #endif
35 #ifdef SK_HAS_WEBP_LIBRARY
36     { SkWebpCodec::IsWebp, SkWebpCodec::NewFromStream },
37 #endif
38     { SkGifCodec::IsGif, SkGifCodec::NewFromStream },
39 #ifdef SK_HAS_PNG_LIBRARY
40     { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
41 #endif
42     { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
43     { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
44 };
45 
MinBufferedBytesNeeded()46 size_t SkCodec::MinBufferedBytesNeeded() {
47     return WEBP_VP8_HEADER_SIZE;
48 }
49 
NewFromStream(SkStream * stream,SkPngChunkReader * chunkReader)50 SkCodec* SkCodec::NewFromStream(SkStream* stream,
51                                 SkPngChunkReader* chunkReader) {
52     if (!stream) {
53         return nullptr;
54     }
55 
56     std::unique_ptr<SkStream> streamDeleter(stream);
57 
58     // 14 is enough to read all of the supported types.
59     const size_t bytesToRead = 14;
60     SkASSERT(bytesToRead <= MinBufferedBytesNeeded());
61 
62     char buffer[bytesToRead];
63     size_t bytesRead = stream->peek(buffer, bytesToRead);
64 
65     // It is also possible to have a complete image less than bytesToRead bytes
66     // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead.
67     // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter
68     // than bytesToRead, so pass that directly to the decoder.
69     // It also is possible the stream uses too small a buffer for peeking, but
70     // we trust the caller to use a large enough buffer.
71 
72     if (0 == bytesRead) {
73         // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this
74         // printf could be useful to notice failures.
75         // SkCodecPrintf("Encoded image data failed to peek!\n");
76 
77         // It is possible the stream does not support peeking, but does support
78         // rewinding.
79         // Attempt to read() and pass the actual amount read to the decoder.
80         bytesRead = stream->read(buffer, bytesToRead);
81         if (!stream->rewind()) {
82             SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
83             return nullptr;
84         }
85     }
86 
87     // PNG is special, since we want to be able to supply an SkPngChunkReader.
88     // But this code follows the same pattern as the loop.
89 #ifdef SK_HAS_PNG_LIBRARY
90     if (SkPngCodec::IsPng(buffer, bytesRead)) {
91         return SkPngCodec::NewFromStream(streamDeleter.release(), chunkReader);
92     } else
93 #endif
94     {
95         for (DecoderProc proc : gDecoderProcs) {
96             if (proc.IsFormat(buffer, bytesRead)) {
97                 return proc.NewFromStream(streamDeleter.release());
98             }
99         }
100 
101 #ifdef SK_CODEC_DECODES_RAW
102         // Try to treat the input as RAW if all the other checks failed.
103         return SkRawCodec::NewFromStream(streamDeleter.release());
104 #endif
105     }
106 
107     return nullptr;
108 }
109 
NewFromData(sk_sp<SkData> data,SkPngChunkReader * reader)110 SkCodec* SkCodec::NewFromData(sk_sp<SkData> data, SkPngChunkReader* reader) {
111     if (!data) {
112         return nullptr;
113     }
114     return NewFromStream(new SkMemoryStream(data), reader);
115 }
116 
SkCodec(int width,int height,const SkEncodedInfo & info,SkStream * stream,sk_sp<SkColorSpace> colorSpace,Origin origin)117 SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream,
118         sk_sp<SkColorSpace> colorSpace, Origin origin)
119     : fEncodedInfo(info)
120     , fSrcInfo(info.makeImageInfo(width, height, std::move(colorSpace)))
121     , fStream(stream)
122     , fNeedsRewind(false)
123     , fOrigin(origin)
124     , fDstInfo()
125     , fOptions()
126     , fCurrScanline(-1)
127 {}
128 
SkCodec(const SkEncodedInfo & info,const SkImageInfo & imageInfo,SkStream * stream,Origin origin)129 SkCodec::SkCodec(const SkEncodedInfo& info, const SkImageInfo& imageInfo, SkStream* stream,
130         Origin origin)
131     : fEncodedInfo(info)
132     , fSrcInfo(imageInfo)
133     , fStream(stream)
134     , fNeedsRewind(false)
135     , fOrigin(origin)
136     , fDstInfo()
137     , fOptions()
138     , fCurrScanline(-1)
139 {}
140 
~SkCodec()141 SkCodec::~SkCodec() {}
142 
rewindIfNeeded()143 bool SkCodec::rewindIfNeeded() {
144     // Store the value of fNeedsRewind so we can update it. Next read will
145     // require a rewind.
146     const bool needsRewind = fNeedsRewind;
147     fNeedsRewind = true;
148     if (!needsRewind) {
149         return true;
150     }
151 
152     // startScanlineDecode will need to be called before decoding scanlines.
153     fCurrScanline = -1;
154     // startIncrementalDecode will need to be called before incrementalDecode.
155     fStartedIncrementalDecode = false;
156 
157     // Some codecs do not have a stream.  They may hold onto their own data or another codec.
158     // They must handle rewinding themselves.
159     if (fStream && !fStream->rewind()) {
160         return false;
161     }
162 
163     return this->onRewind();
164 }
165 
166 #define CHECK_COLOR_TABLE                                   \
167     if (kIndex_8_SkColorType == info.colorType()) {         \
168         if (nullptr == ctable || nullptr == ctableCount) {  \
169             return SkCodec::kInvalidParameters;             \
170         }                                                   \
171     } else {                                                \
172         if (ctableCount) {                                  \
173             *ctableCount = 0;                               \
174         }                                                   \
175         ctableCount = nullptr;                              \
176         ctable = nullptr;                                   \
177     }
178 
179 
getPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options * options,SkPMColor ctable[],int * ctableCount)180 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
181                                    const Options* options, SkPMColor ctable[], int* ctableCount) {
182     if (kUnknown_SkColorType == info.colorType()) {
183         return kInvalidConversion;
184     }
185     if (nullptr == pixels) {
186         return kInvalidParameters;
187     }
188     if (rowBytes < info.minRowBytes()) {
189         return kInvalidParameters;
190     }
191 
192     CHECK_COLOR_TABLE;
193 
194     if (!this->rewindIfNeeded()) {
195         return kCouldNotRewind;
196     }
197 
198     // Default options.
199     Options optsStorage;
200     if (nullptr == options) {
201         options = &optsStorage;
202     } else if (options->fSubset) {
203         SkIRect subset(*options->fSubset);
204         if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
205             // FIXME: How to differentiate between not supporting subset at all
206             // and not supporting this particular subset?
207             return kUnimplemented;
208         }
209     }
210 
211     // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
212     // because it supports arbitrary scaling/subset combinations.
213     if (!this->dimensionsSupported(info.dimensions())) {
214         return kInvalidScale;
215     }
216 
217     fDstInfo = info;
218     fOptions = *options;
219 
220     // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
221     // successfully.
222     int rowsDecoded = 0;
223     const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount,
224             &rowsDecoded);
225 
226     if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
227         SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
228     }
229 
230     // A return value of kIncompleteInput indicates a truncated image stream.
231     // In this case, we will fill any uninitialized memory with a default value.
232     // Some subclasses will take care of filling any uninitialized memory on
233     // their own.  They indicate that all of the memory has been filled by
234     // setting rowsDecoded equal to the height.
235     if (kIncompleteInput == result && rowsDecoded != info.height()) {
236         // FIXME: (skbug.com/5772) fillIncompleteImage will fill using the swizzler's width, unless
237         // there is a subset. In that case, it will use the width of the subset. From here, the
238         // subset will only be non-null in the case of SkWebpCodec, but it treats the subset
239         // differenty from the other codecs, and it needs to use the width specified by the info.
240         // Set the subset to null so SkWebpCodec uses the correct width.
241         fOptions.fSubset = nullptr;
242         this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
243                 rowsDecoded);
244     }
245 
246     return result;
247 }
248 
getPixels(const SkImageInfo & info,void * pixels,size_t rowBytes)249 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
250     return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr);
251 }
252 
startIncrementalDecode(const SkImageInfo & info,void * pixels,size_t rowBytes,const SkCodec::Options * options,SkPMColor * ctable,int * ctableCount)253 SkCodec::Result SkCodec::startIncrementalDecode(const SkImageInfo& info, void* pixels,
254         size_t rowBytes, const SkCodec::Options* options, SkPMColor* ctable, int* ctableCount) {
255     fStartedIncrementalDecode = false;
256 
257     if (kUnknown_SkColorType == info.colorType()) {
258         return kInvalidConversion;
259     }
260     if (nullptr == pixels) {
261         return kInvalidParameters;
262     }
263 
264     // Ensure that valid color ptrs are passed in for kIndex8 color type
265     CHECK_COLOR_TABLE;
266 
267     // FIXME: If the rows come after the rows of a previous incremental decode,
268     // we might be able to skip the rewind, but only the implementation knows
269     // that. (e.g. PNG will always need to rewind, since we called longjmp, but
270     // a bottom-up BMP could skip rewinding if the new rows are above the old
271     // rows.)
272     if (!this->rewindIfNeeded()) {
273         return kCouldNotRewind;
274     }
275 
276     // Set options.
277     Options optsStorage;
278     if (nullptr == options) {
279         options = &optsStorage;
280     } else if (options->fSubset) {
281         SkIRect size = SkIRect::MakeSize(info.dimensions());
282         if (!size.contains(*options->fSubset)) {
283             return kInvalidParameters;
284         }
285 
286         const int top = options->fSubset->top();
287         const int bottom = options->fSubset->bottom();
288         if (top < 0 || top >= info.height() || top >= bottom || bottom > info.height()) {
289             return kInvalidParameters;
290         }
291     }
292 
293     if (!this->dimensionsSupported(info.dimensions())) {
294         return kInvalidScale;
295     }
296 
297     fDstInfo = info;
298     fOptions = *options;
299 
300     const Result result = this->onStartIncrementalDecode(info, pixels, rowBytes,
301             fOptions, ctable, ctableCount);
302     if (kSuccess == result) {
303         fStartedIncrementalDecode = true;
304     } else if (kUnimplemented == result) {
305         // FIXME: This is temporarily necessary, until we transition SkCodec
306         // implementations from scanline decoding to incremental decoding.
307         // SkAndroidCodec will first attempt to use incremental decoding, but
308         // will fall back to scanline decoding if incremental returns
309         // kUnimplemented. rewindIfNeeded(), above, set fNeedsRewind to true
310         // (after potentially rewinding), but we do not want the next call to
311         // startScanlineDecode() to do a rewind.
312         fNeedsRewind = false;
313     }
314     return result;
315 }
316 
317 
startScanlineDecode(const SkImageInfo & info,const SkCodec::Options * options,SkPMColor ctable[],int * ctableCount)318 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info,
319         const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
320     // Reset fCurrScanline in case of failure.
321     fCurrScanline = -1;
322     // Ensure that valid color ptrs are passed in for kIndex8 color type
323     CHECK_COLOR_TABLE;
324 
325     if (!this->rewindIfNeeded()) {
326         return kCouldNotRewind;
327     }
328 
329     // Set options.
330     Options optsStorage;
331     if (nullptr == options) {
332         options = &optsStorage;
333     } else if (options->fSubset) {
334         SkIRect size = SkIRect::MakeSize(info.dimensions());
335         if (!size.contains(*options->fSubset)) {
336             return kInvalidInput;
337         }
338 
339         // We only support subsetting in the x-dimension for scanline decoder.
340         // Subsetting in the y-dimension can be accomplished using skipScanlines().
341         if (options->fSubset->top() != 0 || options->fSubset->height() != info.height()) {
342             return kInvalidInput;
343         }
344     }
345 
346     // FIXME: Support subsets somehow?
347     if (!this->dimensionsSupported(info.dimensions())) {
348         return kInvalidScale;
349     }
350 
351     const Result result = this->onStartScanlineDecode(info, *options, ctable, ctableCount);
352     if (result != SkCodec::kSuccess) {
353         return result;
354     }
355 
356     fCurrScanline = 0;
357     fDstInfo = info;
358     fOptions = *options;
359     return kSuccess;
360 }
361 
362 #undef CHECK_COLOR_TABLE
363 
startScanlineDecode(const SkImageInfo & info)364 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info) {
365     return this->startScanlineDecode(info, nullptr, nullptr, nullptr);
366 }
367 
getScanlines(void * dst,int countLines,size_t rowBytes)368 int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
369     if (fCurrScanline < 0) {
370         return 0;
371     }
372 
373     SkASSERT(!fDstInfo.isEmpty());
374     if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
375         return 0;
376     }
377 
378     const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
379     if (linesDecoded < countLines) {
380         this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized,
381                 countLines, linesDecoded);
382     }
383     fCurrScanline += countLines;
384     return linesDecoded;
385 }
386 
skipScanlines(int countLines)387 bool SkCodec::skipScanlines(int countLines) {
388     if (fCurrScanline < 0) {
389         return false;
390     }
391 
392     SkASSERT(!fDstInfo.isEmpty());
393     if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) {
394         // Arguably, we could just skip the scanlines which are remaining,
395         // and return true. We choose to return false so the client
396         // can catch their bug.
397         return false;
398     }
399 
400     bool result = this->onSkipScanlines(countLines);
401     fCurrScanline += countLines;
402     return result;
403 }
404 
outputScanline(int inputScanline) const405 int SkCodec::outputScanline(int inputScanline) const {
406     SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height());
407     return this->onOutputScanline(inputScanline);
408 }
409 
onOutputScanline(int inputScanline) const410 int SkCodec::onOutputScanline(int inputScanline) const {
411     switch (this->getScanlineOrder()) {
412         case kTopDown_SkScanlineOrder:
413             return inputScanline;
414         case kBottomUp_SkScanlineOrder:
415             return this->getInfo().height() - inputScanline - 1;
416         default:
417             // This case indicates an interlaced gif and is implemented by SkGifCodec.
418             SkASSERT(false);
419             return 0;
420     }
421 }
422 
onGetFillValue(const SkImageInfo & dstInfo) const423 uint64_t SkCodec::onGetFillValue(const SkImageInfo& dstInfo) const {
424     switch (dstInfo.colorType()) {
425         case kRGBA_F16_SkColorType: {
426             static constexpr uint64_t transparentColor = 0;
427             static constexpr uint64_t opaqueColor = ((uint64_t) SK_Half1) << 48;
428             return (kOpaque_SkAlphaType == fSrcInfo.alphaType()) ? opaqueColor : transparentColor;
429         }
430         default: {
431             // This not only handles the kN32 case, but also k565, kGray8, kIndex8, since
432             // the low bits are zeros.
433             return (kOpaque_SkAlphaType == fSrcInfo.alphaType()) ?
434                     SK_ColorBLACK : SK_ColorTRANSPARENT;
435         }
436     }
437 }
438 
fill_proc(const SkImageInfo & info,void * dst,size_t rowBytes,uint64_t colorOrIndex,SkCodec::ZeroInitialized zeroInit,SkSampler * sampler)439 static void fill_proc(const SkImageInfo& info, void* dst, size_t rowBytes,
440         uint64_t colorOrIndex, SkCodec::ZeroInitialized zeroInit, SkSampler* sampler) {
441     if (sampler) {
442         sampler->fill(info, dst, rowBytes, colorOrIndex, zeroInit);
443     } else {
444         SkSampler::Fill(info, dst, rowBytes, colorOrIndex, zeroInit);
445     }
446 }
447 
fillIncompleteImage(const SkImageInfo & info,void * dst,size_t rowBytes,ZeroInitialized zeroInit,int linesRequested,int linesDecoded)448 void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
449         ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {
450 
451     void* fillDst;
452     const uint64_t fillValue = this->getFillValue(info);
453     const int linesRemaining = linesRequested - linesDecoded;
454     SkSampler* sampler = this->getSampler(false);
455 
456     int fillWidth = info.width();
457     if (fOptions.fSubset) {
458         fillWidth = fOptions.fSubset->width();
459     }
460 
461     switch (this->getScanlineOrder()) {
462         case kTopDown_SkScanlineOrder: {
463             const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining);
464             fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes);
465             fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
466             break;
467         }
468         case kBottomUp_SkScanlineOrder: {
469             fillDst = dst;
470             const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining);
471             fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
472             break;
473         }
474     }
475 }
476 
initializeColorXform(const SkImageInfo & dstInfo,SkTransferFunctionBehavior premulBehavior)477 bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo,
478                                    SkTransferFunctionBehavior premulBehavior) {
479     fColorXform = nullptr;
480     bool needsColorCorrectPremul = needs_premul(dstInfo, fEncodedInfo) &&
481                                    SkTransferFunctionBehavior::kRespect == premulBehavior;
482     if (needs_color_xform(dstInfo, fSrcInfo, needsColorCorrectPremul)) {
483         fColorXform = SkColorSpaceXform_Base::New(fSrcInfo.colorSpace(), dstInfo.colorSpace(),
484                                                   premulBehavior);
485         if (!fColorXform) {
486             return false;
487         }
488     }
489 
490     return true;
491 }
492