image/src/RasterImage.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 // Must #include ImageLogging.h before any IPDL-generated files or other files that #include prlog.h
michael@0 7 #include "ImageLogging.h"
michael@0 8
michael@0 9 #include "RasterImage.h"
michael@0 10
michael@0 11 #include "base/histogram.h"
michael@0 12 #include "gfxPlatform.h"
michael@0 13 #include "nsComponentManagerUtils.h"
michael@0 14 #include "imgDecoderObserver.h"
michael@0 15 #include "nsError.h"
michael@0 16 #include "Decoder.h"
michael@0 17 #include "nsAutoPtr.h"
michael@0 18 #include "prenv.h"
michael@0 19 #include "prsystem.h"
michael@0 20 #include "ImageContainer.h"
michael@0 21 #include "Layers.h"
michael@0 22 #include "nsPresContext.h"
michael@0 23 #include "nsIThreadPool.h"
michael@0 24 #include "nsXPCOMCIDInternal.h"
michael@0 25 #include "nsIObserverService.h"
michael@0 26 #include "FrameAnimator.h"
michael@0 27
michael@0 28 #include "nsPNGDecoder.h"
michael@0 29 #include "nsGIFDecoder2.h"
michael@0 30 #include "nsJPEGDecoder.h"
michael@0 31 #include "nsBMPDecoder.h"
michael@0 32 #include "nsICODecoder.h"
michael@0 33 #include "nsIconDecoder.h"
michael@0 34
michael@0 35 #include "gfxContext.h"
michael@0 36
michael@0 37 #include "mozilla/gfx/2D.h"
michael@0 38 #include "mozilla/RefPtr.h"
michael@0 39 #include "mozilla/MemoryReporting.h"
michael@0 40 #include "mozilla/Services.h"
michael@0 41 #include "mozilla/Preferences.h"
michael@0 42 #include <stdint.h>
michael@0 43 #include "mozilla/Telemetry.h"
michael@0 44 #include "mozilla/TimeStamp.h"
michael@0 45 #include "mozilla/ClearOnShutdown.h"
michael@0 46 #include "mozilla/gfx/Scale.h"
michael@0 47
michael@0 48 #include "GeckoProfiler.h"
michael@0 49 #include "gfx2DGlue.h"
michael@0 50 #include <algorithm>
michael@0 51
michael@0 52 #ifdef MOZ_NUWA_PROCESS
michael@0 53 #include "ipc/Nuwa.h"
michael@0 54 #endif
michael@0 55
michael@0 56 using namespace mozilla;
michael@0 57 using namespace mozilla::gfx;
michael@0 58 using namespace mozilla::image;
michael@0 59 using namespace mozilla::layers;
michael@0 60
michael@0 61 // a mask for flags that will affect the decoding
michael@0 62 #define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
michael@0 63 #define DECODE_FLAGS_DEFAULT 0
michael@0 64
michael@0 65 /* Accounting for compressed data */
michael@0 66 #if defined(PR_LOGGING)
michael@0 67 static PRLogModuleInfo *
michael@0 68 GetCompressedImageAccountingLog()
michael@0 69 {
michael@0 70 static PRLogModuleInfo *sLog;
michael@0 71 if (!sLog)
michael@0 72 sLog = PR_NewLogModule("CompressedImageAccounting");
michael@0 73 return sLog;
michael@0 74 }
michael@0 75 #else
michael@0 76 #define GetCompressedImageAccountingLog()
michael@0 77 #endif
michael@0 78
michael@0 79 // Tweakable progressive decoding parameters. These are initialized to 0 here
michael@0 80 // because otherwise, we have to initialize them in a static initializer, which
michael@0 81 // makes us slower to start up.
michael@0 82 static uint32_t gDecodeBytesAtATime = 0;
michael@0 83 static uint32_t gMaxMSBeforeYield = 0;
michael@0 84 static bool gHQDownscaling = false;
michael@0 85 // This is interpreted as a floating-point value / 1000
michael@0 86 static uint32_t gHQDownscalingMinFactor = 1000;
michael@0 87 static bool gMultithreadedDecoding = true;
michael@0 88 static int32_t gDecodingThreadLimit = -1;
michael@0 89 // The number of pixels in a 5 megapixel decoded image.
michael@0 90 // Equivalent to an example 3125x1600 resolution.
michael@0 91 static uint32_t gHQUpscalingMaxSize = 20971520;
michael@0 92
michael@0 93 // The maximum number of times any one RasterImage was decoded. This is only
michael@0 94 // used for statistics.
michael@0 95 static int32_t sMaxDecodeCount = 0;
michael@0 96
michael@0 97 static void
michael@0 98 InitPrefCaches()
michael@0 99 {
michael@0 100 Preferences::AddUintVarCache(&gDecodeBytesAtATime,
michael@0 101 "image.mem.decode_bytes_at_a_time", 200000);
michael@0 102 Preferences::AddUintVarCache(&gMaxMSBeforeYield,
michael@0 103 "image.mem.max_ms_before_yield", 400);
michael@0 104 Preferences::AddBoolVarCache(&gHQDownscaling,
michael@0 105 "image.high_quality_downscaling.enabled", false);
michael@0 106 Preferences::AddUintVarCache(&gHQDownscalingMinFactor,
michael@0 107 "image.high_quality_downscaling.min_factor", 1000);
michael@0 108 Preferences::AddBoolVarCache(&gMultithreadedDecoding,
michael@0 109 "image.multithreaded_decoding.enabled", true);
michael@0 110 Preferences::AddIntVarCache(&gDecodingThreadLimit,
michael@0 111 "image.multithreaded_decoding.limit", -1);
michael@0 112 Preferences::AddUintVarCache(&gHQUpscalingMaxSize,
michael@0 113 "image.high_quality_upscaling.max_size", 20971520);
michael@0 114 }
michael@0 115
michael@0 116 /* We define our own error checking macros here for 2 reasons:
michael@0 117 *
michael@0 118 * 1) Most of the failures we encounter here will (hopefully) be
michael@0 119 * the result of decoding failures (ie, bad data) and not code
michael@0 120 * failures. As such, we don't want to clutter up debug consoles
michael@0 121 * with spurious messages about NS_ENSURE_SUCCESS failures.
michael@0 122 *
michael@0 123 * 2) We want to set the internal error flag, shutdown properly,
michael@0 124 * and end up in an error state.
michael@0 125 *
michael@0 126 * So this macro should be called when the desired failure behavior
michael@0 127 * is to put the container into an error state and return failure.
michael@0 128 * It goes without saying that macro won't compile outside of a
michael@0 129 * non-static RasterImage method.
michael@0 130 */
michael@0 131 #define LOG_CONTAINER_ERROR \
michael@0 132 PR_BEGIN_MACRO \
michael@0 133 PR_LOG (GetImgLog(), PR_LOG_ERROR, \
michael@0 134 ("RasterImage: [this=%p] Error " \
michael@0 135 "detected at line %u for image of " \
michael@0 136 "type %s\n", this, __LINE__, \
michael@0 137 mSourceDataMimeType.get())); \
michael@0 138 PR_END_MACRO
michael@0 139
michael@0 140 #define CONTAINER_ENSURE_SUCCESS(status) \
michael@0 141 PR_BEGIN_MACRO \
michael@0 142 nsresult _status = status; /* eval once */ \
michael@0 143 if (NS_FAILED(_status)) { \
michael@0 144 LOG_CONTAINER_ERROR; \
michael@0 145 DoError(); \
michael@0 146 return _status; \
michael@0 147 } \
michael@0 148 PR_END_MACRO
michael@0 149
michael@0 150 #define CONTAINER_ENSURE_TRUE(arg, rv) \
michael@0 151 PR_BEGIN_MACRO \
michael@0 152 if (!(arg)) { \
michael@0 153 LOG_CONTAINER_ERROR; \
michael@0 154 DoError(); \
michael@0 155 return rv; \
michael@0 156 } \
michael@0 157 PR_END_MACRO
michael@0 158
michael@0 159
michael@0 160
michael@0 161 static int num_containers;
michael@0 162 static int num_discardable_containers;
michael@0 163 static int64_t total_source_bytes;
michael@0 164 static int64_t discardable_source_bytes;
michael@0 165
michael@0 166 /* Are we globally disabling image discarding? */
michael@0 167 static bool
michael@0 168 DiscardingEnabled()
michael@0 169 {
michael@0 170 static bool inited;
michael@0 171 static bool enabled;
michael@0 172
michael@0 173 if (!inited) {
michael@0 174 inited = true;
michael@0 175
michael@0 176 enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nullptr);
michael@0 177 }
michael@0 178
michael@0 179 return enabled;
michael@0 180 }
michael@0 181
michael@0 182 class ScaleRequest
michael@0 183 {
michael@0 184 public:
michael@0 185 ScaleRequest(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame)
michael@0 186 : scale(aScale)
michael@0 187 , dstLocked(false)
michael@0 188 , done(false)
michael@0 189 , stopped(false)
michael@0 190 {
michael@0 191 MOZ_ASSERT(!aSrcFrame->GetIsPaletted());
michael@0 192 MOZ_ASSERT(aScale.width > 0 && aScale.height > 0);
michael@0 193
michael@0 194 weakImage = aImage->asWeakPtr();
michael@0 195 srcRect = aSrcFrame->GetRect();
michael@0 196
michael@0 197 nsIntRect dstRect = srcRect;
michael@0 198 dstRect.ScaleRoundOut(scale.width, scale.height);
michael@0 199 dstSize = dstRect.Size();
michael@0 200 }
michael@0 201
michael@0 202 // This can only be called on the main thread.
michael@0 203 bool GetSurfaces(imgFrame* srcFrame)
michael@0 204 {
michael@0 205 MOZ_ASSERT(NS_IsMainThread());
michael@0 206
michael@0 207 nsRefPtr<RasterImage> image = weakImage.get();
michael@0 208 if (!image) {
michael@0 209 return false;
michael@0 210 }
michael@0 211
michael@0 212 bool success = false;
michael@0 213 if (!dstLocked) {
michael@0 214 // We need to hold a lock onto the RasterImage object itself so that
michael@0 215 // it (and its associated imgFrames) aren't marked as discardable.
michael@0 216 bool imgLocked = NS_SUCCEEDED(image->LockImage());
michael@0 217 bool srcLocked = NS_SUCCEEDED(srcFrame->LockImageData());
michael@0 218 dstLocked = NS_SUCCEEDED(dstFrame->LockImageData());
michael@0 219
michael@0 220 nsRefPtr<gfxASurface> dstASurf;
michael@0 221 nsRefPtr<gfxASurface> srcASurf;
michael@0 222 success = srcLocked && NS_SUCCEEDED(srcFrame->GetSurface(getter_AddRefs(srcASurf)));
michael@0 223 success = success && dstLocked && NS_SUCCEEDED(dstFrame->GetSurface(getter_AddRefs(dstASurf)));
michael@0 224
michael@0 225 success = success && imgLocked && srcLocked && dstLocked && srcASurf && dstASurf;
michael@0 226
michael@0 227 if (success) {
michael@0 228 srcSurface = srcASurf->GetAsImageSurface();
michael@0 229 dstSurface = dstASurf->GetAsImageSurface();
michael@0 230 srcData = srcSurface->Data();
michael@0 231 dstData = dstSurface->Data();
michael@0 232 srcStride = srcSurface->Stride();
michael@0 233 dstStride = dstSurface->Stride();
michael@0 234 srcFormat = mozilla::gfx::ImageFormatToSurfaceFormat(srcFrame->GetFormat());
michael@0 235 }
michael@0 236
michael@0 237 // We have references to the Thebes surfaces, so we don't need to leave
michael@0 238 // the source frame (that we don't own) locked. We'll unlock the
michael@0 239 // destination frame in ReleaseSurfaces(), below.
michael@0 240 if (srcLocked) {
michael@0 241 success = NS_SUCCEEDED(srcFrame->UnlockImageData()) && success;
michael@0 242 }
michael@0 243
michael@0 244 success = success && srcSurface && dstSurface;
michael@0 245 }
michael@0 246
michael@0 247 return success;
michael@0 248 }
michael@0 249
michael@0 250 // This can only be called on the main thread.
michael@0 251 bool ReleaseSurfaces()
michael@0 252 {
michael@0 253 MOZ_ASSERT(NS_IsMainThread());
michael@0 254
michael@0 255 nsRefPtr<RasterImage> image = weakImage.get();
michael@0 256 if (!image) {
michael@0 257 return false;
michael@0 258 }
michael@0 259
michael@0 260 bool success = false;
michael@0 261 if (dstLocked) {
michael@0 262 if (DiscardingEnabled())
michael@0 263 dstFrame->SetDiscardable();
michael@0 264 success = NS_SUCCEEDED(dstFrame->UnlockImageData());
michael@0 265 success = success && NS_SUCCEEDED(image->UnlockImage());
michael@0 266
michael@0 267 dstLocked = false;
michael@0 268 srcData = nullptr;
michael@0 269 dstData = nullptr;
michael@0 270 srcSurface = nullptr;
michael@0 271 dstSurface = nullptr;
michael@0 272 }
michael@0 273 return success;
michael@0 274 }
michael@0 275
michael@0 276 // These values may only be touched on the main thread.
michael@0 277 WeakPtr<RasterImage> weakImage;
michael@0 278 nsAutoPtr<imgFrame> dstFrame;
michael@0 279 nsRefPtr<gfxImageSurface> srcSurface;
michael@0 280 nsRefPtr<gfxImageSurface> dstSurface;
michael@0 281
michael@0 282 // Below are the values that may be touched on the scaling thread.
michael@0 283 gfxSize scale;
michael@0 284 uint8_t* srcData;
michael@0 285 uint8_t* dstData;
michael@0 286 nsIntRect srcRect;
michael@0 287 gfxIntSize dstSize;
michael@0 288 uint32_t srcStride;
michael@0 289 uint32_t dstStride;
michael@0 290 mozilla::gfx::SurfaceFormat srcFormat;
michael@0 291 bool dstLocked;
michael@0 292 bool done;
michael@0 293 // This boolean is accessed from both threads simultaneously without locking.
michael@0 294 // That's safe because stopping a ScaleRequest is strictly an optimization;
michael@0 295 // if we're not cache-coherent, at worst we'll do extra work.
michael@0 296 bool stopped;
michael@0 297 };
michael@0 298
michael@0 299 class DrawRunner : public nsRunnable
michael@0 300 {
michael@0 301 public:
michael@0 302 DrawRunner(ScaleRequest* request)
michael@0 303 : mScaleRequest(request)
michael@0 304 {}
michael@0 305
michael@0 306 NS_IMETHOD Run()
michael@0 307 {
michael@0 308 // ScaleWorker is finished with this request, so we can unlock the data now.
michael@0 309 mScaleRequest->ReleaseSurfaces();
michael@0 310
michael@0 311 nsRefPtr<RasterImage> image = mScaleRequest->weakImage.get();
michael@0 312
michael@0 313 if (image) {
michael@0 314 RasterImage::ScaleStatus status;
michael@0 315 if (mScaleRequest->done) {
michael@0 316 status = RasterImage::SCALE_DONE;
michael@0 317 } else {
michael@0 318 status = RasterImage::SCALE_INVALID;
michael@0 319 }
michael@0 320
michael@0 321 image->ScalingDone(mScaleRequest, status);
michael@0 322 }
michael@0 323
michael@0 324 return NS_OK;
michael@0 325 }
michael@0 326
michael@0 327 private: /* members */
michael@0 328 nsAutoPtr<ScaleRequest> mScaleRequest;
michael@0 329 };
michael@0 330
michael@0 331 class ScaleRunner : public nsRunnable
michael@0 332 {
michael@0 333 public:
michael@0 334 ScaleRunner(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame)
michael@0 335 {
michael@0 336 nsAutoPtr<ScaleRequest> request(new ScaleRequest(aImage, aScale, aSrcFrame));
michael@0 337
michael@0 338 // Destination is unconditionally ARGB32 because that's what the scaler
michael@0 339 // outputs.
michael@0 340 request->dstFrame = new imgFrame();
michael@0 341 nsresult rv = request->dstFrame->Init(0, 0, request->dstSize.width, request->dstSize.height,
michael@0 342 gfxImageFormat::ARGB32);
michael@0 343
michael@0 344 if (NS_FAILED(rv) || !request->GetSurfaces(aSrcFrame)) {
michael@0 345 return;
michael@0 346 }
michael@0 347
michael@0 348 aImage->ScalingStart(request);
michael@0 349
michael@0 350 mScaleRequest = request;
michael@0 351 }
michael@0 352
michael@0 353 NS_IMETHOD Run()
michael@0 354 {
michael@0 355 // An alias just for ease of typing
michael@0 356 ScaleRequest* request = mScaleRequest;
michael@0 357
michael@0 358 if (!request->stopped) {
michael@0 359 request->done = mozilla::gfx::Scale(request->srcData, request->srcRect.width, request->srcRect.height, request->srcStride,
michael@0 360 request->dstData, request->dstSize.width, request->dstSize.height, request->dstStride,
michael@0 361 request->srcFormat);
michael@0 362 } else {
michael@0 363 request->done = false;
michael@0 364 }
michael@0 365
michael@0 366 // OK, we've got a new scaled image. Let's get the main thread to unlock and
michael@0 367 // redraw it.
michael@0 368 nsRefPtr<DrawRunner> runner = new DrawRunner(mScaleRequest.forget());
michael@0 369 NS_DispatchToMainThread(runner, NS_DISPATCH_NORMAL);
michael@0 370
michael@0 371 return NS_OK;
michael@0 372 }
michael@0 373
michael@0 374 bool IsOK() const { return !!mScaleRequest; }
michael@0 375
michael@0 376 private:
michael@0 377 nsAutoPtr<ScaleRequest> mScaleRequest;
michael@0 378 };
michael@0 379
michael@0 380 namespace mozilla {
michael@0 381 namespace image {
michael@0 382
michael@0 383 /* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton;
michael@0 384 static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
michael@0 385
michael@0 386 #ifndef DEBUG
michael@0 387 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties)
michael@0 388 #else
michael@0 389 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties,
michael@0 390 imgIContainerDebug)
michael@0 391 #endif
michael@0 392
michael@0 393 //******************************************************************************
michael@0 394 RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
michael@0 395 ImageURL* aURI /* = nullptr */) :
michael@0 396 ImageResource(aURI), // invoke superclass's constructor
michael@0 397 mSize(0,0),
michael@0 398 mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
michael@0 399 mMultipartDecodedFrame(nullptr),
michael@0 400 mAnim(nullptr),
michael@0 401 mLockCount(0),
michael@0 402 mDecodeCount(0),
michael@0 403 mRequestedSampleSize(0),
michael@0 404 #ifdef DEBUG
michael@0 405 mFramesNotified(0),
michael@0 406 #endif
michael@0 407 mDecodingMonitor("RasterImage Decoding Monitor"),
michael@0 408 mDecoder(nullptr),
michael@0 409 mBytesDecoded(0),
michael@0 410 mInDecoder(false),
michael@0 411 mStatusDiff(ImageStatusDiff::NoChange()),
michael@0 412 mNotifying(false),
michael@0 413 mHasSize(false),
michael@0 414 mDecodeOnDraw(false),
michael@0 415 mMultipart(false),
michael@0 416 mDiscardable(false),
michael@0 417 mHasSourceData(false),
michael@0 418 mDecoded(false),
michael@0 419 mHasBeenDecoded(false),
michael@0 420 mAnimationFinished(false),
michael@0 421 mFinishing(false),
michael@0 422 mInUpdateImageContainer(false),
michael@0 423 mWantFullDecode(false),
michael@0 424 mPendingError(false),
michael@0 425 mScaleRequest(nullptr)
michael@0 426 {
michael@0 427 mStatusTrackerInit = new imgStatusTrackerInit(this, aStatusTracker);
michael@0 428
michael@0 429 // Set up the discard tracker node.
michael@0 430 mDiscardTrackerNode.img = this;
michael@0 431 Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
michael@0 432
michael@0 433 // Statistics
michael@0 434 num_containers++;
michael@0 435 }
michael@0 436
michael@0 437 //******************************************************************************
michael@0 438 RasterImage::~RasterImage()
michael@0 439 {
michael@0 440 // Discardable statistics
michael@0 441 if (mDiscardable) {
michael@0 442 num_discardable_containers--;
michael@0 443 discardable_source_bytes -= mSourceData.Length();
michael@0 444
michael@0 445 PR_LOG (GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
michael@0 446 ("CompressedImageAccounting: destroying RasterImage %p. "
michael@0 447 "Total Containers: %d, Discardable containers: %d, "
michael@0 448 "Total source bytes: %lld, Source bytes for discardable containers %lld",
michael@0 449 this,
michael@0 450 num_containers,
michael@0 451 num_discardable_containers,
michael@0 452 total_source_bytes,
michael@0 453 discardable_source_bytes));
michael@0 454 }
michael@0 455
michael@0 456 if (mDecoder) {
michael@0 457 // Kill off our decode request, if it's pending. (If not, this call is
michael@0 458 // harmless.)
michael@0 459 ReentrantMonitorAutoEnter lock(mDecodingMonitor);
michael@0 460 DecodePool::StopDecoding(this);
michael@0 461 mDecoder = nullptr;
michael@0 462
michael@0 463 // Unlock the last frame (if we have any). Our invariant is that, while we
michael@0 464 // have a decoder open, the last frame is always locked.
michael@0 465 // This would be done in ShutdownDecoder, but since mDecoder is non-null,
michael@0 466 // we didn't call ShutdownDecoder and we need to do it manually.
michael@0 467 if (GetNumFrames() > 0) {
michael@0 468 imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
michael@0 469 curframe->UnlockImageData();
michael@0 470 }
michael@0 471 }
michael@0 472
michael@0 473 delete mAnim;
michael@0 474 mAnim = nullptr;
michael@0 475 delete mMultipartDecodedFrame;
michael@0 476
michael@0 477 // Total statistics
michael@0 478 num_containers--;
michael@0 479 total_source_bytes -= mSourceData.Length();
michael@0 480
michael@0 481 if (NS_IsMainThread()) {
michael@0 482 DiscardTracker::Remove(&mDiscardTrackerNode);
michael@0 483 }
michael@0 484 }
michael@0 485
michael@0 486 /* static */ void
michael@0 487 RasterImage::Initialize()
michael@0 488 {
michael@0 489 InitPrefCaches();
michael@0 490
michael@0 491 // Create our singletons now, so we don't have to worry about what thread
michael@0 492 // they're created on.
michael@0 493 DecodePool::Singleton();
michael@0 494 }
michael@0 495
michael@0 496 nsresult
michael@0 497 RasterImage::Init(const char* aMimeType,
michael@0 498 uint32_t aFlags)
michael@0 499 {
michael@0 500 // We don't support re-initialization
michael@0 501 if (mInitialized)
michael@0 502 return NS_ERROR_ILLEGAL_VALUE;
michael@0 503
michael@0 504 // Not sure an error can happen before init, but be safe
michael@0 505 if (mError)
michael@0 506 return NS_ERROR_FAILURE;
michael@0 507
michael@0 508 NS_ENSURE_ARG_POINTER(aMimeType);
michael@0 509
michael@0 510 // We must be non-discardable and non-decode-on-draw for
michael@0 511 // multipart channels
michael@0 512 NS_ABORT_IF_FALSE(!(aFlags & INIT_FLAG_MULTIPART) ||
michael@0 513 (!(aFlags & INIT_FLAG_DISCARDABLE) &&
michael@0 514 !(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
michael@0 515 "Can't be discardable or decode-on-draw for multipart");
michael@0 516
michael@0 517 // Store initialization data
michael@0 518 mSourceDataMimeType.Assign(aMimeType);
michael@0 519 mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
michael@0 520 mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
michael@0 521 mMultipart = !!(aFlags & INIT_FLAG_MULTIPART);
michael@0 522
michael@0 523 // Statistics
michael@0 524 if (mDiscardable) {
michael@0 525 num_discardable_containers++;
michael@0 526 discardable_source_bytes += mSourceData.Length();
michael@0 527 }
michael@0 528
michael@0 529 // Instantiate the decoder
michael@0 530 nsresult rv = InitDecoder(/* aDoSizeDecode = */ true);
michael@0 531 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 532
michael@0 533 // If we aren't storing source data, we want to switch from a size decode to
michael@0 534 // a full decode as soon as possible.
michael@0 535 if (!StoringSourceData()) {
michael@0 536 mWantFullDecode = true;
michael@0 537 }
michael@0 538
michael@0 539 // Mark us as initialized
michael@0 540 mInitialized = true;
michael@0 541
michael@0 542 return NS_OK;
michael@0 543 }
michael@0 544
michael@0 545 //******************************************************************************
michael@0 546 // [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
michael@0 547 NS_IMETHODIMP_(void)
michael@0 548 RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
michael@0 549 {
michael@0 550 EvaluateAnimation();
michael@0 551
michael@0 552 if (!mAnimating) {
michael@0 553 return;
michael@0 554 }
michael@0 555
michael@0 556 FrameAnimator::RefreshResult res;
michael@0 557 if (mAnim) {
michael@0 558 res = mAnim->RequestRefresh(aTime);
michael@0 559 }
michael@0 560
michael@0 561 if (res.frameAdvanced) {
michael@0 562 // Notify listeners that our frame has actually changed, but do this only
michael@0 563 // once for all frames that we've now passed (if AdvanceFrame() was called
michael@0 564 // more than once).
michael@0 565 #ifdef DEBUG
michael@0 566 mFramesNotified++;
michael@0 567 #endif
michael@0 568
michael@0 569 UpdateImageContainer();
michael@0 570
michael@0 571 // Explicitly call this on mStatusTracker so we're sure to not interfere
michael@0 572 // with the decoding process
michael@0 573 if (mStatusTracker)
michael@0 574 mStatusTracker->FrameChanged(&res.dirtyRect);
michael@0 575 }
michael@0 576
michael@0 577 if (res.animationFinished) {
michael@0 578 mAnimationFinished = true;
michael@0 579 EvaluateAnimation();
michael@0 580 }
michael@0 581 }
michael@0 582
michael@0 583 //******************************************************************************
michael@0 584 /* readonly attribute int32_t width; */
michael@0 585 NS_IMETHODIMP
michael@0 586 RasterImage::GetWidth(int32_t *aWidth)
michael@0 587 {
michael@0 588 NS_ENSURE_ARG_POINTER(aWidth);
michael@0 589
michael@0 590 if (mError) {
michael@0 591 *aWidth = 0;
michael@0 592 return NS_ERROR_FAILURE;
michael@0 593 }
michael@0 594
michael@0 595 *aWidth = mSize.width;
michael@0 596 return NS_OK;
michael@0 597 }
michael@0 598
michael@0 599 //******************************************************************************
michael@0 600 /* readonly attribute int32_t height; */
michael@0 601 NS_IMETHODIMP
michael@0 602 RasterImage::GetHeight(int32_t *aHeight)
michael@0 603 {
michael@0 604 NS_ENSURE_ARG_POINTER(aHeight);
michael@0 605
michael@0 606 if (mError) {
michael@0 607 *aHeight = 0;
michael@0 608 return NS_ERROR_FAILURE;
michael@0 609 }
michael@0 610
michael@0 611 *aHeight = mSize.height;
michael@0 612 return NS_OK;
michael@0 613 }
michael@0 614
michael@0 615 //******************************************************************************
michael@0 616 /* [noscript] readonly attribute nsSize intrinsicSize; */
michael@0 617 NS_IMETHODIMP
michael@0 618 RasterImage::GetIntrinsicSize(nsSize* aSize)
michael@0 619 {
michael@0 620 if (mError)
michael@0 621 return NS_ERROR_FAILURE;
michael@0 622
michael@0 623 *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width),
michael@0 624 nsPresContext::CSSPixelsToAppUnits(mSize.height));
michael@0 625 return NS_OK;
michael@0 626 }
michael@0 627
michael@0 628 //******************************************************************************
michael@0 629 /* [noscript] readonly attribute nsSize intrinsicRatio; */
michael@0 630 NS_IMETHODIMP
michael@0 631 RasterImage::GetIntrinsicRatio(nsSize* aRatio)
michael@0 632 {
michael@0 633 if (mError)
michael@0 634 return NS_ERROR_FAILURE;
michael@0 635
michael@0 636 *aRatio = nsSize(mSize.width, mSize.height);
michael@0 637 return NS_OK;
michael@0 638 }
michael@0 639
michael@0 640 NS_IMETHODIMP_(Orientation)
michael@0 641 RasterImage::GetOrientation()
michael@0 642 {
michael@0 643 return mOrientation;
michael@0 644 }
michael@0 645
michael@0 646 //******************************************************************************
michael@0 647 /* unsigned short GetType(); */
michael@0 648 NS_IMETHODIMP
michael@0 649 RasterImage::GetType(uint16_t *aType)
michael@0 650 {
michael@0 651 NS_ENSURE_ARG_POINTER(aType);
michael@0 652
michael@0 653 *aType = GetType();
michael@0 654 return NS_OK;
michael@0 655 }
michael@0 656
michael@0 657 //******************************************************************************
michael@0 658 /* [noscript, notxpcom] uint16_t GetType(); */
michael@0 659 NS_IMETHODIMP_(uint16_t)
michael@0 660 RasterImage::GetType()
michael@0 661 {
michael@0 662 return imgIContainer::TYPE_RASTER;
michael@0 663 }
michael@0 664
michael@0 665 imgFrame*
michael@0 666 RasterImage::GetImgFrameNoDecode(uint32_t framenum)
michael@0 667 {
michael@0 668 if (!mAnim) {
michael@0 669 NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
michael@0 670 return mFrameBlender.GetFrame(0);
michael@0 671 }
michael@0 672 return mFrameBlender.GetFrame(framenum);
michael@0 673 }
michael@0 674
michael@0 675 imgFrame*
michael@0 676 RasterImage::GetImgFrame(uint32_t framenum)
michael@0 677 {
michael@0 678 nsresult rv = WantDecodedFrames();
michael@0 679 CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr);
michael@0 680 return GetImgFrameNoDecode(framenum);
michael@0 681 }
michael@0 682
michael@0 683 imgFrame*
michael@0 684 RasterImage::GetDrawableImgFrame(uint32_t framenum)
michael@0 685 {
michael@0 686 imgFrame* frame = nullptr;
michael@0 687
michael@0 688 if (mMultipart && framenum == GetCurrentImgFrameIndex()) {
michael@0 689 // In the multipart case we prefer to use mMultipartDecodedFrame, which is
michael@0 690 // the most recent one we completely decoded, rather than display the real
michael@0 691 // current frame and risk severe tearing.
michael@0 692 frame = mMultipartDecodedFrame;
michael@0 693 }
michael@0 694
michael@0 695 if (!frame) {
michael@0 696 frame = GetImgFrame(framenum);
michael@0 697 }
michael@0 698
michael@0 699 // We will return a paletted frame if it's not marked as compositing failed
michael@0 700 // so we can catch crashes for reasons we haven't investigated.
michael@0 701 if (frame && frame->GetCompositingFailed())
michael@0 702 return nullptr;
michael@0 703
michael@0 704 if (frame) {
michael@0 705 frame->ApplyDirtToSurfaces();
michael@0 706 }
michael@0 707
michael@0 708 return frame;
michael@0 709 }
michael@0 710
michael@0 711 uint32_t
michael@0 712 RasterImage::GetCurrentImgFrameIndex() const
michael@0 713 {
michael@0 714 if (mAnim)
michael@0 715 return mAnim->GetCurrentAnimationFrameIndex();
michael@0 716
michael@0 717 return 0;
michael@0 718 }
michael@0 719
michael@0 720 imgFrame*
michael@0 721 RasterImage::GetCurrentImgFrame()
michael@0 722 {
michael@0 723 return GetImgFrame(GetCurrentImgFrameIndex());
michael@0 724 }
michael@0 725
michael@0 726 //******************************************************************************
michael@0 727 /* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
michael@0 728 NS_IMETHODIMP_(bool)
michael@0 729 RasterImage::FrameIsOpaque(uint32_t aWhichFrame)
michael@0 730 {
michael@0 731 if (aWhichFrame > FRAME_MAX_VALUE) {
michael@0 732 NS_WARNING("aWhichFrame outside valid range!");
michael@0 733 return false;
michael@0 734 }
michael@0 735
michael@0 736 if (mError)
michael@0 737 return false;
michael@0 738
michael@0 739 // See if we can get an image frame.
michael@0 740 imgFrame* frame = aWhichFrame == FRAME_FIRST ? GetImgFrameNoDecode(0)
michael@0 741 : GetImgFrameNoDecode(GetCurrentImgFrameIndex());
michael@0 742
michael@0 743 // If we don't get a frame, the safe answer is "not opaque".
michael@0 744 if (!frame)
michael@0 745 return false;
michael@0 746
michael@0 747 // Other, the frame is transparent if either:
michael@0 748 // 1. It needs a background.
michael@0 749 // 2. Its size doesn't cover our entire area.
michael@0 750 nsIntRect framerect = frame->GetRect();
michael@0 751 return !frame->GetNeedsBackground() &&
michael@0 752 framerect.IsEqualInterior(nsIntRect(0, 0, mSize.width, mSize.height));
michael@0 753 }
michael@0 754
michael@0 755 nsIntRect
michael@0 756 RasterImage::FrameRect(uint32_t aWhichFrame)
michael@0 757 {
michael@0 758 if (aWhichFrame > FRAME_MAX_VALUE) {
michael@0 759 NS_WARNING("aWhichFrame outside valid range!");
michael@0 760 return nsIntRect();
michael@0 761 }
michael@0 762
michael@0 763 // Get the requested frame.
michael@0 764 imgFrame* frame = aWhichFrame == FRAME_FIRST ? GetImgFrameNoDecode(0)
michael@0 765 : GetImgFrameNoDecode(GetCurrentImgFrameIndex());
michael@0 766
michael@0 767 // If we have the frame, use that rectangle.
michael@0 768 if (frame) {
michael@0 769 return frame->GetRect();
michael@0 770 }
michael@0 771
michael@0 772 // If the frame doesn't exist, we return the empty rectangle. It's not clear
michael@0 773 // whether this is appropriate in general, but at the moment the only
michael@0 774 // consumer of this method is imgStatusTracker (when it wants to figure out
michael@0 775 // dirty rectangles to send out batched observer updates). This should
michael@0 776 // probably be revisited when we fix bug 503973.
michael@0 777 return nsIntRect();
michael@0 778 }
michael@0 779
michael@0 780 uint32_t
michael@0 781 RasterImage::GetCurrentFrameIndex()
michael@0 782 {
michael@0 783 return GetCurrentImgFrameIndex();
michael@0 784 }
michael@0 785
michael@0 786 uint32_t
michael@0 787 RasterImage::GetNumFrames() const
michael@0 788 {
michael@0 789 return mFrameBlender.GetNumFrames();
michael@0 790 }
michael@0 791
michael@0 792 //******************************************************************************
michael@0 793 /* readonly attribute boolean animated; */
michael@0 794 NS_IMETHODIMP
michael@0 795 RasterImage::GetAnimated(bool *aAnimated)
michael@0 796 {
michael@0 797 if (mError)
michael@0 798 return NS_ERROR_FAILURE;
michael@0 799
michael@0 800 NS_ENSURE_ARG_POINTER(aAnimated);
michael@0 801
michael@0 802 // If we have mAnim, we can know for sure
michael@0 803 if (mAnim) {
michael@0 804 *aAnimated = true;
michael@0 805 return NS_OK;
michael@0 806 }
michael@0 807
michael@0 808 // Otherwise, we need to have been decoded to know for sure, since if we were
michael@0 809 // decoded at least once mAnim would have been created for animated images
michael@0 810 if (!mHasBeenDecoded)
michael@0 811 return NS_ERROR_NOT_AVAILABLE;
michael@0 812
michael@0 813 // We know for sure
michael@0 814 *aAnimated = false;
michael@0 815
michael@0 816 return NS_OK;
michael@0 817 }
michael@0 818
michael@0 819 //******************************************************************************
michael@0 820 /* [notxpcom] int32_t getFirstFrameDelay (); */
michael@0 821 NS_IMETHODIMP_(int32_t)
michael@0 822 RasterImage::GetFirstFrameDelay()
michael@0 823 {
michael@0 824 if (mError)
michael@0 825 return -1;
michael@0 826
michael@0 827 bool animated = false;
michael@0 828 if (NS_FAILED(GetAnimated(&animated)) || !animated)
michael@0 829 return -1;
michael@0 830
michael@0 831 return mFrameBlender.GetTimeoutForFrame(0);
michael@0 832 }
michael@0 833
michael@0 834 nsresult
michael@0 835 RasterImage::CopyFrame(uint32_t aWhichFrame,
michael@0 836 uint32_t aFlags,
michael@0 837 gfxImageSurface **_retval)
michael@0 838 {
michael@0 839 if (aWhichFrame > FRAME_MAX_VALUE)
michael@0 840 return NS_ERROR_INVALID_ARG;
michael@0 841
michael@0 842 if (mError)
michael@0 843 return NS_ERROR_FAILURE;
michael@0 844
michael@0 845 // Disallowed in the API
michael@0 846 if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
michael@0 847 return NS_ERROR_FAILURE;
michael@0 848
michael@0 849 nsresult rv;
michael@0 850
michael@0 851 if (!ApplyDecodeFlags(aFlags, aWhichFrame))
michael@0 852 return NS_ERROR_NOT_AVAILABLE;
michael@0 853
michael@0 854 // If requested, synchronously flush any data we have lying around to the decoder
michael@0 855 if (aFlags & FLAG_SYNC_DECODE) {
michael@0 856 rv = SyncDecode();
michael@0 857 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 858 }
michael@0 859
michael@0 860 NS_ENSURE_ARG_POINTER(_retval);
michael@0 861
michael@0 862 // Get the frame. If it's not there, it's probably the caller's fault for
michael@0 863 // not waiting for the data to be loaded from the network or not passing
michael@0 864 // FLAG_SYNC_DECODE
michael@0 865 uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ?
michael@0 866 0 : GetCurrentImgFrameIndex();
michael@0 867 imgFrame *frame = GetDrawableImgFrame(frameIndex);
michael@0 868 if (!frame) {
michael@0 869 *_retval = nullptr;
michael@0 870 return NS_ERROR_FAILURE;
michael@0 871 }
michael@0 872
michael@0 873 nsRefPtr<gfxPattern> pattern;
michael@0 874 frame->GetPattern(getter_AddRefs(pattern));
michael@0 875 nsIntRect intframerect = frame->GetRect();
michael@0 876 gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height);
michael@0 877
michael@0 878 // Create a 32-bit image surface of our size, but draw using the frame's
michael@0 879 // rect, implicitly padding the frame out to the image's size.
michael@0 880 nsRefPtr<gfxImageSurface> imgsurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
michael@0 881 gfxImageFormat::ARGB32);
michael@0 882 gfxContext ctx(imgsurface);
michael@0 883 ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
michael@0 884 ctx.Rectangle(framerect);
michael@0 885 ctx.Translate(framerect.TopLeft());
michael@0 886 ctx.SetPattern(pattern);
michael@0 887 ctx.Fill();
michael@0 888
michael@0 889 imgsurface.forget(_retval);
michael@0 890 return NS_OK;
michael@0 891 }
michael@0 892
michael@0 893 //******************************************************************************
michael@0 894 /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
michael@0 895 * in uint32_t aFlags); */
michael@0 896 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
michael@0 897 RasterImage::GetFrame(uint32_t aWhichFrame,
michael@0 898 uint32_t aFlags)
michael@0 899 {
michael@0 900 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
michael@0 901
michael@0 902 if (aWhichFrame > FRAME_MAX_VALUE)
michael@0 903 return nullptr;
michael@0 904
michael@0 905 if (mError)
michael@0 906 return nullptr;
michael@0 907
michael@0 908 // Disallowed in the API
michael@0 909 if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
michael@0 910 return nullptr;
michael@0 911
michael@0 912 if (!ApplyDecodeFlags(aFlags, aWhichFrame))
michael@0 913 return nullptr;
michael@0 914
michael@0 915 // If the caller requested a synchronous decode, do it
michael@0 916 if (aFlags & FLAG_SYNC_DECODE) {
michael@0 917 nsresult rv = SyncDecode();
michael@0 918 CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr);
michael@0 919 }
michael@0 920
michael@0 921 // Get the frame. If it's not there, it's probably the caller's fault for
michael@0 922 // not waiting for the data to be loaded from the network or not passing
michael@0 923 // FLAG_SYNC_DECODE
michael@0 924 uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ?
michael@0 925 0 : GetCurrentImgFrameIndex();
michael@0 926 imgFrame *frame = GetDrawableImgFrame(frameIndex);
michael@0 927 if (!frame) {
michael@0 928 return nullptr;
michael@0 929 }
michael@0 930
michael@0 931 nsRefPtr<gfxASurface> framesurf;
michael@0 932
michael@0 933 // If this frame covers the entire image, we can just reuse its existing
michael@0 934 // surface.
michael@0 935 nsIntRect framerect = frame->GetRect();
michael@0 936 if (framerect.x == 0 && framerect.y == 0 &&
michael@0 937 framerect.width == mSize.width &&
michael@0 938 framerect.height == mSize.height) {
michael@0 939 frame->GetSurface(getter_AddRefs(framesurf));
michael@0 940 if (!framesurf && !frame->IsSinglePixel()) {
michael@0 941 // No reason to be optimized away here - the OS threw out the data
michael@0 942 if (!(aFlags & FLAG_SYNC_DECODE))
michael@0 943 return nullptr;
michael@0 944
michael@0 945 // Unconditionally call ForceDiscard() here because GetSurface can only
michael@0 946 // return null when we can forcibly discard and redecode. There are two
michael@0 947 // other cases where GetSurface() can return null - when it is a single
michael@0 948 // pixel image, which we check before getting here, or when this is an
michael@0 949 // indexed image, in which case we shouldn't be in this function at all.
michael@0 950 // The only remaining possibility is that SetDiscardable() was called on
michael@0 951 // this imgFrame, which implies the image can be redecoded.
michael@0 952 ForceDiscard();
michael@0 953 return GetFrame(aWhichFrame, aFlags);
michael@0 954 }
michael@0 955 }
michael@0 956
michael@0 957 // The image doesn't have a surface because it's been optimized away. Create
michael@0 958 // one.
michael@0 959 if (!framesurf) {
michael@0 960 nsRefPtr<gfxImageSurface> imgsurf;
michael@0 961 CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
michael@0 962 framesurf = imgsurf;
michael@0 963 }
michael@0 964
michael@0 965 RefPtr<SourceSurface> result;
michael@0 966
michael@0 967 // As far as Moz2D is concerned, SourceSurface contains premultiplied alpha.
michael@0 968 // If we're abusing it to contain non-premultiplied alpha then we want to
michael@0 969 // avoid having Moz2D do any conversions on it (like copy to another
michael@0 970 // surface). Hence why we try to wrap framesurf's data here for
michael@0 971 // FLAG_DECODE_NO_PREMULTIPLY_ALPHA.
michael@0 972 if ((aFlags & FLAG_WANT_DATA_SURFACE) != 0 ||
michael@0 973 (aFlags & FLAG_DECODE_NO_PREMULTIPLY_ALPHA) != 0) {
michael@0 974 result = gfxPlatform::GetPlatform()->GetWrappedDataSourceSurface(framesurf);
michael@0 975 }
michael@0 976 if (!result) {
michael@0 977 result = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr,
michael@0 978 framesurf);
michael@0 979 }
michael@0 980 return result.forget();
michael@0 981 }
michael@0 982
michael@0 983 already_AddRefed<layers::Image>
michael@0 984 RasterImage::GetCurrentImage()
michael@0 985 {
michael@0 986 if (!mDecoded) {
michael@0 987 // We can't call StartDecoding because that can synchronously notify
michael@0 988 // which can cause DOM modification
michael@0 989 RequestDecodeCore(ASYNCHRONOUS);
michael@0 990 return nullptr;
michael@0 991 }
michael@0 992
michael@0 993 RefPtr<SourceSurface> surface = GetFrame(FRAME_CURRENT, FLAG_NONE);
michael@0 994 if (!surface) {
michael@0 995 // The OS threw out some or all of our buffer. Start decoding again.
michael@0 996 // GetFrame will only return null in the case that the image was
michael@0 997 // discarded. We already checked that the image is decoded, so other
michael@0 998 // error paths are not possible.
michael@0 999 ForceDiscard();
michael@0 1000 RequestDecodeCore(ASYNCHRONOUS);
michael@0 1001 return nullptr;
michael@0 1002 }
michael@0 1003
michael@0 1004 if (!mImageContainer) {
michael@0 1005 mImageContainer = LayerManager::CreateImageContainer();
michael@0 1006 }
michael@0 1007
michael@0 1008 CairoImage::Data cairoData;
michael@0 1009 GetWidth(&cairoData.mSize.width);
michael@0 1010 GetHeight(&cairoData.mSize.height);
michael@0 1011 cairoData.mSourceSurface = surface;
michael@0 1012
michael@0 1013 nsRefPtr<layers::Image> image = mImageContainer->CreateImage(ImageFormat::CAIRO_SURFACE);
michael@0 1014 NS_ASSERTION(image, "Failed to create Image");
michael@0 1015
michael@0 1016 static_cast<CairoImage*>(image.get())->SetData(cairoData);
michael@0 1017
michael@0 1018 return image.forget();
michael@0 1019 }
michael@0 1020
michael@0 1021
michael@0 1022 NS_IMETHODIMP
michael@0 1023 RasterImage::GetImageContainer(LayerManager* aManager, ImageContainer **_retval)
michael@0 1024 {
michael@0 1025 int32_t maxTextureSize = aManager->GetMaxTextureSize();
michael@0 1026 if (mSize.width > maxTextureSize || mSize.height > maxTextureSize) {
michael@0 1027 *_retval = nullptr;
michael@0 1028 return NS_OK;
michael@0 1029 }
michael@0 1030
michael@0 1031 if (IsUnlocked() && mStatusTracker) {
michael@0 1032 mStatusTracker->OnUnlockedDraw();
michael@0 1033 }
michael@0 1034
michael@0 1035 if (!mImageContainer) {
michael@0 1036 mImageContainer = mImageContainerCache;
michael@0 1037 }
michael@0 1038
michael@0 1039 if (mImageContainer) {
michael@0 1040 *_retval = mImageContainer;
michael@0 1041 NS_ADDREF(*_retval);
michael@0 1042 return NS_OK;
michael@0 1043 }
michael@0 1044
michael@0 1045 nsRefPtr<layers::Image> image = GetCurrentImage();
michael@0 1046 if (!image) {
michael@0 1047 return NS_ERROR_NOT_AVAILABLE;
michael@0 1048 }
michael@0 1049 mImageContainer->SetCurrentImageInTransaction(image);
michael@0 1050
michael@0 1051 *_retval = mImageContainer;
michael@0 1052 NS_ADDREF(*_retval);
michael@0 1053 // We only need to be careful about holding on to the image when it is
michael@0 1054 // discardable by the OS.
michael@0 1055 if (CanForciblyDiscardAndRedecode()) {
michael@0 1056 mImageContainerCache = mImageContainer->asWeakPtr();
michael@0 1057 mImageContainer = nullptr;
michael@0 1058 }
michael@0 1059
michael@0 1060 return NS_OK;
michael@0 1061 }
michael@0 1062
michael@0 1063 void
michael@0 1064 RasterImage::UpdateImageContainer()
michael@0 1065 {
michael@0 1066 if (!mImageContainer || IsInUpdateImageContainer()) {
michael@0 1067 return;
michael@0 1068 }
michael@0 1069
michael@0 1070 SetInUpdateImageContainer(true);
michael@0 1071
michael@0 1072 nsRefPtr<layers::Image> image = GetCurrentImage();
michael@0 1073 if (!image) {
michael@0 1074 return;
michael@0 1075 }
michael@0 1076 mImageContainer->SetCurrentImage(image);
michael@0 1077 SetInUpdateImageContainer(false);
michael@0 1078 }
michael@0 1079
michael@0 1080 size_t
michael@0 1081 RasterImage::HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
michael@0 1082 {
michael@0 1083 // n == 0 is possible for two reasons.
michael@0 1084 // - This is a zero-length image.
michael@0 1085 // - We're on a platform where moz_malloc_size_of always returns 0.
michael@0 1086 // In either case the fallback works appropriately.
michael@0 1087 size_t n = mSourceData.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1088 if (n == 0) {
michael@0 1089 n = mSourceData.Length();
michael@0 1090 }
michael@0 1091 return n;
michael@0 1092 }
michael@0 1093
michael@0 1094 size_t
michael@0 1095 RasterImage::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
michael@0 1096 MallocSizeOf aMallocSizeOf) const
michael@0 1097 {
michael@0 1098 size_t n = mFrameBlender.SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
michael@0 1099
michael@0 1100 if (mScaleResult.status == SCALE_DONE) {
michael@0 1101 n += mScaleResult.frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
michael@0 1102 }
michael@0 1103
michael@0 1104 return n;
michael@0 1105 }
michael@0 1106
michael@0 1107 size_t
michael@0 1108 RasterImage::HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const
michael@0 1109 {
michael@0 1110 return SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation::IN_PROCESS_HEAP,
michael@0 1111 aMallocSizeOf);
michael@0 1112 }
michael@0 1113
michael@0 1114 size_t
michael@0 1115 RasterImage::NonHeapSizeOfDecoded() const
michael@0 1116 {
michael@0 1117 return SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation::IN_PROCESS_NONHEAP,
michael@0 1118 nullptr);
michael@0 1119 }
michael@0 1120
michael@0 1121 size_t
michael@0 1122 RasterImage::OutOfProcessSizeOfDecoded() const
michael@0 1123 {
michael@0 1124 return SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation::OUT_OF_PROCESS,
michael@0 1125 nullptr);
michael@0 1126 }
michael@0 1127
michael@0 1128 void
michael@0 1129 RasterImage::EnsureAnimExists()
michael@0 1130 {
michael@0 1131 if (!mAnim) {
michael@0 1132
michael@0 1133 // Create the animation context
michael@0 1134 mAnim = new FrameAnimator(mFrameBlender, mAnimationMode);
michael@0 1135
michael@0 1136 // We don't support discarding animated images (See bug 414259).
michael@0 1137 // Lock the image and throw away the key.
michael@0 1138 //
michael@0 1139 // Note that this is inefficient, since we could get rid of the source
michael@0 1140 // data too. However, doing this is actually hard, because we're probably
michael@0 1141 // calling ensureAnimExists mid-decode, and thus we're decoding out of
michael@0 1142 // the source buffer. Since we're going to fix this anyway later, and
michael@0 1143 // since we didn't kill the source data in the old world either, locking
michael@0 1144 // is acceptable for the moment.
michael@0 1145 LockImage();
michael@0 1146
michael@0 1147 // Notify our observers that we are starting animation.
michael@0 1148 nsRefPtr<imgStatusTracker> statusTracker = CurrentStatusTracker();
michael@0 1149 statusTracker->RecordImageIsAnimated();
michael@0 1150 }
michael@0 1151 }
michael@0 1152
michael@0 1153 nsresult
michael@0 1154 RasterImage::InternalAddFrameHelper(uint32_t framenum, imgFrame *aFrame,
michael@0 1155 uint8_t **imageData, uint32_t *imageLength,
michael@0 1156 uint32_t **paletteData, uint32_t *paletteLength,
michael@0 1157 imgFrame** aRetFrame)
michael@0 1158 {
michael@0 1159 NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!");
michael@0 1160 if (framenum > GetNumFrames())
michael@0 1161 return NS_ERROR_INVALID_ARG;
michael@0 1162
michael@0 1163 nsAutoPtr<imgFrame> frame(aFrame);
michael@0 1164
michael@0 1165 // We are in the middle of decoding. This will be unlocked when we finish
michael@0 1166 // decoding or switch to another frame.
michael@0 1167 frame->LockImageData();
michael@0 1168
michael@0 1169 if (paletteData && paletteLength)
michael@0 1170 frame->GetPaletteData(paletteData, paletteLength);
michael@0 1171
michael@0 1172 frame->GetImageData(imageData, imageLength);
michael@0 1173
michael@0 1174 *aRetFrame = frame;
michael@0 1175
michael@0 1176 mFrameBlender.InsertFrame(framenum, frame.forget());
michael@0 1177
michael@0 1178 return NS_OK;
michael@0 1179 }
michael@0 1180
michael@0 1181 nsresult
michael@0 1182 RasterImage::InternalAddFrame(uint32_t framenum,
michael@0 1183 int32_t aX, int32_t aY,
michael@0 1184 int32_t aWidth, int32_t aHeight,
michael@0 1185 gfxImageFormat aFormat,
michael@0 1186 uint8_t aPaletteDepth,
michael@0 1187 uint8_t **imageData,
michael@0 1188 uint32_t *imageLength,
michael@0 1189 uint32_t **paletteData,
michael@0 1190 uint32_t *paletteLength,
michael@0 1191 imgFrame** aRetFrame)
michael@0 1192 {
michael@0 1193 // We assume that we're in the middle of decoding because we unlock the
michael@0 1194 // previous frame when we create a new frame, and only when decoding do we
michael@0 1195 // lock frames.
michael@0 1196 NS_ABORT_IF_FALSE(mDecoder, "Only decoders may add frames!");
michael@0 1197
michael@0 1198 NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!");
michael@0 1199 if (framenum > GetNumFrames())
michael@0 1200 return NS_ERROR_INVALID_ARG;
michael@0 1201
michael@0 1202 nsAutoPtr<imgFrame> frame(new imgFrame());
michael@0 1203
michael@0 1204 nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
michael@0 1205 if (!(mSize.width > 0 && mSize.height > 0))
michael@0 1206 NS_WARNING("Shouldn't call InternalAddFrame with zero size");
michael@0 1207 if (!NS_SUCCEEDED(rv))
michael@0 1208 NS_WARNING("imgFrame::Init should succeed");
michael@0 1209 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1210
michael@0 1211 // We know we are in a decoder. Therefore, we must unlock the previous frame
michael@0 1212 // when we move on to decoding into the next frame.
michael@0 1213 if (GetNumFrames() > 0) {
michael@0 1214 imgFrame *prevframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
michael@0 1215 prevframe->UnlockImageData();
michael@0 1216 }
michael@0 1217
michael@0 1218 if (GetNumFrames() == 0) {
michael@0 1219 return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
michael@0 1220 paletteData, paletteLength, aRetFrame);
michael@0 1221 }
michael@0 1222
michael@0 1223 if (GetNumFrames() == 1) {
michael@0 1224 // Since we're about to add our second frame, initialize animation stuff
michael@0 1225 EnsureAnimExists();
michael@0 1226
michael@0 1227 // If we dispose of the first frame by clearing it, then the
michael@0 1228 // First Frame's refresh area is all of itself.
michael@0 1229 // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
michael@0 1230 int32_t frameDisposalMethod = mFrameBlender.RawGetFrame(0)->GetFrameDisposalMethod();
michael@0 1231 if (frameDisposalMethod == FrameBlender::kDisposeClear ||
michael@0 1232 frameDisposalMethod == FrameBlender::kDisposeRestorePrevious)
michael@0 1233 mAnim->SetFirstFrameRefreshArea(mFrameBlender.RawGetFrame(0)->GetRect());
michael@0 1234 }
michael@0 1235
michael@0 1236 // Calculate firstFrameRefreshArea
michael@0 1237 // Some gifs are huge but only have a small area that they animate
michael@0 1238 // We only need to refresh that small area when Frame 0 comes around again
michael@0 1239 mAnim->UnionFirstFrameRefreshArea(frame->GetRect());
michael@0 1240
michael@0 1241 rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
michael@0 1242 paletteData, paletteLength, aRetFrame);
michael@0 1243
michael@0 1244 return rv;
michael@0 1245 }
michael@0 1246
michael@0 1247 bool
michael@0 1248 RasterImage::ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame)
michael@0 1249 {
michael@0 1250 if (mFrameDecodeFlags == (aNewFlags & DECODE_FLAGS_MASK))
michael@0 1251 return true; // Not asking very much of us here.
michael@0 1252
michael@0 1253 if (mDecoded) {
michael@0 1254 // If the requested frame is opaque and the current and new decode flags
michael@0 1255 // only differ in the premultiply alpha bit then we can use the existing
michael@0 1256 // frame, we don't need to discard and re-decode.
michael@0 1257 uint32_t currentNonAlphaFlags =
michael@0 1258 (mFrameDecodeFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
michael@0 1259 uint32_t newNonAlphaFlags =
michael@0 1260 (aNewFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
michael@0 1261 if (currentNonAlphaFlags == newNonAlphaFlags && FrameIsOpaque(aWhichFrame)) {
michael@0 1262 return true;
michael@0 1263 }
michael@0 1264
michael@0 1265 // if we can't discard, then we're screwed; we have no way
michael@0 1266 // to re-decode. Similarly if we aren't allowed to do a sync
michael@0 1267 // decode.
michael@0 1268 if (!(aNewFlags & FLAG_SYNC_DECODE))
michael@0 1269 return false;
michael@0 1270 if (!CanForciblyDiscardAndRedecode())
michael@0 1271 return false;
michael@0 1272 ForceDiscard();
michael@0 1273 }
michael@0 1274
michael@0 1275 mFrameDecodeFlags = aNewFlags & DECODE_FLAGS_MASK;
michael@0 1276 return true;
michael@0 1277 }
michael@0 1278
michael@0 1279 nsresult
michael@0 1280 RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation)
michael@0 1281 {
michael@0 1282 MOZ_ASSERT(NS_IsMainThread());
michael@0 1283 mDecodingMonitor.AssertCurrentThreadIn();
michael@0 1284
michael@0 1285 if (mError)
michael@0 1286 return NS_ERROR_FAILURE;
michael@0 1287
michael@0 1288 // Ensure that we have positive values
michael@0 1289 // XXX - Why isn't the size unsigned? Should this be changed?
michael@0 1290 if ((aWidth < 0) || (aHeight < 0))
michael@0 1291 return NS_ERROR_INVALID_ARG;
michael@0 1292
michael@0 1293 // if we already have a size, check the new size against the old one
michael@0 1294 if (!mMultipart && mHasSize &&
michael@0 1295 ((aWidth != mSize.width) ||
michael@0 1296 (aHeight != mSize.height) ||
michael@0 1297 (aOrientation != mOrientation))) {
michael@0 1298 NS_WARNING("Image changed size on redecode! This should not happen!");
michael@0 1299
michael@0 1300 // Make the decoder aware of the error so that it doesn't try to call
michael@0 1301 // FinishInternal during ShutdownDecoder.
michael@0 1302 if (mDecoder)
michael@0 1303 mDecoder->PostResizeError();
michael@0 1304
michael@0 1305 DoError();
michael@0 1306 return NS_ERROR_UNEXPECTED;
michael@0 1307 }
michael@0 1308
michael@0 1309 // Set the size and flag that we have it
michael@0 1310 mSize.SizeTo(aWidth, aHeight);
michael@0 1311 mOrientation = aOrientation;
michael@0 1312 mHasSize = true;
michael@0 1313
michael@0 1314 mFrameBlender.SetSize(mSize);
michael@0 1315
michael@0 1316 return NS_OK;
michael@0 1317 }
michael@0 1318
michael@0 1319 nsresult
michael@0 1320 RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
michael@0 1321 int32_t aWidth, int32_t aHeight,
michael@0 1322 gfxImageFormat aFormat,
michael@0 1323 uint8_t aPaletteDepth,
michael@0 1324 uint8_t **imageData, uint32_t *imageLength,
michael@0 1325 uint32_t **paletteData, uint32_t *paletteLength,
michael@0 1326 imgFrame** aRetFrame)
michael@0 1327 {
michael@0 1328 if (mError)
michael@0 1329 return NS_ERROR_FAILURE;
michael@0 1330
michael@0 1331 NS_ENSURE_ARG_POINTER(imageData);
michael@0 1332 NS_ENSURE_ARG_POINTER(imageLength);
michael@0 1333 NS_ENSURE_ARG_POINTER(aRetFrame);
michael@0 1334 NS_ABORT_IF_FALSE(aFrameNum <= GetNumFrames(), "Invalid frame index!");
michael@0 1335
michael@0 1336 if (aPaletteDepth > 0) {
michael@0 1337 NS_ENSURE_ARG_POINTER(paletteData);
michael@0 1338 NS_ENSURE_ARG_POINTER(paletteLength);
michael@0 1339 }
michael@0 1340
michael@0 1341 if (aFrameNum > GetNumFrames())
michael@0 1342 return NS_ERROR_INVALID_ARG;
michael@0 1343
michael@0 1344 // Adding a frame that doesn't already exist.
michael@0 1345 if (aFrameNum == GetNumFrames()) {
michael@0 1346 return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
michael@0 1347 aPaletteDepth, imageData, imageLength,
michael@0 1348 paletteData, paletteLength, aRetFrame);
michael@0 1349 }
michael@0 1350
michael@0 1351 imgFrame *frame = mFrameBlender.RawGetFrame(aFrameNum);
michael@0 1352 if (!frame) {
michael@0 1353 return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
michael@0 1354 aPaletteDepth, imageData, imageLength,
michael@0 1355 paletteData, paletteLength, aRetFrame);
michael@0 1356 }
michael@0 1357
michael@0 1358 // See if we can re-use the frame that already exists.
michael@0 1359 nsIntRect rect = frame->GetRect();
michael@0 1360 if (rect.x == aX && rect.y == aY && rect.width == aWidth &&
michael@0 1361 rect.height == aHeight && frame->GetFormat() == aFormat &&
michael@0 1362 frame->GetPaletteDepth() == aPaletteDepth) {
michael@0 1363 frame->GetImageData(imageData, imageLength);
michael@0 1364 if (paletteData) {
michael@0 1365 frame->GetPaletteData(paletteData, paletteLength);
michael@0 1366 }
michael@0 1367
michael@0 1368 *aRetFrame = frame;
michael@0 1369
michael@0 1370 // We can re-use the frame if it has image data.
michael@0 1371 if (*imageData && paletteData && *paletteData) {
michael@0 1372 return NS_OK;
michael@0 1373 }
michael@0 1374 if (*imageData && !paletteData) {
michael@0 1375 return NS_OK;
michael@0 1376 }
michael@0 1377 }
michael@0 1378
michael@0 1379 // Not reusable, so replace the frame directly.
michael@0 1380
michael@0 1381 // We know this frame is already locked, because it's the one we're currently
michael@0 1382 // writing to.
michael@0 1383 frame->UnlockImageData();
michael@0 1384
michael@0 1385 mFrameBlender.RemoveFrame(aFrameNum);
michael@0 1386 nsAutoPtr<imgFrame> newFrame(new imgFrame());
michael@0 1387 nsresult rv = newFrame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
michael@0 1388 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1389 return InternalAddFrameHelper(aFrameNum, newFrame.forget(), imageData,
michael@0 1390 imageLength, paletteData, paletteLength,
michael@0 1391 aRetFrame);
michael@0 1392 }
michael@0 1393
michael@0 1394 nsresult
michael@0 1395 RasterImage::EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
michael@0 1396 int32_t aWidth, int32_t aHeight,
michael@0 1397 gfxImageFormat aFormat,
michael@0 1398 uint8_t** imageData, uint32_t* imageLength,
michael@0 1399 imgFrame** aFrame)
michael@0 1400 {
michael@0 1401 return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
michael@0 1402 /* aPaletteDepth = */ 0, imageData, imageLength,
michael@0 1403 /* aPaletteData = */ nullptr,
michael@0 1404 /* aPaletteLength = */ nullptr,
michael@0 1405 aFrame);
michael@0 1406 }
michael@0 1407
michael@0 1408 nsresult
michael@0 1409 RasterImage::SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult)
michael@0 1410 {
michael@0 1411 if (mError)
michael@0 1412 return NS_ERROR_FAILURE;
michael@0 1413
michael@0 1414 NS_ABORT_IF_FALSE(aFrameNum < GetNumFrames(), "Invalid frame index!");
michael@0 1415 if (aFrameNum >= GetNumFrames())
michael@0 1416 return NS_ERROR_INVALID_ARG;
michael@0 1417
michael@0 1418 imgFrame* frame = mFrameBlender.RawGetFrame(aFrameNum);
michael@0 1419 NS_ABORT_IF_FALSE(frame, "Calling SetFrameAsNonPremult on frame that doesn't exist!");
michael@0 1420 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
michael@0 1421
michael@0 1422 frame->SetAsNonPremult(aIsNonPremult);
michael@0 1423
michael@0 1424 return NS_OK;
michael@0 1425 }
michael@0 1426
michael@0 1427 nsresult
michael@0 1428 RasterImage::DecodingComplete()
michael@0 1429 {
michael@0 1430 MOZ_ASSERT(NS_IsMainThread());
michael@0 1431
michael@0 1432 if (mError)
michael@0 1433 return NS_ERROR_FAILURE;
michael@0 1434
michael@0 1435 // Flag that we're done decoding.
michael@0 1436 // XXX - these should probably be combined when we fix animated image
michael@0 1437 // discarding with bug 500402.
michael@0 1438 mDecoded = true;
michael@0 1439 mHasBeenDecoded = true;
michael@0 1440
michael@0 1441 nsresult rv;
michael@0 1442
michael@0 1443 // We now have one of the qualifications for discarding. Re-evaluate.
michael@0 1444 if (CanDiscard()) {
michael@0 1445 NS_ABORT_IF_FALSE(!DiscardingActive(),
michael@0 1446 "We shouldn't have been discardable before this");
michael@0 1447 rv = DiscardTracker::Reset(&mDiscardTrackerNode);
michael@0 1448 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 1449 }
michael@0 1450
michael@0 1451 // If there's only 1 frame, optimize it. Optimizing animated images
michael@0 1452 // is not supported.
michael@0 1453 //
michael@0 1454 // We don't optimize the frame for multipart images because we reuse
michael@0 1455 // the frame.
michael@0 1456 if ((GetNumFrames() == 1) && !mMultipart) {
michael@0 1457 // CanForciblyDiscard is used instead of CanForciblyDiscardAndRedecode
michael@0 1458 // because we know decoding is complete at this point and this is not
michael@0 1459 // an animation
michael@0 1460 if (DiscardingEnabled() && CanForciblyDiscard()) {
michael@0 1461 mFrameBlender.RawGetFrame(0)->SetDiscardable();
michael@0 1462 }
michael@0 1463 rv = mFrameBlender.RawGetFrame(0)->Optimize();
michael@0 1464 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1465 }
michael@0 1466
michael@0 1467 // Double-buffer our frame in the multipart case, since we'll start decoding
michael@0 1468 // into the first frame again immediately and this produces severe tearing.
michael@0 1469 if (mMultipart) {
michael@0 1470 if (GetNumFrames() == 1) {
michael@0 1471 mMultipartDecodedFrame = mFrameBlender.SwapFrame(GetCurrentFrameIndex(),
michael@0 1472 mMultipartDecodedFrame);
michael@0 1473 } else {
michael@0 1474 // Don't double buffer for animated multipart images. It entails more
michael@0 1475 // complexity and it's not really needed since we already are smart about
michael@0 1476 // not displaying the still-decoding frame of an animated image. We may
michael@0 1477 // have already stored an extra frame, though, so we'll release it here.
michael@0 1478 delete mMultipartDecodedFrame;
michael@0 1479 mMultipartDecodedFrame = nullptr;
michael@0 1480 }
michael@0 1481 }
michael@0 1482
michael@0 1483 if (mAnim) {
michael@0 1484 mAnim->SetDoneDecoding(true);
michael@0 1485 }
michael@0 1486
michael@0 1487 return NS_OK;
michael@0 1488 }
michael@0 1489
michael@0 1490 NS_IMETHODIMP
michael@0 1491 RasterImage::SetAnimationMode(uint16_t aAnimationMode)
michael@0 1492 {
michael@0 1493 if (mAnim) {
michael@0 1494 mAnim->SetAnimationMode(aAnimationMode);
michael@0 1495 }
michael@0 1496 return SetAnimationModeInternal(aAnimationMode);
michael@0 1497 }
michael@0 1498
michael@0 1499 //******************************************************************************
michael@0 1500 /* void StartAnimation () */
michael@0 1501 nsresult
michael@0 1502 RasterImage::StartAnimation()
michael@0 1503 {
michael@0 1504 if (mError)
michael@0 1505 return NS_ERROR_FAILURE;
michael@0 1506
michael@0 1507 NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
michael@0 1508
michael@0 1509 EnsureAnimExists();
michael@0 1510
michael@0 1511 imgFrame* currentFrame = GetCurrentImgFrame();
michael@0 1512 // A timeout of -1 means we should display this frame forever.
michael@0 1513 if (currentFrame && mFrameBlender.GetTimeoutForFrame(GetCurrentImgFrameIndex()) < 0) {
michael@0 1514 mAnimationFinished = true;
michael@0 1515 return NS_ERROR_ABORT;
michael@0 1516 }
michael@0 1517
michael@0 1518 if (mAnim) {
michael@0 1519 // We need to set the time that this initial frame was first displayed, as
michael@0 1520 // this is used in AdvanceFrame().
michael@0 1521 mAnim->InitAnimationFrameTimeIfNecessary();
michael@0 1522 }
michael@0 1523
michael@0 1524 return NS_OK;
michael@0 1525 }
michael@0 1526
michael@0 1527 //******************************************************************************
michael@0 1528 /* void stopAnimation (); */
michael@0 1529 nsresult
michael@0 1530 RasterImage::StopAnimation()
michael@0 1531 {
michael@0 1532 NS_ABORT_IF_FALSE(mAnimating, "Should be animating!");
michael@0 1533
michael@0 1534 nsresult rv = NS_OK;
michael@0 1535 if (mError) {
michael@0 1536 rv = NS_ERROR_FAILURE;
michael@0 1537 } else {
michael@0 1538 mAnim->SetAnimationFrameTime(TimeStamp());
michael@0 1539 }
michael@0 1540
michael@0 1541 mAnimating = false;
michael@0 1542 return rv;
michael@0 1543 }
michael@0 1544
michael@0 1545 //******************************************************************************
michael@0 1546 /* void resetAnimation (); */
michael@0 1547 NS_IMETHODIMP
michael@0 1548 RasterImage::ResetAnimation()
michael@0 1549 {
michael@0 1550 if (mError)
michael@0 1551 return NS_ERROR_FAILURE;
michael@0 1552
michael@0 1553 if (mAnimationMode == kDontAnimMode ||
michael@0 1554 !mAnim || mAnim->GetCurrentAnimationFrameIndex() == 0)
michael@0 1555 return NS_OK;
michael@0 1556
michael@0 1557 mAnimationFinished = false;
michael@0 1558
michael@0 1559 if (mAnimating)
michael@0 1560 StopAnimation();
michael@0 1561
michael@0 1562 mFrameBlender.ResetAnimation();
michael@0 1563 mAnim->ResetAnimation();
michael@0 1564
michael@0 1565 UpdateImageContainer();
michael@0 1566
michael@0 1567 // Note - We probably want to kick off a redecode somewhere around here when
michael@0 1568 // we fix bug 500402.
michael@0 1569
michael@0 1570 // Update display
michael@0 1571 if (mStatusTracker) {
michael@0 1572 nsIntRect rect = mAnim->GetFirstFrameRefreshArea();
michael@0 1573 mStatusTracker->FrameChanged(&rect);
michael@0 1574 }
michael@0 1575
michael@0 1576 // Start the animation again. It may not have been running before, if
michael@0 1577 // mAnimationFinished was true before entering this function.
michael@0 1578 EvaluateAnimation();
michael@0 1579
michael@0 1580 return NS_OK;
michael@0 1581 }
michael@0 1582
michael@0 1583 //******************************************************************************
michael@0 1584 // [notxpcom] void setAnimationStartTime ([const] in TimeStamp aTime);
michael@0 1585 NS_IMETHODIMP_(void)
michael@0 1586 RasterImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
michael@0 1587 {
michael@0 1588 if (mError || mAnimationMode == kDontAnimMode || mAnimating || !mAnim)
michael@0 1589 return;
michael@0 1590
michael@0 1591 mAnim->SetAnimationFrameTime(aTime);
michael@0 1592 }
michael@0 1593
michael@0 1594 NS_IMETHODIMP_(float)
michael@0 1595 RasterImage::GetFrameIndex(uint32_t aWhichFrame)
michael@0 1596 {
michael@0 1597 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
michael@0 1598 return (aWhichFrame == FRAME_FIRST || !mAnim)
michael@0 1599 ? 0.0f
michael@0 1600 : mAnim->GetCurrentAnimationFrameIndex();
michael@0 1601 }
michael@0 1602
michael@0 1603 void
michael@0 1604 RasterImage::SetLoopCount(int32_t aLoopCount)
michael@0 1605 {
michael@0 1606 if (mError)
michael@0 1607 return;
michael@0 1608
michael@0 1609 if (mAnim) {
michael@0 1610 // No need to set this if we're not an animation
michael@0 1611 mFrameBlender.SetLoopCount(aLoopCount);
michael@0 1612 }
michael@0 1613 }
michael@0 1614
michael@0 1615 nsresult
michael@0 1616 RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
michael@0 1617 {
michael@0 1618 ReentrantMonitorAutoEnter lock(mDecodingMonitor);
michael@0 1619
michael@0 1620 if (mError)
michael@0 1621 return NS_ERROR_FAILURE;
michael@0 1622
michael@0 1623 NS_ENSURE_ARG_POINTER(aBuffer);
michael@0 1624 nsresult rv = NS_OK;
michael@0 1625
michael@0 1626 // We should not call this if we're not initialized
michael@0 1627 NS_ABORT_IF_FALSE(mInitialized, "Calling AddSourceData() on uninitialized "
michael@0 1628 "RasterImage!");
michael@0 1629
michael@0 1630 // We should not call this if we're already finished adding source data
michael@0 1631 NS_ABORT_IF_FALSE(!mHasSourceData, "Calling AddSourceData() after calling "
michael@0 1632 "sourceDataComplete()!");
michael@0 1633
michael@0 1634 // This call should come straight from necko - no reentrancy allowed
michael@0 1635 NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
michael@0 1636
michael@0 1637 // Image is already decoded, we shouldn't be getting data, but it could
michael@0 1638 // be extra garbage data at the end of a file.
michael@0 1639 if (mDecoded) {
michael@0 1640 return NS_OK;
michael@0 1641 }
michael@0 1642
michael@0 1643 // Starting a new part's frames, let's clean up before we add any
michael@0 1644 // This needs to happen just before we start getting EnsureFrame() call(s),
michael@0 1645 // so that there's no gap for anything to miss us.
michael@0 1646 if (mMultipart && mBytesDecoded == 0) {
michael@0 1647 // Our previous state may have been animated, so let's clean up
michael@0 1648 if (mAnimating)
michael@0 1649 StopAnimation();
michael@0 1650 mAnimationFinished = false;
michael@0 1651 if (mAnim) {
michael@0 1652 delete mAnim;
michael@0 1653 mAnim = nullptr;
michael@0 1654 }
michael@0 1655 // If there's only one frame, this could cause flickering
michael@0 1656 int old_frame_count = GetNumFrames();
michael@0 1657 if (old_frame_count > 1) {
michael@0 1658 mFrameBlender.ClearFrames();
michael@0 1659 }
michael@0 1660 }
michael@0 1661
michael@0 1662 // If we're not storing source data and we've previously gotten the size,
michael@0 1663 // write the data directly to the decoder. (If we haven't gotten the size,
michael@0 1664 // we'll queue up the data and write it out when we do.)
michael@0 1665 if (!StoringSourceData() && mHasSize) {
michael@0 1666 rv = WriteToDecoder(aBuffer, aCount, DECODE_SYNC);
michael@0 1667 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 1668
michael@0 1669 // We're not storing source data, so this data is probably coming straight
michael@0 1670 // from the network. In this case, we want to display data as soon as we
michael@0 1671 // get it, so we want to flush invalidations after every write.
michael@0 1672 nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
michael@0 1673 mInDecoder = true;
michael@0 1674 mDecoder->FlushInvalidations();
michael@0 1675 mInDecoder = false;
michael@0 1676
michael@0 1677 rv = FinishedSomeDecoding();
michael@0 1678 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 1679 }
michael@0 1680
michael@0 1681 // Otherwise, we're storing data in the source buffer
michael@0 1682 else {
michael@0 1683
michael@0 1684 // Store the data
michael@0 1685 char *newElem = mSourceData.AppendElements(aBuffer, aCount);
michael@0 1686 if (!newElem)
michael@0 1687 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1688
michael@0 1689 if (mDecoder) {
michael@0 1690 DecodePool::Singleton()->RequestDecode(this);
michael@0 1691 }
michael@0 1692 }
michael@0 1693
michael@0 1694 // Statistics
michael@0 1695 total_source_bytes += aCount;
michael@0 1696 if (mDiscardable)
michael@0 1697 discardable_source_bytes += aCount;
michael@0 1698 PR_LOG (GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
michael@0 1699 ("CompressedImageAccounting: Added compressed data to RasterImage %p (%s). "
michael@0 1700 "Total Containers: %d, Discardable containers: %d, "
michael@0 1701 "Total source bytes: %lld, Source bytes for discardable containers %lld",
michael@0 1702 this,
michael@0 1703 mSourceDataMimeType.get(),
michael@0 1704 num_containers,
michael@0 1705 num_discardable_containers,
michael@0 1706 total_source_bytes,
michael@0 1707 discardable_source_bytes));
michael@0 1708
michael@0 1709 return NS_OK;
michael@0 1710 }
michael@0 1711
michael@0 1712 /* Note! buf must be declared as char buf[9]; */
michael@0 1713 // just used for logging and hashing the header
michael@0 1714 static void
michael@0 1715 get_header_str (char *buf, char *data, size_t data_len)
michael@0 1716 {
michael@0 1717 int i;
michael@0 1718 int n;
michael@0 1719 static char hex[] = "0123456789abcdef";
michael@0 1720
michael@0 1721 n = data_len < 4 ? data_len : 4;
michael@0 1722
michael@0 1723 for (i = 0; i < n; i++) {
michael@0 1724 buf[i * 2] = hex[(data[i] >> 4) & 0x0f];
michael@0 1725 buf[i * 2 + 1] = hex[data[i] & 0x0f];
michael@0 1726 }
michael@0 1727
michael@0 1728 buf[i * 2] = 0;
michael@0 1729 }
michael@0 1730
michael@0 1731 nsresult
michael@0 1732 RasterImage::DoImageDataComplete()
michael@0 1733 {
michael@0 1734 MOZ_ASSERT(NS_IsMainThread());
michael@0 1735
michael@0 1736 if (mError)
michael@0 1737 return NS_ERROR_FAILURE;
michael@0 1738
michael@0 1739 // If we've been called before, ignore. Otherwise, flag that we have everything
michael@0 1740 if (mHasSourceData)
michael@0 1741 return NS_OK;
michael@0 1742 mHasSourceData = true;
michael@0 1743
michael@0 1744 // If there's a decoder open, synchronously decode the beginning of the image
michael@0 1745 // to check for errors and get the image's size. (If we already have the
michael@0 1746 // image's size, this does nothing.) Then kick off an async decode of the
michael@0 1747 // rest of the image.
michael@0 1748 if (mDecoder) {
michael@0 1749 nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
michael@0 1750 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 1751 }
michael@0 1752
michael@0 1753 {
michael@0 1754 ReentrantMonitorAutoEnter lock(mDecodingMonitor);
michael@0 1755
michael@0 1756 // If we're not storing any source data, then there's nothing more we can do
michael@0 1757 // once we've tried decoding for size.
michael@0 1758 if (!StoringSourceData() && mDecoder) {
michael@0 1759 nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
michael@0 1760 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 1761 }
michael@0 1762
michael@0 1763 // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
michael@0 1764 // finish decoding this image.
michael@0 1765 if (mDecoder) {
michael@0 1766 DecodePool::Singleton()->RequestDecode(this);
michael@0 1767 }
michael@0 1768
michael@0 1769 // Free up any extra space in the backing buffer
michael@0 1770 mSourceData.Compact();
michael@0 1771 }
michael@0 1772
michael@0 1773 // Log header information
michael@0 1774 if (PR_LOG_TEST(GetCompressedImageAccountingLog(), PR_LOG_DEBUG)) {
michael@0 1775 char buf[9];
michael@0 1776 get_header_str(buf, mSourceData.Elements(), mSourceData.Length());
michael@0 1777 PR_LOG (GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
michael@0 1778 ("CompressedImageAccounting: RasterImage::SourceDataComplete() - data "
michael@0 1779 "is done for container %p (%s) - header %p is 0x%s (length %d)",
michael@0 1780 this,
michael@0 1781 mSourceDataMimeType.get(),
michael@0 1782 mSourceData.Elements(),
michael@0 1783 buf,
michael@0 1784 mSourceData.Length()));
michael@0 1785 }
michael@0 1786
michael@0 1787 // We now have one of the qualifications for discarding. Re-evaluate.
michael@0 1788 if (CanDiscard()) {
michael@0 1789 nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
michael@0 1790 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 1791 }
michael@0 1792 return NS_OK;
michael@0 1793 }
michael@0 1794
michael@0 1795 nsresult
michael@0 1796 RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus, bool aLastPart)
michael@0 1797 {
michael@0 1798 nsresult finalStatus = DoImageDataComplete();
michael@0 1799
michael@0 1800 // Give precedence to Necko failure codes.
michael@0 1801 if (NS_FAILED(aStatus))
michael@0 1802 finalStatus = aStatus;
michael@0 1803
michael@0 1804 // We just recorded OnStopRequest; we need to inform our listeners.
michael@0 1805 {
michael@0 1806 ReentrantMonitorAutoEnter lock(mDecodingMonitor);
michael@0 1807
michael@0 1808 nsRefPtr<imgStatusTracker> statusTracker = CurrentStatusTracker();
michael@0 1809 statusTracker->GetDecoderObserver()->OnStopRequest(aLastPart, finalStatus);
michael@0 1810
michael@0 1811 FinishedSomeDecoding();
michael@0 1812 }
michael@0 1813
michael@0 1814 return finalStatus;
michael@0 1815 }
michael@0 1816
michael@0 1817 nsresult
michael@0 1818 RasterImage::OnImageDataAvailable(nsIRequest*,
michael@0 1819 nsISupports*,
michael@0 1820 nsIInputStream* aInStr,
michael@0 1821 uint64_t,
michael@0 1822 uint32_t aCount)
michael@0 1823 {
michael@0 1824 nsresult rv;
michael@0 1825
michael@0 1826 // WriteToRasterImage always consumes everything it gets
michael@0 1827 // if it doesn't run out of memory
michael@0 1828 uint32_t bytesRead;
michael@0 1829 rv = aInStr->ReadSegments(WriteToRasterImage, this, aCount, &bytesRead);
michael@0 1830
michael@0 1831 NS_ABORT_IF_FALSE(bytesRead == aCount || HasError(),
michael@0 1832 "WriteToRasterImage should consume everything or the image must be in error!");
michael@0 1833
michael@0 1834 return rv;
michael@0 1835 }
michael@0 1836
michael@0 1837 nsresult
michael@0 1838 RasterImage::OnNewSourceData()
michael@0 1839 {
michael@0 1840 MOZ_ASSERT(NS_IsMainThread());
michael@0 1841
michael@0 1842 nsresult rv;
michael@0 1843
michael@0 1844 if (mError)
michael@0 1845 return NS_ERROR_FAILURE;
michael@0 1846
michael@0 1847 // The source data should be complete before calling this
michael@0 1848 NS_ABORT_IF_FALSE(mHasSourceData,
michael@0 1849 "Calling NewSourceData before SourceDataComplete!");
michael@0 1850 if (!mHasSourceData)
michael@0 1851 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1852
michael@0 1853 // Only supported for multipart channels. It wouldn't be too hard to change this,
michael@0 1854 // but it would involve making sure that things worked for decode-on-draw and
michael@0 1855 // discarding. Presently there's no need for this, so we don't.
michael@0 1856 NS_ABORT_IF_FALSE(mMultipart, "NewSourceData only supported for multipart");
michael@0 1857 if (!mMultipart)
michael@0 1858 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1859
michael@0 1860 // We're multipart, so we shouldn't be storing source data
michael@0 1861 NS_ABORT_IF_FALSE(!StoringSourceData(),
michael@0 1862 "Shouldn't be storing source data for multipart");
michael@0 1863
michael@0 1864 // We're not storing the source data and we got SourceDataComplete. We should
michael@0 1865 // have shut down the previous decoder
michael@0 1866 NS_ABORT_IF_FALSE(!mDecoder, "Shouldn't have a decoder in NewSourceData");
michael@0 1867
michael@0 1868 // The decoder was shut down and we didn't flag an error, so we should be decoded
michael@0 1869 NS_ABORT_IF_FALSE(mDecoded, "Should be decoded in NewSourceData");
michael@0 1870
michael@0 1871 // Reset some flags
michael@0 1872 mDecoded = false;
michael@0 1873 mHasSourceData = false;
michael@0 1874 mHasSize = false;
michael@0 1875 mWantFullDecode = true;
michael@0 1876 mDecodeRequest = nullptr;
michael@0 1877
michael@0 1878 if (mAnim) {
michael@0 1879 mAnim->SetDoneDecoding(false);
michael@0 1880 }
michael@0 1881
michael@0 1882 // We always need the size first.
michael@0 1883 rv = InitDecoder(/* aDoSizeDecode = */ true);
michael@0 1884 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 1885
michael@0 1886 return NS_OK;
michael@0 1887 }
michael@0 1888
michael@0 1889 nsresult
michael@0 1890 RasterImage::SetSourceSizeHint(uint32_t sizeHint)
michael@0 1891 {
michael@0 1892 if (sizeHint && StoringSourceData())
michael@0 1893 return mSourceData.SetCapacity(sizeHint) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
michael@0 1894 return NS_OK;
michael@0 1895 }
michael@0 1896
michael@0 1897 /********* Methods to implement lazy allocation of nsIProperties object *************/
michael@0 1898 NS_IMETHODIMP
michael@0 1899 RasterImage::Get(const char *prop, const nsIID & iid, void * *result)
michael@0 1900 {
michael@0 1901 if (!mProperties)
michael@0 1902 return NS_ERROR_FAILURE;
michael@0 1903 return mProperties->Get(prop, iid, result);
michael@0 1904 }
michael@0 1905
michael@0 1906 NS_IMETHODIMP
michael@0 1907 RasterImage::Set(const char *prop, nsISupports *value)
michael@0 1908 {
michael@0 1909 if (!mProperties)
michael@0 1910 mProperties = do_CreateInstance("@mozilla.org/properties;1");
michael@0 1911 if (!mProperties)
michael@0 1912 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1913 return mProperties->Set(prop, value);
michael@0 1914 }
michael@0 1915
michael@0 1916 NS_IMETHODIMP
michael@0 1917 RasterImage::Has(const char *prop, bool *_retval)
michael@0 1918 {
michael@0 1919 NS_ENSURE_ARG_POINTER(_retval);
michael@0 1920 if (!mProperties) {
michael@0 1921 *_retval = false;
michael@0 1922 return NS_OK;
michael@0 1923 }
michael@0 1924 return mProperties->Has(prop, _retval);
michael@0 1925 }
michael@0 1926
michael@0 1927 NS_IMETHODIMP
michael@0 1928 RasterImage::Undefine(const char *prop)
michael@0 1929 {
michael@0 1930 if (!mProperties)
michael@0 1931 return NS_ERROR_FAILURE;
michael@0 1932 return mProperties->Undefine(prop);
michael@0 1933 }
michael@0 1934
michael@0 1935 NS_IMETHODIMP
michael@0 1936 RasterImage::GetKeys(uint32_t *count, char ***keys)
michael@0 1937 {
michael@0 1938 if (!mProperties) {
michael@0 1939 *count = 0;
michael@0 1940 *keys = nullptr;
michael@0 1941 return NS_OK;
michael@0 1942 }
michael@0 1943 return mProperties->GetKeys(count, keys);
michael@0 1944 }
michael@0 1945
michael@0 1946 void
michael@0 1947 RasterImage::Discard(bool force)
michael@0 1948 {
michael@0 1949 MOZ_ASSERT(NS_IsMainThread());
michael@0 1950
michael@0 1951 // We should be ok for discard
michael@0 1952 NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!");
michael@0 1953
michael@0 1954 // We should never discard when we have an active decoder
michael@0 1955 NS_ABORT_IF_FALSE(!mDecoder, "Asked to discard with open decoder!");
michael@0 1956
michael@0 1957 // As soon as an image becomes animated, it becomes non-discardable and any
michael@0 1958 // timers are cancelled.
michael@0 1959 NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!");
michael@0 1960
michael@0 1961 // For post-operation logging
michael@0 1962 int old_frame_count = GetNumFrames();
michael@0 1963
michael@0 1964 // Delete all the decoded frames
michael@0 1965 mFrameBlender.Discard();
michael@0 1966
michael@0 1967 // Clear our downscaled frame.
michael@0 1968 mScaleResult.status = SCALE_INVALID;
michael@0 1969 mScaleResult.frame = nullptr;
michael@0 1970
michael@0 1971 // Clear the last decoded multipart frame.
michael@0 1972 delete mMultipartDecodedFrame;
michael@0 1973 mMultipartDecodedFrame = nullptr;
michael@0 1974
michael@0 1975 // Flag that we no longer have decoded frames for this image
michael@0 1976 mDecoded = false;
michael@0 1977
michael@0 1978 // Notify that we discarded
michael@0 1979 if (mStatusTracker)
michael@0 1980 mStatusTracker->OnDiscard();
michael@0 1981
michael@0 1982 mDecodeRequest = nullptr;
michael@0 1983
michael@0 1984 if (force)
michael@0 1985 DiscardTracker::Remove(&mDiscardTrackerNode);
michael@0 1986
michael@0 1987 // Log
michael@0 1988 PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
michael@0 1989 ("CompressedImageAccounting: discarded uncompressed image "
michael@0 1990 "data from RasterImage %p (%s) - %d frames (cached count: %d); "
michael@0 1991 "Total Containers: %d, Discardable containers: %d, "
michael@0 1992 "Total source bytes: %lld, Source bytes for discardable containers %lld",
michael@0 1993 this,
michael@0 1994 mSourceDataMimeType.get(),
michael@0 1995 old_frame_count,
michael@0 1996 GetNumFrames(),
michael@0 1997 num_containers,
michael@0 1998 num_discardable_containers,
michael@0 1999 total_source_bytes,
michael@0 2000 discardable_source_bytes));
michael@0 2001 }
michael@0 2002
michael@0 2003 // Helper method to determine if we can discard an image
michael@0 2004 bool
michael@0 2005 RasterImage::CanDiscard() {
michael@0 2006 return (DiscardingEnabled() && // Globally enabled...
michael@0 2007 mDiscardable && // ...Enabled at creation time...
michael@0 2008 (mLockCount == 0) && // ...not temporarily disabled...
michael@0 2009 mHasSourceData && // ...have the source data...
michael@0 2010 mDecoded); // ...and have something to discard.
michael@0 2011 }
michael@0 2012
michael@0 2013 bool
michael@0 2014 RasterImage::CanForciblyDiscard() {
michael@0 2015 return mDiscardable && // ...Enabled at creation time...
michael@0 2016 mHasSourceData; // ...have the source data...
michael@0 2017 }
michael@0 2018
michael@0 2019 bool
michael@0 2020 RasterImage::CanForciblyDiscardAndRedecode() {
michael@0 2021 return mDiscardable && // ...Enabled at creation time...
michael@0 2022 mHasSourceData && // ...have the source data...
michael@0 2023 !mDecoder && // Can't discard with an open decoder
michael@0 2024 !mAnim; // Can never discard animated images
michael@0 2025 }
michael@0 2026
michael@0 2027 // Helper method to tell us whether the clock is currently running for
michael@0 2028 // discarding this image. Mainly for assertions.
michael@0 2029 bool
michael@0 2030 RasterImage::DiscardingActive() {
michael@0 2031 return mDiscardTrackerNode.isInList();
michael@0 2032 }
michael@0 2033
michael@0 2034 // Helper method to determine if we're storing the source data in a buffer
michael@0 2035 // or just writing it directly to the decoder
michael@0 2036 bool
michael@0 2037 RasterImage::StoringSourceData() const {
michael@0 2038 return (mDecodeOnDraw || mDiscardable);
michael@0 2039 }
michael@0 2040
michael@0 2041
michael@0 2042 // Sets up a decoder for this image. It is an error to call this function
michael@0 2043 // when decoding is already in process (ie - when mDecoder is non-null).
michael@0 2044 nsresult
michael@0 2045 RasterImage::InitDecoder(bool aDoSizeDecode)
michael@0 2046 {
michael@0 2047 // Ensure that the decoder is not already initialized
michael@0 2048 NS_ABORT_IF_FALSE(!mDecoder, "Calling InitDecoder() while already decoding!");
michael@0 2049
michael@0 2050 // We shouldn't be firing up a decoder if we already have the frames decoded
michael@0 2051 NS_ABORT_IF_FALSE(!mDecoded, "Calling InitDecoder() but already decoded!");
michael@0 2052
michael@0 2053 // Since we're not decoded, we should not have a discard timer active
michael@0 2054 NS_ABORT_IF_FALSE(!DiscardingActive(), "Discard Timer active in InitDecoder()!");
michael@0 2055
michael@0 2056 // Make sure we actually get size before doing a full decode.
michael@0 2057 if (!aDoSizeDecode) {
michael@0 2058 NS_ABORT_IF_FALSE(mHasSize, "Must do a size decode before a full decode!");
michael@0 2059 }
michael@0 2060
michael@0 2061 // Figure out which decoder we want
michael@0 2062 eDecoderType type = GetDecoderType(mSourceDataMimeType.get());
michael@0 2063 CONTAINER_ENSURE_TRUE(type != eDecoderType_unknown, NS_IMAGELIB_ERROR_NO_DECODER);
michael@0 2064
michael@0 2065 // Instantiate the appropriate decoder
michael@0 2066 switch (type) {
michael@0 2067 case eDecoderType_png:
michael@0 2068 mDecoder = new nsPNGDecoder(*this);
michael@0 2069 break;
michael@0 2070 case eDecoderType_gif:
michael@0 2071 mDecoder = new nsGIFDecoder2(*this);
michael@0 2072 break;
michael@0 2073 case eDecoderType_jpeg:
michael@0 2074 // If we have all the data we don't want to waste cpu time doing
michael@0 2075 // a progressive decode
michael@0 2076 mDecoder = new nsJPEGDecoder(*this,
michael@0 2077 mHasBeenDecoded ? Decoder::SEQUENTIAL :
michael@0 2078 Decoder::PROGRESSIVE);
michael@0 2079 break;
michael@0 2080 case eDecoderType_bmp:
michael@0 2081 mDecoder = new nsBMPDecoder(*this);
michael@0 2082 break;
michael@0 2083 case eDecoderType_ico:
michael@0 2084 mDecoder = new nsICODecoder(*this);
michael@0 2085 break;
michael@0 2086 case eDecoderType_icon:
michael@0 2087 mDecoder = new nsIconDecoder(*this);
michael@0 2088 break;
michael@0 2089 default:
michael@0 2090 NS_ABORT_IF_FALSE(0, "Shouldn't get here!");
michael@0 2091 }
michael@0 2092
michael@0 2093 // If we already have frames, we're probably in the multipart/x-mixed-replace
michael@0 2094 // case. Regardless, we need to lock the last frame. Our invariant is that,
michael@0 2095 // while we have a decoder open, the last frame is always locked.
michael@0 2096 if (GetNumFrames() > 0) {
michael@0 2097 imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
michael@0 2098 curframe->LockImageData();
michael@0 2099 }
michael@0 2100
michael@0 2101 // Initialize the decoder
michael@0 2102 if (!mDecodeRequest) {
michael@0 2103 mDecodeRequest = new DecodeRequest(this);
michael@0 2104 }
michael@0 2105 MOZ_ASSERT(mDecodeRequest->mStatusTracker);
michael@0 2106 MOZ_ASSERT(mDecodeRequest->mStatusTracker->GetDecoderObserver());
michael@0 2107 mDecoder->SetObserver(mDecodeRequest->mStatusTracker->GetDecoderObserver());
michael@0 2108 mDecoder->SetSizeDecode(aDoSizeDecode);
michael@0 2109 mDecoder->SetDecodeFlags(mFrameDecodeFlags);
michael@0 2110 if (!aDoSizeDecode) {
michael@0 2111 // We already have the size; tell the decoder so it can preallocate a
michael@0 2112 // frame. By default, we create an ARGB frame with no offset. If decoders
michael@0 2113 // need a different type, they need to ask for it themselves.
michael@0 2114 mDecoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
michael@0 2115 gfxImageFormat::ARGB32);
michael@0 2116 mDecoder->AllocateFrame();
michael@0 2117 }
michael@0 2118 mDecoder->Init();
michael@0 2119 CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
michael@0 2120
michael@0 2121 if (!aDoSizeDecode) {
michael@0 2122 Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
michael@0 2123 mDecodeCount++;
michael@0 2124 Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
michael@0 2125
michael@0 2126 if (mDecodeCount > sMaxDecodeCount) {
michael@0 2127 // Don't subtract out 0 from the histogram, because that causes its count
michael@0 2128 // to go negative, which is not kosher.
michael@0 2129 if (sMaxDecodeCount > 0) {
michael@0 2130 Telemetry::GetHistogramById(Telemetry::IMAGE_MAX_DECODE_COUNT)->Subtract(sMaxDecodeCount);
michael@0 2131 }
michael@0 2132 sMaxDecodeCount = mDecodeCount;
michael@0 2133 Telemetry::GetHistogramById(Telemetry::IMAGE_MAX_DECODE_COUNT)->Add(sMaxDecodeCount);
michael@0 2134 }
michael@0 2135 }
michael@0 2136
michael@0 2137 return NS_OK;
michael@0 2138 }
michael@0 2139
michael@0 2140 // Flushes, closes, and nulls-out a decoder. Cleans up any related decoding
michael@0 2141 // state. It is an error to call this function when there is no initialized
michael@0 2142 // decoder.
michael@0 2143 //
michael@0 2144 // aIntent specifies the intent of the shutdown. If aIntent is
michael@0 2145 // eShutdownIntent_Done, an error is flagged if we didn't get what we should
michael@0 2146 // have out of the decode. If aIntent is eShutdownIntent_NotNeeded, we don't
michael@0 2147 // check this. If aIntent is eShutdownIntent_Error, we shut down in error mode.
michael@0 2148 nsresult
michael@0 2149 RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
michael@0 2150 {
michael@0 2151 MOZ_ASSERT(NS_IsMainThread());
michael@0 2152 mDecodingMonitor.AssertCurrentThreadIn();
michael@0 2153
michael@0 2154 // Ensure that our intent is valid
michael@0 2155 NS_ABORT_IF_FALSE((aIntent >= 0) && (aIntent < eShutdownIntent_AllCount),
michael@0 2156 "Invalid shutdown intent");
michael@0 2157
michael@0 2158 // Ensure that the decoder is initialized
michael@0 2159 NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
michael@0 2160
michael@0 2161 // Figure out what kind of decode we were doing before we get rid of our decoder
michael@0 2162 bool wasSizeDecode = mDecoder->IsSizeDecode();
michael@0 2163
michael@0 2164 // Finalize the decoder
michael@0 2165 // null out mDecoder, _then_ check for errors on the close (otherwise the
michael@0 2166 // error routine might re-invoke ShutdownDecoder)
michael@0 2167 nsRefPtr<Decoder> decoder = mDecoder;
michael@0 2168 mDecoder = nullptr;
michael@0 2169
michael@0 2170 mFinishing = true;
michael@0 2171 mInDecoder = true;
michael@0 2172 decoder->Finish(aIntent);
michael@0 2173 mInDecoder = false;
michael@0 2174 mFinishing = false;
michael@0 2175
michael@0 2176 // Unlock the last frame (if we have any). Our invariant is that, while we
michael@0 2177 // have a decoder open, the last frame is always locked.
michael@0 2178 if (GetNumFrames() > 0) {
michael@0 2179 imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
michael@0 2180 curframe->UnlockImageData();
michael@0 2181 }
michael@0 2182
michael@0 2183 // Kill off our decode request, if it's pending. (If not, this call is
michael@0 2184 // harmless.)
michael@0 2185 DecodePool::StopDecoding(this);
michael@0 2186
michael@0 2187 nsresult decoderStatus = decoder->GetDecoderError();
michael@0 2188 if (NS_FAILED(decoderStatus)) {
michael@0 2189 DoError();
michael@0 2190 return decoderStatus;
michael@0 2191 }
michael@0 2192
michael@0 2193 // We just shut down the decoder. If we didn't get what we want, but expected
michael@0 2194 // to, flag an error
michael@0 2195 bool failed = false;
michael@0 2196 if (wasSizeDecode && !mHasSize)
michael@0 2197 failed = true;
michael@0 2198 if (!wasSizeDecode && !mDecoded)
michael@0 2199 failed = true;
michael@0 2200 if ((aIntent == eShutdownIntent_Done) && failed) {
michael@0 2201 DoError();
michael@0 2202 return NS_ERROR_FAILURE;
michael@0 2203 }
michael@0 2204
michael@0 2205 // If we finished a full decode, and we're not meant to be storing source
michael@0 2206 // data, stop storing it.
michael@0 2207 if (!wasSizeDecode && !StoringSourceData()) {
michael@0 2208 mSourceData.Clear();
michael@0 2209 }
michael@0 2210
michael@0 2211 mBytesDecoded = 0;
michael@0 2212
michael@0 2213 return NS_OK;
michael@0 2214 }
michael@0 2215
michael@0 2216 // Writes the data to the decoder, updating the total number of bytes written.
michael@0 2217 nsresult
michael@0 2218 RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
michael@0 2219 {
michael@0 2220 mDecodingMonitor.AssertCurrentThreadIn();
michael@0 2221
michael@0 2222 // We should have a decoder
michael@0 2223 NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
michael@0 2224
michael@0 2225 // Write
michael@0 2226 nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
michael@0 2227 mInDecoder = true;
michael@0 2228 mDecoder->Write(aBuffer, aCount, aStrategy);
michael@0 2229 mInDecoder = false;
michael@0 2230
michael@0 2231 CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
michael@0 2232
michael@0 2233 // Keep track of the total number of bytes written over the lifetime of the
michael@0 2234 // decoder
michael@0 2235 mBytesDecoded += aCount;
michael@0 2236
michael@0 2237 return NS_OK;
michael@0 2238 }
michael@0 2239
michael@0 2240 // This function is called in situations where it's clear that we want the
michael@0 2241 // frames in decoded form (Draw, GetFrame, etc). If we're completely decoded,
michael@0 2242 // this method resets the discard timer (if we're discardable), since wanting
michael@0 2243 // the frames now is a good indicator of wanting them again soon. If we're not
michael@0 2244 // decoded, this method kicks off asynchronous decoding to generate the frames.
michael@0 2245 nsresult
michael@0 2246 RasterImage::WantDecodedFrames()
michael@0 2247 {
michael@0 2248 nsresult rv;
michael@0 2249
michael@0 2250 // If we can discard, the clock should be running. Reset it.
michael@0 2251 if (CanDiscard()) {
michael@0 2252 NS_ABORT_IF_FALSE(DiscardingActive(),
michael@0 2253 "Decoded and discardable but discarding not activated!");
michael@0 2254 rv = DiscardTracker::Reset(&mDiscardTrackerNode);
michael@0 2255 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2256 }
michael@0 2257
michael@0 2258 // Request a decode (no-op if we're decoded)
michael@0 2259 return StartDecoding();
michael@0 2260 }
michael@0 2261
michael@0 2262 //******************************************************************************
michael@0 2263 /* void requestDecode() */
michael@0 2264 NS_IMETHODIMP
michael@0 2265 RasterImage::RequestDecode()
michael@0 2266 {
michael@0 2267 return RequestDecodeCore(SYNCHRONOUS_NOTIFY);
michael@0 2268 }
michael@0 2269
michael@0 2270 /* void startDecode() */
michael@0 2271 NS_IMETHODIMP
michael@0 2272 RasterImage::StartDecoding()
michael@0 2273 {
michael@0 2274 if (!NS_IsMainThread()) {
michael@0 2275 return NS_DispatchToMainThread(
michael@0 2276 NS_NewRunnableMethod(this, &RasterImage::StartDecoding));
michael@0 2277 }
michael@0 2278 // Here we are explicitly trading off flashing for responsiveness in the case
michael@0 2279 // that we're redecoding an image (see bug 845147).
michael@0 2280 return RequestDecodeCore(mHasBeenDecoded ?
michael@0 2281 SYNCHRONOUS_NOTIFY : SYNCHRONOUS_NOTIFY_AND_SOME_DECODE);
michael@0 2282 }
michael@0 2283
michael@0 2284 bool
michael@0 2285 RasterImage::IsDecoded()
michael@0 2286 {
michael@0 2287 return mDecoded || mError;
michael@0 2288 }
michael@0 2289
michael@0 2290 NS_IMETHODIMP
michael@0 2291 RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
michael@0 2292 {
michael@0 2293 MOZ_ASSERT(NS_IsMainThread());
michael@0 2294
michael@0 2295 nsresult rv;
michael@0 2296
michael@0 2297 if (mError)
michael@0 2298 return NS_ERROR_FAILURE;
michael@0 2299
michael@0 2300 // If we're already decoded, there's nothing to do.
michael@0 2301 if (mDecoded)
michael@0 2302 return NS_OK;
michael@0 2303
michael@0 2304 // mFinishing protects against the case when we enter RequestDecode from
michael@0 2305 // ShutdownDecoder -- in that case, we're done with the decode, we're just
michael@0 2306 // not quite ready to admit it. See bug 744309.
michael@0 2307 if (mFinishing)
michael@0 2308 return NS_OK;
michael@0 2309
michael@0 2310 // If we're currently waiting for a new frame, we can't do anything until
michael@0 2311 // that frame is allocated.
michael@0 2312 if (mDecoder && mDecoder->NeedsNewFrame())
michael@0 2313 return NS_OK;
michael@0 2314
michael@0 2315 // If our callstack goes through a size decoder, we have a problem.
michael@0 2316 // We need to shutdown the size decode and replace it with a full
michael@0 2317 // decoder, but can't do that from within the decoder itself. Thus, we post
michael@0 2318 // an asynchronous event to the event loop to do it later. Since
michael@0 2319 // RequestDecode() is an asynchronous function this works fine (though it's
michael@0 2320 // a little slower).
michael@0 2321 if (mInDecoder) {
michael@0 2322 nsRefPtr<imgDecodeRequestor> requestor = new imgDecodeRequestor(*this);
michael@0 2323 return NS_DispatchToCurrentThread(requestor);
michael@0 2324 }
michael@0 2325
michael@0 2326 // If we have a size decoder open, make sure we get the size
michael@0 2327 if (mDecoder && mDecoder->IsSizeDecode()) {
michael@0 2328 nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
michael@0 2329 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2330
michael@0 2331 // If we didn't get the size out of the image, we won't until we get more
michael@0 2332 // data, so signal that we want a full decode and give up for now.
michael@0 2333 if (!mHasSize) {
michael@0 2334 mWantFullDecode = true;
michael@0 2335 return NS_OK;
michael@0 2336 }
michael@0 2337 }
michael@0 2338
michael@0 2339 ReentrantMonitorAutoEnter lock(mDecodingMonitor);
michael@0 2340
michael@0 2341 // If we don't have any bytes to flush to the decoder, we can't do anything.
michael@0 2342 // mBytesDecoded can be bigger than mSourceData.Length() if we're not storing
michael@0 2343 // the source data.
michael@0 2344 if (mBytesDecoded > mSourceData.Length())
michael@0 2345 return NS_OK;
michael@0 2346
michael@0 2347 // If the image is waiting for decode work to be notified, go ahead and do that.
michael@0 2348 if (mDecodeRequest &&
michael@0 2349 mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE &&
michael@0 2350 aDecodeType != ASYNCHRONOUS) {
michael@0 2351 nsresult rv = FinishedSomeDecoding();
michael@0 2352 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2353 }
michael@0 2354
michael@0 2355 // If we're fully decoded, we have nothing to do. We need this check after
michael@0 2356 // DecodeUntilSizeAvailable and FinishedSomeDecoding because they can result
michael@0 2357 // in us finishing an in-progress decode (or kicking off and finishing a
michael@0 2358 // synchronous decode if we're already waiting on a full decode).
michael@0 2359 if (mDecoded) {
michael@0 2360 return NS_OK;
michael@0 2361 }
michael@0 2362
michael@0 2363 // If we've already got a full decoder running, and have already
michael@0 2364 // decoded some bytes, we have nothing to do
michael@0 2365 if (mDecoder && !mDecoder->IsSizeDecode() && mBytesDecoded) {
michael@0 2366 return NS_OK;
michael@0 2367 }
michael@0 2368
michael@0 2369 // If we have a size decode open, interrupt it and shut it down; or if
michael@0 2370 // the decoder has different flags than what we need
michael@0 2371 if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
michael@0 2372 nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
michael@0 2373 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2374 }
michael@0 2375
michael@0 2376 // If we don't have a decoder, create one
michael@0 2377 if (!mDecoder) {
michael@0 2378 rv = InitDecoder(/* aDoSizeDecode = */ false);
michael@0 2379 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2380
michael@0 2381 rv = FinishedSomeDecoding();
michael@0 2382 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2383
michael@0 2384 MOZ_ASSERT(mDecoder);
michael@0 2385 }
michael@0 2386
michael@0 2387 // If we've read all the data we have, we're done
michael@0 2388 if (mHasSourceData && mBytesDecoded == mSourceData.Length())
michael@0 2389 return NS_OK;
michael@0 2390
michael@0 2391 // If we can do decoding now, do so. Small images will decode completely,
michael@0 2392 // large images will decode a bit and post themselves to the event loop
michael@0 2393 // to finish decoding.
michael@0 2394 if (!mDecoded && !mInDecoder && mHasSourceData && aDecodeType == SYNCHRONOUS_NOTIFY_AND_SOME_DECODE) {
michael@0 2395 PROFILER_LABEL_PRINTF("RasterImage", "DecodeABitOf", "%s", GetURIString().get());
michael@0 2396 DecodePool::Singleton()->DecodeABitOf(this, DECODE_SYNC);
michael@0 2397 return NS_OK;
michael@0 2398 }
michael@0 2399
michael@0 2400 if (!mDecoded) {
michael@0 2401 // If we get this far, dispatch the worker. We do this instead of starting
michael@0 2402 // any immediate decoding to guarantee that all our decode notifications are
michael@0 2403 // dispatched asynchronously, and to ensure we stay responsive.
michael@0 2404 DecodePool::Singleton()->RequestDecode(this);
michael@0 2405 }
michael@0 2406
michael@0 2407 return NS_OK;
michael@0 2408 }
michael@0 2409
michael@0 2410 // Synchronously decodes as much data as possible
michael@0 2411 nsresult
michael@0 2412 RasterImage::SyncDecode()
michael@0 2413 {
michael@0 2414 PROFILER_LABEL_PRINTF("RasterImage", "SyncDecode", "%s", GetURIString().get());;
michael@0 2415
michael@0 2416 // If we have a size decoder open, make sure we get the size
michael@0 2417 if (mDecoder && mDecoder->IsSizeDecode()) {
michael@0 2418 nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
michael@0 2419 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2420
michael@0 2421 // If we didn't get the size out of the image, we won't until we get more
michael@0 2422 // data, so signal that we want a full decode and give up for now.
michael@0 2423 if (!mHasSize) {
michael@0 2424 mWantFullDecode = true;
michael@0 2425 return NS_ERROR_NOT_AVAILABLE;
michael@0 2426 }
michael@0 2427 }
michael@0 2428
michael@0 2429 ReentrantMonitorAutoEnter lock(mDecodingMonitor);
michael@0 2430
michael@0 2431 // We really have no good way of forcing a synchronous decode if we're being
michael@0 2432 // called in a re-entrant manner (ie, from an event listener fired by a
michael@0 2433 // decoder), because the decoding machinery is already tied up. We thus explicitly
michael@0 2434 // disallow this type of call in the API, and check for it in API methods.
michael@0 2435 NS_ABORT_IF_FALSE(!mInDecoder, "Yikes, forcing sync in reentrant call!");
michael@0 2436
michael@0 2437 if (mDecodeRequest) {
michael@0 2438 // If the image is waiting for decode work to be notified, go ahead and do that.
michael@0 2439 if (mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
michael@0 2440 nsresult rv = FinishedSomeDecoding();
michael@0 2441 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2442 }
michael@0 2443 }
michael@0 2444
michael@0 2445 nsresult rv;
michael@0 2446
michael@0 2447 // If we're decoded already, or decoding until the size was available
michael@0 2448 // finished us as a side-effect, no worries
michael@0 2449 if (mDecoded)
michael@0 2450 return NS_OK;
michael@0 2451
michael@0 2452 // If we don't have any bytes to flush to the decoder, we can't do anything.
michael@0 2453 // mBytesDecoded can be bigger than mSourceData.Length() if we're not storing
michael@0 2454 // the source data.
michael@0 2455 if (mBytesDecoded > mSourceData.Length())
michael@0 2456 return NS_OK;
michael@0 2457
michael@0 2458 // If we have a decoder open with different flags than what we need, shut it
michael@0 2459 // down
michael@0 2460 if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
michael@0 2461 nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
michael@0 2462 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2463
michael@0 2464 if (mDecoded) {
michael@0 2465 // If we've finished decoding we need to discard so we can re-decode
michael@0 2466 // with the new flags. If we can't discard then there isn't
michael@0 2467 // anything we can do.
michael@0 2468 if (!CanForciblyDiscardAndRedecode())
michael@0 2469 return NS_ERROR_NOT_AVAILABLE;
michael@0 2470 ForceDiscard();
michael@0 2471 }
michael@0 2472 }
michael@0 2473
michael@0 2474 // If we're currently waiting on a new frame for this image, we have to create
michael@0 2475 // it now.
michael@0 2476 if (mDecoder && mDecoder->NeedsNewFrame()) {
michael@0 2477 mDecoder->AllocateFrame();
michael@0 2478 mDecodeRequest->mAllocatedNewFrame = true;
michael@0 2479 }
michael@0 2480
michael@0 2481 // If we don't have a decoder, create one
michael@0 2482 if (!mDecoder) {
michael@0 2483 rv = InitDecoder(/* aDoSizeDecode = */ false);
michael@0 2484 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2485 }
michael@0 2486
michael@0 2487 // Write everything we have
michael@0 2488 rv = DecodeSomeData(mSourceData.Length() - mBytesDecoded, DECODE_SYNC);
michael@0 2489 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2490
michael@0 2491 // When we're doing a sync decode, we want to get as much information from the
michael@0 2492 // image as possible. We've send the decoder all of our data, so now's a good
michael@0 2493 // time to flush any invalidations (in case we don't have all the data and what
michael@0 2494 // we got left us mid-frame).
michael@0 2495 nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
michael@0 2496 mInDecoder = true;
michael@0 2497 mDecoder->FlushInvalidations();
michael@0 2498 mInDecoder = false;
michael@0 2499
michael@0 2500 rv = FinishedSomeDecoding();
michael@0 2501 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2502
michael@0 2503 // If our decoder's still open, there's still work to be done.
michael@0 2504 if (mDecoder) {
michael@0 2505 DecodePool::Singleton()->RequestDecode(this);
michael@0 2506 }
michael@0 2507
michael@0 2508 // All good if no errors!
michael@0 2509 return mError ? NS_ERROR_FAILURE : NS_OK;
michael@0 2510 }
michael@0 2511
michael@0 2512 bool
michael@0 2513 RasterImage::CanQualityScale(const gfxSize& scale)
michael@0 2514 {
michael@0 2515 // If target size is 1:1 with original, don't scale.
michael@0 2516 if (scale.width == 1.0 && scale.height == 1.0)
michael@0 2517 return false;
michael@0 2518
michael@0 2519 // To save memory don't quality upscale images bigger than the limit.
michael@0 2520 if (scale.width > 1.0 || scale.height > 1.0) {
michael@0 2521 uint32_t scaled_size = static_cast<uint32_t>(mSize.width * mSize.height * scale.width * scale.height);
michael@0 2522 if (scaled_size > gHQUpscalingMaxSize)
michael@0 2523 return false;
michael@0 2524 }
michael@0 2525
michael@0 2526 return true;
michael@0 2527 }
michael@0 2528
michael@0 2529 bool
michael@0 2530 RasterImage::CanScale(GraphicsFilter aFilter,
michael@0 2531 gfxSize aScale, uint32_t aFlags)
michael@0 2532 {
michael@0 2533 // The high-quality scaler requires Skia.
michael@0 2534 #ifdef MOZ_ENABLE_SKIA
michael@0 2535 // We don't use the scaler for animated or multipart images to avoid doing a
michael@0 2536 // bunch of work on an image that just gets thrown away.
michael@0 2537 // We only use the scaler when drawing to the window because, if we're not
michael@0 2538 // drawing to a window (eg a canvas), updates to that image will be ignored.
michael@0 2539 if (gHQDownscaling && aFilter == GraphicsFilter::FILTER_GOOD &&
michael@0 2540 !mAnim && mDecoded && !mMultipart && CanQualityScale(aScale) &&
michael@0 2541 (aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
michael@0 2542 gfxFloat factor = gHQDownscalingMinFactor / 1000.0;
michael@0 2543
michael@0 2544 return (aScale.width < factor || aScale.height < factor);
michael@0 2545 }
michael@0 2546 #endif
michael@0 2547
michael@0 2548 return false;
michael@0 2549 }
michael@0 2550
michael@0 2551 void
michael@0 2552 RasterImage::ScalingStart(ScaleRequest* request)
michael@0 2553 {
michael@0 2554 MOZ_ASSERT(request);
michael@0 2555 mScaleResult.scale = request->scale;
michael@0 2556 mScaleResult.status = SCALE_PENDING;
michael@0 2557 mScaleRequest = request;
michael@0 2558 }
michael@0 2559
michael@0 2560 void
michael@0 2561 RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
michael@0 2562 {
michael@0 2563 MOZ_ASSERT(status == SCALE_DONE || status == SCALE_INVALID);
michael@0 2564 MOZ_ASSERT(request);
michael@0 2565
michael@0 2566 if (status == SCALE_DONE) {
michael@0 2567 MOZ_ASSERT(request->done);
michael@0 2568
michael@0 2569 imgFrame *scaledFrame = request->dstFrame.forget();
michael@0 2570 scaledFrame->ImageUpdated(scaledFrame->GetRect());
michael@0 2571 scaledFrame->ApplyDirtToSurfaces();
michael@0 2572
michael@0 2573 if (mStatusTracker) {
michael@0 2574 mStatusTracker->FrameChanged(&request->srcRect);
michael@0 2575 }
michael@0 2576
michael@0 2577 mScaleResult.status = SCALE_DONE;
michael@0 2578 mScaleResult.frame = scaledFrame;
michael@0 2579 mScaleResult.scale = request->scale;
michael@0 2580 } else {
michael@0 2581 mScaleResult.status = SCALE_INVALID;
michael@0 2582 mScaleResult.frame = nullptr;
michael@0 2583 }
michael@0 2584
michael@0 2585 // If we were waiting for this scale to come through, forget the scale
michael@0 2586 // request. Otherwise, we still have a scale outstanding that it's possible
michael@0 2587 // for us to (want to) stop.
michael@0 2588 if (mScaleRequest == request) {
michael@0 2589 mScaleRequest = nullptr;
michael@0 2590 }
michael@0 2591 }
michael@0 2592
michael@0 2593 bool
michael@0 2594 RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
michael@0 2595 gfxContext *aContext,
michael@0 2596 GraphicsFilter aFilter,
michael@0 2597 const gfxMatrix &aUserSpaceToImageSpace,
michael@0 2598 const gfxRect &aFill,
michael@0 2599 const nsIntRect &aSubimage,
michael@0 2600 uint32_t aFlags)
michael@0 2601 {
michael@0 2602 imgFrame *frame = aFrame;
michael@0 2603 nsIntRect framerect = frame->GetRect();
michael@0 2604 gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
michael@0 2605 gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
michael@0 2606 imageSpaceToUserSpace.Invert();
michael@0 2607 gfxSize scale = imageSpaceToUserSpace.ScaleFactors(true);
michael@0 2608 nsIntRect subimage = aSubimage;
michael@0 2609 nsRefPtr<gfxASurface> surf;
michael@0 2610
michael@0 2611 if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) {
michael@0 2612 // If scale factor is still the same that we scaled for and
michael@0 2613 // ScaleWorker isn't still working, then we can use pre-downscaled frame.
michael@0 2614 // If scale factor has changed, order new request.
michael@0 2615 // FIXME: Current implementation doesn't support pre-downscale
michael@0 2616 // mechanism for multiple sizes from same src, since we cache
michael@0 2617 // pre-downscaled frame only for the latest requested scale.
michael@0 2618 // The solution is to cache more than one scaled image frame
michael@0 2619 // for each RasterImage.
michael@0 2620 bool needScaleReq;
michael@0 2621 if (mScaleResult.status == SCALE_DONE && mScaleResult.scale == scale) {
michael@0 2622 // Grab and hold the surface to make sure the OS didn't destroy it
michael@0 2623 mScaleResult.frame->GetSurface(getter_AddRefs(surf));
michael@0 2624 needScaleReq = !surf;
michael@0 2625 if (surf) {
michael@0 2626 frame = mScaleResult.frame;
michael@0 2627 userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width,
michael@0 2628 scale.height));
michael@0 2629
michael@0 2630 // Since we're switching to a scaled image, we need to transform the
michael@0 2631 // area of the subimage to draw accordingly, since imgFrame::Draw()
michael@0 2632 // doesn't know about scaled frames.
michael@0 2633 subimage.ScaleRoundOut(scale.width, scale.height);
michael@0 2634 }
michael@0 2635 } else {
michael@0 2636 needScaleReq = !(mScaleResult.status == SCALE_PENDING &&
michael@0 2637 mScaleResult.scale == scale);
michael@0 2638 }
michael@0 2639
michael@0 2640 // If we're not waiting for exactly this result, and there's only one
michael@0 2641 // instance of this image on this page, ask for a scale.
michael@0 2642 if (needScaleReq && mLockCount == 1) {
michael@0 2643 if (NS_FAILED(frame->LockImageData())) {
michael@0 2644 frame->UnlockImageData();
michael@0 2645 return false;
michael@0 2646 }
michael@0 2647
michael@0 2648 // If we have an outstanding request, signal it to stop (if it can).
michael@0 2649 if (mScaleRequest) {
michael@0 2650 mScaleRequest->stopped = true;
michael@0 2651 }
michael@0 2652
michael@0 2653 nsRefPtr<ScaleRunner> runner = new ScaleRunner(this, scale, frame);
michael@0 2654 if (runner->IsOK()) {
michael@0 2655 if (!sScaleWorkerThread) {
michael@0 2656 NS_NewNamedThread("Image Scaler", getter_AddRefs(sScaleWorkerThread));
michael@0 2657 ClearOnShutdown(&sScaleWorkerThread);
michael@0 2658 }
michael@0 2659
michael@0 2660 sScaleWorkerThread->Dispatch(runner, NS_DISPATCH_NORMAL);
michael@0 2661 }
michael@0 2662 frame->UnlockImageData();
michael@0 2663 }
michael@0 2664 }
michael@0 2665
michael@0 2666 nsIntMargin padding(framerect.y,
michael@0 2667 mSize.width - framerect.XMost(),
michael@0 2668 mSize.height - framerect.YMost(),
michael@0 2669 framerect.x);
michael@0 2670
michael@0 2671 return frame->Draw(aContext, aFilter, userSpaceToImageSpace,
michael@0 2672 aFill, padding, subimage, aFlags);
michael@0 2673 }
michael@0 2674
michael@0 2675 //******************************************************************************
michael@0 2676 /* [noscript] void draw(in gfxContext aContext,
michael@0 2677 * in gfxGraphicsFilter aFilter,
michael@0 2678 * [const] in gfxMatrix aUserSpaceToImageSpace,
michael@0 2679 * [const] in gfxRect aFill,
michael@0 2680 * [const] in nsIntRect aSubimage,
michael@0 2681 * [const] in nsIntSize aViewportSize,
michael@0 2682 * [const] in SVGImageContext aSVGContext,
michael@0 2683 * in uint32_t aWhichFrame,
michael@0 2684 * in uint32_t aFlags); */
michael@0 2685 NS_IMETHODIMP
michael@0 2686 RasterImage::Draw(gfxContext *aContext,
michael@0 2687 GraphicsFilter aFilter,
michael@0 2688 const gfxMatrix &aUserSpaceToImageSpace,
michael@0 2689 const gfxRect &aFill,
michael@0 2690 const nsIntRect &aSubimage,
michael@0 2691 const nsIntSize& /*aViewportSize - ignored*/,
michael@0 2692 const SVGImageContext* /*aSVGContext - ignored*/,
michael@0 2693 uint32_t aWhichFrame,
michael@0 2694 uint32_t aFlags)
michael@0 2695 {
michael@0 2696 if (aWhichFrame > FRAME_MAX_VALUE)
michael@0 2697 return NS_ERROR_INVALID_ARG;
michael@0 2698
michael@0 2699 if (mError)
michael@0 2700 return NS_ERROR_FAILURE;
michael@0 2701
michael@0 2702 // Disallowed in the API
michael@0 2703 if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
michael@0 2704 return NS_ERROR_FAILURE;
michael@0 2705
michael@0 2706 // Illegal -- you can't draw with non-default decode flags.
michael@0 2707 // (Disabling colorspace conversion might make sense to allow, but
michael@0 2708 // we don't currently.)
michael@0 2709 if ((aFlags & DECODE_FLAGS_MASK) != DECODE_FLAGS_DEFAULT)
michael@0 2710 return NS_ERROR_FAILURE;
michael@0 2711
michael@0 2712 NS_ENSURE_ARG_POINTER(aContext);
michael@0 2713
michael@0 2714 // We can only draw without discarding and redecoding in these cases:
michael@0 2715 // * We have the default decode flags.
michael@0 2716 // * We have exactly FLAG_DECODE_NO_PREMULTIPLY_ALPHA and the current frame
michael@0 2717 // is opaque.
michael@0 2718 bool haveDefaultFlags = (mFrameDecodeFlags == DECODE_FLAGS_DEFAULT);
michael@0 2719 bool haveSafeAlphaFlags =
michael@0 2720 (mFrameDecodeFlags == FLAG_DECODE_NO_PREMULTIPLY_ALPHA) &&
michael@0 2721 FrameIsOpaque(FRAME_CURRENT);
michael@0 2722
michael@0 2723 if (!(haveDefaultFlags || haveSafeAlphaFlags)) {
michael@0 2724 if (!CanForciblyDiscardAndRedecode())
michael@0 2725 return NS_ERROR_NOT_AVAILABLE;
michael@0 2726 ForceDiscard();
michael@0 2727
michael@0 2728 mFrameDecodeFlags = DECODE_FLAGS_DEFAULT;
michael@0 2729 }
michael@0 2730
michael@0 2731 // If this image is a candidate for discarding, reset its position in the
michael@0 2732 // discard tracker so we're less likely to discard it right away.
michael@0 2733 //
michael@0 2734 // (We don't normally draw unlocked images, so this conditition will usually
michael@0 2735 // be false. But we will draw unlocked images if image locking is globally
michael@0 2736 // disabled via the image.mem.allow_locking_in_content_processes pref.)
michael@0 2737 if (DiscardingActive()) {
michael@0 2738 DiscardTracker::Reset(&mDiscardTrackerNode);
michael@0 2739 }
michael@0 2740
michael@0 2741
michael@0 2742 if (IsUnlocked() && mStatusTracker) {
michael@0 2743 mStatusTracker->OnUnlockedDraw();
michael@0 2744 }
michael@0 2745
michael@0 2746 // We use !mDecoded && mHasSourceData to mean discarded.
michael@0 2747 if (!mDecoded && mHasSourceData) {
michael@0 2748 mDrawStartTime = TimeStamp::Now();
michael@0 2749 }
michael@0 2750
michael@0 2751 // If a synchronous draw is requested, flush anything that might be sitting around
michael@0 2752 if (aFlags & FLAG_SYNC_DECODE) {
michael@0 2753 nsresult rv = SyncDecode();
michael@0 2754 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2755 }
michael@0 2756
michael@0 2757 uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0
michael@0 2758 : GetCurrentImgFrameIndex();
michael@0 2759 imgFrame* frame = GetDrawableImgFrame(frameIndex);
michael@0 2760 if (!frame) {
michael@0 2761 return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
michael@0 2762 }
michael@0 2763
michael@0 2764 bool drawn = DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter,
michael@0 2765 aUserSpaceToImageSpace, aFill,
michael@0 2766 aSubimage, aFlags);
michael@0 2767 if (!drawn) {
michael@0 2768 // The OS threw out some or all of our buffer. Start decoding again.
michael@0 2769 ForceDiscard();
michael@0 2770 WantDecodedFrames();
michael@0 2771 return NS_OK;
michael@0 2772 }
michael@0 2773
michael@0 2774 if (mDecoded && !mDrawStartTime.IsNull()) {
michael@0 2775 TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
michael@0 2776 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, int32_t(drawLatency.ToMicroseconds()));
michael@0 2777 // clear the value of mDrawStartTime
michael@0 2778 mDrawStartTime = TimeStamp();
michael@0 2779 }
michael@0 2780
michael@0 2781 return NS_OK;
michael@0 2782 }
michael@0 2783
michael@0 2784 //******************************************************************************
michael@0 2785 /* void lockImage() */
michael@0 2786 NS_IMETHODIMP
michael@0 2787 RasterImage::LockImage()
michael@0 2788 {
michael@0 2789 MOZ_ASSERT(NS_IsMainThread(),
michael@0 2790 "Main thread to encourage serialization with UnlockImage");
michael@0 2791 if (mError)
michael@0 2792 return NS_ERROR_FAILURE;
michael@0 2793
michael@0 2794 // Cancel the discard timer if it's there
michael@0 2795 DiscardTracker::Remove(&mDiscardTrackerNode);
michael@0 2796
michael@0 2797 // Increment the lock count
michael@0 2798 mLockCount++;
michael@0 2799
michael@0 2800 return NS_OK;
michael@0 2801 }
michael@0 2802
michael@0 2803 //******************************************************************************
michael@0 2804 /* void unlockImage() */
michael@0 2805 NS_IMETHODIMP
michael@0 2806 RasterImage::UnlockImage()
michael@0 2807 {
michael@0 2808 MOZ_ASSERT(NS_IsMainThread(),
michael@0 2809 "Main thread to encourage serialization with LockImage");
michael@0 2810 if (mError)
michael@0 2811 return NS_ERROR_FAILURE;
michael@0 2812
michael@0 2813 // It's an error to call this function if the lock count is 0
michael@0 2814 NS_ABORT_IF_FALSE(mLockCount > 0,
michael@0 2815 "Calling UnlockImage with mLockCount == 0!");
michael@0 2816 if (mLockCount == 0)
michael@0 2817 return NS_ERROR_ABORT;
michael@0 2818
michael@0 2819 // We're locked, so discarding should not be active
michael@0 2820 NS_ABORT_IF_FALSE(!DiscardingActive(), "Locked, but discarding activated");
michael@0 2821
michael@0 2822 // Decrement our lock count
michael@0 2823 mLockCount--;
michael@0 2824
michael@0 2825 // If we've decoded this image once before, we're currently decoding again,
michael@0 2826 // and our lock count is now zero (so nothing is forcing us to keep the
michael@0 2827 // decoded data around), try to cancel the decode and throw away whatever
michael@0 2828 // we've decoded.
michael@0 2829 if (mHasBeenDecoded && mDecoder &&
michael@0 2830 mLockCount == 0 && CanForciblyDiscard()) {
michael@0 2831 PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
michael@0 2832 ("RasterImage[0x%p] canceling decode because image "
michael@0 2833 "is now unlocked.", this));
michael@0 2834 ReentrantMonitorAutoEnter lock(mDecodingMonitor);
michael@0 2835 FinishedSomeDecoding(eShutdownIntent_NotNeeded);
michael@0 2836 ForceDiscard();
michael@0 2837 return NS_OK;
michael@0 2838 }
michael@0 2839
michael@0 2840 // Otherwise, we might still be a candidate for discarding in the future. If
michael@0 2841 // we are, add ourselves to the discard tracker.
michael@0 2842 if (CanDiscard()) {
michael@0 2843 nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
michael@0 2844 CONTAINER_ENSURE_SUCCESS(rv);
michael@0 2845 }
michael@0 2846
michael@0 2847 return NS_OK;
michael@0 2848 }
michael@0 2849
michael@0 2850 //******************************************************************************
michael@0 2851 /* void requestDiscard() */
michael@0 2852 NS_IMETHODIMP
michael@0 2853 RasterImage::RequestDiscard()
michael@0 2854 {
michael@0 2855 if (CanDiscard() && CanForciblyDiscardAndRedecode()) {
michael@0 2856 ForceDiscard();
michael@0 2857 }
michael@0 2858
michael@0 2859 return NS_OK;
michael@0 2860 }
michael@0 2861
michael@0 2862 // Flushes up to aMaxBytes to the decoder.
michael@0 2863 nsresult
michael@0 2864 RasterImage::DecodeSomeData(uint32_t aMaxBytes, DecodeStrategy aStrategy)
michael@0 2865 {
michael@0 2866 // We should have a decoder if we get here
michael@0 2867 NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
michael@0 2868
michael@0 2869 mDecodingMonitor.AssertCurrentThreadIn();
michael@0 2870
michael@0 2871 // First, if we've just been called because we allocated a frame on the main
michael@0 2872 // thread, let the decoder deal with the data it set aside at that time by
michael@0 2873 // passing it a null buffer.
michael@0 2874 if (mDecodeRequest->mAllocatedNewFrame) {
michael@0 2875 mDecodeRequest->mAllocatedNewFrame = false;
michael@0 2876 nsresult rv = WriteToDecoder(nullptr, 0, aStrategy);
michael@0 2877 if (NS_FAILED(rv) || mDecoder->NeedsNewFrame()) {
michael@0 2878 return rv;
michael@0 2879 }
michael@0 2880 }
michael@0 2881
michael@0 2882 // If we have nothing else to decode, return
michael@0 2883 if (mBytesDecoded == mSourceData.Length())
michael@0 2884 return NS_OK;
michael@0 2885
michael@0 2886 MOZ_ASSERT(mBytesDecoded < mSourceData.Length());
michael@0 2887
michael@0 2888 // write the proper amount of data
michael@0 2889 uint32_t bytesToDecode = std::min(aMaxBytes,
michael@0 2890 mSourceData.Length() - mBytesDecoded);
michael@0 2891 nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
michael@0 2892 bytesToDecode,
michael@0 2893 aStrategy);
michael@0 2894
michael@0 2895 return rv;
michael@0 2896 }
michael@0 2897
michael@0 2898 // There are various indicators that tell us we're finished with the decode
michael@0 2899 // task at hand and can shut down the decoder.
michael@0 2900 //
michael@0 2901 // This method may not be called if there is no decoder.
michael@0 2902 bool
michael@0 2903 RasterImage::IsDecodeFinished()
michael@0 2904 {
michael@0 2905 // Precondition
michael@0 2906 mDecodingMonitor.AssertCurrentThreadIn();
michael@0 2907 NS_ABORT_IF_FALSE(mDecoder, "Can't call IsDecodeFinished() without decoder!");
michael@0 2908
michael@0 2909 // The decode is complete if we got what we wanted.
michael@0 2910 if (mDecoder->IsSizeDecode()) {
michael@0 2911 if (mDecoder->HasSize()) {
michael@0 2912 return true;
michael@0 2913 }
michael@0 2914 } else if (mDecoder->GetDecodeDone()) {
michael@0 2915 return true;
michael@0 2916 }
michael@0 2917
michael@0 2918 // If the decoder returned because it needed a new frame and we haven't
michael@0 2919 // written to it since then, the decoder may be storing data that it hasn't
michael@0 2920 // decoded yet.
michael@0 2921 if (mDecoder->NeedsNewFrame() ||
michael@0 2922 (mDecodeRequest && mDecodeRequest->mAllocatedNewFrame)) {
michael@0 2923 return false;
michael@0 2924 }
michael@0 2925
michael@0 2926 // Otherwise, if we have all the source data and wrote all the source data,
michael@0 2927 // we're done.
michael@0 2928 //
michael@0 2929 // (NB - This can be the case even for non-erroneous images because
michael@0 2930 // Decoder::GetDecodeDone() might not return true until after we call
michael@0 2931 // Decoder::Finish() in ShutdownDecoder())
michael@0 2932 if (mHasSourceData && (mBytesDecoded == mSourceData.Length())) {
michael@0 2933 return true;
michael@0 2934 }
michael@0 2935
michael@0 2936 // If we get here, assume it's not finished.
michael@0 2937 return false;
michael@0 2938 }
michael@0 2939
michael@0 2940 // Indempotent error flagging routine. If a decoder is open, shuts it down.
michael@0 2941 void
michael@0 2942 RasterImage::DoError()
michael@0 2943 {
michael@0 2944 // If we've flagged an error before, we have nothing to do
michael@0 2945 if (mError)
michael@0 2946 return;
michael@0 2947
michael@0 2948 // We can't safely handle errors off-main-thread, so dispatch a worker to do it.
michael@0 2949 if (!NS_IsMainThread()) {
michael@0 2950 HandleErrorWorker::DispatchIfNeeded(this);
michael@0 2951 return;
michael@0 2952 }
michael@0 2953
michael@0 2954 // Calling FinishedSomeDecoding and CurrentStatusTracker requires us to be in
michael@0 2955 // the decoding monitor.
michael@0 2956 ReentrantMonitorAutoEnter lock(mDecodingMonitor);
michael@0 2957
michael@0 2958 // If we're mid-decode, shut down the decoder.
michael@0 2959 if (mDecoder) {
michael@0 2960 FinishedSomeDecoding(eShutdownIntent_Error);
michael@0 2961 }
michael@0 2962
michael@0 2963 // Put the container in an error state.
michael@0 2964 mError = true;
michael@0 2965
michael@0 2966 nsRefPtr<imgStatusTracker> statusTracker = CurrentStatusTracker();
michael@0 2967 statusTracker->GetDecoderObserver()->OnError();
michael@0 2968
michael@0 2969 // Log our error
michael@0 2970 LOG_CONTAINER_ERROR;
michael@0 2971 }
michael@0 2972
michael@0 2973 /* static */ void
michael@0 2974 RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage)
michael@0 2975 {
michael@0 2976 if (!aImage->mPendingError) {
michael@0 2977 aImage->mPendingError = true;
michael@0 2978 nsRefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
michael@0 2979 NS_DispatchToMainThread(worker);
michael@0 2980 }
michael@0 2981 }
michael@0 2982
michael@0 2983 RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage)
michael@0 2984 : mImage(aImage)
michael@0 2985 {
michael@0 2986 MOZ_ASSERT(mImage, "Should have image");
michael@0 2987 }
michael@0 2988
michael@0 2989 NS_IMETHODIMP
michael@0 2990 RasterImage::HandleErrorWorker::Run()
michael@0 2991 {
michael@0 2992 mImage->DoError();
michael@0 2993
michael@0 2994 return NS_OK;
michael@0 2995 }
michael@0 2996
michael@0 2997 // nsIInputStream callback to copy the incoming image data directly to the
michael@0 2998 // RasterImage without processing. The RasterImage is passed as the closure.
michael@0 2999 // Always reads everything it gets, even if the data is erroneous.
michael@0 3000 NS_METHOD
michael@0 3001 RasterImage::WriteToRasterImage(nsIInputStream* /* unused */,
michael@0 3002 void* aClosure,
michael@0 3003 const char* aFromRawSegment,
michael@0 3004 uint32_t /* unused */,
michael@0 3005 uint32_t aCount,
michael@0 3006 uint32_t* aWriteCount)
michael@0 3007 {
michael@0 3008 // Retrieve the RasterImage
michael@0 3009 RasterImage* image = static_cast<RasterImage*>(aClosure);
michael@0 3010
michael@0 3011 // Copy the source data. Unless we hit OOM, we squelch the return value
michael@0 3012 // here, because returning an error means that ReadSegments stops
michael@0 3013 // reading data, violating our invariant that we read everything we get.
michael@0 3014 // If we hit OOM then we fail and the load is aborted.
michael@0 3015 nsresult rv = image->AddSourceData(aFromRawSegment, aCount);
michael@0 3016 if (rv == NS_ERROR_OUT_OF_MEMORY) {
michael@0 3017 image->DoError();
michael@0 3018 return rv;
michael@0 3019 }
michael@0 3020
michael@0 3021 // We wrote everything we got
michael@0 3022 *aWriteCount = aCount;
michael@0 3023
michael@0 3024 return NS_OK;
michael@0 3025 }
michael@0 3026
michael@0 3027 bool
michael@0 3028 RasterImage::ShouldAnimate()
michael@0 3029 {
michael@0 3030 return ImageResource::ShouldAnimate() && GetNumFrames() >= 2 &&
michael@0 3031 !mAnimationFinished;
michael@0 3032 }
michael@0 3033
michael@0 3034 /* readonly attribute uint32_t framesNotified; */
michael@0 3035 #ifdef DEBUG
michael@0 3036 NS_IMETHODIMP
michael@0 3037 RasterImage::GetFramesNotified(uint32_t *aFramesNotified)
michael@0 3038 {
michael@0 3039 NS_ENSURE_ARG_POINTER(aFramesNotified);
michael@0 3040
michael@0 3041 *aFramesNotified = mFramesNotified;
michael@0 3042
michael@0 3043 return NS_OK;
michael@0 3044 }
michael@0 3045 #endif
michael@0 3046
michael@0 3047 nsresult
michael@0 3048 RasterImage::RequestDecodeIfNeeded(nsresult aStatus,
michael@0 3049 eShutdownIntent aIntent,
michael@0 3050 bool aDone,
michael@0 3051 bool aWasSize)
michael@0 3052 {
michael@0 3053 MOZ_ASSERT(NS_IsMainThread());
michael@0 3054
michael@0 3055 // If we were a size decode and a full decode was requested, now's the time.
michael@0 3056 if (NS_SUCCEEDED(aStatus) &&
michael@0 3057 aIntent == eShutdownIntent_Done &&
michael@0 3058 aDone &&
michael@0 3059 aWasSize &&
michael@0 3060 mWantFullDecode) {
michael@0 3061 mWantFullDecode = false;
michael@0 3062
michael@0 3063 // If we're not meant to be storing source data and we just got the size,
michael@0 3064 // we need to synchronously flush all the data we got to a full decoder.
michael@0 3065 // When that decoder is shut down, we'll also clear our source data.
michael@0 3066 return StoringSourceData() ? RequestDecode()
michael@0 3067 : SyncDecode();
michael@0 3068 }
michael@0 3069
michael@0 3070 // We don't need a full decode right now, so just return the existing status.
michael@0 3071 return aStatus;
michael@0 3072 }
michael@0 3073
michael@0 3074 nsresult
michael@0 3075 RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_Done */,
michael@0 3076 DecodeRequest* aRequest /* = nullptr */)
michael@0 3077 {
michael@0 3078 MOZ_ASSERT(NS_IsMainThread());
michael@0 3079
michael@0 3080 mDecodingMonitor.AssertCurrentThreadIn();
michael@0 3081
michael@0 3082 nsRefPtr<DecodeRequest> request;
michael@0 3083 if (aRequest) {
michael@0 3084 request = aRequest;
michael@0 3085 } else {
michael@0 3086 request = mDecodeRequest;
michael@0 3087 }
michael@0 3088
michael@0 3089 // Ensure that, if the decoder is the last reference to the image, we don't
michael@0 3090 // destroy it by destroying the decoder.
michael@0 3091 nsRefPtr<RasterImage> image(this);
michael@0 3092
michael@0 3093 bool done = false;
michael@0 3094 bool wasSize = false;
michael@0 3095 nsresult rv = NS_OK;
michael@0 3096
michael@0 3097 if (image->mDecoder) {
michael@0 3098 image->mDecoder->MarkFrameDirty();
michael@0 3099
michael@0 3100 if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) {
michael@0 3101 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount);
michael@0 3102 }
michael@0 3103
michael@0 3104 if (!image->mHasSize && image->mDecoder->HasSize()) {
michael@0 3105 image->mDecoder->SetSizeOnImage();
michael@0 3106 }
michael@0 3107
michael@0 3108 // If the decode finished, or we're specifically being told to shut down,
michael@0 3109 // tell the image and shut down the decoder.
michael@0 3110 if (image->IsDecodeFinished() || aIntent != eShutdownIntent_Done) {
michael@0 3111 done = true;
michael@0 3112
michael@0 3113 // Hold on to a reference to the decoder until we're done with it
michael@0 3114 nsRefPtr<Decoder> decoder = image->mDecoder;
michael@0 3115
michael@0 3116 wasSize = decoder->IsSizeDecode();
michael@0 3117
michael@0 3118 // Do some telemetry if this isn't a size decode.
michael@0 3119 if (request && !wasSize) {
michael@0 3120 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
michael@0 3121 int32_t(request->mDecodeTime.ToMicroseconds()));
michael@0 3122
michael@0 3123 // We record the speed for only some decoders. The rest have
michael@0 3124 // SpeedHistogram return HistogramCount.
michael@0 3125 Telemetry::ID id = decoder->SpeedHistogram();
michael@0 3126 if (id < Telemetry::HistogramCount) {
michael@0 3127 int32_t KBps = int32_t(request->mImage->mBytesDecoded /
michael@0 3128 (1024 * request->mDecodeTime.ToSeconds()));
michael@0 3129 Telemetry::Accumulate(id, KBps);
michael@0 3130 }
michael@0 3131 }
michael@0 3132
michael@0 3133 // We need to shut down the decoder first, in order to ensure all
michael@0 3134 // decoding routines have been finished.
michael@0 3135 rv = image->ShutdownDecoder(aIntent);
michael@0 3136 if (NS_FAILED(rv)) {
michael@0 3137 image->DoError();
michael@0 3138 }
michael@0 3139 }
michael@0 3140 }
michael@0 3141
michael@0 3142 ImageStatusDiff diff =
michael@0 3143 request ? image->mStatusTracker->Difference(request->mStatusTracker)
michael@0 3144 : image->mStatusTracker->DecodeStateAsDifference();
michael@0 3145 image->mStatusTracker->ApplyDifference(diff);
michael@0 3146
michael@0 3147 if (mNotifying) {
michael@0 3148 // Accumulate the status changes. We don't permit recursive notifications
michael@0 3149 // because they cause subtle concurrency bugs, so we'll delay sending out
michael@0 3150 // the notifications until we pop back to the lowest invocation of
michael@0 3151 // FinishedSomeDecoding on the stack.
michael@0 3152 NS_WARNING("Recursively notifying in RasterImage::FinishedSomeDecoding!");
michael@0 3153 mStatusDiff.Combine(diff);
michael@0 3154 } else {
michael@0 3155 MOZ_ASSERT(mStatusDiff.IsNoChange(), "Shouldn't have an accumulated change at this point");
michael@0 3156
michael@0 3157 while (!diff.IsNoChange()) {
michael@0 3158 // Tell the observers what happened.
michael@0 3159 mNotifying = true;
michael@0 3160 image->mStatusTracker->SyncNotifyDifference(diff);
michael@0 3161 mNotifying = false;
michael@0 3162
michael@0 3163 // Gather any status changes that may have occurred as a result of sending
michael@0 3164 // out the previous notifications. If there were any, we'll send out
michael@0 3165 // notifications for them next.
michael@0 3166 diff = mStatusDiff;
michael@0 3167 mStatusDiff = ImageStatusDiff::NoChange();
michael@0 3168 }
michael@0 3169 }
michael@0 3170
michael@0 3171 return RequestDecodeIfNeeded(rv, aIntent, done, wasSize);
michael@0 3172 }
michael@0 3173
michael@0 3174 NS_IMPL_ISUPPORTS(RasterImage::DecodePool,
michael@0 3175 nsIObserver)
michael@0 3176
michael@0 3177 /* static */ RasterImage::DecodePool*
michael@0 3178 RasterImage::DecodePool::Singleton()
michael@0 3179 {
michael@0 3180 if (!sSingleton) {
michael@0 3181 MOZ_ASSERT(NS_IsMainThread());
michael@0 3182 sSingleton = new DecodePool();
michael@0 3183 ClearOnShutdown(&sSingleton);
michael@0 3184 }
michael@0 3185
michael@0 3186 return sSingleton;
michael@0 3187 }
michael@0 3188
michael@0 3189 already_AddRefed<nsIEventTarget>
michael@0 3190 RasterImage::DecodePool::GetEventTarget()
michael@0 3191 {
michael@0 3192 nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
michael@0 3193 return target.forget();
michael@0 3194 }
michael@0 3195
michael@0 3196 #ifdef MOZ_NUWA_PROCESS
michael@0 3197
michael@0 3198 class RIDThreadPoolListener : public nsIThreadPoolListener
michael@0 3199 {
michael@0 3200 public:
michael@0 3201 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 3202 NS_DECL_NSITHREADPOOLLISTENER
michael@0 3203
michael@0 3204 RIDThreadPoolListener() {}
michael@0 3205 ~RIDThreadPoolListener() {}
michael@0 3206 };
michael@0 3207
michael@0 3208 NS_IMPL_ISUPPORTS(RIDThreadPoolListener, nsIThreadPoolListener)
michael@0 3209
michael@0 3210 NS_IMETHODIMP
michael@0 3211 RIDThreadPoolListener::OnThreadCreated()
michael@0 3212 {
michael@0 3213 if (IsNuwaProcess()) {
michael@0 3214 NuwaMarkCurrentThread((void (*)(void *))nullptr, nullptr);
michael@0 3215 }
michael@0 3216 return NS_OK;
michael@0 3217 }
michael@0 3218
michael@0 3219 NS_IMETHODIMP
michael@0 3220 RIDThreadPoolListener::OnThreadShuttingDown()
michael@0 3221 {
michael@0 3222 return NS_OK;
michael@0 3223 }
michael@0 3224
michael@0 3225 #endif // MOZ_NUWA_PROCESS
michael@0 3226
michael@0 3227 RasterImage::DecodePool::DecodePool()
michael@0 3228 : mThreadPoolMutex("Thread Pool")
michael@0 3229 {
michael@0 3230 if (gMultithreadedDecoding) {
michael@0 3231 mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
michael@0 3232 if (mThreadPool) {
michael@0 3233 mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
michael@0 3234 uint32_t limit;
michael@0 3235 if (gDecodingThreadLimit <= 0) {
michael@0 3236 limit = std::max(PR_GetNumberOfProcessors(), 2) - 1;
michael@0 3237 } else {
michael@0 3238 limit = static_cast<uint32_t>(gDecodingThreadLimit);
michael@0 3239 }
michael@0 3240
michael@0 3241 mThreadPool->SetThreadLimit(limit);
michael@0 3242 mThreadPool->SetIdleThreadLimit(limit);
michael@0 3243
michael@0 3244 #ifdef MOZ_NUWA_PROCESS
michael@0 3245 if (IsNuwaProcess()) {
michael@0 3246 mThreadPool->SetListener(new RIDThreadPoolListener());
michael@0 3247 }
michael@0 3248 #endif
michael@0 3249
michael@0 3250 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
michael@0 3251 if (obsSvc) {
michael@0 3252 obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
michael@0 3253 }
michael@0 3254 }
michael@0 3255 }
michael@0 3256 }
michael@0 3257
michael@0 3258 RasterImage::DecodePool::~DecodePool()
michael@0 3259 {
michael@0 3260 MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
michael@0 3261 }
michael@0 3262
michael@0 3263 NS_IMETHODIMP
michael@0 3264 RasterImage::DecodePool::Observe(nsISupports *subject, const char *topic,
michael@0 3265 const char16_t *data)
michael@0 3266 {
michael@0 3267 NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
michael@0 3268
michael@0 3269 nsCOMPtr<nsIThreadPool> threadPool;
michael@0 3270
michael@0 3271 {
michael@0 3272 MutexAutoLock threadPoolLock(mThreadPoolMutex);
michael@0 3273 threadPool = mThreadPool;
michael@0 3274 mThreadPool = nullptr;
michael@0 3275 }
michael@0 3276
michael@0 3277 if (threadPool) {
michael@0 3278 threadPool->Shutdown();
michael@0 3279 }
michael@0 3280
michael@0 3281 return NS_OK;
michael@0 3282 }
michael@0 3283
michael@0 3284 void
michael@0 3285 RasterImage::DecodePool::RequestDecode(RasterImage* aImg)
michael@0 3286 {
michael@0 3287 MOZ_ASSERT(aImg->mDecoder);
michael@0 3288 aImg->mDecodingMonitor.AssertCurrentThreadIn();
michael@0 3289
michael@0 3290 // If we're currently waiting on a new frame for this image, we can't do any
michael@0 3291 // decoding.
michael@0 3292 if (!aImg->mDecoder->NeedsNewFrame()) {
michael@0 3293 // No matter whether this is currently being decoded, we need to update the
michael@0 3294 // number of bytes we want it to decode.
michael@0 3295 aImg->mDecodeRequest->mBytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded;
michael@0 3296
michael@0 3297 if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_PENDING ||
michael@0 3298 aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_ACTIVE) {
michael@0 3299 // The image is already in our list of images to decode, or currently being
michael@0 3300 // decoded, so we don't have to do anything else.
michael@0 3301 return;
michael@0 3302 }
michael@0 3303
michael@0 3304 aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_PENDING;
michael@0 3305 nsRefPtr<DecodeJob> job = new DecodeJob(aImg->mDecodeRequest, aImg);
michael@0 3306
michael@0 3307 MutexAutoLock threadPoolLock(mThreadPoolMutex);
michael@0 3308 if (!gMultithreadedDecoding || !mThreadPool) {
michael@0 3309 NS_DispatchToMainThread(job);
michael@0 3310 } else {
michael@0 3311 mThreadPool->Dispatch(job, nsIEventTarget::DISPATCH_NORMAL);
michael@0 3312 }
michael@0 3313 }
michael@0 3314 }
michael@0 3315
michael@0 3316 void
michael@0 3317 RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg, DecodeStrategy aStrategy)
michael@0 3318 {
michael@0 3319 MOZ_ASSERT(NS_IsMainThread());
michael@0 3320 aImg->mDecodingMonitor.AssertCurrentThreadIn();
michael@0 3321
michael@0 3322 if (aImg->mDecodeRequest) {
michael@0 3323 // If the image is waiting for decode work to be notified, go ahead and do that.
michael@0 3324 if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
michael@0 3325 aImg->FinishedSomeDecoding();
michael@0 3326 }
michael@0 3327 }
michael@0 3328
michael@0 3329 DecodeSomeOfImage(aImg, aStrategy);
michael@0 3330
michael@0 3331 aImg->FinishedSomeDecoding();
michael@0 3332
michael@0 3333 // If the decoder needs a new frame, enqueue an event to get it; that event
michael@0 3334 // will enqueue another decode request when it's done.
michael@0 3335 if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) {
michael@0 3336 FrameNeededWorker::GetNewFrame(aImg);
michael@0 3337 } else {
michael@0 3338 // If we aren't yet finished decoding and we have more data in hand, add
michael@0 3339 // this request to the back of the priority list.
michael@0 3340 if (aImg->mDecoder &&
michael@0 3341 !aImg->mError &&
michael@0 3342 !aImg->IsDecodeFinished() &&
michael@0 3343 aImg->mSourceData.Length() > aImg->mBytesDecoded) {
michael@0 3344 RequestDecode(aImg);
michael@0 3345 }
michael@0 3346 }
michael@0 3347 }
michael@0 3348
michael@0 3349 /* static */ void
michael@0 3350 RasterImage::DecodePool::StopDecoding(RasterImage* aImg)
michael@0 3351 {
michael@0 3352 aImg->mDecodingMonitor.AssertCurrentThreadIn();
michael@0 3353
michael@0 3354 // If we haven't got a decode request, we're not currently decoding. (Having
michael@0 3355 // a decode request doesn't imply we *are* decoding, though.)
michael@0 3356 if (aImg->mDecodeRequest) {
michael@0 3357 aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_STOPPED;
michael@0 3358 }
michael@0 3359 }
michael@0 3360
michael@0 3361 NS_IMETHODIMP
michael@0 3362 RasterImage::DecodePool::DecodeJob::Run()
michael@0 3363 {
michael@0 3364 ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
michael@0 3365
michael@0 3366 // If we were interrupted, we shouldn't do any work.
michael@0 3367 if (mRequest->mRequestStatus == DecodeRequest::REQUEST_STOPPED) {
michael@0 3368 DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
michael@0 3369 return NS_OK;
michael@0 3370 }
michael@0 3371
michael@0 3372 // If someone came along and synchronously decoded us, there's nothing for us to do.
michael@0 3373 if (!mImage->mDecoder || mImage->IsDecodeFinished()) {
michael@0 3374 DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
michael@0 3375 return NS_OK;
michael@0 3376 }
michael@0 3377
michael@0 3378 // If we're a decode job that's been enqueued since a previous decode that
michael@0 3379 // still needs a new frame, we can't do anything. Wait until the
michael@0 3380 // FrameNeededWorker enqueues another frame.
michael@0 3381 if (mImage->mDecoder->NeedsNewFrame()) {
michael@0 3382 return NS_OK;
michael@0 3383 }
michael@0 3384
michael@0 3385 mRequest->mRequestStatus = DecodeRequest::REQUEST_ACTIVE;
michael@0 3386
michael@0 3387 uint32_t oldByteCount = mImage->mBytesDecoded;
michael@0 3388
michael@0 3389 DecodeType type = DECODE_TYPE_UNTIL_DONE_BYTES;
michael@0 3390
michael@0 3391 // Multithreaded decoding can be disabled. If we've done so, we don't want to
michael@0 3392 // monopolize the main thread, and will allow a timeout in DecodeSomeOfImage.
michael@0 3393 if (NS_IsMainThread()) {
michael@0 3394 type = DECODE_TYPE_UNTIL_TIME;
michael@0 3395 }
michael@0 3396
michael@0 3397 DecodePool::Singleton()->DecodeSomeOfImage(mImage, DECODE_ASYNC, type, mRequest->mBytesToDecode);
michael@0 3398
michael@0 3399 uint32_t bytesDecoded = mImage->mBytesDecoded - oldByteCount;
michael@0 3400
michael@0 3401 mRequest->mRequestStatus = DecodeRequest::REQUEST_WORK_DONE;
michael@0 3402
michael@0 3403 // If the decoder needs a new frame, enqueue an event to get it; that event
michael@0 3404 // will enqueue another decode request when it's done.
michael@0 3405 if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
michael@0 3406 FrameNeededWorker::GetNewFrame(mImage);
michael@0 3407 }
michael@0 3408 // If we aren't yet finished decoding and we have more data in hand, add
michael@0 3409 // this request to the back of the list.
michael@0 3410 else if (mImage->mDecoder &&
michael@0 3411 !mImage->mError &&
michael@0 3412 !mImage->mPendingError &&
michael@0 3413 !mImage->IsDecodeFinished() &&
michael@0 3414 bytesDecoded < mRequest->mBytesToDecode &&
michael@0 3415 bytesDecoded > 0) {
michael@0 3416 DecodePool::Singleton()->RequestDecode(mImage);
michael@0 3417 } else {
michael@0 3418 // Nothing more for us to do - let everyone know what happened.
michael@0 3419 DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
michael@0 3420 }
michael@0 3421
michael@0 3422 return NS_OK;
michael@0 3423 }
michael@0 3424
michael@0 3425 RasterImage::DecodePool::DecodeJob::~DecodeJob()
michael@0 3426 {
michael@0 3427 if (gMultithreadedDecoding) {
michael@0 3428 // Dispatch mImage to main thread to prevent mImage from being destructed by decode thread.
michael@0 3429 nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
michael@0 3430 NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
michael@0 3431 if (mainThread) {
michael@0 3432 // Handle ambiguous nsISupports inheritance
michael@0 3433 RasterImage* rawImg = nullptr;
michael@0 3434 mImage.swap(rawImg);
michael@0 3435 DebugOnly<nsresult> rv = NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
michael@0 3436 MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
michael@0 3437 }
michael@0 3438 }
michael@0 3439 }
michael@0 3440
michael@0 3441 nsresult
michael@0 3442 RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg)
michael@0 3443 {
michael@0 3444 MOZ_ASSERT(NS_IsMainThread());
michael@0 3445 ReentrantMonitorAutoEnter lock(aImg->mDecodingMonitor);
michael@0 3446
michael@0 3447 if (aImg->mDecodeRequest) {
michael@0 3448 // If the image is waiting for decode work to be notified, go ahead and do that.
michael@0 3449 if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
michael@0 3450 nsresult rv = aImg->FinishedSomeDecoding();
michael@0 3451 if (NS_FAILED(rv)) {
michael@0 3452 aImg->DoError();
michael@0 3453 return rv;
michael@0 3454 }
michael@0 3455 }
michael@0 3456 }
michael@0 3457
michael@0 3458 // We use DECODE_ASYNC here because we just want to get the size information
michael@0 3459 // here and defer the rest of the work.
michael@0 3460 nsresult rv = DecodeSomeOfImage(aImg, DECODE_ASYNC, DECODE_TYPE_UNTIL_SIZE);
michael@0 3461 if (NS_FAILED(rv)) {
michael@0 3462 return rv;
michael@0 3463 }
michael@0 3464
michael@0 3465 // If the decoder needs a new frame, enqueue an event to get it; that event
michael@0 3466 // will enqueue another decode request when it's done.
michael@0 3467 if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) {
michael@0 3468 FrameNeededWorker::GetNewFrame(aImg);
michael@0 3469 } else {
michael@0 3470 rv = aImg->FinishedSomeDecoding();
michael@0 3471 }
michael@0 3472
michael@0 3473 return rv;
michael@0 3474 }
michael@0 3475
michael@0 3476 nsresult
michael@0 3477 RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg,
michael@0 3478 DecodeStrategy aStrategy,
michael@0 3479 DecodeType aDecodeType /* = DECODE_TYPE_UNTIL_TIME */,
michael@0 3480 uint32_t bytesToDecode /* = 0 */)
michael@0 3481 {
michael@0 3482 NS_ABORT_IF_FALSE(aImg->mInitialized,
michael@0 3483 "Worker active for uninitialized container!");
michael@0 3484 aImg->mDecodingMonitor.AssertCurrentThreadIn();
michael@0 3485
michael@0 3486 // If an error is flagged, it probably happened while we were waiting
michael@0 3487 // in the event queue.
michael@0 3488 if (aImg->mError)
michael@0 3489 return NS_OK;
michael@0 3490
michael@0 3491 // If there is an error worker pending (say because the main thread has enqueued
michael@0 3492 // another decode request for us before processing the error worker) then bail out.
michael@0 3493 if (aImg->mPendingError)
michael@0 3494 return NS_OK;
michael@0 3495
michael@0 3496 // If mDecoded or we don't have a decoder, we must have finished already (for
michael@0 3497 // example, a synchronous decode request came while the worker was pending).
michael@0 3498 if (!aImg->mDecoder || aImg->mDecoded)
michael@0 3499 return NS_OK;
michael@0 3500
michael@0 3501 // If we're doing synchronous decodes, and we're waiting on a new frame for
michael@0 3502 // this image, get it now.
michael@0 3503 if (aStrategy == DECODE_SYNC && aImg->mDecoder->NeedsNewFrame()) {
michael@0 3504 MOZ_ASSERT(NS_IsMainThread());
michael@0 3505
michael@0 3506 aImg->mDecoder->AllocateFrame();
michael@0 3507 aImg->mDecodeRequest->mAllocatedNewFrame = true;
michael@0 3508 }
michael@0 3509
michael@0 3510 // If we're not synchronous, we can't allocate a frame right now.
michael@0 3511 else if (aImg->mDecoder->NeedsNewFrame()) {
michael@0 3512 return NS_OK;
michael@0 3513 }
michael@0 3514
michael@0 3515 nsRefPtr<Decoder> decoderKungFuDeathGrip = aImg->mDecoder;
michael@0 3516
michael@0 3517 uint32_t maxBytes;
michael@0 3518 if (aImg->mDecoder->IsSizeDecode()) {
michael@0 3519 // Decode all available data if we're a size decode; they're cheap, and we
michael@0 3520 // want them to be more or less synchronous.
michael@0 3521 maxBytes = aImg->mSourceData.Length();
michael@0 3522 } else {
michael@0 3523 // We're only guaranteed to decode this many bytes, so in particular,
michael@0 3524 // gDecodeBytesAtATime should be set high enough for us to read the size
michael@0 3525 // from most images.
michael@0 3526 maxBytes = gDecodeBytesAtATime;
michael@0 3527 }
michael@0 3528
michael@0 3529 if (bytesToDecode == 0) {
michael@0 3530 bytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded;
michael@0 3531 }
michael@0 3532
michael@0 3533 int32_t chunkCount = 0;
michael@0 3534 TimeStamp start = TimeStamp::Now();
michael@0 3535 TimeStamp deadline = start + TimeDuration::FromMilliseconds(gMaxMSBeforeYield);
michael@0 3536
michael@0 3537 // We keep decoding chunks until:
michael@0 3538 // * we don't have any data left to decode,
michael@0 3539 // * the decode completes,
michael@0 3540 // * we're an UNTIL_SIZE decode and we get the size, or
michael@0 3541 // * we run out of time.
michael@0 3542 // We also try to decode at least one "chunk" if we've allocated a new frame,
michael@0 3543 // even if we have no more data to send to the decoder.
michael@0 3544 while ((aImg->mSourceData.Length() > aImg->mBytesDecoded &&
michael@0 3545 bytesToDecode > 0 &&
michael@0 3546 !aImg->IsDecodeFinished() &&
michael@0 3547 !(aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize) &&
michael@0 3548 !aImg->mDecoder->NeedsNewFrame()) ||
michael@0 3549 (aImg->mDecodeRequest && aImg->mDecodeRequest->mAllocatedNewFrame)) {
michael@0 3550 chunkCount++;
michael@0 3551 uint32_t chunkSize = std::min(bytesToDecode, maxBytes);
michael@0 3552 nsresult rv = aImg->DecodeSomeData(chunkSize, aStrategy);
michael@0 3553 if (NS_FAILED(rv)) {
michael@0 3554 aImg->DoError();
michael@0 3555 return rv;
michael@0 3556 }
michael@0 3557
michael@0 3558 bytesToDecode -= chunkSize;
michael@0 3559
michael@0 3560 // Yield if we've been decoding for too long. We check this _after_ decoding
michael@0 3561 // a chunk to ensure that we don't yield without doing any decoding.
michael@0 3562 if (aDecodeType == DECODE_TYPE_UNTIL_TIME && TimeStamp::Now() >= deadline)
michael@0 3563 break;
michael@0 3564 }
michael@0 3565
michael@0 3566 if (aImg->mDecodeRequest) {
michael@0 3567 aImg->mDecodeRequest->mDecodeTime += (TimeStamp::Now() - start);
michael@0 3568 aImg->mDecodeRequest->mChunkCount += chunkCount;
michael@0 3569 }
michael@0 3570
michael@0 3571 // Flush invalidations (and therefore paint) now that we've decoded all the
michael@0 3572 // chunks we're going to.
michael@0 3573 //
michael@0 3574 // However, don't paint if:
michael@0 3575 //
michael@0 3576 // * This was an until-size decode. Until-size decodes are always followed
michael@0 3577 // by normal decodes, so don't bother painting.
michael@0 3578 //
michael@0 3579 // * The decoder flagged an error. The decoder may have written garbage
michael@0 3580 // into the output buffer; don't paint it to the screen.
michael@0 3581 //
michael@0 3582 // * We have all the source data. This disables progressive display of
michael@0 3583 // previously-decoded images, thus letting us finish decoding faster,
michael@0 3584 // since we don't waste time painting while we decode.
michael@0 3585 // Decoder::PostFrameStop() will flush invalidations once the decode is
michael@0 3586 // done.
michael@0 3587
michael@0 3588 if (aDecodeType != DECODE_TYPE_UNTIL_SIZE &&
michael@0 3589 !aImg->mDecoder->HasError() &&
michael@0 3590 !aImg->mHasSourceData) {
michael@0 3591 aImg->mInDecoder = true;
michael@0 3592 aImg->mDecoder->FlushInvalidations();
michael@0 3593 aImg->mInDecoder = false;
michael@0 3594 }
michael@0 3595
michael@0 3596 return NS_OK;
michael@0 3597 }
michael@0 3598
michael@0 3599 RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeRequest* request)
michael@0 3600 : mImage(image)
michael@0 3601 , mRequest(request)
michael@0 3602 {}
michael@0 3603
michael@0 3604 void
michael@0 3605 RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request)
michael@0 3606 {
michael@0 3607 image->mDecodingMonitor.AssertCurrentThreadIn();
michael@0 3608
michael@0 3609 nsCOMPtr<nsIRunnable> worker = new DecodeDoneWorker(image, request);
michael@0 3610 NS_DispatchToMainThread(worker);
michael@0 3611 }
michael@0 3612
michael@0 3613 NS_IMETHODIMP
michael@0 3614 RasterImage::DecodeDoneWorker::Run()
michael@0 3615 {
michael@0 3616 MOZ_ASSERT(NS_IsMainThread());
michael@0 3617 ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
michael@0 3618
michael@0 3619 mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest);
michael@0 3620
michael@0 3621 return NS_OK;
michael@0 3622 }
michael@0 3623
michael@0 3624 RasterImage::FrameNeededWorker::FrameNeededWorker(RasterImage* image)
michael@0 3625 : mImage(image)
michael@0 3626 {}
michael@0 3627
michael@0 3628
michael@0 3629 void
michael@0 3630 RasterImage::FrameNeededWorker::GetNewFrame(RasterImage* image)
michael@0 3631 {
michael@0 3632 nsCOMPtr<nsIRunnable> worker = new FrameNeededWorker(image);
michael@0 3633 NS_DispatchToMainThread(worker);
michael@0 3634 }
michael@0 3635
michael@0 3636 NS_IMETHODIMP
michael@0 3637 RasterImage::FrameNeededWorker::Run()
michael@0 3638 {
michael@0 3639 ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
michael@0 3640 nsresult rv = NS_OK;
michael@0 3641
michael@0 3642 // If we got a synchronous decode in the mean time, we don't need to do
michael@0 3643 // anything.
michael@0 3644 if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
michael@0 3645 rv = mImage->mDecoder->AllocateFrame();
michael@0 3646 mImage->mDecodeRequest->mAllocatedNewFrame = true;
michael@0 3647 }
michael@0 3648
michael@0 3649 if (NS_SUCCEEDED(rv) && mImage->mDecoder) {
michael@0 3650 // By definition, we're not done decoding, so enqueue us for more decoding.
michael@0 3651 DecodePool::Singleton()->RequestDecode(mImage);
michael@0 3652 }
michael@0 3653
michael@0 3654 return NS_OK;
michael@0 3655 }
michael@0 3656
michael@0 3657 } // namespace image
michael@0 3658 } // namespace mozilla

mercurial