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