LCOV - code coverage report
Current view: top level - layout/svg - nsSVGFilterInstance.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 203 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 12 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : // Main header first:
       7             : #include "nsSVGFilterInstance.h"
       8             : 
       9             : // Keep others in (case-insensitive) order:
      10             : #include "gfxPlatform.h"
      11             : #include "gfxUtils.h"
      12             : #include "nsSVGDisplayableFrame.h"
      13             : #include "mozilla/dom/HTMLCanvasElement.h"
      14             : #include "mozilla/dom/SVGFilterElement.h"
      15             : #include "nsReferencedElement.h"
      16             : #include "nsSVGEffects.h"
      17             : #include "nsSVGFilterFrame.h"
      18             : #include "nsSVGUtils.h"
      19             : #include "SVGContentUtils.h"
      20             : #include "FilterSupport.h"
      21             : #include "gfx2DGlue.h"
      22             : 
      23             : using namespace mozilla;
      24             : using namespace mozilla::dom;
      25             : using namespace mozilla::gfx;
      26             : 
      27           0 : nsSVGFilterInstance::nsSVGFilterInstance(const nsStyleFilter& aFilter,
      28             :                                          nsIFrame* aTargetFrame,
      29             :                                          nsIContent* aTargetContent,
      30             :                                          const UserSpaceMetrics& aMetrics,
      31             :                                          const gfxRect& aTargetBBox,
      32           0 :                                          const gfxSize& aUserSpaceToFilterSpaceScale) :
      33             :   mFilter(aFilter),
      34             :   mTargetContent(aTargetContent),
      35             :   mMetrics(aMetrics),
      36             :   mTargetBBox(aTargetBBox),
      37             :   mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale),
      38             :   mSourceAlphaAvailable(false),
      39           0 :   mInitialized(false) {
      40             : 
      41             :   // Get the filter frame.
      42           0 :   mFilterFrame = GetFilterFrame(aTargetFrame);
      43           0 :   if (!mFilterFrame) {
      44           0 :     return;
      45             :   }
      46             : 
      47             :   // Get the filter element.
      48           0 :   mFilterElement = mFilterFrame->GetFilterContent();
      49           0 :   if (!mFilterElement) {
      50           0 :     NS_NOTREACHED("filter frame should have a related element");
      51           0 :     return;
      52             :   }
      53             : 
      54           0 :   mPrimitiveUnits =
      55           0 :     mFilterFrame->GetEnumValue(SVGFilterElement::PRIMITIVEUNITS);
      56             : 
      57           0 :   if (!ComputeBounds()) {
      58           0 :     return;
      59             :   }
      60             : 
      61           0 :   mInitialized = true;
      62             : }
      63             : 
      64             : bool
      65           0 : nsSVGFilterInstance::ComputeBounds()
      66             : {
      67             :   // XXX if filterUnits is set (or has defaulted) to objectBoundingBox, we
      68             :   // should send a warning to the error console if the author has used lengths
      69             :   // with units. This is a common mistake and can result in the filter region
      70             :   // being *massive* below (because we ignore the units and interpret the number
      71             :   // as a factor of the bbox width/height). We should also send a warning if the
      72             :   // user uses a number without units (a future SVG spec should really
      73             :   // deprecate that, since it's too confusing for a bare number to be sometimes
      74             :   // interpreted as a fraction of the bounding box and sometimes as user-space
      75             :   // units). So really only percentage values should be used in this case.
      76             : 
      77             :   // Set the user space bounds (i.e. the filter region in user space).
      78             :   nsSVGLength2 XYWH[4];
      79             :   static_assert(sizeof(mFilterElement->mLengthAttributes) == sizeof(XYWH),
      80             :                 "XYWH size incorrect");
      81           0 :   memcpy(XYWH, mFilterElement->mLengthAttributes,
      82           0 :          sizeof(mFilterElement->mLengthAttributes));
      83           0 :   XYWH[0] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_X);
      84           0 :   XYWH[1] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_Y);
      85           0 :   XYWH[2] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_WIDTH);
      86           0 :   XYWH[3] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_HEIGHT);
      87             :   uint16_t filterUnits =
      88           0 :     mFilterFrame->GetEnumValue(SVGFilterElement::FILTERUNITS);
      89             :   gfxRect userSpaceBounds =
      90           0 :     nsSVGUtils::GetRelativeRect(filterUnits, XYWH, mTargetBBox, mMetrics);
      91             : 
      92             :   // Transform the user space bounds to filter space, so we
      93             :   // can align them with the pixel boundries of the offscreen surface.
      94             :   // The offscreen surface has the same scale as filter space.
      95           0 :   gfxRect filterSpaceBounds = UserSpaceToFilterSpace(userSpaceBounds);
      96           0 :   filterSpaceBounds.RoundOut();
      97           0 :   if (filterSpaceBounds.width <= 0 || filterSpaceBounds.height <= 0) {
      98             :     // 0 disables rendering, < 0 is error. dispatch error console warning
      99             :     // or error as appropriate.
     100           0 :     return false;
     101             :   }
     102             : 
     103             :   // Set the filter space bounds.
     104           0 :   if (!gfxUtils::GfxRectToIntRect(filterSpaceBounds, &mFilterSpaceBounds)) {
     105             :     // The filter region is way too big if there is float -> int overflow.
     106           0 :     return false;
     107             :   }
     108             : 
     109           0 :   return true;
     110             : }
     111             : 
     112             : nsSVGFilterFrame*
     113           0 : nsSVGFilterInstance::GetFilterFrame(nsIFrame* aTargetFrame)
     114             : {
     115           0 :   if (mFilter.GetType() != NS_STYLE_FILTER_URL) {
     116             :     // The filter is not an SVG reference filter.
     117           0 :     return nullptr;
     118             :   }
     119             : 
     120             :   // Get the target element to use as a point of reference for looking up the
     121             :   // filter element.
     122           0 :   if (!mTargetContent) {
     123           0 :     return nullptr;
     124             :   }
     125             : 
     126             :   // aTargetFrame can be null if this filter belongs to a
     127             :   // CanvasRenderingContext2D.
     128             :   nsCOMPtr<nsIURI> url = aTargetFrame
     129           0 :     ? nsSVGEffects::GetFilterURI(aTargetFrame, mFilter)
     130           0 :     : mFilter.GetURL()->ResolveLocalRef(mTargetContent);
     131             : 
     132           0 :   if (!url) {
     133           0 :     NS_NOTREACHED("an nsStyleFilter of type URL should have a non-null URL");
     134           0 :     return nullptr;
     135             :   }
     136             : 
     137             :   // Look up the filter element by URL.
     138           0 :   nsReferencedElement filterElement;
     139           0 :   bool watch = false;
     140           0 :   filterElement.Reset(mTargetContent, url, watch);
     141           0 :   Element* element = filterElement.get();
     142           0 :   if (!element) {
     143             :     // The URL points to no element.
     144           0 :     return nullptr;
     145             :   }
     146             : 
     147             :   // Get the frame of the filter element.
     148           0 :   nsIFrame* frame = element->GetPrimaryFrame();
     149           0 :   if (!frame || !frame->IsSVGFilterFrame()) {
     150             :     // The URL points to an element that's not an SVG filter element, or to an
     151             :     // element that hasn't had its frame constructed yet.
     152           0 :     return nullptr;
     153             :   }
     154             : 
     155           0 :   return static_cast<nsSVGFilterFrame*>(frame);
     156             : }
     157             : 
     158             : float
     159           0 : nsSVGFilterInstance::GetPrimitiveNumber(uint8_t aCtxType, float aValue) const
     160             : {
     161             :   nsSVGLength2 val;
     162           0 :   val.Init(aCtxType, 0xff, aValue,
     163           0 :            nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
     164             : 
     165             :   float value;
     166           0 :   if (mPrimitiveUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
     167           0 :     value = nsSVGUtils::ObjectSpace(mTargetBBox, &val);
     168             :   } else {
     169           0 :     value = nsSVGUtils::UserSpace(mMetrics, &val);
     170             :   }
     171             : 
     172           0 :   switch (aCtxType) {
     173             :   case SVGContentUtils::X:
     174           0 :     return value * mUserSpaceToFilterSpaceScale.width;
     175             :   case SVGContentUtils::Y:
     176           0 :     return value * mUserSpaceToFilterSpaceScale.height;
     177             :   case SVGContentUtils::XY:
     178             :   default:
     179           0 :     return value * SVGContentUtils::ComputeNormalizedHypotenuse(
     180           0 :                      mUserSpaceToFilterSpaceScale.width,
     181           0 :                      mUserSpaceToFilterSpaceScale.height);
     182             :   }
     183             : }
     184             : 
     185             : Point3D
     186           0 : nsSVGFilterInstance::ConvertLocation(const Point3D& aPoint) const
     187             : {
     188             :   nsSVGLength2 val[4];
     189           0 :   val[0].Init(SVGContentUtils::X, 0xff, aPoint.x,
     190           0 :               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
     191           0 :   val[1].Init(SVGContentUtils::Y, 0xff, aPoint.y,
     192           0 :               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
     193             :   // Dummy width/height values
     194             :   val[2].Init(SVGContentUtils::X, 0xff, 0,
     195           0 :               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
     196             :   val[3].Init(SVGContentUtils::Y, 0xff, 0,
     197           0 :               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
     198             : 
     199           0 :   gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
     200           0 :     val, mTargetBBox, mMetrics);
     201           0 :   gfxRect r = UserSpaceToFilterSpace(feArea);
     202           0 :   return Point3D(r.x, r.y, GetPrimitiveNumber(SVGContentUtils::XY, aPoint.z));
     203             : }
     204             : 
     205             : gfxRect
     206           0 : nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
     207             : {
     208           0 :   gfxRect filterSpaceRect = aUserSpaceRect;
     209           0 :   filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
     210           0 :                         mUserSpaceToFilterSpaceScale.height);
     211           0 :   return filterSpaceRect;
     212             : }
     213             : 
     214             : IntRect
     215           0 : nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(nsSVGFE* aFilterElement,
     216             :                                                      const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
     217             :                                                      const nsTArray<int32_t>& aInputIndices)
     218             : {
     219           0 :   nsSVGFE* fE = aFilterElement;
     220             : 
     221           0 :   IntRect defaultFilterSubregion(0,0,0,0);
     222           0 :   if (fE->SubregionIsUnionOfRegions()) {
     223           0 :     for (uint32_t i = 0; i < aInputIndices.Length(); ++i) {
     224           0 :       int32_t inputIndex = aInputIndices[i];
     225           0 :       bool isStandardInput = inputIndex < 0 || inputIndex == mSourceGraphicIndex;
     226             :       IntRect inputSubregion = isStandardInput ?
     227             :         mFilterSpaceBounds :
     228           0 :         aPrimitiveDescrs[inputIndex].PrimitiveSubregion();
     229             : 
     230           0 :       defaultFilterSubregion = defaultFilterSubregion.Union(inputSubregion);
     231             :     }
     232             :   } else {
     233           0 :     defaultFilterSubregion = mFilterSpaceBounds;
     234             :   }
     235             : 
     236           0 :   gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
     237           0 :     &fE->mLengthAttributes[nsSVGFE::ATTR_X], mTargetBBox, mMetrics);
     238           0 :   Rect region = ToRect(UserSpaceToFilterSpace(feArea));
     239             : 
     240           0 :   if (!fE->mLengthAttributes[nsSVGFE::ATTR_X].IsExplicitlySet())
     241           0 :     region.x = defaultFilterSubregion.X();
     242           0 :   if (!fE->mLengthAttributes[nsSVGFE::ATTR_Y].IsExplicitlySet())
     243           0 :     region.y = defaultFilterSubregion.Y();
     244           0 :   if (!fE->mLengthAttributes[nsSVGFE::ATTR_WIDTH].IsExplicitlySet())
     245           0 :     region.width = defaultFilterSubregion.Width();
     246           0 :   if (!fE->mLengthAttributes[nsSVGFE::ATTR_HEIGHT].IsExplicitlySet())
     247           0 :     region.height = defaultFilterSubregion.Height();
     248             : 
     249             :   // We currently require filter primitive subregions to be pixel-aligned.
     250             :   // Following the spec, any pixel partially in the region is included
     251             :   // in the region.
     252           0 :   region.RoundOut();
     253           0 :   return RoundedToInt(region);
     254             : }
     255             : 
     256             : void
     257           0 : nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
     258             :                                          const nsTArray<int32_t>& aInputIndices,
     259             :                                          bool aFilterInputIsTainted,
     260             :                                          nsTArray<bool>& aOutInputsAreTainted)
     261             : {
     262           0 :   for (uint32_t i = 0; i < aInputIndices.Length(); i++) {
     263           0 :     int32_t inputIndex = aInputIndices[i];
     264           0 :     if (inputIndex < 0) {
     265           0 :       aOutInputsAreTainted.AppendElement(aFilterInputIsTainted);
     266             :     } else {
     267           0 :       aOutInputsAreTainted.AppendElement(aPrimitiveDescrs[inputIndex].IsTainted());
     268             :     }
     269             :   }
     270           0 : }
     271             : 
     272             : static int32_t
     273           0 : GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
     274             : {
     275           0 :   uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length();
     276           0 :   return !numPrimitiveDescrs ?
     277             :     FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
     278           0 :     numPrimitiveDescrs - 1;
     279             : }
     280             : 
     281             : int32_t
     282           0 : nsSVGFilterInstance::GetOrCreateSourceAlphaIndex(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
     283             : {
     284             :   // If the SourceAlpha index has already been determined or created for this
     285             :   // SVG filter, just return it.
     286           0 :   if (mSourceAlphaAvailable)
     287           0 :     return mSourceAlphaIndex;
     288             : 
     289             :   // If this is the first filter in the chain, we can just use the
     290             :   // kPrimitiveIndexSourceAlpha keyword to refer to the SourceAlpha of the
     291             :   // original image.
     292           0 :   if (mSourceGraphicIndex < 0) {
     293           0 :     mSourceAlphaIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha;
     294           0 :     mSourceAlphaAvailable = true;
     295           0 :     return mSourceAlphaIndex;
     296             :   }
     297             : 
     298             :   // Otherwise, create a primitive description to turn the previous filter's
     299             :   // output into a SourceAlpha input.
     300           0 :   FilterPrimitiveDescription descr(PrimitiveType::ToAlpha);
     301           0 :   descr.SetInputPrimitive(0, mSourceGraphicIndex);
     302             : 
     303             :   const FilterPrimitiveDescription& sourcePrimitiveDescr =
     304           0 :     aPrimitiveDescrs[mSourceGraphicIndex];
     305           0 :   descr.SetPrimitiveSubregion(sourcePrimitiveDescr.PrimitiveSubregion());
     306           0 :   descr.SetIsTainted(sourcePrimitiveDescr.IsTainted());
     307             : 
     308           0 :   ColorSpace colorSpace = sourcePrimitiveDescr.OutputColorSpace();
     309           0 :   descr.SetInputColorSpace(0, colorSpace);
     310           0 :   descr.SetOutputColorSpace(colorSpace);
     311             : 
     312           0 :   aPrimitiveDescrs.AppendElement(descr);
     313           0 :   mSourceAlphaIndex = aPrimitiveDescrs.Length() - 1;
     314           0 :   mSourceAlphaAvailable = true;
     315           0 :   return mSourceAlphaIndex;
     316             : }
     317             : 
     318             : nsresult
     319           0 : nsSVGFilterInstance::GetSourceIndices(nsSVGFE* aPrimitiveElement,
     320             :                                       nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
     321             :                                       const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
     322             :                                       nsTArray<int32_t>& aSourceIndices)
     323             : {
     324           0 :   AutoTArray<nsSVGStringInfo,2> sources;
     325           0 :   aPrimitiveElement->GetSourceImageNames(sources);
     326             : 
     327           0 :   for (uint32_t j = 0; j < sources.Length(); j++) {
     328           0 :     nsAutoString str;
     329           0 :     sources[j].mString->GetAnimValue(str, sources[j].mElement);
     330             : 
     331           0 :     int32_t sourceIndex = 0;
     332           0 :     if (str.EqualsLiteral("SourceGraphic")) {
     333           0 :       sourceIndex = mSourceGraphicIndex;
     334           0 :     } else if (str.EqualsLiteral("SourceAlpha")) {
     335           0 :       sourceIndex = GetOrCreateSourceAlphaIndex(aPrimitiveDescrs);
     336           0 :     } else if (str.EqualsLiteral("FillPaint")) {
     337           0 :       sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexFillPaint;
     338           0 :     } else if (str.EqualsLiteral("StrokePaint")) {
     339           0 :       sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexStrokePaint;
     340           0 :     } else if (str.EqualsLiteral("BackgroundImage") ||
     341           0 :                str.EqualsLiteral("BackgroundAlpha")) {
     342           0 :       return NS_ERROR_NOT_IMPLEMENTED;
     343           0 :     } else if (str.EqualsLiteral("")) {
     344           0 :       sourceIndex = GetLastResultIndex(aPrimitiveDescrs);
     345             :     } else {
     346           0 :       bool inputExists = aImageTable.Get(str, &sourceIndex);
     347           0 :       if (!inputExists)
     348           0 :         return NS_ERROR_FAILURE;
     349             :     }
     350             : 
     351           0 :     aSourceIndices.AppendElement(sourceIndex);
     352             :   }
     353           0 :   return NS_OK;
     354             : }
     355             : 
     356             : nsresult
     357           0 : nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
     358             :                                      nsTArray<RefPtr<SourceSurface>>& aInputImages,
     359             :                                      bool aInputIsTainted)
     360             : {
     361           0 :   mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs);
     362             : 
     363             :   // Clip previous filter's output to this filter's filter region.
     364           0 :   if (mSourceGraphicIndex >= 0) {
     365           0 :     FilterPrimitiveDescription& sourceDescr = aPrimitiveDescrs[mSourceGraphicIndex];
     366           0 :     sourceDescr.SetPrimitiveSubregion(sourceDescr.PrimitiveSubregion().Intersect(mFilterSpaceBounds));
     367             :   }
     368             : 
     369             :   // Get the filter primitive elements.
     370           0 :   nsTArray<RefPtr<nsSVGFE> > primitives;
     371           0 :   for (nsIContent* child = mFilterElement->nsINode::GetFirstChild();
     372           0 :        child;
     373           0 :        child = child->GetNextSibling()) {
     374           0 :     RefPtr<nsSVGFE> primitive;
     375           0 :     CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
     376           0 :     if (primitive) {
     377           0 :       primitives.AppendElement(primitive);
     378             :     }
     379             :   }
     380             : 
     381             :   // Maps source image name to source index.
     382           0 :   nsDataHashtable<nsStringHashKey, int32_t> imageTable(8);
     383             : 
     384             :   // The principal that we check principals of any loaded images against.
     385           0 :   nsCOMPtr<nsIPrincipal> principal = mTargetContent->NodePrincipal();
     386             : 
     387           0 :   for (uint32_t primitiveElementIndex = 0;
     388           0 :        primitiveElementIndex < primitives.Length();
     389             :        ++primitiveElementIndex) {
     390           0 :     nsSVGFE* filter = primitives[primitiveElementIndex];
     391             : 
     392           0 :     AutoTArray<int32_t,2> sourceIndices;
     393           0 :     nsresult rv = GetSourceIndices(filter, aPrimitiveDescrs, imageTable, sourceIndices);
     394           0 :     if (NS_FAILED(rv)) {
     395           0 :       return rv;
     396             :     }
     397             : 
     398             :     IntRect primitiveSubregion =
     399           0 :       ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices);
     400             : 
     401           0 :     nsTArray<bool> sourcesAreTainted;
     402           0 :     GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, aInputIsTainted, sourcesAreTainted);
     403             : 
     404             :     FilterPrimitiveDescription descr =
     405           0 :       filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages);
     406             : 
     407           0 :     descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal));
     408           0 :     descr.SetFilterSpaceBounds(mFilterSpaceBounds);
     409           0 :     descr.SetPrimitiveSubregion(primitiveSubregion.Intersect(descr.FilterSpaceBounds()));
     410             : 
     411           0 :     for (uint32_t i = 0; i < sourceIndices.Length(); i++) {
     412           0 :       int32_t inputIndex = sourceIndices[i];
     413           0 :       descr.SetInputPrimitive(i, inputIndex);
     414             : 
     415             :       ColorSpace inputColorSpace = inputIndex >= 0
     416           0 :         ? aPrimitiveDescrs[inputIndex].OutputColorSpace()
     417           0 :         : ColorSpace(ColorSpace::SRGB);
     418             : 
     419           0 :       ColorSpace desiredInputColorSpace = filter->GetInputColorSpace(i, inputColorSpace);
     420           0 :       descr.SetInputColorSpace(i, desiredInputColorSpace);
     421           0 :       if (i == 0) {
     422             :         // the output color space is whatever in1 is if there is an in1
     423           0 :         descr.SetOutputColorSpace(desiredInputColorSpace);
     424             :       }
     425             :     }
     426             : 
     427           0 :     if (sourceIndices.Length() == 0) {
     428           0 :       descr.SetOutputColorSpace(filter->GetOutputColorSpace());
     429             :     }
     430             : 
     431           0 :     aPrimitiveDescrs.AppendElement(descr);
     432           0 :     uint32_t primitiveDescrIndex = aPrimitiveDescrs.Length() - 1;
     433             : 
     434           0 :     nsAutoString str;
     435           0 :     filter->GetResultImageName().GetAnimValue(str, filter);
     436           0 :     imageTable.Put(str, primitiveDescrIndex);
     437             :   }
     438             : 
     439           0 :   return NS_OK;
     440             : }

Generated by: LCOV version 1.13