1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=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 /* utility code for drawing images as CSS borders, backgrounds, and shapes. */
8 
9 #include "nsImageRenderer.h"
10 
11 #include "mozilla/webrender/WebRenderAPI.h"
12 
13 #include "gfxContext.h"
14 #include "gfxDrawable.h"
15 #include "ImageOps.h"
16 #include "ImageRegion.h"
17 #include "mozilla/layers/RenderRootStateManager.h"
18 #include "mozilla/layers/StackingContextHelper.h"
19 #include "mozilla/layers/WebRenderLayerManager.h"
20 #include "nsContentUtils.h"
21 #include "nsCSSRendering.h"
22 #include "nsCSSRenderingGradients.h"
23 #include "nsDeviceContext.h"
24 #include "nsIFrame.h"
25 #include "nsLayoutUtils.h"
26 #include "nsStyleStructInlines.h"
27 #include "mozilla/StaticPrefs_image.h"
28 #include "mozilla/ISVGDisplayableFrame.h"
29 #include "mozilla/SVGIntegrationUtils.h"
30 #include "mozilla/SVGPaintServerFrame.h"
31 #include "mozilla/SVGObserverUtils.h"
32 
33 using namespace mozilla;
34 using namespace mozilla::gfx;
35 using namespace mozilla::image;
36 using namespace mozilla::layers;
37 
ComputeConcreteSize() const38 nsSize CSSSizeOrRatio::ComputeConcreteSize() const {
39   NS_ASSERTION(CanComputeConcreteSize(), "Cannot compute");
40   if (mHasWidth && mHasHeight) {
41     return nsSize(mWidth, mHeight);
42   }
43   if (mHasWidth) {
44     return nsSize(mWidth, mRatio.Inverted().ApplyTo(mWidth));
45   }
46 
47   MOZ_ASSERT(mHasHeight);
48   return nsSize(mRatio.ApplyTo(mHeight), mHeight);
49 }
50 
nsImageRenderer(nsIFrame * aForFrame,const StyleImage * aImage,uint32_t aFlags)51 nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
52                                  uint32_t aFlags)
53     : mForFrame(aForFrame),
54       mImage(&aImage->FinalImage()),
55       mImageResolution(aImage->GetResolution()),
56       mType(mImage->tag),
57       mImageContainer(nullptr),
58       mGradientData(nullptr),
59       mPaintServerFrame(nullptr),
60       mPrepareResult(ImgDrawResult::NOT_READY),
61       mSize(0, 0),
62       mFlags(aFlags),
63       mExtendMode(ExtendMode::CLAMP),
64       mMaskOp(StyleMaskMode::MatchSource) {}
65 
PrepareImage()66 bool nsImageRenderer::PrepareImage() {
67   if (mImage->IsNone()) {
68     mPrepareResult = ImgDrawResult::BAD_IMAGE;
69     return false;
70   }
71 
72   const bool isImageRequest = mImage->IsImageRequestType();
73   MOZ_ASSERT_IF(!isImageRequest, !mImage->GetImageRequest());
74   imgRequestProxy* request = nullptr;
75   if (isImageRequest) {
76     request = mImage->GetImageRequest();
77     if (!request) {
78       // request could be null here if the StyleImage refused
79       // to load a same-document URL, or the url was invalid, for example.
80       mPrepareResult = ImgDrawResult::BAD_IMAGE;
81       return false;
82     }
83   }
84 
85   if (!mImage->IsComplete()) {
86     MOZ_DIAGNOSTIC_ASSERT(isImageRequest);
87 
88     // Make sure the image is actually decoding.
89     bool frameComplete =
90         request->StartDecodingWithResult(imgIContainer::FLAG_ASYNC_NOTIFY);
91 
92     // Boost the loading priority since we know we want to draw the image.
93     if (mFlags & nsImageRenderer::FLAG_PAINTING_TO_WINDOW) {
94       request->BoostPriority(imgIRequest::CATEGORY_DISPLAY);
95     }
96 
97     // Check again to see if we finished.
98     // We cannot prepare the image for rendering if it is not fully loaded.
99     if (!frameComplete && !mImage->IsComplete()) {
100       uint32_t imageStatus = 0;
101       request->GetImageStatus(&imageStatus);
102       if (imageStatus & imgIRequest::STATUS_ERROR) {
103         mPrepareResult = ImgDrawResult::BAD_IMAGE;
104         return false;
105       }
106 
107       // Special case: If not errored, and we requested a sync decode, and the
108       // image has loaded, push on through because the Draw() will do a sync
109       // decode then.
110       const bool syncDecodeWillComplete =
111           (mFlags & FLAG_SYNC_DECODE_IMAGES) &&
112           (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE);
113       if (!syncDecodeWillComplete) {
114         mPrepareResult = ImgDrawResult::NOT_READY;
115         return false;
116       }
117     }
118   }
119 
120   if (isImageRequest) {
121     nsCOMPtr<imgIContainer> srcImage;
122     DebugOnly<nsresult> rv = request->GetImage(getter_AddRefs(srcImage));
123     MOZ_ASSERT(NS_SUCCEEDED(rv) && srcImage,
124                "If GetImage() is failing, mImage->IsComplete() "
125                "should have returned false");
126 
127     if (srcImage) {
128       srcImage = nsLayoutUtils::OrientImage(
129           srcImage, mForFrame->StyleVisibility()->mImageOrientation);
130     }
131 
132     if (!mImage->IsRect()) {
133       mImageContainer.swap(srcImage);
134     } else {
135       auto croprect = mImage->ComputeActualCropRect();
136       if (!croprect || croprect->mRect.IsEmpty()) {
137         // The cropped image has zero size
138         mPrepareResult = ImgDrawResult::BAD_IMAGE;
139         return false;
140       }
141       if (croprect->mIsEntireImage) {
142         // The cropped image is identical to the source image
143         mImageContainer.swap(srcImage);
144       } else {
145         nsCOMPtr<imgIContainer> subImage =
146             ImageOps::Clip(srcImage, croprect->mRect, Nothing());
147         mImageContainer.swap(subImage);
148       }
149     }
150     mPrepareResult = ImgDrawResult::SUCCESS;
151   } else if (mImage->IsGradient()) {
152     mGradientData = &*mImage->AsGradient();
153     mPrepareResult = ImgDrawResult::SUCCESS;
154   } else if (mImage->IsElement()) {
155     dom::Element* paintElement =  // may be null
156         SVGObserverUtils::GetAndObserveBackgroundImage(
157             mForFrame->FirstContinuation(), mImage->AsElement().AsAtom());
158     // If the referenced element is an <img>, <canvas>, or <video> element,
159     // prefer SurfaceFromElement as it's more reliable.
160     mImageElementSurface = nsLayoutUtils::SurfaceFromElement(paintElement);
161 
162     if (!mImageElementSurface.GetSourceSurface()) {
163       nsIFrame* paintServerFrame =
164           paintElement ? paintElement->GetPrimaryFrame() : nullptr;
165       // If there's no referenced frame, or the referenced frame is
166       // non-displayable SVG, then we have nothing valid to paint.
167       if (!paintServerFrame ||
168           (paintServerFrame->IsFrameOfType(nsIFrame::eSVG) &&
169            !static_cast<SVGPaintServerFrame*>(
170                do_QueryFrame(paintServerFrame)) &&
171            !static_cast<ISVGDisplayableFrame*>(
172                do_QueryFrame(paintServerFrame)))) {
173         mPrepareResult = ImgDrawResult::BAD_IMAGE;
174         return false;
175       }
176       mPaintServerFrame = paintServerFrame;
177     }
178 
179     mPrepareResult = ImgDrawResult::SUCCESS;
180   } else if (mImage->IsCrossFade()) {
181     // See bug 546052 - cross-fade implementation still being worked
182     // on.
183     mPrepareResult = ImgDrawResult::BAD_IMAGE;
184     return false;
185   } else {
186     MOZ_ASSERT(mImage->IsNone(), "Unknown image type?");
187   }
188 
189   return IsReady();
190 }
191 
ComputeIntrinsicSize()192 CSSSizeOrRatio nsImageRenderer::ComputeIntrinsicSize() {
193   NS_ASSERTION(IsReady(),
194                "Ensure PrepareImage() has returned true "
195                "before calling me");
196 
197   CSSSizeOrRatio result;
198   switch (mType) {
199     case StyleImage::Tag::Rect:
200     case StyleImage::Tag::Url: {
201       bool haveWidth, haveHeight;
202       CSSIntSize imageIntSize;
203       nsLayoutUtils::ComputeSizeForDrawing(mImageContainer, mImageResolution,
204                                            imageIntSize, result.mRatio,
205                                            haveWidth, haveHeight);
206       if (haveWidth) {
207         result.SetWidth(CSSPixel::ToAppUnits(imageIntSize.width));
208       }
209       if (haveHeight) {
210         result.SetHeight(CSSPixel::ToAppUnits(imageIntSize.height));
211       }
212 
213       // If we know the aspect ratio and one of the dimensions,
214       // we can compute the other missing width or height.
215       if (!haveHeight && haveWidth && result.mRatio) {
216         nscoord intrinsicHeight =
217             result.mRatio.Inverted().ApplyTo(imageIntSize.width);
218         result.SetHeight(nsPresContext::CSSPixelsToAppUnits(intrinsicHeight));
219       } else if (haveHeight && !haveWidth && result.mRatio) {
220         nscoord intrinsicWidth = result.mRatio.ApplyTo(imageIntSize.height);
221         result.SetWidth(nsPresContext::CSSPixelsToAppUnits(intrinsicWidth));
222       }
223 
224       break;
225     }
226     case StyleImage::Tag::Element: {
227       // XXX element() should have the width/height of the referenced element,
228       //     and that element's ratio, if it matches.  If it doesn't match, it
229       //     should have no width/height or ratio.  See element() in CSS images:
230       //     <http://dev.w3.org/csswg/css-images-4/#element-notation>.
231       //     Make sure to change nsStyleImageLayers::Size::DependsOnFrameSize
232       //     when fixing this!
233       if (mPaintServerFrame) {
234         // SVG images have no intrinsic size
235         if (!mPaintServerFrame->IsFrameOfType(nsIFrame::eSVG)) {
236           // The intrinsic image size for a generic nsIFrame paint server is
237           // the union of the border-box rects of all of its continuations,
238           // rounded to device pixels.
239           int32_t appUnitsPerDevPixel =
240               mForFrame->PresContext()->AppUnitsPerDevPixel();
241           result.SetSize(IntSizeToAppUnits(
242               SVGIntegrationUtils::GetContinuationUnionSize(mPaintServerFrame)
243                   .ToNearestPixels(appUnitsPerDevPixel),
244               appUnitsPerDevPixel));
245         }
246       } else {
247         NS_ASSERTION(mImageElementSurface.GetSourceSurface(),
248                      "Surface should be ready.");
249         IntSize surfaceSize = mImageElementSurface.mSize;
250         result.SetSize(
251             nsSize(nsPresContext::CSSPixelsToAppUnits(surfaceSize.width),
252                    nsPresContext::CSSPixelsToAppUnits(surfaceSize.height)));
253       }
254       break;
255     }
256     case StyleImage::Tag::ImageSet:
257       MOZ_FALLTHROUGH_ASSERT("image-set should be resolved already");
258     // Bug 546052 cross-fade not yet implemented.
259     case StyleImage::Tag::CrossFade:
260     // Per <http://dev.w3.org/csswg/css3-images/#gradients>, gradients have no
261     // intrinsic dimensions.
262     case StyleImage::Tag::Gradient:
263     case StyleImage::Tag::None:
264       break;
265   }
266 
267   return result;
268 }
269 
270 /* static */
ComputeConcreteSize(const CSSSizeOrRatio & aSpecifiedSize,const CSSSizeOrRatio & aIntrinsicSize,const nsSize & aDefaultSize)271 nsSize nsImageRenderer::ComputeConcreteSize(
272     const CSSSizeOrRatio& aSpecifiedSize, const CSSSizeOrRatio& aIntrinsicSize,
273     const nsSize& aDefaultSize) {
274   // The specified size is fully specified, just use that
275   if (aSpecifiedSize.IsConcrete()) {
276     return aSpecifiedSize.ComputeConcreteSize();
277   }
278 
279   MOZ_ASSERT(!aSpecifiedSize.mHasWidth || !aSpecifiedSize.mHasHeight);
280 
281   if (!aSpecifiedSize.mHasWidth && !aSpecifiedSize.mHasHeight) {
282     // no specified size, try using the intrinsic size
283     if (aIntrinsicSize.CanComputeConcreteSize()) {
284       return aIntrinsicSize.ComputeConcreteSize();
285     }
286 
287     if (aIntrinsicSize.mHasWidth) {
288       return nsSize(aIntrinsicSize.mWidth, aDefaultSize.height);
289     }
290     if (aIntrinsicSize.mHasHeight) {
291       return nsSize(aDefaultSize.width, aIntrinsicSize.mHeight);
292     }
293 
294     // couldn't use the intrinsic size either, revert to using the default size
295     return ComputeConstrainedSize(aDefaultSize, aIntrinsicSize.mRatio, CONTAIN);
296   }
297 
298   MOZ_ASSERT(aSpecifiedSize.mHasWidth || aSpecifiedSize.mHasHeight);
299 
300   // The specified height is partial, try to compute the missing part.
301   if (aSpecifiedSize.mHasWidth) {
302     nscoord height;
303     if (aIntrinsicSize.HasRatio()) {
304       height = aIntrinsicSize.mRatio.Inverted().ApplyTo(aSpecifiedSize.mWidth);
305     } else if (aIntrinsicSize.mHasHeight) {
306       height = aIntrinsicSize.mHeight;
307     } else {
308       height = aDefaultSize.height;
309     }
310     return nsSize(aSpecifiedSize.mWidth, height);
311   }
312 
313   MOZ_ASSERT(aSpecifiedSize.mHasHeight);
314   nscoord width;
315   if (aIntrinsicSize.HasRatio()) {
316     width = aIntrinsicSize.mRatio.ApplyTo(aSpecifiedSize.mHeight);
317   } else if (aIntrinsicSize.mHasWidth) {
318     width = aIntrinsicSize.mWidth;
319   } else {
320     width = aDefaultSize.width;
321   }
322   return nsSize(width, aSpecifiedSize.mHeight);
323 }
324 
325 /* static */
ComputeConstrainedSize(const nsSize & aConstrainingSize,const AspectRatio & aIntrinsicRatio,FitType aFitType)326 nsSize nsImageRenderer::ComputeConstrainedSize(
327     const nsSize& aConstrainingSize, const AspectRatio& aIntrinsicRatio,
328     FitType aFitType) {
329   if (!aIntrinsicRatio) {
330     return aConstrainingSize;
331   }
332 
333   // Suppose we're doing a "contain" fit. If the image's aspect ratio has a
334   // "fatter" shape than the constraint area, then we need to use the
335   // constraint area's full width, and we need to use the aspect ratio to
336   // produce a height. On the other hand, if the aspect ratio is "skinnier", we
337   // use the constraint area's full height, and we use the aspect ratio to
338   // produce a width. (If instead we're doing a "cover" fit, then it can easily
339   // be seen that we should do precisely the opposite.)
340   //
341   // We check if the image's aspect ratio is "fatter" than the constraint area
342   // by simply applying the aspect ratio to the constraint area's height, to
343   // produce a "hypothetical width", and we check whether that
344   // aspect-ratio-provided "hypothetical width" is wider than the constraint
345   // area's actual width. If it is, then the aspect ratio is fatter than the
346   // constraint area.
347   //
348   // This is equivalent to the more descriptive alternative:
349   //
350   //   AspectRatio::FromSize(aConstrainingSize) < aIntrinsicRatio
351   //
352   // But gracefully handling the case where one of the two dimensions from
353   // aConstrainingSize is zero. This is easy to prove since:
354   //
355   //   aConstrainingSize.width / aConstrainingSize.height < aIntrinsicRatio
356   //
357   // Is trivially equivalent to:
358   //
359   //   aIntrinsicRatio.width < aIntrinsicRatio * aConstrainingSize.height
360   //
361   // For the cases where height is not zero.
362   //
363   // We use float math here to avoid losing precision for very large backgrounds
364   // since we use saturating nscoord math otherwise.
365   const float constraintWidth = float(aConstrainingSize.width);
366   const float hypotheticalWidth =
367       aIntrinsicRatio.ApplyToFloat(aConstrainingSize.height);
368 
369   nsSize size;
370   if ((aFitType == CONTAIN) == (constraintWidth < hypotheticalWidth)) {
371     size.width = aConstrainingSize.width;
372     size.height = aIntrinsicRatio.Inverted().ApplyTo(aConstrainingSize.width);
373     // If we're reducing the size by less than one css pixel, then just use the
374     // constraining size.
375     if (aFitType == CONTAIN &&
376         aConstrainingSize.height - size.height < AppUnitsPerCSSPixel()) {
377       size.height = aConstrainingSize.height;
378     }
379   } else {
380     size.height = aConstrainingSize.height;
381     size.width = aIntrinsicRatio.ApplyTo(aConstrainingSize.height);
382     if (aFitType == CONTAIN &&
383         aConstrainingSize.width - size.width < AppUnitsPerCSSPixel()) {
384       size.width = aConstrainingSize.width;
385     }
386   }
387   return size;
388 }
389 
390 /**
391  * mSize is the image's "preferred" size for this particular rendering, while
392  * the drawn (aka concrete) size is the actual rendered size after accounting
393  * for background-size etc..  The preferred size is most often the image's
394  * intrinsic dimensions.  But for images with incomplete intrinsic dimensions,
395  * the preferred size varies, depending on the specified and default sizes, see
396  * nsImageRenderer::Compute*Size.
397  *
398  * This distinction is necessary because the components of a vector image are
399  * specified with respect to its preferred size for a rendering situation, not
400  * to its actual rendered size.  For example, consider a 4px wide background
401  * vector image with no height which contains a left-aligned
402  * 2px wide black rectangle with height 100%.  If the background-size width is
403  * auto (or 4px), the vector image will render 4px wide, and the black rectangle
404  * will be 2px wide.  If the background-size width is 8px, the vector image will
405  * render 8px wide, and the black rectangle will be 4px wide -- *not* 2px wide.
406  * In both cases mSize.width will be 4px; but in the first case the returned
407  * width will be 4px, while in the second case the returned width will be 8px.
408  */
SetPreferredSize(const CSSSizeOrRatio & aIntrinsicSize,const nsSize & aDefaultSize)409 void nsImageRenderer::SetPreferredSize(const CSSSizeOrRatio& aIntrinsicSize,
410                                        const nsSize& aDefaultSize) {
411   mSize.width =
412       aIntrinsicSize.mHasWidth ? aIntrinsicSize.mWidth : aDefaultSize.width;
413   mSize.height =
414       aIntrinsicSize.mHasHeight ? aIntrinsicSize.mHeight : aDefaultSize.height;
415 }
416 
417 // Convert from nsImageRenderer flags to the flags we want to use for drawing in
418 // the imgIContainer namespace.
ConvertImageRendererToDrawFlags(uint32_t aImageRendererFlags)419 static uint32_t ConvertImageRendererToDrawFlags(uint32_t aImageRendererFlags) {
420   uint32_t drawFlags = imgIContainer::FLAG_NONE;
421   if (aImageRendererFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) {
422     drawFlags |= imgIContainer::FLAG_SYNC_DECODE;
423   }
424   if (aImageRendererFlags & (nsImageRenderer::FLAG_PAINTING_TO_WINDOW |
425                              nsImageRenderer::FLAG_HIGH_QUALITY_SCALING)) {
426     drawFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
427   }
428   return drawFlags;
429 }
430 
Draw(nsPresContext * aPresContext,gfxContext & aRenderingContext,const nsRect & aDirtyRect,const nsRect & aDest,const nsRect & aFill,const nsPoint & aAnchor,const nsSize & aRepeatSize,const CSSIntRect & aSrc,float aOpacity)431 ImgDrawResult nsImageRenderer::Draw(nsPresContext* aPresContext,
432                                     gfxContext& aRenderingContext,
433                                     const nsRect& aDirtyRect,
434                                     const nsRect& aDest, const nsRect& aFill,
435                                     const nsPoint& aAnchor,
436                                     const nsSize& aRepeatSize,
437                                     const CSSIntRect& aSrc, float aOpacity) {
438   if (!IsReady()) {
439     MOZ_ASSERT_UNREACHABLE(
440         "Ensure PrepareImage() has returned true before "
441         "calling me");
442     return ImgDrawResult::TEMPORARY_ERROR;
443   }
444 
445   if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
446       mSize.height <= 0) {
447     return ImgDrawResult::SUCCESS;
448   }
449 
450   SamplingFilter samplingFilter =
451       nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
452   ImgDrawResult result = ImgDrawResult::SUCCESS;
453   RefPtr<gfxContext> ctx = &aRenderingContext;
454   IntRect tmpDTRect;
455 
456   if (ctx->CurrentOp() != CompositionOp::OP_OVER ||
457       mMaskOp == StyleMaskMode::Luminance) {
458     gfxRect clipRect = ctx->GetClipExtents(gfxContext::eDeviceSpace);
459     tmpDTRect = RoundedOut(ToRect(clipRect));
460     if (tmpDTRect.IsEmpty()) {
461       return ImgDrawResult::SUCCESS;
462     }
463     RefPtr<DrawTarget> tempDT =
464         gfxPlatform::GetPlatform()->CreateSimilarSoftwareDrawTarget(
465             ctx->GetDrawTarget(), tmpDTRect.Size(), SurfaceFormat::B8G8R8A8);
466     if (!tempDT || !tempDT->IsValid()) {
467       gfxDevCrash(LogReason::InvalidContext)
468           << "ImageRenderer::Draw problem " << gfx::hexa(tempDT);
469       return ImgDrawResult::TEMPORARY_ERROR;
470     }
471     tempDT->SetTransform(ctx->GetDrawTarget()->GetTransform() *
472                          Matrix::Translation(-tmpDTRect.TopLeft()));
473     ctx = gfxContext::CreatePreservingTransformOrNull(tempDT);
474     if (!ctx) {
475       gfxDevCrash(LogReason::InvalidContext)
476           << "ImageRenderer::Draw problem " << gfx::hexa(tempDT);
477       return ImgDrawResult::TEMPORARY_ERROR;
478     }
479   }
480 
481   switch (mType) {
482     case StyleImage::Tag::Rect:
483     case StyleImage::Tag::Url: {
484       result = nsLayoutUtils::DrawBackgroundImage(
485           *ctx, mForFrame, aPresContext, mImageContainer, samplingFilter, aDest,
486           aFill, aRepeatSize, aAnchor, aDirtyRect,
487           ConvertImageRendererToDrawFlags(mFlags), mExtendMode, aOpacity);
488       break;
489     }
490     case StyleImage::Tag::Gradient: {
491       nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
492           aPresContext, mForFrame->Style(), *mGradientData, mSize);
493 
494       renderer.Paint(*ctx, aDest, aFill, aRepeatSize, aSrc, aDirtyRect,
495                      aOpacity);
496       break;
497     }
498     case StyleImage::Tag::Element: {
499       RefPtr<gfxDrawable> drawable = DrawableForElement(aDest, *ctx);
500       if (!drawable) {
501         NS_WARNING("Could not create drawable for element");
502         return ImgDrawResult::TEMPORARY_ERROR;
503       }
504 
505       nsCOMPtr<imgIContainer> image(ImageOps::CreateFromDrawable(drawable));
506       result = nsLayoutUtils::DrawImage(
507           *ctx, mForFrame->Style(), aPresContext, image, samplingFilter, aDest,
508           aFill, aAnchor, aDirtyRect, ConvertImageRendererToDrawFlags(mFlags),
509           aOpacity);
510       break;
511     }
512     case StyleImage::Tag::ImageSet:
513       MOZ_FALLTHROUGH_ASSERT("image-set should be resolved already");
514     // See bug 546052 - cross-fade implementation still being worked
515     // on.
516     case StyleImage::Tag::CrossFade:
517     case StyleImage::Tag::None:
518       break;
519   }
520 
521   if (!tmpDTRect.IsEmpty()) {
522     DrawTarget* dt = aRenderingContext.GetDrawTarget();
523     Matrix oldTransform = dt->GetTransform();
524     dt->SetTransform(Matrix());
525     if (mMaskOp == StyleMaskMode::Luminance) {
526       RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->IntoLuminanceSource(
527           LuminanceType::LUMINANCE, 1.0f);
528       dt->MaskSurface(ColorPattern(DeviceColor(0, 0, 0, 1.0f)), surf,
529                       tmpDTRect.TopLeft(),
530                       DrawOptions(1.0f, aRenderingContext.CurrentOp()));
531     } else {
532       RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->Snapshot();
533       dt->DrawSurface(
534           surf,
535           Rect(tmpDTRect.x, tmpDTRect.y, tmpDTRect.width, tmpDTRect.height),
536           Rect(0, 0, tmpDTRect.width, tmpDTRect.height),
537           DrawSurfaceOptions(SamplingFilter::POINT),
538           DrawOptions(1.0f, aRenderingContext.CurrentOp()));
539     }
540 
541     dt->SetTransform(oldTransform);
542   }
543 
544   if (!mImage->IsComplete()) {
545     result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
546   }
547 
548   return result;
549 }
550 
BuildWebRenderDisplayItems(nsPresContext * aPresContext,mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const mozilla::layers::StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayItem * aItem,const nsRect & aDirtyRect,const nsRect & aDest,const nsRect & aFill,const nsPoint & aAnchor,const nsSize & aRepeatSize,const CSSIntRect & aSrc,float aOpacity)551 ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems(
552     nsPresContext* aPresContext, mozilla::wr::DisplayListBuilder& aBuilder,
553     mozilla::wr::IpcResourceUpdateQueue& aResources,
554     const mozilla::layers::StackingContextHelper& aSc,
555     mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
556     const nsRect& aDirtyRect, const nsRect& aDest, const nsRect& aFill,
557     const nsPoint& aAnchor, const nsSize& aRepeatSize, const CSSIntRect& aSrc,
558     float aOpacity) {
559   if (!IsReady()) {
560     MOZ_ASSERT_UNREACHABLE(
561         "Ensure PrepareImage() has returned true before "
562         "calling me");
563     return ImgDrawResult::NOT_READY;
564   }
565 
566   if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
567       mSize.height <= 0) {
568     return ImgDrawResult::SUCCESS;
569   }
570 
571   ImgDrawResult drawResult = ImgDrawResult::SUCCESS;
572   switch (mType) {
573     case StyleImage::Tag::Gradient: {
574       nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
575           aPresContext, mForFrame->Style(), *mGradientData, mSize);
576 
577       renderer.BuildWebRenderDisplayItems(aBuilder, aSc, aDest, aFill,
578                                           aRepeatSize, aSrc,
579                                           !aItem->BackfaceIsHidden(), aOpacity);
580       break;
581     }
582     case StyleImage::Tag::Rect:
583     case StyleImage::Tag::Url: {
584       ExtendMode extendMode = mExtendMode;
585       if (aDest.Contains(aFill)) {
586         extendMode = ExtendMode::CLAMP;
587       }
588 
589       uint32_t containerFlags = imgIContainer::FLAG_ASYNC_NOTIFY;
590       if (mFlags & (nsImageRenderer::FLAG_PAINTING_TO_WINDOW |
591                     nsImageRenderer::FLAG_HIGH_QUALITY_SCALING)) {
592         containerFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
593       }
594       if (mFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) {
595         containerFlags |= imgIContainer::FLAG_SYNC_DECODE;
596       }
597       if (extendMode == ExtendMode::CLAMP &&
598           StaticPrefs::image_svg_blob_image() &&
599           mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
600         containerFlags |= imgIContainer::FLAG_RECORD_BLOB;
601       }
602 
603       CSSIntSize destCSSSize{
604           nsPresContext::AppUnitsToIntCSSPixels(aDest.width),
605           nsPresContext::AppUnitsToIntCSSPixels(aDest.height)};
606 
607       Maybe<SVGImageContext> svgContext(
608           Some(SVGImageContext(Some(destCSSSize))));
609       Maybe<ImageIntRegion> region;
610 
611       const int32_t appUnitsPerDevPixel =
612           mForFrame->PresContext()->AppUnitsPerDevPixel();
613       LayoutDeviceRect destRect =
614           LayoutDeviceRect::FromAppUnits(aDest, appUnitsPerDevPixel);
615       LayoutDeviceRect clipRect =
616           LayoutDeviceRect::FromAppUnits(aFill, appUnitsPerDevPixel);
617       auto stretchSize = wr::ToLayoutSize(destRect.Size());
618 
619       gfx::IntSize decodeSize =
620           nsLayoutUtils::ComputeImageContainerDrawingParameters(
621               mImageContainer, mForFrame, destRect, clipRect, aSc,
622               containerFlags, svgContext, region);
623 
624       if (extendMode != ExtendMode::CLAMP) {
625         region = Nothing();
626       }
627 
628       RefPtr<layers::ImageContainer> container;
629       drawResult = mImageContainer->GetImageContainerAtSize(
630           aManager->LayerManager(), decodeSize, svgContext, region,
631           containerFlags, getter_AddRefs(container));
632       if (!container) {
633         NS_WARNING("Failed to get image container");
634         break;
635       }
636 
637       if (containerFlags & imgIContainer::FLAG_RECORD_BLOB) {
638         MOZ_ASSERT(extendMode == ExtendMode::CLAMP);
639         aManager->CommandBuilder().PushBlobImage(
640             aItem, container, aBuilder, aResources, clipRect, clipRect);
641         break;
642       }
643 
644       mozilla::wr::ImageRendering rendering = wr::ToImageRendering(
645           nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame()));
646       gfx::IntSize size;
647       Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(
648           aItem, container, aBuilder, aResources, rendering, aSc, size,
649           Nothing());
650 
651       if (key.isNothing()) {
652         break;
653       }
654 
655       wr::LayoutRect dest = wr::ToLayoutRect(destRect);
656       wr::LayoutRect clip = wr::ToLayoutRect(clipRect);
657 
658       if (extendMode == ExtendMode::CLAMP) {
659         // The image is not repeating. Just push as a regular image.
660         aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering,
661                            key.value(), true,
662                            wr::ColorF{1.0f, 1.0f, 1.0f, aOpacity});
663       } else {
664         nsPoint firstTilePos = nsLayoutUtils::GetBackgroundFirstTilePos(
665             aDest.TopLeft(), aFill.TopLeft(), aRepeatSize);
666         LayoutDeviceRect fillRect = LayoutDeviceRect::FromAppUnits(
667             nsRect(firstTilePos.x, firstTilePos.y,
668                    aFill.XMost() - firstTilePos.x,
669                    aFill.YMost() - firstTilePos.y),
670             appUnitsPerDevPixel);
671         wr::LayoutRect fill = wr::ToLayoutRect(fillRect);
672 
673         switch (extendMode) {
674           case ExtendMode::REPEAT_Y:
675             fill.min.x = dest.min.x;
676             fill.max.x = dest.max.x;
677             stretchSize.width = dest.width();
678             break;
679           case ExtendMode::REPEAT_X:
680             fill.min.y = dest.min.y;
681             fill.max.y = dest.max.y;
682             stretchSize.height = dest.height();
683             break;
684           default:
685             break;
686         }
687 
688         LayoutDeviceSize gapSize = LayoutDeviceSize::FromAppUnits(
689             aRepeatSize - aDest.Size(), appUnitsPerDevPixel);
690 
691         aBuilder.PushRepeatingImage(fill, clip, !aItem->BackfaceIsHidden(),
692                                     stretchSize, wr::ToLayoutSize(gapSize),
693                                     rendering, key.value(), true,
694                                     wr::ColorF{1.0f, 1.0f, 1.0f, aOpacity});
695       }
696       break;
697     }
698     default:
699       break;
700   }
701 
702   if (!mImage->IsComplete() && drawResult == ImgDrawResult::SUCCESS) {
703     return ImgDrawResult::SUCCESS_NOT_COMPLETE;
704   }
705   return drawResult;
706 }
707 
DrawableForElement(const nsRect & aImageRect,gfxContext & aContext)708 already_AddRefed<gfxDrawable> nsImageRenderer::DrawableForElement(
709     const nsRect& aImageRect, gfxContext& aContext) {
710   NS_ASSERTION(mType == StyleImage::Tag::Element,
711                "DrawableForElement only makes sense if backed by an element");
712   if (mPaintServerFrame) {
713     // XXX(seth): In order to not pass FLAG_SYNC_DECODE_IMAGES here,
714     // DrawableFromPaintServer would have to return a ImgDrawResult indicating
715     // whether any images could not be painted because they weren't fully
716     // decoded. Even always passing FLAG_SYNC_DECODE_IMAGES won't eliminate all
717     // problems, as it won't help if there are image which haven't finished
718     // loading, but it's better than nothing.
719     int32_t appUnitsPerDevPixel =
720         mForFrame->PresContext()->AppUnitsPerDevPixel();
721     nsRect destRect = aImageRect - aImageRect.TopLeft();
722     nsIntSize roundedOut = destRect.ToOutsidePixels(appUnitsPerDevPixel).Size();
723     IntSize imageSize(roundedOut.width, roundedOut.height);
724 
725     RefPtr<gfxDrawable> drawable;
726 
727     SurfaceFormat format = aContext.GetDrawTarget()->GetFormat();
728     // Don't allow creating images that are too big
729     if (aContext.GetDrawTarget()->CanCreateSimilarDrawTarget(imageSize,
730                                                              format)) {
731       drawable = SVGIntegrationUtils::DrawableFromPaintServer(
732           mPaintServerFrame, mForFrame, mSize, imageSize,
733           aContext.GetDrawTarget(), aContext.CurrentMatrixDouble(),
734           SVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES);
735     }
736 
737     return drawable.forget();
738   }
739   NS_ASSERTION(mImageElementSurface.GetSourceSurface(),
740                "Surface should be ready.");
741   RefPtr<gfxDrawable> drawable =
742       new gfxSurfaceDrawable(mImageElementSurface.GetSourceSurface().get(),
743                              mImageElementSurface.mSize);
744   return drawable.forget();
745 }
746 
DrawLayer(nsPresContext * aPresContext,gfxContext & aRenderingContext,const nsRect & aDest,const nsRect & aFill,const nsPoint & aAnchor,const nsRect & aDirty,const nsSize & aRepeatSize,float aOpacity)747 ImgDrawResult nsImageRenderer::DrawLayer(
748     nsPresContext* aPresContext, gfxContext& aRenderingContext,
749     const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor,
750     const nsRect& aDirty, const nsSize& aRepeatSize, float aOpacity) {
751   if (!IsReady()) {
752     MOZ_ASSERT_UNREACHABLE(
753         "Ensure PrepareImage() has returned true before "
754         "calling me");
755     return ImgDrawResult::TEMPORARY_ERROR;
756   }
757 
758   if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
759       mSize.height <= 0) {
760     return ImgDrawResult::SUCCESS;
761   }
762 
763   return Draw(
764       aPresContext, aRenderingContext, aDirty, aDest, aFill, aAnchor,
765       aRepeatSize,
766       CSSIntRect(0, 0, nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
767                  nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
768       aOpacity);
769 }
770 
BuildWebRenderDisplayItemsForLayer(nsPresContext * aPresContext,mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const mozilla::layers::StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayItem * aItem,const nsRect & aDest,const nsRect & aFill,const nsPoint & aAnchor,const nsRect & aDirty,const nsSize & aRepeatSize,float aOpacity)771 ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItemsForLayer(
772     nsPresContext* aPresContext, mozilla::wr::DisplayListBuilder& aBuilder,
773     mozilla::wr::IpcResourceUpdateQueue& aResources,
774     const mozilla::layers::StackingContextHelper& aSc,
775     mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
776     const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor,
777     const nsRect& aDirty, const nsSize& aRepeatSize, float aOpacity) {
778   if (!IsReady()) {
779     MOZ_ASSERT_UNREACHABLE(
780         "Ensure PrepareImage() has returned true before "
781         "calling me");
782     return mPrepareResult;
783   }
784 
785   CSSIntRect srcRect(0, 0, nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
786                      nsPresContext::AppUnitsToIntCSSPixels(mSize.height));
787 
788   if (aDest.IsEmpty() || aFill.IsEmpty() || srcRect.IsEmpty()) {
789     return ImgDrawResult::SUCCESS;
790   }
791   return BuildWebRenderDisplayItems(aPresContext, aBuilder, aResources, aSc,
792                                     aManager, aItem, aDirty, aDest, aFill,
793                                     aAnchor, aRepeatSize, srcRect, aOpacity);
794 }
795 
796 /**
797  * Compute the size and position of the master copy of the image. I.e., a single
798  * tile used to fill the dest rect.
799  * aFill The destination rect to be filled
800  * aHFill and aVFill are the repeat patterns for the component -
801  * StyleBorderImageRepeat - i.e., how a tiling unit is used to fill aFill
802  * aUnitSize The size of the source rect in dest coords.
803  */
ComputeTile(nsRect & aFill,StyleBorderImageRepeat aHFill,StyleBorderImageRepeat aVFill,const nsSize & aUnitSize,nsSize & aRepeatSize)804 static nsRect ComputeTile(nsRect& aFill, StyleBorderImageRepeat aHFill,
805                           StyleBorderImageRepeat aVFill,
806                           const nsSize& aUnitSize, nsSize& aRepeatSize) {
807   nsRect tile;
808   switch (aHFill) {
809     case StyleBorderImageRepeat::Stretch:
810       tile.x = aFill.x;
811       tile.width = aFill.width;
812       aRepeatSize.width = tile.width;
813       break;
814     case StyleBorderImageRepeat::Repeat:
815       tile.x = aFill.x + aFill.width / 2 - aUnitSize.width / 2;
816       tile.width = aUnitSize.width;
817       aRepeatSize.width = tile.width;
818       break;
819     case StyleBorderImageRepeat::Round:
820       tile.x = aFill.x;
821       tile.width =
822           nsCSSRendering::ComputeRoundedSize(aUnitSize.width, aFill.width);
823       aRepeatSize.width = tile.width;
824       break;
825     case StyleBorderImageRepeat::Space: {
826       nscoord space;
827       aRepeatSize.width = nsCSSRendering::ComputeBorderSpacedRepeatSize(
828           aUnitSize.width, aFill.width, space);
829       tile.x = aFill.x + space;
830       tile.width = aUnitSize.width;
831       aFill.x = tile.x;
832       aFill.width = aFill.width - space * 2;
833     } break;
834     default:
835       MOZ_ASSERT_UNREACHABLE("unrecognized border-image fill style");
836   }
837 
838   switch (aVFill) {
839     case StyleBorderImageRepeat::Stretch:
840       tile.y = aFill.y;
841       tile.height = aFill.height;
842       aRepeatSize.height = tile.height;
843       break;
844     case StyleBorderImageRepeat::Repeat:
845       tile.y = aFill.y + aFill.height / 2 - aUnitSize.height / 2;
846       tile.height = aUnitSize.height;
847       aRepeatSize.height = tile.height;
848       break;
849     case StyleBorderImageRepeat::Round:
850       tile.y = aFill.y;
851       tile.height =
852           nsCSSRendering::ComputeRoundedSize(aUnitSize.height, aFill.height);
853       aRepeatSize.height = tile.height;
854       break;
855     case StyleBorderImageRepeat::Space: {
856       nscoord space;
857       aRepeatSize.height = nsCSSRendering::ComputeBorderSpacedRepeatSize(
858           aUnitSize.height, aFill.height, space);
859       tile.y = aFill.y + space;
860       tile.height = aUnitSize.height;
861       aFill.y = tile.y;
862       aFill.height = aFill.height - space * 2;
863     } break;
864     default:
865       MOZ_ASSERT_UNREACHABLE("unrecognized border-image fill style");
866   }
867 
868   return tile;
869 }
870 
871 /**
872  * Returns true if the given set of arguments will require the tiles which fill
873  * the dest rect to be scaled from the source tile. See comment on ComputeTile
874  * for argument descriptions.
875  */
RequiresScaling(const nsRect & aFill,StyleBorderImageRepeat aHFill,StyleBorderImageRepeat aVFill,const nsSize & aUnitSize)876 static bool RequiresScaling(const nsRect& aFill, StyleBorderImageRepeat aHFill,
877                             StyleBorderImageRepeat aVFill,
878                             const nsSize& aUnitSize) {
879   // If we have no tiling in either direction, we can skip the intermediate
880   // scaling step.
881   return (aHFill != StyleBorderImageRepeat::Stretch ||
882           aVFill != StyleBorderImageRepeat::Stretch) &&
883          (aUnitSize.width != aFill.width || aUnitSize.height != aFill.height);
884 }
885 
DrawBorderImageComponent(nsPresContext * aPresContext,gfxContext & aRenderingContext,const nsRect & aDirtyRect,const nsRect & aFill,const CSSIntRect & aSrc,StyleBorderImageRepeat aHFill,StyleBorderImageRepeat aVFill,const nsSize & aUnitSize,uint8_t aIndex,const Maybe<nsSize> & aSVGViewportSize,const bool aHasIntrinsicRatio)886 ImgDrawResult nsImageRenderer::DrawBorderImageComponent(
887     nsPresContext* aPresContext, gfxContext& aRenderingContext,
888     const nsRect& aDirtyRect, const nsRect& aFill, const CSSIntRect& aSrc,
889     StyleBorderImageRepeat aHFill, StyleBorderImageRepeat aVFill,
890     const nsSize& aUnitSize, uint8_t aIndex,
891     const Maybe<nsSize>& aSVGViewportSize, const bool aHasIntrinsicRatio) {
892   if (!IsReady()) {
893     MOZ_ASSERT_UNREACHABLE(
894         "Ensure PrepareImage() has returned true before "
895         "calling me");
896     return ImgDrawResult::BAD_ARGS;
897   }
898 
899   if (aFill.IsEmpty() || aSrc.IsEmpty()) {
900     return ImgDrawResult::SUCCESS;
901   }
902 
903   const bool isRequestBacked =
904       mType == StyleImage::Tag::Url || mType == StyleImage::Tag::Rect;
905   MOZ_ASSERT(isRequestBacked == mImage->IsImageRequestType());
906 
907   if (isRequestBacked || mType == StyleImage::Tag::Element) {
908     nsCOMPtr<imgIContainer> subImage;
909 
910     // To draw one portion of an image into a border component, we stretch that
911     // portion to match the size of that border component and then draw onto.
912     // However, preserveAspectRatio attribute of a SVG image may break this
913     // rule. To get correct rendering result, we add
914     // FLAG_FORCE_PRESERVEASPECTRATIO_NONE flag here, to tell mImage to ignore
915     // preserveAspectRatio attribute, and always do non-uniform stretch.
916     uint32_t drawFlags = ConvertImageRendererToDrawFlags(mFlags) |
917                          imgIContainer::FLAG_FORCE_PRESERVEASPECTRATIO_NONE;
918     // For those SVG image sources which don't have fixed aspect ratio (i.e.
919     // without viewport size and viewBox), we should scale the source uniformly
920     // after the viewport size is decided by "Default Sizing Algorithm".
921     if (!aHasIntrinsicRatio) {
922       drawFlags = drawFlags | imgIContainer::FLAG_FORCE_UNIFORM_SCALING;
923     }
924     // Retrieve or create the subimage we'll draw.
925     nsIntRect srcRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height);
926     if (isRequestBacked) {
927       CachedBorderImageData* cachedData =
928           mForFrame->GetProperty(nsIFrame::CachedBorderImageDataProperty());
929       if (!cachedData) {
930         cachedData = new CachedBorderImageData();
931         mForFrame->AddProperty(nsIFrame::CachedBorderImageDataProperty(),
932                                cachedData);
933       }
934       if (!(subImage = cachedData->GetSubImage(aIndex))) {
935         subImage = ImageOps::Clip(mImageContainer, srcRect, aSVGViewportSize);
936         cachedData->SetSubImage(aIndex, subImage);
937       }
938     } else {
939       // This path, for eStyleImageType_Element, is currently slower than it
940       // needs to be because we don't cache anything. (In particular, if we have
941       // to draw to a temporary surface inside ClippedImage, we don't cache that
942       // temporary surface since we immediately throw the ClippedImage we create
943       // here away.) However, if we did cache, we'd need to know when to
944       // invalidate that cache, and it's not clear that it's worth the trouble
945       // since using border-image with -moz-element is rare.
946 
947       RefPtr<gfxDrawable> drawable =
948           DrawableForElement(nsRect(nsPoint(), mSize), aRenderingContext);
949       if (!drawable) {
950         NS_WARNING("Could not create drawable for element");
951         return ImgDrawResult::TEMPORARY_ERROR;
952       }
953 
954       nsCOMPtr<imgIContainer> image(ImageOps::CreateFromDrawable(drawable));
955       subImage = ImageOps::Clip(image, srcRect, aSVGViewportSize);
956     }
957 
958     MOZ_ASSERT(!aSVGViewportSize ||
959                subImage->GetType() == imgIContainer::TYPE_VECTOR);
960 
961     SamplingFilter samplingFilter =
962         nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
963 
964     if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
965       ImgDrawResult result = nsLayoutUtils::DrawSingleImage(
966           aRenderingContext, aPresContext, subImage, samplingFilter, aFill,
967           aDirtyRect, /* no SVGImageContext */ Nothing(), drawFlags);
968 
969       if (!mImage->IsComplete()) {
970         result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
971       }
972 
973       return result;
974     }
975 
976     nsSize repeatSize;
977     nsRect fillRect(aFill);
978     nsRect tile = ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize);
979 
980     ImgDrawResult result = nsLayoutUtils::DrawBackgroundImage(
981         aRenderingContext, mForFrame, aPresContext, subImage, samplingFilter,
982         tile, fillRect, repeatSize, tile.TopLeft(), aDirtyRect, drawFlags,
983         ExtendMode::CLAMP, 1.0);
984 
985     if (!mImage->IsComplete()) {
986       result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
987     }
988 
989     return result;
990   }
991 
992   nsSize repeatSize(aFill.Size());
993   nsRect fillRect(aFill);
994   nsRect destTile =
995       RequiresScaling(fillRect, aHFill, aVFill, aUnitSize)
996           ? ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize)
997           : fillRect;
998 
999   return Draw(aPresContext, aRenderingContext, aDirtyRect, destTile, fillRect,
1000               destTile.TopLeft(), repeatSize, aSrc);
1001 }
1002 
DrawShapeImage(nsPresContext * aPresContext,gfxContext & aRenderingContext)1003 ImgDrawResult nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext,
1004                                               gfxContext& aRenderingContext) {
1005   if (!IsReady()) {
1006     MOZ_ASSERT_UNREACHABLE(
1007         "Ensure PrepareImage() has returned true before "
1008         "calling me");
1009     return ImgDrawResult::NOT_READY;
1010   }
1011 
1012   if (mSize.width <= 0 || mSize.height <= 0) {
1013     return ImgDrawResult::SUCCESS;
1014   }
1015 
1016   if (mImage->IsImageRequestType()) {
1017     uint32_t drawFlags =
1018         ConvertImageRendererToDrawFlags(mFlags) | imgIContainer::FRAME_FIRST;
1019     nsRect dest(nsPoint(0, 0), mSize);
1020     // We have a tricky situation in our choice of SamplingFilter. Shape
1021     // images define a float area based on the alpha values in the rendered
1022     // pixels. When multiple device pixels are used for one css pixel, the
1023     // sampling can change crisp edges into aliased edges. For visual pixels,
1024     // that's usually the right choice. For defining a float area, it can
1025     // cause problems. If a style is using a shape-image-threshold value that
1026     // is less than the alpha of the edge pixels, any filtering may smear the
1027     // alpha into adjacent pixels and expand the float area in a confusing
1028     // way. Since the alpha threshold can be set precisely in CSS, and since a
1029     // web author may be counting on that threshold to define a precise float
1030     // area from an image, it is least confusing to have the rendered pixels
1031     // have unfiltered alpha. We use SamplingFilter::POINT to ensure that each
1032     // rendered pixel has an alpha that precisely matches the alpha of the
1033     // closest pixel in the image.
1034     return nsLayoutUtils::DrawSingleImage(
1035         aRenderingContext, aPresContext, mImageContainer, SamplingFilter::POINT,
1036         dest, dest, Nothing(), drawFlags);
1037   }
1038 
1039   if (mImage->IsGradient()) {
1040     nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
1041         aPresContext, mForFrame->Style(), *mGradientData, mSize);
1042     nsRect dest(nsPoint(0, 0), mSize);
1043     renderer.Paint(aRenderingContext, dest, dest, mSize,
1044                    CSSIntRect::FromAppUnitsRounded(dest), dest, 1.0);
1045     return ImgDrawResult::SUCCESS;
1046   }
1047 
1048   // Unsupported image type.
1049   return ImgDrawResult::BAD_IMAGE;
1050 }
1051 
IsRasterImage()1052 bool nsImageRenderer::IsRasterImage() {
1053   return mImageContainer &&
1054          mImageContainer->GetType() == imgIContainer::TYPE_RASTER;
1055 }
1056 
IsAnimatedImage()1057 bool nsImageRenderer::IsAnimatedImage() {
1058   bool animated = false;
1059   return mImageContainer &&
1060          NS_SUCCEEDED(mImageContainer->GetAnimated(&animated)) && animated;
1061 }
1062 
GetImage()1063 already_AddRefed<imgIContainer> nsImageRenderer::GetImage() {
1064   return do_AddRef(mImageContainer);
1065 }
1066 
IsImageContainerAvailable(layers::LayerManager * aManager,uint32_t aFlags)1067 bool nsImageRenderer::IsImageContainerAvailable(layers::LayerManager* aManager,
1068                                                 uint32_t aFlags) {
1069   return mImageContainer &&
1070          mImageContainer->IsImageContainerAvailable(aManager, aFlags);
1071 }
1072 
PurgeCacheForViewportChange(const Maybe<nsSize> & aSVGViewportSize,const bool aHasIntrinsicRatio)1073 void nsImageRenderer::PurgeCacheForViewportChange(
1074     const Maybe<nsSize>& aSVGViewportSize, const bool aHasIntrinsicRatio) {
1075   // Check if we should flush the cached data - only vector images need to do
1076   // the check since they might not have fixed ratio.
1077   if (mImageContainer &&
1078       mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
1079     if (auto* cachedData =
1080             mForFrame->GetProperty(nsIFrame::CachedBorderImageDataProperty())) {
1081       cachedData->PurgeCacheForViewportChange(aSVGViewportSize,
1082                                               aHasIntrinsicRatio);
1083     }
1084   }
1085 }
1086