mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 22:28:36 +00:00
b6f6aa65fb
- Bug 1165518 - Part 2: Replace prlog.h with Logging.h. rs=froydnj (342dfd711) - Bug 1085567 - Fixup synth events within pointer lock implementation for e10s. r=billm (45c50a0e6) - Bug 1128238 - Ajust NPP_SetWindow and synth native event coords to account for the chrome offset of remote tabs. Prior to this the content process always assumed the tab was the window origin. r=aklotz (202ffc006) - Bug 1128934 - Avoid calling GetClientBounds from BasicComposior. r=roc, karlt (5e98232bd) - Bug 1149041 - Make nsIWidget::SizeConstraints use LayoutDeviceIntSize instead of unit-less nsIntSize. r=roc (d0b345926) - shuffle around (fcdc51510) - Bug 1142852 P1 Fix NS_NewNonOwningRunnableMethodWithArgs() so that it compiles. r=froydnj (394d2b200) - Bug 1142852 P2 Make Cache Action use non-threadsafe ref-counting. r=ehsan (0b2b91d86) - Bug 1146349 - Allow NS_NewRunnableMethodWithArgs to take up to 8 arguments. r=froydnj (7207e74d0) - Bug 1146349 - Make the native event synthesization functions in DOMWindowUtils async. r=smaug (e43579a1d) - Bug 949614 - Use === for SimpleTest.is; r=Waldo (9f54faca4) - Bug 1146349 - Update some widget tests to deal with async native key event synthesization. r=smaug,masayuki (f1e82df79) - Bug 920013 - Rewrite test_cocoa_focus.html to use SpecialPowers. r=smichaud (8708d6a71) - Bug 1146349 - Update DOM mochitests to deal with async native event synthesization. r=smaug (a4eceb9f6) - Bug 1146349 - Update browser-chrome mochitests to deal with async native event synthesization. r=Gijs (c864ad939) - Bug 1146349 - Wire up native event synthesization in child processes. r=smaug (e8574457e) - Bug 1146349 - Mark SynthesizedEventObserver::Observe() as override (9063f04b5) - Bug 1149613 - Remove unneded field. r=botond (9aa7155e2) - Bug 1153613 - [e10s], ensure the buffer for the drag image is large enough for SourceSurfaceRawData::GuaranteePersistance() to succeed, r=mattwoodrow (aa885dae7) - remove spurious file (ff199d3ae) - Bug 1043013 - Generalize nsIntRegion into a BaseIntRegion template. r=jrmuizel (9d3e02820) - Bug 1043013 - Introduce IntRegionTyped. r=jrmuizel (f73902921) - Bug 1043013 - Use strongly-typed regions in HitTestingTreeNode. r=kats (7b8c8ae44) - Bug 913443 - Remove mention of the word ThebesLayer in a comment. r=roc (e9706e025) - Bug 913443 - Remove some #ifdefs. r=roc (da76f831b) - Bug 913443 - Break up CreateOrRecyclePaintedLayer into more parts. r=roc (dd94560fe) - Bug 913443 - Extract layer hint calculation. r=roc (5c7806a95) - Bug 913443 - Break CreateOrRecyclePaintedLayer up even more. r=roc (9e5edab17) - Bug 913443 - Remove unused aItemVisibleRect argument. r=roc (f3d1a5195) - Bug 913443 - Move IsWidgetLayerManager() check out of UpdateCommonClipCount. r=roc (3be7b19a6) - Bug 913443 - Change the order of these calls. r=roc (c7bc59fee) - Bug 913443 - Add a display item buffer for PaintedLayerData so that we can assign items without needing to know the actual Layer. r=roc (674df6aa8) - Bug 913443 - Delay PaintedLayer recycling until PopPaintedLayerData() r=roc (9d6de961a) - Bug 913443 - When determining the layer to recycle, only consider layers that have display items in common with the layer we need. r=roc (3327c31c5) - Bug 913443 - Recycle PaintedLayers as soon as possible. r=roc (37ef6d530) - Bug 913443 - Remove duplicated argument variables. r=roc (19c5af270) - Bug 1141408 - Don't include the display item's memory address in the profile label. r=BenWa (5bb1d73c1) - Bug 1141281 - Don't put empty Info: lines into the layers dump. r=BenWa (85f4b28be) - Bug 1035611 - Part 1: Updated Matrix4x4::ProjectRectBounds to properly handle infinite values when untransformed rects cross the w=0 plane. r=mattwoodrow (f47ed4383) - Bug 1035611 - Part 2: Test to ensure that transformed rects crossing the w=0 plane are clipped correctly. r=mattwoodrow (5701b435f) - Bug 1141089 - Add some release assertions to gather more data. r=roc (805c2994d) - Bug 1141595 - Make scroll info display item hoisting work after bug 913443. r=tn (1efcaf164) - Bug 1144307 - Destroy scroll info layer items when we're done with them. r=tn (3ffd3b1f2) - Bug 1148855 - Mark some ContainerState methods as const. r=roc (791585baf) - Bug 1144820 - Use rvalue reference in AddProfileTimelineMarker. r=smaug (12316b65f) - Bug 1148855 - Set overflow:hidden on scrollbar tracks so that layerization knows that the scrollbar thumb won't leave the scrollbar. r=roc (082ec87bf) - Bug 1148855 - Intermediate state that unifies APZ and non-APZ layerization behavior somewhat. (3da45ab96) - Bug 1148855 - Rework how display items are assigned to PaintedLayers and make it work the same way with and without APZ. r=roc (7258f9c90) - Bug 913443 - Add some tests. r=roc (9977682da) - Bug 1039926 - Add a test for not invalidating stuff inside mask layers. (1f8aef65e) - Bug 1148855 - Add some tests. r=roc (2ba1f7f18) - Bug 1148855 - Tweak reftest fuzziness. (a600be7d5) - Bug 1151306 - Add a missing check during background color pulling. r=roc (b3a6c886b) - Bug 1150774 (Part 1) - Add helpers to simplify using typed units in ConfigureLayer. r=botond (baecb2c2e) - Bug 1150774 (Part 2) - Use the correct units in nsDisplayImageContainer::ConfigureLayer and related code. r=botond (a79beb1fb) - Bug 1141089 - Initialize DisplayItemData with a non-null mLayer, so that mLayer is non-null throughout the object's whole lifetime. r=roc (7c2f9eb09) - Bug 1141089 - Remove unused DisplayItemData copy constructor. r=roc (ba5c46c45) - Bug 1141089 - Add a few more MOZ_RELEASE_ASSERT(mLayer) checks, just for fun. r=roc (50a8f2db7) - Bug 1141089 - Use const nsTArray* in all the places where we don't change the array's contents. r=roc (cd07a5bbd) - Bug 1141089 - Turn these two assertions into release assertions. r=roc (71daf5ef3) - Bug 1141089 - Check that we're unsetting sDestroyedFrame correctly. r=roc (6c7da67e7) - Bug 1143522 - Convert Layer::mClipRect to ParentLayerIntRect. r=botond (ddab1c0ff) - Bug 1144951. Warn if the result of ConvertAppUnits is unused. r=mats (e1d79c6ca) - Bug 1144951. Rename ConvertAppUnits to ScaleToOtherAppUnits. r=mats (675c918c3) - Bug 1149035 - Make nsXULPopupManager::PopupResized take a LayoutDeviceIntSize param instead of a unit-less nsIntSize. r=roc (eb9529af7) - missing bit of Bug 1156110 - Make nsView::mWindow an nsCOMPtr; r=roc (972255b4f) - Bug 1147706 - Warn if we don't use the result of const methods on BaseRect and its subclasses. r=tn (9e91deada) - Bug 1152838 - Fix BaseRect::InflateToMultiple and move it to nsIntRect. r=mattwoodrow, r=jrmuizel (5a942df29) - Bug 1125055 - Sync decode layerized images if necessary. r=roc,tn (1379db5c7) - Bug 1145167 - make imgIContainer::type getter infallible; r=seth (7b7091b89) - Bug 1148213 - Implement image locking for VectorImage. r=dholbert (240e14cb3) - Bug 1143653 - Crash in CompositorD3D9 rather than TextureD3D9 if device reset fails, after a few attemps. r=jrmuizel (76607ddc3) - Bug 1147894 - Only use non-null compositors with TextureHost::SetCompositor. r=sotaro (605bf34f6) - Bug 1146912 - Finish replacing TextureHost::GetTextureSource by BindTextureSource. r=sotaro (cf3763879) - Bug 1132854 - Remove useless ThebesIntSize conversion helper. r=Bas (78cdfee14) - Bug 1068881 - Inflate the dest rect to at least one device pixel when computing the optimal image size for drawing. r=roc (3d5644913)
715 lines
21 KiB
C++
715 lines
21 KiB
C++
|
|
/* -*- 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/. */
|
|
|
|
#include "Decoder.h"
|
|
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "DecodePool.h"
|
|
#include "GeckoProfiler.h"
|
|
#include "imgIContainer.h"
|
|
#include "nsIConsoleService.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsProxyRelease.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
using mozilla::gfx::IntSize;
|
|
using mozilla::gfx::SurfaceFormat;
|
|
|
|
namespace mozilla {
|
|
namespace image {
|
|
|
|
Decoder::Decoder(RasterImage* aImage)
|
|
: mImage(aImage)
|
|
, mProgress(NoProgress)
|
|
, mImageData(nullptr)
|
|
, mColormap(nullptr)
|
|
, mChunkCount(0)
|
|
, mFlags(0)
|
|
, mBytesDecoded(0)
|
|
, mSendPartialInvalidations(false)
|
|
, mDataDone(false)
|
|
, mDecodeDone(false)
|
|
, mDataError(false)
|
|
, mDecodeAborted(false)
|
|
, mShouldReportError(false)
|
|
, mImageIsTransient(false)
|
|
, mImageIsLocked(false)
|
|
, mFrameCount(0)
|
|
, mFailCode(NS_OK)
|
|
, mNeedsNewFrame(false)
|
|
, mNeedsToFlushData(false)
|
|
, mInitialized(false)
|
|
, mSizeDecode(false)
|
|
, mInFrame(false)
|
|
, mIsAnimated(false)
|
|
{ }
|
|
|
|
Decoder::~Decoder()
|
|
{
|
|
MOZ_ASSERT(mProgress == NoProgress,
|
|
"Destroying Decoder without taking all its progress changes");
|
|
MOZ_ASSERT(mInvalidRect.IsEmpty(),
|
|
"Destroying Decoder without taking all its invalidations");
|
|
mInitialized = false;
|
|
|
|
if (!NS_IsMainThread()) {
|
|
// Dispatch mImage to main thread to prevent it from being destructed by the
|
|
// decode thread.
|
|
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
|
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
|
|
if (mainThread) {
|
|
// Handle ambiguous nsISupports inheritance.
|
|
RasterImage* rawImg = nullptr;
|
|
mImage.swap(rawImg);
|
|
DebugOnly<nsresult> rv =
|
|
NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Common implementation of the decoder interface.
|
|
*/
|
|
|
|
void
|
|
Decoder::Init()
|
|
{
|
|
// No re-initializing
|
|
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
|
|
|
|
// Fire OnStartDecode at init time to support bug 512435.
|
|
if (!IsSizeDecode()) {
|
|
mProgress |= FLAG_DECODE_STARTED | FLAG_ONLOAD_BLOCKED;
|
|
}
|
|
|
|
// Implementation-specific initialization
|
|
InitInternal();
|
|
|
|
mInitialized = true;
|
|
}
|
|
|
|
// Initializes a decoder whose image and observer is already being used by a
|
|
// parent decoder
|
|
void
|
|
Decoder::InitSharedDecoder(uint8_t* aImageData, uint32_t aImageDataLength,
|
|
uint32_t* aColormap, uint32_t aColormapSize,
|
|
RawAccessFrameRef&& aFrameRef)
|
|
{
|
|
// No re-initializing
|
|
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
|
|
|
|
mImageData = aImageData;
|
|
mImageDataLength = aImageDataLength;
|
|
mColormap = aColormap;
|
|
mColormapSize = aColormapSize;
|
|
mCurrentFrame = Move(aFrameRef);
|
|
|
|
// We have all the frame data, so we've started the frame.
|
|
if (!IsSizeDecode()) {
|
|
mFrameCount++;
|
|
PostFrameStart();
|
|
}
|
|
|
|
// Implementation-specific initialization
|
|
InitInternal();
|
|
mInitialized = true;
|
|
}
|
|
|
|
nsresult
|
|
Decoder::Decode()
|
|
{
|
|
MOZ_ASSERT(mInitialized, "Should be initialized here");
|
|
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
|
|
|
|
// We keep decoding chunks until the decode completes or there are no more
|
|
// chunks available.
|
|
while (!GetDecodeDone() && !HasError()) {
|
|
auto newState = mIterator->AdvanceOrScheduleResume(this);
|
|
|
|
if (newState == SourceBufferIterator::WAITING) {
|
|
// We can't continue because the rest of the data hasn't arrived from the
|
|
// network yet. We don't have to do anything special; the
|
|
// SourceBufferIterator will ensure that Decode() gets called again on a
|
|
// DecodePool thread when more data is available.
|
|
return NS_OK;
|
|
}
|
|
|
|
if (newState == SourceBufferIterator::COMPLETE) {
|
|
mDataDone = true;
|
|
|
|
nsresult finalStatus = mIterator->CompletionStatus();
|
|
if (NS_FAILED(finalStatus)) {
|
|
PostDataError();
|
|
}
|
|
|
|
CompleteDecode();
|
|
return finalStatus;
|
|
}
|
|
|
|
MOZ_ASSERT(newState == SourceBufferIterator::READY);
|
|
|
|
Write(mIterator->Data(), mIterator->Length());
|
|
}
|
|
|
|
CompleteDecode();
|
|
return HasError() ? NS_ERROR_FAILURE : NS_OK;
|
|
}
|
|
|
|
void
|
|
Decoder::Resume()
|
|
{
|
|
DecodePool* decodePool = DecodePool::Singleton();
|
|
MOZ_ASSERT(decodePool);
|
|
|
|
nsCOMPtr<nsIEventTarget> target = decodePool->GetEventTarget();
|
|
if (MOZ_UNLIKELY(!target)) {
|
|
// We're shutting down and the DecodePool's thread pool has been destroyed.
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIRunnable> worker = decodePool->CreateDecodeWorker(this);
|
|
target->Dispatch(worker, nsIEventTarget::DISPATCH_NORMAL);
|
|
}
|
|
|
|
bool
|
|
Decoder::ShouldSyncDecode(size_t aByteLimit)
|
|
{
|
|
MOZ_ASSERT(aByteLimit > 0);
|
|
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
|
|
|
|
return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
|
|
}
|
|
|
|
void
|
|
Decoder::Write(const char* aBuffer, uint32_t aCount)
|
|
{
|
|
PROFILER_LABEL("ImageDecoder", "Write",
|
|
js::ProfileEntry::Category::GRAPHICS);
|
|
|
|
// We're strict about decoder errors
|
|
MOZ_ASSERT(!HasDecoderError(),
|
|
"Not allowed to make more decoder calls after error!");
|
|
|
|
// Begin recording telemetry data.
|
|
TimeStamp start = TimeStamp::Now();
|
|
mChunkCount++;
|
|
|
|
// Keep track of the total number of bytes written.
|
|
mBytesDecoded += aCount;
|
|
|
|
// If we're flushing data, clear the flag.
|
|
if (aBuffer == nullptr && aCount == 0) {
|
|
MOZ_ASSERT(mNeedsToFlushData, "Flushing when we don't need to");
|
|
mNeedsToFlushData = false;
|
|
}
|
|
|
|
// If a data error occured, just ignore future data.
|
|
if (HasDataError())
|
|
return;
|
|
|
|
if (IsSizeDecode() && HasSize()) {
|
|
// More data came in since we found the size. We have nothing to do here.
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(!NeedsNewFrame() || HasDataError(),
|
|
"Should not need a new frame before writing anything");
|
|
MOZ_ASSERT(!NeedsToFlushData() || HasDataError(),
|
|
"Should not need to flush data before writing anything");
|
|
|
|
// Pass the data along to the implementation.
|
|
WriteInternal(aBuffer, aCount);
|
|
|
|
// If we need a new frame to proceed, let's create one and call it again.
|
|
while (NeedsNewFrame() && !HasDataError()) {
|
|
MOZ_ASSERT(!IsSizeDecode(), "Shouldn't need new frame for size decode");
|
|
|
|
nsresult rv = AllocateFrame();
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// Use the data we saved when we asked for a new frame.
|
|
WriteInternal(nullptr, 0);
|
|
}
|
|
|
|
mNeedsToFlushData = false;
|
|
}
|
|
|
|
// Finish telemetry.
|
|
mDecodeTime += (TimeStamp::Now() - start);
|
|
}
|
|
|
|
void
|
|
Decoder::CompleteDecode()
|
|
{
|
|
// Implementation-specific finalization
|
|
if (!HasError())
|
|
FinishInternal();
|
|
|
|
// If the implementation left us mid-frame, finish that up.
|
|
if (mInFrame && !HasError())
|
|
PostFrameStop();
|
|
|
|
// If PostDecodeDone() has not been called, and this decoder wasn't aborted
|
|
// early because of low-memory conditions or losing a race with another
|
|
// decoder, we need to send teardown notifications (and report an error to the
|
|
// console later).
|
|
if (!IsSizeDecode() && !mDecodeDone && !WasAborted()) {
|
|
mShouldReportError = true;
|
|
|
|
// If we only have a data error, we're usable if we have at least one
|
|
// complete frame.
|
|
if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
|
|
// We're usable, so do exactly what we should have when the decoder
|
|
// completed.
|
|
|
|
// Not writing to the entire frame may have left us transparent.
|
|
PostHasTransparency();
|
|
|
|
if (mInFrame) {
|
|
PostFrameStop();
|
|
}
|
|
PostDecodeDone();
|
|
} else {
|
|
// We're not usable. Record some final progress indicating the error.
|
|
if (!IsSizeDecode()) {
|
|
mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
|
|
}
|
|
mProgress |= FLAG_HAS_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::Finish()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(HasError() || !mInFrame, "Finishing while we're still in a frame");
|
|
|
|
// If we detected an error in CompleteDecode(), log it to the error console.
|
|
if (mShouldReportError && !WasAborted()) {
|
|
nsCOMPtr<nsIConsoleService> consoleService =
|
|
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
|
nsCOMPtr<nsIScriptError> errorObject =
|
|
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
|
|
|
|
if (consoleService && errorObject && !HasDecoderError()) {
|
|
nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
|
|
NS_ConvertUTF8toUTF16(mImage->GetURIString()));
|
|
|
|
if (NS_SUCCEEDED(errorObject->InitWithWindowID(
|
|
msg,
|
|
NS_ConvertUTF8toUTF16(mImage->GetURIString()),
|
|
EmptyString(), 0, 0, nsIScriptError::errorFlag,
|
|
"Image", mImage->InnerWindowID()
|
|
))) {
|
|
consoleService->LogMessage(errorObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set image metadata before calling DecodingComplete, because
|
|
// DecodingComplete calls Optimize().
|
|
mImageMetadata.SetOnImage(mImage);
|
|
|
|
if (HasSize()) {
|
|
SetSizeOnImage();
|
|
}
|
|
|
|
if (mDecodeDone && !IsSizeDecode()) {
|
|
MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
|
|
|
|
// If this image wasn't animated and isn't a transient image, mark its frame
|
|
// as optimizable. We don't support optimizing animated images and
|
|
// optimizing transient images isn't worth it.
|
|
if (!mIsAnimated && !mImageIsTransient && mCurrentFrame) {
|
|
mCurrentFrame->SetOptimizable();
|
|
}
|
|
|
|
mImage->OnDecodingComplete();
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::FinishSharedDecoder()
|
|
{
|
|
if (!HasError()) {
|
|
FinishInternal();
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
Decoder::AllocateFrame(const nsIntSize& aTargetSize /* = nsIntSize() */)
|
|
{
|
|
MOZ_ASSERT(mNeedsNewFrame);
|
|
|
|
nsIntSize targetSize = aTargetSize;
|
|
if (targetSize == nsIntSize()) {
|
|
MOZ_ASSERT(HasSize());
|
|
targetSize = mImageMetadata.GetSize();
|
|
}
|
|
|
|
mCurrentFrame = EnsureFrame(mNewFrameData.mFrameNum,
|
|
targetSize,
|
|
mNewFrameData.mFrameRect,
|
|
GetDecodeFlags(),
|
|
mNewFrameData.mFormat,
|
|
mNewFrameData.mPaletteDepth,
|
|
mCurrentFrame.get());
|
|
|
|
if (mCurrentFrame) {
|
|
// Gather the raw pointers the decoders will use.
|
|
mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
|
|
mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
|
|
|
|
if (mNewFrameData.mFrameNum + 1 == mFrameCount) {
|
|
PostFrameStart();
|
|
}
|
|
} else {
|
|
PostDataError();
|
|
}
|
|
|
|
// Mark ourselves as not needing another frame before talking to anyone else
|
|
// so they can tell us if they need yet another.
|
|
mNeedsNewFrame = false;
|
|
|
|
// If we've received any data at all, we may have pending data that needs to
|
|
// be flushed now that we have a frame to decode into.
|
|
if (mBytesDecoded > 0) {
|
|
mNeedsToFlushData = true;
|
|
}
|
|
|
|
return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
RawAccessFrameRef
|
|
Decoder::EnsureFrame(uint32_t aFrameNum,
|
|
const nsIntSize& aTargetSize,
|
|
const nsIntRect& aFrameRect,
|
|
uint32_t aDecodeFlags,
|
|
SurfaceFormat aFormat,
|
|
uint8_t aPaletteDepth,
|
|
imgFrame* aPreviousFrame)
|
|
{
|
|
if (mDataError || NS_FAILED(mFailCode)) {
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!");
|
|
if (aFrameNum > mFrameCount) {
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
// Adding a frame that doesn't already exist. This is the normal case.
|
|
if (aFrameNum == mFrameCount) {
|
|
return InternalAddFrame(aFrameNum, aTargetSize, aFrameRect, aDecodeFlags,
|
|
aFormat, aPaletteDepth, aPreviousFrame);
|
|
}
|
|
|
|
// We're replacing a frame. It must be the first frame; there's no reason to
|
|
// ever replace any other frame, since the first frame is the only one we
|
|
// speculatively allocate without knowing what the decoder really needs.
|
|
// XXX(seth): I'm not convinced there's any reason to support this at all. We
|
|
// should figure out how to avoid triggering this and rip it out.
|
|
MOZ_ASSERT(aFrameNum == 0, "Replacing a frame other than the first?");
|
|
MOZ_ASSERT(mFrameCount == 1, "Should have only one frame");
|
|
MOZ_ASSERT(aPreviousFrame, "Need the previous frame to replace");
|
|
if (aFrameNum != 0 || !aPreviousFrame || mFrameCount != 1) {
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
MOZ_ASSERT(!aPreviousFrame->GetRect().IsEqualEdges(aFrameRect) ||
|
|
aPreviousFrame->GetFormat() != aFormat ||
|
|
aPreviousFrame->GetPaletteDepth() != aPaletteDepth,
|
|
"Replacing first frame with the same kind of frame?");
|
|
|
|
// Reset our state.
|
|
mInFrame = false;
|
|
RawAccessFrameRef ref = Move(mCurrentFrame);
|
|
|
|
MOZ_ASSERT(ref, "No ref to current frame?");
|
|
|
|
// Reinitialize the old frame.
|
|
nsIntSize oldSize = aPreviousFrame->GetImageSize();
|
|
bool nonPremult =
|
|
aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
|
|
if (NS_FAILED(aPreviousFrame->ReinitForDecoder(oldSize, aFrameRect, aFormat,
|
|
aPaletteDepth, nonPremult))) {
|
|
NS_WARNING("imgFrame::ReinitForDecoder should succeed");
|
|
mFrameCount = 0;
|
|
aPreviousFrame->Abort();
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
RawAccessFrameRef
|
|
Decoder::InternalAddFrame(uint32_t aFrameNum,
|
|
const nsIntSize& aTargetSize,
|
|
const nsIntRect& aFrameRect,
|
|
uint32_t aDecodeFlags,
|
|
SurfaceFormat aFormat,
|
|
uint8_t aPaletteDepth,
|
|
imgFrame* aPreviousFrame)
|
|
{
|
|
MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!");
|
|
if (aFrameNum > mFrameCount) {
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
if (aTargetSize.width <= 0 || aTargetSize.height <= 0 ||
|
|
aFrameRect.width <= 0 || aFrameRect.height <= 0) {
|
|
NS_WARNING("Trying to add frame with zero or negative size");
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
const uint32_t bytesPerPixel = aPaletteDepth == 0 ? 4 : 1;
|
|
if (!SurfaceCache::CanHold(aFrameRect.Size(), bytesPerPixel)) {
|
|
NS_WARNING("Trying to add frame that's too large for the SurfaceCache");
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
nsRefPtr<imgFrame> frame = new imgFrame();
|
|
bool nonPremult =
|
|
aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
|
|
if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat,
|
|
aPaletteDepth, nonPremult))) {
|
|
NS_WARNING("imgFrame::Init should succeed");
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
RawAccessFrameRef ref = frame->RawAccessRef();
|
|
if (!ref) {
|
|
frame->Abort();
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
InsertOutcome outcome =
|
|
SurfaceCache::Insert(frame, ImageKey(mImage.get()),
|
|
RasterSurfaceKey(aTargetSize,
|
|
aDecodeFlags,
|
|
aFrameNum),
|
|
Lifetime::Persistent);
|
|
if (outcome != InsertOutcome::SUCCESS) {
|
|
// We either hit InsertOutcome::FAILURE, which is a temporary failure due to
|
|
// low memory (we know it's not permanent because we checked CanHold()
|
|
// above), or InsertOutcome::FAILURE_ALREADY_PRESENT, which means that
|
|
// another decoder beat us to decoding this frame. Either way, we should
|
|
// abort this decoder rather than treat this as a real error.
|
|
mDecodeAborted = true;
|
|
ref->Abort();
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
nsIntRect refreshArea;
|
|
|
|
if (aFrameNum == 1) {
|
|
MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
|
|
aPreviousFrame->SetRawAccessOnly();
|
|
|
|
// 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).
|
|
AnimationData previousFrameData = aPreviousFrame->GetAnimationData();
|
|
if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR ||
|
|
previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL ||
|
|
previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
|
|
refreshArea = previousFrameData.mRect;
|
|
}
|
|
}
|
|
|
|
if (aFrameNum > 0) {
|
|
ref->SetRawAccessOnly();
|
|
|
|
// 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.
|
|
refreshArea.UnionRect(refreshArea, frame->GetRect());
|
|
}
|
|
|
|
mFrameCount++;
|
|
mImage->OnAddedFrame(mFrameCount, refreshArea);
|
|
|
|
return ref;
|
|
}
|
|
|
|
void
|
|
Decoder::SetSizeOnImage()
|
|
{
|
|
MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
|
|
MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
|
|
|
|
nsresult rv = mImage->SetSize(mImageMetadata.GetWidth(),
|
|
mImageMetadata.GetHeight(),
|
|
mImageMetadata.GetOrientation());
|
|
if (NS_FAILED(rv)) {
|
|
PostResizeError();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Hook stubs. Override these as necessary in decoder implementations.
|
|
*/
|
|
|
|
void Decoder::InitInternal() { }
|
|
void Decoder::WriteInternal(const char* aBuffer, uint32_t aCount) { }
|
|
void Decoder::FinishInternal() { }
|
|
|
|
/*
|
|
* Progress Notifications
|
|
*/
|
|
|
|
void
|
|
Decoder::PostSize(int32_t aWidth,
|
|
int32_t aHeight,
|
|
Orientation aOrientation /* = Orientation()*/)
|
|
{
|
|
// Validate
|
|
MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
|
|
MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
|
|
|
|
// Tell the image
|
|
mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
|
|
|
|
// Record this notification.
|
|
mProgress |= FLAG_SIZE_AVAILABLE;
|
|
}
|
|
|
|
void
|
|
Decoder::PostHasTransparency()
|
|
{
|
|
mProgress |= FLAG_HAS_TRANSPARENCY;
|
|
}
|
|
|
|
void
|
|
Decoder::PostFrameStart()
|
|
{
|
|
// We shouldn't already be mid-frame
|
|
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
|
|
|
|
// Update our state to reflect the new frame
|
|
mInFrame = true;
|
|
|
|
// If we just became animated, record that fact.
|
|
if (mFrameCount > 1) {
|
|
mIsAnimated = true;
|
|
mProgress |= FLAG_IS_ANIMATED;
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::PostFrameStop(Opacity aFrameOpacity /* = Opacity::TRANSPARENT */,
|
|
DisposalMethod aDisposalMethod /* = DisposalMethod::KEEP */,
|
|
int32_t aTimeout /* = 0 */,
|
|
BlendMethod aBlendMethod /* = BlendMethod::OVER */)
|
|
{
|
|
// We should be mid-frame
|
|
MOZ_ASSERT(!IsSizeDecode(), "Stopping frame during a size decode");
|
|
MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
|
|
MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
|
|
|
|
// Update our state
|
|
mInFrame = false;
|
|
|
|
mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod);
|
|
|
|
mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
|
|
|
|
// If we're not sending partial invalidations, then we send an invalidation
|
|
// here when the first frame is complete.
|
|
if (!mSendPartialInvalidations && !mIsAnimated) {
|
|
mInvalidRect.UnionRect(mInvalidRect,
|
|
nsIntRect(nsIntPoint(0, 0), GetSize()));
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::PostInvalidation(const nsIntRect& aRect,
|
|
const Maybe<nsIntRect>& aRectAtTargetSize
|
|
/* = 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 (mSendPartialInvalidations && !mIsAnimated) {
|
|
mInvalidRect.UnionRect(mInvalidRect, aRect);
|
|
mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
|
|
{
|
|
MOZ_ASSERT(!IsSizeDecode(), "Can't be done with decoding with size decode!");
|
|
MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
|
|
MOZ_ASSERT(!mDecodeDone, "Decode already done!");
|
|
mDecodeDone = true;
|
|
|
|
mImageMetadata.SetLoopCount(aLoopCount);
|
|
|
|
mProgress |= FLAG_DECODE_COMPLETE;
|
|
}
|
|
|
|
void
|
|
Decoder::PostDataError()
|
|
{
|
|
mDataError = true;
|
|
|
|
if (mInFrame && mCurrentFrame) {
|
|
mCurrentFrame->Abort();
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::PostDecoderError(nsresult aFailureCode)
|
|
{
|
|
MOZ_ASSERT(NS_FAILED(aFailureCode), "Not a failure code!");
|
|
|
|
mFailCode = aFailureCode;
|
|
|
|
// XXXbholley - we should report the image URI here, but imgContainer
|
|
// needs to know its URI first
|
|
NS_WARNING("Image decoding error - This is probably a bug!");
|
|
|
|
if (mInFrame && mCurrentFrame) {
|
|
mCurrentFrame->Abort();
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
|
|
uint32_t width, uint32_t height,
|
|
gfx::SurfaceFormat format,
|
|
uint8_t palette_depth /* = 0 */)
|
|
{
|
|
// Decoders should never call NeedNewFrame without yielding back to Write().
|
|
MOZ_ASSERT(!mNeedsNewFrame);
|
|
|
|
// We don't want images going back in time or skipping frames.
|
|
MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount - 1));
|
|
|
|
mNewFrameData = NewFrameData(framenum,
|
|
nsIntRect(x_offset, y_offset, width, height),
|
|
format, palette_depth);
|
|
mNeedsNewFrame = true;
|
|
}
|
|
|
|
Telemetry::ID
|
|
Decoder::SpeedHistogram()
|
|
{
|
|
// Use HistogramCount as an invalid Histogram ID.
|
|
return Telemetry::HistogramCount;
|
|
}
|
|
|
|
} // namespace image
|
|
} // namespace mozilla
|