mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
627
layout/svg/nsFilterInstance.cpp
Normal file
627
layout/svg/nsFilterInstance.cpp
Normal file
|
|
@ -0,0 +1,627 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Main header first:
|
||||
#include "nsFilterInstance.h"
|
||||
|
||||
// MFBT headers next:
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
// Keep others in (case-insensitive) order:
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "mozilla/gfx/Helpers.h"
|
||||
#include "mozilla/gfx/PatternHelpers.h"
|
||||
#include "nsISVGChildFrame.h"
|
||||
#include "nsCSSFilterInstance.h"
|
||||
#include "nsSVGFilterInstance.h"
|
||||
#include "nsSVGFilterPaintCallback.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include "SVGContentUtils.h"
|
||||
#include "FilterSupport.h"
|
||||
#include "gfx2DGlue.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
FilterDescription
|
||||
nsFilterInstance::GetFilterDescription(nsIContent* aFilteredElement,
|
||||
const nsTArray<nsStyleFilter>& aFilterChain,
|
||||
bool aFilterInputIsTainted,
|
||||
const UserSpaceMetrics& aMetrics,
|
||||
const gfxRect& aBBox,
|
||||
nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages)
|
||||
{
|
||||
gfxMatrix unused; // aPaintTransform arg not used since we're not painting
|
||||
nsFilterInstance instance(nullptr, aFilteredElement, aMetrics,
|
||||
aFilterChain, aFilterInputIsTainted, nullptr,
|
||||
unused, nullptr, nullptr, nullptr, &aBBox);
|
||||
if (!instance.IsInitialized()) {
|
||||
return FilterDescription();
|
||||
}
|
||||
return instance.ExtractDescriptionAndAdditionalImages(aOutAdditionalImages);
|
||||
}
|
||||
|
||||
static UniquePtr<UserSpaceMetrics>
|
||||
UserSpaceMetricsForFrame(nsIFrame* aFrame)
|
||||
{
|
||||
if (aFrame->GetContent()->IsSVGElement()) {
|
||||
nsSVGElement* element = static_cast<nsSVGElement*>(aFrame->GetContent());
|
||||
return MakeUnique<SVGElementMetrics>(element);
|
||||
}
|
||||
return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
|
||||
DrawTarget* aDrawTarget,
|
||||
const gfxMatrix& aTransform,
|
||||
nsSVGFilterPaintCallback *aPaintCallback,
|
||||
const nsRegion *aDirtyArea)
|
||||
{
|
||||
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
|
||||
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
|
||||
// Hardcode InputIsTainted to true because we don't want JS to be able to
|
||||
// read the rendered contents of aFilteredFrame.
|
||||
nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
|
||||
filterChain, /* InputIsTainted */ true, aPaintCallback,
|
||||
aTransform, aDirtyArea, nullptr, nullptr, nullptr);
|
||||
if (!instance.IsInitialized()) {
|
||||
return NS_OK;
|
||||
}
|
||||
return instance.Render(aDrawTarget);
|
||||
}
|
||||
|
||||
nsRegion
|
||||
nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
|
||||
const nsRegion& aPreFilterDirtyRegion)
|
||||
{
|
||||
if (aPreFilterDirtyRegion.IsEmpty()) {
|
||||
return nsRegion();
|
||||
}
|
||||
|
||||
gfxMatrix unused; // aPaintTransform arg not used since we're not painting
|
||||
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
|
||||
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
|
||||
// Hardcode InputIsTainted to true because we don't want JS to be able to
|
||||
// read the rendered contents of aFilteredFrame.
|
||||
nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
|
||||
filterChain, /* InputIsTainted */ true, nullptr, unused,
|
||||
nullptr, &aPreFilterDirtyRegion);
|
||||
if (!instance.IsInitialized()) {
|
||||
return nsRegion();
|
||||
}
|
||||
|
||||
// We've passed in the source's dirty area so the instance knows about it.
|
||||
// Now we can ask the instance to compute the area of the filter output
|
||||
// that's dirty.
|
||||
return instance.ComputePostFilterDirtyRegion();
|
||||
}
|
||||
|
||||
nsRegion
|
||||
nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
|
||||
const nsRegion& aPostFilterDirtyRegion)
|
||||
{
|
||||
gfxMatrix unused; // aPaintTransform arg not used since we're not painting
|
||||
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
|
||||
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
|
||||
// Hardcode InputIsTainted to true because we don't want JS to be able to
|
||||
// read the rendered contents of aFilteredFrame.
|
||||
nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
|
||||
filterChain, /* InputIsTainted */ true, nullptr, unused,
|
||||
&aPostFilterDirtyRegion);
|
||||
if (!instance.IsInitialized()) {
|
||||
return nsRect();
|
||||
}
|
||||
|
||||
// Now we can ask the instance to compute the area of the source
|
||||
// that's needed.
|
||||
return instance.ComputeSourceNeededRect();
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsFilterInstance::GetPostFilterBounds(nsIFrame *aFilteredFrame,
|
||||
const gfxRect *aOverrideBBox,
|
||||
const nsRect *aPreFilterBounds)
|
||||
{
|
||||
MOZ_ASSERT(!(aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
|
||||
!(aFilteredFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
|
||||
"Non-display SVG do not maintain visual overflow rects");
|
||||
|
||||
nsRegion preFilterRegion;
|
||||
nsRegion* preFilterRegionPtr = nullptr;
|
||||
if (aPreFilterBounds) {
|
||||
preFilterRegion = *aPreFilterBounds;
|
||||
preFilterRegionPtr = &preFilterRegion;
|
||||
}
|
||||
|
||||
gfxMatrix unused; // aPaintTransform arg not used since we're not painting
|
||||
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
|
||||
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
|
||||
// Hardcode InputIsTainted to true because we don't want JS to be able to
|
||||
// read the rendered contents of aFilteredFrame.
|
||||
nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
|
||||
filterChain, /* InputIsTainted */ true, nullptr, unused,
|
||||
nullptr, preFilterRegionPtr, aPreFilterBounds,
|
||||
aOverrideBBox);
|
||||
if (!instance.IsInitialized()) {
|
||||
return nsRect();
|
||||
}
|
||||
|
||||
return instance.ComputePostFilterExtents();
|
||||
}
|
||||
|
||||
nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
|
||||
nsIContent* aTargetContent,
|
||||
const UserSpaceMetrics& aMetrics,
|
||||
const nsTArray<nsStyleFilter>& aFilterChain,
|
||||
bool aFilterInputIsTainted,
|
||||
nsSVGFilterPaintCallback *aPaintCallback,
|
||||
const gfxMatrix& aPaintTransform,
|
||||
const nsRegion *aPostFilterDirtyRegion,
|
||||
const nsRegion *aPreFilterDirtyRegion,
|
||||
const nsRect *aPreFilterVisualOverflowRectOverride,
|
||||
const gfxRect *aOverrideBBox)
|
||||
: mTargetFrame(aTargetFrame)
|
||||
, mTargetContent(aTargetContent)
|
||||
, mMetrics(aMetrics)
|
||||
, mPaintCallback(aPaintCallback)
|
||||
, mPaintTransform(aPaintTransform)
|
||||
, mInitialized(false)
|
||||
{
|
||||
if (aOverrideBBox) {
|
||||
mTargetBBox = *aOverrideBBox;
|
||||
} else {
|
||||
MOZ_ASSERT(mTargetFrame, "Need to supply a frame when there's no aOverrideBBox");
|
||||
mTargetBBox = nsSVGUtils::GetBBox(mTargetFrame);
|
||||
}
|
||||
|
||||
// Compute user space to filter space transforms.
|
||||
nsresult rv = ComputeUserSpaceToFilterSpaceScale();
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gfxRect targetBBoxInFilterSpace = UserSpaceToFilterSpace(mTargetBBox);
|
||||
targetBBoxInFilterSpace.RoundOut();
|
||||
if (!gfxUtils::GfxRectToIntRect(targetBBoxInFilterSpace, &mTargetBBoxInFilterSpace)) {
|
||||
// The target's bbox is way too big if there is float->int overflow.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get various transforms:
|
||||
|
||||
gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.width, 0.0f,
|
||||
0.0f, mFilterSpaceToUserSpaceScale.height,
|
||||
0.0f, 0.0f);
|
||||
|
||||
// Only used (so only set) when we paint:
|
||||
if (mPaintCallback) {
|
||||
mFilterSpaceToDeviceSpaceTransform = filterToUserSpace * mPaintTransform;
|
||||
}
|
||||
|
||||
mFilterSpaceToFrameSpaceInCSSPxTransform =
|
||||
filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform();
|
||||
// mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible
|
||||
mFrameSpaceInCSSPxToFilterSpaceTransform =
|
||||
mFilterSpaceToFrameSpaceInCSSPxTransform;
|
||||
mFrameSpaceInCSSPxToFilterSpaceTransform.Invert();
|
||||
|
||||
nsIntRect targetBounds;
|
||||
if (aPreFilterVisualOverflowRectOverride) {
|
||||
targetBounds =
|
||||
FrameSpaceToFilterSpace(aPreFilterVisualOverflowRectOverride);
|
||||
} else if (mTargetFrame) {
|
||||
nsRect preFilterVOR = mTargetFrame->GetPreEffectsVisualOverflowRect();
|
||||
targetBounds = FrameSpaceToFilterSpace(&preFilterVOR);
|
||||
}
|
||||
mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds);
|
||||
|
||||
// Build the filter graph.
|
||||
rv = BuildPrimitives(aFilterChain, aTargetFrame, aFilterInputIsTainted);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPrimitiveDescriptions.IsEmpty()) {
|
||||
// Nothing should be rendered.
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the passed in rects from frame space to filter space:
|
||||
mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion);
|
||||
mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion);
|
||||
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFilterInstance::ComputeUserSpaceToFilterSpaceScale()
|
||||
{
|
||||
gfxMatrix canvasTransform;
|
||||
if (mTargetFrame) {
|
||||
canvasTransform = nsSVGUtils::GetCanvasTM(mTargetFrame);
|
||||
if (canvasTransform.IsSingular()) {
|
||||
// Nothing should be rendered.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
mUserSpaceToFilterSpaceScale = canvasTransform.ScaleFactors(true);
|
||||
if (mUserSpaceToFilterSpaceScale.width <= 0.0f ||
|
||||
mUserSpaceToFilterSpaceScale.height <= 0.0f) {
|
||||
// Nothing should be rendered.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mFilterSpaceToUserSpaceScale = gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width,
|
||||
1.0f / mUserSpaceToFilterSpaceScale.height);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
gfxRect
|
||||
nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
|
||||
{
|
||||
gfxRect filterSpaceRect = aUserSpaceRect;
|
||||
filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
|
||||
mUserSpaceToFilterSpaceScale.height);
|
||||
return filterSpaceRect;
|
||||
}
|
||||
|
||||
gfxRect
|
||||
nsFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const
|
||||
{
|
||||
gfxRect userSpaceRect = aFilterSpaceRect;
|
||||
userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width,
|
||||
mFilterSpaceToUserSpaceScale.height);
|
||||
return userSpaceRect;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFilterInstance::BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
|
||||
nsIFrame* aTargetFrame,
|
||||
bool aFilterInputIsTainted)
|
||||
{
|
||||
NS_ASSERTION(!mPrimitiveDescriptions.Length(),
|
||||
"expected to start building primitives from scratch");
|
||||
|
||||
for (uint32_t i = 0; i < aFilterChain.Length(); i++) {
|
||||
bool inputIsTainted =
|
||||
mPrimitiveDescriptions.IsEmpty() ? aFilterInputIsTainted :
|
||||
mPrimitiveDescriptions.LastElement().IsTainted();
|
||||
nsresult rv = BuildPrimitivesForFilter(aFilterChain[i], aTargetFrame, inputIsTainted);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
mFilterDescription = FilterDescription(mPrimitiveDescriptions);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter,
|
||||
nsIFrame* aTargetFrame,
|
||||
bool aInputIsTainted)
|
||||
{
|
||||
NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f &&
|
||||
mFilterSpaceToUserSpaceScale.height > 0.0f,
|
||||
"scale factors between spaces should be positive values");
|
||||
|
||||
if (aFilter.GetType() == NS_STYLE_FILTER_URL) {
|
||||
// Build primitives for an SVG filter.
|
||||
nsSVGFilterInstance svgFilterInstance(aFilter, aTargetFrame,
|
||||
mTargetContent,
|
||||
mMetrics, mTargetBBox,
|
||||
mUserSpaceToFilterSpaceScale,
|
||||
mFilterSpaceToUserSpaceScale);
|
||||
if (!svgFilterInstance.IsInitialized()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages,
|
||||
aInputIsTainted);
|
||||
}
|
||||
|
||||
// Build primitives for a CSS filter.
|
||||
|
||||
// If we don't have a frame, use opaque black for shadows with unspecified
|
||||
// shadow colors.
|
||||
nscolor shadowFallbackColor =
|
||||
mTargetFrame ? mTargetFrame->StyleColor()->mColor : NS_RGB(0,0,0);
|
||||
|
||||
nsCSSFilterInstance cssFilterInstance(aFilter, shadowFallbackColor,
|
||||
mTargetBounds,
|
||||
mFrameSpaceInCSSPxToFilterSpaceTransform);
|
||||
return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions, aInputIsTainted);
|
||||
}
|
||||
|
||||
void
|
||||
nsFilterInstance::ComputeNeededBoxes()
|
||||
{
|
||||
if (mPrimitiveDescriptions.IsEmpty())
|
||||
return;
|
||||
|
||||
nsIntRegion sourceGraphicNeededRegion;
|
||||
nsIntRegion fillPaintNeededRegion;
|
||||
nsIntRegion strokePaintNeededRegion;
|
||||
|
||||
FilterSupport::ComputeSourceNeededRegions(
|
||||
mFilterDescription, mPostFilterDirtyRegion,
|
||||
sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
|
||||
|
||||
sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, mTargetBounds);
|
||||
|
||||
mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds();
|
||||
mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds();
|
||||
mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFilterInstance::BuildSourcePaint(SourceInfo *aSource,
|
||||
DrawTarget* aTargetDT)
|
||||
{
|
||||
MOZ_ASSERT(mTargetFrame);
|
||||
nsIntRect neededRect = aSource->mNeededBounds;
|
||||
|
||||
RefPtr<DrawTarget> offscreenDT =
|
||||
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
|
||||
neededRect.Size(), SurfaceFormat::B8G8R8A8);
|
||||
if (!offscreenDT || !offscreenDT->IsValid()) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
|
||||
if (!deviceToFilterSpace.Invert()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mPaintTransform.IsSingular()) {
|
||||
RefPtr<gfxContext> gfx = gfxContext::CreateOrNull(offscreenDT);
|
||||
MOZ_ASSERT(gfx); // already checked the draw target above
|
||||
gfx->Save();
|
||||
gfx->Multiply(mPaintTransform *
|
||||
deviceToFilterSpace *
|
||||
gfxMatrix::Translation(-neededRect.TopLeft()));
|
||||
GeneralPattern pattern;
|
||||
if (aSource == &mFillPaint) {
|
||||
nsSVGUtils::MakeFillPatternFor(mTargetFrame, gfx, &pattern);
|
||||
} else if (aSource == &mStrokePaint) {
|
||||
nsSVGUtils::MakeStrokePatternFor(mTargetFrame, gfx, &pattern);
|
||||
}
|
||||
if (pattern.GetPattern()) {
|
||||
offscreenDT->FillRect(ToRect(FilterSpaceToUserSpace(ThebesRect(neededRect))),
|
||||
pattern);
|
||||
}
|
||||
gfx->Restore();
|
||||
}
|
||||
|
||||
aSource->mSourceSurface = offscreenDT->Snapshot();
|
||||
aSource->mSurfaceRect = neededRect;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFilterInstance::BuildSourcePaints(DrawTarget* aTargetDT)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (!mFillPaint.mNeededBounds.IsEmpty()) {
|
||||
rv = BuildSourcePaint(&mFillPaint, aTargetDT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!mStrokePaint.mNeededBounds.IsEmpty()) {
|
||||
rv = BuildSourcePaint(&mStrokePaint, aTargetDT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFilterInstance::BuildSourceImage(DrawTarget* aTargetDT)
|
||||
{
|
||||
MOZ_ASSERT(mTargetFrame);
|
||||
|
||||
nsIntRect neededRect = mSourceGraphic.mNeededBounds;
|
||||
if (neededRect.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> offscreenDT =
|
||||
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
|
||||
neededRect.Size(), SurfaceFormat::B8G8R8A8);
|
||||
if (!offscreenDT || !offscreenDT->IsValid()) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
gfxRect r = FilterSpaceToUserSpace(ThebesRect(neededRect));
|
||||
r.RoundOut();
|
||||
nsIntRect dirty;
|
||||
if (!gfxUtils::GfxRectToIntRect(r, &dirty))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// SVG graphics paint to device space, so we need to set an initial device
|
||||
// space to filter space transform on the gfxContext that SourceGraphic
|
||||
// and SourceAlpha will paint to.
|
||||
//
|
||||
// (In theory it would be better to minimize error by having filtered SVG
|
||||
// graphics temporarily paint to user space when painting the sources and
|
||||
// only set a user space to filter space transform on the gfxContext
|
||||
// (since that would eliminate the transform multiplications from user
|
||||
// space to device space and back again). However, that would make the
|
||||
// code more complex while being hard to get right without introducing
|
||||
// subtle bugs, and in practice it probably makes no real difference.)
|
||||
gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
|
||||
if (!deviceToFilterSpace.Invert()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(offscreenDT);
|
||||
MOZ_ASSERT(ctx); // already checked the draw target above
|
||||
ctx->SetMatrix(
|
||||
ctx->CurrentMatrix().Translate(-neededRect.TopLeft()).
|
||||
PreMultiply(deviceToFilterSpace));
|
||||
|
||||
DrawResult result =
|
||||
mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty);
|
||||
|
||||
mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
|
||||
mSourceGraphic.mSurfaceRect = neededRect;
|
||||
|
||||
return (result == DrawResult::SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFilterInstance::Render(DrawTarget* aDrawTarget)
|
||||
{
|
||||
MOZ_ASSERT(mTargetFrame, "Need a frame for rendering");
|
||||
|
||||
nsIntRect filterRect =
|
||||
mPostFilterDirtyRegion.GetBounds().Intersect(OutputFilterSpaceBounds());
|
||||
gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform();
|
||||
|
||||
if (filterRect.IsEmpty() || ctm.IsSingular()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AutoRestoreTransform autoRestoreTransform(aDrawTarget);
|
||||
Matrix newTM = ToMatrix(ctm).PreTranslate(filterRect.x, filterRect.y) *
|
||||
aDrawTarget->GetTransform();
|
||||
aDrawTarget->SetTransform(newTM);
|
||||
|
||||
ComputeNeededBoxes();
|
||||
|
||||
nsresult rv = BuildSourceImage(aDrawTarget);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
rv = BuildSourcePaints(aDrawTarget);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
FilterSupport::RenderFilterDescription(
|
||||
aDrawTarget, mFilterDescription, IntRectToRect(filterRect),
|
||||
mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect,
|
||||
mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
|
||||
mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect,
|
||||
mInputImages, Point(0, 0));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRegion
|
||||
nsFilterInstance::ComputePostFilterDirtyRegion()
|
||||
{
|
||||
if (mPreFilterDirtyRegion.IsEmpty()) {
|
||||
return nsRegion();
|
||||
}
|
||||
|
||||
nsIntRegion resultChangeRegion =
|
||||
FilterSupport::ComputeResultChangeRegion(mFilterDescription,
|
||||
mPreFilterDirtyRegion, nsIntRegion(), nsIntRegion());
|
||||
return FilterSpaceToFrameSpace(resultChangeRegion);
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsFilterInstance::ComputePostFilterExtents()
|
||||
{
|
||||
nsIntRegion postFilterExtents =
|
||||
FilterSupport::ComputePostFilterExtents(mFilterDescription, mTargetBounds);
|
||||
return FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsFilterInstance::ComputeSourceNeededRect()
|
||||
{
|
||||
ComputeNeededBoxes();
|
||||
return FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds);
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsFilterInstance::OutputFilterSpaceBounds() const
|
||||
{
|
||||
uint32_t numPrimitives = mPrimitiveDescriptions.Length();
|
||||
if (numPrimitives <= 0)
|
||||
return nsIntRect();
|
||||
|
||||
nsIntRect bounds =
|
||||
mPrimitiveDescriptions[numPrimitives - 1].PrimitiveSubregion();
|
||||
bool overflow;
|
||||
IntSize surfaceSize =
|
||||
nsSVGUtils::ConvertToSurfaceSize(bounds.Size(), &overflow);
|
||||
bounds.SizeTo(surfaceSize);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const
|
||||
{
|
||||
nsIntRect rect = OutputFilterSpaceBounds();
|
||||
if (aRect) {
|
||||
if (aRect->IsEmpty()) {
|
||||
return nsIntRect();
|
||||
}
|
||||
gfxRect rectInCSSPx =
|
||||
nsLayoutUtils::RectToGfxRect(*aRect, nsPresContext::AppUnitsPerCSSPixel());
|
||||
gfxRect rectInFilterSpace =
|
||||
mFrameSpaceInCSSPxToFilterSpaceTransform.TransformBounds(rectInCSSPx);
|
||||
rectInFilterSpace.RoundOut();
|
||||
nsIntRect intRect;
|
||||
if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) {
|
||||
rect = intRect;
|
||||
}
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const
|
||||
{
|
||||
if (aRect.IsEmpty()) {
|
||||
return nsRect();
|
||||
}
|
||||
gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
|
||||
r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r);
|
||||
// nsLayoutUtils::RoundGfxRectToAppRect rounds out.
|
||||
return nsLayoutUtils::RoundGfxRectToAppRect(r, nsPresContext::AppUnitsPerCSSPixel());
|
||||
}
|
||||
|
||||
nsIntRegion
|
||||
nsFilterInstance::FrameSpaceToFilterSpace(const nsRegion* aRegion) const
|
||||
{
|
||||
if (!aRegion) {
|
||||
return OutputFilterSpaceBounds();
|
||||
}
|
||||
nsIntRegion result;
|
||||
for (auto iter = aRegion->RectIter(); !iter.Done(); iter.Next()) {
|
||||
// FrameSpaceToFilterSpace rounds out, so this works.
|
||||
result.Or(result, FrameSpaceToFilterSpace(&iter.Get()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nsRegion
|
||||
nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRegion& aRegion) const
|
||||
{
|
||||
nsRegion result;
|
||||
for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
|
||||
// FilterSpaceToFrameSpace rounds out, so this works.
|
||||
result.Or(result, FilterSpaceToFrameSpace(iter.Get()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
gfxMatrix
|
||||
nsFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const
|
||||
{
|
||||
if (!mTargetFrame) {
|
||||
return gfxMatrix();
|
||||
}
|
||||
return gfxMatrix::Translation(-nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mTargetFrame));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue