Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "imgFrame.h"
8 : #include "ImageRegion.h"
9 : #include "ShutdownTracker.h"
10 :
11 : #include "prenv.h"
12 :
13 : #include "gfx2DGlue.h"
14 : #include "gfxPlatform.h"
15 : #include "gfxPrefs.h"
16 : #include "gfxUtils.h"
17 : #include "gfxAlphaRecovery.h"
18 :
19 : #include "GeckoProfiler.h"
20 : #include "MainThreadUtils.h"
21 : #include "mozilla/CheckedInt.h"
22 : #include "mozilla/gfx/Tools.h"
23 : #include "mozilla/layers/SourceSurfaceSharedData.h"
24 : #include "mozilla/layers/SourceSurfaceVolatileData.h"
25 : #include "mozilla/Likely.h"
26 : #include "mozilla/MemoryReporting.h"
27 : #include "nsMargin.h"
28 : #include "nsThreadUtils.h"
29 :
30 :
31 : namespace mozilla {
32 :
33 : using namespace gfx;
34 :
35 : namespace image {
36 :
37 : static void
38 28 : ScopedMapRelease(void* aMap)
39 : {
40 28 : delete static_cast<DataSourceSurface::ScopedMap*>(aMap);
41 28 : }
42 :
43 : static int32_t
44 50 : VolatileSurfaceStride(const IntSize& size, SurfaceFormat format)
45 : {
46 : // Stride must be a multiple of four or cairo will complain.
47 50 : return (size.width * BytesPerPixel(format) + 0x3) & ~0x3;
48 : }
49 :
50 : static already_AddRefed<DataSourceSurface>
51 74 : CreateLockedSurface(DataSourceSurface *aSurface,
52 : const IntSize& size,
53 : SurfaceFormat format)
54 : {
55 : // Shared memory is never released until the surface itself is released
56 74 : if (aSurface->GetType() == SurfaceType::DATA_SHARED) {
57 0 : RefPtr<DataSourceSurface> surf(aSurface);
58 0 : return surf.forget();
59 : }
60 :
61 : DataSourceSurface::ScopedMap* smap =
62 74 : new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE);
63 74 : if (smap->IsMapped()) {
64 : // The ScopedMap is held by this DataSourceSurface.
65 : RefPtr<DataSourceSurface> surf =
66 148 : Factory::CreateWrappingDataSourceSurface(smap->GetData(),
67 74 : aSurface->Stride(),
68 : size,
69 : format,
70 : &ScopedMapRelease,
71 74 : static_cast<void*>(smap));
72 74 : if (surf) {
73 74 : return surf.forget();
74 : }
75 : }
76 :
77 0 : delete smap;
78 0 : return nullptr;
79 : }
80 :
81 : static already_AddRefed<DataSourceSurface>
82 50 : AllocateBufferForImage(const IntSize& size,
83 : SurfaceFormat format,
84 : bool aIsAnimated = false)
85 : {
86 50 : int32_t stride = VolatileSurfaceStride(size, format);
87 50 : if (!aIsAnimated && gfxPrefs::ImageMemShared()) {
88 0 : RefPtr<SourceSurfaceSharedData> newSurf = new SourceSurfaceSharedData();
89 0 : if (newSurf->Init(size, stride, format)) {
90 0 : return newSurf.forget();
91 : }
92 : } else {
93 50 : RefPtr<SourceSurfaceVolatileData> newSurf= new SourceSurfaceVolatileData();
94 50 : if (newSurf->Init(size, stride, format)) {
95 50 : return newSurf.forget();
96 : }
97 : }
98 0 : return nullptr;
99 : }
100 :
101 : static bool
102 50 : ClearSurface(DataSourceSurface* aSurface, const IntSize& aSize, SurfaceFormat aFormat)
103 : {
104 50 : int32_t stride = aSurface->Stride();
105 50 : uint8_t* data = aSurface->GetData();
106 50 : MOZ_ASSERT(data);
107 :
108 50 : if (aFormat == SurfaceFormat::B8G8R8X8) {
109 : // Skia doesn't support RGBX surfaces, so ensure the alpha value is set
110 : // to opaque white. While it would be nice to only do this for Skia,
111 : // imgFrame can run off main thread and past shutdown where
112 : // we might not have gfxPlatform, so just memset everytime instead.
113 0 : memset(data, 0xFF, stride * aSize.height);
114 50 : } else if (aSurface->OnHeap()) {
115 : // We only need to memset it if the buffer was allocated on the heap.
116 : // Otherwise, it's allocated via mmap and refers to a zeroed page and will
117 : // be COW once it's written to.
118 50 : memset(data, 0, stride * aSize.height);
119 : }
120 :
121 50 : return true;
122 : }
123 :
124 : void
125 35 : MarkSurfaceShared(SourceSurface* aSurface)
126 : {
127 : // Depending on what requested the image decoding, the buffer may or may not
128 : // end up being shared with another process (e.g. put in a painted layer,
129 : // used inside a canvas). If not shared, we should ensure are not keeping the
130 : // handle only because we have yet to share it.
131 35 : if (aSurface && aSurface->GetType() == SurfaceType::DATA_SHARED) {
132 0 : auto sharedSurface = static_cast<SourceSurfaceSharedData*>(aSurface);
133 0 : sharedSurface->FinishedSharing();
134 : }
135 35 : }
136 :
137 : // Returns true if an image of aWidth x aHeight is allowed and legal.
138 : static bool
139 118 : AllowedImageSize(int32_t aWidth, int32_t aHeight)
140 : {
141 : // reject over-wide or over-tall images
142 118 : const int32_t k64KLimit = 0x0000FFFF;
143 118 : if (MOZ_UNLIKELY(aWidth > k64KLimit || aHeight > k64KLimit )) {
144 0 : NS_WARNING("image too big");
145 0 : return false;
146 : }
147 :
148 : // protect against invalid sizes
149 118 : if (MOZ_UNLIKELY(aHeight <= 0 || aWidth <= 0)) {
150 0 : return false;
151 : }
152 :
153 : // check to make sure we don't overflow a 32-bit
154 118 : CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * 4;
155 118 : if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
156 0 : NS_WARNING("width or height too large");
157 0 : return false;
158 : }
159 118 : return true;
160 : }
161 :
162 50 : static bool AllowedImageAndFrameDimensions(const nsIntSize& aImageSize,
163 : const nsIntRect& aFrameRect)
164 : {
165 50 : if (!AllowedImageSize(aImageSize.width, aImageSize.height)) {
166 0 : return false;
167 : }
168 50 : if (!AllowedImageSize(aFrameRect.width, aFrameRect.height)) {
169 0 : return false;
170 : }
171 50 : nsIntRect imageRect(0, 0, aImageSize.width, aImageSize.height);
172 50 : if (!imageRect.Contains(aFrameRect)) {
173 0 : NS_WARNING("Animated image frame does not fit inside bounds of image");
174 : }
175 50 : return true;
176 : }
177 :
178 68 : imgFrame::imgFrame()
179 : : mMonitor("imgFrame")
180 : , mDecoded(0, 0, 0, 0)
181 : , mLockCount(0)
182 : , mTimeout(FrameTimeout::FromRawMilliseconds(100))
183 : , mDisposalMethod(DisposalMethod::NOT_SPECIFIED)
184 : , mBlendMethod(BlendMethod::OVER)
185 : , mAborted(false)
186 : , mFinished(false)
187 : , mOptimizable(false)
188 : , mPalettedImageData(nullptr)
189 : , mPaletteDepth(0)
190 : , mNonPremult(false)
191 68 : , mCompositingFailed(false)
192 : {
193 68 : }
194 :
195 0 : imgFrame::~imgFrame()
196 : {
197 : #ifdef DEBUG
198 0 : MonitorAutoLock lock(mMonitor);
199 0 : MOZ_ASSERT(mAborted || AreAllPixelsWritten());
200 0 : MOZ_ASSERT(mAborted || mFinished);
201 : #endif
202 :
203 0 : free(mPalettedImageData);
204 0 : mPalettedImageData = nullptr;
205 0 : }
206 :
207 : nsresult
208 50 : imgFrame::InitForDecoder(const nsIntSize& aImageSize,
209 : const nsIntRect& aRect,
210 : SurfaceFormat aFormat,
211 : uint8_t aPaletteDepth /* = 0 */,
212 : bool aNonPremult /* = false */,
213 : bool aIsAnimated /* = false */)
214 : {
215 : // Assert for properties that should be verified by decoders,
216 : // warn for properties related to bad content.
217 50 : if (!AllowedImageAndFrameDimensions(aImageSize, aRect)) {
218 0 : NS_WARNING("Should have legal image size");
219 0 : mAborted = true;
220 0 : return NS_ERROR_FAILURE;
221 : }
222 :
223 50 : mImageSize = aImageSize;
224 50 : mFrameRect = aRect;
225 :
226 : // We only allow a non-trivial frame rect (i.e., a frame rect that doesn't
227 : // cover the entire image) for paletted animation frames. We never draw those
228 : // frames directly; we just use FrameAnimator to composite them and produce a
229 : // BGRA surface that we actually draw. We enforce this here to make sure that
230 : // imgFrame::Draw(), which is responsible for drawing all other kinds of
231 : // frames, never has to deal with a non-trivial frame rect.
232 200 : if (aPaletteDepth == 0 &&
233 200 : !mFrameRect.IsEqualEdges(IntRect(IntPoint(), mImageSize))) {
234 0 : MOZ_ASSERT_UNREACHABLE("Creating a non-paletted imgFrame with a "
235 : "non-trivial frame rect");
236 : return NS_ERROR_FAILURE;
237 : }
238 :
239 50 : mFormat = aFormat;
240 50 : mPaletteDepth = aPaletteDepth;
241 50 : mNonPremult = aNonPremult;
242 :
243 50 : if (aPaletteDepth != 0) {
244 : // We're creating for a paletted image.
245 0 : if (aPaletteDepth > 8) {
246 0 : NS_WARNING("Should have legal palette depth");
247 0 : NS_ERROR("This Depth is not supported");
248 0 : mAborted = true;
249 0 : return NS_ERROR_FAILURE;
250 : }
251 :
252 : // Use the fallible allocator here. Paletted images always use 1 byte per
253 : // pixel, so calculating the amount of memory we need is straightforward.
254 0 : size_t dataSize = PaletteDataLength() + mFrameRect.Area();
255 0 : mPalettedImageData = static_cast<uint8_t*>(calloc(dataSize, sizeof(uint8_t)));
256 0 : if (!mPalettedImageData) {
257 0 : NS_WARNING("Call to calloc for paletted image data should succeed");
258 : }
259 0 : NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
260 : } else {
261 50 : MOZ_ASSERT(!mLockedSurface, "Called imgFrame::InitForDecoder() twice?");
262 :
263 50 : mRawSurface = AllocateBufferForImage(mFrameRect.Size(), mFormat, aIsAnimated);
264 50 : if (!mRawSurface) {
265 0 : mAborted = true;
266 0 : return NS_ERROR_OUT_OF_MEMORY;
267 : }
268 :
269 50 : mLockedSurface = CreateLockedSurface(mRawSurface, mFrameRect.Size(), mFormat);
270 50 : if (!mLockedSurface) {
271 0 : NS_WARNING("Failed to create LockedSurface");
272 0 : mAborted = true;
273 0 : return NS_ERROR_OUT_OF_MEMORY;
274 : }
275 :
276 50 : if (!ClearSurface(mRawSurface, mFrameRect.Size(), mFormat)) {
277 0 : NS_WARNING("Could not clear allocated buffer");
278 0 : mAborted = true;
279 0 : return NS_ERROR_OUT_OF_MEMORY;
280 : }
281 : }
282 :
283 50 : return NS_OK;
284 : }
285 :
286 : nsresult
287 18 : imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
288 : const nsIntSize& aSize,
289 : const SurfaceFormat aFormat,
290 : SamplingFilter aSamplingFilter,
291 : uint32_t aImageFlags,
292 : gfx::BackendType aBackend)
293 : {
294 : // Assert for properties that should be verified by decoders,
295 : // warn for properties related to bad content.
296 18 : if (!AllowedImageSize(aSize.width, aSize.height)) {
297 0 : NS_WARNING("Should have legal image size");
298 0 : mAborted = true;
299 0 : return NS_ERROR_FAILURE;
300 : }
301 :
302 18 : mImageSize = aSize;
303 18 : mFrameRect = IntRect(IntPoint(0, 0), aSize);
304 :
305 18 : mFormat = aFormat;
306 18 : mPaletteDepth = 0;
307 :
308 36 : RefPtr<DrawTarget> target;
309 :
310 : bool canUseDataSurface =
311 18 : gfxPlatform::GetPlatform()->CanRenderContentToDataSurface();
312 :
313 18 : if (canUseDataSurface) {
314 : // It's safe to use data surfaces for content on this platform, so we can
315 : // get away with using volatile buffers.
316 0 : MOZ_ASSERT(!mLockedSurface, "Called imgFrame::InitWithDrawable() twice?");
317 :
318 0 : mRawSurface = AllocateBufferForImage(mFrameRect.Size(), mFormat);
319 0 : if (!mRawSurface) {
320 0 : mAborted = true;
321 0 : return NS_ERROR_OUT_OF_MEMORY;
322 : }
323 :
324 0 : mLockedSurface = CreateLockedSurface(mRawSurface, mFrameRect.Size(), mFormat);
325 0 : if (!mLockedSurface) {
326 0 : NS_WARNING("Failed to create LockedSurface");
327 0 : mAborted = true;
328 0 : return NS_ERROR_OUT_OF_MEMORY;
329 : }
330 :
331 0 : if (!ClearSurface(mRawSurface, mFrameRect.Size(), mFormat)) {
332 0 : NS_WARNING("Could not clear allocated buffer");
333 0 : mAborted = true;
334 0 : return NS_ERROR_OUT_OF_MEMORY;
335 : }
336 :
337 0 : target = gfxPlatform::CreateDrawTargetForData(
338 0 : mLockedSurface->GetData(),
339 0 : mFrameRect.Size(),
340 0 : mLockedSurface->Stride(),
341 0 : mFormat);
342 : } else {
343 : // We can't use data surfaces for content, so we'll create an offscreen
344 : // surface instead. This means if someone later calls RawAccessRef(), we
345 : // may have to do an expensive readback, but we warned callers about that in
346 : // the documentation for this method.
347 18 : MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?");
348 :
349 18 : if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(aBackend)) {
350 : target = gfxPlatform::GetPlatform()->
351 18 : CreateDrawTargetForBackend(aBackend, mFrameRect.Size(), mFormat);
352 : } else {
353 : target = gfxPlatform::GetPlatform()->
354 0 : CreateOffscreenContentDrawTarget(mFrameRect.Size(), mFormat);
355 : }
356 : }
357 :
358 18 : if (!target || !target->IsValid()) {
359 0 : mAborted = true;
360 0 : return NS_ERROR_OUT_OF_MEMORY;
361 : }
362 :
363 : // Draw using the drawable the caller provided.
364 36 : RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
365 18 : MOZ_ASSERT(ctx); // Already checked the draw target above.
366 54 : gfxUtils::DrawPixelSnapped(ctx, aDrawable, SizeDouble(mFrameRect.Size()),
367 36 : ImageRegion::Create(ThebesRect(mFrameRect)),
368 18 : mFormat, aSamplingFilter, aImageFlags);
369 :
370 18 : if (canUseDataSurface && !mLockedSurface) {
371 0 : NS_WARNING("Failed to create VolatileDataSourceSurface");
372 0 : mAborted = true;
373 0 : return NS_ERROR_OUT_OF_MEMORY;
374 : }
375 :
376 18 : if (!canUseDataSurface) {
377 : // We used an offscreen surface, which is an "optimized" surface from
378 : // imgFrame's perspective.
379 18 : mOptSurface = target->Snapshot();
380 : } else {
381 0 : FinalizeSurface();
382 : }
383 :
384 : // If we reach this point, we should regard ourselves as complete.
385 18 : mDecoded = GetRect();
386 18 : mFinished = true;
387 :
388 : #ifdef DEBUG
389 36 : MonitorAutoLock lock(mMonitor);
390 18 : MOZ_ASSERT(AreAllPixelsWritten());
391 : #endif
392 :
393 18 : return NS_OK;
394 : }
395 :
396 : nsresult
397 35 : imgFrame::Optimize(DrawTarget* aTarget)
398 : {
399 35 : MOZ_ASSERT(NS_IsMainThread());
400 35 : mMonitor.AssertCurrentThreadOwns();
401 :
402 35 : if (mLockCount > 0 || !mOptimizable) {
403 : // Don't optimize right now.
404 31 : return NS_OK;
405 : }
406 :
407 : // Check whether image optimization is disabled -- not thread safe!
408 : static bool gDisableOptimize = false;
409 : static bool hasCheckedOptimize = false;
410 4 : if (!hasCheckedOptimize) {
411 1 : if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
412 0 : gDisableOptimize = true;
413 : }
414 1 : hasCheckedOptimize = true;
415 : }
416 :
417 : // Don't optimize during shutdown because gfxPlatform may not be available.
418 4 : if (ShutdownTracker::ShutdownHasStarted()) {
419 0 : return NS_OK;
420 : }
421 :
422 4 : if (gDisableOptimize) {
423 0 : return NS_OK;
424 : }
425 :
426 4 : if (mPalettedImageData || mOptSurface) {
427 0 : return NS_OK;
428 : }
429 :
430 : // XXX(seth): It's currently unclear if there's any reason why we can't
431 : // optimize non-premult surfaces. We should look into removing this.
432 4 : if (mNonPremult) {
433 0 : return NS_OK;
434 : }
435 :
436 : mOptSurface = gfxPlatform::GetPlatform()
437 4 : ->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mLockedSurface);
438 4 : if (mOptSurface == mLockedSurface) {
439 4 : mOptSurface = nullptr;
440 : }
441 :
442 4 : if (mOptSurface) {
443 : // There's no reason to keep our original surface around if we have an
444 : // optimized surface. Release our reference to it. This will leave
445 : // |mLockedSurface| as the only thing keeping it alive, so it'll get freed
446 : // below.
447 0 : mRawSurface = nullptr;
448 : }
449 :
450 : // Release all strong references to the surface's memory. If the underlying
451 : // surface is volatile, this will allow the operating system to free the
452 : // memory if it needs to.
453 4 : mLockedSurface = nullptr;
454 4 : mOptimizable = false;
455 :
456 4 : return NS_OK;
457 : }
458 :
459 : DrawableFrameRef
460 284 : imgFrame::DrawableRef()
461 : {
462 284 : return DrawableFrameRef(this);
463 : }
464 :
465 : RawAccessFrameRef
466 240 : imgFrame::RawAccessRef()
467 : {
468 240 : return RawAccessFrameRef(this);
469 : }
470 :
471 : void
472 36 : imgFrame::SetRawAccessOnly()
473 : {
474 36 : AssertImageDataLocked();
475 :
476 : // Lock our data and throw away the key.
477 36 : LockImageData();
478 36 : }
479 :
480 :
481 : imgFrame::SurfaceWithFormat
482 35 : imgFrame::SurfaceForDrawing(bool aDoPartialDecode,
483 : bool aDoTile,
484 : ImageRegion& aRegion,
485 : SourceSurface* aSurface)
486 : {
487 35 : MOZ_ASSERT(NS_IsMainThread());
488 35 : mMonitor.AssertCurrentThreadOwns();
489 :
490 35 : if (!aDoPartialDecode) {
491 105 : return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, mImageSize),
492 105 : mFormat);
493 : }
494 :
495 0 : gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width,
496 0 : mDecoded.height);
497 :
498 0 : if (aDoTile) {
499 : // Create a temporary surface.
500 : // Give this surface an alpha channel because there are
501 : // transparent pixels in the padding or undecoded area
502 : RefPtr<DrawTarget> target =
503 : gfxPlatform::GetPlatform()->
504 0 : CreateOffscreenContentDrawTarget(mImageSize, SurfaceFormat::B8G8R8A8);
505 0 : if (!target) {
506 0 : return SurfaceWithFormat();
507 : }
508 :
509 : SurfacePattern pattern(aSurface,
510 : aRegion.GetExtendMode(),
511 0 : Matrix::Translation(mDecoded.x, mDecoded.y));
512 0 : target->FillRect(ToRect(aRegion.Intersect(available).Rect()), pattern);
513 :
514 0 : RefPtr<SourceSurface> newsurf = target->Snapshot();
515 0 : return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, mImageSize),
516 0 : target->GetFormat());
517 : }
518 :
519 : // Not tiling, and we have a surface, so we can account for
520 : // a partial decode just by twiddling parameters.
521 0 : aRegion = aRegion.Intersect(available);
522 0 : IntSize availableSize(mDecoded.width, mDecoded.height);
523 :
524 0 : return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize),
525 0 : mFormat);
526 : }
527 :
528 35 : bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
529 : SamplingFilter aSamplingFilter, uint32_t aImageFlags,
530 : float aOpacity)
531 : {
532 70 : AUTO_PROFILER_LABEL("imgFrame::Draw", GRAPHICS);
533 :
534 35 : MOZ_ASSERT(NS_IsMainThread());
535 35 : NS_ASSERTION(!aRegion.Rect().IsEmpty(), "Drawing empty region!");
536 35 : NS_ASSERTION(!aRegion.IsRestricted() ||
537 : !aRegion.Rect().Intersect(aRegion.Restriction()).IsEmpty(),
538 : "We must be allowed to sample *some* source pixels!");
539 35 : MOZ_ASSERT(mFrameRect.IsEqualEdges(IntRect(IntPoint(), mImageSize)),
540 : "Directly drawing an image with a non-trivial frame rect!");
541 :
542 35 : if (mPalettedImageData) {
543 0 : MOZ_ASSERT_UNREACHABLE("Directly drawing a paletted image!");
544 : return false;
545 : }
546 :
547 70 : MonitorAutoLock lock(mMonitor);
548 :
549 : // Possibly convert this image into a GPU texture, this may also cause our
550 : // mLockedSurface to be released and the OS to release the underlying memory.
551 35 : Optimize(aContext->GetDrawTarget());
552 :
553 35 : bool doPartialDecode = !AreAllPixelsWritten();
554 :
555 70 : RefPtr<SourceSurface> surf = GetSourceSurfaceInternal();
556 35 : if (!surf) {
557 0 : return false;
558 : }
559 :
560 35 : gfxRect imageRect(0, 0, mImageSize.width, mImageSize.height);
561 43 : bool doTile = !imageRect.Contains(aRegion.Rect()) &&
562 43 : !(aImageFlags & imgIContainer::FLAG_CLAMP);
563 :
564 35 : ImageRegion region(aRegion);
565 : SurfaceWithFormat surfaceResult =
566 70 : SurfaceForDrawing(doPartialDecode, doTile, region, surf);
567 :
568 35 : if (surfaceResult.IsValid()) {
569 70 : gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
570 70 : imageRect.Size(), region, surfaceResult.mFormat,
571 35 : aSamplingFilter, aImageFlags, aOpacity);
572 : }
573 :
574 : // Image got put into a painted layer, it will not be shared with another
575 : // process.
576 35 : MarkSurfaceShared(surf);
577 35 : return true;
578 : }
579 :
580 : nsresult
581 387 : imgFrame::ImageUpdated(const nsIntRect& aUpdateRect)
582 : {
583 775 : MonitorAutoLock lock(mMonitor);
584 776 : return ImageUpdatedInternal(aUpdateRect);
585 : }
586 :
587 : nsresult
588 458 : imgFrame::ImageUpdatedInternal(const nsIntRect& aUpdateRect)
589 : {
590 458 : mMonitor.AssertCurrentThreadOwns();
591 :
592 458 : mDecoded.UnionRect(mDecoded, aUpdateRect);
593 :
594 : // Clamp to the frame rect to ensure that decoder bugs don't result in a
595 : // decoded rect that extends outside the bounds of the frame rect.
596 458 : mDecoded.IntersectRect(mDecoded, mFrameRect);
597 :
598 458 : return NS_OK;
599 : }
600 :
601 : void
602 70 : imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */,
603 : DisposalMethod aDisposalMethod /* = DisposalMethod::KEEP */,
604 : FrameTimeout aTimeout
605 : /* = FrameTimeout::FromRawMilliseconds(0) */,
606 : BlendMethod aBlendMethod /* = BlendMethod::OVER */,
607 : const Maybe<IntRect>& aBlendRect /* = Nothing() */,
608 : bool aFinalize /* = true */)
609 : {
610 140 : MonitorAutoLock lock(mMonitor);
611 70 : MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
612 :
613 70 : mDisposalMethod = aDisposalMethod;
614 70 : mTimeout = aTimeout;
615 70 : mBlendMethod = aBlendMethod;
616 70 : mBlendRect = aBlendRect;
617 70 : ImageUpdatedInternal(GetRect());
618 :
619 70 : if (aFinalize) {
620 70 : FinalizeSurfaceInternal();
621 : }
622 :
623 70 : mFinished = true;
624 :
625 : // The image is now complete, wake up anyone who's waiting.
626 70 : mMonitor.NotifyAll();
627 70 : }
628 :
629 : uint32_t
630 192 : imgFrame::GetImageBytesPerRow() const
631 : {
632 192 : mMonitor.AssertCurrentThreadOwns();
633 :
634 192 : if (mRawSurface) {
635 192 : return mFrameRect.width * BytesPerPixel(mFormat);
636 : }
637 :
638 0 : if (mPaletteDepth) {
639 0 : return mFrameRect.width;
640 : }
641 :
642 0 : return 0;
643 : }
644 :
645 : uint32_t
646 192 : imgFrame::GetImageDataLength() const
647 : {
648 192 : return GetImageBytesPerRow() * mFrameRect.height;
649 : }
650 :
651 : void
652 48 : imgFrame::GetImageData(uint8_t** aData, uint32_t* aLength) const
653 : {
654 96 : MonitorAutoLock lock(mMonitor);
655 48 : GetImageDataInternal(aData, aLength);
656 48 : }
657 :
658 : void
659 192 : imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const
660 : {
661 192 : mMonitor.AssertCurrentThreadOwns();
662 192 : MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
663 :
664 192 : if (mLockedSurface) {
665 : // TODO: This is okay for now because we only realloc shared surfaces on
666 : // the main thread after decoding has finished, but if animations want to
667 : // read frame data off the main thread, we will need to reconsider this.
668 192 : *aData = mLockedSurface->GetData();
669 192 : MOZ_ASSERT(*aData,
670 : "mLockedSurface is non-null, but GetData is null in GetImageData");
671 0 : } else if (mPalettedImageData) {
672 0 : *aData = mPalettedImageData + PaletteDataLength();
673 0 : MOZ_ASSERT(*aData,
674 : "mPalettedImageData is non-null, but result is null in GetImageData");
675 : } else {
676 0 : MOZ_ASSERT(false,
677 : "Have neither mLockedSurface nor mPalettedImageData in GetImageData");
678 : *aData = nullptr;
679 : }
680 :
681 192 : *aLength = GetImageDataLength();
682 192 : }
683 :
684 : uint8_t*
685 0 : imgFrame::GetImageData() const
686 : {
687 : uint8_t* data;
688 : uint32_t length;
689 0 : GetImageData(&data, &length);
690 0 : return data;
691 : }
692 :
693 : bool
694 109 : imgFrame::GetIsPaletted() const
695 : {
696 109 : return mPalettedImageData != nullptr;
697 : }
698 :
699 : void
700 48 : imgFrame::GetPaletteData(uint32_t** aPalette, uint32_t* length) const
701 : {
702 48 : AssertImageDataLocked();
703 :
704 48 : if (!mPalettedImageData) {
705 48 : *aPalette = nullptr;
706 48 : *length = 0;
707 : } else {
708 0 : *aPalette = (uint32_t*) mPalettedImageData;
709 0 : *length = PaletteDataLength();
710 : }
711 48 : }
712 :
713 : uint32_t*
714 0 : imgFrame::GetPaletteData() const
715 : {
716 : uint32_t* data;
717 : uint32_t length;
718 0 : GetPaletteData(&data, &length);
719 0 : return data;
720 : }
721 :
722 : nsresult
723 276 : imgFrame::LockImageData()
724 : {
725 552 : MonitorAutoLock lock(mMonitor);
726 :
727 276 : MOZ_ASSERT(mLockCount >= 0, "Unbalanced locks and unlocks");
728 276 : if (mLockCount < 0) {
729 0 : return NS_ERROR_FAILURE;
730 : }
731 :
732 276 : mLockCount++;
733 :
734 : // If we are not the first lock, there's nothing to do.
735 276 : if (mLockCount != 1) {
736 226 : return NS_OK;
737 : }
738 :
739 : // If we're the first lock, but have the locked surface, we're OK.
740 50 : if (mLockedSurface) {
741 50 : return NS_OK;
742 : }
743 :
744 : // Paletted images don't have surfaces, so there's nothing to do.
745 0 : if (mPalettedImageData) {
746 0 : return NS_OK;
747 : }
748 :
749 0 : MOZ_ASSERT_UNREACHABLE("It's illegal to re-lock an optimized imgFrame");
750 : return NS_ERROR_FAILURE;
751 : }
752 :
753 : void
754 96 : imgFrame::AssertImageDataLocked() const
755 : {
756 : #ifdef DEBUG
757 192 : MonitorAutoLock lock(mMonitor);
758 96 : MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
759 : #endif
760 96 : }
761 :
762 : nsresult
763 202 : imgFrame::UnlockImageData()
764 : {
765 404 : MonitorAutoLock lock(mMonitor);
766 :
767 202 : MOZ_ASSERT(mLockCount > 0, "Unlocking an unlocked image!");
768 202 : if (mLockCount <= 0) {
769 0 : return NS_ERROR_FAILURE;
770 : }
771 :
772 202 : MOZ_ASSERT(mLockCount > 1 || mFinished || mAborted,
773 : "Should have Finish()'d or aborted before unlocking");
774 :
775 202 : mLockCount--;
776 :
777 202 : return NS_OK;
778 : }
779 :
780 : void
781 12 : imgFrame::SetOptimizable()
782 : {
783 12 : AssertImageDataLocked();
784 24 : MonitorAutoLock lock(mMonitor);
785 12 : mOptimizable = true;
786 12 : }
787 :
788 : void
789 0 : imgFrame::FinalizeSurface()
790 : {
791 0 : MonitorAutoLock lock(mMonitor);
792 0 : FinalizeSurfaceInternal();
793 0 : }
794 :
795 : void
796 70 : imgFrame::FinalizeSurfaceInternal()
797 : {
798 70 : mMonitor.AssertCurrentThreadOwns();
799 :
800 : // Not all images will have mRawSurface to finalize (i.e. paletted images).
801 70 : if (!mRawSurface || mRawSurface->GetType() != SurfaceType::DATA_SHARED) {
802 70 : return;
803 : }
804 :
805 0 : auto sharedSurf = static_cast<SourceSurfaceSharedData*>(mRawSurface.get());
806 0 : sharedSurf->Finalize();
807 : }
808 :
809 : already_AddRefed<SourceSurface>
810 73 : imgFrame::GetSourceSurface()
811 : {
812 146 : MonitorAutoLock lock(mMonitor);
813 146 : return GetSourceSurfaceInternal();
814 : }
815 :
816 : already_AddRefed<SourceSurface>
817 108 : imgFrame::GetSourceSurfaceInternal()
818 : {
819 108 : mMonitor.AssertCurrentThreadOwns();
820 :
821 108 : if (mOptSurface) {
822 73 : if (mOptSurface->IsValid()) {
823 146 : RefPtr<SourceSurface> surf(mOptSurface);
824 73 : return surf.forget();
825 : } else {
826 0 : mOptSurface = nullptr;
827 : }
828 : }
829 :
830 35 : if (mLockedSurface) {
831 22 : RefPtr<SourceSurface> surf(mLockedSurface);
832 11 : return surf.forget();
833 : }
834 :
835 24 : if (!mRawSurface) {
836 0 : return nullptr;
837 : }
838 :
839 24 : return CreateLockedSurface(mRawSurface, mFrameRect.Size(), mFormat);
840 : }
841 :
842 : AnimationData
843 144 : imgFrame::GetAnimationData() const
844 : {
845 288 : MonitorAutoLock lock(mMonitor);
846 144 : MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
847 :
848 : uint8_t* data;
849 144 : if (mPalettedImageData) {
850 0 : data = mPalettedImageData;
851 : } else {
852 : uint32_t length;
853 144 : GetImageDataInternal(&data, &length);
854 : }
855 :
856 144 : bool hasAlpha = mFormat == SurfaceFormat::B8G8R8A8;
857 :
858 288 : return AnimationData(data, PaletteDataLength(), mTimeout, GetRect(),
859 432 : mBlendMethod, mBlendRect, mDisposalMethod, hasAlpha);
860 : }
861 :
862 : void
863 0 : imgFrame::Abort()
864 : {
865 0 : MonitorAutoLock lock(mMonitor);
866 :
867 0 : mAborted = true;
868 :
869 : // Wake up anyone who's waiting.
870 0 : mMonitor.NotifyAll();
871 0 : }
872 :
873 : bool
874 24 : imgFrame::IsAborted() const
875 : {
876 48 : MonitorAutoLock lock(mMonitor);
877 48 : return mAborted;
878 : }
879 :
880 : bool
881 101 : imgFrame::IsFinished() const
882 : {
883 202 : MonitorAutoLock lock(mMonitor);
884 202 : return mFinished;
885 : }
886 :
887 : void
888 0 : imgFrame::WaitUntilFinished() const
889 : {
890 0 : MonitorAutoLock lock(mMonitor);
891 :
892 : while (true) {
893 : // Return if we're aborted or complete.
894 0 : if (mAborted || mFinished) {
895 0 : return;
896 : }
897 :
898 : // Not complete yet, so we'll have to wait.
899 0 : mMonitor.Wait();
900 : }
901 : }
902 :
903 : bool
904 53 : imgFrame::AreAllPixelsWritten() const
905 : {
906 53 : mMonitor.AssertCurrentThreadOwns();
907 53 : return mDecoded.IsEqualInterior(mFrameRect);
908 : }
909 :
910 46 : bool imgFrame::GetCompositingFailed() const
911 : {
912 46 : MOZ_ASSERT(NS_IsMainThread());
913 46 : return mCompositingFailed;
914 : }
915 :
916 : void
917 22 : imgFrame::SetCompositingFailed(bool val)
918 : {
919 22 : MOZ_ASSERT(NS_IsMainThread());
920 22 : mCompositingFailed = val;
921 22 : }
922 :
923 : void
924 0 : imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
925 : size_t& aHeapSizeOut,
926 : size_t& aNonHeapSizeOut,
927 : size_t& aSharedHandlesOut) const
928 : {
929 0 : MonitorAutoLock lock(mMonitor);
930 :
931 0 : if (mPalettedImageData) {
932 0 : aHeapSizeOut += aMallocSizeOf(mPalettedImageData);
933 : }
934 0 : if (mLockedSurface) {
935 0 : aHeapSizeOut += aMallocSizeOf(mLockedSurface);
936 : }
937 0 : if (mOptSurface) {
938 0 : aHeapSizeOut += aMallocSizeOf(mOptSurface);
939 : }
940 0 : if (mRawSurface) {
941 0 : aHeapSizeOut += aMallocSizeOf(mRawSurface);
942 0 : mRawSurface->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
943 0 : aNonHeapSizeOut);
944 :
945 0 : if (mRawSurface->GetType() == SurfaceType::DATA_SHARED) {
946 : auto sharedSurface =
947 0 : static_cast<SourceSurfaceSharedData*>(mRawSurface.get());
948 0 : if (sharedSurface->CanShare()) {
949 0 : ++aSharedHandlesOut;
950 : }
951 : }
952 : }
953 0 : }
954 :
955 : } // namespace image
956 : } // namespace mozilla
|