1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/RasterImage.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3658 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Must #include ImageLogging.h before any IPDL-generated files or other files that #include prlog.h 1.10 +#include "ImageLogging.h" 1.11 + 1.12 +#include "RasterImage.h" 1.13 + 1.14 +#include "base/histogram.h" 1.15 +#include "gfxPlatform.h" 1.16 +#include "nsComponentManagerUtils.h" 1.17 +#include "imgDecoderObserver.h" 1.18 +#include "nsError.h" 1.19 +#include "Decoder.h" 1.20 +#include "nsAutoPtr.h" 1.21 +#include "prenv.h" 1.22 +#include "prsystem.h" 1.23 +#include "ImageContainer.h" 1.24 +#include "Layers.h" 1.25 +#include "nsPresContext.h" 1.26 +#include "nsIThreadPool.h" 1.27 +#include "nsXPCOMCIDInternal.h" 1.28 +#include "nsIObserverService.h" 1.29 +#include "FrameAnimator.h" 1.30 + 1.31 +#include "nsPNGDecoder.h" 1.32 +#include "nsGIFDecoder2.h" 1.33 +#include "nsJPEGDecoder.h" 1.34 +#include "nsBMPDecoder.h" 1.35 +#include "nsICODecoder.h" 1.36 +#include "nsIconDecoder.h" 1.37 + 1.38 +#include "gfxContext.h" 1.39 + 1.40 +#include "mozilla/gfx/2D.h" 1.41 +#include "mozilla/RefPtr.h" 1.42 +#include "mozilla/MemoryReporting.h" 1.43 +#include "mozilla/Services.h" 1.44 +#include "mozilla/Preferences.h" 1.45 +#include <stdint.h> 1.46 +#include "mozilla/Telemetry.h" 1.47 +#include "mozilla/TimeStamp.h" 1.48 +#include "mozilla/ClearOnShutdown.h" 1.49 +#include "mozilla/gfx/Scale.h" 1.50 + 1.51 +#include "GeckoProfiler.h" 1.52 +#include "gfx2DGlue.h" 1.53 +#include <algorithm> 1.54 + 1.55 +#ifdef MOZ_NUWA_PROCESS 1.56 +#include "ipc/Nuwa.h" 1.57 +#endif 1.58 + 1.59 +using namespace mozilla; 1.60 +using namespace mozilla::gfx; 1.61 +using namespace mozilla::image; 1.62 +using namespace mozilla::layers; 1.63 + 1.64 +// a mask for flags that will affect the decoding 1.65 +#define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION) 1.66 +#define DECODE_FLAGS_DEFAULT 0 1.67 + 1.68 +/* Accounting for compressed data */ 1.69 +#if defined(PR_LOGGING) 1.70 +static PRLogModuleInfo * 1.71 +GetCompressedImageAccountingLog() 1.72 +{ 1.73 + static PRLogModuleInfo *sLog; 1.74 + if (!sLog) 1.75 + sLog = PR_NewLogModule("CompressedImageAccounting"); 1.76 + return sLog; 1.77 +} 1.78 +#else 1.79 +#define GetCompressedImageAccountingLog() 1.80 +#endif 1.81 + 1.82 +// Tweakable progressive decoding parameters. These are initialized to 0 here 1.83 +// because otherwise, we have to initialize them in a static initializer, which 1.84 +// makes us slower to start up. 1.85 +static uint32_t gDecodeBytesAtATime = 0; 1.86 +static uint32_t gMaxMSBeforeYield = 0; 1.87 +static bool gHQDownscaling = false; 1.88 +// This is interpreted as a floating-point value / 1000 1.89 +static uint32_t gHQDownscalingMinFactor = 1000; 1.90 +static bool gMultithreadedDecoding = true; 1.91 +static int32_t gDecodingThreadLimit = -1; 1.92 +// The number of pixels in a 5 megapixel decoded image. 1.93 +// Equivalent to an example 3125x1600 resolution. 1.94 +static uint32_t gHQUpscalingMaxSize = 20971520; 1.95 + 1.96 +// The maximum number of times any one RasterImage was decoded. This is only 1.97 +// used for statistics. 1.98 +static int32_t sMaxDecodeCount = 0; 1.99 + 1.100 +static void 1.101 +InitPrefCaches() 1.102 +{ 1.103 + Preferences::AddUintVarCache(&gDecodeBytesAtATime, 1.104 + "image.mem.decode_bytes_at_a_time", 200000); 1.105 + Preferences::AddUintVarCache(&gMaxMSBeforeYield, 1.106 + "image.mem.max_ms_before_yield", 400); 1.107 + Preferences::AddBoolVarCache(&gHQDownscaling, 1.108 + "image.high_quality_downscaling.enabled", false); 1.109 + Preferences::AddUintVarCache(&gHQDownscalingMinFactor, 1.110 + "image.high_quality_downscaling.min_factor", 1000); 1.111 + Preferences::AddBoolVarCache(&gMultithreadedDecoding, 1.112 + "image.multithreaded_decoding.enabled", true); 1.113 + Preferences::AddIntVarCache(&gDecodingThreadLimit, 1.114 + "image.multithreaded_decoding.limit", -1); 1.115 + Preferences::AddUintVarCache(&gHQUpscalingMaxSize, 1.116 + "image.high_quality_upscaling.max_size", 20971520); 1.117 +} 1.118 + 1.119 +/* We define our own error checking macros here for 2 reasons: 1.120 + * 1.121 + * 1) Most of the failures we encounter here will (hopefully) be 1.122 + * the result of decoding failures (ie, bad data) and not code 1.123 + * failures. As such, we don't want to clutter up debug consoles 1.124 + * with spurious messages about NS_ENSURE_SUCCESS failures. 1.125 + * 1.126 + * 2) We want to set the internal error flag, shutdown properly, 1.127 + * and end up in an error state. 1.128 + * 1.129 + * So this macro should be called when the desired failure behavior 1.130 + * is to put the container into an error state and return failure. 1.131 + * It goes without saying that macro won't compile outside of a 1.132 + * non-static RasterImage method. 1.133 + */ 1.134 +#define LOG_CONTAINER_ERROR \ 1.135 + PR_BEGIN_MACRO \ 1.136 + PR_LOG (GetImgLog(), PR_LOG_ERROR, \ 1.137 + ("RasterImage: [this=%p] Error " \ 1.138 + "detected at line %u for image of " \ 1.139 + "type %s\n", this, __LINE__, \ 1.140 + mSourceDataMimeType.get())); \ 1.141 + PR_END_MACRO 1.142 + 1.143 +#define CONTAINER_ENSURE_SUCCESS(status) \ 1.144 + PR_BEGIN_MACRO \ 1.145 + nsresult _status = status; /* eval once */ \ 1.146 + if (NS_FAILED(_status)) { \ 1.147 + LOG_CONTAINER_ERROR; \ 1.148 + DoError(); \ 1.149 + return _status; \ 1.150 + } \ 1.151 + PR_END_MACRO 1.152 + 1.153 +#define CONTAINER_ENSURE_TRUE(arg, rv) \ 1.154 + PR_BEGIN_MACRO \ 1.155 + if (!(arg)) { \ 1.156 + LOG_CONTAINER_ERROR; \ 1.157 + DoError(); \ 1.158 + return rv; \ 1.159 + } \ 1.160 + PR_END_MACRO 1.161 + 1.162 + 1.163 + 1.164 +static int num_containers; 1.165 +static int num_discardable_containers; 1.166 +static int64_t total_source_bytes; 1.167 +static int64_t discardable_source_bytes; 1.168 + 1.169 +/* Are we globally disabling image discarding? */ 1.170 +static bool 1.171 +DiscardingEnabled() 1.172 +{ 1.173 + static bool inited; 1.174 + static bool enabled; 1.175 + 1.176 + if (!inited) { 1.177 + inited = true; 1.178 + 1.179 + enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nullptr); 1.180 + } 1.181 + 1.182 + return enabled; 1.183 +} 1.184 + 1.185 +class ScaleRequest 1.186 +{ 1.187 +public: 1.188 + ScaleRequest(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame) 1.189 + : scale(aScale) 1.190 + , dstLocked(false) 1.191 + , done(false) 1.192 + , stopped(false) 1.193 + { 1.194 + MOZ_ASSERT(!aSrcFrame->GetIsPaletted()); 1.195 + MOZ_ASSERT(aScale.width > 0 && aScale.height > 0); 1.196 + 1.197 + weakImage = aImage->asWeakPtr(); 1.198 + srcRect = aSrcFrame->GetRect(); 1.199 + 1.200 + nsIntRect dstRect = srcRect; 1.201 + dstRect.ScaleRoundOut(scale.width, scale.height); 1.202 + dstSize = dstRect.Size(); 1.203 + } 1.204 + 1.205 + // This can only be called on the main thread. 1.206 + bool GetSurfaces(imgFrame* srcFrame) 1.207 + { 1.208 + MOZ_ASSERT(NS_IsMainThread()); 1.209 + 1.210 + nsRefPtr<RasterImage> image = weakImage.get(); 1.211 + if (!image) { 1.212 + return false; 1.213 + } 1.214 + 1.215 + bool success = false; 1.216 + if (!dstLocked) { 1.217 + // We need to hold a lock onto the RasterImage object itself so that 1.218 + // it (and its associated imgFrames) aren't marked as discardable. 1.219 + bool imgLocked = NS_SUCCEEDED(image->LockImage()); 1.220 + bool srcLocked = NS_SUCCEEDED(srcFrame->LockImageData()); 1.221 + dstLocked = NS_SUCCEEDED(dstFrame->LockImageData()); 1.222 + 1.223 + nsRefPtr<gfxASurface> dstASurf; 1.224 + nsRefPtr<gfxASurface> srcASurf; 1.225 + success = srcLocked && NS_SUCCEEDED(srcFrame->GetSurface(getter_AddRefs(srcASurf))); 1.226 + success = success && dstLocked && NS_SUCCEEDED(dstFrame->GetSurface(getter_AddRefs(dstASurf))); 1.227 + 1.228 + success = success && imgLocked && srcLocked && dstLocked && srcASurf && dstASurf; 1.229 + 1.230 + if (success) { 1.231 + srcSurface = srcASurf->GetAsImageSurface(); 1.232 + dstSurface = dstASurf->GetAsImageSurface(); 1.233 + srcData = srcSurface->Data(); 1.234 + dstData = dstSurface->Data(); 1.235 + srcStride = srcSurface->Stride(); 1.236 + dstStride = dstSurface->Stride(); 1.237 + srcFormat = mozilla::gfx::ImageFormatToSurfaceFormat(srcFrame->GetFormat()); 1.238 + } 1.239 + 1.240 + // We have references to the Thebes surfaces, so we don't need to leave 1.241 + // the source frame (that we don't own) locked. We'll unlock the 1.242 + // destination frame in ReleaseSurfaces(), below. 1.243 + if (srcLocked) { 1.244 + success = NS_SUCCEEDED(srcFrame->UnlockImageData()) && success; 1.245 + } 1.246 + 1.247 + success = success && srcSurface && dstSurface; 1.248 + } 1.249 + 1.250 + return success; 1.251 + } 1.252 + 1.253 + // This can only be called on the main thread. 1.254 + bool ReleaseSurfaces() 1.255 + { 1.256 + MOZ_ASSERT(NS_IsMainThread()); 1.257 + 1.258 + nsRefPtr<RasterImage> image = weakImage.get(); 1.259 + if (!image) { 1.260 + return false; 1.261 + } 1.262 + 1.263 + bool success = false; 1.264 + if (dstLocked) { 1.265 + if (DiscardingEnabled()) 1.266 + dstFrame->SetDiscardable(); 1.267 + success = NS_SUCCEEDED(dstFrame->UnlockImageData()); 1.268 + success = success && NS_SUCCEEDED(image->UnlockImage()); 1.269 + 1.270 + dstLocked = false; 1.271 + srcData = nullptr; 1.272 + dstData = nullptr; 1.273 + srcSurface = nullptr; 1.274 + dstSurface = nullptr; 1.275 + } 1.276 + return success; 1.277 + } 1.278 + 1.279 + // These values may only be touched on the main thread. 1.280 + WeakPtr<RasterImage> weakImage; 1.281 + nsAutoPtr<imgFrame> dstFrame; 1.282 + nsRefPtr<gfxImageSurface> srcSurface; 1.283 + nsRefPtr<gfxImageSurface> dstSurface; 1.284 + 1.285 + // Below are the values that may be touched on the scaling thread. 1.286 + gfxSize scale; 1.287 + uint8_t* srcData; 1.288 + uint8_t* dstData; 1.289 + nsIntRect srcRect; 1.290 + gfxIntSize dstSize; 1.291 + uint32_t srcStride; 1.292 + uint32_t dstStride; 1.293 + mozilla::gfx::SurfaceFormat srcFormat; 1.294 + bool dstLocked; 1.295 + bool done; 1.296 + // This boolean is accessed from both threads simultaneously without locking. 1.297 + // That's safe because stopping a ScaleRequest is strictly an optimization; 1.298 + // if we're not cache-coherent, at worst we'll do extra work. 1.299 + bool stopped; 1.300 +}; 1.301 + 1.302 +class DrawRunner : public nsRunnable 1.303 +{ 1.304 +public: 1.305 + DrawRunner(ScaleRequest* request) 1.306 + : mScaleRequest(request) 1.307 + {} 1.308 + 1.309 + NS_IMETHOD Run() 1.310 + { 1.311 + // ScaleWorker is finished with this request, so we can unlock the data now. 1.312 + mScaleRequest->ReleaseSurfaces(); 1.313 + 1.314 + nsRefPtr<RasterImage> image = mScaleRequest->weakImage.get(); 1.315 + 1.316 + if (image) { 1.317 + RasterImage::ScaleStatus status; 1.318 + if (mScaleRequest->done) { 1.319 + status = RasterImage::SCALE_DONE; 1.320 + } else { 1.321 + status = RasterImage::SCALE_INVALID; 1.322 + } 1.323 + 1.324 + image->ScalingDone(mScaleRequest, status); 1.325 + } 1.326 + 1.327 + return NS_OK; 1.328 + } 1.329 + 1.330 +private: /* members */ 1.331 + nsAutoPtr<ScaleRequest> mScaleRequest; 1.332 +}; 1.333 + 1.334 +class ScaleRunner : public nsRunnable 1.335 +{ 1.336 +public: 1.337 + ScaleRunner(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame) 1.338 + { 1.339 + nsAutoPtr<ScaleRequest> request(new ScaleRequest(aImage, aScale, aSrcFrame)); 1.340 + 1.341 + // Destination is unconditionally ARGB32 because that's what the scaler 1.342 + // outputs. 1.343 + request->dstFrame = new imgFrame(); 1.344 + nsresult rv = request->dstFrame->Init(0, 0, request->dstSize.width, request->dstSize.height, 1.345 + gfxImageFormat::ARGB32); 1.346 + 1.347 + if (NS_FAILED(rv) || !request->GetSurfaces(aSrcFrame)) { 1.348 + return; 1.349 + } 1.350 + 1.351 + aImage->ScalingStart(request); 1.352 + 1.353 + mScaleRequest = request; 1.354 + } 1.355 + 1.356 + NS_IMETHOD Run() 1.357 + { 1.358 + // An alias just for ease of typing 1.359 + ScaleRequest* request = mScaleRequest; 1.360 + 1.361 + if (!request->stopped) { 1.362 + request->done = mozilla::gfx::Scale(request->srcData, request->srcRect.width, request->srcRect.height, request->srcStride, 1.363 + request->dstData, request->dstSize.width, request->dstSize.height, request->dstStride, 1.364 + request->srcFormat); 1.365 + } else { 1.366 + request->done = false; 1.367 + } 1.368 + 1.369 + // OK, we've got a new scaled image. Let's get the main thread to unlock and 1.370 + // redraw it. 1.371 + nsRefPtr<DrawRunner> runner = new DrawRunner(mScaleRequest.forget()); 1.372 + NS_DispatchToMainThread(runner, NS_DISPATCH_NORMAL); 1.373 + 1.374 + return NS_OK; 1.375 + } 1.376 + 1.377 + bool IsOK() const { return !!mScaleRequest; } 1.378 + 1.379 +private: 1.380 + nsAutoPtr<ScaleRequest> mScaleRequest; 1.381 +}; 1.382 + 1.383 +namespace mozilla { 1.384 +namespace image { 1.385 + 1.386 +/* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton; 1.387 +static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr; 1.388 + 1.389 +#ifndef DEBUG 1.390 +NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties) 1.391 +#else 1.392 +NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties, 1.393 + imgIContainerDebug) 1.394 +#endif 1.395 + 1.396 +//****************************************************************************** 1.397 +RasterImage::RasterImage(imgStatusTracker* aStatusTracker, 1.398 + ImageURL* aURI /* = nullptr */) : 1.399 + ImageResource(aURI), // invoke superclass's constructor 1.400 + mSize(0,0), 1.401 + mFrameDecodeFlags(DECODE_FLAGS_DEFAULT), 1.402 + mMultipartDecodedFrame(nullptr), 1.403 + mAnim(nullptr), 1.404 + mLockCount(0), 1.405 + mDecodeCount(0), 1.406 + mRequestedSampleSize(0), 1.407 +#ifdef DEBUG 1.408 + mFramesNotified(0), 1.409 +#endif 1.410 + mDecodingMonitor("RasterImage Decoding Monitor"), 1.411 + mDecoder(nullptr), 1.412 + mBytesDecoded(0), 1.413 + mInDecoder(false), 1.414 + mStatusDiff(ImageStatusDiff::NoChange()), 1.415 + mNotifying(false), 1.416 + mHasSize(false), 1.417 + mDecodeOnDraw(false), 1.418 + mMultipart(false), 1.419 + mDiscardable(false), 1.420 + mHasSourceData(false), 1.421 + mDecoded(false), 1.422 + mHasBeenDecoded(false), 1.423 + mAnimationFinished(false), 1.424 + mFinishing(false), 1.425 + mInUpdateImageContainer(false), 1.426 + mWantFullDecode(false), 1.427 + mPendingError(false), 1.428 + mScaleRequest(nullptr) 1.429 +{ 1.430 + mStatusTrackerInit = new imgStatusTrackerInit(this, aStatusTracker); 1.431 + 1.432 + // Set up the discard tracker node. 1.433 + mDiscardTrackerNode.img = this; 1.434 + Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0); 1.435 + 1.436 + // Statistics 1.437 + num_containers++; 1.438 +} 1.439 + 1.440 +//****************************************************************************** 1.441 +RasterImage::~RasterImage() 1.442 +{ 1.443 + // Discardable statistics 1.444 + if (mDiscardable) { 1.445 + num_discardable_containers--; 1.446 + discardable_source_bytes -= mSourceData.Length(); 1.447 + 1.448 + PR_LOG (GetCompressedImageAccountingLog(), PR_LOG_DEBUG, 1.449 + ("CompressedImageAccounting: destroying RasterImage %p. " 1.450 + "Total Containers: %d, Discardable containers: %d, " 1.451 + "Total source bytes: %lld, Source bytes for discardable containers %lld", 1.452 + this, 1.453 + num_containers, 1.454 + num_discardable_containers, 1.455 + total_source_bytes, 1.456 + discardable_source_bytes)); 1.457 + } 1.458 + 1.459 + if (mDecoder) { 1.460 + // Kill off our decode request, if it's pending. (If not, this call is 1.461 + // harmless.) 1.462 + ReentrantMonitorAutoEnter lock(mDecodingMonitor); 1.463 + DecodePool::StopDecoding(this); 1.464 + mDecoder = nullptr; 1.465 + 1.466 + // Unlock the last frame (if we have any). Our invariant is that, while we 1.467 + // have a decoder open, the last frame is always locked. 1.468 + // This would be done in ShutdownDecoder, but since mDecoder is non-null, 1.469 + // we didn't call ShutdownDecoder and we need to do it manually. 1.470 + if (GetNumFrames() > 0) { 1.471 + imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1); 1.472 + curframe->UnlockImageData(); 1.473 + } 1.474 + } 1.475 + 1.476 + delete mAnim; 1.477 + mAnim = nullptr; 1.478 + delete mMultipartDecodedFrame; 1.479 + 1.480 + // Total statistics 1.481 + num_containers--; 1.482 + total_source_bytes -= mSourceData.Length(); 1.483 + 1.484 + if (NS_IsMainThread()) { 1.485 + DiscardTracker::Remove(&mDiscardTrackerNode); 1.486 + } 1.487 +} 1.488 + 1.489 +/* static */ void 1.490 +RasterImage::Initialize() 1.491 +{ 1.492 + InitPrefCaches(); 1.493 + 1.494 + // Create our singletons now, so we don't have to worry about what thread 1.495 + // they're created on. 1.496 + DecodePool::Singleton(); 1.497 +} 1.498 + 1.499 +nsresult 1.500 +RasterImage::Init(const char* aMimeType, 1.501 + uint32_t aFlags) 1.502 +{ 1.503 + // We don't support re-initialization 1.504 + if (mInitialized) 1.505 + return NS_ERROR_ILLEGAL_VALUE; 1.506 + 1.507 + // Not sure an error can happen before init, but be safe 1.508 + if (mError) 1.509 + return NS_ERROR_FAILURE; 1.510 + 1.511 + NS_ENSURE_ARG_POINTER(aMimeType); 1.512 + 1.513 + // We must be non-discardable and non-decode-on-draw for 1.514 + // multipart channels 1.515 + NS_ABORT_IF_FALSE(!(aFlags & INIT_FLAG_MULTIPART) || 1.516 + (!(aFlags & INIT_FLAG_DISCARDABLE) && 1.517 + !(aFlags & INIT_FLAG_DECODE_ON_DRAW)), 1.518 + "Can't be discardable or decode-on-draw for multipart"); 1.519 + 1.520 + // Store initialization data 1.521 + mSourceDataMimeType.Assign(aMimeType); 1.522 + mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE); 1.523 + mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW); 1.524 + mMultipart = !!(aFlags & INIT_FLAG_MULTIPART); 1.525 + 1.526 + // Statistics 1.527 + if (mDiscardable) { 1.528 + num_discardable_containers++; 1.529 + discardable_source_bytes += mSourceData.Length(); 1.530 + } 1.531 + 1.532 + // Instantiate the decoder 1.533 + nsresult rv = InitDecoder(/* aDoSizeDecode = */ true); 1.534 + CONTAINER_ENSURE_SUCCESS(rv); 1.535 + 1.536 + // If we aren't storing source data, we want to switch from a size decode to 1.537 + // a full decode as soon as possible. 1.538 + if (!StoringSourceData()) { 1.539 + mWantFullDecode = true; 1.540 + } 1.541 + 1.542 + // Mark us as initialized 1.543 + mInitialized = true; 1.544 + 1.545 + return NS_OK; 1.546 +} 1.547 + 1.548 +//****************************************************************************** 1.549 +// [notxpcom] void requestRefresh ([const] in TimeStamp aTime); 1.550 +NS_IMETHODIMP_(void) 1.551 +RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime) 1.552 +{ 1.553 + EvaluateAnimation(); 1.554 + 1.555 + if (!mAnimating) { 1.556 + return; 1.557 + } 1.558 + 1.559 + FrameAnimator::RefreshResult res; 1.560 + if (mAnim) { 1.561 + res = mAnim->RequestRefresh(aTime); 1.562 + } 1.563 + 1.564 + if (res.frameAdvanced) { 1.565 + // Notify listeners that our frame has actually changed, but do this only 1.566 + // once for all frames that we've now passed (if AdvanceFrame() was called 1.567 + // more than once). 1.568 + #ifdef DEBUG 1.569 + mFramesNotified++; 1.570 + #endif 1.571 + 1.572 + UpdateImageContainer(); 1.573 + 1.574 + // Explicitly call this on mStatusTracker so we're sure to not interfere 1.575 + // with the decoding process 1.576 + if (mStatusTracker) 1.577 + mStatusTracker->FrameChanged(&res.dirtyRect); 1.578 + } 1.579 + 1.580 + if (res.animationFinished) { 1.581 + mAnimationFinished = true; 1.582 + EvaluateAnimation(); 1.583 + } 1.584 +} 1.585 + 1.586 +//****************************************************************************** 1.587 +/* readonly attribute int32_t width; */ 1.588 +NS_IMETHODIMP 1.589 +RasterImage::GetWidth(int32_t *aWidth) 1.590 +{ 1.591 + NS_ENSURE_ARG_POINTER(aWidth); 1.592 + 1.593 + if (mError) { 1.594 + *aWidth = 0; 1.595 + return NS_ERROR_FAILURE; 1.596 + } 1.597 + 1.598 + *aWidth = mSize.width; 1.599 + return NS_OK; 1.600 +} 1.601 + 1.602 +//****************************************************************************** 1.603 +/* readonly attribute int32_t height; */ 1.604 +NS_IMETHODIMP 1.605 +RasterImage::GetHeight(int32_t *aHeight) 1.606 +{ 1.607 + NS_ENSURE_ARG_POINTER(aHeight); 1.608 + 1.609 + if (mError) { 1.610 + *aHeight = 0; 1.611 + return NS_ERROR_FAILURE; 1.612 + } 1.613 + 1.614 + *aHeight = mSize.height; 1.615 + return NS_OK; 1.616 +} 1.617 + 1.618 +//****************************************************************************** 1.619 +/* [noscript] readonly attribute nsSize intrinsicSize; */ 1.620 +NS_IMETHODIMP 1.621 +RasterImage::GetIntrinsicSize(nsSize* aSize) 1.622 +{ 1.623 + if (mError) 1.624 + return NS_ERROR_FAILURE; 1.625 + 1.626 + *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width), 1.627 + nsPresContext::CSSPixelsToAppUnits(mSize.height)); 1.628 + return NS_OK; 1.629 +} 1.630 + 1.631 +//****************************************************************************** 1.632 +/* [noscript] readonly attribute nsSize intrinsicRatio; */ 1.633 +NS_IMETHODIMP 1.634 +RasterImage::GetIntrinsicRatio(nsSize* aRatio) 1.635 +{ 1.636 + if (mError) 1.637 + return NS_ERROR_FAILURE; 1.638 + 1.639 + *aRatio = nsSize(mSize.width, mSize.height); 1.640 + return NS_OK; 1.641 +} 1.642 + 1.643 +NS_IMETHODIMP_(Orientation) 1.644 +RasterImage::GetOrientation() 1.645 +{ 1.646 + return mOrientation; 1.647 +} 1.648 + 1.649 +//****************************************************************************** 1.650 +/* unsigned short GetType(); */ 1.651 +NS_IMETHODIMP 1.652 +RasterImage::GetType(uint16_t *aType) 1.653 +{ 1.654 + NS_ENSURE_ARG_POINTER(aType); 1.655 + 1.656 + *aType = GetType(); 1.657 + return NS_OK; 1.658 +} 1.659 + 1.660 +//****************************************************************************** 1.661 +/* [noscript, notxpcom] uint16_t GetType(); */ 1.662 +NS_IMETHODIMP_(uint16_t) 1.663 +RasterImage::GetType() 1.664 +{ 1.665 + return imgIContainer::TYPE_RASTER; 1.666 +} 1.667 + 1.668 +imgFrame* 1.669 +RasterImage::GetImgFrameNoDecode(uint32_t framenum) 1.670 +{ 1.671 + if (!mAnim) { 1.672 + NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!"); 1.673 + return mFrameBlender.GetFrame(0); 1.674 + } 1.675 + return mFrameBlender.GetFrame(framenum); 1.676 +} 1.677 + 1.678 +imgFrame* 1.679 +RasterImage::GetImgFrame(uint32_t framenum) 1.680 +{ 1.681 + nsresult rv = WantDecodedFrames(); 1.682 + CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr); 1.683 + return GetImgFrameNoDecode(framenum); 1.684 +} 1.685 + 1.686 +imgFrame* 1.687 +RasterImage::GetDrawableImgFrame(uint32_t framenum) 1.688 +{ 1.689 + imgFrame* frame = nullptr; 1.690 + 1.691 + if (mMultipart && framenum == GetCurrentImgFrameIndex()) { 1.692 + // In the multipart case we prefer to use mMultipartDecodedFrame, which is 1.693 + // the most recent one we completely decoded, rather than display the real 1.694 + // current frame and risk severe tearing. 1.695 + frame = mMultipartDecodedFrame; 1.696 + } 1.697 + 1.698 + if (!frame) { 1.699 + frame = GetImgFrame(framenum); 1.700 + } 1.701 + 1.702 + // We will return a paletted frame if it's not marked as compositing failed 1.703 + // so we can catch crashes for reasons we haven't investigated. 1.704 + if (frame && frame->GetCompositingFailed()) 1.705 + return nullptr; 1.706 + 1.707 + if (frame) { 1.708 + frame->ApplyDirtToSurfaces(); 1.709 + } 1.710 + 1.711 + return frame; 1.712 +} 1.713 + 1.714 +uint32_t 1.715 +RasterImage::GetCurrentImgFrameIndex() const 1.716 +{ 1.717 + if (mAnim) 1.718 + return mAnim->GetCurrentAnimationFrameIndex(); 1.719 + 1.720 + return 0; 1.721 +} 1.722 + 1.723 +imgFrame* 1.724 +RasterImage::GetCurrentImgFrame() 1.725 +{ 1.726 + return GetImgFrame(GetCurrentImgFrameIndex()); 1.727 +} 1.728 + 1.729 +//****************************************************************************** 1.730 +/* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */ 1.731 +NS_IMETHODIMP_(bool) 1.732 +RasterImage::FrameIsOpaque(uint32_t aWhichFrame) 1.733 +{ 1.734 + if (aWhichFrame > FRAME_MAX_VALUE) { 1.735 + NS_WARNING("aWhichFrame outside valid range!"); 1.736 + return false; 1.737 + } 1.738 + 1.739 + if (mError) 1.740 + return false; 1.741 + 1.742 + // See if we can get an image frame. 1.743 + imgFrame* frame = aWhichFrame == FRAME_FIRST ? GetImgFrameNoDecode(0) 1.744 + : GetImgFrameNoDecode(GetCurrentImgFrameIndex()); 1.745 + 1.746 + // If we don't get a frame, the safe answer is "not opaque". 1.747 + if (!frame) 1.748 + return false; 1.749 + 1.750 + // Other, the frame is transparent if either: 1.751 + // 1. It needs a background. 1.752 + // 2. Its size doesn't cover our entire area. 1.753 + nsIntRect framerect = frame->GetRect(); 1.754 + return !frame->GetNeedsBackground() && 1.755 + framerect.IsEqualInterior(nsIntRect(0, 0, mSize.width, mSize.height)); 1.756 +} 1.757 + 1.758 +nsIntRect 1.759 +RasterImage::FrameRect(uint32_t aWhichFrame) 1.760 +{ 1.761 + if (aWhichFrame > FRAME_MAX_VALUE) { 1.762 + NS_WARNING("aWhichFrame outside valid range!"); 1.763 + return nsIntRect(); 1.764 + } 1.765 + 1.766 + // Get the requested frame. 1.767 + imgFrame* frame = aWhichFrame == FRAME_FIRST ? GetImgFrameNoDecode(0) 1.768 + : GetImgFrameNoDecode(GetCurrentImgFrameIndex()); 1.769 + 1.770 + // If we have the frame, use that rectangle. 1.771 + if (frame) { 1.772 + return frame->GetRect(); 1.773 + } 1.774 + 1.775 + // If the frame doesn't exist, we return the empty rectangle. It's not clear 1.776 + // whether this is appropriate in general, but at the moment the only 1.777 + // consumer of this method is imgStatusTracker (when it wants to figure out 1.778 + // dirty rectangles to send out batched observer updates). This should 1.779 + // probably be revisited when we fix bug 503973. 1.780 + return nsIntRect(); 1.781 +} 1.782 + 1.783 +uint32_t 1.784 +RasterImage::GetCurrentFrameIndex() 1.785 +{ 1.786 + return GetCurrentImgFrameIndex(); 1.787 +} 1.788 + 1.789 +uint32_t 1.790 +RasterImage::GetNumFrames() const 1.791 +{ 1.792 + return mFrameBlender.GetNumFrames(); 1.793 +} 1.794 + 1.795 +//****************************************************************************** 1.796 +/* readonly attribute boolean animated; */ 1.797 +NS_IMETHODIMP 1.798 +RasterImage::GetAnimated(bool *aAnimated) 1.799 +{ 1.800 + if (mError) 1.801 + return NS_ERROR_FAILURE; 1.802 + 1.803 + NS_ENSURE_ARG_POINTER(aAnimated); 1.804 + 1.805 + // If we have mAnim, we can know for sure 1.806 + if (mAnim) { 1.807 + *aAnimated = true; 1.808 + return NS_OK; 1.809 + } 1.810 + 1.811 + // Otherwise, we need to have been decoded to know for sure, since if we were 1.812 + // decoded at least once mAnim would have been created for animated images 1.813 + if (!mHasBeenDecoded) 1.814 + return NS_ERROR_NOT_AVAILABLE; 1.815 + 1.816 + // We know for sure 1.817 + *aAnimated = false; 1.818 + 1.819 + return NS_OK; 1.820 +} 1.821 + 1.822 +//****************************************************************************** 1.823 +/* [notxpcom] int32_t getFirstFrameDelay (); */ 1.824 +NS_IMETHODIMP_(int32_t) 1.825 +RasterImage::GetFirstFrameDelay() 1.826 +{ 1.827 + if (mError) 1.828 + return -1; 1.829 + 1.830 + bool animated = false; 1.831 + if (NS_FAILED(GetAnimated(&animated)) || !animated) 1.832 + return -1; 1.833 + 1.834 + return mFrameBlender.GetTimeoutForFrame(0); 1.835 +} 1.836 + 1.837 +nsresult 1.838 +RasterImage::CopyFrame(uint32_t aWhichFrame, 1.839 + uint32_t aFlags, 1.840 + gfxImageSurface **_retval) 1.841 +{ 1.842 + if (aWhichFrame > FRAME_MAX_VALUE) 1.843 + return NS_ERROR_INVALID_ARG; 1.844 + 1.845 + if (mError) 1.846 + return NS_ERROR_FAILURE; 1.847 + 1.848 + // Disallowed in the API 1.849 + if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE)) 1.850 + return NS_ERROR_FAILURE; 1.851 + 1.852 + nsresult rv; 1.853 + 1.854 + if (!ApplyDecodeFlags(aFlags, aWhichFrame)) 1.855 + return NS_ERROR_NOT_AVAILABLE; 1.856 + 1.857 + // If requested, synchronously flush any data we have lying around to the decoder 1.858 + if (aFlags & FLAG_SYNC_DECODE) { 1.859 + rv = SyncDecode(); 1.860 + CONTAINER_ENSURE_SUCCESS(rv); 1.861 + } 1.862 + 1.863 + NS_ENSURE_ARG_POINTER(_retval); 1.864 + 1.865 + // Get the frame. If it's not there, it's probably the caller's fault for 1.866 + // not waiting for the data to be loaded from the network or not passing 1.867 + // FLAG_SYNC_DECODE 1.868 + uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ? 1.869 + 0 : GetCurrentImgFrameIndex(); 1.870 + imgFrame *frame = GetDrawableImgFrame(frameIndex); 1.871 + if (!frame) { 1.872 + *_retval = nullptr; 1.873 + return NS_ERROR_FAILURE; 1.874 + } 1.875 + 1.876 + nsRefPtr<gfxPattern> pattern; 1.877 + frame->GetPattern(getter_AddRefs(pattern)); 1.878 + nsIntRect intframerect = frame->GetRect(); 1.879 + gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height); 1.880 + 1.881 + // Create a 32-bit image surface of our size, but draw using the frame's 1.882 + // rect, implicitly padding the frame out to the image's size. 1.883 + nsRefPtr<gfxImageSurface> imgsurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height), 1.884 + gfxImageFormat::ARGB32); 1.885 + gfxContext ctx(imgsurface); 1.886 + ctx.SetOperator(gfxContext::OPERATOR_SOURCE); 1.887 + ctx.Rectangle(framerect); 1.888 + ctx.Translate(framerect.TopLeft()); 1.889 + ctx.SetPattern(pattern); 1.890 + ctx.Fill(); 1.891 + 1.892 + imgsurface.forget(_retval); 1.893 + return NS_OK; 1.894 +} 1.895 + 1.896 +//****************************************************************************** 1.897 +/* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame, 1.898 + * in uint32_t aFlags); */ 1.899 +NS_IMETHODIMP_(TemporaryRef<SourceSurface>) 1.900 +RasterImage::GetFrame(uint32_t aWhichFrame, 1.901 + uint32_t aFlags) 1.902 +{ 1.903 + MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); 1.904 + 1.905 + if (aWhichFrame > FRAME_MAX_VALUE) 1.906 + return nullptr; 1.907 + 1.908 + if (mError) 1.909 + return nullptr; 1.910 + 1.911 + // Disallowed in the API 1.912 + if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE)) 1.913 + return nullptr; 1.914 + 1.915 + if (!ApplyDecodeFlags(aFlags, aWhichFrame)) 1.916 + return nullptr; 1.917 + 1.918 + // If the caller requested a synchronous decode, do it 1.919 + if (aFlags & FLAG_SYNC_DECODE) { 1.920 + nsresult rv = SyncDecode(); 1.921 + CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr); 1.922 + } 1.923 + 1.924 + // Get the frame. If it's not there, it's probably the caller's fault for 1.925 + // not waiting for the data to be loaded from the network or not passing 1.926 + // FLAG_SYNC_DECODE 1.927 + uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ? 1.928 + 0 : GetCurrentImgFrameIndex(); 1.929 + imgFrame *frame = GetDrawableImgFrame(frameIndex); 1.930 + if (!frame) { 1.931 + return nullptr; 1.932 + } 1.933 + 1.934 + nsRefPtr<gfxASurface> framesurf; 1.935 + 1.936 + // If this frame covers the entire image, we can just reuse its existing 1.937 + // surface. 1.938 + nsIntRect framerect = frame->GetRect(); 1.939 + if (framerect.x == 0 && framerect.y == 0 && 1.940 + framerect.width == mSize.width && 1.941 + framerect.height == mSize.height) { 1.942 + frame->GetSurface(getter_AddRefs(framesurf)); 1.943 + if (!framesurf && !frame->IsSinglePixel()) { 1.944 + // No reason to be optimized away here - the OS threw out the data 1.945 + if (!(aFlags & FLAG_SYNC_DECODE)) 1.946 + return nullptr; 1.947 + 1.948 + // Unconditionally call ForceDiscard() here because GetSurface can only 1.949 + // return null when we can forcibly discard and redecode. There are two 1.950 + // other cases where GetSurface() can return null - when it is a single 1.951 + // pixel image, which we check before getting here, or when this is an 1.952 + // indexed image, in which case we shouldn't be in this function at all. 1.953 + // The only remaining possibility is that SetDiscardable() was called on 1.954 + // this imgFrame, which implies the image can be redecoded. 1.955 + ForceDiscard(); 1.956 + return GetFrame(aWhichFrame, aFlags); 1.957 + } 1.958 + } 1.959 + 1.960 + // The image doesn't have a surface because it's been optimized away. Create 1.961 + // one. 1.962 + if (!framesurf) { 1.963 + nsRefPtr<gfxImageSurface> imgsurf; 1.964 + CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf)); 1.965 + framesurf = imgsurf; 1.966 + } 1.967 + 1.968 + RefPtr<SourceSurface> result; 1.969 + 1.970 + // As far as Moz2D is concerned, SourceSurface contains premultiplied alpha. 1.971 + // If we're abusing it to contain non-premultiplied alpha then we want to 1.972 + // avoid having Moz2D do any conversions on it (like copy to another 1.973 + // surface). Hence why we try to wrap framesurf's data here for 1.974 + // FLAG_DECODE_NO_PREMULTIPLY_ALPHA. 1.975 + if ((aFlags & FLAG_WANT_DATA_SURFACE) != 0 || 1.976 + (aFlags & FLAG_DECODE_NO_PREMULTIPLY_ALPHA) != 0) { 1.977 + result = gfxPlatform::GetPlatform()->GetWrappedDataSourceSurface(framesurf); 1.978 + } 1.979 + if (!result) { 1.980 + result = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, 1.981 + framesurf); 1.982 + } 1.983 + return result.forget(); 1.984 +} 1.985 + 1.986 +already_AddRefed<layers::Image> 1.987 +RasterImage::GetCurrentImage() 1.988 +{ 1.989 + if (!mDecoded) { 1.990 + // We can't call StartDecoding because that can synchronously notify 1.991 + // which can cause DOM modification 1.992 + RequestDecodeCore(ASYNCHRONOUS); 1.993 + return nullptr; 1.994 + } 1.995 + 1.996 + RefPtr<SourceSurface> surface = GetFrame(FRAME_CURRENT, FLAG_NONE); 1.997 + if (!surface) { 1.998 + // The OS threw out some or all of our buffer. Start decoding again. 1.999 + // GetFrame will only return null in the case that the image was 1.1000 + // discarded. We already checked that the image is decoded, so other 1.1001 + // error paths are not possible. 1.1002 + ForceDiscard(); 1.1003 + RequestDecodeCore(ASYNCHRONOUS); 1.1004 + return nullptr; 1.1005 + } 1.1006 + 1.1007 + if (!mImageContainer) { 1.1008 + mImageContainer = LayerManager::CreateImageContainer(); 1.1009 + } 1.1010 + 1.1011 + CairoImage::Data cairoData; 1.1012 + GetWidth(&cairoData.mSize.width); 1.1013 + GetHeight(&cairoData.mSize.height); 1.1014 + cairoData.mSourceSurface = surface; 1.1015 + 1.1016 + nsRefPtr<layers::Image> image = mImageContainer->CreateImage(ImageFormat::CAIRO_SURFACE); 1.1017 + NS_ASSERTION(image, "Failed to create Image"); 1.1018 + 1.1019 + static_cast<CairoImage*>(image.get())->SetData(cairoData); 1.1020 + 1.1021 + return image.forget(); 1.1022 +} 1.1023 + 1.1024 + 1.1025 +NS_IMETHODIMP 1.1026 +RasterImage::GetImageContainer(LayerManager* aManager, ImageContainer **_retval) 1.1027 +{ 1.1028 + int32_t maxTextureSize = aManager->GetMaxTextureSize(); 1.1029 + if (mSize.width > maxTextureSize || mSize.height > maxTextureSize) { 1.1030 + *_retval = nullptr; 1.1031 + return NS_OK; 1.1032 + } 1.1033 + 1.1034 + if (IsUnlocked() && mStatusTracker) { 1.1035 + mStatusTracker->OnUnlockedDraw(); 1.1036 + } 1.1037 + 1.1038 + if (!mImageContainer) { 1.1039 + mImageContainer = mImageContainerCache; 1.1040 + } 1.1041 + 1.1042 + if (mImageContainer) { 1.1043 + *_retval = mImageContainer; 1.1044 + NS_ADDREF(*_retval); 1.1045 + return NS_OK; 1.1046 + } 1.1047 + 1.1048 + nsRefPtr<layers::Image> image = GetCurrentImage(); 1.1049 + if (!image) { 1.1050 + return NS_ERROR_NOT_AVAILABLE; 1.1051 + } 1.1052 + mImageContainer->SetCurrentImageInTransaction(image); 1.1053 + 1.1054 + *_retval = mImageContainer; 1.1055 + NS_ADDREF(*_retval); 1.1056 + // We only need to be careful about holding on to the image when it is 1.1057 + // discardable by the OS. 1.1058 + if (CanForciblyDiscardAndRedecode()) { 1.1059 + mImageContainerCache = mImageContainer->asWeakPtr(); 1.1060 + mImageContainer = nullptr; 1.1061 + } 1.1062 + 1.1063 + return NS_OK; 1.1064 +} 1.1065 + 1.1066 +void 1.1067 +RasterImage::UpdateImageContainer() 1.1068 +{ 1.1069 + if (!mImageContainer || IsInUpdateImageContainer()) { 1.1070 + return; 1.1071 + } 1.1072 + 1.1073 + SetInUpdateImageContainer(true); 1.1074 + 1.1075 + nsRefPtr<layers::Image> image = GetCurrentImage(); 1.1076 + if (!image) { 1.1077 + return; 1.1078 + } 1.1079 + mImageContainer->SetCurrentImage(image); 1.1080 + SetInUpdateImageContainer(false); 1.1081 +} 1.1082 + 1.1083 +size_t 1.1084 +RasterImage::HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const 1.1085 +{ 1.1086 + // n == 0 is possible for two reasons. 1.1087 + // - This is a zero-length image. 1.1088 + // - We're on a platform where moz_malloc_size_of always returns 0. 1.1089 + // In either case the fallback works appropriately. 1.1090 + size_t n = mSourceData.SizeOfExcludingThis(aMallocSizeOf); 1.1091 + if (n == 0) { 1.1092 + n = mSourceData.Length(); 1.1093 + } 1.1094 + return n; 1.1095 +} 1.1096 + 1.1097 +size_t 1.1098 +RasterImage::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, 1.1099 + MallocSizeOf aMallocSizeOf) const 1.1100 +{ 1.1101 + size_t n = mFrameBlender.SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf); 1.1102 + 1.1103 + if (mScaleResult.status == SCALE_DONE) { 1.1104 + n += mScaleResult.frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf); 1.1105 + } 1.1106 + 1.1107 + return n; 1.1108 +} 1.1109 + 1.1110 +size_t 1.1111 +RasterImage::HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const 1.1112 +{ 1.1113 + return SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation::IN_PROCESS_HEAP, 1.1114 + aMallocSizeOf); 1.1115 +} 1.1116 + 1.1117 +size_t 1.1118 +RasterImage::NonHeapSizeOfDecoded() const 1.1119 +{ 1.1120 + return SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation::IN_PROCESS_NONHEAP, 1.1121 + nullptr); 1.1122 +} 1.1123 + 1.1124 +size_t 1.1125 +RasterImage::OutOfProcessSizeOfDecoded() const 1.1126 +{ 1.1127 + return SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation::OUT_OF_PROCESS, 1.1128 + nullptr); 1.1129 +} 1.1130 + 1.1131 +void 1.1132 +RasterImage::EnsureAnimExists() 1.1133 +{ 1.1134 + if (!mAnim) { 1.1135 + 1.1136 + // Create the animation context 1.1137 + mAnim = new FrameAnimator(mFrameBlender, mAnimationMode); 1.1138 + 1.1139 + // We don't support discarding animated images (See bug 414259). 1.1140 + // Lock the image and throw away the key. 1.1141 + // 1.1142 + // Note that this is inefficient, since we could get rid of the source 1.1143 + // data too. However, doing this is actually hard, because we're probably 1.1144 + // calling ensureAnimExists mid-decode, and thus we're decoding out of 1.1145 + // the source buffer. Since we're going to fix this anyway later, and 1.1146 + // since we didn't kill the source data in the old world either, locking 1.1147 + // is acceptable for the moment. 1.1148 + LockImage(); 1.1149 + 1.1150 + // Notify our observers that we are starting animation. 1.1151 + nsRefPtr<imgStatusTracker> statusTracker = CurrentStatusTracker(); 1.1152 + statusTracker->RecordImageIsAnimated(); 1.1153 + } 1.1154 +} 1.1155 + 1.1156 +nsresult 1.1157 +RasterImage::InternalAddFrameHelper(uint32_t framenum, imgFrame *aFrame, 1.1158 + uint8_t **imageData, uint32_t *imageLength, 1.1159 + uint32_t **paletteData, uint32_t *paletteLength, 1.1160 + imgFrame** aRetFrame) 1.1161 +{ 1.1162 + NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!"); 1.1163 + if (framenum > GetNumFrames()) 1.1164 + return NS_ERROR_INVALID_ARG; 1.1165 + 1.1166 + nsAutoPtr<imgFrame> frame(aFrame); 1.1167 + 1.1168 + // We are in the middle of decoding. This will be unlocked when we finish 1.1169 + // decoding or switch to another frame. 1.1170 + frame->LockImageData(); 1.1171 + 1.1172 + if (paletteData && paletteLength) 1.1173 + frame->GetPaletteData(paletteData, paletteLength); 1.1174 + 1.1175 + frame->GetImageData(imageData, imageLength); 1.1176 + 1.1177 + *aRetFrame = frame; 1.1178 + 1.1179 + mFrameBlender.InsertFrame(framenum, frame.forget()); 1.1180 + 1.1181 + return NS_OK; 1.1182 +} 1.1183 + 1.1184 +nsresult 1.1185 +RasterImage::InternalAddFrame(uint32_t framenum, 1.1186 + int32_t aX, int32_t aY, 1.1187 + int32_t aWidth, int32_t aHeight, 1.1188 + gfxImageFormat aFormat, 1.1189 + uint8_t aPaletteDepth, 1.1190 + uint8_t **imageData, 1.1191 + uint32_t *imageLength, 1.1192 + uint32_t **paletteData, 1.1193 + uint32_t *paletteLength, 1.1194 + imgFrame** aRetFrame) 1.1195 +{ 1.1196 + // We assume that we're in the middle of decoding because we unlock the 1.1197 + // previous frame when we create a new frame, and only when decoding do we 1.1198 + // lock frames. 1.1199 + NS_ABORT_IF_FALSE(mDecoder, "Only decoders may add frames!"); 1.1200 + 1.1201 + NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!"); 1.1202 + if (framenum > GetNumFrames()) 1.1203 + return NS_ERROR_INVALID_ARG; 1.1204 + 1.1205 + nsAutoPtr<imgFrame> frame(new imgFrame()); 1.1206 + 1.1207 + nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth); 1.1208 + if (!(mSize.width > 0 && mSize.height > 0)) 1.1209 + NS_WARNING("Shouldn't call InternalAddFrame with zero size"); 1.1210 + if (!NS_SUCCEEDED(rv)) 1.1211 + NS_WARNING("imgFrame::Init should succeed"); 1.1212 + NS_ENSURE_SUCCESS(rv, rv); 1.1213 + 1.1214 + // We know we are in a decoder. Therefore, we must unlock the previous frame 1.1215 + // when we move on to decoding into the next frame. 1.1216 + if (GetNumFrames() > 0) { 1.1217 + imgFrame *prevframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1); 1.1218 + prevframe->UnlockImageData(); 1.1219 + } 1.1220 + 1.1221 + if (GetNumFrames() == 0) { 1.1222 + return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength, 1.1223 + paletteData, paletteLength, aRetFrame); 1.1224 + } 1.1225 + 1.1226 + if (GetNumFrames() == 1) { 1.1227 + // Since we're about to add our second frame, initialize animation stuff 1.1228 + EnsureAnimExists(); 1.1229 + 1.1230 + // If we dispose of the first frame by clearing it, then the 1.1231 + // First Frame's refresh area is all of itself. 1.1232 + // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR) 1.1233 + int32_t frameDisposalMethod = mFrameBlender.RawGetFrame(0)->GetFrameDisposalMethod(); 1.1234 + if (frameDisposalMethod == FrameBlender::kDisposeClear || 1.1235 + frameDisposalMethod == FrameBlender::kDisposeRestorePrevious) 1.1236 + mAnim->SetFirstFrameRefreshArea(mFrameBlender.RawGetFrame(0)->GetRect()); 1.1237 + } 1.1238 + 1.1239 + // Calculate firstFrameRefreshArea 1.1240 + // Some gifs are huge but only have a small area that they animate 1.1241 + // We only need to refresh that small area when Frame 0 comes around again 1.1242 + mAnim->UnionFirstFrameRefreshArea(frame->GetRect()); 1.1243 + 1.1244 + rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength, 1.1245 + paletteData, paletteLength, aRetFrame); 1.1246 + 1.1247 + return rv; 1.1248 +} 1.1249 + 1.1250 +bool 1.1251 +RasterImage::ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame) 1.1252 +{ 1.1253 + if (mFrameDecodeFlags == (aNewFlags & DECODE_FLAGS_MASK)) 1.1254 + return true; // Not asking very much of us here. 1.1255 + 1.1256 + if (mDecoded) { 1.1257 + // If the requested frame is opaque and the current and new decode flags 1.1258 + // only differ in the premultiply alpha bit then we can use the existing 1.1259 + // frame, we don't need to discard and re-decode. 1.1260 + uint32_t currentNonAlphaFlags = 1.1261 + (mFrameDecodeFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA; 1.1262 + uint32_t newNonAlphaFlags = 1.1263 + (aNewFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA; 1.1264 + if (currentNonAlphaFlags == newNonAlphaFlags && FrameIsOpaque(aWhichFrame)) { 1.1265 + return true; 1.1266 + } 1.1267 + 1.1268 + // if we can't discard, then we're screwed; we have no way 1.1269 + // to re-decode. Similarly if we aren't allowed to do a sync 1.1270 + // decode. 1.1271 + if (!(aNewFlags & FLAG_SYNC_DECODE)) 1.1272 + return false; 1.1273 + if (!CanForciblyDiscardAndRedecode()) 1.1274 + return false; 1.1275 + ForceDiscard(); 1.1276 + } 1.1277 + 1.1278 + mFrameDecodeFlags = aNewFlags & DECODE_FLAGS_MASK; 1.1279 + return true; 1.1280 +} 1.1281 + 1.1282 +nsresult 1.1283 +RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation) 1.1284 +{ 1.1285 + MOZ_ASSERT(NS_IsMainThread()); 1.1286 + mDecodingMonitor.AssertCurrentThreadIn(); 1.1287 + 1.1288 + if (mError) 1.1289 + return NS_ERROR_FAILURE; 1.1290 + 1.1291 + // Ensure that we have positive values 1.1292 + // XXX - Why isn't the size unsigned? Should this be changed? 1.1293 + if ((aWidth < 0) || (aHeight < 0)) 1.1294 + return NS_ERROR_INVALID_ARG; 1.1295 + 1.1296 + // if we already have a size, check the new size against the old one 1.1297 + if (!mMultipart && mHasSize && 1.1298 + ((aWidth != mSize.width) || 1.1299 + (aHeight != mSize.height) || 1.1300 + (aOrientation != mOrientation))) { 1.1301 + NS_WARNING("Image changed size on redecode! This should not happen!"); 1.1302 + 1.1303 + // Make the decoder aware of the error so that it doesn't try to call 1.1304 + // FinishInternal during ShutdownDecoder. 1.1305 + if (mDecoder) 1.1306 + mDecoder->PostResizeError(); 1.1307 + 1.1308 + DoError(); 1.1309 + return NS_ERROR_UNEXPECTED; 1.1310 + } 1.1311 + 1.1312 + // Set the size and flag that we have it 1.1313 + mSize.SizeTo(aWidth, aHeight); 1.1314 + mOrientation = aOrientation; 1.1315 + mHasSize = true; 1.1316 + 1.1317 + mFrameBlender.SetSize(mSize); 1.1318 + 1.1319 + return NS_OK; 1.1320 +} 1.1321 + 1.1322 +nsresult 1.1323 +RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY, 1.1324 + int32_t aWidth, int32_t aHeight, 1.1325 + gfxImageFormat aFormat, 1.1326 + uint8_t aPaletteDepth, 1.1327 + uint8_t **imageData, uint32_t *imageLength, 1.1328 + uint32_t **paletteData, uint32_t *paletteLength, 1.1329 + imgFrame** aRetFrame) 1.1330 +{ 1.1331 + if (mError) 1.1332 + return NS_ERROR_FAILURE; 1.1333 + 1.1334 + NS_ENSURE_ARG_POINTER(imageData); 1.1335 + NS_ENSURE_ARG_POINTER(imageLength); 1.1336 + NS_ENSURE_ARG_POINTER(aRetFrame); 1.1337 + NS_ABORT_IF_FALSE(aFrameNum <= GetNumFrames(), "Invalid frame index!"); 1.1338 + 1.1339 + if (aPaletteDepth > 0) { 1.1340 + NS_ENSURE_ARG_POINTER(paletteData); 1.1341 + NS_ENSURE_ARG_POINTER(paletteLength); 1.1342 + } 1.1343 + 1.1344 + if (aFrameNum > GetNumFrames()) 1.1345 + return NS_ERROR_INVALID_ARG; 1.1346 + 1.1347 + // Adding a frame that doesn't already exist. 1.1348 + if (aFrameNum == GetNumFrames()) { 1.1349 + return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat, 1.1350 + aPaletteDepth, imageData, imageLength, 1.1351 + paletteData, paletteLength, aRetFrame); 1.1352 + } 1.1353 + 1.1354 + imgFrame *frame = mFrameBlender.RawGetFrame(aFrameNum); 1.1355 + if (!frame) { 1.1356 + return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat, 1.1357 + aPaletteDepth, imageData, imageLength, 1.1358 + paletteData, paletteLength, aRetFrame); 1.1359 + } 1.1360 + 1.1361 + // See if we can re-use the frame that already exists. 1.1362 + nsIntRect rect = frame->GetRect(); 1.1363 + if (rect.x == aX && rect.y == aY && rect.width == aWidth && 1.1364 + rect.height == aHeight && frame->GetFormat() == aFormat && 1.1365 + frame->GetPaletteDepth() == aPaletteDepth) { 1.1366 + frame->GetImageData(imageData, imageLength); 1.1367 + if (paletteData) { 1.1368 + frame->GetPaletteData(paletteData, paletteLength); 1.1369 + } 1.1370 + 1.1371 + *aRetFrame = frame; 1.1372 + 1.1373 + // We can re-use the frame if it has image data. 1.1374 + if (*imageData && paletteData && *paletteData) { 1.1375 + return NS_OK; 1.1376 + } 1.1377 + if (*imageData && !paletteData) { 1.1378 + return NS_OK; 1.1379 + } 1.1380 + } 1.1381 + 1.1382 + // Not reusable, so replace the frame directly. 1.1383 + 1.1384 + // We know this frame is already locked, because it's the one we're currently 1.1385 + // writing to. 1.1386 + frame->UnlockImageData(); 1.1387 + 1.1388 + mFrameBlender.RemoveFrame(aFrameNum); 1.1389 + nsAutoPtr<imgFrame> newFrame(new imgFrame()); 1.1390 + nsresult rv = newFrame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth); 1.1391 + NS_ENSURE_SUCCESS(rv, rv); 1.1392 + return InternalAddFrameHelper(aFrameNum, newFrame.forget(), imageData, 1.1393 + imageLength, paletteData, paletteLength, 1.1394 + aRetFrame); 1.1395 +} 1.1396 + 1.1397 +nsresult 1.1398 +RasterImage::EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY, 1.1399 + int32_t aWidth, int32_t aHeight, 1.1400 + gfxImageFormat aFormat, 1.1401 + uint8_t** imageData, uint32_t* imageLength, 1.1402 + imgFrame** aFrame) 1.1403 +{ 1.1404 + return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat, 1.1405 + /* aPaletteDepth = */ 0, imageData, imageLength, 1.1406 + /* aPaletteData = */ nullptr, 1.1407 + /* aPaletteLength = */ nullptr, 1.1408 + aFrame); 1.1409 +} 1.1410 + 1.1411 +nsresult 1.1412 +RasterImage::SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult) 1.1413 +{ 1.1414 + if (mError) 1.1415 + return NS_ERROR_FAILURE; 1.1416 + 1.1417 + NS_ABORT_IF_FALSE(aFrameNum < GetNumFrames(), "Invalid frame index!"); 1.1418 + if (aFrameNum >= GetNumFrames()) 1.1419 + return NS_ERROR_INVALID_ARG; 1.1420 + 1.1421 + imgFrame* frame = mFrameBlender.RawGetFrame(aFrameNum); 1.1422 + NS_ABORT_IF_FALSE(frame, "Calling SetFrameAsNonPremult on frame that doesn't exist!"); 1.1423 + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 1.1424 + 1.1425 + frame->SetAsNonPremult(aIsNonPremult); 1.1426 + 1.1427 + return NS_OK; 1.1428 +} 1.1429 + 1.1430 +nsresult 1.1431 +RasterImage::DecodingComplete() 1.1432 +{ 1.1433 + MOZ_ASSERT(NS_IsMainThread()); 1.1434 + 1.1435 + if (mError) 1.1436 + return NS_ERROR_FAILURE; 1.1437 + 1.1438 + // Flag that we're done decoding. 1.1439 + // XXX - these should probably be combined when we fix animated image 1.1440 + // discarding with bug 500402. 1.1441 + mDecoded = true; 1.1442 + mHasBeenDecoded = true; 1.1443 + 1.1444 + nsresult rv; 1.1445 + 1.1446 + // We now have one of the qualifications for discarding. Re-evaluate. 1.1447 + if (CanDiscard()) { 1.1448 + NS_ABORT_IF_FALSE(!DiscardingActive(), 1.1449 + "We shouldn't have been discardable before this"); 1.1450 + rv = DiscardTracker::Reset(&mDiscardTrackerNode); 1.1451 + CONTAINER_ENSURE_SUCCESS(rv); 1.1452 + } 1.1453 + 1.1454 + // If there's only 1 frame, optimize it. Optimizing animated images 1.1455 + // is not supported. 1.1456 + // 1.1457 + // We don't optimize the frame for multipart images because we reuse 1.1458 + // the frame. 1.1459 + if ((GetNumFrames() == 1) && !mMultipart) { 1.1460 + // CanForciblyDiscard is used instead of CanForciblyDiscardAndRedecode 1.1461 + // because we know decoding is complete at this point and this is not 1.1462 + // an animation 1.1463 + if (DiscardingEnabled() && CanForciblyDiscard()) { 1.1464 + mFrameBlender.RawGetFrame(0)->SetDiscardable(); 1.1465 + } 1.1466 + rv = mFrameBlender.RawGetFrame(0)->Optimize(); 1.1467 + NS_ENSURE_SUCCESS(rv, rv); 1.1468 + } 1.1469 + 1.1470 + // Double-buffer our frame in the multipart case, since we'll start decoding 1.1471 + // into the first frame again immediately and this produces severe tearing. 1.1472 + if (mMultipart) { 1.1473 + if (GetNumFrames() == 1) { 1.1474 + mMultipartDecodedFrame = mFrameBlender.SwapFrame(GetCurrentFrameIndex(), 1.1475 + mMultipartDecodedFrame); 1.1476 + } else { 1.1477 + // Don't double buffer for animated multipart images. It entails more 1.1478 + // complexity and it's not really needed since we already are smart about 1.1479 + // not displaying the still-decoding frame of an animated image. We may 1.1480 + // have already stored an extra frame, though, so we'll release it here. 1.1481 + delete mMultipartDecodedFrame; 1.1482 + mMultipartDecodedFrame = nullptr; 1.1483 + } 1.1484 + } 1.1485 + 1.1486 + if (mAnim) { 1.1487 + mAnim->SetDoneDecoding(true); 1.1488 + } 1.1489 + 1.1490 + return NS_OK; 1.1491 +} 1.1492 + 1.1493 +NS_IMETHODIMP 1.1494 +RasterImage::SetAnimationMode(uint16_t aAnimationMode) 1.1495 +{ 1.1496 + if (mAnim) { 1.1497 + mAnim->SetAnimationMode(aAnimationMode); 1.1498 + } 1.1499 + return SetAnimationModeInternal(aAnimationMode); 1.1500 +} 1.1501 + 1.1502 +//****************************************************************************** 1.1503 +/* void StartAnimation () */ 1.1504 +nsresult 1.1505 +RasterImage::StartAnimation() 1.1506 +{ 1.1507 + if (mError) 1.1508 + return NS_ERROR_FAILURE; 1.1509 + 1.1510 + NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!"); 1.1511 + 1.1512 + EnsureAnimExists(); 1.1513 + 1.1514 + imgFrame* currentFrame = GetCurrentImgFrame(); 1.1515 + // A timeout of -1 means we should display this frame forever. 1.1516 + if (currentFrame && mFrameBlender.GetTimeoutForFrame(GetCurrentImgFrameIndex()) < 0) { 1.1517 + mAnimationFinished = true; 1.1518 + return NS_ERROR_ABORT; 1.1519 + } 1.1520 + 1.1521 + if (mAnim) { 1.1522 + // We need to set the time that this initial frame was first displayed, as 1.1523 + // this is used in AdvanceFrame(). 1.1524 + mAnim->InitAnimationFrameTimeIfNecessary(); 1.1525 + } 1.1526 + 1.1527 + return NS_OK; 1.1528 +} 1.1529 + 1.1530 +//****************************************************************************** 1.1531 +/* void stopAnimation (); */ 1.1532 +nsresult 1.1533 +RasterImage::StopAnimation() 1.1534 +{ 1.1535 + NS_ABORT_IF_FALSE(mAnimating, "Should be animating!"); 1.1536 + 1.1537 + nsresult rv = NS_OK; 1.1538 + if (mError) { 1.1539 + rv = NS_ERROR_FAILURE; 1.1540 + } else { 1.1541 + mAnim->SetAnimationFrameTime(TimeStamp()); 1.1542 + } 1.1543 + 1.1544 + mAnimating = false; 1.1545 + return rv; 1.1546 +} 1.1547 + 1.1548 +//****************************************************************************** 1.1549 +/* void resetAnimation (); */ 1.1550 +NS_IMETHODIMP 1.1551 +RasterImage::ResetAnimation() 1.1552 +{ 1.1553 + if (mError) 1.1554 + return NS_ERROR_FAILURE; 1.1555 + 1.1556 + if (mAnimationMode == kDontAnimMode || 1.1557 + !mAnim || mAnim->GetCurrentAnimationFrameIndex() == 0) 1.1558 + return NS_OK; 1.1559 + 1.1560 + mAnimationFinished = false; 1.1561 + 1.1562 + if (mAnimating) 1.1563 + StopAnimation(); 1.1564 + 1.1565 + mFrameBlender.ResetAnimation(); 1.1566 + mAnim->ResetAnimation(); 1.1567 + 1.1568 + UpdateImageContainer(); 1.1569 + 1.1570 + // Note - We probably want to kick off a redecode somewhere around here when 1.1571 + // we fix bug 500402. 1.1572 + 1.1573 + // Update display 1.1574 + if (mStatusTracker) { 1.1575 + nsIntRect rect = mAnim->GetFirstFrameRefreshArea(); 1.1576 + mStatusTracker->FrameChanged(&rect); 1.1577 + } 1.1578 + 1.1579 + // Start the animation again. It may not have been running before, if 1.1580 + // mAnimationFinished was true before entering this function. 1.1581 + EvaluateAnimation(); 1.1582 + 1.1583 + return NS_OK; 1.1584 +} 1.1585 + 1.1586 +//****************************************************************************** 1.1587 +// [notxpcom] void setAnimationStartTime ([const] in TimeStamp aTime); 1.1588 +NS_IMETHODIMP_(void) 1.1589 +RasterImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime) 1.1590 +{ 1.1591 + if (mError || mAnimationMode == kDontAnimMode || mAnimating || !mAnim) 1.1592 + return; 1.1593 + 1.1594 + mAnim->SetAnimationFrameTime(aTime); 1.1595 +} 1.1596 + 1.1597 +NS_IMETHODIMP_(float) 1.1598 +RasterImage::GetFrameIndex(uint32_t aWhichFrame) 1.1599 +{ 1.1600 + MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument"); 1.1601 + return (aWhichFrame == FRAME_FIRST || !mAnim) 1.1602 + ? 0.0f 1.1603 + : mAnim->GetCurrentAnimationFrameIndex(); 1.1604 +} 1.1605 + 1.1606 +void 1.1607 +RasterImage::SetLoopCount(int32_t aLoopCount) 1.1608 +{ 1.1609 + if (mError) 1.1610 + return; 1.1611 + 1.1612 + if (mAnim) { 1.1613 + // No need to set this if we're not an animation 1.1614 + mFrameBlender.SetLoopCount(aLoopCount); 1.1615 + } 1.1616 +} 1.1617 + 1.1618 +nsresult 1.1619 +RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount) 1.1620 +{ 1.1621 + ReentrantMonitorAutoEnter lock(mDecodingMonitor); 1.1622 + 1.1623 + if (mError) 1.1624 + return NS_ERROR_FAILURE; 1.1625 + 1.1626 + NS_ENSURE_ARG_POINTER(aBuffer); 1.1627 + nsresult rv = NS_OK; 1.1628 + 1.1629 + // We should not call this if we're not initialized 1.1630 + NS_ABORT_IF_FALSE(mInitialized, "Calling AddSourceData() on uninitialized " 1.1631 + "RasterImage!"); 1.1632 + 1.1633 + // We should not call this if we're already finished adding source data 1.1634 + NS_ABORT_IF_FALSE(!mHasSourceData, "Calling AddSourceData() after calling " 1.1635 + "sourceDataComplete()!"); 1.1636 + 1.1637 + // This call should come straight from necko - no reentrancy allowed 1.1638 + NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!"); 1.1639 + 1.1640 + // Image is already decoded, we shouldn't be getting data, but it could 1.1641 + // be extra garbage data at the end of a file. 1.1642 + if (mDecoded) { 1.1643 + return NS_OK; 1.1644 + } 1.1645 + 1.1646 + // Starting a new part's frames, let's clean up before we add any 1.1647 + // This needs to happen just before we start getting EnsureFrame() call(s), 1.1648 + // so that there's no gap for anything to miss us. 1.1649 + if (mMultipart && mBytesDecoded == 0) { 1.1650 + // Our previous state may have been animated, so let's clean up 1.1651 + if (mAnimating) 1.1652 + StopAnimation(); 1.1653 + mAnimationFinished = false; 1.1654 + if (mAnim) { 1.1655 + delete mAnim; 1.1656 + mAnim = nullptr; 1.1657 + } 1.1658 + // If there's only one frame, this could cause flickering 1.1659 + int old_frame_count = GetNumFrames(); 1.1660 + if (old_frame_count > 1) { 1.1661 + mFrameBlender.ClearFrames(); 1.1662 + } 1.1663 + } 1.1664 + 1.1665 + // If we're not storing source data and we've previously gotten the size, 1.1666 + // write the data directly to the decoder. (If we haven't gotten the size, 1.1667 + // we'll queue up the data and write it out when we do.) 1.1668 + if (!StoringSourceData() && mHasSize) { 1.1669 + rv = WriteToDecoder(aBuffer, aCount, DECODE_SYNC); 1.1670 + CONTAINER_ENSURE_SUCCESS(rv); 1.1671 + 1.1672 + // We're not storing source data, so this data is probably coming straight 1.1673 + // from the network. In this case, we want to display data as soon as we 1.1674 + // get it, so we want to flush invalidations after every write. 1.1675 + nsRefPtr<Decoder> kungFuDeathGrip = mDecoder; 1.1676 + mInDecoder = true; 1.1677 + mDecoder->FlushInvalidations(); 1.1678 + mInDecoder = false; 1.1679 + 1.1680 + rv = FinishedSomeDecoding(); 1.1681 + CONTAINER_ENSURE_SUCCESS(rv); 1.1682 + } 1.1683 + 1.1684 + // Otherwise, we're storing data in the source buffer 1.1685 + else { 1.1686 + 1.1687 + // Store the data 1.1688 + char *newElem = mSourceData.AppendElements(aBuffer, aCount); 1.1689 + if (!newElem) 1.1690 + return NS_ERROR_OUT_OF_MEMORY; 1.1691 + 1.1692 + if (mDecoder) { 1.1693 + DecodePool::Singleton()->RequestDecode(this); 1.1694 + } 1.1695 + } 1.1696 + 1.1697 + // Statistics 1.1698 + total_source_bytes += aCount; 1.1699 + if (mDiscardable) 1.1700 + discardable_source_bytes += aCount; 1.1701 + PR_LOG (GetCompressedImageAccountingLog(), PR_LOG_DEBUG, 1.1702 + ("CompressedImageAccounting: Added compressed data to RasterImage %p (%s). " 1.1703 + "Total Containers: %d, Discardable containers: %d, " 1.1704 + "Total source bytes: %lld, Source bytes for discardable containers %lld", 1.1705 + this, 1.1706 + mSourceDataMimeType.get(), 1.1707 + num_containers, 1.1708 + num_discardable_containers, 1.1709 + total_source_bytes, 1.1710 + discardable_source_bytes)); 1.1711 + 1.1712 + return NS_OK; 1.1713 +} 1.1714 + 1.1715 +/* Note! buf must be declared as char buf[9]; */ 1.1716 +// just used for logging and hashing the header 1.1717 +static void 1.1718 +get_header_str (char *buf, char *data, size_t data_len) 1.1719 +{ 1.1720 + int i; 1.1721 + int n; 1.1722 + static char hex[] = "0123456789abcdef"; 1.1723 + 1.1724 + n = data_len < 4 ? data_len : 4; 1.1725 + 1.1726 + for (i = 0; i < n; i++) { 1.1727 + buf[i * 2] = hex[(data[i] >> 4) & 0x0f]; 1.1728 + buf[i * 2 + 1] = hex[data[i] & 0x0f]; 1.1729 + } 1.1730 + 1.1731 + buf[i * 2] = 0; 1.1732 +} 1.1733 + 1.1734 +nsresult 1.1735 +RasterImage::DoImageDataComplete() 1.1736 +{ 1.1737 + MOZ_ASSERT(NS_IsMainThread()); 1.1738 + 1.1739 + if (mError) 1.1740 + return NS_ERROR_FAILURE; 1.1741 + 1.1742 + // If we've been called before, ignore. Otherwise, flag that we have everything 1.1743 + if (mHasSourceData) 1.1744 + return NS_OK; 1.1745 + mHasSourceData = true; 1.1746 + 1.1747 + // If there's a decoder open, synchronously decode the beginning of the image 1.1748 + // to check for errors and get the image's size. (If we already have the 1.1749 + // image's size, this does nothing.) Then kick off an async decode of the 1.1750 + // rest of the image. 1.1751 + if (mDecoder) { 1.1752 + nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this); 1.1753 + CONTAINER_ENSURE_SUCCESS(rv); 1.1754 + } 1.1755 + 1.1756 + { 1.1757 + ReentrantMonitorAutoEnter lock(mDecodingMonitor); 1.1758 + 1.1759 + // If we're not storing any source data, then there's nothing more we can do 1.1760 + // once we've tried decoding for size. 1.1761 + if (!StoringSourceData() && mDecoder) { 1.1762 + nsresult rv = ShutdownDecoder(eShutdownIntent_Done); 1.1763 + CONTAINER_ENSURE_SUCCESS(rv); 1.1764 + } 1.1765 + 1.1766 + // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker 1.1767 + // finish decoding this image. 1.1768 + if (mDecoder) { 1.1769 + DecodePool::Singleton()->RequestDecode(this); 1.1770 + } 1.1771 + 1.1772 + // Free up any extra space in the backing buffer 1.1773 + mSourceData.Compact(); 1.1774 + } 1.1775 + 1.1776 + // Log header information 1.1777 + if (PR_LOG_TEST(GetCompressedImageAccountingLog(), PR_LOG_DEBUG)) { 1.1778 + char buf[9]; 1.1779 + get_header_str(buf, mSourceData.Elements(), mSourceData.Length()); 1.1780 + PR_LOG (GetCompressedImageAccountingLog(), PR_LOG_DEBUG, 1.1781 + ("CompressedImageAccounting: RasterImage::SourceDataComplete() - data " 1.1782 + "is done for container %p (%s) - header %p is 0x%s (length %d)", 1.1783 + this, 1.1784 + mSourceDataMimeType.get(), 1.1785 + mSourceData.Elements(), 1.1786 + buf, 1.1787 + mSourceData.Length())); 1.1788 + } 1.1789 + 1.1790 + // We now have one of the qualifications for discarding. Re-evaluate. 1.1791 + if (CanDiscard()) { 1.1792 + nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode); 1.1793 + CONTAINER_ENSURE_SUCCESS(rv); 1.1794 + } 1.1795 + return NS_OK; 1.1796 +} 1.1797 + 1.1798 +nsresult 1.1799 +RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus, bool aLastPart) 1.1800 +{ 1.1801 + nsresult finalStatus = DoImageDataComplete(); 1.1802 + 1.1803 + // Give precedence to Necko failure codes. 1.1804 + if (NS_FAILED(aStatus)) 1.1805 + finalStatus = aStatus; 1.1806 + 1.1807 + // We just recorded OnStopRequest; we need to inform our listeners. 1.1808 + { 1.1809 + ReentrantMonitorAutoEnter lock(mDecodingMonitor); 1.1810 + 1.1811 + nsRefPtr<imgStatusTracker> statusTracker = CurrentStatusTracker(); 1.1812 + statusTracker->GetDecoderObserver()->OnStopRequest(aLastPart, finalStatus); 1.1813 + 1.1814 + FinishedSomeDecoding(); 1.1815 + } 1.1816 + 1.1817 + return finalStatus; 1.1818 +} 1.1819 + 1.1820 +nsresult 1.1821 +RasterImage::OnImageDataAvailable(nsIRequest*, 1.1822 + nsISupports*, 1.1823 + nsIInputStream* aInStr, 1.1824 + uint64_t, 1.1825 + uint32_t aCount) 1.1826 +{ 1.1827 + nsresult rv; 1.1828 + 1.1829 + // WriteToRasterImage always consumes everything it gets 1.1830 + // if it doesn't run out of memory 1.1831 + uint32_t bytesRead; 1.1832 + rv = aInStr->ReadSegments(WriteToRasterImage, this, aCount, &bytesRead); 1.1833 + 1.1834 + NS_ABORT_IF_FALSE(bytesRead == aCount || HasError(), 1.1835 + "WriteToRasterImage should consume everything or the image must be in error!"); 1.1836 + 1.1837 + return rv; 1.1838 +} 1.1839 + 1.1840 +nsresult 1.1841 +RasterImage::OnNewSourceData() 1.1842 +{ 1.1843 + MOZ_ASSERT(NS_IsMainThread()); 1.1844 + 1.1845 + nsresult rv; 1.1846 + 1.1847 + if (mError) 1.1848 + return NS_ERROR_FAILURE; 1.1849 + 1.1850 + // The source data should be complete before calling this 1.1851 + NS_ABORT_IF_FALSE(mHasSourceData, 1.1852 + "Calling NewSourceData before SourceDataComplete!"); 1.1853 + if (!mHasSourceData) 1.1854 + return NS_ERROR_ILLEGAL_VALUE; 1.1855 + 1.1856 + // Only supported for multipart channels. It wouldn't be too hard to change this, 1.1857 + // but it would involve making sure that things worked for decode-on-draw and 1.1858 + // discarding. Presently there's no need for this, so we don't. 1.1859 + NS_ABORT_IF_FALSE(mMultipart, "NewSourceData only supported for multipart"); 1.1860 + if (!mMultipart) 1.1861 + return NS_ERROR_ILLEGAL_VALUE; 1.1862 + 1.1863 + // We're multipart, so we shouldn't be storing source data 1.1864 + NS_ABORT_IF_FALSE(!StoringSourceData(), 1.1865 + "Shouldn't be storing source data for multipart"); 1.1866 + 1.1867 + // We're not storing the source data and we got SourceDataComplete. We should 1.1868 + // have shut down the previous decoder 1.1869 + NS_ABORT_IF_FALSE(!mDecoder, "Shouldn't have a decoder in NewSourceData"); 1.1870 + 1.1871 + // The decoder was shut down and we didn't flag an error, so we should be decoded 1.1872 + NS_ABORT_IF_FALSE(mDecoded, "Should be decoded in NewSourceData"); 1.1873 + 1.1874 + // Reset some flags 1.1875 + mDecoded = false; 1.1876 + mHasSourceData = false; 1.1877 + mHasSize = false; 1.1878 + mWantFullDecode = true; 1.1879 + mDecodeRequest = nullptr; 1.1880 + 1.1881 + if (mAnim) { 1.1882 + mAnim->SetDoneDecoding(false); 1.1883 + } 1.1884 + 1.1885 + // We always need the size first. 1.1886 + rv = InitDecoder(/* aDoSizeDecode = */ true); 1.1887 + CONTAINER_ENSURE_SUCCESS(rv); 1.1888 + 1.1889 + return NS_OK; 1.1890 +} 1.1891 + 1.1892 +nsresult 1.1893 +RasterImage::SetSourceSizeHint(uint32_t sizeHint) 1.1894 +{ 1.1895 + if (sizeHint && StoringSourceData()) 1.1896 + return mSourceData.SetCapacity(sizeHint) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; 1.1897 + return NS_OK; 1.1898 +} 1.1899 + 1.1900 +/********* Methods to implement lazy allocation of nsIProperties object *************/ 1.1901 +NS_IMETHODIMP 1.1902 +RasterImage::Get(const char *prop, const nsIID & iid, void * *result) 1.1903 +{ 1.1904 + if (!mProperties) 1.1905 + return NS_ERROR_FAILURE; 1.1906 + return mProperties->Get(prop, iid, result); 1.1907 +} 1.1908 + 1.1909 +NS_IMETHODIMP 1.1910 +RasterImage::Set(const char *prop, nsISupports *value) 1.1911 +{ 1.1912 + if (!mProperties) 1.1913 + mProperties = do_CreateInstance("@mozilla.org/properties;1"); 1.1914 + if (!mProperties) 1.1915 + return NS_ERROR_OUT_OF_MEMORY; 1.1916 + return mProperties->Set(prop, value); 1.1917 +} 1.1918 + 1.1919 +NS_IMETHODIMP 1.1920 +RasterImage::Has(const char *prop, bool *_retval) 1.1921 +{ 1.1922 + NS_ENSURE_ARG_POINTER(_retval); 1.1923 + if (!mProperties) { 1.1924 + *_retval = false; 1.1925 + return NS_OK; 1.1926 + } 1.1927 + return mProperties->Has(prop, _retval); 1.1928 +} 1.1929 + 1.1930 +NS_IMETHODIMP 1.1931 +RasterImage::Undefine(const char *prop) 1.1932 +{ 1.1933 + if (!mProperties) 1.1934 + return NS_ERROR_FAILURE; 1.1935 + return mProperties->Undefine(prop); 1.1936 +} 1.1937 + 1.1938 +NS_IMETHODIMP 1.1939 +RasterImage::GetKeys(uint32_t *count, char ***keys) 1.1940 +{ 1.1941 + if (!mProperties) { 1.1942 + *count = 0; 1.1943 + *keys = nullptr; 1.1944 + return NS_OK; 1.1945 + } 1.1946 + return mProperties->GetKeys(count, keys); 1.1947 +} 1.1948 + 1.1949 +void 1.1950 +RasterImage::Discard(bool force) 1.1951 +{ 1.1952 + MOZ_ASSERT(NS_IsMainThread()); 1.1953 + 1.1954 + // We should be ok for discard 1.1955 + NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!"); 1.1956 + 1.1957 + // We should never discard when we have an active decoder 1.1958 + NS_ABORT_IF_FALSE(!mDecoder, "Asked to discard with open decoder!"); 1.1959 + 1.1960 + // As soon as an image becomes animated, it becomes non-discardable and any 1.1961 + // timers are cancelled. 1.1962 + NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!"); 1.1963 + 1.1964 + // For post-operation logging 1.1965 + int old_frame_count = GetNumFrames(); 1.1966 + 1.1967 + // Delete all the decoded frames 1.1968 + mFrameBlender.Discard(); 1.1969 + 1.1970 + // Clear our downscaled frame. 1.1971 + mScaleResult.status = SCALE_INVALID; 1.1972 + mScaleResult.frame = nullptr; 1.1973 + 1.1974 + // Clear the last decoded multipart frame. 1.1975 + delete mMultipartDecodedFrame; 1.1976 + mMultipartDecodedFrame = nullptr; 1.1977 + 1.1978 + // Flag that we no longer have decoded frames for this image 1.1979 + mDecoded = false; 1.1980 + 1.1981 + // Notify that we discarded 1.1982 + if (mStatusTracker) 1.1983 + mStatusTracker->OnDiscard(); 1.1984 + 1.1985 + mDecodeRequest = nullptr; 1.1986 + 1.1987 + if (force) 1.1988 + DiscardTracker::Remove(&mDiscardTrackerNode); 1.1989 + 1.1990 + // Log 1.1991 + PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG, 1.1992 + ("CompressedImageAccounting: discarded uncompressed image " 1.1993 + "data from RasterImage %p (%s) - %d frames (cached count: %d); " 1.1994 + "Total Containers: %d, Discardable containers: %d, " 1.1995 + "Total source bytes: %lld, Source bytes for discardable containers %lld", 1.1996 + this, 1.1997 + mSourceDataMimeType.get(), 1.1998 + old_frame_count, 1.1999 + GetNumFrames(), 1.2000 + num_containers, 1.2001 + num_discardable_containers, 1.2002 + total_source_bytes, 1.2003 + discardable_source_bytes)); 1.2004 +} 1.2005 + 1.2006 +// Helper method to determine if we can discard an image 1.2007 +bool 1.2008 +RasterImage::CanDiscard() { 1.2009 + return (DiscardingEnabled() && // Globally enabled... 1.2010 + mDiscardable && // ...Enabled at creation time... 1.2011 + (mLockCount == 0) && // ...not temporarily disabled... 1.2012 + mHasSourceData && // ...have the source data... 1.2013 + mDecoded); // ...and have something to discard. 1.2014 +} 1.2015 + 1.2016 +bool 1.2017 +RasterImage::CanForciblyDiscard() { 1.2018 + return mDiscardable && // ...Enabled at creation time... 1.2019 + mHasSourceData; // ...have the source data... 1.2020 +} 1.2021 + 1.2022 +bool 1.2023 +RasterImage::CanForciblyDiscardAndRedecode() { 1.2024 + return mDiscardable && // ...Enabled at creation time... 1.2025 + mHasSourceData && // ...have the source data... 1.2026 + !mDecoder && // Can't discard with an open decoder 1.2027 + !mAnim; // Can never discard animated images 1.2028 +} 1.2029 + 1.2030 +// Helper method to tell us whether the clock is currently running for 1.2031 +// discarding this image. Mainly for assertions. 1.2032 +bool 1.2033 +RasterImage::DiscardingActive() { 1.2034 + return mDiscardTrackerNode.isInList(); 1.2035 +} 1.2036 + 1.2037 +// Helper method to determine if we're storing the source data in a buffer 1.2038 +// or just writing it directly to the decoder 1.2039 +bool 1.2040 +RasterImage::StoringSourceData() const { 1.2041 + return (mDecodeOnDraw || mDiscardable); 1.2042 +} 1.2043 + 1.2044 + 1.2045 +// Sets up a decoder for this image. It is an error to call this function 1.2046 +// when decoding is already in process (ie - when mDecoder is non-null). 1.2047 +nsresult 1.2048 +RasterImage::InitDecoder(bool aDoSizeDecode) 1.2049 +{ 1.2050 + // Ensure that the decoder is not already initialized 1.2051 + NS_ABORT_IF_FALSE(!mDecoder, "Calling InitDecoder() while already decoding!"); 1.2052 + 1.2053 + // We shouldn't be firing up a decoder if we already have the frames decoded 1.2054 + NS_ABORT_IF_FALSE(!mDecoded, "Calling InitDecoder() but already decoded!"); 1.2055 + 1.2056 + // Since we're not decoded, we should not have a discard timer active 1.2057 + NS_ABORT_IF_FALSE(!DiscardingActive(), "Discard Timer active in InitDecoder()!"); 1.2058 + 1.2059 + // Make sure we actually get size before doing a full decode. 1.2060 + if (!aDoSizeDecode) { 1.2061 + NS_ABORT_IF_FALSE(mHasSize, "Must do a size decode before a full decode!"); 1.2062 + } 1.2063 + 1.2064 + // Figure out which decoder we want 1.2065 + eDecoderType type = GetDecoderType(mSourceDataMimeType.get()); 1.2066 + CONTAINER_ENSURE_TRUE(type != eDecoderType_unknown, NS_IMAGELIB_ERROR_NO_DECODER); 1.2067 + 1.2068 + // Instantiate the appropriate decoder 1.2069 + switch (type) { 1.2070 + case eDecoderType_png: 1.2071 + mDecoder = new nsPNGDecoder(*this); 1.2072 + break; 1.2073 + case eDecoderType_gif: 1.2074 + mDecoder = new nsGIFDecoder2(*this); 1.2075 + break; 1.2076 + case eDecoderType_jpeg: 1.2077 + // If we have all the data we don't want to waste cpu time doing 1.2078 + // a progressive decode 1.2079 + mDecoder = new nsJPEGDecoder(*this, 1.2080 + mHasBeenDecoded ? Decoder::SEQUENTIAL : 1.2081 + Decoder::PROGRESSIVE); 1.2082 + break; 1.2083 + case eDecoderType_bmp: 1.2084 + mDecoder = new nsBMPDecoder(*this); 1.2085 + break; 1.2086 + case eDecoderType_ico: 1.2087 + mDecoder = new nsICODecoder(*this); 1.2088 + break; 1.2089 + case eDecoderType_icon: 1.2090 + mDecoder = new nsIconDecoder(*this); 1.2091 + break; 1.2092 + default: 1.2093 + NS_ABORT_IF_FALSE(0, "Shouldn't get here!"); 1.2094 + } 1.2095 + 1.2096 + // If we already have frames, we're probably in the multipart/x-mixed-replace 1.2097 + // case. Regardless, we need to lock the last frame. Our invariant is that, 1.2098 + // while we have a decoder open, the last frame is always locked. 1.2099 + if (GetNumFrames() > 0) { 1.2100 + imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1); 1.2101 + curframe->LockImageData(); 1.2102 + } 1.2103 + 1.2104 + // Initialize the decoder 1.2105 + if (!mDecodeRequest) { 1.2106 + mDecodeRequest = new DecodeRequest(this); 1.2107 + } 1.2108 + MOZ_ASSERT(mDecodeRequest->mStatusTracker); 1.2109 + MOZ_ASSERT(mDecodeRequest->mStatusTracker->GetDecoderObserver()); 1.2110 + mDecoder->SetObserver(mDecodeRequest->mStatusTracker->GetDecoderObserver()); 1.2111 + mDecoder->SetSizeDecode(aDoSizeDecode); 1.2112 + mDecoder->SetDecodeFlags(mFrameDecodeFlags); 1.2113 + if (!aDoSizeDecode) { 1.2114 + // We already have the size; tell the decoder so it can preallocate a 1.2115 + // frame. By default, we create an ARGB frame with no offset. If decoders 1.2116 + // need a different type, they need to ask for it themselves. 1.2117 + mDecoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height, 1.2118 + gfxImageFormat::ARGB32); 1.2119 + mDecoder->AllocateFrame(); 1.2120 + } 1.2121 + mDecoder->Init(); 1.2122 + CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError()); 1.2123 + 1.2124 + if (!aDoSizeDecode) { 1.2125 + Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount); 1.2126 + mDecodeCount++; 1.2127 + Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount); 1.2128 + 1.2129 + if (mDecodeCount > sMaxDecodeCount) { 1.2130 + // Don't subtract out 0 from the histogram, because that causes its count 1.2131 + // to go negative, which is not kosher. 1.2132 + if (sMaxDecodeCount > 0) { 1.2133 + Telemetry::GetHistogramById(Telemetry::IMAGE_MAX_DECODE_COUNT)->Subtract(sMaxDecodeCount); 1.2134 + } 1.2135 + sMaxDecodeCount = mDecodeCount; 1.2136 + Telemetry::GetHistogramById(Telemetry::IMAGE_MAX_DECODE_COUNT)->Add(sMaxDecodeCount); 1.2137 + } 1.2138 + } 1.2139 + 1.2140 + return NS_OK; 1.2141 +} 1.2142 + 1.2143 +// Flushes, closes, and nulls-out a decoder. Cleans up any related decoding 1.2144 +// state. It is an error to call this function when there is no initialized 1.2145 +// decoder. 1.2146 +// 1.2147 +// aIntent specifies the intent of the shutdown. If aIntent is 1.2148 +// eShutdownIntent_Done, an error is flagged if we didn't get what we should 1.2149 +// have out of the decode. If aIntent is eShutdownIntent_NotNeeded, we don't 1.2150 +// check this. If aIntent is eShutdownIntent_Error, we shut down in error mode. 1.2151 +nsresult 1.2152 +RasterImage::ShutdownDecoder(eShutdownIntent aIntent) 1.2153 +{ 1.2154 + MOZ_ASSERT(NS_IsMainThread()); 1.2155 + mDecodingMonitor.AssertCurrentThreadIn(); 1.2156 + 1.2157 + // Ensure that our intent is valid 1.2158 + NS_ABORT_IF_FALSE((aIntent >= 0) && (aIntent < eShutdownIntent_AllCount), 1.2159 + "Invalid shutdown intent"); 1.2160 + 1.2161 + // Ensure that the decoder is initialized 1.2162 + NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!"); 1.2163 + 1.2164 + // Figure out what kind of decode we were doing before we get rid of our decoder 1.2165 + bool wasSizeDecode = mDecoder->IsSizeDecode(); 1.2166 + 1.2167 + // Finalize the decoder 1.2168 + // null out mDecoder, _then_ check for errors on the close (otherwise the 1.2169 + // error routine might re-invoke ShutdownDecoder) 1.2170 + nsRefPtr<Decoder> decoder = mDecoder; 1.2171 + mDecoder = nullptr; 1.2172 + 1.2173 + mFinishing = true; 1.2174 + mInDecoder = true; 1.2175 + decoder->Finish(aIntent); 1.2176 + mInDecoder = false; 1.2177 + mFinishing = false; 1.2178 + 1.2179 + // Unlock the last frame (if we have any). Our invariant is that, while we 1.2180 + // have a decoder open, the last frame is always locked. 1.2181 + if (GetNumFrames() > 0) { 1.2182 + imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1); 1.2183 + curframe->UnlockImageData(); 1.2184 + } 1.2185 + 1.2186 + // Kill off our decode request, if it's pending. (If not, this call is 1.2187 + // harmless.) 1.2188 + DecodePool::StopDecoding(this); 1.2189 + 1.2190 + nsresult decoderStatus = decoder->GetDecoderError(); 1.2191 + if (NS_FAILED(decoderStatus)) { 1.2192 + DoError(); 1.2193 + return decoderStatus; 1.2194 + } 1.2195 + 1.2196 + // We just shut down the decoder. If we didn't get what we want, but expected 1.2197 + // to, flag an error 1.2198 + bool failed = false; 1.2199 + if (wasSizeDecode && !mHasSize) 1.2200 + failed = true; 1.2201 + if (!wasSizeDecode && !mDecoded) 1.2202 + failed = true; 1.2203 + if ((aIntent == eShutdownIntent_Done) && failed) { 1.2204 + DoError(); 1.2205 + return NS_ERROR_FAILURE; 1.2206 + } 1.2207 + 1.2208 + // If we finished a full decode, and we're not meant to be storing source 1.2209 + // data, stop storing it. 1.2210 + if (!wasSizeDecode && !StoringSourceData()) { 1.2211 + mSourceData.Clear(); 1.2212 + } 1.2213 + 1.2214 + mBytesDecoded = 0; 1.2215 + 1.2216 + return NS_OK; 1.2217 +} 1.2218 + 1.2219 +// Writes the data to the decoder, updating the total number of bytes written. 1.2220 +nsresult 1.2221 +RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount, DecodeStrategy aStrategy) 1.2222 +{ 1.2223 + mDecodingMonitor.AssertCurrentThreadIn(); 1.2224 + 1.2225 + // We should have a decoder 1.2226 + NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!"); 1.2227 + 1.2228 + // Write 1.2229 + nsRefPtr<Decoder> kungFuDeathGrip = mDecoder; 1.2230 + mInDecoder = true; 1.2231 + mDecoder->Write(aBuffer, aCount, aStrategy); 1.2232 + mInDecoder = false; 1.2233 + 1.2234 + CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError()); 1.2235 + 1.2236 + // Keep track of the total number of bytes written over the lifetime of the 1.2237 + // decoder 1.2238 + mBytesDecoded += aCount; 1.2239 + 1.2240 + return NS_OK; 1.2241 +} 1.2242 + 1.2243 +// This function is called in situations where it's clear that we want the 1.2244 +// frames in decoded form (Draw, GetFrame, etc). If we're completely decoded, 1.2245 +// this method resets the discard timer (if we're discardable), since wanting 1.2246 +// the frames now is a good indicator of wanting them again soon. If we're not 1.2247 +// decoded, this method kicks off asynchronous decoding to generate the frames. 1.2248 +nsresult 1.2249 +RasterImage::WantDecodedFrames() 1.2250 +{ 1.2251 + nsresult rv; 1.2252 + 1.2253 + // If we can discard, the clock should be running. Reset it. 1.2254 + if (CanDiscard()) { 1.2255 + NS_ABORT_IF_FALSE(DiscardingActive(), 1.2256 + "Decoded and discardable but discarding not activated!"); 1.2257 + rv = DiscardTracker::Reset(&mDiscardTrackerNode); 1.2258 + CONTAINER_ENSURE_SUCCESS(rv); 1.2259 + } 1.2260 + 1.2261 + // Request a decode (no-op if we're decoded) 1.2262 + return StartDecoding(); 1.2263 +} 1.2264 + 1.2265 +//****************************************************************************** 1.2266 +/* void requestDecode() */ 1.2267 +NS_IMETHODIMP 1.2268 +RasterImage::RequestDecode() 1.2269 +{ 1.2270 + return RequestDecodeCore(SYNCHRONOUS_NOTIFY); 1.2271 +} 1.2272 + 1.2273 +/* void startDecode() */ 1.2274 +NS_IMETHODIMP 1.2275 +RasterImage::StartDecoding() 1.2276 +{ 1.2277 + if (!NS_IsMainThread()) { 1.2278 + return NS_DispatchToMainThread( 1.2279 + NS_NewRunnableMethod(this, &RasterImage::StartDecoding)); 1.2280 + } 1.2281 + // Here we are explicitly trading off flashing for responsiveness in the case 1.2282 + // that we're redecoding an image (see bug 845147). 1.2283 + return RequestDecodeCore(mHasBeenDecoded ? 1.2284 + SYNCHRONOUS_NOTIFY : SYNCHRONOUS_NOTIFY_AND_SOME_DECODE); 1.2285 +} 1.2286 + 1.2287 +bool 1.2288 +RasterImage::IsDecoded() 1.2289 +{ 1.2290 + return mDecoded || mError; 1.2291 +} 1.2292 + 1.2293 +NS_IMETHODIMP 1.2294 +RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) 1.2295 +{ 1.2296 + MOZ_ASSERT(NS_IsMainThread()); 1.2297 + 1.2298 + nsresult rv; 1.2299 + 1.2300 + if (mError) 1.2301 + return NS_ERROR_FAILURE; 1.2302 + 1.2303 + // If we're already decoded, there's nothing to do. 1.2304 + if (mDecoded) 1.2305 + return NS_OK; 1.2306 + 1.2307 + // mFinishing protects against the case when we enter RequestDecode from 1.2308 + // ShutdownDecoder -- in that case, we're done with the decode, we're just 1.2309 + // not quite ready to admit it. See bug 744309. 1.2310 + if (mFinishing) 1.2311 + return NS_OK; 1.2312 + 1.2313 + // If we're currently waiting for a new frame, we can't do anything until 1.2314 + // that frame is allocated. 1.2315 + if (mDecoder && mDecoder->NeedsNewFrame()) 1.2316 + return NS_OK; 1.2317 + 1.2318 + // If our callstack goes through a size decoder, we have a problem. 1.2319 + // We need to shutdown the size decode and replace it with a full 1.2320 + // decoder, but can't do that from within the decoder itself. Thus, we post 1.2321 + // an asynchronous event to the event loop to do it later. Since 1.2322 + // RequestDecode() is an asynchronous function this works fine (though it's 1.2323 + // a little slower). 1.2324 + if (mInDecoder) { 1.2325 + nsRefPtr<imgDecodeRequestor> requestor = new imgDecodeRequestor(*this); 1.2326 + return NS_DispatchToCurrentThread(requestor); 1.2327 + } 1.2328 + 1.2329 + // If we have a size decoder open, make sure we get the size 1.2330 + if (mDecoder && mDecoder->IsSizeDecode()) { 1.2331 + nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this); 1.2332 + CONTAINER_ENSURE_SUCCESS(rv); 1.2333 + 1.2334 + // If we didn't get the size out of the image, we won't until we get more 1.2335 + // data, so signal that we want a full decode and give up for now. 1.2336 + if (!mHasSize) { 1.2337 + mWantFullDecode = true; 1.2338 + return NS_OK; 1.2339 + } 1.2340 + } 1.2341 + 1.2342 + ReentrantMonitorAutoEnter lock(mDecodingMonitor); 1.2343 + 1.2344 + // If we don't have any bytes to flush to the decoder, we can't do anything. 1.2345 + // mBytesDecoded can be bigger than mSourceData.Length() if we're not storing 1.2346 + // the source data. 1.2347 + if (mBytesDecoded > mSourceData.Length()) 1.2348 + return NS_OK; 1.2349 + 1.2350 + // If the image is waiting for decode work to be notified, go ahead and do that. 1.2351 + if (mDecodeRequest && 1.2352 + mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE && 1.2353 + aDecodeType != ASYNCHRONOUS) { 1.2354 + nsresult rv = FinishedSomeDecoding(); 1.2355 + CONTAINER_ENSURE_SUCCESS(rv); 1.2356 + } 1.2357 + 1.2358 + // If we're fully decoded, we have nothing to do. We need this check after 1.2359 + // DecodeUntilSizeAvailable and FinishedSomeDecoding because they can result 1.2360 + // in us finishing an in-progress decode (or kicking off and finishing a 1.2361 + // synchronous decode if we're already waiting on a full decode). 1.2362 + if (mDecoded) { 1.2363 + return NS_OK; 1.2364 + } 1.2365 + 1.2366 + // If we've already got a full decoder running, and have already 1.2367 + // decoded some bytes, we have nothing to do 1.2368 + if (mDecoder && !mDecoder->IsSizeDecode() && mBytesDecoded) { 1.2369 + return NS_OK; 1.2370 + } 1.2371 + 1.2372 + // If we have a size decode open, interrupt it and shut it down; or if 1.2373 + // the decoder has different flags than what we need 1.2374 + if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) { 1.2375 + nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded); 1.2376 + CONTAINER_ENSURE_SUCCESS(rv); 1.2377 + } 1.2378 + 1.2379 + // If we don't have a decoder, create one 1.2380 + if (!mDecoder) { 1.2381 + rv = InitDecoder(/* aDoSizeDecode = */ false); 1.2382 + CONTAINER_ENSURE_SUCCESS(rv); 1.2383 + 1.2384 + rv = FinishedSomeDecoding(); 1.2385 + CONTAINER_ENSURE_SUCCESS(rv); 1.2386 + 1.2387 + MOZ_ASSERT(mDecoder); 1.2388 + } 1.2389 + 1.2390 + // If we've read all the data we have, we're done 1.2391 + if (mHasSourceData && mBytesDecoded == mSourceData.Length()) 1.2392 + return NS_OK; 1.2393 + 1.2394 + // If we can do decoding now, do so. Small images will decode completely, 1.2395 + // large images will decode a bit and post themselves to the event loop 1.2396 + // to finish decoding. 1.2397 + if (!mDecoded && !mInDecoder && mHasSourceData && aDecodeType == SYNCHRONOUS_NOTIFY_AND_SOME_DECODE) { 1.2398 + PROFILER_LABEL_PRINTF("RasterImage", "DecodeABitOf", "%s", GetURIString().get()); 1.2399 + DecodePool::Singleton()->DecodeABitOf(this, DECODE_SYNC); 1.2400 + return NS_OK; 1.2401 + } 1.2402 + 1.2403 + if (!mDecoded) { 1.2404 + // If we get this far, dispatch the worker. We do this instead of starting 1.2405 + // any immediate decoding to guarantee that all our decode notifications are 1.2406 + // dispatched asynchronously, and to ensure we stay responsive. 1.2407 + DecodePool::Singleton()->RequestDecode(this); 1.2408 + } 1.2409 + 1.2410 + return NS_OK; 1.2411 +} 1.2412 + 1.2413 +// Synchronously decodes as much data as possible 1.2414 +nsresult 1.2415 +RasterImage::SyncDecode() 1.2416 +{ 1.2417 + PROFILER_LABEL_PRINTF("RasterImage", "SyncDecode", "%s", GetURIString().get());; 1.2418 + 1.2419 + // If we have a size decoder open, make sure we get the size 1.2420 + if (mDecoder && mDecoder->IsSizeDecode()) { 1.2421 + nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this); 1.2422 + CONTAINER_ENSURE_SUCCESS(rv); 1.2423 + 1.2424 + // If we didn't get the size out of the image, we won't until we get more 1.2425 + // data, so signal that we want a full decode and give up for now. 1.2426 + if (!mHasSize) { 1.2427 + mWantFullDecode = true; 1.2428 + return NS_ERROR_NOT_AVAILABLE; 1.2429 + } 1.2430 + } 1.2431 + 1.2432 + ReentrantMonitorAutoEnter lock(mDecodingMonitor); 1.2433 + 1.2434 + // We really have no good way of forcing a synchronous decode if we're being 1.2435 + // called in a re-entrant manner (ie, from an event listener fired by a 1.2436 + // decoder), because the decoding machinery is already tied up. We thus explicitly 1.2437 + // disallow this type of call in the API, and check for it in API methods. 1.2438 + NS_ABORT_IF_FALSE(!mInDecoder, "Yikes, forcing sync in reentrant call!"); 1.2439 + 1.2440 + if (mDecodeRequest) { 1.2441 + // If the image is waiting for decode work to be notified, go ahead and do that. 1.2442 + if (mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) { 1.2443 + nsresult rv = FinishedSomeDecoding(); 1.2444 + CONTAINER_ENSURE_SUCCESS(rv); 1.2445 + } 1.2446 + } 1.2447 + 1.2448 + nsresult rv; 1.2449 + 1.2450 + // If we're decoded already, or decoding until the size was available 1.2451 + // finished us as a side-effect, no worries 1.2452 + if (mDecoded) 1.2453 + return NS_OK; 1.2454 + 1.2455 + // If we don't have any bytes to flush to the decoder, we can't do anything. 1.2456 + // mBytesDecoded can be bigger than mSourceData.Length() if we're not storing 1.2457 + // the source data. 1.2458 + if (mBytesDecoded > mSourceData.Length()) 1.2459 + return NS_OK; 1.2460 + 1.2461 + // If we have a decoder open with different flags than what we need, shut it 1.2462 + // down 1.2463 + if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) { 1.2464 + nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded); 1.2465 + CONTAINER_ENSURE_SUCCESS(rv); 1.2466 + 1.2467 + if (mDecoded) { 1.2468 + // If we've finished decoding we need to discard so we can re-decode 1.2469 + // with the new flags. If we can't discard then there isn't 1.2470 + // anything we can do. 1.2471 + if (!CanForciblyDiscardAndRedecode()) 1.2472 + return NS_ERROR_NOT_AVAILABLE; 1.2473 + ForceDiscard(); 1.2474 + } 1.2475 + } 1.2476 + 1.2477 + // If we're currently waiting on a new frame for this image, we have to create 1.2478 + // it now. 1.2479 + if (mDecoder && mDecoder->NeedsNewFrame()) { 1.2480 + mDecoder->AllocateFrame(); 1.2481 + mDecodeRequest->mAllocatedNewFrame = true; 1.2482 + } 1.2483 + 1.2484 + // If we don't have a decoder, create one 1.2485 + if (!mDecoder) { 1.2486 + rv = InitDecoder(/* aDoSizeDecode = */ false); 1.2487 + CONTAINER_ENSURE_SUCCESS(rv); 1.2488 + } 1.2489 + 1.2490 + // Write everything we have 1.2491 + rv = DecodeSomeData(mSourceData.Length() - mBytesDecoded, DECODE_SYNC); 1.2492 + CONTAINER_ENSURE_SUCCESS(rv); 1.2493 + 1.2494 + // When we're doing a sync decode, we want to get as much information from the 1.2495 + // image as possible. We've send the decoder all of our data, so now's a good 1.2496 + // time to flush any invalidations (in case we don't have all the data and what 1.2497 + // we got left us mid-frame). 1.2498 + nsRefPtr<Decoder> kungFuDeathGrip = mDecoder; 1.2499 + mInDecoder = true; 1.2500 + mDecoder->FlushInvalidations(); 1.2501 + mInDecoder = false; 1.2502 + 1.2503 + rv = FinishedSomeDecoding(); 1.2504 + CONTAINER_ENSURE_SUCCESS(rv); 1.2505 + 1.2506 + // If our decoder's still open, there's still work to be done. 1.2507 + if (mDecoder) { 1.2508 + DecodePool::Singleton()->RequestDecode(this); 1.2509 + } 1.2510 + 1.2511 + // All good if no errors! 1.2512 + return mError ? NS_ERROR_FAILURE : NS_OK; 1.2513 +} 1.2514 + 1.2515 +bool 1.2516 +RasterImage::CanQualityScale(const gfxSize& scale) 1.2517 +{ 1.2518 + // If target size is 1:1 with original, don't scale. 1.2519 + if (scale.width == 1.0 && scale.height == 1.0) 1.2520 + return false; 1.2521 + 1.2522 + // To save memory don't quality upscale images bigger than the limit. 1.2523 + if (scale.width > 1.0 || scale.height > 1.0) { 1.2524 + uint32_t scaled_size = static_cast<uint32_t>(mSize.width * mSize.height * scale.width * scale.height); 1.2525 + if (scaled_size > gHQUpscalingMaxSize) 1.2526 + return false; 1.2527 + } 1.2528 + 1.2529 + return true; 1.2530 +} 1.2531 + 1.2532 +bool 1.2533 +RasterImage::CanScale(GraphicsFilter aFilter, 1.2534 + gfxSize aScale, uint32_t aFlags) 1.2535 +{ 1.2536 +// The high-quality scaler requires Skia. 1.2537 +#ifdef MOZ_ENABLE_SKIA 1.2538 + // We don't use the scaler for animated or multipart images to avoid doing a 1.2539 + // bunch of work on an image that just gets thrown away. 1.2540 + // We only use the scaler when drawing to the window because, if we're not 1.2541 + // drawing to a window (eg a canvas), updates to that image will be ignored. 1.2542 + if (gHQDownscaling && aFilter == GraphicsFilter::FILTER_GOOD && 1.2543 + !mAnim && mDecoded && !mMultipart && CanQualityScale(aScale) && 1.2544 + (aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) { 1.2545 + gfxFloat factor = gHQDownscalingMinFactor / 1000.0; 1.2546 + 1.2547 + return (aScale.width < factor || aScale.height < factor); 1.2548 + } 1.2549 +#endif 1.2550 + 1.2551 + return false; 1.2552 +} 1.2553 + 1.2554 +void 1.2555 +RasterImage::ScalingStart(ScaleRequest* request) 1.2556 +{ 1.2557 + MOZ_ASSERT(request); 1.2558 + mScaleResult.scale = request->scale; 1.2559 + mScaleResult.status = SCALE_PENDING; 1.2560 + mScaleRequest = request; 1.2561 +} 1.2562 + 1.2563 +void 1.2564 +RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status) 1.2565 +{ 1.2566 + MOZ_ASSERT(status == SCALE_DONE || status == SCALE_INVALID); 1.2567 + MOZ_ASSERT(request); 1.2568 + 1.2569 + if (status == SCALE_DONE) { 1.2570 + MOZ_ASSERT(request->done); 1.2571 + 1.2572 + imgFrame *scaledFrame = request->dstFrame.forget(); 1.2573 + scaledFrame->ImageUpdated(scaledFrame->GetRect()); 1.2574 + scaledFrame->ApplyDirtToSurfaces(); 1.2575 + 1.2576 + if (mStatusTracker) { 1.2577 + mStatusTracker->FrameChanged(&request->srcRect); 1.2578 + } 1.2579 + 1.2580 + mScaleResult.status = SCALE_DONE; 1.2581 + mScaleResult.frame = scaledFrame; 1.2582 + mScaleResult.scale = request->scale; 1.2583 + } else { 1.2584 + mScaleResult.status = SCALE_INVALID; 1.2585 + mScaleResult.frame = nullptr; 1.2586 + } 1.2587 + 1.2588 + // If we were waiting for this scale to come through, forget the scale 1.2589 + // request. Otherwise, we still have a scale outstanding that it's possible 1.2590 + // for us to (want to) stop. 1.2591 + if (mScaleRequest == request) { 1.2592 + mScaleRequest = nullptr; 1.2593 + } 1.2594 +} 1.2595 + 1.2596 +bool 1.2597 +RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame, 1.2598 + gfxContext *aContext, 1.2599 + GraphicsFilter aFilter, 1.2600 + const gfxMatrix &aUserSpaceToImageSpace, 1.2601 + const gfxRect &aFill, 1.2602 + const nsIntRect &aSubimage, 1.2603 + uint32_t aFlags) 1.2604 +{ 1.2605 + imgFrame *frame = aFrame; 1.2606 + nsIntRect framerect = frame->GetRect(); 1.2607 + gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace; 1.2608 + gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace; 1.2609 + imageSpaceToUserSpace.Invert(); 1.2610 + gfxSize scale = imageSpaceToUserSpace.ScaleFactors(true); 1.2611 + nsIntRect subimage = aSubimage; 1.2612 + nsRefPtr<gfxASurface> surf; 1.2613 + 1.2614 + if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) { 1.2615 + // If scale factor is still the same that we scaled for and 1.2616 + // ScaleWorker isn't still working, then we can use pre-downscaled frame. 1.2617 + // If scale factor has changed, order new request. 1.2618 + // FIXME: Current implementation doesn't support pre-downscale 1.2619 + // mechanism for multiple sizes from same src, since we cache 1.2620 + // pre-downscaled frame only for the latest requested scale. 1.2621 + // The solution is to cache more than one scaled image frame 1.2622 + // for each RasterImage. 1.2623 + bool needScaleReq; 1.2624 + if (mScaleResult.status == SCALE_DONE && mScaleResult.scale == scale) { 1.2625 + // Grab and hold the surface to make sure the OS didn't destroy it 1.2626 + mScaleResult.frame->GetSurface(getter_AddRefs(surf)); 1.2627 + needScaleReq = !surf; 1.2628 + if (surf) { 1.2629 + frame = mScaleResult.frame; 1.2630 + userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width, 1.2631 + scale.height)); 1.2632 + 1.2633 + // Since we're switching to a scaled image, we need to transform the 1.2634 + // area of the subimage to draw accordingly, since imgFrame::Draw() 1.2635 + // doesn't know about scaled frames. 1.2636 + subimage.ScaleRoundOut(scale.width, scale.height); 1.2637 + } 1.2638 + } else { 1.2639 + needScaleReq = !(mScaleResult.status == SCALE_PENDING && 1.2640 + mScaleResult.scale == scale); 1.2641 + } 1.2642 + 1.2643 + // If we're not waiting for exactly this result, and there's only one 1.2644 + // instance of this image on this page, ask for a scale. 1.2645 + if (needScaleReq && mLockCount == 1) { 1.2646 + if (NS_FAILED(frame->LockImageData())) { 1.2647 + frame->UnlockImageData(); 1.2648 + return false; 1.2649 + } 1.2650 + 1.2651 + // If we have an outstanding request, signal it to stop (if it can). 1.2652 + if (mScaleRequest) { 1.2653 + mScaleRequest->stopped = true; 1.2654 + } 1.2655 + 1.2656 + nsRefPtr<ScaleRunner> runner = new ScaleRunner(this, scale, frame); 1.2657 + if (runner->IsOK()) { 1.2658 + if (!sScaleWorkerThread) { 1.2659 + NS_NewNamedThread("Image Scaler", getter_AddRefs(sScaleWorkerThread)); 1.2660 + ClearOnShutdown(&sScaleWorkerThread); 1.2661 + } 1.2662 + 1.2663 + sScaleWorkerThread->Dispatch(runner, NS_DISPATCH_NORMAL); 1.2664 + } 1.2665 + frame->UnlockImageData(); 1.2666 + } 1.2667 + } 1.2668 + 1.2669 + nsIntMargin padding(framerect.y, 1.2670 + mSize.width - framerect.XMost(), 1.2671 + mSize.height - framerect.YMost(), 1.2672 + framerect.x); 1.2673 + 1.2674 + return frame->Draw(aContext, aFilter, userSpaceToImageSpace, 1.2675 + aFill, padding, subimage, aFlags); 1.2676 +} 1.2677 + 1.2678 +//****************************************************************************** 1.2679 +/* [noscript] void draw(in gfxContext aContext, 1.2680 + * in gfxGraphicsFilter aFilter, 1.2681 + * [const] in gfxMatrix aUserSpaceToImageSpace, 1.2682 + * [const] in gfxRect aFill, 1.2683 + * [const] in nsIntRect aSubimage, 1.2684 + * [const] in nsIntSize aViewportSize, 1.2685 + * [const] in SVGImageContext aSVGContext, 1.2686 + * in uint32_t aWhichFrame, 1.2687 + * in uint32_t aFlags); */ 1.2688 +NS_IMETHODIMP 1.2689 +RasterImage::Draw(gfxContext *aContext, 1.2690 + GraphicsFilter aFilter, 1.2691 + const gfxMatrix &aUserSpaceToImageSpace, 1.2692 + const gfxRect &aFill, 1.2693 + const nsIntRect &aSubimage, 1.2694 + const nsIntSize& /*aViewportSize - ignored*/, 1.2695 + const SVGImageContext* /*aSVGContext - ignored*/, 1.2696 + uint32_t aWhichFrame, 1.2697 + uint32_t aFlags) 1.2698 +{ 1.2699 + if (aWhichFrame > FRAME_MAX_VALUE) 1.2700 + return NS_ERROR_INVALID_ARG; 1.2701 + 1.2702 + if (mError) 1.2703 + return NS_ERROR_FAILURE; 1.2704 + 1.2705 + // Disallowed in the API 1.2706 + if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE)) 1.2707 + return NS_ERROR_FAILURE; 1.2708 + 1.2709 + // Illegal -- you can't draw with non-default decode flags. 1.2710 + // (Disabling colorspace conversion might make sense to allow, but 1.2711 + // we don't currently.) 1.2712 + if ((aFlags & DECODE_FLAGS_MASK) != DECODE_FLAGS_DEFAULT) 1.2713 + return NS_ERROR_FAILURE; 1.2714 + 1.2715 + NS_ENSURE_ARG_POINTER(aContext); 1.2716 + 1.2717 + // We can only draw without discarding and redecoding in these cases: 1.2718 + // * We have the default decode flags. 1.2719 + // * We have exactly FLAG_DECODE_NO_PREMULTIPLY_ALPHA and the current frame 1.2720 + // is opaque. 1.2721 + bool haveDefaultFlags = (mFrameDecodeFlags == DECODE_FLAGS_DEFAULT); 1.2722 + bool haveSafeAlphaFlags = 1.2723 + (mFrameDecodeFlags == FLAG_DECODE_NO_PREMULTIPLY_ALPHA) && 1.2724 + FrameIsOpaque(FRAME_CURRENT); 1.2725 + 1.2726 + if (!(haveDefaultFlags || haveSafeAlphaFlags)) { 1.2727 + if (!CanForciblyDiscardAndRedecode()) 1.2728 + return NS_ERROR_NOT_AVAILABLE; 1.2729 + ForceDiscard(); 1.2730 + 1.2731 + mFrameDecodeFlags = DECODE_FLAGS_DEFAULT; 1.2732 + } 1.2733 + 1.2734 + // If this image is a candidate for discarding, reset its position in the 1.2735 + // discard tracker so we're less likely to discard it right away. 1.2736 + // 1.2737 + // (We don't normally draw unlocked images, so this conditition will usually 1.2738 + // be false. But we will draw unlocked images if image locking is globally 1.2739 + // disabled via the image.mem.allow_locking_in_content_processes pref.) 1.2740 + if (DiscardingActive()) { 1.2741 + DiscardTracker::Reset(&mDiscardTrackerNode); 1.2742 + } 1.2743 + 1.2744 + 1.2745 + if (IsUnlocked() && mStatusTracker) { 1.2746 + mStatusTracker->OnUnlockedDraw(); 1.2747 + } 1.2748 + 1.2749 + // We use !mDecoded && mHasSourceData to mean discarded. 1.2750 + if (!mDecoded && mHasSourceData) { 1.2751 + mDrawStartTime = TimeStamp::Now(); 1.2752 + } 1.2753 + 1.2754 + // If a synchronous draw is requested, flush anything that might be sitting around 1.2755 + if (aFlags & FLAG_SYNC_DECODE) { 1.2756 + nsresult rv = SyncDecode(); 1.2757 + NS_ENSURE_SUCCESS(rv, rv); 1.2758 + } 1.2759 + 1.2760 + uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0 1.2761 + : GetCurrentImgFrameIndex(); 1.2762 + imgFrame* frame = GetDrawableImgFrame(frameIndex); 1.2763 + if (!frame) { 1.2764 + return NS_OK; // Getting the frame (above) touches the image and kicks off decoding 1.2765 + } 1.2766 + 1.2767 + bool drawn = DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter, 1.2768 + aUserSpaceToImageSpace, aFill, 1.2769 + aSubimage, aFlags); 1.2770 + if (!drawn) { 1.2771 + // The OS threw out some or all of our buffer. Start decoding again. 1.2772 + ForceDiscard(); 1.2773 + WantDecodedFrames(); 1.2774 + return NS_OK; 1.2775 + } 1.2776 + 1.2777 + if (mDecoded && !mDrawStartTime.IsNull()) { 1.2778 + TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime; 1.2779 + Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, int32_t(drawLatency.ToMicroseconds())); 1.2780 + // clear the value of mDrawStartTime 1.2781 + mDrawStartTime = TimeStamp(); 1.2782 + } 1.2783 + 1.2784 + return NS_OK; 1.2785 +} 1.2786 + 1.2787 +//****************************************************************************** 1.2788 +/* void lockImage() */ 1.2789 +NS_IMETHODIMP 1.2790 +RasterImage::LockImage() 1.2791 +{ 1.2792 + MOZ_ASSERT(NS_IsMainThread(), 1.2793 + "Main thread to encourage serialization with UnlockImage"); 1.2794 + if (mError) 1.2795 + return NS_ERROR_FAILURE; 1.2796 + 1.2797 + // Cancel the discard timer if it's there 1.2798 + DiscardTracker::Remove(&mDiscardTrackerNode); 1.2799 + 1.2800 + // Increment the lock count 1.2801 + mLockCount++; 1.2802 + 1.2803 + return NS_OK; 1.2804 +} 1.2805 + 1.2806 +//****************************************************************************** 1.2807 +/* void unlockImage() */ 1.2808 +NS_IMETHODIMP 1.2809 +RasterImage::UnlockImage() 1.2810 +{ 1.2811 + MOZ_ASSERT(NS_IsMainThread(), 1.2812 + "Main thread to encourage serialization with LockImage"); 1.2813 + if (mError) 1.2814 + return NS_ERROR_FAILURE; 1.2815 + 1.2816 + // It's an error to call this function if the lock count is 0 1.2817 + NS_ABORT_IF_FALSE(mLockCount > 0, 1.2818 + "Calling UnlockImage with mLockCount == 0!"); 1.2819 + if (mLockCount == 0) 1.2820 + return NS_ERROR_ABORT; 1.2821 + 1.2822 + // We're locked, so discarding should not be active 1.2823 + NS_ABORT_IF_FALSE(!DiscardingActive(), "Locked, but discarding activated"); 1.2824 + 1.2825 + // Decrement our lock count 1.2826 + mLockCount--; 1.2827 + 1.2828 + // If we've decoded this image once before, we're currently decoding again, 1.2829 + // and our lock count is now zero (so nothing is forcing us to keep the 1.2830 + // decoded data around), try to cancel the decode and throw away whatever 1.2831 + // we've decoded. 1.2832 + if (mHasBeenDecoded && mDecoder && 1.2833 + mLockCount == 0 && CanForciblyDiscard()) { 1.2834 + PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG, 1.2835 + ("RasterImage[0x%p] canceling decode because image " 1.2836 + "is now unlocked.", this)); 1.2837 + ReentrantMonitorAutoEnter lock(mDecodingMonitor); 1.2838 + FinishedSomeDecoding(eShutdownIntent_NotNeeded); 1.2839 + ForceDiscard(); 1.2840 + return NS_OK; 1.2841 + } 1.2842 + 1.2843 + // Otherwise, we might still be a candidate for discarding in the future. If 1.2844 + // we are, add ourselves to the discard tracker. 1.2845 + if (CanDiscard()) { 1.2846 + nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode); 1.2847 + CONTAINER_ENSURE_SUCCESS(rv); 1.2848 + } 1.2849 + 1.2850 + return NS_OK; 1.2851 +} 1.2852 + 1.2853 +//****************************************************************************** 1.2854 +/* void requestDiscard() */ 1.2855 +NS_IMETHODIMP 1.2856 +RasterImage::RequestDiscard() 1.2857 +{ 1.2858 + if (CanDiscard() && CanForciblyDiscardAndRedecode()) { 1.2859 + ForceDiscard(); 1.2860 + } 1.2861 + 1.2862 + return NS_OK; 1.2863 +} 1.2864 + 1.2865 +// Flushes up to aMaxBytes to the decoder. 1.2866 +nsresult 1.2867 +RasterImage::DecodeSomeData(uint32_t aMaxBytes, DecodeStrategy aStrategy) 1.2868 +{ 1.2869 + // We should have a decoder if we get here 1.2870 + NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!"); 1.2871 + 1.2872 + mDecodingMonitor.AssertCurrentThreadIn(); 1.2873 + 1.2874 + // First, if we've just been called because we allocated a frame on the main 1.2875 + // thread, let the decoder deal with the data it set aside at that time by 1.2876 + // passing it a null buffer. 1.2877 + if (mDecodeRequest->mAllocatedNewFrame) { 1.2878 + mDecodeRequest->mAllocatedNewFrame = false; 1.2879 + nsresult rv = WriteToDecoder(nullptr, 0, aStrategy); 1.2880 + if (NS_FAILED(rv) || mDecoder->NeedsNewFrame()) { 1.2881 + return rv; 1.2882 + } 1.2883 + } 1.2884 + 1.2885 + // If we have nothing else to decode, return 1.2886 + if (mBytesDecoded == mSourceData.Length()) 1.2887 + return NS_OK; 1.2888 + 1.2889 + MOZ_ASSERT(mBytesDecoded < mSourceData.Length()); 1.2890 + 1.2891 + // write the proper amount of data 1.2892 + uint32_t bytesToDecode = std::min(aMaxBytes, 1.2893 + mSourceData.Length() - mBytesDecoded); 1.2894 + nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded, 1.2895 + bytesToDecode, 1.2896 + aStrategy); 1.2897 + 1.2898 + return rv; 1.2899 +} 1.2900 + 1.2901 +// There are various indicators that tell us we're finished with the decode 1.2902 +// task at hand and can shut down the decoder. 1.2903 +// 1.2904 +// This method may not be called if there is no decoder. 1.2905 +bool 1.2906 +RasterImage::IsDecodeFinished() 1.2907 +{ 1.2908 + // Precondition 1.2909 + mDecodingMonitor.AssertCurrentThreadIn(); 1.2910 + NS_ABORT_IF_FALSE(mDecoder, "Can't call IsDecodeFinished() without decoder!"); 1.2911 + 1.2912 + // The decode is complete if we got what we wanted. 1.2913 + if (mDecoder->IsSizeDecode()) { 1.2914 + if (mDecoder->HasSize()) { 1.2915 + return true; 1.2916 + } 1.2917 + } else if (mDecoder->GetDecodeDone()) { 1.2918 + return true; 1.2919 + } 1.2920 + 1.2921 + // If the decoder returned because it needed a new frame and we haven't 1.2922 + // written to it since then, the decoder may be storing data that it hasn't 1.2923 + // decoded yet. 1.2924 + if (mDecoder->NeedsNewFrame() || 1.2925 + (mDecodeRequest && mDecodeRequest->mAllocatedNewFrame)) { 1.2926 + return false; 1.2927 + } 1.2928 + 1.2929 + // Otherwise, if we have all the source data and wrote all the source data, 1.2930 + // we're done. 1.2931 + // 1.2932 + // (NB - This can be the case even for non-erroneous images because 1.2933 + // Decoder::GetDecodeDone() might not return true until after we call 1.2934 + // Decoder::Finish() in ShutdownDecoder()) 1.2935 + if (mHasSourceData && (mBytesDecoded == mSourceData.Length())) { 1.2936 + return true; 1.2937 + } 1.2938 + 1.2939 + // If we get here, assume it's not finished. 1.2940 + return false; 1.2941 +} 1.2942 + 1.2943 +// Indempotent error flagging routine. If a decoder is open, shuts it down. 1.2944 +void 1.2945 +RasterImage::DoError() 1.2946 +{ 1.2947 + // If we've flagged an error before, we have nothing to do 1.2948 + if (mError) 1.2949 + return; 1.2950 + 1.2951 + // We can't safely handle errors off-main-thread, so dispatch a worker to do it. 1.2952 + if (!NS_IsMainThread()) { 1.2953 + HandleErrorWorker::DispatchIfNeeded(this); 1.2954 + return; 1.2955 + } 1.2956 + 1.2957 + // Calling FinishedSomeDecoding and CurrentStatusTracker requires us to be in 1.2958 + // the decoding monitor. 1.2959 + ReentrantMonitorAutoEnter lock(mDecodingMonitor); 1.2960 + 1.2961 + // If we're mid-decode, shut down the decoder. 1.2962 + if (mDecoder) { 1.2963 + FinishedSomeDecoding(eShutdownIntent_Error); 1.2964 + } 1.2965 + 1.2966 + // Put the container in an error state. 1.2967 + mError = true; 1.2968 + 1.2969 + nsRefPtr<imgStatusTracker> statusTracker = CurrentStatusTracker(); 1.2970 + statusTracker->GetDecoderObserver()->OnError(); 1.2971 + 1.2972 + // Log our error 1.2973 + LOG_CONTAINER_ERROR; 1.2974 +} 1.2975 + 1.2976 +/* static */ void 1.2977 +RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage) 1.2978 +{ 1.2979 + if (!aImage->mPendingError) { 1.2980 + aImage->mPendingError = true; 1.2981 + nsRefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage); 1.2982 + NS_DispatchToMainThread(worker); 1.2983 + } 1.2984 +} 1.2985 + 1.2986 +RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage) 1.2987 + : mImage(aImage) 1.2988 +{ 1.2989 + MOZ_ASSERT(mImage, "Should have image"); 1.2990 +} 1.2991 + 1.2992 +NS_IMETHODIMP 1.2993 +RasterImage::HandleErrorWorker::Run() 1.2994 +{ 1.2995 + mImage->DoError(); 1.2996 + 1.2997 + return NS_OK; 1.2998 +} 1.2999 + 1.3000 +// nsIInputStream callback to copy the incoming image data directly to the 1.3001 +// RasterImage without processing. The RasterImage is passed as the closure. 1.3002 +// Always reads everything it gets, even if the data is erroneous. 1.3003 +NS_METHOD 1.3004 +RasterImage::WriteToRasterImage(nsIInputStream* /* unused */, 1.3005 + void* aClosure, 1.3006 + const char* aFromRawSegment, 1.3007 + uint32_t /* unused */, 1.3008 + uint32_t aCount, 1.3009 + uint32_t* aWriteCount) 1.3010 +{ 1.3011 + // Retrieve the RasterImage 1.3012 + RasterImage* image = static_cast<RasterImage*>(aClosure); 1.3013 + 1.3014 + // Copy the source data. Unless we hit OOM, we squelch the return value 1.3015 + // here, because returning an error means that ReadSegments stops 1.3016 + // reading data, violating our invariant that we read everything we get. 1.3017 + // If we hit OOM then we fail and the load is aborted. 1.3018 + nsresult rv = image->AddSourceData(aFromRawSegment, aCount); 1.3019 + if (rv == NS_ERROR_OUT_OF_MEMORY) { 1.3020 + image->DoError(); 1.3021 + return rv; 1.3022 + } 1.3023 + 1.3024 + // We wrote everything we got 1.3025 + *aWriteCount = aCount; 1.3026 + 1.3027 + return NS_OK; 1.3028 +} 1.3029 + 1.3030 +bool 1.3031 +RasterImage::ShouldAnimate() 1.3032 +{ 1.3033 + return ImageResource::ShouldAnimate() && GetNumFrames() >= 2 && 1.3034 + !mAnimationFinished; 1.3035 +} 1.3036 + 1.3037 +/* readonly attribute uint32_t framesNotified; */ 1.3038 +#ifdef DEBUG 1.3039 +NS_IMETHODIMP 1.3040 +RasterImage::GetFramesNotified(uint32_t *aFramesNotified) 1.3041 +{ 1.3042 + NS_ENSURE_ARG_POINTER(aFramesNotified); 1.3043 + 1.3044 + *aFramesNotified = mFramesNotified; 1.3045 + 1.3046 + return NS_OK; 1.3047 +} 1.3048 +#endif 1.3049 + 1.3050 +nsresult 1.3051 +RasterImage::RequestDecodeIfNeeded(nsresult aStatus, 1.3052 + eShutdownIntent aIntent, 1.3053 + bool aDone, 1.3054 + bool aWasSize) 1.3055 +{ 1.3056 + MOZ_ASSERT(NS_IsMainThread()); 1.3057 + 1.3058 + // If we were a size decode and a full decode was requested, now's the time. 1.3059 + if (NS_SUCCEEDED(aStatus) && 1.3060 + aIntent == eShutdownIntent_Done && 1.3061 + aDone && 1.3062 + aWasSize && 1.3063 + mWantFullDecode) { 1.3064 + mWantFullDecode = false; 1.3065 + 1.3066 + // If we're not meant to be storing source data and we just got the size, 1.3067 + // we need to synchronously flush all the data we got to a full decoder. 1.3068 + // When that decoder is shut down, we'll also clear our source data. 1.3069 + return StoringSourceData() ? RequestDecode() 1.3070 + : SyncDecode(); 1.3071 + } 1.3072 + 1.3073 + // We don't need a full decode right now, so just return the existing status. 1.3074 + return aStatus; 1.3075 +} 1.3076 + 1.3077 +nsresult 1.3078 +RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_Done */, 1.3079 + DecodeRequest* aRequest /* = nullptr */) 1.3080 +{ 1.3081 + MOZ_ASSERT(NS_IsMainThread()); 1.3082 + 1.3083 + mDecodingMonitor.AssertCurrentThreadIn(); 1.3084 + 1.3085 + nsRefPtr<DecodeRequest> request; 1.3086 + if (aRequest) { 1.3087 + request = aRequest; 1.3088 + } else { 1.3089 + request = mDecodeRequest; 1.3090 + } 1.3091 + 1.3092 + // Ensure that, if the decoder is the last reference to the image, we don't 1.3093 + // destroy it by destroying the decoder. 1.3094 + nsRefPtr<RasterImage> image(this); 1.3095 + 1.3096 + bool done = false; 1.3097 + bool wasSize = false; 1.3098 + nsresult rv = NS_OK; 1.3099 + 1.3100 + if (image->mDecoder) { 1.3101 + image->mDecoder->MarkFrameDirty(); 1.3102 + 1.3103 + if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) { 1.3104 + Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount); 1.3105 + } 1.3106 + 1.3107 + if (!image->mHasSize && image->mDecoder->HasSize()) { 1.3108 + image->mDecoder->SetSizeOnImage(); 1.3109 + } 1.3110 + 1.3111 + // If the decode finished, or we're specifically being told to shut down, 1.3112 + // tell the image and shut down the decoder. 1.3113 + if (image->IsDecodeFinished() || aIntent != eShutdownIntent_Done) { 1.3114 + done = true; 1.3115 + 1.3116 + // Hold on to a reference to the decoder until we're done with it 1.3117 + nsRefPtr<Decoder> decoder = image->mDecoder; 1.3118 + 1.3119 + wasSize = decoder->IsSizeDecode(); 1.3120 + 1.3121 + // Do some telemetry if this isn't a size decode. 1.3122 + if (request && !wasSize) { 1.3123 + Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME, 1.3124 + int32_t(request->mDecodeTime.ToMicroseconds())); 1.3125 + 1.3126 + // We record the speed for only some decoders. The rest have 1.3127 + // SpeedHistogram return HistogramCount. 1.3128 + Telemetry::ID id = decoder->SpeedHistogram(); 1.3129 + if (id < Telemetry::HistogramCount) { 1.3130 + int32_t KBps = int32_t(request->mImage->mBytesDecoded / 1.3131 + (1024 * request->mDecodeTime.ToSeconds())); 1.3132 + Telemetry::Accumulate(id, KBps); 1.3133 + } 1.3134 + } 1.3135 + 1.3136 + // We need to shut down the decoder first, in order to ensure all 1.3137 + // decoding routines have been finished. 1.3138 + rv = image->ShutdownDecoder(aIntent); 1.3139 + if (NS_FAILED(rv)) { 1.3140 + image->DoError(); 1.3141 + } 1.3142 + } 1.3143 + } 1.3144 + 1.3145 + ImageStatusDiff diff = 1.3146 + request ? image->mStatusTracker->Difference(request->mStatusTracker) 1.3147 + : image->mStatusTracker->DecodeStateAsDifference(); 1.3148 + image->mStatusTracker->ApplyDifference(diff); 1.3149 + 1.3150 + if (mNotifying) { 1.3151 + // Accumulate the status changes. We don't permit recursive notifications 1.3152 + // because they cause subtle concurrency bugs, so we'll delay sending out 1.3153 + // the notifications until we pop back to the lowest invocation of 1.3154 + // FinishedSomeDecoding on the stack. 1.3155 + NS_WARNING("Recursively notifying in RasterImage::FinishedSomeDecoding!"); 1.3156 + mStatusDiff.Combine(diff); 1.3157 + } else { 1.3158 + MOZ_ASSERT(mStatusDiff.IsNoChange(), "Shouldn't have an accumulated change at this point"); 1.3159 + 1.3160 + while (!diff.IsNoChange()) { 1.3161 + // Tell the observers what happened. 1.3162 + mNotifying = true; 1.3163 + image->mStatusTracker->SyncNotifyDifference(diff); 1.3164 + mNotifying = false; 1.3165 + 1.3166 + // Gather any status changes that may have occurred as a result of sending 1.3167 + // out the previous notifications. If there were any, we'll send out 1.3168 + // notifications for them next. 1.3169 + diff = mStatusDiff; 1.3170 + mStatusDiff = ImageStatusDiff::NoChange(); 1.3171 + } 1.3172 + } 1.3173 + 1.3174 + return RequestDecodeIfNeeded(rv, aIntent, done, wasSize); 1.3175 +} 1.3176 + 1.3177 +NS_IMPL_ISUPPORTS(RasterImage::DecodePool, 1.3178 + nsIObserver) 1.3179 + 1.3180 +/* static */ RasterImage::DecodePool* 1.3181 +RasterImage::DecodePool::Singleton() 1.3182 +{ 1.3183 + if (!sSingleton) { 1.3184 + MOZ_ASSERT(NS_IsMainThread()); 1.3185 + sSingleton = new DecodePool(); 1.3186 + ClearOnShutdown(&sSingleton); 1.3187 + } 1.3188 + 1.3189 + return sSingleton; 1.3190 +} 1.3191 + 1.3192 +already_AddRefed<nsIEventTarget> 1.3193 +RasterImage::DecodePool::GetEventTarget() 1.3194 +{ 1.3195 + nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool); 1.3196 + return target.forget(); 1.3197 +} 1.3198 + 1.3199 +#ifdef MOZ_NUWA_PROCESS 1.3200 + 1.3201 +class RIDThreadPoolListener : public nsIThreadPoolListener 1.3202 +{ 1.3203 +public: 1.3204 + NS_DECL_THREADSAFE_ISUPPORTS 1.3205 + NS_DECL_NSITHREADPOOLLISTENER 1.3206 + 1.3207 + RIDThreadPoolListener() {} 1.3208 + ~RIDThreadPoolListener() {} 1.3209 +}; 1.3210 + 1.3211 +NS_IMPL_ISUPPORTS(RIDThreadPoolListener, nsIThreadPoolListener) 1.3212 + 1.3213 +NS_IMETHODIMP 1.3214 +RIDThreadPoolListener::OnThreadCreated() 1.3215 +{ 1.3216 + if (IsNuwaProcess()) { 1.3217 + NuwaMarkCurrentThread((void (*)(void *))nullptr, nullptr); 1.3218 + } 1.3219 + return NS_OK; 1.3220 +} 1.3221 + 1.3222 +NS_IMETHODIMP 1.3223 +RIDThreadPoolListener::OnThreadShuttingDown() 1.3224 +{ 1.3225 + return NS_OK; 1.3226 +} 1.3227 + 1.3228 +#endif // MOZ_NUWA_PROCESS 1.3229 + 1.3230 +RasterImage::DecodePool::DecodePool() 1.3231 + : mThreadPoolMutex("Thread Pool") 1.3232 +{ 1.3233 + if (gMultithreadedDecoding) { 1.3234 + mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID); 1.3235 + if (mThreadPool) { 1.3236 + mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder")); 1.3237 + uint32_t limit; 1.3238 + if (gDecodingThreadLimit <= 0) { 1.3239 + limit = std::max(PR_GetNumberOfProcessors(), 2) - 1; 1.3240 + } else { 1.3241 + limit = static_cast<uint32_t>(gDecodingThreadLimit); 1.3242 + } 1.3243 + 1.3244 + mThreadPool->SetThreadLimit(limit); 1.3245 + mThreadPool->SetIdleThreadLimit(limit); 1.3246 + 1.3247 +#ifdef MOZ_NUWA_PROCESS 1.3248 + if (IsNuwaProcess()) { 1.3249 + mThreadPool->SetListener(new RIDThreadPoolListener()); 1.3250 + } 1.3251 +#endif 1.3252 + 1.3253 + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); 1.3254 + if (obsSvc) { 1.3255 + obsSvc->AddObserver(this, "xpcom-shutdown-threads", false); 1.3256 + } 1.3257 + } 1.3258 + } 1.3259 +} 1.3260 + 1.3261 +RasterImage::DecodePool::~DecodePool() 1.3262 +{ 1.3263 + MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!"); 1.3264 +} 1.3265 + 1.3266 +NS_IMETHODIMP 1.3267 +RasterImage::DecodePool::Observe(nsISupports *subject, const char *topic, 1.3268 + const char16_t *data) 1.3269 +{ 1.3270 + NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops"); 1.3271 + 1.3272 + nsCOMPtr<nsIThreadPool> threadPool; 1.3273 + 1.3274 + { 1.3275 + MutexAutoLock threadPoolLock(mThreadPoolMutex); 1.3276 + threadPool = mThreadPool; 1.3277 + mThreadPool = nullptr; 1.3278 + } 1.3279 + 1.3280 + if (threadPool) { 1.3281 + threadPool->Shutdown(); 1.3282 + } 1.3283 + 1.3284 + return NS_OK; 1.3285 +} 1.3286 + 1.3287 +void 1.3288 +RasterImage::DecodePool::RequestDecode(RasterImage* aImg) 1.3289 +{ 1.3290 + MOZ_ASSERT(aImg->mDecoder); 1.3291 + aImg->mDecodingMonitor.AssertCurrentThreadIn(); 1.3292 + 1.3293 + // If we're currently waiting on a new frame for this image, we can't do any 1.3294 + // decoding. 1.3295 + if (!aImg->mDecoder->NeedsNewFrame()) { 1.3296 + // No matter whether this is currently being decoded, we need to update the 1.3297 + // number of bytes we want it to decode. 1.3298 + aImg->mDecodeRequest->mBytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded; 1.3299 + 1.3300 + if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_PENDING || 1.3301 + aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_ACTIVE) { 1.3302 + // The image is already in our list of images to decode, or currently being 1.3303 + // decoded, so we don't have to do anything else. 1.3304 + return; 1.3305 + } 1.3306 + 1.3307 + aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_PENDING; 1.3308 + nsRefPtr<DecodeJob> job = new DecodeJob(aImg->mDecodeRequest, aImg); 1.3309 + 1.3310 + MutexAutoLock threadPoolLock(mThreadPoolMutex); 1.3311 + if (!gMultithreadedDecoding || !mThreadPool) { 1.3312 + NS_DispatchToMainThread(job); 1.3313 + } else { 1.3314 + mThreadPool->Dispatch(job, nsIEventTarget::DISPATCH_NORMAL); 1.3315 + } 1.3316 + } 1.3317 +} 1.3318 + 1.3319 +void 1.3320 +RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg, DecodeStrategy aStrategy) 1.3321 +{ 1.3322 + MOZ_ASSERT(NS_IsMainThread()); 1.3323 + aImg->mDecodingMonitor.AssertCurrentThreadIn(); 1.3324 + 1.3325 + if (aImg->mDecodeRequest) { 1.3326 + // If the image is waiting for decode work to be notified, go ahead and do that. 1.3327 + if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) { 1.3328 + aImg->FinishedSomeDecoding(); 1.3329 + } 1.3330 + } 1.3331 + 1.3332 + DecodeSomeOfImage(aImg, aStrategy); 1.3333 + 1.3334 + aImg->FinishedSomeDecoding(); 1.3335 + 1.3336 + // If the decoder needs a new frame, enqueue an event to get it; that event 1.3337 + // will enqueue another decode request when it's done. 1.3338 + if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) { 1.3339 + FrameNeededWorker::GetNewFrame(aImg); 1.3340 + } else { 1.3341 + // If we aren't yet finished decoding and we have more data in hand, add 1.3342 + // this request to the back of the priority list. 1.3343 + if (aImg->mDecoder && 1.3344 + !aImg->mError && 1.3345 + !aImg->IsDecodeFinished() && 1.3346 + aImg->mSourceData.Length() > aImg->mBytesDecoded) { 1.3347 + RequestDecode(aImg); 1.3348 + } 1.3349 + } 1.3350 +} 1.3351 + 1.3352 +/* static */ void 1.3353 +RasterImage::DecodePool::StopDecoding(RasterImage* aImg) 1.3354 +{ 1.3355 + aImg->mDecodingMonitor.AssertCurrentThreadIn(); 1.3356 + 1.3357 + // If we haven't got a decode request, we're not currently decoding. (Having 1.3358 + // a decode request doesn't imply we *are* decoding, though.) 1.3359 + if (aImg->mDecodeRequest) { 1.3360 + aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_STOPPED; 1.3361 + } 1.3362 +} 1.3363 + 1.3364 +NS_IMETHODIMP 1.3365 +RasterImage::DecodePool::DecodeJob::Run() 1.3366 +{ 1.3367 + ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor); 1.3368 + 1.3369 + // If we were interrupted, we shouldn't do any work. 1.3370 + if (mRequest->mRequestStatus == DecodeRequest::REQUEST_STOPPED) { 1.3371 + DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest); 1.3372 + return NS_OK; 1.3373 + } 1.3374 + 1.3375 + // If someone came along and synchronously decoded us, there's nothing for us to do. 1.3376 + if (!mImage->mDecoder || mImage->IsDecodeFinished()) { 1.3377 + DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest); 1.3378 + return NS_OK; 1.3379 + } 1.3380 + 1.3381 + // If we're a decode job that's been enqueued since a previous decode that 1.3382 + // still needs a new frame, we can't do anything. Wait until the 1.3383 + // FrameNeededWorker enqueues another frame. 1.3384 + if (mImage->mDecoder->NeedsNewFrame()) { 1.3385 + return NS_OK; 1.3386 + } 1.3387 + 1.3388 + mRequest->mRequestStatus = DecodeRequest::REQUEST_ACTIVE; 1.3389 + 1.3390 + uint32_t oldByteCount = mImage->mBytesDecoded; 1.3391 + 1.3392 + DecodeType type = DECODE_TYPE_UNTIL_DONE_BYTES; 1.3393 + 1.3394 + // Multithreaded decoding can be disabled. If we've done so, we don't want to 1.3395 + // monopolize the main thread, and will allow a timeout in DecodeSomeOfImage. 1.3396 + if (NS_IsMainThread()) { 1.3397 + type = DECODE_TYPE_UNTIL_TIME; 1.3398 + } 1.3399 + 1.3400 + DecodePool::Singleton()->DecodeSomeOfImage(mImage, DECODE_ASYNC, type, mRequest->mBytesToDecode); 1.3401 + 1.3402 + uint32_t bytesDecoded = mImage->mBytesDecoded - oldByteCount; 1.3403 + 1.3404 + mRequest->mRequestStatus = DecodeRequest::REQUEST_WORK_DONE; 1.3405 + 1.3406 + // If the decoder needs a new frame, enqueue an event to get it; that event 1.3407 + // will enqueue another decode request when it's done. 1.3408 + if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) { 1.3409 + FrameNeededWorker::GetNewFrame(mImage); 1.3410 + } 1.3411 + // If we aren't yet finished decoding and we have more data in hand, add 1.3412 + // this request to the back of the list. 1.3413 + else if (mImage->mDecoder && 1.3414 + !mImage->mError && 1.3415 + !mImage->mPendingError && 1.3416 + !mImage->IsDecodeFinished() && 1.3417 + bytesDecoded < mRequest->mBytesToDecode && 1.3418 + bytesDecoded > 0) { 1.3419 + DecodePool::Singleton()->RequestDecode(mImage); 1.3420 + } else { 1.3421 + // Nothing more for us to do - let everyone know what happened. 1.3422 + DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest); 1.3423 + } 1.3424 + 1.3425 + return NS_OK; 1.3426 +} 1.3427 + 1.3428 +RasterImage::DecodePool::DecodeJob::~DecodeJob() 1.3429 +{ 1.3430 + if (gMultithreadedDecoding) { 1.3431 + // Dispatch mImage to main thread to prevent mImage from being destructed by decode thread. 1.3432 + nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); 1.3433 + NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!"); 1.3434 + if (mainThread) { 1.3435 + // Handle ambiguous nsISupports inheritance 1.3436 + RasterImage* rawImg = nullptr; 1.3437 + mImage.swap(rawImg); 1.3438 + DebugOnly<nsresult> rv = NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg)); 1.3439 + MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread"); 1.3440 + } 1.3441 + } 1.3442 +} 1.3443 + 1.3444 +nsresult 1.3445 +RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg) 1.3446 +{ 1.3447 + MOZ_ASSERT(NS_IsMainThread()); 1.3448 + ReentrantMonitorAutoEnter lock(aImg->mDecodingMonitor); 1.3449 + 1.3450 + if (aImg->mDecodeRequest) { 1.3451 + // If the image is waiting for decode work to be notified, go ahead and do that. 1.3452 + if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) { 1.3453 + nsresult rv = aImg->FinishedSomeDecoding(); 1.3454 + if (NS_FAILED(rv)) { 1.3455 + aImg->DoError(); 1.3456 + return rv; 1.3457 + } 1.3458 + } 1.3459 + } 1.3460 + 1.3461 + // We use DECODE_ASYNC here because we just want to get the size information 1.3462 + // here and defer the rest of the work. 1.3463 + nsresult rv = DecodeSomeOfImage(aImg, DECODE_ASYNC, DECODE_TYPE_UNTIL_SIZE); 1.3464 + if (NS_FAILED(rv)) { 1.3465 + return rv; 1.3466 + } 1.3467 + 1.3468 + // If the decoder needs a new frame, enqueue an event to get it; that event 1.3469 + // will enqueue another decode request when it's done. 1.3470 + if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) { 1.3471 + FrameNeededWorker::GetNewFrame(aImg); 1.3472 + } else { 1.3473 + rv = aImg->FinishedSomeDecoding(); 1.3474 + } 1.3475 + 1.3476 + return rv; 1.3477 +} 1.3478 + 1.3479 +nsresult 1.3480 +RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg, 1.3481 + DecodeStrategy aStrategy, 1.3482 + DecodeType aDecodeType /* = DECODE_TYPE_UNTIL_TIME */, 1.3483 + uint32_t bytesToDecode /* = 0 */) 1.3484 +{ 1.3485 + NS_ABORT_IF_FALSE(aImg->mInitialized, 1.3486 + "Worker active for uninitialized container!"); 1.3487 + aImg->mDecodingMonitor.AssertCurrentThreadIn(); 1.3488 + 1.3489 + // If an error is flagged, it probably happened while we were waiting 1.3490 + // in the event queue. 1.3491 + if (aImg->mError) 1.3492 + return NS_OK; 1.3493 + 1.3494 + // If there is an error worker pending (say because the main thread has enqueued 1.3495 + // another decode request for us before processing the error worker) then bail out. 1.3496 + if (aImg->mPendingError) 1.3497 + return NS_OK; 1.3498 + 1.3499 + // If mDecoded or we don't have a decoder, we must have finished already (for 1.3500 + // example, a synchronous decode request came while the worker was pending). 1.3501 + if (!aImg->mDecoder || aImg->mDecoded) 1.3502 + return NS_OK; 1.3503 + 1.3504 + // If we're doing synchronous decodes, and we're waiting on a new frame for 1.3505 + // this image, get it now. 1.3506 + if (aStrategy == DECODE_SYNC && aImg->mDecoder->NeedsNewFrame()) { 1.3507 + MOZ_ASSERT(NS_IsMainThread()); 1.3508 + 1.3509 + aImg->mDecoder->AllocateFrame(); 1.3510 + aImg->mDecodeRequest->mAllocatedNewFrame = true; 1.3511 + } 1.3512 + 1.3513 + // If we're not synchronous, we can't allocate a frame right now. 1.3514 + else if (aImg->mDecoder->NeedsNewFrame()) { 1.3515 + return NS_OK; 1.3516 + } 1.3517 + 1.3518 + nsRefPtr<Decoder> decoderKungFuDeathGrip = aImg->mDecoder; 1.3519 + 1.3520 + uint32_t maxBytes; 1.3521 + if (aImg->mDecoder->IsSizeDecode()) { 1.3522 + // Decode all available data if we're a size decode; they're cheap, and we 1.3523 + // want them to be more or less synchronous. 1.3524 + maxBytes = aImg->mSourceData.Length(); 1.3525 + } else { 1.3526 + // We're only guaranteed to decode this many bytes, so in particular, 1.3527 + // gDecodeBytesAtATime should be set high enough for us to read the size 1.3528 + // from most images. 1.3529 + maxBytes = gDecodeBytesAtATime; 1.3530 + } 1.3531 + 1.3532 + if (bytesToDecode == 0) { 1.3533 + bytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded; 1.3534 + } 1.3535 + 1.3536 + int32_t chunkCount = 0; 1.3537 + TimeStamp start = TimeStamp::Now(); 1.3538 + TimeStamp deadline = start + TimeDuration::FromMilliseconds(gMaxMSBeforeYield); 1.3539 + 1.3540 + // We keep decoding chunks until: 1.3541 + // * we don't have any data left to decode, 1.3542 + // * the decode completes, 1.3543 + // * we're an UNTIL_SIZE decode and we get the size, or 1.3544 + // * we run out of time. 1.3545 + // We also try to decode at least one "chunk" if we've allocated a new frame, 1.3546 + // even if we have no more data to send to the decoder. 1.3547 + while ((aImg->mSourceData.Length() > aImg->mBytesDecoded && 1.3548 + bytesToDecode > 0 && 1.3549 + !aImg->IsDecodeFinished() && 1.3550 + !(aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize) && 1.3551 + !aImg->mDecoder->NeedsNewFrame()) || 1.3552 + (aImg->mDecodeRequest && aImg->mDecodeRequest->mAllocatedNewFrame)) { 1.3553 + chunkCount++; 1.3554 + uint32_t chunkSize = std::min(bytesToDecode, maxBytes); 1.3555 + nsresult rv = aImg->DecodeSomeData(chunkSize, aStrategy); 1.3556 + if (NS_FAILED(rv)) { 1.3557 + aImg->DoError(); 1.3558 + return rv; 1.3559 + } 1.3560 + 1.3561 + bytesToDecode -= chunkSize; 1.3562 + 1.3563 + // Yield if we've been decoding for too long. We check this _after_ decoding 1.3564 + // a chunk to ensure that we don't yield without doing any decoding. 1.3565 + if (aDecodeType == DECODE_TYPE_UNTIL_TIME && TimeStamp::Now() >= deadline) 1.3566 + break; 1.3567 + } 1.3568 + 1.3569 + if (aImg->mDecodeRequest) { 1.3570 + aImg->mDecodeRequest->mDecodeTime += (TimeStamp::Now() - start); 1.3571 + aImg->mDecodeRequest->mChunkCount += chunkCount; 1.3572 + } 1.3573 + 1.3574 + // Flush invalidations (and therefore paint) now that we've decoded all the 1.3575 + // chunks we're going to. 1.3576 + // 1.3577 + // However, don't paint if: 1.3578 + // 1.3579 + // * This was an until-size decode. Until-size decodes are always followed 1.3580 + // by normal decodes, so don't bother painting. 1.3581 + // 1.3582 + // * The decoder flagged an error. The decoder may have written garbage 1.3583 + // into the output buffer; don't paint it to the screen. 1.3584 + // 1.3585 + // * We have all the source data. This disables progressive display of 1.3586 + // previously-decoded images, thus letting us finish decoding faster, 1.3587 + // since we don't waste time painting while we decode. 1.3588 + // Decoder::PostFrameStop() will flush invalidations once the decode is 1.3589 + // done. 1.3590 + 1.3591 + if (aDecodeType != DECODE_TYPE_UNTIL_SIZE && 1.3592 + !aImg->mDecoder->HasError() && 1.3593 + !aImg->mHasSourceData) { 1.3594 + aImg->mInDecoder = true; 1.3595 + aImg->mDecoder->FlushInvalidations(); 1.3596 + aImg->mInDecoder = false; 1.3597 + } 1.3598 + 1.3599 + return NS_OK; 1.3600 +} 1.3601 + 1.3602 +RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeRequest* request) 1.3603 + : mImage(image) 1.3604 + , mRequest(request) 1.3605 +{} 1.3606 + 1.3607 +void 1.3608 +RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request) 1.3609 +{ 1.3610 + image->mDecodingMonitor.AssertCurrentThreadIn(); 1.3611 + 1.3612 + nsCOMPtr<nsIRunnable> worker = new DecodeDoneWorker(image, request); 1.3613 + NS_DispatchToMainThread(worker); 1.3614 +} 1.3615 + 1.3616 +NS_IMETHODIMP 1.3617 +RasterImage::DecodeDoneWorker::Run() 1.3618 +{ 1.3619 + MOZ_ASSERT(NS_IsMainThread()); 1.3620 + ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor); 1.3621 + 1.3622 + mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest); 1.3623 + 1.3624 + return NS_OK; 1.3625 +} 1.3626 + 1.3627 +RasterImage::FrameNeededWorker::FrameNeededWorker(RasterImage* image) 1.3628 + : mImage(image) 1.3629 +{} 1.3630 + 1.3631 + 1.3632 +void 1.3633 +RasterImage::FrameNeededWorker::GetNewFrame(RasterImage* image) 1.3634 +{ 1.3635 + nsCOMPtr<nsIRunnable> worker = new FrameNeededWorker(image); 1.3636 + NS_DispatchToMainThread(worker); 1.3637 +} 1.3638 + 1.3639 +NS_IMETHODIMP 1.3640 +RasterImage::FrameNeededWorker::Run() 1.3641 +{ 1.3642 + ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor); 1.3643 + nsresult rv = NS_OK; 1.3644 + 1.3645 + // If we got a synchronous decode in the mean time, we don't need to do 1.3646 + // anything. 1.3647 + if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) { 1.3648 + rv = mImage->mDecoder->AllocateFrame(); 1.3649 + mImage->mDecodeRequest->mAllocatedNewFrame = true; 1.3650 + } 1.3651 + 1.3652 + if (NS_SUCCEEDED(rv) && mImage->mDecoder) { 1.3653 + // By definition, we're not done decoding, so enqueue us for more decoding. 1.3654 + DecodePool::Singleton()->RequestDecode(mImage); 1.3655 + } 1.3656 + 1.3657 + return NS_OK; 1.3658 +} 1.3659 + 1.3660 +} // namespace image 1.3661 +} // namespace mozilla