зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1555356 - Make images inside of SVGs active. r=aosmond
This is done using a similar approach to CreateWebRenderCommands but slightly modified. In particular the active layer check needs to be done before we're ready to CreateWebRenderCommands, but once we decide to activate an item, we can't let CreateWebRenderCommands fail. Unfortunately, the need to query ImageLib for support means we need to do basically ~all of the work of CreateWebRenderCommands to do this check. As such, this introduces a modified version of CreateWebRenderCommands that SVGGeometryFrames implement with a "dry run" flag. When true, it runs the same code but stops short of mutating the WR DL/state. ImageLib may be encouraged to do some extra work that could be thrown away, but I'm not sure there's any way to avoid that. For now, only SVGImageFrame actually provides an implementation. The bulk of the implementation is handling the on-by-default preserveAspectRatio feature of SVG images. It was cleaner to just reimplement that logic than reuse the existing preserveAspectRatio code, as it was too tangled up in the particulars of how the PaintSVG path is designed. Differential Revision: https://phabricator.services.mozilla.com/D59925 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
6f7cc8920f
Коммит
6b442a22a5
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/layout/SVGGeometryFrame.h"
|
||||
#include "mozilla/layers/AnimationHelper.h"
|
||||
#include "mozilla/layers/ClipManager.h"
|
||||
#include "mozilla/layers/ImageClient.h"
|
||||
|
@ -1101,14 +1102,22 @@ class WebRenderGroupData : public WebRenderUserData {
|
|||
DIGroup mFollowingGroup;
|
||||
};
|
||||
|
||||
static bool IsItemProbablyActive(nsDisplayItem* aItem,
|
||||
nsDisplayListBuilder* aDisplayListBuilder,
|
||||
bool aParentActive = true);
|
||||
static bool IsItemProbablyActive(
|
||||
nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::RenderRootStateManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder, bool aParentActive = true);
|
||||
|
||||
static bool HasActiveChildren(const nsDisplayList& aList,
|
||||
mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::RenderRootStateManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder) {
|
||||
for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
|
||||
if (IsItemProbablyActive(i, aDisplayListBuilder, false)) {
|
||||
if (IsItemProbablyActive(i, aBuilder, aResources, aSc, aManager,
|
||||
aDisplayListBuilder, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1122,9 +1131,12 @@ static bool HasActiveChildren(const nsDisplayList& aList,
|
|||
//
|
||||
// We can't easily use GetLayerState because it wants a bunch of layers related
|
||||
// information.
|
||||
static bool IsItemProbablyActive(nsDisplayItem* aItem,
|
||||
nsDisplayListBuilder* aDisplayListBuilder,
|
||||
bool aParentActive) {
|
||||
static bool IsItemProbablyActive(
|
||||
nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::RenderRootStateManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder, bool aParentActive) {
|
||||
switch (aItem->GetType()) {
|
||||
case DisplayItemType::TYPE_TRANSFORM: {
|
||||
nsDisplayTransform* transformItem =
|
||||
|
@ -1135,33 +1147,41 @@ static bool IsItemProbablyActive(nsDisplayItem* aItem,
|
|||
GP("active: %d\n", transformItem->MayBeAnimated(aDisplayListBuilder));
|
||||
return transformItem->MayBeAnimated(aDisplayListBuilder, false) ||
|
||||
!is2D ||
|
||||
HasActiveChildren(*transformItem->GetChildren(),
|
||||
aDisplayListBuilder);
|
||||
HasActiveChildren(*transformItem->GetChildren(), aBuilder,
|
||||
aResources, aSc, aManager, aDisplayListBuilder);
|
||||
}
|
||||
case DisplayItemType::TYPE_OPACITY: {
|
||||
nsDisplayOpacity* opacityItem = static_cast<nsDisplayOpacity*>(aItem);
|
||||
bool active = opacityItem->NeedsActiveLayer(aDisplayListBuilder,
|
||||
opacityItem->Frame(), false);
|
||||
GP("active: %d\n", active);
|
||||
return active || HasActiveChildren(*opacityItem->GetChildren(),
|
||||
aDisplayListBuilder);
|
||||
return active ||
|
||||
HasActiveChildren(*opacityItem->GetChildren(), aBuilder,
|
||||
aResources, aSc, aManager, aDisplayListBuilder);
|
||||
}
|
||||
case DisplayItemType::TYPE_FOREIGN_OBJECT: {
|
||||
return true;
|
||||
}
|
||||
case DisplayItemType::TYPE_SVG_GEOMETRY: {
|
||||
auto* svgItem = static_cast<nsDisplaySVGGeometry*>(aItem);
|
||||
return svgItem->ShouldBeActive(aBuilder, aResources, aSc, aManager,
|
||||
aDisplayListBuilder);
|
||||
}
|
||||
case DisplayItemType::TYPE_BLEND_MODE: {
|
||||
/* BLEND_MODE needs to be active if it might have a previous sibling
|
||||
* that is active. We use the activeness of the parent as a rough
|
||||
* proxy for this situation. */
|
||||
return aParentActive ||
|
||||
HasActiveChildren(*aItem->GetChildren(), aDisplayListBuilder);
|
||||
HasActiveChildren(*aItem->GetChildren(), aBuilder, aResources, aSc,
|
||||
aManager, aDisplayListBuilder);
|
||||
}
|
||||
case DisplayItemType::TYPE_WRAP_LIST:
|
||||
case DisplayItemType::TYPE_CONTAINER:
|
||||
case DisplayItemType::TYPE_MASK:
|
||||
case DisplayItemType::TYPE_PERSPECTIVE: {
|
||||
if (aItem->GetChildren()) {
|
||||
return HasActiveChildren(*aItem->GetChildren(), aDisplayListBuilder);
|
||||
return HasActiveChildren(*aItem->GetChildren(), aBuilder, aResources,
|
||||
aSc, aManager, aDisplayListBuilder);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1187,8 +1207,12 @@ void Grouper::ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder,
|
|||
|
||||
nsDisplayItem* item = aList->GetBottom();
|
||||
nsDisplayItem* startOfCurrentGroup = item;
|
||||
RenderRootStateManager* manager =
|
||||
aCommandBuilder->mManager->GetRenderRootStateManager(
|
||||
aBuilder.GetRenderRoot());
|
||||
while (item) {
|
||||
if (IsItemProbablyActive(item, mDisplayListBuilder)) {
|
||||
if (IsItemProbablyActive(item, aBuilder, aResources, aSc, manager,
|
||||
mDisplayListBuilder)) {
|
||||
// We're going to be starting a new group.
|
||||
RefPtr<WebRenderGroupData> groupData =
|
||||
aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(
|
||||
|
@ -1251,9 +1275,6 @@ void Grouper::ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder,
|
|||
sIndent++;
|
||||
// Note: this call to CreateWebRenderCommands can recurse back into
|
||||
// this function.
|
||||
RenderRootStateManager* manager =
|
||||
aCommandBuilder->mManager->GetRenderRootStateManager(
|
||||
aBuilder.GetRenderRoot());
|
||||
bool createdWRCommands = item->CreateWebRenderCommands(
|
||||
aBuilder, aResources, aSc, manager, mDisplayListBuilder);
|
||||
sIndent--;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
== image-filter-01.svg image-filter-01-ref.svg
|
||||
== image-load-01.svg ../pass.svg
|
||||
fuzzy-if(Android&&!browserIsRemote,0-4,0-32) == image-opacity-01.svg image-opacity-01-ref.svg # Bug 779514 for Android
|
||||
fuzzy-if(Android,0-4,0-34) == image-opacity-02.svg image-opacity-02-ref.svg # Bug 776039 for Android
|
||||
fuzzy-if(Android,0-4,0-34) fuzzy-if(webrender,0-1,0-64) == image-opacity-02.svg image-opacity-02-ref.svg # Bug 776039 for Android
|
||||
== image-rotate-01.svg image-rotate-01-ref.svg
|
||||
== image-rotate-02a.svg image-rotate-02-ref.svg
|
||||
== image-rotate-02b.svg image-rotate-02-ref.svg
|
||||
|
|
|
@ -63,7 +63,7 @@ fuzzy(0-11,0-7155) == blur-inside-clipPath.svg blur-inside-clipPath-ref.svg
|
|||
== clip-01.svg pass.svg
|
||||
== clip-02a.svg clip-02-ref.svg
|
||||
== clip-02b.svg clip-02-ref.svg
|
||||
== clip-surface-clone-01.svg clip-surface-clone-01-ref.svg
|
||||
fuzzy-if(webrender,0-1,0-10000) == clip-surface-clone-01.svg clip-surface-clone-01-ref.svg
|
||||
== clip-use-element-01.svg pass.svg
|
||||
== clip-use-element-02.svg pass.svg
|
||||
|
||||
|
@ -370,7 +370,7 @@ fuzzy-if(skiaContent,0-1,0-400) == path-06.svg path-06-ref.svg
|
|||
== pathLength-02.svg pass.svg
|
||||
|
||||
== pattern-basic-01.svg pass.svg
|
||||
fuzzy(0-1,0-5) == pattern-big-image.html pattern-big-image-ref.html
|
||||
fuzzy(0-1,0-5) fuzzy-if(webrender,0-2,0-61) == pattern-big-image.html pattern-big-image-ref.html
|
||||
== pattern-css-transform.html pattern-css-transform-ref.html
|
||||
== pattern-invalid-01.svg pattern-invalid-01-ref.svg
|
||||
fuzzy-if(skiaContent,0-1,0-5) == pattern-live-01a.svg pattern-live-01-ref.svg
|
||||
|
|
|
@ -53,39 +53,6 @@ NS_QUERYFRAME_HEAD(SVGGeometryFrame)
|
|||
NS_QUERYFRAME_ENTRY(SVGGeometryFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Display list item:
|
||||
|
||||
class nsDisplaySVGGeometry final : public nsPaintedDisplayItem {
|
||||
typedef mozilla::image::imgDrawingParams imgDrawingParams;
|
||||
|
||||
public:
|
||||
nsDisplaySVGGeometry(nsDisplayListBuilder* aBuilder, SVGGeometryFrame* aFrame)
|
||||
: nsPaintedDisplayItem(aBuilder, aFrame) {
|
||||
MOZ_COUNT_CTOR(nsDisplaySVGGeometry);
|
||||
MOZ_ASSERT(aFrame, "Must have a frame!");
|
||||
}
|
||||
#ifdef NS_BUILD_REFCNT_LOGGING
|
||||
virtual ~nsDisplaySVGGeometry() { MOZ_COUNT_DTOR(nsDisplaySVGGeometry); }
|
||||
#endif
|
||||
|
||||
NS_DISPLAY_DECL_NAME("nsDisplaySVGGeometry", TYPE_SVG_GEOMETRY)
|
||||
|
||||
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
||||
HitTestState* aState,
|
||||
nsTArray<nsIFrame*>* aOutFrames) override;
|
||||
virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
|
||||
|
||||
nsDisplayItemGeometry* AllocateGeometry(
|
||||
nsDisplayListBuilder* aBuilder) override {
|
||||
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
|
||||
}
|
||||
|
||||
void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayItemGeometry* aGeometry,
|
||||
nsRegion* aInvalidRegion) const override;
|
||||
};
|
||||
|
||||
void nsDisplaySVGGeometry::HitTest(nsDisplayListBuilder* aBuilder,
|
||||
const nsRect& aRect, HitTestState* aState,
|
||||
nsTArray<nsIFrame*>* aOutFrames) {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "gfxMatrix.h"
|
||||
#include "gfxRect.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsFrame.h"
|
||||
#include "nsSVGDisplayableFrame.h"
|
||||
#include "nsLiteralString.h"
|
||||
|
@ -19,13 +20,13 @@
|
|||
namespace mozilla {
|
||||
class SVGGeometryFrame;
|
||||
class SVGMarkerObserver;
|
||||
class nsDisplaySVGGeometry;
|
||||
namespace gfx {
|
||||
class DrawTarget;
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
class gfxContext;
|
||||
class nsDisplaySVGGeometry;
|
||||
class nsAtom;
|
||||
class nsIFrame;
|
||||
class nsSVGMarkerFrame;
|
||||
|
@ -47,7 +48,7 @@ class SVGGeometryFrame : public nsFrame, public nsSVGDisplayableFrame {
|
|||
friend nsIFrame* ::NS_NewSVGGeometryFrame(mozilla::PresShell* aPresShell,
|
||||
ComputedStyle* aStyle);
|
||||
|
||||
friend class ::nsDisplaySVGGeometry;
|
||||
friend class nsDisplaySVGGeometry;
|
||||
|
||||
protected:
|
||||
SVGGeometryFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
|
||||
|
@ -118,6 +119,16 @@ class SVGGeometryFrame : public nsFrame, public nsSVGDisplayableFrame {
|
|||
void Render(gfxContext* aContext, uint32_t aRenderComponents,
|
||||
const gfxMatrix& aTransform, imgDrawingParams& aImgParams);
|
||||
|
||||
virtual bool CreateWebRenderCommands(
|
||||
mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::RenderRootStateManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder, nsDisplaySVGGeometry* aItem,
|
||||
bool aDryRun) {
|
||||
MOZ_RELEASE_ASSERT(aDryRun, "You shouldn't be calling this directly");
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @param aMatrix The transform that must be multiplied onto aContext to
|
||||
* establish this frame's SVG user space.
|
||||
|
@ -125,6 +136,72 @@ class SVGGeometryFrame : public nsFrame, public nsSVGDisplayableFrame {
|
|||
void PaintMarkers(gfxContext& aContext, const gfxMatrix& aTransform,
|
||||
imgDrawingParams& aImgParams);
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Display list item:
|
||||
|
||||
class nsDisplaySVGGeometry final : public nsPaintedDisplayItem {
|
||||
typedef mozilla::image::imgDrawingParams imgDrawingParams;
|
||||
|
||||
public:
|
||||
nsDisplaySVGGeometry(nsDisplayListBuilder* aBuilder, SVGGeometryFrame* aFrame)
|
||||
: nsPaintedDisplayItem(aBuilder, aFrame) {
|
||||
MOZ_COUNT_CTOR(nsDisplaySVGGeometry);
|
||||
MOZ_ASSERT(aFrame, "Must have a frame!");
|
||||
}
|
||||
#ifdef NS_BUILD_REFCNT_LOGGING
|
||||
virtual ~nsDisplaySVGGeometry() { MOZ_COUNT_DTOR(nsDisplaySVGGeometry); }
|
||||
#endif
|
||||
|
||||
NS_DISPLAY_DECL_NAME("nsDisplaySVGGeometry", TYPE_SVG_GEOMETRY)
|
||||
|
||||
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
||||
HitTestState* aState,
|
||||
nsTArray<nsIFrame*>* aOutFrames) override;
|
||||
virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
|
||||
|
||||
nsDisplayItemGeometry* AllocateGeometry(
|
||||
nsDisplayListBuilder* aBuilder) override {
|
||||
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
|
||||
}
|
||||
|
||||
void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayItemGeometry* aGeometry,
|
||||
nsRegion* aInvalidRegion) const override;
|
||||
|
||||
// Whether this part of the SVG should be natively handled by webrender,
|
||||
// potentially becoming an "active layer" inside a blob image.
|
||||
bool ShouldBeActive(mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::RenderRootStateManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder) {
|
||||
// We delegate this question to the parent frame to take advantage of
|
||||
// the SVGGeometryFrame inheritance hierarchy which provides actual
|
||||
// implementation details. The dryRun flag prevents serious side-effects.
|
||||
auto* frame = static_cast<SVGGeometryFrame*>(mFrame);
|
||||
return frame->CreateWebRenderCommands(aBuilder, aResources, aSc, aManager,
|
||||
aDisplayListBuilder, this,
|
||||
/*aDryRun=*/true);
|
||||
}
|
||||
|
||||
virtual bool CreateWebRenderCommands(
|
||||
mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::RenderRootStateManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder) override {
|
||||
// We delegate this question to the parent frame to take advantage of
|
||||
// the SVGGeometryFrame inheritance hierarchy which provides actual
|
||||
// implementation details.
|
||||
auto* frame = static_cast<SVGGeometryFrame*>(mFrame);
|
||||
bool result = frame->CreateWebRenderCommands(aBuilder, aResources, aSc,
|
||||
aManager, aDisplayListBuilder,
|
||||
this, /*aDryRun=*/false);
|
||||
MOZ_ASSERT(result, "ShouldBeActive inconsistent with CreateWRCommands?");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // __SVGGEOMETRYFRAME_H__
|
||||
|
|
|
@ -17,6 +17,7 @@ if CONFIG['ENABLE_TESTS']:
|
|||
|
||||
EXPORTS += [
|
||||
'nsFilterInstance.h',
|
||||
'nsSVGDisplayableFrame.h',
|
||||
'nsSVGFilterInstance.h',
|
||||
'nsSVGForeignObjectFrame.h',
|
||||
'nsSVGImageFrame.h',
|
||||
|
@ -31,6 +32,10 @@ EXPORTS.mozilla += [
|
|||
'SVGContextPaint.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.layout += [
|
||||
'SVGGeometryFrame.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsCSSClipPathInstance.cpp',
|
||||
'nsCSSFilterInstance.cpp',
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "gfxContext.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/layers/RenderRootStateManager.h"
|
||||
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "nsIImageLoadingContent.h"
|
||||
|
@ -31,6 +33,7 @@ using namespace mozilla;
|
|||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::image;
|
||||
using namespace mozilla::dom::SVGPreserveAspectRatio_Binding;
|
||||
namespace SVGT = SVGGeometryProperty::Tags;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
@ -382,6 +385,233 @@ void nsSVGImageFrame::PaintSVG(gfxContext& aContext,
|
|||
}
|
||||
}
|
||||
|
||||
bool nsSVGImageFrame::CreateWebRenderCommands(
|
||||
mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::RenderRootStateManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder, nsDisplaySVGGeometry* aItem,
|
||||
bool aDryRun) {
|
||||
if (!StyleVisibility()->IsVisible()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
float opacity = 1.0f;
|
||||
if (nsSVGUtils::CanOptimizeOpacity(this)) {
|
||||
opacity = StyleEffects()->mOpacity;
|
||||
}
|
||||
|
||||
if (opacity != 1.0f) {
|
||||
// FIXME: not implemented, might be trivial
|
||||
return false;
|
||||
}
|
||||
if (StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
|
||||
// FIXME: not implemented
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to setup the image
|
||||
if (!mImageContainer) {
|
||||
nsCOMPtr<imgIRequest> currentRequest;
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader =
|
||||
do_QueryInterface(GetContent());
|
||||
if (imageLoader) {
|
||||
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
||||
getter_AddRefs(currentRequest));
|
||||
}
|
||||
|
||||
if (currentRequest) {
|
||||
currentRequest->GetImage(getter_AddRefs(mImageContainer));
|
||||
}
|
||||
}
|
||||
|
||||
if (!mImageContainer) {
|
||||
// nothing to draw (yet)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
|
||||
if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
|
||||
flags |= imgIContainer::FLAG_SYNC_DECODE;
|
||||
}
|
||||
if (aDisplayListBuilder->IsPaintingToWindow()) {
|
||||
flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
|
||||
}
|
||||
|
||||
// Compute bounds of the image
|
||||
nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
|
||||
int32_t appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
|
||||
|
||||
float x, y, width, height;
|
||||
SVGImageElement* imgElem = static_cast<SVGImageElement*>(GetContent());
|
||||
SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height>(
|
||||
imgElem, &x, &y, &width, &height);
|
||||
NS_ASSERTION(width > 0 && height > 0,
|
||||
"Should only be painting things with valid width/height");
|
||||
|
||||
auto toReferenceFrame = aItem->ToReferenceFrame();
|
||||
auto appRect = nsLayoutUtils::RoundGfxRectToAppRect(Rect(0, 0, width, height),
|
||||
appUnitsPerCSSPixel);
|
||||
appRect += toReferenceFrame;
|
||||
auto destRect = LayoutDeviceRect::FromAppUnits(appRect, appUnitsPerDevPx);
|
||||
auto clipRect = destRect;
|
||||
|
||||
if (StyleDisplay()->IsScrollableOverflow()) {
|
||||
// Apply potential non-trivial clip
|
||||
auto cssClip = nsSVGUtils::GetClipRectForFrame(this, 0, 0, width, height);
|
||||
auto appClip =
|
||||
nsLayoutUtils::RoundGfxRectToAppRect(cssClip, appUnitsPerCSSPixel);
|
||||
appClip += toReferenceFrame;
|
||||
clipRect = LayoutDeviceRect::FromAppUnits(appClip, appUnitsPerDevPx);
|
||||
|
||||
// Apply preserveAspectRatio
|
||||
if (mImageContainer->GetType() == imgIContainer::TYPE_RASTER) {
|
||||
int32_t nativeWidth, nativeHeight;
|
||||
if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) ||
|
||||
NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) ||
|
||||
nativeWidth == 0 || nativeHeight == 0) {
|
||||
// Image has no size; nothing to draw
|
||||
return true;
|
||||
}
|
||||
|
||||
auto preserveAspectRatio = imgElem->mPreserveAspectRatio.GetAnimValue();
|
||||
uint16_t align = preserveAspectRatio.GetAlign();
|
||||
uint16_t meetOrSlice = preserveAspectRatio.GetMeetOrSlice();
|
||||
|
||||
// default to the defaults
|
||||
if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN) {
|
||||
align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
|
||||
}
|
||||
if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN) {
|
||||
meetOrSlice = SVG_MEETORSLICE_MEET;
|
||||
}
|
||||
|
||||
// aspect > 1 is horizontal
|
||||
// aspect < 1 is vertical
|
||||
float nativeAspect = ((float)nativeWidth) / ((float)nativeHeight);
|
||||
float viewAspect = width / height;
|
||||
|
||||
// "Meet" is "fit image to view"; "Slice" is "cover view with image".
|
||||
//
|
||||
// Whether we meet or slice, one side of the destRect will always be
|
||||
// perfectly spanned by our image. The only questions to answer are
|
||||
// "which side won't span perfectly" and "should that side be grown
|
||||
// or shrunk".
|
||||
//
|
||||
// Because we fit our image to the destRect, this all just reduces to:
|
||||
// "if meet, shrink to fit. if slice, grow to fit."
|
||||
if (align != SVG_PRESERVEASPECTRATIO_NONE && nativeAspect != viewAspect) {
|
||||
// Slightly redundant bools, but they make the conditions clearer
|
||||
bool tooTall = nativeAspect > viewAspect;
|
||||
bool tooWide = nativeAspect < viewAspect;
|
||||
if ((meetOrSlice == SVG_MEETORSLICE_MEET && tooTall) ||
|
||||
(meetOrSlice == SVG_MEETORSLICE_SLICE && tooWide)) {
|
||||
// Adjust height and realign y
|
||||
auto oldHeight = destRect.height;
|
||||
destRect.height = destRect.width / nativeAspect;
|
||||
auto heightChange = oldHeight - destRect.height;
|
||||
switch (align) {
|
||||
case SVG_PRESERVEASPECTRATIO_XMINYMIN:
|
||||
case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
|
||||
case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
|
||||
// align to top (no-op)
|
||||
break;
|
||||
case SVG_PRESERVEASPECTRATIO_XMINYMID:
|
||||
case SVG_PRESERVEASPECTRATIO_XMIDYMID:
|
||||
case SVG_PRESERVEASPECTRATIO_XMAXYMID:
|
||||
// align to center
|
||||
destRect.y += heightChange / 2.0f;
|
||||
break;
|
||||
case SVG_PRESERVEASPECTRATIO_XMINYMAX:
|
||||
case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
|
||||
case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
|
||||
// align to bottom
|
||||
destRect.y += heightChange;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown value for align");
|
||||
}
|
||||
} else if ((meetOrSlice == SVG_MEETORSLICE_MEET && tooWide) ||
|
||||
(meetOrSlice == SVG_MEETORSLICE_SLICE && tooTall)) {
|
||||
// Adjust width and realign x
|
||||
auto oldWidth = destRect.width;
|
||||
destRect.width = destRect.height * nativeAspect;
|
||||
auto widthChange = oldWidth - destRect.width;
|
||||
switch (align) {
|
||||
case SVG_PRESERVEASPECTRATIO_XMINYMIN:
|
||||
case SVG_PRESERVEASPECTRATIO_XMINYMID:
|
||||
case SVG_PRESERVEASPECTRATIO_XMINYMAX:
|
||||
// align to left (no-op)
|
||||
break;
|
||||
case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
|
||||
case SVG_PRESERVEASPECTRATIO_XMIDYMID:
|
||||
case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
|
||||
// align to center
|
||||
destRect.x += widthChange / 2.0f;
|
||||
break;
|
||||
case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
|
||||
case SVG_PRESERVEASPECTRATIO_XMAXYMID:
|
||||
case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
|
||||
// align to right
|
||||
destRect.x += widthChange;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown value for align");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<SVGImageContext> svgContext;
|
||||
if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
|
||||
// Forward preserveAspectRatio to inner SVGs
|
||||
svgContext.emplace(Some(CSSIntSize::Truncate(width, height)),
|
||||
Some(imgElem->mPreserveAspectRatio.GetAnimValue()));
|
||||
}
|
||||
|
||||
IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters(
|
||||
mImageContainer, this, destRect, aSc, flags, svgContext);
|
||||
|
||||
RefPtr<layers::ImageContainer> container;
|
||||
ImgDrawResult drawResult = mImageContainer->GetImageContainerAtSize(
|
||||
aManager->LayerManager(), decodeSize, svgContext, flags,
|
||||
getter_AddRefs(container));
|
||||
|
||||
// While we got a container, it may not contain a fully decoded surface. If
|
||||
// that is the case, and we have an image we were previously displaying which
|
||||
// has a fully decoded surface, then we should prefer the previous image.
|
||||
switch (drawResult) {
|
||||
case ImgDrawResult::NOT_READY:
|
||||
case ImgDrawResult::INCOMPLETE:
|
||||
case ImgDrawResult::TEMPORARY_ERROR:
|
||||
// nothing to draw (yet)
|
||||
return true;
|
||||
case ImgDrawResult::NOT_SUPPORTED:
|
||||
// things we haven't implemented for WR yet
|
||||
return false;
|
||||
default:
|
||||
// image is ready to draw
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't do any actual mutations to state if we're doing a dry run
|
||||
// (used to decide if we're making this into an active layer)
|
||||
if (!aDryRun) {
|
||||
// If the image container is empty, we don't want to fallback. Any other
|
||||
// failure will be due to resource constraints and fallback is unlikely to
|
||||
// help us. Hence we can ignore the return value from PushImage.
|
||||
if (container) {
|
||||
aManager->CommandBuilder().PushImage(aItem, container, aBuilder,
|
||||
aResources, aSc, destRect, clipRect);
|
||||
}
|
||||
|
||||
nsDisplayItemGenericImageGeometry::UpdateDrawResult(aItem, drawResult);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIFrame* nsSVGImageFrame::GetFrameForPoint(const gfxPoint& aPoint) {
|
||||
if (!(GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) && !GetHitTestFlags()) {
|
||||
return nullptr;
|
||||
|
|
|
@ -50,6 +50,14 @@ class nsSVGImageFrame final : public mozilla::SVGGeometryFrame,
|
|||
friend nsIFrame* NS_NewSVGImageFrame(mozilla::PresShell* aPresShell,
|
||||
ComputedStyle* aStyle);
|
||||
|
||||
virtual bool CreateWebRenderCommands(
|
||||
mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::RenderRootStateManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder,
|
||||
mozilla::nsDisplaySVGGeometry* aItem, bool aDryRun) override;
|
||||
|
||||
protected:
|
||||
explicit nsSVGImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
|
||||
: SVGGeometryFrame(aStyle, aPresContext, kClassID),
|
||||
|
|
Загрузка…
Ссылка в новой задаче