Line data Source code
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 : #include "ClippedImage.h"
7 :
8 : #include <algorithm>
9 : #include <new> // Workaround for bug in VS10; see bug 981264.
10 : #include <cmath>
11 : #include <utility>
12 :
13 : #include "gfxDrawable.h"
14 : #include "gfxPlatform.h"
15 : #include "gfxUtils.h"
16 : #include "mozilla/gfx/2D.h"
17 : #include "mozilla/Move.h"
18 : #include "mozilla/RefPtr.h"
19 : #include "mozilla/Pair.h"
20 : #include "mozilla/Tuple.h"
21 :
22 : #include "ImageRegion.h"
23 : #include "Orientation.h"
24 : #include "SVGImageContext.h"
25 :
26 : namespace mozilla {
27 :
28 : using namespace gfx;
29 : using layers::LayerManager;
30 : using layers::ImageContainer;
31 : using std::make_pair;
32 : using std::max;
33 : using std::modf;
34 : using std::pair;
35 :
36 : namespace image {
37 :
38 0 : class ClippedImageCachedSurface
39 : {
40 : public:
41 0 : ClippedImageCachedSurface(already_AddRefed<SourceSurface> aSurface,
42 : const nsIntSize& aSize,
43 : const Maybe<SVGImageContext>& aSVGContext,
44 : float aFrame,
45 : uint32_t aFlags,
46 : DrawResult aDrawResult)
47 0 : : mSurface(aSurface)
48 : , mSize(aSize)
49 : , mSVGContext(aSVGContext)
50 : , mFrame(aFrame)
51 : , mFlags(aFlags)
52 0 : , mDrawResult(aDrawResult)
53 : {
54 0 : MOZ_ASSERT(mSurface, "Must have a valid surface");
55 0 : }
56 :
57 0 : bool Matches(const nsIntSize& aSize,
58 : const Maybe<SVGImageContext>& aSVGContext,
59 : float aFrame,
60 : uint32_t aFlags) const
61 : {
62 0 : return mSize == aSize &&
63 0 : mSVGContext == aSVGContext &&
64 0 : mFrame == aFrame &&
65 0 : mFlags == aFlags;
66 : }
67 :
68 0 : already_AddRefed<SourceSurface> Surface() const
69 : {
70 0 : RefPtr<SourceSurface> surf(mSurface);
71 0 : return surf.forget();
72 : }
73 :
74 0 : DrawResult GetDrawResult() const
75 : {
76 0 : return mDrawResult;
77 : }
78 :
79 0 : bool NeedsRedraw() const
80 : {
81 0 : return mDrawResult != DrawResult::SUCCESS &&
82 0 : mDrawResult != DrawResult::BAD_IMAGE;
83 : }
84 :
85 : private:
86 : RefPtr<SourceSurface> mSurface;
87 : const nsIntSize mSize;
88 : Maybe<SVGImageContext> mSVGContext;
89 : const float mFrame;
90 : const uint32_t mFlags;
91 : const DrawResult mDrawResult;
92 : };
93 :
94 0 : class DrawSingleTileCallback : public gfxDrawingCallback
95 : {
96 : public:
97 0 : DrawSingleTileCallback(ClippedImage* aImage,
98 : const nsIntSize& aSize,
99 : const Maybe<SVGImageContext>& aSVGContext,
100 : uint32_t aWhichFrame,
101 : uint32_t aFlags,
102 : float aOpacity)
103 0 : : mImage(aImage)
104 : , mSize(aSize)
105 : , mSVGContext(aSVGContext)
106 : , mWhichFrame(aWhichFrame)
107 : , mFlags(aFlags)
108 : , mDrawResult(DrawResult::NOT_READY)
109 0 : , mOpacity(aOpacity)
110 : {
111 0 : MOZ_ASSERT(mImage, "Must have an image to clip");
112 0 : }
113 :
114 0 : virtual bool operator()(gfxContext* aContext,
115 : const gfxRect& aFillRect,
116 : const SamplingFilter aSamplingFilter,
117 : const gfxMatrix& aTransform)
118 : {
119 0 : MOZ_ASSERT(aTransform.IsIdentity(),
120 : "Caller is probably CreateSamplingRestrictedDrawable, "
121 : "which should not happen");
122 :
123 : // Draw the image. |gfxCallbackDrawable| always calls this function with
124 : // arguments that guarantee we never tile.
125 0 : mDrawResult =
126 0 : mImage->DrawSingleTile(aContext, mSize, ImageRegion::Create(aFillRect),
127 0 : mWhichFrame, aSamplingFilter, mSVGContext, mFlags,
128 : mOpacity);
129 :
130 0 : return true;
131 : }
132 :
133 0 : DrawResult GetDrawResult() { return mDrawResult; }
134 :
135 : private:
136 : RefPtr<ClippedImage> mImage;
137 : const nsIntSize mSize;
138 : const Maybe<SVGImageContext>& mSVGContext;
139 : const uint32_t mWhichFrame;
140 : const uint32_t mFlags;
141 : DrawResult mDrawResult;
142 : float mOpacity;
143 : };
144 :
145 56 : ClippedImage::ClippedImage(Image* aImage,
146 : nsIntRect aClip,
147 56 : const Maybe<nsSize>& aSVGViewportSize)
148 : : ImageWrapper(aImage)
149 56 : , mClip(aClip)
150 : {
151 56 : MOZ_ASSERT(aImage != nullptr, "ClippedImage requires an existing Image");
152 56 : MOZ_ASSERT_IF(aSVGViewportSize,
153 : aImage->GetType() == imgIContainer::TYPE_VECTOR);
154 56 : if (aSVGViewportSize) {
155 0 : mSVGViewportSize = Some(aSVGViewportSize->ToNearestPixels(
156 0 : nsPresContext::AppUnitsPerCSSPixel()));
157 : }
158 56 : }
159 :
160 112 : ClippedImage::~ClippedImage()
161 168 : { }
162 :
163 : bool
164 172 : ClippedImage::ShouldClip()
165 : {
166 : // We need to evaluate the clipping region against the image's width and
167 : // height once they're available to determine if it's valid and whether we
168 : // actually need to do any work. We may fail if the image's width and height
169 : // aren't available yet, in which case we'll try again later.
170 172 : if (mShouldClip.isNothing()) {
171 : int32_t width, height;
172 : RefPtr<ProgressTracker> progressTracker =
173 112 : InnerImage()->GetProgressTracker();
174 56 : if (InnerImage()->HasError()) {
175 : // If there's a problem with the inner image we'll let it handle
176 : // everything.
177 0 : mShouldClip.emplace(false);
178 56 : } else if (mSVGViewportSize && !mSVGViewportSize->IsEmpty()) {
179 : // Clamp the clipping region to the size of the SVG viewport.
180 0 : nsIntRect svgViewportRect(nsIntPoint(0,0), *mSVGViewportSize);
181 :
182 0 : mClip = mClip.Intersect(svgViewportRect);
183 :
184 : // If the clipping region is the same size as the SVG viewport size
185 : // we don't have to do anything.
186 0 : mShouldClip.emplace(!mClip.IsEqualInterior(svgViewportRect));
187 224 : } else if (NS_SUCCEEDED(InnerImage()->GetWidth(&width)) && width > 0 &&
188 168 : NS_SUCCEEDED(InnerImage()->GetHeight(&height)) && height > 0) {
189 : // Clamp the clipping region to the size of the underlying image.
190 56 : mClip = mClip.Intersect(nsIntRect(0, 0, width, height));
191 :
192 : // If the clipping region is the same size as the underlying image we
193 : // don't have to do anything.
194 168 : mShouldClip.emplace(!mClip.IsEqualInterior(nsIntRect(0, 0, width,
195 112 : height)));
196 0 : } else if (progressTracker &&
197 0 : !(progressTracker->GetProgress() & FLAG_LOAD_COMPLETE)) {
198 : // The image just hasn't finished loading yet. We don't yet know whether
199 : // clipping with be needed or not for now. Just return without memorizing
200 : // anything.
201 0 : return false;
202 : } else {
203 : // We have a fully loaded image without a clearly defined width and
204 : // height. This can happen with SVG images.
205 0 : mShouldClip.emplace(false);
206 : }
207 : }
208 :
209 172 : MOZ_ASSERT(mShouldClip.isSome(), "Should have computed a result");
210 172 : return *mShouldClip;
211 : }
212 :
213 448 : NS_IMPL_ISUPPORTS_INHERITED0(ClippedImage, ImageWrapper)
214 :
215 : NS_IMETHODIMP
216 52 : ClippedImage::GetWidth(int32_t* aWidth)
217 : {
218 52 : if (!ShouldClip()) {
219 0 : return InnerImage()->GetWidth(aWidth);
220 : }
221 :
222 52 : *aWidth = mClip.width;
223 52 : return NS_OK;
224 : }
225 :
226 : NS_IMETHODIMP
227 52 : ClippedImage::GetHeight(int32_t* aHeight)
228 : {
229 52 : if (!ShouldClip()) {
230 0 : return InnerImage()->GetHeight(aHeight);
231 : }
232 :
233 52 : *aHeight = mClip.height;
234 52 : return NS_OK;
235 : }
236 :
237 : NS_IMETHODIMP
238 0 : ClippedImage::GetIntrinsicSize(nsSize* aSize)
239 : {
240 0 : if (!ShouldClip()) {
241 0 : return InnerImage()->GetIntrinsicSize(aSize);
242 : }
243 :
244 0 : *aSize = nsSize(mClip.width, mClip.height);
245 0 : return NS_OK;
246 : }
247 :
248 : NS_IMETHODIMP
249 52 : ClippedImage::GetIntrinsicRatio(nsSize* aRatio)
250 : {
251 52 : if (!ShouldClip()) {
252 0 : return InnerImage()->GetIntrinsicRatio(aRatio);
253 : }
254 :
255 52 : *aRatio = nsSize(mClip.width, mClip.height);
256 52 : return NS_OK;
257 : }
258 :
259 : NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
260 0 : ClippedImage::GetFrame(uint32_t aWhichFrame,
261 : uint32_t aFlags)
262 : {
263 : DrawResult result;
264 0 : RefPtr<SourceSurface> surface;
265 0 : Tie(result, surface) = GetFrameInternal(mClip.Size(), Nothing(), aWhichFrame, aFlags, 1.0);
266 0 : return surface.forget();
267 : }
268 :
269 : NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
270 0 : ClippedImage::GetFrameAtSize(const IntSize& aSize,
271 : uint32_t aWhichFrame,
272 : uint32_t aFlags)
273 : {
274 : // XXX(seth): It'd be nice to support downscale-during-decode for this case,
275 : // but right now we just fall back to the intrinsic size.
276 0 : return GetFrame(aWhichFrame, aFlags);
277 : }
278 :
279 : Pair<DrawResult, RefPtr<SourceSurface>>
280 0 : ClippedImage::GetFrameInternal(const nsIntSize& aSize,
281 : const Maybe<SVGImageContext>& aSVGContext,
282 : uint32_t aWhichFrame,
283 : uint32_t aFlags,
284 : float aOpacity)
285 : {
286 0 : if (!ShouldClip()) {
287 0 : RefPtr<SourceSurface> surface = InnerImage()->GetFrame(aWhichFrame, aFlags);
288 0 : return MakePair(surface ? DrawResult::SUCCESS : DrawResult::NOT_READY,
289 0 : Move(surface));
290 : }
291 :
292 0 : float frameToDraw = InnerImage()->GetFrameIndex(aWhichFrame);
293 0 : if (!mCachedSurface ||
294 0 : !mCachedSurface->Matches(aSize, aSVGContext, frameToDraw, aFlags) ||
295 0 : mCachedSurface->NeedsRedraw()) {
296 : // Create a surface to draw into.
297 : RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->
298 0 : CreateOffscreenContentDrawTarget(IntSize(aSize.width, aSize.height),
299 0 : SurfaceFormat::B8G8R8A8);
300 0 : if (!target || !target->IsValid()) {
301 0 : NS_ERROR("Could not create a DrawTarget");
302 0 : return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr<SourceSurface>());
303 : }
304 :
305 0 : RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
306 0 : MOZ_ASSERT(ctx); // already checked the draw target above
307 :
308 : // Create our callback.
309 : RefPtr<DrawSingleTileCallback> drawTileCallback =
310 : new DrawSingleTileCallback(this, aSize, aSVGContext, aWhichFrame, aFlags,
311 0 : aOpacity);
312 : RefPtr<gfxDrawable> drawable =
313 0 : new gfxCallbackDrawable(drawTileCallback, aSize);
314 :
315 : // Actually draw. The callback will end up invoking DrawSingleTile.
316 0 : gfxUtils::DrawPixelSnapped(ctx, drawable, SizeDouble(aSize),
317 0 : ImageRegion::Create(aSize),
318 : SurfaceFormat::B8G8R8A8,
319 : SamplingFilter::LINEAR,
320 0 : imgIContainer::FLAG_CLAMP);
321 :
322 : // Cache the resulting surface.
323 : mCachedSurface =
324 0 : MakeUnique<ClippedImageCachedSurface>(target->Snapshot(), aSize, aSVGContext,
325 : frameToDraw, aFlags,
326 0 : drawTileCallback->GetDrawResult());
327 : }
328 :
329 0 : MOZ_ASSERT(mCachedSurface, "Should have a cached surface now");
330 0 : RefPtr<SourceSurface> surface = mCachedSurface->Surface();
331 0 : return MakePair(mCachedSurface->GetDrawResult(), Move(surface));
332 : }
333 :
334 : NS_IMETHODIMP_(bool)
335 0 : ClippedImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
336 : {
337 0 : if (!ShouldClip()) {
338 0 : return InnerImage()->IsImageContainerAvailable(aManager, aFlags);
339 : }
340 0 : return false;
341 : }
342 :
343 : NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
344 0 : ClippedImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
345 : {
346 : // XXX(seth): We currently don't have a way of clipping the result of
347 : // GetImageContainer. We work around this by always returning null, but if it
348 : // ever turns out that ClippedImage is widely used on codepaths that can
349 : // actually benefit from GetImageContainer, it would be a good idea to fix
350 : // that method for performance reasons.
351 :
352 0 : if (!ShouldClip()) {
353 0 : return InnerImage()->GetImageContainer(aManager, aFlags);
354 : }
355 :
356 0 : return nullptr;
357 : }
358 :
359 : static bool
360 16 : MustCreateSurface(gfxContext* aContext,
361 : const nsIntSize& aSize,
362 : const ImageRegion& aRegion,
363 : const uint32_t aFlags)
364 : {
365 16 : gfxRect imageRect(0, 0, aSize.width, aSize.height);
366 16 : bool willTile = !imageRect.Contains(aRegion.Rect()) &&
367 16 : !(aFlags & imgIContainer::FLAG_CLAMP);
368 48 : bool willResample = aContext->CurrentMatrix().HasNonIntegerTranslation() &&
369 16 : (willTile || !aRegion.RestrictionContains(imageRect));
370 16 : return willTile || willResample;
371 : }
372 :
373 : NS_IMETHODIMP_(DrawResult)
374 8 : ClippedImage::Draw(gfxContext* aContext,
375 : const nsIntSize& aSize,
376 : const ImageRegion& aRegion,
377 : uint32_t aWhichFrame,
378 : SamplingFilter aSamplingFilter,
379 : const Maybe<SVGImageContext>& aSVGContext,
380 : uint32_t aFlags,
381 : float aOpacity)
382 : {
383 8 : if (!ShouldClip()) {
384 0 : return InnerImage()->Draw(aContext, aSize, aRegion, aWhichFrame,
385 0 : aSamplingFilter, aSVGContext, aFlags, aOpacity);
386 : }
387 :
388 : // Check for tiling. If we need to tile then we need to create a
389 : // gfxCallbackDrawable to handle drawing for us.
390 8 : if (MustCreateSurface(aContext, aSize, aRegion, aFlags)) {
391 : // Create a temporary surface containing a single tile of this image.
392 : // GetFrame will call DrawSingleTile internally.
393 : DrawResult result;
394 0 : RefPtr<SourceSurface> surface;
395 0 : Tie(result, surface) =
396 0 : GetFrameInternal(aSize, aSVGContext, aWhichFrame, aFlags, aOpacity);
397 0 : if (!surface) {
398 0 : MOZ_ASSERT(result != DrawResult::SUCCESS);
399 0 : return result;
400 : }
401 :
402 : // Create a drawable from that surface.
403 : RefPtr<gfxSurfaceDrawable> drawable =
404 0 : new gfxSurfaceDrawable(surface, aSize);
405 :
406 : // Draw.
407 0 : gfxUtils::DrawPixelSnapped(aContext, drawable, SizeDouble(aSize), aRegion,
408 : SurfaceFormat::B8G8R8A8, aSamplingFilter,
409 0 : aOpacity);
410 :
411 0 : return result;
412 : }
413 :
414 : return DrawSingleTile(aContext, aSize, aRegion, aWhichFrame,
415 8 : aSamplingFilter, aSVGContext, aFlags, aOpacity);
416 : }
417 :
418 : DrawResult
419 8 : ClippedImage::DrawSingleTile(gfxContext* aContext,
420 : const nsIntSize& aSize,
421 : const ImageRegion& aRegion,
422 : uint32_t aWhichFrame,
423 : SamplingFilter aSamplingFilter,
424 : const Maybe<SVGImageContext>& aSVGContext,
425 : uint32_t aFlags,
426 : float aOpacity)
427 : {
428 8 : MOZ_ASSERT(!MustCreateSurface(aContext, aSize, aRegion, aFlags),
429 : "Shouldn't need to create a surface");
430 :
431 8 : gfxRect clip(mClip.x, mClip.y, mClip.width, mClip.height);
432 8 : nsIntSize size(aSize), innerSize(aSize);
433 8 : bool needScale = false;
434 8 : if (mSVGViewportSize && !mSVGViewportSize->IsEmpty()) {
435 0 : innerSize = *mSVGViewportSize;
436 0 : needScale = true;
437 16 : } else if (NS_SUCCEEDED(InnerImage()->GetWidth(&innerSize.width)) &&
438 8 : NS_SUCCEEDED(InnerImage()->GetHeight(&innerSize.height))) {
439 8 : needScale = true;
440 : } else {
441 0 : MOZ_ASSERT_UNREACHABLE(
442 : "If ShouldClip() led us to draw then we should never get here");
443 : }
444 :
445 8 : if (needScale) {
446 8 : double scaleX = aSize.width / clip.width;
447 8 : double scaleY = aSize.height / clip.height;
448 :
449 : // Map the clip and size to the scale requested by the caller.
450 8 : clip.Scale(scaleX, scaleY);
451 8 : size = innerSize;
452 8 : size.Scale(scaleX, scaleY);
453 : }
454 :
455 : // We restrict our drawing to only the clipping region, and translate so that
456 : // the clipping region is placed at the position the caller expects.
457 8 : ImageRegion region(aRegion);
458 8 : region.MoveBy(clip.x, clip.y);
459 8 : region = region.Intersect(clip);
460 :
461 16 : gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
462 8 : aContext->Multiply(gfxMatrix::Translation(-clip.x, -clip.y));
463 :
464 8 : auto unclipViewport = [&](const SVGImageContext& aOldContext) {
465 : // Map the viewport to the inner image. Note that we don't take the aSize
466 : // parameter of imgIContainer::Draw into account, just the clipping region.
467 : // The size in pixels at which the output will ultimately be drawn is
468 : // irrelevant here since the purpose of the SVG viewport size is to
469 : // determine what *region* of the SVG document will be drawn.
470 8 : SVGImageContext context(aOldContext);
471 16 : auto oldViewport = aOldContext.GetViewportSize();
472 8 : if (oldViewport) {
473 8 : CSSIntSize newViewport;
474 8 : newViewport.width =
475 24 : ceil(oldViewport->width * double(innerSize.width) / mClip.width);
476 8 : newViewport.height =
477 24 : ceil(oldViewport->height * double(innerSize.height) / mClip.height);
478 8 : context.SetViewportSize(Some(newViewport));
479 : }
480 16 : return context;
481 8 : };
482 :
483 8 : return InnerImage()->Draw(aContext, size, region,
484 : aWhichFrame, aSamplingFilter,
485 16 : aSVGContext.map(unclipViewport),
486 24 : aFlags, aOpacity);
487 : }
488 :
489 : NS_IMETHODIMP
490 0 : ClippedImage::RequestDiscard()
491 : {
492 : // We're very aggressive about discarding.
493 0 : mCachedSurface = nullptr;
494 :
495 0 : return InnerImage()->RequestDiscard();
496 : }
497 :
498 : NS_IMETHODIMP_(Orientation)
499 0 : ClippedImage::GetOrientation()
500 : {
501 : // XXX(seth): This should not actually be here; this is just to work around a
502 : // what appears to be a bug in MSVC's linker.
503 0 : return InnerImage()->GetOrientation();
504 : }
505 :
506 : nsIntSize
507 8 : ClippedImage::OptimalImageSizeForDest(const gfxSize& aDest,
508 : uint32_t aWhichFrame,
509 : SamplingFilter aSamplingFilter,
510 : uint32_t aFlags)
511 : {
512 8 : if (!ShouldClip()) {
513 0 : return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
514 0 : aSamplingFilter, aFlags);
515 : }
516 :
517 : int32_t imgWidth, imgHeight;
518 8 : bool needScale = false;
519 8 : bool forceUniformScaling = false;
520 8 : if (mSVGViewportSize && !mSVGViewportSize->IsEmpty()) {
521 0 : imgWidth = mSVGViewportSize->width;
522 0 : imgHeight = mSVGViewportSize->height;
523 0 : needScale = true;
524 0 : forceUniformScaling = (aFlags & imgIContainer::FLAG_FORCE_UNIFORM_SCALING);
525 16 : } else if (NS_SUCCEEDED(InnerImage()->GetWidth(&imgWidth)) &&
526 8 : NS_SUCCEEDED(InnerImage()->GetHeight(&imgHeight))) {
527 8 : needScale = true;
528 : }
529 :
530 8 : if (needScale) {
531 : // To avoid ugly sampling artifacts, ClippedImage needs the image size to
532 : // be chosen such that the clipping region lies on pixel boundaries.
533 :
534 : // First, we select a scale that's good for ClippedImage. An integer
535 : // multiple of the size of the clipping region is always fine.
536 8 : IntSize scale = IntSize::Ceil(aDest.width / mClip.width,
537 16 : aDest.height / mClip.height);
538 :
539 8 : if (forceUniformScaling) {
540 0 : scale.width = scale.height = max(scale.height, scale.width);
541 : }
542 :
543 : // Determine the size we'd prefer to render the inner image at, and ask the
544 : // inner image what size we should actually use.
545 8 : gfxSize desiredSize(imgWidth * scale.width, imgHeight * scale.height);
546 : nsIntSize innerDesiredSize =
547 8 : InnerImage()->OptimalImageSizeForDest(desiredSize, aWhichFrame,
548 8 : aSamplingFilter, aFlags);
549 :
550 : // To get our final result, we take the inner image's desired size and
551 : // determine how large the clipped region would be at that scale. (Again, we
552 : // ensure an integer multiple of the size of the clipping region.)
553 8 : IntSize finalScale = IntSize::Ceil(double(innerDesiredSize.width) / imgWidth,
554 16 : double(innerDesiredSize.height) / imgHeight);
555 8 : return mClip.Size() * finalScale;
556 : }
557 :
558 0 : MOZ_ASSERT(false,
559 : "If ShouldClip() led us to draw then we should never get here");
560 : return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
561 : aSamplingFilter, aFlags);
562 : }
563 :
564 : NS_IMETHODIMP_(nsIntRect)
565 0 : ClippedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
566 : {
567 0 : if (!ShouldClip()) {
568 0 : return InnerImage()->GetImageSpaceInvalidationRect(aRect);
569 : }
570 :
571 0 : nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
572 0 : rect = rect.Intersect(mClip);
573 0 : rect.MoveBy(-mClip.x, -mClip.y);
574 0 : return rect;
575 : }
576 :
577 : } // namespace image
578 : } // namespace mozilla
|