Bug 447044 ? First -moz-box-shadow not drawn when second one has large blur value...
[wine-gecko.git] / layout / base / nsCSSRendering.cpp
blob3cdb5f74b306f346c48ae013a8c198958c25878d
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
14 * License.
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.
23 * Contributor(s):
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"
49 #include "nsIImage.h"
50 #include "nsIFrame.h"
51 #include "nsPoint.h"
52 #include "nsRect.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"
69 #include "nsITheme.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()
100 void Reset()
102 mBoundingBox.SetRect(0,0,0,0);
103 mContinuationPoint = mLineContinuationPoint = mUnbrokenWidth = 0;
104 mFrame = mBlockFrame = nsnull;
107 nsRect GetContinuousRect(nsIFrame* aFrame)
109 SetFrame(aFrame);
111 nscoord x;
112 if (mBidiEnabled) {
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();
143 if (isRtlBlock) {
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;
151 } else {
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)
163 SetFrame(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);
174 return boundingBox;
177 protected:
178 nsIFrame* mFrame;
179 nscoord mContinuationPoint;
180 nscoord mUnbrokenWidth;
181 nsRect mBoundingBox;
183 PRBool mBidiEnabled;
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.
195 Reset();
196 Init(aFrame);
197 return;
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.
205 if (mBidiEnabled &&
206 (aFrame->GetPrevInFlow() || !AreOnSameLine(mFrame, aFrame))) {
207 mLineContinuationPoint = mContinuationPoint;
210 mFrame = aFrame;
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
228 // unbroken width.
229 inlineFrame = aFrame;
230 while (inlineFrame) {
231 nsRect rect = inlineFrame->GetRect();
232 mUnbrokenWidth += rect.width;
233 mBoundingBox.UnionRect(mBoundingBox, rect);
234 inlineFrame = inlineFrame->GetNextContinuation();
237 mFrame = aFrame;
239 mBidiEnabled = aFrame->PresContext()->BidiEnabled();
240 if (mBidiEnabled) {
241 // Find the containing block frame
242 nsIFrame* frame = aFrame;
243 nsresult rv = NS_ERROR_FAILURE;
244 while (frame &&
245 frame->IsFrameOfType(nsIFrame::eLineParticipant) &&
246 NS_FAILED(rv)) {
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();
272 if (!gInlineBGData)
273 return NS_ERROR_OUT_OF_MEMORY;
275 return NS_OK;
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,
289 nsRect* aGap)
291 if (nsnull == aGap) {
292 aContext.DrawLine(aX1, aY1, aX2, aY2);
293 } else {
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);
305 } else {
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[],
315 PRInt32 aNumPoints,
316 nsRect* aGap)
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
326 nsPoint points[4];
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];
335 points[j] = swap;
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)) {
348 nsPoint leftRect[4];
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);
363 } else {
364 aContext.FillPolygon(aPoints, aNumPoints);
370 * Make a bevel color
372 nscolor nsCSSRendering::MakeBevelColor(PRIntn whichSide, PRUint8 style,
373 nscolor aBackgroundColor,
374 nscolor aBorderColor)
377 nscolor colors[2];
378 nscolor theColor;
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
387 switch (whichSide) {
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;
395 switch (whichSide) {
396 case NS_SIDE_BOTTOM:
397 theColor = colors[1];
398 break;
399 case NS_SIDE_RIGHT:
400 theColor = colors[1];
401 break;
402 case NS_SIDE_TOP:
403 theColor = colors[0];
404 break;
405 case NS_SIDE_LEFT:
406 default:
407 theColor = colors[0];
408 break;
410 return theColor;
413 nscolor
414 nsCSSRendering::TransformColor(nscolor aMapColor,PRBool aNoBackGround)
416 PRUint16 hue,sat,value;
417 nscolor newcolor;
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.
429 if (value > sat) {
430 value = sat;
431 // convert this color back into the RGB color space.
432 NS_HSV2RGB(newcolor,hue,sat,value);
435 return newcolor;
438 //----------------------------------------------------------------------
439 // Thebes Border Rendering Code Start
441 // helper function to convert a nsRect to a gfxRect
442 static 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.
459 static void
460 ComputePixelRadii(const nscoord *aTwipsRadii,
461 const nsRect& outerRect,
462 const nsMargin& borderMargin,
463 PRIntn skipSides,
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) {
471 border.top = 0;
472 twipsRadii[C_TL] = 0;
473 twipsRadii[C_TR] = 0;
476 if (skipSides & SIDE_BIT_RIGHT) {
477 border.right = 0;
478 twipsRadii[C_TR] = 0;
479 twipsRadii[C_BR] = 0;
482 if (skipSides & SIDE_BIT_BOTTOM) {
483 border.bottom = 0;
484 twipsRadii[C_BR] = 0;
485 twipsRadii[C_BL] = 0;
488 if (skipSides & SIDE_BIT_LEFT) {
489 border.left = 0;
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);
503 gfxFloat f[4];
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]);
515 void
516 nsCSSRendering::PaintBorder(nsPresContext* aPresContext,
517 nsIRenderingContext& aRenderingContext,
518 nsIFrame* aForFrame,
519 const nsRect& aDirtyRect,
520 const nsRect& aBorderArea,
521 const nsStyleBorder& aBorderStyle,
522 nsStyleContext* aStyleContext,
523 PRIntn aSkipSides)
525 nsMargin border;
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);
544 return;
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)) {
558 // Empty border area
559 return;
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 };
589 // convert the radii
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]);
604 if (transparent)
605 borderColors[i] = 0x0;
606 else if (foreground)
607 borderColors[i] = ourColor->mColor;
610 SF(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
612 // start drawing
613 gfxContext *ctx = aRenderingContext.ThebesContext();
615 ctx->Save();
617 #if 0
618 // this will draw a transparent red backround underneath the oRect area
619 ctx->Save();
620 ctx->Rectangle(oRect);
621 ctx->SetColor(gfxRGBA(1.0, 0.0, 0.0, 0.5));
622 ctx->Fill();
623 ctx->Restore();
624 #endif
626 //SF ("borderRadii: %f %f %f %f\n", borderRadii[0], borderRadii[1], borderRadii[2], borderRadii[3]);
628 nsCSSBorderRenderer br(twipsPerPixel,
629 ctx,
630 oRect,
631 borderStyles,
632 borderWidths,
633 borderRadii,
634 borderColors,
635 compositeColors,
636 aSkipSides,
637 bgColor->mBackgroundColor);
638 br.DrawBorders();
640 ctx->Restore();
642 SN();
645 void
646 nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
647 nsIRenderingContext& aRenderingContext,
648 nsIFrame* aForFrame,
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();
660 nscoord width;
661 aOutlineStyle.GetOutlineWidth(width);
663 if (width == 0) {
664 // Empty outline
665 return;
668 const nsStyleBackground* bgColor = nsCSSRendering::FindNonTransparentBackground
669 (aStyleContext, PR_FALSE);
671 // get the radius for our outline
672 GetBorderRadiusTwips(aOutlineStyle.mOutlineRadius, aBorderArea.width, twipsRadii);
674 nscoord offset;
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;
684 do {
685 nsIAtom *pseudoType = frameForArea->GetStyleContext()->GetPseudoType();
686 if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock &&
687 pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock)
688 break;
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);
693 nsRect overflowArea;
694 if (frameForArea == aForFrame) {
695 overflowArea = aForFrame->GetOverflowRect();
696 } else {
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
701 // the border.
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);
715 } else {
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)) {
732 return;
735 // Get our conversion values
736 nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
738 // get the outer rectangles
739 gfxRect oRect(RectToGfxRect(outerRect, twipsPerPixel));
741 // convert the radii
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,
748 outlineStyle,
749 outlineStyle,
750 outlineStyle };
752 nscolor outlineColor;
753 // PR_FALSE means use the initial color; PR_TRUE means a color was
754 // set.
755 if (!aOutlineStyle.GetOutlineColor(outlineColor))
756 outlineColor = ourColor->mColor;
757 nscolor outlineColors[4] = { outlineColor,
758 outlineColor,
759 outlineColor,
760 outlineColor };
762 // convert the border widths
763 gfxFloat outlineWidths[4] = { width / twipsPerPixel,
764 width / twipsPerPixel,
765 width / twipsPerPixel,
766 width / twipsPerPixel };
768 // start drawing
769 gfxContext *ctx = aRenderingContext.ThebesContext();
771 ctx->Save();
773 nsCSSBorderRenderer br(twipsPerPixel,
774 ctx,
775 oRect,
776 outlineStyles,
777 outlineWidths,
778 outlineRadii,
779 outlineColors,
780 nsnull, 0,
781 bgColor->mBackgroundColor);
782 br.DrawBorders();
784 ctx->Restore();
786 SN();
789 void
790 nsCSSRendering::PaintFocus(nsPresContext* aPresContext,
791 nsIRenderingContext& aRenderingContext,
792 const nsRect& aFocusRect,
793 nscolor aColor)
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,
805 &focusRadii);
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();
820 ctx->Save();
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,
829 ctx,
830 focusRect,
831 focusStyles,
832 focusWidths,
833 focusRadii,
834 focusColors,
835 nsnull, 0,
836 NS_RGB(255, 0, 0));
837 br.DrawBorders();
839 ctx->Restore();
841 SN();
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
852 // be placed
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.
868 static void
869 ComputeBackgroundAnchorPoint(const nsStyleBackground& aColor,
870 const nsRect& aOriginBounds,
871 const nsRect& aClipBounds,
872 nscoord aTileWidth, nscoord aTileHeight,
873 nsPoint& aResult)
875 nscoord x;
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;
885 else {
886 x = 0;
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
893 // rendered.
894 if (x < 0) {
895 x = -x;
896 if (x < 0) {
897 // Some joker gave us max-negative-integer.
898 x = 0;
900 x %= aTileWidth;
901 x = -x;
903 else if (x != 0) {
904 x %= aTileWidth;
905 if (x > 0) {
906 x = x - aTileWidth;
910 NS_POSTCONDITION((x >= -(aTileWidth - 1)) && (x <= 0), "bad computed anchor value");
912 aResult.x = x;
914 nscoord y;
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;
924 else {
925 y = 0;
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
932 // rendered.
933 if (y < 0) {
934 y = -y;
935 if (y < 0) {
936 // Some joker gave us max-negative-integer.
937 y = 0;
939 y %= aTileHeight;
940 y = -y;
942 else if (y != 0) {
943 y %= aTileHeight;
944 if (y > 0) {
945 y = y - aTileHeight;
949 NS_POSTCONDITION((y >= -(aTileHeight - 1)) && (y <= 0), "bad computed anchor value");
951 aResult.y = y;
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();
965 if (!context) {
966 context = aContext;
969 while (context) {
970 result = context->GetStyleBackground();
971 if (0 == (result->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT))
972 break;
974 context = context->GetParent();
976 return result;
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
986 * entire canvas.
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
1008 * canvas.
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.
1015 inline PRBool
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;
1026 inline PRBool
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);
1041 if (htmlDoc) {
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.
1052 if (bodyContent) {
1053 nsIFrame *bodyFrame = aForFrame->PresContext()->GetPresShell()->
1054 GetPrimaryFrameFor(bodyContent);
1055 if (bodyFrame)
1056 result = bodyFrame->GetStyleBackground();
1061 *aBackground = result;
1062 } else {
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
1065 // being loaded.
1066 *aBackground = aForFrame->GetStyleBackground();
1069 return PR_TRUE;
1072 inline PRBool
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.
1078 return PR_FALSE;
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);
1098 if (!htmlDoc)
1099 return PR_TRUE;
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)
1109 return PR_TRUE;
1111 const nsStyleBackground* htmlBG = aRootElementFrame->GetStyleBackground();
1112 return !htmlBG->IsTransparent();
1115 PRBool
1116 nsCSSRendering::FindBackground(nsPresContext* aPresContext,
1117 nsIFrame* aForFrame,
1118 const nsStyleBackground** aBackground,
1119 PRBool* aIsCanvas)
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);
1130 void
1131 nsCSSRendering::DidPaint()
1133 gInlineBGData->Reset();
1136 /* static */ PRBool
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++) {
1151 aTwipsRadii[i] = 0;
1152 float percent;
1154 switch (bordStyleRadius[i].GetUnit()) {
1155 case eStyleUnit_Percent:
1156 percent = bordStyleRadius[i].GetPercentValue();
1157 aTwipsRadii[i] = (nscoord)(percent * aFrameWidth);
1158 break;
1160 case eStyleUnit_Coord:
1161 aTwipsRadii[i] = bordStyleRadius[i].GetCoordValue();
1162 break;
1164 default:
1165 break;
1168 if (aTwipsRadii[i])
1169 result = PR_TRUE;
1171 return result;
1174 void
1175 nsCSSRendering::PaintBoxShadow(nsPresContext* aPresContext,
1176 nsIRenderingContext& aRenderingContext,
1177 nsIFrame* aForFrame,
1178 const nsPoint& aForFramePt)
1180 nsMargin borderValues;
1181 PRIntn sidesToSkip;
1182 nsRect frameRect;
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);
1223 if (!shadowContext)
1224 continue;
1226 // Set the shadow color; if not specified, use the foreground color
1227 nscolor shadowColor;
1228 if (shadowItem->mHasColor)
1229 shadowColor = shadowItem->mColor;
1230 else
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
1237 // the frame
1238 renderContext->NewPath();
1239 renderContext->Rectangle(shadowRectPlusBlur);
1240 if (hasBorderRadius)
1241 renderContext->RoundedRectangle(frameGfxRect, borderRadii);
1242 else
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);
1255 else
1256 shadowContext->Rectangle(shadowRect);
1257 shadowContext->Fill();
1259 blurringArea.DoPaint();
1260 renderContext->Restore();
1264 void
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");
1278 PRBool isCanvas;
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) {
1288 return;
1291 nsIContent* content = aForFrame->GetContent();
1292 if (!content || content->GetParent()) {
1293 return;
1296 color = aForFrame->GetStyleBackground();
1298 if (!isCanvas) {
1299 PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
1300 aDirtyRect, aBorderArea, *color, aBorder,
1301 aPadding, aUsePrintSettings, aBGClipRect);
1302 return;
1305 nsStyleBackground canvasColor(*color);
1307 nsIViewManager* vm = aPresContext->GetViewManager();
1309 if (canvasColor.mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT) {
1310 nsIView* rootView;
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
1342 // on it elsewhere.
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
1353 // on it elsewhere.
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.
1361 static nscoord
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.
1373 static nscoord
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;
1381 static void
1382 PixelSnapRectangle(gfxContext* aContext, nsIDeviceContext *aDC, nsRect& aRect)
1384 gfxRect tmpRect;
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;
1398 static void
1399 PixelSnapPoint(gfxContext* aContext, nsIDeviceContext *aDC, nsPoint& aPoint)
1401 gfxRect tmpRect;
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);
1413 static PRBool
1414 IsSolidBorderEdge(const nsStyleBorder& aBorder, PRUint32 aSide)
1416 if (aBorder.GetActualBorder().side(aSide) == 0)
1417 return PR_TRUE;
1418 if (aBorder.GetBorderStyle(aSide) != NS_STYLE_BORDER_STYLE_SOLID)
1419 return PR_FALSE;
1421 nscolor color;
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.
1431 static PRBool
1432 IsSolidBorder(const nsStyleBorder& aBorder)
1434 if (nsLayoutUtils::HasNonZeroSide(aBorder.mBorderRadius) || aBorder.mBorderColors)
1435 return PR_FALSE;
1436 for (PRUint32 i = 0; i < 4; ++i) {
1437 if (!IsSolidBorderEdge(aBorder, i))
1438 return PR_FALSE;
1440 return PR_TRUE;
1443 void
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)) {
1472 nsRect dirty;
1473 dirty.IntersectRect(aDirtyRect, aBorderArea);
1474 theme->DrawWidgetBackground(&aRenderingContext, aForFrame,
1475 displayData->mAppearance, aBorderArea, dirty);
1476 return;
1480 nsRect bgClipArea;
1481 if (aBGClipRect) {
1482 bgClipArea = *aBGClipRect;
1484 else {
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
1507 nsRect dirtyRect;
1508 if (!dirtyRect.IntersectRect(bgClipArea, aDirtyRect)) {
1509 // Nothing to paint
1510 return;
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);
1517 return;
1520 // We have a background image
1522 // Lookup the image
1523 imgIRequest *req = aPresContext->LoadImage(aColor.mBackgroundImage,
1524 aForFrame);
1526 PRUint32 status = imgIRequest::STATUS_ERROR;
1527 if (req)
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);
1533 return;
1536 nsCOMPtr<imgIContainer> image;
1537 req->GetImage(getter_AddRefs(image));
1539 nsSize imageSize;
1540 image->GetWidth(&imageSize.width);
1541 image->GetHeight(&imageSize.height);
1543 imageSize.width = nsPresContext::CSSPixelsToAppUnits(imageSize.width);
1544 imageSize.height = nsPresContext::CSSPixelsToAppUnits(imageSize.height);
1546 req = nsnull;
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;
1556 break;
1557 case NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX:
1558 bgOriginArea = gInlineBGData->GetBoundingRect(aForFrame) +
1559 aBorderArea.TopLeft();
1560 break;
1561 default:
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();
1567 break;
1570 else {
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;
1602 switch (repeat) {
1603 case NS_STYLE_BG_REPEAT_X:
1604 break;
1605 case NS_STYLE_BG_REPEAT_Y:
1606 break;
1607 case NS_STYLE_BG_REPEAT_XY:
1608 if (needBackgroundColor) {
1609 // If the image is completely opaque, we do not need to paint the
1610 // background color
1611 nsCOMPtr<gfxIImageFrame> gfxImgFrame;
1612 image->GetCurrentFrame(getter_AddRefs(gfxImgFrame));
1613 if (gfxImgFrame) {
1614 gfxImgFrame->GetNeedsBackground(&needBackgroundColor);
1616 /* check for tiling of a image where frame smaller than container */
1617 nsSize iSize;
1618 image->GetWidth(&iSize.width);
1619 image->GetHeight(&iSize.height);
1620 nsRect iframeRect;
1621 gfxImgFrame->GetRect(iframeRect);
1622 if (iSize.width != iframeRect.width ||
1623 iSize.height != iframeRect.height) {
1624 needBackgroundColor = PR_TRUE;
1628 break;
1629 case NS_STYLE_BG_REPEAT_OFF:
1630 default:
1631 NS_ASSERTION(repeat == NS_STYLE_BG_REPEAT_OFF, "unknown background-repeat value");
1632 break;
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
1643 return;
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
1655 nsPoint anchor;
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()) {
1669 pageContentFrame =
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
1693 // coordinates.
1694 anchor -= aForFrame->GetOffsetTo(topFrame);
1695 } else {
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);
1717 } else {
1718 ComputeBackgroundAnchorPoint(aColor, bgOriginArea, bgClipArea, tileWidth, tileHeight, anchor);
1720 } else {
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;
1739 ctx->Save();
1741 nscoord appUnitsPerPixel = aPresContext->DevPixelsToAppUnits(1);
1743 ctx->NewPath();
1744 ctx->Rectangle(RectToGfxRect(dirtyRect, appUnitsPerPixel), PR_TRUE);
1745 ctx->Clip();
1747 nscoord borderRadii[4];
1748 PRBool haveRadius = GetBorderRadiusTwips(aBorder.mBorderRadius, aForFrame->GetSize().width, borderRadii);
1750 if (haveRadius) {
1751 gfxCornerSizes radii;
1752 ComputePixelRadii(borderRadii, bgClipArea, aBorder.GetActualBorder(),
1753 aForFrame ? aForFrame->GetSkipSides() : 0,
1754 appUnitsPerPixel, &radii);
1756 gfxRect oRect(RectToGfxRect(bgClipArea, appUnitsPerPixel));
1757 oRect.Round();
1758 oRect.Condition();
1760 ctx->NewPath();
1761 ctx->RoundedRectangle(oRect, radii);
1762 ctx->Clip();
1765 // Compute the x and y starting points and limits for tiling
1767 /* An Overview Of The Following Logic
1769 A........ . . . . . . . . . . . . . .
1770 : +---:-------.-------.-------.---- /|\
1771 : | : . . . | nh
1772 :.......: . . . x . . . . . . . . . . \|/
1773 . | . . . .
1774 . | . . ########### .
1775 . . . . . . . . . .#. . . . .#. . . .
1776 . | . . ########### . /|\
1777 . | . . . . | h
1778 . . | . . . . . . . . . . . . . z . . \|/
1779 . | . . . .
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
1801 the dirtyRect area.
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)
1811 Therefore,
1813 x0 = bgClipArea.x + anchor.x + n * tileWidth;
1815 ...where n is an integer greater or equal to 0 fitting:
1817 n * tileWidth <=
1818 dirtyRect.x - (bgClipArea.x + anchor.x) <=
1819 (n+1) * tileWidth
1821 ...i.e.,
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) *
1833 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 <=
1843 m * tileWidth
1845 ...i.e.,
1847 m - 1 < (dirtyRect.x + dirtyRect.width - x0) / tileWidth <= m
1849 ...which, treating the division as an integer divide, and making it
1850 round up, gives:
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
1861 viewport, above.
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);
1875 tileRect.x = x0;
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);
1884 tileRect.y = y0;
1885 tileRect.height = y1 - y0;
1888 // Take the intersection again to paint only the required area.
1889 nsRect absTileRect = tileRect + borderAreaOriginSnapped;
1891 nsRect drawRect;
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);
1914 } else {
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);
1923 ctx->Restore();
1927 void
1928 nsCSSRendering::DrawBorderImage(nsPresContext* aPresContext,
1929 nsIRenderingContext& aRenderingContext,
1930 nsIFrame* aForFrame,
1931 const nsRect& aBorderArea,
1932 const nsStyleBorder& aBorderStyle)
1934 float percent;
1935 nsStyleCoord borderImageSplit[4];
1936 PRInt32 borderImageSplitInt[4];
1937 nsMargin border;
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
1945 return;
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));
1958 nsSize imageSize;
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);
1972 else
1973 borderImageSplitInt[side] = (nscoord)(percent * imageSize.width);
1974 break;
1975 case eStyleUnit_Integer:
1976 borderImageSplitInt[side] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit[side].
1977 GetIntValue());
1978 break;
1979 case eStyleUnit_Factor:
1980 borderImageSplitInt[side] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit[side].GetFactorValue());
1981 break;
1982 default:
1983 break;
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);
2005 gfxRect rectToDraw,
2006 rectToDrawSource;
2008 gfxRect clipRect;
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);
2015 thebesCtx->Save();
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,
2026 interSizeMiddle;
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,
2032 middleScaleV;
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;
2038 } else {
2039 middleScaleH = 1.0;
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;
2046 } else {
2047 middleScaleV = 1.0;
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);
2077 // draw top
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);
2103 // draw right
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);
2131 // draw bottom
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);
2158 // draw left
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);
2171 // Draw middle
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);
2186 thebesCtx->Paint();
2187 thebesCtx->Restore();
2190 void
2191 nsCSSRendering::DrawBorderImageSide(gfxContext *aThebesContext,
2192 nsIDeviceContext* aDeviceContext,
2193 imgIContainer* aImage,
2194 gfxRect& aDestRect,
2195 gfxSize& aInterSize,
2196 gfxRect& aSourceRect,
2197 PRUint8 aHFillType,
2198 PRUint8 aVFillType)
2200 if (aDestRect.size.width < 1.0 || aDestRect.size.height < 1.0 ||
2201 aSourceRect.size.width < 1.0 || aSourceRect.size.height < 1.0) {
2202 return;
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)
2214 return;
2216 if (aInterSize.width < 1.0 ||
2217 aInterSize.height < 1.0)
2218 return;
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));
2233 if(NS_FAILED(rv))
2234 return;
2235 nsCOMPtr<nsIImage> image;
2236 image = do_GetInterface(frame);
2237 if(!image)
2238 return;
2240 // surface for the whole image
2241 nsRefPtr<gfxPattern> imagePattern;
2242 rv = image->GetPattern(getter_AddRefs(imagePattern));
2243 if(NS_FAILED(rv) || !imagePattern)
2244 return;
2246 gfxMatrix mat;
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);
2254 srcCtx->Paint();
2255 srcCtx = nsnull;
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);
2275 break;
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);
2279 break;
2280 case NS_STYLE_BORDER_IMAGE_STRETCH:
2281 default:
2282 hScale = aInterSize.width/aDestRect.size.width;
2283 break;
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);
2291 break;
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);
2295 break;
2296 case NS_STYLE_BORDER_IMAGE_STRETCH:
2297 default:
2298 vScale = aInterSize.height/aDestRect.size.height;
2299 break;
2302 // Adjust the matrix scale for Step 2 of the spec
2303 srcMatrix.Scale(hScale,vScale);
2304 pattern->SetMatrix(srcMatrix);
2306 // render
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();
2316 void
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())) {
2332 // nothing to paint
2333 return;
2336 nscoord borderRadii[4];
2337 nsRect bgClipArea(aBgClipArea);
2339 GetBorderRadiusTwips(aBorder.mBorderRadius, aForFrame->GetSize().width, borderRadii);
2341 PRUint8 side = 0;
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,
2347 aCanPaintNonWhite);
2348 return;
2352 nscolor color;
2353 if (!aCanPaintNonWhite) {
2354 color = NS_RGB(255, 255, 255);
2355 } else {
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
2367 void
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));
2402 oRect.Round();
2403 oRect.Condition();
2404 if (oRect.IsEmpty())
2405 return;
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;
2429 ctx->NewPath();
2430 ctx->RoundedRectangle(oRect, radii);
2431 ctx->SetColor(gfxRGBA(color));
2432 ctx->Fill();
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
2440 static nscoord
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.
2448 return aValue;
2450 nscoord halfPixel = NSToCoordRound(aTwipsPerPixel / 2.0f);
2451 nscoord extra = aValue % aTwipsPerPixel;
2452 nscoord finalValue = (!aRoundDown && (extra >= halfPixel)) ? aValue + (aTwipsPerPixel - extra) : aValue - extra;
2453 return finalValue;
2456 static nscoord
2457 RoundFloatToPixel(float aValue,
2458 nscoord aTwipsPerPixel,
2459 PRBool aRoundDown = PR_FALSE)
2461 return RoundIntToPixel(NSToCoordRound(aValue), aTwipsPerPixel, aRoundDown);
2464 static void
2465 SetPoly(const nsRect& aRect,
2466 nsPoint* poly)
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;
2480 static void
2481 DrawSolidBorderSegment(nsIRenderingContext& aContext,
2482 nsRect aRect,
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);
2496 else
2497 aContext.FillRect(aRect);
2499 else {
2500 if (1 == aRect.width)
2501 aContext.DrawLine(aRect.x, aRect.y, aRect.x + aRect.width, aRect.y);
2502 else
2503 aContext.FillRect(aRect);
2506 else {
2507 // polygon with beveling
2508 nsPoint poly[5];
2509 SetPoly(aRect, poly);
2510 switch(aStartBevelSide) {
2511 case NS_SIDE_TOP:
2512 poly[0].x += aStartBevelOffset;
2513 poly[4].x = poly[0].x;
2514 break;
2515 case NS_SIDE_BOTTOM:
2516 poly[3].x += aStartBevelOffset;
2517 break;
2518 case NS_SIDE_RIGHT:
2519 poly[1].y += aStartBevelOffset;
2520 break;
2521 case NS_SIDE_LEFT:
2522 poly[0].y += aStartBevelOffset;
2523 poly[4].y = poly[0].y;
2526 switch(aEndBevelSide) {
2527 case NS_SIDE_TOP:
2528 poly[1].x -= aEndBevelOffset;
2529 break;
2530 case NS_SIDE_BOTTOM:
2531 poly[2].x -= aEndBevelOffset;
2532 break;
2533 case NS_SIDE_RIGHT:
2534 poly[2].y -= aEndBevelOffset;
2535 break;
2536 case NS_SIDE_LEFT:
2537 poly[3].y -= aEndBevelOffset;
2540 aContext.FillPolygon(poly, 5);
2546 static void
2547 GetDashInfo(nscoord aBorderLength,
2548 nscoord aDashLength,
2549 nscoord aTwipsPerPixel,
2550 PRInt32& aNumDashSpaces,
2551 nscoord& aStartDashLength,
2552 nscoord& aEndDashLength)
2554 aNumDashSpaces = 0;
2555 if (aStartDashLength + aDashLength + aEndDashLength >= aBorderLength) {
2556 aStartDashLength = aBorderLength;
2557 aEndDashLength = 0;
2559 else {
2560 aNumDashSpaces = aBorderLength / (2 * aDashLength); // round down
2561 nscoord extra = aBorderLength - aStartDashLength - aEndDashLength - (((2 * aNumDashSpaces) - 1) * aDashLength);
2562 if (extra > 0) {
2563 nscoord half = RoundIntToPixel(extra / 2, aTwipsPerPixel);
2564 aStartDashLength += half;
2565 aEndDashLength += (extra - half);
2570 void
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");
2603 break;
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;
2617 if (horizontal) {
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);
2627 else {
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);
2638 break;
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);
2648 else {
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;
2654 aContext.SetColor (
2655 MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
2656 nsRect rect(aBorder);
2657 nscoord half;
2658 if (horizontal) { // top, bottom
2659 half = RoundFloatToPixel(0.5f * (float)aBorder.height, twipsPerPixel);
2660 rect.height = half;
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);
2673 rect.width = half;
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);
2685 rect = aBorder;
2686 ridgeGrooveSide = (NS_SIDE_TOP == ridgeGrooveSide) ? NS_SIDE_BOTTOM : NS_SIDE_RIGHT;
2687 aContext.SetColor (
2688 MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
2689 if (horizontal) {
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);
2702 else {
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);
2716 break;
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);
2777 break;
2779 // else fall through to solid
2780 case NS_STYLE_BORDER_STYLE_SOLID:
2781 DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide,
2782 aStartBevelOffset, aEndBevelSide, aEndBevelOffset);
2783 break;
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");
2787 break;
2788 case NS_STYLE_BORDER_STYLE_AUTO:
2789 NS_ASSERTION(PR_FALSE, "Unexpected 'auto' table border");
2790 break;
2793 ctx->SetAntialiasMode(oldMode);
2796 // End table border-collapsing section
2798 void
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)
2808 gfxRect rect =
2809 GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
2810 aDecoration, aStyle);
2811 if (rect.IsEmpty())
2812 return;
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!");
2819 return;
2822 gfxFloat lineHeight = PR_MAX(NS_round(aLineSize.height), 1.0);
2823 PRBool contextIsSaved = PR_FALSE;
2825 gfxFloat oldLineWidth;
2826 nsRefPtr<gfxPattern> oldPattern;
2828 switch (aStyle) {
2829 case NS_STYLE_BORDER_STYLE_SOLID:
2830 case NS_STYLE_BORDER_STYLE_DOUBLE:
2831 oldLineWidth = aGfxContext->CurrentLineWidth();
2832 oldPattern = aGfxContext->GetPattern();
2833 break;
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);
2841 break;
2843 case NS_STYLE_BORDER_STYLE_DOTTED: {
2844 aGfxContext->Save();
2845 contextIsSaved = PR_TRUE;
2846 gfxFloat dashWidth = lineHeight * DOT_LENGTH;
2847 gfxFloat dash[2];
2848 if (lineHeight > 2.0) {
2849 dash[0] = 0.0;
2850 dash[1] = dashWidth * 2.0;
2851 aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
2852 } else {
2853 dash[0] = dashWidth;
2854 dash[1] = dashWidth;
2856 aGfxContext->SetDash(dash, 2, 0.0);
2857 break;
2859 default:
2860 NS_ERROR("Invalid style value!");
2861 return;
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);
2869 switch (aStyle) {
2870 case NS_STYLE_BORDER_STYLE_SOLID:
2871 aGfxContext->NewPath();
2872 aGfxContext->MoveTo(rect.TopLeft());
2873 aGfxContext->LineTo(rect.TopRight());
2874 aGfxContext->Stroke();
2875 break;
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();
2884 break;
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();
2891 break;
2892 default:
2893 NS_ERROR("Invalid style value!");
2894 break;
2897 if (contextIsSaved) {
2898 aGfxContext->Restore();
2899 } else {
2900 aGfxContext->SetPattern(oldPattern);
2901 aGfxContext->SetLineWidth(oldLineWidth);
2905 nsRect
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");
2915 gfxRect rect =
2916 GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset,
2917 aDecoration, aStyle);
2918 // The rect values are already rounded to nearest device pixels.
2919 nsRect r;
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());
2924 return r;
2927 gfxRect
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)
2935 gfxRect r;
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;
2946 } else {
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:
2954 offset = aOffset;
2955 break;
2956 case NS_STYLE_TEXT_DECORATION_OVERLINE:
2957 offset = aOffset - basesize + r.Height();
2958 break;
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;
2963 break;
2965 default:
2966 NS_ERROR("Invalid decoration value!");
2968 r.pos.y = baseline - NS_floor(offset + 0.5);
2969 return r;
2972 // -----
2973 // nsContextBoxBlur
2974 // -----
2975 void
2976 nsContextBoxBlur::BoxBlurHorizontal(unsigned char* aInput,
2977 unsigned char* aOutput,
2978 PRUint32 aLeftLobe,
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];
3011 void
3012 nsContextBoxBlur::BoxBlurVertical(unsigned char* aInput,
3013 unsigned char* aOutput,
3014 PRUint32 aTopLobe,
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];
3043 gfxContext*
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;
3052 return mContext;
3055 // Convert from app units to device pixels
3056 mRect = aRect;
3057 mRect.Outset(aBlurRadius);
3058 mRect.ScaleInverse(aAppUnitsPerDevPixel);
3059 mRect.RoundOut();
3061 if (mRect.IsEmpty()) {
3062 mBlurRadius = 0;
3063 mContext = aDestinationCtx;
3064 return mContext;
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())
3074 return nsnull;
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
3078 // of mRect.
3079 mImageSurface->SetDeviceOffset(-mRect.TopLeft());
3081 mContext = new gfxContext(mImageSurface);
3082 return mContext;
3085 void
3086 nsContextBoxBlur::DoPaint()
3088 if (mBlurRadius <= 0)
3089 return;
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()))
3099 return; // OOM
3101 // Here we do like what the SVG gaussian blur filter does in calculating
3102 // the lobes.
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);
3111 } else {
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);
3124 gfxContext*
3125 nsContextBoxBlur::GetContext()
3127 return mContext;