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