/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Decoder::~Decoder() {
MOZ_ASSERT(mProgress == NoProgress || !mImage, "Destroying Decoder without taking all its progress changes");
MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage, "Destroying Decoder without taking all its invalidations");
mInitialized = false;
if (mInProfile) { // mTransform belongs to us only if mInProfile is non-null if (mTransform) {
qcms_transform_release(mTransform);
}
qcms_profile_release(mInProfile);
}
if (mImage && !NS_IsMainThread()) { // Dispatch mImage to main thread to prevent it from being destructed by the // decode thread.
SurfaceCache::ReleaseImageOnMainThread(mImage.forget());
}
}
void Decoder::SetSurfaceFlags(SurfaceFlags aSurfaceFlags) {
MOZ_ASSERT(!mInitialized);
MOZ_ASSERT(!(mSurfaceFlags & SurfaceFlags::NO_COLORSPACE_CONVERSION) ||
!(mSurfaceFlags & SurfaceFlags::TO_SRGB_COLORSPACE));
mSurfaceFlags = aSurfaceFlags; if (mSurfaceFlags & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
mCMSMode = CMSMode::Off;
} if (mSurfaceFlags & SurfaceFlags::TO_SRGB_COLORSPACE) { // CMSMode::TaggedOnly and CMSMode::All are equivalent when the // TO_SRGB_COLORSPACE flag is set (for untagged images CMSMode::All assumes // they are in sRGB space so it does nothing, which is same as what // CMSMode::TaggedOnly does for untagged images). We just want to avoid // CMSMode::Off so that the sRGB conversion actually happens.
mCMSMode = CMSMode::All;
}
}
qcms_transform* Decoder::GetCMSsRGBTransform(SurfaceFormat aFormat) const { if (mSurfaceFlags & SurfaceFlags::TO_SRGB_COLORSPACE) { // We want a transform to convert from sRGB to device space, but we are // already using sRGB as our device space. That means we can skip // color management entirely. return nullptr;
} if (qcms_profile_is_sRGB(gfxPlatform::GetCMSOutputProfile())) { // Device space is sRGB so we can skip color management as well. return nullptr;
}
switch (aFormat) { case SurfaceFormat::B8G8R8A8: case SurfaceFormat::B8G8R8X8: return gfxPlatform::GetCMSBGRATransform(); case SurfaceFormat::R8G8B8A8: case SurfaceFormat::R8G8B8X8: return gfxPlatform::GetCMSRGBATransform(); case SurfaceFormat::R8G8B8: return gfxPlatform::GetCMSRGBTransform(); default:
MOZ_ASSERT_UNREACHABLE("Unsupported surface format!"); return nullptr;
}
}
/* * Common implementation of the decoder interface.
*/
nsresult Decoder::Init() { // No re-initializing
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
// All decoders must have a SourceBufferIterator.
MOZ_ASSERT(mIterator);
// Metadata decoders must not set an output size.
MOZ_ASSERT_IF(mMetadataDecode, !mHaveExplicitOutputSize);
// All decoders must be anonymous except for metadata decoders. // XXX(seth): Soon that exception will be removed.
MOZ_ASSERT_IF(mImage, IsMetadataDecode());
// We can only request the frame count for metadata decoders.
MOZ_ASSERT_IF(WantsFrameCount(), IsMetadataDecode());
if (lexerResult.is<Yield>()) { // We either need more data to continue (in which case either @aOnResume or // the caller will reschedule us to run again later), or the decoder is // yielding to allow the caller access to some intermediate output. return lexerResult;
}
// We reached a terminal state; we're now done decoding.
MOZ_ASSERT(lexerResult.is<TerminalState>());
mReachedTerminalState = true;
// If decoding failed, record that fact. if (lexerResult.as<TerminalState>() == TerminalState::FAILURE) {
PostError();
}
if (IsMetadataDecode()) { // If this was a metadata decode and we never got a size, the decode failed. if (!HasSize()) {
PostError();
} return;
}
// If the implementation left us mid-frame, finish that up. Note that it may // have left us transparent. if (mInFrame) {
PostHasTransparency();
PostFrameStop();
}
// If PostDecodeDone() has not been called, we may need to send teardown // notifications if it is unrecoverable. if (mDecodeDone) {
MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
} else { // We should always report an error to the console in this case.
mShouldReportError = true;
if (GetCompleteFrameCount() > 0) { // We're usable if we have at least one complete frame, so do exactly // what we should have when the decoder completed.
PostHasTransparency();
PostDecodeDone();
} else { // We're not usable. Record some final progress indicating the error.
mProgress |= FLAG_DECODE_COMPLETE | FLAG_HAS_ERROR;
}
}
}
// We should now be on |aFrameNum|. (Note that we're comparing the frame // number, which is zero-based, with the frame count, which is one-based.)
MOZ_ASSERT_IF(aAnimParams, aAnimParams->mFrameNum + 1 == mFrameCount);
// If we're past the first frame, PostIsAnimated() should've been called.
MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
// Update our state to reflect the new frame.
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
mInFrame = true;
} else {
mImageData = nullptr;
mImageDataLength = 0;
}
uint32_t frameNum = aAnimParams ? aAnimParams->mFrameNum : 0; if (frameNum != mFrameCount) {
MOZ_ASSERT_UNREACHABLE("Allocating frames out of order"); return RawAccessFrameRef();
}
if (aOutputSize.width <= 0 || aOutputSize.height <= 0) {
NS_WARNING("Trying to add frame with zero or negative size"); return RawAccessFrameRef();
}
if (frameNum > 0) { if (aPreviousFrame->GetDisposalMethod() !=
DisposalMethod::RESTORE_PREVIOUS) { // If the new restore frame is the direct previous frame, then we know // the dirty rect is composed only of the current frame's blend rect and // the restore frame's clear rect (if applicable) which are handled in // filters.
mRestoreFrame = std::move(aPreviousFrame);
mRestoreDirtyRect.SetBox(0, 0, 0, 0);
} else { // We only need the previous frame's dirty rect, because while there may // have been several frames between us and mRestoreFrame, the only areas // that changed are the restore frame's clear rect, the current frame // blending rect, and the previous frame's blending rect. All else is // forgotten due to us restoring the same frame again.
mRestoreDirtyRect = aPreviousFrame->GetBoundedBlendRect();
}
}
RawAccessFrameRef ref;
// If we have a frame recycler, it must be for an animated image producing // full frames. If the higher layers are discarding frames because of the // memory footprint, then the recycler will allow us to reuse the buffers. // Each frame should be the same size and have mostly the same properties. if (mFrameRecycler) {
MOZ_ASSERT(aAnimParams);
ref = mFrameRecycler->RecycleFrame(mRecycleRect); if (ref) { // If the recycled frame is actually the current restore frame, we cannot // use it. If the next restore frame is the new frame we are creating, in // theory we could reuse it, but we would need to store the restore frame // animation parameters elsewhere. For now we just drop it. bool blocked = ref.get() == mRestoreFrame.get(); if (!blocked) {
blocked = NS_FAILED(
ref->InitForDecoderRecycle(aAnimParams.ref(), &mImageDataLength));
}
if (blocked) {
ref.reset();
}
}
}
// Either the recycler had nothing to give us, or we don't have a recycler. // Produce a new frame to store the data. if (!ref) { // There is no underlying data to reuse, so reset the recycle rect to be // the full frame, to ensure the restore frame is fully copied.
mRecycleRect = IntRect(IntPoint(0, 0), aOutputSize);
bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA); auto frame = MakeNotNull<RefPtr<imgFrame>>(); if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFormat, nonPremult,
aAnimParams, bool(mFrameRecycler),
&mImageDataLength))) {
NS_WARNING("imgFrame::Init should succeed"); return RawAccessFrameRef();
}
// Set our intrinsic size.
mImageMetadata.SetSize(aWidth, aHeight, aOrientation, aResolution);
// Verify it is the expected size, if given. Note that this is only used by // the ICO decoder for embedded image types, so only its subdecoders are // required to handle failures in PostSize. if (!IsExpectedSize()) {
PostError(); return;
}
// Set our output size if it's not already set. if (!mOutputSize) {
mOutputSize = Some(mImageMetadata.GetSize());
}
MOZ_ASSERT(mOutputSize->width <= mImageMetadata.GetSize().width &&
mOutputSize->height <= mImageMetadata.GetSize().height, "Output size will result in upscaling");
// Record this notification.
mProgress |= FLAG_SIZE_AVAILABLE;
}
void Decoder::PostFrameStop(Opacity aFrameOpacity) { // We should be mid-frame
MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
if (mFrameCount == 1) { // If we're not sending partial invalidations, then we send an invalidation // here when the first frame is complete. if (!ShouldSendPartialInvalidations()) {
mInvalidRect.UnionRect(mInvalidRect,
OrientedIntRect(OrientedIntPoint(), Size()));
}
// If we dispose of the first frame by clearing it, then the first frame's // refresh area is all of itself. RESTORE_PREVIOUS is invalid (assumed to // be DISPOSE_CLEAR). switch (mCurrentFrame->GetDisposalMethod()) { default:
MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod"); case DisposalMethod::CLEAR: case DisposalMethod::CLEAR_ALL: case DisposalMethod::RESTORE_PREVIOUS:
mFirstFrameRefreshArea = IntRect(IntPoint(), Size().ToUnknownSize()); break; case DisposalMethod::KEEP: case DisposalMethod::NOT_SPECIFIED: break;
}
} else { // Some GIFs are huge but only have a small area that they animate. We only // need to refresh that small area when frame 0 comes around again.
mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea,
mCurrentFrame->GetBoundedBlendRect());
}
}
void Decoder::PostInvalidation(const OrientedIntRect& aRect, const Maybe<OrientedIntRect>& aRectAtOutputSize /* = Nothing() */) { // We should be mid-frame
MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!");
MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!");
// Record this invalidation, unless we're not sending partial invalidations // or we're past the first frame. if (ShouldSendPartialInvalidations() && mFrameCount == 1) {
mInvalidRect.UnionRect(mInvalidRect, aRect);
mCurrentFrame->ImageUpdated(
aRectAtOutputSize.valueOr(aRect).ToUnknownRect());
}
}
void Decoder::PostDecodeDone() {
MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
MOZ_ASSERT(!mDecodeDone, "Decode already done!");
mDecodeDone = true;
// Some metadata that we track should take into account every frame in the // image. If this is a first-frame-only decode, our accumulated loop length // and first frame refresh area only includes the first frame, so it's not // correct and we don't record it. if (!IsFirstFrameDecode()) {
mImageMetadata.SetLoopLength(mLoopLength);
mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea);
}
¤ 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.0.14Bemerkung:
(vorverarbeitet)
¤
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 ist noch experimentell.