namespace SkCodecs { // A static variable inside a function avoids a static initializer. // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/static_initializers.md#removing-static-initializers static std::vector<Decoder>* get_decoders_for_editing() { static SkNoDestructor<std::vector<Decoder>> decoders; #if !defined(SK_DISABLE_LEGACY_INIT_DECODERS) static SkOnce once;
once([] { if (decoders->empty()) { #ifdefined(SK_CODEC_DECODES_PNG)
decoders->push_back(SkPngDecoder::Decoder()); #endif #ifdefined(SK_CODEC_DECODES_JPEG)
decoders->push_back(SkJpegDecoder::Decoder()); #endif #ifdefined(SK_CODEC_DECODES_WEBP)
decoders->push_back(SkWebpDecoder::Decoder()); #endif #ifdefined(SK_CODEC_DECODES_GIF) || defined(SK_HAS_WUFFS_LIBRARY)
decoders->push_back(SkGifDecoder::Decoder()); #endif #ifdefined(SK_CODEC_DECODES_ICO)
decoders->push_back(SkIcoDecoder::Decoder()); #endif #ifdefined(SK_CODEC_DECODES_BMP)
decoders->push_back(SkBmpDecoder::Decoder()); #endif #ifdefined(SK_CODEC_DECODES_WBMP)
decoders->push_back(SkWbmpDecoder::Decoder()); #endif #ifdefined(SK_CODEC_DECODES_AVIF) #ifdefined(SK_BUILD_FOR_ANDROID_FRAMEWORK) // Register CrabbyAvif based SkAvifDecoder on the Android framework // if it is allowed. Otherwise Android framework will use // SkHeifDecoder for decoding AVIF. // TODO: Codec registration for the Android framework has to be // moved outside of skia and this logic has to be moved there. if (property_get_int32("media.avif.crabbyavif", 0) != 0) {
decoders->push_back(SkAvifDecoder::CrabbyAvif::Decoder());
} #else
decoders->push_back(SkAvifDecoder::LibAvif::Decoder()); #endif #endif #ifdefined(SK_CODEC_DECODES_JPEGXL)
decoders->push_back(SkJpegxlDecoder::Decoder()); #endif #ifdefined(SK_HAS_HEIF_LIBRARY)
decoders->push_back(SkHeifDecoder::Decoder()); #endif #ifdefined(SK_CODEC_DECODES_RAW)
decoders->push_back(SkRawDecoder::Decoder()); #endif
}
}); #endif// !defined(SK_DISABLE_LEGACY_INIT_DECODERS) return decoders.get();
}
// 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");
*outResult = kCouldNotRewind; return nullptr;
}
}
SkCodecs::MakeFromStreamCallback rawFallback = nullptr; for (const SkCodecs::Decoder& proc : decoders) { if (proc.isFormat(buffer, bytesRead)) { // Some formats are special, since we want to be able to provide an extra parameter. if (proc.id == "png") { return proc.makeFromStream(std::move(stream), outResult, chunkReader);
} elseif (proc.id == "heif" || proc.id == "gif") { return proc.makeFromStream(std::move(stream), outResult, &selectionPolicy);
} elseif (proc.id == "raw") {
rawFallback = proc.makeFromStream; continue;
} return proc.makeFromStream(std::move(stream), outResult, nullptr);
}
} if (rawFallback != nullptr) { // Fallback to raw. return rawFallback(std::move(stream), outResult, nullptr);
}
switch (dst.colorType()) { case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: case kRGBA_F16_SkColorType: case kBGRA_10101010_XR_SkColorType: returntrue; case kBGR_101010x_XR_SkColorType: case kRGB_565_SkColorType: return srcIsOpaque; case kGray_8_SkColorType: return SkEncodedInfo::kGray_Color == fEncodedInfo.color() && srcIsOpaque; case kAlpha_8_SkColorType: // conceptually we can convert anything into alpha_8, but we haven't actually coded // all of those other conversions yet. return SkEncodedInfo::kXAlpha_Color == fEncodedInfo.color(); default: returnfalse;
}
}
bool SkCodec::rewindIfNeeded() { // Store the value of fNeedsRewind so we can update it. Next read will // require a rewind. constbool needsRewind = fNeedsRewind;
fNeedsRewind = true; if (!needsRewind) { returntrue;
}
// startScanlineDecode will need to be called before decoding scanlines.
fCurrScanline = -1; // startIncrementalDecode will need to be called before incrementalDecode.
fStartedIncrementalDecode = false;
// Some codecs do not have a stream. They may hold onto their own data or another codec. // They must handle rewinding themselves. if (fStream && !fStream->rewind()) { returnfalse;
}
SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options& options, GetPixelsCallback getPixelsFn) { if (getPixelsFn) { // If a callback is used, it handles the frame index, so calls from this SkCodec // should always short-circuit in the else case below.
fUsingCallbackForHandleFrameIndex = true;
} elseif (fUsingCallbackForHandleFrameIndex) { return kSuccess;
}
if (!this->rewindIfNeeded()) { return kCouldNotRewind;
}
constint index = options.fFrameIndex; if (0 == index) { return this->initializeColorXform(info, fEncodedInfo.alpha(), fEncodedInfo.opaque())
? kSuccess : kInvalidConversion;
}
if (index < 0) { return kInvalidParameters;
}
if (options.fSubset) { // If we add support for this, we need to update the code that zeroes // a kRestoreBGColor frame. return kInvalidParameters;
}
if (index >= this->onGetFrameCount()) { return kIncompleteInput;
}
constint requiredFrame = frame->getRequiredFrame(); if (requiredFrame != kNoFrame) { // Decode earlier frame if necessary const SkFrame* preppedFrame = nullptr; if (options.fPriorFrame == kNoFrame) {
Result result = kInternalError; // getPixelsFn will be set when things like SkAndroidCodec are calling this function. // Thus, we call the provided function when recursively decoding previous frames, // but only when necessary (i.e. there is a required frame). if (getPixelsFn) {
result = getPixelsFn(info, pixels, rowBytes, options, requiredFrame);
} else {
Options prevFrameOptions(options);
prevFrameOptions.fFrameIndex = requiredFrame;
result = this->getPixels(info, pixels, rowBytes, &prevFrameOptions);
} if (result != kSuccess) { return result;
}
preppedFrame = frameHolder->getFrame(requiredFrame);
} else { // Check for a valid frame as a starting point. Alternatively, we could // treat an invalid frame as not providing one, but rejecting it will // make it easier to catch the mistake. if (options.fPriorFrame < requiredFrame || options.fPriorFrame >= index) { return kInvalidParameters;
}
preppedFrame = frameHolder->getFrame(options.fPriorFrame);
}
SkASSERT(preppedFrame); switch (preppedFrame->getDisposalMethod()) { case SkCodecAnimation::DisposalMethod::kRestorePrevious:
SkASSERT(options.fPriorFrame != kNoFrame); return kInvalidParameters; case SkCodecAnimation::DisposalMethod::kRestoreBGColor: // If a frame after the required frame is provided, there is no // need to clear, since it must be covered by the desired frame. // FIXME: If the required frame is kRestoreBGColor, we don't actually need to decode // it, since we'll just clear it to transparent. Instead, we could decode *its* // required frame and then clear. if (preppedFrame->frameId() == requiredFrame) {
SkIRect preppedRect = preppedFrame->frameRect(); if (!zero_rect(info, pixels, rowBytes, this->dimensions(), preppedRect)) { return kInternalError;
}
} break; default: break;
}
}
// Default options.
Options optsStorage; if (nullptr == options) {
options = &optsStorage;
} else { if (options->fSubset) {
SkIRect subset(*options->fSubset); if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) { // FIXME: How to differentiate between not supporting subset at all // and not supporting this particular subset? return kUnimplemented;
}
}
}
const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
*options); if (frameIndexResult != kSuccess) { return frameIndexResult;
}
// FIXME: Support subsets somehow? Note that this works for SkWebpCodec // because it supports arbitrary scaling/subset combinations. if (!this->dimensionsSupported(info.dimensions())) { return kInvalidScale;
}
fDstInfo = info;
fOptions = *options;
// On an incomplete decode, the subclass will specify the number of scanlines that it decoded // successfully. int rowsDecoded = 0; const Result result = this->onGetPixels(info, pixels, rowBytes, *options, &rowsDecoded);
// A return value of kIncompleteInput indicates a truncated image stream. // In this case, we will fill any uninitialized memory with a default value. // Some subclasses will take care of filling any uninitialized memory on // their own. They indicate that all of the memory has been filled by // setting rowsDecoded equal to the height. if ((kIncompleteInput == result || kErrorInInput == result) && rowsDecoded != info.height()) { // FIXME: (skbug.com/5772) fillIncompleteImage will fill using the swizzler's width, unless // there is a subset. In that case, it will use the width of the subset. From here, the // subset will only be non-null in the case of SkWebpCodec, but it treats the subset // differenty from the other codecs, and it needs to use the width specified by the info. // Set the subset to null so SkWebpCodec uses the correct width.
fOptions.fSubset = nullptr;
this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
rowsDecoded);
}
Result result; auto decode = [this, options, &result](const SkPixmap& pm) {
result = this->getPixels(pm, options); switch (result) { case SkCodec::kSuccess: case SkCodec::kIncompleteInput: case SkCodec::kErrorInInput: returntrue; default: returnfalse;
}
};
// If the codec reports this image is rotated, we will decode it into // a temporary buffer, then copy it (rotated) into the pixmap belonging // to bm that we allocated above. If the image is not rotated, we will // decode straight into that allocated pixmap. if (!SkPixmapUtils::Orient(bm.pixmap(), this->getOrigin(), decode)) { return {nullptr, result};
} // Setting the bitmap to be immutable saves us from having to copy it.
bm.setImmutable(); return {SkImages::RasterFromBitmap(bm), kSuccess};
}
std::tuple<sk_sp<SkImage>, SkCodec::Result> SkCodec::getImage() { // If the codec reports that it is rotated, we need to rotate the image info // it says it is, so the output is what the user wants.
SkImageInfo info = this->getInfo(); if (SkEncodedOriginSwapsWidthHeight(this->getOrigin())) {
info = SkPixmapUtils::SwapWidthHeight(info);
} return this->getImage(info, nullptr);
}
if (kUnknown_SkColorType == info.colorType()) { return kInvalidConversion;
} if (nullptr == pixels) { return kInvalidParameters;
}
// Set options.
Options optsStorage; if (nullptr == options) {
options = &optsStorage;
} else { if (options->fSubset) {
SkIRect size = SkIRect::MakeSize(info.dimensions()); if (!size.contains(*options->fSubset)) { return kInvalidParameters;
}
constint top = options->fSubset->top(); constint bottom = options->fSubset->bottom(); if (top < 0 || top >= info.height() || top >= bottom || bottom > info.height()) { return kInvalidParameters;
}
}
}
const Result frameIndexResult = this->handleFrameIndex(info, pixels, rowBytes,
*options); if (frameIndexResult != kSuccess) { return frameIndexResult;
}
if (!this->dimensionsSupported(info.dimensions())) { return kInvalidScale;
}
fDstInfo = info;
fOptions = *options;
const Result result = this->onStartIncrementalDecode(info, pixels, rowBytes, fOptions); if (kSuccess == result) {
fStartedIncrementalDecode = true;
} elseif (kUnimplemented == result) { // FIXME: This is temporarily necessary, until we transition SkCodec // implementations from scanline decoding to incremental decoding. // SkAndroidCodec will first attempt to use incremental decoding, but // will fall back to scanline decoding if incremental returns // kUnimplemented. rewindIfNeeded(), above, set fNeedsRewind to true // (after potentially rewinding), but we do not want the next call to // startScanlineDecode() to do a rewind.
fNeedsRewind = false;
} return result;
}
SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info, const SkCodec::Options* options) { // Reset fCurrScanline in case of failure.
fCurrScanline = -1;
// Set options.
Options optsStorage; if (nullptr == options) {
options = &optsStorage;
} elseif (options->fSubset) {
SkIRect size = SkIRect::MakeSize(info.dimensions()); if (!size.contains(*options->fSubset)) { return kInvalidInput;
}
// We only support subsetting in the x-dimension for scanline decoder. // Subsetting in the y-dimension can be accomplished using skipScanlines(). if (options->fSubset->top() != 0 || options->fSubset->height() != info.height()) { return kInvalidInput;
}
}
// Scanline decoding only supports decoding the first frame. if (options->fFrameIndex != 0) { return kUnimplemented;
}
// The void* dst and rowbytes in handleFrameIndex or only used for decoding prior // frames, which is not supported here anyway, so it is safe to pass nullptr/0. const Result frameIndexResult = this->handleFrameIndex(info, nullptr, 0, *options); if (frameIndexResult != kSuccess) { return frameIndexResult;
}
// FIXME: Support subsets somehow? if (!this->dimensionsSupported(info.dimensions())) { return kInvalidScale;
}
const Result result = this->onStartScanlineDecode(info, *options); if (result != SkCodec::kSuccess) { return result;
}
// FIXME: See startIncrementalDecode. That method set fNeedsRewind to false // so that when onStartScanlineDecode calls rewindIfNeeded it would not // rewind. But it also relies on that call to rewindIfNeeded to set // fNeedsRewind to true for future decodes. When // fUsingCallbackForHandleFrameIndex is true, that call to rewindIfNeeded is // skipped, so this method sets it back to true.
SkASSERT(fUsingCallbackForHandleFrameIndex || fNeedsRewind);
fNeedsRewind = true;
SkASSERT(!fDstInfo.isEmpty()); if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) { // Arguably, we could just skip the scanlines which are remaining, // and return true. We choose to return false so the client // can catch their bug. returnfalse;
}
bool result = this->onSkipScanlines(countLines);
fCurrScanline += countLines; return result;
}
int SkCodec::onOutputScanline(int inputScanline) const { switch (this->getScanlineOrder()) { case kTopDown_SkScanlineOrder: return inputScanline; case kBottomUp_SkScanlineOrder: return fEncodedInfo.height() - inputScanline - 1; default: // This case indicates an interlaced gif and is implemented by SkGifCodec.
SkASSERT(false); return 0;
}
}
void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
ZeroInitialized zeroInit, int linesRequested, int linesDecoded) { if (kYes_ZeroInitialized == zeroInit) { return;
}
void SkCodec::applyColorXform(void* dst, constvoid* src, int count) const { // It is okay for srcProfile to be null. This will use sRGB. constauto* srcProfile = fEncodedInfo.profile();
SkAssertResult(skcms_Transform(src, fSrcXformFormat, skcms_AlphaFormat_Unpremul, srcProfile,
dst, fDstXformFormat, fDstXformAlphaFormat, &fDstProfile,
count));
}
if (frameCount == 1 && !this->onGetFrameInfo(0, nullptr)) { // Not animated. return std::vector<FrameInfo>{};
}
std::vector<FrameInfo> result(frameCount); for (int i = 0; i < frameCount; ++i) {
SkAssertResult(this->onGetFrameInfo(i, &result[i]));
} return result;
}
constchar* SkCodec::ResultToString(Result result) { switch (result) { case kSuccess: return"success"; case kIncompleteInput: return"incomplete input"; case kErrorInInput: return"error in input"; case kInvalidConversion: return"invalid conversion"; case kInvalidScale: return"invalid scale"; case kInvalidParameters: return"invalid parameters"; case kInvalidInput: return"invalid input"; case kCouldNotRewind: return"could not rewind"; case kInternalError: return"internal error"; case kUnimplemented: return"unimplemented"; default:
SkASSERT(false); return"bogus result value";
}
}
// As its name suggests, this method computes a frame's alpha (e.g. completely // opaque, unpremul, binary) and its required frame (a preceding frame that // this frame depends on, to draw the complete image at this frame's point in // the animation stream), and calls this frame's setter methods with that // computed information. // // A required frame of kNoFrame means that this frame is independent: drawing // the complete image at this frame's point in the animation stream does not // require first preparing the pixel buffer based on another frame. Instead, // drawing can start from an uninitialized pixel buffer. // // "Uninitialized" is from the SkCodec's caller's point of view. In the SkCodec // implementation, for independent frames, first party Skia code (in src/codec) // will typically fill the buffer with a uniform background color (e.g. // transparent black) before calling into third party codec-specific code (e.g. // libjpeg or libpng). Pixels outside of the frame's rect will remain this // background color after drawing this frame. For incomplete decodes, pixels // inside that rect may be (at least temporarily) set to that background color. // In an incremental decode, later passes may then overwrite that background // color. // // Determining kNoFrame or otherwise involves testing a number of conditions // sequentially. The first satisfied condition results in setting the required // frame to kNoFrame (an "INDx" condition) or to a non-negative frame number (a // "DEPx" condition), and the function returning early. Those "INDx" and "DEPx" // labels also map to comments in the function body. // // - IND1: this frame is the first frame. // - IND2: this frame fills out the whole image, and it is completely opaque // or it overwrites (not blends with) the previous frame. // - IND3: all preceding frames' disposals are kRestorePrevious. // - IND4: the prevFrame's disposal is kRestoreBGColor, and it fills out the // whole image or it is itself otherwise independent. // - DEP5: this frame reports alpha (it is not completely opaque) and it // blends with (not overwrites) the previous frame. // - IND6: this frame's rect covers the rects of all preceding frames back to // and including the most recent independent frame before this frame. // - DEP7: unconditional. // // The "prevFrame" variable initially points to the previous frame (also known // as the prior frame), but that variable may iterate further backwards over // the course of this computation. void SkFrameHolder::setAlphaAndRequiredFrame(SkFrame* frame) { constbool reportsAlpha = frame->reportedAlpha() != SkEncodedInfo::kOpaque_Alpha; constauto screenRect = SkIRect::MakeWH(fScreenWidth, fScreenHeight); constauto frameRect = frame_rect_on_screen(frame->frameRect(), screenRect);
constint i = frame->frameId(); if (0 == i) {
frame->setHasAlpha(reportsAlpha || frameRect != screenRect);
frame->setRequiredFrame(SkCodec::kNoFrame); // IND1 return;
}
constbool clearPrevFrame = restore_bg(*prevFrame); auto prevFrameRect = frame_rect_on_screen(prevFrame->frameRect(), screenRect);
if (clearPrevFrame) { if (prevFrameRect == screenRect || independent(*prevFrame)) {
frame->setHasAlpha(true);
frame->setRequiredFrame(SkCodec::kNoFrame); // IND4 return;
}
}
if (reportsAlpha && blendWithPrevFrame) { // Note: We could be more aggressive here. If prevFrame clears // to background color and covers its required frame (and that // frame is independent), prevFrame could be marked independent. // Would this extra complexity be worth it?
frame->setRequiredFrame(prevFrame->frameId()); // DEP5
frame->setHasAlpha(prevFrame->hasAlpha() || clearPrevFrame); return;
}
while (frameRect.contains(prevFrameRect)) { constint prevRequiredFrame = prevFrame->getRequiredFrame(); if (prevRequiredFrame == SkCodec::kNoFrame) {
frame->setRequiredFrame(SkCodec::kNoFrame); // IND6
frame->setHasAlpha(true); return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.