1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
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/. */
7 /* utility functions for drawing borders and backgrounds */
11 #include "gfx2DGlue.h"
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/DebugOnly.h"
14 #include "mozilla/gfx/2D.h"
15 #include "mozilla/gfx/Helpers.h"
16 #include "mozilla/gfx/PathHelpers.h"
17 #include "mozilla/HashFunctions.h"
18 #include "mozilla/MathAlgorithms.h"
20 #include "nsStyleConsts.h"
21 #include "nsPresContext.h"
25 #include "nsIPresShell.h"
26 #include "nsFrameManager.h"
27 #include "nsStyleContext.h"
28 #include "nsGkAtoms.h"
29 #include "nsCSSAnonBoxes.h"
30 #include "nsIContent.h"
31 #include "nsIDocumentInlines.h"
32 #include "nsIScrollableFrame.h"
33 #include "imgIRequest.h"
34 #include "imgIContainer.h"
36 #include "nsCSSRendering.h"
37 #include "nsCSSColorUtils.h"
39 #include "nsThemeConstants.h"
40 #include "nsLayoutUtils.h"
41 #include "nsBlockFrame.h"
42 #include "gfxContext.h"
43 #include "nsRenderingContext.h"
44 #include "nsStyleStructInlines.h"
45 #include "nsCSSFrameConstructor.h"
46 #include "nsCSSProps.h"
47 #include "nsContentUtils.h"
48 #include "nsSVGEffects.h"
49 #include "nsSVGIntegrationUtils.h"
50 #include "gfxDrawable.h"
51 #include "GeckoProfiler.h"
52 #include "nsCSSRenderingBorders.h"
53 #include "mozilla/css/ImageLoader.h"
54 #include "ImageContainer.h"
55 #include "mozilla/Telemetry.h"
58 #include "gfxGradientCache.h"
59 #include "GraphicsFilter.h"
62 using namespace mozilla
;
63 using namespace mozilla::css
;
64 using namespace mozilla::gfx
;
65 using namespace mozilla::image
;
66 using mozilla::CSSSizeOrRatio
;
68 static int gFrameTreeLockCount
= 0;
70 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
71 // recalculating this for each frame in a continuation (perf), hold
72 // a cache of various coordinate information that we need in order
73 // to paint inline backgrounds.
74 struct InlineBackgroundData
76 InlineBackgroundData()
77 : mFrame(nullptr), mBlockFrame(nullptr)
81 ~InlineBackgroundData()
87 mBoundingBox
.SetRect(0,0,0,0);
88 mContinuationPoint
= mLineContinuationPoint
= mUnbrokenMeasure
= 0;
89 mFrame
= mBlockFrame
= nullptr;
90 mPIStartBorderData
.Reset();
94 * Return a continuous rect for (an inline) aFrame relative to the
95 * continuation that draws the left-most part of the background.
96 * This is used when painting backgrounds.
98 nsRect
GetContinuousRect(nsIFrame
* aFrame
)
100 MOZ_ASSERT(aFrame
->GetType() == nsGkAtoms::inlineFrame
);
104 nscoord pos
; // an x coordinate if writing-mode is horizontal;
105 // y coordinate if vertical
107 pos
= mLineContinuationPoint
;
109 // Scan continuations on the same line as aFrame and accumulate the widths
110 // of frames that are to the left (if this is an LTR block) or right
111 // (if it's RTL) of the current one.
112 bool isRtlBlock
= (mBlockFrame
->StyleVisibility()->mDirection
==
113 NS_STYLE_DIRECTION_RTL
);
114 nscoord curOffset
= mVertical
? aFrame
->GetOffsetTo(mBlockFrame
).y
115 : aFrame
->GetOffsetTo(mBlockFrame
).x
;
117 // If the continuation is fluid we know inlineFrame is not on the same line.
118 // If it's not fluid, we need to test further to be sure.
119 nsIFrame
* inlineFrame
= aFrame
->GetPrevContinuation();
120 while (inlineFrame
&& !inlineFrame
->GetNextInFlow() &&
121 AreOnSameLine(aFrame
, inlineFrame
)) {
122 nscoord frameOffset
= mVertical
123 ? inlineFrame
->GetOffsetTo(mBlockFrame
).y
124 : inlineFrame
->GetOffsetTo(mBlockFrame
).x
;
125 if (isRtlBlock
== (frameOffset
>= curOffset
)) {
127 ? inlineFrame
->GetSize().height
128 : inlineFrame
->GetSize().width
;
130 inlineFrame
= inlineFrame
->GetPrevContinuation();
133 inlineFrame
= aFrame
->GetNextContinuation();
134 while (inlineFrame
&& !inlineFrame
->GetPrevInFlow() &&
135 AreOnSameLine(aFrame
, inlineFrame
)) {
136 nscoord frameOffset
= mVertical
137 ? inlineFrame
->GetOffsetTo(mBlockFrame
).y
138 : inlineFrame
->GetOffsetTo(mBlockFrame
).x
;
139 if (isRtlBlock
== (frameOffset
>= curOffset
)) {
141 ? inlineFrame
->GetSize().height
142 : inlineFrame
->GetSize().width
;
144 inlineFrame
= inlineFrame
->GetNextContinuation();
147 // aFrame itself is also to the right of its left edge, so add its width.
148 pos
+= mVertical
? aFrame
->GetSize().height
: aFrame
->GetSize().width
;
149 // pos is now the distance from the left [top] edge of aFrame to the right [bottom] edge
150 // of the unbroken content. Change it to indicate the distance from the
151 // left [top] edge of the unbroken content to the left [top] edge of aFrame.
152 pos
= mUnbrokenMeasure
- pos
;
155 pos
= mContinuationPoint
;
158 // Assume background-origin: border and return a rect with offsets
159 // relative to (0,0). If we have a different background-origin,
160 // then our rect should be deflated appropriately by our caller.
162 ? nsRect(0, -pos
, mFrame
->GetSize().width
, mUnbrokenMeasure
)
163 : nsRect(-pos
, 0, mUnbrokenMeasure
, mFrame
->GetSize().height
);
167 * Return a continuous rect for (an inline) aFrame relative to the
168 * continuation that should draw the left[top]-border. This is used when painting
169 * borders and clipping backgrounds. This may NOT be the same continuous rect
170 * as for drawing backgrounds; the continuation with the left[top]-border might be
171 * somewhere in the middle of that rect (e.g. BIDI), in those cases we need
172 * the reverse background order starting at the left[top]-border continuation.
174 nsRect
GetBorderContinuousRect(nsIFrame
* aFrame
, nsRect aBorderArea
)
176 // Calling GetContinuousRect(aFrame) here may lead to Reset/Init which
177 // resets our mPIStartBorderData so we save it ...
178 PhysicalInlineStartBorderData
saved(mPIStartBorderData
);
179 nsRect joinedBorderArea
= GetContinuousRect(aFrame
);
180 if (!saved
.mIsValid
|| saved
.mFrame
!= mPIStartBorderData
.mFrame
) {
181 if (aFrame
== mPIStartBorderData
.mFrame
) {
183 mPIStartBorderData
.SetCoord(joinedBorderArea
.y
);
185 mPIStartBorderData
.SetCoord(joinedBorderArea
.x
);
187 } else if (mPIStartBorderData
.mFrame
) {
189 mPIStartBorderData
.SetCoord(GetContinuousRect(mPIStartBorderData
.mFrame
).y
);
191 mPIStartBorderData
.SetCoord(GetContinuousRect(mPIStartBorderData
.mFrame
).x
);
195 // ... and restore it when possible.
196 mPIStartBorderData
.mCoord
= saved
.mCoord
;
199 if (joinedBorderArea
.y
> mPIStartBorderData
.mCoord
) {
201 -(mUnbrokenMeasure
+ joinedBorderArea
.y
- aBorderArea
.height
);
203 joinedBorderArea
.y
-= mPIStartBorderData
.mCoord
;
206 if (joinedBorderArea
.x
> mPIStartBorderData
.mCoord
) {
208 -(mUnbrokenMeasure
+ joinedBorderArea
.x
- aBorderArea
.width
);
210 joinedBorderArea
.x
-= mPIStartBorderData
.mCoord
;
213 return joinedBorderArea
;
216 nsRect
GetBoundingRect(nsIFrame
* aFrame
)
220 // Move the offsets relative to (0,0) which puts the bounding box into
221 // our coordinate system rather than our parent's. We do this by
222 // moving it the back distance from us to the bounding box.
223 // This also assumes background-origin: border, so our caller will
224 // need to deflate us if needed.
225 nsRect
boundingBox(mBoundingBox
);
226 nsPoint point
= mFrame
->GetPosition();
227 boundingBox
.MoveBy(-point
.x
, -point
.y
);
233 // This is a coordinate on the inline axis, but is not a true logical inline-
234 // coord because it is always measured from left to right (if horizontal) or
235 // from top to bottom (if vertical), ignoring any bidi RTL directionality.
236 // We'll call this "physical inline start", or PIStart for short.
237 struct PhysicalInlineStartBorderData
{
238 nsIFrame
* mFrame
; // the continuation that may have a left-border
239 nscoord mCoord
; // cached GetContinuousRect(mFrame).x or .y
240 bool mIsValid
; // true if mCoord is valid
241 void Reset() { mFrame
= nullptr; mIsValid
= false; }
242 void SetCoord(nscoord aCoord
) { mCoord
= aCoord
; mIsValid
= true; }
246 nsBlockFrame
* mBlockFrame
;
248 nscoord mContinuationPoint
;
249 nscoord mUnbrokenMeasure
;
250 nscoord mLineContinuationPoint
;
251 PhysicalInlineStartBorderData mPIStartBorderData
;
255 void SetFrame(nsIFrame
* aFrame
)
257 NS_PRECONDITION(aFrame
, "Need a frame");
258 NS_ASSERTION(gFrameTreeLockCount
> 0,
259 "Can't call this when frame tree is not locked");
261 if (aFrame
== mFrame
) {
265 nsIFrame
*prevContinuation
= GetPrevContinuation(aFrame
);
267 if (!prevContinuation
|| mFrame
!= prevContinuation
) {
268 // Ok, we've got the wrong frame. We have to start from scratch.
274 // Get our last frame's size and add its width to our continuation
275 // point before we cache the new frame.
276 mContinuationPoint
+= mVertical
? mFrame
->GetSize().height
277 : mFrame
->GetSize().width
;
279 // If this a new line, update mLineContinuationPoint.
281 (aFrame
->GetPrevInFlow() || !AreOnSameLine(mFrame
, aFrame
))) {
282 mLineContinuationPoint
= mContinuationPoint
;
288 nsIFrame
* GetPrevContinuation(nsIFrame
* aFrame
)
290 nsIFrame
* prevCont
= aFrame
->GetPrevContinuation();
292 (aFrame
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
)) {
293 nsIFrame
* block
= static_cast<nsIFrame
*>
294 (aFrame
->Properties().Get(nsIFrame::IBSplitPrevSibling()));
296 // The {ib} properties are only stored on first continuations
297 NS_ASSERTION(!block
->GetPrevContinuation(),
298 "Incorrect value for IBSplitPrevSibling");
299 prevCont
= static_cast<nsIFrame
*>
300 (block
->Properties().Get(nsIFrame::IBSplitPrevSibling()));
301 NS_ASSERTION(prevCont
, "How did that happen?");
307 nsIFrame
* GetNextContinuation(nsIFrame
* aFrame
)
309 nsIFrame
* nextCont
= aFrame
->GetNextContinuation();
311 (aFrame
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
)) {
312 // The {ib} properties are only stored on first continuations
313 aFrame
= aFrame
->FirstContinuation();
314 nsIFrame
* block
= static_cast<nsIFrame
*>
315 (aFrame
->Properties().Get(nsIFrame::IBSplitSibling()));
317 nextCont
= static_cast<nsIFrame
*>
318 (block
->Properties().Get(nsIFrame::IBSplitSibling()));
319 NS_ASSERTION(nextCont
, "How did that happen?");
325 void Init(nsIFrame
* aFrame
)
327 mPIStartBorderData
.Reset();
328 mBidiEnabled
= aFrame
->PresContext()->BidiEnabled();
330 // Find the containing block frame
331 nsIFrame
* frame
= aFrame
;
333 frame
= frame
->GetParent();
334 mBlockFrame
= do_QueryFrame(frame
);
336 while (frame
&& frame
->IsFrameOfType(nsIFrame::eLineParticipant
));
338 NS_ASSERTION(mBlockFrame
, "Cannot find containing block.");
341 mVertical
= aFrame
->GetWritingMode().IsVertical();
343 // Start with the previous flow frame as our continuation point
344 // is the total of the widths of the previous frames.
345 nsIFrame
* inlineFrame
= GetPrevContinuation(aFrame
);
346 while (inlineFrame
) {
347 if (!mPIStartBorderData
.mFrame
&&
348 !(mVertical
? inlineFrame
->GetSkipSides().Top()
349 : inlineFrame
->GetSkipSides().Left())) {
350 mPIStartBorderData
.mFrame
= inlineFrame
;
352 nsRect rect
= inlineFrame
->GetRect();
353 mContinuationPoint
+= mVertical
? rect
.height
: rect
.width
;
354 if (mBidiEnabled
&& !AreOnSameLine(aFrame
, inlineFrame
)) {
355 mLineContinuationPoint
+= mVertical
? rect
.height
: rect
.width
;
357 mUnbrokenMeasure
+= mVertical
? rect
.height
: rect
.width
;
358 mBoundingBox
.UnionRect(mBoundingBox
, rect
);
359 inlineFrame
= GetPrevContinuation(inlineFrame
);
362 // Next add this frame and subsequent frames to the bounding box and
364 inlineFrame
= aFrame
;
365 while (inlineFrame
) {
366 if (!mPIStartBorderData
.mFrame
&&
367 !(mVertical
? inlineFrame
->GetSkipSides().Top()
368 : inlineFrame
->GetSkipSides().Left())) {
369 mPIStartBorderData
.mFrame
= inlineFrame
;
371 nsRect rect
= inlineFrame
->GetRect();
372 mUnbrokenMeasure
+= mVertical
? rect
.height
: rect
.width
;
373 mBoundingBox
.UnionRect(mBoundingBox
, rect
);
374 inlineFrame
= GetNextContinuation(inlineFrame
);
380 bool AreOnSameLine(nsIFrame
* aFrame1
, nsIFrame
* aFrame2
) {
381 bool isValid1
, isValid2
;
382 nsBlockInFlowLineIterator
it1(mBlockFrame
, aFrame1
, &isValid1
);
383 nsBlockInFlowLineIterator
it2(mBlockFrame
, aFrame2
, &isValid2
);
384 return isValid1
&& isValid2
&&
385 // Make sure aFrame1 and aFrame2 are in the same continuation of
387 it1
.GetContainer() == it2
.GetContainer() &&
388 // And on the same line in it
389 it1
.GetLine() == it2
.GetLine();
393 // A resolved color stop --- with a specific position along the gradient line,
394 // and a Thebes color
396 ColorStop(): mPosition(0), mIsMidpoint(false) {}
397 ColorStop(double aPosition
, bool aIsMidPoint
, gfxRGBA aColor
) :
398 mPosition(aPosition
), mIsMidpoint(aIsMidPoint
), mColor(aColor
) {}
399 double mPosition
; // along the gradient line; 0=start, 1=end
404 /* Local functions */
405 static void DrawBorderImage(nsPresContext
* aPresContext
,
406 nsRenderingContext
& aRenderingContext
,
408 const nsRect
& aBorderArea
,
409 const nsStyleBorder
& aStyleBorder
,
410 const nsRect
& aDirtyRect
,
413 static nscolor
MakeBevelColor(mozilla::css::Side whichSide
, uint8_t style
,
414 nscolor aBackgroundColor
,
415 nscolor aBorderColor
);
417 static InlineBackgroundData
* gInlineBGData
= nullptr;
419 // Initialize any static variables used by nsCSSRendering.
420 void nsCSSRendering::Init()
422 NS_ASSERTION(!gInlineBGData
, "Init called twice");
423 gInlineBGData
= new InlineBackgroundData();
426 // Clean up any global variables used by nsCSSRendering.
427 void nsCSSRendering::Shutdown()
429 delete gInlineBGData
;
430 gInlineBGData
= nullptr;
437 MakeBevelColor(mozilla::css::Side whichSide
, uint8_t style
,
438 nscolor aBackgroundColor
, nscolor aBorderColor
)
444 // Given a background color and a border color
445 // calculate the color used for the shading
446 NS_GetSpecial3DColors(colors
, aBackgroundColor
, aBorderColor
);
448 if ((style
== NS_STYLE_BORDER_STYLE_OUTSET
) ||
449 (style
== NS_STYLE_BORDER_STYLE_RIDGE
)) {
450 // Flip colors for these two border styles
452 case NS_SIDE_BOTTOM
: whichSide
= NS_SIDE_TOP
; break;
453 case NS_SIDE_RIGHT
: whichSide
= NS_SIDE_LEFT
; break;
454 case NS_SIDE_TOP
: whichSide
= NS_SIDE_BOTTOM
; break;
455 case NS_SIDE_LEFT
: whichSide
= NS_SIDE_RIGHT
; break;
461 theColor
= colors
[1];
464 theColor
= colors
[1];
467 theColor
= colors
[0];
471 theColor
= colors
[0];
478 GetRadii(nsIFrame
* aForFrame
, const nsStyleBorder
& aBorder
,
479 const nsRect
& aOrigBorderArea
, const nsRect
& aBorderArea
,
482 bool haveRoundedCorners
;
483 nsSize sz
= aBorderArea
.Size();
484 nsSize frameSize
= aForFrame
->GetSize();
485 if (&aBorder
== aForFrame
->StyleBorder() &&
486 frameSize
== aOrigBorderArea
.Size()) {
487 haveRoundedCorners
= aForFrame
->GetBorderRadii(sz
, sz
, Sides(), aRadii
);
490 nsIFrame::ComputeBorderRadii(aBorder
.mBorderRadius
, frameSize
, sz
, Sides(), aRadii
);
493 return haveRoundedCorners
;
497 GetRadii(nsIFrame
* aForFrame
, const nsStyleBorder
& aBorder
,
498 const nsRect
& aOrigBorderArea
, const nsRect
& aBorderArea
,
499 RectCornerRadii
* aBgRadii
)
502 bool haveRoundedCorners
= GetRadii(aForFrame
, aBorder
, aOrigBorderArea
, aBorderArea
, radii
);
504 if (haveRoundedCorners
) {
505 auto d2a
= aForFrame
->PresContext()->AppUnitsPerDevPixel();
506 nsCSSRendering::ComputePixelRadii(radii
, d2a
, aBgRadii
);
508 return haveRoundedCorners
;
512 JoinBoxesForVerticalSlice(nsIFrame
* aFrame
, const nsRect
& aBorderArea
)
514 // Inflate vertically as if our continuations were laid out vertically
515 // adjacent. Note that we don't touch the width.
516 nsRect borderArea
= aBorderArea
;
518 nsIFrame
* f
= aFrame
->GetNextContinuation();
519 for (; f
; f
= f
->GetNextContinuation()) {
520 MOZ_ASSERT(!(f
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
),
521 "anonymous ib-split block shouldn't have border/background");
522 h
+= f
->GetRect().height
;
524 borderArea
.height
+= h
;
526 f
= aFrame
->GetPrevContinuation();
527 for (; f
; f
= f
->GetPrevContinuation()) {
528 MOZ_ASSERT(!(f
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
),
529 "anonymous ib-split block shouldn't have border/background");
530 h
+= f
->GetRect().height
;
533 borderArea
.height
+= h
;
538 * Inflate aBorderArea which is relative to aFrame's origin to calculate
539 * a hypothetical non-split frame area for all the continuations.
540 * See "Joining Boxes for 'slice'" in
541 * http://dev.w3.org/csswg/css-break/#break-decoration
543 enum InlineBoxOrder
{ eForBorder
, eForBackground
};
545 JoinBoxesForSlice(nsIFrame
* aFrame
, const nsRect
& aBorderArea
,
546 InlineBoxOrder aOrder
)
548 if (aFrame
->GetType() == nsGkAtoms::inlineFrame
) {
549 return (aOrder
== eForBorder
550 ? gInlineBGData
->GetBorderContinuousRect(aFrame
, aBorderArea
)
551 : gInlineBGData
->GetContinuousRect(aFrame
)) +
552 aBorderArea
.TopLeft();
554 return JoinBoxesForVerticalSlice(aFrame
, aBorderArea
);
558 IsBoxDecorationSlice(const nsStyleBorder
& aStyleBorder
)
560 return aStyleBorder
.mBoxDecorationBreak
==
561 NS_STYLE_BOX_DECORATION_BREAK_SLICE
;
565 BoxDecorationRectForBorder(nsIFrame
* aFrame
, const nsRect
& aBorderArea
,
567 const nsStyleBorder
* aStyleBorder
= nullptr)
570 aStyleBorder
= aFrame
->StyleBorder();
572 // If aSkipSides.IsEmpty() then there are no continuations, or it's
573 // a ::first-letter that wants all border sides on the first continuation.
574 return ::IsBoxDecorationSlice(*aStyleBorder
) && !aSkipSides
.IsEmpty()
575 ? ::JoinBoxesForSlice(aFrame
, aBorderArea
, eForBorder
)
580 BoxDecorationRectForBackground(nsIFrame
* aFrame
, const nsRect
& aBorderArea
,
582 const nsStyleBorder
* aStyleBorder
= nullptr)
585 aStyleBorder
= aFrame
->StyleBorder();
587 // If aSkipSides.IsEmpty() then there are no continuations, or it's
588 // a ::first-letter that wants all border sides on the first continuation.
589 return ::IsBoxDecorationSlice(*aStyleBorder
) && !aSkipSides
.IsEmpty()
590 ? ::JoinBoxesForSlice(aFrame
, aBorderArea
, eForBackground
)
594 //----------------------------------------------------------------------
595 // Thebes Border Rendering Code Start
598 * Compute the float-pixel radii that should be used for drawing
599 * this border/outline, given the various input bits.
602 nsCSSRendering::ComputePixelRadii(const nscoord
*aAppUnitsRadii
,
603 nscoord aAppUnitsPerPixel
,
604 RectCornerRadii
*oBorderRadii
)
607 NS_FOR_CSS_HALF_CORNERS(corner
)
608 radii
[corner
] = Float(aAppUnitsRadii
[corner
]) / aAppUnitsPerPixel
;
610 (*oBorderRadii
)[C_TL
] = Size(radii
[NS_CORNER_TOP_LEFT_X
],
611 radii
[NS_CORNER_TOP_LEFT_Y
]);
612 (*oBorderRadii
)[C_TR
] = Size(radii
[NS_CORNER_TOP_RIGHT_X
],
613 radii
[NS_CORNER_TOP_RIGHT_Y
]);
614 (*oBorderRadii
)[C_BR
] = Size(radii
[NS_CORNER_BOTTOM_RIGHT_X
],
615 radii
[NS_CORNER_BOTTOM_RIGHT_Y
]);
616 (*oBorderRadii
)[C_BL
] = Size(radii
[NS_CORNER_BOTTOM_LEFT_X
],
617 radii
[NS_CORNER_BOTTOM_LEFT_Y
]);
621 nsCSSRendering::PaintBorder(nsPresContext
* aPresContext
,
622 nsRenderingContext
& aRenderingContext
,
624 const nsRect
& aDirtyRect
,
625 const nsRect
& aBorderArea
,
626 nsStyleContext
* aStyleContext
,
629 PROFILER_LABEL("nsCSSRendering", "PaintBorder",
630 js::ProfileEntry::Category::GRAPHICS
);
632 nsStyleContext
*styleIfVisited
= aStyleContext
->GetStyleIfVisited();
633 const nsStyleBorder
*styleBorder
= aStyleContext
->StyleBorder();
634 // Don't check RelevantLinkVisited here, since we want to take the
635 // same amount of time whether or not it's true.
636 if (!styleIfVisited
) {
637 PaintBorderWithStyleBorder(aPresContext
, aRenderingContext
, aForFrame
,
638 aDirtyRect
, aBorderArea
, *styleBorder
,
639 aStyleContext
, aSkipSides
);
643 nsStyleBorder
newStyleBorder(*styleBorder
);
644 // We could do something fancy to avoid the TrackImage/UntrackImage
645 // work, but it doesn't seem worth it. (We need to call TrackImage
646 // since we're not going through nsRuleNode::ComputeBorderData.)
647 newStyleBorder
.TrackImage(aPresContext
);
649 NS_FOR_CSS_SIDES(side
) {
650 newStyleBorder
.SetBorderColor(side
,
651 aStyleContext
->GetVisitedDependentColor(
652 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color
)[side
]));
654 PaintBorderWithStyleBorder(aPresContext
, aRenderingContext
, aForFrame
,
655 aDirtyRect
, aBorderArea
, newStyleBorder
,
656 aStyleContext
, aSkipSides
);
658 // We could do something fancy to avoid the TrackImage/UntrackImage
659 // work, but it doesn't seem worth it. (We need to call UntrackImage
660 // since we're not going through nsStyleBorder::Destroy.)
661 newStyleBorder
.UntrackImage(aPresContext
);
665 nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext
* aPresContext
,
666 nsRenderingContext
& aRenderingContext
,
668 const nsRect
& aDirtyRect
,
669 const nsRect
& aBorderArea
,
670 const nsStyleBorder
& aStyleBorder
,
671 nsStyleContext
* aStyleContext
,
674 DrawTarget
& aDrawTarget
= *aRenderingContext
.GetDrawTarget();
676 PrintAsStringNewline("++ PaintBorder");
678 // Check to see if we have an appearance defined. If so, we let the theme
679 // renderer draw the border. DO not get the data from aForFrame, since the passed in style context
680 // may be different! Always use |aStyleContext|!
681 const nsStyleDisplay
* displayData
= aStyleContext
->StyleDisplay();
682 if (displayData
->mAppearance
) {
683 nsITheme
*theme
= aPresContext
->GetTheme();
684 if (theme
&& theme
->ThemeSupportsWidget(aPresContext
, aForFrame
, displayData
->mAppearance
))
685 return; // Let the theme handle it.
688 if (aStyleBorder
.IsBorderImageLoaded()) {
689 DrawBorderImage(aPresContext
, aRenderingContext
, aForFrame
,
690 aBorderArea
, aStyleBorder
, aDirtyRect
, aSkipSides
);
694 // Get our style context's color struct.
695 const nsStyleColor
* ourColor
= aStyleContext
->StyleColor();
697 // In NavQuirks mode we want to use the parent's context as a starting point
698 // for determining the background color.
699 bool quirks
= aPresContext
->CompatibilityMode() == eCompatibility_NavQuirks
;
700 nsIFrame
* bgFrame
= FindNonTransparentBackgroundFrame(aForFrame
, quirks
);
701 nsStyleContext
* bgContext
= bgFrame
->StyleContext();
703 bgContext
->GetVisitedDependentColor(eCSSProperty_background_color
);
705 nsMargin border
= aStyleBorder
.GetComputedBorder();
706 if (0 == border
.left
&& 0 == border
.right
&&
707 0 == border
.top
&& 0 == border
.bottom
) {
712 // Compute the outermost boundary of the area that might be painted.
713 // Same coordinate space as aBorderArea & aBGClipRect.
714 nsRect joinedBorderArea
=
715 ::BoxDecorationRectForBorder(aForFrame
, aBorderArea
, aSkipSides
, &aStyleBorder
);
716 RectCornerRadii bgRadii
;
717 ::GetRadii(aForFrame
, aStyleBorder
, aBorderArea
, joinedBorderArea
, &bgRadii
);
719 PrintAsFormatString(" joinedBorderArea: %d %d %d %d\n", joinedBorderArea
.x
, joinedBorderArea
.y
,
720 joinedBorderArea
.width
, joinedBorderArea
.height
);
723 gfxContext
* ctx
= aRenderingContext
.ThebesContext();
726 if (::IsBoxDecorationSlice(aStyleBorder
)) {
727 if (joinedBorderArea
.IsEqualEdges(aBorderArea
)) {
728 // No need for a clip, just skip the sides we don't want.
729 border
.ApplySkipSides(aSkipSides
);
731 // We're drawing borders around the joined continuation boxes so we need
732 // to clip that to the slice that we want for this frame.
733 aRenderingContext
.ThebesContext()->
734 Clip(NSRectToSnappedRect(aBorderArea
,
735 aForFrame
->PresContext()->AppUnitsPerDevPixel(),
739 MOZ_ASSERT(joinedBorderArea
.IsEqualEdges(aBorderArea
),
740 "Should use aBorderArea for box-decoration-break:clone");
741 MOZ_ASSERT(aForFrame
->GetSkipSides().IsEmpty(),
742 "Should not skip sides for box-decoration-break:clone except "
743 "::first-letter/line continuations or other frame types that "
744 "don't have borders but those shouldn't reach this point.");
747 // Convert to dev pixels.
748 nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
749 Rect joinedBorderAreaPx
= NSRectToRect(joinedBorderArea
, twipsPerPixel
);
750 Float borderWidths
[4] = { Float(border
.top
/ twipsPerPixel
),
751 Float(border
.right
/ twipsPerPixel
),
752 Float(border
.bottom
/ twipsPerPixel
),
753 Float(border
.left
/ twipsPerPixel
) };
755 uint8_t borderStyles
[4];
756 nscolor borderColors
[4];
757 nsBorderColors
*compositeColors
[4];
759 // pull out styles, colors, composite colors
760 NS_FOR_CSS_SIDES (i
) {
762 borderStyles
[i
] = aStyleBorder
.GetBorderStyle(i
);
763 aStyleBorder
.GetBorderColor(i
, borderColors
[i
], foreground
);
764 aStyleBorder
.GetCompositeColors(i
, &compositeColors
[i
]);
767 borderColors
[i
] = ourColor
->mColor
;
770 PrintAsFormatString(" borderStyles: %d %d %d %d\n", borderStyles
[0], borderStyles
[1], borderStyles
[2], borderStyles
[3]);
771 //PrintAsFormatString ("bgRadii: %f %f %f %f\n", bgRadii[0], bgRadii[1], bgRadii[2], bgRadii[3]);
774 // this will draw a transparent red backround underneath the border area
775 ColorPattern
color(ToDeviceColor(Color(1.f
, 0.f
, 0.f
, 0.5f
)));
776 aDrawTarget
.FillRect(joinedBorderAreaPx
, color
);
779 nsCSSBorderRenderer
br(&aDrawTarget
,
791 PrintAsStringNewline();
795 GetOutlineInnerRect(nsIFrame
* aFrame
)
797 nsRect
* savedOutlineInnerRect
= static_cast<nsRect
*>
798 (aFrame
->Properties().Get(nsIFrame::OutlineInnerRectProperty()));
799 if (savedOutlineInnerRect
)
800 return *savedOutlineInnerRect
;
801 NS_NOTREACHED("we should have saved a frame property");
802 return nsRect(nsPoint(0, 0), aFrame
->GetSize());
806 nsCSSRendering::PaintOutline(nsPresContext
* aPresContext
,
807 nsRenderingContext
& aRenderingContext
,
809 const nsRect
& aDirtyRect
,
810 const nsRect
& aBorderArea
,
811 nsStyleContext
* aStyleContext
)
813 nscoord twipsRadii
[8];
815 // Get our style context's color struct.
816 const nsStyleOutline
* ourOutline
= aStyleContext
->StyleOutline();
817 MOZ_ASSERT(ourOutline
!= NS_STYLE_BORDER_STYLE_NONE
,
818 "shouldn't have created nsDisplayOutline item");
820 uint8_t outlineStyle
= ourOutline
->GetOutlineStyle();
822 ourOutline
->GetOutlineWidth(width
);
824 if (width
== 0 && outlineStyle
!= NS_STYLE_BORDER_STYLE_AUTO
) {
829 nsIFrame
* bgFrame
= nsCSSRendering::FindNonTransparentBackgroundFrame
831 nsStyleContext
* bgContext
= bgFrame
->StyleContext();
833 bgContext
->GetVisitedDependentColor(eCSSProperty_background_color
);
838 aStyleContext
->GetPseudoType() == nsCSSPseudoElements::ePseudo_XULTree
843 innerRect
= aBorderArea
;
845 innerRect
= GetOutlineInnerRect(aForFrame
) + aBorderArea
.TopLeft();
847 nscoord offset
= ourOutline
->mOutlineOffset
;
848 innerRect
.Inflate(offset
, offset
);
849 // If the dirty rect is completely inside the border area (e.g., only the
850 // content is being painted), then we can skip out now
851 // XXX this isn't exactly true for rounded borders, where the inside curves may
852 // encroach into the content area. A safer calculation would be to
853 // shorten insideRect by the radius one each side before performing this test.
854 if (innerRect
.Contains(aDirtyRect
))
857 nsRect outerRect
= innerRect
;
858 outerRect
.Inflate(width
, width
);
860 // get the radius for our outline
861 nsIFrame::ComputeBorderRadii(ourOutline
->mOutlineRadius
, aBorderArea
.Size(),
862 outerRect
.Size(), Sides(), twipsRadii
);
864 // Get our conversion values
865 nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
867 // get the outer rectangles
868 Rect
oRect(NSRectToRect(outerRect
, twipsPerPixel
));
871 nsMargin
outlineMargin(width
, width
, width
, width
);
872 RectCornerRadii outlineRadii
;
873 ComputePixelRadii(twipsRadii
, twipsPerPixel
, &outlineRadii
);
875 if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
876 if (outlineStyle
== NS_STYLE_BORDER_STYLE_AUTO
) {
877 nsITheme
* theme
= aPresContext
->GetTheme();
878 if (theme
&& theme
->ThemeSupportsWidget(aPresContext
, aForFrame
,
879 NS_THEME_FOCUS_OUTLINE
)) {
880 theme
->DrawWidgetBackground(&aRenderingContext
, aForFrame
,
881 NS_THEME_FOCUS_OUTLINE
, innerRect
,
884 } else if (width
== 0) {
885 return; // empty outline
887 // http://dev.w3.org/csswg/css-ui/#outline
888 // "User agents may treat 'auto' as 'solid'."
889 outlineStyle
= NS_STYLE_BORDER_STYLE_SOLID
;
893 uint8_t outlineStyles
[4] = { outlineStyle
, outlineStyle
,
894 outlineStyle
, outlineStyle
};
896 // This handles treating the initial color as 'currentColor'; if we
897 // ever want 'invert' back we'll need to do a bit of work here too.
898 nscolor outlineColor
=
899 aStyleContext
->GetVisitedDependentColor(eCSSProperty_outline_color
);
900 nscolor outlineColors
[4] = { outlineColor
,
905 // convert the border widths
906 Float outlineWidths
[4] = { Float(width
/ twipsPerPixel
),
907 Float(width
/ twipsPerPixel
),
908 Float(width
/ twipsPerPixel
),
909 Float(width
/ twipsPerPixel
) };
912 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
914 nsCSSBorderRenderer
br(ctx
->GetDrawTarget(),
924 PrintAsStringNewline();
928 nsCSSRendering::PaintFocus(nsPresContext
* aPresContext
,
929 nsRenderingContext
& aRenderingContext
,
930 const nsRect
& aFocusRect
,
933 nscoord oneCSSPixel
= nsPresContext::CSSPixelsToAppUnits(1);
934 nscoord oneDevPixel
= aPresContext
->DevPixelsToAppUnits(1);
936 Rect
focusRect(NSRectToRect(aFocusRect
, oneDevPixel
));
938 RectCornerRadii focusRadii
;
940 nscoord twipsRadii
[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
941 ComputePixelRadii(twipsRadii
, oneDevPixel
, &focusRadii
);
943 Float focusWidths
[4] = { Float(oneCSSPixel
/ oneDevPixel
),
944 Float(oneCSSPixel
/ oneDevPixel
),
945 Float(oneCSSPixel
/ oneDevPixel
),
946 Float(oneCSSPixel
/ oneDevPixel
) };
948 uint8_t focusStyles
[4] = { NS_STYLE_BORDER_STYLE_DOTTED
,
949 NS_STYLE_BORDER_STYLE_DOTTED
,
950 NS_STYLE_BORDER_STYLE_DOTTED
,
951 NS_STYLE_BORDER_STYLE_DOTTED
};
952 nscolor focusColors
[4] = { aColor
, aColor
, aColor
, aColor
};
954 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
956 // Because this renders a dotted border, the background color
957 // should not be used. Therefore, we provide a value that will
958 // be blatantly wrong if it ever does get used. (If this becomes
959 // something that CSS can style, this function will then have access
960 // to a style context and can use the same logic that PaintBorder
961 // and PaintOutline do.)
962 nsCSSBorderRenderer
br(ctx
->GetDrawTarget(),
972 PrintAsStringNewline();
975 // Thebes Border Rendering Code End
976 //----------------------------------------------------------------------
979 //----------------------------------------------------------------------
982 * Helper for ComputeObjectAnchorPoint; parameters are the same as for
983 * that function, except they're for a single coordinate / a single size
984 * dimension. (so, x/width vs. y/height)
986 typedef nsStyleBackground::Position::PositionCoord PositionCoord
;
988 ComputeObjectAnchorCoord(const PositionCoord
& aCoord
,
989 const nscoord aOriginBounds
,
990 const nscoord aImageSize
,
991 nscoord
* aTopLeftCoord
,
992 nscoord
* aAnchorPointCoord
)
994 *aAnchorPointCoord
= aCoord
.mLength
;
995 *aTopLeftCoord
= aCoord
.mLength
;
997 if (aCoord
.mHasPercent
) {
998 // Adjust aTopLeftCoord by the specified % of the extra space.
999 nscoord extraSpace
= aOriginBounds
- aImageSize
;
1000 *aTopLeftCoord
+= NSToCoordRound(aCoord
.mPercent
* extraSpace
);
1002 // The anchor-point doesn't care about our image's size; just the size
1003 // of the region we're rendering into.
1004 *aAnchorPointCoord
+= NSToCoordRound(aCoord
.mPercent
* aOriginBounds
);
1009 nsImageRenderer::ComputeObjectAnchorPoint(
1010 const nsStyleBackground::Position
& aPos
,
1011 const nsSize
& aOriginBounds
,
1012 const nsSize
& aImageSize
,
1014 nsPoint
* aAnchorPoint
)
1016 ComputeObjectAnchorCoord(aPos
.mXPosition
,
1017 aOriginBounds
.width
, aImageSize
.width
,
1018 &aTopLeft
->x
, &aAnchorPoint
->x
);
1020 ComputeObjectAnchorCoord(aPos
.mYPosition
,
1021 aOriginBounds
.height
, aImageSize
.height
,
1022 &aTopLeft
->y
, &aAnchorPoint
->y
);
1026 nsCSSRendering::FindNonTransparentBackgroundFrame(nsIFrame
* aFrame
,
1027 bool aStartAtParent
/*= false*/)
1029 NS_ASSERTION(aFrame
, "Cannot find NonTransparentBackgroundFrame in a null frame");
1031 nsIFrame
* frame
= nullptr;
1032 if (aStartAtParent
) {
1033 frame
= nsLayoutUtils::GetParentOrPlaceholderFor(aFrame
);
1040 // No need to call GetVisitedDependentColor because it always uses
1041 // this alpha component anyway.
1042 if (NS_GET_A(frame
->StyleBackground()->mBackgroundColor
) > 0)
1045 if (frame
->IsThemed())
1048 nsIFrame
* parent
= nsLayoutUtils::GetParentOrPlaceholderFor(frame
);
1057 // Returns true if aFrame is a canvas frame.
1058 // We need to treat the viewport as canvas because, even though
1059 // it does not actually paint a background, we need to get the right
1060 // background style so we correctly detect transparent documents.
1062 nsCSSRendering::IsCanvasFrame(nsIFrame
* aFrame
)
1064 nsIAtom
* frameType
= aFrame
->GetType();
1065 return frameType
== nsGkAtoms::canvasFrame
||
1066 frameType
== nsGkAtoms::rootFrame
||
1067 frameType
== nsGkAtoms::pageContentFrame
||
1068 frameType
== nsGkAtoms::viewportFrame
;
1072 nsCSSRendering::FindBackgroundStyleFrame(nsIFrame
* aForFrame
)
1074 const nsStyleBackground
* result
= aForFrame
->StyleBackground();
1076 // Check if we need to do propagation from BODY rather than HTML.
1077 if (!result
->IsTransparent()) {
1081 nsIContent
* content
= aForFrame
->GetContent();
1082 // The root element content can't be null. We wouldn't know what
1083 // frame to create for aFrame.
1084 // Use |OwnerDoc| so it works during destruction.
1089 nsIDocument
* document
= content
->OwnerDoc();
1091 dom::Element
* bodyContent
= document
->GetBodyElement();
1092 // We need to null check the body node (bug 118829) since
1093 // there are cases, thanks to the fix for bug 5569, where we
1094 // will reflow a document with no body. In particular, if a
1095 // SCRIPT element in the head blocks the parser and then has a
1096 // SCRIPT that does "document.location.href = 'foo'", then
1097 // nsParser::Terminate will call |DidBuildModel| methods
1098 // through to the content sink, which will call |StartLayout|
1099 // and thus |Initialize| on the pres shell. See bug 119351
1100 // for the ugly details.
1105 nsIFrame
*bodyFrame
= bodyContent
->GetPrimaryFrame();
1110 return nsLayoutUtils::GetStyleFrame(bodyFrame
);
1114 * |FindBackground| finds the correct style data to use to paint the
1115 * background. It is responsible for handling the following two
1116 * statements in section 14.2 of CSS2:
1118 * The background of the box generated by the root element covers the
1121 * For HTML documents, however, we recommend that authors specify the
1122 * background for the BODY element rather than the HTML element. User
1123 * agents should observe the following precedence rules to fill in the
1124 * background: if the value of the 'background' property for the HTML
1125 * element is different from 'transparent' then use it, else use the
1126 * value of the 'background' property for the BODY element. If the
1127 * resulting value is 'transparent', the rendering is undefined.
1129 * Thus, in our implementation, it is responsible for ensuring that:
1130 * + we paint the correct background on the |nsCanvasFrame|,
1131 * |nsRootBoxFrame|, or |nsPageFrame|,
1132 * + we don't paint the background on the root element, and
1133 * + we don't paint the background on the BODY element in *some* cases,
1134 * and for SGML-based HTML documents only.
1136 * |FindBackground| returns true if a background should be painted, and
1137 * the resulting style context to use for the background information
1138 * will be filled in to |aBackground|.
1141 nsCSSRendering::FindRootFrameBackground(nsIFrame
* aForFrame
)
1143 return FindBackgroundStyleFrame(aForFrame
)->StyleContext();
1147 FindElementBackground(nsIFrame
* aForFrame
, nsIFrame
* aRootElementFrame
,
1148 nsStyleContext
** aBackgroundSC
)
1150 if (aForFrame
== aRootElementFrame
) {
1151 // We must have propagated our background to the viewport or canvas. Abort.
1155 *aBackgroundSC
= aForFrame
->StyleContext();
1157 // Return true unless the frame is for a BODY element whose background
1158 // was propagated to the viewport.
1160 nsIContent
* content
= aForFrame
->GetContent();
1161 if (!content
|| content
->Tag() != nsGkAtoms::body
)
1162 return true; // not frame for a "body" element
1163 // It could be a non-HTML "body" element but that's OK, we'd fail the
1164 // bodyContent check below
1166 if (aForFrame
->StyleContext()->GetPseudo())
1167 return true; // A pseudo-element frame.
1169 // We should only look at the <html> background if we're in an HTML document
1170 nsIDocument
* document
= content
->OwnerDoc();
1172 dom::Element
* bodyContent
= document
->GetBodyElement();
1173 if (bodyContent
!= content
)
1174 return true; // this wasn't the background that was propagated
1176 // This can be called even when there's no root element yet, during frame
1177 // construction, via nsLayoutUtils::FrameHasTransparency and
1178 // nsContainerFrame::SyncFrameViewProperties.
1179 if (!aRootElementFrame
)
1182 const nsStyleBackground
* htmlBG
= aRootElementFrame
->StyleBackground();
1183 return !htmlBG
->IsTransparent();
1187 nsCSSRendering::FindBackground(nsIFrame
* aForFrame
,
1188 nsStyleContext
** aBackgroundSC
)
1190 nsIFrame
* rootElementFrame
=
1191 aForFrame
->PresContext()->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
1192 if (IsCanvasFrame(aForFrame
)) {
1193 *aBackgroundSC
= FindCanvasBackground(aForFrame
, rootElementFrame
);
1196 return FindElementBackground(aForFrame
, rootElementFrame
, aBackgroundSC
);
1201 nsCSSRendering::BeginFrameTreesLocked()
1203 ++gFrameTreeLockCount
;
1207 nsCSSRendering::EndFrameTreesLocked()
1209 NS_ASSERTION(gFrameTreeLockCount
> 0, "Unbalanced EndFrameTreeLocked");
1210 --gFrameTreeLockCount
;
1211 if (gFrameTreeLockCount
== 0) {
1212 gInlineBGData
->Reset();
1217 nsCSSRendering::PaintBoxShadowOuter(nsPresContext
* aPresContext
,
1218 nsRenderingContext
& aRenderingContext
,
1219 nsIFrame
* aForFrame
,
1220 const nsRect
& aFrameArea
,
1221 const nsRect
& aDirtyRect
,
1224 DrawTarget
& aDrawTarget
= *aRenderingContext
.GetDrawTarget();
1225 const nsStyleBorder
* styleBorder
= aForFrame
->StyleBorder();
1226 nsCSSShadowArray
* shadows
= styleBorder
->mBoxShadow
;
1230 gfxContextAutoSaveRestore gfxStateRestorer
;
1231 bool hasBorderRadius
;
1232 bool nativeTheme
; // mutually exclusive with hasBorderRadius
1233 const nsStyleDisplay
* styleDisplay
= aForFrame
->StyleDisplay();
1234 nsITheme::Transparency transparency
;
1235 if (aForFrame
->IsThemed(styleDisplay
, &transparency
)) {
1236 // We don't respect border-radius for native-themed widgets
1237 hasBorderRadius
= false;
1238 // For opaque (rectangular) theme widgets we can take the generic
1239 // border-box path with border-radius disabled.
1240 nativeTheme
= transparency
!= nsITheme::eOpaque
;
1242 nativeTheme
= false;
1243 hasBorderRadius
= true; // we'll update this below
1246 nsRect frameRect
= nativeTheme
?
1247 aForFrame
->GetVisualOverflowRectRelativeToSelf() + aFrameArea
.TopLeft() :
1249 Sides skipSides
= aForFrame
->GetSkipSides();
1250 frameRect
= ::BoxDecorationRectForBorder(aForFrame
, frameRect
, skipSides
);
1252 // Get any border radius, since box-shadow must also have rounded corners if
1254 RectCornerRadii borderRadii
;
1255 const nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
1256 if (hasBorderRadius
) {
1257 nscoord twipsRadii
[8];
1258 NS_ASSERTION(aFrameArea
.Size() == aForFrame
->VisualBorderRectRelativeToSelf().Size(),
1260 nsSize sz
= frameRect
.Size();
1261 hasBorderRadius
= aForFrame
->GetBorderRadii(sz
, sz
, Sides(), twipsRadii
);
1262 if (hasBorderRadius
) {
1263 ComputePixelRadii(twipsRadii
, twipsPerPixel
, &borderRadii
);
1267 Rect frameGfxRect
= NSRectToRect(frameRect
, twipsPerPixel
);
1268 frameGfxRect
.Round();
1270 // We don't show anything that intersects with the frame we're blurring on. So tell the
1271 // blurrer not to do unnecessary work there.
1272 gfxRect skipGfxRect
= ThebesRect(frameGfxRect
);
1273 bool useSkipGfxRect
= true;
1275 // Optimize non-leaf native-themed frames by skipping computing pixels
1276 // in the padding-box. We assume the padding-box is going to be painted
1277 // opaquely for non-leaf frames.
1278 // XXX this may not be a safe assumption; we should make this go away
1279 // by optimizing box-shadow drawing more for the cases where we don't have a skip-rect.
1280 useSkipGfxRect
= !aForFrame
->IsLeaf();
1281 nsRect paddingRect
=
1282 aForFrame
->GetPaddingRect() - aForFrame
->GetPosition() + aFrameArea
.TopLeft();
1283 skipGfxRect
= nsLayoutUtils::RectToGfxRect(paddingRect
, twipsPerPixel
);
1284 } else if (hasBorderRadius
) {
1285 skipGfxRect
.Deflate(gfxMargin(
1286 std::max(borderRadii
[C_TL
].height
, borderRadii
[C_TR
].height
), 0,
1287 std::max(borderRadii
[C_BL
].height
, borderRadii
[C_BR
].height
), 0));
1290 gfxContext
* renderContext
= aRenderingContext
.ThebesContext();
1292 for (uint32_t i
= shadows
->Length(); i
> 0; --i
) {
1293 nsCSSShadowItem
* shadowItem
= shadows
->ShadowAt(i
- 1);
1294 if (shadowItem
->mInset
)
1297 nsRect shadowRect
= frameRect
;
1298 shadowRect
.MoveBy(shadowItem
->mXOffset
, shadowItem
->mYOffset
);
1300 shadowRect
.Inflate(shadowItem
->mSpread
, shadowItem
->mSpread
);
1303 // shadowRect won't include the blur, so make an extra rect here that includes the blur
1304 // for use in the even-odd rule below.
1305 nsRect shadowRectPlusBlur
= shadowRect
;
1306 nscoord blurRadius
= shadowItem
->mRadius
;
1307 shadowRectPlusBlur
.Inflate(
1308 nsContextBoxBlur::GetBlurRadiusMargin(blurRadius
, twipsPerPixel
));
1310 Rect shadowGfxRectPlusBlur
=
1311 NSRectToRect(shadowRectPlusBlur
, twipsPerPixel
);
1312 shadowGfxRectPlusBlur
.RoundOut();
1313 MaybeSnapToDevicePixels(shadowGfxRectPlusBlur
, aDrawTarget
, true);
1315 // Set the shadow color; if not specified, use the foreground color
1316 nscolor shadowColor
;
1317 if (shadowItem
->mHasColor
)
1318 shadowColor
= shadowItem
->mColor
;
1320 shadowColor
= aForFrame
->StyleColor()->mColor
;
1322 gfxRGBA
gfxShadowColor(shadowColor
);
1323 gfxShadowColor
.a
*= aOpacity
;
1326 nsContextBoxBlur blurringArea
;
1328 // When getting the widget shape from the native theme, we're going
1329 // to draw the widget into the shadow surface to create a mask.
1330 // We need to ensure that there actually *is* a shadow surface
1331 // and that we're not going to draw directly into renderContext.
1332 gfxContext
* shadowContext
=
1333 blurringArea
.Init(shadowRect
, shadowItem
->mSpread
,
1334 blurRadius
, twipsPerPixel
, renderContext
, aDirtyRect
,
1335 useSkipGfxRect
? &skipGfxRect
: nullptr,
1336 nsContextBoxBlur::FORCE_MASK
);
1340 MOZ_ASSERT(shadowContext
== blurringArea
.GetContext());
1342 renderContext
->Save();
1343 renderContext
->SetColor(gfxShadowColor
);
1345 // Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur
1346 // doesn't make any temporary surfaces if blur is 0 and it just returns the original
1347 // surface? If we have no blur, we're painting this fill on the actual content surface
1348 // (renderContext == shadowContext) which is why we set up the color and clip
1349 // before doing this.
1351 // We don't clip the border-box from the shadow, nor any other box.
1352 // We assume that the native theme is going to paint over the shadow.
1354 // Draw the widget shape
1355 gfxContextMatrixAutoSaveRestore
save(shadowContext
);
1356 gfxPoint devPixelOffset
=
1357 nsLayoutUtils::PointToGfxPoint(nsPoint(shadowItem
->mXOffset
,
1358 shadowItem
->mYOffset
),
1359 aPresContext
->AppUnitsPerDevPixel());
1360 shadowContext
->SetMatrix(
1361 shadowContext
->CurrentMatrix().Translate(devPixelOffset
));
1364 nativeRect
.IntersectRect(frameRect
, aDirtyRect
);
1365 nsRenderingContext
wrapperCtx(shadowContext
);
1366 aPresContext
->GetTheme()->DrawWidgetBackground(&wrapperCtx
, aForFrame
,
1367 styleDisplay
->mAppearance
, aFrameArea
, nativeRect
);
1369 blurringArea
.DoPaint();
1370 renderContext
->Restore();
1372 renderContext
->Save();
1375 // Clip out the interior of the frame's border edge so that the shadow
1376 // is only painted outside that area.
1377 RefPtr
<PathBuilder
> builder
=
1378 aDrawTarget
.CreatePathBuilder(FillRule::FILL_EVEN_ODD
);
1379 AppendRectToPath(builder
, shadowGfxRectPlusBlur
);
1380 if (hasBorderRadius
) {
1381 AppendRoundedRectToPath(builder
, frameGfxRect
, borderRadii
);
1383 AppendRectToPath(builder
, frameGfxRect
);
1385 RefPtr
<Path
> path
= builder
->Finish();
1386 renderContext
->Clip(path
);
1389 // Clip the shadow so that we only get the part that applies to aForFrame.
1390 nsRect fragmentClip
= shadowRectPlusBlur
;
1391 if (!skipSides
.IsEmpty()) {
1392 if (skipSides
.Left()) {
1393 nscoord xmost
= fragmentClip
.XMost();
1394 fragmentClip
.x
= aFrameArea
.x
;
1395 fragmentClip
.width
= xmost
- fragmentClip
.x
;
1397 if (skipSides
.Right()) {
1398 nscoord xmost
= fragmentClip
.XMost();
1399 nscoord overflow
= xmost
- aFrameArea
.XMost();
1401 fragmentClip
.width
-= overflow
;
1404 if (skipSides
.Top()) {
1405 nscoord ymost
= fragmentClip
.YMost();
1406 fragmentClip
.y
= aFrameArea
.y
;
1407 fragmentClip
.height
= ymost
- fragmentClip
.y
;
1409 if (skipSides
.Bottom()) {
1410 nscoord ymost
= fragmentClip
.YMost();
1411 nscoord overflow
= ymost
- aFrameArea
.YMost();
1413 fragmentClip
.height
-= overflow
;
1418 Clip(NSRectToSnappedRect(fragmentClip
,
1419 aForFrame
->PresContext()->AppUnitsPerDevPixel(),
1422 RectCornerRadii clipRectRadii
;
1423 if (hasBorderRadius
) {
1424 Float spreadDistance
= shadowItem
->mSpread
/ twipsPerPixel
;
1426 Float borderSizes
[4];
1428 borderSizes
[NS_SIDE_LEFT
] = spreadDistance
;
1429 borderSizes
[NS_SIDE_TOP
] = spreadDistance
;
1430 borderSizes
[NS_SIDE_RIGHT
] = spreadDistance
;
1431 borderSizes
[NS_SIDE_BOTTOM
] = spreadDistance
;
1433 nsCSSBorderRenderer::ComputeOuterRadii(borderRadii
, borderSizes
,
1437 nsContextBoxBlur::BlurRectangle(renderContext
,
1440 hasBorderRadius
? &clipRectRadii
: nullptr,
1445 renderContext
->Restore();
1452 nsCSSRendering::PaintBoxShadowInner(nsPresContext
* aPresContext
,
1453 nsRenderingContext
& aRenderingContext
,
1454 nsIFrame
* aForFrame
,
1455 const nsRect
& aFrameArea
,
1456 const nsRect
& aDirtyRect
)
1458 const nsStyleBorder
* styleBorder
= aForFrame
->StyleBorder();
1459 nsCSSShadowArray
* shadows
= styleBorder
->mBoxShadow
;
1462 if (aForFrame
->IsThemed() && aForFrame
->GetContent() &&
1463 !nsContentUtils::IsChromeDoc(aForFrame
->GetContent()->GetCurrentDoc())) {
1464 // There's no way of getting hold of a shape corresponding to a
1465 // "padding-box" for native-themed widgets, so just don't draw
1466 // inner box-shadows for them. But we allow chrome to paint inner
1467 // box shadows since chrome can be aware of the platform theme.
1471 NS_ASSERTION(aForFrame
->GetType() == nsGkAtoms::fieldSetFrame
||
1472 aFrameArea
.Size() == aForFrame
->GetSize(), "unexpected size");
1474 Sides skipSides
= aForFrame
->GetSkipSides();
1476 ::BoxDecorationRectForBorder(aForFrame
, aFrameArea
, skipSides
);
1477 nsRect paddingRect
= frameRect
;
1478 nsMargin border
= aForFrame
->GetUsedBorder();
1479 paddingRect
.Deflate(border
);
1481 // Get any border radius, since box-shadow must also have rounded corners
1482 // if the frame does.
1483 nscoord twipsRadii
[8];
1484 nsSize sz
= frameRect
.Size();
1485 bool hasBorderRadius
= aForFrame
->GetBorderRadii(sz
, sz
, Sides(), twipsRadii
);
1486 const nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
1488 RectCornerRadii innerRadii
;
1489 if (hasBorderRadius
) {
1490 RectCornerRadii borderRadii
;
1492 ComputePixelRadii(twipsRadii
, twipsPerPixel
, &borderRadii
);
1493 Float borderSizes
[4] = {
1494 Float(border
.top
/ twipsPerPixel
),
1495 Float(border
.right
/ twipsPerPixel
),
1496 Float(border
.bottom
/ twipsPerPixel
),
1497 Float(border
.left
/ twipsPerPixel
)
1499 nsCSSBorderRenderer::ComputeInnerRadii(borderRadii
, borderSizes
,
1503 for (uint32_t i
= shadows
->Length(); i
> 0; --i
) {
1504 nsCSSShadowItem
* shadowItem
= shadows
->ShadowAt(i
- 1);
1505 if (!shadowItem
->mInset
)
1508 // shadowPaintRect: the area to paint on the temp surface
1509 // shadowClipRect: the area on the temporary surface within shadowPaintRect
1510 // that we will NOT paint in
1511 nscoord blurRadius
= shadowItem
->mRadius
;
1512 nsMargin blurMargin
=
1513 nsContextBoxBlur::GetBlurRadiusMargin(blurRadius
, twipsPerPixel
);
1514 nsRect shadowPaintRect
= paddingRect
;
1515 shadowPaintRect
.Inflate(blurMargin
);
1517 nsRect shadowClipRect
= paddingRect
;
1518 shadowClipRect
.MoveBy(shadowItem
->mXOffset
, shadowItem
->mYOffset
);
1519 shadowClipRect
.Deflate(shadowItem
->mSpread
, shadowItem
->mSpread
);
1521 RectCornerRadii clipRectRadii
;
1522 if (hasBorderRadius
) {
1523 // Calculate the radii the inner clipping rect will have
1524 Float spreadDistance
= shadowItem
->mSpread
/ twipsPerPixel
;
1525 Float borderSizes
[4] = {0, 0, 0, 0};
1527 // See PaintBoxShadowOuter and bug 514670
1528 if (innerRadii
[C_TL
].width
> 0 || innerRadii
[C_BL
].width
> 0) {
1529 borderSizes
[NS_SIDE_LEFT
] = spreadDistance
;
1532 if (innerRadii
[C_TL
].height
> 0 || innerRadii
[C_TR
].height
> 0) {
1533 borderSizes
[NS_SIDE_TOP
] = spreadDistance
;
1536 if (innerRadii
[C_TR
].width
> 0 || innerRadii
[C_BR
].width
> 0) {
1537 borderSizes
[NS_SIDE_RIGHT
] = spreadDistance
;
1540 if (innerRadii
[C_BL
].height
> 0 || innerRadii
[C_BR
].height
> 0) {
1541 borderSizes
[NS_SIDE_BOTTOM
] = spreadDistance
;
1544 nsCSSBorderRenderer::ComputeInnerRadii(innerRadii
, borderSizes
,
1548 // Set the "skip rect" to the area within the frame that we don't paint in,
1549 // including after blurring.
1550 nsRect skipRect
= shadowClipRect
;
1551 skipRect
.Deflate(blurMargin
);
1552 gfxRect skipGfxRect
= nsLayoutUtils::RectToGfxRect(skipRect
, twipsPerPixel
);
1553 if (hasBorderRadius
) {
1554 skipGfxRect
.Deflate(gfxMargin(
1555 std::max(clipRectRadii
[C_TL
].height
, clipRectRadii
[C_TR
].height
), 0,
1556 std::max(clipRectRadii
[C_BL
].height
, clipRectRadii
[C_BR
].height
), 0));
1559 // When there's a blur radius, gfxAlphaBoxBlur leaves the skiprect area
1560 // unchanged. And by construction the gfxSkipRect is not touched by the
1561 // rendered shadow (even after blurring), so those pixels must be completely
1562 // transparent in the shadow, so drawing them changes nothing.
1563 gfxContext
* renderContext
= aRenderingContext
.ThebesContext();
1564 DrawTarget
* drawTarget
= renderContext
->GetDrawTarget();
1565 nsContextBoxBlur blurringArea
;
1566 gfxContext
* shadowContext
=
1567 blurringArea
.Init(shadowPaintRect
, 0, blurRadius
, twipsPerPixel
,
1568 renderContext
, aDirtyRect
, &skipGfxRect
);
1571 DrawTarget
* shadowDT
= shadowContext
->GetDrawTarget();
1573 // shadowContext is owned by either blurringArea or aRenderingContext.
1574 MOZ_ASSERT(shadowContext
== renderContext
||
1575 shadowContext
== blurringArea
.GetContext());
1577 // Set the shadow color; if not specified, use the foreground color
1578 Color shadowColor
= Color::FromABGR(shadowItem
->mHasColor
?
1579 shadowItem
->mColor
:
1580 aForFrame
->StyleColor()->mColor
);
1581 renderContext
->Save();
1582 renderContext
->SetColor(ThebesColor(shadowColor
));
1584 // Clip the context to the area of the frame's padding rect, so no part of the
1585 // shadow is painted outside. Also cut out anything beyond where the inset shadow
1587 Rect shadowGfxRect
= NSRectToRect(paddingRect
, twipsPerPixel
);
1588 shadowGfxRect
.Round();
1589 if (hasBorderRadius
) {
1590 RefPtr
<Path
> roundedRect
=
1591 MakePathForRoundedRect(*drawTarget
, shadowGfxRect
, innerRadii
);
1592 renderContext
->Clip(roundedRect
);
1594 renderContext
->Clip(shadowGfxRect
);
1597 // Fill the surface minus the area within the frame that we should
1598 // not paint in, and blur and apply it.
1599 Rect shadowPaintGfxRect
= NSRectToRect(shadowPaintRect
, twipsPerPixel
);
1600 shadowPaintGfxRect
.RoundOut();
1601 Rect shadowClipGfxRect
= NSRectToRect(shadowClipRect
, twipsPerPixel
);
1602 shadowClipGfxRect
.Round();
1603 RefPtr
<PathBuilder
> builder
=
1604 shadowDT
->CreatePathBuilder(FillRule::FILL_EVEN_ODD
);
1605 AppendRectToPath(builder
, shadowPaintGfxRect
, true);
1606 if (hasBorderRadius
) {
1607 AppendRoundedRectToPath(builder
, shadowClipGfxRect
, clipRectRadii
, false);
1609 AppendRectToPath(builder
, shadowClipGfxRect
, false);
1611 RefPtr
<Path
> path
= builder
->Finish();
1612 shadowContext
->SetPath(path
);
1613 shadowContext
->Fill();
1614 shadowContext
->NewPath();
1616 blurringArea
.DoPaint();
1617 renderContext
->Restore();
1622 nsCSSRendering::PaintBackground(nsPresContext
* aPresContext
,
1623 nsRenderingContext
& aRenderingContext
,
1624 nsIFrame
* aForFrame
,
1625 const nsRect
& aDirtyRect
,
1626 const nsRect
& aBorderArea
,
1628 nsRect
* aBGClipRect
,
1631 PROFILER_LABEL("nsCSSRendering", "PaintBackground",
1632 js::ProfileEntry::Category::GRAPHICS
);
1634 NS_PRECONDITION(aForFrame
,
1635 "Frame is expected to be provided to PaintBackground");
1638 if (!FindBackground(aForFrame
, &sc
)) {
1639 // We don't want to bail out if moz-appearance is set on a root
1640 // node. If it has a parent content node, bail because it's not
1641 // a root, otherwise keep going in order to let the theme stuff
1642 // draw the background. The canvas really should be drawing the
1643 // bg, but there's no way to hook that up via css.
1644 if (!aForFrame
->StyleDisplay()->mAppearance
) {
1645 return DrawResult::SUCCESS
;
1648 nsIContent
* content
= aForFrame
->GetContent();
1649 if (!content
|| content
->GetParent()) {
1650 return DrawResult::SUCCESS
;
1653 sc
= aForFrame
->StyleContext();
1656 return PaintBackgroundWithSC(aPresContext
, aRenderingContext
, aForFrame
,
1657 aDirtyRect
, aBorderArea
, sc
,
1658 *aForFrame
->StyleBorder(), aFlags
,
1659 aBGClipRect
, aLayer
);
1663 IsOpaqueBorderEdge(const nsStyleBorder
& aBorder
, mozilla::css::Side aSide
)
1665 if (aBorder
.GetComputedBorder().Side(aSide
) == 0)
1667 switch (aBorder
.GetBorderStyle(aSide
)) {
1668 case NS_STYLE_BORDER_STYLE_SOLID
:
1669 case NS_STYLE_BORDER_STYLE_GROOVE
:
1670 case NS_STYLE_BORDER_STYLE_RIDGE
:
1671 case NS_STYLE_BORDER_STYLE_INSET
:
1672 case NS_STYLE_BORDER_STYLE_OUTSET
:
1678 // If we're using a border image, assume it's not fully opaque,
1679 // because we may not even have the image loaded at this point, and
1680 // even if we did, checking whether the relevant tile is fully
1681 // opaque would be too much work.
1682 if (aBorder
.mBorderImageSource
.GetType() != eStyleImageType_Null
)
1687 aBorder
.GetBorderColor(aSide
, color
, isForeground
);
1689 // We don't know the foreground color here, so if it's being used
1690 // we must assume it might be transparent.
1694 return NS_GET_A(color
) == 255;
1698 * Returns true if all border edges are either missing or opaque.
1701 IsOpaqueBorder(const nsStyleBorder
& aBorder
)
1703 if (aBorder
.mBorderColors
)
1705 NS_FOR_CSS_SIDES(i
) {
1706 if (!IsOpaqueBorderEdge(aBorder
, i
))
1713 SetupDirtyRects(const nsRect
& aBGClipArea
, const nsRect
& aCallerDirtyRect
,
1714 nscoord aAppUnitsPerPixel
,
1716 nsRect
* aDirtyRect
, gfxRect
* aDirtyRectGfx
)
1718 aDirtyRect
->IntersectRect(aBGClipArea
, aCallerDirtyRect
);
1720 // Compute the Thebes equivalent of the dirtyRect.
1721 *aDirtyRectGfx
= nsLayoutUtils::RectToGfxRect(*aDirtyRect
, aAppUnitsPerPixel
);
1722 NS_WARN_IF_FALSE(aDirtyRect
->IsEmpty() || !aDirtyRectGfx
->IsEmpty(),
1723 "converted dirty rect should not be empty");
1724 NS_ABORT_IF_FALSE(!aDirtyRect
->IsEmpty() || aDirtyRectGfx
->IsEmpty(),
1725 "second should be empty if first is");
1729 nsCSSRendering::GetBackgroundClip(const nsStyleBackground::Layer
& aLayer
,
1730 nsIFrame
* aForFrame
, const nsStyleBorder
& aBorder
,
1731 const nsRect
& aBorderArea
, const nsRect
& aCallerDirtyRect
,
1732 bool aWillPaintBorder
, nscoord aAppUnitsPerPixel
,
1733 /* out */ BackgroundClipState
* aClipState
)
1735 // Compute the outermost boundary of the area that might be painted.
1736 // Same coordinate space as aBorderArea.
1737 Sides skipSides
= aForFrame
->GetSkipSides();
1738 nsRect clipBorderArea
=
1739 ::BoxDecorationRectForBorder(aForFrame
, aBorderArea
, skipSides
, &aBorder
);
1741 bool haveRoundedCorners
= GetRadii(aForFrame
, aBorder
, aBorderArea
,
1742 clipBorderArea
, aClipState
->mRadii
);
1744 uint8_t backgroundClip
= aLayer
.mClip
;
1746 bool isSolidBorder
=
1747 aWillPaintBorder
&& IsOpaqueBorder(aBorder
);
1748 if (isSolidBorder
&& backgroundClip
== NS_STYLE_BG_CLIP_BORDER
) {
1749 // If we have rounded corners, we need to inflate the background
1750 // drawing area a bit to avoid seams between the border and
1752 backgroundClip
= haveRoundedCorners
?
1753 NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING
: NS_STYLE_BG_CLIP_PADDING
;
1756 aClipState
->mBGClipArea
= clipBorderArea
;
1757 aClipState
->mHasAdditionalBGClipArea
= false;
1758 aClipState
->mCustomClip
= false;
1760 if (aForFrame
->GetType() == nsGkAtoms::scrollFrame
&&
1761 NS_STYLE_BG_ATTACHMENT_LOCAL
== aLayer
.mAttachment
) {
1762 // As of this writing, this is still in discussion in the CSS Working Group
1763 // http://lists.w3.org/Archives/Public/www-style/2013Jul/0250.html
1765 // The rectangle for 'background-clip' scrolls with the content,
1766 // but the background is also clipped at a non-scrolling 'padding-box'
1767 // like the content. (See below.)
1768 // Therefore, only 'content-box' makes a difference here.
1769 if (backgroundClip
== NS_STYLE_BG_CLIP_CONTENT
) {
1770 nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(aForFrame
);
1771 // Clip at a rectangle attached to the scrolled content.
1772 aClipState
->mHasAdditionalBGClipArea
= true;
1773 aClipState
->mAdditionalBGClipArea
= nsRect(
1774 aClipState
->mBGClipArea
.TopLeft()
1775 + scrollableFrame
->GetScrolledFrame()->GetPosition()
1776 // For the dir=rtl case:
1777 + scrollableFrame
->GetScrollRange().TopLeft(),
1778 scrollableFrame
->GetScrolledRect().Size());
1779 nsMargin padding
= aForFrame
->GetUsedPadding();
1780 // padding-bottom is ignored on scrollable frames:
1781 // https://bugzilla.mozilla.org/show_bug.cgi?id=748518
1783 padding
.ApplySkipSides(skipSides
);
1784 aClipState
->mAdditionalBGClipArea
.Deflate(padding
);
1787 // Also clip at a non-scrolling, rounded-corner 'padding-box',
1788 // same as the scrolled content because of the 'overflow' property.
1789 backgroundClip
= NS_STYLE_BG_CLIP_PADDING
;
1792 if (backgroundClip
!= NS_STYLE_BG_CLIP_BORDER
) {
1793 nsMargin border
= aForFrame
->GetUsedBorder();
1794 if (backgroundClip
== NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING
) {
1795 // Reduce |border| by 1px (device pixels) on all sides, if
1796 // possible, so that we don't get antialiasing seams between the
1797 // background and border.
1798 border
.top
= std::max(0, border
.top
- aAppUnitsPerPixel
);
1799 border
.right
= std::max(0, border
.right
- aAppUnitsPerPixel
);
1800 border
.bottom
= std::max(0, border
.bottom
- aAppUnitsPerPixel
);
1801 border
.left
= std::max(0, border
.left
- aAppUnitsPerPixel
);
1802 } else if (backgroundClip
!= NS_STYLE_BG_CLIP_PADDING
) {
1803 NS_ASSERTION(backgroundClip
== NS_STYLE_BG_CLIP_CONTENT
,
1804 "unexpected background-clip");
1805 border
+= aForFrame
->GetUsedPadding();
1807 border
.ApplySkipSides(skipSides
);
1808 aClipState
->mBGClipArea
.Deflate(border
);
1810 if (haveRoundedCorners
) {
1811 nsIFrame::InsetBorderRadii(aClipState
->mRadii
, border
);
1815 if (haveRoundedCorners
) {
1816 auto d2a
= aForFrame
->PresContext()->AppUnitsPerDevPixel();
1817 nsCSSRendering::ComputePixelRadii(aClipState
->mRadii
, d2a
, &aClipState
->mClippedRadii
);
1818 aClipState
->mHasRoundedCorners
= true;
1820 aClipState
->mHasRoundedCorners
= false;
1824 if (!haveRoundedCorners
&& aClipState
->mHasAdditionalBGClipArea
) {
1825 // Do the intersection here to account for the fast path(?) below.
1826 aClipState
->mBGClipArea
=
1827 aClipState
->mBGClipArea
.Intersect(aClipState
->mAdditionalBGClipArea
);
1828 aClipState
->mHasAdditionalBGClipArea
= false;
1831 SetupDirtyRects(aClipState
->mBGClipArea
, aCallerDirtyRect
, aAppUnitsPerPixel
,
1832 &aClipState
->mDirtyRect
, &aClipState
->mDirtyRectGfx
);
1836 SetupBackgroundClip(nsCSSRendering::BackgroundClipState
& aClipState
,
1837 gfxContext
*aCtx
, nscoord aAppUnitsPerPixel
,
1838 gfxContextAutoSaveRestore
* aAutoSR
)
1840 if (aClipState
.mDirtyRectGfx
.IsEmpty()) {
1841 // Our caller won't draw anything under this condition, so no need
1846 if (aClipState
.mCustomClip
) {
1847 // We don't support custom clips and rounded corners, arguably a bug, but
1848 // table painting seems to depend on it.
1852 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
1854 // If we have rounded corners, clip all subsequent drawing to the
1855 // rounded rectangle defined by bgArea and bgRadii (we don't know
1856 // whether the rounded corners intrude on the dirtyRect or not).
1857 // Do not do this if we have a caller-provided clip rect --
1858 // as above with bgArea, arguably a bug, but table painting seems
1861 if (aClipState
.mHasAdditionalBGClipArea
) {
1862 gfxRect bgAreaGfx
= nsLayoutUtils::RectToGfxRect(
1863 aClipState
.mAdditionalBGClipArea
, aAppUnitsPerPixel
);
1865 bgAreaGfx
.Condition();
1867 aAutoSR
->EnsureSaved(aCtx
);
1869 aCtx
->Rectangle(bgAreaGfx
, true);
1873 if (aClipState
.mHasRoundedCorners
) {
1874 Rect bgAreaGfx
= NSRectToRect(aClipState
.mBGClipArea
, aAppUnitsPerPixel
);
1877 if (bgAreaGfx
.IsEmpty()) {
1878 // I think it's become possible to hit this since
1879 // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
1880 NS_WARNING("converted background area should not be empty");
1881 // Make our caller not do anything.
1882 aClipState
.mDirtyRectGfx
.SizeTo(gfxSize(0.0, 0.0));
1886 aAutoSR
->EnsureSaved(aCtx
);
1888 RefPtr
<Path
> roundedRect
=
1889 MakePathForRoundedRect(*drawTarget
, bgAreaGfx
, aClipState
.mClippedRadii
);
1890 aCtx
->Clip(roundedRect
);
1895 DrawBackgroundColor(nsCSSRendering::BackgroundClipState
& aClipState
,
1896 gfxContext
*aCtx
, nscoord aAppUnitsPerPixel
)
1898 if (aClipState
.mDirtyRectGfx
.IsEmpty()) {
1899 // Our caller won't draw anything under this condition, so no need
1904 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
1906 // We don't support custom clips and rounded corners, arguably a bug, but
1907 // table painting seems to depend on it.
1908 if (!aClipState
.mHasRoundedCorners
|| aClipState
.mCustomClip
) {
1910 aCtx
->Rectangle(aClipState
.mDirtyRectGfx
, true);
1915 Rect bgAreaGfx
= NSRectToRect(aClipState
.mBGClipArea
, aAppUnitsPerPixel
);
1918 if (bgAreaGfx
.IsEmpty()) {
1919 // I think it's become possible to hit this since
1920 // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
1921 NS_WARNING("converted background area should not be empty");
1922 // Make our caller not do anything.
1923 aClipState
.mDirtyRectGfx
.SizeTo(gfxSize(0.0, 0.0));
1928 gfxRect dirty
= ThebesRect(bgAreaGfx
).Intersect(aClipState
.mDirtyRectGfx
);
1931 aCtx
->Rectangle(dirty
, true);
1934 if (aClipState
.mHasAdditionalBGClipArea
) {
1935 gfxRect bgAdditionalAreaGfx
= nsLayoutUtils::RectToGfxRect(
1936 aClipState
.mAdditionalBGClipArea
, aAppUnitsPerPixel
);
1937 bgAdditionalAreaGfx
.Round();
1938 bgAdditionalAreaGfx
.Condition();
1940 aCtx
->Rectangle(bgAdditionalAreaGfx
, true);
1944 RefPtr
<Path
> roundedRect
=
1945 MakePathForRoundedRect(*drawTarget
, bgAreaGfx
, aClipState
.mClippedRadii
);
1946 aCtx
->SetPath(roundedRect
);
1952 nsCSSRendering::DetermineBackgroundColor(nsPresContext
* aPresContext
,
1953 nsStyleContext
* aStyleContext
,
1955 bool& aDrawBackgroundImage
,
1956 bool& aDrawBackgroundColor
)
1958 aDrawBackgroundImage
= true;
1959 aDrawBackgroundColor
= true;
1961 if (aFrame
->HonorPrintBackgroundSettings()) {
1962 aDrawBackgroundImage
= aPresContext
->GetBackgroundImageDraw();
1963 aDrawBackgroundColor
= aPresContext
->GetBackgroundColorDraw();
1966 const nsStyleBackground
*bg
= aStyleContext
->StyleBackground();
1968 if (aDrawBackgroundColor
) {
1970 aStyleContext
->GetVisitedDependentColor(eCSSProperty_background_color
);
1971 if (NS_GET_A(bgColor
) == 0) {
1972 aDrawBackgroundColor
= false;
1975 // If GetBackgroundColorDraw() is false, we are still expected to
1976 // draw color in the background of any frame that's not completely
1977 // transparent, but we are expected to use white instead of whatever
1978 // color was specified.
1979 bgColor
= NS_RGB(255, 255, 255);
1980 if (aDrawBackgroundImage
|| !bg
->IsTransparent()) {
1981 aDrawBackgroundColor
= true;
1983 bgColor
= NS_RGBA(0,0,0,0);
1987 // We can skip painting the background color if a background image is opaque.
1988 if (aDrawBackgroundColor
&&
1989 bg
->BottomLayer().mRepeat
.mXRepeat
== NS_STYLE_BG_REPEAT_REPEAT
&&
1990 bg
->BottomLayer().mRepeat
.mYRepeat
== NS_STYLE_BG_REPEAT_REPEAT
&&
1991 bg
->BottomLayer().mImage
.IsOpaque() &&
1992 bg
->BottomLayer().mBlendMode
== NS_STYLE_BLEND_NORMAL
) {
1993 aDrawBackgroundColor
= false;
2000 ConvertGradientValueToPixels(const nsStyleCoord
& aCoord
,
2001 gfxFloat aFillLength
,
2002 int32_t aAppUnitsPerPixel
)
2004 switch (aCoord
.GetUnit()) {
2005 case eStyleUnit_Percent
:
2006 return aCoord
.GetPercentValue() * aFillLength
;
2007 case eStyleUnit_Coord
:
2008 return NSAppUnitsToFloatPixels(aCoord
.GetCoordValue(), aAppUnitsPerPixel
);
2009 case eStyleUnit_Calc
: {
2010 const nsStyleCoord::Calc
*calc
= aCoord
.GetCalcValue();
2011 return calc
->mPercent
* aFillLength
+
2012 NSAppUnitsToFloatPixels(calc
->mLength
, aAppUnitsPerPixel
);
2015 NS_WARNING("Unexpected coord unit");
2020 // Given a box with size aBoxSize and origin (0,0), and an angle aAngle,
2021 // and a starting point for the gradient line aStart, find the endpoint of
2022 // the gradient line --- the intersection of the gradient line with a line
2023 // perpendicular to aAngle that passes through the farthest corner in the
2024 // direction aAngle.
2026 ComputeGradientLineEndFromAngle(const gfxPoint
& aStart
,
2028 const gfxSize
& aBoxSize
)
2030 double dx
= cos(-aAngle
);
2031 double dy
= sin(-aAngle
);
2032 gfxPoint
farthestCorner(dx
> 0 ? aBoxSize
.width
: 0,
2033 dy
> 0 ? aBoxSize
.height
: 0);
2034 gfxPoint delta
= farthestCorner
- aStart
;
2035 double u
= delta
.x
*dy
- delta
.y
*dx
;
2036 return farthestCorner
+ gfxPoint(-u
*dy
, u
*dx
);
2039 // Compute the start and end points of the gradient line for a linear gradient.
2041 ComputeLinearGradientLine(nsPresContext
* aPresContext
,
2042 nsStyleGradient
* aGradient
,
2043 const gfxSize
& aBoxSize
,
2044 gfxPoint
* aLineStart
,
2047 if (aGradient
->mBgPosX
.GetUnit() == eStyleUnit_None
) {
2049 if (aGradient
->mAngle
.IsAngleValue()) {
2050 angle
= aGradient
->mAngle
.GetAngleValueInRadians();
2051 if (!aGradient
->mLegacySyntax
) {
2052 angle
= M_PI_2
- angle
;
2055 angle
= -M_PI_2
; // defaults to vertical gradient starting from top
2057 gfxPoint
center(aBoxSize
.width
/2, aBoxSize
.height
/2);
2058 *aLineEnd
= ComputeGradientLineEndFromAngle(center
, angle
, aBoxSize
);
2059 *aLineStart
= gfxPoint(aBoxSize
.width
, aBoxSize
.height
) - *aLineEnd
;
2060 } else if (!aGradient
->mLegacySyntax
) {
2061 float xSign
= aGradient
->mBgPosX
.GetPercentValue() * 2 - 1;
2062 float ySign
= 1 - aGradient
->mBgPosY
.GetPercentValue() * 2;
2063 double angle
= atan2(ySign
* aBoxSize
.width
, xSign
* aBoxSize
.height
);
2064 gfxPoint
center(aBoxSize
.width
/2, aBoxSize
.height
/2);
2065 *aLineEnd
= ComputeGradientLineEndFromAngle(center
, angle
, aBoxSize
);
2066 *aLineStart
= gfxPoint(aBoxSize
.width
, aBoxSize
.height
) - *aLineEnd
;
2068 int32_t appUnitsPerPixel
= aPresContext
->AppUnitsPerDevPixel();
2069 *aLineStart
= gfxPoint(
2070 ConvertGradientValueToPixels(aGradient
->mBgPosX
, aBoxSize
.width
,
2072 ConvertGradientValueToPixels(aGradient
->mBgPosY
, aBoxSize
.height
,
2074 if (aGradient
->mAngle
.IsAngleValue()) {
2075 MOZ_ASSERT(aGradient
->mLegacySyntax
);
2076 double angle
= aGradient
->mAngle
.GetAngleValueInRadians();
2077 *aLineEnd
= ComputeGradientLineEndFromAngle(*aLineStart
, angle
, aBoxSize
);
2079 // No angle, the line end is just the reflection of the start point
2080 // through the center of the box
2081 *aLineEnd
= gfxPoint(aBoxSize
.width
, aBoxSize
.height
) - *aLineStart
;
2086 // Compute the start and end points of the gradient line for a radial gradient.
2087 // Also returns the horizontal and vertical radii defining the circle or
2090 ComputeRadialGradientLine(nsPresContext
* aPresContext
,
2091 nsStyleGradient
* aGradient
,
2092 const gfxSize
& aBoxSize
,
2093 gfxPoint
* aLineStart
,
2098 if (aGradient
->mBgPosX
.GetUnit() == eStyleUnit_None
) {
2099 // Default line start point is the center of the box
2100 *aLineStart
= gfxPoint(aBoxSize
.width
/2, aBoxSize
.height
/2);
2102 int32_t appUnitsPerPixel
= aPresContext
->AppUnitsPerDevPixel();
2103 *aLineStart
= gfxPoint(
2104 ConvertGradientValueToPixels(aGradient
->mBgPosX
, aBoxSize
.width
,
2106 ConvertGradientValueToPixels(aGradient
->mBgPosY
, aBoxSize
.height
,
2110 // Compute gradient shape: the x and y radii of an ellipse.
2111 double radiusX
, radiusY
;
2112 double leftDistance
= Abs(aLineStart
->x
);
2113 double rightDistance
= Abs(aBoxSize
.width
- aLineStart
->x
);
2114 double topDistance
= Abs(aLineStart
->y
);
2115 double bottomDistance
= Abs(aBoxSize
.height
- aLineStart
->y
);
2116 switch (aGradient
->mSize
) {
2117 case NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE
:
2118 radiusX
= std::min(leftDistance
, rightDistance
);
2119 radiusY
= std::min(topDistance
, bottomDistance
);
2120 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_CIRCULAR
) {
2121 radiusX
= radiusY
= std::min(radiusX
, radiusY
);
2124 case NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER
: {
2125 // Compute x and y distances to nearest corner
2126 double offsetX
= std::min(leftDistance
, rightDistance
);
2127 double offsetY
= std::min(topDistance
, bottomDistance
);
2128 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_CIRCULAR
) {
2129 radiusX
= radiusY
= NS_hypot(offsetX
, offsetY
);
2131 // maintain aspect ratio
2132 radiusX
= offsetX
*M_SQRT2
;
2133 radiusY
= offsetY
*M_SQRT2
;
2137 case NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE
:
2138 radiusX
= std::max(leftDistance
, rightDistance
);
2139 radiusY
= std::max(topDistance
, bottomDistance
);
2140 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_CIRCULAR
) {
2141 radiusX
= radiusY
= std::max(radiusX
, radiusY
);
2144 case NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER
: {
2145 // Compute x and y distances to nearest corner
2146 double offsetX
= std::max(leftDistance
, rightDistance
);
2147 double offsetY
= std::max(topDistance
, bottomDistance
);
2148 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_CIRCULAR
) {
2149 radiusX
= radiusY
= NS_hypot(offsetX
, offsetY
);
2151 // maintain aspect ratio
2152 radiusX
= offsetX
*M_SQRT2
;
2153 radiusY
= offsetY
*M_SQRT2
;
2157 case NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE
: {
2158 int32_t appUnitsPerPixel
= aPresContext
->AppUnitsPerDevPixel();
2159 radiusX
= ConvertGradientValueToPixels(aGradient
->mRadiusX
,
2160 aBoxSize
.width
, appUnitsPerPixel
);
2161 radiusY
= ConvertGradientValueToPixels(aGradient
->mRadiusY
,
2162 aBoxSize
.height
, appUnitsPerPixel
);
2166 radiusX
= radiusY
= 0;
2167 NS_ABORT_IF_FALSE(false, "unknown radial gradient sizing method");
2169 *aRadiusX
= radiusX
;
2170 *aRadiusY
= radiusY
;
2173 if (aGradient
->mAngle
.IsAngleValue()) {
2174 angle
= aGradient
->mAngle
.GetAngleValueInRadians();
2176 // Default angle is 0deg
2180 // The gradient line end point is where the gradient line intersects
2182 *aLineEnd
= *aLineStart
+ gfxPoint(radiusX
*cos(-angle
), radiusY
*sin(-angle
));
2186 static float Interpolate(float aF1
, float aF2
, float aFrac
)
2188 return aF1
+ aFrac
* (aF2
- aF1
);
2191 // Returns aFrac*aC2 + (1 - aFrac)*C1. The interpolation is done
2192 // in unpremultiplied space, which is what SVG gradients and cairo
2193 // gradients expect.
2195 InterpolateColor(const gfxRGBA
& aC1
, const gfxRGBA
& aC2
, double aFrac
)
2197 double other
= 1 - aFrac
;
2198 return gfxRGBA(aC2
.r
*aFrac
+ aC1
.r
*other
,
2199 aC2
.g
*aFrac
+ aC1
.g
*other
,
2200 aC2
.b
*aFrac
+ aC1
.b
*other
,
2201 aC2
.a
*aFrac
+ aC1
.a
*other
);
2205 FindTileStart(nscoord aDirtyCoord
, nscoord aTilePos
, nscoord aTileDim
)
2207 NS_ASSERTION(aTileDim
> 0, "Non-positive tile dimension");
2208 double multiples
= floor(double(aDirtyCoord
- aTilePos
)/aTileDim
);
2209 return NSToCoordRound(multiples
*aTileDim
+ aTilePos
);
2213 LinearGradientStopPositionForPoint(const gfxPoint
& aGradientStart
,
2214 const gfxPoint
& aGradientEnd
,
2215 const gfxPoint
& aPoint
)
2217 gfxPoint d
= aGradientEnd
- aGradientStart
;
2218 gfxPoint p
= aPoint
- aGradientStart
;
2220 * Compute a parameter t such that a line perpendicular to the
2221 * d vector, passing through aGradientStart + d*t, also
2222 * passes through aPoint.
2225 * (p.x - d.x*t)*d.x + (p.y - d.y*t)*d.y = 0
2227 * Solving for t we get
2228 * numerator = d.x*p.x + d.y*p.y
2229 * denominator = d.x^2 + d.y^2
2230 * t = numerator/denominator
2232 * In nsCSSRendering::PaintGradient we know the length of d
2235 double numerator
= d
.x
* p
.x
+ d
.y
* p
.y
;
2236 double denominator
= d
.x
* d
.x
+ d
.y
* d
.y
;
2237 return numerator
/ denominator
;
2241 RectIsBeyondLinearGradientEdge(const gfxRect
& aRect
,
2242 const gfxMatrix
& aPatternMatrix
,
2243 const nsTArray
<ColorStop
>& aStops
,
2244 const gfxPoint
& aGradientStart
,
2245 const gfxPoint
& aGradientEnd
,
2246 gfxRGBA
* aOutEdgeColor
)
2248 gfxFloat topLeft
= LinearGradientStopPositionForPoint(
2249 aGradientStart
, aGradientEnd
, aPatternMatrix
.Transform(aRect
.TopLeft()));
2250 gfxFloat topRight
= LinearGradientStopPositionForPoint(
2251 aGradientStart
, aGradientEnd
, aPatternMatrix
.Transform(aRect
.TopRight()));
2252 gfxFloat bottomLeft
= LinearGradientStopPositionForPoint(
2253 aGradientStart
, aGradientEnd
, aPatternMatrix
.Transform(aRect
.BottomLeft()));
2254 gfxFloat bottomRight
= LinearGradientStopPositionForPoint(
2255 aGradientStart
, aGradientEnd
, aPatternMatrix
.Transform(aRect
.BottomRight()));
2257 const ColorStop
& firstStop
= aStops
[0];
2258 if (topLeft
< firstStop
.mPosition
&& topRight
< firstStop
.mPosition
&&
2259 bottomLeft
< firstStop
.mPosition
&& bottomRight
< firstStop
.mPosition
) {
2260 *aOutEdgeColor
= firstStop
.mColor
;
2264 const ColorStop
& lastStop
= aStops
.LastElement();
2265 if (topLeft
>= lastStop
.mPosition
&& topRight
>= lastStop
.mPosition
&&
2266 bottomLeft
>= lastStop
.mPosition
&& bottomRight
>= lastStop
.mPosition
) {
2267 *aOutEdgeColor
= lastStop
.mColor
;
2274 static void ResolveMidpoints(nsTArray
<ColorStop
>& stops
)
2276 for (size_t x
= 1; x
< stops
.Length() - 1;) {
2277 if (!stops
[x
].mIsMidpoint
) {
2282 gfxRGBA color1
= stops
[x
-1].mColor
;
2283 gfxRGBA color2
= stops
[x
+1].mColor
;
2284 float offset1
= stops
[x
-1].mPosition
;
2285 float offset2
= stops
[x
+1].mPosition
;
2286 float offset
= stops
[x
].mPosition
;
2287 // check if everything coincides. If so, ignore the midpoint.
2288 if (offset
- offset1
== offset2
- offset
) {
2289 stops
.RemoveElementAt(x
);
2293 // Check if we coincide with the left colorstop.
2294 if (offset1
== offset
) {
2295 // Morph the midpoint to a regular stop with the color of the next
2297 stops
[x
].mColor
= color2
;
2298 stops
[x
].mIsMidpoint
= false;
2302 // Check if we coincide with the right colorstop.
2303 if (offset2
== offset
) {
2304 // Morph the midpoint to a regular stop with the color of the previous
2306 stops
[x
].mColor
= color1
;
2307 stops
[x
].mIsMidpoint
= false;
2311 float midpoint
= (offset
- offset1
) / (offset2
- offset1
);
2312 ColorStop newStops
[9];
2313 if (midpoint
> .5f
) {
2314 for (size_t y
= 0; y
< 7; y
++) {
2315 newStops
[y
].mPosition
= offset1
+ (offset
- offset1
) * (7 + y
) / 13;
2318 newStops
[7].mPosition
= offset
+ (offset2
- offset
) / 3;
2319 newStops
[8].mPosition
= offset
+ (offset2
- offset
) * 2 / 3;
2321 newStops
[0].mPosition
= offset1
+ (offset
- offset1
) / 3;
2322 newStops
[1].mPosition
= offset1
+ (offset
- offset1
) * 2 / 3;
2324 for (size_t y
= 0; y
< 7; y
++) {
2325 newStops
[y
+2].mPosition
= offset
+ (offset2
- offset
) * y
/ 13;
2330 for (size_t y
= 0; y
< 9; y
++) {
2331 // Calculate the intermediate color stops per the formula of the CSS images
2332 // spec. http://dev.w3.org/csswg/css-images/#color-stop-syntax
2333 // 9 points were chosen since it is the minimum number of stops that always
2334 // give the smoothest appearace regardless of midpoint position and difference
2335 // in luminance of the end points.
2336 float relativeOffset
= (newStops
[y
].mPosition
- offset1
) / (offset2
- offset1
);
2337 float multiplier
= powf(relativeOffset
, logf(.5f
) / logf(midpoint
));
2339 gfxFloat red
= color1
.r
+ multiplier
* (color2
.r
- color1
.r
);
2340 gfxFloat green
= color1
.g
+ multiplier
* (color2
.g
- color1
.g
);
2341 gfxFloat blue
= color1
.b
+ multiplier
* (color2
.b
- color1
.b
);
2342 gfxFloat alpha
= color1
.a
+ multiplier
* (color2
.a
- color1
.a
);
2344 newStops
[y
].mColor
= gfxRGBA(red
, green
, blue
, alpha
);
2347 stops
.ReplaceElementsAt(x
, 1, newStops
, 9);
2353 Premultiply(const gfxRGBA
& aColor
)
2355 gfxFloat a
= aColor
.a
;
2356 return gfxRGBA(aColor
.r
* a
, aColor
.g
* a
, aColor
.b
* a
, a
);
2360 Unpremultiply(const gfxRGBA
& aColor
)
2362 gfxFloat a
= aColor
.a
;
2363 return (a
> 0.0) ? gfxRGBA(aColor
.r
/ a
, aColor
.g
/ a
, aColor
.b
/ a
, a
) : aColor
;
2367 TransparentColor(gfxRGBA aColor
) {
2372 // Adjusts and adds color stops in such a way that drawing the gradient with
2373 // unpremultiplied interpolation looks nearly the same as if it were drawn with
2374 // premultiplied interpolation.
2375 static const float kAlphaIncrementPerGradientStep
= 0.1f
;
2377 ResolvePremultipliedAlpha(nsTArray
<ColorStop
>& aStops
)
2379 for (size_t x
= 1; x
< aStops
.Length(); x
++) {
2380 const ColorStop leftStop
= aStops
[x
- 1];
2381 const ColorStop rightStop
= aStops
[x
];
2383 // if the left and right stop have the same alpha value, we don't need
2385 if (leftStop
.mColor
.a
== rightStop
.mColor
.a
) {
2389 // Is the stop on the left 100% transparent? If so, have it adopt the color
2390 // of the right stop
2391 if (leftStop
.mColor
.a
== 0) {
2392 aStops
[x
- 1].mColor
= TransparentColor(rightStop
.mColor
);
2396 // Is the stop on the right completely transparent?
2397 // If so, duplicate it and assign it the color on the left.
2398 if (rightStop
.mColor
.a
== 0) {
2399 ColorStop newStop
= rightStop
;
2400 newStop
.mColor
= TransparentColor(leftStop
.mColor
);
2401 aStops
.InsertElementAt(x
, newStop
);
2406 // Now handle cases where one or both of the stops are partially transparent.
2407 if (leftStop
.mColor
.a
!= 1.0f
|| rightStop
.mColor
.a
!= 1.0f
) {
2408 gfxRGBA premulLeftColor
= Premultiply(leftStop
.mColor
);
2409 gfxRGBA premulRightColor
= Premultiply(rightStop
.mColor
);
2410 // Calculate how many extra steps. We do a step per 10% transparency.
2411 size_t stepCount
= NSToIntFloor(fabs(leftStop
.mColor
.a
- rightStop
.mColor
.a
) / kAlphaIncrementPerGradientStep
);
2412 for (size_t y
= 1; y
< stepCount
; y
++) {
2413 float frac
= static_cast<float>(y
) / stepCount
;
2414 ColorStop
newStop(Interpolate(leftStop
.mPosition
, rightStop
.mPosition
, frac
), false,
2415 Unpremultiply(InterpolateColor(premulLeftColor
, premulRightColor
, frac
)));
2416 aStops
.InsertElementAt(x
, newStop
);
2424 nsCSSRendering::PaintGradient(nsPresContext
* aPresContext
,
2425 nsRenderingContext
& aRenderingContext
,
2426 nsStyleGradient
* aGradient
,
2427 const nsRect
& aDirtyRect
,
2428 const nsRect
& aDest
,
2429 const nsRect
& aFillArea
,
2430 const CSSIntRect
& aSrc
,
2431 const nsSize
& aIntrinsicSize
)
2433 PROFILER_LABEL("nsCSSRendering", "PaintGradient",
2434 js::ProfileEntry::Category::GRAPHICS
);
2436 Telemetry::AutoTimer
<Telemetry::GRADIENT_DURATION
, Telemetry::Microsecond
> gradientTimer
;
2437 if (aDest
.IsEmpty() || aFillArea
.IsEmpty()) {
2441 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
2442 nscoord appUnitsPerDevPixel
= aPresContext
->AppUnitsPerDevPixel();
2443 gfxSize srcSize
= gfxSize(gfxFloat(aIntrinsicSize
.width
)/appUnitsPerDevPixel
,
2444 gfxFloat(aIntrinsicSize
.height
)/appUnitsPerDevPixel
);
2446 bool cellContainsFill
= aDest
.Contains(aFillArea
);
2448 // Compute "gradient line" start and end relative to the intrinsic size of
2450 gfxPoint lineStart
, lineEnd
;
2451 double radiusX
= 0, radiusY
= 0; // for radial gradients only
2452 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_LINEAR
) {
2453 ComputeLinearGradientLine(aPresContext
, aGradient
, srcSize
,
2454 &lineStart
, &lineEnd
);
2456 ComputeRadialGradientLine(aPresContext
, aGradient
, srcSize
,
2457 &lineStart
, &lineEnd
, &radiusX
, &radiusY
);
2459 gfxFloat lineLength
= NS_hypot(lineEnd
.x
- lineStart
.x
,
2460 lineEnd
.y
- lineStart
.y
);
2462 NS_ABORT_IF_FALSE(aGradient
->mStops
.Length() >= 2,
2463 "The parser should reject gradients with less than two stops");
2465 // Build color stop array and compute stop positions
2466 nsTArray
<ColorStop
> stops
;
2467 // If there is a run of stops before stop i that did not have specified
2468 // positions, then this is the index of the first stop in that run, otherwise
2470 int32_t firstUnsetPosition
= -1;
2471 for (uint32_t i
= 0; i
< aGradient
->mStops
.Length(); ++i
) {
2472 const nsStyleGradientStop
& stop
= aGradient
->mStops
[i
];
2474 switch (stop
.mLocation
.GetUnit()) {
2475 case eStyleUnit_None
:
2477 // First stop defaults to position 0.0
2479 } else if (i
== aGradient
->mStops
.Length() - 1) {
2480 // Last stop defaults to position 1.0
2483 // Other stops with no specified position get their position assigned
2484 // later by interpolation, see below.
2485 // Remeber where the run of stops with no specified position starts,
2486 // if it starts here.
2487 if (firstUnsetPosition
< 0) {
2488 firstUnsetPosition
= i
;
2490 stops
.AppendElement(ColorStop(0, stop
.mIsInterpolationHint
, stop
.mColor
));
2494 case eStyleUnit_Percent
:
2495 position
= stop
.mLocation
.GetPercentValue();
2497 case eStyleUnit_Coord
:
2498 position
= lineLength
< 1e-6 ? 0.0 :
2499 stop
.mLocation
.GetCoordValue() / appUnitsPerDevPixel
/ lineLength
;
2501 case eStyleUnit_Calc
:
2502 nsStyleCoord::Calc
*calc
;
2503 calc
= stop
.mLocation
.GetCalcValue();
2504 position
= calc
->mPercent
+
2505 ((lineLength
< 1e-6) ? 0.0 :
2506 (NSAppUnitsToFloatPixels(calc
->mLength
, appUnitsPerDevPixel
) / lineLength
));
2509 NS_ABORT_IF_FALSE(false, "Unknown stop position type");
2513 // Prevent decreasing stop positions by advancing this position
2514 // to the previous stop position, if necessary
2515 position
= std::max(position
, stops
[i
- 1].mPosition
);
2517 stops
.AppendElement(ColorStop(position
, stop
.mIsInterpolationHint
, stop
.mColor
));
2518 if (firstUnsetPosition
> 0) {
2519 // Interpolate positions for all stops that didn't have a specified position
2520 double p
= stops
[firstUnsetPosition
- 1].mPosition
;
2521 double d
= (stops
[i
].mPosition
- p
)/(i
- firstUnsetPosition
+ 1);
2522 for (uint32_t j
= firstUnsetPosition
; j
< i
; ++j
) {
2524 stops
[j
].mPosition
= p
;
2526 firstUnsetPosition
= -1;
2530 // Eliminate negative-position stops if the gradient is radial.
2531 double firstStop
= stops
[0].mPosition
;
2532 if (aGradient
->mShape
!= NS_STYLE_GRADIENT_SHAPE_LINEAR
&& firstStop
< 0.0) {
2533 if (aGradient
->mRepeating
) {
2534 // Choose an instance of the repeated pattern that gives us all positive
2536 double lastStop
= stops
[stops
.Length() - 1].mPosition
;
2537 double stopDelta
= lastStop
- firstStop
;
2538 // If all the stops are in approximately the same place then logic below
2539 // will kick in that makes us draw just the last stop color, so don't
2540 // try to do anything in that case. We certainly need to avoid
2541 // dividing by zero.
2542 if (stopDelta
>= 1e-6) {
2543 double instanceCount
= ceil(-firstStop
/stopDelta
);
2544 // Advance stops by instanceCount multiples of the period of the
2545 // repeating gradient.
2546 double offset
= instanceCount
*stopDelta
;
2547 for (uint32_t i
= 0; i
< stops
.Length(); i
++) {
2548 stops
[i
].mPosition
+= offset
;
2552 // Move negative-position stops to position 0.0. We may also need
2553 // to set the color of the stop to the color the gradient should have
2554 // at the center of the ellipse.
2555 for (uint32_t i
= 0; i
< stops
.Length(); i
++) {
2556 double pos
= stops
[i
].mPosition
;
2558 stops
[i
].mPosition
= 0.0;
2559 // If this is the last stop, we don't need to adjust the color,
2560 // it will fill the entire area.
2561 if (i
< stops
.Length() - 1) {
2562 double nextPos
= stops
[i
+ 1].mPosition
;
2563 // If nextPos is approximately equal to pos, then we don't
2564 // need to adjust the color of this stop because it's
2565 // not going to be displayed.
2566 // If nextPos is negative, we don't need to adjust the color of
2567 // this stop since it's not going to be displayed because
2568 // nextPos will also be moved to 0.0.
2569 if (nextPos
>= 0.0 && nextPos
- pos
>= 1e-6) {
2570 // Compute how far the new position 0.0 is along the interval
2571 // between pos and nextPos.
2572 // XXX Color interpolation (in cairo, too) should use the
2573 // CSS 'color-interpolation' property!
2574 double frac
= (0.0 - pos
)/(nextPos
- pos
);
2576 InterpolateColor(stops
[i
].mColor
, stops
[i
+ 1].mColor
, frac
);
2582 firstStop
= stops
[0].mPosition
;
2583 NS_ABORT_IF_FALSE(firstStop
>= 0.0, "Failed to fix stop offsets");
2586 if (aGradient
->mShape
!= NS_STYLE_GRADIENT_SHAPE_LINEAR
&& !aGradient
->mRepeating
) {
2587 // Direct2D can only handle a particular class of radial gradients because
2588 // of the way the it specifies gradients. Setting firstStop to 0, when we
2589 // can, will help us stay on the fast path. Currently we don't do this
2590 // for repeating gradients but we could by adjusting the stop collection
2595 double lastStop
= stops
[stops
.Length() - 1].mPosition
;
2596 // Cairo gradients must have stop positions in the range [0, 1]. So,
2597 // stop positions will be normalized below by subtracting firstStop and then
2598 // multiplying by stopScale.
2600 double stopOrigin
= firstStop
;
2601 double stopEnd
= lastStop
;
2602 double stopDelta
= lastStop
- firstStop
;
2603 bool zeroRadius
= aGradient
->mShape
!= NS_STYLE_GRADIENT_SHAPE_LINEAR
&&
2604 (radiusX
< 1e-6 || radiusY
< 1e-6);
2605 if (stopDelta
< 1e-6 || lineLength
< 1e-6 || zeroRadius
) {
2606 // Stops are all at the same place. Map all stops to 0.0.
2607 // For repeating radial gradients, or for any radial gradients with
2608 // a zero radius, we need to fill with the last stop color, so just set
2610 if (aGradient
->mRepeating
|| zeroRadius
) {
2611 radiusX
= radiusY
= 0.0;
2614 lastStop
= firstStop
;
2617 // Don't normalize non-repeating or degenerate gradients below 0..1
2618 // This keeps the gradient line as large as the box and doesn't
2619 // lets us avoiding having to get padding correct for stops
2621 if (!aGradient
->mRepeating
|| stopDelta
== 0.0) {
2622 stopOrigin
= std::min(stopOrigin
, 0.0);
2623 stopEnd
= std::max(stopEnd
, 1.0);
2625 stopScale
= 1.0/(stopEnd
- stopOrigin
);
2627 // Create the gradient pattern.
2628 nsRefPtr
<gfxPattern
> gradientPattern
;
2629 bool forceRepeatToCoverTiles
= false;
2631 gfxPoint gradientStart
;
2632 gfxPoint gradientEnd
;
2633 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_LINEAR
) {
2634 // Compute the actual gradient line ends we need to pass to cairo after
2635 // stops have been normalized.
2636 gradientStart
= lineStart
+ (lineEnd
- lineStart
)*stopOrigin
;
2637 gradientEnd
= lineStart
+ (lineEnd
- lineStart
)*stopEnd
;
2638 gfxPoint gradientStopStart
= lineStart
+ (lineEnd
- lineStart
)*firstStop
;
2639 gfxPoint gradientStopEnd
= lineStart
+ (lineEnd
- lineStart
)*lastStop
;
2641 if (stopDelta
== 0.0) {
2642 // Stops are all at the same place. For repeating gradients, this will
2643 // just paint the last stop color. We don't need to do anything.
2644 // For non-repeating gradients, this should render as two colors, one
2645 // on each "side" of the gradient line segment, which is a point. All
2646 // our stops will be at 0.0; we just need to set the direction vector
2648 gradientEnd
= gradientStart
+ (lineEnd
- lineStart
);
2649 gradientStopEnd
= gradientStopStart
+ (lineEnd
- lineStart
);
2652 gradientPattern
= new gfxPattern(gradientStart
.x
, gradientStart
.y
,
2653 gradientEnd
.x
, gradientEnd
.y
);
2655 // When the gradient line is parallel to the x axis from the left edge
2656 // to the right edge of a tile, then we can repeat by just repeating the
2658 if (!cellContainsFill
&&
2659 ((gradientStopStart
.y
== gradientStopEnd
.y
&& gradientStopStart
.x
== 0 &&
2660 gradientStopEnd
.x
== srcSize
.width
) ||
2661 (gradientStopStart
.x
== gradientStopEnd
.x
&& gradientStopStart
.y
== 0 &&
2662 gradientStopEnd
.y
== srcSize
.height
))) {
2663 forceRepeatToCoverTiles
= true;
2666 NS_ASSERTION(firstStop
>= 0.0,
2667 "Negative stops not allowed for radial gradients");
2669 // To form an ellipse, we'll stretch a circle vertically, if necessary.
2670 // So our radii are based on radiusX.
2671 double innerRadius
= radiusX
*stopOrigin
;
2672 double outerRadius
= radiusX
*stopEnd
;
2673 if (stopDelta
== 0.0) {
2674 // Stops are all at the same place. See above (except we now have
2675 // the inside vs. outside of an ellipse).
2676 outerRadius
= innerRadius
+ 1;
2678 gradientPattern
= new gfxPattern(lineStart
.x
, lineStart
.y
, innerRadius
,
2679 lineStart
.x
, lineStart
.y
, outerRadius
);
2680 if (radiusX
!= radiusY
) {
2681 // Stretch the circles into ellipses vertically by setting a transform
2683 // Recall that this is the transform from user space to pattern space.
2684 // So to stretch the ellipse by factor of P vertically, we scale
2685 // user coordinates by 1/P.
2686 matrix
.Translate(lineStart
);
2687 matrix
.Scale(1.0, radiusX
/radiusY
);
2688 matrix
.Translate(-lineStart
);
2691 // Use a pattern transform to take account of source and dest rects
2692 matrix
.Translate(gfxPoint(aPresContext
->CSSPixelsToDevPixels(aSrc
.x
),
2693 aPresContext
->CSSPixelsToDevPixels(aSrc
.y
)));
2694 matrix
.Scale(gfxFloat(aPresContext
->CSSPixelsToAppUnits(aSrc
.width
))/aDest
.width
,
2695 gfxFloat(aPresContext
->CSSPixelsToAppUnits(aSrc
.height
))/aDest
.height
);
2696 gradientPattern
->SetMatrix(matrix
);
2698 if (gradientPattern
->CairoStatus())
2701 if (stopDelta
== 0.0) {
2702 // Non-repeating gradient with all stops in same place -> just add
2703 // first stop and last stop, both at position 0.
2704 // Repeating gradient with all stops in the same place, or radial
2705 // gradient with radius of 0 -> just paint the last stop color.
2706 // We use firstStop offset to keep |stops| with same units (will later normalize to 0).
2707 gfxRGBA
firstColor(stops
[0].mColor
);
2708 gfxRGBA
lastColor(stops
.LastElement().mColor
);
2711 if (!aGradient
->mRepeating
&& !zeroRadius
) {
2712 stops
.AppendElement(ColorStop(firstStop
, false, firstColor
));
2714 stops
.AppendElement(ColorStop(firstStop
, false, lastColor
));
2717 ResolveMidpoints(stops
);
2718 ResolvePremultipliedAlpha(stops
);
2720 bool isRepeat
= aGradient
->mRepeating
|| forceRepeatToCoverTiles
;
2722 // Now set normalized color stops in pattern.
2723 // Offscreen gradient surface cache (not a tile):
2724 // On some backends (e.g. D2D), the GradientStops object holds an offscreen surface
2725 // which is a lookup table used to evaluate the gradient. This surface can use
2726 // much memory (ram and/or GPU ram) and can be expensive to create. So we cache it.
2727 // The cache key correlates 1:1 with the arguments for CreateGradientStops (also the implied backend type)
2728 // Note that GradientStop is a simple struct with a stop value (while GradientStops has the surface).
2729 nsTArray
<gfx::GradientStop
> rawStops(stops
.Length());
2730 rawStops
.SetLength(stops
.Length());
2731 for(uint32_t i
= 0; i
< stops
.Length(); i
++) {
2732 rawStops
[i
].color
= gfx::Color(stops
[i
].mColor
.r
, stops
[i
].mColor
.g
, stops
[i
].mColor
.b
, stops
[i
].mColor
.a
);
2733 rawStops
[i
].offset
= stopScale
* (stops
[i
].mPosition
- stopOrigin
);
2735 mozilla::RefPtr
<mozilla::gfx::GradientStops
> gs
=
2736 gfxGradientCache::GetOrCreateGradientStops(ctx
->GetDrawTarget(),
2738 isRepeat
? gfx::ExtendMode::REPEAT
: gfx::ExtendMode::CLAMP
);
2739 gradientPattern
->SetColorStops(gs
);
2741 // Paint gradient tiles. This isn't terribly efficient, but doing it this
2742 // way is simple and sure to get pixel-snapping right. We could speed things
2743 // up by drawing tiles into temporary surfaces and copying those to the
2744 // destination, but after pixel-snapping tiles may not all be the same size.
2746 if (!dirty
.IntersectRect(aDirtyRect
, aFillArea
))
2749 gfxRect areaToFill
=
2750 nsLayoutUtils::RectToGfxRect(aFillArea
, appUnitsPerDevPixel
);
2751 gfxRect dirtyAreaToFill
= nsLayoutUtils::RectToGfxRect(dirty
, appUnitsPerDevPixel
);
2752 dirtyAreaToFill
.RoundOut();
2754 gfxMatrix ctm
= ctx
->CurrentMatrix();
2755 bool isCTMPreservingAxisAlignedRectangles
= ctm
.PreservesAxisAlignedRectangles();
2757 // xStart/yStart are the top-left corner of the top-left tile.
2758 nscoord xStart
= FindTileStart(dirty
.x
, aDest
.x
, aDest
.width
);
2759 nscoord yStart
= FindTileStart(dirty
.y
, aDest
.y
, aDest
.height
);
2760 nscoord xEnd
= forceRepeatToCoverTiles
? xStart
+ aDest
.width
: dirty
.XMost();
2761 nscoord yEnd
= forceRepeatToCoverTiles
? yStart
+ aDest
.height
: dirty
.YMost();
2763 // x and y are the top-left corner of the tile to draw
2764 for (nscoord y
= yStart
; y
< yEnd
; y
+= aDest
.height
) {
2765 for (nscoord x
= xStart
; x
< xEnd
; x
+= aDest
.width
) {
2766 // The coordinates of the tile
2767 gfxRect tileRect
= nsLayoutUtils::RectToGfxRect(
2768 nsRect(x
, y
, aDest
.width
, aDest
.height
),
2769 appUnitsPerDevPixel
);
2770 // The actual area to fill with this tile is the intersection of this
2771 // tile with the overall area we're supposed to be filling
2773 forceRepeatToCoverTiles
? areaToFill
: tileRect
.Intersect(areaToFill
);
2774 // Try snapping the fill rect. Snap its top-left and bottom-right
2775 // independently to preserve the orientation.
2776 gfxPoint snappedFillRectTopLeft
= fillRect
.TopLeft();
2777 gfxPoint snappedFillRectTopRight
= fillRect
.TopRight();
2778 gfxPoint snappedFillRectBottomRight
= fillRect
.BottomRight();
2779 // Snap three points instead of just two to ensure we choose the
2780 // correct orientation if there's a reflection.
2781 if (isCTMPreservingAxisAlignedRectangles
&&
2782 ctx
->UserToDevicePixelSnapped(snappedFillRectTopLeft
, true) &&
2783 ctx
->UserToDevicePixelSnapped(snappedFillRectBottomRight
, true) &&
2784 ctx
->UserToDevicePixelSnapped(snappedFillRectTopRight
, true)) {
2785 if (snappedFillRectTopLeft
.x
== snappedFillRectBottomRight
.x
||
2786 snappedFillRectTopLeft
.y
== snappedFillRectBottomRight
.y
) {
2787 // Nothing to draw; avoid scaling by zero and other weirdness that
2788 // could put the context in an error state.
2791 // Set the context's transform to the transform that maps fillRect to
2792 // snappedFillRect. The part of the gradient that was going to
2793 // exactly fill fillRect will fill snappedFillRect instead.
2794 gfxMatrix transform
= gfxUtils::TransformRectToRect(fillRect
,
2795 snappedFillRectTopLeft
, snappedFillRectTopRight
,
2796 snappedFillRectBottomRight
);
2797 ctx
->SetMatrix(transform
);
2800 ctx
->Rectangle(fillRect
);
2802 gfxRect dirtyFillRect
= fillRect
.Intersect(dirtyAreaToFill
);
2803 gfxRect fillRectRelativeToTile
= dirtyFillRect
- tileRect
.TopLeft();
2805 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_LINEAR
&& !isRepeat
&&
2806 RectIsBeyondLinearGradientEdge(fillRectRelativeToTile
, matrix
, stops
,
2807 gradientStart
, gradientEnd
, &edgeColor
)) {
2808 ctx
->SetColor(edgeColor
);
2811 ctx
->CurrentMatrix().Copy().Translate(tileRect
.TopLeft()));
2812 ctx
->SetPattern(gradientPattern
);
2815 ctx
->SetMatrix(ctm
);
2821 nsCSSRendering::PaintBackgroundWithSC(nsPresContext
* aPresContext
,
2822 nsRenderingContext
& aRenderingContext
,
2823 nsIFrame
* aForFrame
,
2824 const nsRect
& aDirtyRect
,
2825 const nsRect
& aBorderArea
,
2826 nsStyleContext
* aBackgroundSC
,
2827 const nsStyleBorder
& aBorder
,
2829 nsRect
* aBGClipRect
,
2832 NS_PRECONDITION(aForFrame
,
2833 "Frame is expected to be provided to PaintBackground");
2835 // Initialize our result to success. We update it only if its value is
2836 // currently DrawResult::SUCCESS, which means that as soon as we hit our first
2837 // non-successful draw, we stop updating and will return that value.
2838 DrawResult result
= DrawResult::SUCCESS
;
2840 // Check to see if we have an appearance defined. If so, we let the theme
2841 // renderer draw the background and bail out.
2842 // XXXzw this ignores aBGClipRect.
2843 const nsStyleDisplay
* displayData
= aForFrame
->StyleDisplay();
2844 if (displayData
->mAppearance
) {
2845 nsITheme
*theme
= aPresContext
->GetTheme();
2846 if (theme
&& theme
->ThemeSupportsWidget(aPresContext
, aForFrame
,
2847 displayData
->mAppearance
)) {
2848 nsRect
drawing(aBorderArea
);
2849 theme
->GetWidgetOverflow(aPresContext
->DeviceContext(),
2850 aForFrame
, displayData
->mAppearance
, &drawing
);
2851 drawing
.IntersectRect(drawing
, aDirtyRect
);
2852 theme
->DrawWidgetBackground(&aRenderingContext
, aForFrame
,
2853 displayData
->mAppearance
, aBorderArea
,
2855 return DrawResult::SUCCESS
;
2859 // For canvas frames (in the CSS sense) we draw the background color using
2860 // a solid color item that gets added in nsLayoutUtils::PaintFrame,
2861 // or nsSubDocumentFrame::BuildDisplayList (bug 488242). (The solid
2862 // color may be moved into nsDisplayCanvasBackground by
2863 // nsPresShell::AddCanvasBackgroundColorItem, and painted by
2864 // nsDisplayCanvasBackground directly.) Either way we don't need to
2865 // paint the background color here.
2866 bool isCanvasFrame
= IsCanvasFrame(aForFrame
);
2868 // Determine whether we are drawing background images and/or
2869 // background colors.
2870 bool drawBackgroundImage
;
2871 bool drawBackgroundColor
;
2873 nscolor bgColor
= DetermineBackgroundColor(aPresContext
,
2876 drawBackgroundImage
,
2877 drawBackgroundColor
);
2879 // If we're drawing a specific layer, we don't want to draw the
2880 // background color.
2881 const nsStyleBackground
*bg
= aBackgroundSC
->StyleBackground();
2882 if (drawBackgroundColor
&& aLayer
>= 0) {
2883 drawBackgroundColor
= false;
2886 // At this point, drawBackgroundImage and drawBackgroundColor are
2887 // true if and only if we are actually supposed to paint an image or
2888 // color into aDirtyRect, respectively.
2889 if (!drawBackgroundImage
&& !drawBackgroundColor
)
2890 return DrawResult::SUCCESS
;
2892 // Compute the outermost boundary of the area that might be painted.
2893 // Same coordinate space as aBorderArea & aBGClipRect.
2894 Sides skipSides
= aForFrame
->GetSkipSides();
2895 nsRect paintBorderArea
=
2896 ::BoxDecorationRectForBackground(aForFrame
, aBorderArea
, skipSides
, &aBorder
);
2897 nsRect clipBorderArea
=
2898 ::BoxDecorationRectForBorder(aForFrame
, aBorderArea
, skipSides
, &aBorder
);
2900 // The 'bgClipArea' (used only by the image tiling logic, far below)
2901 // is the caller-provided aBGClipRect if any, or else the area
2902 // determined by the value of 'background-clip' in
2903 // SetupCurrentBackgroundClip. (Arguably it should be the
2904 // intersection, but that breaks the table painter -- in particular,
2905 // taking the intersection breaks reftests/bugs/403249-1[ab].)
2906 gfxContext
* ctx
= aRenderingContext
.ThebesContext();
2907 nscoord appUnitsPerPixel
= aPresContext
->AppUnitsPerDevPixel();
2908 BackgroundClipState clipState
;
2910 clipState
.mBGClipArea
= *aBGClipRect
;
2911 clipState
.mCustomClip
= true;
2912 clipState
.mHasRoundedCorners
= false;
2913 SetupDirtyRects(clipState
.mBGClipArea
, aDirtyRect
, appUnitsPerPixel
,
2914 &clipState
.mDirtyRect
, &clipState
.mDirtyRectGfx
);
2916 GetBackgroundClip(bg
->BottomLayer(),
2917 aForFrame
, aBorder
, aBorderArea
,
2918 aDirtyRect
, (aFlags
& PAINTBG_WILL_PAINT_BORDER
), appUnitsPerPixel
,
2922 // If we might be using a background color, go ahead and set it now.
2923 if (drawBackgroundColor
&& !isCanvasFrame
)
2924 ctx
->SetColor(gfxRGBA(bgColor
));
2926 // NOTE: no Save() yet, we do that later by calling autoSR.EnsureSaved(ctx)
2927 // in the cases we need it.
2928 gfxContextAutoSaveRestore autoSR
;
2930 // If there is no background image, draw a color. (If there is
2931 // neither a background image nor a color, we wouldn't have gotten
2933 if (!drawBackgroundImage
) {
2934 if (!isCanvasFrame
) {
2935 DrawBackgroundColor(clipState
, ctx
, appUnitsPerPixel
);
2937 return DrawResult::SUCCESS
;
2940 if (bg
->mImageCount
< 1) {
2941 // Return if there are no background layers, all work from this point
2942 // onwards happens iteratively on these.
2943 return DrawResult::SUCCESS
;
2946 // Validate the layer range before we start iterating.
2947 int32_t startLayer
= aLayer
;
2948 int32_t nLayers
= 1;
2949 if (startLayer
< 0) {
2950 startLayer
= (int32_t)bg
->mImageCount
- 1;
2951 nLayers
= bg
->mImageCount
;
2954 // Ensure we get invalidated for loads of the image. We need to do
2955 // this here because this might be the only code that knows about the
2956 // association of the style data with the frame.
2957 if (aBackgroundSC
!= aForFrame
->StyleContext()) {
2958 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT_WITH_RANGE(i
, bg
, startLayer
, nLayers
) {
2959 aForFrame
->AssociateImage(bg
->mLayers
[i
].mImage
, aPresContext
);
2963 // The background color is rendered over the entire dirty area,
2964 // even if the image isn't.
2965 if (drawBackgroundColor
&& !isCanvasFrame
) {
2966 DrawBackgroundColor(clipState
, ctx
, appUnitsPerPixel
);
2969 if (drawBackgroundImage
) {
2970 bool clipSet
= false;
2971 uint8_t currentBackgroundClip
= NS_STYLE_BG_CLIP_BORDER
;
2972 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT_WITH_RANGE(i
, bg
, bg
->mImageCount
- 1,
2973 nLayers
+ (bg
->mImageCount
-
2975 const nsStyleBackground::Layer
&layer
= bg
->mLayers
[i
];
2977 if (currentBackgroundClip
!= layer
.mClip
|| !clipSet
) {
2978 currentBackgroundClip
= layer
.mClip
;
2979 // If clipSet is false that means this is the bottom layer and we
2980 // already called GetBackgroundClip above and it stored its results
2983 autoSR
.Restore(); // reset the previous one
2984 GetBackgroundClip(layer
, aForFrame
,
2985 aBorder
, aBorderArea
, aDirtyRect
, (aFlags
& PAINTBG_WILL_PAINT_BORDER
),
2986 appUnitsPerPixel
, &clipState
);
2988 SetupBackgroundClip(clipState
, ctx
, appUnitsPerPixel
, &autoSR
);
2990 if (!clipBorderArea
.IsEqualEdges(aBorderArea
)) {
2991 // We're drawing the background for the joined continuation boxes
2992 // so we need to clip that to the slice that we want for this frame.
2994 nsLayoutUtils::RectToGfxRect(aBorderArea
, appUnitsPerPixel
);
2995 autoSR
.EnsureSaved(ctx
);
2997 ctx
->SnappedRectangle(clip
);
3002 if ((aLayer
< 0 || i
== (uint32_t)startLayer
) &&
3003 !clipState
.mDirtyRectGfx
.IsEmpty()) {
3004 nsBackgroundLayerState state
= PrepareBackgroundLayer(aPresContext
, aForFrame
,
3005 aFlags
, paintBorderArea
, clipState
.mBGClipArea
, layer
);
3006 if (!state
.mFillArea
.IsEmpty()) {
3007 if (state
.mCompositingOp
!= gfxContext::OPERATOR_OVER
) {
3008 NS_ASSERTION(ctx
->CurrentOperator() == gfxContext::OPERATOR_OVER
,
3009 "It is assumed the initial operator is OPERATOR_OVER, when it is restored later");
3010 ctx
->SetOperator(state
.mCompositingOp
);
3013 DrawResult resultForLayer
=
3014 state
.mImageRenderer
.DrawBackground(aPresContext
, aRenderingContext
,
3015 state
.mDestArea
, state
.mFillArea
,
3016 state
.mAnchor
+ paintBorderArea
.TopLeft(),
3017 clipState
.mDirtyRect
);
3019 if (result
== DrawResult::SUCCESS
) {
3020 result
= resultForLayer
;
3023 if (state
.mCompositingOp
!= gfxContext::OPERATOR_OVER
) {
3024 ctx
->SetOperator(gfxContext::OPERATOR_OVER
);
3035 IsTransformed(nsIFrame
* aForFrame
, nsIFrame
* aTopFrame
)
3037 for (nsIFrame
* f
= aForFrame
; f
!= aTopFrame
; f
= f
->GetParent()) {
3038 if (f
->IsTransformed()) {
3046 nsCSSRendering::ComputeBackgroundPositioningArea(nsPresContext
* aPresContext
,
3047 nsIFrame
* aForFrame
,
3048 const nsRect
& aBorderArea
,
3049 const nsStyleBackground::Layer
& aLayer
,
3050 nsIFrame
** aAttachedToFrame
)
3052 // Compute background origin area relative to aBorderArea now as we may need
3053 // it to compute the effective image size for a CSS gradient.
3054 nsRect bgPositioningArea
;
3056 nsIAtom
* frameType
= aForFrame
->GetType();
3057 nsIFrame
* geometryFrame
= aForFrame
;
3058 if (MOZ_UNLIKELY(frameType
== nsGkAtoms::scrollFrame
&&
3059 NS_STYLE_BG_ATTACHMENT_LOCAL
== aLayer
.mAttachment
)) {
3060 nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(aForFrame
);
3061 bgPositioningArea
= nsRect(
3062 scrollableFrame
->GetScrolledFrame()->GetPosition()
3063 // For the dir=rtl case:
3064 + scrollableFrame
->GetScrollRange().TopLeft(),
3065 scrollableFrame
->GetScrolledRect().Size());
3066 // The ScrolledRect’s size does not include the borders or scrollbars,
3067 // reverse the handling of background-origin
3068 // compared to the common case below.
3069 if (aLayer
.mOrigin
== NS_STYLE_BG_ORIGIN_BORDER
) {
3070 nsMargin border
= geometryFrame
->GetUsedBorder();
3071 border
.ApplySkipSides(geometryFrame
->GetSkipSides());
3072 bgPositioningArea
.Inflate(border
);
3073 bgPositioningArea
.Inflate(scrollableFrame
->GetActualScrollbarSizes());
3074 } else if (aLayer
.mOrigin
!= NS_STYLE_BG_ORIGIN_PADDING
) {
3075 nsMargin padding
= geometryFrame
->GetUsedPadding();
3076 padding
.ApplySkipSides(geometryFrame
->GetSkipSides());
3077 bgPositioningArea
.Deflate(padding
);
3078 NS_ASSERTION(aLayer
.mOrigin
== NS_STYLE_BG_ORIGIN_CONTENT
,
3079 "unknown background-origin value");
3081 *aAttachedToFrame
= aForFrame
;
3082 return bgPositioningArea
;
3085 if (MOZ_UNLIKELY(frameType
== nsGkAtoms::canvasFrame
)) {
3086 geometryFrame
= aForFrame
->GetFirstPrincipalChild();
3087 // geometryFrame might be null if this canvas is a page created
3088 // as an overflow container (e.g. the in-flow content has already
3089 // finished and this page only displays the continuations of
3090 // absolutely positioned content).
3091 if (geometryFrame
) {
3092 bgPositioningArea
= geometryFrame
->GetRect();
3095 bgPositioningArea
= nsRect(nsPoint(0,0), aBorderArea
.Size());
3098 // Background images are tiled over the 'background-clip' area
3099 // but the origin of the tiling is based on the 'background-origin' area
3100 if (aLayer
.mOrigin
!= NS_STYLE_BG_ORIGIN_BORDER
&& geometryFrame
) {
3101 nsMargin border
= geometryFrame
->GetUsedBorder();
3102 if (aLayer
.mOrigin
!= NS_STYLE_BG_ORIGIN_PADDING
) {
3103 border
+= geometryFrame
->GetUsedPadding();
3104 NS_ASSERTION(aLayer
.mOrigin
== NS_STYLE_BG_ORIGIN_CONTENT
,
3105 "unknown background-origin value");
3107 bgPositioningArea
.Deflate(border
);
3110 nsIFrame
* attachedToFrame
= aForFrame
;
3111 if (NS_STYLE_BG_ATTACHMENT_FIXED
== aLayer
.mAttachment
) {
3112 // If it's a fixed background attachment, then the image is placed
3113 // relative to the viewport, which is the area of the root frame
3114 // in a screen context or the page content frame in a print context.
3115 attachedToFrame
= aPresContext
->PresShell()->FrameManager()->GetRootFrame();
3116 NS_ASSERTION(attachedToFrame
, "no root frame");
3117 nsIFrame
* pageContentFrame
= nullptr;
3118 if (aPresContext
->IsPaginated()) {
3120 nsLayoutUtils::GetClosestFrameOfType(aForFrame
, nsGkAtoms::pageContentFrame
);
3121 if (pageContentFrame
) {
3122 attachedToFrame
= pageContentFrame
;
3124 // else this is an embedded shell and its root frame is what we want
3127 // Set the background positioning area to the viewport's area
3128 // (relative to aForFrame)
3130 nsRect(-aForFrame
->GetOffsetTo(attachedToFrame
), attachedToFrame
->GetSize());
3132 if (!pageContentFrame
) {
3133 // Subtract the size of scrollbars.
3134 nsIScrollableFrame
* scrollableFrame
=
3135 aPresContext
->PresShell()->GetRootScrollFrameAsScrollable();
3136 if (scrollableFrame
) {
3137 nsMargin scrollbars
= scrollableFrame
->GetActualScrollbarSizes();
3138 bgPositioningArea
.Deflate(scrollbars
);
3142 *aAttachedToFrame
= attachedToFrame
;
3144 return bgPositioningArea
;
3147 // Apply the CSS image sizing algorithm as it applies to background images.
3148 // See http://www.w3.org/TR/css3-background/#the-background-size .
3149 // aIntrinsicSize is the size that the background image 'would like to be'.
3150 // It can be found by calling nsImageRenderer::ComputeIntrinsicSize.
3152 ComputeDrawnSizeForBackground(const CSSSizeOrRatio
& aIntrinsicSize
,
3153 const nsSize
& aBgPositioningArea
,
3154 const nsStyleBackground::Size
& aLayerSize
)
3156 // Size is dictated by cover or contain rules.
3157 if (aLayerSize
.mWidthType
== nsStyleBackground::Size::eContain
||
3158 aLayerSize
.mWidthType
== nsStyleBackground::Size::eCover
) {
3159 nsImageRenderer::FitType fitType
=
3160 aLayerSize
.mWidthType
== nsStyleBackground::Size::eCover
3161 ? nsImageRenderer::COVER
3162 : nsImageRenderer::CONTAIN
;
3163 return nsImageRenderer::ComputeConstrainedSize(aBgPositioningArea
,
3164 aIntrinsicSize
.mRatio
,
3168 // No cover/contain constraint, use default algorithm.
3169 CSSSizeOrRatio specifiedSize
;
3170 if (aLayerSize
.mWidthType
== nsStyleBackground::Size::eLengthPercentage
) {
3171 specifiedSize
.SetWidth(
3172 aLayerSize
.ResolveWidthLengthPercentage(aBgPositioningArea
));
3174 if (aLayerSize
.mHeightType
== nsStyleBackground::Size::eLengthPercentage
) {
3175 specifiedSize
.SetHeight(
3176 aLayerSize
.ResolveHeightLengthPercentage(aBgPositioningArea
));
3179 return nsImageRenderer::ComputeConcreteSize(specifiedSize
,
3181 aBgPositioningArea
);
3184 nsBackgroundLayerState
3185 nsCSSRendering::PrepareBackgroundLayer(nsPresContext
* aPresContext
,
3186 nsIFrame
* aForFrame
,
3188 const nsRect
& aBorderArea
,
3189 const nsRect
& aBGClipRect
,
3190 const nsStyleBackground::Layer
& aLayer
)
3193 * The properties we need to keep in mind when drawing background
3198 * background-attachment
3199 * background-position
3203 * background-blend-mode
3204 * box-decoration-break
3206 * (background-color applies to the entire element and not to individual
3207 * layers, so it is irrelevant to this method.)
3209 * These properties have the following dependencies upon each other when
3210 * determining rendering:
3216 * background-attachment
3218 * background-position
3219 * depends upon background-size (for the image's scaled size) and
3220 * background-break (for the background positioning area)
3224 * depends upon background-attachment (only in the case where that value
3227 * depends upon box-decoration-break (for the background positioning area
3228 * for resolving percentages), background-image (for the image's intrinsic
3229 * size), background-repeat (if that value is 'round'), and
3230 * background-origin (for the background painting area, when
3231 * background-repeat is 'round')
3232 * box-decoration-break
3235 * As a result of only-if dependencies we don't strictly do a topological
3236 * sort of the above properties when processing, but it's pretty close to one:
3238 * background-clip (by caller)
3240 * box-decoration-break, background-origin
3241 * background-attachment (postfix for background-origin if 'fixed')
3243 * background-position
3247 uint32_t irFlags
= 0;
3248 if (aFlags
& nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES
) {
3249 irFlags
|= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES
;
3251 if (aFlags
& nsCSSRendering::PAINTBG_TO_WINDOW
) {
3252 irFlags
|= nsImageRenderer::FLAG_PAINTING_TO_WINDOW
;
3255 nsBackgroundLayerState
state(aForFrame
, &aLayer
.mImage
, irFlags
);
3256 if (!state
.mImageRenderer
.PrepareImage()) {
3257 // There's no image or it's not ready to be painted.
3261 // The frame to which the background is attached
3262 nsIFrame
* attachedToFrame
= aForFrame
;
3263 // Compute background origin area relative to aBorderArea now as we may need
3264 // it to compute the effective image size for a CSS gradient.
3265 nsRect bgPositioningArea
=
3266 ComputeBackgroundPositioningArea(aPresContext
, aForFrame
, aBorderArea
,
3267 aLayer
, &attachedToFrame
);
3269 // For background-attachment:fixed backgrounds, we'll limit the area
3270 // where the background can be drawn to the viewport.
3271 nsRect bgClipRect
= aBGClipRect
;
3273 // Compute the anchor point.
3275 // relative to aBorderArea.TopLeft() (which is where the top-left
3276 // of aForFrame's border-box will be rendered)
3277 nsPoint imageTopLeft
;
3278 if (NS_STYLE_BG_ATTACHMENT_FIXED
== aLayer
.mAttachment
) {
3279 if ((aFlags
& nsCSSRendering::PAINTBG_TO_WINDOW
) &&
3280 !IsTransformed(aForFrame
, attachedToFrame
)) {
3281 // Clip background-attachment:fixed backgrounds to the viewport, if we're
3282 // painting to the screen and not transformed. This avoids triggering
3283 // tiling in common cases, without affecting output since drawing is
3284 // always clipped to the viewport when we draw to the screen. (But it's
3285 // not a pure optimization since it can affect the values of pixels at the
3286 // edge of the viewport --- whether they're sampled from a putative "next
3288 bgClipRect
.IntersectRect(bgClipRect
, bgPositioningArea
+ aBorderArea
.TopLeft());
3292 // Scale the image as specified for background-size and as required for
3293 // proper background positioning when background-position is defined with
3295 CSSSizeOrRatio intrinsicSize
= state
.mImageRenderer
.ComputeIntrinsicSize();
3296 nsSize bgPositionSize
= bgPositioningArea
.Size();
3297 nsSize imageSize
= ComputeDrawnSizeForBackground(intrinsicSize
,
3300 if (imageSize
.width
<= 0 || imageSize
.height
<= 0)
3303 state
.mImageRenderer
.SetPreferredSize(intrinsicSize
,
3306 // Compute the position of the background now that the background's size is
3308 nsImageRenderer::ComputeObjectAnchorPoint(aLayer
.mPosition
,
3309 bgPositionSize
, imageSize
,
3310 &imageTopLeft
, &state
.mAnchor
);
3311 imageTopLeft
+= bgPositioningArea
.TopLeft();
3312 state
.mAnchor
+= bgPositioningArea
.TopLeft();
3314 state
.mDestArea
= nsRect(imageTopLeft
+ aBorderArea
.TopLeft(), imageSize
);
3315 state
.mFillArea
= state
.mDestArea
;
3316 int repeatX
= aLayer
.mRepeat
.mXRepeat
;
3317 int repeatY
= aLayer
.mRepeat
.mYRepeat
;
3318 if (repeatX
== NS_STYLE_BG_REPEAT_REPEAT
) {
3319 state
.mFillArea
.x
= bgClipRect
.x
;
3320 state
.mFillArea
.width
= bgClipRect
.width
;
3322 if (repeatY
== NS_STYLE_BG_REPEAT_REPEAT
) {
3323 state
.mFillArea
.y
= bgClipRect
.y
;
3324 state
.mFillArea
.height
= bgClipRect
.height
;
3326 state
.mFillArea
.IntersectRect(state
.mFillArea
, bgClipRect
);
3328 state
.mCompositingOp
= GetGFXBlendMode(aLayer
.mBlendMode
);
3334 nsCSSRendering::GetBackgroundLayerRect(nsPresContext
* aPresContext
,
3335 nsIFrame
* aForFrame
,
3336 const nsRect
& aBorderArea
,
3337 const nsRect
& aClipRect
,
3338 const nsStyleBackground::Layer
& aLayer
,
3341 Sides skipSides
= aForFrame
->GetSkipSides();
3343 ::BoxDecorationRectForBackground(aForFrame
, aBorderArea
, skipSides
);
3344 nsBackgroundLayerState state
=
3345 PrepareBackgroundLayer(aPresContext
, aForFrame
, aFlags
, borderArea
,
3347 return state
.mFillArea
;
3351 DrawBorderImage(nsPresContext
* aPresContext
,
3352 nsRenderingContext
& aRenderingContext
,
3353 nsIFrame
* aForFrame
,
3354 const nsRect
& aBorderArea
,
3355 const nsStyleBorder
& aStyleBorder
,
3356 const nsRect
& aDirtyRect
,
3359 NS_PRECONDITION(aStyleBorder
.IsBorderImageLoaded(),
3360 "drawing border image that isn't successfully loaded");
3362 if (aDirtyRect
.IsEmpty())
3365 nsImageRenderer
renderer(aForFrame
, &aStyleBorder
.mBorderImageSource
, 0);
3367 // Ensure we get invalidated for loads and animations of the image.
3368 // We need to do this here because this might be the only code that
3369 // knows about the association of the style data with the frame.
3370 // XXX We shouldn't really... since if anybody is passing in a
3371 // different style, they'll potentially have the wrong size for the
3373 aForFrame
->AssociateImage(aStyleBorder
.mBorderImageSource
, aPresContext
);
3375 if (!renderer
.PrepareImage()) {
3379 // NOTE: no Save() yet, we do that later by calling autoSR.EnsureSaved()
3380 // in case we need it.
3381 gfxContextAutoSaveRestore autoSR
;
3383 // Determine the border image area, which by default corresponds to the
3384 // border box but can be modified by 'border-image-outset'.
3385 // Note that 'border-radius' do not apply to 'border-image' borders per
3386 // <http://dev.w3.org/csswg/css-backgrounds/#corner-clipping>.
3387 nsRect borderImgArea
;
3388 nsMargin
borderWidths(aStyleBorder
.GetComputedBorder());
3389 nsMargin
imageOutset(aStyleBorder
.GetImageOutset());
3390 if (::IsBoxDecorationSlice(aStyleBorder
) && !aSkipSides
.IsEmpty()) {
3391 borderImgArea
= ::BoxDecorationRectForBorder(aForFrame
, aBorderArea
,
3392 aSkipSides
, &aStyleBorder
);
3393 if (borderImgArea
.IsEqualEdges(aBorderArea
)) {
3394 // No need for a clip, just skip the sides we don't want.
3395 borderWidths
.ApplySkipSides(aSkipSides
);
3396 imageOutset
.ApplySkipSides(aSkipSides
);
3397 borderImgArea
.Inflate(imageOutset
);
3399 // We're drawing borders around the joined continuation boxes so we need
3400 // to clip that to the slice that we want for this frame.
3401 borderImgArea
.Inflate(imageOutset
);
3402 imageOutset
.ApplySkipSides(aSkipSides
);
3403 nsRect clip
= aBorderArea
;
3404 clip
.Inflate(imageOutset
);
3405 autoSR
.EnsureSaved(aRenderingContext
.ThebesContext());
3406 aRenderingContext
.ThebesContext()->
3407 Clip(NSRectToSnappedRect(clip
,
3408 aForFrame
->PresContext()->AppUnitsPerDevPixel(),
3409 *aRenderingContext
.GetDrawTarget()));
3412 borderImgArea
= aBorderArea
;
3413 borderImgArea
.Inflate(imageOutset
);
3416 // Calculate the image size used to compute slice points.
3417 CSSSizeOrRatio intrinsicSize
= renderer
.ComputeIntrinsicSize();
3418 nsSize imageSize
= nsImageRenderer::ComputeConcreteSize(CSSSizeOrRatio(),
3420 borderImgArea
.Size());
3421 renderer
.SetPreferredSize(intrinsicSize
, imageSize
);
3423 // Compute the used values of 'border-image-slice' and 'border-image-width';
3424 // we do them together because the latter can depend on the former.
3427 NS_FOR_CSS_SIDES(s
) {
3428 nsStyleCoord coord
= aStyleBorder
.mBorderImageSlice
.Get(s
);
3429 int32_t imgDimension
= NS_SIDE_IS_VERTICAL(s
)
3430 ? imageSize
.width
: imageSize
.height
;
3431 nscoord borderDimension
= NS_SIDE_IS_VERTICAL(s
)
3432 ? borderImgArea
.width
: borderImgArea
.height
;
3434 switch (coord
.GetUnit()) {
3435 case eStyleUnit_Percent
:
3436 value
= coord
.GetPercentValue() * imgDimension
;
3438 case eStyleUnit_Factor
:
3439 value
= nsPresContext::CSSPixelsToAppUnits(
3440 NS_lround(coord
.GetFactorValue()));
3443 NS_NOTREACHED("unexpected CSS unit for image slice");
3449 if (value
> imgDimension
)
3450 value
= imgDimension
;
3451 slice
.Side(s
) = value
;
3453 coord
= aStyleBorder
.mBorderImageWidth
.Get(s
);
3454 switch (coord
.GetUnit()) {
3455 case eStyleUnit_Coord
: // absolute dimension
3456 value
= coord
.GetCoordValue();
3458 case eStyleUnit_Percent
:
3459 value
= coord
.GetPercentValue() * borderDimension
;
3461 case eStyleUnit_Factor
:
3462 value
= coord
.GetFactorValue() * borderWidths
.Side(s
);
3464 case eStyleUnit_Auto
: // same as the slice value, in CSS pixels
3465 value
= slice
.Side(s
);
3468 NS_NOTREACHED("unexpected CSS unit for border image area division");
3472 // NSToCoordRoundWithClamp rounds towards infinity, but that's OK
3473 // because we expect value to be non-negative.
3474 MOZ_ASSERT(value
>= 0);
3475 border
.Side(s
) = NSToCoordRoundWithClamp(value
);
3476 MOZ_ASSERT(border
.Side(s
) >= 0);
3479 // "If two opposite border-image-width offsets are large enough that they
3480 // overlap, their used values are proportionately reduced until they no
3482 uint32_t combinedBorderWidth
= uint32_t(border
.left
) +
3483 uint32_t(border
.right
);
3484 double scaleX
= combinedBorderWidth
> uint32_t(borderImgArea
.width
)
3485 ? borderImgArea
.width
/ double(combinedBorderWidth
)
3487 uint32_t combinedBorderHeight
= uint32_t(border
.top
) +
3488 uint32_t(border
.bottom
);
3489 double scaleY
= combinedBorderHeight
> uint32_t(borderImgArea
.height
)
3490 ? borderImgArea
.height
/ double(combinedBorderHeight
)
3492 double scale
= std::min(scaleX
, scaleY
);
3494 border
.left
*= scale
;
3495 border
.right
*= scale
;
3496 border
.top
*= scale
;
3497 border
.bottom
*= scale
;
3498 NS_ASSERTION(border
.left
+ border
.right
<= borderImgArea
.width
&&
3499 border
.top
+ border
.bottom
<= borderImgArea
.height
,
3500 "rounding error in width reduction???");
3503 // These helper tables recharacterize the 'slice' and 'width' margins
3504 // in a more convenient form: they are the x/y/width/height coords
3505 // required for various bands of the border, and they have been transformed
3506 // to be relative to the innerRect (for 'slice') or the page (for 'border').
3508 LEFT
, MIDDLE
, RIGHT
,
3509 TOP
= LEFT
, BOTTOM
= RIGHT
3511 const nscoord borderX
[3] = {
3512 borderImgArea
.x
+ 0,
3513 borderImgArea
.x
+ border
.left
,
3514 borderImgArea
.x
+ borderImgArea
.width
- border
.right
,
3516 const nscoord borderY
[3] = {
3517 borderImgArea
.y
+ 0,
3518 borderImgArea
.y
+ border
.top
,
3519 borderImgArea
.y
+ borderImgArea
.height
- border
.bottom
,
3521 const nscoord borderWidth
[3] = {
3523 borderImgArea
.width
- border
.left
- border
.right
,
3526 const nscoord borderHeight
[3] = {
3528 borderImgArea
.height
- border
.top
- border
.bottom
,
3531 const int32_t sliceX
[3] = {
3534 imageSize
.width
- slice
.right
,
3536 const int32_t sliceY
[3] = {
3539 imageSize
.height
- slice
.bottom
,
3541 const int32_t sliceWidth
[3] = {
3543 std::max(imageSize
.width
- slice
.left
- slice
.right
, 0),
3546 const int32_t sliceHeight
[3] = {
3548 std::max(imageSize
.height
- slice
.top
- slice
.bottom
, 0),
3552 for (int i
= LEFT
; i
<= RIGHT
; i
++) {
3553 for (int j
= TOP
; j
<= BOTTOM
; j
++) {
3554 uint8_t fillStyleH
, fillStyleV
;
3557 if (i
== MIDDLE
&& j
== MIDDLE
) {
3558 // Discard the middle portion unless set to fill.
3559 if (NS_STYLE_BORDER_IMAGE_SLICE_NOFILL
==
3560 aStyleBorder
.mBorderImageFill
) {
3564 NS_ASSERTION(NS_STYLE_BORDER_IMAGE_SLICE_FILL
==
3565 aStyleBorder
.mBorderImageFill
,
3566 "Unexpected border image fill");
3569 // The middle image's width is scaled by the same factor as the
3570 // top image unless that factor is zero or infinity, in which
3571 // case the scaling factor of the bottom is substituted, and
3572 // failing that, the width is not scaled. The height of the
3573 // middle image is scaled by the same factor as the left image
3574 // unless that factor is zero or infinity, in which case the
3575 // scaling factor of the right image is substituted, and failing
3576 // that, the height is not scaled.
3577 gfxFloat hFactor
, vFactor
;
3579 if (0 < border
.left
&& 0 < slice
.left
)
3580 vFactor
= gfxFloat(border
.left
)/slice
.left
;
3581 else if (0 < border
.right
&& 0 < slice
.right
)
3582 vFactor
= gfxFloat(border
.right
)/slice
.right
;
3586 if (0 < border
.top
&& 0 < slice
.top
)
3587 hFactor
= gfxFloat(border
.top
)/slice
.top
;
3588 else if (0 < border
.bottom
&& 0 < slice
.bottom
)
3589 hFactor
= gfxFloat(border
.bottom
)/slice
.bottom
;
3593 unitSize
.width
= sliceWidth
[i
]*hFactor
;
3594 unitSize
.height
= sliceHeight
[j
]*vFactor
;
3595 fillStyleH
= aStyleBorder
.mBorderImageRepeatH
;
3596 fillStyleV
= aStyleBorder
.mBorderImageRepeatV
;
3598 } else if (i
== MIDDLE
) { // top, bottom
3599 // Sides are always stretched to the thickness of their border,
3600 // and stretched proportionately on the other axis.
3602 if (0 < borderHeight
[j
] && 0 < sliceHeight
[j
])
3603 factor
= gfxFloat(borderHeight
[j
])/sliceHeight
[j
];
3607 unitSize
.width
= sliceWidth
[i
]*factor
;
3608 unitSize
.height
= borderHeight
[j
];
3609 fillStyleH
= aStyleBorder
.mBorderImageRepeatH
;
3610 fillStyleV
= NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH
;
3612 } else if (j
== MIDDLE
) { // left, right
3614 if (0 < borderWidth
[i
] && 0 < sliceWidth
[i
])
3615 factor
= gfxFloat(borderWidth
[i
])/sliceWidth
[i
];
3619 unitSize
.width
= borderWidth
[i
];
3620 unitSize
.height
= sliceHeight
[j
]*factor
;
3621 fillStyleH
= NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH
;
3622 fillStyleV
= aStyleBorder
.mBorderImageRepeatV
;
3625 // Corners are always stretched to fit the corner.
3626 unitSize
.width
= borderWidth
[i
];
3627 unitSize
.height
= borderHeight
[j
];
3628 fillStyleH
= NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH
;
3629 fillStyleV
= NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH
;
3632 nsRect
destArea(borderX
[i
], borderY
[j
], borderWidth
[i
], borderHeight
[j
]);
3633 nsRect
subArea(sliceX
[i
], sliceY
[j
], sliceWidth
[i
], sliceHeight
[j
]);
3634 nsIntRect intSubArea
= subArea
.ToOutsidePixels(nsPresContext::AppUnitsPerCSSPixel());
3636 renderer
.DrawBorderImageComponent(aPresContext
,
3637 aRenderingContext
, aDirtyRect
,
3638 destArea
, CSSIntRect(intSubArea
.x
,
3642 fillStyleH
, fillStyleV
,
3643 unitSize
, j
* (RIGHT
+ 1) + i
);
3648 // Begin table border-collapsing section
3649 // These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
3650 // At some point, all functions should be unified to include the additional functionality that these provide
3653 RoundIntToPixel(nscoord aValue
,
3654 nscoord aTwipsPerPixel
,
3655 bool aRoundDown
= false)
3657 if (aTwipsPerPixel
<= 0)
3658 // We must be rendering to a device that has a resolution greater than Twips!
3659 // In that case, aValue is as accurate as it's going to get.
3662 nscoord halfPixel
= NSToCoordRound(aTwipsPerPixel
/ 2.0f
);
3663 nscoord extra
= aValue
% aTwipsPerPixel
;
3664 nscoord finalValue
= (!aRoundDown
&& (extra
>= halfPixel
)) ? aValue
+ (aTwipsPerPixel
- extra
) : aValue
- extra
;
3669 RoundFloatToPixel(float aValue
,
3670 nscoord aTwipsPerPixel
,
3671 bool aRoundDown
= false)
3673 return RoundIntToPixel(NSToCoordRound(aValue
), aTwipsPerPixel
, aRoundDown
);
3676 static void SetPoly(const Rect
& aRect
, Point
* poly
)
3678 poly
[0].x
= aRect
.x
;
3679 poly
[0].y
= aRect
.y
;
3680 poly
[1].x
= aRect
.x
+ aRect
.width
;
3681 poly
[1].y
= aRect
.y
;
3682 poly
[2].x
= aRect
.x
+ aRect
.width
;
3683 poly
[2].y
= aRect
.y
+ aRect
.height
;
3684 poly
[3].x
= aRect
.x
;
3685 poly
[3].y
= aRect
.y
+ aRect
.height
;
3689 DrawSolidBorderSegment(nsRenderingContext
& aContext
,
3692 int32_t aAppUnitsPerDevPixel
,
3693 nscoord aTwipsPerPixel
,
3694 uint8_t aStartBevelSide
= 0,
3695 nscoord aStartBevelOffset
= 0,
3696 uint8_t aEndBevelSide
= 0,
3697 nscoord aEndBevelOffset
= 0)
3699 DrawTarget
* drawTarget
= aContext
.GetDrawTarget();
3701 ColorPattern
color(ToDeviceColor(aColor
));
3702 DrawOptions
drawOptions(1.f
, CompositionOp::OP_OVER
, AntialiasMode::NONE
);
3704 if ((aRect
.width
== aTwipsPerPixel
) || (aRect
.height
== aTwipsPerPixel
) ||
3705 ((0 == aStartBevelOffset
) && (0 == aEndBevelOffset
))) {
3706 // simple line or rectangle
3707 if ((NS_SIDE_TOP
== aStartBevelSide
) || (NS_SIDE_BOTTOM
== aStartBevelSide
)) {
3708 if (1 == aRect
.height
)
3709 StrokeLineWithSnapping(aRect
.TopLeft(), aRect
.BottomLeft(),
3710 aAppUnitsPerDevPixel
, *drawTarget
,
3711 color
, StrokeOptions(), drawOptions
);
3713 drawTarget
->FillRect(NSRectToSnappedRect(aRect
, aAppUnitsPerDevPixel
,
3715 color
, drawOptions
);
3718 if (1 == aRect
.width
)
3719 StrokeLineWithSnapping(aRect
.TopLeft(), aRect
.TopRight(),
3720 aAppUnitsPerDevPixel
, *drawTarget
,
3721 color
, StrokeOptions(), drawOptions
);
3723 drawTarget
->FillRect(NSRectToSnappedRect(aRect
, aAppUnitsPerDevPixel
,
3725 color
, drawOptions
);
3729 // polygon with beveling
3731 SetPoly(NSRectToSnappedRect(aRect
, aAppUnitsPerDevPixel
, *drawTarget
),
3734 Float startBevelOffset
=
3735 NSAppUnitsToFloatPixels(aStartBevelOffset
, aAppUnitsPerDevPixel
);
3736 switch(aStartBevelSide
) {
3738 poly
[0].x
+= startBevelOffset
;
3740 case NS_SIDE_BOTTOM
:
3741 poly
[3].x
+= startBevelOffset
;
3744 poly
[1].y
+= startBevelOffset
;
3747 poly
[0].y
+= startBevelOffset
;
3750 Float endBevelOffset
=
3751 NSAppUnitsToFloatPixels(aEndBevelOffset
, aAppUnitsPerDevPixel
);
3752 switch(aEndBevelSide
) {
3754 poly
[1].x
-= endBevelOffset
;
3756 case NS_SIDE_BOTTOM
:
3757 poly
[2].x
-= endBevelOffset
;
3760 poly
[2].y
-= endBevelOffset
;
3763 poly
[3].y
-= endBevelOffset
;
3766 RefPtr
<PathBuilder
> builder
= drawTarget
->CreatePathBuilder();
3767 builder
->MoveTo(poly
[0]);
3768 builder
->LineTo(poly
[1]);
3769 builder
->LineTo(poly
[2]);
3770 builder
->LineTo(poly
[3]);
3772 RefPtr
<Path
> path
= builder
->Finish();
3773 drawTarget
->Fill(path
, color
, drawOptions
);
3778 GetDashInfo(nscoord aBorderLength
,
3779 nscoord aDashLength
,
3780 nscoord aTwipsPerPixel
,
3781 int32_t& aNumDashSpaces
,
3782 nscoord
& aStartDashLength
,
3783 nscoord
& aEndDashLength
)
3786 if (aStartDashLength
+ aDashLength
+ aEndDashLength
>= aBorderLength
) {
3787 aStartDashLength
= aBorderLength
;
3791 aNumDashSpaces
= (aBorderLength
- aDashLength
)/ (2 * aDashLength
); // round down
3792 nscoord extra
= aBorderLength
- aStartDashLength
- aEndDashLength
- (((2 * aNumDashSpaces
) - 1) * aDashLength
);
3794 nscoord half
= RoundIntToPixel(extra
/ 2, aTwipsPerPixel
);
3795 aStartDashLength
+= half
;
3796 aEndDashLength
+= (extra
- half
);
3802 nsCSSRendering::DrawTableBorderSegment(nsRenderingContext
& aContext
,
3803 uint8_t aBorderStyle
,
3804 nscolor aBorderColor
,
3805 const nsStyleBackground
* aBGColor
,
3806 const nsRect
& aBorder
,
3807 int32_t aAppUnitsPerDevPixel
,
3808 int32_t aAppUnitsPerCSSPixel
,
3809 uint8_t aStartBevelSide
,
3810 nscoord aStartBevelOffset
,
3811 uint8_t aEndBevelSide
,
3812 nscoord aEndBevelOffset
)
3814 bool horizontal
= ((NS_SIDE_TOP
== aStartBevelSide
) || (NS_SIDE_BOTTOM
== aStartBevelSide
));
3815 nscoord twipsPerPixel
= NSIntPixelsToAppUnits(1, aAppUnitsPerCSSPixel
);
3816 uint8_t ridgeGroove
= NS_STYLE_BORDER_STYLE_RIDGE
;
3818 if ((twipsPerPixel
>= aBorder
.width
) || (twipsPerPixel
>= aBorder
.height
) ||
3819 (NS_STYLE_BORDER_STYLE_DASHED
== aBorderStyle
) || (NS_STYLE_BORDER_STYLE_DOTTED
== aBorderStyle
)) {
3820 // no beveling for 1 pixel border, dash or dot
3821 aStartBevelOffset
= 0;
3822 aEndBevelOffset
= 0;
3825 gfxContext
*ctx
= aContext
.ThebesContext();
3826 AntialiasMode oldMode
= ctx
->CurrentAntialiasMode();
3827 ctx
->SetAntialiasMode(AntialiasMode::NONE
);
3829 ctx
->SetColor(aBorderColor
);
3831 switch (aBorderStyle
) {
3832 case NS_STYLE_BORDER_STYLE_NONE
:
3833 case NS_STYLE_BORDER_STYLE_HIDDEN
:
3834 //NS_ASSERTION(false, "style of none or hidden");
3836 case NS_STYLE_BORDER_STYLE_DOTTED
:
3837 case NS_STYLE_BORDER_STYLE_DASHED
:
3839 nscoord dashLength
= (NS_STYLE_BORDER_STYLE_DASHED
== aBorderStyle
) ? DASH_LENGTH
: DOT_LENGTH
;
3840 // make the dash length proportional to the border thickness
3841 dashLength
*= (horizontal
) ? aBorder
.height
: aBorder
.width
;
3842 // make the min dash length for the ends 1/2 the dash length
3843 nscoord minDashLength
= (NS_STYLE_BORDER_STYLE_DASHED
== aBorderStyle
)
3844 ? RoundFloatToPixel(((float)dashLength
) / 2.0f
, twipsPerPixel
) : dashLength
;
3845 minDashLength
= std::max(minDashLength
, twipsPerPixel
);
3846 nscoord numDashSpaces
= 0;
3847 nscoord startDashLength
= minDashLength
;
3848 nscoord endDashLength
= minDashLength
;
3850 GetDashInfo(aBorder
.width
, dashLength
, twipsPerPixel
, numDashSpaces
, startDashLength
, endDashLength
);
3851 nsRect
rect(aBorder
.x
, aBorder
.y
, startDashLength
, aBorder
.height
);
3852 DrawSolidBorderSegment(aContext
, rect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
);
3853 for (int32_t spaceX
= 0; spaceX
< numDashSpaces
; spaceX
++) {
3854 rect
.x
+= rect
.width
+ dashLength
;
3855 rect
.width
= (spaceX
== (numDashSpaces
- 1)) ? endDashLength
: dashLength
;
3856 DrawSolidBorderSegment(aContext
, rect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
);
3860 GetDashInfo(aBorder
.height
, dashLength
, twipsPerPixel
, numDashSpaces
, startDashLength
, endDashLength
);
3861 nsRect
rect(aBorder
.x
, aBorder
.y
, aBorder
.width
, startDashLength
);
3862 DrawSolidBorderSegment(aContext
, rect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
);
3863 for (int32_t spaceY
= 0; spaceY
< numDashSpaces
; spaceY
++) {
3864 rect
.y
+= rect
.height
+ dashLength
;
3865 rect
.height
= (spaceY
== (numDashSpaces
- 1)) ? endDashLength
: dashLength
;
3866 DrawSolidBorderSegment(aContext
, rect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
);
3871 case NS_STYLE_BORDER_STYLE_GROOVE
:
3872 ridgeGroove
= NS_STYLE_BORDER_STYLE_GROOVE
; // and fall through to ridge
3873 case NS_STYLE_BORDER_STYLE_RIDGE
:
3874 if ((horizontal
&& (twipsPerPixel
>= aBorder
.height
)) ||
3875 (!horizontal
&& (twipsPerPixel
>= aBorder
.width
))) {
3876 // a one pixel border
3877 DrawSolidBorderSegment(aContext
, aBorder
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
,
3878 aStartBevelSide
, aStartBevelOffset
,
3879 aEndBevelSide
, aEndBevelOffset
);
3882 nscoord startBevel
= (aStartBevelOffset
> 0)
3883 ? RoundFloatToPixel(0.5f
* (float)aStartBevelOffset
, twipsPerPixel
, true) : 0;
3884 nscoord endBevel
= (aEndBevelOffset
> 0)
3885 ? RoundFloatToPixel(0.5f
* (float)aEndBevelOffset
, twipsPerPixel
, true) : 0;
3886 mozilla::css::Side ridgeGrooveSide
= (horizontal
) ? NS_SIDE_TOP
: NS_SIDE_LEFT
;
3887 // FIXME: In theory, this should use the visited-dependent
3888 // background color, but I don't care.
3890 MakeBevelColor(ridgeGrooveSide
, ridgeGroove
, aBGColor
->mBackgroundColor
, aBorderColor
));
3891 nsRect
rect(aBorder
);
3893 if (horizontal
) { // top, bottom
3894 half
= RoundFloatToPixel(0.5f
* (float)aBorder
.height
, twipsPerPixel
);
3896 if (NS_SIDE_TOP
== aStartBevelSide
) {
3897 rect
.x
+= startBevel
;
3898 rect
.width
-= startBevel
;
3900 if (NS_SIDE_TOP
== aEndBevelSide
) {
3901 rect
.width
-= endBevel
;
3903 DrawSolidBorderSegment(aContext
, rect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
, aStartBevelSide
,
3904 startBevel
, aEndBevelSide
, endBevel
);
3906 else { // left, right
3907 half
= RoundFloatToPixel(0.5f
* (float)aBorder
.width
, twipsPerPixel
);
3909 if (NS_SIDE_LEFT
== aStartBevelSide
) {
3910 rect
.y
+= startBevel
;
3911 rect
.height
-= startBevel
;
3913 if (NS_SIDE_LEFT
== aEndBevelSide
) {
3914 rect
.height
-= endBevel
;
3916 DrawSolidBorderSegment(aContext
, rect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
, aStartBevelSide
,
3917 startBevel
, aEndBevelSide
, endBevel
);
3921 ridgeGrooveSide
= (NS_SIDE_TOP
== ridgeGrooveSide
) ? NS_SIDE_BOTTOM
: NS_SIDE_RIGHT
;
3922 // FIXME: In theory, this should use the visited-dependent
3923 // background color, but I don't care.
3925 MakeBevelColor(ridgeGrooveSide
, ridgeGroove
, aBGColor
->mBackgroundColor
, aBorderColor
));
3927 rect
.y
= rect
.y
+ half
;
3928 rect
.height
= aBorder
.height
- half
;
3929 if (NS_SIDE_BOTTOM
== aStartBevelSide
) {
3930 rect
.x
+= startBevel
;
3931 rect
.width
-= startBevel
;
3933 if (NS_SIDE_BOTTOM
== aEndBevelSide
) {
3934 rect
.width
-= endBevel
;
3936 DrawSolidBorderSegment(aContext
, rect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
, aStartBevelSide
,
3937 startBevel
, aEndBevelSide
, endBevel
);
3940 rect
.x
= rect
.x
+ half
;
3941 rect
.width
= aBorder
.width
- half
;
3942 if (NS_SIDE_RIGHT
== aStartBevelSide
) {
3943 rect
.y
+= aStartBevelOffset
- startBevel
;
3944 rect
.height
-= startBevel
;
3946 if (NS_SIDE_RIGHT
== aEndBevelSide
) {
3947 rect
.height
-= endBevel
;
3949 DrawSolidBorderSegment(aContext
, rect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
, aStartBevelSide
,
3950 startBevel
, aEndBevelSide
, endBevel
);
3954 case NS_STYLE_BORDER_STYLE_DOUBLE
:
3955 // We can only do "double" borders if the thickness of the border
3956 // is more than 2px. Otherwise, we fall through to painting a
3958 if ((aBorder
.width
> 2*twipsPerPixel
|| horizontal
) &&
3959 (aBorder
.height
> 2*twipsPerPixel
|| !horizontal
)) {
3960 nscoord startBevel
= (aStartBevelOffset
> 0)
3961 ? RoundFloatToPixel(0.333333f
* (float)aStartBevelOffset
, twipsPerPixel
) : 0;
3962 nscoord endBevel
= (aEndBevelOffset
> 0)
3963 ? RoundFloatToPixel(0.333333f
* (float)aEndBevelOffset
, twipsPerPixel
) : 0;
3964 if (horizontal
) { // top, bottom
3965 nscoord thirdHeight
= RoundFloatToPixel(0.333333f
* (float)aBorder
.height
, twipsPerPixel
);
3967 // draw the top line or rect
3968 nsRect
topRect(aBorder
.x
, aBorder
.y
, aBorder
.width
, thirdHeight
);
3969 if (NS_SIDE_TOP
== aStartBevelSide
) {
3970 topRect
.x
+= aStartBevelOffset
- startBevel
;
3971 topRect
.width
-= aStartBevelOffset
- startBevel
;
3973 if (NS_SIDE_TOP
== aEndBevelSide
) {
3974 topRect
.width
-= aEndBevelOffset
- endBevel
;
3976 DrawSolidBorderSegment(aContext
, topRect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
, aStartBevelSide
,
3977 startBevel
, aEndBevelSide
, endBevel
);
3979 // draw the botom line or rect
3980 nscoord heightOffset
= aBorder
.height
- thirdHeight
;
3981 nsRect
bottomRect(aBorder
.x
, aBorder
.y
+ heightOffset
, aBorder
.width
, aBorder
.height
- heightOffset
);
3982 if (NS_SIDE_BOTTOM
== aStartBevelSide
) {
3983 bottomRect
.x
+= aStartBevelOffset
- startBevel
;
3984 bottomRect
.width
-= aStartBevelOffset
- startBevel
;
3986 if (NS_SIDE_BOTTOM
== aEndBevelSide
) {
3987 bottomRect
.width
-= aEndBevelOffset
- endBevel
;
3989 DrawSolidBorderSegment(aContext
, bottomRect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
, aStartBevelSide
,
3990 startBevel
, aEndBevelSide
, endBevel
);
3992 else { // left, right
3993 nscoord thirdWidth
= RoundFloatToPixel(0.333333f
* (float)aBorder
.width
, twipsPerPixel
);
3995 nsRect
leftRect(aBorder
.x
, aBorder
.y
, thirdWidth
, aBorder
.height
);
3996 if (NS_SIDE_LEFT
== aStartBevelSide
) {
3997 leftRect
.y
+= aStartBevelOffset
- startBevel
;
3998 leftRect
.height
-= aStartBevelOffset
- startBevel
;
4000 if (NS_SIDE_LEFT
== aEndBevelSide
) {
4001 leftRect
.height
-= aEndBevelOffset
- endBevel
;
4003 DrawSolidBorderSegment(aContext
, leftRect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
, aStartBevelSide
,
4004 startBevel
, aEndBevelSide
, endBevel
);
4006 nscoord widthOffset
= aBorder
.width
- thirdWidth
;
4007 nsRect
rightRect(aBorder
.x
+ widthOffset
, aBorder
.y
, aBorder
.width
- widthOffset
, aBorder
.height
);
4008 if (NS_SIDE_RIGHT
== aStartBevelSide
) {
4009 rightRect
.y
+= aStartBevelOffset
- startBevel
;
4010 rightRect
.height
-= aStartBevelOffset
- startBevel
;
4012 if (NS_SIDE_RIGHT
== aEndBevelSide
) {
4013 rightRect
.height
-= aEndBevelOffset
- endBevel
;
4015 DrawSolidBorderSegment(aContext
, rightRect
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
, aStartBevelSide
,
4016 startBevel
, aEndBevelSide
, endBevel
);
4020 // else fall through to solid
4021 case NS_STYLE_BORDER_STYLE_SOLID
:
4022 DrawSolidBorderSegment(aContext
, aBorder
, aBorderColor
, aAppUnitsPerDevPixel
, twipsPerPixel
, aStartBevelSide
,
4023 aStartBevelOffset
, aEndBevelSide
, aEndBevelOffset
);
4025 case NS_STYLE_BORDER_STYLE_OUTSET
:
4026 case NS_STYLE_BORDER_STYLE_INSET
:
4027 NS_ASSERTION(false, "inset, outset should have been converted to groove, ridge");
4029 case NS_STYLE_BORDER_STYLE_AUTO
:
4030 NS_ASSERTION(false, "Unexpected 'auto' table border");
4034 ctx
->SetAntialiasMode(oldMode
);
4037 // End table border-collapsing section
4040 nsCSSRendering::ExpandPaintingRectForDecorationLine(
4042 const uint8_t aStyle
,
4043 const Rect
& aClippedRect
,
4044 const Float aICoordInFrame
,
4045 const Float aCycleLength
,
4049 case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED
:
4050 case NS_STYLE_TEXT_DECORATION_STYLE_DASHED
:
4051 case NS_STYLE_TEXT_DECORATION_STYLE_WAVY
:
4054 NS_ERROR("Invalid style was specified");
4055 return aClippedRect
;
4058 nsBlockFrame
* block
= nullptr;
4059 // Note that when we paint the decoration lines in relative positioned
4060 // box, we should paint them like all of the boxes are positioned as static.
4061 nscoord framePosInBlockAppUnits
= 0;
4062 for (nsIFrame
* f
= aFrame
; f
; f
= f
->GetParent()) {
4063 block
= do_QueryFrame(f
);
4067 framePosInBlockAppUnits
+= aVertical
?
4068 f
->GetNormalPosition().y
: f
->GetNormalPosition().x
;
4071 NS_ENSURE_TRUE(block
, aClippedRect
);
4073 nsPresContext
*pc
= aFrame
->PresContext();
4074 Float framePosInBlock
= Float(pc
->AppUnitsToGfxUnits(framePosInBlockAppUnits
));
4075 int32_t rectPosInBlock
=
4076 int32_t(NS_round(framePosInBlock
+ aICoordInFrame
));
4077 int32_t extraStartEdge
=
4078 rectPosInBlock
- (rectPosInBlock
/ int32_t(aCycleLength
) * aCycleLength
);
4079 Rect
rect(aClippedRect
);
4081 rect
.y
-= extraStartEdge
;
4082 rect
.height
+= extraStartEdge
;
4084 rect
.x
-= extraStartEdge
;
4085 rect
.width
+= extraStartEdge
;
4091 nsCSSRendering::PaintDecorationLine(nsIFrame
* aFrame
,
4092 DrawTarget
& aDrawTarget
,
4093 const Rect
& aDirtyRect
,
4094 const nscolor aColor
,
4095 const gfxPoint
& aPt
,
4096 const Float aICoordInFrame
,
4097 const gfxSize
& aLineSize
,
4098 const gfxFloat aAscent
,
4099 const gfxFloat aOffset
,
4100 const uint8_t aDecoration
,
4101 const uint8_t aStyle
,
4103 const gfxFloat aDescentLimit
)
4105 NS_ASSERTION(aStyle
!= NS_STYLE_TEXT_DECORATION_STYLE_NONE
, "aStyle is none");
4108 GetTextDecorationRectInternal(aPt
, aLineSize
, aAscent
, aOffset
,
4109 aDecoration
, aStyle
, aVertical
,
4111 if (rect
.IsEmpty() || !rect
.Intersects(aDirtyRect
)) {
4115 if (aDecoration
!= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE
&&
4116 aDecoration
!= NS_STYLE_TEXT_DECORATION_LINE_OVERLINE
&&
4117 aDecoration
!= NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH
) {
4118 NS_ERROR("Invalid decoration value!");
4122 Float lineThickness
= std::max(NS_round(aLineSize
.height
), 1.0);
4124 ColorPattern
color(ToDeviceColor(aColor
));
4125 StrokeOptions
strokeOptions(lineThickness
);
4126 DrawOptions drawOptions
;
4130 AutoPopClips
autoPopClips(&aDrawTarget
);
4133 case NS_STYLE_TEXT_DECORATION_STYLE_SOLID
:
4134 case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE
:
4136 case NS_STYLE_TEXT_DECORATION_STYLE_DASHED
: {
4137 autoPopClips
.PushClipRect(rect
);
4138 Float dashWidth
= lineThickness
* DOT_LENGTH
* DASH_LENGTH
;
4139 dash
[0] = dashWidth
;
4140 dash
[1] = dashWidth
;
4141 strokeOptions
.mDashPattern
= dash
;
4142 strokeOptions
.mDashLength
= MOZ_ARRAY_LENGTH(dash
);
4143 strokeOptions
.mLineCap
= CapStyle::BUTT
;
4144 rect
= ExpandPaintingRectForDecorationLine(aFrame
, aStyle
, rect
,
4148 // We should continue to draw the last dash even if it is not in the rect.
4149 rect
.width
+= dashWidth
;
4152 case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED
: {
4153 autoPopClips
.PushClipRect(rect
);
4154 Float dashWidth
= lineThickness
* DOT_LENGTH
;
4155 if (lineThickness
> 2.0) {
4157 dash
[1] = dashWidth
* 2.f
;
4158 strokeOptions
.mLineCap
= CapStyle::ROUND
;
4160 dash
[0] = dashWidth
;
4161 dash
[1] = dashWidth
;
4163 strokeOptions
.mDashPattern
= dash
;
4164 strokeOptions
.mDashLength
= MOZ_ARRAY_LENGTH(dash
);
4165 rect
= ExpandPaintingRectForDecorationLine(aFrame
, aStyle
, rect
,
4169 // We should continue to draw the last dot even if it is not in the rect.
4170 rect
.width
+= dashWidth
;
4173 case NS_STYLE_TEXT_DECORATION_STYLE_WAVY
:
4174 autoPopClips
.PushClipRect(rect
);
4175 if (lineThickness
> 2.0) {
4176 drawOptions
.mAntialiasMode
= AntialiasMode::SUBPIXEL
;
4178 // Don't use anti-aliasing here. Because looks like lighter color wavy
4179 // line at this case. And probably, users don't think the
4180 // non-anti-aliased wavy line is not pretty.
4181 drawOptions
.mAntialiasMode
= AntialiasMode::NONE
;
4185 NS_ERROR("Invalid style value!");
4189 // The block-direction position should be set to the middle of the line.
4191 rect
.x
+= lineThickness
/ 2;
4193 rect
.y
+= lineThickness
/ 2;
4197 case NS_STYLE_TEXT_DECORATION_STYLE_SOLID
:
4198 case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED
:
4199 case NS_STYLE_TEXT_DECORATION_STYLE_DASHED
: {
4200 Point p1
= rect
.TopLeft();
4201 Point p2
= aVertical
? rect
.BottomLeft() : rect
.TopRight();
4202 aDrawTarget
.StrokeLine(p1
, p2
, color
, strokeOptions
, drawOptions
);
4205 case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE
: {
4207 * We are drawing double line as:
4209 * +-------------------------------------------+
4210 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
4211 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
4212 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
4215 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
4216 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
4217 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
4218 * +-------------------------------------------+
4220 Point p1
= rect
.TopLeft();
4221 Point p2
= aVertical
? rect
.BottomLeft() : rect
.TopRight();
4222 aDrawTarget
.StrokeLine(p1
, p2
, color
, strokeOptions
, drawOptions
);
4225 rect
.width
-= lineThickness
;
4227 rect
.height
-= lineThickness
;
4230 p1
= aVertical
? rect
.TopRight() : rect
.BottomLeft();
4231 p2
= rect
.BottomRight();
4232 aDrawTarget
.StrokeLine(p1
, p2
, color
, strokeOptions
, drawOptions
);
4235 case NS_STYLE_TEXT_DECORATION_STYLE_WAVY
: {
4237 * We are drawing wavy line as:
4239 * P: Path, X: Painted pixel
4241 * +---------------------------------------+
4242 * XX|X XXXXXX XXXXXX |
4243 * PP|PX XPPPPPPX XPPPPPPX | ^
4244 * XX|XPX XPXXXXXXPX XPXXXXXXPX| |
4245 * | XPX XPX XPX XPX XP|X |adv
4246 * | XPXXXXXXPX XPXXXXXXPX X|PX |
4247 * | XPPPPPPX XPPPPPPX |XPX v
4248 * | XXXXXX XXXXXX | XX
4249 * +---------------------------------------+
4251 * adv flatLengthAtVertex rightMost
4253 * 1. Always starts from top-left of the drawing area, however, we need
4254 * to draw the line from outside of the rect. Because the start
4255 * point of the line is not good style if we draw from inside it.
4256 * 2. First, draw horizontal line from outside the rect to top-left of
4258 * 3. Goes down to bottom of the area at 45 degrees.
4259 * 4. Slides to right horizontaly, see |flatLengthAtVertex|.
4260 * 5. Goes up to top of the area at 45 degrees.
4261 * 6. Slides to right horizontaly.
4262 * 7. Repeat from 2 until reached to right-most edge of the area.
4264 * In the vertical case, swap horizontal and vertical coordinates and
4265 * directions in the above description.
4268 Float
& rectICoord
= aVertical
? rect
.y
: rect
.x
;
4269 Float
& rectISize
= aVertical
? rect
.height
: rect
.width
;
4270 const Float rectBSize
= aVertical
? rect
.width
: rect
.height
;
4272 const Float adv
= rectBSize
- lineThickness
;
4273 const Float flatLengthAtVertex
=
4274 std::max((lineThickness
- 1.0) * 2.0, 1.0);
4276 // Align the start of wavy lines to the nearest ancestor block.
4277 const Float cycleLength
= 2 * (adv
+ flatLengthAtVertex
);
4278 rect
= ExpandPaintingRectForDecorationLine(aFrame
, aStyle
, rect
,
4279 aICoordInFrame
, cycleLength
,
4281 // figure out if we can trim whole cycles from the left and right edges
4282 // of the line, to try and avoid creating an unnecessarily long and
4284 const Float dirtyRectICoord
= aVertical
? aDirtyRect
.y
: aDirtyRect
.x
;
4285 int32_t skipCycles
= floor((dirtyRectICoord
- rectICoord
) / cycleLength
);
4286 if (skipCycles
> 0) {
4287 rectICoord
+= skipCycles
* cycleLength
;
4288 rectISize
-= skipCycles
* cycleLength
;
4291 rectICoord
+= lineThickness
/ 2.0;
4292 Point
pt(rect
.TopLeft());
4293 Float
& ptICoord
= aVertical
? pt
.y
: pt
.x
;
4294 Float
& ptBCoord
= aVertical
? pt
.x
: pt
.y
;
4296 ptBCoord
+= adv
+ lineThickness
/ 2.0;
4298 Float iCoordLimit
= ptICoord
+ rectISize
+ lineThickness
;
4300 const Float dirtyRectIMost
= aVertical
?
4301 aDirtyRect
.YMost() : aDirtyRect
.XMost();
4302 skipCycles
= floor((iCoordLimit
- dirtyRectIMost
) / cycleLength
);
4303 if (skipCycles
> 0) {
4304 iCoordLimit
-= skipCycles
* cycleLength
;
4307 RefPtr
<PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
4310 ptICoord
-= lineThickness
;
4311 builder
->MoveTo(pt
); // 1
4313 ptICoord
= rectICoord
;
4314 builder
->LineTo(pt
); // 2
4316 // In vertical mode, to go "down" relative to the text we need to
4317 // decrease the block coordinate, whereas in horizontal we increase
4318 // it. So the sense of this flag is effectively inverted.
4319 bool goDown
= aVertical
? false : true;
4321 while (ptICoord
< iCoordLimit
) {
4322 if (++iter
> 1000) {
4323 // stroke the current path and start again, to avoid pathological
4324 // behavior in cairo with huge numbers of path segments
4325 path
= builder
->Finish();
4326 aDrawTarget
.Stroke(path
, color
, strokeOptions
, drawOptions
);
4327 builder
= aDrawTarget
.CreatePathBuilder();
4328 builder
->MoveTo(pt
);
4332 ptBCoord
+= goDown
? adv
: -adv
;
4334 builder
->LineTo(pt
); // 3 and 5
4336 ptICoord
+= flatLengthAtVertex
;
4337 builder
->LineTo(pt
); // 4 and 6
4341 path
= builder
->Finish();
4342 aDrawTarget
.Stroke(path
, color
, strokeOptions
, drawOptions
);
4346 NS_ERROR("Invalid style value!");
4351 nsCSSRendering::DecorationLineToPath(nsIFrame
* aFrame
,
4352 gfxContext
* aGfxContext
,
4353 const gfxRect
& aDirtyRect
,
4354 const nscolor aColor
,
4355 const gfxPoint
& aPt
,
4356 const gfxFloat aICoordInFrame
,
4357 const gfxSize
& aLineSize
,
4358 const gfxFloat aAscent
,
4359 const gfxFloat aOffset
,
4360 const uint8_t aDecoration
,
4361 const uint8_t aStyle
,
4363 const gfxFloat aDescentLimit
)
4365 NS_ASSERTION(aStyle
!= NS_STYLE_TEXT_DECORATION_STYLE_NONE
, "aStyle is none");
4367 aGfxContext
->NewPath();
4370 GetTextDecorationRectInternal(aPt
, aLineSize
, aAscent
, aOffset
,
4371 aDecoration
, aStyle
, aVertical
,
4373 if (rect
.IsEmpty() || !rect
.Intersects(aDirtyRect
)) {
4377 if (aDecoration
!= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE
&&
4378 aDecoration
!= NS_STYLE_TEXT_DECORATION_LINE_OVERLINE
&&
4379 aDecoration
!= NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH
) {
4380 NS_ERROR("Invalid decoration value!");
4384 if (aStyle
!= NS_STYLE_TEXT_DECORATION_STYLE_SOLID
) {
4385 // For the moment, we support only solid text decorations.
4389 gfxFloat lineThickness
= std::max(NS_round(aLineSize
.height
), 1.0);
4391 // The block-direction position should be set to the middle of the line.
4393 rect
.x
+= lineThickness
/ 2;
4394 aGfxContext
->Rectangle
4395 (gfxRect(gfxPoint(rect
.TopLeft() - gfxPoint(lineThickness
/ 2, 0.0)),
4396 gfxSize(lineThickness
, rect
.Height())));
4398 rect
.y
+= lineThickness
/ 2;
4399 aGfxContext
->Rectangle
4400 (gfxRect(gfxPoint(rect
.TopLeft() - gfxPoint(0.0, lineThickness
/ 2)),
4401 gfxSize(rect
.Width(), lineThickness
)));
4406 nsCSSRendering::GetTextDecorationRect(nsPresContext
* aPresContext
,
4407 const gfxSize
& aLineSize
,
4408 const gfxFloat aAscent
,
4409 const gfxFloat aOffset
,
4410 const uint8_t aDecoration
,
4411 const uint8_t aStyle
,
4413 const gfxFloat aDescentLimit
)
4415 NS_ASSERTION(aPresContext
, "aPresContext is null");
4416 NS_ASSERTION(aStyle
!= NS_STYLE_TEXT_DECORATION_STYLE_NONE
, "aStyle is none");
4419 GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize
, aAscent
, aOffset
,
4420 aDecoration
, aStyle
, aVertical
,
4422 // The rect values are already rounded to nearest device pixels.
4424 r
.x
= aPresContext
->GfxUnitsToAppUnits(rect
.X());
4425 r
.y
= aPresContext
->GfxUnitsToAppUnits(rect
.Y());
4426 r
.width
= aPresContext
->GfxUnitsToAppUnits(rect
.Width());
4427 r
.height
= aPresContext
->GfxUnitsToAppUnits(rect
.Height());
4432 nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint
& aPt
,
4433 const gfxSize
& aLineSize
,
4434 const gfxFloat aAscent
,
4435 const gfxFloat aOffset
,
4436 const uint8_t aDecoration
,
4437 const uint8_t aStyle
,
4439 const gfxFloat aDescentLimit
)
4441 NS_ASSERTION(aStyle
<= NS_STYLE_TEXT_DECORATION_STYLE_WAVY
,
4442 "Invalid aStyle value");
4444 if (aStyle
== NS_STYLE_TEXT_DECORATION_STYLE_NONE
)
4445 return gfxRect(0, 0, 0, 0);
4447 bool canLiftUnderline
= aDescentLimit
>= 0.0;
4449 gfxFloat iCoord
= aVertical
? aPt
.y
: aPt
.x
;
4450 gfxFloat bCoord
= aVertical
? aPt
.x
: aPt
.y
;
4452 // 'left' and 'right' are relative to the line, so for vertical writing modes
4453 // they will actually become top and bottom of the rendered line.
4454 // Similarly, aLineSize.width and .height are actually length and thickness
4455 // of the line, which runs horizontally or vertically according to aVertical.
4456 const gfxFloat left
= floor(iCoord
+ 0.5),
4457 right
= floor(iCoord
+ aLineSize
.width
+ 0.5);
4459 // We compute |r| as if for a horizontal text run, and then swap vertical
4460 // and horizontal coordinates at the end if vertical was requested.
4461 gfxRect
r(left
, 0, right
- left
, 0);
4463 gfxFloat lineThickness
= NS_round(aLineSize
.height
);
4464 lineThickness
= std::max(lineThickness
, 1.0);
4466 gfxFloat ascent
= NS_round(aAscent
);
4467 gfxFloat descentLimit
= floor(aDescentLimit
);
4469 gfxFloat suggestedMaxRectHeight
= std::max(std::min(ascent
, descentLimit
), 1.0);
4470 r
.height
= lineThickness
;
4471 if (aStyle
== NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE
) {
4473 * We will draw double line as:
4475 * +-------------------------------------------+
4476 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
4477 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
4478 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
4482 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
4483 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
4484 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
4485 * +-------------------------------------------+
4487 gfxFloat gap
= NS_round(lineThickness
/ 2.0);
4488 gap
= std::max(gap
, 1.0);
4489 r
.height
= lineThickness
* 2.0 + gap
;
4490 if (canLiftUnderline
) {
4491 if (r
.Height() > suggestedMaxRectHeight
) {
4492 // Don't shrink the line height, because the thickness has some meaning.
4493 // We can just shrink the gap at this time.
4494 r
.height
= std::max(suggestedMaxRectHeight
, lineThickness
* 2.0 + 1.0);
4497 } else if (aStyle
== NS_STYLE_TEXT_DECORATION_STYLE_WAVY
) {
4499 * We will draw wavy line as:
4501 * +-------------------------------------------+
4502 * |XXXXX XXXXXX XXXXXX | ^
4503 * |XXXXXX XXXXXXXX XXXXXXXX | | lineThickness
4504 * |XXXXXXX XXXXXXXXXX XXXXXXXXXX| v
4505 * | XXX XXX XXX XXX XX|
4506 * | XXXXXXXXXX XXXXXXXXXX X|
4507 * | XXXXXXXX XXXXXXXX |
4509 * +-------------------------------------------+
4511 r
.height
= lineThickness
> 2.0 ? lineThickness
* 4.0 : lineThickness
* 3.0;
4512 if (canLiftUnderline
) {
4513 if (r
.Height() > suggestedMaxRectHeight
) {
4514 // Don't shrink the line height even if there is not enough space,
4515 // because the thickness has some meaning. E.g., the 1px wavy line and
4516 // 2px wavy line can be used for different meaning in IME selections
4518 r
.height
= std::max(suggestedMaxRectHeight
, lineThickness
* 2.0);
4523 gfxFloat baseline
= floor(bCoord
+ aAscent
+ 0.5);
4524 gfxFloat offset
= 0.0;
4525 switch (aDecoration
) {
4526 case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE
:
4528 if (canLiftUnderline
) {
4529 if (descentLimit
< -offset
+ r
.Height()) {
4530 // If we can ignore the offset and the decoration line is overflowing,
4531 // we should align the bottom edge of the decoration line rect if it's
4532 // possible. Otherwise, we should lift up the top edge of the rect as
4534 gfxFloat offsetBottomAligned
= -descentLimit
+ r
.Height();
4535 gfxFloat offsetTopAligned
= 0.0;
4536 offset
= std::min(offsetBottomAligned
, offsetTopAligned
);
4540 case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE
:
4541 offset
= aOffset
- lineThickness
+ r
.Height();
4543 case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH
: {
4544 gfxFloat extra
= floor(r
.Height() / 2.0 + 0.5);
4545 extra
= std::max(extra
, lineThickness
);
4546 offset
= aOffset
- lineThickness
+ extra
;
4550 NS_ERROR("Invalid decoration value!");
4554 r
.y
= baseline
+ floor(aOffset
+ 0.5); // this will need updating when we
4555 // support sideways-left orientation
4557 Swap(r
.width
, r
.height
);
4559 r
.y
= baseline
- floor(aOffset
+ 0.5);
4565 // ------------------
4567 // ------------------
4568 nsImageRenderer::nsImageRenderer(nsIFrame
* aForFrame
,
4569 const nsStyleImage
* aImage
,
4571 : mForFrame(aForFrame
)
4573 , mType(aImage
->GetType())
4574 , mImageContainer(nullptr)
4575 , mGradientData(nullptr)
4576 , mPaintServerFrame(nullptr)
4583 nsImageRenderer::~nsImageRenderer()
4588 nsImageRenderer::PrepareImage()
4590 if (mImage
->IsEmpty())
4593 if (!mImage
->IsComplete()) {
4594 // Make sure the image is actually decoding
4595 mImage
->StartDecoding();
4597 // check again to see if we finished
4598 if (!mImage
->IsComplete()) {
4599 // We can not prepare the image for rendering if it is not fully loaded.
4601 // Special case: If we requested a sync decode and we have an image, push
4602 // on through because the Draw() will do a sync decode then
4603 nsCOMPtr
<imgIContainer
> img
;
4604 if (!((mFlags
& FLAG_SYNC_DECODE_IMAGES
) &&
4605 (mType
== eStyleImageType_Image
) &&
4606 (NS_SUCCEEDED(mImage
->GetImageData()->GetImage(getter_AddRefs(img
))))))
4612 case eStyleImageType_Image
:
4614 nsCOMPtr
<imgIContainer
> srcImage
;
4615 DebugOnly
<nsresult
> rv
=
4616 mImage
->GetImageData()->GetImage(getter_AddRefs(srcImage
));
4617 NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv
) && srcImage
,
4618 "If GetImage() is failing, mImage->IsComplete() "
4619 "should have returned false");
4621 if (!mImage
->GetCropRect()) {
4622 mImageContainer
.swap(srcImage
);
4624 nsIntRect actualCropRect
;
4627 mImage
->ComputeActualCropRect(actualCropRect
, &isEntireImage
);
4628 NS_ASSERTION(success
, "ComputeActualCropRect() should not fail here");
4629 if (!success
|| actualCropRect
.IsEmpty()) {
4630 // The cropped image has zero size
4633 if (isEntireImage
) {
4634 // The cropped image is identical to the source image
4635 mImageContainer
.swap(srcImage
);
4637 nsCOMPtr
<imgIContainer
> subImage
= ImageOps::Clip(srcImage
, actualCropRect
);
4638 mImageContainer
.swap(subImage
);
4644 case eStyleImageType_Gradient
:
4645 mGradientData
= mImage
->GetGradientData();
4648 case eStyleImageType_Element
:
4650 nsAutoString elementId
=
4651 NS_LITERAL_STRING("#") + nsDependentString(mImage
->GetElementId());
4652 nsCOMPtr
<nsIURI
> targetURI
;
4653 nsCOMPtr
<nsIURI
> base
= mForFrame
->GetContent()->GetBaseURI();
4654 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI
), elementId
,
4655 mForFrame
->GetContent()->GetCurrentDoc(), base
);
4656 nsSVGPaintingProperty
* property
= nsSVGEffects::GetPaintingPropertyForURI(
4657 targetURI
, mForFrame
->FirstContinuation(),
4658 nsSVGEffects::BackgroundImageProperty());
4661 mPaintServerFrame
= property
->GetReferencedFrame();
4663 // If the referenced element doesn't have a frame we might still be able
4664 // to paint it if it's an <img>, <canvas>, or <video> element.
4665 if (!mPaintServerFrame
) {
4666 mImageElementSurface
=
4667 nsLayoutUtils::SurfaceFromElement(property
->GetReferencedElement());
4668 if (!mImageElementSurface
.mSourceSurface
)
4674 case eStyleImageType_Null
:
4683 CSSSizeOrRatio::ComputeConcreteSize() const
4685 NS_ASSERTION(CanComputeConcreteSize(), "Cannot compute");
4686 if (mHasWidth
&& mHasHeight
) {
4687 return nsSize(mWidth
, mHeight
);
4690 nscoord height
= NSCoordSaturatingNonnegativeMultiply(
4692 double(mRatio
.height
) / mRatio
.width
);
4693 return nsSize(mWidth
, height
);
4696 MOZ_ASSERT(mHasHeight
);
4697 nscoord width
= NSCoordSaturatingNonnegativeMultiply(
4699 double(mRatio
.width
) / mRatio
.height
);
4700 return nsSize(width
, mHeight
);
4704 nsImageRenderer::ComputeIntrinsicSize()
4706 NS_ASSERTION(mIsReady
, "Ensure PrepareImage() has returned true "
4707 "before calling me");
4709 CSSSizeOrRatio result
;
4711 case eStyleImageType_Image
:
4713 bool haveWidth
, haveHeight
;
4714 nsIntSize imageIntSize
;
4715 nsLayoutUtils::ComputeSizeForDrawing(mImageContainer
, imageIntSize
,
4716 result
.mRatio
, haveWidth
, haveHeight
);
4718 result
.SetWidth(nsPresContext::CSSPixelsToAppUnits(imageIntSize
.width
));
4721 result
.SetHeight(nsPresContext::CSSPixelsToAppUnits(imageIntSize
.height
));
4725 case eStyleImageType_Element
:
4727 // XXX element() should have the width/height of the referenced element,
4728 // and that element's ratio, if it matches. If it doesn't match, it
4729 // should have no width/height or ratio. See element() in CSS images:
4730 // <http://dev.w3.org/csswg/css-images-4/#element-notation>.
4731 // Make sure to change nsStyleBackground::Size::DependsOnFrameSize
4732 // when fixing this!
4733 if (mPaintServerFrame
) {
4734 // SVG images have no intrinsic size
4735 if (!mPaintServerFrame
->IsFrameOfType(nsIFrame::eSVG
)) {
4736 // The intrinsic image size for a generic nsIFrame paint server is
4737 // the union of the border-box rects of all of its continuations,
4738 // rounded to device pixels.
4739 int32_t appUnitsPerDevPixel
=
4740 mForFrame
->PresContext()->AppUnitsPerDevPixel();
4742 nsSVGIntegrationUtils::GetContinuationUnionSize(mPaintServerFrame
).
4743 ToNearestPixels(appUnitsPerDevPixel
).
4744 ToAppUnits(appUnitsPerDevPixel
));
4747 NS_ASSERTION(mImageElementSurface
.mSourceSurface
, "Surface should be ready.");
4748 gfxIntSize surfaceSize
= mImageElementSurface
.mSize
;
4750 nsSize(nsPresContext::CSSPixelsToAppUnits(surfaceSize
.width
),
4751 nsPresContext::CSSPixelsToAppUnits(surfaceSize
.height
)));
4755 case eStyleImageType_Gradient
:
4756 // Per <http://dev.w3.org/csswg/css3-images/#gradients>, gradients have no
4757 // intrinsic dimensions.
4758 case eStyleImageType_Null
:
4767 nsImageRenderer::ComputeConcreteSize(const CSSSizeOrRatio
& aSpecifiedSize
,
4768 const CSSSizeOrRatio
& aIntrinsicSize
,
4769 const nsSize
& aDefaultSize
)
4771 // The specified size is fully specified, just use that
4772 if (aSpecifiedSize
.IsConcrete()) {
4773 return aSpecifiedSize
.ComputeConcreteSize();
4776 MOZ_ASSERT(!aSpecifiedSize
.mHasWidth
|| !aSpecifiedSize
.mHasHeight
);
4778 if (!aSpecifiedSize
.mHasWidth
&& !aSpecifiedSize
.mHasHeight
) {
4779 // no specified size, try using the intrinsic size
4780 if (aIntrinsicSize
.CanComputeConcreteSize()) {
4781 return aIntrinsicSize
.ComputeConcreteSize();
4784 if (aIntrinsicSize
.mHasWidth
) {
4785 return nsSize(aIntrinsicSize
.mWidth
, aDefaultSize
.height
);
4787 if (aIntrinsicSize
.mHasHeight
) {
4788 return nsSize(aDefaultSize
.width
, aIntrinsicSize
.mHeight
);
4791 // couldn't use the intrinsic size either, revert to using the default size
4792 return ComputeConstrainedSize(aDefaultSize
,
4793 aIntrinsicSize
.mRatio
,
4797 MOZ_ASSERT(aSpecifiedSize
.mHasWidth
|| aSpecifiedSize
.mHasHeight
);
4799 // The specified height is partial, try to compute the missing part.
4800 if (aSpecifiedSize
.mHasWidth
) {
4802 if (aIntrinsicSize
.HasRatio()) {
4803 height
= NSCoordSaturatingNonnegativeMultiply(
4804 aSpecifiedSize
.mWidth
,
4805 double(aIntrinsicSize
.mRatio
.height
) / aIntrinsicSize
.mRatio
.width
);
4806 } else if (aIntrinsicSize
.mHasHeight
) {
4807 height
= aIntrinsicSize
.mHeight
;
4809 height
= aDefaultSize
.height
;
4811 return nsSize(aSpecifiedSize
.mWidth
, height
);
4814 MOZ_ASSERT(aSpecifiedSize
.mHasHeight
);
4816 if (aIntrinsicSize
.HasRatio()) {
4817 width
= NSCoordSaturatingNonnegativeMultiply(
4818 aSpecifiedSize
.mHeight
,
4819 double(aIntrinsicSize
.mRatio
.width
) / aIntrinsicSize
.mRatio
.height
);
4820 } else if (aIntrinsicSize
.mHasWidth
) {
4821 width
= aIntrinsicSize
.mWidth
;
4823 width
= aDefaultSize
.width
;
4825 return nsSize(width
, aSpecifiedSize
.mHeight
);
4829 nsImageRenderer::ComputeConstrainedSize(const nsSize
& aConstrainingSize
,
4830 const nsSize
& aIntrinsicRatio
,
4833 if (aIntrinsicRatio
.width
<= 0 && aIntrinsicRatio
.height
<= 0) {
4834 return aConstrainingSize
;
4837 float scaleX
= double(aConstrainingSize
.width
) / aIntrinsicRatio
.width
;
4838 float scaleY
= double(aConstrainingSize
.height
) / aIntrinsicRatio
.height
;
4840 if ((aFitType
== CONTAIN
) == (scaleX
< scaleY
)) {
4841 size
.width
= aConstrainingSize
.width
;
4842 size
.height
= NSCoordSaturatingNonnegativeMultiply(
4843 aIntrinsicRatio
.height
, scaleX
);
4845 size
.width
= NSCoordSaturatingNonnegativeMultiply(
4846 aIntrinsicRatio
.width
, scaleY
);
4847 size
.height
= aConstrainingSize
.height
;
4853 * mSize is the image's "preferred" size for this particular rendering, while
4854 * the drawn (aka concrete) size is the actual rendered size after accounting
4855 * for background-size etc.. The preferred size is most often the image's
4856 * intrinsic dimensions. But for images with incomplete intrinsic dimensions,
4857 * the preferred size varies, depending on the specified and default sizes, see
4858 * nsImageRenderer::Compute*Size.
4860 * This distinction is necessary because the components of a vector image are
4861 * specified with respect to its preferred size for a rendering situation, not
4862 * to its actual rendered size. For example, consider a 4px wide background
4863 * vector image with no height which contains a left-aligned
4864 * 2px wide black rectangle with height 100%. If the background-size width is
4865 * auto (or 4px), the vector image will render 4px wide, and the black rectangle
4866 * will be 2px wide. If the background-size width is 8px, the vector image will
4867 * render 8px wide, and the black rectangle will be 4px wide -- *not* 2px wide.
4868 * In both cases mSize.width will be 4px; but in the first case the returned
4869 * width will be 4px, while in the second case the returned width will be 8px.
4872 nsImageRenderer::SetPreferredSize(const CSSSizeOrRatio
& aIntrinsicSize
,
4873 const nsSize
& aDefaultSize
)
4875 mSize
.width
= aIntrinsicSize
.mHasWidth
4876 ? aIntrinsicSize
.mWidth
4877 : aDefaultSize
.width
;
4878 mSize
.height
= aIntrinsicSize
.mHasHeight
4879 ? aIntrinsicSize
.mHeight
4880 : aDefaultSize
.height
;
4883 // Convert from nsImageRenderer flags to the flags we want to use for drawing in
4884 // the imgIContainer namespace.
4886 ConvertImageRendererToDrawFlags(uint32_t aImageRendererFlags
)
4888 uint32_t drawFlags
= imgIContainer::FLAG_NONE
;
4889 if (aImageRendererFlags
& nsImageRenderer::FLAG_SYNC_DECODE_IMAGES
) {
4890 drawFlags
|= imgIContainer::FLAG_SYNC_DECODE
;
4892 if (aImageRendererFlags
& nsImageRenderer::FLAG_PAINTING_TO_WINDOW
) {
4893 drawFlags
|= imgIContainer::FLAG_HIGH_QUALITY_SCALING
;
4899 nsImageRenderer::Draw(nsPresContext
* aPresContext
,
4900 nsRenderingContext
& aRenderingContext
,
4901 const nsRect
& aDirtyRect
,
4902 const nsRect
& aDest
,
4903 const nsRect
& aFill
,
4904 const nsPoint
& aAnchor
,
4905 const CSSIntRect
& aSrc
)
4908 NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
4909 return DrawResult::TEMPORARY_ERROR
;
4911 if (aDest
.IsEmpty() || aFill
.IsEmpty() ||
4912 mSize
.width
<= 0 || mSize
.height
<= 0) {
4913 return DrawResult::SUCCESS
;
4916 GraphicsFilter filter
= nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame
);
4919 case eStyleImageType_Image
:
4921 nsIntSize
imageSize(nsPresContext::AppUnitsToIntCSSPixels(mSize
.width
),
4922 nsPresContext::AppUnitsToIntCSSPixels(mSize
.height
));
4924 nsLayoutUtils::DrawBackgroundImage(*aRenderingContext
.ThebesContext(),
4926 mImageContainer
, imageSize
, filter
,
4927 aDest
, aFill
, aAnchor
, aDirtyRect
,
4928 ConvertImageRendererToDrawFlags(mFlags
));
4930 case eStyleImageType_Gradient
:
4932 nsCSSRendering::PaintGradient(aPresContext
, aRenderingContext
,
4933 mGradientData
, aDirtyRect
,
4934 aDest
, aFill
, aSrc
, mSize
);
4935 return DrawResult::SUCCESS
;
4937 case eStyleImageType_Element
:
4939 nsRefPtr
<gfxDrawable
> drawable
= DrawableForElement(aDest
,
4942 NS_WARNING("Could not create drawable for element");
4943 return DrawResult::TEMPORARY_ERROR
;
4946 gfxContext
* ctx
= aRenderingContext
.ThebesContext();
4947 gfxContext::GraphicsOperator op
= ctx
->CurrentOperator();
4948 if (op
!= gfxContext::OPERATOR_OVER
) {
4949 ctx
->PushGroup(gfxContentType::COLOR_ALPHA
);
4950 ctx
->SetOperator(gfxContext::OPERATOR_OVER
);
4953 nsCOMPtr
<imgIContainer
> image(ImageOps::CreateFromDrawable(drawable
));
4954 nsLayoutUtils::DrawImage(*aRenderingContext
.ThebesContext(),
4955 aPresContext
, image
,
4956 filter
, aDest
, aFill
, aAnchor
, aDirtyRect
,
4957 ConvertImageRendererToDrawFlags(mFlags
));
4959 if (op
!= gfxContext::OPERATOR_OVER
) {
4960 ctx
->PopGroupToSource();
4964 return DrawResult::SUCCESS
;
4966 case eStyleImageType_Null
:
4968 return DrawResult::SUCCESS
;
4972 already_AddRefed
<gfxDrawable
>
4973 nsImageRenderer::DrawableForElement(const nsRect
& aImageRect
,
4974 nsRenderingContext
& aRenderingContext
)
4976 NS_ASSERTION(mType
== eStyleImageType_Element
,
4977 "DrawableForElement only makes sense if backed by an element");
4978 if (mPaintServerFrame
) {
4979 int32_t appUnitsPerDevPixel
= mForFrame
->PresContext()->AppUnitsPerDevPixel();
4980 nsRect destRect
= aImageRect
- aImageRect
.TopLeft();
4981 nsIntSize roundedOut
= destRect
.ToOutsidePixels(appUnitsPerDevPixel
).Size();
4982 gfxIntSize
imageSize(roundedOut
.width
, roundedOut
.height
);
4983 nsRefPtr
<gfxDrawable
> drawable
=
4984 nsSVGIntegrationUtils::DrawableFromPaintServer(
4985 mPaintServerFrame
, mForFrame
, mSize
, imageSize
,
4986 aRenderingContext
.GetDrawTarget(),
4987 aRenderingContext
.ThebesContext()->CurrentMatrix(),
4988 mFlags
& FLAG_SYNC_DECODE_IMAGES
4989 ? nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES
4992 return drawable
.forget();
4994 NS_ASSERTION(mImageElementSurface
.mSourceSurface
, "Surface should be ready.");
4995 nsRefPtr
<gfxDrawable
> drawable
= new gfxSurfaceDrawable(
4996 mImageElementSurface
.mSourceSurface
,
4997 mImageElementSurface
.mSize
);
4998 return drawable
.forget();
5002 nsImageRenderer::DrawBackground(nsPresContext
* aPresContext
,
5003 nsRenderingContext
& aRenderingContext
,
5004 const nsRect
& aDest
,
5005 const nsRect
& aFill
,
5006 const nsPoint
& aAnchor
,
5007 const nsRect
& aDirty
)
5010 NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
5011 return DrawResult::TEMPORARY_ERROR
;
5013 if (aDest
.IsEmpty() || aFill
.IsEmpty() ||
5014 mSize
.width
<= 0 || mSize
.height
<= 0) {
5015 return DrawResult::SUCCESS
;
5018 return Draw(aPresContext
, aRenderingContext
,
5019 aDirty
, aDest
, aFill
, aAnchor
,
5021 nsPresContext::AppUnitsToIntCSSPixels(mSize
.width
),
5022 nsPresContext::AppUnitsToIntCSSPixels(mSize
.height
)));
5026 * Compute the size and position of the master copy of the image. I.e., a single
5027 * tile used to fill the dest rect.
5028 * aFill The destination rect to be filled
5029 * aHFill and aVFill are the repeat patterns for the component -
5030 * NS_STYLE_BORDER_IMAGE_REPEAT_* - i.e., how a tiling unit is used to fill aFill
5031 * aUnitSize The size of the source rect in dest coords.
5034 ComputeTile(const nsRect
& aFill
,
5037 const nsSize
& aUnitSize
)
5041 case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH
:
5043 tile
.width
= aFill
.width
;
5045 case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT
:
5046 tile
.x
= aFill
.x
+ aFill
.width
/2 - aUnitSize
.width
/2;
5047 tile
.width
= aUnitSize
.width
;
5049 case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND
:
5051 tile
.width
= aFill
.width
/ ceil(gfxFloat(aFill
.width
)/aUnitSize
.width
);
5054 NS_NOTREACHED("unrecognized border-image fill style");
5058 case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH
:
5060 tile
.height
= aFill
.height
;
5062 case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT
:
5063 tile
.y
= aFill
.y
+ aFill
.height
/2 - aUnitSize
.height
/2;
5064 tile
.height
= aUnitSize
.height
;
5066 case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND
:
5068 tile
.height
= aFill
.height
/ceil(gfxFloat(aFill
.height
)/aUnitSize
.height
);
5071 NS_NOTREACHED("unrecognized border-image fill style");
5078 * Returns true if the given set of arguments will require the tiles which fill
5079 * the dest rect to be scaled from the source tile. See comment on ComputeTile
5080 * for argument descriptions.
5083 RequiresScaling(const nsRect
& aFill
,
5086 const nsSize
& aUnitSize
)
5088 // If we have no tiling in either direction, we can skip the intermediate
5090 return (aHFill
!= NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH
||
5091 aVFill
!= NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH
) &&
5092 (aUnitSize
.width
!= aFill
.width
||
5093 aUnitSize
.height
!= aFill
.height
);
5097 nsImageRenderer::DrawBorderImageComponent(nsPresContext
* aPresContext
,
5098 nsRenderingContext
& aRenderingContext
,
5099 const nsRect
& aDirtyRect
,
5100 const nsRect
& aFill
,
5101 const CSSIntRect
& aSrc
,
5104 const nsSize
& aUnitSize
,
5108 NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
5111 if (aFill
.IsEmpty() || aSrc
.IsEmpty()) {
5115 if (mType
== eStyleImageType_Image
|| mType
== eStyleImageType_Element
) {
5116 nsCOMPtr
<imgIContainer
> subImage
;
5118 // Retrieve or create the subimage we'll draw.
5119 nsIntRect
srcRect(aSrc
.x
, aSrc
.y
, aSrc
.width
, aSrc
.height
);
5120 if (mType
== eStyleImageType_Image
) {
5121 if ((subImage
= mImage
->GetSubImage(aIndex
)) == nullptr) {
5122 subImage
= ImageOps::Clip(mImageContainer
, srcRect
);
5123 mImage
->SetSubImage(aIndex
, subImage
);
5126 // This path, for eStyleImageType_Element, is currently slower than it
5127 // needs to be because we don't cache anything. (In particular, if we have
5128 // to draw to a temporary surface inside ClippedImage, we don't cache that
5129 // temporary surface since we immediately throw the ClippedImage we create
5130 // here away.) However, if we did cache, we'd need to know when to
5131 // invalidate that cache, and it's not clear that it's worth the trouble
5132 // since using border-image with -moz-element is rare.
5134 nsRefPtr
<gfxDrawable
> drawable
= DrawableForElement(nsRect(nsPoint(), mSize
),
5137 NS_WARNING("Could not create drawable for element");
5141 nsCOMPtr
<imgIContainer
> image(ImageOps::CreateFromDrawable(drawable
));
5142 subImage
= ImageOps::Clip(image
, srcRect
);
5145 GraphicsFilter graphicsFilter
=
5146 nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame
);
5148 if (!RequiresScaling(aFill
, aHFill
, aVFill
, aUnitSize
)) {
5149 nsLayoutUtils::DrawSingleImage(*aRenderingContext
.ThebesContext(),
5155 imgIContainer::FLAG_NONE
);
5159 nsRect tile
= ComputeTile(aFill
, aHFill
, aVFill
, aUnitSize
);
5160 nsLayoutUtils::DrawImage(*aRenderingContext
.ThebesContext(),
5164 tile
, aFill
, tile
.TopLeft(), aDirtyRect
,
5165 imgIContainer::FLAG_NONE
);
5169 nsRect destTile
= RequiresScaling(aFill
, aHFill
, aVFill
, aUnitSize
)
5170 ? ComputeTile(aFill
, aHFill
, aVFill
, aUnitSize
)
5173 Draw(aPresContext
, aRenderingContext
, aDirtyRect
, destTile
,
5174 aFill
, destTile
.TopLeft(), aSrc
);
5178 nsImageRenderer::IsRasterImage()
5180 if (mType
!= eStyleImageType_Image
|| !mImageContainer
)
5182 return mImageContainer
->GetType() == imgIContainer::TYPE_RASTER
;
5186 nsImageRenderer::IsAnimatedImage()
5188 if (mType
!= eStyleImageType_Image
|| !mImageContainer
)
5190 bool animated
= false;
5191 if (NS_SUCCEEDED(mImageContainer
->GetAnimated(&animated
)) && animated
)
5197 already_AddRefed
<mozilla::layers::ImageContainer
>
5198 nsImageRenderer::GetContainer(LayerManager
* aManager
)
5200 if (mType
!= eStyleImageType_Image
|| !mImageContainer
)
5203 nsRefPtr
<ImageContainer
> container
;
5204 nsresult rv
= mImageContainer
->GetImageContainer(aManager
, getter_AddRefs(container
));
5205 NS_ENSURE_SUCCESS(rv
, nullptr);
5206 return container
.forget();
5209 #define MAX_BLUR_RADIUS 300
5210 #define MAX_SPREAD_RADIUS 50
5212 static inline gfxPoint
ComputeBlurStdDev(nscoord aBlurRadius
,
5213 int32_t aAppUnitsPerDevPixel
,
5217 // http://dev.w3.org/csswg/css3-background/#box-shadow says that the
5218 // standard deviation of the blur should be half the given blur value.
5219 gfxFloat blurStdDev
= gfxFloat(aBlurRadius
) / gfxFloat(aAppUnitsPerDevPixel
);
5221 return gfxPoint(std::min((blurStdDev
* aScaleX
),
5222 gfxFloat(MAX_BLUR_RADIUS
)) / 2.0,
5223 std::min((blurStdDev
* aScaleY
),
5224 gfxFloat(MAX_BLUR_RADIUS
)) / 2.0);
5227 static inline gfxIntSize
5228 ComputeBlurRadius(nscoord aBlurRadius
,
5229 int32_t aAppUnitsPerDevPixel
,
5230 gfxFloat aScaleX
= 1.0,
5231 gfxFloat aScaleY
= 1.0)
5233 gfxPoint scaledBlurStdDev
= ComputeBlurStdDev(aBlurRadius
, aAppUnitsPerDevPixel
,
5236 gfxAlphaBoxBlur::CalculateBlurRadius(scaledBlurStdDev
);
5243 nsContextBoxBlur::Init(const nsRect
& aRect
, nscoord aSpreadRadius
,
5244 nscoord aBlurRadius
,
5245 int32_t aAppUnitsPerDevPixel
,
5246 gfxContext
* aDestinationCtx
,
5247 const nsRect
& aDirtyRect
,
5248 const gfxRect
* aSkipRect
,
5251 if (aRect
.IsEmpty()) {
5256 gfxFloat scaleX
= 1;
5257 gfxFloat scaleY
= 1;
5259 // Do blurs in device space when possible.
5260 // Chrome/Skia always does the blurs in device space
5261 // and will sometimes get incorrect results (e.g. rotated blurs)
5262 gfxMatrix transform
= aDestinationCtx
->CurrentMatrix();
5263 // XXX: we could probably handle negative scales but for now it's easier just to fallback
5264 if (transform
.HasNonAxisAlignedTransform() || transform
._11
<= 0.0 || transform
._22
<= 0.0) {
5265 transform
= gfxMatrix();
5267 scaleX
= transform
._11
;
5268 scaleY
= transform
._22
;
5271 // compute a large or smaller blur radius
5272 gfxIntSize blurRadius
= ComputeBlurRadius(aBlurRadius
, aAppUnitsPerDevPixel
, scaleX
, scaleY
);
5273 gfxIntSize spreadRadius
= gfxIntSize(std::min(int32_t(aSpreadRadius
* scaleX
/ aAppUnitsPerDevPixel
),
5274 int32_t(MAX_SPREAD_RADIUS
)),
5275 std::min(int32_t(aSpreadRadius
* scaleY
/ aAppUnitsPerDevPixel
),
5276 int32_t(MAX_SPREAD_RADIUS
)));
5277 mDestinationCtx
= aDestinationCtx
;
5279 // If not blurring, draw directly onto the destination device
5280 if (blurRadius
.width
<= 0 && blurRadius
.height
<= 0 &&
5281 spreadRadius
.width
<= 0 && spreadRadius
.height
<= 0 &&
5282 !(aFlags
& FORCE_MASK
)) {
5283 mContext
= aDestinationCtx
;
5287 // Convert from app units to device pixels
5288 gfxRect rect
= nsLayoutUtils::RectToGfxRect(aRect
, aAppUnitsPerDevPixel
);
5291 nsLayoutUtils::RectToGfxRect(aDirtyRect
, aAppUnitsPerDevPixel
);
5292 dirtyRect
.RoundOut();
5294 rect
= transform
.TransformBounds(rect
);
5296 mPreTransformed
= !transform
.IsIdentity();
5298 // Create the temporary surface for blurring
5299 dirtyRect
= transform
.TransformBounds(dirtyRect
);
5301 gfxRect skipRect
= transform
.TransformBounds(*aSkipRect
);
5302 mContext
= mAlphaBoxBlur
.Init(rect
, spreadRadius
,
5303 blurRadius
, &dirtyRect
, &skipRect
);
5305 mContext
= mAlphaBoxBlur
.Init(rect
, spreadRadius
,
5306 blurRadius
, &dirtyRect
, nullptr);
5310 // we don't need to blur if skipRect is equal to rect
5311 // and mContext will be nullptr
5312 mContext
->Multiply(transform
);
5318 nsContextBoxBlur::DoPaint()
5320 if (mContext
== mDestinationCtx
)
5323 gfxContextMatrixAutoSaveRestore
saveMatrix(mDestinationCtx
);
5325 if (mPreTransformed
) {
5326 mDestinationCtx
->SetMatrix(gfxMatrix());
5329 mAlphaBoxBlur
.Paint(mDestinationCtx
);
5333 nsContextBoxBlur::GetContext()
5338 /* static */ nsMargin
5339 nsContextBoxBlur::GetBlurRadiusMargin(nscoord aBlurRadius
,
5340 int32_t aAppUnitsPerDevPixel
)
5342 gfxIntSize blurRadius
= ComputeBlurRadius(aBlurRadius
, aAppUnitsPerDevPixel
);
5345 result
.top
= result
.bottom
= blurRadius
.height
* aAppUnitsPerDevPixel
;
5346 result
.left
= result
.right
= blurRadius
.width
* aAppUnitsPerDevPixel
;
5351 nsContextBoxBlur::BlurRectangle(gfxContext
* aDestinationCtx
,
5352 const nsRect
& aRect
,
5353 int32_t aAppUnitsPerDevPixel
,
5354 RectCornerRadii
* aCornerRadii
,
5355 nscoord aBlurRadius
,
5356 const gfxRGBA
& aShadowColor
,
5357 const nsRect
& aDirtyRect
,
5358 const gfxRect
& aSkipRect
)
5360 DrawTarget
& aDestDrawTarget
= *aDestinationCtx
->GetDrawTarget();
5362 if (aRect
.IsEmpty()) {
5366 Rect shadowGfxRect
= NSRectToRect(aRect
, aAppUnitsPerDevPixel
);
5368 if (aBlurRadius
<= 0) {
5369 ColorPattern
color(ToDeviceColor(aShadowColor
));
5371 RefPtr
<Path
> roundedRect
= MakePathForRoundedRect(aDestDrawTarget
,
5374 aDestDrawTarget
.Fill(roundedRect
, color
);
5376 aDestDrawTarget
.FillRect(shadowGfxRect
, color
);
5381 gfxFloat scaleX
= 1;
5382 gfxFloat scaleY
= 1;
5384 // Do blurs in device space when possible.
5385 // Chrome/Skia always does the blurs in device space
5386 // and will sometimes get incorrect results (e.g. rotated blurs)
5387 gfxMatrix transform
= aDestinationCtx
->CurrentMatrix();
5388 // XXX: we could probably handle negative scales but for now it's easier just to fallback
5389 if (!transform
.HasNonAxisAlignedTransform() && transform
._11
> 0.0 && transform
._22
> 0.0) {
5390 scaleX
= transform
._11
;
5391 scaleY
= transform
._22
;
5392 aDestinationCtx
->SetMatrix(gfxMatrix());
5394 transform
= gfxMatrix();
5397 gfxPoint blurStdDev
= ComputeBlurStdDev(aBlurRadius
, aAppUnitsPerDevPixel
, scaleX
, scaleY
);
5400 nsLayoutUtils::RectToGfxRect(aDirtyRect
, aAppUnitsPerDevPixel
);
5401 dirtyRect
.RoundOut();
5403 gfxRect shadowThebesRect
= transform
.TransformBounds(ThebesRect(shadowGfxRect
));
5404 dirtyRect
= transform
.TransformBounds(dirtyRect
);
5405 gfxRect skipRect
= transform
.TransformBounds(aSkipRect
);
5408 aCornerRadii
->Scale(scaleX
, scaleY
);
5411 gfxAlphaBoxBlur::BlurRectangle(aDestinationCtx
,