tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SVGFilterInstance.cpp (15843B)


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