/* * Read enough of the stream to initialize the SkGifCodec. * Returns a bool representing success or failure. * * @param codecOut * If it returned true, and codecOut was not nullptr, * codecOut will be set to a new SkGifCodec. * * @param gifOut * If it returned true, and codecOut was nullptr, * gifOut must be non-nullptr and gifOut will be set to a new * GifFileType pointer. * * @param stream * Deleted on failure. * codecOut will take ownership of it in the case where we created a codec. * Ownership is unchanged when we returned a gifOut. * */ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) { SkAutoTDelete<SkStream> streamDeleter(stream); // Read gif header, logical screen descriptor, and global color table SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); if (nullptr == gif) { gif_error("DGifOpen failed.\n"); return false; } // Read through gif extensions to get to the image data. Set the // transparent index based on the extension data. uint32_t transIndex; SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex); if (kSuccess != result){ return false; } // Read the image descriptor if (GIF_ERROR == DGifGetImageDesc(gif)) { return false; } // If reading the image descriptor is successful, the image count will be // incremented. SkASSERT(gif->ImageCount >= 1); if (nullptr != codecOut) { SkISize size; SkIRect frameRect; if (!GetDimensions(gif, &size, &frameRect)) { gif_error("Invalid gif size.\n"); return false; } bool frameIsSubset = (size != frameRect.size()); // Determine the encoded alpha type. The transIndex might be valid if it less // than 256. We are not certain that the index is valid until we process the color // table, since some gifs have color tables with less than 256 colors. If // there might be a valid transparent index, we must indicate that the image has // alpha. // In the case where we must support alpha, we indicate kBinary, since every // pixel will either be fully opaque or fully transparent. SkEncodedInfo::Alpha alpha = (transIndex < 256) ? SkEncodedInfo::kBinary_Alpha : SkEncodedInfo::kOpaque_Alpha; // Return the codec // Use kPalette since Gifs are encoded with a color table. // Use 8-bits per component, since this is the output we get from giflib. // FIXME: Gifs can actually be encoded with 4-bits per pixel. Can we support this? SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kPalette_Color, alpha, 8); *codecOut = new SkGifCodec(size.width(), size.height(), info, streamDeleter.release(), gif.release(), transIndex, frameRect, frameIsSubset); } else { SkASSERT(nullptr != gifOut); streamDeleter.release(); *gifOut = gif.release(); } return true; }
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(); } }
SkCodec* SkHeifCodec::NewFromStream(SkStream* stream, Result* result) { std::unique_ptr<SkStream> streamDeleter(stream); std::unique_ptr<HeifDecoder> heifDecoder(createHeifDecoder()); if (heifDecoder.get() == nullptr) { *result = kInternalError; return nullptr; } HeifFrameInfo frameInfo; if (!heifDecoder->init(new SkHeifStreamWrapper(streamDeleter.release()), &frameInfo)) { *result = kInvalidInput; return nullptr; } SkEncodedInfo info = SkEncodedInfo::Make( SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8); Origin orientation = get_orientation(frameInfo); sk_sp<SkColorSpace> colorSpace = nullptr; if ((frameInfo.mIccSize > 0) && (frameInfo.mIccData != nullptr)) { SkColorSpace_Base::ICCTypeFlag iccType = SkColorSpace_Base::kRGB_ICCTypeFlag; colorSpace = SkColorSpace_Base::MakeICC( frameInfo.mIccData.get(), frameInfo.mIccSize, iccType); } if (!colorSpace) { colorSpace = SkColorSpace::MakeSRGB(); } *result = kSuccess; return new SkHeifCodec(frameInfo.mWidth, frameInfo.mHeight, info, heifDecoder.release(), std::move(colorSpace), orientation); }
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(); } }
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; }
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; } }
status_t AVFormatReader::Sniff(int32* _streamCount) { TRACE("AVFormatReader::Sniff\n"); BPositionIO* source = dynamic_cast<BPositionIO*>(Source()); if (source == NULL) { TRACE(" not a BPositionIO, but we need it to be one.\n"); return B_NOT_SUPPORTED; } Stream* stream = new(std::nothrow) Stream(source, &fSourceLock); if (stream == NULL) { ERROR("AVFormatReader::Sniff() - failed to allocate Stream\n"); return B_NO_MEMORY; } ObjectDeleter<Stream> streamDeleter(stream); status_t ret = stream->Open(); if (ret != B_OK) { TRACE(" failed to detect stream: %s\n", strerror(ret)); return ret; } delete[] fStreams; fStreams = NULL; int32 streamCount = stream->CountStreams(); if (streamCount == 0) { TRACE(" failed to detect any streams: %s\n", strerror(ret)); return B_ERROR; } fStreams = new(std::nothrow) Stream*[streamCount]; if (fStreams == NULL) { ERROR("AVFormatReader::Sniff() - failed to allocate streams\n"); return B_NO_MEMORY; } memset(fStreams, 0, sizeof(Stream*) * streamCount); fStreams[0] = stream; streamDeleter.Detach(); #ifdef TRACE_AVFORMAT_READER dump_format(const_cast<AVFormatContext*>(stream->Context()), 0, "", 0); #endif if (_streamCount != NULL) *_streamCount = streamCount; return B_OK; }
SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) { SkAutoTDelete<SkStream> streamDeleter(stream); SkCodec* codec = nullptr; if (ReadHeader(stream, &codec, nullptr)) { // Codec has taken ownership of the stream, we do not need to delete it SkASSERT(codec); streamDeleter.detach(); return codec; } return nullptr; }
/* * Creates a bmp decoder * Reads enough of the stream to determine the image format */ SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool inIco) { SkAutoTDelete<SkStream> streamDeleter(stream); SkCodec* codec = nullptr; if (ReadHeader(stream, inIco, &codec)) { // codec has taken ownership of stream, so we do not need to // delete it. SkASSERT(codec); streamDeleter.detach(); return codec; } return nullptr; }
SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) { SkAutoTDelete<SkStream> streamDeleter(stream); SkCodec* outCodec = nullptr; if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { // Codec has taken ownership of the stream. SkASSERT(outCodec); streamDeleter.release(); return outCodec; } return nullptr; }
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; } }
/* * Read enough of the stream to initialize the SkGifCodec. * Returns a bool representing success or failure. * * @param codecOut * If it returned true, and codecOut was not nullptr, * codecOut will be set to a new SkGifCodec. * * @param gifOut * If it returned true, and codecOut was nullptr, * gifOut must be non-nullptr and gifOut will be set to a new * GifFileType pointer. * * @param stream * Deleted on failure. * codecOut will take ownership of it in the case where we created a codec. * Ownership is unchanged when we returned a gifOut. * */ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) { SkAutoTDelete<SkStream> streamDeleter(stream); // Read gif header, logical screen descriptor, and global color table SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); if (nullptr == gif) { gif_error("DGifOpen failed.\n"); return false; } // Read through gif extensions to get to the image data. Set the // transparent index based on the extension data. uint32_t transIndex; SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex); if (kSuccess != result){ return false; } // Read the image descriptor if (GIF_ERROR == DGifGetImageDesc(gif)) { return false; } // If reading the image descriptor is successful, the image count will be // incremented. SkASSERT(gif->ImageCount >= 1); if (nullptr != codecOut) { SkISize size; SkIRect frameRect; if (!GetDimensions(gif, &size, &frameRect)) { gif_error("Invalid gif size.\n"); return false; } bool frameIsSubset = (size != frameRect.size()); // Determine the recommended alpha type. The transIndex might be valid if it less // than 256. We are not certain that the index is valid until we process the color // table, since some gifs have color tables with less than 256 colors. If // there might be a valid transparent index, we must indicate that the image has // alpha. // In the case where we must support alpha, we have the option to set the // suggested alpha type to kPremul or kUnpremul. Both are valid since the alpha // component will always be 0xFF or the entire 32-bit pixel will be set to zero. // We prefer kPremul because we support kPremul, and it is more efficient to use // kPremul directly even when kUnpremul is supported. SkAlphaType alphaType = (transIndex < 256) ? kPremul_SkAlphaType : kOpaque_SkAlphaType; // Return the codec // kIndex is the most natural color type for gifs, so we set this as // the default. SkImageInfo imageInfo = SkImageInfo::Make(size.width(), size.height(), kIndex_8_SkColorType, alphaType); *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach(), transIndex, frameRect, frameIsSubset); } else { SkASSERT(nullptr != gifOut); streamDeleter.detach(); *gifOut = gif.detach(); } return true; }