mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1079627 (Part 3) - Support multiple decoders for a single RasterImage. r=tn
This commit is contained in:
parent
f245e814b3
commit
59329b9aa6
@ -237,16 +237,15 @@ private:
|
||||
DECL_GFX_PREF(Live, "image.high_quality_downscaling.enabled", ImageHQDownscalingEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.high_quality_downscaling.min_factor", ImageHQDownscalingMinFactor, uint32_t, 1000);
|
||||
DECL_GFX_PREF(Live, "image.high_quality_upscaling.max_size", ImageHQUpscalingMaxSize, uint32_t, 20971520);
|
||||
DECL_GFX_PREF(Live, "image.mem.decode_bytes_at_a_time", ImageMemDecodeBytesAtATime, uint32_t, 200000);
|
||||
DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time", ImageMemDecodeBytesAtATime, uint32_t, 200000);
|
||||
DECL_GFX_PREF(Live, "image.mem.decodeondraw", ImageMemDecodeOnDraw, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.mem.discardable", ImageMemDiscardable, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.mem.max_ms_before_yield", ImageMemMaxMSBeforeYield, uint32_t, 400);
|
||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
|
||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb", ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
|
||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000);
|
||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor", ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
|
||||
DECL_GFX_PREF(Live, "image.mozsamplesize.enabled", ImageMozSampleSizeEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1);
|
||||
DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1);
|
||||
|
||||
DECL_GFX_PREF(Once, "layers.acceleration.disabled", LayersAccelerationDisabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps", LayersDrawFPS, bool, false);
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "prsystem.h"
|
||||
|
||||
@ -42,102 +41,88 @@ public:
|
||||
* Called by the DecodePool when it's done some significant portion of
|
||||
* decoding, so that progress can be recorded and notifications can be sent.
|
||||
*/
|
||||
static void Dispatch(RasterImage* aImage)
|
||||
static void Dispatch(RasterImage* aImage,
|
||||
Progress aProgress,
|
||||
const nsIntRect& aInvalidRect,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> worker = new NotifyProgressWorker(aImage);
|
||||
MOZ_ASSERT(aImage);
|
||||
|
||||
nsCOMPtr<nsIRunnable> worker =
|
||||
new NotifyProgressWorker(aImage, aProgress, aInvalidRect, aFlags);
|
||||
NS_DispatchToMainThread(worker);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
||||
|
||||
mImage->FinishedSomeDecoding(ShutdownReason::DONE);
|
||||
|
||||
mImage->NotifyProgress(mProgress, mInvalidRect, mFlags);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit NotifyProgressWorker(RasterImage* aImage)
|
||||
NotifyProgressWorker(RasterImage* aImage, Progress aProgress,
|
||||
const nsIntRect& aInvalidRect, uint32_t aFlags)
|
||||
: mImage(aImage)
|
||||
, mProgress(aProgress)
|
||||
, mInvalidRect(aInvalidRect)
|
||||
, mFlags(aFlags)
|
||||
{ }
|
||||
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
const Progress mProgress;
|
||||
const nsIntRect mInvalidRect;
|
||||
const uint32_t mFlags;
|
||||
};
|
||||
|
||||
class NotifyDecodeCompleteWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Called by the DecodePool when decoding is complete, so that final cleanup
|
||||
* can be performed.
|
||||
*/
|
||||
static void Dispatch(Decoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(aDecoder);
|
||||
|
||||
nsCOMPtr<nsIRunnable> worker = new NotifyDecodeCompleteWorker(aDecoder);
|
||||
NS_DispatchToMainThread(worker);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DecodePool::Singleton()->NotifyDecodeComplete(mDecoder);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit NotifyDecodeCompleteWorker(Decoder* aDecoder)
|
||||
: mDecoder(aDecoder)
|
||||
{ }
|
||||
|
||||
nsRefPtr<Decoder> mDecoder;
|
||||
};
|
||||
|
||||
class DecodeWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit DecodeWorker(RasterImage* aImage)
|
||||
: mImage(aImage)
|
||||
{ }
|
||||
explicit DecodeWorker(Decoder* aDecoder)
|
||||
: mDecoder(aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(mDecoder);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
||||
|
||||
// If we were interrupted, we shouldn't do any work.
|
||||
if (mImage->mDecodeStatus == DecodeStatus::STOPPED) {
|
||||
NotifyProgressWorker::Dispatch(mImage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If someone came along and synchronously decoded us, there's nothing for us to do.
|
||||
if (!mImage->mDecoder || mImage->IsDecodeFinished()) {
|
||||
NotifyProgressWorker::Dispatch(mImage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mImage->mDecodeStatus = DecodeStatus::ACTIVE;
|
||||
|
||||
size_t oldByteCount = mImage->mDecoder->BytesDecoded();
|
||||
|
||||
size_t maxBytes = mImage->mSourceData.Length() -
|
||||
mImage->mDecoder->BytesDecoded();
|
||||
DecodePool::Singleton()->DecodeSomeOfImage(mImage, DecodeUntil::DONE_BYTES,
|
||||
maxBytes);
|
||||
|
||||
size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount;
|
||||
|
||||
mImage->mDecodeStatus = DecodeStatus::WORK_DONE;
|
||||
|
||||
if (mImage->mDecoder &&
|
||||
!mImage->mError &&
|
||||
!mImage->mPendingError &&
|
||||
!mImage->IsDecodeFinished() &&
|
||||
bytesDecoded < maxBytes &&
|
||||
bytesDecoded > 0) {
|
||||
// We aren't finished decoding, and we have more data, so add this request
|
||||
// to the back of the list.
|
||||
DecodePool::Singleton()->RequestDecode(mImage);
|
||||
} else {
|
||||
// Nothing more for us to do - let everyone know what happened.
|
||||
NotifyProgressWorker::Dispatch(mImage);
|
||||
}
|
||||
|
||||
DecodePool::Singleton()->Decode(mDecoder);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~DecodeWorker()
|
||||
{
|
||||
// Dispatch mImage to main thread to prevent mImage from being destructed by 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");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
nsRefPtr<Decoder> mDecoder;
|
||||
};
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
@ -194,13 +179,6 @@ DecodePool::Singleton()
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
DecodePool::GetEventTarget()
|
||||
{
|
||||
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
|
||||
return target.forget();
|
||||
}
|
||||
|
||||
DecodePool::DecodePool()
|
||||
: mThreadPoolMutex("Thread Pool")
|
||||
{
|
||||
@ -258,20 +236,11 @@ DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
|
||||
}
|
||||
|
||||
void
|
||||
DecodePool::RequestDecode(RasterImage* aImage)
|
||||
DecodePool::AsyncDecode(Decoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(aImage->mDecoder);
|
||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
MOZ_ASSERT(aDecoder);
|
||||
|
||||
if (aImage->mDecodeStatus == DecodeStatus::PENDING ||
|
||||
aImage->mDecodeStatus == DecodeStatus::ACTIVE) {
|
||||
// The image is already in our list of images to decode, or currently being
|
||||
// decoded, so we don't have to do anything else.
|
||||
return;
|
||||
}
|
||||
|
||||
aImage->mDecodeStatus = DecodeStatus::PENDING;
|
||||
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aImage);
|
||||
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
|
||||
|
||||
// Dispatch to the thread pool if it exists. If it doesn't, we're currently
|
||||
// shutting down, so it's OK to just drop the job on the floor.
|
||||
@ -282,136 +251,90 @@ DecodePool::RequestDecode(RasterImage* aImage)
|
||||
}
|
||||
|
||||
void
|
||||
DecodePool::DecodeABitOf(RasterImage* aImage)
|
||||
DecodePool::SyncDecodeIfSmall(Decoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
MOZ_ASSERT(aDecoder);
|
||||
|
||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
||||
if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
|
||||
aImage->FinishedSomeDecoding();
|
||||
if (aDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime())) {
|
||||
Decode(aDecoder);
|
||||
return;
|
||||
}
|
||||
|
||||
DecodeSomeOfImage(aImage);
|
||||
|
||||
aImage->FinishedSomeDecoding();
|
||||
|
||||
// If we aren't yet finished decoding and we have more data in hand, add
|
||||
// this request to the back of the priority list.
|
||||
if (aImage->mDecoder &&
|
||||
!aImage->mError &&
|
||||
!aImage->IsDecodeFinished() &&
|
||||
aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded()) {
|
||||
RequestDecode(aImage);
|
||||
}
|
||||
AsyncDecode(aDecoder);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
DecodePool::StopDecoding(RasterImage* aImage)
|
||||
{
|
||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If we haven't got a decode request, we're not currently decoding. (Having
|
||||
// a decode request doesn't imply we *are* decoding, though.)
|
||||
aImage->mDecodeStatus = DecodeStatus::STOPPED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DecodePool::DecodeUntilSizeAvailable(RasterImage* aImage)
|
||||
void
|
||||
DecodePool::SyncDecodeIfPossible(Decoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter lock(aImage->mDecodingMonitor);
|
||||
Decode(aDecoder);
|
||||
}
|
||||
|
||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
||||
if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
|
||||
nsresult rv = aImage->FinishedSomeDecoding();
|
||||
if (NS_FAILED(rv)) {
|
||||
aImage->DoError();
|
||||
return rv;
|
||||
already_AddRefed<nsIEventTarget>
|
||||
DecodePool::GetEventTarget()
|
||||
{
|
||||
MutexAutoLock threadPoolLock(mThreadPoolMutex);
|
||||
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
|
||||
return target.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIRunnable>
|
||||
DecodePool::CreateDecodeWorker(Decoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(aDecoder);
|
||||
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
|
||||
return worker.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DecodePool::Decode(Decoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(aDecoder);
|
||||
|
||||
nsresult rv = aDecoder->Decode();
|
||||
|
||||
if (NS_SUCCEEDED(rv) && !aDecoder->GetDecodeDone()) {
|
||||
if (aDecoder->HasProgress()) {
|
||||
NotifyProgress(aDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = DecodeSomeOfImage(aImage, DecodeUntil::SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return aImage->FinishedSomeDecoding();
|
||||
}
|
||||
|
||||
nsresult
|
||||
DecodePool::DecodeSomeOfImage(RasterImage* aImage,
|
||||
DecodeUntil aDecodeUntil /* = DecodeUntil::TIME */,
|
||||
uint32_t bytesToDecode /* = 0 */)
|
||||
{
|
||||
MOZ_ASSERT(aImage->mInitialized, "Worker active for uninitialized container");
|
||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If an error is flagged, it probably happened while we were waiting
|
||||
// in the event queue.
|
||||
if (aImage->mError) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If there is an error worker pending (say because the main thread has enqueued
|
||||
// another decode request for us before processing the error worker) then bail out.
|
||||
if (aImage->mPendingError) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If mDecoded or we don't have a decoder, we must have finished already (for
|
||||
// example, a synchronous decode request came while the worker was pending).
|
||||
if (!aImage->mDecoder || aImage->mDecoded) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<Decoder> decoderKungFuDeathGrip = aImage->mDecoder;
|
||||
|
||||
uint32_t maxBytes;
|
||||
if (aImage->mDecoder->IsSizeDecode()) {
|
||||
// Decode all available data if we're a size decode; they're cheap, and we
|
||||
// want them to be more or less synchronous.
|
||||
maxBytes = aImage->mSourceData.Length();
|
||||
// The decoder will ensure that a new worker gets enqueued to continue
|
||||
// decoding when more data is available.
|
||||
} else {
|
||||
// We're only guaranteed to decode this many bytes, so in particular,
|
||||
// gfxPrefs::ImageMemDecodeBytesAtATime should be set high enough for us
|
||||
// to read the size from most images.
|
||||
maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime();
|
||||
NotifyDecodeComplete(aDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DecodePool::NotifyProgress(Decoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(aDecoder);
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
NotifyProgressWorker::Dispatch(aDecoder->GetImage(),
|
||||
aDecoder->TakeProgress(),
|
||||
aDecoder->TakeInvalidRect(),
|
||||
aDecoder->GetDecodeFlags());
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytesToDecode == 0) {
|
||||
bytesToDecode = aImage->mSourceData.Length() - aImage->mDecoder->BytesDecoded();
|
||||
aDecoder->GetImage()->NotifyProgress(aDecoder->TakeProgress(),
|
||||
aDecoder->TakeInvalidRect(),
|
||||
aDecoder->GetDecodeFlags());
|
||||
}
|
||||
|
||||
void
|
||||
DecodePool::NotifyDecodeComplete(Decoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(aDecoder);
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
NotifyDecodeCompleteWorker::Dispatch(aDecoder);
|
||||
return;
|
||||
}
|
||||
|
||||
TimeStamp deadline = TimeStamp::Now() +
|
||||
TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield());
|
||||
|
||||
// We keep decoding chunks until:
|
||||
// * we don't have any data left to decode,
|
||||
// * the decode completes,
|
||||
// * we're an DecodeUntil::SIZE decode and we get the size, or
|
||||
// * we run out of time.
|
||||
while (aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded() &&
|
||||
bytesToDecode > 0 &&
|
||||
!aImage->IsDecodeFinished() &&
|
||||
!(aDecodeUntil == DecodeUntil::SIZE && aImage->mHasSize)) {
|
||||
uint32_t chunkSize = min(bytesToDecode, maxBytes);
|
||||
nsresult rv = aImage->DecodeSomeData(chunkSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
aImage->DoError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
bytesToDecode -= chunkSize;
|
||||
|
||||
// Yield if we've been decoding for too long. We check this _after_ decoding
|
||||
// a chunk to ensure that we don't yield without doing any decoding.
|
||||
if (aDecodeUntil == DecodeUntil::TIME && TimeStamp::Now() >= deadline) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
aDecoder->Finish();
|
||||
aDecoder->GetImage()->FinalizeDecoder(aDecoder);
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
|
@ -25,39 +25,15 @@ namespace image {
|
||||
class Decoder;
|
||||
class RasterImage;
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(DecodeStatus, uint8_t)
|
||||
INACTIVE,
|
||||
PENDING,
|
||||
ACTIVE,
|
||||
WORK_DONE,
|
||||
STOPPED
|
||||
MOZ_END_ENUM_CLASS(DecodeStatus)
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(DecodeUntil, uint8_t)
|
||||
TIME,
|
||||
SIZE,
|
||||
DONE_BYTES
|
||||
MOZ_END_ENUM_CLASS(DecodeUntil)
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(ShutdownReason, uint8_t)
|
||||
DONE,
|
||||
NOT_NEEDED,
|
||||
FATAL_ERROR
|
||||
MOZ_END_ENUM_CLASS(ShutdownReason)
|
||||
|
||||
|
||||
/**
|
||||
* DecodePool is a singleton class we use when decoding large images.
|
||||
* DecodePool is a singleton class that manages decoding of raster images. It
|
||||
* owns a pool of image decoding threads that are used for asynchronous
|
||||
* decoding.
|
||||
*
|
||||
* When we wish to decode an image larger than
|
||||
* image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode()
|
||||
* for the image. This adds the image to a queue of pending requests and posts
|
||||
* the DecodePool singleton to the event queue, if it's not already pending
|
||||
* there.
|
||||
*
|
||||
* When the DecodePool is run from the event queue, it decodes the image (and
|
||||
* all others it's managing) in chunks, periodically yielding control back to
|
||||
* the event loop.
|
||||
* DecodePool allows callers to run a decoder, handling management of the
|
||||
* decoder's lifecycle and whether it executes on the main thread,
|
||||
* off-main-thread in the image decoding thread pool, or on some combination of
|
||||
* the two.
|
||||
*/
|
||||
class DecodePool : public nsIObserver
|
||||
{
|
||||
@ -67,58 +43,52 @@ public:
|
||||
|
||||
static DecodePool* Singleton();
|
||||
|
||||
/**
|
||||
* Ask the DecodePool to asynchronously decode this image.
|
||||
*/
|
||||
void RequestDecode(RasterImage* aImage);
|
||||
/// Ask the DecodePool to run @aDecoder asynchronously and return immediately.
|
||||
void AsyncDecode(Decoder* aDecoder);
|
||||
|
||||
/**
|
||||
* Decode aImage for a short amount of time, and post the remainder to the
|
||||
* queue.
|
||||
* Run @aDecoder synchronously if the image it's decoding is small. If the
|
||||
* image is too large, or if the source data isn't complete yet, run @aDecoder
|
||||
* asynchronously instead.
|
||||
*/
|
||||
void DecodeABitOf(RasterImage* aImage);
|
||||
void SyncDecodeIfSmall(Decoder* aDecoder);
|
||||
|
||||
/**
|
||||
* Ask the DecodePool to stop decoding this image. Internally, we also
|
||||
* call this function when we finish decoding an image.
|
||||
* Run aDecoder synchronously if at all possible. If it can't complete
|
||||
* synchronously because the source data isn't complete, asynchronously decode
|
||||
* the rest.
|
||||
*/
|
||||
void SyncDecodeIfPossible(Decoder* aDecoder);
|
||||
|
||||
/**
|
||||
* Returns an event target interface to the DecodePool's underlying thread
|
||||
* pool. Callers can use this event target to submit work to the image
|
||||
* decoding thread pool.
|
||||
*
|
||||
* Since the DecodePool keeps raw pointers to RasterImages, make sure you
|
||||
* call this before a RasterImage is destroyed!
|
||||
*/
|
||||
static void StopDecoding(RasterImage* aImage);
|
||||
|
||||
/**
|
||||
* Synchronously decode the beginning of the image until we run out of
|
||||
* bytes or we get the image's size. Note that this done on a best-effort
|
||||
* basis; if the size is burried too deep in the image, we'll give up.
|
||||
*
|
||||
* @return NS_ERROR if an error is encountered, and NS_OK otherwise. (Note
|
||||
* that we return NS_OK even when the size was not found.)
|
||||
*/
|
||||
nsresult DecodeUntilSizeAvailable(RasterImage* aImage);
|
||||
|
||||
/**
|
||||
* Returns an event target interface to the thread pool; primarily for
|
||||
* OnDataAvailable delivery off main thread.
|
||||
*
|
||||
* @return An nsIEventTarget interface to mThreadPool.
|
||||
* @return An nsIEventTarget interface to the thread pool.
|
||||
*/
|
||||
already_AddRefed<nsIEventTarget> GetEventTarget();
|
||||
|
||||
/**
|
||||
* Decode some chunks of the given image. If aDecodeUntil is SIZE,
|
||||
* decode until we have the image's size, then stop. If bytesToDecode is
|
||||
* non-0, at most bytesToDecode bytes will be decoded. if aDecodeUntil is
|
||||
* DONE_BYTES, decode until all bytesToDecode bytes are decoded.
|
||||
* Creates a worker which can be used to attempt further decoding using the
|
||||
* provided decoder.
|
||||
*
|
||||
* @return The new worker, which should be posted to the event target returned
|
||||
* by GetEventTarget.
|
||||
*/
|
||||
nsresult DecodeSomeOfImage(RasterImage* aImage,
|
||||
DecodeUntil aDecodeUntil = DecodeUntil::TIME,
|
||||
uint32_t bytesToDecode = 0);
|
||||
already_AddRefed<nsIRunnable> CreateDecodeWorker(Decoder* aDecoder);
|
||||
|
||||
private:
|
||||
friend class DecodeWorker;
|
||||
friend class NotifyDecodeCompleteWorker;
|
||||
|
||||
DecodePool();
|
||||
virtual ~DecodePool();
|
||||
|
||||
void Decode(Decoder* aDecoder);
|
||||
void NotifyDecodeComplete(Decoder* aDecoder);
|
||||
void NotifyProgress(Decoder* aDecoder);
|
||||
|
||||
static StaticRefPtr<DecodePool> sSingleton;
|
||||
|
||||
// mThreadPoolMutex protects mThreadPool. For all RasterImages R,
|
||||
|
@ -7,10 +7,12 @@
|
||||
#include "Decoder.h"
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "DecodePool.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
||||
@ -29,8 +31,11 @@ Decoder::Decoder(RasterImage* aImage)
|
||||
, mDecodeFlags(0)
|
||||
, mBytesDecoded(0)
|
||||
, mSendPartialInvalidations(false)
|
||||
, mDataDone(false)
|
||||
, mDecodeDone(false)
|
||||
, mDataError(false)
|
||||
, mDecodeAborted(false)
|
||||
, mImageIsTransient(false)
|
||||
, mFrameCount(0)
|
||||
, mFailCode(NS_OK)
|
||||
, mNeedsNewFrame(false)
|
||||
@ -73,7 +78,7 @@ void
|
||||
Decoder::Init()
|
||||
{
|
||||
// No re-initializing
|
||||
NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
|
||||
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
|
||||
|
||||
// Fire OnStartDecode at init time to support bug 512435.
|
||||
if (!IsSizeDecode()) {
|
||||
@ -113,6 +118,69 @@ Decoder::InitSharedDecoder(uint8_t* aImageData, uint32_t aImageDataLength,
|
||||
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();
|
||||
}
|
||||
|
||||
return finalStatus;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(newState == SourceBufferIterator::READY);
|
||||
|
||||
Write(mIterator->Data(), mIterator->Length());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -172,7 +240,7 @@ Decoder::Write(const char* aBuffer, uint32_t aCount)
|
||||
}
|
||||
|
||||
void
|
||||
Decoder::Finish(ShutdownReason aReason)
|
||||
Decoder::Finish()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -184,9 +252,10 @@ Decoder::Finish(ShutdownReason aReason)
|
||||
if (mInFrame && !HasError())
|
||||
PostFrameStop();
|
||||
|
||||
// If PostDecodeDone() has not been called, we need to sent teardown
|
||||
// notifications.
|
||||
if (!IsSizeDecode() && !mDecodeDone) {
|
||||
// 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.
|
||||
if (!IsSizeDecode() && !mDecodeDone && !WasAborted()) {
|
||||
|
||||
// Log data errors to the error console
|
||||
nsCOMPtr<nsIConsoleService> consoleService =
|
||||
@ -208,22 +277,17 @@ Decoder::Finish(ShutdownReason aReason)
|
||||
}
|
||||
}
|
||||
|
||||
bool usable = !HasDecoderError();
|
||||
if (aReason != ShutdownReason::NOT_NEEDED && !HasDecoderError()) {
|
||||
// If we only have a data error, we're usable if we have at least one complete frame.
|
||||
if (GetCompleteFrameCount() == 0) {
|
||||
usable = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're usable, do exactly what we should have when the decoder
|
||||
// completed.
|
||||
if (usable) {
|
||||
// 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.
|
||||
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;
|
||||
}
|
||||
@ -235,9 +299,21 @@ Decoder::Finish(ShutdownReason aReason)
|
||||
// DecodingComplete calls Optimize().
|
||||
mImageMetadata.SetOnImage(mImage);
|
||||
|
||||
if (mDecodeDone) {
|
||||
if (HasSize()) {
|
||||
SetSizeOnImage();
|
||||
}
|
||||
|
||||
if (mDecodeDone && !IsSizeDecode()) {
|
||||
MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
|
||||
mImage->DecodingComplete(mCurrentFrame.get());
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,6 +466,12 @@ Decoder::InternalAddFrame(uint32_t aFrameNum,
|
||||
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();
|
||||
}
|
||||
@ -431,9 +513,12 @@ Decoder::SetSizeOnImage()
|
||||
MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
|
||||
MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
|
||||
|
||||
mImage->SetSize(mImageMetadata.GetWidth(),
|
||||
mImageMetadata.GetHeight(),
|
||||
mImageMetadata.GetOrientation());
|
||||
nsresult rv = mImage->SetSize(mImageMetadata.GetWidth(),
|
||||
mImageMetadata.GetHeight(),
|
||||
mImageMetadata.GetOrientation());
|
||||
if (NS_FAILED(rv)) {
|
||||
PostResizeError();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -12,13 +12,14 @@
|
||||
#include "DecodePool.h"
|
||||
#include "ImageMetadata.h"
|
||||
#include "Orientation.h"
|
||||
#include "SourceBuffer.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace image {
|
||||
|
||||
class Decoder
|
||||
class Decoder : public IResumable
|
||||
{
|
||||
public:
|
||||
|
||||
@ -26,8 +27,6 @@ public:
|
||||
|
||||
/**
|
||||
* Initialize an image decoder. Decoders may not be re-initialized.
|
||||
*
|
||||
* Notifications Sent: TODO
|
||||
*/
|
||||
void Init();
|
||||
|
||||
@ -42,28 +41,30 @@ public:
|
||||
RawAccessFrameRef&& aFrameRef);
|
||||
|
||||
/**
|
||||
* Writes data to the decoder.
|
||||
*
|
||||
* Decodes, reading all data currently available in the SourceBuffer. If more
|
||||
* If aBuffer is null and aCount is 0, Write() flushes any buffered data to
|
||||
* the decoder. Data is buffered if the decoder wasn't able to completely
|
||||
* decode it because it needed a new frame. If it's necessary to flush data,
|
||||
* NeedsToFlushData() will return true.
|
||||
*
|
||||
* @param aBuffer buffer containing the data to be written
|
||||
* @param aCount the number of bytes to write
|
||||
* data is needed, Decode() automatically ensures that it will be called again
|
||||
* on a DecodePool thread when the data becomes available.
|
||||
*
|
||||
* Any errors are reported by setting the appropriate state on the decoder.
|
||||
*
|
||||
* Notifications Sent: TODO
|
||||
*/
|
||||
void Write(const char* aBuffer, uint32_t aCount);
|
||||
nsresult Decode();
|
||||
|
||||
/**
|
||||
* Informs the decoder that all the data has been written.
|
||||
*
|
||||
* Notifications Sent: TODO
|
||||
* Cleans up the decoder's state and notifies our image about success or
|
||||
* failure. May only be called on the main thread.
|
||||
*/
|
||||
void Finish(ShutdownReason aReason);
|
||||
void Finish();
|
||||
|
||||
/**
|
||||
* Given a maximum number of bytes we're willing to decode, @aByteLimit,
|
||||
* returns true if we should attempt to run this decoder synchronously.
|
||||
*/
|
||||
bool ShouldSyncDecode(size_t aByteLimit);
|
||||
|
||||
/**
|
||||
* Informs the shared decoder that all the data has been written.
|
||||
@ -98,9 +99,20 @@ public:
|
||||
return progress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there's any progress to report.
|
||||
*/
|
||||
bool HasProgress() const
|
||||
{
|
||||
return mProgress != NoProgress || !mInvalidRect.IsEmpty();
|
||||
}
|
||||
|
||||
// We're not COM-y, so we don't get refcounts by default
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
|
||||
|
||||
// Implement IResumable.
|
||||
virtual void Resume() MOZ_OVERRIDE;
|
||||
|
||||
/*
|
||||
* State.
|
||||
*/
|
||||
@ -111,7 +123,7 @@ public:
|
||||
bool IsSizeDecode() { return mSizeDecode; }
|
||||
void SetSizeDecode(bool aSizeDecode)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mInitialized, "Can't set size decode after Init()!");
|
||||
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
|
||||
mSizeDecode = aSizeDecode;
|
||||
}
|
||||
|
||||
@ -133,6 +145,32 @@ public:
|
||||
mSendPartialInvalidations = aSend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an iterator to the SourceBuffer which will feed data to this decoder.
|
||||
*
|
||||
* This should be called for almost all decoders; the exceptions are the
|
||||
* contained decoders of an nsICODecoder, which will be fed manually via Write
|
||||
* instead.
|
||||
*
|
||||
* This must be called before Init() is called.
|
||||
*/
|
||||
void SetIterator(SourceBufferIterator&& aIterator)
|
||||
{
|
||||
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
|
||||
mIterator.emplace(Move(aIterator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this decoder is associated with a transient image. The decoder
|
||||
* may choose to avoid certain optimizations that don't pay off for
|
||||
* short-lived images in this case.
|
||||
*/
|
||||
void SetImageIsTransient(bool aIsTransient)
|
||||
{
|
||||
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
|
||||
mImageIsTransient = aIsTransient;
|
||||
}
|
||||
|
||||
size_t BytesDecoded() const { return mBytesDecoded; }
|
||||
|
||||
// The amount of time we've spent inside Write() so far for this decoder.
|
||||
@ -149,15 +187,28 @@ public:
|
||||
uint32_t GetCompleteFrameCount() { return mInFrame ? mFrameCount - 1 : mFrameCount; }
|
||||
|
||||
// Error tracking
|
||||
bool HasError() { return HasDataError() || HasDecoderError(); }
|
||||
bool HasDataError() { return mDataError; }
|
||||
bool HasDecoderError() { return NS_FAILED(mFailCode); }
|
||||
nsresult GetDecoderError() { return mFailCode; }
|
||||
bool HasError() const { return HasDataError() || HasDecoderError(); }
|
||||
bool HasDataError() const { return mDataError; }
|
||||
bool HasDecoderError() const { return NS_FAILED(mFailCode); }
|
||||
nsresult GetDecoderError() const { return mFailCode; }
|
||||
void PostResizeError() { PostDataError(); }
|
||||
bool GetDecodeDone() const {
|
||||
return mDecodeDone;
|
||||
|
||||
bool GetDecodeDone() const
|
||||
{
|
||||
return mDecodeDone || (mSizeDecode && HasSize()) || HasError() || mDataDone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this decoder was aborted.
|
||||
*
|
||||
* This may happen due to a low-memory condition, or because another decoder
|
||||
* was racing with this one to decode the same frames with the same flags and
|
||||
* this decoder lost the race. Either way, this is not a permanent situation
|
||||
* and does not constitute an error, so we don't report any errors when this
|
||||
* happens.
|
||||
*/
|
||||
bool WasAborted() const { return mDecodeAborted; }
|
||||
|
||||
// flags. Keep these in sync with imgIContainer.idl.
|
||||
// SetDecodeFlags must be called before Init(), otherwise
|
||||
// default flags are assumed.
|
||||
@ -313,11 +364,22 @@ protected:
|
||||
gfx::SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth,
|
||||
imgFrame* aPreviousFrame);
|
||||
/**
|
||||
* Writes data to the decoder.
|
||||
*
|
||||
* @param aBuffer buffer containing the data to be written
|
||||
* @param aCount the number of bytes to write
|
||||
*
|
||||
* Any errors are reported by setting the appropriate state on the decoder.
|
||||
*/
|
||||
void Write(const char* aBuffer, uint32_t aCount);
|
||||
|
||||
/*
|
||||
* Member variables.
|
||||
*
|
||||
*/
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
Maybe<SourceBufferIterator> mIterator;
|
||||
RawAccessFrameRef mCurrentFrame;
|
||||
ImageMetadata mImageMetadata;
|
||||
nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
|
||||
@ -335,8 +397,11 @@ protected:
|
||||
uint32_t mDecodeFlags;
|
||||
size_t mBytesDecoded;
|
||||
bool mSendPartialInvalidations;
|
||||
bool mDataDone;
|
||||
bool mDecodeDone;
|
||||
bool mDataError;
|
||||
bool mDecodeAborted;
|
||||
bool mImageIsTransient;
|
||||
|
||||
private:
|
||||
uint32_t mFrameCount; // Number of frames, including anything in-progress
|
||||
|
@ -290,6 +290,11 @@ int32_t
|
||||
FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
|
||||
{
|
||||
RawAccessFrameRef frame = GetRawFrame(aFrameNum);
|
||||
if (!frame) {
|
||||
NS_WARNING("No frame; called GetTimeoutForFrame too early?");
|
||||
return 100;
|
||||
}
|
||||
|
||||
AnimationData data = frame->GetAnimationData();
|
||||
|
||||
// Ensure a minimal time between updates so we don't throttle the UI thread.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,6 @@
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/TypedEnum.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
@ -132,6 +131,13 @@ namespace image {
|
||||
|
||||
class Decoder;
|
||||
class FrameAnimator;
|
||||
class SourceBuffer;
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(DecodeStrategy, uint8_t)
|
||||
ASYNC,
|
||||
SYNC_FOR_SMALL_IMAGES,
|
||||
SYNC_IF_POSSIBLE
|
||||
MOZ_END_ENUM_CLASS(DecodeStrategy)
|
||||
|
||||
class RasterImage MOZ_FINAL : public ImageResource
|
||||
, public nsIProperties
|
||||
@ -162,10 +168,10 @@ public:
|
||||
virtual void OnSurfaceDiscarded() MOZ_OVERRIDE;
|
||||
|
||||
// Raster-specific methods
|
||||
static NS_METHOD WriteToRasterImage(nsIInputStream* aIn, void* aClosure,
|
||||
const char* aFromRawSegment,
|
||||
uint32_t aToOffset, uint32_t aCount,
|
||||
uint32_t* aWriteCount);
|
||||
static NS_METHOD WriteToSourceBuffer(nsIInputStream* aIn, void* aClosure,
|
||||
const char* aFromRawSegment,
|
||||
uint32_t aToOffset, uint32_t aCount,
|
||||
uint32_t* aWriteCount);
|
||||
|
||||
/* The total number of frames in this image. */
|
||||
uint32_t GetNumFrames() const { return mFrameCount; }
|
||||
@ -196,24 +202,36 @@ public:
|
||||
*/
|
||||
void SetLoopCount(int32_t aLoopCount);
|
||||
|
||||
/* notification that the entire image has been decoded */
|
||||
void DecodingComplete(imgFrame* aFinalFrame);
|
||||
/// Notification that the entire image has been decoded.
|
||||
void OnDecodingComplete();
|
||||
|
||||
/**
|
||||
* Sends the provided progress notifications to ProgressTracker.
|
||||
*
|
||||
* Main-thread only.
|
||||
*
|
||||
* @param aProgress The progress notifications to send.
|
||||
* @param aInvalidRect An invalidation rect to send.
|
||||
* @param aFlags The decode flags used by the decoder that generated
|
||||
* these notifications, or DECODE_FLAGS_DEFAULT if the
|
||||
* notifications don't come from a decoder.
|
||||
*/
|
||||
void NotifyProgress(Progress aProgress,
|
||||
const nsIntRect& aInvalidRect = nsIntRect(),
|
||||
uint32_t aFlags = 0);
|
||||
|
||||
/**
|
||||
* Records telemetry and does final teardown of the provided decoder.
|
||||
*
|
||||
* Main-thread only.
|
||||
*/
|
||||
void FinalizeDecoder(Decoder* aDecoder);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Network callbacks.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* Add compressed source data to the imgContainer.
|
||||
*
|
||||
* The decoder will use this data, either immediately or at draw time, to
|
||||
* decode the image.
|
||||
*
|
||||
* XXX This method's only caller (WriteToContainer) ignores the return
|
||||
* value. Should this just return void?
|
||||
*/
|
||||
nsresult AddSourceData(const char *aBuffer, uint32_t aCount);
|
||||
|
||||
virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsIInputStream* aInStr,
|
||||
@ -237,7 +255,7 @@ public:
|
||||
* Thus, pre-allocation simplifies code and reduces the total number of
|
||||
* allocations.
|
||||
*/
|
||||
nsresult SetSourceSizeHint(uint32_t sizeHint);
|
||||
nsresult SetSourceSizeHint(uint32_t aSizeHint);
|
||||
|
||||
/* Provide a hint for the requested resolution of the resulting image. */
|
||||
void SetRequestedResolution(const nsIntSize requestedResolution) {
|
||||
@ -267,14 +285,6 @@ public:
|
||||
static void Initialize();
|
||||
|
||||
private:
|
||||
friend class DecodePool;
|
||||
friend class DecodeWorker;
|
||||
friend class FrameNeededWorker;
|
||||
friend class NotifyProgressWorker;
|
||||
|
||||
nsresult FinishedSomeDecoding(ShutdownReason aReason = ShutdownReason::DONE,
|
||||
Progress aProgress = NoProgress);
|
||||
|
||||
void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
||||
gfxContext* aContext,
|
||||
const nsIntSize& aSize,
|
||||
@ -304,44 +314,34 @@ private:
|
||||
size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
|
||||
MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
nsresult DoImageDataComplete();
|
||||
|
||||
already_AddRefed<layers::Image> GetCurrentImage();
|
||||
void UpdateImageContainer();
|
||||
|
||||
enum RequestDecodeType {
|
||||
ASYNCHRONOUS,
|
||||
SYNCHRONOUS_NOTIFY,
|
||||
SYNCHRONOUS_NOTIFY_AND_SOME_DECODE
|
||||
};
|
||||
NS_IMETHOD RequestDecodeCore(RequestDecodeType aDecodeType);
|
||||
|
||||
// We would like to just check if we have a zero lock count, but we can't do
|
||||
// that for animated images because in EnsureAnimExists we lock the image and
|
||||
// never unlock so that animated images always have their lock count >= 1. In
|
||||
// that case we use our animation consumers count as a proxy for lock count.
|
||||
bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); }
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Decoding.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
already_AddRefed<Decoder> CreateDecoder(bool aDoSizeDecode, uint32_t aFlags);
|
||||
|
||||
void WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
|
||||
|
||||
NS_IMETHOD Decode(DecodeStrategy aStrategy, uint32_t aFlags,
|
||||
bool aDoSizeDecode = false);
|
||||
|
||||
private: // data
|
||||
nsIntSize mSize;
|
||||
Orientation mOrientation;
|
||||
|
||||
// Whether our frames were decoded using any special flags.
|
||||
// Some flags (e.g. unpremultiplied data) may not be compatible
|
||||
// with the browser's needs for displaying the image to the user.
|
||||
// As such, we may need to redecode if we're being asked for
|
||||
// a frame with different flags. 0 indicates default flags.
|
||||
//
|
||||
// Valid flag bits are imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
|
||||
// and imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION.
|
||||
uint32_t mFrameDecodeFlags;
|
||||
|
||||
nsCOMPtr<nsIProperties> mProperties;
|
||||
|
||||
//! All the frames of the image.
|
||||
// IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
|
||||
// that the frames actually exist (they may have been discarded to save memory, or
|
||||
// we maybe decoding on draw).
|
||||
/// If this image is animated, a FrameAnimator which manages its animation.
|
||||
UniquePtr<FrameAnimator> mAnim;
|
||||
|
||||
// Image locking.
|
||||
@ -370,18 +370,8 @@ private: // data
|
||||
uint32_t mFramesNotified;
|
||||
#endif
|
||||
|
||||
// Below are the pieces of data that can be accessed on more than one thread
|
||||
// at once, and hence need to be locked by mDecodingMonitor.
|
||||
|
||||
// BEGIN LOCKED MEMBER VARIABLES
|
||||
ReentrantMonitor mDecodingMonitor;
|
||||
|
||||
FallibleTArray<char> mSourceData;
|
||||
|
||||
// Decoder and friends
|
||||
nsRefPtr<Decoder> mDecoder;
|
||||
DecodeStatus mDecodeStatus;
|
||||
// END LOCKED MEMBER VARIABLES
|
||||
// The source data for this image.
|
||||
nsRefPtr<SourceBuffer> mSourceBuffer;
|
||||
|
||||
// The number of frames this image has.
|
||||
uint32_t mFrameCount;
|
||||
@ -397,10 +387,7 @@ private: // data
|
||||
bool mTransient:1; // Is the image short-lived?
|
||||
bool mDiscardable:1; // Is container discardable?
|
||||
bool mHasSourceData:1; // Do we have source data?
|
||||
|
||||
// Do we have the frames in decoded form?
|
||||
bool mDecoded:1;
|
||||
bool mHasBeenDecoded:1;
|
||||
bool mHasBeenDecoded:1; // Decoded at least once?
|
||||
|
||||
// Whether we're waiting to start animation. If we get a StartAnimation() call
|
||||
// but we don't yet have more than one frame, mPendingAnimation is set so that
|
||||
@ -415,27 +402,11 @@ private: // data
|
||||
// off a full decode.
|
||||
bool mWantFullDecode:1;
|
||||
|
||||
// Set when a decode worker detects an error off-main-thread. Once the error
|
||||
// is handled on the main thread, mError is set, but mPendingError is used to
|
||||
// stop decode work immediately.
|
||||
bool mPendingError:1;
|
||||
|
||||
// Decoding
|
||||
nsresult RequestDecodeIfNeeded(nsresult aStatus, ShutdownReason aReason,
|
||||
bool aDone, bool aWasSize);
|
||||
nsresult WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
|
||||
nsresult SyncDecode();
|
||||
nsresult InitDecoder(bool aDoSizeDecode);
|
||||
nsresult WriteToDecoder(const char *aBuffer, uint32_t aCount);
|
||||
nsresult DecodeSomeData(size_t aMaxBytes);
|
||||
bool IsDecodeFinished();
|
||||
TimeStamp mDrawStartTime;
|
||||
|
||||
// Initializes ProgressTracker and resets it on RasterImage destruction.
|
||||
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
|
||||
|
||||
nsresult ShutdownDecoder(ShutdownReason aReason);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Scaling.
|
||||
@ -476,7 +447,6 @@ private: // data
|
||||
|
||||
// Helpers
|
||||
bool CanDiscard();
|
||||
bool StoringSourceData() const;
|
||||
|
||||
protected:
|
||||
explicit RasterImage(ProgressTracker* aProgressTracker = nullptr,
|
||||
|
@ -3785,9 +3785,6 @@ pref("image.mem.allow_locking_in_content_processes", true);
|
||||
// Chunk size for calls to the image decoders
|
||||
pref("image.mem.decode_bytes_at_a_time", 16384);
|
||||
|
||||
// The longest time we can spend in an iteration of an async decode
|
||||
pref("image.mem.max_ms_before_yield", 5);
|
||||
|
||||
// Minimum timeout for expiring unused images from the surface cache, in
|
||||
// milliseconds. This controls how long we store cached temporary surfaces.
|
||||
pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60ms
|
||||
|
Loading…
Reference in New Issue
Block a user