1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Mats Palmgren <matspal@gmail.com>
25 * Takeshi Ichimaru <ayakawa.m@gmail.com>
26 * Masayuki Nakano <masayuki@d-toybox.com>
27 * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
28 * Michael Ventnor <m.ventnor@gmail.com>
29 * Rob Arnold <robarnold@mozilla.com>
30 * Jeff Walden <jwalden+code@mit.edu>
32 * Alternatively, the contents of this file may be used under the terms of
33 * either of the GNU General Public License Version 2 or later (the "GPL"),
34 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 * in which case the provisions of the GPL or the LGPL are applicable instead
36 * of those above. If you wish to allow use of your version of this file only
37 * under the terms of either the GPL or the LGPL, and not to allow others to
38 * use your version of this file under the terms of the MPL, indicate your
39 * decision by deleting the provisions above and replace them with the notice
40 * and other provisions required by the GPL or the LGPL. If you do not delete
41 * the provisions above, a recipient may use your version of this file under
42 * the terms of any one of the MPL, the GPL or the LGPL.
44 * ***** END LICENSE BLOCK ***** */
46 /* utility functions for drawing borders and backgrounds */
48 #include "nsStyleConsts.h"
49 #include "nsPresContext.h"
53 #include "nsIViewManager.h"
54 #include "nsIPresShell.h"
55 #include "nsFrameManager.h"
56 #include "nsStyleContext.h"
57 #include "nsGkAtoms.h"
58 #include "nsCSSAnonBoxes.h"
59 #include "nsTransform2D.h"
60 #include "nsIDeviceContext.h"
61 #include "nsIContent.h"
62 #include "nsIDocument.h"
63 #include "nsIScrollableFrame.h"
64 #include "imgIRequest.h"
65 #include "imgIContainer.h"
66 #include "nsCSSRendering.h"
67 #include "nsCSSColorUtils.h"
69 #include "nsThemeConstants.h"
70 #include "nsIServiceManager.h"
71 #include "nsIHTMLDocument.h"
72 #include "nsLayoutUtils.h"
73 #include "nsINameSpaceManager.h"
74 #include "nsBlockFrame.h"
75 #include "gfxContext.h"
76 #include "nsIInterfaceRequestorUtils.h"
77 #include "gfxPlatform.h"
78 #include "gfxImageSurface.h"
79 #include "nsStyleStructInlines.h"
80 #include "nsCSSFrameConstructor.h"
81 #include "nsCSSProps.h"
82 #include "nsContentUtils.h"
84 #include "nsSVGEffects.h"
85 #include "nsSVGIntegrationUtils.h"
86 #include "gfxDrawable.h"
89 #include "nsCSSRenderingBorders.h"
92 * This is a small wrapper class to encapsulate image drawing that can draw an
93 * nsStyleImage image, which may internally be a real image, a sub image, or a
96 * @note Always call the member functions in the order of PrepareImage(),
97 * ComputeSize(), and Draw().
102 FLAG_SYNC_DECODE_IMAGES
= 0x01
104 ImageRenderer(nsIFrame
* aForFrame
, const nsStyleImage
* aImage
, PRUint32 aFlags
);
107 * Populates member variables to get ready for rendering.
108 * @return PR_TRUE iff the image is ready, and there is at least a pixel to
111 PRBool
PrepareImage();
113 * @return the image size in appunits. CSS gradient images don't have an
114 * intrinsic size so we have to pass in a default that they will use.
116 nsSize
ComputeSize(const nsSize
& aDefault
);
118 * Draws the image to the target rendering context.
119 * @see nsLayoutUtils::DrawImage() for other parameters
121 void Draw(nsPresContext
* aPresContext
,
122 nsIRenderingContext
& aRenderingContext
,
125 const nsPoint
& aAnchor
,
126 const nsRect
& aDirty
);
130 const nsStyleImage
* mImage
;
131 nsStyleImageType mType
;
132 nsCOMPtr
<imgIContainer
> mImageContainer
;
133 nsRefPtr
<nsStyleGradient
> mGradientData
;
135 nsIFrame
* mPaintServerFrame
;
136 nsLayoutUtils::SurfaceFromElementResult mImageElementSurface
;
143 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
144 // recalculating this for each frame in a continuation (perf), hold
145 // a cache of various coordinate information that we need in order
146 // to paint inline backgrounds.
147 struct InlineBackgroundData
149 InlineBackgroundData()
150 : mFrame(nsnull
), mBlockFrame(nsnull
)
154 ~InlineBackgroundData()
160 mBoundingBox
.SetRect(0,0,0,0);
161 mContinuationPoint
= mLineContinuationPoint
= mUnbrokenWidth
= 0;
162 mFrame
= mBlockFrame
= nsnull
;
165 nsRect
GetContinuousRect(nsIFrame
* aFrame
)
171 x
= mLineContinuationPoint
;
173 // Scan continuations on the same line as aFrame and accumulate the widths
174 // of frames that are to the left (if this is an LTR block) or right
175 // (if it's RTL) of the current one.
176 PRBool isRtlBlock
= (mBlockFrame
->GetStyleVisibility()->mDirection
==
177 NS_STYLE_DIRECTION_RTL
);
178 nscoord curOffset
= aFrame
->GetOffsetTo(mBlockFrame
).x
;
180 // No need to use our GetPrevContinuation/GetNextContinuation methods
181 // here, since ib special siblings are certainly not on the same line.
183 nsIFrame
* inlineFrame
= aFrame
->GetPrevContinuation();
184 // If the continuation is fluid we know inlineFrame is not on the same line.
185 // If it's not fluid, we need to test further to be sure.
186 while (inlineFrame
&& !inlineFrame
->GetNextInFlow() &&
187 AreOnSameLine(aFrame
, inlineFrame
)) {
188 nscoord frameXOffset
= inlineFrame
->GetOffsetTo(mBlockFrame
).x
;
189 if(isRtlBlock
== (frameXOffset
>= curOffset
)) {
190 x
+= inlineFrame
->GetSize().width
;
192 inlineFrame
= inlineFrame
->GetPrevContinuation();
195 inlineFrame
= aFrame
->GetNextContinuation();
196 while (inlineFrame
&& !inlineFrame
->GetPrevInFlow() &&
197 AreOnSameLine(aFrame
, inlineFrame
)) {
198 nscoord frameXOffset
= inlineFrame
->GetOffsetTo(mBlockFrame
).x
;
199 if(isRtlBlock
== (frameXOffset
>= curOffset
)) {
200 x
+= inlineFrame
->GetSize().width
;
202 inlineFrame
= inlineFrame
->GetNextContinuation();
205 // aFrame itself is also to the right of its left edge, so add its width.
206 x
+= aFrame
->GetSize().width
;
207 // x is now the distance from the left edge of aFrame to the right edge
208 // of the unbroken content. Change it to indicate the distance from the
209 // left edge of the unbroken content to the left edge of aFrame.
210 x
= mUnbrokenWidth
- x
;
213 x
= mContinuationPoint
;
216 // Assume background-origin: border and return a rect with offsets
217 // relative to (0,0). If we have a different background-origin,
218 // then our rect should be deflated appropriately by our caller.
219 return nsRect(-x
, 0, mUnbrokenWidth
, mFrame
->GetSize().height
);
222 nsRect
GetBoundingRect(nsIFrame
* aFrame
)
226 // Move the offsets relative to (0,0) which puts the bounding box into
227 // our coordinate system rather than our parent's. We do this by
228 // moving it the back distance from us to the bounding box.
229 // This also assumes background-origin: border, so our caller will
230 // need to deflate us if needed.
231 nsRect
boundingBox(mBoundingBox
);
232 nsPoint point
= mFrame
->GetPosition();
233 boundingBox
.MoveBy(-point
.x
, -point
.y
);
240 nsBlockFrame
* mBlockFrame
;
242 nscoord mContinuationPoint
;
243 nscoord mUnbrokenWidth
;
244 nscoord mLineContinuationPoint
;
247 void SetFrame(nsIFrame
* aFrame
)
249 NS_PRECONDITION(aFrame
, "Need a frame");
251 nsIFrame
*prevContinuation
= GetPrevContinuation(aFrame
);
253 if (!prevContinuation
|| mFrame
!= prevContinuation
) {
254 // Ok, we've got the wrong frame. We have to start from scratch.
260 // Get our last frame's size and add its width to our continuation
261 // point before we cache the new frame.
262 mContinuationPoint
+= mFrame
->GetSize().width
;
264 // If this a new line, update mLineContinuationPoint.
266 (aFrame
->GetPrevInFlow() || !AreOnSameLine(mFrame
, aFrame
))) {
267 mLineContinuationPoint
= mContinuationPoint
;
273 nsIFrame
* GetPrevContinuation(nsIFrame
* aFrame
)
275 nsIFrame
* prevCont
= aFrame
->GetPrevContinuation();
276 if (!prevCont
&& (aFrame
->GetStateBits() & NS_FRAME_IS_SPECIAL
)) {
277 nsIFrame
* block
= static_cast<nsIFrame
*>
278 (aFrame
->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
280 // The {ib} properties are only stored on first continuations
281 NS_ASSERTION(!block
->GetPrevContinuation(),
282 "Incorrect value for IBSplitSpecialPrevSibling");
283 prevCont
= static_cast<nsIFrame
*>
284 (block
->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
285 NS_ASSERTION(prevCont
, "How did that happen?");
291 nsIFrame
* GetNextContinuation(nsIFrame
* aFrame
)
293 nsIFrame
* nextCont
= aFrame
->GetNextContinuation();
294 if (!nextCont
&& (aFrame
->GetStateBits() & NS_FRAME_IS_SPECIAL
)) {
295 // The {ib} properties are only stored on first continuations
296 aFrame
= aFrame
->GetFirstContinuation();
297 nsIFrame
* block
= static_cast<nsIFrame
*>
298 (aFrame
->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
300 nextCont
= static_cast<nsIFrame
*>
301 (block
->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
302 NS_ASSERTION(nextCont
, "How did that happen?");
308 void Init(nsIFrame
* aFrame
)
310 // Start with the previous flow frame as our continuation point
311 // is the total of the widths of the previous frames.
312 nsIFrame
* inlineFrame
= GetPrevContinuation(aFrame
);
314 while (inlineFrame
) {
315 nsRect rect
= inlineFrame
->GetRect();
316 mContinuationPoint
+= rect
.width
;
317 mUnbrokenWidth
+= rect
.width
;
318 mBoundingBox
.UnionRect(mBoundingBox
, rect
);
319 inlineFrame
= GetPrevContinuation(inlineFrame
);
322 // Next add this frame and subsequent frames to the bounding box and
324 inlineFrame
= aFrame
;
325 while (inlineFrame
) {
326 nsRect rect
= inlineFrame
->GetRect();
327 mUnbrokenWidth
+= rect
.width
;
328 mBoundingBox
.UnionRect(mBoundingBox
, rect
);
329 inlineFrame
= GetNextContinuation(inlineFrame
);
334 mBidiEnabled
= aFrame
->PresContext()->BidiEnabled();
336 // Find the containing block frame
337 nsIFrame
* frame
= aFrame
;
339 frame
= frame
->GetParent();
340 mBlockFrame
= do_QueryFrame(frame
);
342 while (frame
&& frame
->IsFrameOfType(nsIFrame::eLineParticipant
));
344 NS_ASSERTION(mBlockFrame
, "Cannot find containing block.");
346 mLineContinuationPoint
= mContinuationPoint
;
350 PRBool
AreOnSameLine(nsIFrame
* aFrame1
, nsIFrame
* aFrame2
) {
351 // Assumes that aFrame1 and aFrame2 are both decsendants of mBlockFrame.
352 PRBool isValid1
, isValid2
;
353 nsBlockInFlowLineIterator
it1(mBlockFrame
, aFrame1
, &isValid1
);
354 nsBlockInFlowLineIterator
it2(mBlockFrame
, aFrame2
, &isValid2
);
355 return isValid1
&& isValid2
&& it1
.GetLine() == it2
.GetLine();
359 /* Local functions */
360 static void DrawBorderImage(nsPresContext
* aPresContext
,
361 nsIRenderingContext
& aRenderingContext
,
363 const nsRect
& aBorderArea
,
364 const nsStyleBorder
& aStyleBorder
,
365 const nsRect
& aDirtyRect
);
367 static void DrawBorderImageComponent(nsIRenderingContext
& aRenderingContext
,
369 imgIContainer
* aImage
,
370 const nsRect
& aDirtyRect
,
372 const nsIntRect
& aSrc
,
375 const nsSize
& aUnitSize
,
376 const nsStyleBorder
& aStyleBorder
,
379 static nscolor
MakeBevelColor(mozilla::css::Side whichSide
, PRUint8 style
,
380 nscolor aBackgroundColor
,
381 nscolor aBorderColor
);
383 static InlineBackgroundData
* gInlineBGData
= nsnull
;
385 // Initialize any static variables used by nsCSSRendering.
386 nsresult
nsCSSRendering::Init()
388 NS_ASSERTION(!gInlineBGData
, "Init called twice");
389 gInlineBGData
= new InlineBackgroundData();
391 return NS_ERROR_OUT_OF_MEMORY
;
396 // Clean up any global variables used by nsCSSRendering.
397 void nsCSSRendering::Shutdown()
399 delete gInlineBGData
;
400 gInlineBGData
= nsnull
;
407 MakeBevelColor(mozilla::css::Side whichSide
, PRUint8 style
,
408 nscolor aBackgroundColor
, nscolor aBorderColor
)
414 // Given a background color and a border color
415 // calculate the color used for the shading
416 NS_GetSpecial3DColors(colors
, aBackgroundColor
, aBorderColor
);
418 if ((style
== NS_STYLE_BORDER_STYLE_OUTSET
) ||
419 (style
== NS_STYLE_BORDER_STYLE_RIDGE
)) {
420 // Flip colors for these two border styles
422 case NS_SIDE_BOTTOM
: whichSide
= NS_SIDE_TOP
; break;
423 case NS_SIDE_RIGHT
: whichSide
= NS_SIDE_LEFT
; break;
424 case NS_SIDE_TOP
: whichSide
= NS_SIDE_BOTTOM
; break;
425 case NS_SIDE_LEFT
: whichSide
= NS_SIDE_RIGHT
; break;
431 theColor
= colors
[1];
434 theColor
= colors
[1];
437 theColor
= colors
[0];
441 theColor
= colors
[0];
447 //----------------------------------------------------------------------
448 // Thebes Border Rendering Code Start
451 * Compute the float-pixel radii that should be used for drawing
452 * this border/outline, given the various input bits.
455 nsCSSRendering::ComputePixelRadii(const nscoord
*aAppUnitsRadii
,
456 nscoord aAppUnitsPerPixel
,
457 gfxCornerSizes
*oBorderRadii
)
460 NS_FOR_CSS_HALF_CORNERS(corner
)
461 radii
[corner
] = gfxFloat(aAppUnitsRadii
[corner
]) / aAppUnitsPerPixel
;
463 (*oBorderRadii
)[C_TL
] = gfxSize(radii
[NS_CORNER_TOP_LEFT_X
],
464 radii
[NS_CORNER_TOP_LEFT_Y
]);
465 (*oBorderRadii
)[C_TR
] = gfxSize(radii
[NS_CORNER_TOP_RIGHT_X
],
466 radii
[NS_CORNER_TOP_RIGHT_Y
]);
467 (*oBorderRadii
)[C_BR
] = gfxSize(radii
[NS_CORNER_BOTTOM_RIGHT_X
],
468 radii
[NS_CORNER_BOTTOM_RIGHT_Y
]);
469 (*oBorderRadii
)[C_BL
] = gfxSize(radii
[NS_CORNER_BOTTOM_LEFT_X
],
470 radii
[NS_CORNER_BOTTOM_LEFT_Y
]);
474 nsCSSRendering::PaintBorder(nsPresContext
* aPresContext
,
475 nsIRenderingContext
& aRenderingContext
,
477 const nsRect
& aDirtyRect
,
478 const nsRect
& aBorderArea
,
479 nsStyleContext
* aStyleContext
,
482 nsStyleContext
*styleIfVisited
= aStyleContext
->GetStyleIfVisited();
483 const nsStyleBorder
*styleBorder
= aStyleContext
->GetStyleBorder();
484 // Don't check RelevantLinkVisited here, since we want to take the
485 // same amount of time whether or not it's true.
486 if (!styleIfVisited
) {
487 PaintBorderWithStyleBorder(aPresContext
, aRenderingContext
, aForFrame
,
488 aDirtyRect
, aBorderArea
, *styleBorder
,
489 aStyleContext
, aSkipSides
);
493 nsStyleBorder
newStyleBorder(*styleBorder
);
494 // We're making an ephemeral stack copy here, so just copy this debug-only
495 // member to prevent assertions.
497 newStyleBorder
.mImageTracked
= styleBorder
->mImageTracked
;
500 NS_FOR_CSS_SIDES(side
) {
501 newStyleBorder
.SetBorderColor(side
,
502 aStyleContext
->GetVisitedDependentColor(
503 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color
)[side
]));
505 PaintBorderWithStyleBorder(aPresContext
, aRenderingContext
, aForFrame
,
506 aDirtyRect
, aBorderArea
, newStyleBorder
,
507 aStyleContext
, aSkipSides
);
510 newStyleBorder
.mImageTracked
= false;
515 nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext
* aPresContext
,
516 nsIRenderingContext
& aRenderingContext
,
518 const nsRect
& aDirtyRect
,
519 const nsRect
& aBorderArea
,
520 const nsStyleBorder
& aStyleBorder
,
521 nsStyleContext
* aStyleContext
,
525 nscoord twipsRadii
[8];
526 nsCompatibility compatMode
= aPresContext
->CompatibilityMode();
528 SN("++ PaintBorder");
530 // Check to see if we have an appearance defined. If so, we let the theme
531 // renderer draw the border. DO not get the data from aForFrame, since the passed in style context
532 // may be different! Always use |aStyleContext|!
533 const nsStyleDisplay
* displayData
= aStyleContext
->GetStyleDisplay();
534 if (displayData
->mAppearance
) {
535 nsITheme
*theme
= aPresContext
->GetTheme();
536 if (theme
&& theme
->ThemeSupportsWidget(aPresContext
, aForFrame
, displayData
->mAppearance
))
537 return; // Let the theme handle it.
540 if (aStyleBorder
.IsBorderImageLoaded()) {
541 DrawBorderImage(aPresContext
, aRenderingContext
, aForFrame
,
542 aBorderArea
, aStyleBorder
, aDirtyRect
);
546 // Get our style context's color struct.
547 const nsStyleColor
* ourColor
= aStyleContext
->GetStyleColor();
549 // in NavQuirks mode we want to use the parent's context as a starting point
550 // for determining the background color
551 nsIFrame
* bgFrame
= nsCSSRendering::FindNonTransparentBackgroundFrame
552 (aForFrame
, compatMode
== eCompatibility_NavQuirks
? PR_TRUE
: PR_FALSE
);
553 nsStyleContext
* bgContext
= bgFrame
->GetStyleContext();
555 bgContext
->GetVisitedDependentColor(eCSSProperty_background_color
);
557 border
= aStyleBorder
.GetComputedBorder();
558 if ((0 == border
.left
) && (0 == border
.right
) &&
559 (0 == border
.top
) && (0 == border
.bottom
)) {
564 nsSize frameSize
= aForFrame
->GetSize();
565 if (&aStyleBorder
== aForFrame
->GetStyleBorder() &&
566 frameSize
== aBorderArea
.Size()) {
567 aForFrame
->GetBorderRadii(twipsRadii
);
569 nsIFrame::ComputeBorderRadii(aStyleBorder
.mBorderRadius
, frameSize
,
570 aBorderArea
.Size(), aSkipSides
, twipsRadii
);
573 // Turn off rendering for all of the zero sized sides
574 if (aSkipSides
& SIDE_BIT_TOP
) border
.top
= 0;
575 if (aSkipSides
& SIDE_BIT_RIGHT
) border
.right
= 0;
576 if (aSkipSides
& SIDE_BIT_BOTTOM
) border
.bottom
= 0;
577 if (aSkipSides
& SIDE_BIT_LEFT
) border
.left
= 0;
579 // get the inside and outside parts of the border
580 nsRect
outerRect(aBorderArea
);
582 SF(" outerRect: %d %d %d %d\n", outerRect
.x
, outerRect
.y
, outerRect
.width
, outerRect
.height
);
584 // we can assume that we're already clipped to aDirtyRect -- I think? (!?)
586 // Get our conversion values
587 nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
589 // convert outer and inner rects
590 gfxRect
oRect(nsLayoutUtils::RectToGfxRect(outerRect
, twipsPerPixel
));
592 // convert the border widths
593 gfxFloat borderWidths
[4] = { gfxFloat(border
.top
/ twipsPerPixel
),
594 gfxFloat(border
.right
/ twipsPerPixel
),
595 gfxFloat(border
.bottom
/ twipsPerPixel
),
596 gfxFloat(border
.left
/ twipsPerPixel
) };
599 gfxCornerSizes borderRadii
;
600 ComputePixelRadii(twipsRadii
, twipsPerPixel
, &borderRadii
);
602 PRUint8 borderStyles
[4];
603 nscolor borderColors
[4];
604 nsBorderColors
*compositeColors
[4];
606 // pull out styles, colors, composite colors
607 NS_FOR_CSS_SIDES (i
) {
609 borderStyles
[i
] = aStyleBorder
.GetBorderStyle(i
);
610 aStyleBorder
.GetBorderColor(i
, borderColors
[i
], foreground
);
611 aStyleBorder
.GetCompositeColors(i
, &compositeColors
[i
]);
614 borderColors
[i
] = ourColor
->mColor
;
617 SF(" borderStyles: %d %d %d %d\n", borderStyles
[0], borderStyles
[1], borderStyles
[2], borderStyles
[3]);
620 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
625 // this will draw a transparent red backround underneath the oRect area
627 ctx
->Rectangle(oRect
);
628 ctx
->SetColor(gfxRGBA(1.0, 0.0, 0.0, 0.5));
633 //SF ("borderRadii: %f %f %f %f\n", borderRadii[0], borderRadii[1], borderRadii[2], borderRadii[3]);
635 nsCSSBorderRenderer
br(twipsPerPixel
,
653 GetOutlineInnerRect(nsIFrame
* aFrame
)
655 nsRect
* savedOutlineInnerRect
= static_cast<nsRect
*>
656 (aFrame
->Properties().Get(nsIFrame::OutlineInnerRectProperty()));
657 if (savedOutlineInnerRect
)
658 return *savedOutlineInnerRect
;
659 // FIXME (bug 599652): We probably want something narrower than either
660 // overflow rect here, but for now use the visual overflow in order to
661 // be consistent with ComputeOutlineAndEffectsRect in nsFrame.cpp.
662 return aFrame
->GetVisualOverflowRect();
666 nsCSSRendering::PaintOutline(nsPresContext
* aPresContext
,
667 nsIRenderingContext
& aRenderingContext
,
669 const nsRect
& aDirtyRect
,
670 const nsRect
& aBorderArea
,
671 nsStyleContext
* aStyleContext
)
673 nscoord twipsRadii
[8];
675 // Get our style context's color struct.
676 const nsStyleOutline
* ourOutline
= aStyleContext
->GetStyleOutline();
679 ourOutline
->GetOutlineWidth(width
);
686 nsIFrame
* bgFrame
= nsCSSRendering::FindNonTransparentBackgroundFrame
687 (aForFrame
, PR_FALSE
);
688 nsStyleContext
* bgContext
= bgFrame
->GetStyleContext();
690 bgContext
->GetVisitedDependentColor(eCSSProperty_background_color
);
692 // When the outline property is set on :-moz-anonymous-block or
693 // :-moz-anonyomus-positioned-block pseudo-elements, it inherited that
694 // outline from the inline that was broken because it contained a
695 // block. In that case, we don't want a really wide outline if the
696 // block inside the inline is narrow, so union the actual contents of
697 // the anonymous blocks.
698 nsIFrame
*frameForArea
= aForFrame
;
700 nsIAtom
*pseudoType
= frameForArea
->GetStyleContext()->GetPseudo();
701 if (pseudoType
!= nsCSSAnonBoxes::mozAnonymousBlock
&&
702 pseudoType
!= nsCSSAnonBoxes::mozAnonymousPositionedBlock
)
704 // If we're done, we really want it and all its later siblings.
705 frameForArea
= frameForArea
->GetFirstChild(nsnull
);
706 NS_ASSERTION(frameForArea
, "anonymous block with no children?");
707 } while (frameForArea
);
708 nsRect innerRect
; // relative to aBorderArea.TopLeft()
709 if (frameForArea
== aForFrame
) {
710 innerRect
= GetOutlineInnerRect(aForFrame
);
712 for (; frameForArea
; frameForArea
= frameForArea
->GetNextSibling()) {
713 // The outline has already been included in aForFrame's overflow
714 // area, but not in those of its descendants, so we have to
715 // include it. Otherwise we'll end up drawing the outline inside
717 nsRect
r(GetOutlineInnerRect(frameForArea
) +
718 frameForArea
->GetOffsetTo(aForFrame
));
719 innerRect
.UnionRect(innerRect
, r
);
723 innerRect
+= aBorderArea
.TopLeft();
724 nscoord offset
= ourOutline
->mOutlineOffset
;
725 innerRect
.Inflate(offset
, offset
);
726 // If the dirty rect is completely inside the border area (e.g., only the
727 // content is being painted), then we can skip out now
728 // XXX this isn't exactly true for rounded borders, where the inside curves may
729 // encroach into the content area. A safer calculation would be to
730 // shorten insideRect by the radius one each side before performing this test.
731 if (innerRect
.Contains(aDirtyRect
))
734 nsRect outerRect
= innerRect
;
735 outerRect
.Inflate(width
, width
);
737 // get the radius for our outline
738 nsIFrame::ComputeBorderRadii(ourOutline
->mOutlineRadius
, aBorderArea
.Size(),
739 outerRect
.Size(), 0, twipsRadii
);
741 // Get our conversion values
742 nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
744 // get the outer rectangles
745 gfxRect
oRect(nsLayoutUtils::RectToGfxRect(outerRect
, twipsPerPixel
));
748 nsMargin
outlineMargin(width
, width
, width
, width
);
749 gfxCornerSizes outlineRadii
;
750 ComputePixelRadii(twipsRadii
, twipsPerPixel
, &outlineRadii
);
752 PRUint8 outlineStyle
= ourOutline
->GetOutlineStyle();
753 PRUint8 outlineStyles
[4] = { outlineStyle
,
758 // This handles treating the initial color as 'currentColor'; if we
759 // ever want 'invert' back we'll need to do a bit of work here too.
760 nscolor outlineColor
=
761 aStyleContext
->GetVisitedDependentColor(eCSSProperty_outline_color
);
762 nscolor outlineColors
[4] = { outlineColor
,
767 // convert the border widths
768 gfxFloat outlineWidths
[4] = { gfxFloat(width
/ twipsPerPixel
),
769 gfxFloat(width
/ twipsPerPixel
),
770 gfxFloat(width
/ twipsPerPixel
),
771 gfxFloat(width
/ twipsPerPixel
) };
774 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
778 nsCSSBorderRenderer
br(twipsPerPixel
,
795 nsCSSRendering::PaintFocus(nsPresContext
* aPresContext
,
796 nsIRenderingContext
& aRenderingContext
,
797 const nsRect
& aFocusRect
,
800 nscoord oneCSSPixel
= nsPresContext::CSSPixelsToAppUnits(1);
801 nscoord oneDevPixel
= aPresContext
->DevPixelsToAppUnits(1);
803 gfxRect
focusRect(nsLayoutUtils::RectToGfxRect(aFocusRect
, oneDevPixel
));
805 gfxCornerSizes focusRadii
;
807 nscoord twipsRadii
[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
808 ComputePixelRadii(twipsRadii
, oneDevPixel
, &focusRadii
);
810 gfxFloat focusWidths
[4] = { gfxFloat(oneCSSPixel
/ oneDevPixel
),
811 gfxFloat(oneCSSPixel
/ oneDevPixel
),
812 gfxFloat(oneCSSPixel
/ oneDevPixel
),
813 gfxFloat(oneCSSPixel
/ oneDevPixel
) };
815 PRUint8 focusStyles
[4] = { NS_STYLE_BORDER_STYLE_DOTTED
,
816 NS_STYLE_BORDER_STYLE_DOTTED
,
817 NS_STYLE_BORDER_STYLE_DOTTED
,
818 NS_STYLE_BORDER_STYLE_DOTTED
};
819 nscolor focusColors
[4] = { aColor
, aColor
, aColor
, aColor
};
821 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
825 // Because this renders a dotted border, the background color
826 // should not be used. Therefore, we provide a value that will
827 // be blatantly wrong if it ever does get used. (If this becomes
828 // something that CSS can style, this function will then have access
829 // to a style context and can use the same logic that PaintBorder
830 // and PaintOutline do.)
831 nsCSSBorderRenderer
br(oneDevPixel
,
847 // Thebes Border Rendering Code End
848 //----------------------------------------------------------------------
851 //----------------------------------------------------------------------
854 * Computes the placement of a background image.
856 * @param aOriginBounds is the box to which the tiling position should be
858 * This should correspond to 'background-origin' for the frame,
859 * except when painting on the canvas, in which case the origin bounds
860 * should be the bounds of the root element's frame.
861 * @param aTopLeft the top-left corner where an image tile should be drawn
862 * @param aAnchorPoint a point which should be pixel-aligned by
863 * nsLayoutUtils::DrawImage. This is the same as aTopLeft, unless CSS
864 * specifies a percentage (including 'right' or 'bottom'), in which case
865 * it's that percentage within of aOriginBounds. So 'right' would set
866 * aAnchorPoint.x to aOriginBounds.XMost().
868 * Points are returned relative to aOriginBounds.
871 ComputeBackgroundAnchorPoint(const nsStyleBackground::Layer
& aLayer
,
872 const nsSize
& aOriginBounds
,
873 const nsSize
& aImageSize
,
875 nsPoint
* aAnchorPoint
)
877 double percentX
= aLayer
.mPosition
.mXPosition
.mPercent
;
878 nscoord lengthX
= aLayer
.mPosition
.mXPosition
.mLength
;
879 aAnchorPoint
->x
= lengthX
+ NSToCoordRound(percentX
*aOriginBounds
.width
);
880 aTopLeft
->x
= lengthX
+
881 NSToCoordRound(percentX
*(aOriginBounds
.width
- aImageSize
.width
));
883 double percentY
= aLayer
.mPosition
.mYPosition
.mPercent
;
884 nscoord lengthY
= aLayer
.mPosition
.mYPosition
.mLength
;
885 aAnchorPoint
->y
= lengthY
+ NSToCoordRound(percentY
*aOriginBounds
.height
);
886 aTopLeft
->y
= lengthY
+
887 NSToCoordRound(percentY
*(aOriginBounds
.height
- aImageSize
.height
));
891 nsCSSRendering::FindNonTransparentBackgroundFrame(nsIFrame
* aFrame
,
892 PRBool aStartAtParent
/*= PR_FALSE*/)
894 NS_ASSERTION(aFrame
, "Cannot find NonTransparentBackgroundFrame in a null frame");
896 nsIFrame
* frame
= nsnull
;
897 if (aStartAtParent
) {
898 frame
= nsLayoutUtils::GetParentOrPlaceholderFor(
899 aFrame
->PresContext()->FrameManager(), aFrame
);
906 // No need to call GetVisitedDependentColor because it always uses
907 // this alpha component anyway.
908 if (NS_GET_A(frame
->GetStyleBackground()->mBackgroundColor
) > 0)
911 if (frame
->IsThemed())
914 nsIFrame
* parent
= nsLayoutUtils::GetParentOrPlaceholderFor(
915 frame
->PresContext()->FrameManager(), frame
);
924 // Returns true if aFrame is a canvas frame.
925 // We need to treat the viewport as canvas because, even though
926 // it does not actually paint a background, we need to get the right
927 // background style so we correctly detect transparent documents.
929 nsCSSRendering::IsCanvasFrame(nsIFrame
* aFrame
)
931 nsIAtom
* frameType
= aFrame
->GetType();
932 return frameType
== nsGkAtoms::canvasFrame
||
933 frameType
== nsGkAtoms::rootFrame
||
934 frameType
== nsGkAtoms::pageFrame
||
935 frameType
== nsGkAtoms::pageContentFrame
||
936 frameType
== nsGkAtoms::viewportFrame
;
940 nsCSSRendering::FindBackgroundStyleFrame(nsIFrame
* aForFrame
)
942 const nsStyleBackground
* result
= aForFrame
->GetStyleBackground();
944 // Check if we need to do propagation from BODY rather than HTML.
945 if (result
->IsTransparent()) {
946 nsIContent
* content
= aForFrame
->GetContent();
947 // The root element content can't be null. We wouldn't know what
948 // frame to create for aFrame.
949 // Use |GetOwnerDoc| so it works during destruction.
951 nsIDocument
* document
= content
->GetOwnerDoc();
952 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(document
);
954 nsIContent
* bodyContent
= htmlDoc
->GetBodyContentExternal();
955 // We need to null check the body node (bug 118829) since
956 // there are cases, thanks to the fix for bug 5569, where we
957 // will reflow a document with no body. In particular, if a
958 // SCRIPT element in the head blocks the parser and then has a
959 // SCRIPT that does "document.location.href = 'foo'", then
960 // nsParser::Terminate will call |DidBuildModel| methods
961 // through to the content sink, which will call |StartLayout|
962 // and thus |InitialReflow| on the pres shell. See bug 119351
963 // for the ugly details.
965 nsIFrame
*bodyFrame
= bodyContent
->GetPrimaryFrame();
967 return nsLayoutUtils::GetStyleFrame(bodyFrame
);
978 * |FindBackground| finds the correct style data to use to paint the
979 * background. It is responsible for handling the following two
980 * statements in section 14.2 of CSS2:
982 * The background of the box generated by the root element covers the
985 * For HTML documents, however, we recommend that authors specify the
986 * background for the BODY element rather than the HTML element. User
987 * agents should observe the following precedence rules to fill in the
988 * background: if the value of the 'background' property for the HTML
989 * element is different from 'transparent' then use it, else use the
990 * value of the 'background' property for the BODY element. If the
991 * resulting value is 'transparent', the rendering is undefined.
993 * Thus, in our implementation, it is responsible for ensuring that:
994 * + we paint the correct background on the |nsCanvasFrame|,
995 * |nsRootBoxFrame|, or |nsPageFrame|,
996 * + we don't paint the background on the root element, and
997 * + we don't paint the background on the BODY element in *some* cases,
998 * and for SGML-based HTML documents only.
1000 * |FindBackground| returns true if a background should be painted, and
1001 * the resulting style context to use for the background information
1002 * will be filled in to |aBackground|.
1005 nsCSSRendering::FindRootFrameBackground(nsIFrame
* aForFrame
)
1007 return FindBackgroundStyleFrame(aForFrame
)->GetStyleContext();
1011 FindElementBackground(nsIFrame
* aForFrame
, nsIFrame
* aRootElementFrame
,
1012 nsStyleContext
** aBackgroundSC
)
1014 if (aForFrame
== aRootElementFrame
) {
1015 // We must have propagated our background to the viewport or canvas. Abort.
1019 *aBackgroundSC
= aForFrame
->GetStyleContext();
1021 // Return true unless the frame is for a BODY element whose background
1022 // was propagated to the viewport.
1024 nsIContent
* content
= aForFrame
->GetContent();
1025 if (!content
|| content
->Tag() != nsGkAtoms::body
)
1026 return PR_TRUE
; // not frame for a "body" element
1027 // It could be a non-HTML "body" element but that's OK, we'd fail the
1028 // bodyContent check below
1030 if (aForFrame
->GetStyleContext()->GetPseudo())
1031 return PR_TRUE
; // A pseudo-element frame.
1033 // We should only look at the <html> background if we're in an HTML document
1034 nsIDocument
* document
= content
->GetOwnerDoc();
1035 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(document
);
1039 nsIContent
* bodyContent
= htmlDoc
->GetBodyContentExternal();
1040 if (bodyContent
!= content
)
1041 return PR_TRUE
; // this wasn't the background that was propagated
1043 // This can be called even when there's no root element yet, during frame
1044 // construction, via nsLayoutUtils::FrameHasTransparency and
1045 // nsContainerFrame::SyncFrameViewProperties.
1046 if (!aRootElementFrame
)
1049 const nsStyleBackground
* htmlBG
= aRootElementFrame
->GetStyleBackground();
1050 return !htmlBG
->IsTransparent();
1054 nsCSSRendering::FindBackground(nsPresContext
* aPresContext
,
1055 nsIFrame
* aForFrame
,
1056 nsStyleContext
** aBackgroundSC
)
1058 nsIFrame
* rootElementFrame
=
1059 aPresContext
->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
1060 if (IsCanvasFrame(aForFrame
)) {
1061 *aBackgroundSC
= FindCanvasBackground(aForFrame
, rootElementFrame
);
1064 return FindElementBackground(aForFrame
, rootElementFrame
, aBackgroundSC
);
1069 nsCSSRendering::DidPaint()
1071 gInlineBGData
->Reset();
1075 nsCSSRendering::PaintBoxShadowOuter(nsPresContext
* aPresContext
,
1076 nsIRenderingContext
& aRenderingContext
,
1077 nsIFrame
* aForFrame
,
1078 const nsRect
& aFrameArea
,
1079 const nsRect
& aDirtyRect
)
1081 const nsStyleBorder
* styleBorder
= aForFrame
->GetStyleBorder();
1082 nsCSSShadowArray
* shadows
= styleBorder
->mBoxShadow
;
1085 nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
1087 PRBool hasBorderRadius
;
1088 PRBool nativeTheme
; // mutually exclusive with hasBorderRadius
1089 gfxCornerSizes borderRadii
;
1091 // Get any border radius, since box-shadow must also have rounded corners if the frame does
1092 const nsStyleDisplay
* styleDisplay
= aForFrame
->GetStyleDisplay();
1093 nsITheme::Transparency transparency
;
1094 if (aForFrame
->IsThemed(styleDisplay
, &transparency
)) {
1095 // We don't respect border-radius for native-themed widgets
1096 hasBorderRadius
= PR_FALSE
;
1097 // For opaque (rectangular) theme widgets we can take the generic
1098 // border-box path with border-radius disabled.
1099 nativeTheme
= transparency
!= nsITheme::eOpaque
;
1101 nativeTheme
= PR_FALSE
;
1102 nscoord twipsRadii
[8];
1103 NS_ASSERTION(aFrameArea
.Size() == aForFrame
->GetSize(), "unexpected size");
1104 hasBorderRadius
= aForFrame
->GetBorderRadii(twipsRadii
);
1105 if (hasBorderRadius
) {
1106 ComputePixelRadii(twipsRadii
, twipsPerPixel
, &borderRadii
);
1111 nativeTheme
? aForFrame
->GetVisualOverflowRectRelativeToSelf() + aFrameArea
.TopLeft() : aFrameArea
;
1112 gfxRect
frameGfxRect(nsLayoutUtils::RectToGfxRect(frameRect
, twipsPerPixel
));
1113 frameGfxRect
.Round();
1115 // We don't show anything that intersects with the frame we're blurring on. So tell the
1116 // blurrer not to do unnecessary work there.
1117 gfxRect skipGfxRect
= frameGfxRect
;
1118 PRBool useSkipGfxRect
= PR_TRUE
;
1120 // Optimize non-leaf native-themed frames by skipping computing pixels
1121 // in the padding-box. We assume the padding-box is going to be painted
1122 // opaquely for non-leaf frames.
1123 // XXX this may not be a safe assumption; we should make this go away
1124 // by optimizing box-shadow drawing more for the cases where we don't have a skip-rect.
1125 useSkipGfxRect
= !aForFrame
->IsLeaf();
1126 nsRect paddingRect
=
1127 aForFrame
->GetPaddingRect() - aForFrame
->GetPosition() + aFrameArea
.TopLeft();
1128 skipGfxRect
= nsLayoutUtils::RectToGfxRect(paddingRect
, twipsPerPixel
);
1129 } else if (hasBorderRadius
) {
1131 PR_MAX(borderRadii
[C_TL
].height
, borderRadii
[C_TR
].height
), 0,
1132 PR_MAX(borderRadii
[C_BL
].height
, borderRadii
[C_BR
].height
), 0);
1135 for (PRUint32 i
= shadows
->Length(); i
> 0; --i
) {
1136 nsCSSShadowItem
* shadowItem
= shadows
->ShadowAt(i
- 1);
1137 if (shadowItem
->mInset
)
1140 nsRect shadowRect
= frameRect
;
1141 shadowRect
.MoveBy(shadowItem
->mXOffset
, shadowItem
->mYOffset
);
1142 nscoord pixelSpreadRadius
;
1144 pixelSpreadRadius
= shadowItem
->mSpread
;
1146 shadowRect
.Inflate(shadowItem
->mSpread
, shadowItem
->mSpread
);
1147 pixelSpreadRadius
= 0;
1150 // shadowRect won't include the blur, so make an extra rect here that includes the blur
1151 // for use in the even-odd rule below.
1152 nsRect shadowRectPlusBlur
= shadowRect
;
1153 nscoord blurRadius
= shadowItem
->mRadius
;
1154 shadowRectPlusBlur
.Inflate(
1155 nsContextBoxBlur::GetBlurRadiusMargin(blurRadius
, twipsPerPixel
));
1157 gfxRect shadowGfxRect
=
1158 nsLayoutUtils::RectToGfxRect(shadowRect
, twipsPerPixel
);
1159 gfxRect shadowGfxRectPlusBlur
=
1160 nsLayoutUtils::RectToGfxRect(shadowRectPlusBlur
, twipsPerPixel
);
1161 shadowGfxRect
.Round();
1162 shadowGfxRectPlusBlur
.RoundOut();
1164 gfxContext
* renderContext
= aRenderingContext
.ThebesContext();
1165 nsRefPtr
<gfxContext
> shadowContext
;
1166 nsContextBoxBlur blurringArea
;
1168 // When getting the widget shape from the native theme, we're going
1169 // to draw the widget into the shadow surface to create a mask.
1170 // We need to ensure that there actually *is* a shadow surface
1171 // and that we're not going to draw directly into renderContext.
1173 blurringArea
.Init(shadowRect
, pixelSpreadRadius
,
1174 blurRadius
, twipsPerPixel
, renderContext
, aDirtyRect
,
1175 useSkipGfxRect
? &skipGfxRect
: nsnull
,
1176 nativeTheme
? nsContextBoxBlur::FORCE_MASK
: 0);
1180 // Set the shadow color; if not specified, use the foreground color
1181 nscolor shadowColor
;
1182 if (shadowItem
->mHasColor
)
1183 shadowColor
= shadowItem
->mColor
;
1185 shadowColor
= aForFrame
->GetStyleColor()->mColor
;
1187 renderContext
->Save();
1188 renderContext
->SetColor(gfxRGBA(shadowColor
));
1190 // Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur
1191 // doesn't make any temporary surfaces if blur is 0 and it just returns the original
1192 // surface? If we have no blur, we're painting this fill on the actual content surface
1193 // (renderContext == shadowContext) which is why we set up the color and clip
1194 // before doing this.
1196 // We don't clip the border-box from the shadow, nor any other box.
1197 // We assume that the native theme is going to paint over the shadow.
1199 // Draw the widget shape
1200 gfxContextMatrixAutoSaveRestore
save(shadowContext
);
1201 nsIDeviceContext
* devCtx
= aPresContext
->DeviceContext();
1202 nsCOMPtr
<nsIRenderingContext
> wrapperCtx
;
1203 devCtx
->CreateRenderingContextInstance(*getter_AddRefs(wrapperCtx
));
1204 wrapperCtx
->Init(devCtx
, shadowContext
);
1205 wrapperCtx
->Translate(shadowItem
->mXOffset
, shadowItem
->mYOffset
);
1208 nativeRect
.IntersectRect(frameRect
, aDirtyRect
);
1210 aPresContext
->GetTheme()->DrawWidgetBackground(wrapperCtx
, aForFrame
,
1211 styleDisplay
->mAppearance
, aFrameArea
, nativeRect
);
1213 // Clip out the area of the actual frame so the shadow is not shown within
1215 renderContext
->NewPath();
1216 renderContext
->Rectangle(shadowGfxRectPlusBlur
);
1217 if (hasBorderRadius
) {
1218 renderContext
->RoundedRectangle(frameGfxRect
, borderRadii
);
1220 renderContext
->Rectangle(frameGfxRect
);
1223 renderContext
->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD
);
1224 renderContext
->Clip();
1226 shadowContext
->NewPath();
1227 if (hasBorderRadius
) {
1228 gfxCornerSizes clipRectRadii
;
1229 gfxFloat spreadDistance
= -shadowItem
->mSpread
/ twipsPerPixel
;
1230 gfxFloat borderSizes
[4] = { 0, 0, 0, 0 };
1232 // We only give the spread radius to corners with a radius on them, otherwise we'll
1233 // give a rounded shadow corner to a frame corner with 0 border radius, should
1234 // the author use non-uniform border radii sizes (border-top-left-radius etc)
1236 if (borderRadii
[C_TL
].width
> 0 || borderRadii
[C_BL
].width
> 0) {
1237 borderSizes
[NS_SIDE_LEFT
] = spreadDistance
;
1240 if (borderRadii
[C_TL
].height
> 0 || borderRadii
[C_TR
].height
> 0) {
1241 borderSizes
[NS_SIDE_TOP
] = spreadDistance
;
1244 if (borderRadii
[C_TR
].width
> 0 || borderRadii
[C_BR
].width
> 0) {
1245 borderSizes
[NS_SIDE_RIGHT
] = spreadDistance
;
1248 if (borderRadii
[C_BL
].height
> 0 || borderRadii
[C_BR
].height
> 0) {
1249 borderSizes
[NS_SIDE_BOTTOM
] = spreadDistance
;
1252 nsCSSBorderRenderer::ComputeInnerRadii(borderRadii
, borderSizes
,
1254 shadowContext
->RoundedRectangle(shadowGfxRect
, clipRectRadii
);
1256 shadowContext
->Rectangle(shadowGfxRect
);
1258 shadowContext
->Fill();
1261 blurringArea
.DoPaint();
1262 renderContext
->Restore();
1267 nsCSSRendering::PaintBoxShadowInner(nsPresContext
* aPresContext
,
1268 nsIRenderingContext
& aRenderingContext
,
1269 nsIFrame
* aForFrame
,
1270 const nsRect
& aFrameArea
,
1271 const nsRect
& aDirtyRect
)
1273 const nsStyleBorder
* styleBorder
= aForFrame
->GetStyleBorder();
1274 nsCSSShadowArray
* shadows
= styleBorder
->mBoxShadow
;
1277 if (aForFrame
->IsThemed() && aForFrame
->GetContent() &&
1278 !nsContentUtils::IsChromeDoc(aForFrame
->GetContent()->GetCurrentDoc())) {
1279 // There's no way of getting hold of a shape corresponding to a
1280 // "padding-box" for native-themed widgets, so just don't draw
1281 // inner box-shadows for them. But we allow chrome to paint inner
1282 // box shadows since chrome can be aware of the platform theme.
1286 // Get any border radius, since box-shadow must also have rounded corners
1287 // if the frame does.
1288 nscoord twipsRadii
[8];
1289 NS_ASSERTION(aForFrame
->GetType() == nsGkAtoms::fieldSetFrame
||
1290 aFrameArea
.Size() == aForFrame
->GetSize(), "unexpected size");
1291 PRBool hasBorderRadius
= aForFrame
->GetBorderRadii(twipsRadii
);
1292 nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
1294 nsRect paddingRect
= aFrameArea
;
1295 nsMargin border
= aForFrame
->GetUsedBorder();
1296 aForFrame
->ApplySkipSides(border
);
1297 paddingRect
.Deflate(border
);
1299 gfxCornerSizes innerRadii
;
1300 if (hasBorderRadius
) {
1301 gfxCornerSizes borderRadii
;
1303 ComputePixelRadii(twipsRadii
, twipsPerPixel
, &borderRadii
);
1304 gfxFloat borderSizes
[4] = {
1305 gfxFloat(border
.top
/ twipsPerPixel
),
1306 gfxFloat(border
.right
/ twipsPerPixel
),
1307 gfxFloat(border
.bottom
/ twipsPerPixel
),
1308 gfxFloat(border
.left
/ twipsPerPixel
)
1310 nsCSSBorderRenderer::ComputeInnerRadii(borderRadii
, borderSizes
,
1314 for (PRUint32 i
= shadows
->Length(); i
> 0; --i
) {
1315 nsCSSShadowItem
* shadowItem
= shadows
->ShadowAt(i
- 1);
1316 if (!shadowItem
->mInset
)
1320 * shadowRect: the frame's padding rect
1321 * shadowPaintRect: the area to paint on the temp surface, larger than shadowRect
1322 * so that blurs still happen properly near the edges
1323 * shadowClipRect: the area on the temporary surface within shadowPaintRect
1324 * that we will NOT paint in
1326 nscoord blurRadius
= shadowItem
->mRadius
;
1327 nsMargin blurMargin
=
1328 nsContextBoxBlur::GetBlurRadiusMargin(blurRadius
, twipsPerPixel
);
1329 nsRect shadowPaintRect
= paddingRect
;
1330 shadowPaintRect
.Inflate(blurMargin
);
1332 nsRect shadowClipRect
= paddingRect
;
1333 shadowClipRect
.MoveBy(shadowItem
->mXOffset
, shadowItem
->mYOffset
);
1334 shadowClipRect
.Deflate(shadowItem
->mSpread
, shadowItem
->mSpread
);
1336 gfxCornerSizes clipRectRadii
;
1337 if (hasBorderRadius
) {
1338 // Calculate the radii the inner clipping rect will have
1339 gfxFloat spreadDistance
= shadowItem
->mSpread
/ twipsPerPixel
;
1340 gfxFloat borderSizes
[4] = {0, 0, 0, 0};
1342 // See PaintBoxShadowOuter and bug 514670
1343 if (innerRadii
[C_TL
].width
> 0 || innerRadii
[C_BL
].width
> 0) {
1344 borderSizes
[NS_SIDE_LEFT
] = spreadDistance
;
1347 if (innerRadii
[C_TL
].height
> 0 || innerRadii
[C_TR
].height
> 0) {
1348 borderSizes
[NS_SIDE_TOP
] = spreadDistance
;
1351 if (innerRadii
[C_TR
].width
> 0 || innerRadii
[C_BR
].width
> 0) {
1352 borderSizes
[NS_SIDE_RIGHT
] = spreadDistance
;
1355 if (innerRadii
[C_BL
].height
> 0 || innerRadii
[C_BR
].height
> 0) {
1356 borderSizes
[NS_SIDE_BOTTOM
] = spreadDistance
;
1359 nsCSSBorderRenderer::ComputeInnerRadii(innerRadii
, borderSizes
,
1363 // Set the "skip rect" to the area within the frame that we don't paint in,
1364 // including after blurring.
1365 nsRect skipRect
= shadowClipRect
;
1366 skipRect
.Deflate(blurMargin
);
1367 gfxRect skipGfxRect
= nsLayoutUtils::RectToGfxRect(skipRect
, twipsPerPixel
);
1368 if (hasBorderRadius
) {
1369 skipGfxRect
.Inset(PR_MAX(clipRectRadii
[C_TL
].height
, clipRectRadii
[C_TR
].height
), 0,
1370 PR_MAX(clipRectRadii
[C_BL
].height
, clipRectRadii
[C_BR
].height
), 0);
1373 // When there's a blur radius, gfxAlphaBoxBlur leaves the skiprect area
1374 // unchanged. And by construction the gfxSkipRect is not touched by the
1375 // rendered shadow (even after blurring), so those pixels must be completely
1376 // transparent in the shadow, so drawing them changes nothing.
1377 gfxContext
* renderContext
= aRenderingContext
.ThebesContext();
1378 nsRefPtr
<gfxContext
> shadowContext
;
1379 nsContextBoxBlur blurringArea
;
1381 blurringArea
.Init(shadowPaintRect
, 0, blurRadius
, twipsPerPixel
,
1382 renderContext
, aDirtyRect
, &skipGfxRect
);
1386 // Set the shadow color; if not specified, use the foreground color
1387 nscolor shadowColor
;
1388 if (shadowItem
->mHasColor
)
1389 shadowColor
= shadowItem
->mColor
;
1391 shadowColor
= aForFrame
->GetStyleColor()->mColor
;
1393 renderContext
->Save();
1394 renderContext
->SetColor(gfxRGBA(shadowColor
));
1396 // Clip the context to the area of the frame's padding rect, so no part of the
1397 // shadow is painted outside. Also cut out anything beyond where the inset shadow
1399 gfxRect shadowGfxRect
=
1400 nsLayoutUtils::RectToGfxRect(paddingRect
, twipsPerPixel
);
1401 shadowGfxRect
.Round();
1402 renderContext
->NewPath();
1403 if (hasBorderRadius
)
1404 renderContext
->RoundedRectangle(shadowGfxRect
, innerRadii
, PR_FALSE
);
1406 renderContext
->Rectangle(shadowGfxRect
);
1407 renderContext
->Clip();
1409 // Fill the surface minus the area within the frame that we should
1410 // not paint in, and blur and apply it.
1411 gfxRect shadowPaintGfxRect
=
1412 nsLayoutUtils::RectToGfxRect(shadowPaintRect
, twipsPerPixel
);
1413 shadowPaintGfxRect
.RoundOut();
1414 gfxRect shadowClipGfxRect
=
1415 nsLayoutUtils::RectToGfxRect(shadowClipRect
, twipsPerPixel
);
1416 shadowClipGfxRect
.Round();
1417 shadowContext
->NewPath();
1418 shadowContext
->Rectangle(shadowPaintGfxRect
);
1419 if (hasBorderRadius
)
1420 shadowContext
->RoundedRectangle(shadowClipGfxRect
, clipRectRadii
, PR_FALSE
);
1422 shadowContext
->Rectangle(shadowClipGfxRect
);
1423 shadowContext
->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD
);
1424 shadowContext
->Fill();
1426 blurringArea
.DoPaint();
1427 renderContext
->Restore();
1432 nsCSSRendering::PaintBackground(nsPresContext
* aPresContext
,
1433 nsIRenderingContext
& aRenderingContext
,
1434 nsIFrame
* aForFrame
,
1435 const nsRect
& aDirtyRect
,
1436 const nsRect
& aBorderArea
,
1438 nsRect
* aBGClipRect
)
1440 NS_PRECONDITION(aForFrame
,
1441 "Frame is expected to be provided to PaintBackground");
1444 if (!FindBackground(aPresContext
, aForFrame
, &sc
)) {
1445 // We don't want to bail out if moz-appearance is set on a root
1446 // node. If it has a parent content node, bail because it's not
1447 // a root, other wise keep going in order to let the theme stuff
1448 // draw the background. The canvas really should be drawing the
1449 // bg, but there's no way to hook that up via css.
1450 if (!aForFrame
->GetStyleDisplay()->mAppearance
) {
1454 nsIContent
* content
= aForFrame
->GetContent();
1455 if (!content
|| content
->GetParent()) {
1459 sc
= aForFrame
->GetStyleContext();
1462 PaintBackgroundWithSC(aPresContext
, aRenderingContext
, aForFrame
,
1463 aDirtyRect
, aBorderArea
, sc
,
1464 *aForFrame
->GetStyleBorder(), aFlags
,
1469 IsOpaqueBorderEdge(const nsStyleBorder
& aBorder
, mozilla::css::Side aSide
)
1471 if (aBorder
.GetActualBorder().side(aSide
) == 0)
1473 switch (aBorder
.GetBorderStyle(aSide
)) {
1474 case NS_STYLE_BORDER_STYLE_SOLID
:
1475 case NS_STYLE_BORDER_STYLE_GROOVE
:
1476 case NS_STYLE_BORDER_STYLE_RIDGE
:
1477 case NS_STYLE_BORDER_STYLE_INSET
:
1478 case NS_STYLE_BORDER_STYLE_OUTSET
:
1484 // If we're using a border image, assume it's not fully opaque,
1485 // because we may not even have the image loaded at this point, and
1486 // even if we did, checking whether the relevant tile is fully
1487 // opaque would be too much work.
1488 if (aBorder
.GetBorderImage())
1492 PRBool isForeground
;
1493 aBorder
.GetBorderColor(aSide
, color
, isForeground
);
1495 // We don't know the foreground color here, so if it's being used
1496 // we must assume it might be transparent.
1500 return NS_GET_A(color
) == 255;
1504 * Returns true if all border edges are either missing or opaque.
1507 IsOpaqueBorder(const nsStyleBorder
& aBorder
)
1509 if (aBorder
.mBorderColors
)
1511 NS_FOR_CSS_SIDES(i
) {
1512 if (!IsOpaqueBorderEdge(aBorder
, i
))
1519 SetupDirtyRects(const nsRect
& aBGClipArea
, const nsRect
& aCallerDirtyRect
,
1520 nscoord aAppUnitsPerPixel
,
1522 nsRect
* aDirtyRect
, gfxRect
* aDirtyRectGfx
)
1524 aDirtyRect
->IntersectRect(aBGClipArea
, aCallerDirtyRect
);
1526 // Compute the Thebes equivalent of the dirtyRect.
1527 *aDirtyRectGfx
= nsLayoutUtils::RectToGfxRect(*aDirtyRect
, aAppUnitsPerPixel
);
1528 NS_WARN_IF_FALSE(aDirtyRect
->IsEmpty() || !aDirtyRectGfx
->IsEmpty(),
1529 "converted dirty rect should not be empty");
1530 NS_ABORT_IF_FALSE(!aDirtyRect
->IsEmpty() || aDirtyRectGfx
->IsEmpty(),
1531 "second should be empty if first is");
1534 struct BackgroundClipState
{
1537 gfxRect mDirtyRectGfx
;
1539 gfxCornerSizes mClippedRadii
;
1540 PRPackedBool mRadiiAreOuter
;
1542 // Whether we are being asked to draw with a caller provided background
1543 // clipping area. If this is true we also disable rounded corners.
1544 PRPackedBool mCustomClip
;
1548 GetBackgroundClip(gfxContext
*aCtx
, PRUint8 aBackgroundClip
,
1549 nsIFrame
* aForFrame
, const nsRect
& aBorderArea
,
1550 const nsRect
& aCallerDirtyRect
, PRBool aHaveRoundedCorners
,
1551 const gfxCornerSizes
& aBGRadii
, nscoord aAppUnitsPerPixel
,
1552 /* out */ BackgroundClipState
* aClipState
)
1554 aClipState
->mBGClipArea
= aBorderArea
;
1555 aClipState
->mCustomClip
= PR_FALSE
;
1556 aClipState
->mRadiiAreOuter
= PR_TRUE
;
1557 aClipState
->mClippedRadii
= aBGRadii
;
1558 if (aBackgroundClip
!= NS_STYLE_BG_CLIP_BORDER
) {
1559 nsMargin border
= aForFrame
->GetUsedBorder();
1560 if (aBackgroundClip
!= NS_STYLE_BG_CLIP_PADDING
) {
1561 NS_ASSERTION(aBackgroundClip
== NS_STYLE_BG_CLIP_CONTENT
,
1562 "unexpected background-clip");
1563 border
+= aForFrame
->GetUsedPadding();
1565 aForFrame
->ApplySkipSides(border
);
1566 aClipState
->mBGClipArea
.Deflate(border
);
1568 if (aHaveRoundedCorners
) {
1569 gfxFloat borderSizes
[4] = {
1570 gfxFloat(border
.top
/ aAppUnitsPerPixel
),
1571 gfxFloat(border
.right
/ aAppUnitsPerPixel
),
1572 gfxFloat(border
.bottom
/ aAppUnitsPerPixel
),
1573 gfxFloat(border
.left
/ aAppUnitsPerPixel
)
1575 nsCSSBorderRenderer::ComputeInnerRadii(aBGRadii
, borderSizes
,
1576 &aClipState
->mClippedRadii
);
1577 aClipState
->mRadiiAreOuter
= PR_FALSE
;
1581 SetupDirtyRects(aClipState
->mBGClipArea
, aCallerDirtyRect
, aAppUnitsPerPixel
,
1582 &aClipState
->mDirtyRect
, &aClipState
->mDirtyRectGfx
);
1586 SetupBackgroundClip(BackgroundClipState
& aClipState
, gfxContext
*aCtx
,
1587 PRBool aHaveRoundedCorners
, nscoord aAppUnitsPerPixel
,
1588 gfxContextAutoSaveRestore
* aAutoSR
)
1590 if (aClipState
.mDirtyRectGfx
.IsEmpty()) {
1591 // Our caller won't draw anything under this condition, so no need
1596 if (aClipState
.mCustomClip
) {
1597 // We don't support custom clips and rounded corners, arguably a bug, but
1598 // table painting seems to depend on it.
1602 // If we have rounded corners, clip all subsequent drawing to the
1603 // rounded rectangle defined by bgArea and bgRadii (we don't know
1604 // whether the rounded corners intrude on the dirtyRect or not).
1605 // Do not do this if we have a caller-provided clip rect --
1606 // as above with bgArea, arguably a bug, but table painting seems
1609 if (aHaveRoundedCorners
) {
1611 nsLayoutUtils::RectToGfxRect(aClipState
.mBGClipArea
, aAppUnitsPerPixel
);
1613 bgAreaGfx
.Condition();
1615 if (bgAreaGfx
.IsEmpty()) {
1616 // I think it's become possible to hit this since
1617 // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
1618 NS_WARNING("converted background area should not be empty");
1619 // Make our caller not do anything.
1620 aClipState
.mDirtyRectGfx
.size
.SizeTo(0.0, 0.0);
1624 aAutoSR
->Reset(aCtx
);
1626 aCtx
->RoundedRectangle(bgAreaGfx
, aClipState
.mClippedRadii
, aClipState
.mRadiiAreOuter
);
1632 DrawBackgroundColor(BackgroundClipState
& aClipState
, gfxContext
*aCtx
,
1633 PRBool aHaveRoundedCorners
, nscoord aAppUnitsPerPixel
)
1635 if (aClipState
.mDirtyRectGfx
.IsEmpty()) {
1636 // Our caller won't draw anything under this condition, so no need
1641 // We don't support custom clips and rounded corners, arguably a bug, but
1642 // table painting seems to depend on it.
1643 if (!aHaveRoundedCorners
|| aClipState
.mCustomClip
) {
1645 aCtx
->Rectangle(aClipState
.mDirtyRectGfx
, PR_TRUE
);
1651 nsLayoutUtils::RectToGfxRect(aClipState
.mBGClipArea
, aAppUnitsPerPixel
);
1653 bgAreaGfx
.Condition();
1655 if (bgAreaGfx
.IsEmpty()) {
1656 // I think it's become possible to hit this since
1657 // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
1658 NS_WARNING("converted background area should not be empty");
1659 // Make our caller not do anything.
1660 aClipState
.mDirtyRectGfx
.size
.SizeTo(0.0, 0.0);
1665 gfxRect dirty
= bgAreaGfx
.Intersect(aClipState
.mDirtyRectGfx
);
1668 aCtx
->Rectangle(dirty
, PR_TRUE
);
1672 aCtx
->RoundedRectangle(bgAreaGfx
, aClipState
.mClippedRadii
,
1673 aClipState
.mRadiiAreOuter
);
1679 DetermineBackgroundColorInternal(nsPresContext
* aPresContext
,
1680 nsStyleContext
* aStyleContext
,
1682 PRBool
& aDrawBackgroundImage
,
1683 PRBool
& aDrawBackgroundColor
)
1685 aDrawBackgroundImage
= PR_TRUE
;
1686 aDrawBackgroundColor
= PR_TRUE
;
1688 if (aFrame
->HonorPrintBackgroundSettings()) {
1689 aDrawBackgroundImage
= aPresContext
->GetBackgroundImageDraw();
1690 aDrawBackgroundColor
= aPresContext
->GetBackgroundColorDraw();
1694 if (aDrawBackgroundColor
) {
1696 aStyleContext
->GetVisitedDependentColor(eCSSProperty_background_color
);
1697 if (NS_GET_A(bgColor
) == 0)
1698 aDrawBackgroundColor
= PR_FALSE
;
1700 // If GetBackgroundColorDraw() is false, we are still expected to
1701 // draw color in the background of any frame that's not completely
1702 // transparent, but we are expected to use white instead of whatever
1703 // color was specified.
1704 bgColor
= NS_RGB(255, 255, 255);
1705 if (aDrawBackgroundImage
||
1706 !aStyleContext
->GetStyleBackground()->IsTransparent())
1707 aDrawBackgroundColor
= PR_TRUE
;
1709 bgColor
= NS_RGBA(0,0,0,0);
1716 nsCSSRendering::DetermineBackgroundColor(nsPresContext
* aPresContext
,
1717 nsStyleContext
* aStyleContext
,
1720 PRBool drawBackgroundImage
;
1721 PRBool drawBackgroundColor
;
1722 return DetermineBackgroundColorInternal(aPresContext
,
1725 drawBackgroundImage
,
1726 drawBackgroundColor
);
1730 ConvertGradientValueToPixels(const nsStyleCoord
& aCoord
,
1731 gfxFloat aFillLength
,
1732 PRInt32 aAppUnitsPerPixel
)
1734 switch (aCoord
.GetUnit()) {
1735 case eStyleUnit_Percent
:
1736 return aCoord
.GetPercentValue() * aFillLength
;
1737 case eStyleUnit_Coord
:
1738 return NSAppUnitsToFloatPixels(aCoord
.GetCoordValue(), aAppUnitsPerPixel
);
1739 case eStyleUnit_Calc
: {
1740 const nsStyleCoord::Calc
*calc
= aCoord
.GetCalcValue();
1741 return calc
->mPercent
* aFillLength
+
1742 NSAppUnitsToFloatPixels(calc
->mLength
, aAppUnitsPerPixel
);
1745 NS_WARNING("Unexpected coord unit");
1750 // Given a box with size aBoxSize and origin (0,0), and an angle aAngle,
1751 // and a starting point for the gradient line aStart, find the endpoint of
1752 // the gradient line --- the intersection of the gradient line with a line
1753 // perpendicular to aAngle that passes through the farthest corner in the
1754 // direction aAngle.
1756 ComputeGradientLineEndFromAngle(const gfxPoint
& aStart
,
1758 const gfxSize
& aBoxSize
)
1760 double dx
= cos(-aAngle
);
1761 double dy
= sin(-aAngle
);
1762 gfxPoint
farthestCorner(dx
> 0 ? aBoxSize
.width
: 0,
1763 dy
> 0 ? aBoxSize
.height
: 0);
1764 gfxPoint delta
= farthestCorner
- aStart
;
1765 double u
= delta
.x
*dy
- delta
.y
*dx
;
1766 return farthestCorner
+ gfxPoint(-u
*dy
, u
*dx
);
1769 // Compute the start and end points of the gradient line for a linear gradient.
1771 ComputeLinearGradientLine(nsPresContext
* aPresContext
,
1772 nsStyleGradient
* aGradient
,
1773 const gfxSize
& aBoxSize
,
1774 gfxPoint
* aLineStart
,
1777 if (aGradient
->mBgPosX
.GetUnit() == eStyleUnit_None
) {
1779 if (aGradient
->mAngle
.IsAngleValue()) {
1780 angle
= aGradient
->mAngle
.GetAngleValueInRadians();
1782 angle
= -M_PI_2
; // defaults to vertical gradient starting from top
1784 gfxPoint
center(aBoxSize
.width
/2, aBoxSize
.height
/2);
1785 *aLineEnd
= ComputeGradientLineEndFromAngle(center
, angle
, aBoxSize
);
1786 *aLineStart
= gfxPoint(aBoxSize
.width
, aBoxSize
.height
) - *aLineEnd
;
1788 PRInt32 appUnitsPerPixel
= aPresContext
->AppUnitsPerDevPixel();
1789 *aLineStart
= gfxPoint(
1790 ConvertGradientValueToPixels(aGradient
->mBgPosX
, aBoxSize
.width
,
1792 ConvertGradientValueToPixels(aGradient
->mBgPosY
, aBoxSize
.height
,
1794 if (aGradient
->mAngle
.IsAngleValue()) {
1795 double angle
= aGradient
->mAngle
.GetAngleValueInRadians();
1796 *aLineEnd
= ComputeGradientLineEndFromAngle(*aLineStart
, angle
, aBoxSize
);
1798 // No angle, the line end is just the reflection of the start point
1799 // through the center of the box
1800 *aLineEnd
= gfxPoint(aBoxSize
.width
, aBoxSize
.height
) - *aLineStart
;
1805 // Compute the start and end points of the gradient line for a radial gradient.
1806 // Also returns the horizontal and vertical radii defining the circle or
1809 ComputeRadialGradientLine(nsPresContext
* aPresContext
,
1810 nsStyleGradient
* aGradient
,
1811 const gfxSize
& aBoxSize
,
1812 gfxPoint
* aLineStart
,
1817 if (aGradient
->mBgPosX
.GetUnit() == eStyleUnit_None
) {
1818 // Default line start point is the center of the box
1819 *aLineStart
= gfxPoint(aBoxSize
.width
/2, aBoxSize
.height
/2);
1821 PRInt32 appUnitsPerPixel
= aPresContext
->AppUnitsPerDevPixel();
1822 *aLineStart
= gfxPoint(
1823 ConvertGradientValueToPixels(aGradient
->mBgPosX
, aBoxSize
.width
,
1825 ConvertGradientValueToPixels(aGradient
->mBgPosY
, aBoxSize
.height
,
1829 // Compute gradient shape: the x and y radii of an ellipse.
1830 double radiusX
, radiusY
;
1831 double leftDistance
= PR_ABS(aLineStart
->x
);
1832 double rightDistance
= PR_ABS(aBoxSize
.width
- aLineStart
->x
);
1833 double topDistance
= PR_ABS(aLineStart
->y
);
1834 double bottomDistance
= PR_ABS(aBoxSize
.height
- aLineStart
->y
);
1835 switch (aGradient
->mSize
) {
1836 case NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE
:
1837 radiusX
= NS_MIN(leftDistance
, rightDistance
);
1838 radiusY
= NS_MIN(topDistance
, bottomDistance
);
1839 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_CIRCULAR
) {
1840 radiusX
= radiusY
= NS_MIN(radiusX
, radiusY
);
1843 case NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER
: {
1844 // Compute x and y distances to nearest corner
1845 double offsetX
= NS_MIN(leftDistance
, rightDistance
);
1846 double offsetY
= NS_MIN(topDistance
, bottomDistance
);
1847 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_CIRCULAR
) {
1848 radiusX
= radiusY
= NS_hypot(offsetX
, offsetY
);
1850 // maintain aspect ratio
1851 radiusX
= offsetX
*M_SQRT2
;
1852 radiusY
= offsetY
*M_SQRT2
;
1856 case NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE
:
1857 radiusX
= NS_MAX(leftDistance
, rightDistance
);
1858 radiusY
= NS_MAX(topDistance
, bottomDistance
);
1859 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_CIRCULAR
) {
1860 radiusX
= radiusY
= NS_MAX(radiusX
, radiusY
);
1863 case NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER
: {
1864 // Compute x and y distances to nearest corner
1865 double offsetX
= NS_MAX(leftDistance
, rightDistance
);
1866 double offsetY
= NS_MAX(topDistance
, bottomDistance
);
1867 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_CIRCULAR
) {
1868 radiusX
= radiusY
= NS_hypot(offsetX
, offsetY
);
1870 // maintain aspect ratio
1871 radiusX
= offsetX
*M_SQRT2
;
1872 radiusY
= offsetY
*M_SQRT2
;
1877 NS_ABORT_IF_FALSE(PR_FALSE
, "unknown radial gradient sizing method");
1879 *aRadiusX
= radiusX
;
1880 *aRadiusY
= radiusY
;
1883 if (aGradient
->mAngle
.IsAngleValue()) {
1884 angle
= aGradient
->mAngle
.GetAngleValueInRadians();
1886 // Default angle is 0deg
1890 // The gradient line end point is where the gradient line intersects
1892 *aLineEnd
= *aLineStart
+ gfxPoint(radiusX
*cos(-angle
), radiusY
*sin(-angle
));
1895 // A resolved color stop --- with a specific position along the gradient line,
1896 // and a Thebes color
1898 ColorStop(double aPosition
, nscolor aColor
) :
1899 mPosition(aPosition
), mColor(aColor
) {}
1900 double mPosition
; // along the gradient line; 0=start, 1=end
1904 // Returns aFrac*aC2 + (1 - aFrac)*C1. The interpolation is done
1905 // in unpremultiplied space, which is what SVG gradients and cairo
1906 // gradients expect.
1908 InterpolateColor(const gfxRGBA
& aC1
, const gfxRGBA
& aC2
, double aFrac
)
1910 double other
= 1 - aFrac
;
1911 return gfxRGBA(aC2
.r
*aFrac
+ aC1
.r
*other
,
1912 aC2
.g
*aFrac
+ aC1
.g
*other
,
1913 aC2
.b
*aFrac
+ aC1
.b
*other
,
1914 aC2
.a
*aFrac
+ aC1
.a
*other
);
1918 FindTileStart(nscoord aDirtyCoord
, nscoord aTilePos
, nscoord aTileDim
)
1920 NS_ASSERTION(aTileDim
> 0, "Non-positive tile dimension");
1921 double multiples
= NS_floor(double(aDirtyCoord
- aTilePos
)/aTileDim
);
1922 return NSToCoordRound(multiples
*aTileDim
+ aTilePos
);
1926 nsCSSRendering::PaintGradient(nsPresContext
* aPresContext
,
1927 nsIRenderingContext
& aRenderingContext
,
1928 nsStyleGradient
* aGradient
,
1929 const nsRect
& aDirtyRect
,
1930 const nsRect
& aOneCellArea
,
1931 const nsRect
& aFillArea
)
1933 if (aOneCellArea
.IsEmpty())
1936 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
1937 nscoord appUnitsPerPixel
= aPresContext
->AppUnitsPerDevPixel();
1938 gfxRect oneCellArea
=
1939 nsLayoutUtils::RectToGfxRect(aOneCellArea
, appUnitsPerPixel
);
1941 // Compute "gradient line" start and end relative to oneCellArea
1942 gfxPoint lineStart
, lineEnd
;
1943 double radiusX
= 0, radiusY
= 0; // for radial gradients only
1944 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_LINEAR
) {
1945 ComputeLinearGradientLine(aPresContext
, aGradient
, oneCellArea
.size
,
1946 &lineStart
, &lineEnd
);
1948 ComputeRadialGradientLine(aPresContext
, aGradient
, oneCellArea
.size
,
1949 &lineStart
, &lineEnd
, &radiusX
, &radiusY
);
1951 gfxFloat lineLength
= NS_hypot(lineEnd
.x
- lineStart
.x
,
1952 lineEnd
.y
- lineStart
.y
);
1954 NS_ABORT_IF_FALSE(aGradient
->mStops
.Length() >= 2,
1955 "The parser should reject gradients with less than two stops");
1957 // Build color stop array and compute stop positions
1958 nsTArray
<ColorStop
> stops
;
1959 // If there is a run of stops before stop i that did not have specified
1960 // positions, then this is the index of the first stop in that run, otherwise
1962 PRInt32 firstUnsetPosition
= -1;
1963 for (PRUint32 i
= 0; i
< aGradient
->mStops
.Length(); ++i
) {
1964 const nsStyleGradientStop
& stop
= aGradient
->mStops
[i
];
1966 switch (stop
.mLocation
.GetUnit()) {
1967 case eStyleUnit_None
:
1969 // First stop defaults to position 0.0
1971 } else if (i
== aGradient
->mStops
.Length() - 1) {
1972 // Last stop defaults to position 1.0
1975 // Other stops with no specified position get their position assigned
1976 // later by interpolation, see below.
1977 // Remeber where the run of stops with no specified position starts,
1978 // if it starts here.
1979 if (firstUnsetPosition
< 0) {
1980 firstUnsetPosition
= i
;
1982 stops
.AppendElement(ColorStop(0, stop
.mColor
));
1986 case eStyleUnit_Percent
:
1987 position
= stop
.mLocation
.GetPercentValue();
1989 case eStyleUnit_Coord
:
1990 position
= lineLength
< 1e-6 ? 0.0 :
1991 stop
.mLocation
.GetCoordValue() / appUnitsPerPixel
/ lineLength
;
1994 NS_ABORT_IF_FALSE(PR_FALSE
, "Unknown stop position type");
1998 // Prevent decreasing stop positions by advancing this position
1999 // to the previous stop position, if necessary
2000 position
= NS_MAX(position
, stops
[i
- 1].mPosition
);
2002 stops
.AppendElement(ColorStop(position
, stop
.mColor
));
2003 if (firstUnsetPosition
> 0) {
2004 // Interpolate positions for all stops that didn't have a specified position
2005 double p
= stops
[firstUnsetPosition
- 1].mPosition
;
2006 double d
= (stops
[i
].mPosition
- p
)/(i
- firstUnsetPosition
+ 1);
2007 for (PRUint32 j
= firstUnsetPosition
; j
< i
; ++j
) {
2009 stops
[j
].mPosition
= p
;
2011 firstUnsetPosition
= -1;
2015 // Eliminate negative-position stops if the gradient is radial.
2016 double firstStop
= stops
[0].mPosition
;
2017 if (aGradient
->mShape
!= NS_STYLE_GRADIENT_SHAPE_LINEAR
&& firstStop
< 0.0) {
2018 if (aGradient
->mRepeating
) {
2019 // Choose an instance of the repeated pattern that gives us all positive
2021 double lastStop
= stops
[stops
.Length() - 1].mPosition
;
2022 double stopDelta
= lastStop
- firstStop
;
2023 // If all the stops are in approximately the same place then logic below
2024 // will kick in that makes us draw just the last stop color, so don't
2025 // try to do anything in that case. We certainly need to avoid
2026 // dividing by zero.
2027 if (stopDelta
>= 1e-6) {
2028 double instanceCount
= NS_ceil(-firstStop
/stopDelta
);
2029 // Advance stops by instanceCount multiples of the period of the
2030 // repeating gradient.
2031 double offset
= instanceCount
*stopDelta
;
2032 for (PRUint32 i
= 0; i
< stops
.Length(); i
++) {
2033 stops
[i
].mPosition
+= offset
;
2037 // Move negative-position stops to position 0.0. We may also need
2038 // to set the color of the stop to the color the gradient should have
2039 // at the center of the ellipse.
2040 for (PRUint32 i
= 0; i
< stops
.Length(); i
++) {
2041 double pos
= stops
[i
].mPosition
;
2043 stops
[i
].mPosition
= 0.0;
2044 // If this is the last stop, we don't need to adjust the color,
2045 // it will fill the entire area.
2046 if (i
< stops
.Length() - 1) {
2047 double nextPos
= stops
[i
+ 1].mPosition
;
2048 // If nextPos is approximately equal to pos, then we don't
2049 // need to adjust the color of this stop because it's
2050 // not going to be displayed.
2051 // If nextPos is negative, we don't need to adjust the color of
2052 // this stop since it's not going to be displayed because
2053 // nextPos will also be moved to 0.0.
2054 if (nextPos
>= 0.0 && nextPos
- pos
>= 1e-6) {
2055 // Compute how far the new position 0.0 is along the interval
2056 // between pos and nextPos.
2057 // XXX Color interpolation (in cairo, too) should use the
2058 // CSS 'color-interpolation' property!
2059 double frac
= (0.0 - pos
)/(nextPos
- pos
);
2061 InterpolateColor(stops
[i
].mColor
, stops
[i
+ 1].mColor
, frac
);
2067 firstStop
= stops
[0].mPosition
;
2068 NS_ABORT_IF_FALSE(firstStop
>= 0.0, "Failed to fix stop offsets");
2071 double lastStop
= stops
[stops
.Length() - 1].mPosition
;
2072 // Cairo gradients must have stop positions in the range [0, 1]. So,
2073 // stop positions will be normalized below by subtracting firstStop and then
2074 // multiplying by stopScale.
2076 double stopDelta
= lastStop
- firstStop
;
2077 if (stopDelta
< 1e-6 || lineLength
< 1e-6 ||
2078 (aGradient
->mShape
!= NS_STYLE_GRADIENT_SHAPE_LINEAR
&&
2079 (radiusX
< 1e-6 || radiusY
< 1e-6))) {
2080 // Stops are all at the same place. Map all stops to 0.0.
2081 // For radial gradients we need to fill with the last stop color,
2082 // so just set both radii to 0.
2084 radiusX
= radiusY
= 0.0;
2085 lastStop
= firstStop
;
2087 stopScale
= 1.0/stopDelta
;
2090 // Create the gradient pattern.
2091 nsRefPtr
<gfxPattern
> gradientPattern
;
2092 if (aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_LINEAR
) {
2093 // Compute the actual gradient line ends we need to pass to cairo after
2094 // stops have been normalized.
2095 gfxPoint gradientStart
= lineStart
+ (lineEnd
- lineStart
)*firstStop
;
2096 gfxPoint gradientEnd
= lineStart
+ (lineEnd
- lineStart
)*lastStop
;
2098 if (stopScale
== 0.0) {
2099 // Stops are all at the same place. For repeating gradients, this will
2100 // just paint the last stop color. We don't need to do anything.
2101 // For non-repeating gradients, this should render as two colors, one
2102 // on each "side" of the gradient line segment, which is a point. All
2103 // our stops will be at 0.0; we just need to set the direction vector
2105 gradientEnd
= gradientStart
+ (lineEnd
- lineStart
);
2108 gradientPattern
= new gfxPattern(gradientStart
.x
, gradientStart
.y
,
2109 gradientEnd
.x
, gradientEnd
.y
);
2111 NS_ASSERTION(firstStop
>= 0.0,
2112 "Negative stops not allowed for radial gradients");
2114 // To form an ellipse, we'll stretch a circle vertically, if necessary.
2115 // So our radii are based on radiusX.
2116 double innerRadius
= radiusX
*firstStop
;
2117 double outerRadius
= radiusX
*lastStop
;
2118 gradientPattern
= new gfxPattern(lineStart
.x
, lineStart
.y
, innerRadius
,
2119 lineStart
.x
, lineStart
.y
, outerRadius
);
2120 if (gradientPattern
&& radiusX
!= radiusY
) {
2121 // Stretch the circles into ellipses vertically by setting a transform
2123 // Recall that this is the transform from user space to pattern space.
2124 // So to stretch the ellipse by factor of P vertically, we scale
2125 // user coordinates by 1/P.
2127 matrix
.Translate(lineStart
);
2128 matrix
.Scale(1.0, radiusX
/radiusY
);
2129 matrix
.Translate(-lineStart
);
2130 gradientPattern
->SetMatrix(matrix
);
2133 if (!gradientPattern
|| gradientPattern
->CairoStatus())
2136 // Now set normalized color stops in pattern.
2137 if (stopScale
== 0.0) {
2138 // Non-repeating linear gradient with all stops in same place -> just add
2139 // first stop and last stop, both at position 0.
2140 // Repeating or radial gradient with all stops in the same place -> just
2141 // paint the last stop color.
2142 if (!aGradient
->mRepeating
&&
2143 aGradient
->mShape
== NS_STYLE_GRADIENT_SHAPE_LINEAR
) {
2144 gradientPattern
->AddColorStop(0.0, stops
[0].mColor
);
2146 gradientPattern
->AddColorStop(0.0, stops
[stops
.Length() - 1].mColor
);
2149 for (PRUint32 i
= 0; i
< stops
.Length(); i
++) {
2150 double pos
= stopScale
*(stops
[i
].mPosition
- firstStop
);
2151 gradientPattern
->AddColorStop(pos
, stops
[i
].mColor
);
2155 // Set repeat mode. Default cairo extend mode is PAD.
2156 if (aGradient
->mRepeating
) {
2157 gradientPattern
->SetExtend(gfxPattern::EXTEND_REPEAT
);
2160 // Paint gradient tiles. This isn't terribly efficient, but doing it this
2161 // way is simple and sure to get pixel-snapping right. We could speed things
2162 // up by drawing tiles into temporary surfaces and copying those to the
2163 // destination, but after pixel-snapping tiles may not all be the same size.
2165 if (!dirty
.IntersectRect(aDirtyRect
, aFillArea
))
2168 gfxRect areaToFill
=
2169 nsLayoutUtils::RectToGfxRect(aFillArea
, appUnitsPerPixel
);
2170 gfxMatrix ctm
= ctx
->CurrentMatrix();
2172 // xStart/yStart are the top-left corner of the top-left tile.
2173 nscoord xStart
= FindTileStart(dirty
.x
, aOneCellArea
.x
, aOneCellArea
.width
);
2174 nscoord yStart
= FindTileStart(dirty
.y
, aOneCellArea
.y
, aOneCellArea
.height
);
2175 nscoord xEnd
= dirty
.XMost();
2176 nscoord yEnd
= dirty
.YMost();
2177 // x and y are the top-left corner of the tile to draw
2178 for (nscoord y
= yStart
; y
< yEnd
; y
+= aOneCellArea
.height
) {
2179 for (nscoord x
= xStart
; x
< xEnd
; x
+= aOneCellArea
.width
) {
2180 // The coordinates of the tile
2181 gfxRect tileRect
= nsLayoutUtils::RectToGfxRect(
2182 nsRect(x
, y
, aOneCellArea
.width
, aOneCellArea
.height
),
2184 // The actual area to fill with this tile is the intersection of this
2185 // tile with the overall area we're supposed to be filling
2186 gfxRect fillRect
= tileRect
.Intersect(areaToFill
);
2188 ctx
->Translate(tileRect
.pos
);
2189 ctx
->SetPattern(gradientPattern
);
2190 ctx
->Rectangle(fillRect
- tileRect
.pos
, PR_TRUE
);
2192 ctx
->SetMatrix(ctm
);
2198 * A struct representing all the information needed to paint a background
2199 * image to some target, taking into account all CSS background-* properties.
2200 * See PrepareBackgroundLayer.
2202 struct BackgroundLayerState
{
2204 * @param aFlags some combination of nsCSSRendering::PAINTBG_* flags
2206 BackgroundLayerState(nsIFrame
* aForFrame
, const nsStyleImage
* aImage
, PRUint32 aFlags
)
2207 : mImageRenderer(aForFrame
, aImage
, aFlags
) {}
2210 * The ImageRenderer that will be used to draw the background.
2212 ImageRenderer mImageRenderer
;
2214 * A rectangle that one copy of the image tile is mapped onto. Same
2215 * coordinate system as aBorderArea/aBGClipRect passed into
2216 * PrepareBackgroundLayer.
2220 * The actual rectangle that should be filled with (complete or partial)
2221 * image tiles. Same coordinate system as aBorderArea/aBGClipRect passed into
2222 * PrepareBackgroundLayer.
2226 * The anchor point that should be snapped to a pixel corner. Same
2227 * coordinate system as aBorderArea/aBGClipRect passed into
2228 * PrepareBackgroundLayer.
2233 static BackgroundLayerState
2234 PrepareBackgroundLayer(nsPresContext
* aPresContext
,
2235 nsIFrame
* aForFrame
,
2237 const nsRect
& aBorderArea
,
2238 const nsRect
& aBGClipRect
,
2239 const nsStyleBackground
& aBackground
,
2240 const nsStyleBackground::Layer
& aLayer
);
2243 nsCSSRendering::PaintBackgroundWithSC(nsPresContext
* aPresContext
,
2244 nsIRenderingContext
& aRenderingContext
,
2245 nsIFrame
* aForFrame
,
2246 const nsRect
& aDirtyRect
,
2247 const nsRect
& aBorderArea
,
2248 nsStyleContext
* aBackgroundSC
,
2249 const nsStyleBorder
& aBorder
,
2251 nsRect
* aBGClipRect
)
2253 NS_PRECONDITION(aForFrame
,
2254 "Frame is expected to be provided to PaintBackground");
2256 // Check to see if we have an appearance defined. If so, we let the theme
2257 // renderer draw the background and bail out.
2258 // XXXzw this ignores aBGClipRect.
2259 const nsStyleDisplay
* displayData
= aForFrame
->GetStyleDisplay();
2260 if (displayData
->mAppearance
) {
2261 nsITheme
*theme
= aPresContext
->GetTheme();
2262 if (theme
&& theme
->ThemeSupportsWidget(aPresContext
, aForFrame
,
2263 displayData
->mAppearance
)) {
2264 nsRect
drawing(aBorderArea
);
2265 theme
->GetWidgetOverflow(aPresContext
->DeviceContext(),
2266 aForFrame
, displayData
->mAppearance
, &drawing
);
2267 drawing
.IntersectRect(drawing
, aDirtyRect
);
2268 theme
->DrawWidgetBackground(&aRenderingContext
, aForFrame
,
2269 displayData
->mAppearance
, aBorderArea
,
2275 // For canvas frames (in the CSS sense) we draw the background color using
2276 // a solid color item that gets added in nsLayoutUtils::PaintFrame,
2277 // or nsSubDocumentFrame::BuildDisplayList (bug 488242). (The solid
2278 // color may be moved into nsDisplayCanvasBackground by
2279 // nsPresShell::AddCanvasBackgroundColorItem, and painted by
2280 // nsDisplayCanvasBackground directly.) Either way we don't need to
2281 // paint the background color here.
2282 PRBool isCanvasFrame
= IsCanvasFrame(aForFrame
);
2284 // Determine whether we are drawing background images and/or
2285 // background colors.
2286 PRBool drawBackgroundImage
;
2287 PRBool drawBackgroundColor
;
2289 nscolor bgColor
= DetermineBackgroundColorInternal(aPresContext
,
2292 drawBackgroundImage
,
2293 drawBackgroundColor
);
2295 // At this point, drawBackgroundImage and drawBackgroundColor are
2296 // true if and only if we are actually supposed to paint an image or
2297 // color into aDirtyRect, respectively.
2298 if (!drawBackgroundImage
&& !drawBackgroundColor
)
2301 // Compute the outermost boundary of the area that might be painted.
2302 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
2303 nscoord appUnitsPerPixel
= aPresContext
->AppUnitsPerDevPixel();
2305 // Same coordinate space as aBorderArea & aBGClipRect
2306 gfxCornerSizes bgRadii
;
2307 PRBool haveRoundedCorners
;
2310 nsSize frameSize
= aForFrame
->GetSize();
2311 if (&aBorder
== aForFrame
->GetStyleBorder() &&
2312 frameSize
== aBorderArea
.Size()) {
2313 haveRoundedCorners
= aForFrame
->GetBorderRadii(radii
);
2315 haveRoundedCorners
= nsIFrame::ComputeBorderRadii(aBorder
.mBorderRadius
,
2316 frameSize
, aBorderArea
.Size(),
2317 aForFrame
->GetSkipSides(), radii
);
2319 if (haveRoundedCorners
)
2320 ComputePixelRadii(radii
, appUnitsPerPixel
, &bgRadii
);
2323 // The 'bgClipArea' (used only by the image tiling logic, far below)
2324 // is the caller-provided aBGClipRect if any, or else the area
2325 // determined by the value of 'background-clip' in
2326 // SetupCurrentBackgroundClip. (Arguably it should be the
2327 // intersection, but that breaks the table painter -- in particular,
2328 // taking the intersection breaks reftests/bugs/403249-1[ab].)
2329 const nsStyleBackground
*bg
= aBackgroundSC
->GetStyleBackground();
2330 BackgroundClipState clipState
;
2331 PRUint8 currentBackgroundClip
;
2332 PRBool isSolidBorder
;
2334 clipState
.mBGClipArea
= *aBGClipRect
;
2335 clipState
.mCustomClip
= PR_TRUE
;
2336 SetupDirtyRects(clipState
.mBGClipArea
, aDirtyRect
, appUnitsPerPixel
,
2337 &clipState
.mDirtyRect
, &clipState
.mDirtyRectGfx
);
2339 // The background is rendered over the 'background-clip' area,
2340 // which is normally equal to the border area but may be reduced
2341 // to the padding area by CSS. Also, if the border is solid, we
2342 // don't need to draw outside the padding area. In either case,
2343 // if the borders are rounded, make sure we use the same inner
2344 // radii as the border code will.
2345 // The background-color is drawn based on the bottom
2347 currentBackgroundClip
= bg
->BottomLayer().mClip
;
2349 (aFlags
& PAINTBG_WILL_PAINT_BORDER
) && IsOpaqueBorder(aBorder
);
2350 if (isSolidBorder
&& currentBackgroundClip
== NS_STYLE_BG_CLIP_BORDER
)
2351 currentBackgroundClip
= NS_STYLE_BG_CLIP_PADDING
;
2353 GetBackgroundClip(ctx
, currentBackgroundClip
, aForFrame
, aBorderArea
,
2354 aDirtyRect
, haveRoundedCorners
, bgRadii
, appUnitsPerPixel
,
2358 // If we might be using a background color, go ahead and set it now.
2359 if (drawBackgroundColor
&& !isCanvasFrame
)
2360 ctx
->SetColor(gfxRGBA(bgColor
));
2362 gfxContextAutoSaveRestore autoSR
;
2364 // If there is no background image, draw a color. (If there is
2365 // neither a background image nor a color, we wouldn't have gotten
2367 if (!drawBackgroundImage
) {
2368 if (!isCanvasFrame
) {
2369 DrawBackgroundColor(clipState
, ctx
, haveRoundedCorners
, appUnitsPerPixel
);
2374 // Ensure we get invalidated for loads of the image. We need to do
2375 // this here because this might be the only code that knows about the
2376 // association of the style data with the frame.
2377 aPresContext
->SetupBackgroundImageLoaders(aForFrame
, bg
);
2379 // We can skip painting the background color if a background image is opaque.
2380 if (drawBackgroundColor
&&
2381 bg
->BottomLayer().mRepeat
== NS_STYLE_BG_REPEAT_XY
&&
2382 bg
->BottomLayer().mImage
.IsOpaque())
2383 drawBackgroundColor
= PR_FALSE
;
2385 // The background color is rendered over the entire dirty area,
2386 // even if the image isn't.
2387 if (drawBackgroundColor
&& !isCanvasFrame
) {
2388 DrawBackgroundColor(clipState
, ctx
, haveRoundedCorners
, appUnitsPerPixel
);
2391 if (drawBackgroundImage
) {
2392 bool clipSet
= false;
2393 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i
, bg
) {
2394 const nsStyleBackground::Layer
&layer
= bg
->mLayers
[i
];
2396 PRUint8 newBackgroundClip
= layer
.mClip
;
2397 if (isSolidBorder
&& newBackgroundClip
== NS_STYLE_BG_CLIP_BORDER
)
2398 newBackgroundClip
= NS_STYLE_BG_CLIP_PADDING
;
2399 if (currentBackgroundClip
!= newBackgroundClip
|| !clipSet
) {
2400 currentBackgroundClip
= newBackgroundClip
;
2401 // If clipSet is false that means this is the bottom layer and we
2402 // already called GetBackgroundClip above and it stored its results
2405 GetBackgroundClip(ctx
, currentBackgroundClip
, aForFrame
,
2406 aBorderArea
, aDirtyRect
, haveRoundedCorners
,
2407 bgRadii
, appUnitsPerPixel
, &clipState
);
2409 SetupBackgroundClip(clipState
, ctx
, haveRoundedCorners
,
2410 appUnitsPerPixel
, &autoSR
);
2414 if (!clipState
.mDirtyRectGfx
.IsEmpty()) {
2415 BackgroundLayerState state
= PrepareBackgroundLayer(aPresContext
, aForFrame
,
2416 aFlags
, aBorderArea
, clipState
.mBGClipArea
, *bg
, layer
);
2417 if (!state
.mFillArea
.IsEmpty()) {
2418 state
.mImageRenderer
.Draw(aPresContext
, aRenderingContext
,
2419 state
.mDestArea
, state
.mFillArea
,
2420 state
.mAnchor
+ aBorderArea
.TopLeft(),
2421 clipState
.mDirtyRect
);
2429 ScaleDimension(const nsStyleBackground::Size::Dimension
& aDimension
,
2431 nscoord aLength
, nscoord aAvailLength
)
2434 case nsStyleBackground::Size::eLengthPercentage
:
2435 // negative values could result from calc()
2436 return NS_MAX(double(aDimension
.mPercent
) * double(aAvailLength
) +
2437 double(aDimension
.mLength
),
2441 NS_ABORT_IF_FALSE(PR_FALSE
, "bad aDimension.mType");
2443 case nsStyleBackground::Size::eAuto
:
2444 NS_ABORT_IF_FALSE(PR_FALSE
, "aDimension.mType == eAuto isn't handled");
2449 static inline PRBool
2450 IsTransformed(nsIFrame
* aForFrame
, nsIFrame
* aTopFrame
)
2452 for (nsIFrame
* f
= aForFrame
; f
!= aTopFrame
; f
= f
->GetParent()) {
2453 if (f
->IsTransformed()) {
2460 static BackgroundLayerState
2461 PrepareBackgroundLayer(nsPresContext
* aPresContext
,
2462 nsIFrame
* aForFrame
,
2464 const nsRect
& aBorderArea
,
2465 const nsRect
& aBGClipRect
,
2466 const nsStyleBackground
& aBackground
,
2467 const nsStyleBackground::Layer
& aLayer
)
2470 * The background properties we need to keep in mind when drawing background
2475 * background-attachment
2476 * background-position
2480 * background-break (-moz-background-inline-policy)
2482 * (background-color applies to the entire element and not to individual
2483 * layers, so it is irrelevant to this method.)
2485 * These properties have the following dependencies upon each other when
2486 * determining rendering:
2492 * background-attachment
2494 * background-position
2495 * depends upon background-size (for the image's scaled size) and
2496 * background-break (for the background positioning area)
2500 * depends upon background-attachment (only in the case where that value
2503 * depends upon background-break (for the background positioning area for
2504 * resolving percentages), background-image (for the image's intrinsic
2505 * size), background-repeat (if that value is 'round'), and
2506 * background-origin (for the background painting area, when
2507 * background-repeat is 'round')
2509 * depends upon background-origin (specifying how the boxes making up the
2510 * background positioning area are determined)
2512 * As a result of only-if dependencies we don't strictly do a topological
2513 * sort of the above properties when processing, but it's pretty close to one:
2515 * background-clip (by caller)
2517 * background-break, background-origin
2518 * background-attachment (postfix for background-{origin,break} if 'fixed')
2520 * background-position
2524 PRUint32 irFlags
= 0;
2525 if (aFlags
& nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES
) {
2526 irFlags
|= ImageRenderer::FLAG_SYNC_DECODE_IMAGES
;
2529 BackgroundLayerState
state(aForFrame
, &aLayer
.mImage
, irFlags
);
2530 if (!state
.mImageRenderer
.PrepareImage()) {
2531 // There's no image or it's not ready to be painted.
2535 // Compute background origin area relative to aBorderArea now as we may need
2536 // it to compute the effective image size for a CSS gradient.
2537 nsRect
bgPositioningArea(0, 0, 0, 0);
2539 nsIAtom
* frameType
= aForFrame
->GetType();
2540 nsIFrame
* geometryFrame
= aForFrame
;
2541 if (frameType
== nsGkAtoms::inlineFrame
||
2542 frameType
== nsGkAtoms::positionedInlineFrame
) {
2543 // XXXjwalden Strictly speaking this is not quite faithful to how
2544 // background-break is supposed to interact with background-origin values,
2545 // but it's a non-trivial amount of work to make it fully conformant, and
2546 // until the specification is more finalized (and assuming background-break
2547 // even makes the cut) it doesn't make sense to hammer out exact behavior.
2548 switch (aBackground
.mBackgroundInlinePolicy
) {
2549 case NS_STYLE_BG_INLINE_POLICY_EACH_BOX
:
2550 bgPositioningArea
= nsRect(nsPoint(0,0), aBorderArea
.Size());
2552 case NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX
:
2553 bgPositioningArea
= gInlineBGData
->GetBoundingRect(aForFrame
);
2556 NS_ERROR("Unknown background-inline-policy value! "
2557 "Please, teach me what to do.");
2558 case NS_STYLE_BG_INLINE_POLICY_CONTINUOUS
:
2559 bgPositioningArea
= gInlineBGData
->GetContinuousRect(aForFrame
);
2562 } else if (frameType
== nsGkAtoms::canvasFrame
) {
2563 geometryFrame
= aForFrame
->GetFirstChild(nsnull
);
2564 // geometryFrame might be null if this canvas is a page created
2565 // as an overflow container (e.g. the in-flow content has already
2566 // finished and this page only displays the continuations of
2567 // absolutely positioned content).
2568 if (geometryFrame
) {
2569 bgPositioningArea
= geometryFrame
->GetRect();
2572 bgPositioningArea
= nsRect(nsPoint(0,0), aBorderArea
.Size());
2575 // Background images are tiled over the 'background-clip' area
2576 // but the origin of the tiling is based on the 'background-origin' area
2577 if (aLayer
.mOrigin
!= NS_STYLE_BG_ORIGIN_BORDER
&& geometryFrame
) {
2578 nsMargin border
= geometryFrame
->GetUsedBorder();
2579 if (aLayer
.mOrigin
!= NS_STYLE_BG_ORIGIN_PADDING
) {
2580 border
+= geometryFrame
->GetUsedPadding();
2581 NS_ASSERTION(aLayer
.mOrigin
== NS_STYLE_BG_ORIGIN_CONTENT
,
2582 "unknown background-origin value");
2584 geometryFrame
->ApplySkipSides(border
);
2585 bgPositioningArea
.Deflate(border
);
2588 // For background-attachment:fixed backgrounds, we'll limit the area
2589 // where the background can be drawn to the viewport.
2590 nsRect bgClipRect
= aBGClipRect
;
2592 // Compute the anchor point.
2594 // relative to aBorderArea.TopLeft() (which is where the top-left
2595 // of aForFrame's border-box will be rendered)
2596 nsPoint imageTopLeft
;
2597 if (NS_STYLE_BG_ATTACHMENT_FIXED
== aLayer
.mAttachment
) {
2598 aPresContext
->SetHasFixedBackgroundFrame();
2600 // If it's a fixed background attachment, then the image is placed
2601 // relative to the viewport, which is the area of the root frame
2602 // in a screen context or the page content frame in a print context.
2603 nsIFrame
* topFrame
=
2604 aPresContext
->PresShell()->FrameManager()->GetRootFrame();
2605 NS_ASSERTION(topFrame
, "no root frame");
2606 nsIFrame
* pageContentFrame
= nsnull
;
2607 if (aPresContext
->IsPaginated()) {
2609 nsLayoutUtils::GetClosestFrameOfType(aForFrame
, nsGkAtoms::pageContentFrame
);
2610 if (pageContentFrame
) {
2611 topFrame
= pageContentFrame
;
2613 // else this is an embedded shell and its root frame is what we want
2616 // Set the background positioning area to the viewport's area
2617 // (relative to aForFrame)
2618 bgPositioningArea
= nsRect(-aForFrame
->GetOffsetTo(topFrame
), topFrame
->GetSize());
2620 if (!pageContentFrame
) {
2621 // Subtract the size of scrollbars.
2622 nsIScrollableFrame
* scrollableFrame
=
2623 aPresContext
->PresShell()->GetRootScrollFrameAsScrollable();
2624 if (scrollableFrame
) {
2625 nsMargin scrollbars
= scrollableFrame
->GetActualScrollbarSizes();
2626 bgPositioningArea
.Deflate(scrollbars
);
2630 if (aFlags
& nsCSSRendering::PAINTBG_TO_WINDOW
&&
2631 !IsTransformed(aForFrame
, topFrame
)) {
2632 // Clip background-attachment:fixed backgrounds to the viewport, if we're
2633 // painting to the screen and not transformed. This avoids triggering
2634 // tiling in common cases, without affecting output since drawing is
2635 // always clipped to the viewport when we draw to the screen. (But it's
2636 // not a pure optimization since it can affect the values of pixels at the
2637 // edge of the viewport --- whether they're sampled from a putative "next
2639 bgClipRect
.IntersectRect(bgClipRect
, bgPositioningArea
+ aBorderArea
.TopLeft());
2643 nsSize imageSize
= state
.mImageRenderer
.ComputeSize(bgPositioningArea
.Size());
2644 if (imageSize
.width
<= 0 || imageSize
.height
<= 0)
2647 // Scale the image as specified for background-size and as required for
2648 // proper background positioning when background-position is defined with
2650 float scaleX
, scaleY
;
2651 switch (aLayer
.mSize
.mWidthType
) {
2652 case nsStyleBackground::Size::eContain
:
2653 case nsStyleBackground::Size::eCover
: {
2654 float scaleFitX
= double(bgPositioningArea
.width
) / imageSize
.width
;
2655 float scaleFitY
= double(bgPositioningArea
.height
) / imageSize
.height
;
2656 if (aLayer
.mSize
.mWidthType
== nsStyleBackground::Size::eCover
) {
2657 scaleX
= scaleY
= NS_MAX(scaleFitX
, scaleFitY
);
2659 scaleX
= scaleY
= NS_MIN(scaleFitX
, scaleFitY
);
2664 if (aLayer
.mSize
.mWidthType
== nsStyleBackground::Size::eAuto
) {
2665 if (aLayer
.mSize
.mHeightType
== nsStyleBackground::Size::eAuto
) {
2666 scaleX
= scaleY
= 1.0f
;
2669 ScaleDimension(aLayer
.mSize
.mHeight
, aLayer
.mSize
.mHeightType
,
2670 imageSize
.height
, bgPositioningArea
.height
);
2673 if (aLayer
.mSize
.mHeightType
== nsStyleBackground::Size::eAuto
) {
2675 ScaleDimension(aLayer
.mSize
.mWidth
, aLayer
.mSize
.mWidthType
,
2676 imageSize
.width
, bgPositioningArea
.width
);
2678 scaleX
= ScaleDimension(aLayer
.mSize
.mWidth
, aLayer
.mSize
.mWidthType
,
2679 imageSize
.width
, bgPositioningArea
.width
);
2680 scaleY
= ScaleDimension(aLayer
.mSize
.mHeight
, aLayer
.mSize
.mHeightType
,
2681 imageSize
.height
, bgPositioningArea
.height
);
2687 imageSize
.width
= NSCoordSaturatingNonnegativeMultiply(imageSize
.width
, scaleX
);
2688 imageSize
.height
= NSCoordSaturatingNonnegativeMultiply(imageSize
.height
, scaleY
);
2690 // Compute the position of the background now that the background's size is
2692 ComputeBackgroundAnchorPoint(aLayer
, bgPositioningArea
.Size(), imageSize
,
2693 &imageTopLeft
, &state
.mAnchor
);
2694 imageTopLeft
+= bgPositioningArea
.TopLeft();
2695 state
.mAnchor
+= bgPositioningArea
.TopLeft();
2697 state
.mDestArea
= nsRect(imageTopLeft
+ aBorderArea
.TopLeft(), imageSize
);
2698 state
.mFillArea
= state
.mDestArea
;
2699 PRIntn repeat
= aLayer
.mRepeat
;
2700 PR_STATIC_ASSERT(NS_STYLE_BG_REPEAT_XY
==
2701 (NS_STYLE_BG_REPEAT_X
| NS_STYLE_BG_REPEAT_Y
));
2702 if (repeat
& NS_STYLE_BG_REPEAT_X
) {
2703 state
.mFillArea
.x
= bgClipRect
.x
;
2704 state
.mFillArea
.width
= bgClipRect
.width
;
2706 if (repeat
& NS_STYLE_BG_REPEAT_Y
) {
2707 state
.mFillArea
.y
= bgClipRect
.y
;
2708 state
.mFillArea
.height
= bgClipRect
.height
;
2710 state
.mFillArea
.IntersectRect(state
.mFillArea
, bgClipRect
);
2715 nsCSSRendering::GetBackgroundLayerRect(nsPresContext
* aPresContext
,
2716 nsIFrame
* aForFrame
,
2717 const nsRect
& aBorderArea
,
2718 const nsStyleBackground
& aBackground
,
2719 const nsStyleBackground::Layer
& aLayer
)
2721 BackgroundLayerState state
=
2722 PrepareBackgroundLayer(aPresContext
, aForFrame
, 0, aBorderArea
,
2723 aBorderArea
, aBackground
, aLayer
);
2724 return state
.mFillArea
;
2728 DrawBorderImage(nsPresContext
* aPresContext
,
2729 nsIRenderingContext
& aRenderingContext
,
2730 nsIFrame
* aForFrame
,
2731 const nsRect
& aBorderArea
,
2732 const nsStyleBorder
& aStyleBorder
,
2733 const nsRect
& aDirtyRect
)
2735 if (aDirtyRect
.IsEmpty())
2738 // Ensure we get invalidated for loads and animations of the image.
2739 // We need to do this here because this might be the only code that
2740 // knows about the association of the style data with the frame.
2741 // XXX We shouldn't really... since if anybody is passing in a
2742 // different style, they'll potentially have the wrong size for the
2744 aPresContext
->SetupBorderImageLoaders(aForFrame
, &aStyleBorder
);
2746 imgIRequest
*req
= aStyleBorder
.GetBorderImage();
2750 PRUint32 status
= imgIRequest::STATUS_ERROR
;
2752 req
->GetImageStatus(&status
);
2754 NS_ASSERTION(req
&& (status
& imgIRequest::STATUS_LOAD_COMPLETE
),
2755 "no image to draw");
2759 // Get the actual image, and determine where the split points are.
2760 // Note that mBorderImageSplit is in image pixels, not necessarily
2763 nsCOMPtr
<imgIContainer
> imgContainer
;
2764 req
->GetImage(getter_AddRefs(imgContainer
));
2766 nsIntSize imageSize
;
2767 if (NS_FAILED(imgContainer
->GetWidth(&imageSize
.width
))) {
2769 nsPresContext::AppUnitsToIntCSSPixels(aBorderArea
.width
);
2771 if (NS_FAILED(imgContainer
->GetHeight(&imageSize
.height
))) {
2773 nsPresContext::AppUnitsToIntCSSPixels(aBorderArea
.height
);
2776 // Convert percentages and clamp values to the image size.
2778 NS_FOR_CSS_SIDES(s
) {
2779 nsStyleCoord coord
= aStyleBorder
.mBorderImageSplit
.Get(s
);
2780 PRInt32 imgDimension
= ((s
== NS_SIDE_TOP
|| s
== NS_SIDE_BOTTOM
)
2784 switch (coord
.GetUnit()) {
2785 case eStyleUnit_Percent
:
2786 value
= coord
.GetPercentValue() * imgDimension
;
2788 case eStyleUnit_Factor
:
2789 value
= coord
.GetFactorValue();
2792 NS_ASSERTION(coord
.GetUnit() == eStyleUnit_Null
,
2793 "unexpected CSS unit for image split");
2799 if (value
> imgDimension
)
2800 value
= imgDimension
;
2801 split
.side(s
) = NS_lround(value
);
2804 nsMargin
border(aStyleBorder
.GetActualBorder());
2806 // These helper tables recharacterize the 'split' and 'border' margins
2807 // in a more convenient form: they are the x/y/width/height coords
2808 // required for various bands of the border, and they have been transformed
2809 // to be relative to the image (for 'split') or the page (for 'border').
2811 LEFT
, MIDDLE
, RIGHT
,
2812 TOP
= LEFT
, BOTTOM
= RIGHT
2814 const nscoord borderX
[3] = {
2816 aBorderArea
.x
+ border
.left
,
2817 aBorderArea
.x
+ aBorderArea
.width
- border
.right
,
2819 const nscoord borderY
[3] = {
2821 aBorderArea
.y
+ border
.top
,
2822 aBorderArea
.y
+ aBorderArea
.height
- border
.bottom
,
2824 const nscoord borderWidth
[3] = {
2826 aBorderArea
.width
- border
.left
- border
.right
,
2829 const nscoord borderHeight
[3] = {
2831 aBorderArea
.height
- border
.top
- border
.bottom
,
2835 const PRInt32 splitX
[3] = {
2838 imageSize
.width
- split
.right
,
2840 const PRInt32 splitY
[3] = {
2843 imageSize
.height
- split
.bottom
,
2845 const PRInt32 splitWidth
[3] = {
2847 imageSize
.width
- split
.left
- split
.right
,
2850 const PRInt32 splitHeight
[3] = {
2852 imageSize
.height
- split
.top
- split
.bottom
,
2856 // In all the 'factor' calculations below, 'border' measurements are
2857 // in app units but 'split' measurements are in image/CSS pixels, so
2858 // the factor corresponding to no additional scaling is
2859 // CSSPixelsToAppUnits(1), not simply 1.
2860 for (int i
= LEFT
; i
<= RIGHT
; i
++) {
2861 for (int j
= TOP
; j
<= BOTTOM
; j
++) {
2862 nsRect
destArea(borderX
[i
], borderY
[j
], borderWidth
[i
], borderHeight
[j
]);
2863 nsIntRect
subArea(splitX
[i
], splitY
[j
], splitWidth
[i
], splitHeight
[j
]);
2865 PRUint8 fillStyleH
, fillStyleV
;
2868 if (i
== MIDDLE
&& j
== MIDDLE
) {
2870 // The middle image's width is scaled by the same factor as the
2871 // top image unless that factor is zero or infinity, in which
2872 // case the scaling factor of the bottom is substituted, and
2873 // failing that, the width is not scaled. The height of the
2874 // middle image is scaled by the same factor as the left image
2875 // unless that factor is zero or infinity, in which case the
2876 // scaling factor of the right image is substituted, and failing
2877 // that, the height is not scaled.
2878 gfxFloat hFactor
, vFactor
;
2880 if (0 < border
.left
&& 0 < split
.left
)
2881 vFactor
= gfxFloat(border
.left
)/split
.left
;
2882 else if (0 < border
.right
&& 0 < split
.right
)
2883 vFactor
= gfxFloat(border
.right
)/split
.right
;
2885 vFactor
= nsPresContext::CSSPixelsToAppUnits(1);
2887 if (0 < border
.top
&& 0 < split
.top
)
2888 hFactor
= gfxFloat(border
.top
)/split
.top
;
2889 else if (0 < border
.bottom
&& 0 < split
.bottom
)
2890 hFactor
= gfxFloat(border
.bottom
)/split
.bottom
;
2892 hFactor
= nsPresContext::CSSPixelsToAppUnits(1);
2894 unitSize
.width
= splitWidth
[i
]*hFactor
;
2895 unitSize
.height
= splitHeight
[j
]*vFactor
;
2896 fillStyleH
= aStyleBorder
.mBorderImageHFill
;
2897 fillStyleV
= aStyleBorder
.mBorderImageVFill
;
2899 } else if (i
== MIDDLE
) { // top, bottom
2900 // Sides are always stretched to the thickness of their border,
2901 // and stretched proportionately on the other axis.
2903 if (0 < borderHeight
[j
] && 0 < splitHeight
[j
])
2904 factor
= gfxFloat(borderHeight
[j
])/splitHeight
[j
];
2906 factor
= nsPresContext::CSSPixelsToAppUnits(1);
2908 unitSize
.width
= splitWidth
[i
]*factor
;
2909 unitSize
.height
= borderHeight
[j
];
2910 fillStyleH
= aStyleBorder
.mBorderImageHFill
;
2911 fillStyleV
= NS_STYLE_BORDER_IMAGE_STRETCH
;
2913 } else if (j
== MIDDLE
) { // left, right
2915 if (0 < borderWidth
[i
] && 0 < splitWidth
[i
])
2916 factor
= gfxFloat(borderWidth
[i
])/splitWidth
[i
];
2918 factor
= nsPresContext::CSSPixelsToAppUnits(1);
2920 unitSize
.width
= borderWidth
[i
];
2921 unitSize
.height
= splitHeight
[j
]*factor
;
2922 fillStyleH
= NS_STYLE_BORDER_IMAGE_STRETCH
;
2923 fillStyleV
= aStyleBorder
.mBorderImageVFill
;
2926 // Corners are always stretched to fit the corner.
2927 unitSize
.width
= borderWidth
[i
];
2928 unitSize
.height
= borderHeight
[j
];
2929 fillStyleH
= NS_STYLE_BORDER_IMAGE_STRETCH
;
2930 fillStyleV
= NS_STYLE_BORDER_IMAGE_STRETCH
;
2933 DrawBorderImageComponent(aRenderingContext
, aForFrame
,
2934 imgContainer
, aDirtyRect
,
2936 fillStyleH
, fillStyleV
,
2937 unitSize
, aStyleBorder
, i
* (RIGHT
+ 1) + j
);
2943 DrawBorderImageComponent(nsIRenderingContext
& aRenderingContext
,
2944 nsIFrame
* aForFrame
,
2945 imgIContainer
* aImage
,
2946 const nsRect
& aDirtyRect
,
2947 const nsRect
& aFill
,
2948 const nsIntRect
& aSrc
,
2951 const nsSize
& aUnitSize
,
2952 const nsStyleBorder
& aStyleBorder
,
2955 if (aFill
.IsEmpty() || aSrc
.IsEmpty())
2958 // Don't bother trying to cache sub images if the border image is animated
2959 // We can only sucessfully call GetAnimated() if we are fully decoded, so default to PR_TRUE
2960 PRBool animated
= PR_TRUE
;
2961 aImage
->GetAnimated(&animated
);
2963 nsCOMPtr
<imgIContainer
> subImage
;
2964 if (animated
|| (subImage
= aStyleBorder
.GetSubImage(aIndex
)) == 0) {
2965 if (NS_FAILED(aImage
->ExtractFrame(imgIContainer::FRAME_CURRENT
, aSrc
,
2966 imgIContainer::FLAG_SYNC_DECODE
,
2967 getter_AddRefs(subImage
))))
2971 aStyleBorder
.SetSubImage(aIndex
, subImage
);
2974 gfxPattern::GraphicsFilter graphicsFilter
=
2975 nsLayoutUtils::GetGraphicsFilterForFrame(aForFrame
);
2977 // If we have no tiling in either direction, we can skip the intermediate
2979 if ((aHFill
== NS_STYLE_BORDER_IMAGE_STRETCH
&&
2980 aVFill
== NS_STYLE_BORDER_IMAGE_STRETCH
) ||
2981 (aUnitSize
.width
== aFill
.width
&&
2982 aUnitSize
.height
== aFill
.height
)) {
2983 nsLayoutUtils::DrawSingleImage(&aRenderingContext
, subImage
,
2985 aFill
, aDirtyRect
, imgIContainer::FLAG_NONE
);
2989 // Compute the scale and position of the master copy of the image.
2992 case NS_STYLE_BORDER_IMAGE_STRETCH
:
2994 tile
.width
= aFill
.width
;
2996 case NS_STYLE_BORDER_IMAGE_REPEAT
:
2997 tile
.x
= aFill
.x
+ aFill
.width
/2 - aUnitSize
.width
/2;
2998 tile
.width
= aUnitSize
.width
;
3001 case NS_STYLE_BORDER_IMAGE_ROUND
:
3003 tile
.width
= aFill
.width
/ ceil(gfxFloat(aFill
.width
)/aUnitSize
.width
);
3007 NS_NOTREACHED("unrecognized border-image fill style");
3011 case NS_STYLE_BORDER_IMAGE_STRETCH
:
3013 tile
.height
= aFill
.height
;
3015 case NS_STYLE_BORDER_IMAGE_REPEAT
:
3016 tile
.y
= aFill
.y
+ aFill
.height
/2 - aUnitSize
.height
/2;
3017 tile
.height
= aUnitSize
.height
;
3020 case NS_STYLE_BORDER_IMAGE_ROUND
:
3022 tile
.height
= aFill
.height
/ceil(gfxFloat(aFill
.height
)/aUnitSize
.height
);
3026 NS_NOTREACHED("unrecognized border-image fill style");
3029 nsLayoutUtils::DrawImage(&aRenderingContext
, subImage
, graphicsFilter
,
3030 tile
, aFill
, tile
.TopLeft(), aDirtyRect
,
3031 imgIContainer::FLAG_NONE
);
3034 // Begin table border-collapsing section
3035 // These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
3036 // At some point, all functions should be unified to include the additional functionality that these provide
3039 RoundIntToPixel(nscoord aValue
,
3040 nscoord aTwipsPerPixel
,
3041 PRBool aRoundDown
= PR_FALSE
)
3043 if (aTwipsPerPixel
<= 0)
3044 // We must be rendering to a device that has a resolution greater than Twips!
3045 // In that case, aValue is as accurate as it's going to get.
3048 nscoord halfPixel
= NSToCoordRound(aTwipsPerPixel
/ 2.0f
);
3049 nscoord extra
= aValue
% aTwipsPerPixel
;
3050 nscoord finalValue
= (!aRoundDown
&& (extra
>= halfPixel
)) ? aValue
+ (aTwipsPerPixel
- extra
) : aValue
- extra
;
3055 RoundFloatToPixel(float aValue
,
3056 nscoord aTwipsPerPixel
,
3057 PRBool aRoundDown
= PR_FALSE
)
3059 return RoundIntToPixel(NSToCoordRound(aValue
), aTwipsPerPixel
, aRoundDown
);
3063 SetPoly(const nsRect
& aRect
,
3066 poly
[0].x
= aRect
.x
;
3067 poly
[0].y
= aRect
.y
;
3068 poly
[1].x
= aRect
.x
+ aRect
.width
;
3069 poly
[1].y
= aRect
.y
;
3070 poly
[2].x
= aRect
.x
+ aRect
.width
;
3071 poly
[2].y
= aRect
.y
+ aRect
.height
;
3072 poly
[3].x
= aRect
.x
;
3073 poly
[3].y
= aRect
.y
+ aRect
.height
;
3074 poly
[4].x
= aRect
.x
;
3075 poly
[4].y
= aRect
.y
;
3079 DrawSolidBorderSegment(nsIRenderingContext
& aContext
,
3081 nscoord aTwipsPerPixel
,
3082 PRUint8 aStartBevelSide
= 0,
3083 nscoord aStartBevelOffset
= 0,
3084 PRUint8 aEndBevelSide
= 0,
3085 nscoord aEndBevelOffset
= 0)
3088 if ((aRect
.width
== aTwipsPerPixel
) || (aRect
.height
== aTwipsPerPixel
) ||
3089 ((0 == aStartBevelOffset
) && (0 == aEndBevelOffset
))) {
3090 // simple line or rectangle
3091 if ((NS_SIDE_TOP
== aStartBevelSide
) || (NS_SIDE_BOTTOM
== aStartBevelSide
)) {
3092 if (1 == aRect
.height
)
3093 aContext
.DrawLine(aRect
.x
, aRect
.y
, aRect
.x
, aRect
.y
+ aRect
.height
);
3095 aContext
.FillRect(aRect
);
3098 if (1 == aRect
.width
)
3099 aContext
.DrawLine(aRect
.x
, aRect
.y
, aRect
.x
+ aRect
.width
, aRect
.y
);
3101 aContext
.FillRect(aRect
);
3105 // polygon with beveling
3107 SetPoly(aRect
, poly
);
3108 switch(aStartBevelSide
) {
3110 poly
[0].x
+= aStartBevelOffset
;
3111 poly
[4].x
= poly
[0].x
;
3113 case NS_SIDE_BOTTOM
:
3114 poly
[3].x
+= aStartBevelOffset
;
3117 poly
[1].y
+= aStartBevelOffset
;
3120 poly
[0].y
+= aStartBevelOffset
;
3121 poly
[4].y
= poly
[0].y
;
3124 switch(aEndBevelSide
) {
3126 poly
[1].x
-= aEndBevelOffset
;
3128 case NS_SIDE_BOTTOM
:
3129 poly
[2].x
-= aEndBevelOffset
;
3132 poly
[2].y
-= aEndBevelOffset
;
3135 poly
[3].y
-= aEndBevelOffset
;
3138 aContext
.FillPolygon(poly
, 5);
3145 GetDashInfo(nscoord aBorderLength
,
3146 nscoord aDashLength
,
3147 nscoord aTwipsPerPixel
,
3148 PRInt32
& aNumDashSpaces
,
3149 nscoord
& aStartDashLength
,
3150 nscoord
& aEndDashLength
)
3153 if (aStartDashLength
+ aDashLength
+ aEndDashLength
>= aBorderLength
) {
3154 aStartDashLength
= aBorderLength
;
3158 aNumDashSpaces
= (aBorderLength
- aDashLength
)/ (2 * aDashLength
); // round down
3159 nscoord extra
= aBorderLength
- aStartDashLength
- aEndDashLength
- (((2 * aNumDashSpaces
) - 1) * aDashLength
);
3161 nscoord half
= RoundIntToPixel(extra
/ 2, aTwipsPerPixel
);
3162 aStartDashLength
+= half
;
3163 aEndDashLength
+= (extra
- half
);
3169 nsCSSRendering::DrawTableBorderSegment(nsIRenderingContext
& aContext
,
3170 PRUint8 aBorderStyle
,
3171 nscolor aBorderColor
,
3172 const nsStyleBackground
* aBGColor
,
3173 const nsRect
& aBorder
,
3174 PRInt32 aAppUnitsPerCSSPixel
,
3175 PRUint8 aStartBevelSide
,
3176 nscoord aStartBevelOffset
,
3177 PRUint8 aEndBevelSide
,
3178 nscoord aEndBevelOffset
)
3180 aContext
.SetColor (aBorderColor
);
3182 PRBool horizontal
= ((NS_SIDE_TOP
== aStartBevelSide
) || (NS_SIDE_BOTTOM
== aStartBevelSide
));
3183 nscoord twipsPerPixel
= NSIntPixelsToAppUnits(1, aAppUnitsPerCSSPixel
);
3184 PRUint8 ridgeGroove
= NS_STYLE_BORDER_STYLE_RIDGE
;
3186 if ((twipsPerPixel
>= aBorder
.width
) || (twipsPerPixel
>= aBorder
.height
) ||
3187 (NS_STYLE_BORDER_STYLE_DASHED
== aBorderStyle
) || (NS_STYLE_BORDER_STYLE_DOTTED
== aBorderStyle
)) {
3188 // no beveling for 1 pixel border, dash or dot
3189 aStartBevelOffset
= 0;
3190 aEndBevelOffset
= 0;
3193 gfxContext
*ctx
= aContext
.ThebesContext();
3194 gfxContext::AntialiasMode oldMode
= ctx
->CurrentAntialiasMode();
3195 ctx
->SetAntialiasMode(gfxContext::MODE_ALIASED
);
3197 switch (aBorderStyle
) {
3198 case NS_STYLE_BORDER_STYLE_NONE
:
3199 case NS_STYLE_BORDER_STYLE_HIDDEN
:
3200 //NS_ASSERTION(PR_FALSE, "style of none or hidden");
3202 case NS_STYLE_BORDER_STYLE_DOTTED
:
3203 case NS_STYLE_BORDER_STYLE_DASHED
:
3205 nscoord dashLength
= (NS_STYLE_BORDER_STYLE_DASHED
== aBorderStyle
) ? DASH_LENGTH
: DOT_LENGTH
;
3206 // make the dash length proportional to the border thickness
3207 dashLength
*= (horizontal
) ? aBorder
.height
: aBorder
.width
;
3208 // make the min dash length for the ends 1/2 the dash length
3209 nscoord minDashLength
= (NS_STYLE_BORDER_STYLE_DASHED
== aBorderStyle
)
3210 ? RoundFloatToPixel(((float)dashLength
) / 2.0f
, twipsPerPixel
) : dashLength
;
3211 minDashLength
= NS_MAX(minDashLength
, twipsPerPixel
);
3212 nscoord numDashSpaces
= 0;
3213 nscoord startDashLength
= minDashLength
;
3214 nscoord endDashLength
= minDashLength
;
3216 GetDashInfo(aBorder
.width
, dashLength
, twipsPerPixel
, numDashSpaces
, startDashLength
, endDashLength
);
3217 nsRect
rect(aBorder
.x
, aBorder
.y
, startDashLength
, aBorder
.height
);
3218 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
);
3219 for (PRInt32 spaceX
= 0; spaceX
< numDashSpaces
; spaceX
++) {
3220 rect
.x
+= rect
.width
+ dashLength
;
3221 rect
.width
= (spaceX
== (numDashSpaces
- 1)) ? endDashLength
: dashLength
;
3222 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
);
3226 GetDashInfo(aBorder
.height
, dashLength
, twipsPerPixel
, numDashSpaces
, startDashLength
, endDashLength
);
3227 nsRect
rect(aBorder
.x
, aBorder
.y
, aBorder
.width
, startDashLength
);
3228 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
);
3229 for (PRInt32 spaceY
= 0; spaceY
< numDashSpaces
; spaceY
++) {
3230 rect
.y
+= rect
.height
+ dashLength
;
3231 rect
.height
= (spaceY
== (numDashSpaces
- 1)) ? endDashLength
: dashLength
;
3232 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
);
3237 case NS_STYLE_BORDER_STYLE_GROOVE
:
3238 ridgeGroove
= NS_STYLE_BORDER_STYLE_GROOVE
; // and fall through to ridge
3239 case NS_STYLE_BORDER_STYLE_RIDGE
:
3240 if ((horizontal
&& (twipsPerPixel
>= aBorder
.height
)) ||
3241 (!horizontal
&& (twipsPerPixel
>= aBorder
.width
))) {
3242 // a one pixel border
3243 DrawSolidBorderSegment(aContext
, aBorder
, twipsPerPixel
, aStartBevelSide
, aStartBevelOffset
,
3244 aEndBevelSide
, aEndBevelOffset
);
3247 nscoord startBevel
= (aStartBevelOffset
> 0)
3248 ? RoundFloatToPixel(0.5f
* (float)aStartBevelOffset
, twipsPerPixel
, PR_TRUE
) : 0;
3249 nscoord endBevel
= (aEndBevelOffset
> 0)
3250 ? RoundFloatToPixel(0.5f
* (float)aEndBevelOffset
, twipsPerPixel
, PR_TRUE
) : 0;
3251 mozilla::css::Side ridgeGrooveSide
= (horizontal
) ? NS_SIDE_TOP
: NS_SIDE_LEFT
;
3252 // FIXME: In theory, this should use the visited-dependent
3253 // background color, but I don't care.
3255 MakeBevelColor(ridgeGrooveSide
, ridgeGroove
, aBGColor
->mBackgroundColor
, aBorderColor
));
3256 nsRect
rect(aBorder
);
3258 if (horizontal
) { // top, bottom
3259 half
= RoundFloatToPixel(0.5f
* (float)aBorder
.height
, twipsPerPixel
);
3261 if (NS_SIDE_TOP
== aStartBevelSide
) {
3262 rect
.x
+= startBevel
;
3263 rect
.width
-= startBevel
;
3265 if (NS_SIDE_TOP
== aEndBevelSide
) {
3266 rect
.width
-= endBevel
;
3268 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
, aStartBevelSide
,
3269 startBevel
, aEndBevelSide
, endBevel
);
3271 else { // left, right
3272 half
= RoundFloatToPixel(0.5f
* (float)aBorder
.width
, twipsPerPixel
);
3274 if (NS_SIDE_LEFT
== aStartBevelSide
) {
3275 rect
.y
+= startBevel
;
3276 rect
.height
-= startBevel
;
3278 if (NS_SIDE_LEFT
== aEndBevelSide
) {
3279 rect
.height
-= endBevel
;
3281 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
, aStartBevelSide
,
3282 startBevel
, aEndBevelSide
, endBevel
);
3286 ridgeGrooveSide
= (NS_SIDE_TOP
== ridgeGrooveSide
) ? NS_SIDE_BOTTOM
: NS_SIDE_RIGHT
;
3287 // FIXME: In theory, this should use the visited-dependent
3288 // background color, but I don't care.
3290 MakeBevelColor(ridgeGrooveSide
, ridgeGroove
, aBGColor
->mBackgroundColor
, aBorderColor
));
3292 rect
.y
= rect
.y
+ half
;
3293 rect
.height
= aBorder
.height
- half
;
3294 if (NS_SIDE_BOTTOM
== aStartBevelSide
) {
3295 rect
.x
+= startBevel
;
3296 rect
.width
-= startBevel
;
3298 if (NS_SIDE_BOTTOM
== aEndBevelSide
) {
3299 rect
.width
-= endBevel
;
3301 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
, aStartBevelSide
,
3302 startBevel
, aEndBevelSide
, endBevel
);
3305 rect
.x
= rect
.x
+ half
;
3306 rect
.width
= aBorder
.width
- half
;
3307 if (NS_SIDE_RIGHT
== aStartBevelSide
) {
3308 rect
.y
+= aStartBevelOffset
- startBevel
;
3309 rect
.height
-= startBevel
;
3311 if (NS_SIDE_RIGHT
== aEndBevelSide
) {
3312 rect
.height
-= endBevel
;
3314 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
, aStartBevelSide
,
3315 startBevel
, aEndBevelSide
, endBevel
);
3319 case NS_STYLE_BORDER_STYLE_DOUBLE
:
3320 if ((aBorder
.width
> 2) && (aBorder
.height
> 2)) {
3321 nscoord startBevel
= (aStartBevelOffset
> 0)
3322 ? RoundFloatToPixel(0.333333f
* (float)aStartBevelOffset
, twipsPerPixel
) : 0;
3323 nscoord endBevel
= (aEndBevelOffset
> 0)
3324 ? RoundFloatToPixel(0.333333f
* (float)aEndBevelOffset
, twipsPerPixel
) : 0;
3325 if (horizontal
) { // top, bottom
3326 nscoord thirdHeight
= RoundFloatToPixel(0.333333f
* (float)aBorder
.height
, twipsPerPixel
);
3328 // draw the top line or rect
3329 nsRect
topRect(aBorder
.x
, aBorder
.y
, aBorder
.width
, thirdHeight
);
3330 if (NS_SIDE_TOP
== aStartBevelSide
) {
3331 topRect
.x
+= aStartBevelOffset
- startBevel
;
3332 topRect
.width
-= aStartBevelOffset
- startBevel
;
3334 if (NS_SIDE_TOP
== aEndBevelSide
) {
3335 topRect
.width
-= aEndBevelOffset
- endBevel
;
3337 DrawSolidBorderSegment(aContext
, topRect
, twipsPerPixel
, aStartBevelSide
,
3338 startBevel
, aEndBevelSide
, endBevel
);
3340 // draw the botom line or rect
3341 nscoord heightOffset
= aBorder
.height
- thirdHeight
;
3342 nsRect
bottomRect(aBorder
.x
, aBorder
.y
+ heightOffset
, aBorder
.width
, aBorder
.height
- heightOffset
);
3343 if (NS_SIDE_BOTTOM
== aStartBevelSide
) {
3344 bottomRect
.x
+= aStartBevelOffset
- startBevel
;
3345 bottomRect
.width
-= aStartBevelOffset
- startBevel
;
3347 if (NS_SIDE_BOTTOM
== aEndBevelSide
) {
3348 bottomRect
.width
-= aEndBevelOffset
- endBevel
;
3350 DrawSolidBorderSegment(aContext
, bottomRect
, twipsPerPixel
, aStartBevelSide
,
3351 startBevel
, aEndBevelSide
, endBevel
);
3353 else { // left, right
3354 nscoord thirdWidth
= RoundFloatToPixel(0.333333f
* (float)aBorder
.width
, twipsPerPixel
);
3356 nsRect
leftRect(aBorder
.x
, aBorder
.y
, thirdWidth
, aBorder
.height
);
3357 if (NS_SIDE_LEFT
== aStartBevelSide
) {
3358 leftRect
.y
+= aStartBevelOffset
- startBevel
;
3359 leftRect
.height
-= aStartBevelOffset
- startBevel
;
3361 if (NS_SIDE_LEFT
== aEndBevelSide
) {
3362 leftRect
.height
-= aEndBevelOffset
- endBevel
;
3364 DrawSolidBorderSegment(aContext
, leftRect
, twipsPerPixel
, aStartBevelSide
,
3365 startBevel
, aEndBevelSide
, endBevel
);
3367 nscoord widthOffset
= aBorder
.width
- thirdWidth
;
3368 nsRect
rightRect(aBorder
.x
+ widthOffset
, aBorder
.y
, aBorder
.width
- widthOffset
, aBorder
.height
);
3369 if (NS_SIDE_RIGHT
== aStartBevelSide
) {
3370 rightRect
.y
+= aStartBevelOffset
- startBevel
;
3371 rightRect
.height
-= aStartBevelOffset
- startBevel
;
3373 if (NS_SIDE_RIGHT
== aEndBevelSide
) {
3374 rightRect
.height
-= aEndBevelOffset
- endBevel
;
3376 DrawSolidBorderSegment(aContext
, rightRect
, twipsPerPixel
, aStartBevelSide
,
3377 startBevel
, aEndBevelSide
, endBevel
);
3381 // else fall through to solid
3382 case NS_STYLE_BORDER_STYLE_SOLID
:
3383 DrawSolidBorderSegment(aContext
, aBorder
, twipsPerPixel
, aStartBevelSide
,
3384 aStartBevelOffset
, aEndBevelSide
, aEndBevelOffset
);
3386 case NS_STYLE_BORDER_STYLE_OUTSET
:
3387 case NS_STYLE_BORDER_STYLE_INSET
:
3388 NS_ASSERTION(PR_FALSE
, "inset, outset should have been converted to groove, ridge");
3390 case NS_STYLE_BORDER_STYLE_AUTO
:
3391 NS_ASSERTION(PR_FALSE
, "Unexpected 'auto' table border");
3395 ctx
->SetAntialiasMode(oldMode
);
3398 // End table border-collapsing section
3401 nsCSSRendering::PaintDecorationLine(gfxContext
* aGfxContext
,
3402 const nscolor aColor
,
3403 const gfxPoint
& aPt
,
3404 const gfxSize
& aLineSize
,
3405 const gfxFloat aAscent
,
3406 const gfxFloat aOffset
,
3407 const PRUint8 aDecoration
,
3408 const PRUint8 aStyle
,
3409 const gfxFloat aDescentLimit
)
3411 NS_ASSERTION(aStyle
!= DECORATION_STYLE_NONE
, "aStyle is none");
3414 GetTextDecorationRectInternal(aPt
, aLineSize
, aAscent
, aOffset
,
3415 aDecoration
, aStyle
, aDescentLimit
);
3419 if (aDecoration
!= NS_STYLE_TEXT_DECORATION_UNDERLINE
&&
3420 aDecoration
!= NS_STYLE_TEXT_DECORATION_OVERLINE
&&
3421 aDecoration
!= NS_STYLE_TEXT_DECORATION_LINE_THROUGH
)
3423 NS_ERROR("Invalid decoration value!");
3427 gfxFloat lineHeight
= NS_MAX(NS_round(aLineSize
.height
), 1.0);
3428 PRBool contextIsSaved
= PR_FALSE
;
3430 gfxFloat oldLineWidth
;
3431 nsRefPtr
<gfxPattern
> oldPattern
;
3434 case DECORATION_STYLE_SOLID
:
3435 case DECORATION_STYLE_DOUBLE
:
3436 oldLineWidth
= aGfxContext
->CurrentLineWidth();
3437 oldPattern
= aGfxContext
->GetPattern();
3439 case DECORATION_STYLE_DASHED
: {
3440 aGfxContext
->Save();
3441 contextIsSaved
= PR_TRUE
;
3442 aGfxContext
->Clip(rect
);
3443 gfxFloat dashWidth
= lineHeight
* DOT_LENGTH
* DASH_LENGTH
;
3444 gfxFloat dash
[2] = { dashWidth
, dashWidth
};
3445 aGfxContext
->SetLineCap(gfxContext::LINE_CAP_BUTT
);
3446 aGfxContext
->SetDash(dash
, 2, 0.0);
3447 // We should continue to draw the last dash even if it is not in the rect.
3448 rect
.size
.width
+= dashWidth
;
3451 case DECORATION_STYLE_DOTTED
: {
3452 aGfxContext
->Save();
3453 contextIsSaved
= PR_TRUE
;
3454 aGfxContext
->Clip(rect
);
3455 gfxFloat dashWidth
= lineHeight
* DOT_LENGTH
;
3457 if (lineHeight
> 2.0) {
3459 dash
[1] = dashWidth
* 2.0;
3460 aGfxContext
->SetLineCap(gfxContext::LINE_CAP_ROUND
);
3462 dash
[0] = dashWidth
;
3463 dash
[1] = dashWidth
;
3465 aGfxContext
->SetDash(dash
, 2, 0.0);
3466 // We should continue to draw the last dot even if it is not in the rect.
3467 rect
.size
.width
+= dashWidth
;
3470 case DECORATION_STYLE_WAVY
:
3471 aGfxContext
->Save();
3472 contextIsSaved
= PR_TRUE
;
3473 aGfxContext
->Clip(rect
);
3474 if (lineHeight
> 2.0) {
3475 aGfxContext
->SetAntialiasMode(gfxContext::MODE_COVERAGE
);
3477 // Don't use anti-aliasing here. Because looks like lighter color wavy
3478 // line at this case. And probably, users don't think the
3479 // non-anti-aliased wavy line is not pretty.
3480 aGfxContext
->SetAntialiasMode(gfxContext::MODE_ALIASED
);
3484 NS_ERROR("Invalid style value!");
3488 // The y position should be set to the middle of the line.
3489 rect
.pos
.y
+= lineHeight
/ 2;
3491 aGfxContext
->SetColor(gfxRGBA(aColor
));
3492 aGfxContext
->SetLineWidth(lineHeight
);
3494 case DECORATION_STYLE_SOLID
:
3495 aGfxContext
->NewPath();
3496 aGfxContext
->MoveTo(rect
.TopLeft());
3497 aGfxContext
->LineTo(rect
.TopRight());
3498 aGfxContext
->Stroke();
3500 case DECORATION_STYLE_DOUBLE
:
3502 * We are drawing double line as:
3504 * +-------------------------------------------+
3505 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3506 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3507 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3510 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3511 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3512 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3513 * +-------------------------------------------+
3515 aGfxContext
->NewPath();
3516 aGfxContext
->MoveTo(rect
.TopLeft());
3517 aGfxContext
->LineTo(rect
.TopRight());
3518 rect
.size
.height
-= lineHeight
;
3519 aGfxContext
->MoveTo(rect
.BottomLeft());
3520 aGfxContext
->LineTo(rect
.BottomRight());
3521 aGfxContext
->Stroke();
3523 case DECORATION_STYLE_DOTTED
:
3524 case DECORATION_STYLE_DASHED
:
3525 aGfxContext
->NewPath();
3526 aGfxContext
->MoveTo(rect
.TopLeft());
3527 aGfxContext
->LineTo(rect
.TopRight());
3528 aGfxContext
->Stroke();
3530 case DECORATION_STYLE_WAVY
: {
3532 * We are drawing wavy line as:
3534 * P: Path, X: Painted pixel
3536 * +---------------------------------------+
3537 * XX|X XXXXXX XXXXXX |
3538 * PP|PX XPPPPPPX XPPPPPPX | ^
3539 * XX|XPX XPXXXXXXPX XPXXXXXXPX| |
3540 * | XPX XPX XPX XPX XP|X |adv
3541 * | XPXXXXXXPX XPXXXXXXPX X|PX |
3542 * | XPPPPPPX XPPPPPPX |XPX v
3543 * | XXXXXX XXXXXX | XX
3544 * +---------------------------------------+
3546 * adv flatLengthAtVertex rightMost
3548 * 1. Always starts from top-left of the drawing area, however, we need
3549 * to draw the line from outside of the rect. Because the start
3550 * point of the line is not good style if we draw from inside it.
3551 * 2. First, draw horizontal line from outside the rect to top-left of
3553 * 3. Goes down to bottom of the area at 45 degrees.
3554 * 4. Slides to right horizontaly, see |flatLengthAtVertex|.
3555 * 5. Goes up to top of the area at 45 degrees.
3556 * 6. Slides to right horizontaly.
3557 * 7. Repeat from 2 until reached to right-most edge of the area.
3560 rect
.pos
.x
+= lineHeight
/ 2.0;
3561 aGfxContext
->NewPath();
3563 gfxPoint
pt(rect
.pos
);
3564 gfxFloat rightMost
= pt
.x
+ rect
.Width() + lineHeight
;
3565 gfxFloat adv
= rect
.Height() - lineHeight
;
3566 gfxFloat flatLengthAtVertex
= NS_MAX((lineHeight
- 1.0) * 2.0, 1.0);
3569 aGfxContext
->MoveTo(pt
); // 1
3572 aGfxContext
->LineTo(pt
); // 2
3574 PRBool goDown
= PR_TRUE
;
3575 while (pt
.x
< rightMost
) {
3577 pt
.y
+= goDown
? adv
: -adv
;
3579 aGfxContext
->LineTo(pt
); // 3 and 5
3581 pt
.x
+= flatLengthAtVertex
;
3582 aGfxContext
->LineTo(pt
); // 4 and 6
3586 aGfxContext
->Stroke();
3590 NS_ERROR("Invalid style value!");
3594 if (contextIsSaved
) {
3595 aGfxContext
->Restore();
3597 aGfxContext
->SetPattern(oldPattern
);
3598 aGfxContext
->SetLineWidth(oldLineWidth
);
3603 nsCSSRendering::GetTextDecorationRect(nsPresContext
* aPresContext
,
3604 const gfxSize
& aLineSize
,
3605 const gfxFloat aAscent
,
3606 const gfxFloat aOffset
,
3607 const PRUint8 aDecoration
,
3608 const PRUint8 aStyle
,
3609 const gfxFloat aDescentLimit
)
3611 NS_ASSERTION(aPresContext
, "aPresContext is null");
3612 NS_ASSERTION(aStyle
!= DECORATION_STYLE_NONE
, "aStyle is none");
3615 GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize
, aAscent
, aOffset
,
3616 aDecoration
, aStyle
, aDescentLimit
);
3617 // The rect values are already rounded to nearest device pixels.
3619 r
.x
= aPresContext
->GfxUnitsToAppUnits(rect
.X());
3620 r
.y
= aPresContext
->GfxUnitsToAppUnits(rect
.Y());
3621 r
.width
= aPresContext
->GfxUnitsToAppUnits(rect
.Width());
3622 r
.height
= aPresContext
->GfxUnitsToAppUnits(rect
.Height());
3627 nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint
& aPt
,
3628 const gfxSize
& aLineSize
,
3629 const gfxFloat aAscent
,
3630 const gfxFloat aOffset
,
3631 const PRUint8 aDecoration
,
3632 const PRUint8 aStyle
,
3633 const gfxFloat aDescentLimit
)
3635 NS_ASSERTION(aStyle
<= DECORATION_STYLE_WAVY
, "Invalid aStyle value");
3637 if (aStyle
== DECORATION_STYLE_NONE
)
3638 return gfxRect(0, 0, 0, 0);
3640 PRBool canLiftUnderline
= aDescentLimit
>= 0.0;
3643 r
.pos
.x
= NS_floor(aPt
.x
+ 0.5);
3644 r
.size
.width
= NS_round(aLineSize
.width
);
3646 gfxFloat lineHeight
= NS_round(aLineSize
.height
);
3647 lineHeight
= NS_MAX(lineHeight
, 1.0);
3649 gfxFloat ascent
= NS_round(aAscent
);
3650 gfxFloat descentLimit
= NS_floor(aDescentLimit
);
3652 gfxFloat suggestedMaxRectHeight
= NS_MAX(NS_MIN(ascent
, descentLimit
), 1.0);
3653 r
.size
.height
= lineHeight
;
3654 if (aStyle
== DECORATION_STYLE_DOUBLE
) {
3656 * We will draw double line as:
3658 * +-------------------------------------------+
3659 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3660 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3661 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3665 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3666 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3667 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3668 * +-------------------------------------------+
3670 gfxFloat gap
= NS_round(lineHeight
/ 2.0);
3671 gap
= NS_MAX(gap
, 1.0);
3672 r
.size
.height
= lineHeight
* 2.0 + gap
;
3673 if (canLiftUnderline
) {
3674 if (r
.Height() > suggestedMaxRectHeight
) {
3675 // Don't shrink the line height, because the thickness has some meaning.
3676 // We can just shrink the gap at this time.
3677 r
.size
.height
= NS_MAX(suggestedMaxRectHeight
, lineHeight
* 2.0 + 1.0);
3680 } else if (aStyle
== DECORATION_STYLE_WAVY
) {
3682 * We will draw wavy line as:
3684 * +-------------------------------------------+
3685 * |XXXXX XXXXXX XXXXXX | ^
3686 * |XXXXXX XXXXXXXX XXXXXXXX | | lineHeight
3687 * |XXXXXXX XXXXXXXXXX XXXXXXXXXX| v
3688 * | XXX XXX XXX XXX XX|
3689 * | XXXXXXXXXX XXXXXXXXXX X|
3690 * | XXXXXXXX XXXXXXXX |
3692 * +-------------------------------------------+
3694 r
.size
.height
= lineHeight
> 2.0 ? lineHeight
* 4.0 : lineHeight
* 3.0;
3695 if (canLiftUnderline
) {
3696 if (r
.Height() > suggestedMaxRectHeight
) {
3697 // Don't shrink the line height even if there is not enough space,
3698 // because the thickness has some meaning. E.g., the 1px wavy line and
3699 // 2px wavy line can be used for different meaning in IME selections
3701 r
.size
.height
= NS_MAX(suggestedMaxRectHeight
, lineHeight
* 2.0);
3706 gfxFloat baseline
= NS_floor(aPt
.y
+ aAscent
+ 0.5);
3707 gfxFloat offset
= 0.0;
3708 switch (aDecoration
) {
3709 case NS_STYLE_TEXT_DECORATION_UNDERLINE
:
3711 if (canLiftUnderline
) {
3712 if (descentLimit
< -offset
+ r
.Height()) {
3713 // If we can ignore the offset and the decoration line is overflowing,
3714 // we should align the bottom edge of the decoration line rect if it's
3715 // possible. Otherwise, we should lift up the top edge of the rect as
3717 gfxFloat offsetBottomAligned
= -descentLimit
+ r
.Height();
3718 gfxFloat offsetTopAligned
= 0.0;
3719 offset
= NS_MIN(offsetBottomAligned
, offsetTopAligned
);
3723 case NS_STYLE_TEXT_DECORATION_OVERLINE
:
3724 offset
= aOffset
- lineHeight
+ r
.Height();
3726 case NS_STYLE_TEXT_DECORATION_LINE_THROUGH
: {
3727 gfxFloat extra
= NS_floor(r
.Height() / 2.0 + 0.5);
3728 extra
= NS_MAX(extra
, lineHeight
);
3729 offset
= aOffset
- lineHeight
+ extra
;
3733 NS_ERROR("Invalid decoration value!");
3735 r
.pos
.y
= baseline
- NS_floor(offset
+ 0.5);
3739 // ------------------
3741 // ------------------
3742 ImageRenderer::ImageRenderer(nsIFrame
* aForFrame
,
3743 const nsStyleImage
* aImage
,
3745 : mForFrame(aForFrame
)
3747 , mType(aImage
->GetType())
3748 , mImageContainer(nsnull
)
3749 , mGradientData(nsnull
)
3751 , mPaintServerFrame(nsnull
)
3753 , mIsReady(PR_FALSE
)
3759 ImageRenderer::~ImageRenderer()
3764 ImageRenderer::PrepareImage()
3766 if (mImage
->IsEmpty() || !mImage
->IsComplete()) {
3767 // Make sure the image is actually decoding
3768 mImage
->RequestDecode();
3770 // We can not prepare the image for rendering if it is not fully loaded.
3772 // Special case: If we requested a sync decode and we have an image, push
3774 nsCOMPtr
<imgIContainer
> img
;
3775 if (!((mFlags
& FLAG_SYNC_DECODE_IMAGES
) &&
3776 (mType
== eStyleImageType_Image
) &&
3777 (NS_SUCCEEDED(mImage
->GetImageData()->GetImage(getter_AddRefs(img
))) && img
)))
3782 case eStyleImageType_Image
:
3784 nsCOMPtr
<imgIContainer
> srcImage
;
3785 mImage
->GetImageData()->GetImage(getter_AddRefs(srcImage
));
3786 NS_ABORT_IF_FALSE(srcImage
, "If srcImage is null, mImage->IsComplete() "
3787 "should have returned false");
3789 if (!mImage
->GetCropRect()) {
3790 mImageContainer
.swap(srcImage
);
3792 nsIntRect actualCropRect
;
3793 PRBool isEntireImage
;
3795 mImage
->ComputeActualCropRect(actualCropRect
, &isEntireImage
);
3796 NS_ASSERTION(success
, "ComputeActualCropRect() should not fail here");
3797 if (!success
|| actualCropRect
.IsEmpty()) {
3798 // The cropped image has zero size
3801 if (isEntireImage
) {
3802 // The cropped image is identical to the source image
3803 mImageContainer
.swap(srcImage
);
3805 nsCOMPtr
<imgIContainer
> subImage
;
3806 PRUint32 aExtractFlags
= (mFlags
& FLAG_SYNC_DECODE_IMAGES
)
3807 ? (PRUint32
) imgIContainer::FLAG_SYNC_DECODE
3808 : (PRUint32
) imgIContainer::FLAG_NONE
;
3809 nsresult rv
= srcImage
->ExtractFrame(imgIContainer::FRAME_CURRENT
,
3810 actualCropRect
, aExtractFlags
,
3811 getter_AddRefs(subImage
));
3812 if (NS_FAILED(rv
)) {
3813 NS_WARNING("The cropped image contains no pixels to draw; "
3814 "maybe the crop rect is outside the image frame rect");
3817 mImageContainer
.swap(subImage
);
3823 case eStyleImageType_Gradient
:
3824 mGradientData
= mImage
->GetGradientData();
3828 case eStyleImageType_Element
:
3830 nsAutoString elementId
=
3831 NS_LITERAL_STRING("#") + nsDependentString(mImage
->GetElementId());
3832 nsCOMPtr
<nsIURI
> targetURI
;
3833 nsCOMPtr
<nsIURI
> base
= mForFrame
->GetContent()->GetBaseURI();
3834 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI
), elementId
,
3835 mForFrame
->GetContent()->GetCurrentDoc(), base
);
3836 nsSVGPaintingProperty
* property
= nsSVGEffects::GetPaintingPropertyForURI(
3837 targetURI
, mForFrame
->GetFirstContinuation(),
3838 nsSVGEffects::BackgroundImageProperty());
3841 mPaintServerFrame
= property
->GetReferencedFrame();
3843 // If the referenced element doesn't have a frame we might still be able
3844 // to paint it if it's an <img>, <canvas>, or <video> element.
3845 if (!mPaintServerFrame
) {
3846 nsCOMPtr
<nsIDOMElement
> imageElement
=
3847 do_QueryInterface(property
->GetReferencedElement());
3848 mImageElementSurface
= nsLayoutUtils::SurfaceFromElement(imageElement
);
3849 if (!mImageElementSurface
.mSurface
)
3856 case eStyleImageType_Null
:
3865 ImageRenderer::ComputeSize(const nsSize
& aDefault
)
3867 NS_ASSERTION(mIsReady
, "Ensure PrepareImage() has returned true "
3868 "before calling me");
3871 case eStyleImageType_Image
:
3873 nsIntSize imageIntSize
;
3874 PRBool gotHeight
, gotWidth
;
3875 nsLayoutUtils::ComputeSizeForDrawing(mImageContainer
, imageIntSize
,
3876 gotWidth
, gotHeight
);
3878 mSize
.width
= gotWidth
?
3879 nsPresContext::CSSPixelsToAppUnits(imageIntSize
.width
) :
3882 mSize
.height
= gotHeight
?
3883 nsPresContext::CSSPixelsToAppUnits(imageIntSize
.height
) :
3888 case eStyleImageType_Gradient
:
3892 case eStyleImageType_Element
:
3894 if (mPaintServerFrame
) {
3895 if (mPaintServerFrame
->IsFrameOfType(nsIFrame::eSVG
)) {
3898 // The intrinsic image size for a generic nsIFrame paint server is
3899 // the frame's bbox size rounded to device pixels.
3900 PRInt32 appUnitsPerDevPixel
=
3901 mForFrame
->PresContext()->AppUnitsPerDevPixel();
3903 nsSVGIntegrationUtils::GetNonSVGUserSpace(mPaintServerFrame
);
3904 nsRect size
= rect
- rect
.TopLeft();
3905 nsIntRect rounded
= size
.ToNearestPixels(appUnitsPerDevPixel
);
3906 mSize
= rounded
.ToAppUnits(appUnitsPerDevPixel
).Size();
3909 NS_ASSERTION(mImageElementSurface
.mSurface
, "Surface should be ready.");
3910 gfxIntSize size
= mImageElementSurface
.mSize
;
3911 mSize
.width
= nsPresContext::CSSPixelsToAppUnits(size
.width
);
3912 mSize
.height
= nsPresContext::CSSPixelsToAppUnits(size
.height
);
3917 case eStyleImageType_Null
:
3927 ImageRenderer::Draw(nsPresContext
* aPresContext
,
3928 nsIRenderingContext
& aRenderingContext
,
3929 const nsRect
& aDest
,
3930 const nsRect
& aFill
,
3931 const nsPoint
& aAnchor
,
3932 const nsRect
& aDirty
)
3935 NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
3939 if (aDest
.IsEmpty() || aFill
.IsEmpty() ||
3940 mSize
.width
<= 0 || mSize
.height
<= 0)
3943 gfxPattern::GraphicsFilter graphicsFilter
=
3944 nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame
);
3947 case eStyleImageType_Image
:
3949 PRUint32 drawFlags
= (mFlags
& FLAG_SYNC_DECODE_IMAGES
)
3950 ? (PRUint32
) imgIContainer::FLAG_SYNC_DECODE
3951 : (PRUint32
) imgIContainer::FLAG_NONE
;
3952 nsLayoutUtils::DrawImage(&aRenderingContext
, mImageContainer
,
3954 aDest
, aFill
, aAnchor
, aDirty
, drawFlags
);
3957 case eStyleImageType_Gradient
:
3958 nsCSSRendering::PaintGradient(aPresContext
, aRenderingContext
,
3959 mGradientData
, aDirty
, aDest
, aFill
);
3962 case eStyleImageType_Element
:
3963 if (mPaintServerFrame
) {
3964 nsSVGIntegrationUtils::DrawPaintServer(
3965 &aRenderingContext
, mForFrame
, mPaintServerFrame
, graphicsFilter
,
3966 aDest
, aFill
, aAnchor
, aDirty
, mSize
);
3968 NS_ASSERTION(mImageElementSurface
.mSurface
, "Surface should be ready.");
3969 nsRefPtr
<gfxDrawable
> surfaceDrawable
=
3970 new gfxSurfaceDrawable(mImageElementSurface
.mSurface
,
3971 mImageElementSurface
.mSize
);
3972 nsLayoutUtils::DrawPixelSnapped(
3973 &aRenderingContext
, surfaceDrawable
, graphicsFilter
,
3974 aDest
, aFill
, aAnchor
, aDirty
);
3978 case eStyleImageType_Null
:
3984 #define MAX_BLUR_RADIUS 300
3985 #define MAX_SPREAD_RADIUS 50
3987 static inline gfxIntSize
3988 ComputeBlurRadius(nscoord aBlurRadius
, PRInt32 aAppUnitsPerDevPixel
)
3990 // http://dev.w3.org/csswg/css3-background/#box-shadow says that the
3991 // standard deviation of the blur should be half the given blur value.
3992 gfxFloat blurStdDev
=
3993 NS_MIN(gfxFloat(aBlurRadius
) / gfxFloat(aAppUnitsPerDevPixel
),
3994 gfxFloat(MAX_BLUR_RADIUS
))
3997 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(blurStdDev
, blurStdDev
));
4004 nsContextBoxBlur::Init(const nsRect
& aRect
, nscoord aSpreadRadius
,
4005 nscoord aBlurRadius
,
4006 PRInt32 aAppUnitsPerDevPixel
,
4007 gfxContext
* aDestinationCtx
,
4008 const nsRect
& aDirtyRect
,
4009 const gfxRect
* aSkipRect
,
4012 if (aRect
.IsEmpty()) {
4017 gfxIntSize blurRadius
= ComputeBlurRadius(aBlurRadius
, aAppUnitsPerDevPixel
);
4018 PRInt32 spreadRadius
= NS_MIN(PRInt32(aSpreadRadius
/ aAppUnitsPerDevPixel
),
4019 PRInt32(MAX_SPREAD_RADIUS
));
4020 mDestinationCtx
= aDestinationCtx
;
4022 // If not blurring, draw directly onto the destination device
4023 if (blurRadius
.width
<= 0 && blurRadius
.height
<= 0 && spreadRadius
<= 0 &&
4024 !(aFlags
& FORCE_MASK
)) {
4025 mContext
= aDestinationCtx
;
4029 // Convert from app units to device pixels
4030 gfxRect rect
= nsLayoutUtils::RectToGfxRect(aRect
, aAppUnitsPerDevPixel
);
4033 nsLayoutUtils::RectToGfxRect(aDirtyRect
, aAppUnitsPerDevPixel
);
4034 dirtyRect
.RoundOut();
4036 // Create the temporary surface for blurring
4037 mContext
= blur
.Init(rect
, gfxIntSize(spreadRadius
, spreadRadius
),
4038 blurRadius
, &dirtyRect
, aSkipRect
);
4043 nsContextBoxBlur::DoPaint()
4045 if (mContext
== mDestinationCtx
)
4048 blur
.Paint(mDestinationCtx
);
4052 nsContextBoxBlur::GetContext()
4057 /* static */ nsMargin
4058 nsContextBoxBlur::GetBlurRadiusMargin(nscoord aBlurRadius
,
4059 PRInt32 aAppUnitsPerDevPixel
)
4061 gfxIntSize blurRadius
= ComputeBlurRadius(aBlurRadius
, aAppUnitsPerDevPixel
);
4064 result
.top
= blurRadius
.height
* aAppUnitsPerDevPixel
;
4065 result
.right
= blurRadius
.width
* aAppUnitsPerDevPixel
;
4066 result
.bottom
= blurRadius
.height
* aAppUnitsPerDevPixel
;
4067 result
.left
= blurRadius
.width
* aAppUnitsPerDevPixel
;