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/. */
7 #include "SVGImageFrame.h"
9 // Keep in (case-insensitive) order:
10 #include "gfxContext.h"
11 #include "gfxPlatform.h"
12 #include "mozilla/ComputedStyleInlines.h"
13 #include "mozilla/image/WebRenderImageProvider.h"
14 #include "mozilla/layers/RenderRootStateManager.h"
15 #include "mozilla/layers/WebRenderLayerManager.h"
16 #include "imgIContainer.h"
17 #include "ImageRegion.h"
18 #include "nsContainerFrame.h"
19 #include "nsIImageLoadingContent.h"
20 #include "nsLayoutUtils.h"
21 #include "imgINotificationObserver.h"
22 #include "SVGGeometryProperty.h"
23 #include "mozilla/PresShell.h"
24 #include "mozilla/StaticPrefs_image.h"
25 #include "mozilla/SVGContentUtils.h"
26 #include "mozilla/SVGImageContext.h"
27 #include "mozilla/SVGObserverUtils.h"
28 #include "mozilla/SVGUtils.h"
29 #include "mozilla/dom/MutationEventBinding.h"
30 #include "mozilla/dom/SVGImageElement.h"
31 #include "nsIReflowCallback.h"
33 using namespace mozilla::dom
;
34 using namespace mozilla::gfx
;
35 using namespace mozilla::image
;
36 using namespace mozilla::dom::SVGPreserveAspectRatio_Binding
;
37 namespace SVGT
= SVGGeometryProperty::Tags
;
41 class SVGImageListener final
: public imgINotificationObserver
{
43 explicit SVGImageListener(SVGImageFrame
* aFrame
);
46 NS_DECL_IMGINOTIFICATIONOBSERVER
48 void SetFrame(SVGImageFrame
* frame
) { mFrame
= frame
; }
51 ~SVGImageListener() = default;
53 SVGImageFrame
* mFrame
;
56 // ---------------------------------------------------------------------
57 // nsQueryFrame methods
59 NS_QUERYFRAME_HEAD(SVGImageFrame
)
60 NS_QUERYFRAME_ENTRY(ISVGDisplayableFrame
)
61 NS_QUERYFRAME_ENTRY(SVGImageFrame
)
62 NS_QUERYFRAME_TAIL_INHERITING(nsIFrame
)
64 } // namespace mozilla
66 nsIFrame
* NS_NewSVGImageFrame(mozilla::PresShell
* aPresShell
,
67 mozilla::ComputedStyle
* aStyle
) {
68 return new (aPresShell
)
69 mozilla::SVGImageFrame(aStyle
, aPresShell
->GetPresContext());
74 NS_IMPL_FRAMEARENA_HELPERS(SVGImageFrame
)
76 SVGImageFrame::~SVGImageFrame() {
77 // set the frame to null so we don't send messages to a dead object.
79 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
=
80 do_QueryInterface(GetContent());
82 imageLoader
->RemoveNativeObserver(mListener
);
84 reinterpret_cast<SVGImageListener
*>(mListener
.get())->SetFrame(nullptr);
89 void SVGImageFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
90 nsIFrame
* aPrevInFlow
) {
91 NS_ASSERTION(aContent
->IsSVGElement(nsGkAtoms::image
),
92 "Content is not an SVG image!");
94 AddStateBits(aParent
->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD
);
95 nsIFrame::Init(aContent
, aParent
, aPrevInFlow
);
97 if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY
)) {
98 // Non-display frames are likely to be patterns, masks or the like.
99 // Treat them as always visible.
100 // This call must happen before the FrameCreated. This is because the
101 // primary frame pointer on our content node isn't set until after this
102 // function ends, so there is no way for the resulting OnVisibilityChange
103 // notification to get a frame. FrameCreated has a workaround for this in
104 // that it passes our frame around so it can be accessed. OnVisibilityChange
105 // doesn't have that workaround.
106 IncApproximateVisibleCount();
109 mListener
= new SVGImageListener(this);
110 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
=
111 do_QueryInterface(GetContent());
113 MOZ_CRASH("Why is this not an image loading content?");
116 // We should have a PresContext now, so let's notify our image loader that
117 // we need to register any image animations with the refresh driver.
118 imageLoader
->FrameCreated(this);
120 imageLoader
->AddNativeObserver(mListener
);
124 void SVGImageFrame::Destroy(DestroyContext
& aContext
) {
125 if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY
)) {
126 DecApproximateVisibleCount();
129 if (mReflowCallbackPosted
) {
130 PresShell()->CancelReflowCallback(this);
131 mReflowCallbackPosted
= false;
134 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
=
135 do_QueryInterface(nsIFrame::mContent
);
138 imageLoader
->FrameDestroyed(this);
141 nsIFrame::Destroy(aContext
);
145 void SVGImageFrame::DidSetComputedStyle(ComputedStyle
* aOldStyle
) {
146 nsIFrame::DidSetComputedStyle(aOldStyle
);
148 if (!mImageContainer
|| !aOldStyle
) {
152 nsCOMPtr
<imgIRequest
> currentRequest
;
153 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
=
154 do_QueryInterface(GetContent());
156 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
157 getter_AddRefs(currentRequest
));
160 StyleImageOrientation newOrientation
=
161 StyleVisibility()->UsedImageOrientation(currentRequest
);
162 StyleImageOrientation oldOrientation
=
163 aOldStyle
->StyleVisibility()->UsedImageOrientation(currentRequest
);
165 if (oldOrientation
!= newOrientation
) {
166 nsCOMPtr
<imgIContainer
> image(mImageContainer
->Unwrap());
167 mImageContainer
= nsLayoutUtils::OrientImage(image
, newOrientation
);
170 // TODO(heycam): We should handle aspect-ratio, like nsImageFrame does.
173 bool SVGImageFrame::IsSVGTransformed(gfx::Matrix
* aOwnTransform
,
174 gfx::Matrix
* aFromParentTransform
) const {
175 return SVGUtils::IsSVGTransformed(this, aOwnTransform
, aFromParentTransform
);
178 //----------------------------------------------------------------------
181 nsresult
SVGImageFrame::AttributeChanged(int32_t aNameSpaceID
,
182 nsAtom
* aAttribute
, int32_t aModType
) {
183 if (aNameSpaceID
== kNameSpaceID_None
) {
184 if (aAttribute
== nsGkAtoms::preserveAspectRatio
) {
185 // We don't paint the content of the image using display lists, therefore
186 // we have to invalidate for this children-only transform changes since
187 // there is no layer tree to notice that the transform changed and
194 // Currently our SMIL implementation does not modify the DOM attributes. Once
195 // we implement the SVG 2 SMIL behaviour this can be removed
196 // SVGImageElement::AfterSetAttr's implementation will be sufficient.
197 if (aModType
== MutationEvent_Binding::SMIL
&&
198 aAttribute
== nsGkAtoms::href
&&
199 (aNameSpaceID
== kNameSpaceID_XLink
||
200 aNameSpaceID
== kNameSpaceID_None
)) {
201 SVGImageElement
* element
= static_cast<SVGImageElement
*>(GetContent());
204 element
->mStringAttributes
[SVGImageElement::HREF
].IsExplicitlySet() ||
205 element
->mStringAttributes
[SVGImageElement::XLINK_HREF
]
208 element
->LoadSVGImage(true, true);
210 element
->CancelImageRequests(true);
217 void SVGImageFrame::OnVisibilityChange(
218 Visibility aNewVisibility
, const Maybe
<OnNonvisible
>& aNonvisibleAction
) {
219 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
=
220 do_QueryInterface(GetContent());
222 imageLoader
->OnVisibilityChange(aNewVisibility
, aNonvisibleAction
);
225 nsIFrame::OnVisibilityChange(aNewVisibility
, aNonvisibleAction
);
228 gfx::Matrix
SVGImageFrame::GetRasterImageTransform(int32_t aNativeWidth
,
229 int32_t aNativeHeight
) {
230 float x
, y
, width
, height
;
231 SVGImageElement
* element
= static_cast<SVGImageElement
*>(GetContent());
232 SVGGeometryProperty::ResolveAll
<SVGT::X
, SVGT::Y
, SVGT::Width
, SVGT::Height
>(
233 element
, &x
, &y
, &width
, &height
);
235 Matrix viewBoxTM
= SVGContentUtils::GetViewBoxTransform(
236 width
, height
, 0, 0, aNativeWidth
, aNativeHeight
,
237 element
->mPreserveAspectRatio
);
239 return viewBoxTM
* gfx::Matrix::Translation(x
, y
);
242 gfx::Matrix
SVGImageFrame::GetVectorImageTransform() {
244 SVGImageElement
* element
= static_cast<SVGImageElement
*>(GetContent());
245 SVGGeometryProperty::ResolveAll
<SVGT::X
, SVGT::Y
>(element
, &x
, &y
);
247 // No viewBoxTM needed here -- our height/width overrides any concept of
248 // "native size" that the SVG image has, and it will handle viewBox and
249 // preserveAspectRatio on its own once we give it a region to draw into.
251 return gfx::Matrix::Translation(x
, y
);
254 bool SVGImageFrame::GetIntrinsicImageDimensions(
255 mozilla::gfx::Size
& aSize
, mozilla::AspectRatio
& aAspectRatio
) const {
256 if (!mImageContainer
) {
260 ImageResolution resolution
= mImageContainer
->GetResolution();
262 int32_t width
, height
;
263 if (NS_FAILED(mImageContainer
->GetWidth(&width
))) {
267 resolution
.ApplyXTo(aSize
.width
);
270 if (NS_FAILED(mImageContainer
->GetHeight(&height
))) {
273 aSize
.height
= height
;
274 resolution
.ApplyYTo(aSize
.height
);
277 Maybe
<AspectRatio
> asp
= mImageContainer
->GetIntrinsicRatio();
278 aAspectRatio
= asp
.valueOr(AspectRatio
{});
283 bool SVGImageFrame::TransformContextForPainting(gfxContext
* aGfxContext
,
284 const gfxMatrix
& aTransform
) {
285 gfx::Matrix imageTransform
;
286 if (mImageContainer
->GetType() == imgIContainer::TYPE_VECTOR
) {
287 imageTransform
= GetVectorImageTransform() * ToMatrix(aTransform
);
289 int32_t nativeWidth
, nativeHeight
;
290 if (NS_FAILED(mImageContainer
->GetWidth(&nativeWidth
)) ||
291 NS_FAILED(mImageContainer
->GetHeight(&nativeHeight
)) ||
292 nativeWidth
== 0 || nativeHeight
== 0) {
295 mImageContainer
->GetResolution().ApplyTo(nativeWidth
, nativeHeight
);
296 imageTransform
= GetRasterImageTransform(nativeWidth
, nativeHeight
) *
297 ToMatrix(aTransform
);
299 // NOTE: We need to cancel out the effects of Full-Page-Zoom, or else
300 // it'll get applied an extra time by DrawSingleUnscaledImage.
301 nscoord appUnitsPerDevPx
= PresContext()->AppUnitsPerDevPixel();
302 gfxFloat pageZoomFactor
=
303 nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPx
);
304 imageTransform
.PreScale(pageZoomFactor
, pageZoomFactor
);
307 if (imageTransform
.IsSingular()) {
311 aGfxContext
->Multiply(ThebesMatrix(imageTransform
));
315 //----------------------------------------------------------------------
316 // ISVGDisplayableFrame methods
318 void SVGImageFrame::PaintSVG(gfxContext
& aContext
, const gfxMatrix
& aTransform
,
319 imgDrawingParams
& aImgParams
) {
320 if (!StyleVisibility()->IsVisible()) {
324 float x
, y
, width
, height
;
325 SVGImageElement
* imgElem
= static_cast<SVGImageElement
*>(GetContent());
326 SVGGeometryProperty::ResolveAll
<SVGT::X
, SVGT::Y
, SVGT::Width
, SVGT::Height
>(
327 imgElem
, &x
, &y
, &width
, &height
);
328 NS_ASSERTION(width
> 0 && height
> 0,
329 "Should only be painting things with valid width/height");
331 if (!mImageContainer
) {
332 nsCOMPtr
<imgIRequest
> currentRequest
;
333 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
=
334 do_QueryInterface(GetContent());
336 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
337 getter_AddRefs(currentRequest
));
340 currentRequest
->GetImage(getter_AddRefs(mImageContainer
));
343 if (mImageContainer
) {
344 gfxClipAutoSaveRestore
autoSaveClip(&aContext
);
346 if (StyleDisplay()->IsScrollableOverflow()) {
348 SVGUtils::GetClipRectForFrame(this, x
, y
, width
, height
);
349 autoSaveClip
.TransformedClip(aTransform
, clipRect
);
352 gfxContextMatrixAutoSaveRestore
autoSaveMatrix(&aContext
);
354 if (!TransformContextForPainting(&aContext
, aTransform
)) {
358 // fill-opacity doesn't affect <image>, so if we're allowed to
359 // optimize group opacity, the opacity used for compositing the
360 // image into the current canvas is just the group opacity.
361 float opacity
= 1.0f
;
362 if (SVGUtils::CanOptimizeOpacity(this)) {
363 opacity
= StyleEffects()->mOpacity
;
366 gfxGroupForBlendAutoSaveRestore
autoGroupForBlend(&aContext
);
367 if (opacity
!= 1.0f
|| StyleEffects()->HasMixBlendMode()) {
368 autoGroupForBlend
.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
,
372 nscoord appUnitsPerDevPx
= PresContext()->AppUnitsPerDevPixel();
373 uint32_t flags
= aImgParams
.imageFlags
;
374 if (mForceSyncDecoding
) {
375 flags
|= imgIContainer::FLAG_SYNC_DECODE
;
378 if (mImageContainer
->GetType() == imgIContainer::TYPE_VECTOR
) {
379 // Package up the attributes of this image element which can override the
380 // attributes of mImageContainer's internal SVG document. The 'width' &
381 // 'height' values we're passing in here are in CSS units (though they
382 // come from width/height *attributes* in SVG). They influence the region
383 // of the SVG image's internal document that is visible, in combination
384 // with preserveAspectRatio and viewBox.
385 const SVGImageContext
context(
386 Some(CSSIntSize::Ceil(width
, height
)),
387 Some(imgElem
->mPreserveAspectRatio
.GetAnimValue()));
389 // For the actual draw operation to draw crisply (and at the right size),
390 // our destination rect needs to be |width|x|height|, *in dev pixels*.
391 LayoutDeviceSize
devPxSize(width
, height
);
392 nsRect
destRect(nsPoint(), LayoutDevicePixel::ToAppUnits(
393 devPxSize
, appUnitsPerDevPx
));
395 // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case.
396 // That method needs our image to have a fixed native width & height,
397 // and that's not always true for TYPE_VECTOR images.
398 aImgParams
.result
&= nsLayoutUtils::DrawSingleImage(
399 aContext
, PresContext(), mImageContainer
,
400 nsLayoutUtils::GetSamplingFilterForFrame(this), destRect
, destRect
,
402 } else { // mImageContainer->GetType() == TYPE_RASTER
403 aImgParams
.result
&= nsLayoutUtils::DrawSingleUnscaledImage(
404 aContext
, PresContext(), mImageContainer
,
405 nsLayoutUtils::GetSamplingFilterForFrame(this), nsPoint(0, 0),
406 nullptr, SVGImageContext(), flags
);
411 void SVGImageFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
412 const nsDisplayListSet
& aLists
) {
413 if (!static_cast<const SVGElement
*>(GetContent())->HasValidDimensions()) {
417 if (aBuilder
->IsForPainting()) {
418 if (!IsVisibleForPainting()) {
421 if (StyleEffects()->IsTransparent()) {
424 aBuilder
->BuildCompositorHitTestInfoIfNeeded(this,
425 aLists
.BorderBackground());
428 DisplayOutline(aBuilder
, aLists
);
429 aLists
.Content()->AppendNewToTop
<DisplaySVGImage
>(aBuilder
, this);
432 bool SVGImageFrame::IsInvisible() const {
433 if (!StyleVisibility()->IsVisible()) {
437 // Anything below will round to zero later down the pipeline.
438 constexpr float opacity_threshold
= 1.0 / 128.0;
440 return StyleEffects()->mOpacity
<= opacity_threshold
;
443 bool SVGImageFrame::CreateWebRenderCommands(
444 mozilla::wr::DisplayListBuilder
& aBuilder
,
445 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
446 const mozilla::layers::StackingContextHelper
& aSc
,
447 mozilla::layers::RenderRootStateManager
* aManager
,
448 nsDisplayListBuilder
* aDisplayListBuilder
, DisplaySVGImage
* aItem
,
450 if (!StyleVisibility()->IsVisible()) {
454 float opacity
= 1.0f
;
455 if (SVGUtils::CanOptimizeOpacity(this)) {
456 opacity
= StyleEffects()->mOpacity
;
459 if (opacity
!= 1.0f
) {
460 // FIXME: not implemented, might be trivial
463 if (StyleEffects()->HasMixBlendMode()) {
464 // FIXME: not implemented
468 // try to setup the image
469 if (!mImageContainer
) {
470 nsCOMPtr
<imgIRequest
> currentRequest
;
471 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
=
472 do_QueryInterface(GetContent());
474 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
475 getter_AddRefs(currentRequest
));
478 if (currentRequest
) {
479 currentRequest
->GetImage(getter_AddRefs(mImageContainer
));
483 if (!mImageContainer
) {
484 // nothing to draw (yet)
488 uint32_t flags
= aDisplayListBuilder
->GetImageDecodeFlags();
489 if (mForceSyncDecoding
) {
490 flags
|= imgIContainer::FLAG_SYNC_DECODE
;
493 // Compute bounds of the image
494 nscoord appUnitsPerDevPx
= PresContext()->AppUnitsPerDevPixel();
495 int32_t appUnitsPerCSSPixel
= AppUnitsPerCSSPixel();
497 float x
, y
, width
, height
;
498 SVGImageElement
* imgElem
= static_cast<SVGImageElement
*>(GetContent());
499 SVGGeometryProperty::ResolveAll
<SVGT::X
, SVGT::Y
, SVGT::Width
, SVGT::Height
>(
500 imgElem
, &x
, &y
, &width
, &height
);
501 NS_ASSERTION(width
> 0 && height
> 0,
502 "Should only be painting things with valid width/height");
504 auto toReferenceFrame
= aItem
->ToReferenceFrame();
505 auto appRect
= nsLayoutUtils::RoundGfxRectToAppRect(Rect(0, 0, width
, height
),
506 appUnitsPerCSSPixel
);
507 appRect
+= toReferenceFrame
;
508 auto destRect
= LayoutDeviceRect::FromAppUnits(appRect
, appUnitsPerDevPx
);
509 auto clipRect
= destRect
;
511 if (StyleDisplay()->IsScrollableOverflow()) {
512 // Apply potential non-trivial clip
513 auto cssClip
= SVGUtils::GetClipRectForFrame(this, 0, 0, width
, height
);
515 nsLayoutUtils::RoundGfxRectToAppRect(cssClip
, appUnitsPerCSSPixel
);
516 appClip
+= toReferenceFrame
;
517 clipRect
= LayoutDeviceRect::FromAppUnits(appClip
, appUnitsPerDevPx
);
519 // Apply preserveAspectRatio
520 if (mImageContainer
->GetType() == imgIContainer::TYPE_RASTER
) {
521 int32_t nativeWidth
, nativeHeight
;
522 if (NS_FAILED(mImageContainer
->GetWidth(&nativeWidth
)) ||
523 NS_FAILED(mImageContainer
->GetHeight(&nativeHeight
)) ||
524 nativeWidth
== 0 || nativeHeight
== 0) {
525 // Image has no size; nothing to draw
529 mImageContainer
->GetResolution().ApplyTo(nativeWidth
, nativeHeight
);
531 auto preserveAspectRatio
= imgElem
->mPreserveAspectRatio
.GetAnimValue();
532 uint16_t align
= preserveAspectRatio
.GetAlign();
533 uint16_t meetOrSlice
= preserveAspectRatio
.GetMeetOrSlice();
535 // default to the defaults
536 if (align
== SVG_PRESERVEASPECTRATIO_UNKNOWN
) {
537 align
= SVG_PRESERVEASPECTRATIO_XMIDYMID
;
539 if (meetOrSlice
== SVG_MEETORSLICE_UNKNOWN
) {
540 meetOrSlice
= SVG_MEETORSLICE_MEET
;
543 // aspect > 1 is horizontal
544 // aspect < 1 is vertical
545 float nativeAspect
= ((float)nativeWidth
) / ((float)nativeHeight
);
546 float viewAspect
= width
/ height
;
548 // "Meet" is "fit image to view"; "Slice" is "cover view with image".
550 // Whether we meet or slice, one side of the destRect will always be
551 // perfectly spanned by our image. The only questions to answer are
552 // "which side won't span perfectly" and "should that side be grown
555 // Because we fit our image to the destRect, this all just reduces to:
556 // "if meet, shrink to fit. if slice, grow to fit."
557 if (align
!= SVG_PRESERVEASPECTRATIO_NONE
&& nativeAspect
!= viewAspect
) {
558 // Slightly redundant bools, but they make the conditions clearer
559 bool tooTall
= nativeAspect
> viewAspect
;
560 bool tooWide
= nativeAspect
< viewAspect
;
561 if ((meetOrSlice
== SVG_MEETORSLICE_MEET
&& tooTall
) ||
562 (meetOrSlice
== SVG_MEETORSLICE_SLICE
&& tooWide
)) {
563 // Adjust height and realign y
564 auto oldHeight
= destRect
.height
;
565 destRect
.height
= destRect
.width
/ nativeAspect
;
566 auto heightChange
= oldHeight
- destRect
.height
;
568 case SVG_PRESERVEASPECTRATIO_XMINYMIN
:
569 case SVG_PRESERVEASPECTRATIO_XMIDYMIN
:
570 case SVG_PRESERVEASPECTRATIO_XMAXYMIN
:
571 // align to top (no-op)
573 case SVG_PRESERVEASPECTRATIO_XMINYMID
:
574 case SVG_PRESERVEASPECTRATIO_XMIDYMID
:
575 case SVG_PRESERVEASPECTRATIO_XMAXYMID
:
577 destRect
.y
+= heightChange
/ 2.0f
;
579 case SVG_PRESERVEASPECTRATIO_XMINYMAX
:
580 case SVG_PRESERVEASPECTRATIO_XMIDYMAX
:
581 case SVG_PRESERVEASPECTRATIO_XMAXYMAX
:
583 destRect
.y
+= heightChange
;
586 MOZ_ASSERT_UNREACHABLE("Unknown value for align");
588 } else if ((meetOrSlice
== SVG_MEETORSLICE_MEET
&& tooWide
) ||
589 (meetOrSlice
== SVG_MEETORSLICE_SLICE
&& tooTall
)) {
590 // Adjust width and realign x
591 auto oldWidth
= destRect
.width
;
592 destRect
.width
= destRect
.height
* nativeAspect
;
593 auto widthChange
= oldWidth
- destRect
.width
;
595 case SVG_PRESERVEASPECTRATIO_XMINYMIN
:
596 case SVG_PRESERVEASPECTRATIO_XMINYMID
:
597 case SVG_PRESERVEASPECTRATIO_XMINYMAX
:
598 // align to left (no-op)
600 case SVG_PRESERVEASPECTRATIO_XMIDYMIN
:
601 case SVG_PRESERVEASPECTRATIO_XMIDYMID
:
602 case SVG_PRESERVEASPECTRATIO_XMIDYMAX
:
604 destRect
.x
+= widthChange
/ 2.0f
;
606 case SVG_PRESERVEASPECTRATIO_XMAXYMIN
:
607 case SVG_PRESERVEASPECTRATIO_XMAXYMID
:
608 case SVG_PRESERVEASPECTRATIO_XMAXYMAX
:
610 destRect
.x
+= widthChange
;
613 MOZ_ASSERT_UNREACHABLE("Unknown value for align");
620 SVGImageContext svgContext
;
621 if (mImageContainer
->GetType() == imgIContainer::TYPE_VECTOR
) {
622 if (StaticPrefs::image_svg_blob_image()) {
623 flags
|= imgIContainer::FLAG_RECORD_BLOB
;
625 // Forward preserveAspectRatio to inner SVGs
626 svgContext
.SetViewportSize(Some(CSSIntSize::Ceil(width
, height
)));
627 svgContext
.SetPreserveAspectRatio(
628 Some(imgElem
->mPreserveAspectRatio
.GetAnimValue()));
631 Maybe
<ImageIntRegion
> region
;
632 IntSize decodeSize
= nsLayoutUtils::ComputeImageContainerDrawingParameters(
633 mImageContainer
, this, destRect
, clipRect
, aSc
, flags
, svgContext
,
636 RefPtr
<image::WebRenderImageProvider
> provider
;
637 ImgDrawResult drawResult
= mImageContainer
->GetImageProvider(
638 aManager
->LayerManager(), decodeSize
, svgContext
, region
, flags
,
639 getter_AddRefs(provider
));
641 // While we got a container, it may not contain a fully decoded surface. If
642 // that is the case, and we have an image we were previously displaying which
643 // has a fully decoded surface, then we should prefer the previous image.
644 switch (drawResult
) {
645 case ImgDrawResult::NOT_READY
:
646 case ImgDrawResult::TEMPORARY_ERROR
:
647 // nothing to draw (yet)
649 case ImgDrawResult::NOT_SUPPORTED
:
650 // things we haven't implemented for WR yet
653 // image is ready to draw
657 // Don't do any actual mutations to state if we're doing a dry run
658 // (used to decide if we're making this into an active layer)
660 // If the image container is empty, we don't want to fallback. Any other
661 // failure will be due to resource constraints and fallback is unlikely to
662 // help us. Hence we can ignore the return value from PushImage.
664 aManager
->CommandBuilder().PushImageProvider(aItem
, provider
, drawResult
,
665 aBuilder
, aResources
,
673 nsIFrame
* SVGImageFrame::GetFrameForPoint(const gfxPoint
& aPoint
) {
674 if (!HasAnyStateBits(NS_STATE_SVG_CLIPPATH_CHILD
) && IgnoreHitTest()) {
679 SVGImageElement
* element
= static_cast<SVGImageElement
*>(GetContent());
680 SVGGeometryProperty::ResolveAll
<SVGT::X
, SVGT::Y
, SVGT::Width
, SVGT::Height
>(
681 element
, &rect
.x
, &rect
.y
, &rect
.width
, &rect
.height
);
683 if (!rect
.Contains(ToPoint(aPoint
))) {
687 // Special case for raster images -- we only want to accept points that fall
688 // in the underlying image's (scaled to fit) native bounds. That region
689 // doesn't necessarily map to our <image> element's [x,y,width,height] if the
690 // raster image's aspect ratio is being preserved. We have to look up the
691 // native image size & our viewBox transform in order to filter out points
692 // that fall outside that area. (This special case doesn't apply to vector
693 // images because they don't limit their drawing to explicit "native
694 // bounds" -- they have an infinite canvas on which to place content.)
695 if (StyleDisplay()->IsScrollableOverflow() && mImageContainer
) {
696 if (mImageContainer
->GetType() == imgIContainer::TYPE_RASTER
) {
697 int32_t nativeWidth
, nativeHeight
;
698 if (NS_FAILED(mImageContainer
->GetWidth(&nativeWidth
)) ||
699 NS_FAILED(mImageContainer
->GetHeight(&nativeHeight
)) ||
700 nativeWidth
== 0 || nativeHeight
== 0) {
703 mImageContainer
->GetResolution().ApplyTo(nativeWidth
, nativeHeight
);
704 Matrix viewBoxTM
= SVGContentUtils::GetViewBoxTransform(
705 rect
.width
, rect
.height
, 0, 0, nativeWidth
, nativeHeight
,
706 element
->mPreserveAspectRatio
);
707 if (!SVGUtils::HitTestRect(viewBoxTM
, 0, 0, nativeWidth
, nativeHeight
,
708 aPoint
.x
- rect
.x
, aPoint
.y
- rect
.y
)) {
717 void SVGImageFrame::ReflowSVG() {
718 NS_ASSERTION(SVGUtils::OuterSVGIsCallingReflowSVG(this),
719 "This call is probably a wasteful mistake");
721 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY
),
722 "ReflowSVG mechanism not designed for this");
724 if (!SVGUtils::NeedsReflowSVG(this)) {
728 float x
, y
, width
, height
;
729 SVGImageElement
* element
= static_cast<SVGImageElement
*>(GetContent());
730 SVGGeometryProperty::ResolveAll
<SVGT::X
, SVGT::Y
, SVGT::Width
, SVGT::Height
>(
731 element
, &x
, &y
, &width
, &height
);
733 Rect
extent(x
, y
, width
, height
);
735 if (!extent
.IsEmpty()) {
736 mRect
= nsLayoutUtils::RoundGfxRectToAppRect(extent
, AppUnitsPerCSSPixel());
741 if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW
)) {
742 // Make sure we have our filter property (if any) before calling
743 // FinishAndStoreOverflow (subsequent filter changes are handled off
744 // nsChangeHint_UpdateEffects):
745 SVGObserverUtils::UpdateEffects(this);
747 if (!mReflowCallbackPosted
) {
748 mReflowCallbackPosted
= true;
749 PresShell()->PostReflowCallback(this);
753 nsRect overflow
= nsRect(nsPoint(0, 0), mRect
.Size());
754 OverflowAreas
overflowAreas(overflow
, overflow
);
755 FinishAndStoreOverflow(overflowAreas
, mRect
.Size());
757 RemoveStateBits(NS_FRAME_FIRST_REFLOW
| NS_FRAME_IS_DIRTY
|
758 NS_FRAME_HAS_DIRTY_CHILDREN
);
760 // Invalidate, but only if this is not our first reflow (since if it is our
761 // first reflow then we haven't had our first paint yet).
762 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
)) {
767 bool SVGImageFrame::ReflowFinished() {
768 mReflowCallbackPosted
= false;
770 // XXX(seth): We don't need this. The purpose of updating visibility
771 // synchronously is to ensure that animated images start animating
772 // immediately. In the short term, however,
773 // nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that
774 // animations start as soon as the image is painted for the first time, and in
775 // the long term we want to update visibility information from the display
776 // list whenever we paint, so we don't actually need to do this. However, to
777 // avoid behavior changes during the transition from the old image visibility
778 // code, we'll leave it in for now.
779 UpdateVisibilitySynchronously();
784 void SVGImageFrame::ReflowCallbackCanceled() { mReflowCallbackPosted
= false; }
786 bool SVGImageFrame::IgnoreHitTest() const {
787 switch (Style()->PointerEvents()) {
788 case StylePointerEvents::None
:
790 case StylePointerEvents::Visiblepainted
:
791 case StylePointerEvents::Auto
:
792 if (StyleVisibility()->IsVisible()) {
793 /* XXX: should check pixel transparency */
797 case StylePointerEvents::Visiblefill
:
798 case StylePointerEvents::Visiblestroke
:
799 case StylePointerEvents::Visible
:
800 if (StyleVisibility()->IsVisible()) {
804 case StylePointerEvents::Painted
:
805 /* XXX: should check pixel transparency */
807 case StylePointerEvents::Fill
:
808 case StylePointerEvents::Stroke
:
809 case StylePointerEvents::All
:
812 NS_ERROR("not reached");
819 void SVGImageFrame::NotifySVGChanged(uint32_t aFlags
) {
820 MOZ_ASSERT(aFlags
& (TRANSFORM_CHANGED
| COORD_CONTEXT_CHANGED
),
821 "Invalidation logic may need adjusting");
824 SVGBBox
SVGImageFrame::GetBBoxContribution(const Matrix
& aToBBoxUserspace
,
826 if (aToBBoxUserspace
.IsSingular()) {
827 // XXX ReportToConsole
831 if ((aFlags
& SVGUtils::eForGetClientRects
) &&
832 aToBBoxUserspace
.PreservesAxisAlignedRectangles()) {
833 Rect rect
= NSRectToRect(mRect
, AppUnitsPerCSSPixel());
834 return aToBBoxUserspace
.TransformBounds(rect
);
837 auto* element
= static_cast<SVGImageElement
*>(GetContent());
839 return element
->GeometryBounds(aToBBoxUserspace
);
842 //----------------------------------------------------------------------
843 // SVGImageListener implementation
845 NS_IMPL_ISUPPORTS(SVGImageListener
, imgINotificationObserver
)
847 SVGImageListener::SVGImageListener(SVGImageFrame
* aFrame
) : mFrame(aFrame
) {}
849 void SVGImageListener::Notify(imgIRequest
* aRequest
, int32_t aType
,
850 const nsIntRect
* aData
) {
855 if (aType
== imgINotificationObserver::LOAD_COMPLETE
) {
856 mFrame
->InvalidateFrame();
857 nsLayoutUtils::PostRestyleEvent(mFrame
->GetContent()->AsElement(),
859 nsChangeHint_InvalidateRenderingObservers
);
860 SVGUtils::ScheduleReflowSVG(mFrame
);
863 if (aType
== imgINotificationObserver::FRAME_UPDATE
) {
864 // No new dimensions, so we don't need to call
865 // SVGUtils::InvalidateAndScheduleBoundsUpdate.
866 nsLayoutUtils::PostRestyleEvent(mFrame
->GetContent()->AsElement(),
868 nsChangeHint_InvalidateRenderingObservers
);
869 mFrame
->InvalidateFrame();
872 if (aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
873 // Called once the resource's dimensions have been obtained.
874 nsCOMPtr
<imgIContainer
> image
;
875 aRequest
->GetImage(getter_AddRefs(image
));
877 StyleImageOrientation orientation
=
878 mFrame
->StyleVisibility()->UsedImageOrientation(aRequest
);
879 image
= nsLayoutUtils::OrientImage(image
, orientation
);
880 image
->SetAnimationMode(mFrame
->PresContext()->ImageAnimationMode());
881 mFrame
->mImageContainer
= std::move(image
);
883 mFrame
->InvalidateFrame();
884 nsLayoutUtils::PostRestyleEvent(mFrame
->GetContent()->AsElement(),
886 nsChangeHint_InvalidateRenderingObservers
);
887 SVGUtils::ScheduleReflowSVG(mFrame
);
891 } // namespace mozilla