blob: cfeeb51d0d52f817d370d9d748f7cfc171785a1a [file] [log] [blame]
scroggof24f2242015-03-03 08:59:20 -08001/*
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
msarettb46e5e22015-07-30 11:36:40 -07008#include "SkBmpCodec.h"
scroggof24f2242015-03-03 08:59:20 -08009#include "SkCodec.h"
msarett8c8f22a2015-04-01 06:58:48 -070010#include "SkCodecPriv.h"
msarett1a464672016-01-07 13:17:19 -080011#include "SkData.h"
12#include "SkGifCodec.h"
13#include "SkIcoCodec.h"
msarettb747b902015-11-06 11:15:49 -080014#if !defined(GOOGLE3)
msarette16b04a2015-04-15 07:32:19 -070015#include "SkJpegCodec.h"
msarett1c8a5872015-07-07 08:50:01 -070016#endif
msarettbe1d5552016-01-21 09:05:23 -080017#include "SkPngCodec.h"
yujieqin916de9f2016-01-25 08:26:16 -080018#ifdef SK_CODEC_DECODES_RAW
19#include "SkRawCodec.h"
20#endif
scroggof24f2242015-03-03 08:59:20 -080021#include "SkStream.h"
msarett1a464672016-01-07 13:17:19 -080022#include "SkWbmpCodec.h"
scroggo6f5e6192015-06-18 12:53:43 -070023#include "SkWebpCodec.h"
scroggof24f2242015-03-03 08:59:20 -080024
msarett74114382015-03-16 11:55:18 -070025struct DecoderProc {
scroggodb30be22015-12-08 18:54:13 -080026 bool (*IsFormat)(const void*, size_t);
msarett74114382015-03-16 11:55:18 -070027 SkCodec* (*NewFromStream)(SkStream*);
28};
29
30static const DecoderProc gDecoderProcs[] = {
msarettb747b902015-11-06 11:15:49 -080031#if !defined(GOOGLE3)
msarette16b04a2015-04-15 07:32:19 -070032 { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream },
msarett1c8a5872015-07-07 08:50:01 -070033#endif
scroggo6f5e6192015-06-18 12:53:43 -070034 { SkWebpCodec::IsWebp, SkWebpCodec::NewFromStream },
msarett8c8f22a2015-04-01 06:58:48 -070035 { SkGifCodec::IsGif, SkGifCodec::NewFromStream },
msarett9bde9182015-03-25 05:27:48 -070036 { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
halcanarya096d7a2015-03-27 12:16:53 -070037 { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
38 { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
msarett74114382015-03-16 11:55:18 -070039};
40
scroggodb30be22015-12-08 18:54:13 -080041size_t SkCodec::MinBufferedBytesNeeded() {
42 return WEBP_VP8_HEADER_SIZE;
43}
44
scroggocf98fa92015-11-23 08:14:40 -080045SkCodec* SkCodec::NewFromStream(SkStream* stream,
46 SkPngChunkReader* chunkReader) {
scroggof24f2242015-03-03 08:59:20 -080047 if (!stream) {
halcanary96fcdcc2015-08-27 07:41:13 -070048 return nullptr;
scroggof24f2242015-03-03 08:59:20 -080049 }
scroggo0a7e69c2015-04-03 07:22:22 -070050
51 SkAutoTDelete<SkStream> streamDeleter(stream);
scroggodb30be22015-12-08 18:54:13 -080052
53 // 14 is enough to read all of the supported types.
54 const size_t bytesToRead = 14;
55 SkASSERT(bytesToRead <= MinBufferedBytesNeeded());
56
57 char buffer[bytesToRead];
58 size_t bytesRead = stream->peek(buffer, bytesToRead);
59
60 // It is also possible to have a complete image less than bytesToRead bytes
61 // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead.
62 // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter
63 // than bytesToRead, so pass that directly to the decoder.
64 // It also is possible the stream uses too small a buffer for peeking, but
65 // we trust the caller to use a large enough buffer.
66
67 if (0 == bytesRead) {
scroggo3ab9f2e2016-01-06 09:53:34 -080068 // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this
69 // printf could be useful to notice failures.
70 // SkCodecPrintf("Encoded image data failed to peek!\n");
71
scroggodb30be22015-12-08 18:54:13 -080072 // It is possible the stream does not support peeking, but does support
73 // rewinding.
74 // Attempt to read() and pass the actual amount read to the decoder.
75 bytesRead = stream->read(buffer, bytesToRead);
76 if (!stream->rewind()) {
scroggo3ab9f2e2016-01-06 09:53:34 -080077 SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
scroggodb30be22015-12-08 18:54:13 -080078 return nullptr;
79 }
80 }
81
scroggocf98fa92015-11-23 08:14:40 -080082 // PNG is special, since we want to be able to supply an SkPngChunkReader.
83 // But this code follows the same pattern as the loop.
scroggodb30be22015-12-08 18:54:13 -080084 if (SkPngCodec::IsPng(buffer, bytesRead)) {
msarettf44631b2016-01-13 10:54:20 -080085 return SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader);
scroggocf98fa92015-11-23 08:14:40 -080086 } else {
87 for (DecoderProc proc : gDecoderProcs) {
scroggodb30be22015-12-08 18:54:13 -080088 if (proc.IsFormat(buffer, bytesRead)) {
msarettf44631b2016-01-13 10:54:20 -080089 return proc.NewFromStream(streamDeleter.detach());
scroggocf98fa92015-11-23 08:14:40 -080090 }
msarett74114382015-03-16 11:55:18 -070091 }
yujieqin916de9f2016-01-25 08:26:16 -080092
93#ifdef SK_CODEC_DECODES_RAW
94 // Try to treat the input as RAW if all the other checks failed.
95 return SkRawCodec::NewFromStream(streamDeleter.detach());
96#endif
scroggof24f2242015-03-03 08:59:20 -080097 }
msarett8c8f22a2015-04-01 06:58:48 -070098
msarettf44631b2016-01-13 10:54:20 -080099 return nullptr;
scroggof24f2242015-03-03 08:59:20 -0800100}
101
scroggocf98fa92015-11-23 08:14:40 -0800102SkCodec* SkCodec::NewFromData(SkData* data, SkPngChunkReader* reader) {
scroggof24f2242015-03-03 08:59:20 -0800103 if (!data) {
halcanary96fcdcc2015-08-27 07:41:13 -0700104 return nullptr;
scroggof24f2242015-03-03 08:59:20 -0800105 }
scroggocf98fa92015-11-23 08:14:40 -0800106 return NewFromStream(new SkMemoryStream(data), reader);
scroggof24f2242015-03-03 08:59:20 -0800107}
108
109SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream)
scroggo46c57472015-09-30 08:57:13 -0700110 : fSrcInfo(info)
scroggof24f2242015-03-03 08:59:20 -0800111 , fStream(stream)
112 , fNeedsRewind(false)
scroggo46c57472015-09-30 08:57:13 -0700113 , fDstInfo()
114 , fOptions()
115 , fCurrScanline(-1)
scroggof24f2242015-03-03 08:59:20 -0800116{}
117
scroggo9b2cdbf42015-07-10 12:07:02 -0700118SkCodec::~SkCodec() {}
scroggoeb602a52015-07-09 08:16:03 -0700119
scroggob427db12015-08-12 07:24:13 -0700120bool SkCodec::rewindIfNeeded() {
scroggo3a7701c2015-09-30 09:15:14 -0700121 if (!fStream) {
122 // Some codecs do not have a stream, but they hold others that do. They
123 // must handle rewinding themselves.
124 return true;
125 }
126
scroggof24f2242015-03-03 08:59:20 -0800127 // Store the value of fNeedsRewind so we can update it. Next read will
128 // require a rewind.
halcanarya096d7a2015-03-27 12:16:53 -0700129 const bool needsRewind = fNeedsRewind;
scroggof24f2242015-03-03 08:59:20 -0800130 fNeedsRewind = true;
halcanarya096d7a2015-03-27 12:16:53 -0700131 if (!needsRewind) {
scroggob427db12015-08-12 07:24:13 -0700132 return true;
halcanarya096d7a2015-03-27 12:16:53 -0700133 }
scroggob427db12015-08-12 07:24:13 -0700134
scroggo46c57472015-09-30 08:57:13 -0700135 // startScanlineDecode will need to be called before decoding scanlines.
136 fCurrScanline = -1;
137
scroggob427db12015-08-12 07:24:13 -0700138 if (!fStream->rewind()) {
139 return false;
140 }
141
142 return this->onRewind();
scroggof24f2242015-03-03 08:59:20 -0800143}
scroggo05245902015-03-25 11:11:52 -0700144
scroggoeb602a52015-07-09 08:16:03 -0700145SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
146 const Options* options, SkPMColor ctable[], int* ctableCount) {
147 if (kUnknown_SkColorType == info.colorType()) {
148 return kInvalidConversion;
149 }
halcanary96fcdcc2015-08-27 07:41:13 -0700150 if (nullptr == pixels) {
scroggoeb602a52015-07-09 08:16:03 -0700151 return kInvalidParameters;
152 }
153 if (rowBytes < info.minRowBytes()) {
154 return kInvalidParameters;
155 }
156
157 if (kIndex_8_SkColorType == info.colorType()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700158 if (nullptr == ctable || nullptr == ctableCount) {
scroggoeb602a52015-07-09 08:16:03 -0700159 return kInvalidParameters;
160 }
161 } else {
162 if (ctableCount) {
163 *ctableCount = 0;
164 }
halcanary96fcdcc2015-08-27 07:41:13 -0700165 ctableCount = nullptr;
166 ctable = nullptr;
scroggoeb602a52015-07-09 08:16:03 -0700167 }
168
scroggocc2feb12015-08-14 08:32:46 -0700169 {
170 SkAlphaType canonical;
171 if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &canonical)
172 || canonical != info.alphaType())
173 {
174 return kInvalidConversion;
175 }
176 }
177
scroggo3a7701c2015-09-30 09:15:14 -0700178 if (!this->rewindIfNeeded()) {
179 return kCouldNotRewind;
180 }
181
scroggoeb602a52015-07-09 08:16:03 -0700182 // Default options.
183 Options optsStorage;
halcanary96fcdcc2015-08-27 07:41:13 -0700184 if (nullptr == options) {
scroggoeb602a52015-07-09 08:16:03 -0700185 options = &optsStorage;
scroggoe7fc14b2015-10-02 13:14:46 -0700186 } else if (options->fSubset) {
187 SkIRect subset(*options->fSubset);
188 if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
189 // FIXME: How to differentiate between not supporting subset at all
190 // and not supporting this particular subset?
191 return kUnimplemented;
192 }
scroggoeb602a52015-07-09 08:16:03 -0700193 }
scroggoe7fc14b2015-10-02 13:14:46 -0700194
195 // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
196 // because it supports arbitrary scaling/subset combinations.
197 if (!this->dimensionsSupported(info.dimensions())) {
198 return kInvalidScale;
199 }
200
msarette6dd0042015-10-09 11:07:34 -0700201 // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
202 // successfully.
203 int rowsDecoded = 0;
204 const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount,
205 &rowsDecoded);
scroggoeb602a52015-07-09 08:16:03 -0700206
207 if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
208 SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
209 }
msarette6dd0042015-10-09 11:07:34 -0700210
211 // A return value of kIncompleteInput indicates a truncated image stream.
212 // In this case, we will fill any uninitialized memory with a default value.
213 // Some subclasses will take care of filling any uninitialized memory on
214 // their own. They indicate that all of the memory has been filled by
215 // setting rowsDecoded equal to the height.
216 if (kIncompleteInput == result && rowsDecoded != info.height()) {
217 this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
218 rowsDecoded);
219 }
220
scroggoeb602a52015-07-09 08:16:03 -0700221 return result;
222}
223
224SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
halcanary96fcdcc2015-08-27 07:41:13 -0700225 return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr);
scroggoeb602a52015-07-09 08:16:03 -0700226}
scroggo46c57472015-09-30 08:57:13 -0700227
228SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo,
229 const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
230 // Reset fCurrScanline in case of failure.
231 fCurrScanline = -1;
232 // Ensure that valid color ptrs are passed in for kIndex8 color type
233 if (kIndex_8_SkColorType == dstInfo.colorType()) {
234 if (nullptr == ctable || nullptr == ctableCount) {
235 return SkCodec::kInvalidParameters;
236 }
237 } else {
238 if (ctableCount) {
239 *ctableCount = 0;
240 }
241 ctableCount = nullptr;
242 ctable = nullptr;
243 }
244
scroggo3a7701c2015-09-30 09:15:14 -0700245 if (!this->rewindIfNeeded()) {
246 return kCouldNotRewind;
247 }
248
scroggo46c57472015-09-30 08:57:13 -0700249 // Set options.
250 Options optsStorage;
251 if (nullptr == options) {
252 options = &optsStorage;
scroggoe7fc14b2015-10-02 13:14:46 -0700253 } else if (options->fSubset) {
msarettfdb47572015-10-13 12:50:14 -0700254 SkIRect size = SkIRect::MakeSize(dstInfo.dimensions());
255 if (!size.contains(*options->fSubset)) {
256 return kInvalidInput;
257 }
258
259 // We only support subsetting in the x-dimension for scanline decoder.
260 // Subsetting in the y-dimension can be accomplished using skipScanlines().
261 if (options->fSubset->top() != 0 || options->fSubset->height() != dstInfo.height()) {
262 return kInvalidInput;
scroggoe7fc14b2015-10-02 13:14:46 -0700263 }
264 }
265
266 // FIXME: Support subsets somehow?
267 if (!this->dimensionsSupported(dstInfo.dimensions())) {
268 return kInvalidScale;
scroggo46c57472015-09-30 08:57:13 -0700269 }
270
271 const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount);
272 if (result != SkCodec::kSuccess) {
273 return result;
274 }
275
276 fCurrScanline = 0;
277 fDstInfo = dstInfo;
278 fOptions = *options;
279 return kSuccess;
280}
281
282SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) {
283 return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr);
284}
285
msarette6dd0042015-10-09 11:07:34 -0700286int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
scroggo46c57472015-09-30 08:57:13 -0700287 if (fCurrScanline < 0) {
msarette6dd0042015-10-09 11:07:34 -0700288 return 0;
scroggo46c57472015-09-30 08:57:13 -0700289 }
290
291 SkASSERT(!fDstInfo.isEmpty());
scroggoe7fc14b2015-10-02 13:14:46 -0700292 if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
msarette6dd0042015-10-09 11:07:34 -0700293 return 0;
scroggo46c57472015-09-30 08:57:13 -0700294 }
295
msarette6dd0042015-10-09 11:07:34 -0700296 const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
297 if (linesDecoded < countLines) {
298 this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized,
299 countLines, linesDecoded);
300 }
301 fCurrScanline += countLines;
302 return linesDecoded;
303}
304
305bool SkCodec::skipScanlines(int countLines) {
306 if (fCurrScanline < 0) {
307 return false;
308 }
309
310 SkASSERT(!fDstInfo.isEmpty());
311 if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) {
312 // Arguably, we could just skip the scanlines which are remaining,
313 // and return true. We choose to return false so the client
314 // can catch their bug.
315 return false;
316 }
317
318 bool result = this->onSkipScanlines(countLines);
scroggo46c57472015-09-30 08:57:13 -0700319 fCurrScanline += countLines;
320 return result;
321}
322
msarette6dd0042015-10-09 11:07:34 -0700323int SkCodec::outputScanline(int inputScanline) const {
324 SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height());
325 return this->onOutputScanline(inputScanline);
326}
scroggo46c57472015-09-30 08:57:13 -0700327
msarette6dd0042015-10-09 11:07:34 -0700328int SkCodec::onOutputScanline(int inputScanline) const {
329 switch (this->getScanlineOrder()) {
330 case kTopDown_SkScanlineOrder:
331 case kNone_SkScanlineOrder:
332 return inputScanline;
333 case kBottomUp_SkScanlineOrder:
334 return this->getInfo().height() - inputScanline - 1;
335 default:
336 // This case indicates an interlaced gif and is implemented by SkGifCodec.
337 SkASSERT(false);
338 return 0;
scroggo46c57472015-09-30 08:57:13 -0700339 }
msarette6dd0042015-10-09 11:07:34 -0700340}
scroggo46c57472015-09-30 08:57:13 -0700341
msarette6dd0042015-10-09 11:07:34 -0700342static void fill_proc(const SkImageInfo& info, void* dst, size_t rowBytes,
343 uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit, SkSampler* sampler) {
344 if (sampler) {
345 sampler->fill(info, dst, rowBytes, colorOrIndex, zeroInit);
346 } else {
347 SkSampler::Fill(info, dst, rowBytes, colorOrIndex, zeroInit);
348 }
349}
350
351void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
352 ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {
353
354 void* fillDst;
355 const uint32_t fillValue = this->getFillValue(info.colorType(), info.alphaType());
356 const int linesRemaining = linesRequested - linesDecoded;
357 SkSampler* sampler = this->getSampler(false);
358
359 switch (this->getScanlineOrder()) {
360 case kTopDown_SkScanlineOrder:
361 case kNone_SkScanlineOrder: {
362 const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining);
363 fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes);
364 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
365 break;
366 }
367 case kBottomUp_SkScanlineOrder: {
368 fillDst = dst;
369 const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining);
370 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
371 break;
372 }
373 case kOutOfOrder_SkScanlineOrder: {
374 SkASSERT(1 == linesRequested || this->getInfo().height() == linesRequested);
375 const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
376 for (int srcY = linesDecoded; srcY < linesRequested; srcY++) {
377 fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * rowBytes);
378 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
379 }
380 break;
381 }
382 }
scroggo46c57472015-09-30 08:57:13 -0700383}