FilterInstance.cpp (83712B)
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 "FilterInstance.h" 9 10 // MFBT headers next: 11 #include "mozilla/UniquePtr.h" 12 13 // Keep others in (case-insensitive) order: 14 #include "CSSFilterInstance.h" 15 #include "FilterSupport.h" 16 #include "ImgDrawResult.h" 17 #include "SVGContentUtils.h" 18 #include "SVGIntegrationUtils.h" 19 #include "gfx2DGlue.h" 20 #include "gfxContext.h" 21 #include "gfxPlatform.h" 22 #include "gfxUtils.h" 23 #include "mozilla/ISVGDisplayableFrame.h" 24 #include "mozilla/SVGFilterInstance.h" 25 #include "mozilla/SVGObserverUtils.h" 26 #include "mozilla/SVGUtils.h" 27 #include "mozilla/StaticPrefs_gfx.h" 28 #include "mozilla/dom/Document.h" 29 #include "mozilla/gfx/Filters.h" 30 #include "mozilla/gfx/Helpers.h" 31 #include "mozilla/gfx/Logging.h" 32 #include "mozilla/gfx/PatternHelpers.h" 33 #include "nsLayoutUtils.h" 34 35 using namespace mozilla::dom; 36 using namespace mozilla::gfx; 37 using namespace mozilla::image; 38 39 namespace mozilla { 40 41 FilterDescription FilterInstance::GetFilterDescription( 42 nsIContent* aFilteredElement, Span<const StyleFilter> aFilterChain, 43 ISVGFilterObserverList* aFiltersObserverList, bool aFilterInputIsTainted, 44 const UserSpaceMetrics& aMetrics, const gfxRect& aBBox, 45 nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages) { 46 gfxMatrix identity; 47 48 nsTArray<SVGFilterFrame*> filterFrames; 49 if (SVGObserverUtils::GetAndObserveFilters(aFiltersObserverList, 50 &filterFrames) == 51 SVGObserverUtils::eHasRefsSomeInvalid) { 52 return FilterDescription(); 53 } 54 55 FilterInstance instance(nullptr, aFilteredElement, aMetrics, aFilterChain, 56 filterFrames, aFilterInputIsTainted, nullptr, 57 identity, nullptr, nullptr, nullptr, &aBBox); 58 if (!instance.IsInitialized()) { 59 return FilterDescription(); 60 } 61 return instance.ExtractDescriptionAndAdditionalImages(aOutAdditionalImages); 62 } 63 64 static UniquePtr<UserSpaceMetrics> UserSpaceMetricsForFrame(nsIFrame* aFrame) { 65 if (auto* element = SVGElement::FromNodeOrNull(aFrame->GetContent())) { 66 return MakeUnique<SVGElementMetrics>(element); 67 } 68 return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame); 69 } 70 71 void FilterInstance::PaintFilteredFrame( 72 nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilterChain, 73 const nsTArray<SVGFilterFrame*>& aFilterFrames, gfxContext* aCtx, 74 const SVGFilterPaintCallback& aPaintCallback, const nsRegion* aDirtyArea, 75 imgDrawingParams& aImgParams, float aOpacity, 76 const gfxRect* aOverrideBBox) { 77 UniquePtr<UserSpaceMetrics> metrics = 78 UserSpaceMetricsForFrame(aFilteredFrame); 79 80 gfxContextMatrixAutoSaveRestore autoSR(aCtx); 81 auto scaleFactors = aCtx->CurrentMatrixDouble().ScaleFactors(); 82 if (scaleFactors.xScale == 0 || scaleFactors.yScale == 0) { 83 return; 84 } 85 86 gfxMatrix scaleMatrix(scaleFactors.xScale, 0.0f, 0.0f, scaleFactors.yScale, 87 0.0f, 0.0f); 88 89 gfxMatrix reverseScaleMatrix = scaleMatrix; 90 DebugOnly<bool> invertible = reverseScaleMatrix.Invert(); 91 MOZ_ASSERT(invertible); 92 93 gfxMatrix scaleMatrixInDevUnits = 94 scaleMatrix * SVGUtils::GetCSSPxToDevPxMatrix(aFilteredFrame); 95 96 // Hardcode InputIsTainted to true because we don't want JS to be able to 97 // read the rendered contents of aFilteredFrame. 98 FilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), 99 *metrics, aFilterChain, aFilterFrames, 100 /* InputIsTainted */ true, aPaintCallback, 101 scaleMatrixInDevUnits, aDirtyArea, nullptr, nullptr, 102 aOverrideBBox); 103 if (instance.IsInitialized()) { 104 // Pull scale vector out of aCtx's transform, put all scale factors, which 105 // includes css and css-to-dev-px scale, into scaleMatrixInDevUnits. 106 aCtx->SetMatrixDouble(reverseScaleMatrix * aCtx->CurrentMatrixDouble()); 107 108 instance.Render(aCtx, aImgParams, aOpacity); 109 } else { 110 // Render the unfiltered contents. 111 aPaintCallback(*aCtx, aImgParams, nullptr, nullptr); 112 } 113 } 114 115 static mozilla::wr::ComponentTransferFuncType FuncTypeToWr(uint8_t aFuncType) { 116 MOZ_ASSERT(aFuncType != SVG_FECOMPONENTTRANSFER_SAME_AS_R); 117 switch (aFuncType) { 118 case SVG_FECOMPONENTTRANSFER_TYPE_TABLE: 119 return mozilla::wr::ComponentTransferFuncType::Table; 120 case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE: 121 return mozilla::wr::ComponentTransferFuncType::Discrete; 122 case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR: 123 return mozilla::wr::ComponentTransferFuncType::Linear; 124 case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA: 125 return mozilla::wr::ComponentTransferFuncType::Gamma; 126 case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY: 127 default: 128 return mozilla::wr::ComponentTransferFuncType::Identity; 129 } 130 MOZ_ASSERT_UNREACHABLE("all func types not handled?"); 131 return mozilla::wr::ComponentTransferFuncType::Identity; 132 } 133 134 WrFiltersStatus FilterInstance::BuildWebRenderFilters( 135 nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilters, 136 StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters, 137 const nsPoint& aOffsetForSVGFilters) { 138 WrFiltersStatus status = WrFiltersStatus::BLOB_FALLBACK; 139 if (StaticPrefs::gfx_webrender_svg_filter_effects()) { 140 status = 141 BuildWebRenderSVGFiltersImpl(aFilteredFrame, aFilters, aStyleFilterType, 142 aWrFilters, aOffsetForSVGFilters); 143 } 144 if (status == WrFiltersStatus::BLOB_FALLBACK) { 145 status = BuildWebRenderFiltersImpl(aFilteredFrame, aFilters, 146 aStyleFilterType, aWrFilters); 147 } 148 if (status == WrFiltersStatus::BLOB_FALLBACK) { 149 aFilteredFrame->PresContext()->Document()->SetUseCounter( 150 eUseCounter_custom_WrFilterFallback); 151 } 152 153 return status; 154 } 155 156 WrFiltersStatus FilterInstance::BuildWebRenderFiltersImpl( 157 nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilters, 158 StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters) { 159 aWrFilters.filters.Clear(); 160 aWrFilters.filter_datas.Clear(); 161 aWrFilters.values.Clear(); 162 aWrFilters.post_filters_clip = Nothing(); 163 164 nsIFrame* firstFrame = 165 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFilteredFrame); 166 167 nsTArray<SVGFilterFrame*> filterFrames; 168 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, &filterFrames, 169 aStyleFilterType) == 170 SVGObserverUtils::eHasRefsSomeInvalid) { 171 return WrFiltersStatus::UNSUPPORTED; 172 } 173 174 UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(firstFrame); 175 176 // TODO: simply using an identity matrix here, was pulling the scale from a 177 // gfx context for the non-wr path. 178 gfxMatrix scaleMatrix; 179 gfxMatrix scaleMatrixInDevUnits = 180 scaleMatrix * SVGUtils::GetCSSPxToDevPxMatrix(firstFrame); 181 182 // Hardcode inputIsTainted to true because we don't want JS to be able to 183 // read the rendered contents of aFilteredFrame. 184 FilterInstance instance(firstFrame, firstFrame->GetContent(), *metrics, 185 aFilters, filterFrames, /* inputIsTainted */ true, 186 nullptr, scaleMatrixInDevUnits, nullptr, nullptr, 187 nullptr, nullptr); 188 189 if (!instance.IsInitialized()) { 190 return WrFiltersStatus::UNSUPPORTED; 191 } 192 193 // If there are too many filters to render, then just pretend that we 194 // succeeded, and don't render any of them. 195 if (instance.mFilterDescription.mPrimitives.Length() > 196 StaticPrefs::gfx_webrender_max_filter_ops_per_chain()) { 197 return WrFiltersStatus::DISABLED_FOR_PERFORMANCE; 198 } 199 200 Maybe<IntRect> finalClip; 201 bool srgb = true; 202 // We currently apply the clip on the stacking context after applying filters, 203 // but primitive subregions imply clipping after each filter and not just the 204 // end of the chain. For some types of filter it doesn't matter, but for those 205 // which sample outside of the location of the destination pixel like blurs, 206 // only clipping after could produce incorrect results, so we bail out in this 207 // case. 208 // We can lift this restriction once we have added support for primitive 209 // subregions to WebRender's filters. 210 for (uint32_t i = 0; i < instance.mFilterDescription.mPrimitives.Length(); 211 i++) { 212 const auto& primitive = instance.mFilterDescription.mPrimitives[i]; 213 214 // WebRender only supports filters with one input. 215 if (primitive.NumberOfInputs() != 1) { 216 return WrFiltersStatus::BLOB_FALLBACK; 217 } 218 // The first primitive must have the source graphic as the input, all 219 // other primitives must have the prior primitive as the input, otherwise 220 // it's not supported by WebRender. 221 if (i == 0) { 222 if (primitive.InputPrimitiveIndex(0) != 223 FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic) { 224 return WrFiltersStatus::BLOB_FALLBACK; 225 } 226 } else if (primitive.InputPrimitiveIndex(0) != int32_t(i - 1)) { 227 return WrFiltersStatus::BLOB_FALLBACK; 228 } 229 230 bool previousSrgb = srgb; 231 bool primNeedsSrgb = primitive.InputColorSpace(0) == gfx::ColorSpace::SRGB; 232 if (srgb && !primNeedsSrgb) { 233 aWrFilters.filters.AppendElement(wr::FilterOp::SrgbToLinear()); 234 } else if (!srgb && primNeedsSrgb) { 235 aWrFilters.filters.AppendElement(wr::FilterOp::LinearToSrgb()); 236 } 237 srgb = primitive.OutputColorSpace() == gfx::ColorSpace::SRGB; 238 239 const PrimitiveAttributes& attr = primitive.Attributes(); 240 241 bool filterIsNoop = false; 242 243 if (attr.is<OpacityAttributes>()) { 244 float opacity = attr.as<OpacityAttributes>().mOpacity; 245 aWrFilters.filters.AppendElement(wr::FilterOp::Opacity( 246 wr::PropertyBinding<float>::Value(opacity), opacity)); 247 } else if (attr.is<ColorMatrixAttributes>()) { 248 const ColorMatrixAttributes& attributes = 249 attr.as<ColorMatrixAttributes>(); 250 251 float transposed[20]; 252 if (gfx::ComputeColorMatrix(attributes, transposed)) { 253 float matrix[20] = { 254 transposed[0], transposed[5], transposed[10], transposed[15], 255 transposed[1], transposed[6], transposed[11], transposed[16], 256 transposed[2], transposed[7], transposed[12], transposed[17], 257 transposed[3], transposed[8], transposed[13], transposed[18], 258 transposed[4], transposed[9], transposed[14], transposed[19]}; 259 260 aWrFilters.filters.AppendElement(wr::FilterOp::ColorMatrix(matrix)); 261 } else { 262 filterIsNoop = true; 263 } 264 } else if (attr.is<GaussianBlurAttributes>()) { 265 if (finalClip) { 266 // There's a clip that needs to apply before the blur filter, but 267 // WebRender only lets us apply the clip at the end of the filter 268 // chain. Clipping after a blur is not equivalent to clipping before 269 // a blur, so bail out. 270 return WrFiltersStatus::BLOB_FALLBACK; 271 } 272 273 const GaussianBlurAttributes& blur = attr.as<GaussianBlurAttributes>(); 274 275 const Size& stdDev = blur.mStdDeviation; 276 if (stdDev.width != 0.0 || stdDev.height != 0.0) { 277 aWrFilters.filters.AppendElement( 278 wr::FilterOp::Blur(stdDev.width, stdDev.height)); 279 } else { 280 filterIsNoop = true; 281 } 282 } else if (attr.is<DropShadowAttributes>()) { 283 if (finalClip) { 284 // We have to bail out for the same reason we would with a blur filter. 285 return WrFiltersStatus::BLOB_FALLBACK; 286 } 287 288 const DropShadowAttributes& shadow = attr.as<DropShadowAttributes>(); 289 290 const Size& stdDev = shadow.mStdDeviation; 291 if (stdDev.width != stdDev.height) { 292 return WrFiltersStatus::BLOB_FALLBACK; 293 } 294 295 sRGBColor color = shadow.mColor; 296 if (!primNeedsSrgb) { 297 color = FilterWrappers::SRGBToLinearRGB(color); 298 } 299 wr::Shadow wrShadow; 300 wrShadow.offset = {shadow.mOffset.x, shadow.mOffset.y}; 301 wrShadow.color = wr::ToColorF(ToDeviceColor(color)); 302 wrShadow.blur_radius = stdDev.width; 303 wr::FilterOp filterOp = wr::FilterOp::DropShadow(wrShadow); 304 305 aWrFilters.filters.AppendElement(filterOp); 306 } else if (attr.is<ComponentTransferAttributes>()) { 307 const ComponentTransferAttributes& attributes = 308 attr.as<ComponentTransferAttributes>(); 309 310 size_t numValues = 311 attributes.mValues[0].Length() + attributes.mValues[1].Length() + 312 attributes.mValues[2].Length() + attributes.mValues[3].Length(); 313 if (numValues > 1024) { 314 // Depending on how the wr shaders are implemented we may need to 315 // limit the total number of values. 316 return WrFiltersStatus::BLOB_FALLBACK; 317 } 318 319 wr::FilterOp filterOp = {wr::FilterOp::Tag::ComponentTransfer}; 320 wr::WrFilterData filterData; 321 aWrFilters.values.AppendElement(nsTArray<float>()); 322 nsTArray<float>* values = &aWrFilters.values.LastElement(); 323 values->SetCapacity(numValues); 324 325 filterData.funcR_type = FuncTypeToWr(attributes.mTypes[0]); 326 size_t R_startindex = values->Length(); 327 values->AppendElements(attributes.mValues[0]); 328 filterData.R_values_count = attributes.mValues[0].Length(); 329 330 size_t indexToUse = 331 attributes.mTypes[1] == SVG_FECOMPONENTTRANSFER_SAME_AS_R ? 0 : 1; 332 filterData.funcG_type = FuncTypeToWr(attributes.mTypes[indexToUse]); 333 size_t G_startindex = values->Length(); 334 values->AppendElements(attributes.mValues[indexToUse]); 335 filterData.G_values_count = attributes.mValues[indexToUse].Length(); 336 337 indexToUse = 338 attributes.mTypes[2] == SVG_FECOMPONENTTRANSFER_SAME_AS_R ? 0 : 2; 339 filterData.funcB_type = FuncTypeToWr(attributes.mTypes[indexToUse]); 340 size_t B_startindex = values->Length(); 341 values->AppendElements(attributes.mValues[indexToUse]); 342 filterData.B_values_count = attributes.mValues[indexToUse].Length(); 343 344 filterData.funcA_type = FuncTypeToWr(attributes.mTypes[3]); 345 size_t A_startindex = values->Length(); 346 values->AppendElements(attributes.mValues[3]); 347 filterData.A_values_count = attributes.mValues[3].Length(); 348 349 filterData.R_values = 350 filterData.R_values_count > 0 ? &((*values)[R_startindex]) : nullptr; 351 filterData.G_values = 352 filterData.G_values_count > 0 ? &((*values)[G_startindex]) : nullptr; 353 filterData.B_values = 354 filterData.B_values_count > 0 ? &((*values)[B_startindex]) : nullptr; 355 filterData.A_values = 356 filterData.A_values_count > 0 ? &((*values)[A_startindex]) : nullptr; 357 358 aWrFilters.filters.AppendElement(filterOp); 359 aWrFilters.filter_datas.AppendElement(filterData); 360 } else { 361 return WrFiltersStatus::BLOB_FALLBACK; 362 } 363 364 if (filterIsNoop && !aWrFilters.filters.IsEmpty() && 365 (aWrFilters.filters.LastElement().tag == 366 wr::FilterOp::Tag::SrgbToLinear || 367 aWrFilters.filters.LastElement().tag == 368 wr::FilterOp::Tag::LinearToSrgb)) { 369 // We pushed a color space conversion filter in prevision of applying 370 // another filter which turned out to be a no-op, so the conversion is 371 // unnecessary. Remove it from the filter list. 372 // This is both an optimization and a way to pass the wptest 373 // css/filter-effects/filter-scale-001.html for which the needless 374 // sRGB->linear->no-op->sRGB roundtrip introduces a slight error and we 375 // cannot add fuzziness to the test. 376 (void)aWrFilters.filters.PopLastElement(); 377 srgb = previousSrgb; 378 } 379 380 if (!filterIsNoop) { 381 if (finalClip.isNothing()) { 382 finalClip = Some(primitive.PrimitiveSubregion()); 383 } else { 384 finalClip = 385 Some(primitive.PrimitiveSubregion().Intersect(finalClip.value())); 386 } 387 } 388 } 389 390 if (!srgb) { 391 aWrFilters.filters.AppendElement(wr::FilterOp::LinearToSrgb()); 392 } 393 394 if (finalClip) { 395 aWrFilters.post_filters_clip = 396 Some(instance.FilterSpaceToFrameSpace(finalClip.value())); 397 } 398 return WrFiltersStatus::CHAIN; 399 } 400 401 static WrFiltersStatus WrSVGFEInputBuild(wr::FilterOpGraphPictureReference& pic, 402 int32_t aSource, int16_t aNodeOutput, 403 int16_t aSourceGraphic, 404 int16_t aSourceAlpha, 405 const int16_t aBufferIdMapping[]) { 406 switch (aSource) { 407 case FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic: 408 pic.buffer_id = 409 wr::FilterOpGraphPictureBufferId::BufferId(aSourceGraphic); 410 break; 411 case FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha: 412 pic.buffer_id = wr::FilterOpGraphPictureBufferId::BufferId(aSourceAlpha); 413 break; 414 case FilterPrimitiveDescription::kPrimitiveIndexFillPaint: 415 case FilterPrimitiveDescription::kPrimitiveIndexStrokePaint: 416 // https://bugzilla.mozilla.org/show_bug.cgi?id=1897878 417 // Fill and Stroke paints are not yet supported by WebRender, they may be 418 // a color (most common) or pattern fill, so we could implement them with 419 // feFlood or feImage + feTile depending on the nature of the fill. 420 return WrFiltersStatus::BLOB_FALLBACK; 421 default: 422 MOZ_RELEASE_ASSERT( 423 aSource >= 0, 424 "Unrecognized SVG filter primitive enum value - added another?"); 425 MOZ_RELEASE_ASSERT(aSource < aNodeOutput, 426 "Invalid DAG - nodes can only refer to earlier nodes"); 427 if (aSource < 0 || aSource >= aNodeOutput) { 428 return WrFiltersStatus::UNSUPPORTED; 429 } 430 // Look up the node we remapped this id to. 431 // This can't overflow because aSource < aNodeOutput and the table is the 432 // same size. 433 pic.buffer_id = 434 wr::FilterOpGraphPictureBufferId::BufferId(aBufferIdMapping[aSource]); 435 break; 436 } 437 return WrFiltersStatus::SVGFE; 438 } 439 440 static WrFiltersStatus WrFilterOpSVGFEOpacity( 441 WrFiltersHolder& aWrFilters, const wr::FilterOpGraphNode& aGraphNode, 442 const OpacityAttributes& aAttributes) { 443 // CSS opacity 444 // This is the only CSS property that is has no direct analog in SVG, although 445 // technically it can be represented as SVGFEComponentTransfer or 446 // SVGFEColorMatrix or SVGFECompositeArithmetic, those would be inefficient 447 // approaches. 448 if (!StaticPrefs::gfx_webrender_svg_filter_effects_opacity()) { 449 // Fallback if pref is disabled 450 return WrFiltersStatus::BLOB_FALLBACK; 451 } 452 float opacity = aAttributes.mOpacity; 453 if (opacity != 1.0f) { 454 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEOpacity( 455 aGraphNode, wr::PropertyBinding<float>::Value(opacity), opacity)); 456 } else { 457 // If it's a no-op, we still have to generate a graph node 458 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEIdentity(aGraphNode)); 459 } 460 return WrFiltersStatus::SVGFE; 461 } 462 463 static WrFiltersStatus WrFilterOpSVGFEToAlpha( 464 WrFiltersHolder& aWrFilters, const wr::FilterOpGraphNode& aGraphNode, 465 const ToAlphaAttributes& aAttributes) { 466 // Convert a color image to an alpha channel - internal use; generated by 467 // SVGFilterInstance::GetOrCreateSourceAlphaIndex(). 468 if (!StaticPrefs::GetPrefName_gfx_webrender_svg_filter_effects_toalpha()) { 469 // Fallback if pref is disabled 470 return WrFiltersStatus::BLOB_FALLBACK; 471 } 472 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEToAlpha(aGraphNode)); 473 return WrFiltersStatus::SVGFE; 474 } 475 476 static WrFiltersStatus WrFilterOpSVGFEBlend( 477 WrFiltersHolder& aWrFilters, const wr::FilterOpGraphNode& aGraphNode, 478 const BlendAttributes& aAttributes) { 479 // SVGFEBlend - common 480 if (!StaticPrefs::gfx_webrender_svg_filter_effects_feblend()) { 481 // Fallback if pref is disabled 482 return WrFiltersStatus::BLOB_FALLBACK; 483 } 484 switch (aAttributes.mBlendMode) { 485 case SVG_FEBLEND_MODE_COLOR: 486 aWrFilters.filters.AppendElement( 487 wr::FilterOp::SVGFEBlendColor(aGraphNode)); 488 return WrFiltersStatus::SVGFE; 489 case SVG_FEBLEND_MODE_COLOR_BURN: 490 aWrFilters.filters.AppendElement( 491 wr::FilterOp::SVGFEBlendColorBurn(aGraphNode)); 492 return WrFiltersStatus::SVGFE; 493 case SVG_FEBLEND_MODE_COLOR_DODGE: 494 aWrFilters.filters.AppendElement( 495 wr::FilterOp::SVGFEBlendColorDodge(aGraphNode)); 496 return WrFiltersStatus::SVGFE; 497 case SVG_FEBLEND_MODE_DARKEN: 498 aWrFilters.filters.AppendElement( 499 wr::FilterOp::SVGFEBlendDarken(aGraphNode)); 500 return WrFiltersStatus::SVGFE; 501 case SVG_FEBLEND_MODE_DIFFERENCE: 502 aWrFilters.filters.AppendElement( 503 wr::FilterOp::SVGFEBlendDifference(aGraphNode)); 504 return WrFiltersStatus::SVGFE; 505 case SVG_FEBLEND_MODE_EXCLUSION: 506 aWrFilters.filters.AppendElement( 507 wr::FilterOp::SVGFEBlendExclusion(aGraphNode)); 508 return WrFiltersStatus::SVGFE; 509 case SVG_FEBLEND_MODE_HARD_LIGHT: 510 aWrFilters.filters.AppendElement( 511 wr::FilterOp::SVGFEBlendHardLight(aGraphNode)); 512 return WrFiltersStatus::SVGFE; 513 case SVG_FEBLEND_MODE_HUE: 514 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEBlendHue(aGraphNode)); 515 return WrFiltersStatus::SVGFE; 516 case SVG_FEBLEND_MODE_LIGHTEN: 517 aWrFilters.filters.AppendElement( 518 wr::FilterOp::SVGFEBlendLighten(aGraphNode)); 519 return WrFiltersStatus::SVGFE; 520 case SVG_FEBLEND_MODE_LUMINOSITY: 521 aWrFilters.filters.AppendElement( 522 wr::FilterOp::SVGFEBlendLuminosity(aGraphNode)); 523 return WrFiltersStatus::SVGFE; 524 case SVG_FEBLEND_MODE_MULTIPLY: 525 aWrFilters.filters.AppendElement( 526 wr::FilterOp::SVGFEBlendMultiply(aGraphNode)); 527 return WrFiltersStatus::SVGFE; 528 case SVG_FEBLEND_MODE_NORMAL: 529 aWrFilters.filters.AppendElement( 530 wr::FilterOp::SVGFEBlendNormal(aGraphNode)); 531 return WrFiltersStatus::SVGFE; 532 case SVG_FEBLEND_MODE_OVERLAY: 533 aWrFilters.filters.AppendElement( 534 wr::FilterOp::SVGFEBlendOverlay(aGraphNode)); 535 return WrFiltersStatus::SVGFE; 536 case SVG_FEBLEND_MODE_SATURATION: 537 aWrFilters.filters.AppendElement( 538 wr::FilterOp::SVGFEBlendSaturation(aGraphNode)); 539 return WrFiltersStatus::SVGFE; 540 case SVG_FEBLEND_MODE_SCREEN: 541 aWrFilters.filters.AppendElement( 542 wr::FilterOp::SVGFEBlendScreen(aGraphNode)); 543 return WrFiltersStatus::SVGFE; 544 case SVG_FEBLEND_MODE_SOFT_LIGHT: 545 aWrFilters.filters.AppendElement( 546 wr::FilterOp::SVGFEBlendSoftLight(aGraphNode)); 547 return WrFiltersStatus::SVGFE; 548 default: 549 break; 550 } 551 MOZ_CRASH("Unrecognized SVG_FEBLEND_MODE"); 552 return WrFiltersStatus::BLOB_FALLBACK; 553 } 554 555 static WrFiltersStatus WrFilterOpSVGFEComposite( 556 WrFiltersHolder& aWrFilters, const wr::FilterOpGraphNode& aGraphNode, 557 const CompositeAttributes& aAttributes) { 558 // SVGFEComposite - common 559 if (!StaticPrefs::gfx_webrender_svg_filter_effects_fecomposite()) { 560 // Fallback if pref is disabled 561 return WrFiltersStatus::BLOB_FALLBACK; 562 } 563 switch (aAttributes.mOperator) { 564 case SVG_FECOMPOSITE_OPERATOR_ARITHMETIC: 565 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFECompositeArithmetic( 566 aGraphNode, aAttributes.mCoefficients[0], 567 aAttributes.mCoefficients[1], aAttributes.mCoefficients[2], 568 aAttributes.mCoefficients[3])); 569 return WrFiltersStatus::SVGFE; 570 case SVG_FECOMPOSITE_OPERATOR_ATOP: 571 aWrFilters.filters.AppendElement( 572 wr::FilterOp::SVGFECompositeATop(aGraphNode)); 573 return WrFiltersStatus::SVGFE; 574 case SVG_FECOMPOSITE_OPERATOR_IN: 575 aWrFilters.filters.AppendElement( 576 wr::FilterOp::SVGFECompositeIn(aGraphNode)); 577 return WrFiltersStatus::SVGFE; 578 case SVG_FECOMPOSITE_OPERATOR_LIGHTER: 579 aWrFilters.filters.AppendElement( 580 wr::FilterOp::SVGFECompositeLighter(aGraphNode)); 581 return WrFiltersStatus::SVGFE; 582 case SVG_FECOMPOSITE_OPERATOR_OUT: 583 aWrFilters.filters.AppendElement( 584 wr::FilterOp::SVGFECompositeOut(aGraphNode)); 585 return WrFiltersStatus::SVGFE; 586 case SVG_FECOMPOSITE_OPERATOR_OVER: 587 aWrFilters.filters.AppendElement( 588 wr::FilterOp::SVGFECompositeOver(aGraphNode)); 589 return WrFiltersStatus::SVGFE; 590 case SVG_FECOMPOSITE_OPERATOR_XOR: 591 aWrFilters.filters.AppendElement( 592 wr::FilterOp::SVGFECompositeXOR(aGraphNode)); 593 return WrFiltersStatus::SVGFE; 594 default: 595 break; 596 } 597 MOZ_CRASH("Unrecognized SVG_FECOMPOSITE_OPERATOR"); 598 return WrFiltersStatus::BLOB_FALLBACK; 599 } 600 601 static WrFiltersStatus WrFilterOpSVGFEColorMatrix( 602 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 603 const ColorMatrixAttributes& aAttributes) { 604 // SVGFEColorMatrix - common 605 if (!StaticPrefs::gfx_webrender_svg_filter_effects_fecolormatrix()) { 606 // Fallback if pref is disabled 607 return WrFiltersStatus::BLOB_FALLBACK; 608 } 609 float transposed[20]; 610 if (gfx::ComputeColorMatrix(aAttributes, transposed)) { 611 float matrix[20] = { 612 transposed[0], transposed[5], transposed[10], transposed[15], 613 transposed[1], transposed[6], transposed[11], transposed[16], 614 transposed[2], transposed[7], transposed[12], transposed[17], 615 transposed[3], transposed[8], transposed[13], transposed[18], 616 transposed[4], transposed[9], transposed[14], transposed[19]}; 617 aWrFilters.filters.AppendElement( 618 wr::FilterOp::SVGFEColorMatrix(aGraphNode, matrix)); 619 } else { 620 // If it's a no-op, we still have to generate a graph node 621 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEIdentity(aGraphNode)); 622 } 623 return WrFiltersStatus::SVGFE; 624 } 625 626 static WrFiltersStatus WrFilterOpSVGFEComponentTransfer( 627 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 628 const ComponentTransferAttributes& aAttributes) { 629 // SVGFEComponentTransfer - common 630 if (!StaticPrefs::gfx_webrender_svg_filter_effects_fecomponenttransfer()) { 631 // Fallback if pref is disabled 632 return WrFiltersStatus::BLOB_FALLBACK; 633 } 634 // We ensure that there are at least 256 values for each channel so that 635 // the shader can skip interpolation math for simplicity. 636 size_t stops = 256; 637 for (const auto& v : aAttributes.mValues) { 638 if (stops < v.Length()) { 639 stops = v.Length(); 640 } 641 } 642 aWrFilters.values.AppendElement(nsTArray<float>()); 643 nsTArray<float>& values = aWrFilters.values.LastElement(); 644 values.SetCapacity(stops * 4); 645 646 // Set the FilterData funcs for whether or not to interpolate the values 647 // between stops, although we use enough stops that it may not matter. 648 // The only type that doesn't use interpolation is discrete. 649 wr::WrFilterData filterData{}; 650 filterData.funcR_type = 651 aAttributes.mTypes[0] != SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE 652 ? mozilla::wr::ComponentTransferFuncType::Table 653 : mozilla::wr::ComponentTransferFuncType::Discrete; 654 filterData.funcG_type = 655 aAttributes.mTypes[1] != SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE 656 ? mozilla::wr::ComponentTransferFuncType::Table 657 : mozilla::wr::ComponentTransferFuncType::Discrete; 658 filterData.funcB_type = 659 aAttributes.mTypes[2] != SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE 660 ? mozilla::wr::ComponentTransferFuncType::Table 661 : mozilla::wr::ComponentTransferFuncType::Discrete; 662 filterData.funcA_type = 663 aAttributes.mTypes[3] != SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE 664 ? mozilla::wr::ComponentTransferFuncType::Table 665 : mozilla::wr::ComponentTransferFuncType::Discrete; 666 667 // This is a bit of a strange way to store the table, it is an interleaved 668 // array of RGBA values that we want to store in a single gpucache array 669 // of raw pixels, so it's easiest to send it to WebRender as a single 670 // channel, but FilterData requires it to be 4 channels, so we send it as 671 // 4 groups of values but the data is interleaved. 672 values.SetLength(stops * 4); 673 filterData.R_values = &(values[0]); 674 filterData.R_values_count = stops; 675 filterData.G_values = &(values[stops]); 676 filterData.G_values_count = stops; 677 filterData.B_values = &(values[stops * 2]); 678 filterData.B_values_count = stops; 679 filterData.A_values = &(values[stops * 3]); 680 filterData.A_values_count = stops; 681 682 // This builds a single interleaved RGBA table as it is well suited to GPU 683 // texture fetches without any dynamic component indexing in the shader which 684 // can confuse buggy shader compilers. 685 for (size_t c = 0; c < 4; c++) { 686 auto f = aAttributes.mTypes[c]; 687 // Check if there's no data (we have crashtests for this). 688 if (aAttributes.mValues[c].IsEmpty() && 689 f != SVG_FECOMPONENTTRANSFER_SAME_AS_R) { 690 f = SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY; 691 } 692 // Check for misuse of SVG_FECOMPONENTTRANSFER_SAME_AS_R. 693 if (c == 0 && f == SVG_FECOMPONENTTRANSFER_SAME_AS_R) { 694 f = SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY; 695 } 696 switch (f) { 697 case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE: { 698 size_t length = (size_t)aAttributes.mValues[c].Length(); 699 size_t length1 = length - 1; 700 float step = (float)length / (float)stops; 701 for (size_t i = 0; i < stops; i++) { 702 // find the corresponding color in the table 703 // this can not overflow due to the length check 704 float kf = (float)i * step; 705 float floorkf = floor(kf); 706 size_t k = (size_t)floorkf; 707 k = std::min(k, length1); 708 float v = aAttributes.mValues[c][k]; 709 v = std::clamp(v, 0.0f, 1.0f); 710 values[i * 4 + c] = v; 711 } 712 break; 713 } 714 case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA: { 715 float step = 1.0f / (float)(stops - 1); 716 float amplitude = aAttributes.mValues[c][0]; 717 float exponent = aAttributes.mValues[c][1]; 718 float offset = aAttributes.mValues[c][2]; 719 for (size_t i = 0; i < stops; i++) { 720 float v = amplitude * pow((float)i * step, exponent) + offset; 721 v = std::clamp(v, 0.0f, 1.0f); 722 values[i * 4 + c] = v; 723 } 724 break; 725 } 726 case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY: { 727 float step = 1.0f / (float)(stops - 1); 728 for (size_t i = 0; i < stops; i++) { 729 float v = (float)i * step; 730 v = std::clamp(v, 0.0f, 1.0f); 731 values[i * 4 + c] = v; 732 } 733 break; 734 } 735 case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR: { 736 float step = aAttributes.mValues[c][0] / (float)(stops - 1); 737 float intercept = aAttributes.mValues[c][1]; 738 for (size_t i = 0; i < stops; i++) { 739 float v = (float)i * step + intercept; 740 v = std::clamp(v, 0.0f, 1.0f); 741 values[i * 4 + c] = v; 742 } 743 break; 744 } 745 case SVG_FECOMPONENTTRANSFER_TYPE_TABLE: { 746 size_t length1 = (size_t)aAttributes.mValues[c].Length() - 1; 747 float step = (float)length1 / (float)(stops - 1); 748 for (size_t i = 0; i < stops; i++) { 749 // Find the corresponding color in the table and interpolate 750 float kf = (float)i * step; 751 float floorkf = floor(kf); 752 size_t k = (size_t)floorkf; 753 float v1 = aAttributes.mValues[c][k]; 754 float v2 = aAttributes.mValues[c][(k + 1 <= length1) ? k + 1 : k]; 755 float v = v1 + (v2 - v1) * (kf - floorkf); 756 v = std::clamp(v, 0.0f, 1.0f); 757 values[i * 4 + c] = v; 758 } 759 break; 760 } 761 case SVG_FECOMPONENTTRANSFER_SAME_AS_R: { 762 // We already checked c > 0 above. 763 for (size_t i = 0; i < stops; i++) { 764 values[i * 4 + c] = values[i * 4]; 765 } 766 break; 767 } 768 default: { 769 MOZ_CRASH("Unrecognized feComponentTransfer type"); 770 return WrFiltersStatus::BLOB_FALLBACK; 771 } 772 } 773 } 774 aWrFilters.filters.AppendElement( 775 wr::FilterOp::SVGFEComponentTransfer(aGraphNode)); 776 aWrFilters.filter_datas.AppendElement(filterData); 777 return WrFiltersStatus::SVGFE; 778 } 779 780 static WrFiltersStatus WrFilterOpSVGFEConvolveMatrix( 781 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 782 const ConvolveMatrixAttributes& aAttributes) { 783 // SVGFEConvolveMatrix - extremely rare 784 if (!StaticPrefs::gfx_webrender_svg_filter_effects_feconvolvematrix()) { 785 // Fallback if pref is disabled 786 return WrFiltersStatus::BLOB_FALLBACK; 787 } 788 // Limited kernel size for performance reasons - spec allows us to drop 789 // the whole filter graph if anything is unreasonable, so we only support 790 // up to 5x5 kernel as that is pretty fast in hardware 791 static constexpr int32_t width = 5; 792 static constexpr int32_t height = 5; 793 if (aAttributes.mKernelSize.Width() < 1 || 794 aAttributes.mKernelSize.Width() > width || 795 aAttributes.mKernelSize.Height() < 1 || 796 aAttributes.mKernelSize.Height() > height || 797 (size_t)aAttributes.mKernelSize.Width() * 798 (size_t)aAttributes.mKernelSize.Height() > 799 width * height) { 800 return WrFiltersStatus::BLOB_FALLBACK; 801 } 802 // Reject kernel matrix if it is fewer values than dimensions suggest 803 if (aAttributes.mKernelMatrix.Length() < 804 (size_t)aAttributes.mKernelSize.Width() * 805 (size_t)aAttributes.mKernelSize.Height()) { 806 return WrFiltersStatus::UNSUPPORTED; 807 } 808 // Arrange the values in the order the shader expects 809 float matrix[width * height]; 810 for (size_t y = 0; y < height; y++) { 811 for (size_t x = 0; x < width; x++) { 812 if (x < (size_t)aAttributes.mKernelSize.Width() && 813 y < (size_t)aAttributes.mKernelSize.Height()) { 814 matrix[y * width + x] = 815 aAttributes.mKernelMatrix[y * aAttributes.mKernelSize.Width() + x]; 816 } else { 817 matrix[y * width + x] = 0.0f; 818 } 819 } 820 } 821 switch (aAttributes.mEdgeMode) { 822 case SVG_EDGEMODE_UNKNOWN: 823 case SVG_EDGEMODE_DUPLICATE: 824 aWrFilters.filters.AppendElement( 825 wr::FilterOp::SVGFEConvolveMatrixEdgeModeDuplicate( 826 aGraphNode, aAttributes.mKernelSize.Width(), 827 aAttributes.mKernelSize.Height(), matrix, aAttributes.mDivisor, 828 aAttributes.mBias, aAttributes.mTarget.x.value, 829 aAttributes.mTarget.y.value, 830 aAttributes.mKernelUnitLength.Width(), 831 aAttributes.mKernelUnitLength.Height(), 832 aAttributes.mPreserveAlpha)); 833 return WrFiltersStatus::SVGFE; 834 case SVG_EDGEMODE_NONE: 835 aWrFilters.filters.AppendElement( 836 wr::FilterOp::SVGFEConvolveMatrixEdgeModeNone( 837 aGraphNode, aAttributes.mKernelSize.Width(), 838 aAttributes.mKernelSize.Height(), matrix, aAttributes.mDivisor, 839 aAttributes.mBias, aAttributes.mTarget.x.value, 840 aAttributes.mTarget.y.value, 841 aAttributes.mKernelUnitLength.Width(), 842 aAttributes.mKernelUnitLength.Height(), 843 aAttributes.mPreserveAlpha)); 844 return WrFiltersStatus::SVGFE; 845 case SVG_EDGEMODE_WRAP: 846 aWrFilters.filters.AppendElement( 847 wr::FilterOp::SVGFEConvolveMatrixEdgeModeWrap( 848 aGraphNode, aAttributes.mKernelSize.Width(), 849 aAttributes.mKernelSize.Height(), matrix, aAttributes.mDivisor, 850 aAttributes.mBias, aAttributes.mTarget.x.value, 851 aAttributes.mTarget.y.value, 852 aAttributes.mKernelUnitLength.Width(), 853 aAttributes.mKernelUnitLength.Height(), 854 aAttributes.mPreserveAlpha)); 855 return WrFiltersStatus::SVGFE; 856 default: 857 break; 858 } 859 MOZ_CRASH("Unrecognized SVG_EDGEMODE"); 860 return WrFiltersStatus::BLOB_FALLBACK; 861 } 862 863 static WrFiltersStatus WrFilterOpSVGFEDiffuseLighting( 864 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 865 const DiffuseLightingAttributes& aAttributes, 866 const LayoutDevicePoint& aUserspaceOffset) { 867 // SVGFEDiffuseLighting - extremely rare 868 if (!StaticPrefs::gfx_webrender_svg_filter_effects_fediffuselighting()) { 869 // Fallback if pref is disabled 870 return WrFiltersStatus::BLOB_FALLBACK; 871 } 872 switch (aAttributes.mLightType) { 873 case LightType::Distant: 874 aWrFilters.filters.AppendElement( 875 wr::FilterOp::SVGFEDiffuseLightingDistant( 876 aGraphNode, aAttributes.mSurfaceScale, 877 aAttributes.mLightingConstant, 878 aAttributes.mKernelUnitLength.width, 879 aAttributes.mKernelUnitLength.height, aAttributes.mLightValues[0], 880 aAttributes.mLightValues[1])); 881 return WrFiltersStatus::SVGFE; 882 case LightType::Point: 883 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEDiffuseLightingPoint( 884 aGraphNode, aAttributes.mSurfaceScale, aAttributes.mLightingConstant, 885 aAttributes.mKernelUnitLength.width, 886 aAttributes.mKernelUnitLength.height, 887 aAttributes.mLightValues[0] + aUserspaceOffset.x.value, 888 aAttributes.mLightValues[1] + aUserspaceOffset.y.value, 889 aAttributes.mLightValues[2])); 890 return WrFiltersStatus::SVGFE; 891 case LightType::Spot: 892 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEDiffuseLightingSpot( 893 aGraphNode, aAttributes.mSurfaceScale, aAttributes.mLightingConstant, 894 aAttributes.mKernelUnitLength.width, 895 aAttributes.mKernelUnitLength.height, 896 aAttributes.mLightValues[0] + aUserspaceOffset.x.value, 897 aAttributes.mLightValues[1] + aUserspaceOffset.y.value, 898 aAttributes.mLightValues[2], 899 aAttributes.mLightValues[3] + aUserspaceOffset.x.value, 900 aAttributes.mLightValues[4] + aUserspaceOffset.y.value, 901 aAttributes.mLightValues[5], aAttributes.mLightValues[6], 902 aAttributes.mLightValues[7])); 903 return WrFiltersStatus::SVGFE; 904 case LightType::None: 905 case LightType::Max: 906 // No default case, so that the compiler will warn if new enums are added 907 break; 908 } 909 MOZ_CRASH("Unrecognized LightType"); 910 return WrFiltersStatus::BLOB_FALLBACK; 911 } 912 913 static WrFiltersStatus WrFilterOpSVGFEDisplacementMap( 914 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 915 const DisplacementMapAttributes& aAttributes) { 916 // SVGFEDisplacementMap - extremely rare 917 if (!StaticPrefs::gfx_webrender_svg_filter_effects_fedisplacementmap()) { 918 // Fallback if pref is disabled 919 return WrFiltersStatus::BLOB_FALLBACK; 920 } 921 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEDisplacementMap( 922 aGraphNode, aAttributes.mScale, aAttributes.mXChannel, 923 aAttributes.mYChannel)); 924 return WrFiltersStatus::SVGFE; 925 } 926 927 static WrFiltersStatus WrFilterOpSVGFEDropShadow( 928 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 929 const DropShadowAttributes& aAttributes) { 930 // SVGFEDropShadow - extremely rare 931 if (!StaticPrefs::gfx_webrender_svg_filter_effects_fedropshadow()) { 932 // Fallback if pref is disabled 933 return WrFiltersStatus::BLOB_FALLBACK; 934 } 935 // This color is used in a shader coefficient that is in sRGB color space, 936 // so it needs to go through the regular device color transformation. 937 // This does not premultiply the alpha - the shader will do that. 938 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEDropShadow( 939 aGraphNode, wr::ToColorF(ToDeviceColor(aAttributes.mColor)), 940 aAttributes.mOffset.x, aAttributes.mOffset.y, 941 aAttributes.mStdDeviation.width, aAttributes.mStdDeviation.height)); 942 return WrFiltersStatus::SVGFE; 943 } 944 945 static WrFiltersStatus WrFilterOpSVGFEFlood( 946 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 947 const FloodAttributes& aAttributes) { 948 // SVGFEFlood - common 949 if (!StaticPrefs::gfx_webrender_svg_filter_effects_feflood()) { 950 // Fallback if pref is disabled 951 return WrFiltersStatus::BLOB_FALLBACK; 952 } 953 // This color is used in a shader coefficient that is in sRGB color space, 954 // so it needs to go through the regular device color transformation. 955 // This does not premultiply the alpha - the shader will do that. 956 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEFlood( 957 aGraphNode, wr::ToColorF(ToDeviceColor(aAttributes.mColor)))); 958 return WrFiltersStatus::SVGFE; 959 } 960 961 static WrFiltersStatus WrFilterOpSVGFEGaussianBlur( 962 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 963 const GaussianBlurAttributes& aAttributes) { 964 // SVGFEGaussianBlur - common 965 if (!StaticPrefs::gfx_webrender_svg_filter_effects_fegaussianblur()) { 966 // Fallback if pref is disabled 967 return WrFiltersStatus::BLOB_FALLBACK; 968 } 969 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEGaussianBlur( 970 aGraphNode, aAttributes.mStdDeviation.width, 971 aAttributes.mStdDeviation.height)); 972 return WrFiltersStatus::SVGFE; 973 } 974 975 static WrFiltersStatus WrFilterOpSVGFEImage( 976 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 977 const ImageAttributes& aAttributes, 978 const LayoutDevicePoint& aUserspaceOffset) { 979 // SVGFEImage - Extremely rare 980 if (!StaticPrefs::gfx_webrender_svg_filter_effects_feimage()) { 981 // Fallback if pref is disabled 982 return WrFiltersStatus::BLOB_FALLBACK; 983 } 984 float matrix[6]; 985 matrix[0] = aAttributes.mTransform.components[0]; 986 matrix[1] = aAttributes.mTransform.components[1]; 987 matrix[2] = aAttributes.mTransform.components[2]; 988 matrix[3] = aAttributes.mTransform.components[3]; 989 matrix[4] = aAttributes.mTransform.components[4] + aUserspaceOffset.x.value; 990 matrix[5] = aAttributes.mTransform.components[5] + aUserspaceOffset.y.value; 991 // TODO: We need to resolve aAttributes.mInputIndex to an actual image 992 // somehow. 993 aWrFilters.filters.AppendElement( 994 wr::FilterOp::SVGFEImage(aGraphNode, aAttributes.mFilter, matrix)); 995 return WrFiltersStatus::SVGFE; 996 } 997 998 static WrFiltersStatus WrFilterOpSVGFEMerge( 999 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 1000 const MergeAttributes& aAttributes, FilterPrimitiveDescription& aPrimitive, 1001 int16_t aNodeOutput, int16_t aSourceGraphic, int16_t aSourceAlpha, 1002 const int16_t aBufferIdMapping[], size_t aMaxFilters) { 1003 // SVGFEMerge - common 1004 if (!StaticPrefs::gfx_webrender_svg_filter_effects_femerge()) { 1005 // Fallback if pref is disabled 1006 return WrFiltersStatus::BLOB_FALLBACK; 1007 } 1008 // There is no SVGFEMerge, so we need to expand the provided inputs to a 1009 // chain of SVGFECompositeOver ops before handing it to WebRender. 1010 if (aPrimitive.NumberOfInputs() >= 2) { 1011 wr::FilterOpGraphPictureReference previous{}; 1012 for (size_t index = 0; index < aPrimitive.NumberOfInputs(); index++) { 1013 wr::FilterOpGraphPictureReference current{}; 1014 WrFiltersStatus status = WrSVGFEInputBuild( 1015 current, aPrimitive.InputPrimitiveIndex(index), aNodeOutput, 1016 aSourceGraphic, aSourceAlpha, aBufferIdMapping); 1017 if (status != WrFiltersStatus::SVGFE) { 1018 // If the input is an invalid ref, we have to disable filters on this. 1019 return status; 1020 } 1021 aGraphNode.input = current; 1022 aGraphNode.input2 = previous; 1023 if (aWrFilters.filters.Length() >= aMaxFilters) { 1024 // Reject the graph if it has too many filters to even process 1025 return WrFiltersStatus::DISABLED_FOR_PERFORMANCE; 1026 } 1027 if (index >= 1) { 1028 // Emit a node that composites this pic over the previous pics. 1029 aWrFilters.filters.AppendElement( 1030 wr::FilterOp::SVGFECompositeOver(aGraphNode)); 1031 // Use this graph node as input2 (background) on the next node. 1032 previous.buffer_id = wr::FilterOpGraphPictureBufferId::BufferId( 1033 (int16_t)(aWrFilters.filters.Length() - 1)); 1034 } else { 1035 // Conceptually the first pic is composited over transparent black 1036 // which is a no-op, so we just use the first pic as a direct input 1037 // on the first node we actually emit. 1038 previous.buffer_id = current.buffer_id; 1039 } 1040 } 1041 } else if (aPrimitive.NumberOfInputs() == 1) { 1042 // If we only got a single feMergeNode pic, we still want to apply 1043 // the subregion clip, so make an SVGFEIdentity op. 1044 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEIdentity(aGraphNode)); 1045 } else { 1046 // feMerge with no feMergeNodes is just blank. 1047 wr::ColorF blank = {0.0f, 0.0f, 0.0f, 0.0f}; 1048 aWrFilters.filters.AppendElement( 1049 wr::FilterOp::SVGFEFlood(aGraphNode, blank)); 1050 } 1051 return WrFiltersStatus::SVGFE; 1052 } 1053 1054 static WrFiltersStatus WrFilterOpSVGFEMorphology( 1055 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 1056 const MorphologyAttributes& aAttributes) { 1057 // SVGFEMorphology - Rare 1058 if (!StaticPrefs::gfx_webrender_svg_filter_effects_femorphology()) { 1059 // Fallback if pref is disabled 1060 return WrFiltersStatus::BLOB_FALLBACK; 1061 } 1062 switch (aAttributes.mOperator) { 1063 case SVG_OPERATOR_DILATE: 1064 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEMorphologyDilate( 1065 aGraphNode, aAttributes.mRadii.width, aAttributes.mRadii.height)); 1066 return WrFiltersStatus::SVGFE; 1067 case SVG_OPERATOR_ERODE: 1068 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEMorphologyErode( 1069 aGraphNode, aAttributes.mRadii.width, aAttributes.mRadii.height)); 1070 return WrFiltersStatus::SVGFE; 1071 default: 1072 break; 1073 } 1074 MOZ_CRASH("Unrecognized SVG_OPERATOR"); 1075 return WrFiltersStatus::BLOB_FALLBACK; 1076 } 1077 1078 static WrFiltersStatus WrFilterOpSVGFEOffset( 1079 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 1080 const OffsetAttributes& aAttributes) { 1081 // SVGFEOffset - Common 1082 if (!StaticPrefs::gfx_webrender_svg_filter_effects_feoffset()) { 1083 // Fallback if pref is disabled 1084 return WrFiltersStatus::BLOB_FALLBACK; 1085 } 1086 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEOffset( 1087 aGraphNode, (float)aAttributes.mValue.x, (float)aAttributes.mValue.y)); 1088 return WrFiltersStatus::SVGFE; 1089 } 1090 1091 static WrFiltersStatus WrFilterOpSVGFETile(WrFiltersHolder& aWrFilters, 1092 wr::FilterOpGraphNode& aGraphNode, 1093 const TileAttributes& aAttributes) { 1094 // SVGFETile - Extremely rare 1095 if (!StaticPrefs::gfx_webrender_svg_filter_effects_fetile()) { 1096 // Fallback if pref is disabled 1097 return WrFiltersStatus::BLOB_FALLBACK; 1098 } 1099 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFETile(aGraphNode)); 1100 return WrFiltersStatus::SVGFE; 1101 } 1102 1103 static WrFiltersStatus WrFilterOpSVGFESpecularLighting( 1104 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 1105 const SpecularLightingAttributes& aAttributes, 1106 const LayoutDevicePoint& aUserspaceOffset) { 1107 // SVGFESpecularLighting - extremely rare 1108 if (!StaticPrefs::gfx_webrender_svg_filter_effects_fespecularlighting()) { 1109 // Fallback if pref is disabled 1110 return WrFiltersStatus::BLOB_FALLBACK; 1111 } 1112 switch (aAttributes.mLightType) { 1113 case LightType::Distant: 1114 aWrFilters.filters.AppendElement( 1115 wr::FilterOp::SVGFESpecularLightingDistant( 1116 aGraphNode, aAttributes.mSurfaceScale, 1117 aAttributes.mLightingConstant, aAttributes.mSpecularExponent, 1118 aAttributes.mKernelUnitLength.width, 1119 aAttributes.mKernelUnitLength.height, aAttributes.mLightValues[0], 1120 aAttributes.mLightValues[1])); 1121 return WrFiltersStatus::SVGFE; 1122 case LightType::Point: 1123 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFESpecularLightingPoint( 1124 aGraphNode, aAttributes.mSurfaceScale, aAttributes.mLightingConstant, 1125 aAttributes.mSpecularExponent, aAttributes.mKernelUnitLength.width, 1126 aAttributes.mKernelUnitLength.height, 1127 aAttributes.mLightValues[0] + aUserspaceOffset.x.value, 1128 aAttributes.mLightValues[1] + aUserspaceOffset.y.value, 1129 aAttributes.mLightValues[2])); 1130 return WrFiltersStatus::SVGFE; 1131 case LightType::Spot: 1132 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFESpecularLightingSpot( 1133 aGraphNode, aAttributes.mSurfaceScale, aAttributes.mLightingConstant, 1134 aAttributes.mSpecularExponent, aAttributes.mKernelUnitLength.width, 1135 aAttributes.mKernelUnitLength.height, 1136 aAttributes.mLightValues[0] + aUserspaceOffset.x.value, 1137 aAttributes.mLightValues[1] + aUserspaceOffset.y.value, 1138 aAttributes.mLightValues[2], 1139 aAttributes.mLightValues[3] + aUserspaceOffset.x.value, 1140 aAttributes.mLightValues[4] + aUserspaceOffset.y.value, 1141 aAttributes.mLightValues[5], aAttributes.mLightValues[6], 1142 aAttributes.mLightValues[7])); 1143 return WrFiltersStatus::SVGFE; 1144 case LightType::None: 1145 case LightType::Max: 1146 // No default case, so that the compiler will warn if new enums are added 1147 break; 1148 } 1149 MOZ_CRASH("Unrecognized LightType"); 1150 return WrFiltersStatus::BLOB_FALLBACK; 1151 } 1152 1153 static WrFiltersStatus WrFilterOpSVGFETurbulence( 1154 WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode, 1155 const TurbulenceAttributes& aAttributes, 1156 const LayoutDevicePoint& aUserspaceOffset) { 1157 // SVGFETurbulence - Rare 1158 if (!StaticPrefs::gfx_webrender_svg_filter_effects_feturbulence()) { 1159 // Fallback if pref is disabled 1160 return WrFiltersStatus::BLOB_FALLBACK; 1161 } 1162 // The software implementation we use converts float to uint32_t and then 1163 // to int32_t, so we do that here to get identical results to the prior 1164 // implementation, in contrast to the spec which uses purely signed math 1165 // for setting up the seed. 1166 int32_t m1 = 2147483647 - 1; 1167 int32_t seed = (int32_t)((uint32_t)aAttributes.mSeed); 1168 if (seed <= 0) { 1169 seed = -(seed % m1) + 1; 1170 } 1171 if (seed > m1) { 1172 seed = m1; 1173 } 1174 switch (aAttributes.mType) { 1175 case SVG_TURBULENCE_TYPE_FRACTALNOISE: 1176 if (aAttributes.mStitchable) { 1177 aWrFilters.filters.AppendElement( 1178 wr::FilterOp::SVGFETurbulenceWithFractalNoiseWithStitching( 1179 aGraphNode, aAttributes.mBaseFrequency.width, 1180 aAttributes.mBaseFrequency.height, aAttributes.mOctaves, seed)); 1181 } else { 1182 aWrFilters.filters.AppendElement( 1183 wr::FilterOp::SVGFETurbulenceWithFractalNoiseWithNoStitching( 1184 aGraphNode, aAttributes.mBaseFrequency.width, 1185 aAttributes.mBaseFrequency.height, aAttributes.mOctaves, seed)); 1186 } 1187 return WrFiltersStatus::SVGFE; 1188 case SVG_TURBULENCE_TYPE_TURBULENCE: 1189 if (aAttributes.mStitchable) { 1190 aWrFilters.filters.AppendElement( 1191 wr::FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching( 1192 aGraphNode, aAttributes.mBaseFrequency.width, 1193 aAttributes.mBaseFrequency.height, aAttributes.mOctaves, seed)); 1194 } else { 1195 aWrFilters.filters.AppendElement( 1196 wr::FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching( 1197 aGraphNode, aAttributes.mBaseFrequency.width, 1198 aAttributes.mBaseFrequency.height, aAttributes.mOctaves, seed)); 1199 } 1200 return WrFiltersStatus::SVGFE; 1201 default: 1202 break; 1203 } 1204 MOZ_CRASH("Unrecognized SVG_TURBULENCE_TYPE"); 1205 return WrFiltersStatus::BLOB_FALLBACK; 1206 } 1207 1208 /// Builds filter DAG for fully accelerated rendering of SVG filter primitives 1209 /// and CSS filter chains using SVG filter primitives 1210 WrFiltersStatus FilterInstance::BuildWebRenderSVGFiltersImpl( 1211 nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilters, 1212 StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters, 1213 const nsPoint& aOffsetForSVGFilters) { 1214 // If we return without making a valid filter graph, we need to restore 1215 // aInitialized before the fallback code is run. 1216 aWrFilters.filters.Clear(); 1217 aWrFilters.filter_datas.Clear(); 1218 aWrFilters.values.Clear(); 1219 aWrFilters.post_filters_clip = Nothing(); 1220 1221 nsIFrame* firstFrame = 1222 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFilteredFrame); 1223 1224 nsTArray<SVGFilterFrame*> filterFrames; 1225 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, &filterFrames, 1226 aStyleFilterType) == 1227 SVGObserverUtils::eHasRefsSomeInvalid) { 1228 return WrFiltersStatus::UNSUPPORTED; 1229 } 1230 1231 UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(firstFrame); 1232 1233 gfxRect filterSpaceBoundsNotSnapped; 1234 1235 // TODO: simply using an identity matrix here, was pulling the scale from a 1236 // gfx context for the non-wr path. 1237 gfxMatrix scaleMatrix; 1238 gfxMatrix scaleMatrixInDevUnits = 1239 scaleMatrix * SVGUtils::GetCSSPxToDevPxMatrix(firstFrame); 1240 1241 // Hardcode inputIsTainted to true because we don't want JS to be able to 1242 // read the rendered contents of aFilteredFrame. 1243 FilterInstance instance(firstFrame, firstFrame->GetContent(), *metrics, 1244 aFilters, filterFrames, /* inputIsTainted */ true, 1245 nullptr, scaleMatrixInDevUnits, nullptr, nullptr, 1246 nullptr, nullptr, &filterSpaceBoundsNotSnapped); 1247 1248 if (!instance.IsInitialized()) { 1249 return WrFiltersStatus::UNSUPPORTED; 1250 } 1251 1252 // If there more filters than the limit pref allows, we can drop the entire 1253 // filter graph and pretend we succeeded, the SVG spec allows us to drop any 1254 // overly complex graph, very large graphs tend to OOM anyway. 1255 if (instance.mFilterDescription.mPrimitives.Length() > 1256 StaticPrefs::gfx_webrender_max_filter_ops_per_chain()) { 1257 return WrFiltersStatus::DISABLED_FOR_PERFORMANCE; 1258 } 1259 1260 // We have to remap the input nodes to a possibly larger number of output 1261 // nodes due to expanding feMerge. 1262 static constexpr size_t maxFilters = wr::SVGFE_GRAPH_MAX; 1263 int16_t bufferIdMapping[maxFilters]; 1264 // Just drop the graph if there are too many filters to process. 1265 if (instance.mFilterDescription.mPrimitives.Length() > maxFilters) { 1266 return WrFiltersStatus::DISABLED_FOR_PERFORMANCE; 1267 } 1268 1269 // For subregions and filter parameters, we need to transform into the SVG 1270 // User Space coordinate system, which is the parent stacking context 1271 // coordinate system, not to be confused with Filter Space (which is this 1272 // stacking context's child coordinate system) or Frame Space. 1273 // 1274 // See nsLayoutUtils::ComputeOffsetToUserSpace for further explanation, and 1275 // SVGIntegrationUtils.cpp EffectOffsets::ComputeEffectOffset is how this is 1276 // done in the blob fallback. 1277 // 1278 // The display list we are building already puts the child elements' geometry 1279 // (if any) in SVG User Space, so we want the filter region and primitive 1280 // subregions to be in SVG User Space, so uerspaceOffset represents the offset 1281 // from Filter to User Space, which is in LayoutDevice units. 1282 // 1283 // As a practical matter, things like regular view zoom change Filter Space 1284 // scale so we don't have to do anything for that, pinch zoom in apz can be 1285 // doing its own thing but doesn't affect the coordinate system we use here, 1286 // as everything is multiplied by subregion and divided by filterRegion, so 1287 // they only need to be matching scale from WebRender perspective. 1288 LayoutDevicePoint userspaceOffset = LayoutDevicePoint::FromAppUnits( 1289 aOffsetForSVGFilters, 1290 aFilteredFrame->PresContext()->AppUnitsPerDevPixel()); 1291 1292 // The bounds of SourceGraphic are defined in the spec as being equal to the 1293 // filter region, so we need to compute that, and while subregion bounds are 1294 // always integer, the bounds of the filter element (and hence filter region) 1295 // are not actually integer, so we need to account for the non-integer filter 1296 // region clip by using filterSpaceBoundsNotSnapped, this matters in: 1297 // ./mach reftest layout/reftests/svg/filter-scaled-01.svg 1298 wr::LayoutRect filterRegion = { 1299 {(float)(filterSpaceBoundsNotSnapped.TopLeft().x + 1300 userspaceOffset.x.value), 1301 (float)(filterSpaceBoundsNotSnapped.TopLeft().y + 1302 userspaceOffset.y.value)}, 1303 {(float)(filterSpaceBoundsNotSnapped.BottomRight().x + 1304 userspaceOffset.x.value), 1305 (float)(filterSpaceBoundsNotSnapped.BottomRight().y + 1306 userspaceOffset.y.value)}}; 1307 1308 // To enforce the filterRegion clipping SourceGraphic before it enters the 1309 // graph we have to create a SourceGraphic node and SourceAlpha node, when we 1310 // implement StrokePaint and FillPaint they will need to create nodes on 1311 // demand however as they have custom colors (feFlood) and patterns (feTile). 1312 auto sourceGraphicNode = (int16_t)aWrFilters.filters.Length(); 1313 auto sourceNode = wr::FilterOpGraphNode{}; 1314 sourceNode.subregion = filterRegion; 1315 aWrFilters.filters.AppendElement( 1316 wr::FilterOp::SVGFESourceGraphic(sourceNode)); 1317 auto sourceAlphaNode = (int16_t)aWrFilters.filters.Length(); 1318 aWrFilters.filters.AppendElement(wr::FilterOp::SVGFESourceAlpha(sourceNode)); 1319 1320 // We have some failure modes that can occur when processing the graph. 1321 WrFiltersStatus status = WrFiltersStatus::SVGFE; 1322 1323 for (uint32_t i = 0; i < instance.mFilterDescription.mPrimitives.Length(); 1324 i++) { 1325 const auto& primitive = instance.mFilterDescription.mPrimitives[i]; 1326 const PrimitiveAttributes& attr = primitive.Attributes(); 1327 const bool linear = primitive.OutputColorSpace() == ColorSpace::LinearRGB; 1328 const size_t inputs = primitive.NumberOfInputs(); 1329 wr::FilterOpGraphNode graphNode = wr::FilterOpGraphNode{}; 1330 // Physical (linear) colorspace is the default in SVG filters, whereas all 1331 // CSS filters use sRGB (curved / naive) colorspace calculations for math, 1332 // this is the color-interpolation-filter property in SVG spec. Note that 1333 // feFlood cares about the color-interpolation property on the color value 1334 // provided, rather than the regular color-interpolation-filter property. 1335 graphNode.linear = linear; 1336 // Transform the subregion into SVG 'user space' which WebRender expects. 1337 graphNode.subregion = 1338 wr::ToLayoutRect(Rect(primitive.PrimitiveSubregion()) + 1339 userspaceOffset.ToUnknownPoint()); 1340 // We need to clip the final output node by the filterRegion, as it could 1341 // be non-integer (whereas the subregions were computed by SVGFilterInstance 1342 // code as integer only). 1343 if (i == instance.mFilterDescription.mPrimitives.Length() - 1) { 1344 if (graphNode.subregion.min.x < filterRegion.min.x) { 1345 graphNode.subregion.min.x = filterRegion.min.x; 1346 } 1347 if (graphNode.subregion.min.y < filterRegion.min.y) { 1348 graphNode.subregion.min.y = filterRegion.min.y; 1349 } 1350 if (graphNode.subregion.max.x > filterRegion.max.x) { 1351 graphNode.subregion.max.x = filterRegion.max.x; 1352 } 1353 if (graphNode.subregion.max.y > filterRegion.max.y) { 1354 graphNode.subregion.max.y = filterRegion.max.y; 1355 } 1356 } 1357 1358 // Buffer ids are matched up later by WebRender to understand the DAG, we 1359 // hold the following assumptions (and verify them regularly): 1360 // * Inputs referencing buffer ids are always < node index 1361 // (This means the DAG can be walked sequentially as a flat array and 1362 // always evaluate correctly) 1363 // * node index < maxFilters 1364 graphNode.input.buffer_id = wr::FilterOpGraphPictureBufferId::None(); 1365 graphNode.input2.buffer_id = wr::FilterOpGraphPictureBufferId::None(); 1366 if (inputs >= 1) { 1367 status = WrSVGFEInputBuild( 1368 graphNode.input, primitive.InputPrimitiveIndex(0), (int16_t)i, 1369 sourceGraphicNode, sourceAlphaNode, bufferIdMapping); 1370 if (status != WrFiltersStatus::SVGFE) { 1371 break; 1372 } 1373 if (inputs >= 2) { 1374 status = WrSVGFEInputBuild( 1375 graphNode.input2, primitive.InputPrimitiveIndex(1), (int16_t)i, 1376 sourceGraphicNode, sourceAlphaNode, bufferIdMapping); 1377 if (status != WrFiltersStatus::SVGFE) { 1378 break; 1379 } 1380 } 1381 } 1382 1383 // If there are too many filters (after feMerge expansion) to keep track of 1384 // in bufferIdMapping[] then we can just drop the entire graph, the SVG spec 1385 // allows us to drop overly complex graphs and maxFilters is not a small 1386 // quantity. 1387 if (aWrFilters.filters.Length() >= maxFilters) { 1388 status = WrFiltersStatus::DISABLED_FOR_PERFORMANCE; 1389 break; 1390 } 1391 1392 if (attr.is<OpacityAttributes>()) { 1393 status = WrFilterOpSVGFEOpacity(aWrFilters, graphNode, 1394 attr.as<OpacityAttributes>()); 1395 } else if (attr.is<ToAlphaAttributes>()) { 1396 status = WrFilterOpSVGFEToAlpha(aWrFilters, graphNode, 1397 attr.as<ToAlphaAttributes>()); 1398 } else if (attr.is<BlendAttributes>()) { 1399 status = WrFilterOpSVGFEBlend(aWrFilters, graphNode, 1400 attr.as<BlendAttributes>()); 1401 } else if (attr.is<ColorMatrixAttributes>()) { 1402 status = WrFilterOpSVGFEColorMatrix(aWrFilters, graphNode, 1403 attr.as<ColorMatrixAttributes>()); 1404 } else if (attr.is<ComponentTransferAttributes>()) { 1405 status = WrFilterOpSVGFEComponentTransfer( 1406 aWrFilters, graphNode, attr.as<ComponentTransferAttributes>()); 1407 } else if (attr.is<CompositeAttributes>()) { 1408 status = WrFilterOpSVGFEComposite(aWrFilters, graphNode, 1409 attr.as<CompositeAttributes>()); 1410 } else if (attr.is<ConvolveMatrixAttributes>()) { 1411 status = WrFilterOpSVGFEConvolveMatrix( 1412 aWrFilters, graphNode, attr.as<ConvolveMatrixAttributes>()); 1413 } else if (attr.is<DiffuseLightingAttributes>()) { 1414 status = WrFilterOpSVGFEDiffuseLighting( 1415 aWrFilters, graphNode, attr.as<DiffuseLightingAttributes>(), 1416 userspaceOffset); 1417 } else if (attr.is<DisplacementMapAttributes>()) { 1418 status = WrFilterOpSVGFEDisplacementMap( 1419 aWrFilters, graphNode, attr.as<DisplacementMapAttributes>()); 1420 } else if (attr.is<DropShadowAttributes>()) { 1421 status = WrFilterOpSVGFEDropShadow(aWrFilters, graphNode, 1422 attr.as<DropShadowAttributes>()); 1423 } else if (attr.is<FloodAttributes>()) { 1424 status = WrFilterOpSVGFEFlood(aWrFilters, graphNode, 1425 attr.as<FloodAttributes>()); 1426 } else if (attr.is<GaussianBlurAttributes>()) { 1427 status = WrFilterOpSVGFEGaussianBlur(aWrFilters, graphNode, 1428 attr.as<GaussianBlurAttributes>()); 1429 } else if (attr.is<ImageAttributes>()) { 1430 status = WrFilterOpSVGFEImage( 1431 aWrFilters, graphNode, attr.as<ImageAttributes>(), userspaceOffset); 1432 } else if (attr.is<MergeAttributes>()) { 1433 status = WrFilterOpSVGFEMerge( 1434 aWrFilters, graphNode, attr.as<MergeAttributes>(), 1435 instance.mFilterDescription.mPrimitives[i], (int16_t)i, 1436 sourceGraphicNode, sourceAlphaNode, bufferIdMapping, maxFilters); 1437 } else if (attr.is<MorphologyAttributes>()) { 1438 status = WrFilterOpSVGFEMorphology(aWrFilters, graphNode, 1439 attr.as<MorphologyAttributes>()); 1440 } else if (attr.is<OffsetAttributes>()) { 1441 status = WrFilterOpSVGFEOffset(aWrFilters, graphNode, 1442 attr.as<OffsetAttributes>()); 1443 } else if (attr.is<SpecularLightingAttributes>()) { 1444 status = WrFilterOpSVGFESpecularLighting( 1445 aWrFilters, graphNode, attr.as<SpecularLightingAttributes>(), 1446 userspaceOffset); 1447 } else if (attr.is<TileAttributes>()) { 1448 status = 1449 WrFilterOpSVGFETile(aWrFilters, graphNode, attr.as<TileAttributes>()); 1450 } else if (attr.is<TurbulenceAttributes>()) { 1451 status = WrFilterOpSVGFETurbulence(aWrFilters, graphNode, 1452 attr.as<TurbulenceAttributes>(), 1453 userspaceOffset); 1454 } else { 1455 // Unknown attributes type? 1456 status = WrFiltersStatus::BLOB_FALLBACK; 1457 } 1458 if (status != WrFiltersStatus::SVGFE) { 1459 break; 1460 } 1461 // Set the remapping table entry 1462 bufferIdMapping[i] = (int16_t)(aWrFilters.filters.Length() - 1); 1463 } 1464 if (status != WrFiltersStatus::SVGFE) { 1465 // If we couldn't handle this graph, clear the filters before returning. 1466 aWrFilters.filters.Clear(); 1467 aWrFilters.filter_datas.Clear(); 1468 aWrFilters.values.Clear(); 1469 aWrFilters.post_filters_clip = Nothing(); 1470 } 1471 return status; 1472 } 1473 1474 nsRegion FilterInstance::GetPreFilterNeededArea( 1475 nsIFrame* aFilteredFrame, const nsTArray<SVGFilterFrame*>& aFilterFrames, 1476 const nsRegion& aPostFilterDirtyRegion) { 1477 gfxMatrix tm = SVGUtils::GetCanvasTM(aFilteredFrame); 1478 auto filterChain = aFilteredFrame->StyleEffects()->mFilters.AsSpan(); 1479 UniquePtr<UserSpaceMetrics> metrics = 1480 UserSpaceMetricsForFrame(aFilteredFrame); 1481 // Hardcode InputIsTainted to true because we don't want JS to be able to 1482 // read the rendered contents of aFilteredFrame. 1483 FilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), 1484 *metrics, filterChain, aFilterFrames, 1485 /* InputIsTainted */ true, nullptr, tm, 1486 &aPostFilterDirtyRegion); 1487 if (!instance.IsInitialized()) { 1488 return nsRect(); 1489 } 1490 1491 // Now we can ask the instance to compute the area of the source 1492 // that's needed. 1493 return instance.ComputeSourceNeededRect(); 1494 } 1495 1496 Maybe<nsRect> FilterInstance::GetPostFilterBounds( 1497 nsIFrame* aFilteredFrame, const nsTArray<SVGFilterFrame*>& aFilterFrames, 1498 const gfxRect* aOverrideBBox, const nsRect* aPreFilterBounds) { 1499 MOZ_ASSERT(!aFilteredFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) || 1500 !aFilteredFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY), 1501 "Non-display SVG do not maintain ink overflow rects"); 1502 1503 nsRegion preFilterRegion; 1504 nsRegion* preFilterRegionPtr = nullptr; 1505 if (aPreFilterBounds) { 1506 preFilterRegion = *aPreFilterBounds; 1507 preFilterRegionPtr = &preFilterRegion; 1508 } 1509 1510 gfxMatrix tm = SVGUtils::GetCanvasTM(aFilteredFrame); 1511 auto filterChain = aFilteredFrame->StyleEffects()->mFilters.AsSpan(); 1512 UniquePtr<UserSpaceMetrics> metrics = 1513 UserSpaceMetricsForFrame(aFilteredFrame); 1514 // Hardcode InputIsTainted to true because we don't want JS to be able to 1515 // read the rendered contents of aFilteredFrame. 1516 FilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), 1517 *metrics, filterChain, aFilterFrames, 1518 /* InputIsTainted */ true, nullptr, tm, nullptr, 1519 preFilterRegionPtr, aPreFilterBounds, aOverrideBBox); 1520 if (!instance.IsInitialized()) { 1521 return Nothing(); 1522 } 1523 1524 return Some(instance.ComputePostFilterExtents()); 1525 } 1526 1527 FilterInstance::FilterInstance( 1528 nsIFrame* aTargetFrame, nsIContent* aTargetContent, 1529 const UserSpaceMetrics& aMetrics, Span<const StyleFilter> aFilterChain, 1530 const nsTArray<SVGFilterFrame*>& aFilterFrames, bool aFilterInputIsTainted, 1531 const SVGFilterPaintCallback& aPaintCallback, 1532 const gfxMatrix& aPaintTransform, const nsRegion* aPostFilterDirtyRegion, 1533 const nsRegion* aPreFilterDirtyRegion, 1534 const nsRect* aPreFilterInkOverflowRectOverride, 1535 const gfxRect* aOverrideBBox, gfxRect* aFilterSpaceBoundsNotSnapped) 1536 : mTargetFrame(aTargetFrame), 1537 mTargetContent(aTargetContent), 1538 mMetrics(aMetrics), 1539 mPaintCallback(aPaintCallback), 1540 mPaintTransform(aPaintTransform), 1541 mInitialized(false) { 1542 if (aOverrideBBox) { 1543 mTargetBBox = *aOverrideBBox; 1544 } else { 1545 MOZ_ASSERT(mTargetFrame, 1546 "Need to supply a frame when there's no aOverrideBBox"); 1547 mTargetBBox = 1548 SVGUtils::GetBBox(mTargetFrame, SVGUtils::eUseFrameBoundsForOuterSVG | 1549 SVGUtils::eBBoxIncludeFillGeometry); 1550 } 1551 1552 // Compute user space to filter space transforms. 1553 if (!ComputeUserSpaceToFilterSpaceScale()) { 1554 return; 1555 } 1556 1557 if (!ComputeTargetBBoxInFilterSpace()) { 1558 return; 1559 } 1560 1561 // Get various transforms: 1562 gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.xScale, 0.0f, 0.0f, 1563 mFilterSpaceToUserSpaceScale.yScale, 0.0f, 0.0f); 1564 1565 mFilterSpaceToFrameSpaceInCSSPxTransform = 1566 filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform(); 1567 // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible 1568 mFrameSpaceInCSSPxToFilterSpaceTransform = 1569 mFilterSpaceToFrameSpaceInCSSPxTransform; 1570 mFrameSpaceInCSSPxToFilterSpaceTransform.Invert(); 1571 1572 nsIntRect targetBounds; 1573 if (aPreFilterInkOverflowRectOverride) { 1574 targetBounds = FrameSpaceToFilterSpace(aPreFilterInkOverflowRectOverride); 1575 } else if (mTargetFrame) { 1576 nsRect preFilterVOR = mTargetFrame->PreEffectsInkOverflowRect(); 1577 targetBounds = FrameSpaceToFilterSpace(&preFilterVOR); 1578 } 1579 mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds); 1580 1581 // Build the filter graph. 1582 if (NS_FAILED(BuildPrimitives(aFilterChain, aFilterFrames, 1583 aFilterInputIsTainted))) { 1584 return; 1585 } 1586 1587 // Convert the passed in rects from frame space to filter space: 1588 mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion); 1589 mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion); 1590 1591 if (aFilterSpaceBoundsNotSnapped) { 1592 *aFilterSpaceBoundsNotSnapped = mFilterSpaceBoundsNotSnapped; 1593 } 1594 1595 mInitialized = true; 1596 } 1597 1598 bool FilterInstance::ComputeTargetBBoxInFilterSpace() { 1599 gfxRect targetBBoxInFilterSpace = UserSpaceToFilterSpace(mTargetBBox); 1600 targetBBoxInFilterSpace.RoundOut(); 1601 1602 return gfxUtils::GfxRectToIntRect(targetBBoxInFilterSpace, 1603 &mTargetBBoxInFilterSpace); 1604 } 1605 1606 bool FilterInstance::ComputeUserSpaceToFilterSpaceScale() { 1607 if (mTargetFrame) { 1608 mUserSpaceToFilterSpaceScale = mPaintTransform.ScaleFactors(); 1609 if (mUserSpaceToFilterSpaceScale.xScale <= 0.0f || 1610 mUserSpaceToFilterSpaceScale.yScale <= 0.0f) { 1611 // Nothing should be rendered. 1612 return false; 1613 } 1614 } else { 1615 mUserSpaceToFilterSpaceScale = MatrixScalesDouble(); 1616 } 1617 1618 mFilterSpaceToUserSpaceScale = 1619 MatrixScalesDouble(1.0f / mUserSpaceToFilterSpaceScale.xScale, 1620 1.0f / mUserSpaceToFilterSpaceScale.yScale); 1621 1622 return true; 1623 } 1624 1625 gfxRect FilterInstance::UserSpaceToFilterSpace( 1626 const gfxRect& aUserSpaceRect) const { 1627 gfxRect filterSpaceRect = aUserSpaceRect; 1628 filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale); 1629 return filterSpaceRect; 1630 } 1631 1632 gfxRect FilterInstance::FilterSpaceToUserSpace( 1633 const gfxRect& aFilterSpaceRect) const { 1634 gfxRect userSpaceRect = aFilterSpaceRect; 1635 userSpaceRect.Scale(mFilterSpaceToUserSpaceScale); 1636 return userSpaceRect; 1637 } 1638 1639 nsresult FilterInstance::BuildPrimitives( 1640 Span<const StyleFilter> aFilterChain, 1641 const nsTArray<SVGFilterFrame*>& aFilterFrames, 1642 bool aFilterInputIsTainted) { 1643 AutoTArray<FilterPrimitiveDescription, 8> primitiveDescriptions; 1644 1645 uint32_t filterIndex = 0; 1646 1647 for (const auto& filter : aFilterChain) { 1648 if (filter.IsUrl() && aFilterFrames.IsEmpty()) { 1649 return NS_ERROR_FAILURE; 1650 } 1651 auto* filterFrame = filter.IsUrl() ? aFilterFrames[filterIndex++] : nullptr; 1652 bool inputIsTainted = primitiveDescriptions.IsEmpty() 1653 ? aFilterInputIsTainted 1654 : primitiveDescriptions.LastElement().IsTainted(); 1655 nsresult rv = BuildPrimitivesForFilter(filter, filterFrame, inputIsTainted, 1656 primitiveDescriptions); 1657 if (NS_FAILED(rv)) { 1658 return rv; 1659 } 1660 } 1661 1662 mFilterDescription = FilterDescription(std::move(primitiveDescriptions)); 1663 1664 return NS_OK; 1665 } 1666 1667 nsresult FilterInstance::BuildPrimitivesForFilter( 1668 const StyleFilter& aFilter, SVGFilterFrame* aFilterFrame, 1669 bool aInputIsTainted, 1670 nsTArray<FilterPrimitiveDescription>& aPrimitiveDescriptions) { 1671 NS_ASSERTION(mUserSpaceToFilterSpaceScale.xScale > 0.0f && 1672 mFilterSpaceToUserSpaceScale.yScale > 0.0f, 1673 "scale factors between spaces should be positive values"); 1674 1675 if (aFilter.IsUrl()) { 1676 // Build primitives for an SVG filter. 1677 SVGFilterInstance svgFilterInstance( 1678 aFilter, aFilterFrame, mTargetContent, mMetrics, mTargetBBox, 1679 mUserSpaceToFilterSpaceScale, mFilterSpaceBoundsNotSnapped); 1680 if (!svgFilterInstance.IsInitialized()) { 1681 return NS_ERROR_FAILURE; 1682 } 1683 1684 return svgFilterInstance.BuildPrimitives(aPrimitiveDescriptions, 1685 mInputImages, aInputIsTainted); 1686 } 1687 1688 // Build primitives for a CSS filter. 1689 1690 // If we don't have a frame, use opaque black for shadows with unspecified 1691 // shadow colors. 1692 nscolor shadowFallbackColor = 1693 mTargetFrame ? mTargetFrame->StyleText()->mColor.ToColor() 1694 : NS_RGB(0, 0, 0); 1695 1696 CSSFilterInstance cssFilterInstance(aFilter, shadowFallbackColor, 1697 mTargetBounds, 1698 mFrameSpaceInCSSPxToFilterSpaceTransform); 1699 return cssFilterInstance.BuildPrimitives(aPrimitiveDescriptions, 1700 aInputIsTainted); 1701 } 1702 1703 static void UpdateNeededBounds(const nsIntRegion& aRegion, nsIntRect& aBounds) { 1704 aBounds = aRegion.GetBounds(); 1705 1706 if (aBounds.IsEmpty()) { 1707 return; 1708 } 1709 1710 bool overflow; 1711 IntSize surfaceSize = 1712 SVGUtils::ConvertToSurfaceSize(SizeDouble(aBounds.Size()), &overflow); 1713 if (overflow) { 1714 aBounds.SizeTo(surfaceSize); 1715 } 1716 } 1717 1718 void FilterInstance::ComputeNeededBoxes() { 1719 if (mFilterDescription.mPrimitives.IsEmpty()) { 1720 return; 1721 } 1722 1723 nsIntRegion sourceGraphicNeededRegion; 1724 nsIntRegion fillPaintNeededRegion; 1725 nsIntRegion strokePaintNeededRegion; 1726 1727 FilterSupport::ComputeSourceNeededRegions( 1728 mFilterDescription, mPostFilterDirtyRegion, sourceGraphicNeededRegion, 1729 fillPaintNeededRegion, strokePaintNeededRegion); 1730 1731 sourceGraphicNeededRegion.AndWith(mTargetBounds); 1732 1733 UpdateNeededBounds(sourceGraphicNeededRegion, mSourceGraphic.mNeededBounds); 1734 UpdateNeededBounds(fillPaintNeededRegion, mFillPaint.mNeededBounds); 1735 UpdateNeededBounds(strokePaintNeededRegion, mStrokePaint.mNeededBounds); 1736 } 1737 1738 void FilterInstance::BuildSourcePaint(SourceInfo* aSource, 1739 imgDrawingParams& aImgParams) { 1740 MOZ_ASSERT(mTargetFrame); 1741 nsIntRect neededRect = aSource->mNeededBounds; 1742 if (neededRect.IsEmpty()) { 1743 return; 1744 } 1745 1746 RefPtr<DrawTarget> offscreenDT = 1747 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( 1748 neededRect.Size(), SurfaceFormat::B8G8R8A8); 1749 if (!offscreenDT || !offscreenDT->IsValid()) { 1750 return; 1751 } 1752 1753 gfxContext ctx(offscreenDT); 1754 gfxContextAutoSaveRestore saver(&ctx); 1755 1756 ctx.SetMatrixDouble(mPaintTransform * 1757 gfxMatrix::Translation(-neededRect.TopLeft())); 1758 GeneralPattern pattern; 1759 if (aSource == &mFillPaint) { 1760 SVGUtils::MakeFillPatternFor(mTargetFrame, &ctx, &pattern, aImgParams); 1761 } else if (aSource == &mStrokePaint) { 1762 SVGUtils::MakeStrokePatternFor(mTargetFrame, &ctx, &pattern, aImgParams); 1763 } 1764 1765 if (pattern.GetPattern()) { 1766 offscreenDT->FillRect( 1767 ToRect(FilterSpaceToUserSpace(ThebesRect(neededRect))), pattern); 1768 } 1769 1770 aSource->mSourceSurface = offscreenDT->Snapshot(); 1771 aSource->mSurfaceRect = neededRect; 1772 } 1773 1774 void FilterInstance::BuildSourcePaints(imgDrawingParams& aImgParams) { 1775 if (!mFillPaint.mNeededBounds.IsEmpty()) { 1776 BuildSourcePaint(&mFillPaint, aImgParams); 1777 } 1778 1779 if (!mStrokePaint.mNeededBounds.IsEmpty()) { 1780 BuildSourcePaint(&mStrokePaint, aImgParams); 1781 } 1782 } 1783 1784 void FilterInstance::BuildSourceImage(DrawTarget* aDest, 1785 imgDrawingParams& aImgParams, 1786 FilterNode* aFilter, FilterNode* aSource, 1787 const Rect& aSourceRect) { 1788 MOZ_ASSERT(mTargetFrame); 1789 1790 nsIntRect neededRect = mSourceGraphic.mNeededBounds; 1791 if (neededRect.IsEmpty()) { 1792 return; 1793 } 1794 1795 RefPtr<DrawTarget> offscreenDT; 1796 SurfaceFormat format = SurfaceFormat::B8G8R8A8; 1797 if (aDest->CanCreateSimilarDrawTarget(neededRect.Size(), format)) { 1798 offscreenDT = aDest->CreateSimilarDrawTargetForFilter( 1799 neededRect.Size(), format, aFilter, aSource, aSourceRect, Point(0, 0)); 1800 } 1801 if (!offscreenDT || !offscreenDT->IsValid()) { 1802 return; 1803 } 1804 1805 gfxRect r = FilterSpaceToUserSpace(ThebesRect(neededRect)); 1806 r.RoundOut(); 1807 nsIntRect dirty; 1808 if (!gfxUtils::GfxRectToIntRect(r, &dirty)) { 1809 return; 1810 } 1811 1812 // SVG graphics paint to device space, so we need to set an initial device 1813 // space to filter space transform on the gfxContext that SourceGraphic 1814 // and SourceAlpha will paint to. 1815 // 1816 // (In theory it would be better to minimize error by having filtered SVG 1817 // graphics temporarily paint to user space when painting the sources and 1818 // only set a user space to filter space transform on the gfxContext 1819 // (since that would eliminate the transform multiplications from user 1820 // space to device space and back again). However, that would make the 1821 // code more complex while being hard to get right without introducing 1822 // subtle bugs, and in practice it probably makes no real difference.) 1823 gfxContext ctx(offscreenDT); 1824 gfxMatrix devPxToCssPxTM = SVGUtils::GetCSSPxToDevPxMatrix(mTargetFrame); 1825 DebugOnly<bool> invertible = devPxToCssPxTM.Invert(); 1826 MOZ_ASSERT(invertible); 1827 ctx.SetMatrixDouble(devPxToCssPxTM * mPaintTransform * 1828 gfxMatrix::Translation(-neededRect.TopLeft())); 1829 1830 auto imageFlags = aImgParams.imageFlags; 1831 if (mTargetFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) { 1832 // We're coming from a mask or pattern instance. Patterns 1833 // are painted into a separate surface and it seems we can't 1834 // handle the differently sized surface that might be returned 1835 // with FLAG_HIGH_QUALITY_SCALING 1836 imageFlags &= ~imgIContainer::FLAG_HIGH_QUALITY_SCALING; 1837 } 1838 imgDrawingParams imgParams(imageFlags); 1839 mPaintCallback(ctx, imgParams, &mPaintTransform, &dirty); 1840 aImgParams.result = imgParams.result; 1841 1842 mSourceGraphic.mSourceSurface = offscreenDT->Snapshot(); 1843 mSourceGraphic.mSurfaceRect = neededRect; 1844 } 1845 1846 void FilterInstance::Render(gfxContext* aCtx, imgDrawingParams& aImgParams, 1847 float aOpacity) { 1848 MOZ_ASSERT(mTargetFrame, "Need a frame for rendering"); 1849 1850 if (mFilterDescription.mPrimitives.IsEmpty()) { 1851 // An filter without any primitive. Treat it as success and paint nothing. 1852 return; 1853 } 1854 1855 nsIntRect filterRect = 1856 mPostFilterDirtyRegion.GetBounds().Intersect(OutputFilterSpaceBounds()); 1857 if (filterRect.IsEmpty() || mPaintTransform.IsSingular()) { 1858 return; 1859 } 1860 1861 gfxContextMatrixAutoSaveRestore autoSR(aCtx); 1862 aCtx->SetMatrix( 1863 aCtx->CurrentMatrix().PreTranslate(filterRect.x, filterRect.y)); 1864 1865 ComputeNeededBoxes(); 1866 1867 Rect renderRect = IntRectToRect(filterRect); 1868 RefPtr<DrawTarget> dt = aCtx->GetDrawTarget(); 1869 1870 MOZ_ASSERT(dt); 1871 if (!dt->IsValid()) { 1872 return; 1873 } 1874 1875 BuildSourcePaints(aImgParams); 1876 RefPtr<FilterNode> sourceGraphic, fillPaint, strokePaint; 1877 if (mFillPaint.mSourceSurface) { 1878 fillPaint = FilterWrappers::ForSurface(dt, mFillPaint.mSourceSurface, 1879 mFillPaint.mSurfaceRect.TopLeft()); 1880 } 1881 if (mStrokePaint.mSourceSurface) { 1882 strokePaint = FilterWrappers::ForSurface( 1883 dt, mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect.TopLeft()); 1884 } 1885 1886 // We make the sourceGraphic filter but don't set its inputs until after so 1887 // that we can make the sourceGraphic size depend on the filter chain 1888 sourceGraphic = dt->CreateFilter(FilterType::TRANSFORM); 1889 if (sourceGraphic) { 1890 // Make sure we set the translation before calling BuildSourceImage 1891 // so that CreateSimilarDrawTargetForFilter works properly 1892 IntPoint offset = mSourceGraphic.mNeededBounds.TopLeft(); 1893 sourceGraphic->SetAttribute(ATT_TRANSFORM_MATRIX, 1894 Matrix::Translation(offset.x, offset.y)); 1895 } 1896 1897 RefPtr<FilterNode> resultFilter = FilterNodeGraphFromDescription( 1898 dt, mFilterDescription, renderRect, sourceGraphic, 1899 mSourceGraphic.mSurfaceRect, fillPaint, strokePaint, mInputImages); 1900 1901 if (!resultFilter) { 1902 gfxWarning() << "Filter is NULL."; 1903 return; 1904 } 1905 1906 BuildSourceImage(dt, aImgParams, resultFilter, sourceGraphic, renderRect); 1907 if (sourceGraphic) { 1908 if (mSourceGraphic.mSourceSurface) { 1909 sourceGraphic->SetInput(IN_TRANSFORM_IN, mSourceGraphic.mSourceSurface); 1910 } else { 1911 RefPtr<FilterNode> clear = FilterWrappers::Clear(aCtx->GetDrawTarget()); 1912 sourceGraphic->SetInput(IN_TRANSFORM_IN, clear); 1913 } 1914 } 1915 1916 dt->DrawFilter(resultFilter, renderRect, Point(0, 0), DrawOptions(aOpacity)); 1917 } 1918 1919 nsRegion FilterInstance::ComputePostFilterDirtyRegion() { 1920 if (mPreFilterDirtyRegion.IsEmpty() || 1921 mFilterDescription.mPrimitives.IsEmpty()) { 1922 return nsRegion(); 1923 } 1924 1925 nsIntRegion resultChangeRegion = FilterSupport::ComputeResultChangeRegion( 1926 mFilterDescription, mPreFilterDirtyRegion, nsIntRegion(), nsIntRegion()); 1927 return FilterSpaceToFrameSpace(resultChangeRegion); 1928 } 1929 1930 nsRect FilterInstance::ComputePostFilterExtents() { 1931 if (mFilterDescription.mPrimitives.IsEmpty()) { 1932 return nsRect(); 1933 } 1934 1935 nsIntRegion postFilterExtents = FilterSupport::ComputePostFilterExtents( 1936 mFilterDescription, mTargetBounds); 1937 return FilterSpaceToFrameSpace(postFilterExtents.GetBounds()); 1938 } 1939 1940 nsRect FilterInstance::ComputeSourceNeededRect() { 1941 ComputeNeededBoxes(); 1942 return FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds); 1943 } 1944 1945 nsIntRect FilterInstance::OutputFilterSpaceBounds() const { 1946 if (mFilterDescription.mPrimitives.IsEmpty()) { 1947 return nsIntRect(); 1948 } 1949 1950 return mFilterDescription.mPrimitives.LastElement().PrimitiveSubregion(); 1951 } 1952 1953 nsIntRect FilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const { 1954 nsIntRect rect = OutputFilterSpaceBounds(); 1955 if (aRect) { 1956 if (aRect->IsEmpty()) { 1957 return nsIntRect(); 1958 } 1959 gfxRect rectInCSSPx = 1960 nsLayoutUtils::RectToGfxRect(*aRect, AppUnitsPerCSSPixel()); 1961 gfxRect rectInFilterSpace = 1962 mFrameSpaceInCSSPxToFilterSpaceTransform.TransformBounds(rectInCSSPx); 1963 rectInFilterSpace.RoundOut(); 1964 nsIntRect intRect; 1965 if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) { 1966 rect = intRect; 1967 } 1968 } 1969 return rect; 1970 } 1971 1972 nsRect FilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const { 1973 if (aRect.IsEmpty()) { 1974 return nsRect(); 1975 } 1976 gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height); 1977 r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r); 1978 // nsLayoutUtils::RoundGfxRectToAppRect rounds out. 1979 return nsLayoutUtils::RoundGfxRectToAppRect(r, AppUnitsPerCSSPixel()); 1980 } 1981 1982 nsIntRegion FilterInstance::FrameSpaceToFilterSpace( 1983 const nsRegion* aRegion) const { 1984 if (!aRegion) { 1985 return OutputFilterSpaceBounds(); 1986 } 1987 nsIntRegion result; 1988 for (auto iter = aRegion->RectIter(); !iter.Done(); iter.Next()) { 1989 // FrameSpaceToFilterSpace rounds out, so this works. 1990 nsRect rect = iter.Get(); 1991 result.OrWith(FrameSpaceToFilterSpace(&rect)); 1992 } 1993 return result; 1994 } 1995 1996 nsRegion FilterInstance::FilterSpaceToFrameSpace( 1997 const nsIntRegion& aRegion) const { 1998 nsRegion result; 1999 for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { 2000 // FilterSpaceToFrameSpace rounds out, so this works. 2001 result.OrWith(FilterSpaceToFrameSpace(iter.Get())); 2002 } 2003 return result; 2004 } 2005 2006 gfxMatrix FilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const { 2007 if (!mTargetFrame) { 2008 return gfxMatrix(); 2009 } 2010 return gfxMatrix::Translation( 2011 -SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mTargetFrame)); 2012 } 2013 2014 } // namespace mozilla