layout/svg/nsSVGPatternFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 // Main header first:
michael@0 7 #include "nsSVGPatternFrame.h"
michael@0 8
michael@0 9 // Keep others in (case-insensitive) order:
michael@0 10 #include "gfx2DGlue.h"
michael@0 11 #include "gfxContext.h"
michael@0 12 #include "gfxMatrix.h"
michael@0 13 #include "gfxPattern.h"
michael@0 14 #include "gfxPlatform.h"
michael@0 15 #include "mozilla/gfx/2D.h"
michael@0 16 #include "nsContentUtils.h"
michael@0 17 #include "nsGkAtoms.h"
michael@0 18 #include "nsISVGChildFrame.h"
michael@0 19 #include "nsRenderingContext.h"
michael@0 20 #include "nsStyleContext.h"
michael@0 21 #include "nsSVGEffects.h"
michael@0 22 #include "nsSVGPathGeometryFrame.h"
michael@0 23 #include "mozilla/dom/SVGPatternElement.h"
michael@0 24 #include "nsSVGUtils.h"
michael@0 25 #include "nsSVGAnimatedTransformList.h"
michael@0 26 #include "SVGContentUtils.h"
michael@0 27 #include "gfxColor.h"
michael@0 28
michael@0 29 using namespace mozilla;
michael@0 30 using namespace mozilla::dom;
michael@0 31 using namespace mozilla::gfx;
michael@0 32
michael@0 33 //----------------------------------------------------------------------
michael@0 34 // Helper classes
michael@0 35
michael@0 36 class MOZ_STACK_CLASS nsSVGPatternFrame::AutoPatternReferencer
michael@0 37 {
michael@0 38 public:
michael@0 39 AutoPatternReferencer(nsSVGPatternFrame *aFrame
michael@0 40 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
michael@0 41 : mFrame(aFrame)
michael@0 42 {
michael@0 43 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 44 // Reference loops should normally be detected in advance and handled, so
michael@0 45 // we're not expecting to encounter them here
michael@0 46 NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!");
michael@0 47 mFrame->mLoopFlag = true;
michael@0 48 }
michael@0 49 ~AutoPatternReferencer() {
michael@0 50 mFrame->mLoopFlag = false;
michael@0 51 }
michael@0 52 private:
michael@0 53 nsSVGPatternFrame *mFrame;
michael@0 54 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
michael@0 55 };
michael@0 56
michael@0 57 //----------------------------------------------------------------------
michael@0 58 // Implementation
michael@0 59
michael@0 60 nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext) :
michael@0 61 nsSVGPatternFrameBase(aContext),
michael@0 62 mLoopFlag(false),
michael@0 63 mNoHRefURI(false)
michael@0 64 {
michael@0 65 }
michael@0 66
michael@0 67 NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame)
michael@0 68
michael@0 69 //----------------------------------------------------------------------
michael@0 70 // nsIFrame methods:
michael@0 71
michael@0 72 nsresult
michael@0 73 nsSVGPatternFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 74 nsIAtom* aAttribute,
michael@0 75 int32_t aModType)
michael@0 76 {
michael@0 77 if (aNameSpaceID == kNameSpaceID_None &&
michael@0 78 (aAttribute == nsGkAtoms::patternUnits ||
michael@0 79 aAttribute == nsGkAtoms::patternContentUnits ||
michael@0 80 aAttribute == nsGkAtoms::patternTransform ||
michael@0 81 aAttribute == nsGkAtoms::x ||
michael@0 82 aAttribute == nsGkAtoms::y ||
michael@0 83 aAttribute == nsGkAtoms::width ||
michael@0 84 aAttribute == nsGkAtoms::height ||
michael@0 85 aAttribute == nsGkAtoms::preserveAspectRatio ||
michael@0 86 aAttribute == nsGkAtoms::viewBox)) {
michael@0 87 nsSVGEffects::InvalidateDirectRenderingObservers(this);
michael@0 88 }
michael@0 89
michael@0 90 if (aNameSpaceID == kNameSpaceID_XLink &&
michael@0 91 aAttribute == nsGkAtoms::href) {
michael@0 92 // Blow away our reference, if any
michael@0 93 Properties().Delete(nsSVGEffects::HrefProperty());
michael@0 94 mNoHRefURI = false;
michael@0 95 // And update whoever references us
michael@0 96 nsSVGEffects::InvalidateDirectRenderingObservers(this);
michael@0 97 }
michael@0 98
michael@0 99 return nsSVGPatternFrameBase::AttributeChanged(aNameSpaceID,
michael@0 100 aAttribute, aModType);
michael@0 101 }
michael@0 102
michael@0 103 #ifdef DEBUG
michael@0 104 void
michael@0 105 nsSVGPatternFrame::Init(nsIContent* aContent,
michael@0 106 nsIFrame* aParent,
michael@0 107 nsIFrame* aPrevInFlow)
michael@0 108 {
michael@0 109 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::pattern), "Content is not an SVG pattern");
michael@0 110
michael@0 111 nsSVGPatternFrameBase::Init(aContent, aParent, aPrevInFlow);
michael@0 112 }
michael@0 113 #endif /* DEBUG */
michael@0 114
michael@0 115 nsIAtom*
michael@0 116 nsSVGPatternFrame::GetType() const
michael@0 117 {
michael@0 118 return nsGkAtoms::svgPatternFrame;
michael@0 119 }
michael@0 120
michael@0 121 //----------------------------------------------------------------------
michael@0 122 // nsSVGContainerFrame methods:
michael@0 123
michael@0 124 // If our GetCanvasTM is getting called, we
michael@0 125 // need to return *our current* transformation
michael@0 126 // matrix, which depends on our units parameters
michael@0 127 // and X, Y, Width, and Height
michael@0 128 gfxMatrix
michael@0 129 nsSVGPatternFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
michael@0 130 {
michael@0 131 if (mCTM) {
michael@0 132 return *mCTM;
michael@0 133 }
michael@0 134
michael@0 135 // Do we know our rendering parent?
michael@0 136 if (mSource) {
michael@0 137 // Yes, use it!
michael@0 138 return mSource->GetCanvasTM(aFor, aTransformRoot);
michael@0 139 }
michael@0 140
michael@0 141 // We get here when geometry in the <pattern> container is updated
michael@0 142 return gfxMatrix();
michael@0 143 }
michael@0 144
michael@0 145 // -------------------------------------------------------------------------
michael@0 146 // Helper functions
michael@0 147 // -------------------------------------------------------------------------
michael@0 148
michael@0 149 /** Calculate the maximum expansion of a matrix */
michael@0 150 static float
michael@0 151 MaxExpansion(const Matrix &aMatrix)
michael@0 152 {
michael@0 153 // maximum expansion derivation from
michael@0 154 // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
michael@0 155 // and also implemented in cairo_matrix_transformed_circle_major_axis
michael@0 156 double a = aMatrix._11;
michael@0 157 double b = aMatrix._12;
michael@0 158 double c = aMatrix._21;
michael@0 159 double d = aMatrix._22;
michael@0 160 double f = (a * a + b * b + c * c + d * d) / 2;
michael@0 161 double g = (a * a + b * b - c * c - d * d) / 2;
michael@0 162 double h = a * c + b * d;
michael@0 163 return sqrt(f + sqrt(g * g + h * h));
michael@0 164 }
michael@0 165
michael@0 166 // The SVG specification says that the 'patternContentUnits' attribute "has no effect if
michael@0 167 // attribute ‘viewBox’ is specified". We still need to include a bbox scale
michael@0 168 // if the viewBox is specified and _patternUnits_ is set to or defaults to
michael@0 169 // objectBoundingBox though, since in that case the viewBox is relative to the bbox
michael@0 170 static bool
michael@0 171 IncludeBBoxScale(const nsSVGViewBox& aViewBox,
michael@0 172 uint32_t aPatternContentUnits, uint32_t aPatternUnits)
michael@0 173 {
michael@0 174 return (!aViewBox.IsExplicitlySet() &&
michael@0 175 aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) ||
michael@0 176 (aViewBox.IsExplicitlySet() &&
michael@0 177 aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
michael@0 178 }
michael@0 179
michael@0 180 // Given the matrix for the pattern element's own transform, this returns a
michael@0 181 // combined matrix including the transforms applicable to its target.
michael@0 182 static gfxMatrix
michael@0 183 GetPatternMatrix(uint16_t aPatternUnits,
michael@0 184 const gfxMatrix &patternTransform,
michael@0 185 const gfxRect &bbox,
michael@0 186 const gfxRect &callerBBox,
michael@0 187 const Matrix &callerCTM)
michael@0 188 {
michael@0 189 // We really want the pattern matrix to handle translations
michael@0 190 gfxFloat minx = bbox.X();
michael@0 191 gfxFloat miny = bbox.Y();
michael@0 192
michael@0 193 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
michael@0 194 minx += callerBBox.X();
michael@0 195 miny += callerBBox.Y();
michael@0 196 }
michael@0 197
michael@0 198 float scale = 1.0f / MaxExpansion(callerCTM);
michael@0 199 gfxMatrix patternMatrix = patternTransform;
michael@0 200 patternMatrix.Scale(scale, scale);
michael@0 201 patternMatrix.Translate(gfxPoint(minx, miny));
michael@0 202
michael@0 203 return patternMatrix;
michael@0 204 }
michael@0 205
michael@0 206 static nsresult
michael@0 207 GetTargetGeometry(gfxRect *aBBox,
michael@0 208 const nsSVGViewBox &aViewBox,
michael@0 209 uint16_t aPatternContentUnits,
michael@0 210 uint16_t aPatternUnits,
michael@0 211 nsIFrame *aTarget,
michael@0 212 const Matrix &aContextMatrix,
michael@0 213 const gfxRect *aOverrideBounds)
michael@0 214 {
michael@0 215 *aBBox = aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aTarget);
michael@0 216
michael@0 217 // Sanity check
michael@0 218 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) &&
michael@0 219 (aBBox->Width() <= 0 || aBBox->Height() <= 0)) {
michael@0 220 return NS_ERROR_FAILURE;
michael@0 221 }
michael@0 222
michael@0 223 // OK, now fix up the bounding box to reflect user coordinates
michael@0 224 // We handle device unit scaling in pattern matrix
michael@0 225 float scale = MaxExpansion(aContextMatrix);
michael@0 226 if (scale <= 0) {
michael@0 227 return NS_ERROR_FAILURE;
michael@0 228 }
michael@0 229 aBBox->Scale(scale);
michael@0 230 return NS_OK;
michael@0 231 }
michael@0 232
michael@0 233 nsresult
michael@0 234 nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
michael@0 235 gfxMatrix* patternMatrix,
michael@0 236 const gfxMatrix &aContextMatrix,
michael@0 237 nsIFrame *aSource,
michael@0 238 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
michael@0 239 float aGraphicOpacity,
michael@0 240 const gfxRect *aOverrideBounds)
michael@0 241 {
michael@0 242 /*
michael@0 243 * General approach:
michael@0 244 * Set the content geometry stuff
michael@0 245 * Calculate our bbox (using x,y,width,height & patternUnits &
michael@0 246 * patternTransform)
michael@0 247 * Create the surface
michael@0 248 * Calculate the content transformation matrix
michael@0 249 * Get our children (we may need to get them from another Pattern)
michael@0 250 * Call SVGPaint on all of our children
michael@0 251 * Return
michael@0 252 */
michael@0 253 *surface = nullptr;
michael@0 254
michael@0 255 // Get the first child of the pattern data we will render
michael@0 256 nsIFrame* firstKid = GetPatternFirstChild();
michael@0 257 if (!firstKid)
michael@0 258 return NS_ERROR_FAILURE; // Either no kids or a bad reference
michael@0 259
michael@0 260 const nsSVGViewBox& viewBox = GetViewBox();
michael@0 261
michael@0 262 uint16_t patternContentUnits =
michael@0 263 GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
michael@0 264 uint16_t patternUnits =
michael@0 265 GetEnumValue(SVGPatternElement::PATTERNUNITS);
michael@0 266
michael@0 267 /*
michael@0 268 * Get the content geometry information. This is a little tricky --
michael@0 269 * our parent is probably a <defs>, but we are rendering in the context
michael@0 270 * of some geometry source. Our content geometry information needs to
michael@0 271 * come from our rendering parent as opposed to our content parent. We
michael@0 272 * get that information from aSource, which is passed to us from the
michael@0 273 * backend renderer.
michael@0 274 *
michael@0 275 * There are three "geometries" that we need:
michael@0 276 * 1) The bounding box for the pattern. We use this to get the
michael@0 277 * width and height for the surface, and as the return to
michael@0 278 * GetBBox.
michael@0 279 * 2) The transformation matrix for the pattern. This is not *quite*
michael@0 280 * the same as the canvas transformation matrix that we will
michael@0 281 * provide to our rendering children since we "fudge" it a little
michael@0 282 * to get the renderer to handle the translations correctly for us.
michael@0 283 * 3) The CTM that we return to our children who make up the pattern.
michael@0 284 */
michael@0 285
michael@0 286 // Get all of the information we need from our "caller" -- i.e.
michael@0 287 // the geometry that is being rendered with a pattern
michael@0 288 gfxRect callerBBox;
michael@0 289 if (NS_FAILED(GetTargetGeometry(&callerBBox,
michael@0 290 viewBox,
michael@0 291 patternContentUnits, patternUnits,
michael@0 292 aSource,
michael@0 293 ToMatrix(aContextMatrix),
michael@0 294 aOverrideBounds)))
michael@0 295 return NS_ERROR_FAILURE;
michael@0 296
michael@0 297 // Construct the CTM that we will provide to our children when we
michael@0 298 // render them into the tile.
michael@0 299 gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits,
michael@0 300 callerBBox, ToMatrix(aContextMatrix), aSource);
michael@0 301 if (ctm.IsSingular()) {
michael@0 302 return NS_ERROR_FAILURE;
michael@0 303 }
michael@0 304
michael@0 305 // Get the pattern we are going to render
michael@0 306 nsSVGPatternFrame *patternFrame =
michael@0 307 static_cast<nsSVGPatternFrame*>(firstKid->GetParent());
michael@0 308 if (patternFrame->mCTM) {
michael@0 309 *patternFrame->mCTM = ctm;
michael@0 310 } else {
michael@0 311 patternFrame->mCTM = new gfxMatrix(ctm);
michael@0 312 }
michael@0 313
michael@0 314 // Get the bounding box of the pattern. This will be used to determine
michael@0 315 // the size of the surface, and will also be used to define the bounding
michael@0 316 // box for the pattern tile.
michael@0 317 gfxRect bbox = GetPatternRect(patternUnits, callerBBox, ToMatrix(aContextMatrix), aSource);
michael@0 318 if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) {
michael@0 319 return NS_ERROR_FAILURE;
michael@0 320 }
michael@0 321
michael@0 322 // Get the pattern transform
michael@0 323 gfxMatrix patternTransform = GetPatternTransform();
michael@0 324
michael@0 325 // revert the vector effect transform so that the pattern appears unchanged
michael@0 326 if (aFillOrStroke == &nsStyleSVG::mStroke) {
michael@0 327 patternTransform.Multiply(nsSVGUtils::GetStrokeTransform(aSource).Invert());
michael@0 328 }
michael@0 329
michael@0 330 // Get the transformation matrix that we will hand to the renderer's pattern
michael@0 331 // routine.
michael@0 332 *patternMatrix = GetPatternMatrix(patternUnits, patternTransform,
michael@0 333 bbox, callerBBox, ToMatrix(aContextMatrix));
michael@0 334 if (patternMatrix->IsSingular()) {
michael@0 335 return NS_ERROR_FAILURE;
michael@0 336 }
michael@0 337
michael@0 338 // Now that we have all of the necessary geometries, we can
michael@0 339 // create our surface.
michael@0 340 gfxRect transformedBBox = patternTransform.TransformBounds(bbox);
michael@0 341
michael@0 342 bool resultOverflows;
michael@0 343 IntSize surfaceSize =
michael@0 344 nsSVGUtils::ConvertToSurfaceSize(
michael@0 345 transformedBBox.Size(), &resultOverflows).ToIntSize();
michael@0 346
michael@0 347 // 0 disables rendering, < 0 is an error
michael@0 348 if (surfaceSize.width <= 0 || surfaceSize.height <= 0)
michael@0 349 return NS_ERROR_FAILURE;
michael@0 350
michael@0 351 gfxFloat patternWidth = bbox.Width();
michael@0 352 gfxFloat patternHeight = bbox.Height();
michael@0 353
michael@0 354 if (resultOverflows ||
michael@0 355 patternWidth != surfaceSize.width ||
michael@0 356 patternHeight != surfaceSize.height) {
michael@0 357 // scale drawing to pattern surface size
michael@0 358 gfxMatrix tempTM =
michael@0 359 gfxMatrix(surfaceSize.width / patternWidth, 0.0f,
michael@0 360 0.0f, surfaceSize.height / patternHeight,
michael@0 361 0.0f, 0.0f);
michael@0 362 patternFrame->mCTM->PreMultiply(tempTM);
michael@0 363
michael@0 364 // and rescale pattern to compensate
michael@0 365 patternMatrix->Scale(patternWidth / surfaceSize.width,
michael@0 366 patternHeight / surfaceSize.height);
michael@0 367 }
michael@0 368
michael@0 369 nsRefPtr<gfxASurface> tmpSurface =
michael@0 370 gfxPlatform::GetPlatform()->CreateOffscreenSurface(surfaceSize,
michael@0 371 gfxContentType::COLOR_ALPHA);
michael@0 372 if (!tmpSurface || tmpSurface->CairoStatus())
michael@0 373 return NS_ERROR_FAILURE;
michael@0 374
michael@0 375 nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
michael@0 376 context->Init(aSource->PresContext()->DeviceContext(), tmpSurface);
michael@0 377 gfxContext* gfx = context->ThebesContext();
michael@0 378
michael@0 379 // Fill with transparent black
michael@0 380 gfx->SetOperator(gfxContext::OPERATOR_CLEAR);
michael@0 381 gfx->Paint();
michael@0 382 gfx->SetOperator(gfxContext::OPERATOR_OVER);
michael@0 383
michael@0 384 if (aGraphicOpacity != 1.0f) {
michael@0 385 gfx->Save();
michael@0 386 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
michael@0 387 }
michael@0 388
michael@0 389 // OK, now render -- note that we use "firstKid", which
michael@0 390 // we got at the beginning because it takes care of the
michael@0 391 // referenced pattern situation for us
michael@0 392
michael@0 393 if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) {
michael@0 394 // Set the geometrical parent of the pattern we are rendering
michael@0 395 patternFrame->mSource = static_cast<nsSVGPathGeometryFrame*>(aSource);
michael@0 396 }
michael@0 397
michael@0 398 // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
michael@0 399 // give back a clear surface if there's a loop
michael@0 400 if (!(patternFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)) {
michael@0 401 patternFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
michael@0 402 for (nsIFrame* kid = firstKid; kid;
michael@0 403 kid = kid->GetNextSibling()) {
michael@0 404 // The CTM of each frame referencing us can be different
michael@0 405 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
michael@0 406 if (SVGFrame) {
michael@0 407 SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
michael@0 408 }
michael@0 409 nsSVGUtils::PaintFrameWithEffects(context, nullptr, kid);
michael@0 410 }
michael@0 411 patternFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
michael@0 412 }
michael@0 413
michael@0 414 patternFrame->mSource = nullptr;
michael@0 415
michael@0 416 if (aGraphicOpacity != 1.0f) {
michael@0 417 gfx->PopGroupToSource();
michael@0 418 gfx->Paint(aGraphicOpacity);
michael@0 419 gfx->Restore();
michael@0 420 }
michael@0 421
michael@0 422 // caller now owns the surface
michael@0 423 tmpSurface.forget(surface);
michael@0 424 return NS_OK;
michael@0 425 }
michael@0 426
michael@0 427 /* Will probably need something like this... */
michael@0 428 // How do we handle the insertion of a new frame?
michael@0 429 // We really don't want to rerender this every time,
michael@0 430 // do we?
michael@0 431 nsIFrame*
michael@0 432 nsSVGPatternFrame::GetPatternFirstChild()
michael@0 433 {
michael@0 434 // Do we have any children ourselves?
michael@0 435 nsIFrame* kid = mFrames.FirstChild();
michael@0 436 if (kid)
michael@0 437 return kid;
michael@0 438
michael@0 439 // No, see if we chain to someone who does
michael@0 440 AutoPatternReferencer patternRef(this);
michael@0 441
michael@0 442 nsSVGPatternFrame* next = GetReferencedPatternIfNotInUse();
michael@0 443 if (!next)
michael@0 444 return nullptr;
michael@0 445
michael@0 446 return next->GetPatternFirstChild();
michael@0 447 }
michael@0 448
michael@0 449 uint16_t
michael@0 450 nsSVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault)
michael@0 451 {
michael@0 452 nsSVGEnum& thisEnum =
michael@0 453 static_cast<SVGPatternElement *>(mContent)->mEnumAttributes[aIndex];
michael@0 454
michael@0 455 if (thisEnum.IsExplicitlySet())
michael@0 456 return thisEnum.GetAnimValue();
michael@0 457
michael@0 458 AutoPatternReferencer patternRef(this);
michael@0 459
michael@0 460 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
michael@0 461 return next ? next->GetEnumValue(aIndex, aDefault) :
michael@0 462 static_cast<SVGPatternElement *>(aDefault)->
michael@0 463 mEnumAttributes[aIndex].GetAnimValue();
michael@0 464 }
michael@0 465
michael@0 466 nsSVGAnimatedTransformList*
michael@0 467 nsSVGPatternFrame::GetPatternTransformList(nsIContent* aDefault)
michael@0 468 {
michael@0 469 nsSVGAnimatedTransformList *thisTransformList =
michael@0 470 static_cast<SVGPatternElement *>(mContent)->GetAnimatedTransformList();
michael@0 471
michael@0 472 if (thisTransformList && thisTransformList->IsExplicitlySet())
michael@0 473 return thisTransformList;
michael@0 474
michael@0 475 AutoPatternReferencer patternRef(this);
michael@0 476
michael@0 477 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
michael@0 478 return next ? next->GetPatternTransformList(aDefault) :
michael@0 479 static_cast<SVGPatternElement *>(aDefault)->mPatternTransform.get();
michael@0 480 }
michael@0 481
michael@0 482 gfxMatrix
michael@0 483 nsSVGPatternFrame::GetPatternTransform()
michael@0 484 {
michael@0 485 nsSVGAnimatedTransformList* animTransformList =
michael@0 486 GetPatternTransformList(mContent);
michael@0 487 if (!animTransformList)
michael@0 488 return gfxMatrix();
michael@0 489
michael@0 490 return animTransformList->GetAnimValue().GetConsolidationMatrix();
michael@0 491 }
michael@0 492
michael@0 493 const nsSVGViewBox &
michael@0 494 nsSVGPatternFrame::GetViewBox(nsIContent* aDefault)
michael@0 495 {
michael@0 496 const nsSVGViewBox &thisViewBox =
michael@0 497 static_cast<SVGPatternElement *>(mContent)->mViewBox;
michael@0 498
michael@0 499 if (thisViewBox.IsExplicitlySet())
michael@0 500 return thisViewBox;
michael@0 501
michael@0 502 AutoPatternReferencer patternRef(this);
michael@0 503
michael@0 504 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
michael@0 505 return next ? next->GetViewBox(aDefault) :
michael@0 506 static_cast<SVGPatternElement *>(aDefault)->mViewBox;
michael@0 507 }
michael@0 508
michael@0 509 const SVGAnimatedPreserveAspectRatio &
michael@0 510 nsSVGPatternFrame::GetPreserveAspectRatio(nsIContent *aDefault)
michael@0 511 {
michael@0 512 const SVGAnimatedPreserveAspectRatio &thisPar =
michael@0 513 static_cast<SVGPatternElement *>(mContent)->mPreserveAspectRatio;
michael@0 514
michael@0 515 if (thisPar.IsExplicitlySet())
michael@0 516 return thisPar;
michael@0 517
michael@0 518 AutoPatternReferencer patternRef(this);
michael@0 519
michael@0 520 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
michael@0 521 return next ? next->GetPreserveAspectRatio(aDefault) :
michael@0 522 static_cast<SVGPatternElement *>(aDefault)->mPreserveAspectRatio;
michael@0 523 }
michael@0 524
michael@0 525 const nsSVGLength2 *
michael@0 526 nsSVGPatternFrame::GetLengthValue(uint32_t aIndex, nsIContent *aDefault)
michael@0 527 {
michael@0 528 const nsSVGLength2 *thisLength =
michael@0 529 &static_cast<SVGPatternElement *>(mContent)->mLengthAttributes[aIndex];
michael@0 530
michael@0 531 if (thisLength->IsExplicitlySet())
michael@0 532 return thisLength;
michael@0 533
michael@0 534 AutoPatternReferencer patternRef(this);
michael@0 535
michael@0 536 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
michael@0 537 return next ? next->GetLengthValue(aIndex, aDefault) :
michael@0 538 &static_cast<SVGPatternElement *>(aDefault)->mLengthAttributes[aIndex];
michael@0 539 }
michael@0 540
michael@0 541 // Private (helper) methods
michael@0 542 nsSVGPatternFrame *
michael@0 543 nsSVGPatternFrame::GetReferencedPattern()
michael@0 544 {
michael@0 545 if (mNoHRefURI)
michael@0 546 return nullptr;
michael@0 547
michael@0 548 nsSVGPaintingProperty *property = static_cast<nsSVGPaintingProperty*>
michael@0 549 (Properties().Get(nsSVGEffects::HrefProperty()));
michael@0 550
michael@0 551 if (!property) {
michael@0 552 // Fetch our pattern element's xlink:href attribute
michael@0 553 SVGPatternElement *pattern = static_cast<SVGPatternElement *>(mContent);
michael@0 554 nsAutoString href;
michael@0 555 pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(href, pattern);
michael@0 556 if (href.IsEmpty()) {
michael@0 557 mNoHRefURI = true;
michael@0 558 return nullptr; // no URL
michael@0 559 }
michael@0 560
michael@0 561 // Convert href to an nsIURI
michael@0 562 nsCOMPtr<nsIURI> targetURI;
michael@0 563 nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
michael@0 564 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
michael@0 565 mContent->GetCurrentDoc(), base);
michael@0 566
michael@0 567 property =
michael@0 568 nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty());
michael@0 569 if (!property)
michael@0 570 return nullptr;
michael@0 571 }
michael@0 572
michael@0 573 nsIFrame *result = property->GetReferencedFrame();
michael@0 574 if (!result)
michael@0 575 return nullptr;
michael@0 576
michael@0 577 nsIAtom* frameType = result->GetType();
michael@0 578 if (frameType != nsGkAtoms::svgPatternFrame)
michael@0 579 return nullptr;
michael@0 580
michael@0 581 return static_cast<nsSVGPatternFrame*>(result);
michael@0 582 }
michael@0 583
michael@0 584 nsSVGPatternFrame *
michael@0 585 nsSVGPatternFrame::GetReferencedPatternIfNotInUse()
michael@0 586 {
michael@0 587 nsSVGPatternFrame *referenced = GetReferencedPattern();
michael@0 588 if (!referenced)
michael@0 589 return nullptr;
michael@0 590
michael@0 591 if (referenced->mLoopFlag) {
michael@0 592 // XXXjwatt: we should really send an error to the JavaScript Console here:
michael@0 593 NS_WARNING("pattern reference loop detected while inheriting attribute!");
michael@0 594 return nullptr;
michael@0 595 }
michael@0 596
michael@0 597 return referenced;
michael@0 598 }
michael@0 599
michael@0 600 gfxRect
michael@0 601 nsSVGPatternFrame::GetPatternRect(uint16_t aPatternUnits,
michael@0 602 const gfxRect &aTargetBBox,
michael@0 603 const Matrix &aTargetCTM,
michael@0 604 nsIFrame *aTarget)
michael@0 605 {
michael@0 606 // We need to initialize our box
michael@0 607 float x,y,width,height;
michael@0 608
michael@0 609 // Get the pattern x,y,width, and height
michael@0 610 const nsSVGLength2 *tmpX, *tmpY, *tmpHeight, *tmpWidth;
michael@0 611 tmpX = GetLengthValue(SVGPatternElement::ATTR_X);
michael@0 612 tmpY = GetLengthValue(SVGPatternElement::ATTR_Y);
michael@0 613 tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT);
michael@0 614 tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH);
michael@0 615
michael@0 616 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
michael@0 617 x = nsSVGUtils::ObjectSpace(aTargetBBox, tmpX);
michael@0 618 y = nsSVGUtils::ObjectSpace(aTargetBBox, tmpY);
michael@0 619 width = nsSVGUtils::ObjectSpace(aTargetBBox, tmpWidth);
michael@0 620 height = nsSVGUtils::ObjectSpace(aTargetBBox, tmpHeight);
michael@0 621 } else {
michael@0 622 float scale = MaxExpansion(aTargetCTM);
michael@0 623 x = nsSVGUtils::UserSpace(aTarget, tmpX) * scale;
michael@0 624 y = nsSVGUtils::UserSpace(aTarget, tmpY) * scale;
michael@0 625 width = nsSVGUtils::UserSpace(aTarget, tmpWidth) * scale;
michael@0 626 height = nsSVGUtils::UserSpace(aTarget, tmpHeight) * scale;
michael@0 627 }
michael@0 628
michael@0 629 return gfxRect(x, y, width, height);
michael@0 630 }
michael@0 631
michael@0 632 gfxMatrix
michael@0 633 nsSVGPatternFrame::ConstructCTM(const nsSVGViewBox& aViewBox,
michael@0 634 uint16_t aPatternContentUnits,
michael@0 635 uint16_t aPatternUnits,
michael@0 636 const gfxRect &callerBBox,
michael@0 637 const Matrix &callerCTM,
michael@0 638 nsIFrame *aTarget)
michael@0 639 {
michael@0 640 gfxMatrix tCTM;
michael@0 641 SVGSVGElement *ctx = nullptr;
michael@0 642 nsIContent* targetContent = aTarget->GetContent();
michael@0 643
michael@0 644 // The objectBoundingBox conversion must be handled in the CTM:
michael@0 645 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) {
michael@0 646 tCTM.Scale(callerBBox.Width(), callerBBox.Height());
michael@0 647 } else {
michael@0 648 if (targetContent->IsSVG()) {
michael@0 649 ctx = static_cast<nsSVGElement*>(targetContent)->GetCtx();
michael@0 650 }
michael@0 651 float scale = MaxExpansion(callerCTM);
michael@0 652 tCTM.Scale(scale, scale);
michael@0 653 }
michael@0 654
michael@0 655 if (!aViewBox.IsExplicitlySet()) {
michael@0 656 return tCTM;
michael@0 657 }
michael@0 658 const nsSVGViewBoxRect viewBoxRect = aViewBox.GetAnimValue();
michael@0 659
michael@0 660 if (viewBoxRect.height <= 0.0f || viewBoxRect.width <= 0.0f) {
michael@0 661 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
michael@0 662 }
michael@0 663
michael@0 664 float viewportWidth, viewportHeight;
michael@0 665 if (targetContent->IsSVG()) {
michael@0 666 // If we're dealing with an SVG target only retrieve the context once.
michael@0 667 // Calling the nsIFrame* variant of GetAnimValue would look it up on
michael@0 668 // every call.
michael@0 669 viewportWidth =
michael@0 670 GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx);
michael@0 671 viewportHeight =
michael@0 672 GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx);
michael@0 673 } else {
michael@0 674 // No SVG target, call the nsIFrame* variant of GetAnimValue.
michael@0 675 viewportWidth =
michael@0 676 GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget);
michael@0 677 viewportHeight =
michael@0 678 GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget);
michael@0 679 }
michael@0 680
michael@0 681 if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
michael@0 682 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
michael@0 683 }
michael@0 684
michael@0 685 Matrix tm = SVGContentUtils::GetViewBoxTransform(
michael@0 686 viewportWidth, viewportHeight,
michael@0 687 viewBoxRect.x, viewBoxRect.y,
michael@0 688 viewBoxRect.width, viewBoxRect.height,
michael@0 689 GetPreserveAspectRatio());
michael@0 690
michael@0 691 return ThebesMatrix(tm) * tCTM;
michael@0 692 }
michael@0 693
michael@0 694 //----------------------------------------------------------------------
michael@0 695 // nsSVGPaintServerFrame methods:
michael@0 696
michael@0 697 already_AddRefed<gfxPattern>
michael@0 698 nsSVGPatternFrame::GetPaintServerPattern(nsIFrame *aSource,
michael@0 699 const gfxMatrix& aContextMatrix,
michael@0 700 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
michael@0 701 float aGraphicOpacity,
michael@0 702 const gfxRect *aOverrideBounds)
michael@0 703 {
michael@0 704 if (aGraphicOpacity == 0.0f) {
michael@0 705 nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0));
michael@0 706 return pattern.forget();
michael@0 707 }
michael@0 708
michael@0 709 // Paint it!
michael@0 710 nsRefPtr<gfxASurface> surface;
michael@0 711 gfxMatrix pMatrix;
michael@0 712 nsresult rv = PaintPattern(getter_AddRefs(surface), &pMatrix, aContextMatrix,
michael@0 713 aSource, aFillOrStroke, aGraphicOpacity, aOverrideBounds);
michael@0 714
michael@0 715 if (NS_FAILED(rv)) {
michael@0 716 return nullptr;
michael@0 717 }
michael@0 718
michael@0 719 pMatrix.Invert();
michael@0 720
michael@0 721 nsRefPtr<gfxPattern> pattern = new gfxPattern(surface);
michael@0 722
michael@0 723 if (!pattern || pattern->CairoStatus())
michael@0 724 return nullptr;
michael@0 725
michael@0 726 pattern->SetMatrix(pMatrix);
michael@0 727 pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
michael@0 728 return pattern.forget();
michael@0 729 }
michael@0 730
michael@0 731 // -------------------------------------------------------------------------
michael@0 732 // Public functions
michael@0 733 // -------------------------------------------------------------------------
michael@0 734
michael@0 735 nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
michael@0 736 nsStyleContext* aContext)
michael@0 737 {
michael@0 738 return new (aPresShell) nsSVGPatternFrame(aContext);
michael@0 739 }
michael@0 740

mercurial