зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1555356 - Make images inside of SVGs active. r=aosmond
(rebased Alexis' patch) Differential Revision: https://phabricator.services.mozilla.com/D59925
This commit is contained in:
Родитель
6c52620bc9
Коммит
2546aafcc2
|
@ -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"
|
||||
|
@ -1051,14 +1052,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;
|
||||
}
|
||||
}
|
||||
|
@ -1072,9 +1081,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 =
|
||||
|
@ -1085,33 +1097,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;
|
||||
}
|
||||
|
@ -1137,8 +1157,11 @@ void Grouper::ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder,
|
|||
|
||||
nsDisplayItem* item = aList->GetBottom();
|
||||
nsDisplayItem* startOfCurrentGroup = item;
|
||||
RenderRootStateManager* manager =
|
||||
aCommandBuilder->mManager->GetRenderRootStateManager();
|
||||
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>(
|
||||
|
@ -1199,8 +1222,6 @@ void Grouper::ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder,
|
|||
sIndent++;
|
||||
// Note: this call to CreateWebRenderCommands can recurse back into
|
||||
// this function.
|
||||
RenderRootStateManager* manager =
|
||||
aCommandBuilder->mManager->GetRenderRootStateManager();
|
||||
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-100) == 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
|
||||
MOZ_COUNTED_DTOR_OVERRIDE(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 != StyleBlend::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),
|
||||
|
|
Загрузка…
Ссылка в новой задаче