/* * Initiates the bitmap decode */ SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts, SkPMColor* inputColorPtr, int* inputColorCount) { if (!this->rewindIfNeeded()) { return kCouldNotRewind; } if (opts.fSubset) { // Subsets are not supported. return kUnimplemented; } if (dstInfo.dimensions() != this->getInfo().dimensions()) { SkCodecPrintf("Error: scaling not supported.\n"); return kInvalidScale; } if (!conversion_possible(dstInfo, this->getInfo())) { SkCodecPrintf("Error: cannot convert input type to output type.\n"); return kInvalidConversion; } // Initialize a the mask swizzler if (!this->initializeSwizzler(dstInfo)) { SkCodecPrintf("Error: cannot initialize swizzler.\n"); return kInvalidConversion; } return this->decode(dstInfo, dst, dstRowBytes, opts); }
SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, SkPMColor ctable[], int* ctableCount) { // Set the jump location for libjpeg errors if (setjmp(fDecoderMgr->getJmpBuf())) { SkCodecPrintf("setjmp: Error from libjpeg\n"); return kInvalidInput; } // Check if we can decode to the requested destination and set the output color space if (!this->setOutputColorSpace(dstInfo)) { return kInvalidConversion; } // Remove objects used for sampling. fSwizzler.reset(nullptr); fSrcRow = nullptr; fStorage.free(); // Now, given valid output dimensions, we can start the decompress if (!jpeg_start_decompress(fDecoderMgr->dinfo())) { SkCodecPrintf("start decompress failed\n"); return kInvalidInput; } // We will need a swizzler if we are performing a subset decode or // converting from CMYK. if (options.fSubset || JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) { this->initializeSwizzler(dstInfo, options); } return kSuccess; }
/* * Initiates the Ico decode */ SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts, SkPMColor* ct, int* ptr) { if (opts.fSubset) { // Subsets are not supported. return kUnimplemented; } // We return invalid scale if there is no candidate image with matching // dimensions. Result result = kInvalidScale; for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) { // If the dimensions match, try to decode if (dstInfo.dimensions() == fEmbeddedCodecs->operator[](i)->getInfo().dimensions()) { // Perform the decode result = fEmbeddedCodecs->operator[](i)->getPixels(dstInfo, dst, dstRowBytes, &opts, ct, ptr); // On a fatal error, keep trying to find an image to decode if (kInvalidConversion == result || kInvalidInput == result || kInvalidScale == result) { SkCodecPrintf("Warning: Attempt to decode candidate ico failed.\n"); continue; } // On success or partial success, return the result return result; } } SkCodecPrintf("Error: No matching candidate image in ico.\n"); return result; }
/* * Initiates the bitmap decode */ SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts, SkPMColor* inputColorPtr, int* inputColorCount, int* rowsDecoded) { if (opts.fSubset) { // Subsets are not supported. return kUnimplemented; } if (dstInfo.dimensions() != this->getInfo().dimensions()) { SkCodecPrintf("Error: scaling not supported.\n"); return kInvalidScale; } if (!conversion_possible(dstInfo, this->getInfo())) { SkCodecPrintf("Error: cannot convert input type to output type.\n"); return kInvalidConversion; } Result result = this->prepareToDecode(dstInfo, opts, inputColorPtr, inputColorCount); if (kSuccess != result) { return result; } int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts); if (rows != dstInfo.height()) { *rowsDecoded = rows; return kIncompleteInput; } return kSuccess; }
SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) { // FIXME: Support subsets for scanline decodes. if (options.fSubset) { // Subsets are not supported. return kUnimplemented; } // Reset fSampleX. If it needs to be a value other than 1, it will get modified by // the sampler. fSampleX = 1; fLinesToSkip = 0; // Create the color table if necessary and prepare the stream for decode // Note that if it is non-NULL, inputColorCount will be modified if (!this->createColorTable(dstInfo.colorType(), inputColorCount)) { SkCodecPrintf("Error: could not create color table.\n"); return SkCodec::kInvalidInput; } // Copy the color table to the client if necessary copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount); // Initialize a buffer for encoded RLE data fRLEBytes = fOrigRLEBytes; if (!this->initializeStreamBuffer()) { SkCodecPrintf("Error: cannot initialize stream buffer.\n"); return SkCodec::kInvalidInput; } return SkCodec::kSuccess; }
SkCodec* SkCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) { if (!stream) { return nullptr; } SkAutoTDelete<SkStream> streamDeleter(stream); // 14 is enough to read all of the supported types. const size_t bytesToRead = 14; SkASSERT(bytesToRead <= MinBufferedBytesNeeded()); char buffer[bytesToRead]; size_t bytesRead = stream->peek(buffer, bytesToRead); // It is also possible to have a complete image less than bytesToRead bytes // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead. // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter // than bytesToRead, so pass that directly to the decoder. // It also is possible the stream uses too small a buffer for peeking, but // we trust the caller to use a large enough buffer. if (0 == bytesRead) { SkCodecPrintf("Could not peek!\n"); // It is possible the stream does not support peeking, but does support // rewinding. // Attempt to read() and pass the actual amount read to the decoder. bytesRead = stream->read(buffer, bytesToRead); if (!stream->rewind()) { SkCodecPrintf("Could not rewind!\n"); return nullptr; } } SkAutoTDelete<SkCodec> codec(nullptr); // PNG is special, since we want to be able to supply an SkPngChunkReader. // But this code follows the same pattern as the loop. if (SkPngCodec::IsPng(buffer, bytesRead)) { codec.reset(SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader)); } else { for (DecoderProc proc : gDecoderProcs) { if (proc.IsFormat(buffer, bytesRead)) { codec.reset(proc.NewFromStream(streamDeleter.detach())); break; } } } // Set the max size at 128 megapixels (512 MB for kN32). // This is about 4x smaller than a test image that takes a few minutes for // dm to decode and draw. const int32_t maxSize = 1 << 27; if (codec && codec->getInfo().width() * codec->getInfo().height() > maxSize) { SkCodecPrintf("Error: Image size too large, cannot decode.\n"); return nullptr; } else { return codec.detach(); } }
SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( SkStreamRewindable* stream, Strategy strategy) { SkAutoTDelete<SkStreamRewindable> streamDeleter(stream); switch (strategy) { case kCanvas_Strategy: { SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(streamDeleter.detach())); if (nullptr == codec) { SkCodecPrintf("Error: Failed to create decoder.\n"); return nullptr; } SkEncodedFormat format = codec->getEncodedFormat(); switch (format) { case SkEncodedFormat::kJPEG_SkEncodedFormat: case SkEncodedFormat::kPNG_SkEncodedFormat: break; default: // FIXME: Support webp using a special case. Webp does not support // scanline decoding. return nullptr; } // If the image is a jpeg or a png, the scanline ordering should always be // kTopDown or kNone. It is relevant to check because this implementation // only supports these two scanline orderings. SkASSERT(SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder() || SkCodec::kNone_SkScanlineOrder == codec->getScanlineOrder()); return new SkBitmapRegionCanvas(codec.detach()); } case kAndroidCodec_Strategy: { SkAutoTDelete<SkAndroidCodec> codec = SkAndroidCodec::NewFromStream(streamDeleter.detach()); if (nullptr == codec) { SkCodecPrintf("Error: Failed to create codec.\n"); return NULL; } SkEncodedFormat format = codec->getEncodedFormat(); switch (format) { case SkEncodedFormat::kJPEG_SkEncodedFormat: case SkEncodedFormat::kPNG_SkEncodedFormat: case SkEncodedFormat::kWEBP_SkEncodedFormat: break; default: return nullptr; } return new SkBitmapRegionCodec(codec.detach()); } default: SkASSERT(false); return nullptr; } }
bool SkBmpRLECodec::initializeStreamBuffer() { // Setup a buffer to contain the full input stream size_t totalBytes = this->stream()->read(fStreamBuffer.get(), fRLEBytes); if (totalBytes < fRLEBytes) { fRLEBytes = totalBytes; SkCodecPrintf("Warning: incomplete RLE file.\n"); } if (fRLEBytes == 0) { SkCodecPrintf("Error: could not read RLE image data.\n"); return false; } return true; }
/* * Performs the decoding */ int SkBmpMaskCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts) { // Iterate over rows of the image uint8_t* srcRow = fSrcBuffer.get(); const int height = dstInfo.height(); for (int y = 0; y < height; y++) { // Read a row of the input if (this->stream()->read(srcRow, this->srcRowBytes()) != this->srcRowBytes()) { SkCodecPrintf("Warning: incomplete input stream.\n"); return y; } // Decode the row in destination format uint32_t row = this->getDstRow(y, height); void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes); if (this->colorXform()) { SkImageInfo xformInfo = dstInfo.makeWH(fMaskSwizzler->swizzleWidth(), dstInfo.height()); fMaskSwizzler->swizzle(this->xformBuffer(), srcRow); this->applyColorXform(xformInfo, dstRow, this->xformBuffer()); } else { fMaskSwizzler->swizzle(dstRow, srcRow); } } // Finished decoding the entire image return height; }
bool SkHeifCodec::conversionSupported(const SkImageInfo& dstInfo, bool srcIsOpaque, bool needsColorXform) { SkASSERT(srcIsOpaque); if (kUnknown_SkAlphaType == dstInfo.alphaType()) { return false; } if (kOpaque_SkAlphaType != dstInfo.alphaType()) { SkCodecPrintf("Warning: an opaque image should be decoded as opaque " "- it is being decoded as non-opaque, which will draw slower\n"); } switch (dstInfo.colorType()) { case kRGBA_8888_SkColorType: return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); case kBGRA_8888_SkColorType: return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888); case kRGB_565_SkColorType: if (needsColorXform) { return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); } else { return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565); } case kRGBA_F16_SkColorType: SkASSERT(needsColorXform); return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); default: return false; } }
SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options, SkPMColor colorTable[], int* colorCount) { if (!ico_conversion_possible(dstInfo)) { return kInvalidConversion; } int index = 0; SkCodec::Result result = kInvalidScale; while (true) { index = this->chooseCodec(dstInfo.dimensions(), index); if (index < 0) { break; } SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index); SkImageInfo decodeInfo = fix_embedded_alpha(dstInfo, embeddedCodec->getInfo().alphaType()); result = embeddedCodec->startScanlineDecode(decodeInfo, &options, colorTable, colorCount); if (kSuccess == result) { fCurrScanlineCodec = embeddedCodec; return result; } index++; } SkCodecPrintf("Error: No matching candidate image in ico.\n"); return result; }
SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, const Options& options, SkPMColor ctable[], int* ctableCount) { // FIXME: Could we use the return value of setjmp to specify the type of // error? if (setjmp(png_jmpbuf(fPng_ptr))) { SkCodecPrintf("setjmp long jump!\n"); return kInvalidInput; } png_read_update_info(fPng_ptr, fInfo_ptr); if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { if (!this->createColorTable(requestedInfo.colorType(), kPremul_SkAlphaType == requestedInfo.alphaType(), ctableCount)) { return kInvalidInput; } } // Copy the color table to the client if they request kIndex8 mode copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); // Create the swizzler. SkPngCodec retains ownership of the color table. const SkPMColor* colors = get_color_ptr(fColorTable.get()); fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, requestedInfo, options)); SkASSERT(fSwizzler); return kSuccess; }
/* * * Process an input mask to obtain the necessary information * */ const SkMasks::MaskInfo process_mask(uint32_t mask, uint32_t bpp) { // Determine properties of the mask uint32_t tempMask = mask; uint32_t shift = 0; uint32_t size = 0; if (tempMask != 0) { // Count trailing zeros on masks for (; (tempMask & 1) == 0; tempMask >>= 1) { shift++; } // Count the size of the mask for (; tempMask & 1; tempMask >>= 1) { size++; } // Verify that the mask is continuous if (tempMask) { SkCodecPrintf("Warning: Bit mask is not continuous.\n"); // Finish processing the mask for (; tempMask; tempMask >>= 1) { size++; } } // Truncate masks greater than 8 bits if (size > 8) { shift += size - 8; size = 8; mask &= 0xFF << shift; } } // Save the calculated values const SkMasks::MaskInfo info = { mask, shift, size }; return info; }
SkCodec* SkCodec::NewFromStream(SkStream* stream) { if (!stream) { return NULL; } SkAutoTDelete<SkStream> streamDeleter(stream); SkAutoTDelete<SkCodec> codec(NULL); for (uint32_t i = 0; i < SK_ARRAY_COUNT(gDecoderProcs); i++) { DecoderProc proc = gDecoderProcs[i]; const bool correctFormat = proc.IsFormat(stream); if (!stream->rewind()) { return NULL; } if (correctFormat) { codec.reset(proc.NewFromStream(streamDeleter.detach())); break; } } // Set the max size at 128 megapixels (512 MB for kN32). // This is about 4x smaller than a test image that takes a few minutes for // dm to decode and draw. const int32_t maxSize = 1 << 27; if (codec && codec->getInfo().width() * codec->getInfo().height() > maxSize) { SkCodecPrintf("Error: Image size too large, cannot decode.\n"); return NULL; } else { return codec.detach(); } }
/* * Initiates the bitmap decode */ SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts, SkPMColor* inputColorPtr, int* inputColorCount, int* rowsDecoded) { if (opts.fSubset) { // Subsets are not supported. return kUnimplemented; } if (!conversion_possible_ignore_color_space(dstInfo, this->getInfo())) { SkCodecPrintf("Error: cannot convert input type to output type.\n"); return kInvalidConversion; } Result result = this->prepareToDecode(dstInfo, opts, inputColorPtr, inputColorCount); if (kSuccess != result) { return result; } // Perform the decode int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts); if (rows != dstInfo.height()) { // We set rowsDecoded equal to the height because the background has already // been filled. RLE encodings sometimes skip pixels, so we always start by // filling the background. *rowsDecoded = dstInfo.height(); return kIncompleteInput; } return kSuccess; }
/* * Performs the decoding */ SkCodec::Result SkBmpMaskCodec::decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts) { // Set constant values const int width = dstInfo.width(); const int height = dstInfo.height(); const size_t rowBytes = SkAlign4(compute_row_bytes(width, this->bitsPerPixel())); // Iterate over rows of the image uint8_t* srcRow = fSrcBuffer.get(); for (int y = 0; y < height; y++) { // Read a row of the input if (this->stream()->read(srcRow, rowBytes) != rowBytes) { SkCodecPrintf("Warning: incomplete input stream.\n"); // Fill the destination image on failure SkPMColor fillColor = dstInfo.alphaType() == kOpaque_SkAlphaType ? SK_ColorBLACK : SK_ColorTRANSPARENT; if (kNo_ZeroInitialized == opts.fZeroInitialized || 0 != fillColor) { void* dstStart = this->getDstStartRow(dst, dstRowBytes, y); SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y, fillColor, nullptr); } return kIncompleteInput; } // Decode the row in destination format int row = SkBmpCodec::kBottomUp_RowOrder == this->rowOrder() ? height - 1 - y : y; void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes); fMaskSwizzler->swizzle(dstRow, srcRow); } // Finished decoding the entire image return kSuccess; }
/* * Initiates the bitmap decode */ SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts, int* rowsDecoded) { if (opts.fSubset) { // Subsets are not supported. return kUnimplemented; } if (dstInfo.dimensions() != this->dimensions()) { SkCodecPrintf("Error: scaling not supported.\n"); return kInvalidScale; } Result result = this->prepareToDecode(dstInfo, opts); if (kSuccess != result) { return result; } int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts); if (rows != dstInfo.height()) { *rowsDecoded = rows; return kIncompleteInput; } return kSuccess; }
SkCodec* SkCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) { if (!stream) { return nullptr; } SkAutoTDelete<SkStream> streamDeleter(stream); // 14 is enough to read all of the supported types. const size_t bytesToRead = 14; SkASSERT(bytesToRead <= MinBufferedBytesNeeded()); char buffer[bytesToRead]; size_t bytesRead = stream->peek(buffer, bytesToRead); // It is also possible to have a complete image less than bytesToRead bytes // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead. // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter // than bytesToRead, so pass that directly to the decoder. // It also is possible the stream uses too small a buffer for peeking, but // we trust the caller to use a large enough buffer. if (0 == bytesRead) { // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this // printf could be useful to notice failures. // SkCodecPrintf("Encoded image data failed to peek!\n"); // It is possible the stream does not support peeking, but does support // rewinding. // Attempt to read() and pass the actual amount read to the decoder. bytesRead = stream->read(buffer, bytesToRead); if (!stream->rewind()) { SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n"); return nullptr; } } // PNG is special, since we want to be able to supply an SkPngChunkReader. // But this code follows the same pattern as the loop. #ifdef SK_CODEC_DECODES_PNG if (SkPngCodec::IsPng(buffer, bytesRead)) { return SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader); } else #endif { for (DecoderProc proc : gDecoderProcs) { if (proc.IsFormat(buffer, bytesRead)) { return proc.NewFromStream(streamDeleter.detach()); } } #ifdef SK_CODEC_DECODES_RAW // Try to treat the input as RAW if all the other checks failed. return SkRawCodec::NewFromStream(streamDeleter.detach()); #endif } return nullptr; }
void onFinish() override { if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n"); return; } jpeg_finish_decompress(fCodec->fDecoderMgr->dinfo()); }
int readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count, int startRow) override { if (setjmp(png_jmpbuf(fPng_ptr))) { SkCodecPrintf("Failed to get scanlines.\n"); // FIXME (msarett): Returning 0 is pessimistic. If we can complete a single pass, // we may be able to report that all of the memory has been initialized. Even if we // fail on the first pass, we can still report than some scanlines are initialized. return 0; } SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); uint8_t* srcRow; for (int i = 0; i < fNumberPasses; i++) { // Discard rows that we planned to skip. for (int y = 0; y < startRow; y++){ png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); } // Read rows we care about into storage. srcRow = storage.get(); for (int y = 0; y < count; y++) { png_read_row(fPng_ptr, srcRow, nullptr); srcRow += fSrcRowBytes; } // Discard rows that we don't need. for (int y = 0; y < this->getInfo().height() - startRow - count; y++) { png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); } } // Swizzle and xform the rows we care about void* swizzlerDstRow = dst; size_t swizzlerDstRowBytes = rowBytes; bool colorXform = fColorXform && apply_xform_on_decode(dstInfo.colorType(), this->getEncodedInfo().color()); if (colorXform) { swizzlerDstRow = fColorXformSrcRow; swizzlerDstRowBytes = 0; } SkAlphaType xformAlphaType = xform_alpha_type(dstInfo.alphaType(), this->getInfo().alphaType()); srcRow = storage.get(); for (int y = 0; y < count; y++) { fSwizzler->swizzle(swizzlerDstRow, srcRow); srcRow = SkTAddOffset<uint8_t>(srcRow, fSrcRowBytes); if (colorXform) { fColorXform->apply(dst, (const uint32_t*) swizzlerDstRow, fSwizzler->swizzleWidth(), dstInfo.colorType(), xformAlphaType); dst = SkTAddOffset<void>(dst, rowBytes); } swizzlerDstRow = SkTAddOffset<void>(swizzlerDstRow, swizzlerDstRowBytes); } return count; }
SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, size_t dstRowBytes, const Options& options, SkPMColor ctable[], int* ctableCount, int* rowsDecoded) { if (!conversion_possible(requestedInfo, this->getInfo())) { SkCodecPrintf("Error: cannot convert input type to output type.\n"); return kInvalidConversion; } SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler( SkSwizzler::kRGB, nullptr, requestedInfo, options)); SkASSERT(swizzler); const int width = requestedInfo.width(); const int height = requestedInfo.height(); SkAutoTDelete<dng_image> image(fDngImage->render(width, height)); if (!image) { return kInvalidInput; } // Because the DNG SDK can not guarantee to render to requested size, we allow a small // difference. Only the overlapping region will be converted. const float maxDiffRatio = 1.03f; const dng_point& imageSize = image->Size(); if (imageSize.h / width > maxDiffRatio || imageSize.h < width || imageSize.v / height > maxDiffRatio || imageSize.v < height) { return SkCodec::kInvalidScale; } void* dstRow = dst; SkAutoTMalloc<uint8_t> srcRow(width * 3); dng_pixel_buffer buffer; buffer.fData = &srcRow[0]; buffer.fPlane = 0; buffer.fPlanes = 3; buffer.fColStep = buffer.fPlanes; buffer.fPlaneStep = 1; buffer.fPixelType = ttByte; buffer.fPixelSize = sizeof(uint8_t); buffer.fRowStep = width * 3; for (int i = 0; i < height; ++i) { buffer.fArea = dng_rect(i, 0, i + 1, width); try { image->Get(buffer, dng_image::edge_zero); } catch (...) { *rowsDecoded = i; return kIncompleteInput; } swizzler->swizzle(dstRow, &srcRow[0]); dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); } return kSuccess; }
SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) { if (!conversion_possible(dstInfo, this->getInfo())) { SkCodecPrintf("Error: cannot convert input type to output type.\n"); return kInvalidConversion; } return prepareToDecode(dstInfo, options, inputColorPtr, inputColorCount); }
bool write(const void* buffer, size_t size) override { size_t newSize; if (!safe_add_to_size_t(this->bytesWritten(), size, &newSize) || newSize > kMaxStreamSize) { SkCodecPrintf("Error: Stream size exceeds the limit.\n"); return false; } return this->INHERITED::write(buffer, size); }
bool onSkipScanlines(int count) override { if (setjmp(png_jmpbuf(fPng_ptr))) { SkCodecPrintf("Failed to skip row.\n"); return false; } for (int row = 0; row < count; row++) { png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); } return true; }
SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( SkStreamRewindable* stream, Strategy strategy) { SkAutoTDelete<SkStreamRewindable> streamDeleter(stream); switch (strategy) { case kCanvas_Strategy: { SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(streamDeleter.detach())); if (nullptr == codec) { SkCodecPrintf("Error: Failed to create decoder.\n"); return nullptr; } if (SkEncodedFormat::kWEBP_SkEncodedFormat == codec->getEncodedFormat()) { // FIXME: Support webp using a special case. Webp does not support // scanline decoding. return nullptr; } switch (codec->getScanlineOrder()) { case SkCodec::kTopDown_SkScanlineOrder: case SkCodec::kNone_SkScanlineOrder: break; default: SkCodecPrintf("Error: Scanline ordering not supported.\n"); return nullptr; } return new SkBitmapRegionCanvas(codec.detach()); } case kAndroidCodec_Strategy: { SkAutoTDelete<SkAndroidCodec> codec = SkAndroidCodec::NewFromStream(streamDeleter.detach()); if (NULL == codec) { SkCodecPrintf("Error: Failed to create codec.\n"); return NULL; } return new SkBitmapRegionCodec(codec.detach()); } default: SkASSERT(false); return nullptr; } }
bool onSkipScanlines(int count) override { // Assume that an error in libpng indicates an incomplete input. if (setjmp(png_jmpbuf(this->png_ptr()))) { SkCodecPrintf("setjmp long jump!\n"); return false; } for (int row = 0; row < count; row++) { png_read_row(this->png_ptr(), fSrcRow, nullptr); } return true; }
bool SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options, SkPMColor ctable[], int* ctableCount) { if (setjmp(png_jmpbuf(fPng_ptr))) { SkCodecPrintf("Failed on png_read_update_info.\n"); return false; } png_read_update_info(fPng_ptr, fInfo_ptr); // It's important to reset fColorXform to nullptr. We don't do this on rewinding // because the interlaced scanline decoder may need to rewind. fColorXform = nullptr; SkImageInfo swizzlerInfo = dstInfo; bool needsColorXform = needs_color_xform(dstInfo, this->getInfo()); if (needsColorXform) { switch (dstInfo.colorType()) { case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: case kRGBA_F16_SkColorType: swizzlerInfo = swizzlerInfo.makeColorType(kRGBA_8888_SkColorType); if (kPremul_SkAlphaType == dstInfo.alphaType()) { swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType); } break; case kIndex_8_SkColorType: break; default: return false; } fColorXform = SkColorSpaceXform::New(sk_ref_sp(this->getInfo().colorSpace()), sk_ref_sp(dstInfo.colorSpace())); if (!fColorXform && kRGBA_F16_SkColorType == dstInfo.colorType()) { return false; } } if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { if (!this->createColorTable(dstInfo, ctableCount)) { return false; } } // Copy the color table to the client if they request kIndex8 mode copy_color_table(swizzlerInfo, fColorTable, ctable, ctableCount); // Create the swizzler. SkPngCodec retains ownership of the color table. const SkPMColor* colors = get_color_ptr(fColorTable.get()); fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, swizzlerInfo, options)); SkASSERT(fSwizzler); return true; }
/* * Initiates the bitmap decode */ SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts, SkPMColor* inputColorPtr, int* inputColorCount) { if (!this->rewindIfNeeded()) { return kCouldNotRewind; } if (opts.fSubset) { // Subsets are not supported. return kUnimplemented; } if (dstInfo.dimensions() != this->getInfo().dimensions()) { SkCodecPrintf("Error: scaling not supported.\n"); return kInvalidScale; } if (!conversion_possible(dstInfo, this->getInfo())) { SkCodecPrintf("Error: cannot convert input type to output type.\n"); return kInvalidConversion; } // Create the color table if necessary and prepare the stream for decode // Note that if it is non-NULL, inputColorCount will be modified if (!this->createColorTable(inputColorCount)) { SkCodecPrintf("Error: could not create color table.\n"); return kInvalidInput; } // Copy the color table to the client if necessary copy_color_table(dstInfo, fColorTable, inputColorPtr, inputColorCount); // Initialize a swizzler if necessary if (!this->initializeStreamBuffer()) { SkCodecPrintf("Error: cannot initialize swizzler.\n"); return kInvalidConversion; } // Perform the decode return decode(dstInfo, dst, dstRowBytes, opts); }
void SkSampler::Fill(const SkImageInfo& info, void* dst, size_t rowBytes, SkCodec::ZeroInitialized zeroInit) { SkASSERT(dst != nullptr); if (SkCodec::kYes_ZeroInitialized == zeroInit) { return; } const int width = info.width(); const int numRows = info.height(); // Use the proper memset routine to fill the remaining bytes switch (info.colorType()) { case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: { uint32_t* dstRow = (uint32_t*) dst; for (int row = 0; row < numRows; row++) { sk_memset32(dstRow, 0, width); dstRow = SkTAddOffset<uint32_t>(dstRow, rowBytes); } break; } case kRGB_565_SkColorType: { uint16_t* dstRow = (uint16_t*) dst; for (int row = 0; row < numRows; row++) { sk_memset16(dstRow, 0, width); dstRow = SkTAddOffset<uint16_t>(dstRow, rowBytes); } break; } case kGray_8_SkColorType: { uint8_t* dstRow = (uint8_t*) dst; for (int row = 0; row < numRows; row++) { memset(dstRow, 0, width); dstRow = SkTAddOffset<uint8_t>(dstRow, rowBytes); } break; } case kRGBA_F16_SkColorType: { uint64_t* dstRow = (uint64_t*) dst; for (int row = 0; row < numRows; row++) { sk_memset64(dstRow, 0, width); dstRow = SkTAddOffset<uint64_t>(dstRow, rowBytes); } break; } default: SkCodecPrintf("Error: Unsupported dst color type for fill(). Doing nothing.\n"); SkASSERT(false); break; } }
bool SkBmpRLECodec::initializeStreamBuffer() { // Setup a buffer to contain the full input stream // TODO (msarett): I'm not sure it is smart or optimal to trust fRLEBytes (read from header) // as the size of our buffer. First of all, the decode fails if fRLEBytes is // corrupt (negative, zero, or small) when we might be able to decode // successfully with a fixed size buffer. Additionally, we would save memory // using a fixed size buffer if the RLE encoding is large. On the other hand, // we may also waste memory with a fixed size buffer. And determining a // minimum size for our buffer would depend on the image width (so it's not // really "fixed" size), and we may end up allocating a buffer that is // generally larger than the average encoded size anyway. size_t totalBytes = this->stream()->read(fStreamBuffer.get(), fRLEBytes); if (totalBytes < fRLEBytes) { fRLEBytes = totalBytes; SkCodecPrintf("Warning: incomplete RLE file.\n"); } if (fRLEBytes == 0) { SkCodecPrintf("Error: could not read RLE image data.\n"); return false; } fCurrRLEByte = 0; return true; }