/* * 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; }
/* * 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); }
/* * 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(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; }
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); }
SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr, int* inputColorCount, const Options& opts) { // Check for valid input parameters if (!conversion_possible(dstInfo, this->getInfo())) { return gif_error("Cannot convert input type to output type.\n", kInvalidConversion); } // Initialize color table and copy to the client if necessary this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount); return kSuccess; }
Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, SkPMColor ctable[], int* ctableCount) override { if (!conversion_possible(dstInfo, this->getInfo())) { return kInvalidConversion; } const Result result = this->initializeSwizzler(dstInfo, options, ctable, ctableCount); if (result != kSuccess) { return result; } fStorage.reset(this->getInfo().width() * (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()))); fSrcRow = fStorage.get(); return kSuccess; }
SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, size_t dstRowBytes, const Options& options, SkPMColor ctable[], int* ctableCount, int* rowsDecoded) { if (!conversion_possible(requestedInfo, this->getInfo())) { return kInvalidConversion; } if (options.fSubset) { // Subsets are not supported. return kUnimplemented; } // Note that ctable and ctableCount may be modified if there is a color table if (!this->initializeSwizzler(requestedInfo, options, ctable, ctableCount)) { return kInvalidInput; // or parameters? } return this->decodeAllRows(dst, dstRowBytes, rowsDecoded); }
SkScanlineDecoder* SkJpegCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, const Options& options, SkPMColor ctable[], int* ctableCount) { // Rewind the stream if needed if (!this->handleRewind()) { SkCodecPrintf("Could not rewind\n"); return NULL; } // Set the jump location for libjpeg errors if (setjmp(fDecoderMgr->getJmpBuf())) { SkCodecPrintf("setjmp: Error from libjpeg\n"); return NULL; } // Check if we can decode to the requested destination if (!conversion_possible(dstInfo, this->getInfo())) { SkCodecPrintf("Cannot convert to output type\n"); return NULL; } // Perform the necessary scaling if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) { SkCodecPrintf("Cannot scale ot output dimensions\n"); return NULL; } // Now, given valid output dimensions, we can start the decompress if (!jpeg_start_decompress(fDecoderMgr->dinfo())) { SkCodecPrintf("start decompress failed\n"); return NULL; } // Create the swizzler this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options); if (NULL == fSwizzler) { SkCodecPrintf("Could not create swizzler\n"); return NULL; } // Return the new scanline decoder return SkNEW_ARGS(SkJpegScanlineDecoder, (dstInfo, this)); }
SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) override { if (!fCodec->rewindIfNeeded()) { return SkCodec::kCouldNotRewind; } if (options.fSubset) { // Subsets are not supported. return SkCodec::kUnimplemented; } if (dstInfo.dimensions() != this->getInfo().dimensions()) { if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { return SkCodec::kInvalidScale; } } if (!conversion_possible(dstInfo, this->getInfo())) { SkCodecPrintf("Error: cannot convert input type to output type.\n"); return SkCodec::kInvalidConversion; } return fCodec->prepareToDecode(dstInfo, options, inputColorPtr, inputColorCount); }
/* * 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); }
SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, const SkCodec::Options& options, SkPMColor* ctable, int* ctableCount) { if (!conversion_possible(dstInfo, this->getInfo())) { return kInvalidConversion; } if (!this->initializeSwizzler(dstInfo, options, ctable, ctableCount)) { return kInvalidInput; } int firstRow, lastRow; if (options.fSubset) { firstRow = options.fSubset->top(); lastRow = options.fSubset->bottom() - 1; } else { firstRow = 0; lastRow = dstInfo.height() - 1; } this->setRange(firstRow, lastRow, dst, rowBytes); return kSuccess; }
Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, SkPMColor ctable[], int* ctableCount) override { if (!conversion_possible(dstInfo, this->getInfo())) { return kInvalidConversion; } const Result result = this->initializeSwizzler(dstInfo, options, ctable, ctableCount); if (result != kSuccess) { return result; } fHeight = dstInfo.height(); // FIXME: This need not be called on a second call to onStartScanlineDecode. fSrcRowBytes = this->getInfo().width() * (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())); fGarbageRow.reset(fSrcRowBytes); fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); fCanSkipRewind = true; return SkCodec::kSuccess; }
bool SkBitmapRegionCodec::conversionSupported(SkColorType colorType) { return conversion_possible(fCodec->getInfo().makeColorType(colorType), fCodec->getInfo()); }
SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, size_t dstRowBytes, const Options& options, SkPMColor ctable[], int* ctableCount, int* rowsDecoded) { if (!conversion_possible(requestedInfo, this->getInfo())) { return kInvalidConversion; } if (options.fSubset) { // Subsets are not supported. return kUnimplemented; } // Note that ctable and ctableCount may be modified if there is a color table const Result result = this->initializeSwizzler(requestedInfo, options, ctable, ctableCount); if (result != kSuccess) { return result; } const int width = requestedInfo.width(); const int height = requestedInfo.height(); const int bpp = bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()); const size_t srcRowBytes = width * bpp; // FIXME: Could we use the return value of setjmp to specify the type of // error? int row = 0; // This must be declared above the call to setjmp to avoid memory leaks on incomplete images. SkAutoTMalloc<uint8_t> storage; if (setjmp(png_jmpbuf(fPng_ptr))) { // Assume that any error that occurs while reading rows is caused by an incomplete input. if (fNumberPasses > 1) { // FIXME (msarett): Handle incomplete interlaced pngs. return (row == height) ? kSuccess : kInvalidInput; } // FIXME: We do a poor job on incomplete pngs compared to other decoders (ex: Chromium, // Ubuntu Image Viewer). This is because we use the default buffer size in libpng (8192 // bytes), and if we can't fill the buffer, we immediately fail. // For example, if we try to read 8192 bytes, and the image (incorrectly) only contains // half that, which may have been enough to contain a non-zero number of lines, we fail // when we could have decoded a few more lines and then failed. // The read function that we provide for libpng has no way of indicating that we have // made a partial read. // Making our buffer size smaller improves our incomplete decodes, but what impact does // it have on regular decode performance? Should we investigate using a different API // instead of png_read_row? Chromium uses png_process_data. *rowsDecoded = row; return (row == height) ? kSuccess : kIncompleteInput; } // FIXME: We could split these out based on subclass. void* dstRow = dst; if (fNumberPasses > 1) { storage.reset(height * srcRowBytes); uint8_t* const base = storage.get(); for (int i = 0; i < fNumberPasses; i++) { uint8_t* srcRow = base; for (int y = 0; y < height; y++) { png_read_row(fPng_ptr, srcRow, nullptr); srcRow += srcRowBytes; } } // Now swizzle it. uint8_t* srcRow = base; for (; row < height; row++) { fSwizzler->swizzle(dstRow, srcRow); dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); srcRow += srcRowBytes; } } else { storage.reset(srcRowBytes); uint8_t* srcRow = storage.get(); for (; row < height; row++) { png_read_row(fPng_ptr, srcRow, nullptr); fSwizzler->swizzle(dstRow, srcRow); dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); } } // read rest of file, and get additional comment and time chunks in info_ptr png_read_end(fPng_ptr, fInfo_ptr); return kSuccess; }
bool SkBitmapRegionCodec::conversionSupported(SkColorType colorType) { // FIXME: Call virtual function when it lands. SkImageInfo info = SkImageInfo::Make(0, 0, colorType, fCodec->getInfo().alphaType(), fCodec->getInfo().profileType()); return conversion_possible(info, fCodec->getInfo()); }
/* * Performs the jpeg decode */ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& options, SkPMColor*, int*) { // Rewind the stream if needed if (!this->handleRewind()) { fDecoderMgr->returnFailure("could not rewind stream", kCouldNotRewind); } // Get a pointer to the decompress info since we will use it quite frequently jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo(); // Set the jump location for libjpeg errors if (setjmp(fDecoderMgr->getJmpBuf())) { return fDecoderMgr->returnFailure("setjmp", kInvalidInput); } // Check if we can decode to the requested destination if (!conversion_possible(dstInfo, this->getInfo())) { return fDecoderMgr->returnFailure("conversion_possible", kInvalidConversion); } // Perform the necessary scaling if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) { fDecoderMgr->returnFailure("cannot scale to requested dims", kInvalidScale); } // Now, given valid output dimensions, we can start the decompress if (!jpeg_start_decompress(dinfo)) { return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); } // Create the swizzler this->initializeSwizzler(dstInfo, dst, dstRowBytes, options); if (NULL == fSwizzler) { return fDecoderMgr->returnFailure("getSwizzler", kUnimplemented); } // This is usually 1, but can also be 2 or 4. // If we wanted to always read one row at a time, we could, but we will save space and time // by using the recommendation from libjpeg. const uint32_t rowsPerDecode = dinfo->rec_outbuf_height; SkASSERT(rowsPerDecode <= 4); // Create a buffer to contain decoded rows (libjpeg requires a 2D array) SkASSERT(0 != fSrcRowBytes); SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, fSrcRowBytes * rowsPerDecode)); JSAMPLE* srcRows[4]; uint8_t* srcPtr = srcBuffer.get(); for (uint8_t i = 0; i < rowsPerDecode; i++) { srcRows[i] = (JSAMPLE*) srcPtr; srcPtr += fSrcRowBytes; } // Ensure that we loop enough times to decode all of the rows // libjpeg will prevent us from reading past the bottom of the image uint32_t dstHeight = dstInfo.height(); for (uint32_t y = 0; y < dstHeight + rowsPerDecode - 1; y += rowsPerDecode) { // Read rows of the image uint32_t rowsDecoded = jpeg_read_scanlines(dinfo, srcRows, rowsPerDecode); // Convert to RGB if necessary if (JCS_CMYK == dinfo->out_color_space) { convert_CMYK_to_RGB(srcRows[0], dstInfo.width() * rowsDecoded); } // Swizzle to output destination for (uint32_t i = 0; i < rowsDecoded; i++) { fSwizzler->next(srcRows[i]); } // If we cannot read enough rows, assume the input is incomplete if (rowsDecoded < rowsPerDecode && y + rowsDecoded < dstHeight) { // Fill the remainder of the image with black. This error handling // behavior is unspecified but SkCodec consistently uses black as // the fill color for opaque images. If the destination is kGray, // the low 8 bits of SK_ColorBLACK will be used. Conveniently, // these are zeros, which is the representation for black in kGray. SkSwizzler::Fill(fSwizzler->getDstRow(), dstInfo, dstRowBytes, dstHeight - y - rowsDecoded, SK_ColorBLACK, NULL); // Prevent libjpeg from failing on incomplete decode dinfo->output_scanline = dstHeight; // Finish the decode and indicate that the input was incomplete. jpeg_finish_decompress(dinfo); return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput); } } jpeg_finish_decompress(dinfo); return kSuccess; }
SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& options, SkPMColor ctable[], int* ctableCount, int* rowsDecoded) { if (!conversion_possible(dstInfo, this->getInfo()) || !this->initializeColorXform(dstInfo, options.fPremulBehavior)) { SkCodecPrintf("Error: cannot convert input type to output type.\n"); return kInvalidConversion; } static const SkColorType kXformSrcColorType = kRGBA_8888_SkColorType; SkImageInfo swizzlerInfo = dstInfo; std::unique_ptr<uint32_t[]> xformBuffer = nullptr; if (this->colorXform()) { swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType); xformBuffer.reset(new uint32_t[dstInfo.width()]); } std::unique_ptr<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler( this->getEncodedInfo(), nullptr, swizzlerInfo, options)); SkASSERT(swizzler); const int width = dstInfo.width(); const int height = dstInfo.height(); std::unique_ptr<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 / (float) width > maxDiffRatio || imageSize.h < width || imageSize.v / (float) 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; } if (this->colorXform()) { swizzler->swizzle(xformBuffer.get(), &srcRow[0]); const SkColorSpaceXform::ColorFormat srcFormat = select_xform_format(kXformSrcColorType); const SkColorSpaceXform::ColorFormat dstFormat = select_xform_format(dstInfo.colorType()); this->colorXform()->apply(dstFormat, dstRow, srcFormat, xformBuffer.get(), dstInfo.width(), kOpaque_SkAlphaType); dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); } else { swizzler->swizzle(dstRow, &srcRow[0]); dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); } } return kSuccess; }