• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkData.h"
12 #include "SkGifCodec.h"
13 #include "SkIcoCodec.h"
14 #include "SkJpegCodec.h"
15 #ifdef SK_CODEC_DECODES_PNG
16 #include "SkPngCodec.h"
17 #endif
18 #include "SkRawCodec.h"
19 #include "SkStream.h"
20 #include "SkWbmpCodec.h"
21 #include "SkWebpCodec.h"
22 
23 struct DecoderProc {
24     bool (*IsFormat)(const void*, size_t);
25     SkCodec* (*NewFromStream)(SkStream*);
26 };
27 
28 static const DecoderProc gDecoderProcs[] = {
29 #ifdef SK_CODEC_DECODES_JPEG
30     { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream },
31 #endif
32 #ifdef SK_CODEC_DECODES_WEBP
33     { SkWebpCodec::IsWebp, SkWebpCodec::NewFromStream },
34 #endif
35 #ifdef SK_CODEC_DECODES_GIF
36     { SkGifCodec::IsGif, SkGifCodec::NewFromStream },
37 #endif
38 #ifdef SK_CODEC_DECODES_PNG
39     { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
40 #endif
41     { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
42     { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
43 };
44 
MinBufferedBytesNeeded()45 size_t SkCodec::MinBufferedBytesNeeded() {
46     return WEBP_VP8_HEADER_SIZE;
47 }
48 
NewFromStream(SkStream * stream,SkPngChunkReader * chunkReader)49 SkCodec* SkCodec::NewFromStream(SkStream* stream,
50                                 SkPngChunkReader* chunkReader) {
51     if (!stream) {
52         return nullptr;
53     }
54 
55     SkAutoTDelete<SkStream> streamDeleter(stream);
56 
57     // 14 is enough to read all of the supported types.
58     const size_t bytesToRead = 14;
59     SkASSERT(bytesToRead <= MinBufferedBytesNeeded());
60 
61     char buffer[bytesToRead];
62     size_t bytesRead = stream->peek(buffer, bytesToRead);
63 
64     // It is also possible to have a complete image less than bytesToRead bytes
65     // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead.
66     // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter
67     // than bytesToRead, so pass that directly to the decoder.
68     // It also is possible the stream uses too small a buffer for peeking, but
69     // we trust the caller to use a large enough buffer.
70 
71     if (0 == bytesRead) {
72         // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this
73         // printf could be useful to notice failures.
74         // SkCodecPrintf("Encoded image data failed to peek!\n");
75 
76         // It is possible the stream does not support peeking, but does support
77         // rewinding.
78         // Attempt to read() and pass the actual amount read to the decoder.
79         bytesRead = stream->read(buffer, bytesToRead);
80         if (!stream->rewind()) {
81             SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
82             return nullptr;
83         }
84     }
85 
86     // PNG is special, since we want to be able to supply an SkPngChunkReader.
87     // But this code follows the same pattern as the loop.
88 #ifdef SK_CODEC_DECODES_PNG
89     if (SkPngCodec::IsPng(buffer, bytesRead)) {
90         return SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader);
91     } else
92 #endif
93     {
94         for (DecoderProc proc : gDecoderProcs) {
95             if (proc.IsFormat(buffer, bytesRead)) {
96                 return proc.NewFromStream(streamDeleter.detach());
97             }
98         }
99 
100 #ifdef SK_CODEC_DECODES_RAW
101         // Try to treat the input as RAW if all the other checks failed.
102         return SkRawCodec::NewFromStream(streamDeleter.detach());
103 #endif
104     }
105 
106     return nullptr;
107 }
108 
NewFromData(SkData * data,SkPngChunkReader * reader)109 SkCodec* SkCodec::NewFromData(SkData* data, SkPngChunkReader* reader) {
110     if (!data) {
111         return nullptr;
112     }
113     return NewFromStream(new SkMemoryStream(data), reader);
114 }
115 
SkCodec(const SkImageInfo & info,SkStream * stream)116 SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream)
117     : fSrcInfo(info)
118     , fStream(stream)
119     , fNeedsRewind(false)
120     , fDstInfo()
121     , fOptions()
122     , fCurrScanline(-1)
123 {}
124 
~SkCodec()125 SkCodec::~SkCodec() {}
126 
rewindIfNeeded()127 bool SkCodec::rewindIfNeeded() {
128     if (!fStream) {
129         // Some codecs do not have a stream, but they hold others that do. They
130         // must handle rewinding themselves.
131         return true;
132     }
133 
134     // Store the value of fNeedsRewind so we can update it. Next read will
135     // require a rewind.
136     const bool needsRewind = fNeedsRewind;
137     fNeedsRewind = true;
138     if (!needsRewind) {
139         return true;
140     }
141 
142     // startScanlineDecode will need to be called before decoding scanlines.
143     fCurrScanline = -1;
144 
145     if (!fStream->rewind()) {
146         return false;
147     }
148 
149     return this->onRewind();
150 }
151 
getPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options * options,SkPMColor ctable[],int * ctableCount)152 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
153                                    const Options* options, SkPMColor ctable[], int* ctableCount) {
154     if (kUnknown_SkColorType == info.colorType()) {
155         return kInvalidConversion;
156     }
157     if (nullptr == pixels) {
158         return kInvalidParameters;
159     }
160     if (rowBytes < info.minRowBytes()) {
161         return kInvalidParameters;
162     }
163 
164     if (kIndex_8_SkColorType == info.colorType()) {
165         if (nullptr == ctable || nullptr == ctableCount) {
166             return kInvalidParameters;
167         }
168     } else {
169         if (ctableCount) {
170             *ctableCount = 0;
171         }
172         ctableCount = nullptr;
173         ctable = nullptr;
174     }
175 
176     if (!this->rewindIfNeeded()) {
177         return kCouldNotRewind;
178     }
179 
180     // Default options.
181     Options optsStorage;
182     if (nullptr == options) {
183         options = &optsStorage;
184     } else if (options->fSubset) {
185         SkIRect subset(*options->fSubset);
186         if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
187             // FIXME: How to differentiate between not supporting subset at all
188             // and not supporting this particular subset?
189             return kUnimplemented;
190         }
191     }
192 
193     // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
194     // because it supports arbitrary scaling/subset combinations.
195     if (!this->dimensionsSupported(info.dimensions())) {
196         return kInvalidScale;
197     }
198 
199     // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
200     // successfully.
201     int rowsDecoded = 0;
202     const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount,
203             &rowsDecoded);
204 
205     if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
206         SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
207     }
208 
209     // A return value of kIncompleteInput indicates a truncated image stream.
210     // In this case, we will fill any uninitialized memory with a default value.
211     // Some subclasses will take care of filling any uninitialized memory on
212     // their own.  They indicate that all of the memory has been filled by
213     // setting rowsDecoded equal to the height.
214     if (kIncompleteInput == result && rowsDecoded != info.height()) {
215         this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
216                 rowsDecoded);
217     }
218 
219     return result;
220 }
221 
getPixels(const SkImageInfo & info,void * pixels,size_t rowBytes)222 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
223     return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr);
224 }
225 
startScanlineDecode(const SkImageInfo & dstInfo,const SkCodec::Options * options,SkPMColor ctable[],int * ctableCount)226 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo,
227         const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
228     // Reset fCurrScanline in case of failure.
229     fCurrScanline = -1;
230     // Ensure that valid color ptrs are passed in for kIndex8 color type
231     if (kIndex_8_SkColorType == dstInfo.colorType()) {
232         if (nullptr == ctable || nullptr == ctableCount) {
233             return SkCodec::kInvalidParameters;
234         }
235     } else {
236         if (ctableCount) {
237             *ctableCount = 0;
238         }
239         ctableCount = nullptr;
240         ctable = nullptr;
241     }
242 
243     if (!this->rewindIfNeeded()) {
244         return kCouldNotRewind;
245     }
246 
247     // Set options.
248     Options optsStorage;
249     if (nullptr == options) {
250         options = &optsStorage;
251     } else if (options->fSubset) {
252         SkIRect size = SkIRect::MakeSize(dstInfo.dimensions());
253         if (!size.contains(*options->fSubset)) {
254             return kInvalidInput;
255         }
256 
257         // We only support subsetting in the x-dimension for scanline decoder.
258         // Subsetting in the y-dimension can be accomplished using skipScanlines().
259         if (options->fSubset->top() != 0 || options->fSubset->height() != dstInfo.height()) {
260             return kInvalidInput;
261         }
262     }
263 
264     // FIXME: Support subsets somehow?
265     if (!this->dimensionsSupported(dstInfo.dimensions())) {
266         return kInvalidScale;
267     }
268 
269     const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount);
270     if (result != SkCodec::kSuccess) {
271         return result;
272     }
273 
274     fCurrScanline = 0;
275     fDstInfo = dstInfo;
276     fOptions = *options;
277     return kSuccess;
278 }
279 
startScanlineDecode(const SkImageInfo & dstInfo)280 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) {
281     return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr);
282 }
283 
getScanlines(void * dst,int countLines,size_t rowBytes)284 int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
285     if (fCurrScanline < 0) {
286         return 0;
287     }
288 
289     SkASSERT(!fDstInfo.isEmpty());
290     if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
291         return 0;
292     }
293 
294     const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
295     if (linesDecoded < countLines) {
296         this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized,
297                 countLines, linesDecoded);
298     }
299     fCurrScanline += countLines;
300     return linesDecoded;
301 }
302 
skipScanlines(int countLines)303 bool SkCodec::skipScanlines(int countLines) {
304     if (fCurrScanline < 0) {
305         return false;
306     }
307 
308     SkASSERT(!fDstInfo.isEmpty());
309     if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) {
310         // Arguably, we could just skip the scanlines which are remaining,
311         // and return true. We choose to return false so the client
312         // can catch their bug.
313         return false;
314     }
315 
316     bool result = this->onSkipScanlines(countLines);
317     fCurrScanline += countLines;
318     return result;
319 }
320 
outputScanline(int inputScanline) const321 int SkCodec::outputScanline(int inputScanline) const {
322     SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height());
323     return this->onOutputScanline(inputScanline);
324 }
325 
onOutputScanline(int inputScanline) const326 int SkCodec::onOutputScanline(int inputScanline) const {
327     switch (this->getScanlineOrder()) {
328         case kTopDown_SkScanlineOrder:
329         case kNone_SkScanlineOrder:
330             return inputScanline;
331         case kBottomUp_SkScanlineOrder:
332             return this->getInfo().height() - inputScanline - 1;
333         default:
334             // This case indicates an interlaced gif and is implemented by SkGifCodec.
335             SkASSERT(false);
336             return 0;
337     }
338 }
339 
fill_proc(const SkImageInfo & info,void * dst,size_t rowBytes,uint32_t colorOrIndex,SkCodec::ZeroInitialized zeroInit,SkSampler * sampler)340 static void fill_proc(const SkImageInfo& info, void* dst, size_t rowBytes,
341         uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit, SkSampler* sampler) {
342     if (sampler) {
343         sampler->fill(info, dst, rowBytes, colorOrIndex, zeroInit);
344     } else {
345         SkSampler::Fill(info, dst, rowBytes, colorOrIndex, zeroInit);
346     }
347 }
348 
fillIncompleteImage(const SkImageInfo & info,void * dst,size_t rowBytes,ZeroInitialized zeroInit,int linesRequested,int linesDecoded)349 void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
350         ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {
351 
352     void* fillDst;
353     const uint32_t fillValue = this->getFillValue(info.colorType());
354     const int linesRemaining = linesRequested - linesDecoded;
355     SkSampler* sampler = this->getSampler(false);
356 
357     int fillWidth = info.width();
358     if (fOptions.fSubset) {
359         fillWidth = fOptions.fSubset->width();
360     }
361 
362     switch (this->getScanlineOrder()) {
363         case kTopDown_SkScanlineOrder:
364         case kNone_SkScanlineOrder: {
365             const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining);
366             fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes);
367             fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
368             break;
369         }
370         case kBottomUp_SkScanlineOrder: {
371             fillDst = dst;
372             const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining);
373             fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
374             break;
375         }
376         case kOutOfOrder_SkScanlineOrder: {
377             SkASSERT(1 == linesRequested || this->getInfo().height() == linesRequested);
378             const SkImageInfo fillInfo = info.makeWH(fillWidth, 1);
379             for (int srcY = linesDecoded; srcY < linesRequested; srcY++) {
380                 fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * rowBytes);
381                 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
382             }
383             break;
384         }
385     }
386 }
387