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