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 <mats.palmgren@bredband.net>
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>
31 * Alternatively, the contents of this file may be used under the terms of
32 * either of the GNU General Public License Version 2 or later (the "GPL"),
33 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34 * in which case the provisions of the GPL or the LGPL are applicable instead
35 * of those above. If you wish to allow use of your version of this file only
36 * under the terms of either the GPL or the LGPL, and not to allow others to
37 * use your version of this file under the terms of the MPL, indicate your
38 * decision by deleting the provisions above and replace them with the notice
39 * and other provisions required by the GPL or the LGPL. If you do not delete
40 * the provisions above, a recipient may use your version of this file under
41 * the terms of any one of the MPL, the GPL or the LGPL.
43 * ***** END LICENSE BLOCK ***** */
45 /* utility functions for drawing borders and backgrounds */
47 #include "nsStyleConsts.h"
48 #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 "gfxIImageFrame.h"
67 #include "nsCSSRendering.h"
68 #include "nsCSSColorUtils.h"
70 #include "nsThemeConstants.h"
71 #include "nsIServiceManager.h"
72 #include "nsIHTMLDocument.h"
73 #include "nsLayoutUtils.h"
74 #include "nsINameSpaceManager.h"
75 #include "nsBlockFrame.h"
76 #include "gfxContext.h"
77 #include "nsIInterfaceRequestorUtils.h"
78 #include "gfxPlatform.h"
79 #include "gfxImageSurface.h"
80 #include "nsStyleStructInlines.h"
81 #include "nsCSSFrameConstructor.h"
83 #include "nsCSSRenderingBorders.h"
85 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
86 // recalculating this for each frame in a continuation (perf), hold
87 // a cache of various coordinate information that we need in order
88 // to paint inline backgrounds.
89 struct InlineBackgroundData
91 InlineBackgroundData()
92 : mFrame(nsnull
), mBlockFrame(nsnull
)
96 ~InlineBackgroundData()
102 mBoundingBox
.SetRect(0,0,0,0);
103 mContinuationPoint
= mLineContinuationPoint
= mUnbrokenWidth
= 0;
104 mFrame
= mBlockFrame
= nsnull
;
107 nsRect
GetContinuousRect(nsIFrame
* aFrame
)
113 x
= mLineContinuationPoint
;
115 // Scan continuations on the same line as aFrame and accumulate the widths
116 // of frames that are to the left (if this is an LTR block) or right
117 // (if it's RTL) of the current one.
118 PRBool isRtlBlock
= (mBlockFrame
->GetStyleVisibility()->mDirection
==
119 NS_STYLE_DIRECTION_RTL
);
120 nscoord curOffset
= aFrame
->GetOffsetTo(mBlockFrame
).x
;
122 nsIFrame
* inlineFrame
= aFrame
->GetPrevContinuation();
123 // If the continuation is fluid we know inlineFrame is not on the same line.
124 // If it's not fluid, we need to test furhter to be sure.
125 while (inlineFrame
&& !inlineFrame
->GetNextInFlow() &&
126 AreOnSameLine(aFrame
, inlineFrame
)) {
127 nscoord frameXOffset
= inlineFrame
->GetOffsetTo(mBlockFrame
).x
;
128 if(isRtlBlock
== (frameXOffset
>= curOffset
)) {
129 x
+= inlineFrame
->GetSize().width
;
131 inlineFrame
= inlineFrame
->GetPrevContinuation();
134 inlineFrame
= aFrame
->GetNextContinuation();
135 while (inlineFrame
&& !inlineFrame
->GetPrevInFlow() &&
136 AreOnSameLine(aFrame
, inlineFrame
)) {
137 nscoord frameXOffset
= inlineFrame
->GetOffsetTo(mBlockFrame
).x
;
138 if(isRtlBlock
== (frameXOffset
>= curOffset
)) {
139 x
+= inlineFrame
->GetSize().width
;
141 inlineFrame
= inlineFrame
->GetNextContinuation();
144 // aFrame itself is also to the right of its left edge, so add its width.
145 x
+= aFrame
->GetSize().width
;
146 // x is now the distance from the left edge of aFrame to the right edge
147 // of the unbroken content. Change it to indicate the distance from the
148 // left edge of the unbroken content to the left edge of aFrame.
149 x
= mUnbrokenWidth
- x
;
152 x
= mContinuationPoint
;
155 // Assume background-origin: border and return a rect with offsets
156 // relative to (0,0). If we have a different background-origin,
157 // then our rect should be deflated appropriately by our caller.
158 return nsRect(-x
, 0, mUnbrokenWidth
, mFrame
->GetSize().height
);
161 nsRect
GetBoundingRect(nsIFrame
* aFrame
)
165 // Move the offsets relative to (0,0) which puts the bounding box into
166 // our coordinate system rather than our parent's. We do this by
167 // moving it the back distance from us to the bounding box.
168 // This also assumes background-origin: border, so our caller will
169 // need to deflate us if needed.
170 nsRect
boundingBox(mBoundingBox
);
171 nsPoint point
= mFrame
->GetPosition();
172 boundingBox
.MoveBy(-point
.x
, -point
.y
);
179 nscoord mContinuationPoint
;
180 nscoord mUnbrokenWidth
;
184 nsBlockFrame
* mBlockFrame
;
185 nscoord mLineContinuationPoint
;
187 void SetFrame(nsIFrame
* aFrame
)
189 NS_PRECONDITION(aFrame
, "Need a frame");
191 nsIFrame
*prevContinuation
= aFrame
->GetPrevContinuation();
193 if (!prevContinuation
|| mFrame
!= prevContinuation
) {
194 // Ok, we've got the wrong frame. We have to start from scratch.
200 // Get our last frame's size and add its width to our continuation
201 // point before we cache the new frame.
202 mContinuationPoint
+= mFrame
->GetSize().width
;
204 // If this a new line, update mLineContinuationPoint.
206 (aFrame
->GetPrevInFlow() || !AreOnSameLine(mFrame
, aFrame
))) {
207 mLineContinuationPoint
= mContinuationPoint
;
213 void Init(nsIFrame
* aFrame
)
215 // Start with the previous flow frame as our continuation point
216 // is the total of the widths of the previous frames.
217 nsIFrame
* inlineFrame
= aFrame
->GetPrevContinuation();
219 while (inlineFrame
) {
220 nsRect rect
= inlineFrame
->GetRect();
221 mContinuationPoint
+= rect
.width
;
222 mUnbrokenWidth
+= rect
.width
;
223 mBoundingBox
.UnionRect(mBoundingBox
, rect
);
224 inlineFrame
= inlineFrame
->GetPrevContinuation();
227 // Next add this frame and subsequent frames to the bounding box and
229 inlineFrame
= aFrame
;
230 while (inlineFrame
) {
231 nsRect rect
= inlineFrame
->GetRect();
232 mUnbrokenWidth
+= rect
.width
;
233 mBoundingBox
.UnionRect(mBoundingBox
, rect
);
234 inlineFrame
= inlineFrame
->GetNextContinuation();
239 mBidiEnabled
= aFrame
->PresContext()->BidiEnabled();
241 // Find the containing block frame
242 nsIFrame
* frame
= aFrame
;
243 nsresult rv
= NS_ERROR_FAILURE
;
245 frame
->IsFrameOfType(nsIFrame::eLineParticipant
) &&
247 frame
= frame
->GetParent();
248 rv
= frame
->QueryInterface(kBlockFrameCID
, (void**)&mBlockFrame
);
250 NS_ASSERTION(NS_SUCCEEDED(rv
) && mBlockFrame
, "Cannot find containing block.");
252 mLineContinuationPoint
= mContinuationPoint
;
256 PRBool
AreOnSameLine(nsIFrame
* aFrame1
, nsIFrame
* aFrame2
) {
257 // Assumes that aFrame1 and aFrame2 are both decsendants of mBlockFrame.
258 PRBool isValid1
, isValid2
;
259 nsBlockInFlowLineIterator
it1(mBlockFrame
, aFrame1
, &isValid1
);
260 nsBlockInFlowLineIterator
it2(mBlockFrame
, aFrame2
, &isValid2
);
261 return isValid1
&& isValid2
&& it1
.GetLine() == it2
.GetLine();
265 static InlineBackgroundData
* gInlineBGData
= nsnull
;
267 // Initialize any static variables used by nsCSSRendering.
268 nsresult
nsCSSRendering::Init()
270 NS_ASSERTION(!gInlineBGData
, "Init called twice");
271 gInlineBGData
= new InlineBackgroundData();
273 return NS_ERROR_OUT_OF_MEMORY
;
278 // Clean up any global variables used by nsCSSRendering.
279 void nsCSSRendering::Shutdown()
281 delete gInlineBGData
;
282 gInlineBGData
= nsnull
;
285 // Draw a line, skipping that portion which crosses aGap. aGap defines a rectangle gap
286 // This services fieldset legends and only works for coords defining horizontal lines.
287 void nsCSSRendering::DrawLine (nsIRenderingContext
& aContext
,
288 nscoord aX1
, nscoord aY1
, nscoord aX2
, nscoord aY2
,
291 if (nsnull
== aGap
) {
292 aContext
.DrawLine(aX1
, aY1
, aX2
, aY2
);
294 nscoord x1
= (aX1
< aX2
) ? aX1
: aX2
;
295 nscoord x2
= (aX1
< aX2
) ? aX2
: aX1
;
296 nsPoint
gapUpperRight(aGap
->x
+ aGap
->width
, aGap
->y
);
297 nsPoint
gapLowerRight(aGap
->x
+ aGap
->width
, aGap
->y
+ aGap
->height
);
298 if ((aGap
->y
<= aY1
) && (gapLowerRight
.y
>= aY2
)) {
299 if ((aGap
->x
> x1
) && (aGap
->x
< x2
)) {
300 aContext
.DrawLine(x1
, aY1
, aGap
->x
, aY1
);
302 if ((gapLowerRight
.x
> x1
) && (gapLowerRight
.x
< x2
)) {
303 aContext
.DrawLine(gapUpperRight
.x
, aY2
, x2
, aY2
);
306 aContext
.DrawLine(aX1
, aY1
, aX2
, aY2
);
311 // Fill a polygon, skipping that portion which crosses aGap. aGap defines a rectangle gap
312 // This services fieldset legends and only works for points defining a horizontal rectangle
313 void nsCSSRendering::FillPolygon (nsIRenderingContext
& aContext
,
314 const nsPoint aPoints
[],
319 if (nsnull
== aGap
) {
320 aContext
.FillPolygon(aPoints
, aNumPoints
);
321 } else if (4 == aNumPoints
) {
322 nsPoint
gapUpperRight(aGap
->x
+ aGap
->width
, aGap
->y
);
323 nsPoint
gapLowerRight(aGap
->x
+ aGap
->width
, aGap
->y
+ aGap
->height
);
325 // sort the 4 points by x
327 for (PRInt32 pX
= 0; pX
< 4; pX
++) {
328 points
[pX
] = aPoints
[pX
];
330 for (PRInt32 i
= 0; i
< 3; i
++) {
331 for (PRInt32 j
= i
+1; j
< 4; j
++) {
332 if (points
[j
].x
< points
[i
].x
) {
333 nsPoint swap
= points
[i
];
334 points
[i
] = points
[j
];
340 nsPoint upperLeft
= (points
[0].y
<= points
[1].y
) ? points
[0] : points
[1];
341 nsPoint lowerLeft
= (points
[0].y
<= points
[1].y
) ? points
[1] : points
[0];
342 nsPoint upperRight
= (points
[2].y
<= points
[3].y
) ? points
[2] : points
[3];
343 nsPoint lowerRight
= (points
[2].y
<= points
[3].y
) ? points
[3] : points
[2];
346 if ((aGap
->y
<= upperLeft
.y
) && (gapLowerRight
.y
>= lowerRight
.y
)) {
347 if ((aGap
->x
> upperLeft
.x
) && (aGap
->x
< upperRight
.x
)) {
349 leftRect
[0] = upperLeft
;
350 leftRect
[1] = nsPoint(aGap
->x
, upperLeft
.y
);
351 leftRect
[2] = nsPoint(aGap
->x
, lowerLeft
.y
);
352 leftRect
[3] = lowerLeft
;
353 aContext
.FillPolygon(leftRect
, 4);
355 if ((gapUpperRight
.x
> upperLeft
.x
) && (gapUpperRight
.x
< upperRight
.x
)) {
356 nsPoint rightRect
[4];
357 rightRect
[0] = nsPoint(gapUpperRight
.x
, upperRight
.y
);
358 rightRect
[1] = upperRight
;
359 rightRect
[2] = lowerRight
;
360 rightRect
[3] = nsPoint(gapLowerRight
.x
, lowerRight
.y
);
361 aContext
.FillPolygon(rightRect
, 4);
364 aContext
.FillPolygon(aPoints
, aNumPoints
);
372 nscolor
nsCSSRendering::MakeBevelColor(PRIntn whichSide
, PRUint8 style
,
373 nscolor aBackgroundColor
,
374 nscolor aBorderColor
)
380 // Given a background color and a border color
381 // calculate the color used for the shading
382 NS_GetSpecial3DColors(colors
, aBackgroundColor
, aBorderColor
);
384 if ((style
== NS_STYLE_BORDER_STYLE_OUTSET
) ||
385 (style
== NS_STYLE_BORDER_STYLE_RIDGE
)) {
386 // Flip colors for these two border styles
388 case NS_SIDE_BOTTOM
: whichSide
= NS_SIDE_TOP
; break;
389 case NS_SIDE_RIGHT
: whichSide
= NS_SIDE_LEFT
; break;
390 case NS_SIDE_TOP
: whichSide
= NS_SIDE_BOTTOM
; break;
391 case NS_SIDE_LEFT
: whichSide
= NS_SIDE_RIGHT
; break;
397 theColor
= colors
[1];
400 theColor
= colors
[1];
403 theColor
= colors
[0];
407 theColor
= colors
[0];
414 nsCSSRendering::TransformColor(nscolor aMapColor
,PRBool aNoBackGround
)
416 PRUint16 hue
,sat
,value
;
419 newcolor
= aMapColor
;
420 if (PR_TRUE
== aNoBackGround
){
421 // convert the RBG to HSV so we can get the lightness (which is the v)
422 NS_RGB2HSV(newcolor
,hue
,sat
,value
);
423 // The goal here is to send white to black while letting colored
424 // stuff stay colored... So we adopt the following approach.
425 // Something with sat = 0 should end up with value = 0. Something
426 // with a high sat can end up with a high value and it's ok.... At
427 // the same time, we don't want to make things lighter. Do
428 // something simple, since it seems to work.
431 // convert this color back into the RGB color space.
432 NS_HSV2RGB(newcolor
,hue
,sat
,value
);
438 //----------------------------------------------------------------------
439 // Thebes Border Rendering Code Start
441 // helper function to convert a nsRect to a gfxRect
443 RectToGfxRect(const nsRect
& rect
, nscoord twipsPerPixel
)
445 return gfxRect(gfxFloat(rect
.x
) / twipsPerPixel
,
446 gfxFloat(rect
.y
) / twipsPerPixel
,
447 gfxFloat(rect
.width
) / twipsPerPixel
,
448 gfxFloat(rect
.height
) / twipsPerPixel
);
452 * Compute the float-pixel radii that should be used for drawing
453 * this border/outline, given the various input bits.
455 * If a side is skipped via skipSides, its corners are forced to 0,
456 * otherwise the resulting radius is the smaller of the specified
457 * radius and half of each adjacent side's length.
460 ComputePixelRadii(const nscoord
*aTwipsRadii
,
461 const nsRect
& outerRect
,
462 const nsMargin
& borderMargin
,
464 nscoord twipsPerPixel
,
465 gfxCornerSizes
*oBorderRadii
)
467 nscoord twipsRadii
[4] = { aTwipsRadii
[0], aTwipsRadii
[1], aTwipsRadii
[2], aTwipsRadii
[3] };
468 nsMargin
border(borderMargin
);
470 if (skipSides
& SIDE_BIT_TOP
) {
472 twipsRadii
[C_TL
] = 0;
473 twipsRadii
[C_TR
] = 0;
476 if (skipSides
& SIDE_BIT_RIGHT
) {
478 twipsRadii
[C_TR
] = 0;
479 twipsRadii
[C_BR
] = 0;
482 if (skipSides
& SIDE_BIT_BOTTOM
) {
484 twipsRadii
[C_BR
] = 0;
485 twipsRadii
[C_BL
] = 0;
488 if (skipSides
& SIDE_BIT_LEFT
) {
490 twipsRadii
[C_BL
] = 0;
491 twipsRadii
[C_TL
] = 0;
494 nsRect
innerRect(outerRect
);
495 innerRect
.Deflate(border
);
497 // make sure the corner radii don't get too big
498 nsMargin
maxRadiusSize(innerRect
.width
/2 + border
.left
,
499 innerRect
.height
/2 + border
.top
,
500 innerRect
.width
/2 + border
.right
,
501 innerRect
.height
/2 + border
.bottom
);
504 f
[C_TL
] = gfxFloat(PR_MIN(twipsRadii
[C_TL
], PR_MIN(maxRadiusSize
.top
, maxRadiusSize
.left
))) / twipsPerPixel
;
505 f
[C_TR
] = gfxFloat(PR_MIN(twipsRadii
[C_TR
], PR_MIN(maxRadiusSize
.top
, maxRadiusSize
.right
))) / twipsPerPixel
;
506 f
[C_BL
] = gfxFloat(PR_MIN(twipsRadii
[C_BL
], PR_MIN(maxRadiusSize
.bottom
, maxRadiusSize
.left
))) / twipsPerPixel
;
507 f
[C_BR
] = gfxFloat(PR_MIN(twipsRadii
[C_BR
], PR_MIN(maxRadiusSize
.bottom
, maxRadiusSize
.right
))) / twipsPerPixel
;
509 (*oBorderRadii
)[C_TL
] = gfxSize(f
[C_TL
], f
[C_TL
]);
510 (*oBorderRadii
)[C_TR
] = gfxSize(f
[C_TR
], f
[C_TR
]);
511 (*oBorderRadii
)[C_BL
] = gfxSize(f
[C_BL
], f
[C_BL
]);
512 (*oBorderRadii
)[C_BR
] = gfxSize(f
[C_BR
], f
[C_BR
]);
516 nsCSSRendering::PaintBorder(nsPresContext
* aPresContext
,
517 nsIRenderingContext
& aRenderingContext
,
519 const nsRect
& aDirtyRect
,
520 const nsRect
& aBorderArea
,
521 const nsStyleBorder
& aBorderStyle
,
522 nsStyleContext
* aStyleContext
,
526 nscoord twipsRadii
[4];
527 nsCompatibility compatMode
= aPresContext
->CompatibilityMode();
529 SN("++ PaintBorder");
531 // Check to see if we have an appearance defined. If so, we let the theme
532 // renderer draw the border. DO not get the data from aForFrame, since the passed in style context
533 // may be different! Always use |aStyleContext|!
534 const nsStyleDisplay
* displayData
= aStyleContext
->GetStyleDisplay();
535 if (displayData
->mAppearance
) {
536 nsITheme
*theme
= aPresContext
->GetTheme();
537 if (theme
&& theme
->ThemeSupportsWidget(aPresContext
, aForFrame
, displayData
->mAppearance
))
538 return; // Let the theme handle it.
541 if (aBorderStyle
.IsBorderImageLoaded()) {
542 DrawBorderImage(aPresContext
, aRenderingContext
, aForFrame
,
543 aBorderArea
, aBorderStyle
);
547 // Get our style context's color struct.
548 const nsStyleColor
* ourColor
= aStyleContext
->GetStyleColor();
550 // in NavQuirks mode we want to use the parent's context as a starting point
551 // for determining the background color
552 const nsStyleBackground
* bgColor
= nsCSSRendering::FindNonTransparentBackground
553 (aStyleContext
, compatMode
== eCompatibility_NavQuirks
? PR_TRUE
: PR_FALSE
);
555 border
= aBorderStyle
.GetComputedBorder();
556 if ((0 == border
.left
) && (0 == border
.right
) &&
557 (0 == border
.top
) && (0 == border
.bottom
)) {
562 GetBorderRadiusTwips(aBorderStyle
.mBorderRadius
, aForFrame
->GetSize().width
, twipsRadii
);
564 // Turn off rendering for all of the zero sized sides
565 if (aSkipSides
& SIDE_BIT_TOP
) border
.top
= 0;
566 if (aSkipSides
& SIDE_BIT_RIGHT
) border
.right
= 0;
567 if (aSkipSides
& SIDE_BIT_BOTTOM
) border
.bottom
= 0;
568 if (aSkipSides
& SIDE_BIT_LEFT
) border
.left
= 0;
570 // get the inside and outside parts of the border
571 nsRect
outerRect(aBorderArea
);
573 SF(" outerRect: %d %d %d %d\n", outerRect
.x
, outerRect
.y
, outerRect
.width
, outerRect
.height
);
575 // we can assume that we're already clipped to aDirtyRect -- I think? (!?)
577 // Get our conversion values
578 nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
580 // convert outer and inner rects
581 gfxRect
oRect(RectToGfxRect(outerRect
, twipsPerPixel
));
583 // convert the border widths
584 gfxFloat borderWidths
[4] = { border
.top
/ twipsPerPixel
,
585 border
.right
/ twipsPerPixel
,
586 border
.bottom
/ twipsPerPixel
,
587 border
.left
/ twipsPerPixel
};
590 gfxCornerSizes borderRadii
;
591 ComputePixelRadii(twipsRadii
, outerRect
, border
, aSkipSides
, twipsPerPixel
, &borderRadii
);
593 PRUint8 borderStyles
[4];
594 nscolor borderColors
[4];
595 nsBorderColors
*compositeColors
[4];
597 // pull out styles, colors, composite colors
598 NS_FOR_CSS_SIDES (i
) {
599 PRBool transparent
, foreground
;
600 borderStyles
[i
] = aBorderStyle
.GetBorderStyle(i
);
601 aBorderStyle
.GetBorderColor(i
, borderColors
[i
], transparent
, foreground
);
602 aBorderStyle
.GetCompositeColors(i
, &compositeColors
[i
]);
605 borderColors
[i
] = 0x0;
607 borderColors
[i
] = ourColor
->mColor
;
610 SF(" borderStyles: %d %d %d %d\n", borderStyles
[0], borderStyles
[1], borderStyles
[2], borderStyles
[3]);
613 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
618 // this will draw a transparent red backround underneath the oRect area
620 ctx
->Rectangle(oRect
);
621 ctx
->SetColor(gfxRGBA(1.0, 0.0, 0.0, 0.5));
626 //SF ("borderRadii: %f %f %f %f\n", borderRadii[0], borderRadii[1], borderRadii[2], borderRadii[3]);
628 nsCSSBorderRenderer
br(twipsPerPixel
,
637 bgColor
->mBackgroundColor
);
646 nsCSSRendering::PaintOutline(nsPresContext
* aPresContext
,
647 nsIRenderingContext
& aRenderingContext
,
649 const nsRect
& aDirtyRect
,
650 const nsRect
& aBorderArea
,
651 const nsStyleBorder
& aBorderStyle
,
652 const nsStyleOutline
& aOutlineStyle
,
653 nsStyleContext
* aStyleContext
)
655 nscoord twipsRadii
[4];
657 // Get our style context's color struct.
658 const nsStyleColor
* ourColor
= aStyleContext
->GetStyleColor();
661 aOutlineStyle
.GetOutlineWidth(width
);
668 const nsStyleBackground
* bgColor
= nsCSSRendering::FindNonTransparentBackground
669 (aStyleContext
, PR_FALSE
);
671 // get the radius for our outline
672 GetBorderRadiusTwips(aOutlineStyle
.mOutlineRadius
, aBorderArea
.width
, twipsRadii
);
675 aOutlineStyle
.GetOutlineOffset(offset
);
677 // When the outline property is set on :-moz-anonymous-block or
678 // :-moz-anonyomus-positioned-block pseudo-elements, it inherited that
679 // outline from the inline that was broken because it contained a
680 // block. In that case, we don't want a really wide outline if the
681 // block inside the inline is narrow, so union the actual contents of
682 // the anonymous blocks.
683 nsIFrame
*frameForArea
= aForFrame
;
685 nsIAtom
*pseudoType
= frameForArea
->GetStyleContext()->GetPseudoType();
686 if (pseudoType
!= nsCSSAnonBoxes::mozAnonymousBlock
&&
687 pseudoType
!= nsCSSAnonBoxes::mozAnonymousPositionedBlock
)
689 // If we're done, we really want it and all its later siblings.
690 frameForArea
= frameForArea
->GetFirstChild(nsnull
);
691 NS_ASSERTION(frameForArea
, "anonymous block with no children?");
692 } while (frameForArea
);
694 if (frameForArea
== aForFrame
) {
695 overflowArea
= aForFrame
->GetOverflowRect();
697 for (; frameForArea
; frameForArea
= frameForArea
->GetNextSibling()) {
698 // The outline has already been included in aForFrame's overflow
699 // area, but not in those of its descendants, so we have to
700 // include it. Otherwise we'll end up drawing the outline inside
702 nsRect
r(frameForArea
->GetOverflowRect() +
703 frameForArea
->GetOffsetTo(aForFrame
));
704 nscoord delta
= PR_MAX(offset
+ width
, 0);
705 r
.Inflate(delta
, delta
);
706 overflowArea
.UnionRect(overflowArea
, r
);
710 nsRect
outerRect(overflowArea
+ aBorderArea
.TopLeft());
711 nsRect
innerRect(outerRect
);
712 if (width
+ offset
>= 0) {
713 // the overflow area is exactly the outside edge of the outline
714 innerRect
.Deflate(width
, width
);
716 // the overflow area is exactly the rectangle containing the frame and its
717 // children; we can compute the outline directly
718 innerRect
.Deflate(-offset
, -offset
);
719 if (innerRect
.width
< 0 || innerRect
.height
< 0) {
720 return; // Protect against negative outline sizes
722 outerRect
= innerRect
;
723 outerRect
.Inflate(width
, width
);
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
)) {
735 // Get our conversion values
736 nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
738 // get the outer rectangles
739 gfxRect
oRect(RectToGfxRect(outerRect
, twipsPerPixel
));
742 nsMargin
outlineMargin(width
, width
, width
, width
);
743 gfxCornerSizes outlineRadii
;
744 ComputePixelRadii(twipsRadii
, outerRect
, outlineMargin
, 0, twipsPerPixel
, &outlineRadii
);
746 PRUint8 outlineStyle
= aOutlineStyle
.GetOutlineStyle();
747 PRUint8 outlineStyles
[4] = { outlineStyle
,
752 nscolor outlineColor
;
753 // PR_FALSE means use the initial color; PR_TRUE means a color was
755 if (!aOutlineStyle
.GetOutlineColor(outlineColor
))
756 outlineColor
= ourColor
->mColor
;
757 nscolor outlineColors
[4] = { outlineColor
,
762 // convert the border widths
763 gfxFloat outlineWidths
[4] = { width
/ twipsPerPixel
,
764 width
/ twipsPerPixel
,
765 width
/ twipsPerPixel
,
766 width
/ twipsPerPixel
};
769 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
773 nsCSSBorderRenderer
br(twipsPerPixel
,
781 bgColor
->mBackgroundColor
);
790 nsCSSRendering::PaintFocus(nsPresContext
* aPresContext
,
791 nsIRenderingContext
& aRenderingContext
,
792 const nsRect
& aFocusRect
,
795 nscoord oneCSSPixel
= nsPresContext::CSSPixelsToAppUnits(1);
796 nscoord oneDevPixel
= aPresContext
->DevPixelsToAppUnits(1);
798 gfxRect
focusRect(RectToGfxRect(aFocusRect
, oneDevPixel
));
800 gfxCornerSizes focusRadii
;
802 nscoord twipsRadii
[4] = { 0, 0, 0, 0 };
803 nsMargin
focusMargin(oneCSSPixel
, oneCSSPixel
, oneCSSPixel
, oneCSSPixel
);
804 ComputePixelRadii(twipsRadii
, aFocusRect
, focusMargin
, 0, oneDevPixel
,
807 gfxFloat focusWidths
[4] = { oneCSSPixel
/ oneDevPixel
,
808 oneCSSPixel
/ oneDevPixel
,
809 oneCSSPixel
/ oneDevPixel
,
810 oneCSSPixel
/ oneDevPixel
};
812 PRUint8 focusStyles
[4] = { NS_STYLE_BORDER_STYLE_DOTTED
,
813 NS_STYLE_BORDER_STYLE_DOTTED
,
814 NS_STYLE_BORDER_STYLE_DOTTED
,
815 NS_STYLE_BORDER_STYLE_DOTTED
};
816 nscolor focusColors
[4] = { aColor
, aColor
, aColor
, aColor
};
818 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
822 // Because this renders a dotted border, the background color
823 // should not be used. Therefore, we provide a value that will
824 // be blatantly wrong if it ever does get used. (If this becomes
825 // something that CSS can style, this function will then have access
826 // to a style context and can use the same logic that PaintBorder
827 // and PaintOutline do.)
828 nsCSSBorderRenderer
br(oneDevPixel
,
844 // Thebes Border Rendering Code End
845 //----------------------------------------------------------------------
848 //----------------------------------------------------------------------
850 // Returns the anchor point to use for the background image. The
851 // anchor point is the (x, y) location where the first tile should
854 // For repeated tiling, the anchor values are normalized wrt to the upper-left
855 // edge of the bounds, and are always in the range:
856 // -(aTileWidth - 1) <= anchor.x <= 0
857 // -(aTileHeight - 1) <= anchor.y <= 0
859 // i.e., they are either 0 or a negative number whose absolute value is
860 // less than the tile size in that dimension
862 // aOriginBounds is the box to which the tiling position should be relative
863 // aClipBounds is the box in which the tiling will actually be done
864 // They should correspond to 'background-origin' and 'background-clip',
865 // except when painting on the canvas, in which case the origin bounds
866 // should be the bounds of the root element's frame and the clip bounds
867 // should be the bounds of the canvas frame.
869 ComputeBackgroundAnchorPoint(const nsStyleBackground
& aColor
,
870 const nsRect
& aOriginBounds
,
871 const nsRect
& aClipBounds
,
872 nscoord aTileWidth
, nscoord aTileHeight
,
876 if (NS_STYLE_BG_X_POSITION_LENGTH
& aColor
.mBackgroundFlags
) {
877 x
= aColor
.mBackgroundXPosition
.mCoord
;
879 else if (NS_STYLE_BG_X_POSITION_PERCENT
& aColor
.mBackgroundFlags
) {
880 PRFloat64 percent
= PRFloat64(aColor
.mBackgroundXPosition
.mFloat
);
881 nscoord tilePos
= nscoord(percent
* PRFloat64(aTileWidth
));
882 nscoord boxPos
= nscoord(percent
* PRFloat64(aOriginBounds
.width
));
883 x
= boxPos
- tilePos
;
888 x
+= aOriginBounds
.x
- aClipBounds
.x
;
889 if (NS_STYLE_BG_REPEAT_X
& aColor
.mBackgroundRepeat
) {
890 // When we are tiling in the x direction the loop will run from
891 // the left edge of the box to the right edge of the box. We need
892 // to adjust the starting coordinate to lie within the band being
897 // Some joker gave us max-negative-integer.
910 NS_POSTCONDITION((x
>= -(aTileWidth
- 1)) && (x
<= 0), "bad computed anchor value");
915 if (NS_STYLE_BG_Y_POSITION_LENGTH
& aColor
.mBackgroundFlags
) {
916 y
= aColor
.mBackgroundYPosition
.mCoord
;
918 else if (NS_STYLE_BG_Y_POSITION_PERCENT
& aColor
.mBackgroundFlags
){
919 PRFloat64 percent
= PRFloat64(aColor
.mBackgroundYPosition
.mFloat
);
920 nscoord tilePos
= nscoord(percent
* PRFloat64(aTileHeight
));
921 nscoord boxPos
= nscoord(percent
* PRFloat64(aOriginBounds
.height
));
922 y
= boxPos
- tilePos
;
927 y
+= aOriginBounds
.y
- aClipBounds
.y
;
928 if (NS_STYLE_BG_REPEAT_Y
& aColor
.mBackgroundRepeat
) {
929 // When we are tiling in the y direction the loop will run from
930 // the top edge of the box to the bottom edge of the box. We need
931 // to adjust the starting coordinate to lie within the band being
936 // Some joker gave us max-negative-integer.
949 NS_POSTCONDITION((y
>= -(aTileHeight
- 1)) && (y
<= 0), "bad computed anchor value");
954 const nsStyleBackground
*
955 nsCSSRendering::FindNonTransparentBackground(nsStyleContext
* aContext
,
956 PRBool aStartAtParent
/*= PR_FALSE*/)
958 NS_ASSERTION(aContext
, "Cannot find NonTransparentBackground in a null context" );
960 const nsStyleBackground
* result
= nsnull
;
961 nsStyleContext
* context
= nsnull
;
962 if (aStartAtParent
) {
963 context
= aContext
->GetParent();
970 result
= context
->GetStyleBackground();
971 if (0 == (result
->mBackgroundFlags
& NS_STYLE_BG_COLOR_TRANSPARENT
))
974 context
= context
->GetParent();
981 * |FindBackground| finds the correct style data to use to paint the
982 * background. It is responsible for handling the following two
983 * statements in section 14.2 of CSS2:
985 * The background of the box generated by the root element covers the
988 * For HTML documents, however, we recommend that authors specify the
989 * background for the BODY element rather than the HTML element. User
990 * agents should observe the following precedence rules to fill in the
991 * background: if the value of the 'background' property for the HTML
992 * element is different from 'transparent' then use it, else use the
993 * value of the 'background' property for the BODY element. If the
994 * resulting value is 'transparent', the rendering is undefined.
996 * Thus, in our implementation, it is responsible for ensuring that:
997 * + we paint the correct background on the |nsCanvasFrame|,
998 * |nsRootBoxFrame|, or |nsPageFrame|,
999 * + we don't paint the background on the root element, and
1000 * + we don't paint the background on the BODY element in *some* cases,
1001 * and for SGML-based HTML documents only.
1003 * |FindBackground| returns true if a background should be painted, and
1004 * the resulting style context to use for the background information
1005 * will be filled in to |aBackground|. It fills in a boolean indicating
1006 * whether the frame is the canvas frame to allow PaintBackground to
1007 * ensure that it always paints something non-transparent for the
1011 // Returns true if aFrame is a canvas frame.
1012 // We need to treat the viewport as canvas because, even though
1013 // it does not actually paint a background, we need to get the right
1014 // background style so we correctly detect transparent documents.
1016 IsCanvasFrame(nsIFrame
*aFrame
)
1018 nsIAtom
* frameType
= aFrame
->GetType();
1019 return frameType
== nsGkAtoms::canvasFrame
||
1020 frameType
== nsGkAtoms::rootFrame
||
1021 frameType
== nsGkAtoms::pageFrame
||
1022 frameType
== nsGkAtoms::pageContentFrame
||
1023 frameType
== nsGkAtoms::viewportFrame
;
1027 FindCanvasBackground(nsIFrame
* aForFrame
, nsIFrame
* aRootElementFrame
,
1028 const nsStyleBackground
** aBackground
)
1030 if (aRootElementFrame
) {
1031 const nsStyleBackground
* result
= aRootElementFrame
->GetStyleBackground();
1033 // Check if we need to do propagation from BODY rather than HTML.
1034 if (result
->IsTransparent()) {
1035 nsIContent
* content
= aRootElementFrame
->GetContent();
1036 // The root element content can't be null. We wouldn't know what
1037 // frame to create for aRootElementFrame.
1038 // Use |GetOwnerDoc| so it works during destruction.
1039 nsIDocument
* document
= content
->GetOwnerDoc();
1040 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(document
);
1042 nsIContent
* bodyContent
= htmlDoc
->GetBodyContentExternal();
1043 // We need to null check the body node (bug 118829) since
1044 // there are cases, thanks to the fix for bug 5569, where we
1045 // will reflow a document with no body. In particular, if a
1046 // SCRIPT element in the head blocks the parser and then has a
1047 // SCRIPT that does "document.location.href = 'foo'", then
1048 // nsParser::Terminate will call |DidBuildModel| methods
1049 // through to the content sink, which will call |StartLayout|
1050 // and thus |InitialReflow| on the pres shell. See bug 119351
1051 // for the ugly details.
1053 nsIFrame
*bodyFrame
= aForFrame
->PresContext()->GetPresShell()->
1054 GetPrimaryFrameFor(bodyContent
);
1056 result
= bodyFrame
->GetStyleBackground();
1061 *aBackground
= result
;
1063 // This should always give transparent, so we'll fill it in with the
1064 // default color if needed. This seems to happen a bit while a page is
1066 *aBackground
= aForFrame
->GetStyleBackground();
1073 FindElementBackground(nsIFrame
* aForFrame
, nsIFrame
* aRootElementFrame
,
1074 const nsStyleBackground
** aBackground
)
1076 if (aForFrame
== aRootElementFrame
) {
1077 // We must have propagated our background to the viewport or canvas. Abort.
1081 *aBackground
= aForFrame
->GetStyleBackground();
1083 // Return true unless the frame is for a BODY element whose background
1084 // was propagated to the viewport.
1086 nsIContent
* content
= aForFrame
->GetContent();
1087 if (!content
|| content
->Tag() != nsGkAtoms::body
)
1088 return PR_TRUE
; // not frame for a "body" element
1089 // It could be a non-HTML "body" element but that's OK, we'd fail the
1090 // bodyContent check below
1092 if (aForFrame
->GetStyleContext()->GetPseudoType())
1093 return PR_TRUE
; // A pseudo-element frame.
1095 // We should only look at the <html> background if we're in an HTML document
1096 nsIDocument
* document
= content
->GetOwnerDoc();
1097 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(document
);
1101 nsIContent
* bodyContent
= htmlDoc
->GetBodyContentExternal();
1102 if (bodyContent
!= content
)
1103 return PR_TRUE
; // this wasn't the background that was propagated
1105 // This can be called even when there's no root element yet, during frame
1106 // construction, via nsLayoutUtils::FrameHasTransparency and
1107 // nsContainerFrame::SyncFrameViewProperties.
1108 if (!aRootElementFrame
)
1111 const nsStyleBackground
* htmlBG
= aRootElementFrame
->GetStyleBackground();
1112 return !htmlBG
->IsTransparent();
1116 nsCSSRendering::FindBackground(nsPresContext
* aPresContext
,
1117 nsIFrame
* aForFrame
,
1118 const nsStyleBackground
** aBackground
,
1121 nsIFrame
* rootElementFrame
=
1122 aPresContext
->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
1123 PRBool isCanvasFrame
= IsCanvasFrame(aForFrame
);
1124 *aIsCanvas
= isCanvasFrame
;
1125 return isCanvasFrame
1126 ? FindCanvasBackground(aForFrame
, rootElementFrame
, aBackground
)
1127 : FindElementBackground(aForFrame
, rootElementFrame
, aBackground
);
1131 nsCSSRendering::DidPaint()
1133 gInlineBGData
->Reset();
1137 nsCSSRendering::GetBorderRadiusTwips(const nsStyleSides
& aBorderRadius
,
1138 const nscoord
& aFrameWidth
,
1139 nscoord aTwipsRadii
[4])
1141 nsStyleCoord bordStyleRadius
[4];
1142 PRBool result
= PR_FALSE
;
1144 bordStyleRadius
[gfxCorner::TOP_LEFT
] = aBorderRadius
.GetTop();
1145 bordStyleRadius
[gfxCorner::TOP_RIGHT
] = aBorderRadius
.GetRight();
1146 bordStyleRadius
[gfxCorner::BOTTOM_RIGHT
] = aBorderRadius
.GetBottom();
1147 bordStyleRadius
[gfxCorner::BOTTOM_LEFT
] = aBorderRadius
.GetLeft();
1149 // Convert percentage values
1150 for (int i
= 0; i
< 4; i
++) {
1154 switch (bordStyleRadius
[i
].GetUnit()) {
1155 case eStyleUnit_Percent
:
1156 percent
= bordStyleRadius
[i
].GetPercentValue();
1157 aTwipsRadii
[i
] = (nscoord
)(percent
* aFrameWidth
);
1160 case eStyleUnit_Coord
:
1161 aTwipsRadii
[i
] = bordStyleRadius
[i
].GetCoordValue();
1175 nsCSSRendering::PaintBoxShadow(nsPresContext
* aPresContext
,
1176 nsIRenderingContext
& aRenderingContext
,
1177 nsIFrame
* aForFrame
,
1178 const nsPoint
& aForFramePt
)
1180 nsMargin borderValues
;
1184 const nsStyleBorder
* styleBorder
= aForFrame
->GetStyleBorder();
1185 borderValues
= styleBorder
->GetActualBorder();
1186 sidesToSkip
= aForFrame
->GetSkipSides();
1187 frameRect
= nsRect(aForFramePt
, aForFrame
->GetSize());
1189 // Get any border radius, since box-shadow must also have rounded corners if the frame does
1190 nscoord twipsRadii
[4];
1191 PRBool hasBorderRadius
= GetBorderRadiusTwips(styleBorder
->mBorderRadius
, frameRect
.width
, twipsRadii
);
1192 nscoord twipsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
1194 gfxCornerSizes borderRadii
;
1195 ComputePixelRadii(twipsRadii
, frameRect
, borderValues
, sidesToSkip
, twipsPerPixel
, &borderRadii
);
1197 gfxRect frameGfxRect
= RectToGfxRect(frameRect
, twipsPerPixel
);
1198 for (PRUint32 i
= styleBorder
->mBoxShadow
->Length(); i
> 0; --i
) {
1199 nsCSSShadowItem
* shadowItem
= styleBorder
->mBoxShadow
->ShadowAt(i
- 1);
1200 gfxRect
shadowRect(frameRect
.x
, frameRect
.y
, frameRect
.width
, frameRect
.height
);
1201 shadowRect
.MoveBy(gfxPoint(shadowItem
->mXOffset
.GetCoordValue(),
1202 shadowItem
->mYOffset
.GetCoordValue()));
1203 shadowRect
.Outset(shadowItem
->mSpread
.GetCoordValue());
1205 gfxRect shadowRectPlusBlur
= shadowRect
;
1206 shadowRect
.ScaleInverse(twipsPerPixel
);
1207 shadowRect
.RoundOut();
1209 // shadowRect won't include the blur, so make an extra rect here that includes the blur
1210 // for use in the even-odd rule below.
1211 nscoord blurRadius
= shadowItem
->mRadius
.GetCoordValue();
1212 shadowRectPlusBlur
.Outset(blurRadius
);
1213 shadowRectPlusBlur
.ScaleInverse(twipsPerPixel
);
1214 shadowRectPlusBlur
.RoundOut();
1216 gfxContext
* renderContext
= aRenderingContext
.ThebesContext();
1217 nsRefPtr
<gfxContext
> shadowContext
;
1218 nsContextBoxBlur blurringArea
;
1220 // shadowRect has already been converted to device pixels, pass 1 as the appunits/pixel value
1221 blurRadius
/= twipsPerPixel
;
1222 shadowContext
= blurringArea
.Init(shadowRect
, blurRadius
, 1, renderContext
);
1226 // Set the shadow color; if not specified, use the foreground color
1227 nscolor shadowColor
;
1228 if (shadowItem
->mHasColor
)
1229 shadowColor
= shadowItem
->mColor
;
1231 shadowColor
= aForFrame
->GetStyleColor()->mColor
;
1233 renderContext
->Save();
1234 renderContext
->SetColor(gfxRGBA(shadowColor
));
1236 // Clip out the area of the actual frame so the shadow is not shown within
1238 renderContext
->NewPath();
1239 renderContext
->Rectangle(shadowRectPlusBlur
);
1240 if (hasBorderRadius
)
1241 renderContext
->RoundedRectangle(frameGfxRect
, borderRadii
);
1243 renderContext
->Rectangle(frameGfxRect
);
1244 renderContext
->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD
);
1245 renderContext
->Clip();
1247 // Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur
1248 // doesn't make any temporary surfaces if blur is 0 and it just returns the original
1249 // surface? If we have no blur, we're painting this fill on the actual content surface
1250 // (renderContext == shadowContext) which is why we set up the color and clip
1251 // before doing this.
1252 shadowContext
->NewPath();
1253 if (hasBorderRadius
)
1254 shadowContext
->RoundedRectangle(shadowRect
, borderRadii
);
1256 shadowContext
->Rectangle(shadowRect
);
1257 shadowContext
->Fill();
1259 blurringArea
.DoPaint();
1260 renderContext
->Restore();
1265 nsCSSRendering::PaintBackground(nsPresContext
* aPresContext
,
1266 nsIRenderingContext
& aRenderingContext
,
1267 nsIFrame
* aForFrame
,
1268 const nsRect
& aDirtyRect
,
1269 const nsRect
& aBorderArea
,
1270 const nsStyleBorder
& aBorder
,
1271 const nsStylePadding
& aPadding
,
1272 PRBool aUsePrintSettings
,
1273 nsRect
* aBGClipRect
)
1275 NS_PRECONDITION(aForFrame
,
1276 "Frame is expected to be provided to PaintBackground");
1279 const nsStyleBackground
*color
;
1281 if (!FindBackground(aPresContext
, aForFrame
, &color
, &isCanvas
)) {
1282 // we don't want to bail out of moz-appearance is set on a root
1283 // node. If it has a parent content node, bail because it's not
1284 // a root, other wise keep going in order to let the theme stuff
1285 // draw the background. The canvas really should be drawing the
1286 // bg, but there's no way to hook that up via css.
1287 if (!aForFrame
->GetStyleDisplay()->mAppearance
) {
1291 nsIContent
* content
= aForFrame
->GetContent();
1292 if (!content
|| content
->GetParent()) {
1296 color
= aForFrame
->GetStyleBackground();
1299 PaintBackgroundWithSC(aPresContext
, aRenderingContext
, aForFrame
,
1300 aDirtyRect
, aBorderArea
, *color
, aBorder
,
1301 aPadding
, aUsePrintSettings
, aBGClipRect
);
1305 nsStyleBackground
canvasColor(*color
);
1307 nsIViewManager
* vm
= aPresContext
->GetViewManager();
1309 if (canvasColor
.mBackgroundFlags
& NS_STYLE_BG_COLOR_TRANSPARENT
) {
1311 vm
->GetRootView(rootView
);
1312 if (!rootView
->GetParent()) {
1313 PRBool widgetIsTransparent
= PR_FALSE
;
1315 if (rootView
->HasWidget())
1316 // We don't want to draw a bg for glass windows either
1317 widgetIsTransparent
= eTransparencyOpaque
!= rootView
->GetWidget()->GetTransparencyMode();
1319 if (!widgetIsTransparent
) {
1320 // Ensure that we always paint a color for the root (in case there's
1321 // no background at all or a partly transparent image).
1322 canvasColor
.mBackgroundFlags
&= ~NS_STYLE_BG_COLOR_TRANSPARENT
;
1323 canvasColor
.mBackgroundColor
= aPresContext
->DefaultBackgroundColor();
1328 vm
->SetDefaultBackgroundColor(canvasColor
.mBackgroundColor
);
1330 PaintBackgroundWithSC(aPresContext
, aRenderingContext
, aForFrame
,
1331 aDirtyRect
, aBorderArea
, canvasColor
,
1332 aBorder
, aPadding
, aUsePrintSettings
, aBGClipRect
);
1335 inline nscoord
IntDivFloor(nscoord aDividend
, nscoord aDivisor
)
1337 NS_PRECONDITION(aDivisor
> 0,
1338 "this function only works for positive divisors");
1339 // ANSI C, ISO 9899:1999 section 6.5.5 defines integer division as
1340 // truncation of the result towards zero. Earlier C standards, as
1341 // well as the C++ standards (1998 and 2003) do not, but we depend
1343 return (aDividend
< 0 ? (aDividend
- aDivisor
+ 1) : aDividend
) / aDivisor
;
1346 inline nscoord
IntDivCeil(nscoord aDividend
, nscoord aDivisor
)
1348 NS_PRECONDITION(aDivisor
> 0,
1349 "this function only works for positive divisors");
1350 // ANSI C, ISO 9899:1999 section 6.5.5 defines integer division as
1351 // truncation of the result towards zero. Earlier C standards, as
1352 // well as the C++ standards (1998 and 2003) do not, but we depend
1354 return (aDividend
> 0 ? (aDividend
+ aDivisor
- 1) : aDividend
) / aDivisor
;
1358 * Return the largest 'v' such that v = aTileOffset + N*aTileSize, for some
1359 * integer N, and v <= aDirtyStart.
1362 FindTileStart(nscoord aDirtyStart
, nscoord aTileOffset
, nscoord aTileSize
)
1364 // Find largest integer N such that aTileOffset + N*aTileSize <= aDirtyStart
1365 return aTileOffset
+
1366 IntDivFloor(aDirtyStart
- aTileOffset
, aTileSize
) * aTileSize
;
1370 * Return the smallest 'v' such that v = aTileOffset + N*aTileSize, for some
1371 * integer N, and v >= aDirtyEnd.
1374 FindTileEnd(nscoord aDirtyEnd
, nscoord aTileOffset
, nscoord aTileSize
)
1376 // Find smallest integer N such that aTileOffset + N*aTileSize >= aDirtyEnd
1377 return aTileOffset
+
1378 IntDivCeil(aDirtyEnd
- aTileOffset
, aTileSize
) * aTileSize
;
1382 PixelSnapRectangle(gfxContext
* aContext
, nsIDeviceContext
*aDC
, nsRect
& aRect
)
1385 tmpRect
.pos
.x
= aDC
->AppUnitsToGfxUnits(aRect
.x
);
1386 tmpRect
.pos
.y
= aDC
->AppUnitsToGfxUnits(aRect
.y
);
1387 tmpRect
.size
.width
= aDC
->AppUnitsToGfxUnits(aRect
.width
);
1388 tmpRect
.size
.height
= aDC
->AppUnitsToGfxUnits(aRect
.height
);
1389 if (aContext
->UserToDevicePixelSnapped(tmpRect
)) {
1390 tmpRect
= aContext
->DeviceToUser(tmpRect
);
1391 aRect
.x
= aDC
->GfxUnitsToAppUnits(tmpRect
.pos
.x
);
1392 aRect
.y
= aDC
->GfxUnitsToAppUnits(tmpRect
.pos
.y
);
1393 aRect
.width
= aDC
->GfxUnitsToAppUnits(tmpRect
.XMost()) - aRect
.x
;
1394 aRect
.height
= aDC
->GfxUnitsToAppUnits(tmpRect
.YMost()) - aRect
.y
;
1399 PixelSnapPoint(gfxContext
* aContext
, nsIDeviceContext
*aDC
, nsPoint
& aPoint
)
1402 tmpRect
.pos
.x
= aDC
->AppUnitsToGfxUnits(aPoint
.x
);
1403 tmpRect
.pos
.y
= aDC
->AppUnitsToGfxUnits(aPoint
.y
);
1404 tmpRect
.size
.width
= 0;
1405 tmpRect
.size
.height
= 0;
1406 if (aContext
->UserToDevicePixelSnapped(tmpRect
)) {
1407 tmpRect
= aContext
->DeviceToUser(tmpRect
);
1408 aPoint
.x
= aDC
->GfxUnitsToAppUnits(tmpRect
.pos
.x
);
1409 aPoint
.y
= aDC
->GfxUnitsToAppUnits(tmpRect
.pos
.y
);
1414 IsSolidBorderEdge(const nsStyleBorder
& aBorder
, PRUint32 aSide
)
1416 if (aBorder
.GetActualBorder().side(aSide
) == 0)
1418 if (aBorder
.GetBorderStyle(aSide
) != NS_STYLE_BORDER_STYLE_SOLID
)
1422 PRBool isTransparent
;
1423 PRBool isForeground
;
1424 aBorder
.GetBorderColor(aSide
, color
, isTransparent
, isForeground
);
1425 return !isTransparent
&& NS_GET_A(color
) == 255;
1429 * Returns true if all border edges are either missing or opaque.
1432 IsSolidBorder(const nsStyleBorder
& aBorder
)
1434 if (nsLayoutUtils::HasNonZeroSide(aBorder
.mBorderRadius
) || aBorder
.mBorderColors
)
1436 for (PRUint32 i
= 0; i
< 4; ++i
) {
1437 if (!IsSolidBorderEdge(aBorder
, i
))
1444 nsCSSRendering::PaintBackgroundWithSC(nsPresContext
* aPresContext
,
1445 nsIRenderingContext
& aRenderingContext
,
1446 nsIFrame
* aForFrame
,
1447 const nsRect
& aDirtyRect
,
1448 const nsRect
& aBorderArea
,
1449 const nsStyleBackground
& aColor
,
1450 const nsStyleBorder
& aBorder
,
1451 const nsStylePadding
& aPadding
,
1452 PRBool aUsePrintSettings
,
1453 nsRect
* aBGClipRect
)
1455 NS_PRECONDITION(aForFrame
,
1456 "Frame is expected to be provided to PaintBackground");
1458 PRBool canDrawBackgroundImage
= PR_TRUE
;
1459 PRBool canDrawBackgroundColor
= PR_TRUE
;
1461 if (aUsePrintSettings
) {
1462 canDrawBackgroundImage
= aPresContext
->GetBackgroundImageDraw();
1463 canDrawBackgroundColor
= aPresContext
->GetBackgroundColorDraw();
1466 // Check to see if we have an appearance defined. If so, we let the theme
1467 // renderer draw the background and bail out.
1468 const nsStyleDisplay
* displayData
= aForFrame
->GetStyleDisplay();
1469 if (displayData
->mAppearance
) {
1470 nsITheme
*theme
= aPresContext
->GetTheme();
1471 if (theme
&& theme
->ThemeSupportsWidget(aPresContext
, aForFrame
, displayData
->mAppearance
)) {
1473 dirty
.IntersectRect(aDirtyRect
, aBorderArea
);
1474 theme
->DrawWidgetBackground(&aRenderingContext
, aForFrame
,
1475 displayData
->mAppearance
, aBorderArea
, dirty
);
1482 bgClipArea
= *aBGClipRect
;
1485 // The background is rendered over the 'background-clip' area.
1486 bgClipArea
= aBorderArea
;
1487 // If the border is solid, then clip the background to the padding-box
1488 // so that we don't draw unnecessary tiles.
1489 if (aColor
.mBackgroundClip
!= NS_STYLE_BG_CLIP_BORDER
||
1490 IsSolidBorder(aBorder
)) {
1491 nsMargin border
= aForFrame
->GetUsedBorder();
1492 aForFrame
->ApplySkipSides(border
);
1493 bgClipArea
.Deflate(border
);
1497 nsIDeviceContext
*dc
= aPresContext
->DeviceContext();
1498 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
1500 // Snap bgClipArea to device pixel boundaries. (We have to snap
1501 // bgOriginArea below; if we don't do this as well then we could make
1502 // incorrect decisions about various optimizations.)
1503 PixelSnapRectangle(ctx
, dc
, bgClipArea
);
1505 // The actual dirty rect is the intersection of the 'background-clip'
1506 // area and the dirty rect we were given
1508 if (!dirtyRect
.IntersectRect(bgClipArea
, aDirtyRect
)) {
1513 // if there is no background image or background images are turned off, try a color.
1514 if (!aColor
.mBackgroundImage
|| !canDrawBackgroundImage
) {
1515 PaintBackgroundColor(aPresContext
, aRenderingContext
, aForFrame
, bgClipArea
,
1516 aColor
, aBorder
, aPadding
, canDrawBackgroundColor
);
1520 // We have a background image
1523 imgIRequest
*req
= aPresContext
->LoadImage(aColor
.mBackgroundImage
,
1526 PRUint32 status
= imgIRequest::STATUS_ERROR
;
1528 req
->GetImageStatus(&status
);
1530 if (!req
|| !(status
& imgIRequest::STATUS_FRAME_COMPLETE
) || !(status
& imgIRequest::STATUS_SIZE_AVAILABLE
)) {
1531 PaintBackgroundColor(aPresContext
, aRenderingContext
, aForFrame
, bgClipArea
,
1532 aColor
, aBorder
, aPadding
, canDrawBackgroundColor
);
1536 nsCOMPtr
<imgIContainer
> image
;
1537 req
->GetImage(getter_AddRefs(image
));
1540 image
->GetWidth(&imageSize
.width
);
1541 image
->GetHeight(&imageSize
.height
);
1543 imageSize
.width
= nsPresContext::CSSPixelsToAppUnits(imageSize
.width
);
1544 imageSize
.height
= nsPresContext::CSSPixelsToAppUnits(imageSize
.height
);
1548 nsRect bgOriginArea
;
1550 nsIAtom
* frameType
= aForFrame
->GetType();
1551 if (frameType
== nsGkAtoms::inlineFrame
||
1552 frameType
== nsGkAtoms::positionedInlineFrame
) {
1553 switch (aColor
.mBackgroundInlinePolicy
) {
1554 case NS_STYLE_BG_INLINE_POLICY_EACH_BOX
:
1555 bgOriginArea
= aBorderArea
;
1557 case NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX
:
1558 bgOriginArea
= gInlineBGData
->GetBoundingRect(aForFrame
) +
1559 aBorderArea
.TopLeft();
1562 NS_ERROR("Unknown background-inline-policy value! "
1563 "Please, teach me what to do.");
1564 case NS_STYLE_BG_INLINE_POLICY_CONTINUOUS
:
1565 bgOriginArea
= gInlineBGData
->GetContinuousRect(aForFrame
) +
1566 aBorderArea
.TopLeft();
1571 bgOriginArea
= aBorderArea
;
1574 // Background images are tiled over the 'background-clip' area
1575 // but the origin of the tiling is based on the 'background-origin' area
1576 if (aColor
.mBackgroundOrigin
!= NS_STYLE_BG_ORIGIN_BORDER
) {
1577 nsMargin border
= aForFrame
->GetUsedBorder();
1578 aForFrame
->ApplySkipSides(border
);
1579 bgOriginArea
.Deflate(border
);
1580 if (aColor
.mBackgroundOrigin
!= NS_STYLE_BG_ORIGIN_PADDING
) {
1581 nsMargin padding
= aForFrame
->GetUsedPadding();
1582 aForFrame
->ApplySkipSides(padding
);
1583 bgOriginArea
.Deflate(padding
);
1584 NS_ASSERTION(aColor
.mBackgroundOrigin
== NS_STYLE_BG_ORIGIN_CONTENT
,
1585 "unknown background-origin value");
1589 // Snap bgOriginArea to device pixel boundaries to avoid variations in
1590 // tiling when the subpixel position of the element changes.
1591 PixelSnapRectangle(ctx
, dc
, bgOriginArea
);
1593 // Based on the repeat setting, compute how many tiles we should
1594 // lay down for each axis. The value computed is the maximum based
1595 // on the dirty rect before accounting for the background-position.
1596 nscoord tileWidth
= imageSize
.width
;
1597 nscoord tileHeight
= imageSize
.height
;
1598 PRBool needBackgroundColor
= !(aColor
.mBackgroundFlags
&
1599 NS_STYLE_BG_COLOR_TRANSPARENT
);
1600 PRIntn repeat
= aColor
.mBackgroundRepeat
;
1603 case NS_STYLE_BG_REPEAT_X
:
1605 case NS_STYLE_BG_REPEAT_Y
:
1607 case NS_STYLE_BG_REPEAT_XY
:
1608 if (needBackgroundColor
) {
1609 // If the image is completely opaque, we do not need to paint the
1611 nsCOMPtr
<gfxIImageFrame
> gfxImgFrame
;
1612 image
->GetCurrentFrame(getter_AddRefs(gfxImgFrame
));
1614 gfxImgFrame
->GetNeedsBackground(&needBackgroundColor
);
1616 /* check for tiling of a image where frame smaller than container */
1618 image
->GetWidth(&iSize
.width
);
1619 image
->GetHeight(&iSize
.height
);
1621 gfxImgFrame
->GetRect(iframeRect
);
1622 if (iSize
.width
!= iframeRect
.width
||
1623 iSize
.height
!= iframeRect
.height
) {
1624 needBackgroundColor
= PR_TRUE
;
1629 case NS_STYLE_BG_REPEAT_OFF
:
1631 NS_ASSERTION(repeat
== NS_STYLE_BG_REPEAT_OFF
, "unknown background-repeat value");
1635 // The background color is rendered over the 'background-clip' area
1636 if (needBackgroundColor
) {
1637 PaintBackgroundColor(aPresContext
, aRenderingContext
, aForFrame
, bgClipArea
,
1638 aColor
, aBorder
, aPadding
, canDrawBackgroundColor
);
1641 if ((tileWidth
== 0) || (tileHeight
== 0) || dirtyRect
.IsEmpty()) {
1642 // Nothing left to paint
1646 nsPoint borderAreaOriginSnapped
= aBorderArea
.TopLeft();
1647 PixelSnapPoint(ctx
, dc
, borderAreaOriginSnapped
);
1649 // Compute the anchor point.
1651 // When tiling, the anchor coordinate values will be negative offsets
1652 // from the background-origin area.
1654 // relative to the origin of aForFrame
1656 if (NS_STYLE_BG_ATTACHMENT_FIXED
== aColor
.mBackgroundAttachment
) {
1657 // If it's a fixed background attachment, then the image is placed
1658 // relative to the viewport, which is the area of the root frame
1659 // in a screen context or the page content frame in a print context.
1661 // Remember that we've drawn position-varying content in this prescontext
1662 aPresContext
->SetRenderedPositionVaryingContent();
1664 nsIFrame
* topFrame
=
1665 aPresContext
->PresShell()->FrameManager()->GetRootFrame();
1666 NS_ASSERTION(topFrame
, "no root frame");
1667 nsIFrame
* pageContentFrame
= nsnull
;
1668 if (aPresContext
->IsPaginated()) {
1670 nsLayoutUtils::GetClosestFrameOfType(aForFrame
, nsGkAtoms::pageContentFrame
);
1671 if (pageContentFrame
) {
1672 topFrame
= pageContentFrame
;
1674 // else this is an embedded shell and its root frame is what we want
1677 nsRect viewportArea
= topFrame
->GetRect();
1679 if (!pageContentFrame
) {
1680 // Subtract the size of scrollbars.
1681 nsIScrollableFrame
* scrollableFrame
=
1682 aPresContext
->PresShell()->GetRootScrollFrameAsScrollable();
1683 if (scrollableFrame
) {
1684 nsMargin scrollbars
= scrollableFrame
->GetActualScrollbarSizes();
1685 viewportArea
.Deflate(scrollbars
);
1689 // Get the anchor point, relative to the viewport.
1690 ComputeBackgroundAnchorPoint(aColor
, viewportArea
, viewportArea
, tileWidth
, tileHeight
, anchor
);
1692 // Convert the anchor point from viewport coordinates to aForFrame
1694 anchor
-= aForFrame
->GetOffsetTo(topFrame
);
1696 if (frameType
== nsGkAtoms::canvasFrame
) {
1697 // If the frame is the canvas, the image is placed relative to
1698 // the root element's (first) frame (see bug 46446)
1699 nsRect firstRootElementFrameArea
;
1700 nsIFrame
* firstRootElementFrame
= aForFrame
->GetFirstChild(nsnull
);
1701 NS_ASSERTION(firstRootElementFrame
, "A canvas with a background "
1702 "image had no child frame, which is impossible according to CSS. "
1703 "Make sure there isn't a background image specified on the "
1704 "|:viewport| pseudo-element in |html.css|.");
1706 // temporary null check -- see bug 97226
1707 if (firstRootElementFrame
) {
1708 firstRootElementFrameArea
= firstRootElementFrame
->GetRect();
1710 // Take the border out of the frame's rect
1711 const nsStyleBorder
* borderStyle
= firstRootElementFrame
->GetStyleBorder();
1712 firstRootElementFrameArea
.Deflate(borderStyle
->GetActualBorder());
1714 // Get the anchor point
1715 ComputeBackgroundAnchorPoint(aColor
, firstRootElementFrameArea
+
1716 aBorderArea
.TopLeft(), bgClipArea
, tileWidth
, tileHeight
, anchor
);
1718 ComputeBackgroundAnchorPoint(aColor
, bgOriginArea
, bgClipArea
, tileWidth
, tileHeight
, anchor
);
1721 // Otherwise, it is the normal case, and the background is
1722 // simply placed relative to the frame's background-clip area
1723 ComputeBackgroundAnchorPoint(aColor
, bgOriginArea
, bgClipArea
, tileWidth
, tileHeight
, anchor
);
1726 // For scrolling attachment, the anchor is within the 'background-clip'
1727 anchor
.x
+= bgClipArea
.x
- borderAreaOriginSnapped
.x
;
1728 anchor
.y
+= bgClipArea
.y
- borderAreaOriginSnapped
.y
;
1731 // Pixel-snap the anchor point so that we don't end up with blurry
1732 // images due to subpixel positions. But round 0.5 down rather than
1733 // up, since that's what we've always done. (And do that by just
1734 // snapping the negative of the point.)
1735 anchor
.x
= -anchor
.x
; anchor
.y
= -anchor
.y
;
1736 PixelSnapPoint(ctx
, dc
, anchor
);
1737 anchor
.x
= -anchor
.x
; anchor
.y
= -anchor
.y
;
1741 nscoord appUnitsPerPixel
= aPresContext
->DevPixelsToAppUnits(1);
1744 ctx
->Rectangle(RectToGfxRect(dirtyRect
, appUnitsPerPixel
), PR_TRUE
);
1747 nscoord borderRadii
[4];
1748 PRBool haveRadius
= GetBorderRadiusTwips(aBorder
.mBorderRadius
, aForFrame
->GetSize().width
, borderRadii
);
1751 gfxCornerSizes radii
;
1752 ComputePixelRadii(borderRadii
, bgClipArea
, aBorder
.GetActualBorder(),
1753 aForFrame
? aForFrame
->GetSkipSides() : 0,
1754 appUnitsPerPixel
, &radii
);
1756 gfxRect
oRect(RectToGfxRect(bgClipArea
, appUnitsPerPixel
));
1761 ctx
->RoundedRectangle(oRect
, radii
);
1765 // Compute the x and y starting points and limits for tiling
1767 /* An Overview Of The Following Logic
1769 A........ . . . . . . . . . . . . . .
1770 : +---:-------.-------.-------.---- /|\
1772 :.......: . . . x . . . . . . . . . . \|/
1774 . | . . ########### .
1775 . . . . . . . . . .#. . . . .#. . . .
1776 . | . . ########### . /|\
1778 . . | . . . . . . . . . . . . . z . . \|/
1780 |<-----nw------>| |<--w-->|
1782 ---- = the background clip area edge. The painting is done within
1783 to this area. If the background is positioned relative to the
1784 viewport ('fixed') then this is the viewport edge.
1786 .... = the primary tile.
1788 . . = the other tiles.
1790 #### = the dirtyRect. This is the minimum region we want to cover.
1792 A = The anchor point. This is the point at which the tile should
1793 start. Always negative or zero.
1795 x = x0 and y0 in the code. The point at which tiling must start
1796 so that the fewest tiles are laid out while completely
1797 covering the dirtyRect area.
1799 z = x1 and y1 in the code. The point at which tiling must end so
1800 that the fewest tiles are laid out while completely covering
1803 w = the width of the tile (tileWidth).
1805 h = the height of the tile (tileHeight).
1807 n = the number of whole tiles that fit between 'A' and 'x'.
1808 (the vertical n and the horizontal n are different)
1813 x0 = bgClipArea.x + anchor.x + n * tileWidth;
1815 ...where n is an integer greater or equal to 0 fitting:
1818 dirtyRect.x - (bgClipArea.x + anchor.x) <=
1823 n <= (dirtyRect.x - (bgClipArea.x + anchor.x)) / tileWidth < n + 1
1825 ...which, treating the division as an integer divide rounding down, gives:
1827 n = (dirtyRect.x - (bgClipArea.x + anchor.x)) / tileWidth
1829 Substituting into the original expression for x0:
1831 x0 = bgClipArea.x + anchor.x +
1832 ((dirtyRect.x - (bgClipArea.x + anchor.x)) / tileWidth) *
1835 From this x1 is determined,
1837 x1 = x0 + m * tileWidth;
1839 ...where m is an integer greater than 0 fitting:
1841 (m - 1) * tileWidth <
1842 dirtyRect.x + dirtyRect.width - x0 <=
1847 m - 1 < (dirtyRect.x + dirtyRect.width - x0) / tileWidth <= m
1849 ...which, treating the division as an integer divide, and making it
1852 m = (dirtyRect.x + dirtyRect.width - x0 + tileWidth - 1) / tileWidth
1854 Substituting into the original expression for x1:
1856 x1 = x0 + ((dirtyRect.x + dirtyRect.width - x0 + tileWidth - 1) /
1857 tileWidth) * tileWidth
1859 The vertical case is analogous. If the background is fixed, then
1860 bgClipArea.x and bgClipArea.y are set to zero when finding the parent
1865 // relative to aBorderArea.TopLeft()
1866 // ... but pixel-snapped, so that it comes out correctly relative to
1867 // all the other pixel-snapped things
1868 nsRect
tileRect(anchor
, nsSize(tileWidth
, tileHeight
));
1869 if (repeat
& NS_STYLE_BG_REPEAT_X
) {
1870 // When tiling in the x direction, adjust the starting position of the
1871 // tile to account for dirtyRect.x. When tiling in x, the anchor.x value
1872 // will be a negative value used to adjust the starting coordinate.
1873 nscoord x0
= FindTileStart(dirtyRect
.x
- borderAreaOriginSnapped
.x
, anchor
.x
, tileWidth
);
1874 nscoord x1
= FindTileEnd(dirtyRect
.XMost() - borderAreaOriginSnapped
.x
, anchor
.x
, tileWidth
);
1876 tileRect
.width
= x1
- x0
;
1878 if (repeat
& NS_STYLE_BG_REPEAT_Y
) {
1879 // When tiling in the y direction, adjust the starting position of the
1880 // tile to account for dirtyRect.y. When tiling in y, the anchor.y value
1881 // will be a negative value used to adjust the starting coordinate.
1882 nscoord y0
= FindTileStart(dirtyRect
.y
- borderAreaOriginSnapped
.y
, anchor
.y
, tileHeight
);
1883 nscoord y1
= FindTileEnd(dirtyRect
.YMost() - borderAreaOriginSnapped
.y
, anchor
.y
, tileHeight
);
1885 tileRect
.height
= y1
- y0
;
1888 // Take the intersection again to paint only the required area.
1889 nsRect absTileRect
= tileRect
+ borderAreaOriginSnapped
;
1892 if (drawRect
.IntersectRect(absTileRect
, dirtyRect
)) {
1893 // Note that due to the way FindTileStart works we're guaranteed
1894 // that drawRect overlaps the top-left-most tile when repeating.
1895 NS_ASSERTION(drawRect
.x
>= absTileRect
.x
&& drawRect
.y
>= absTileRect
.y
,
1896 "Bogus intersection");
1897 NS_ASSERTION(drawRect
.x
< absTileRect
.x
+ tileWidth
,
1898 "Bogus x coord for draw rect");
1899 NS_ASSERTION(drawRect
.y
< absTileRect
.y
+ tileHeight
,
1900 "Bogus y coord for draw rect");
1901 // Figure out whether we can get away with not tiling at all.
1902 nsRect sourceRect
= drawRect
- absTileRect
.TopLeft();
1903 // Compute the subimage rectangle that we expect to be sampled.
1904 // This is the tile rectangle, clipped to the bgClipArea, and then
1905 // passed in relative to the image top-left.
1906 nsRect destRect
; // The rectangle we would draw ignoring dirty-rect
1907 destRect
.IntersectRect(absTileRect
, bgClipArea
);
1908 nsRect subimageRect
= destRect
- borderAreaOriginSnapped
- tileRect
.TopLeft();
1909 if (sourceRect
.XMost() <= tileWidth
&& sourceRect
.YMost() <= tileHeight
) {
1910 // The entire drawRect is contained inside a single tile; just
1911 // draw the corresponding part of the image once.
1912 nsLayoutUtils::DrawImage(&aRenderingContext
, image
,
1913 destRect
, drawRect
, &subimageRect
);
1915 // Note that the subimage is in tile space so it may cover
1916 // multiple tiles of the image.
1917 subimageRect
.ScaleRoundOutInverse(nsIDeviceContext::AppUnitsPerCSSPixel());
1918 aRenderingContext
.DrawTile(image
, absTileRect
.x
, absTileRect
.y
,
1919 &drawRect
, &subimageRect
);
1928 nsCSSRendering::DrawBorderImage(nsPresContext
* aPresContext
,
1929 nsIRenderingContext
& aRenderingContext
,
1930 nsIFrame
* aForFrame
,
1931 const nsRect
& aBorderArea
,
1932 const nsStyleBorder
& aBorderStyle
)
1935 nsStyleCoord borderImageSplit
[4];
1936 PRInt32 borderImageSplitInt
[4];
1938 gfxFloat borderTop
, borderRight
, borderBottom
, borderLeft
;
1939 gfxFloat borderImageSplitGfx
[4];
1941 border
= aBorderStyle
.GetActualBorder();
1942 if ((0 == border
.left
) && (0 == border
.right
) &&
1943 (0 == border
.top
) && (0 == border
.bottom
)) {
1944 // Empty border area
1948 borderImageSplit
[NS_SIDE_TOP
] = aBorderStyle
.mBorderImageSplit
.GetTop();
1949 borderImageSplit
[NS_SIDE_RIGHT
] = aBorderStyle
.mBorderImageSplit
.GetRight();
1950 borderImageSplit
[NS_SIDE_BOTTOM
] = aBorderStyle
.mBorderImageSplit
.GetBottom();
1951 borderImageSplit
[NS_SIDE_LEFT
] = aBorderStyle
.mBorderImageSplit
.GetLeft();
1953 imgIRequest
*req
= aPresContext
->LoadBorderImage(aBorderStyle
.GetBorderImage(), aForFrame
);
1955 nsCOMPtr
<imgIContainer
> image
;
1956 req
->GetImage(getter_AddRefs(image
));
1959 image
->GetWidth(&imageSize
.width
);
1960 image
->GetHeight(&imageSize
.height
);
1961 imageSize
.width
= nsPresContext::CSSPixelsToAppUnits(imageSize
.width
);
1962 imageSize
.height
= nsPresContext::CSSPixelsToAppUnits(imageSize
.height
);
1964 // convert percentage values
1965 NS_FOR_CSS_SIDES(side
) {
1966 borderImageSplitInt
[side
] = 0;
1967 switch (borderImageSplit
[side
].GetUnit()) {
1968 case eStyleUnit_Percent
:
1969 percent
= borderImageSplit
[side
].GetPercentValue();
1970 if (side
== NS_SIDE_TOP
|| side
== NS_SIDE_BOTTOM
)
1971 borderImageSplitInt
[side
] = (nscoord
)(percent
* imageSize
.height
);
1973 borderImageSplitInt
[side
] = (nscoord
)(percent
* imageSize
.width
);
1975 case eStyleUnit_Integer
:
1976 borderImageSplitInt
[side
] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit
[side
].
1979 case eStyleUnit_Factor
:
1980 borderImageSplitInt
[side
] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit
[side
].GetFactorValue());
1987 gfxContext
*thebesCtx
= aRenderingContext
.ThebesContext();
1988 nsCOMPtr
<nsIDeviceContext
> dc
;
1989 aRenderingContext
.GetDeviceContext(*getter_AddRefs(dc
));
1991 NS_FOR_CSS_SIDES(side
) {
1992 borderImageSplitGfx
[side
] = nsPresContext::AppUnitsToFloatCSSPixels(borderImageSplitInt
[side
]);
1995 borderTop
= dc
->AppUnitsToGfxUnits(border
.top
);
1996 borderRight
= dc
->AppUnitsToGfxUnits(border
.right
);
1997 borderBottom
= dc
->AppUnitsToGfxUnits(border
.bottom
);
1998 borderLeft
= dc
->AppUnitsToGfxUnits(border
.left
);
2000 gfxSize gfxImageSize
;
2001 gfxImageSize
.width
= nsPresContext::AppUnitsToFloatCSSPixels(imageSize
.width
);
2002 gfxImageSize
.height
= nsPresContext::AppUnitsToFloatCSSPixels(imageSize
.height
);
2004 nsRect
outerRect(aBorderArea
);
2009 clipRect
.pos
.x
= dc
->AppUnitsToGfxUnits(outerRect
.x
);
2010 clipRect
.pos
.y
= dc
->AppUnitsToGfxUnits(outerRect
.y
);
2011 clipRect
.size
.width
= dc
->AppUnitsToGfxUnits(outerRect
.width
);
2012 clipRect
.size
.height
= dc
->AppUnitsToGfxUnits(outerRect
.height
);
2013 thebesCtx
->UserToDevicePixelSnapped(clipRect
);
2016 thebesCtx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
2018 gfxSize
middleSize(clipRect
.size
.width
- (borderLeft
+ borderRight
),
2019 clipRect
.size
.height
- (borderTop
+ borderBottom
));
2021 // middle size in source space
2022 gfxIntSize
middleSizeSource(gfxImageSize
.width
- (borderImageSplitGfx
[NS_SIDE_RIGHT
] + borderImageSplitGfx
[NS_SIDE_LEFT
]),
2023 gfxImageSize
.height
- (borderImageSplitGfx
[NS_SIDE_TOP
] + borderImageSplitGfx
[NS_SIDE_BOTTOM
]));
2025 gfxSize interSizeTop
, interSizeBottom
, interSizeLeft
, interSizeRight
,
2027 gfxFloat topScale
= borderTop
/borderImageSplitGfx
[NS_SIDE_TOP
];
2028 gfxFloat bottomScale
= borderBottom
/borderImageSplitGfx
[NS_SIDE_BOTTOM
];
2029 gfxFloat leftScale
= borderLeft
/borderImageSplitGfx
[NS_SIDE_LEFT
];
2030 gfxFloat rightScale
= borderRight
/borderImageSplitGfx
[NS_SIDE_RIGHT
];
2031 gfxFloat middleScaleH
,
2033 // TODO: check for nan and properly check for inf
2034 if (topScale
!= 0.0 && borderImageSplitGfx
[NS_SIDE_TOP
] != 0.0) {
2035 middleScaleH
= topScale
;
2036 } else if (bottomScale
!= 0.0 && borderImageSplitGfx
[NS_SIDE_BOTTOM
] != 0.0) {
2037 middleScaleH
= bottomScale
;
2042 if (leftScale
!= 0.0 && borderImageSplitGfx
[NS_SIDE_LEFT
] != 0.0) {
2043 middleScaleV
= leftScale
;
2044 } else if (rightScale
!= 0.0 && borderImageSplitGfx
[NS_SIDE_RIGHT
] != 0.0) {
2045 middleScaleV
= rightScale
;
2050 interSizeTop
.height
= borderTop
;
2051 interSizeTop
.width
= middleSizeSource
.width
*topScale
;
2053 interSizeBottom
.height
= borderBottom
;
2054 interSizeBottom
.width
= middleSizeSource
.width
*bottomScale
;
2056 interSizeLeft
.width
= borderLeft
;
2057 interSizeLeft
.height
= middleSizeSource
.height
*leftScale
;
2059 interSizeRight
.width
= borderRight
;
2060 interSizeRight
.height
= middleSizeSource
.height
*rightScale
;
2062 interSizeMiddle
.width
= middleSizeSource
.width
*middleScaleH
;
2063 interSizeMiddle
.height
= middleSizeSource
.height
*middleScaleV
;
2065 // draw top left corner
2066 rectToDraw
= clipRect
;
2067 rectToDraw
.size
.width
= borderLeft
;
2068 rectToDraw
.size
.height
= borderTop
;
2069 rectToDrawSource
.pos
.x
= 0;
2070 rectToDrawSource
.pos
.y
= 0;
2071 rectToDrawSource
.size
.width
= borderImageSplitGfx
[NS_SIDE_LEFT
];
2072 rectToDrawSource
.size
.height
= borderImageSplitGfx
[NS_SIDE_TOP
];
2073 DrawBorderImageSide(thebesCtx
, dc
, image
,
2074 rectToDraw
, rectToDraw
.size
, rectToDrawSource
,
2075 NS_STYLE_BORDER_IMAGE_STRETCH
, NS_STYLE_BORDER_IMAGE_STRETCH
);
2078 rectToDraw
= clipRect
;
2079 rectToDraw
.pos
.x
+= borderLeft
;
2080 rectToDraw
.size
.width
= middleSize
.width
;
2081 rectToDraw
.size
.height
= borderTop
;
2082 rectToDrawSource
.pos
.x
= borderImageSplitGfx
[NS_SIDE_LEFT
];
2083 rectToDrawSource
.pos
.y
= 0;
2084 rectToDrawSource
.size
.width
= middleSizeSource
.width
;
2085 rectToDrawSource
.size
.height
= borderImageSplitGfx
[NS_SIDE_TOP
];
2086 DrawBorderImageSide(thebesCtx
, dc
, image
,
2087 rectToDraw
, interSizeTop
, rectToDrawSource
,
2088 aBorderStyle
.mBorderImageHFill
, NS_STYLE_BORDER_IMAGE_STRETCH
);
2090 // draw top right corner
2091 rectToDraw
= clipRect
;
2092 rectToDraw
.pos
.x
+= clipRect
.size
.width
- borderRight
;
2093 rectToDraw
.size
.width
= borderRight
;
2094 rectToDraw
.size
.height
= borderTop
;
2095 rectToDrawSource
.pos
.x
= gfxImageSize
.width
- borderImageSplitGfx
[NS_SIDE_RIGHT
];
2096 rectToDrawSource
.pos
.y
= 0;
2097 rectToDrawSource
.size
.width
= borderImageSplitGfx
[NS_SIDE_RIGHT
];
2098 rectToDrawSource
.size
.height
= borderImageSplitGfx
[NS_SIDE_TOP
];
2099 DrawBorderImageSide(thebesCtx
, dc
, image
,
2100 rectToDraw
, rectToDraw
.size
, rectToDrawSource
,
2101 NS_STYLE_BORDER_IMAGE_STRETCH
, NS_STYLE_BORDER_IMAGE_STRETCH
);
2104 rectToDraw
= clipRect
;
2105 rectToDraw
.pos
.x
+= clipRect
.size
.width
- borderRight
;
2106 rectToDraw
.pos
.y
+= borderTop
;
2107 rectToDraw
.size
.width
= borderRight
;
2108 rectToDraw
.size
.height
= middleSize
.height
;
2109 rectToDrawSource
.pos
.x
= gfxImageSize
.width
- borderImageSplitGfx
[NS_SIDE_RIGHT
];
2110 rectToDrawSource
.pos
.y
= borderImageSplitGfx
[NS_SIDE_TOP
];
2111 rectToDrawSource
.size
.width
= borderImageSplitGfx
[NS_SIDE_RIGHT
];
2112 rectToDrawSource
.size
.height
= middleSizeSource
.height
;
2113 DrawBorderImageSide(thebesCtx
, dc
, image
,
2114 rectToDraw
, interSizeRight
, rectToDrawSource
,
2115 NS_STYLE_BORDER_IMAGE_STRETCH
, aBorderStyle
.mBorderImageVFill
);
2117 // draw bottom right corner
2118 rectToDraw
= clipRect
;
2119 rectToDraw
.pos
.x
+= clipRect
.size
.width
- borderRight
;
2120 rectToDraw
.pos
.y
+= clipRect
.size
.height
- borderBottom
;
2121 rectToDraw
.size
.width
= borderRight
;
2122 rectToDraw
.size
.height
= borderBottom
;
2123 rectToDrawSource
.pos
.x
= gfxImageSize
.width
- borderImageSplitGfx
[NS_SIDE_RIGHT
];
2124 rectToDrawSource
.pos
.y
= gfxImageSize
.height
- borderImageSplitGfx
[NS_SIDE_BOTTOM
];
2125 rectToDrawSource
.size
.width
= borderImageSplitGfx
[NS_SIDE_RIGHT
];
2126 rectToDrawSource
.size
.height
= borderImageSplitGfx
[NS_SIDE_BOTTOM
];
2127 DrawBorderImageSide(thebesCtx
, dc
, image
,
2128 rectToDraw
, rectToDraw
.size
, rectToDrawSource
,
2129 NS_STYLE_BORDER_IMAGE_STRETCH
, NS_STYLE_BORDER_IMAGE_STRETCH
);
2132 rectToDraw
= clipRect
;
2133 rectToDraw
.pos
.x
+= borderLeft
;
2134 rectToDraw
.pos
.y
+= clipRect
.size
.height
- borderBottom
;
2135 rectToDraw
.size
.width
= middleSize
.width
;
2136 rectToDraw
.size
.height
= borderBottom
;
2137 rectToDrawSource
.pos
.x
= borderImageSplitGfx
[NS_SIDE_LEFT
];
2138 rectToDrawSource
.pos
.y
= gfxImageSize
.height
- borderImageSplitGfx
[NS_SIDE_BOTTOM
];
2139 rectToDrawSource
.size
.width
= middleSizeSource
.width
;
2140 rectToDrawSource
.size
.height
= borderImageSplitGfx
[NS_SIDE_BOTTOM
];
2141 DrawBorderImageSide(thebesCtx
, dc
, image
,
2142 rectToDraw
, interSizeBottom
, rectToDrawSource
,
2143 aBorderStyle
.mBorderImageHFill
, NS_STYLE_BORDER_IMAGE_STRETCH
);
2145 // draw bottom left corner
2146 rectToDraw
= clipRect
;
2147 rectToDraw
.pos
.y
+= clipRect
.size
.height
- borderBottom
;
2148 rectToDraw
.size
.width
= borderLeft
;
2149 rectToDraw
.size
.height
= borderBottom
;
2150 rectToDrawSource
.pos
.x
= 0;
2151 rectToDrawSource
.pos
.y
= gfxImageSize
.height
- borderImageSplitGfx
[NS_SIDE_BOTTOM
];
2152 rectToDrawSource
.size
.width
= borderImageSplitGfx
[NS_SIDE_LEFT
];
2153 rectToDrawSource
.size
.height
= borderImageSplitGfx
[NS_SIDE_BOTTOM
];
2154 DrawBorderImageSide(thebesCtx
, dc
, image
,
2155 rectToDraw
, rectToDraw
.size
, rectToDrawSource
,
2156 NS_STYLE_BORDER_IMAGE_STRETCH
, NS_STYLE_BORDER_IMAGE_STRETCH
);
2159 rectToDraw
= clipRect
;
2160 rectToDraw
.pos
.y
+= borderTop
;
2161 rectToDraw
.size
.width
= borderLeft
;
2162 rectToDraw
.size
.height
= middleSize
.height
;
2163 rectToDrawSource
.pos
.x
= 0;
2164 rectToDrawSource
.pos
.y
= borderImageSplitGfx
[NS_SIDE_TOP
];
2165 rectToDrawSource
.size
.width
= borderImageSplitGfx
[NS_SIDE_LEFT
];
2166 rectToDrawSource
.size
.height
= middleSizeSource
.height
;
2167 DrawBorderImageSide(thebesCtx
, dc
, image
,
2168 rectToDraw
, interSizeLeft
, rectToDrawSource
,
2169 NS_STYLE_BORDER_IMAGE_STRETCH
, aBorderStyle
.mBorderImageVFill
);
2172 rectToDraw
= clipRect
;
2173 rectToDraw
.pos
.x
+= borderLeft
;
2174 rectToDraw
.pos
.y
+= borderTop
;
2175 rectToDraw
.size
.width
= middleSize
.width
;
2176 rectToDraw
.size
.height
= middleSize
.height
;
2177 rectToDrawSource
.pos
.x
= borderImageSplitGfx
[NS_SIDE_LEFT
];
2178 rectToDrawSource
.pos
.y
= borderImageSplitGfx
[NS_SIDE_TOP
];
2179 rectToDrawSource
.size
= middleSizeSource
;
2180 DrawBorderImageSide(thebesCtx
, dc
, image
,
2181 rectToDraw
, interSizeMiddle
, rectToDrawSource
,
2182 aBorderStyle
.mBorderImageHFill
, aBorderStyle
.mBorderImageVFill
);
2184 thebesCtx
->PopGroupToSource();
2185 thebesCtx
->SetOperator(gfxContext::OPERATOR_OVER
);
2187 thebesCtx
->Restore();
2191 nsCSSRendering::DrawBorderImageSide(gfxContext
*aThebesContext
,
2192 nsIDeviceContext
* aDeviceContext
,
2193 imgIContainer
* aImage
,
2195 gfxSize
& aInterSize
,
2196 gfxRect
& aSourceRect
,
2200 if (aDestRect
.size
.width
< 1.0 || aDestRect
.size
.height
< 1.0 ||
2201 aSourceRect
.size
.width
< 1.0 || aSourceRect
.size
.height
< 1.0) {
2205 gfxIntSize
gfxSourceSize((PRInt32
)aSourceRect
.size
.width
,
2206 (PRInt32
)aSourceRect
.size
.height
);
2208 // where the actual border ends up being rendered
2209 aThebesContext
->UserToDevicePixelSnapped(aDestRect
);
2210 aThebesContext
->UserToDevicePixelSnapped(aSourceRect
);
2212 if (aDestRect
.size
.height
< 1.0 ||
2213 aDestRect
.size
.width
< 1.0)
2216 if (aInterSize
.width
< 1.0 ||
2217 aInterSize
.height
< 1.0)
2220 // Surface will hold just the part of the source image specified by the aSourceRect
2221 // but at a different size
2222 nsRefPtr
<gfxASurface
> interSurface
=
2223 gfxPlatform::GetPlatform()->CreateOffscreenSurface(
2224 gfxSourceSize
, gfxASurface::ImageFormatARGB32
);
2226 gfxMatrix srcMatrix
;
2227 // Adjust the matrix scale for Step 1 of the spec
2228 srcMatrix
.Scale(aSourceRect
.size
.width
/aInterSize
.width
,
2229 aSourceRect
.size
.height
/aInterSize
.height
);
2231 nsCOMPtr
<gfxIImageFrame
> frame
;
2232 nsresult rv
= aImage
->GetCurrentFrame(getter_AddRefs(frame
));
2235 nsCOMPtr
<nsIImage
> image
;
2236 image
= do_GetInterface(frame
);
2240 // surface for the whole image
2241 nsRefPtr
<gfxPattern
> imagePattern
;
2242 rv
= image
->GetPattern(getter_AddRefs(imagePattern
));
2243 if(NS_FAILED(rv
) || !imagePattern
)
2247 mat
.Translate(aSourceRect
.pos
);
2248 imagePattern
->SetMatrix(mat
);
2250 // Straightforward blit - no resizing
2251 nsRefPtr
<gfxContext
> srcCtx
= new gfxContext(interSurface
);
2252 srcCtx
->SetPattern(imagePattern
);
2253 srcCtx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
2259 // offset to make the middle tile centered in the middle of the border
2260 gfxPoint
renderOffset(0, 0);
2261 gfxSize
rectSize(aDestRect
.size
);
2263 aThebesContext
->Save();
2264 aThebesContext
->Clip(aDestRect
);
2266 gfxFloat
hScale(1.0), vScale(1.0);
2268 nsRefPtr
<gfxPattern
> pattern
= new gfxPattern(interSurface
);
2269 pattern
->SetExtend(gfxPattern::EXTEND_PAD
);
2270 switch (aHFillType
) {
2271 case NS_STYLE_BORDER_IMAGE_REPEAT
:
2272 renderOffset
.x
= (rectSize
.width
- aInterSize
.width
*NS_ceil(rectSize
.width
/aInterSize
.width
))*-0.5;
2273 aDestRect
.pos
.x
-= renderOffset
.x
;
2274 pattern
->SetExtend(gfxPattern::EXTEND_REPEAT
);
2276 case NS_STYLE_BORDER_IMAGE_ROUND
:
2277 hScale
= aInterSize
.width
*(NS_ceil(aDestRect
.size
.width
/aInterSize
.width
)/aDestRect
.size
.width
);
2278 pattern
->SetExtend(gfxPattern::EXTEND_REPEAT
);
2280 case NS_STYLE_BORDER_IMAGE_STRETCH
:
2282 hScale
= aInterSize
.width
/aDestRect
.size
.width
;
2286 switch (aVFillType
) {
2287 case NS_STYLE_BORDER_IMAGE_REPEAT
:
2288 renderOffset
.y
= (rectSize
.height
- aInterSize
.height
*NS_ceil(rectSize
.height
/aInterSize
.height
))*-0.5;
2289 aDestRect
.pos
.y
-= renderOffset
.y
;
2290 pattern
->SetExtend(gfxPattern::EXTEND_REPEAT
);
2292 case NS_STYLE_BORDER_IMAGE_ROUND
:
2293 vScale
= aInterSize
.height
*(NS_ceil(aDestRect
.size
.height
/aInterSize
.height
)/aDestRect
.size
.height
);
2294 pattern
->SetExtend(gfxPattern::EXTEND_REPEAT
);
2296 case NS_STYLE_BORDER_IMAGE_STRETCH
:
2298 vScale
= aInterSize
.height
/aDestRect
.size
.height
;
2302 // Adjust the matrix scale for Step 2 of the spec
2303 srcMatrix
.Scale(hScale
,vScale
);
2304 pattern
->SetMatrix(srcMatrix
);
2307 aThebesContext
->Translate(aDestRect
.pos
);
2308 aThebesContext
->SetPattern(pattern
);
2309 aThebesContext
->NewPath();
2310 aThebesContext
->Rectangle(gfxRect(renderOffset
, rectSize
));
2311 aThebesContext
->SetOperator(gfxContext::OPERATOR_ADD
);
2312 aThebesContext
->Fill();
2313 aThebesContext
->Restore();
2317 nsCSSRendering::PaintBackgroundColor(nsPresContext
* aPresContext
,
2318 nsIRenderingContext
& aRenderingContext
,
2319 nsIFrame
* aForFrame
,
2320 const nsRect
& aBgClipArea
,
2321 const nsStyleBackground
& aColor
,
2322 const nsStyleBorder
& aBorder
,
2323 const nsStylePadding
& aPadding
,
2324 PRBool aCanPaintNonWhite
)
2326 // If we're only allowed to paint white, then don't bail out on transparent
2327 // color if we're not completely transparent. See the corresponding check
2328 // for whether we're allowed to paint background images in
2329 // PaintBackgroundWithSC before the first call to PaintBackgroundColor.
2330 if ((aColor
.mBackgroundFlags
& NS_STYLE_BG_COLOR_TRANSPARENT
) &&
2331 (aCanPaintNonWhite
|| aColor
.IsTransparent())) {
2336 nscoord borderRadii
[4];
2337 nsRect
bgClipArea(aBgClipArea
);
2339 GetBorderRadiusTwips(aBorder
.mBorderRadius
, aForFrame
->GetSize().width
, borderRadii
);
2342 // Rounded version of the border
2343 for (side
= 0; side
< 4; ++side
) {
2344 if (borderRadii
[side
] > 0) {
2345 PaintRoundedBackground(aPresContext
, aRenderingContext
, aForFrame
,
2346 bgClipArea
, aColor
, aBorder
, borderRadii
,
2353 if (!aCanPaintNonWhite
) {
2354 color
= NS_RGB(255, 255, 255);
2356 color
= aColor
.mBackgroundColor
;
2359 aRenderingContext
.SetColor(color
);
2360 aRenderingContext
.FillRect(bgClipArea
);
2363 /** ---------------------------------------------------
2364 * See documentation in nsCSSRendering.h
2365 * @update 3/26/99 dwc
2368 nsCSSRendering::PaintRoundedBackground(nsPresContext
* aPresContext
,
2369 nsIRenderingContext
& aRenderingContext
,
2370 nsIFrame
* aForFrame
,
2371 const nsRect
& aBgClipArea
,
2372 const nsStyleBackground
& aColor
,
2373 const nsStyleBorder
& aBorder
,
2374 nscoord aTheRadius
[4],
2375 PRBool aCanPaintNonWhite
)
2377 gfxContext
*ctx
= aRenderingContext
.ThebesContext();
2379 // needed for our border thickness
2380 nscoord appUnitsPerPixel
= aPresContext
->AppUnitsPerDevPixel();
2382 nscolor color
= aColor
.mBackgroundColor
;
2383 if (!aCanPaintNonWhite
) {
2384 color
= NS_RGB(255, 255, 255);
2386 aRenderingContext
.SetColor(color
);
2388 // Adjust for background-clip, if necessary
2389 if (aColor
.mBackgroundClip
!= NS_STYLE_BG_CLIP_BORDER
) {
2390 NS_ASSERTION(aColor
.mBackgroundClip
== NS_STYLE_BG_CLIP_PADDING
, "unknown background-clip value");
2392 // Get the radius to the outer edge of the padding.
2393 // -moz-border-radius is the radius to the outer edge of the border.
2394 NS_FOR_CSS_SIDES(side
) {
2395 aTheRadius
[side
] -= aBorder
.GetActualBorderWidth(side
);
2396 aTheRadius
[side
] = PR_MAX(aTheRadius
[side
], 0);
2400 // the bgClipArea is the outside
2401 gfxRect
oRect(RectToGfxRect(aBgClipArea
, appUnitsPerPixel
));
2404 if (oRect
.IsEmpty())
2407 // convert the radii
2408 gfxCornerSizes radii
;
2409 nsMargin border
= aBorder
.GetActualBorder();
2411 ComputePixelRadii(aTheRadius
, aBgClipArea
, border
,
2412 aForFrame
? aForFrame
->GetSkipSides() : 0,
2413 appUnitsPerPixel
, &radii
);
2415 // Add 1.0 to any border radii; if we don't, the border and background
2416 // curves will combine to have fringing at the rounded corners. Since
2417 // alpha is used for coverage, we have problems because the border and
2418 // background should have identical coverage, and the border should
2419 // overlay the background exactly. The way to avoid this is by using
2420 // a supersampling scheme, but we don't have the mechanism in place to do
2421 // this. So, this will do for now.
2422 for (int i
= 0; i
< 4; i
++) {
2423 if (radii
[i
].width
> 0.0)
2424 radii
[i
].width
+= 1.0;
2425 if (radii
[i
].height
> 0.0)
2426 radii
[i
].height
+= 1.0;
2430 ctx
->RoundedRectangle(oRect
, radii
);
2431 ctx
->SetColor(gfxRGBA(color
));
2436 // Begin table border-collapsing section
2437 // These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
2438 // At some point, all functions should be unified to include the additional functionality that these provide
2441 RoundIntToPixel(nscoord aValue
,
2442 nscoord aTwipsPerPixel
,
2443 PRBool aRoundDown
= PR_FALSE
)
2445 if (aTwipsPerPixel
<= 0)
2446 // We must be rendering to a device that has a resolution greater than Twips!
2447 // In that case, aValue is as accurate as it's going to get.
2450 nscoord halfPixel
= NSToCoordRound(aTwipsPerPixel
/ 2.0f
);
2451 nscoord extra
= aValue
% aTwipsPerPixel
;
2452 nscoord finalValue
= (!aRoundDown
&& (extra
>= halfPixel
)) ? aValue
+ (aTwipsPerPixel
- extra
) : aValue
- extra
;
2457 RoundFloatToPixel(float aValue
,
2458 nscoord aTwipsPerPixel
,
2459 PRBool aRoundDown
= PR_FALSE
)
2461 return RoundIntToPixel(NSToCoordRound(aValue
), aTwipsPerPixel
, aRoundDown
);
2465 SetPoly(const nsRect
& aRect
,
2468 poly
[0].x
= aRect
.x
;
2469 poly
[0].y
= aRect
.y
;
2470 poly
[1].x
= aRect
.x
+ aRect
.width
;
2471 poly
[1].y
= aRect
.y
;
2472 poly
[2].x
= aRect
.x
+ aRect
.width
;
2473 poly
[2].y
= aRect
.y
+ aRect
.height
;
2474 poly
[3].x
= aRect
.x
;
2475 poly
[3].y
= aRect
.y
+ aRect
.height
;
2476 poly
[4].x
= aRect
.x
;
2477 poly
[4].y
= aRect
.y
;
2481 DrawSolidBorderSegment(nsIRenderingContext
& aContext
,
2483 nscoord aTwipsPerPixel
,
2484 PRUint8 aStartBevelSide
= 0,
2485 nscoord aStartBevelOffset
= 0,
2486 PRUint8 aEndBevelSide
= 0,
2487 nscoord aEndBevelOffset
= 0)
2490 if ((aRect
.width
== aTwipsPerPixel
) || (aRect
.height
== aTwipsPerPixel
) ||
2491 ((0 == aStartBevelOffset
) && (0 == aEndBevelOffset
))) {
2492 // simple line or rectangle
2493 if ((NS_SIDE_TOP
== aStartBevelSide
) || (NS_SIDE_BOTTOM
== aStartBevelSide
)) {
2494 if (1 == aRect
.height
)
2495 aContext
.DrawLine(aRect
.x
, aRect
.y
, aRect
.x
, aRect
.y
+ aRect
.height
);
2497 aContext
.FillRect(aRect
);
2500 if (1 == aRect
.width
)
2501 aContext
.DrawLine(aRect
.x
, aRect
.y
, aRect
.x
+ aRect
.width
, aRect
.y
);
2503 aContext
.FillRect(aRect
);
2507 // polygon with beveling
2509 SetPoly(aRect
, poly
);
2510 switch(aStartBevelSide
) {
2512 poly
[0].x
+= aStartBevelOffset
;
2513 poly
[4].x
= poly
[0].x
;
2515 case NS_SIDE_BOTTOM
:
2516 poly
[3].x
+= aStartBevelOffset
;
2519 poly
[1].y
+= aStartBevelOffset
;
2522 poly
[0].y
+= aStartBevelOffset
;
2523 poly
[4].y
= poly
[0].y
;
2526 switch(aEndBevelSide
) {
2528 poly
[1].x
-= aEndBevelOffset
;
2530 case NS_SIDE_BOTTOM
:
2531 poly
[2].x
-= aEndBevelOffset
;
2534 poly
[2].y
-= aEndBevelOffset
;
2537 poly
[3].y
-= aEndBevelOffset
;
2540 aContext
.FillPolygon(poly
, 5);
2547 GetDashInfo(nscoord aBorderLength
,
2548 nscoord aDashLength
,
2549 nscoord aTwipsPerPixel
,
2550 PRInt32
& aNumDashSpaces
,
2551 nscoord
& aStartDashLength
,
2552 nscoord
& aEndDashLength
)
2555 if (aStartDashLength
+ aDashLength
+ aEndDashLength
>= aBorderLength
) {
2556 aStartDashLength
= aBorderLength
;
2560 aNumDashSpaces
= aBorderLength
/ (2 * aDashLength
); // round down
2561 nscoord extra
= aBorderLength
- aStartDashLength
- aEndDashLength
- (((2 * aNumDashSpaces
) - 1) * aDashLength
);
2563 nscoord half
= RoundIntToPixel(extra
/ 2, aTwipsPerPixel
);
2564 aStartDashLength
+= half
;
2565 aEndDashLength
+= (extra
- half
);
2571 nsCSSRendering::DrawTableBorderSegment(nsIRenderingContext
& aContext
,
2572 PRUint8 aBorderStyle
,
2573 nscolor aBorderColor
,
2574 const nsStyleBackground
* aBGColor
,
2575 const nsRect
& aBorder
,
2576 PRInt32 aAppUnitsPerCSSPixel
,
2577 PRUint8 aStartBevelSide
,
2578 nscoord aStartBevelOffset
,
2579 PRUint8 aEndBevelSide
,
2580 nscoord aEndBevelOffset
)
2582 aContext
.SetColor (aBorderColor
);
2584 PRBool horizontal
= ((NS_SIDE_TOP
== aStartBevelSide
) || (NS_SIDE_BOTTOM
== aStartBevelSide
));
2585 nscoord twipsPerPixel
= NSIntPixelsToAppUnits(1, aAppUnitsPerCSSPixel
);
2586 PRUint8 ridgeGroove
= NS_STYLE_BORDER_STYLE_RIDGE
;
2588 if ((twipsPerPixel
>= aBorder
.width
) || (twipsPerPixel
>= aBorder
.height
) ||
2589 (NS_STYLE_BORDER_STYLE_DASHED
== aBorderStyle
) || (NS_STYLE_BORDER_STYLE_DOTTED
== aBorderStyle
)) {
2590 // no beveling for 1 pixel border, dash or dot
2591 aStartBevelOffset
= 0;
2592 aEndBevelOffset
= 0;
2595 gfxContext
*ctx
= aContext
.ThebesContext();
2596 gfxContext::AntialiasMode oldMode
= ctx
->CurrentAntialiasMode();
2597 ctx
->SetAntialiasMode(gfxContext::MODE_ALIASED
);
2599 switch (aBorderStyle
) {
2600 case NS_STYLE_BORDER_STYLE_NONE
:
2601 case NS_STYLE_BORDER_STYLE_HIDDEN
:
2602 //NS_ASSERTION(PR_FALSE, "style of none or hidden");
2604 case NS_STYLE_BORDER_STYLE_DOTTED
:
2605 case NS_STYLE_BORDER_STYLE_DASHED
:
2607 nscoord dashLength
= (NS_STYLE_BORDER_STYLE_DASHED
== aBorderStyle
) ? DASH_LENGTH
: DOT_LENGTH
;
2608 // make the dash length proportional to the border thickness
2609 dashLength
*= (horizontal
) ? aBorder
.height
: aBorder
.width
;
2610 // make the min dash length for the ends 1/2 the dash length
2611 nscoord minDashLength
= (NS_STYLE_BORDER_STYLE_DASHED
== aBorderStyle
)
2612 ? RoundFloatToPixel(((float)dashLength
) / 2.0f
, twipsPerPixel
) : dashLength
;
2613 minDashLength
= PR_MAX(minDashLength
, twipsPerPixel
);
2614 nscoord numDashSpaces
= 0;
2615 nscoord startDashLength
= minDashLength
;
2616 nscoord endDashLength
= minDashLength
;
2618 GetDashInfo(aBorder
.width
, dashLength
, twipsPerPixel
, numDashSpaces
, startDashLength
, endDashLength
);
2619 nsRect
rect(aBorder
.x
, aBorder
.y
, startDashLength
, aBorder
.height
);
2620 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
);
2621 for (PRInt32 spaceX
= 0; spaceX
< numDashSpaces
; spaceX
++) {
2622 rect
.x
+= rect
.width
+ dashLength
;
2623 rect
.width
= (spaceX
== (numDashSpaces
- 1)) ? endDashLength
: dashLength
;
2624 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
);
2628 GetDashInfo(aBorder
.height
, dashLength
, twipsPerPixel
, numDashSpaces
, startDashLength
, endDashLength
);
2629 nsRect
rect(aBorder
.x
, aBorder
.y
, aBorder
.width
, startDashLength
);
2630 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
);
2631 for (PRInt32 spaceY
= 0; spaceY
< numDashSpaces
; spaceY
++) {
2632 rect
.y
+= rect
.height
+ dashLength
;
2633 rect
.height
= (spaceY
== (numDashSpaces
- 1)) ? endDashLength
: dashLength
;
2634 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
);
2639 case NS_STYLE_BORDER_STYLE_GROOVE
:
2640 ridgeGroove
= NS_STYLE_BORDER_STYLE_GROOVE
; // and fall through to ridge
2641 case NS_STYLE_BORDER_STYLE_RIDGE
:
2642 if ((horizontal
&& (twipsPerPixel
>= aBorder
.height
)) ||
2643 (!horizontal
&& (twipsPerPixel
>= aBorder
.width
))) {
2644 // a one pixel border
2645 DrawSolidBorderSegment(aContext
, aBorder
, twipsPerPixel
, aStartBevelSide
, aStartBevelOffset
,
2646 aEndBevelSide
, aEndBevelOffset
);
2649 nscoord startBevel
= (aStartBevelOffset
> 0)
2650 ? RoundFloatToPixel(0.5f
* (float)aStartBevelOffset
, twipsPerPixel
, PR_TRUE
) : 0;
2651 nscoord endBevel
= (aEndBevelOffset
> 0)
2652 ? RoundFloatToPixel(0.5f
* (float)aEndBevelOffset
, twipsPerPixel
, PR_TRUE
) : 0;
2653 PRUint8 ridgeGrooveSide
= (horizontal
) ? NS_SIDE_TOP
: NS_SIDE_LEFT
;
2655 MakeBevelColor(ridgeGrooveSide
, ridgeGroove
, aBGColor
->mBackgroundColor
, aBorderColor
));
2656 nsRect
rect(aBorder
);
2658 if (horizontal
) { // top, bottom
2659 half
= RoundFloatToPixel(0.5f
* (float)aBorder
.height
, twipsPerPixel
);
2661 if (NS_SIDE_TOP
== aStartBevelSide
) {
2662 rect
.x
+= startBevel
;
2663 rect
.width
-= startBevel
;
2665 if (NS_SIDE_TOP
== aEndBevelSide
) {
2666 rect
.width
-= endBevel
;
2668 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
, aStartBevelSide
,
2669 startBevel
, aEndBevelSide
, endBevel
);
2671 else { // left, right
2672 half
= RoundFloatToPixel(0.5f
* (float)aBorder
.width
, twipsPerPixel
);
2674 if (NS_SIDE_LEFT
== aStartBevelSide
) {
2675 rect
.y
+= startBevel
;
2676 rect
.height
-= startBevel
;
2678 if (NS_SIDE_LEFT
== aEndBevelSide
) {
2679 rect
.height
-= endBevel
;
2681 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
, aStartBevelSide
,
2682 startBevel
, aEndBevelSide
, endBevel
);
2686 ridgeGrooveSide
= (NS_SIDE_TOP
== ridgeGrooveSide
) ? NS_SIDE_BOTTOM
: NS_SIDE_RIGHT
;
2688 MakeBevelColor(ridgeGrooveSide
, ridgeGroove
, aBGColor
->mBackgroundColor
, aBorderColor
));
2690 rect
.y
= rect
.y
+ half
;
2691 rect
.height
= aBorder
.height
- half
;
2692 if (NS_SIDE_BOTTOM
== aStartBevelSide
) {
2693 rect
.x
+= startBevel
;
2694 rect
.width
-= startBevel
;
2696 if (NS_SIDE_BOTTOM
== aEndBevelSide
) {
2697 rect
.width
-= endBevel
;
2699 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
, aStartBevelSide
,
2700 startBevel
, aEndBevelSide
, endBevel
);
2703 rect
.x
= rect
.x
+ half
;
2704 rect
.width
= aBorder
.width
- half
;
2705 if (NS_SIDE_RIGHT
== aStartBevelSide
) {
2706 rect
.y
+= aStartBevelOffset
- startBevel
;
2707 rect
.height
-= startBevel
;
2709 if (NS_SIDE_RIGHT
== aEndBevelSide
) {
2710 rect
.height
-= endBevel
;
2712 DrawSolidBorderSegment(aContext
, rect
, twipsPerPixel
, aStartBevelSide
,
2713 startBevel
, aEndBevelSide
, endBevel
);
2717 case NS_STYLE_BORDER_STYLE_DOUBLE
:
2718 if ((aBorder
.width
> 2) && (aBorder
.height
> 2)) {
2719 nscoord startBevel
= (aStartBevelOffset
> 0)
2720 ? RoundFloatToPixel(0.333333f
* (float)aStartBevelOffset
, twipsPerPixel
) : 0;
2721 nscoord endBevel
= (aEndBevelOffset
> 0)
2722 ? RoundFloatToPixel(0.333333f
* (float)aEndBevelOffset
, twipsPerPixel
) : 0;
2723 if (horizontal
) { // top, bottom
2724 nscoord thirdHeight
= RoundFloatToPixel(0.333333f
* (float)aBorder
.height
, twipsPerPixel
);
2726 // draw the top line or rect
2727 nsRect
topRect(aBorder
.x
, aBorder
.y
, aBorder
.width
, thirdHeight
);
2728 if (NS_SIDE_TOP
== aStartBevelSide
) {
2729 topRect
.x
+= aStartBevelOffset
- startBevel
;
2730 topRect
.width
-= aStartBevelOffset
- startBevel
;
2732 if (NS_SIDE_TOP
== aEndBevelSide
) {
2733 topRect
.width
-= aEndBevelOffset
- endBevel
;
2735 DrawSolidBorderSegment(aContext
, topRect
, twipsPerPixel
, aStartBevelSide
,
2736 startBevel
, aEndBevelSide
, endBevel
);
2738 // draw the botom line or rect
2739 nscoord heightOffset
= aBorder
.height
- thirdHeight
;
2740 nsRect
bottomRect(aBorder
.x
, aBorder
.y
+ heightOffset
, aBorder
.width
, aBorder
.height
- heightOffset
);
2741 if (NS_SIDE_BOTTOM
== aStartBevelSide
) {
2742 bottomRect
.x
+= aStartBevelOffset
- startBevel
;
2743 bottomRect
.width
-= aStartBevelOffset
- startBevel
;
2745 if (NS_SIDE_BOTTOM
== aEndBevelSide
) {
2746 bottomRect
.width
-= aEndBevelOffset
- endBevel
;
2748 DrawSolidBorderSegment(aContext
, bottomRect
, twipsPerPixel
, aStartBevelSide
,
2749 startBevel
, aEndBevelSide
, endBevel
);
2751 else { // left, right
2752 nscoord thirdWidth
= RoundFloatToPixel(0.333333f
* (float)aBorder
.width
, twipsPerPixel
);
2754 nsRect
leftRect(aBorder
.x
, aBorder
.y
, thirdWidth
, aBorder
.height
);
2755 if (NS_SIDE_LEFT
== aStartBevelSide
) {
2756 leftRect
.y
+= aStartBevelOffset
- startBevel
;
2757 leftRect
.height
-= aStartBevelOffset
- startBevel
;
2759 if (NS_SIDE_LEFT
== aEndBevelSide
) {
2760 leftRect
.height
-= aEndBevelOffset
- endBevel
;
2762 DrawSolidBorderSegment(aContext
, leftRect
, twipsPerPixel
, aStartBevelSide
,
2763 startBevel
, aEndBevelSide
, endBevel
);
2765 nscoord widthOffset
= aBorder
.width
- thirdWidth
;
2766 nsRect
rightRect(aBorder
.x
+ widthOffset
, aBorder
.y
, aBorder
.width
- widthOffset
, aBorder
.height
);
2767 if (NS_SIDE_RIGHT
== aStartBevelSide
) {
2768 rightRect
.y
+= aStartBevelOffset
- startBevel
;
2769 rightRect
.height
-= aStartBevelOffset
- startBevel
;
2771 if (NS_SIDE_RIGHT
== aEndBevelSide
) {
2772 rightRect
.height
-= aEndBevelOffset
- endBevel
;
2774 DrawSolidBorderSegment(aContext
, rightRect
, twipsPerPixel
, aStartBevelSide
,
2775 startBevel
, aEndBevelSide
, endBevel
);
2779 // else fall through to solid
2780 case NS_STYLE_BORDER_STYLE_SOLID
:
2781 DrawSolidBorderSegment(aContext
, aBorder
, twipsPerPixel
, aStartBevelSide
,
2782 aStartBevelOffset
, aEndBevelSide
, aEndBevelOffset
);
2784 case NS_STYLE_BORDER_STYLE_OUTSET
:
2785 case NS_STYLE_BORDER_STYLE_INSET
:
2786 NS_ASSERTION(PR_FALSE
, "inset, outset should have been converted to groove, ridge");
2788 case NS_STYLE_BORDER_STYLE_AUTO
:
2789 NS_ASSERTION(PR_FALSE
, "Unexpected 'auto' table border");
2793 ctx
->SetAntialiasMode(oldMode
);
2796 // End table border-collapsing section
2799 nsCSSRendering::PaintDecorationLine(gfxContext
* aGfxContext
,
2800 const nscolor aColor
,
2801 const gfxPoint
& aPt
,
2802 const gfxSize
& aLineSize
,
2803 const gfxFloat aAscent
,
2804 const gfxFloat aOffset
,
2805 const PRUint8 aDecoration
,
2806 const PRUint8 aStyle
)
2809 GetTextDecorationRectInternal(aPt
, aLineSize
, aAscent
, aOffset
,
2810 aDecoration
, aStyle
);
2814 if (aDecoration
!= NS_STYLE_TEXT_DECORATION_UNDERLINE
&&
2815 aDecoration
!= NS_STYLE_TEXT_DECORATION_OVERLINE
&&
2816 aDecoration
!= NS_STYLE_TEXT_DECORATION_LINE_THROUGH
)
2818 NS_ERROR("Invalid decoration value!");
2822 gfxFloat lineHeight
= PR_MAX(NS_round(aLineSize
.height
), 1.0);
2823 PRBool contextIsSaved
= PR_FALSE
;
2825 gfxFloat oldLineWidth
;
2826 nsRefPtr
<gfxPattern
> oldPattern
;
2829 case NS_STYLE_BORDER_STYLE_SOLID
:
2830 case NS_STYLE_BORDER_STYLE_DOUBLE
:
2831 oldLineWidth
= aGfxContext
->CurrentLineWidth();
2832 oldPattern
= aGfxContext
->GetPattern();
2834 case NS_STYLE_BORDER_STYLE_DASHED
: {
2835 aGfxContext
->Save();
2836 contextIsSaved
= PR_TRUE
;
2837 gfxFloat dashWidth
= lineHeight
* DOT_LENGTH
* DASH_LENGTH
;
2838 gfxFloat dash
[2] = { dashWidth
, dashWidth
};
2839 aGfxContext
->SetLineCap(gfxContext::LINE_CAP_BUTT
);
2840 aGfxContext
->SetDash(dash
, 2, 0.0);
2843 case NS_STYLE_BORDER_STYLE_DOTTED
: {
2844 aGfxContext
->Save();
2845 contextIsSaved
= PR_TRUE
;
2846 gfxFloat dashWidth
= lineHeight
* DOT_LENGTH
;
2848 if (lineHeight
> 2.0) {
2850 dash
[1] = dashWidth
* 2.0;
2851 aGfxContext
->SetLineCap(gfxContext::LINE_CAP_ROUND
);
2853 dash
[0] = dashWidth
;
2854 dash
[1] = dashWidth
;
2856 aGfxContext
->SetDash(dash
, 2, 0.0);
2860 NS_ERROR("Invalid style value!");
2864 // The y position should be set to the middle of the line.
2865 rect
.pos
.y
+= lineHeight
/ 2;
2867 aGfxContext
->SetColor(gfxRGBA(aColor
));
2868 aGfxContext
->SetLineWidth(lineHeight
);
2870 case NS_STYLE_BORDER_STYLE_SOLID
:
2871 aGfxContext
->NewPath();
2872 aGfxContext
->MoveTo(rect
.TopLeft());
2873 aGfxContext
->LineTo(rect
.TopRight());
2874 aGfxContext
->Stroke();
2876 case NS_STYLE_BORDER_STYLE_DOUBLE
:
2877 aGfxContext
->NewPath();
2878 aGfxContext
->MoveTo(rect
.TopLeft());
2879 aGfxContext
->LineTo(rect
.TopRight());
2880 rect
.size
.height
-= lineHeight
;
2881 aGfxContext
->MoveTo(rect
.BottomLeft());
2882 aGfxContext
->LineTo(rect
.BottomRight());
2883 aGfxContext
->Stroke();
2885 case NS_STYLE_BORDER_STYLE_DOTTED
:
2886 case NS_STYLE_BORDER_STYLE_DASHED
:
2887 aGfxContext
->NewPath();
2888 aGfxContext
->MoveTo(rect
.TopLeft());
2889 aGfxContext
->LineTo(rect
.TopRight());
2890 aGfxContext
->Stroke();
2893 NS_ERROR("Invalid style value!");
2897 if (contextIsSaved
) {
2898 aGfxContext
->Restore();
2900 aGfxContext
->SetPattern(oldPattern
);
2901 aGfxContext
->SetLineWidth(oldLineWidth
);
2906 nsCSSRendering::GetTextDecorationRect(nsPresContext
* aPresContext
,
2907 const gfxSize
& aLineSize
,
2908 const gfxFloat aAscent
,
2909 const gfxFloat aOffset
,
2910 const PRUint8 aDecoration
,
2911 const PRUint8 aStyle
)
2913 NS_ASSERTION(aPresContext
, "aPresContext is null");
2916 GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize
, aAscent
, aOffset
,
2917 aDecoration
, aStyle
);
2918 // The rect values are already rounded to nearest device pixels.
2920 r
.x
= aPresContext
->GfxUnitsToAppUnits(rect
.X());
2921 r
.y
= aPresContext
->GfxUnitsToAppUnits(rect
.Y());
2922 r
.width
= aPresContext
->GfxUnitsToAppUnits(rect
.Width());
2923 r
.height
= aPresContext
->GfxUnitsToAppUnits(rect
.Height());
2928 nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint
& aPt
,
2929 const gfxSize
& aLineSize
,
2930 const gfxFloat aAscent
,
2931 const gfxFloat aOffset
,
2932 const PRUint8 aDecoration
,
2933 const PRUint8 aStyle
)
2936 r
.pos
.x
= NS_floor(aPt
.x
+ 0.5);
2937 r
.size
.width
= NS_round(aLineSize
.width
);
2939 gfxFloat basesize
= NS_round(aLineSize
.height
);
2940 basesize
= PR_MAX(basesize
, 1.0);
2941 r
.size
.height
= basesize
;
2942 if (aStyle
== NS_STYLE_BORDER_STYLE_DOUBLE
) {
2943 gfxFloat gap
= NS_round(basesize
/ 2.0);
2944 gap
= PR_MAX(gap
, 1.0);
2945 r
.size
.height
= basesize
* 2.0 + gap
;
2947 r
.size
.height
= basesize
;
2950 gfxFloat baseline
= NS_floor(aPt
.y
+ aAscent
+ 0.5);
2951 gfxFloat offset
= 0;
2952 switch (aDecoration
) {
2953 case NS_STYLE_TEXT_DECORATION_UNDERLINE
:
2956 case NS_STYLE_TEXT_DECORATION_OVERLINE
:
2957 offset
= aOffset
- basesize
+ r
.Height();
2959 case NS_STYLE_TEXT_DECORATION_LINE_THROUGH
: {
2960 gfxFloat extra
= NS_floor(r
.Height() / 2.0 + 0.5);
2961 extra
= PR_MAX(extra
, basesize
);
2962 offset
= aOffset
- basesize
+ extra
;
2966 NS_ERROR("Invalid decoration value!");
2968 r
.pos
.y
= baseline
- NS_floor(offset
+ 0.5);
2976 nsContextBoxBlur::BoxBlurHorizontal(unsigned char* aInput
,
2977 unsigned char* aOutput
,
2979 PRUint32 aRightLobe
)
2981 // Box blur involves looking at one pixel, and setting its value to the average of
2982 // its neighbouring pixels. leftLobe is how many pixels to the left to include
2983 // in the average, rightLobe is to the right.
2984 // boxSize is how many pixels total will be averaged when looking at each pixel.
2985 PRUint32 boxSize
= aLeftLobe
+ aRightLobe
+ 1;
2987 long stride
= mImageSurface
->Stride();
2988 PRUint32 rows
= mRect
.Height();
2990 for (PRUint32 y
= 0; y
< rows
; y
++) {
2991 PRUint32 alphaSum
= 0;
2992 for (PRUint32 i
= 0; i
< boxSize
; i
++) {
2993 PRInt32 pos
= i
- aLeftLobe
;
2994 pos
= PR_MAX(pos
, 0);
2995 pos
= PR_MIN(pos
, stride
- 1);
2996 alphaSum
+= aInput
[stride
* y
+ pos
];
2998 for (PRInt32 x
= 0; x
< stride
; x
++) {
2999 PRInt32 tmp
= x
- aLeftLobe
;
3000 PRInt32 last
= PR_MAX(tmp
, 0);
3001 PRInt32 next
= PR_MIN(tmp
+ boxSize
, stride
- 1);
3003 aOutput
[stride
* y
+ x
] = alphaSum
/boxSize
;
3005 alphaSum
+= aInput
[stride
* y
+ next
] -
3006 aInput
[stride
* y
+ last
];
3012 nsContextBoxBlur::BoxBlurVertical(unsigned char* aInput
,
3013 unsigned char* aOutput
,
3015 PRUint32 aBottomLobe
)
3017 PRUint32 boxSize
= aTopLobe
+ aBottomLobe
+ 1;
3019 long stride
= mImageSurface
->Stride();
3020 PRUint32 rows
= mRect
.Height();
3022 for (PRInt32 x
= 0; x
< stride
; x
++) {
3023 PRUint32 alphaSum
= 0;
3024 for (PRUint32 i
= 0; i
< boxSize
; i
++) {
3025 PRInt32 pos
= i
- aTopLobe
;
3026 pos
= PR_MAX(pos
, 0);
3027 pos
= PR_MIN(pos
, rows
- 1);
3028 alphaSum
+= aInput
[stride
* pos
+ x
];
3030 for (PRUint32 y
= 0; y
< rows
; y
++) {
3031 PRInt32 tmp
= y
- aTopLobe
;
3032 PRInt32 last
= PR_MAX(tmp
, 0);
3033 PRInt32 next
= PR_MIN(tmp
+ boxSize
, rows
- 1);
3035 aOutput
[stride
* y
+ x
] = alphaSum
/boxSize
;
3037 alphaSum
+= aInput
[stride
* next
+ x
] -
3038 aInput
[stride
* last
+ x
];
3044 nsContextBoxBlur::Init(const gfxRect
& aRect
, nscoord aBlurRadius
,
3045 PRInt32 aAppUnitsPerDevPixel
,
3046 gfxContext
* aDestinationCtx
)
3048 mBlurRadius
= aBlurRadius
/ aAppUnitsPerDevPixel
;
3050 if (mBlurRadius
<= 0) {
3051 mContext
= aDestinationCtx
;
3055 // Convert from app units to device pixels
3057 mRect
.Outset(aBlurRadius
);
3058 mRect
.ScaleInverse(aAppUnitsPerDevPixel
);
3061 if (mRect
.IsEmpty()) {
3063 mContext
= aDestinationCtx
;
3067 mDestinationCtx
= aDestinationCtx
;
3069 // Make an alpha-only surface to draw on. We will play with the data after everything is drawn
3070 // to create a blur effect.
3071 mImageSurface
= new gfxImageSurface(gfxIntSize(mRect
.Width(), mRect
.Height()),
3072 gfxASurface::ImageFormatA8
);
3073 if (!mImageSurface
|| mImageSurface
->CairoStatus())
3076 // Use a device offset so callers don't need to worry about translating coordinates,
3077 // they can draw as if this was part of the destination context at the coordinates
3079 mImageSurface
->SetDeviceOffset(-mRect
.TopLeft());
3081 mContext
= new gfxContext(mImageSurface
);
3086 nsContextBoxBlur::DoPaint()
3088 if (mBlurRadius
<= 0)
3091 unsigned char* boxData
= mImageSurface
->Data();
3093 // A blur radius of 1 achieves nothing (1/2 = 0 in int terms),
3094 // but we still want a blur!
3095 mBlurRadius
= PR_MAX(mBlurRadius
, 2);
3097 nsTArray
<unsigned char> tempAlphaDataBuf
;
3098 if (!tempAlphaDataBuf
.SetLength(mImageSurface
->GetDataSize()))
3101 // Here we do like what the SVG gaussian blur filter does in calculating
3103 if (mBlurRadius
& 1) {
3104 // blur radius is odd
3105 BoxBlurHorizontal(boxData
, tempAlphaDataBuf
.Elements(), mBlurRadius
/2, mBlurRadius
/2);
3106 BoxBlurHorizontal(tempAlphaDataBuf
.Elements(), boxData
, mBlurRadius
/2, mBlurRadius
/2);
3107 BoxBlurHorizontal(boxData
, tempAlphaDataBuf
.Elements(), mBlurRadius
/2, mBlurRadius
/2);
3108 BoxBlurVertical(tempAlphaDataBuf
.Elements(), boxData
, mBlurRadius
/2, mBlurRadius
/2);
3109 BoxBlurVertical(boxData
, tempAlphaDataBuf
.Elements(), mBlurRadius
/2, mBlurRadius
/2);
3110 BoxBlurVertical(tempAlphaDataBuf
.Elements(), boxData
, mBlurRadius
/2, mBlurRadius
/2);
3112 // blur radius is even
3113 BoxBlurHorizontal(boxData
, tempAlphaDataBuf
.Elements(), mBlurRadius
/2, mBlurRadius
/2 - 1);
3114 BoxBlurHorizontal(tempAlphaDataBuf
.Elements(), boxData
, mBlurRadius
/2 - 1, mBlurRadius
/2);
3115 BoxBlurHorizontal(boxData
, tempAlphaDataBuf
.Elements(), mBlurRadius
/2, mBlurRadius
/2);
3116 BoxBlurVertical(tempAlphaDataBuf
.Elements(), boxData
, mBlurRadius
/2, mBlurRadius
/2 - 1);
3117 BoxBlurVertical(boxData
, tempAlphaDataBuf
.Elements(), mBlurRadius
/2 - 1, mBlurRadius
/2);
3118 BoxBlurVertical(tempAlphaDataBuf
.Elements(), boxData
, mBlurRadius
/2, mBlurRadius
/2);
3121 mDestinationCtx
->Mask(mImageSurface
);
3125 nsContextBoxBlur::GetContext()