diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /layout/svg/nsSVGFilterInstance.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/svg/nsSVGFilterInstance.cpp')
-rw-r--r-- | layout/svg/nsSVGFilterInstance.cpp | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/layout/svg/nsSVGFilterInstance.cpp b/layout/svg/nsSVGFilterInstance.cpp new file mode 100644 index 000000000..59b5a27ba --- /dev/null +++ b/layout/svg/nsSVGFilterInstance.cpp @@ -0,0 +1,453 @@ +/* -*- 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 "nsSVGFilterInstance.h" + +// Keep others in (case-insensitive) order: +#include "gfxPlatform.h" +#include "gfxUtils.h" +#include "nsISVGChildFrame.h" +#include "mozilla/dom/HTMLCanvasElement.h" +#include "mozilla/dom/SVGFilterElement.h" +#include "nsReferencedElement.h" +#include "nsSVGFilterFrame.h" +#include "nsSVGUtils.h" +#include "SVGContentUtils.h" +#include "FilterSupport.h" +#include "gfx2DGlue.h" + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::gfx; + +nsSVGFilterInstance::nsSVGFilterInstance(const nsStyleFilter& aFilter, + nsIFrame* aTargetFrame, + nsIContent* aTargetContent, + const UserSpaceMetrics& aMetrics, + const gfxRect& aTargetBBox, + const gfxSize& aUserSpaceToFilterSpaceScale, + const gfxSize& aFilterSpaceToUserSpaceScale) : + mFilter(aFilter), + mTargetContent(aTargetContent), + mMetrics(aMetrics), + mTargetBBox(aTargetBBox), + mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale), + mFilterSpaceToUserSpaceScale(aFilterSpaceToUserSpaceScale), + mSourceAlphaAvailable(false), + mInitialized(false) { + + // Get the filter frame. + mFilterFrame = GetFilterFrame(aTargetFrame); + if (!mFilterFrame) { + return; + } + + // Get the filter element. + mFilterElement = mFilterFrame->GetFilterContent(); + if (!mFilterElement) { + NS_NOTREACHED("filter frame should have a related element"); + return; + } + + mPrimitiveUnits = + mFilterFrame->GetEnumValue(SVGFilterElement::PRIMITIVEUNITS); + + nsresult rv = ComputeBounds(); + if (NS_FAILED(rv)) { + return; + } + + mInitialized = true; +} + +nsresult +nsSVGFilterInstance::ComputeBounds() +{ + // XXX if filterUnits is set (or has defaulted) to objectBoundingBox, we + // should send a warning to the error console if the author has used lengths + // with units. This is a common mistake and can result in the filter region + // being *massive* below (because we ignore the units and interpret the number + // as a factor of the bbox width/height). We should also send a warning if the + // user uses a number without units (a future SVG spec should really + // deprecate that, since it's too confusing for a bare number to be sometimes + // interpreted as a fraction of the bounding box and sometimes as user-space + // units). So really only percentage values should be used in this case. + + // Set the user space bounds (i.e. the filter region in user space). + nsSVGLength2 XYWH[4]; + static_assert(sizeof(mFilterElement->mLengthAttributes) == sizeof(XYWH), + "XYWH size incorrect"); + memcpy(XYWH, mFilterElement->mLengthAttributes, + sizeof(mFilterElement->mLengthAttributes)); + XYWH[0] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_X); + XYWH[1] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_Y); + XYWH[2] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_WIDTH); + XYWH[3] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_HEIGHT); + uint16_t filterUnits = + mFilterFrame->GetEnumValue(SVGFilterElement::FILTERUNITS); + gfxRect userSpaceBounds = nsSVGUtils::GetRelativeRect(filterUnits, + XYWH, mTargetBBox, mMetrics); + + // Transform the user space bounds to filter space, so we + // can align them with the pixel boundries of the offscreen surface. + // The offscreen surface has the same scale as filter space. + gfxRect filterSpaceBounds = UserSpaceToFilterSpace(userSpaceBounds); + filterSpaceBounds.RoundOut(); + if (filterSpaceBounds.width <= 0 || filterSpaceBounds.height <= 0) { + // 0 disables rendering, < 0 is error. dispatch error console warning + // or error as appropriate. + return NS_ERROR_FAILURE; + } + + // Set the filter space bounds. + if (!gfxUtils::GfxRectToIntRect(filterSpaceBounds, &mFilterSpaceBounds)) { + // The filter region is way too big if there is float -> int overflow. + return NS_ERROR_FAILURE; + } + + mUserSpaceBounds = FilterSpaceToUserSpace(filterSpaceBounds); + + return NS_OK; +} + +nsSVGFilterFrame* +nsSVGFilterInstance::GetFilterFrame(nsIFrame* aTargetFrame) +{ + if (mFilter.GetType() != NS_STYLE_FILTER_URL) { + // The filter is not an SVG reference filter. + return nullptr; + } + + // Get the target element to use as a point of reference for looking up the + // filter element. + if (!mTargetContent) { + return nullptr; + } + + // aTargetFrame can be null if this filter belongs to a + // CanvasRenderingContext2D. + nsCOMPtr<nsIURI> url = aTargetFrame + ? nsSVGEffects::GetFilterURI(aTargetFrame, mFilter) + : mFilter.GetURL()->ResolveLocalRef(mTargetContent); + + if (!url) { + NS_NOTREACHED("an nsStyleFilter of type URL should have a non-null URL"); + return nullptr; + } + + // Look up the filter element by URL. + nsReferencedElement filterElement; + bool watch = false; + filterElement.Reset(mTargetContent, url, watch); + Element* element = filterElement.get(); + if (!element) { + // The URL points to no element. + return nullptr; + } + + // Get the frame of the filter element. + nsIFrame* frame = element->GetPrimaryFrame(); + if (!frame || frame->GetType() != nsGkAtoms::svgFilterFrame) { + // The URL points to an element that's not an SVG filter element, or to an + // element that hasn't had its frame constructed yet. + return nullptr; + } + + return static_cast<nsSVGFilterFrame*>(frame); +} + +float +nsSVGFilterInstance::GetPrimitiveNumber(uint8_t aCtxType, float aValue) const +{ + nsSVGLength2 val; + val.Init(aCtxType, 0xff, aValue, + nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER); + + float value; + if (mPrimitiveUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { + value = nsSVGUtils::ObjectSpace(mTargetBBox, &val); + } else { + value = nsSVGUtils::UserSpace(mMetrics, &val); + } + + switch (aCtxType) { + case SVGContentUtils::X: + return value * mUserSpaceToFilterSpaceScale.width; + case SVGContentUtils::Y: + return value * mUserSpaceToFilterSpaceScale.height; + case SVGContentUtils::XY: + default: + return value * SVGContentUtils::ComputeNormalizedHypotenuse( + mUserSpaceToFilterSpaceScale.width, + mUserSpaceToFilterSpaceScale.height); + } +} + +Point3D +nsSVGFilterInstance::ConvertLocation(const Point3D& aPoint) const +{ + nsSVGLength2 val[4]; + val[0].Init(SVGContentUtils::X, 0xff, aPoint.x, + nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER); + val[1].Init(SVGContentUtils::Y, 0xff, aPoint.y, + nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER); + // Dummy width/height values + val[2].Init(SVGContentUtils::X, 0xff, 0, + nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER); + val[3].Init(SVGContentUtils::Y, 0xff, 0, + nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER); + + gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits, + val, mTargetBBox, mMetrics); + gfxRect r = UserSpaceToFilterSpace(feArea); + return Point3D(r.x, r.y, GetPrimitiveNumber(SVGContentUtils::XY, aPoint.z)); +} + +gfxRect +nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const +{ + gfxRect filterSpaceRect = aUserSpaceRect; + filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width, + mUserSpaceToFilterSpaceScale.height); + return filterSpaceRect; +} + +gfxRect +nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const +{ + gfxRect userSpaceRect = aFilterSpaceRect; + userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width, + mFilterSpaceToUserSpaceScale.height); + return userSpaceRect; +} + +IntRect +nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(nsSVGFE* aFilterElement, + const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, + const nsTArray<int32_t>& aInputIndices) +{ + nsSVGFE* fE = aFilterElement; + + IntRect defaultFilterSubregion(0,0,0,0); + if (fE->SubregionIsUnionOfRegions()) { + for (uint32_t i = 0; i < aInputIndices.Length(); ++i) { + int32_t inputIndex = aInputIndices[i]; + bool isStandardInput = inputIndex < 0 || inputIndex == mSourceGraphicIndex; + IntRect inputSubregion = isStandardInput ? + mFilterSpaceBounds : + aPrimitiveDescrs[inputIndex].PrimitiveSubregion(); + + defaultFilterSubregion = defaultFilterSubregion.Union(inputSubregion); + } + } else { + defaultFilterSubregion = mFilterSpaceBounds; + } + + gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits, + &fE->mLengthAttributes[nsSVGFE::ATTR_X], mTargetBBox, mMetrics); + Rect region = ToRect(UserSpaceToFilterSpace(feArea)); + + if (!fE->mLengthAttributes[nsSVGFE::ATTR_X].IsExplicitlySet()) + region.x = defaultFilterSubregion.X(); + if (!fE->mLengthAttributes[nsSVGFE::ATTR_Y].IsExplicitlySet()) + region.y = defaultFilterSubregion.Y(); + if (!fE->mLengthAttributes[nsSVGFE::ATTR_WIDTH].IsExplicitlySet()) + region.width = defaultFilterSubregion.Width(); + if (!fE->mLengthAttributes[nsSVGFE::ATTR_HEIGHT].IsExplicitlySet()) + region.height = defaultFilterSubregion.Height(); + + // We currently require filter primitive subregions to be pixel-aligned. + // Following the spec, any pixel partially in the region is included + // in the region. + region.RoundOut(); + return RoundedToInt(region); +} + +void +nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, + const nsTArray<int32_t>& aInputIndices, + bool aFilterInputIsTainted, + nsTArray<bool>& aOutInputsAreTainted) +{ + for (uint32_t i = 0; i < aInputIndices.Length(); i++) { + int32_t inputIndex = aInputIndices[i]; + if (inputIndex < 0) { + aOutInputsAreTainted.AppendElement(aFilterInputIsTainted); + } else { + aOutInputsAreTainted.AppendElement(aPrimitiveDescrs[inputIndex].IsTainted()); + } + } +} + +static int32_t +GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) +{ + uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length(); + return !numPrimitiveDescrs ? + FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic : + numPrimitiveDescrs - 1; +} + +int32_t +nsSVGFilterInstance::GetOrCreateSourceAlphaIndex(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) +{ + // If the SourceAlpha index has already been determined or created for this + // SVG filter, just return it. + if (mSourceAlphaAvailable) + return mSourceAlphaIndex; + + // If this is the first filter in the chain, we can just use the + // kPrimitiveIndexSourceAlpha keyword to refer to the SourceAlpha of the + // original image. + if (mSourceGraphicIndex < 0) { + mSourceAlphaIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha; + mSourceAlphaAvailable = true; + return mSourceAlphaIndex; + } + + // Otherwise, create a primitive description to turn the previous filter's + // output into a SourceAlpha input. + FilterPrimitiveDescription descr(PrimitiveType::ToAlpha); + descr.SetInputPrimitive(0, mSourceGraphicIndex); + + const FilterPrimitiveDescription& sourcePrimitiveDescr = + aPrimitiveDescrs[mSourceGraphicIndex]; + descr.SetPrimitiveSubregion(sourcePrimitiveDescr.PrimitiveSubregion()); + descr.SetIsTainted(sourcePrimitiveDescr.IsTainted()); + + ColorSpace colorSpace = sourcePrimitiveDescr.OutputColorSpace(); + descr.SetInputColorSpace(0, colorSpace); + descr.SetOutputColorSpace(colorSpace); + + aPrimitiveDescrs.AppendElement(descr); + mSourceAlphaIndex = aPrimitiveDescrs.Length() - 1; + mSourceAlphaAvailable = true; + return mSourceAlphaIndex; +} + +nsresult +nsSVGFilterInstance::GetSourceIndices(nsSVGFE* aPrimitiveElement, + nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, + const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable, + nsTArray<int32_t>& aSourceIndices) +{ + AutoTArray<nsSVGStringInfo,2> sources; + aPrimitiveElement->GetSourceImageNames(sources); + + for (uint32_t j = 0; j < sources.Length(); j++) { + nsAutoString str; + sources[j].mString->GetAnimValue(str, sources[j].mElement); + + int32_t sourceIndex = 0; + if (str.EqualsLiteral("SourceGraphic")) { + sourceIndex = mSourceGraphicIndex; + } else if (str.EqualsLiteral("SourceAlpha")) { + sourceIndex = GetOrCreateSourceAlphaIndex(aPrimitiveDescrs); + } else if (str.EqualsLiteral("FillPaint")) { + sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexFillPaint; + } else if (str.EqualsLiteral("StrokePaint")) { + sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexStrokePaint; + } else if (str.EqualsLiteral("BackgroundImage") || + str.EqualsLiteral("BackgroundAlpha")) { + return NS_ERROR_NOT_IMPLEMENTED; + } else if (str.EqualsLiteral("")) { + sourceIndex = GetLastResultIndex(aPrimitiveDescrs); + } else { + bool inputExists = aImageTable.Get(str, &sourceIndex); + if (!inputExists) + return NS_ERROR_FAILURE; + } + + aSourceIndices.AppendElement(sourceIndex); + } + return NS_OK; +} + +nsresult +nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, + nsTArray<RefPtr<SourceSurface>>& aInputImages, + bool aInputIsTainted) +{ + mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs); + + // Clip previous filter's output to this filter's filter region. + if (mSourceGraphicIndex >= 0) { + FilterPrimitiveDescription& sourceDescr = aPrimitiveDescrs[mSourceGraphicIndex]; + sourceDescr.SetPrimitiveSubregion(sourceDescr.PrimitiveSubregion().Intersect(mFilterSpaceBounds)); + } + + // Get the filter primitive elements. + nsTArray<RefPtr<nsSVGFE> > primitives; + for (nsIContent* child = mFilterElement->nsINode::GetFirstChild(); + child; + child = child->GetNextSibling()) { + RefPtr<nsSVGFE> primitive; + CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive)); + if (primitive) { + primitives.AppendElement(primitive); + } + } + + // Maps source image name to source index. + nsDataHashtable<nsStringHashKey, int32_t> imageTable(8); + + // The principal that we check principals of any loaded images against. + nsCOMPtr<nsIPrincipal> principal = mTargetContent->NodePrincipal(); + + for (uint32_t primitiveElementIndex = 0; + primitiveElementIndex < primitives.Length(); + ++primitiveElementIndex) { + nsSVGFE* filter = primitives[primitiveElementIndex]; + + AutoTArray<int32_t,2> sourceIndices; + nsresult rv = GetSourceIndices(filter, aPrimitiveDescrs, imageTable, sourceIndices); + if (NS_FAILED(rv)) { + return rv; + } + + IntRect primitiveSubregion = + ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices); + + nsTArray<bool> sourcesAreTainted; + GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, aInputIsTainted, sourcesAreTainted); + + FilterPrimitiveDescription descr = + filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages); + + descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal)); + descr.SetFilterSpaceBounds(mFilterSpaceBounds); + descr.SetPrimitiveSubregion(primitiveSubregion.Intersect(descr.FilterSpaceBounds())); + + for (uint32_t i = 0; i < sourceIndices.Length(); i++) { + int32_t inputIndex = sourceIndices[i]; + descr.SetInputPrimitive(i, inputIndex); + + ColorSpace inputColorSpace = inputIndex >= 0 + ? aPrimitiveDescrs[inputIndex].OutputColorSpace() + : ColorSpace(ColorSpace::SRGB); + + ColorSpace desiredInputColorSpace = filter->GetInputColorSpace(i, inputColorSpace); + descr.SetInputColorSpace(i, desiredInputColorSpace); + if (i == 0) { + // the output color space is whatever in1 is if there is an in1 + descr.SetOutputColorSpace(desiredInputColorSpace); + } + } + + if (sourceIndices.Length() == 0) { + descr.SetOutputColorSpace(filter->GetOutputColorSpace()); + } + + aPrimitiveDescrs.AppendElement(descr); + uint32_t primitiveDescrIndex = aPrimitiveDescrs.Length() - 1; + + nsAutoString str; + filter->GetResultImageName().GetAnimValue(str, filter); + imageTable.Put(str, primitiveDescrIndex); + } + + return NS_OK; +} |