SVGPatternFrame.cpp (26105B)
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 "SVGPatternFrame.h" 9 10 // Keep others in (case-insensitive) order: 11 #include "AutoReferenceChainGuard.h" 12 #include "SVGAnimatedTransformList.h" 13 #include "gfx2DGlue.h" 14 #include "gfxContext.h" 15 #include "gfxMatrix.h" 16 #include "gfxPattern.h" 17 #include "gfxPlatform.h" 18 #include "mozilla/ComputedStyle.h" 19 #include "mozilla/ISVGDisplayableFrame.h" 20 #include "mozilla/PresShell.h" 21 #include "mozilla/SVGContentUtils.h" 22 #include "mozilla/SVGGeometryFrame.h" 23 #include "mozilla/SVGObserverUtils.h" 24 #include "mozilla/SVGUtils.h" 25 #include "mozilla/dom/SVGPatternElement.h" 26 #include "mozilla/dom/SVGUnitTypesBinding.h" 27 #include "mozilla/gfx/2D.h" 28 #include "nsGkAtoms.h" 29 #include "nsIFrameInlines.h" 30 31 using namespace mozilla::dom; 32 using namespace mozilla::dom::SVGUnitTypes_Binding; 33 using namespace mozilla::gfx; 34 using namespace mozilla::image; 35 36 namespace mozilla { 37 38 //---------------------------------------------------------------------- 39 // Implementation 40 41 SVGPatternFrame::SVGPatternFrame(ComputedStyle* aStyle, 42 nsPresContext* aPresContext) 43 : SVGPaintServerFrame(aStyle, aPresContext, kClassID), 44 mSource(nullptr), 45 mLoopFlag(false), 46 mNoHRefURI(false) {} 47 48 NS_IMPL_FRAMEARENA_HELPERS(SVGPatternFrame) 49 50 NS_QUERYFRAME_HEAD(SVGPatternFrame) 51 NS_QUERYFRAME_ENTRY(SVGPatternFrame) 52 NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame) 53 54 //---------------------------------------------------------------------- 55 // nsIFrame methods: 56 57 nsresult SVGPatternFrame::AttributeChanged(int32_t aNameSpaceID, 58 nsAtom* aAttribute, 59 AttrModType aModType) { 60 if (aNameSpaceID == kNameSpaceID_None && 61 (aAttribute == nsGkAtoms::patternUnits || 62 aAttribute == nsGkAtoms::patternContentUnits || 63 aAttribute == nsGkAtoms::patternTransform || 64 aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y || 65 aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height || 66 aAttribute == nsGkAtoms::preserveAspectRatio || 67 aAttribute == nsGkAtoms::viewBox)) { 68 SVGObserverUtils::InvalidateRenderingObservers(this); 69 } 70 71 if ((aNameSpaceID == kNameSpaceID_XLink || 72 aNameSpaceID == kNameSpaceID_None) && 73 aAttribute == nsGkAtoms::href) { 74 // Blow away our reference, if any 75 SVGObserverUtils::RemoveTemplateObserver(this); 76 mNoHRefURI = false; 77 // And update whoever references us 78 SVGObserverUtils::InvalidateRenderingObservers(this); 79 } 80 81 return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute, 82 aModType); 83 } 84 85 #ifdef DEBUG 86 void SVGPatternFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, 87 nsIFrame* aPrevInFlow) { 88 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::pattern), 89 "Content is not an SVG pattern"); 90 91 SVGPaintServerFrame::Init(aContent, aParent, aPrevInFlow); 92 } 93 #endif /* DEBUG */ 94 95 //---------------------------------------------------------------------- 96 // SVGContainerFrame methods: 97 98 // If our GetCanvasTM is getting called, we 99 // need to return *our current* transformation 100 // matrix, which depends on our units parameters 101 // and X, Y, Width, and Height 102 gfxMatrix SVGPatternFrame::GetCanvasTM() { 103 if (mCTM) { 104 return *mCTM; 105 } 106 107 // Do we know our rendering parent? 108 if (mSource) { 109 // Yes, use it! 110 return mSource->GetCanvasTM(); 111 } 112 113 // We get here when geometry in the <pattern> container is updated 114 return gfxMatrix(); 115 } 116 117 // ------------------------------------------------------------------------- 118 // Helper functions 119 // ------------------------------------------------------------------------- 120 121 /** Calculate the maximum expansion of a matrix */ 122 static float MaxExpansion(const Matrix& aMatrix) { 123 // maximum expansion derivation from 124 // http://lists.cairographics.org/archives/cairo/2004-October/001980.html 125 // and also implemented in cairo_matrix_transformed_circle_major_axis 126 double a = aMatrix._11; 127 double b = aMatrix._12; 128 double c = aMatrix._21; 129 double d = aMatrix._22; 130 double f = (a * a + b * b + c * c + d * d) / 2; 131 double g = (a * a + b * b - c * c - d * d) / 2; 132 double h = a * c + b * d; 133 return sqrt(f + sqrt(g * g + h * h)); 134 } 135 136 // The SVG specification says that the 'patternContentUnits' attribute "has no 137 // effect if attribute ‘viewBox’ is specified". We still need to include a bbox 138 // scale if the viewBox is specified and _patternUnits_ is set to or defaults to 139 // objectBoundingBox though, since in that case the viewBox is relative to the 140 // bbox 141 static bool IncludeBBoxScale(const SVGAnimatedViewBox& aViewBox, 142 uint32_t aPatternContentUnits, 143 uint32_t aPatternUnits) { 144 return (!aViewBox.IsExplicitlySet() && 145 aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) || 146 (aViewBox.IsExplicitlySet() && 147 aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX); 148 } 149 150 // Given the matrix for the pattern element's own transform, this returns a 151 // combined matrix including the transforms applicable to its target. 152 static Matrix GetPatternMatrix(nsIFrame* aSource, 153 const StyleSVGPaint nsStyleSVG::* aFillOrStroke, 154 uint16_t aPatternUnits, 155 const gfxMatrix& patternTransform, 156 const gfxRect& bbox, const gfxRect& callerBBox, 157 const Matrix& callerCTM) { 158 // We really want the pattern matrix to handle translations 159 gfxFloat minx = bbox.X(); 160 gfxFloat miny = bbox.Y(); 161 162 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 163 minx += callerBBox.X(); 164 miny += callerBBox.Y(); 165 } 166 167 double scale = 1.0 / MaxExpansion(callerCTM); 168 auto patternMatrix = patternTransform; 169 patternMatrix.PreScale(scale, scale); 170 patternMatrix.PreTranslate(minx, miny); 171 172 // revert the vector effect transform so that the pattern appears unchanged 173 if (aFillOrStroke == &nsStyleSVG::mStroke) { 174 gfxMatrix userToOuterSVG; 175 if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) { 176 patternMatrix *= userToOuterSVG; 177 } 178 } 179 180 return ToMatrix(patternMatrix); 181 } 182 183 static nsresult GetTargetGeometry(gfxRect* aBBox, 184 const SVGAnimatedViewBox& aViewBox, 185 uint16_t aPatternContentUnits, 186 uint16_t aPatternUnits, nsIFrame* aTarget, 187 const Matrix& aContextMatrix, 188 const gfxRect* aOverrideBounds) { 189 *aBBox = 190 aOverrideBounds 191 ? *aOverrideBounds 192 : SVGUtils::GetBBox(aTarget, SVGUtils::eUseFrameBoundsForOuterSVG | 193 SVGUtils::eBBoxIncludeFillGeometry); 194 195 // Sanity check 196 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) && 197 aBBox->IsEmpty()) { 198 return NS_ERROR_FAILURE; 199 } 200 201 // OK, now fix up the bounding box to reflect user coordinates 202 // We handle device unit scaling in pattern matrix 203 float scale = MaxExpansion(aContextMatrix); 204 if (scale <= 0) { 205 return NS_ERROR_FAILURE; 206 } 207 aBBox->Scale(scale); 208 return NS_OK; 209 } 210 211 void SVGPatternFrame::PaintChildren(DrawTarget* aDrawTarget, 212 SVGPatternFrame* aPatternWithChildren, 213 nsIFrame* aSource, float aGraphicOpacity, 214 imgDrawingParams& aImgParams) { 215 gfxContext ctx(aDrawTarget); 216 gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&ctx); 217 218 if (aGraphicOpacity != 1.0f) { 219 autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 220 aGraphicOpacity); 221 } 222 223 // OK, now render -- note that we use "firstKid", which 224 // we got at the beginning because it takes care of the 225 // referenced pattern situation for us 226 227 if (aSource->IsSVGGeometryFrame()) { 228 // Set the geometrical parent of the pattern we are rendering 229 aPatternWithChildren->mSource = static_cast<SVGGeometryFrame*>(aSource); 230 } 231 232 // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can 233 // give back a clear surface if there's a loop 234 if (!aPatternWithChildren->HasAnyStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER)) { 235 AutoSetRestorePaintServerState paintServer(aPatternWithChildren); 236 for (auto* kid : aPatternWithChildren->mFrames) { 237 gfxMatrix tm = *(aPatternWithChildren->mCTM); 238 239 // The CTM of each frame referencing us can be different 240 ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); 241 if (SVGFrame) { 242 SVGFrame->NotifySVGChanged( 243 ISVGDisplayableFrame::ChangeFlag::TransformChanged); 244 tm = SVGUtils::GetTransformMatrixInUserSpace(kid) * tm; 245 } 246 247 SVGUtils::PaintFrameWithEffects(kid, ctx, tm, aImgParams); 248 } 249 } 250 251 aPatternWithChildren->mSource = nullptr; 252 } 253 254 already_AddRefed<SourceSurface> SVGPatternFrame::PaintPattern( 255 const DrawTarget* aDrawTarget, Matrix* patternMatrix, 256 const Matrix& aContextMatrix, nsIFrame* aSource, 257 StyleSVGPaint nsStyleSVG::* aFillOrStroke, float aGraphicOpacity, 258 const gfxRect* aOverrideBounds, imgDrawingParams& aImgParams) { 259 /* 260 * General approach: 261 * Set the content geometry stuff 262 * Calculate our bbox (using x,y,width,height & patternUnits & 263 * patternTransform) 264 * Create the surface 265 * Calculate the content transformation matrix 266 * Get our children (we may need to get them from another Pattern) 267 * Call SVGPaint on all of our children 268 * Return 269 */ 270 271 SVGPatternFrame* patternWithChildren = GetPatternWithChildren(); 272 if (!patternWithChildren) { 273 // Either no kids or a bad reference 274 return nullptr; 275 } 276 277 const SVGAnimatedViewBox& viewBox = GetViewBox(); 278 279 uint16_t patternContentUnits = 280 GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS); 281 uint16_t patternUnits = GetEnumValue(SVGPatternElement::PATTERNUNITS); 282 283 /* 284 * Get the content geometry information. This is a little tricky -- 285 * our parent is probably a <defs>, but we are rendering in the context 286 * of some geometry source. Our content geometry information needs to 287 * come from our rendering parent as opposed to our content parent. We 288 * get that information from aSource, which is passed to us from the 289 * backend renderer. 290 * 291 * There are three "geometries" that we need: 292 * 1) The bounding box for the pattern. We use this to get the 293 * width and height for the surface, and as the return to 294 * GetBBox. 295 * 2) The transformation matrix for the pattern. This is not *quite* 296 * the same as the canvas transformation matrix that we will 297 * provide to our rendering children since we "fudge" it a little 298 * to get the renderer to handle the translations correctly for us. 299 * 3) The CTM that we return to our children who make up the pattern. 300 */ 301 302 // Get all of the information we need from our "caller" -- i.e. 303 // the geometry that is being rendered with a pattern 304 gfxRect callerBBox; 305 if (NS_FAILED(GetTargetGeometry(&callerBBox, viewBox, patternContentUnits, 306 patternUnits, aSource, aContextMatrix, 307 aOverrideBounds))) { 308 return nullptr; 309 } 310 311 // Construct the CTM that we will provide to our children when we 312 // render them into the tile. 313 gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits, 314 callerBBox, aContextMatrix, aSource); 315 if (ctm.IsSingular()) { 316 return nullptr; 317 } 318 319 if (patternWithChildren->mCTM) { 320 *patternWithChildren->mCTM = ctm; 321 } else { 322 patternWithChildren->mCTM = MakeUnique<gfxMatrix>(ctm); 323 } 324 325 // Get the bounding box of the pattern. This will be used to determine 326 // the size of the surface, and will also be used to define the bounding 327 // box for the pattern tile. 328 gfxRect bbox = 329 GetPatternRect(patternUnits, callerBBox, aContextMatrix, aSource); 330 if (bbox.IsEmpty()) { 331 return nullptr; 332 } 333 334 // Get the pattern transform 335 auto patternTransform = GetPatternTransform(); 336 337 // Get the transformation matrix that we will hand to the renderer's pattern 338 // routine. 339 *patternMatrix = 340 GetPatternMatrix(aSource, aFillOrStroke, patternUnits, patternTransform, 341 bbox, callerBBox, aContextMatrix); 342 if (patternMatrix->IsSingular()) { 343 return nullptr; 344 } 345 346 // Now that we have all of the necessary geometries, we can 347 // create our surface. 348 gfxSize scaledSize = bbox.Size() * MaxExpansion(ToMatrix(patternTransform)); 349 350 bool resultOverflows; 351 IntSize surfaceSize = 352 SVGUtils::ConvertToSurfaceSize(scaledSize, &resultOverflows); 353 354 // 0 disables rendering, < 0 is an error 355 if (surfaceSize.width <= 0 || surfaceSize.height <= 0) { 356 return nullptr; 357 } 358 359 gfxFloat patternWidth = bbox.Width(); 360 gfxFloat patternHeight = bbox.Height(); 361 362 if (resultOverflows || patternWidth != surfaceSize.width || 363 patternHeight != surfaceSize.height) { 364 // scale drawing to pattern surface size 365 patternWithChildren->mCTM->PostScale(surfaceSize.width / patternWidth, 366 surfaceSize.height / patternHeight); 367 368 // and rescale pattern to compensate 369 patternMatrix->PreScale(patternWidth / surfaceSize.width, 370 patternHeight / surfaceSize.height); 371 } 372 373 RefPtr<DrawTarget> dt = aDrawTarget->CreateSimilarDrawTargetWithBacking( 374 surfaceSize, SurfaceFormat::B8G8R8A8); 375 if (!dt || !dt->IsValid()) { 376 return nullptr; 377 } 378 dt->ClearRect(Rect(0, 0, surfaceSize.width, surfaceSize.height)); 379 380 PaintChildren(dt, patternWithChildren, aSource, aGraphicOpacity, aImgParams); 381 382 // caller now owns the surface 383 return dt->GetBackingSurface(); 384 } 385 386 /* Will probably need something like this... */ 387 // How do we handle the insertion of a new frame? 388 // We really don't want to rerender this every time, 389 // do we? 390 SVGPatternFrame* SVGPatternFrame::GetPatternWithChildren() { 391 // Do we have any children ourselves? 392 if (!mFrames.IsEmpty()) { 393 return this; 394 } 395 396 // No, see if we chain to someone who does 397 398 // Before we recurse, make sure we'll break reference loops and over long 399 // reference chains: 400 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 401 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 402 &sRefChainLengthCounter); 403 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 404 // Break reference chain 405 return nullptr; 406 } 407 408 SVGPatternFrame* next = GetReferencedPattern(); 409 if (!next) { 410 return nullptr; 411 } 412 413 return next->GetPatternWithChildren(); 414 } 415 416 uint16_t SVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) { 417 SVGAnimatedEnumeration& thisEnum = 418 static_cast<SVGPatternElement*>(GetContent())->mEnumAttributes[aIndex]; 419 420 if (thisEnum.IsExplicitlySet()) { 421 return thisEnum.GetAnimValue(); 422 } 423 424 // Before we recurse, make sure we'll break reference loops and over long 425 // reference chains: 426 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 427 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 428 &sRefChainLengthCounter); 429 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 430 // Break reference chain 431 return static_cast<SVGPatternElement*>(aDefault) 432 ->mEnumAttributes[aIndex] 433 .GetAnimValue(); 434 } 435 436 SVGPatternFrame* next = GetReferencedPattern(); 437 return next ? next->GetEnumValue(aIndex, aDefault) 438 : static_cast<SVGPatternElement*>(aDefault) 439 ->mEnumAttributes[aIndex] 440 .GetAnimValue(); 441 } 442 443 SVGPatternFrame* SVGPatternFrame::GetPatternTransformFrame( 444 SVGPatternFrame* aDefault) { 445 if (!StyleDisplay()->mTransform.IsNone()) { 446 return this; 447 } 448 449 // Before we recurse, make sure we'll break reference loops and over long 450 // reference chains: 451 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 452 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 453 &sRefChainLengthCounter); 454 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 455 // Break reference chain 456 return aDefault; 457 } 458 459 if (SVGPatternFrame* next = GetReferencedPattern()) { 460 return next->GetPatternTransformFrame(aDefault); 461 } 462 return aDefault; 463 } 464 465 gfxMatrix SVGPatternFrame::GetPatternTransform() { 466 return SVGUtils::GetTransformMatrixInUserSpace( 467 GetPatternTransformFrame(this)); 468 } 469 470 const SVGAnimatedViewBox& SVGPatternFrame::GetViewBox(nsIContent* aDefault) { 471 const SVGAnimatedViewBox& thisViewBox = 472 static_cast<SVGPatternElement*>(GetContent())->mViewBox; 473 474 if (thisViewBox.IsExplicitlySet()) { 475 return thisViewBox; 476 } 477 478 // Before we recurse, make sure we'll break reference loops and over long 479 // reference chains: 480 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 481 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 482 &sRefChainLengthCounter); 483 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 484 // Break reference chain 485 return static_cast<SVGPatternElement*>(aDefault)->mViewBox; 486 } 487 488 SVGPatternFrame* next = GetReferencedPattern(); 489 return next ? next->GetViewBox(aDefault) 490 : static_cast<SVGPatternElement*>(aDefault)->mViewBox; 491 } 492 493 const SVGAnimatedPreserveAspectRatio& SVGPatternFrame::GetPreserveAspectRatio( 494 nsIContent* aDefault) { 495 const SVGAnimatedPreserveAspectRatio& thisPar = 496 static_cast<SVGPatternElement*>(GetContent())->mPreserveAspectRatio; 497 498 if (thisPar.IsExplicitlySet()) { 499 return thisPar; 500 } 501 502 // Before we recurse, make sure we'll break reference loops and over long 503 // reference chains: 504 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 505 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 506 &sRefChainLengthCounter); 507 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 508 // Break reference chain 509 return static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio; 510 } 511 512 SVGPatternFrame* next = GetReferencedPattern(); 513 return next ? next->GetPreserveAspectRatio(aDefault) 514 : static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio; 515 } 516 517 const SVGAnimatedLength* SVGPatternFrame::GetLengthValue(uint32_t aIndex, 518 nsIContent* aDefault) { 519 const SVGAnimatedLength* thisLength = 520 &static_cast<SVGPatternElement*>(GetContent())->mLengthAttributes[aIndex]; 521 522 if (thisLength->IsExplicitlySet()) { 523 return thisLength; 524 } 525 526 // Before we recurse, make sure we'll break reference loops and over long 527 // reference chains: 528 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 529 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 530 &sRefChainLengthCounter); 531 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 532 // Break reference chain 533 return &static_cast<SVGPatternElement*>(aDefault) 534 ->mLengthAttributes[aIndex]; 535 } 536 537 SVGPatternFrame* next = GetReferencedPattern(); 538 return next ? next->GetLengthValue(aIndex, aDefault) 539 : &static_cast<SVGPatternElement*>(aDefault) 540 ->mLengthAttributes[aIndex]; 541 } 542 543 // Private (helper) methods 544 545 SVGPatternFrame* SVGPatternFrame::GetReferencedPattern() { 546 if (mNoHRefURI) { 547 return nullptr; 548 } 549 550 auto GetHref = [this](nsAString& aHref) { 551 SVGPatternElement* pattern = 552 static_cast<SVGPatternElement*>(this->GetContent()); 553 if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) { 554 pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(aHref, 555 pattern); 556 } else { 557 pattern->mStringAttributes[SVGPatternElement::XLINK_HREF].GetAnimValue( 558 aHref, pattern); 559 } 560 this->mNoHRefURI = aHref.IsEmpty(); 561 }; 562 563 // We don't call SVGObserverUtils::RemoveTemplateObserver and set 564 // `mNoHRefURI = false` on failure since we want to be invalidated if the ID 565 // specified by our href starts resolving to a different/valid element. 566 567 return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref)); 568 } 569 570 gfxRect SVGPatternFrame::GetPatternRect(uint16_t aPatternUnits, 571 const gfxRect& aTargetBBox, 572 const Matrix& aTargetCTM, 573 nsIFrame* aTarget) { 574 // We need to initialize our box 575 float x, y, width, height; 576 577 // Get the pattern x,y,width, and height 578 const SVGAnimatedLength *tmpX, *tmpY, *tmpHeight, *tmpWidth; 579 tmpX = GetLengthValue(SVGPatternElement::ATTR_X); 580 tmpY = GetLengthValue(SVGPatternElement::ATTR_Y); 581 tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT); 582 tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH); 583 584 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 585 SVGElementMetrics metrics(SVGElement::FromNode(GetContent())); 586 x = SVGUtils::ObjectSpace(aTargetBBox, metrics, tmpX); 587 y = SVGUtils::ObjectSpace(aTargetBBox, metrics, tmpY); 588 width = SVGUtils::ObjectSpace(aTargetBBox, metrics, tmpWidth); 589 height = SVGUtils::ObjectSpace(aTargetBBox, metrics, tmpHeight); 590 } else { 591 if (aTarget->IsTextFrame()) { 592 aTarget = aTarget->GetParent(); 593 } 594 float scale = MaxExpansion(aTargetCTM); 595 x = SVGUtils::UserSpace(aTarget, tmpX) * scale; 596 y = SVGUtils::UserSpace(aTarget, tmpY) * scale; 597 width = SVGUtils::UserSpace(aTarget, tmpWidth) * scale; 598 height = SVGUtils::UserSpace(aTarget, tmpHeight) * scale; 599 } 600 601 return gfxRect(x, y, width, height); 602 } 603 604 gfxMatrix SVGPatternFrame::ConstructCTM(const SVGAnimatedViewBox& aViewBox, 605 uint16_t aPatternContentUnits, 606 uint16_t aPatternUnits, 607 const gfxRect& callerBBox, 608 const Matrix& callerCTM, 609 nsIFrame* aTarget) { 610 if (aTarget->IsTextFrame()) { 611 aTarget = aTarget->GetParent(); 612 } 613 nsIContent* targetContent = aTarget->GetContent(); 614 SVGViewportElement* ctx = nullptr; 615 gfxFloat scaleX, scaleY; 616 617 // The objectBoundingBox conversion must be handled in the CTM: 618 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) { 619 scaleX = callerBBox.Width(); 620 scaleY = callerBBox.Height(); 621 } else { 622 if (targetContent->IsSVGElement()) { 623 ctx = static_cast<SVGElement*>(targetContent)->GetCtx(); 624 } 625 scaleX = scaleY = MaxExpansion(callerCTM); 626 } 627 628 if (!aViewBox.IsExplicitlySet()) { 629 return gfxMatrix(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0); 630 } 631 const SVGViewBox& viewBox = 632 aViewBox.GetAnimValue() * Style()->EffectiveZoom().ToFloat(); 633 634 if (!viewBox.IsValid()) { 635 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular 636 } 637 638 float viewportWidth, viewportHeight; 639 if (targetContent->IsSVGElement()) { 640 // If we're dealing with an SVG target only retrieve the context once. 641 // Calling the nsIFrame* variant of GetAnimValue would look it up on 642 // every call. 643 viewportWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH) 644 ->GetAnimValueWithZoom(ctx); 645 viewportHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT) 646 ->GetAnimValueWithZoom(ctx); 647 } else { 648 // No SVG target, call the nsIFrame* variant of GetAnimValue. 649 viewportWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH) 650 ->GetAnimValueWithZoom(aTarget); 651 viewportHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT) 652 ->GetAnimValueWithZoom(aTarget); 653 } 654 655 if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) { 656 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular 657 } 658 659 Matrix tm = SVGContentUtils::GetViewBoxTransform( 660 viewportWidth * scaleX, viewportHeight * scaleY, viewBox.x, viewBox.y, 661 viewBox.width, viewBox.height, GetPreserveAspectRatio()); 662 663 return ThebesMatrix(tm); 664 } 665 666 //---------------------------------------------------------------------- 667 // SVGPaintServerFrame methods: 668 already_AddRefed<gfxPattern> SVGPatternFrame::GetPaintServerPattern( 669 nsIFrame* aSource, const DrawTarget* aDrawTarget, 670 const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::* aFillOrStroke, 671 float aGraphicOpacity, imgDrawingParams& aImgParams, 672 const gfxRect* aOverrideBounds) { 673 if (aGraphicOpacity == 0.0f) { 674 return do_AddRef(new gfxPattern(DeviceColor())); 675 } 676 677 // Paint it! 678 Matrix pMatrix; 679 RefPtr<SourceSurface> surface = 680 PaintPattern(aDrawTarget, &pMatrix, ToMatrix(aContextMatrix), aSource, 681 aFillOrStroke, aGraphicOpacity, aOverrideBounds, aImgParams); 682 683 if (!surface) { 684 return nullptr; 685 } 686 687 auto pattern = MakeRefPtr<gfxPattern>(surface, pMatrix); 688 pattern->SetExtend(ExtendMode::REPEAT); 689 690 return pattern.forget(); 691 } 692 693 } // namespace mozilla 694 695 // ------------------------------------------------------------------------- 696 // Public functions 697 // ------------------------------------------------------------------------- 698 699 nsIFrame* NS_NewSVGPatternFrame(mozilla::PresShell* aPresShell, 700 mozilla::ComputedStyle* aStyle) { 701 return new (aPresShell) 702 mozilla::SVGPatternFrame(aStyle, aPresShell->GetPresContext()); 703 }