diff options
author | Ben Murdoch <benm@google.com> | 2011-06-02 12:07:03 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-06-10 10:47:21 +0100 |
commit | 2daae5fd11344eaa88a0d92b0f6d65f8d2255c00 (patch) | |
tree | e4964fbd1cb70599f7718ff03e50ea1dab33890b /Source/WebCore/rendering/RenderBoxModelObject.cpp | |
parent | 87bdf0060a247bfbe668342b87e0874182e0ffa9 (diff) | |
download | external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.zip external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.tar.gz external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.tar.bz2 |
Merge WebKit at r84325: Initial merge by git.
Change-Id: Ic1a909300ecc0a13ddc6b4e784371d2ac6e3d59b
Diffstat (limited to 'Source/WebCore/rendering/RenderBoxModelObject.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderBoxModelObject.cpp | 884 |
1 files changed, 670 insertions, 214 deletions
diff --git a/Source/WebCore/rendering/RenderBoxModelObject.cpp b/Source/WebCore/rendering/RenderBoxModelObject.cpp index 3e2974d..52196e3 100644 --- a/Source/WebCore/rendering/RenderBoxModelObject.cpp +++ b/Source/WebCore/rendering/RenderBoxModelObject.cpp @@ -559,7 +559,20 @@ int RenderBoxModelObject::paddingEnd(bool) const return padding.calcMinValue(w); } -void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op, RenderObject* backgroundObject) +RoundedIntRect RenderBoxModelObject::getBackgroundRoundedRect(const IntRect& borderRect, InlineFlowBox* box, int inlineBoxWidth, int inlineBoxHeight, + bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +{ + RoundedIntRect border = style()->getRoundedBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); + if (box && (box->nextLineBox() || box->prevLineBox())) { + RoundedIntRect segmentBorder = style()->getRoundedBorderFor(IntRect(0, 0, inlineBoxWidth, inlineBoxHeight), includeLogicalLeftEdge, includeLogicalRightEdge); + border.setRadii(segmentBorder.radii()); + } + + return border; +} + +void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer* bgLayer, int tx, int ty, int w, int h, + InlineFlowBox* box, int inlineBoxWidth, int inlineBoxHeight, CompositeOperator op, RenderObject* backgroundObject) { GraphicsContext* context = paintInfo.context; if (context->paintingDisabled()) @@ -567,13 +580,53 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true; bool includeRightEdge = box ? box->includeLogicalRightEdge() : true; - int bLeft = includeLeftEdge ? borderLeft() : 0; - int bRight = includeRightEdge ? borderRight() : 0; - int pLeft = includeLeftEdge ? paddingLeft() : 0; - int pRight = includeRightEdge ? paddingRight() : 0; + + bool hasRoundedBorder = style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge); + bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment; + bool isBorderFill = bgLayer->clip() == BorderFillBox; + bool isRoot = this->isRoot(); + + Color bgColor = color; + StyleImage* bgImage = bgLayer->image(); + bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(style()->effectiveZoom()); + + // When this style flag is set, change existing background colors and images to a solid white background. + // If there's no bg color or image, leave it untouched to avoid affecting transparency. + // We don't try to avoid loading the background images, because this style flag is only set + // when printing, and at that point we've already loaded the background images anyway. (To avoid + // loading the background images we'd have to do this check when applying styles rather than + // while rendering.) + if (style()->forceBackgroundsToWhite()) { + // Note that we can't reuse this variable below because the bgColor might be changed + bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0; + if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) { + bgColor = Color::white; + shouldPaintBackgroundImage = false; + } + } + + bool colorVisible = bgColor.isValid() && bgColor.alpha() > 0; + + // Fast path for drawing simple color backgrounds. + if (!isRoot && !clippedWithLocalScrolling && !shouldPaintBackgroundImage && isBorderFill) { + if (!colorVisible) + return; + + IntRect borderRect(tx, ty, w, h); + if (borderRect.isEmpty()) + return; + + if (hasRoundedBorder) { + RoundedIntRect border = getBackgroundRoundedRect(borderRect, box, inlineBoxWidth, inlineBoxHeight, includeLeftEdge, includeRightEdge); + context->fillRoundedRect(border, bgColor, style()->colorSpace()); + } else + context->fillRect(borderRect, bgColor, style()->colorSpace()); + + return; + } bool clippedToBorderRadius = false; - if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) { + if (hasRoundedBorder) { IntRect borderRect(tx, ty, w, h); if (borderRect.isEmpty()) @@ -581,13 +634,16 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co context->save(); - RoundedIntRect border = style()->getRoundedBorderFor(borderRect); - border.excludeLogicalEdges(box && box->isHorizontal(), !includeLeftEdge, !includeRightEdge); + RoundedIntRect border = getBackgroundRoundedRect(borderRect, box, inlineBoxWidth, inlineBoxHeight, includeLeftEdge, includeRightEdge); context->addRoundedRectClip(border); clippedToBorderRadius = true; } + + int bLeft = includeLeftEdge ? borderLeft() : 0; + int bRight = includeRightEdge ? borderRight() : 0; + int pLeft = includeLeftEdge ? paddingLeft() : 0; + int pRight = includeRightEdge ? paddingRight() : 0; - bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment; if (clippedWithLocalScrolling) { // Clip to the overflow area. context->save(); @@ -628,9 +684,10 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co // Now add the text to the clip. We do this by painting using a special paint phase that signals to // InlineTextBoxes that they should just add their contents to the clip. PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0); - if (box) - box->paint(info, tx - box->x(), ty - box->y()); - else { + if (box) { + RootInlineBox* root = box->root(); + box->paint(info, tx - box->x(), ty - box->y(), root->lineTop(), root->lineBottom()); + } else { int x = isBox() ? toRenderBox(this)->x() : 0; int y = isBox() ? toRenderBox(this)->y() : 0; paint(info, tx - x, ty - y); @@ -641,27 +698,6 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co context->clipToImageBuffer(maskImage.get(), maskRect); } - StyleImage* bg = bgLayer->image(); - bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom()); - Color bgColor = c; - - // When this style flag is set, change existing background colors and images to a solid white background. - // If there's no bg color or image, leave it untouched to avoid affecting transparency. - // We don't try to avoid loading the background images, because this style flag is only set - // when printing, and at that point we've already loaded the background images anyway. (To avoid - // loading the background images we'd have to do this check when applying styles rather than - // while rendering.) - if (style()->forceBackgroundsToWhite()) { - // Note that we can't reuse this variable below because the bgColor might be changed - bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0; - if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) { - bgColor = Color::white; - shouldPaintBackgroundImage = false; - } - } - - bool isRoot = this->isRoot(); - // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with // no background in the child document should show the parent's background. bool isOpaqueRoot = false; @@ -726,18 +762,16 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co phase += destRect.location() - destOrigin; CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this; - RefPtr<Image> image = bg->image(clientForBackgroundImage, tileSize); + RefPtr<Image> image = bgImage->image(clientForBackgroundImage, tileSize); bool useLowQualityScaling = shouldPaintAtLowQuality(context, image.get(), bgLayer, tileSize); context->drawTiledImage(image.get(), style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling); } } - if (bgLayer->clip() != BorderFillBox) - // Undo the background clip + if (!isBorderFill) // Undo the background clip context->restore(); - if (clippedToBorderRadius) - // Undo the border radius clip + if (clippedToBorderRadius) // Undo the border radius clip context->restore(); if (clippedWithLocalScrolling) // Undo the clip for local background attachments. @@ -1001,170 +1035,560 @@ bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, } #if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING) -static bool borderWillArcInnerEdge(const IntSize& firstRadius, const IntSize& secondRadius, int firstBorderWidth, int secondBorderWidth, int middleBorderWidth) +static bool borderWillArcInnerEdge(const IntSize& firstRadius, const IntSize& secondRadius) { - // FIXME: This test is insufficient. We need to take border style into account. - return (!firstRadius.width() || firstRadius.width() >= firstBorderWidth) - && (!firstRadius.height() || firstRadius.height() >= middleBorderWidth) - && (!secondRadius.width() || secondRadius.width() >= secondBorderWidth) - && (!secondRadius.height() || secondRadius.height() >= middleBorderWidth); + return !firstRadius.isZero() || !secondRadius.isZero(); } -void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, - const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +enum BorderEdgeFlag { + TopBorderEdge = 1 << BSTop, + RightBorderEdge = 1 << BSRight, + BottomBorderEdge = 1 << BSBottom, + LeftBorderEdge = 1 << BSLeft, + AllBorderEdges = TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBorderEdge +}; + +static inline BorderEdgeFlag edgeFlagForSide(BoxSide side) { - if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) - return; + return static_cast<BorderEdgeFlag>(1 << side); +} - if (graphicsContext->paintingDisabled()) - return; +static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) +{ + return flags & edgeFlagForSide(side); +} - const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor); - const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor); - const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor); - const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor); +class BorderEdge { +public: + BorderEdge(int edgeWidth, const Color& edgeColor, EBorderStyle edgeStyle, bool edgeIsTransparent, bool edgeIsPresent) + : width(edgeWidth) + , color(edgeColor) + , style(edgeStyle) + , isTransparent(edgeIsTransparent) + , isPresent(edgeIsPresent) + { + if (style == DOUBLE && edgeWidth < 3) + style = SOLID; + } + + bool hasVisibleColorAndStyle() const { return style > BHIDDEN && !isTransparent; } + bool shouldRender() const { return isPresent && hasVisibleColorAndStyle(); } + bool presentButInvisible() const { return usedWidth() && !hasVisibleColorAndStyle(); } - bool topTransparent = style->borderTopIsTransparent(); - bool bottomTransparent = style->borderBottomIsTransparent(); - bool rightTransparent = style->borderRightIsTransparent(); - bool leftTransparent = style->borderLeftIsTransparent(); + int usedWidth() const { return isPresent ? width : 0; } + + void getDoubleBorderStripeWidths(int& outerWidth, int& innerWidth) const + { + int fullWidth = usedWidth(); + outerWidth = fullWidth / 3; + innerWidth = fullWidth * 2 / 3; + + // We need certain integer rounding results + if (fullWidth % 3 == 2) + outerWidth += 1; + + if (fullWidth % 3 == 1) + innerWidth += 1; + } + + int width; + Color color; + EBorderStyle style; + bool isTransparent; + bool isPresent; +}; - EBorderStyle topStyle = style->borderTopStyle(); - EBorderStyle bottomStyle = style->borderBottomStyle(); - EBorderStyle leftStyle = style->borderLeftStyle(); - EBorderStyle rightStyle = style->borderRightStyle(); +inline bool edgesShareColor(const BorderEdge& firstEdge, const BorderEdge& secondEdge) +{ + return firstEdge.color == secondEdge.color; +} - bool horizontal = style->isHorizontalWritingMode(); +inline bool styleRequiresClipPolygon(EBorderStyle style) +{ + return style == DOTTED || style == DASHED; // These are drawn with a stroke, so we have to clip to get corner miters. +} - bool renderTop = topStyle > BHIDDEN && !topTransparent && (horizontal || includeLogicalLeftEdge); - bool renderLeft = leftStyle > BHIDDEN && !leftTransparent && (!horizontal || includeLogicalLeftEdge); - bool renderRight = rightStyle > BHIDDEN && !rightTransparent && (!horizontal || includeLogicalRightEdge); - bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent && (horizontal || includeLogicalRightEdge); +static bool borderStyleFillsBorderArea(EBorderStyle style) +{ + return !(style == DOTTED || style == DASHED || style == DOUBLE); +} - Path roundedPath; - RoundedIntRect border(tx, ty, w, h); +static bool borderStyleHasInnerDetail(EBorderStyle style) +{ + return style == GROOVE || style == RIDGE || style == DOUBLE; +} - if (style->hasBorderRadius()) { - border.includeLogicalEdges(style->getRoundedBorderFor(border.rect()).radii(), - horizontal, includeLogicalLeftEdge, includeLogicalRightEdge); +static bool borderStyleIsDottedOrDashed(EBorderStyle style) +{ + return style == DOTTED || style == DASHED; +} - int leftWidth = (!horizontal || includeLogicalLeftEdge) ? style->borderLeftWidth() : 0; - int rightWidth = (!horizontal || includeLogicalRightEdge) ? style->borderRightWidth() : 0; - int topWidth = (horizontal || includeLogicalLeftEdge) ? style->borderTopWidth() : 0; - int bottomWidth = (horizontal || includeLogicalRightEdge) ? style->borderBottomWidth() : 0; +// OUTSET darkens the bottom and right (and maybe lightens the top and left) +// INSET darkens the top and left (and maybe lightens the bottom and right) +static inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide side, BoxSide adjacentSide) +{ + // These styles match at the top/left and bottom/right. + if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET) { + const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight); + const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft); - RoundedIntRect inner(borderInnerRect(border.rect(), topWidth, bottomWidth, leftWidth, rightWidth)); - inner.includeLogicalEdges(style->getRoundedInnerBorderWithBorderWidths(inner.rect(), topWidth, bottomWidth, leftWidth, rightWidth).radii(), - horizontal, includeLogicalLeftEdge, includeLogicalRightEdge); + BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacentSide); + return flags == topRightFlags || flags == bottomLeftFlags; + } + return false; +} - if (border.isRounded()) { - // Clip to the inner and outer radii rects. - graphicsContext->save(); - graphicsContext->addRoundedRectClip(border); - graphicsContext->clipOutRoundedRect(inner); - roundedPath.addRoundedRect(border.rect(), border.radii().topLeft(), border.radii().topRight(), border.radii().bottomLeft(), border.radii().bottomRight()); - } +static inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[]) +{ + if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) + return false; + + if (!edgesShareColor(edges[side], edges[adjacentSide])) + return false; + + return !borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide); +} + +// This assumes that we draw in order: top, bottom, left, right. +static inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[]) +{ + switch (side) { + case BSTop: + case BSBottom: + if (edges[adjacentSide].presentButInvisible()) + return false; + + if (!edgesShareColor(edges[side], edges[adjacentSide]) && edges[adjacentSide].color.hasAlpha()) + return false; + + if (!borderStyleFillsBorderArea(edges[adjacentSide].style)) + return false; + + return true; + + case BSLeft: + case BSRight: + // These draw last, so are never overdrawn. + return false; } + return false; +} - bool renderRadii = border.isRounded(); - bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor); - bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE); - bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE); - bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor); +static inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorderStyle style, EBorderStyle adjacentStyle) +{ + if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE) + return true; - if (renderTop) { - int x = tx; - int x2 = tx + w; + if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjacentStyle)) + return true; + + if (style != adjacentStyle) + return true; - if (renderRadii && borderWillArcInnerEdge(border.radii().topLeft(), border.radii().topRight(), style->borderLeftWidth(), style->borderRightWidth(), style->borderTopWidth())) { + return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); +} + +static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[], bool allowOverdraw) +{ + if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edges[adjacentSide].isPresent) + return false; + + if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges)) + return false; + + if (!edgesShareColor(edges[side], edges[adjacentSide])) + return true; + + if (borderStylesRequireMitre(side, adjacentSide, edges[side].style, edges[adjacentSide].style)) + return true; + + return false; +} + +void RenderBoxModelObject::paintOneBorderSide(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedIntRect& outerBorder, const RoundedIntRect& innerBorder, + const IntRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge edges[], const Path* path, + bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor) +{ + const BorderEdge& edgeToRender = edges[side]; + const BorderEdge& adjacentEdge1 = edges[adjacentSide1]; + const BorderEdge& adjacentEdge2 = edges[adjacentSide2]; + + bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !antialias); + bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !antialias); + + bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edges); + bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edges); + + const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.color; + + if (path) { + graphicsContext->save(); + clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch); + float thickness = max(max(edgeToRender.width, adjacentEdge1.width), adjacentEdge2.width); + drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, edgeToRender.width, thickness, side, style, colorToPaint, edgeToRender.style, includeLogicalLeftEdge, includeLogicalRightEdge); + graphicsContext->restore(); + } else { + bool didClip = false; + + if (styleRequiresClipPolygon(edgeToRender.style) && (mitreAdjacentSide1 || mitreAdjacentSide2)) { graphicsContext->save(); - clipBorderSidePolygon(graphicsContext, border, BSTop, upperLeftBorderStylesMatch, upperRightBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); - float thickness = max(max(style->borderTopWidth(), style->borderLeftWidth()), style->borderRightWidth()); - drawBoxSideFromPath(graphicsContext, border.rect(), roundedPath, style->borderTopWidth(), thickness, BSTop, style, topColor, topStyle); + clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, !mitreAdjacentSide1, !mitreAdjacentSide2); + didClip = true; + // Since we clipped, no need to draw with a mitre. + mitreAdjacentSide1 = false; + mitreAdjacentSide2 = false; + } + + drawLineForBoxSide(graphicsContext, sideRect.x(), sideRect.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.style, + mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? adjacentEdge2.width : 0, antialias); + + if (didClip) graphicsContext->restore(); - } else { - bool ignoreLeft = (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET - && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); - bool ignoreRight = (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET - && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); + } +} - drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle, - ignoreLeft ? 0 : style->borderLeftWidth(), ignoreRight ? 0 : style->borderRightWidth()); - } +void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedIntRect& outerBorder, const RoundedIntRect& innerBorder, + const BorderEdge edges[], BorderEdgeFlags edgeSet, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor) +{ + bool renderRadii = outerBorder.isRounded(); + + Path roundedPath; + if (renderRadii) + roundedPath.addRoundedRect(outerBorder); + + if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) { + IntRect sideRect = outerBorder.rect(); + sideRect.setHeight(edges[BSTop].width); + + bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].style) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorder.radii().topRight())); + paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); } - if (renderBottom) { - int x = tx; - int x2 = tx + w; + if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) { + IntRect sideRect = outerBorder.rect(); + sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width); - if (renderRadii && borderWillArcInnerEdge(border.radii().bottomLeft(), border.radii().bottomRight(), style->borderLeftWidth(), style->borderRightWidth(), style->borderBottomWidth())) { - graphicsContext->save(); - clipBorderSidePolygon(graphicsContext, border, BSBottom, lowerLeftBorderStylesMatch, lowerRightBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); - float thickness = max(max(style->borderBottomWidth(), style->borderLeftWidth()), style->borderRightWidth()); - drawBoxSideFromPath(graphicsContext, border.rect(), roundedPath, style->borderBottomWidth(), thickness, BSBottom, style, bottomColor, bottomStyle); - graphicsContext->restore(); - } else { - bool ignoreLeft = (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET - && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); + bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().bottomRight())); + paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + } - bool ignoreRight = (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET - && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); + if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) { + IntRect sideRect = outerBorder.rect(); + sideRect.setWidth(edges[BSLeft].width); + + bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().topLeft())); + paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + } + + if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) { + IntRect sideRect = outerBorder.rect(); + sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width); + + bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].style) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), innerBorder.radii().topRight())); + paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + } +} - drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, - bottomStyle, ignoreLeft ? 0 : style->borderLeftWidth(), - ignoreRight ? 0 : style->borderRightWidth()); +void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedIntRect& outerBorder, const RoundedIntRect& innerBorder, + const BorderEdge edges[], bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias) +{ + BorderEdgeFlags edgesToDraw = AllBorderEdges; + while (edgesToDraw) { + // Find undrawn edges sharing a color. + Color commonColor; + + BorderEdgeFlags commonColorEdgeSet = 0; + for (int i = BSTop; i <= BSLeft; ++i) { + BoxSide currSide = static_cast<BoxSide>(i); + if (!includesEdge(edgesToDraw, currSide)) + continue; + + bool includeEdge; + if (!commonColorEdgeSet) { + commonColor = edges[currSide].color; + includeEdge = true; + } else + includeEdge = edges[currSide].color == commonColor; + + if (includeEdge) + commonColorEdgeSet |= edgeFlagForSide(currSide); } + + bool useTransparencyLayer = commonColor.hasAlpha(); + if (useTransparencyLayer) { + graphicsContext->beginTransparencyLayer(static_cast<float>(commonColor.alpha()) / 255); + commonColor = Color(commonColor.red(), commonColor.green(), commonColor.blue()); + } + + paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, commonColorEdgeSet, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, &commonColor); + + if (useTransparencyLayer) + graphicsContext->endTransparencyLayer(); + + edgesToDraw &= ~commonColorEdgeSet; } +} - if (renderLeft) { - int y = ty; - int y2 = ty + h; +void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, + const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +{ + if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) + return; - if (renderRadii && borderWillArcInnerEdge(border.radii().bottomLeft(), border.radii().topLeft(), style->borderBottomWidth(), style->borderTopWidth(), style->borderLeftWidth())) { - graphicsContext->save(); - clipBorderSidePolygon(graphicsContext, border, BSLeft, upperLeftBorderStylesMatch, lowerLeftBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); - float thickness = max(max(style->borderLeftWidth(), style->borderTopWidth()), style->borderBottomWidth()); - drawBoxSideFromPath(graphicsContext, border.rect(), roundedPath, style->borderLeftWidth(), thickness, BSLeft, style, leftColor, leftStyle); - graphicsContext->restore(); - } else { - bool ignoreTop = (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET - && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); + if (graphicsContext->paintingDisabled()) + return; - bool ignoreBottom = (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET - && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); + bool horizontal = style->isHorizontalWritingMode(); - drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor, - leftStyle, ignoreTop ? 0 : style->borderTopWidth(), ignoreBottom ? 0 : style->borderBottomWidth()); + BorderEdge edges[4] = { + // BSTop + BorderEdge(style->borderTopWidth(), + style->visitedDependentColor(CSSPropertyBorderTopColor), + style->borderTopStyle(), + style->borderTopIsTransparent(), + horizontal || includeLogicalLeftEdge), + // BSRight + BorderEdge(style->borderRightWidth(), + style->visitedDependentColor(CSSPropertyBorderRightColor), + style->borderRightStyle(), + style->borderRightIsTransparent(), + !horizontal || includeLogicalRightEdge), + // BSBottom + BorderEdge(style->borderBottomWidth(), + style->visitedDependentColor(CSSPropertyBorderBottomColor), + style->borderBottomStyle(), + style->borderBottomIsTransparent(), + horizontal || includeLogicalRightEdge), + // BSLeft + BorderEdge(style->borderLeftWidth(), + style->visitedDependentColor(CSSPropertyBorderLeftColor), + style->borderLeftStyle(), + style->borderLeftIsTransparent(), + !horizontal || includeLogicalLeftEdge) + }; + + IntRect borderRect(tx, ty, w, h); + RoundedIntRect outerBorder = style->getRoundedBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); + RoundedIntRect innerBorder = style->getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); + + const AffineTransform& currentCTM = graphicsContext->getCTM(); + // FIXME: this isn't quite correct. We may want to antialias when scaled by a non-integral value, or when the translation is non-integral. + bool antialias = !currentCTM.isIdentityOrTranslationOrFlipped(); + + bool haveAlphaColor = false; + bool haveAllSolidEdges = true; + bool allEdgesVisible = true; + bool allEdgesShareColor = true; + int firstVisibleEdge = -1; + + for (int i = BSTop; i <= BSLeft; ++i) { + const BorderEdge& currEdge = edges[i]; + if (currEdge.presentButInvisible()) { + allEdgesVisible = false; + continue; } + + if (!currEdge.width) + continue; + + if (firstVisibleEdge == -1) + firstVisibleEdge = i; + else if (currEdge.color != edges[firstVisibleEdge].color) + allEdgesShareColor = false; + + if (currEdge.color.hasAlpha()) + haveAlphaColor = true; + + if (currEdge.style != SOLID) + haveAllSolidEdges = false; } + + // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787 + if (haveAllSolidEdges && allEdgesVisible && allEdgesShareColor && innerBorder.isRenderable()) { + // Fast path for drawing all solid edges. + if (outerBorder.isRounded() || haveAlphaColor) { + Path path; + + // FIXME: Path should take a RoundedIntRect directly. + if (outerBorder.isRounded()) + path.addRoundedRect(outerBorder); + else + path.addRect(outerBorder.rect()); - if (renderRight) { - if (renderRadii && borderWillArcInnerEdge(border.radii().bottomRight(), border.radii().topRight(), style->borderBottomWidth(), style->borderTopWidth(), style->borderRightWidth())) { - graphicsContext->save(); - clipBorderSidePolygon(graphicsContext, border, BSRight, upperRightBorderStylesMatch, lowerRightBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); - float thickness = max(max(style->borderRightWidth(), style->borderTopWidth()), style->borderBottomWidth()); - drawBoxSideFromPath(graphicsContext, border.rect(), roundedPath, style->borderRightWidth(), thickness, BSRight, style, rightColor, rightStyle); - graphicsContext->restore(); - } else { - bool ignoreTop = ((topColor == rightColor) && (topTransparent == rightTransparent) - && (rightStyle >= DOTTED || rightStyle == INSET) - && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); + if (innerBorder.isRounded()) + path.addRoundedRect(innerBorder); + else + path.addRect(innerBorder.rect()); + + graphicsContext->setFillRule(RULE_EVENODD); + graphicsContext->setFillColor(edges[firstVisibleEdge].color, style->colorSpace()); + graphicsContext->fillPath(path); + } else + paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, AllBorderEdges, includeLogicalLeftEdge, includeLogicalRightEdge, antialias); + + return; + } - bool ignoreBottom = ((bottomColor == rightColor) && (bottomTransparent == rightTransparent) - && (rightStyle >= DOTTED || rightStyle == INSET) - && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); + if (outerBorder.isRounded()) { + // Clip to the inner and outer radii rects. + graphicsContext->save(); + graphicsContext->addRoundedRectClip(outerBorder); + graphicsContext->clipOutRoundedRect(innerBorder); + } + + if (haveAlphaColor) + paintTranslucentBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, includeLogicalLeftEdge, includeLogicalRightEdge, antialias); + else + paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, AllBorderEdges, includeLogicalLeftEdge, includeLogicalRightEdge, antialias); + + if (outerBorder.isRounded()) + graphicsContext->restore(); +} + +void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, const IntRect& borderRect, const Path& borderPath, const BorderEdge edges[], + float thickness, float drawThickness, BoxSide side, const RenderStyle* style, + Color color, EBorderStyle borderStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +{ + if (thickness <= 0) + return; - int y = ty; - int y2 = ty + h; + if (borderStyle == DOUBLE && thickness < 3) + borderStyle = SOLID; - drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, - rightStyle, ignoreTop ? 0 : style->borderTopWidth(), - ignoreBottom ? 0 : style->borderBottomWidth()); + switch (borderStyle) { + case BNONE: + case BHIDDEN: + return; + case DOTTED: + case DASHED: { + graphicsContext->setStrokeColor(color, style->colorSpace()); + + // The stroke is doubled here because the provided path is the + // outside edge of the border so half the stroke is clipped off. + // The extra multiplier is so that the clipping mask can antialias + // the edges to prevent jaggies. + graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f); + graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke); + + // If the number of dashes that fit in the path is odd and non-integral then we + // will have an awkwardly-sized dash at the end of the path. To try to avoid that + // here, we simply make the whitespace dashes ever so slightly bigger. + // FIXME: This could be even better if we tried to manipulate the dash offset + // and possibly the gapLength to get the corners dash-symmetrical. + float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f); + float gapLength = dashLength; + float numberOfDashes = borderPath.length() / dashLength; + // Don't try to show dashes if we have less than 2 dashes + 2 gaps. + // FIXME: should do this test per side. + if (numberOfDashes >= 4) { + bool evenNumberOfFullDashes = !((int)numberOfDashes % 2); + bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes); + if (!evenNumberOfFullDashes && !integralNumberOfDashes) { + float numberOfGaps = numberOfDashes / 2; + gapLength += (dashLength / numberOfGaps); + } + + DashArray lineDash; + lineDash.append(dashLength); + lineDash.append(gapLength); + graphicsContext->setLineDash(lineDash, dashLength); } + + // FIXME: stroking the border path causes issues with tight corners: + // https://bugs.webkit.org/show_bug.cgi?id=58711 + // Also, to get the best appearance we should stroke a path between the two borders. + graphicsContext->strokePath(borderPath); + return; } + case DOUBLE: { + // Get the inner border rects for both the outer border line and the inner border line + int outerBorderTopWidth; + int innerBorderTopWidth; + edges[BSTop].getDoubleBorderStripeWidths(outerBorderTopWidth, innerBorderTopWidth); + + int outerBorderRightWidth; + int innerBorderRightWidth; + edges[BSRight].getDoubleBorderStripeWidths(outerBorderRightWidth, innerBorderRightWidth); + + int outerBorderBottomWidth; + int innerBorderBottomWidth; + edges[BSBottom].getDoubleBorderStripeWidths(outerBorderBottomWidth, innerBorderBottomWidth); + + int outerBorderLeftWidth; + int innerBorderLeftWidth; + edges[BSLeft].getDoubleBorderStripeWidths(outerBorderLeftWidth, innerBorderLeftWidth); + + // Draw inner border line + graphicsContext->save(); + + RoundedIntRect innerClip = style->getRoundedInnerBorderFor(borderRect, + innerBorderTopWidth, innerBorderBottomWidth, innerBorderLeftWidth, innerBorderRightWidth, + includeLogicalLeftEdge, includeLogicalRightEdge); + + graphicsContext->addRoundedRectClip(innerClip); + drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, includeLogicalLeftEdge, includeLogicalRightEdge); + graphicsContext->restore(); - if (renderRadii) + // Draw outer border line + graphicsContext->save(); + + RoundedIntRect outerClip = style->getRoundedInnerBorderFor(borderRect, + outerBorderTopWidth, outerBorderBottomWidth, outerBorderLeftWidth, outerBorderRightWidth, + includeLogicalLeftEdge, includeLogicalRightEdge); + + graphicsContext->clipOutRoundedRect(outerClip); + drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, includeLogicalLeftEdge, includeLogicalRightEdge); + graphicsContext->restore(); + return; + } + case RIDGE: + case GROOVE: + { + EBorderStyle s1; + EBorderStyle s2; + if (borderStyle == GROOVE) { + s1 = INSET; + s2 = OUTSET; + } else { + s1 = OUTSET; + s2 = INSET; + } + + // Paint full border + drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s1, includeLogicalLeftEdge, includeLogicalRightEdge); + + // Paint inner only + graphicsContext->save(); + + int topWidth = edges[BSTop].usedWidth() / 2; + int bottomWidth = edges[BSBottom].usedWidth() / 2; + int leftWidth = edges[BSLeft].usedWidth() / 2; + int rightWidth = edges[BSRight].usedWidth() / 2; + + RoundedIntRect clipRect = style->getRoundedInnerBorderFor(borderRect, + topWidth, bottomWidth, leftWidth, rightWidth, + includeLogicalLeftEdge, includeLogicalRightEdge); + + graphicsContext->addRoundedRectClip(clipRect); + drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s2, includeLogicalLeftEdge, includeLogicalRightEdge); graphicsContext->restore(); + return; + } + case INSET: + if (side == BSTop || side == BSLeft) + color = color.dark(); + break; + case OUTSET: + if (side == BSBottom || side == BSRight) + color = color.dark(); + break; + default: + break; + } + + graphicsContext->setStrokeStyle(NoStroke); + graphicsContext->setFillColor(color, style->colorSpace()); + graphicsContext->drawRect(borderRect); } #else void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, @@ -1507,50 +1931,92 @@ void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, } #endif -void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const RoundedIntRect& border, - const BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches, const RenderStyle* style, - bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +static void findInnerVertex(const FloatPoint& outerCorner, const FloatPoint& innerCorner, const FloatPoint& centerPoint, FloatPoint& result) { - FloatPoint quad[4]; - int tx = border.rect().x(); - int ty = border.rect().y(); - int w = border.rect().width(); - int h = border.rect().height(); + // If the line between outer and inner corner is towards the horizontal, intersect with a vertical line through the center, + // otherwise with a horizontal line through the center. The points that form this line are arbitrary (we use 0, 100). + // Note that if findIntersection fails, it will leave result untouched. + if (fabs(outerCorner.x() - innerCorner.x()) > fabs(outerCorner.y() - innerCorner.y())) + findIntersection(outerCorner, innerCorner, FloatPoint(centerPoint.x(), 0), FloatPoint(centerPoint.x(), 100), result); + else + findIntersection(outerCorner, innerCorner, FloatPoint(0, centerPoint.y()), FloatPoint(100, centerPoint.y()), result); +} - bool horizontal = style->isHorizontalWritingMode(); - int leftWidth = (!horizontal || includeLogicalLeftEdge) ? style->borderLeftWidth() : 0; - int rightWidth = (!horizontal || includeLogicalRightEdge) ? style->borderRightWidth() : 0; - int topWidth = (horizontal || includeLogicalLeftEdge) ? style->borderTopWidth() : 0; - int bottomWidth = (horizontal || includeLogicalRightEdge) ? style->borderBottomWidth() : 0; +void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const RoundedIntRect& outerBorder, const RoundedIntRect& innerBorder, + BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches) +{ + FloatPoint quad[4]; - // For each side, create an array of FloatPoints where each point is based on whichever value in each corner - // is larger -- the radius width/height or the border width/height -- as appropriate. + const IntRect& outerRect = outerBorder.rect(); + const IntRect& innerRect = innerBorder.rect(); + + FloatPoint centerPoint(innerRect.location().x() + static_cast<float>(innerRect.width()) / 2, innerRect.location().y() + static_cast<float>(innerRect.height()) / 2); + + // For each side, create a quad that encompasses all parts of that side that may draw, + // including areas inside the innerBorder. + // + // 0----------------3 + // 0 \ / 0 + // |\ 1----------- 2 /| + // | 1 1 | + // | | | | + // | | | | + // | 2 2 | + // |/ 1------------2 \| + // 3 / \ 3 + // 0----------------3 + // switch (side) { case BSTop: - quad[0] = FloatPoint(tx, ty); - quad[1] = FloatPoint(tx + max(border.radii().topLeft().width(), leftWidth), ty + max(border.radii().topLeft().height(), topWidth)); - quad[2] = FloatPoint(tx + w - max(border.radii().topRight().width(), rightWidth), ty + max(border.radii().topRight().height(), topWidth)); - quad[3] = FloatPoint(tx + w, ty); + quad[0] = outerRect.minXMinYCorner(); + quad[1] = innerRect.minXMinYCorner(); + quad[2] = innerRect.maxXMinYCorner(); + quad[3] = outerRect.maxXMinYCorner(); + + if (!innerBorder.radii().topLeft().isZero()) + findInnerVertex(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), centerPoint, quad[1]); + + if (!innerBorder.radii().topRight().isZero()) + findInnerVertex(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), centerPoint, quad[2]); break; + case BSLeft: - quad[0] = FloatPoint(tx, ty); - quad[1] = FloatPoint(tx + max(border.radii().topLeft().width(), leftWidth), ty + max(border.radii().topLeft().height(), topWidth)); - quad[2] = FloatPoint(tx + max(border.radii().bottomLeft().width(), leftWidth), ty + h - max(border.radii().bottomLeft().height(), bottomWidth)); - quad[3] = FloatPoint(tx, ty + h); + quad[0] = outerRect.minXMinYCorner(); + quad[1] = innerRect.minXMinYCorner(); + quad[2] = innerRect.minXMaxYCorner(); + quad[3] = outerRect.minXMaxYCorner(); + + if (!innerBorder.radii().topLeft().isZero()) + findInnerVertex(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), centerPoint, quad[1]); + + if (!innerBorder.radii().bottomLeft().isZero()) + findInnerVertex(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), centerPoint, quad[2]); break; + case BSBottom: - quad[0] = FloatPoint(tx, ty + h); - quad[1] = FloatPoint(tx + max(border.radii().bottomLeft().width(), leftWidth), ty + h - max(border.radii().bottomLeft().height(), bottomWidth)); - quad[2] = FloatPoint(tx + w - max(border.radii().bottomRight().width(), rightWidth), ty + h - max(border.radii().bottomRight().height(), bottomWidth)); - quad[3] = FloatPoint(tx + w, ty + h); + quad[0] = outerRect.minXMaxYCorner(); + quad[1] = innerRect.minXMaxYCorner(); + quad[2] = innerRect.maxXMaxYCorner(); + quad[3] = outerRect.maxXMaxYCorner(); + + if (!innerBorder.radii().bottomLeft().isZero()) + findInnerVertex(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), centerPoint, quad[1]); + + if (!innerBorder.radii().bottomRight().isZero()) + findInnerVertex(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), centerPoint, quad[2]); break; + case BSRight: - quad[0] = FloatPoint(tx + w, ty); - quad[1] = FloatPoint(tx + w - max(border.radii().topRight().width(), rightWidth), ty + max(border.radii().topRight().height(), topWidth)); - quad[2] = FloatPoint(tx + w - max(border.radii().bottomRight().width(), rightWidth), ty + h - max(border.radii().bottomRight().height(), bottomWidth)); - quad[3] = FloatPoint(tx + w, ty + h); - break; - default: + quad[0] = outerRect.maxXMinYCorner(); + quad[1] = innerRect.maxXMinYCorner(); + quad[2] = innerRect.maxXMaxYCorner(); + quad[3] = outerRect.maxXMaxYCorner(); + + if (!innerBorder.radii().topRight().isZero()) + findInnerVertex(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), centerPoint, quad[1]); + + if (!innerBorder.radii().bottomRight().isZero()) + findInnerVertex(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), centerPoint, quad[2]); break; } @@ -1561,6 +2027,7 @@ void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContex return; } + // Square off the end which shouldn't be affected by antialiasing, and clip. FloatPoint firstQuad[4]; firstQuad[0] = quad[0]; firstQuad[1] = quad[1]; @@ -1575,6 +2042,7 @@ void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContex : FloatPoint(quad[1].x(), quad[0].y()); secondQuad[2] = quad[2]; secondQuad[3] = quad[3]; + // Antialiasing affects the second side. graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches); } @@ -1599,21 +2067,13 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int if (context->paintingDisabled() || !s->boxShadow()) return; - RoundedIntRect border(tx, ty, w, h); + IntRect borderRect(tx, ty, w, h); + RoundedIntRect border = (shadowStyle == Inset) ? s->getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge) + : s->getRoundedBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); + bool hasBorderRadius = s->hasBorderRadius(); bool isHorizontal = s->isHorizontalWritingMode(); - - if (shadowStyle == Inset) - border.setRect(IntRect(border.rect().x() + (includeLogicalLeftEdge || !isHorizontal ? borderLeft() : 0), - border.rect().y() + (includeLogicalLeftEdge || isHorizontal ? borderTop() : 0), - border.rect().width() - ((includeLogicalLeftEdge || !isHorizontal) ? borderLeft() : 0) - ((includeLogicalRightEdge || !isHorizontal) ? borderRight() : 0), - border.rect().height() - ((includeLogicalLeftEdge || isHorizontal) ? borderTop() : 0) - ((includeLogicalRightEdge || isHorizontal) ? borderBottom() : 0))); - - if (hasBorderRadius && (includeLogicalLeftEdge || includeLogicalRightEdge)) { - RoundedIntRect::Radii radii = ((shadowStyle == Inset) ? s->getRoundedInnerBorderWithBorderWidths(border.rect(), borderTop(), borderBottom(), borderLeft(), borderRight()) : s->getRoundedBorderFor(border.rect())).radii(); - border.includeLogicalEdges(radii, isHorizontal, includeLogicalLeftEdge, includeLogicalRightEdge); - } - + bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && s->visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255; for (const ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next()) { if (shadow->style() != shadowStyle) @@ -1661,9 +2121,7 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int if (!rectToClipOut.isEmpty()) context->clipOutRoundedRect(rectToClipOut); - if (shadowSpread < 0) - fillRect.expandRadii(shadowSpread); - + fillRect.expandRadii(shadowSpread); context->fillRoundedRect(fillRect, Color::black, s->colorSpace()); } else { IntRect rectToClipOut = border.rect(); @@ -1717,13 +2175,15 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255); IntRect outerRect = areaCastingShadowInHole(border.rect(), shadowBlur, shadowSpread, shadowOffset); + RoundedIntRect roundedHole(holeRect, border.radii()); + context->save(); - Path path; if (hasBorderRadius) { Path path; - path.addRoundedRect(border.rect(), border.radii().topLeft(), border.radii().topRight(), border.radii().bottomLeft(), border.radii().bottomRight()); + path.addRoundedRect(border); context->clip(path); + roundedHole.shrinkRadii(shadowSpread); } else context->clip(border.rect()); @@ -1731,15 +2191,11 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int context->translate(extraOffset.width(), extraOffset.height()); shadowOffset -= extraOffset; - if (hasBorderRadius && shadowSpread > 0) - border.shrinkRadii(shadowSpread); - if (shadow->isWebkitBoxShadow()) context->setLegacyShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); else context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); - RoundedIntRect roundedHole(holeRect, border.radii()); context->fillRectWithRoundedHole(outerRect, roundedHole, fillColor, s->colorSpace()); context->restore(); |