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 45 size_t SkCodec::MinBufferedBytesNeeded() { 46 return WEBP_VP8_HEADER_SIZE; 47 } 48 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 109 SkCodec* SkCodec::NewFromData(SkData* data, SkPngChunkReader* reader) { 110 if (!data) { 111 return nullptr; 112 } 113 return NewFromStream(new SkMemoryStream(data), reader); 114 } 115 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 125 SkCodec::~SkCodec() {} 126 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 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 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 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 280 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) { 281 return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr); 282 } 283 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 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 321 int SkCodec::outputScanline(int inputScanline) const { 322 SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height()); 323 return this->onOutputScanline(inputScanline); 324 } 325 326 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 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 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