diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/WebCore/rendering/RenderBoxModelObject.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/WebCore/rendering/RenderBoxModelObject.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderBoxModelObject.cpp | 1681 |
1 files changed, 961 insertions, 720 deletions
diff --git a/Source/WebCore/rendering/RenderBoxModelObject.cpp b/Source/WebCore/rendering/RenderBoxModelObject.cpp index 749bee2bf..767fa919b 100644 --- a/Source/WebCore/rendering/RenderBoxModelObject.cpp +++ b/Source/WebCore/rendering/RenderBoxModelObject.cpp @@ -26,11 +26,7 @@ #include "config.h" #include "RenderBoxModelObject.h" -#include "BorderEdge.h" -#include "FloatRoundedRect.h" #include "Frame.h" -#include "FrameView.h" -#include "GeometryUtilities.h" #include "GraphicsContext.h" #include "HTMLFrameOwnerElement.h" #include "HTMLNames.h" @@ -41,21 +37,19 @@ #include "RenderBlock.h" #include "RenderInline.h" #include "RenderLayer.h" -#include "RenderLayerBacking.h" -#include "RenderLayerCompositor.h" -#include "RenderMultiColumnFlowThread.h" #include "RenderNamedFlowFragment.h" #include "RenderNamedFlowThread.h" #include "RenderRegion.h" #include "RenderTable.h" -#include "RenderTableRow.h" -#include "RenderText.h" -#include "RenderTextFragment.h" #include "RenderView.h" #include "ScrollingConstraints.h" #include "Settings.h" #include "TransformState.h" -#include <wtf/NeverDestroyed.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerBacking.h" +#include "RenderLayerCompositor.h" +#endif namespace WebCore { @@ -69,16 +63,12 @@ using namespace HTMLNames; // <b><i><p>Hello</p></i></b>. In this example the <i> will have a block as // its continuation but the <b> will just have an inline as its continuation. typedef HashMap<const RenderBoxModelObject*, RenderBoxModelObject*> ContinuationMap; -static ContinuationMap& continuationMap() -{ - static NeverDestroyed<ContinuationMap> map; - return map; -} +static ContinuationMap* continuationMap = 0; // This HashMap is similar to the continuation map, but connects first-letter // renderers to their remaining text fragments. typedef HashMap<const RenderBoxModelObject*, RenderTextFragment*> FirstLetterRemainingTextMap; -static FirstLetterRemainingTextMap* firstLetterRemainingTextMap = nullptr; +static FirstLetterRemainingTextMap* firstLetterRemainingTextMap = 0; void RenderBoxModelObject::setSelectionState(SelectionState state) { @@ -99,6 +89,7 @@ void RenderBoxModelObject::setSelectionState(SelectionState state) containingBlock->setSelectionState(state); } +#if USE(ACCELERATED_COMPOSITING) void RenderBoxModelObject::contentChanged(ContentChangeType changeType) { if (!hasLayer()) @@ -160,14 +151,20 @@ void RenderBoxModelObject::suspendAnimations(double time) ASSERT(isComposited()); layer()->backing()->suspendAnimations(time); } +#endif -RenderBoxModelObject::RenderBoxModelObject(Element& element, Ref<RenderStyle>&& style, BaseTypeFlags baseTypeFlags) - : RenderLayerModelObject(element, WTFMove(style), baseTypeFlags | RenderBoxModelObjectFlag) +bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Image* image, const void* layer, const LayoutSize& size) { + return view().imageQualityController().shouldPaintAtLowQuality(context, this, image, layer, size); } -RenderBoxModelObject::RenderBoxModelObject(Document& document, Ref<RenderStyle>&& style, BaseTypeFlags baseTypeFlags) - : RenderLayerModelObject(document, WTFMove(style), baseTypeFlags | RenderBoxModelObjectFlag) +RenderBoxModelObject::RenderBoxModelObject(Element& element, PassRef<RenderStyle> style, unsigned baseTypeFlags) + : RenderLayerModelObject(element, std::move(style), baseTypeFlags | RenderBoxModelObjectFlag) +{ +} + +RenderBoxModelObject::RenderBoxModelObject(Document& document, PassRef<RenderStyle> style, unsigned baseTypeFlags) + : RenderLayerModelObject(document, std::move(style), baseTypeFlags | RenderBoxModelObjectFlag) { } @@ -177,15 +174,13 @@ RenderBoxModelObject::~RenderBoxModelObject() void RenderBoxModelObject::willBeDestroyed() { - if (hasContinuation()) { - continuation()->destroy(); - setContinuation(nullptr); - } + // A continuation of this RenderObject should be destroyed at subclasses. + ASSERT(!continuation()); // If this is a first-letter object with a remaining text fragment then the // entry needs to be cleared from the map. if (firstLetterRemainingText()) - setFirstLetterRemainingText(nullptr); + setFirstLetterRemainingText(0); if (!documentBeingDestroyed()) view().imageQualityController().rendererWillBeDestroyed(*this); @@ -193,11 +188,6 @@ void RenderBoxModelObject::willBeDestroyed() RenderLayerModelObject::willBeDestroyed(); } -bool RenderBoxModelObject::hasBoxDecorationStyle() const -{ - return hasBackground() || style().hasBorderDecoration() || style().hasAppearance() || style().boxShadow(); -} - void RenderBoxModelObject::updateFromStyle() { RenderLayerModelObject::updateFromStyle(); @@ -205,12 +195,10 @@ void RenderBoxModelObject::updateFromStyle() // Set the appropriate bits for a box model object. Since all bits are cleared in styleWillChange, // we only check for bits that could possibly be set to true. const RenderStyle& styleToUse = style(); - setHasBoxDecorations(hasBoxDecorationStyle()); + setHasBoxDecorations(hasBackground() || styleToUse.hasBorder() || styleToUse.hasAppearance() || styleToUse.boxShadow()); setInline(styleToUse.isDisplayInlineType()); setPositionState(styleToUse.position()); setHorizontalWritingMode(styleToUse.isHorizontalWritingMode()); - if (styleToUse.isFlippedBlocksWritingMode()) - view().frameView().setHasFlippedBlockRenderers(true); } static LayoutSize accumulateInFlowPositionOffsets(const RenderObject* child) @@ -218,9 +206,13 @@ static LayoutSize accumulateInFlowPositionOffsets(const RenderObject* child) if (!child->isAnonymousBlock() || !child->isInFlowPositioned()) return LayoutSize(); LayoutSize offset; - for (RenderElement* parent = downcast<RenderBlock>(*child).inlineElementContinuation(); is<RenderInline>(parent); parent = parent->parent()) { - if (parent->isInFlowPositioned()) - offset += downcast<RenderInline>(*parent).offsetForInFlowPosition(); + RenderElement* p = toRenderBlock(child)->inlineElementContinuation(); + while (p && p->isRenderInline()) { + if (p->isInFlowPositioned()) { + RenderInline* renderInline = toRenderInline(p); + offset += renderInline->offsetForInFlowPosition(); + } + p = p->parent(); } return offset; } @@ -234,13 +226,13 @@ bool RenderBoxModelObject::hasAutoHeightOrContainingBlockWithAutoHeight() const // For percentage heights: The percentage is calculated with respect to the height of the generated box's // containing block. If the height of the containing block is not specified explicitly (i.e., it depends // on content height), and this element is not absolutely positioned, the value computes to 'auto'. - if (!logicalHeightLength.isPercentOrCalculated() || isOutOfFlowPositioned() || document().inQuirksMode()) + if (!logicalHeightLength.isPercent() || isOutOfFlowPositioned() || document().inQuirksMode()) return false; // Anonymous block boxes are ignored when resolving percentage values that would refer to it: // the closest non-anonymous ancestor box is used instead. RenderBlock* cb = containingBlock(); - while (cb && !is<RenderView>(*cb) && cb->isAnonymous()) + while (cb->isAnonymous() && !cb->isRenderView()) cb = cb->containingBlock(); // Matching RenderBox::percentageLogicalHeightIsResolvableFromBlock() by @@ -249,37 +241,30 @@ bool RenderBoxModelObject::hasAutoHeightOrContainingBlockWithAutoHeight() const // don't care if the cell specified a height or not. if (cb->isTableCell()) return false; - - // Match RenderBox::availableLogicalHeightUsing by special casing - // the render view. The available height is taken from the frame. - if (cb->isRenderView()) - return false; - - if (cb->isOutOfFlowPositioned() && !cb->style().logicalTop().isAuto() && !cb->style().logicalBottom().isAuto()) + + if (!cb->style().logicalHeight().isAuto() || (!cb->style().logicalTop().isAuto() && !cb->style().logicalBottom().isAuto())) return false; - // If the height of the containing block computes to 'auto', then it hasn't been 'specified explictly'. - return cb->hasAutoHeightOrContainingBlockWithAutoHeight(); + return true; } LayoutSize RenderBoxModelObject::relativePositionOffset() const { - // This function has been optimized to avoid calls to containingBlock() in the common case - // where all values are either auto or fixed. - LayoutSize offset = accumulateInFlowPositionOffsets(this); + RenderBlock* containingBlock = this->containingBlock(); + // Objects that shrink to avoid floats normally use available line width when computing containing block width. However // in the case of relative positioning using percentages, we can't do this. The offset should always be resolved using the // available width of the containing block. Therefore we don't use containingBlockLogicalWidthForContent() here, but instead explicitly // call availableWidth on our containing block. if (!style().left().isAuto()) { - if (!style().right().isAuto() && !containingBlock()->style().isLeftToRightDirection()) - offset.setWidth(-valueForLength(style().right(), !style().right().isFixed() ? containingBlock()->availableWidth() : LayoutUnit())); + if (!style().right().isAuto() && !containingBlock->style().isLeftToRightDirection()) + offset.setWidth(-valueForLength(style().right(), containingBlock->availableWidth())); else - offset.expand(valueForLength(style().left(), !style().left().isFixed() ? containingBlock()->availableWidth() : LayoutUnit()), 0); + offset.expand(valueForLength(style().left(), containingBlock->availableWidth()), 0); } else if (!style().right().isAuto()) { - offset.expand(-valueForLength(style().right(), !style().right().isFixed() ? containingBlock()->availableWidth() : LayoutUnit()), 0); + offset.expand(-valueForLength(style().right(), containingBlock->availableWidth()), 0); } // If the containing block of a relatively positioned element does not @@ -289,16 +274,16 @@ LayoutSize RenderBoxModelObject::relativePositionOffset() const // calculate the percent offset based on this height. // See <https://bugs.webkit.org/show_bug.cgi?id=26396>. if (!style().top().isAuto() - && (!style().top().isPercentOrCalculated() - || !containingBlock()->hasAutoHeightOrContainingBlockWithAutoHeight() - || containingBlock()->stretchesToViewport())) - offset.expand(0, valueForLength(style().top(), !style().top().isFixed() ? containingBlock()->availableHeight() : LayoutUnit())); + && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() + || !style().top().isPercent() + || containingBlock->stretchesToViewport())) + offset.expand(0, valueForLength(style().top(), containingBlock->availableHeight())); else if (!style().bottom().isAuto() - && (!style().bottom().isPercentOrCalculated() - || !containingBlock()->hasAutoHeightOrContainingBlockWithAutoHeight() - || containingBlock()->stretchesToViewport())) - offset.expand(0, -valueForLength(style().bottom(), !style().bottom().isFixed() ? containingBlock()->availableHeight() : LayoutUnit())); + && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() + || !style().bottom().isPercent() + || containingBlock->stretchesToViewport())) + offset.expand(0, -valueForLength(style().bottom(), containingBlock->availableHeight())); return offset; } @@ -311,13 +296,14 @@ LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const L return LayoutPoint(); LayoutPoint referencePoint = startPoint; + referencePoint.move(parent()->offsetForColumns(referencePoint)); // If the offsetParent of the element is null, or is the HTML body element, // return the distance between the canvas origin and the left border edge // of the element and stop this algorithm. if (const RenderBoxModelObject* offsetParent = this->offsetParent()) { - if (is<RenderBox>(*offsetParent) && !offsetParent->isBody() && !is<RenderTable>(*offsetParent)) - referencePoint.move(-downcast<RenderBox>(*offsetParent).borderLeft(), -downcast<RenderBox>(*offsetParent).borderTop()); + if (offsetParent->isBox() && !offsetParent->isBody() && !offsetParent->isTable()) + referencePoint.move(-toRenderBox(offsetParent)->borderLeft(), -toRenderBox(offsetParent)->borderTop()); if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) { if (isRelPositioned()) referencePoint.move(relativePositionOffset()); @@ -327,29 +313,23 @@ LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const L // CSS regions specification says that region flows should return the body element as their offsetParent. // Since we will bypass the body’s renderer anyway, just end the loop if we encounter a region flow (named flow thread). // See http://dev.w3.org/csswg/css-regions/#cssomview-offset-attributes - auto* ancestor = parent(); - while (ancestor != offsetParent && !is<RenderNamedFlowThread>(*ancestor)) { + auto curr = parent(); + while (curr != offsetParent && !curr->isRenderNamedFlowThread()) { // FIXME: What are we supposed to do inside SVG content? - - if (is<RenderMultiColumnFlowThread>(*ancestor)) { - // We need to apply a translation based off what region we are inside. - RenderRegion* region = downcast<RenderMultiColumnFlowThread>(*ancestor).physicalTranslationFromFlowToRegion(referencePoint); - if (region) - referencePoint.moveBy(region->topLeftLocation()); - } else if (!isOutOfFlowPositioned()) { - if (is<RenderBox>(*ancestor) && !is<RenderTableRow>(*ancestor)) - referencePoint.moveBy(downcast<RenderBox>(*ancestor).topLeftLocation()); + if (!isOutOfFlowPositioned()) { + if (curr->isBox() && !curr->isTableRow()) + referencePoint.moveBy(toRenderBox(curr)->topLeftLocation()); + referencePoint.move(curr->parent()->offsetForColumns(referencePoint)); } - - ancestor = ancestor->parent(); + curr = curr->parent(); } // Compute the offset position for elements inside named flow threads for which the offsetParent was the body. // See https://bugs.webkit.org/show_bug.cgi?id=115899 - if (is<RenderNamedFlowThread>(*ancestor)) - referencePoint = downcast<RenderNamedFlowThread>(*ancestor).adjustedPositionRelativeToOffsetParent(*this, referencePoint); - else if (is<RenderBox>(*offsetParent) && offsetParent->isBody() && !offsetParent->isPositioned()) - referencePoint.moveBy(downcast<RenderBox>(*offsetParent).topLeftLocation()); + if (curr->isRenderNamedFlowThread()) + referencePoint = toRenderNamedFlowThread(curr)->adjustedPositionRelativeToOffsetParent(*this, referencePoint); + else if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent->isPositioned()) + referencePoint.moveBy(toRenderBox(offsetParent)->topLeftLocation()); } } @@ -362,7 +342,7 @@ void RenderBoxModelObject::computeStickyPositionConstraints(StickyPositionViewpo RenderBlock* containingBlock = this->containingBlock(); RenderLayer* enclosingClippingLayer = layer()->enclosingOverflowClipLayer(ExcludeSelf); - RenderBox& enclosingClippingBox = enclosingClippingLayer ? downcast<RenderBox>(enclosingClippingLayer->renderer()) : view(); + RenderBox& enclosingClippingBox = enclosingClippingLayer ? toRenderBox(enclosingClippingLayer->renderer()) : view(); LayoutRect containerContentRect; if (!enclosingClippingLayer || (containingBlock != &enclosingClippingBox)) @@ -438,30 +418,26 @@ void RenderBoxModelObject::computeStickyPositionConstraints(StickyPositionViewpo } } -FloatRect RenderBoxModelObject::constrainingRectForStickyPosition() const +LayoutSize RenderBoxModelObject::stickyPositionOffset() const { + FloatRect constrainingRect; + + ASSERT(hasLayer()); RenderLayer* enclosingClippingLayer = layer()->enclosingOverflowClipLayer(ExcludeSelf); if (enclosingClippingLayer) { - RenderBox& enclosingClippingBox = downcast<RenderBox>(enclosingClippingLayer->renderer()); + RenderBox& enclosingClippingBox = toRenderBox(enclosingClippingLayer->renderer()); LayoutRect clipRect = enclosingClippingBox.overflowClipRect(LayoutPoint(), 0); // FIXME: make this work in regions. - clipRect.contract(LayoutSize(enclosingClippingBox.paddingLeft() + enclosingClippingBox.paddingRight(), - enclosingClippingBox.paddingTop() + enclosingClippingBox.paddingBottom())); - - FloatRect constrainingRect = enclosingClippingBox.localToContainerQuad(FloatRect(clipRect), &view()).boundingBox(); + constrainingRect = enclosingClippingBox.localToContainerQuad(FloatRect(clipRect), &view()).boundingBox(); FloatPoint scrollOffset = FloatPoint() + enclosingClippingLayer->scrollOffset(); constrainingRect.setLocation(scrollOffset); - return constrainingRect; + } else { + LayoutRect viewportRect = view().frameView().viewportConstrainedVisibleContentRect(); + float scale = view().frameView().frame().frameScaleFactor(); + viewportRect.scale(1 / scale); + constrainingRect = viewportRect; } - return view().frameView().viewportConstrainedVisibleContentRect(); -} - -LayoutSize RenderBoxModelObject::stickyPositionOffset() const -{ - ASSERT(hasLayer()); - - FloatRect constrainingRect = constrainingRectForStickyPosition(); StickyPositionViewportConstraints constraints; computeStickyPositionConstraints(constraints, constrainingRect); @@ -494,10 +470,20 @@ LayoutUnit RenderBoxModelObject::offsetTop() const return adjustedPositionRelativeToOffsetParent(LayoutPoint()).y(); } +int RenderBoxModelObject::pixelSnappedOffsetWidth() const +{ + return snapSizeToPixel(offsetWidth(), offsetLeft()); +} + +int RenderBoxModelObject::pixelSnappedOffsetHeight() const +{ + return snapSizeToPixel(offsetHeight(), offsetTop()); +} + LayoutUnit RenderBoxModelObject::computedCSSPadding(const Length& padding) const { LayoutUnit w = 0; - if (padding.isPercentOrCalculated()) + if (padding.isPercent()) w = containingBlockLogicalWidthForContent(); return minimumValueForLength(padding, w); } @@ -505,70 +491,67 @@ LayoutUnit RenderBoxModelObject::computedCSSPadding(const Length& padding) const RoundedRect RenderBoxModelObject::getBackgroundRoundedRect(const LayoutRect& borderRect, InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const { - RoundedRect border = style().getRoundedBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); + RoundedRect border = style().getRoundedBorderFor(borderRect, &view(), includeLogicalLeftEdge, includeLogicalRightEdge); if (box && (box->nextLineBox() || box->prevLineBox())) { - RoundedRect segmentBorder = style().getRoundedBorderFor(LayoutRect(0, 0, inlineBoxWidth, inlineBoxHeight), includeLogicalLeftEdge, includeLogicalRightEdge); + RoundedRect segmentBorder = style().getRoundedBorderFor(LayoutRect(0, 0, inlineBoxWidth, inlineBoxHeight), &view(), includeLogicalLeftEdge, includeLogicalRightEdge); border.setRadii(segmentBorder.radii()); } + return border; } -void RenderBoxModelObject::clipRoundedInnerRect(GraphicsContext& context, const FloatRect& rect, const FloatRoundedRect& clipRect) +void RenderBoxModelObject::clipRoundedInnerRect(GraphicsContext * context, const LayoutRect& rect, const RoundedRect& clipRect) { if (clipRect.isRenderable()) - context.clipRoundedRect(clipRect); + context->clipRoundedRect(clipRect); else { // We create a rounded rect for each of the corners and clip it, while making sure we clip opposing corners together. if (!clipRect.radii().topLeft().isEmpty() || !clipRect.radii().bottomRight().isEmpty()) { - FloatRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.maxX() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y()); - FloatRoundedRect::Radii topCornerRadii; + IntRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.maxX() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y()); + RoundedRect::Radii topCornerRadii; topCornerRadii.setTopLeft(clipRect.radii().topLeft()); - context.clipRoundedRect(FloatRoundedRect(topCorner, topCornerRadii)); + context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); - FloatRect bottomCorner(rect.x(), rect.y(), clipRect.rect().maxX() - rect.x(), clipRect.rect().maxY() - rect.y()); - FloatRoundedRect::Radii bottomCornerRadii; + IntRect bottomCorner(rect.x(), rect.y(), clipRect.rect().maxX() - rect.x(), clipRect.rect().maxY() - rect.y()); + RoundedRect::Radii bottomCornerRadii; bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight()); - context.clipRoundedRect(FloatRoundedRect(bottomCorner, bottomCornerRadii)); + context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii)); } if (!clipRect.radii().topRight().isEmpty() || !clipRect.radii().bottomLeft().isEmpty()) { - FloatRect topCorner(rect.x(), clipRect.rect().y(), clipRect.rect().maxX() - rect.x(), rect.maxY() - clipRect.rect().y()); - FloatRoundedRect::Radii topCornerRadii; + IntRect topCorner(rect.x(), clipRect.rect().y(), clipRect.rect().maxX() - rect.x(), rect.maxY() - clipRect.rect().y()); + RoundedRect::Radii topCornerRadii; topCornerRadii.setTopRight(clipRect.radii().topRight()); - context.clipRoundedRect(FloatRoundedRect(topCorner, topCornerRadii)); + context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); - FloatRect bottomCorner(clipRect.rect().x(), rect.y(), rect.maxX() - clipRect.rect().x(), clipRect.rect().maxY() - rect.y()); - FloatRoundedRect::Radii bottomCornerRadii; + IntRect bottomCorner(clipRect.rect().x(), rect.y(), rect.maxX() - clipRect.rect().x(), clipRect.rect().maxY() - rect.y()); + RoundedRect::Radii bottomCornerRadii; bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft()); - context.clipRoundedRect(FloatRoundedRect(bottomCorner, bottomCornerRadii)); + context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii)); } } } -static LayoutRect shrinkRectByOneDevicePixel(const GraphicsContext& context, const LayoutRect& rect, float devicePixelRatio) +static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, const LayoutRect& rect) { LayoutRect shrunkRect = rect; - AffineTransform transform = context.getCTM(); - shrunkRect.inflateX(-ceilToDevicePixel(LayoutUnit::fromPixel(1) / transform.xScale(), devicePixelRatio)); - shrunkRect.inflateY(-ceilToDevicePixel(LayoutUnit::fromPixel(1) / transform.yScale(), devicePixelRatio)); + AffineTransform transform = context->getCTM(); + shrunkRect.inflateX(-static_cast<LayoutUnit>(ceil(1 / transform.xScale()))); + shrunkRect.inflateY(-static_cast<LayoutUnit>(ceil(1 / transform.yScale()))); return shrunkRect; } -LayoutRect RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance(const GraphicsContext& context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance) const +LayoutRect RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance) const { - if (bleedAvoidance != BackgroundBleedBackgroundOverBorder) - return rect; - - // We shrink the rectangle by one device pixel on each side to make it fully overlap the anti-aliased background border - return shrinkRectByOneDevicePixel(context, rect, document().deviceScaleFactor()); + // We shrink the rectangle by one pixel on each side to make it fully overlap the anti-aliased background border + return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? shrinkRectByOnePixel(context, rect) : rect; } -RoundedRect RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance(const GraphicsContext& context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const +RoundedRect RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const { if (bleedAvoidance == BackgroundBleedShrinkBackground) { - // We shrink the rectangle by one device pixel on each side because the bleed is one pixel maximum. - return getBackgroundRoundedRect(shrinkRectByOneDevicePixel(context, borderRect, document().deviceScaleFactor()), box, boxSize.width(), boxSize.height(), - includeLogicalLeftEdge, includeLogicalRightEdge); + // We shrink the rectangle by one pixel on each side because the bleed is one pixel maximum. + return getBackgroundRoundedRect(shrinkRectByOnePixel(context, borderRect), box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge); } if (bleedAvoidance == BackgroundBleedBackgroundOverBorder) return style().getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); @@ -576,7 +559,7 @@ RoundedRect RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance return getBackgroundRoundedRect(borderRect, box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge); } -static void applyBoxShadowForBackground(GraphicsContext& context, RenderStyle* style) +static void applyBoxShadowForBackground(GraphicsContext* context, RenderStyle* style) { const ShadowData* boxShadow = style->boxShadow(); while (boxShadow->style() != Normal) @@ -584,42 +567,38 @@ static void applyBoxShadowForBackground(GraphicsContext& context, RenderStyle* s FloatSize shadowOffset(boxShadow->x(), boxShadow->y()); if (!boxShadow->isWebkitBoxShadow()) - context.setShadow(shadowOffset, boxShadow->radius(), boxShadow->color()); + context->setShadow(shadowOffset, boxShadow->radius(), boxShadow->color(), style->colorSpace()); else - context.setLegacyShadow(shadowOffset, boxShadow->radius(), boxShadow->color()); -} - -InterpolationQuality RenderBoxModelObject::chooseInterpolationQuality(GraphicsContext& context, Image& image, const void* layer, const LayoutSize& size) -{ - return view().imageQualityController().chooseInterpolationQuality(context, this, image, layer, size); + context->setLegacyShadow(shadowOffset, boxShadow->radius(), boxShadow->color(), style->colorSpace()); } -void RenderBoxModelObject::paintMaskForTextFillBox(ImageBuffer* maskImage, const IntRect& maskRect, InlineFlowBox* box, const LayoutRect& scrolledPaintRect) +void RenderBoxModelObject::paintMaskForTextFillBox(ImageBuffer* maskImage, const IntRect& maskRect, InlineFlowBox* box, const LayoutRect& scrolledPaintRect, RenderRegion* region) { - GraphicsContext& maskImageContext = maskImage->context(); - maskImageContext.translate(-maskRect.x(), -maskRect.y()); + GraphicsContext* maskImageContext = maskImage->context(); + maskImageContext->translate(-maskRect.x(), -maskRect.y()); // 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, PaintBehaviorForceBlackText); + PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, PaintBehaviorForceBlackText, 0, region); if (box) { const RootInlineBox& rootBox = box->root(); box->paint(info, LayoutPoint(scrolledPaintRect.x() - box->x(), scrolledPaintRect.y() - box->y()), rootBox.lineTop(), rootBox.lineBottom()); } else if (isRenderNamedFlowFragmentContainer()) { - RenderNamedFlowFragment& region = *downcast<RenderBlockFlow>(*this).renderNamedFlowFragment(); - if (region.isValid()) - region.flowThread()->layer()->paintNamedFlowThreadInsideRegion(maskImageContext, ®ion, maskRect, maskRect.location(), PaintBehaviorForceBlackText, RenderLayer::PaintLayerTemporaryClipRects); + RenderNamedFlowFragment* region = toRenderBlockFlow(this)->renderNamedFlowFragment(); + if (!region->flowThread()) + return; + region->flowThread()->layer()->paintNamedFlowThreadInsideRegion(maskImageContext, region, maskRect, maskRect.location(), PaintBehaviorForceBlackText, RenderLayer::PaintLayerTemporaryClipRects); } else { - LayoutSize localOffset = is<RenderBox>(*this) ? downcast<RenderBox>(*this).locationOffset() : LayoutSize(); + LayoutSize localOffset = isBox() ? toRenderBox(this)->locationOffset() : LayoutSize(); paint(info, scrolledPaintRect.location() - localOffset); } } void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer* bgLayer, const LayoutRect& rect, - BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, CompositeOperator op, RenderElement* backgroundObject, BaseBackgroundColorUsage baseBgColorUsage) + BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, CompositeOperator op, RenderElement* backgroundObject) { - GraphicsContext& context = paintInfo.context(); - if (context.paintingDisabled() || rect.isEmpty()) + GraphicsContext* context = paintInfo.context; + if (context->paintingDisabled() || rect.isEmpty()) return; bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true; @@ -628,7 +607,7 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co bool hasRoundedBorder = style().hasBorderRadius() && (includeLeftEdge || includeRightEdge); bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment; bool isBorderFill = bgLayer->clip() == BorderFillBox; - bool isRoot = this->isDocumentElementRenderer(); + bool isRoot = this->isRoot(); Color bgColor = color; StyleImage* bgImage = bgLayer->image(); @@ -658,44 +637,37 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co } } - bool baseBgColorOnly = (baseBgColorUsage == BaseBackgroundColorOnly); - if (baseBgColorOnly && (!isRoot || bgLayer->next() || (bgColor.isValid() && !bgColor.hasAlpha()))) - return; - bool colorVisible = bgColor.isValid() && bgColor.alpha(); - float deviceScaleFactor = document().deviceScaleFactor(); - FloatRect pixelSnappedRect = snapRectToDevicePixels(rect, deviceScaleFactor); - + // Fast path for drawing simple color backgrounds. if (!isRoot && !clippedWithLocalScrolling && !shouldPaintBackgroundImage && isBorderFill && !bgLayer->next()) { if (!colorVisible) return; - bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(rect.location(), bleedAvoidance, box); - GraphicsContextStateSaver shadowStateSaver(context, boxShadowShouldBeAppliedToBackground); + bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box); + GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground); if (boxShadowShouldBeAppliedToBackground) applyBoxShadowForBackground(context, &style()); if (hasRoundedBorder && bleedAvoidance != BackgroundBleedUseTransparencyLayer) { - FloatRoundedRect pixelSnappedBorder = backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, - includeLeftEdge, includeRightEdge).pixelSnappedRoundedRectForPainting(deviceScaleFactor); - if (pixelSnappedBorder.isRenderable()) - context.fillRoundedRect(pixelSnappedBorder, bgColor); + RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge); + if (border.isRenderable()) + context->fillRoundedRect(border, bgColor, style().colorSpace()); else { - context.save(); - clipRoundedInnerRect(context, pixelSnappedRect, pixelSnappedBorder); - context.fillRect(pixelSnappedBorder.rect(), bgColor); - context.restore(); + context->save(); + clipRoundedInnerRect(context, rect, border); + context->fillRect(border.rect(), bgColor, style().colorSpace()); + context->restore(); } } else - context.fillRect(pixelSnappedRect, bgColor); + context->fillRect(pixelSnappedIntRect(rect), bgColor, style().colorSpace()); return; } // BorderFillBox radius clipping is taken care of by BackgroundBleedUseTransparencyLayer bool clipToBorderRadius = hasRoundedBorder && !(isBorderFill && bleedAvoidance == BackgroundBleedUseTransparencyLayer); - GraphicsContextStateSaver clipToBorderStateSaver(context, clipToBorderRadius); + GraphicsContextStateSaver clipToBorderStateSaver(*context, clipToBorderRadius); if (clipToBorderRadius) { RoundedRect border = isBorderFill ? backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge) : getBackgroundRoundedRect(rect, box, boxSize.width(), boxSize.height(), includeLeftEdge, includeRightEdge); @@ -706,29 +678,29 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co } else if (bgLayer->clip() == PaddingFillBox) border = style().getRoundedInnerBorderFor(border.rect(), includeLeftEdge, includeRightEdge); - clipRoundedInnerRect(context, pixelSnappedRect, border.pixelSnappedRoundedRectForPainting(deviceScaleFactor)); + clipRoundedInnerRect(context, rect, border); } - LayoutUnit bLeft = includeLeftEdge ? borderLeft() : LayoutUnit::fromPixel(0); - LayoutUnit bRight = includeRightEdge ? borderRight() : LayoutUnit::fromPixel(0); + int bLeft = includeLeftEdge ? borderLeft() : 0; + int bRight = includeRightEdge ? borderRight() : 0; LayoutUnit pLeft = includeLeftEdge ? paddingLeft() : LayoutUnit(); LayoutUnit pRight = includeRightEdge ? paddingRight() : LayoutUnit(); - GraphicsContextStateSaver clipWithScrollingStateSaver(context, clippedWithLocalScrolling); + GraphicsContextStateSaver clipWithScrollingStateSaver(*context, clippedWithLocalScrolling); LayoutRect scrolledPaintRect = rect; if (clippedWithLocalScrolling) { // Clip to the overflow area. - auto& thisBox = downcast<RenderBox>(*this); - context.clip(thisBox.overflowClipRect(rect.location(), currentRenderNamedFlowFragment())); + RenderBox* thisBox = toRenderBox(this); + context->clip(thisBox->overflowClipRect(rect.location(), paintInfo.renderRegion)); // Adjust the paint rect to reflect a scrolled content box with borders at the ends. - IntSize offset = thisBox.scrolledContentOffset(); + IntSize offset = thisBox->scrolledContentOffset(); scrolledPaintRect.move(-offset); scrolledPaintRect.setWidth(bLeft + layer()->scrollWidth() + bRight); scrolledPaintRect.setHeight(borderTop() + layer()->scrollHeight() + borderBottom()); } - GraphicsContextStateSaver backgroundClipStateSaver(context, false); + GraphicsContextStateSaver backgroundClipStateSaver(*context, false); std::unique_ptr<ImageBuffer> maskImage; IntRect maskRect; @@ -741,25 +713,25 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co scrolledPaintRect.width() - bLeft - bRight - (includePadding ? pLeft + pRight : LayoutUnit()), scrolledPaintRect.height() - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : LayoutUnit())); backgroundClipStateSaver.save(); - context.clip(clipRect); + context->clip(clipRect); } } else if (bgLayer->clip() == TextFillBox) { // We have to draw our text into a mask that can then be used to clip background drawing. // First figure out how big the mask has to be. It should be no bigger than what we need // to actually render, so we should intersect the dirty rect with the border box of the background. - maskRect = snappedIntRect(rect); - maskRect.intersect(snappedIntRect(paintInfo.rect)); + maskRect = pixelSnappedIntRect(rect); + maskRect.intersect(pixelSnappedIntRect(paintInfo.rect)); // Now create the mask. - maskImage = context.createCompatibleBuffer(maskRect.size()); + maskImage = context->createCompatibleBuffer(maskRect.size()); if (!maskImage) return; - paintMaskForTextFillBox(maskImage.get(), maskRect, box, scrolledPaintRect); + paintMaskForTextFillBox(maskImage.get(), maskRect, box, scrolledPaintRect, paintInfo.renderRegion); // The mask has been created. Now we just need to clip to it. backgroundClipStateSaver.save(); - context.clip(maskRect); - context.beginTransparencyLayer(1); + context->clip(maskRect); + context->beginTransparencyLayer(1); } // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with @@ -768,20 +740,24 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co if (isRoot) { isOpaqueRoot = true; if (!bgLayer->next() && !(bgColor.isValid() && bgColor.alpha() == 255)) { - HTMLFrameOwnerElement* ownerElement = document().ownerElement(); + Element* ownerElement = document().ownerElement(); if (ownerElement) { if (!ownerElement->hasTagName(frameTag)) { // Locate the <body> element using the DOM. This is easier than trying // to crawl around a render tree with potential :before/:after content and // anonymous blocks created by inline <body> tags etc. We can locate the <body> // render object very easily via the DOM. - if (HTMLElement* body = document().bodyOrFrameset()) { + HTMLElement* body = document().body(); + if (body) { // Can't scroll a frameset document anyway. - isOpaqueRoot = is<HTMLFrameSetElement>(*body); - } else { + isOpaqueRoot = body->hasLocalName(framesetTag); + } +#if ENABLE(SVG) + else { // SVG documents and XML documents with SVG root nodes are transparent. isOpaqueRoot = !document().hasSVGRootNode(); } +#endif } } else isOpaqueRoot = !view().frameView().isTransparent(); @@ -793,125 +769,135 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co // FIXME: In the bgLayer->hasFiniteBounds() case, we could improve the culling test // by verifying whether the background image covers the entire layout rect. if (!bgLayer->next()) { - LayoutRect backgroundRect(scrolledPaintRect); - bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(rect.location(), bleedAvoidance, box); - if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage || !bgLayer->hasOpaqueImage(*this) || !bgLayer->hasRepeatXY()) { + IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect)); + bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box); + if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage || !bgLayer->hasOpaqueImage(this) || !bgLayer->hasRepeatXY()) { if (!boxShadowShouldBeAppliedToBackground) - backgroundRect.intersect(paintInfo.rect); + backgroundRect.intersect(pixelSnappedIntRect(paintInfo.rect)); - // If we have an alpha and we are painting the root element, blend with the base background color. + // If we have an alpha and we are painting the root element, go ahead and blend with the base background color. Color baseColor; bool shouldClearBackground = false; - if ((baseBgColorUsage != BaseBackgroundColorSkip) && isOpaqueRoot) { + if (isOpaqueRoot) { baseColor = view().frameView().baseBackgroundColor(); if (!baseColor.alpha()) shouldClearBackground = true; } - GraphicsContextStateSaver shadowStateSaver(context, boxShadowShouldBeAppliedToBackground); + GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground); if (boxShadowShouldBeAppliedToBackground) applyBoxShadowForBackground(context, &style()); - FloatRect backgroundRectForPainting = snapRectToDevicePixels(backgroundRect, deviceScaleFactor); if (baseColor.alpha()) { - if (!baseBgColorOnly && bgColor.alpha()) + if (bgColor.alpha()) baseColor = baseColor.blend(bgColor); - context.fillRect(backgroundRectForPainting, baseColor, CompositeCopy); - } else if (!baseBgColorOnly && bgColor.alpha()) { - CompositeOperator operation = shouldClearBackground ? CompositeCopy : context.compositeOperation(); - context.fillRect(backgroundRectForPainting, bgColor, operation); + context->fillRect(backgroundRect, baseColor, style().colorSpace(), CompositeCopy); + } else if (bgColor.alpha()) { + CompositeOperator operation = shouldClearBackground ? CompositeCopy : context->compositeOperation(); + context->fillRect(backgroundRect, bgColor, style().colorSpace(), operation); } else if (shouldClearBackground) - context.clearRect(backgroundRectForPainting); + context->clearRect(backgroundRect); } } // no progressive loading of the background image - if (!baseBgColorOnly && shouldPaintBackgroundImage) { - BackgroundImageGeometry geometry = calculateBackgroundImageGeometry(paintInfo.paintContainer, *bgLayer, rect.location(), scrolledPaintRect, backgroundObject); - geometry.clip(LayoutRect(pixelSnappedRect)); - RefPtr<Image> image; - if (!geometry.destRect().isEmpty() && (image = bgImage->image(backgroundObject ? backgroundObject : this, geometry.tileSize()))) { + if (shouldPaintBackgroundImage) { + BackgroundImageGeometry geometry; + calculateBackgroundImageGeometry(paintInfo.paintContainer, bgLayer, scrolledPaintRect, geometry, backgroundObject); + geometry.clip(pixelSnappedIntRect(paintInfo.rect)); + if (!geometry.destRect().isEmpty()) { CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; - context.setDrawLuminanceMask(bgLayer->maskSourceType() == MaskLuminance); - - InterpolationQuality interpolation = chooseInterpolationQuality(context, *image, bgLayer, geometry.tileSize()); - context.drawTiledImage(*image, geometry.destRect(), toLayoutPoint(geometry.relativePhase()), geometry.tileSize(), geometry.spaceSize(), ImagePaintingOptions(compositeOp, bgLayer->blendMode(), ImageOrientationDescription(), interpolation)); + auto clientForBackgroundImage = backgroundObject ? backgroundObject : this; + RefPtr<Image> image = bgImage->image(clientForBackgroundImage, geometry.tileSize()); + context->setDrawLuminanceMask(bgLayer->maskSourceType() == MaskLuminance); + bool useLowQualityScaling = shouldPaintAtLowQuality(context, image.get(), bgLayer, geometry.tileSize()); + if (image.get()) + image->setSpaceSize(geometry.spaceSize()); + context->drawTiledImage(image.get(), style().colorSpace(), geometry.destRect(), geometry.relativePhase(), geometry.tileSize(), + compositeOp, useLowQualityScaling, bgLayer->blendMode()); } } - if (maskImage && bgLayer->clip() == TextFillBox) { - context.drawConsumingImageBuffer(WTFMove(maskImage), maskRect, CompositeDestinationIn); - context.endTransparencyLayer(); + if (bgLayer->clip() == TextFillBox) { + context->drawImageBuffer(maskImage.get(), ColorSpaceDeviceRGB, maskRect, CompositeDestinationIn); + context->endTransparencyLayer(); } } -static inline LayoutUnit resolveWidthForRatio(LayoutUnit height, const LayoutSize& intrinsicRatio) +static inline int resolveWidthForRatio(int height, const FloatSize& intrinsicRatio) { - return height * intrinsicRatio.width() / intrinsicRatio.height(); + return ceilf(height * intrinsicRatio.width() / intrinsicRatio.height()); } -static inline LayoutUnit resolveHeightForRatio(LayoutUnit width, const LayoutSize& intrinsicRatio) +static inline int resolveHeightForRatio(int width, const FloatSize& intrinsicRatio) { - return width * intrinsicRatio.height() / intrinsicRatio.width(); + return ceilf(width * intrinsicRatio.height() / intrinsicRatio.width()); } -static inline LayoutSize resolveAgainstIntrinsicWidthOrHeightAndRatio(const LayoutSize& size, const LayoutSize& intrinsicRatio, LayoutUnit useWidth, LayoutUnit useHeight) +static inline IntSize resolveAgainstIntrinsicWidthOrHeightAndRatio(const IntSize& size, const FloatSize& intrinsicRatio, int useWidth, int useHeight) { if (intrinsicRatio.isEmpty()) { if (useWidth) - return LayoutSize(useWidth, size.height()); - return LayoutSize(size.width(), useHeight); + return IntSize(useWidth, size.height()); + return IntSize(size.width(), useHeight); } if (useWidth) - return LayoutSize(useWidth, resolveHeightForRatio(useWidth, intrinsicRatio)); - return LayoutSize(resolveWidthForRatio(useHeight, intrinsicRatio), useHeight); + return IntSize(useWidth, resolveHeightForRatio(useWidth, intrinsicRatio)); + return IntSize(resolveWidthForRatio(useHeight, intrinsicRatio), useHeight); } -static inline LayoutSize resolveAgainstIntrinsicRatio(const LayoutSize& size, const LayoutSize& intrinsicRatio) +static inline IntSize resolveAgainstIntrinsicRatio(const IntSize& size, const FloatSize& intrinsicRatio) { // Two possible solutions: (size.width(), solutionHeight) or (solutionWidth, size.height()) // "... must be assumed to be the largest dimensions..." = easiest answer: the rect with the largest surface area. - LayoutUnit solutionWidth = resolveWidthForRatio(size.height(), intrinsicRatio); - LayoutUnit solutionHeight = resolveHeightForRatio(size.width(), intrinsicRatio); + int solutionWidth = resolveWidthForRatio(size.height(), intrinsicRatio); + int solutionHeight = resolveHeightForRatio(size.width(), intrinsicRatio); if (solutionWidth <= size.width()) { if (solutionHeight <= size.height()) { // If both solutions fit, choose the one covering the larger area. - LayoutUnit areaOne = solutionWidth * size.height(); - LayoutUnit areaTwo = size.width() * solutionHeight; + int areaOne = solutionWidth * size.height(); + int areaTwo = size.width() * solutionHeight; if (areaOne < areaTwo) - return LayoutSize(size.width(), solutionHeight); - return LayoutSize(solutionWidth, size.height()); + return IntSize(size.width(), solutionHeight); + return IntSize(solutionWidth, size.height()); } // Only the first solution fits. - return LayoutSize(solutionWidth, size.height()); + return IntSize(solutionWidth, size.height()); } // Only the second solution fits, assert that. ASSERT(solutionHeight <= size.height()); - return LayoutSize(size.width(), solutionHeight); + return IntSize(size.width(), solutionHeight); } -LayoutSize RenderBoxModelObject::calculateImageIntrinsicDimensions(StyleImage* image, const LayoutSize& positioningAreaSize, ScaleByEffectiveZoomOrNot shouldScaleOrNot) const +IntSize RenderBoxModelObject::calculateImageIntrinsicDimensions(StyleImage* image, const IntSize& positioningAreaSize, ScaleByEffectiveZoomOrNot shouldScaleOrNot) const { // A generated image without a fixed size, will always return the container size as intrinsic size. if (image->isGeneratedImage() && image->usesImageContainerSize()) - return LayoutSize(positioningAreaSize.width(), positioningAreaSize.height()); + return IntSize(positioningAreaSize.width(), positioningAreaSize.height()); Length intrinsicWidth; Length intrinsicHeight; FloatSize intrinsicRatio; image->computeIntrinsicDimensions(this, intrinsicWidth, intrinsicHeight, intrinsicRatio); - ASSERT(!intrinsicWidth.isPercentOrCalculated()); - ASSERT(!intrinsicHeight.isPercentOrCalculated()); - - LayoutSize resolvedSize(intrinsicWidth.value(), intrinsicHeight.value()); - LayoutSize minimumSize(resolvedSize.width() > 0 ? 1 : 0, resolvedSize.height() > 0 ? 1 : 0); + // Intrinsic dimensions expressed as percentages must be resolved relative to the dimensions of the rectangle + // that establishes the coordinate system for the 'background-position' property. + + // FIXME: Remove unnecessary rounding when layout is off ints: webkit.org/b/63656 + if (intrinsicWidth.isPercent() && intrinsicHeight.isPercent() && intrinsicRatio.isEmpty()) { + // Resolve width/height percentages against positioningAreaSize, only if no intrinsic ratio is provided. + int resolvedWidth = static_cast<int>(round(positioningAreaSize.width() * intrinsicWidth.percent() / 100)); + int resolvedHeight = static_cast<int>(round(positioningAreaSize.height() * intrinsicHeight.percent() / 100)); + return IntSize(resolvedWidth, resolvedHeight); + } + IntSize resolvedSize(intrinsicWidth.isFixed() ? intrinsicWidth.value() : 0, intrinsicHeight.isFixed() ? intrinsicHeight.value() : 0); + IntSize minimumSize(resolvedSize.width() > 0 ? 1 : 0, resolvedSize.height() > 0 ? 1 : 0); if (shouldScaleOrNot == ScaleByEffectiveZoom) resolvedSize.scale(style().effectiveZoom()); resolvedSize.clampToMinimumSize(minimumSize); @@ -924,48 +910,51 @@ LayoutSize RenderBoxModelObject::calculateImageIntrinsicDimensions(StyleImage* i // * and no intrinsic aspect ratio, then the missing dimension is assumed to be the size of the rectangle that // establishes the coordinate system for the 'background-position' property. if (resolvedSize.width() > 0 || resolvedSize.height() > 0) - return resolveAgainstIntrinsicWidthOrHeightAndRatio(positioningAreaSize, LayoutSize(intrinsicRatio), resolvedSize.width(), resolvedSize.height()); + return resolveAgainstIntrinsicWidthOrHeightAndRatio(positioningAreaSize, intrinsicRatio, resolvedSize.width(), resolvedSize.height()); // If the image has no intrinsic dimensions and has an intrinsic ratio the dimensions must be assumed to be the // largest dimensions at that ratio such that neither dimension exceeds the dimensions of the rectangle that // establishes the coordinate system for the 'background-position' property. if (!intrinsicRatio.isEmpty()) - return resolveAgainstIntrinsicRatio(positioningAreaSize, LayoutSize(intrinsicRatio)); + return resolveAgainstIntrinsicRatio(positioningAreaSize, intrinsicRatio); // If the image has no intrinsic ratio either, then the dimensions must be assumed to be the rectangle that // establishes the coordinate system for the 'background-position' property. return positioningAreaSize; } -LayoutSize RenderBoxModelObject::calculateFillTileSize(const FillLayer& fillLayer, const LayoutSize& positioningAreaSize) const +static inline void applySubPixelHeuristicForTileSize(LayoutSize& tileSize, const IntSize& positioningAreaSize) { - StyleImage* image = fillLayer.image(); - EFillSizeType type = fillLayer.size().type; + tileSize.setWidth(positioningAreaSize.width() - tileSize.width() <= 1 ? tileSize.width().ceil() : tileSize.width().floor()); + tileSize.setHeight(positioningAreaSize.height() - tileSize.height() <= 1 ? tileSize.height().ceil() : tileSize.height().floor()); +} - LayoutSize imageIntrinsicSize; - if (image) { - imageIntrinsicSize = calculateImageIntrinsicDimensions(image, positioningAreaSize, ScaleByEffectiveZoom); - imageIntrinsicSize.scale(1 / image->imageScaleFactor(), 1 / image->imageScaleFactor()); - } else - imageIntrinsicSize = positioningAreaSize; +IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, const IntSize& positioningAreaSize) const +{ + StyleImage* image = fillLayer->image(); + EFillSizeType type = fillLayer->size().type; + IntSize imageIntrinsicSize = calculateImageIntrinsicDimensions(image, positioningAreaSize, ScaleByEffectiveZoom); + imageIntrinsicSize.scale(1 / image->imageScaleFactor(), 1 / image->imageScaleFactor()); switch (type) { case SizeLength: { LayoutSize tileSize = positioningAreaSize; - Length layerWidth = fillLayer.size().size.width(); - Length layerHeight = fillLayer.size().size.height(); + Length layerWidth = fillLayer->size().size.width(); + Length layerHeight = fillLayer->size().size.height(); if (layerWidth.isFixed()) tileSize.setWidth(layerWidth.value()); - else if (layerWidth.isPercentOrCalculated()) + else if (layerWidth.isPercent() || layerWidth.isViewportPercentage()) tileSize.setWidth(valueForLength(layerWidth, positioningAreaSize.width())); if (layerHeight.isFixed()) tileSize.setHeight(layerHeight.value()); - else if (layerHeight.isPercentOrCalculated()) + else if (layerHeight.isPercent() || layerHeight.isViewportPercentage()) tileSize.setHeight(valueForLength(layerHeight, positioningAreaSize.height())); + applySubPixelHeuristicForTileSize(tileSize, positioningAreaSize); + // If one of the values is auto we have to use the appropriate // scale to maintain our aspect ratio. if (layerWidth.isAuto() && !layerHeight.isAuto()) { @@ -980,7 +969,7 @@ LayoutSize RenderBoxModelObject::calculateFillTileSize(const FillLayer& fillLaye } tileSize.clampNegativeToZero(); - return tileSize; + return flooredIntSize(tileSize); } case SizeNone: { // If both values are ‘auto’ then the intrinsic width and/or height of the image should be used, if any. @@ -993,34 +982,54 @@ LayoutSize RenderBoxModelObject::calculateFillTileSize(const FillLayer& fillLaye FALLTHROUGH; case Contain: case Cover: { - // Scale computation needs higher precision than what LayoutUnit can offer. - FloatSize localImageIntrinsicSize = imageIntrinsicSize; - FloatSize localPositioningAreaSize = positioningAreaSize; - - float horizontalScaleFactor = localImageIntrinsicSize.width() ? (localPositioningAreaSize.width() / localImageIntrinsicSize.width()) : 1; - float verticalScaleFactor = localImageIntrinsicSize.height() ? (localPositioningAreaSize.height() / localImageIntrinsicSize.height()) : 1; + float horizontalScaleFactor = imageIntrinsicSize.width() + ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1; + float verticalScaleFactor = imageIntrinsicSize.height() + ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1; float scaleFactor = type == Contain ? std::min(horizontalScaleFactor, verticalScaleFactor) : std::max(horizontalScaleFactor, verticalScaleFactor); - float deviceScaleFactor = document().deviceScaleFactor(); - return LayoutSize(std::max<LayoutUnit>(1 / deviceScaleFactor, localImageIntrinsicSize.width() * scaleFactor), - std::max<LayoutUnit>(1 / deviceScaleFactor, localImageIntrinsicSize.height() * scaleFactor)); + return IntSize(std::max(1, static_cast<int>(imageIntrinsicSize.width() * scaleFactor)), std::max(1, static_cast<int>(imageIntrinsicSize.height() * scaleFactor))); } } ASSERT_NOT_REACHED(); - return LayoutSize(); + return IntSize(); +} + +void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatX(int xOffset) +{ + m_destRect.move(std::max(xOffset, 0), 0); + m_phase.setX(-std::min(xOffset, 0)); + m_destRect.setWidth(m_tileSize.width() + std::min(xOffset, 0)); +} +void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatY(int yOffset) +{ + m_destRect.move(0, std::max(yOffset, 0)); + m_phase.setY(-std::min(yOffset, 0)); + m_destRect.setHeight(m_tileSize.height() + std::min(yOffset, 0)); +} + +void RenderBoxModelObject::BackgroundImageGeometry::useFixedAttachment(const IntPoint& attachmentPoint) +{ + IntPoint alignedPoint = attachmentPoint; + m_phase.move(std::max(alignedPoint.x() - m_destRect.x(), 0), std::max(alignedPoint.y() - m_destRect.y(), 0)); } -static void pixelSnapBackgroundImageGeometryForPainting(LayoutRect& destinationRect, LayoutSize& tileSize, LayoutSize& phase, LayoutSize& space, float scaleFactor) +void RenderBoxModelObject::BackgroundImageGeometry::clip(const IntRect& clipRect) { - tileSize = LayoutSize(snapRectToDevicePixels(LayoutRect(destinationRect.location(), tileSize), scaleFactor).size()); - phase = LayoutSize(snapRectToDevicePixels(LayoutRect(destinationRect.location(), phase), scaleFactor).size()); - space = LayoutSize(snapRectToDevicePixels(LayoutRect(LayoutPoint(), space), scaleFactor).size()); - destinationRect = LayoutRect(snapRectToDevicePixels(destinationRect, scaleFactor)); + m_destRect.intersect(clipRect); +} + +IntPoint RenderBoxModelObject::BackgroundImageGeometry::relativePhase() const +{ + IntPoint phase = m_phase; + phase += m_destRect.location() - m_destOrigin; + return phase; } bool RenderBoxModelObject::fixedBackgroundPaintsInLocalCoordinates() const { - if (!isDocumentElementRenderer()) +#if USE(ACCELERATED_COMPOSITING) + if (!isRoot()) return false; if (view().frameView().paintBehavior() & PaintBehaviorFlattenCompositingLayers) @@ -1031,54 +1040,57 @@ bool RenderBoxModelObject::fixedBackgroundPaintsInLocalCoordinates() const return false; return rootLayer->backing()->backgroundLayerPaintsFixedRootBackground(); +#else + return false; +#endif } -static inline LayoutUnit getSpace(LayoutUnit areaSize, LayoutUnit tileSize) +static inline int getSpace(int areaSize, int tileSize) { int numberOfTiles = areaSize / tileSize; - LayoutUnit space = -1; + int space = -1; if (numberOfTiles > 1) - space = (areaSize - numberOfTiles * tileSize) / (numberOfTiles - 1); + space = roundedLayoutUnit((float)(areaSize - numberOfTiles * tileSize) / (numberOfTiles - 1)); return space; } -static LayoutUnit resolveEdgeRelativeLength(const Length& length, Edge edge, LayoutUnit availableSpace, const LayoutSize& areaSize, const LayoutSize& tileSize) -{ - LayoutUnit result = minimumValueForLength(length, availableSpace, false); - - if (edge == Edge::Right) - return areaSize.width() - tileSize.width() - result; - - if (edge == Edge::Bottom) - return areaSize.height() - tileSize.height() - result; - - return result; -} - -BackgroundImageGeometry RenderBoxModelObject::calculateBackgroundImageGeometry(const RenderLayerModelObject* paintContainer, const FillLayer& fillLayer, const LayoutPoint& paintOffset, - const LayoutRect& borderBoxRect, RenderElement* backgroundObject) const +void RenderBoxModelObject::calculateBackgroundImageGeometry(const RenderLayerModelObject* paintContainer, const FillLayer* fillLayer, const LayoutRect& paintRect, + BackgroundImageGeometry& geometry, RenderElement* backgroundObject) const { LayoutUnit left = 0; LayoutUnit top = 0; - LayoutSize positioningAreaSize; - // Determine the background positioning area and set destination rect to the background painting area. - // Destination rect will be adjusted later if the background is non-repeating. + IntSize positioningAreaSize; + IntRect snappedPaintRect = pixelSnappedIntRect(paintRect); + + // Determine the background positioning area and set destRect to the background painting area. + // destRect will be adjusted later if the background is non-repeating. // FIXME: transforms spec says that fixed backgrounds behave like scroll inside transforms. https://bugs.webkit.org/show_bug.cgi?id=15679 - LayoutRect destinationRect(borderBoxRect); - bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment; - float deviceScaleFactor = document().deviceScaleFactor(); + bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment; + +#if ENABLE(FAST_MOBILE_SCROLLING) + if (view().frameView().canBlitOnScroll()) { + // As a side effect of an optimization to blit on scroll, we do not honor the CSS + // property "background-attachment: fixed" because it may result in rendering + // artifacts. Note, these artifacts only appear if we are blitting on scroll of + // a page that has fixed background images. + fixedAttachment = false; + } +#endif + if (!fixedAttachment) { + geometry.setDestRect(snappedPaintRect); + LayoutUnit right = 0; LayoutUnit bottom = 0; // Scroll and Local. - if (fillLayer.origin() != BorderFillBox) { + if (fillLayer->origin() != BorderFillBox) { left = borderLeft(); right = borderRight(); top = borderTop(); bottom = borderBottom(); - if (fillLayer.origin() == ContentFillBox) { + if (fillLayer->origin() == ContentFillBox) { left += paddingLeft(); right += paddingRight(); top += paddingTop(); @@ -1089,166 +1101,140 @@ BackgroundImageGeometry RenderBoxModelObject::calculateBackgroundImageGeometry(c // The background of the box generated by the root element covers the entire canvas including // its margins. Since those were added in already, we have to factor them out when computing // the background positioning area. - if (isDocumentElementRenderer()) { - positioningAreaSize = downcast<RenderBox>(*this).size() - LayoutSize(left + right, top + bottom); - positioningAreaSize = LayoutSize(snapSizeToDevicePixel(positioningAreaSize, LayoutPoint(), deviceScaleFactor)); - if (view().frameView().hasExtendedBackgroundRectForPainting()) { - LayoutRect extendedBackgroundRect = view().frameView().extendedBackgroundRectForPainting(); + if (isRoot()) { + positioningAreaSize = pixelSnappedIntSize(toRenderBox(this)->size() - LayoutSize(left + right, top + bottom), toRenderBox(this)->location()); + if (view().frameView().hasExtendedBackground()) { + IntRect extendedBackgroundRect = view().frameView().extendedBackgroundRect(); left += (marginLeft() - extendedBackgroundRect.x()); top += (marginTop() - extendedBackgroundRect.y()); } - } else { - positioningAreaSize = borderBoxRect.size() - LayoutSize(left + right, top + bottom); - positioningAreaSize = LayoutSize(snapRectToDevicePixels(LayoutRect(paintOffset, positioningAreaSize), deviceScaleFactor).size()); - } + } else + positioningAreaSize = pixelSnappedIntSize(paintRect.size() - LayoutSize(left + right, top + bottom), paintRect.location()); } else { - LayoutRect viewportRect; - float topContentInset = 0; - if (frame().settings().fixedBackgroundsPaintRelativeToDocument()) - viewportRect = view().unscaledDocumentRect(); - else { - FrameView& frameView = view().frameView(); - bool useFixedLayout = frameView.useFixedLayout() && !frameView.fixedLayoutSize().isEmpty(); - - if (useFixedLayout) { - // Use the fixedLayoutSize() when useFixedLayout() because the rendering will scale - // down the frameView to to fit in the current viewport. - viewportRect.setSize(frameView.fixedLayoutSize()); - } else - viewportRect.setSize(frameView.unscaledVisibleContentSizeIncludingObscuredArea()); - - if (fixedBackgroundPaintsInLocalCoordinates()) { - if (!useFixedLayout) { - // Shifting location up by topContentInset is needed for layout tests which expect - // layout to be shifted down when calling window.internals.setTopContentInset(). - topContentInset = frameView.topContentInset(ScrollView::TopContentInsetType::WebCoreOrPlatformContentInset); - viewportRect.setLocation(LayoutPoint(0, -topContentInset)); - } - } else if (useFixedLayout || frameView.frameScaleFactor() != 1) { - // scrollPositionForFixedPosition() is adjusted for page scale and it does not include - // topContentInset so do not add it to the calculation below. - viewportRect.setLocation(frameView.scrollPositionForFixedPosition()); - } else { - // documentScrollPositionRelativeToViewOrigin() includes -topContentInset in its height - // so we need to account for that in calculating the phase size - topContentInset = frameView.topContentInset(ScrollView::TopContentInsetType::WebCoreOrPlatformContentInset); - viewportRect.setLocation(frameView.documentScrollPositionRelativeToViewOrigin()); - } + geometry.setHasNonLocalGeometry(); - top += topContentInset; + IntRect viewportRect = pixelSnappedIntRect(view().viewRect()); + if (fixedBackgroundPaintsInLocalCoordinates()) + viewportRect.setLocation(IntPoint()); + else + viewportRect.setLocation(IntPoint(view().frameView().scrollOffsetForFixedPosition())); + + if (paintContainer) { + IntPoint absoluteContainerOffset = roundedIntPoint(paintContainer->localToAbsolute(FloatPoint())); + viewportRect.moveBy(-absoluteContainerOffset); } - - if (paintContainer) - viewportRect.moveBy(LayoutPoint(-paintContainer->localToAbsolute(FloatPoint()))); - destinationRect = viewportRect; - positioningAreaSize = destinationRect.size(); - positioningAreaSize.setHeight(positioningAreaSize.height() - topContentInset); - positioningAreaSize = LayoutSize(snapRectToDevicePixels(LayoutRect(destinationRect.location(), positioningAreaSize), deviceScaleFactor).size()); + geometry.setDestRect(pixelSnappedIntRect(viewportRect)); + positioningAreaSize = geometry.destRect().size(); } auto clientForBackgroundImage = backgroundObject ? backgroundObject : this; - LayoutSize tileSize = calculateFillTileSize(fillLayer, positioningAreaSize); - if (StyleImage* layerImage = fillLayer.image()) - layerImage->setContainerSizeForRenderer(clientForBackgroundImage, tileSize, style().effectiveZoom()); - - EFillRepeat backgroundRepeatX = fillLayer.repeatX(); - EFillRepeat backgroundRepeatY = fillLayer.repeatY(); - LayoutUnit availableWidth = positioningAreaSize.width() - tileSize.width(); - LayoutUnit availableHeight = positioningAreaSize.height() - tileSize.height(); + IntSize fillTileSize = calculateFillTileSize(fillLayer, positioningAreaSize); + fillLayer->image()->setContainerSizeForRenderer(clientForBackgroundImage, fillTileSize, style().effectiveZoom()); + geometry.setTileSize(fillTileSize); + + EFillRepeat backgroundRepeatX = fillLayer->repeatX(); + EFillRepeat backgroundRepeatY = fillLayer->repeatY(); + int availableWidth = positioningAreaSize.width() - geometry.tileSize().width(); + int availableHeight = positioningAreaSize.height() - geometry.tileSize().height(); - LayoutSize spaceSize; - LayoutSize phase; - LayoutSize noRepeat; - LayoutUnit computedXPosition = resolveEdgeRelativeLength(fillLayer.xPosition(), fillLayer.backgroundXOrigin(), availableWidth, positioningAreaSize, tileSize); - if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > 0 && tileSize.width() > 0) { - int numTiles = std::max(1, roundToInt(positioningAreaSize.width() / tileSize.width())); - if (fillLayer.size().size.height().isAuto() && backgroundRepeatY != RoundFill) - tileSize.setHeight(tileSize.height() * positioningAreaSize.width() / (numTiles * tileSize.width())); + LayoutUnit computedXPosition = minimumValueForLength(fillLayer->xPosition(), availableWidth, true); + if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > 0 && fillTileSize.width() > 0) { + long nrTiles = lroundf((float)positioningAreaSize.width() / fillTileSize.width()); + if (!nrTiles) + nrTiles = 1; - tileSize.setWidth(positioningAreaSize.width() / numTiles); - phase.setWidth(tileSize.width() ? tileSize.width() - fmodf((computedXPosition + left), tileSize.width()) : 0); + if (fillLayer->size().size.height().isAuto() && backgroundRepeatY != RoundFill) + fillTileSize.setHeight(fillTileSize.height() * positioningAreaSize.width() / (nrTiles * fillTileSize.width())); + + fillTileSize.setWidth(positioningAreaSize.width() / nrTiles); + geometry.setTileSize(fillTileSize); + geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0); + geometry.setSpaceSize(FloatSize()); } - LayoutUnit computedYPosition = resolveEdgeRelativeLength(fillLayer.yPosition(), fillLayer.backgroundYOrigin(), availableHeight, positioningAreaSize, tileSize); - if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > 0 && tileSize.height() > 0) { - int numTiles = std::max(1, roundToInt(positioningAreaSize.height() / tileSize.height())); - if (fillLayer.size().size.width().isAuto() && backgroundRepeatX != RoundFill) - tileSize.setWidth(tileSize.width() * positioningAreaSize.height() / (numTiles * tileSize.height())); + LayoutUnit computedYPosition = minimumValueForLength(fillLayer->yPosition(), availableHeight, true); + if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > 0 && fillTileSize.height() > 0) { + long nrTiles = lroundf((float)positioningAreaSize.height() / fillTileSize.height()); + if (!nrTiles) + nrTiles = 1; + + if (fillLayer->size().size.width().isAuto() && backgroundRepeatX != RoundFill) + fillTileSize.setWidth(fillTileSize.width() * positioningAreaSize.height() / (nrTiles * fillTileSize.height())); - tileSize.setHeight(positioningAreaSize.height() / numTiles); - phase.setHeight(tileSize.height() ? tileSize.height() - fmodf((computedYPosition + top), tileSize.height()) : 0); + fillTileSize.setHeight(positioningAreaSize.height() / nrTiles); + geometry.setTileSize(fillTileSize); + geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0); + geometry.setSpaceSize(FloatSize()); } if (backgroundRepeatX == RepeatFill) { - phase.setWidth(tileSize.width() ? tileSize.width() - fmodf(computedXPosition + left, tileSize.width()) : 0); - spaceSize.setWidth(0); - } else if (backgroundRepeatX == SpaceFill && tileSize.width() > 0) { - LayoutUnit space = getSpace(positioningAreaSize.width(), tileSize.width()); + geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0); + geometry.setSpaceSize(FloatSize(0, geometry.spaceSize().height())); + } else if (backgroundRepeatX == SpaceFill && fillTileSize.width() > 0) { + int space = getSpace(positioningAreaSize.width(), geometry.tileSize().width()); + int actualWidth = geometry.tileSize().width() + space; + if (space >= 0) { - LayoutUnit actualWidth = tileSize.width() + space; - computedXPosition = minimumValueForLength(Length(), availableWidth, false); - spaceSize.setWidth(space); - spaceSize.setHeight(0); - phase.setWidth(actualWidth ? actualWidth - fmodf((computedXPosition + left), actualWidth) : 0); + computedXPosition = minimumValueForLength(Length(), availableWidth, true); + geometry.setSpaceSize(FloatSize(space, 0)); + geometry.setPhaseX(actualWidth ? actualWidth - roundToInt(computedXPosition + left) % actualWidth : 0); } else backgroundRepeatX = NoRepeatFill; } - if (backgroundRepeatX == NoRepeatFill) { - LayoutUnit xOffset = left + computedXPosition; - if (xOffset > 0) - destinationRect.move(xOffset, 0); - xOffset = std::min<LayoutUnit>(xOffset, 0); - phase.setWidth(-xOffset); - destinationRect.setWidth(tileSize.width() + xOffset); - spaceSize.setWidth(0); + int xOffset = fillLayer->backgroundXOrigin() == RightEdge ? availableWidth - computedXPosition : computedXPosition; + geometry.setNoRepeatX(left + xOffset); + geometry.setSpaceSize(FloatSize(0, geometry.spaceSize().height())); } if (backgroundRepeatY == RepeatFill) { - phase.setHeight(tileSize.height() ? tileSize.height() - fmodf(computedYPosition + top, tileSize.height()) : 0); - spaceSize.setHeight(0); - } else if (backgroundRepeatY == SpaceFill && tileSize.height() > 0) { - LayoutUnit space = getSpace(positioningAreaSize.height(), tileSize.height()); + geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0); + geometry.setSpaceSize(FloatSize(geometry.spaceSize().width(), 0)); + } else if (backgroundRepeatY == SpaceFill && fillTileSize.height() > 0) { + int space = getSpace(positioningAreaSize.height(), geometry.tileSize().height()); + int actualHeight = geometry.tileSize().height() + space; if (space >= 0) { - LayoutUnit actualHeight = tileSize.height() + space; - computedYPosition = minimumValueForLength(Length(), availableHeight, false); - spaceSize.setHeight(space); - phase.setHeight(actualHeight ? actualHeight - fmodf((computedYPosition + top), actualHeight) : 0); + computedYPosition = minimumValueForLength(Length(), availableHeight, true); + geometry.setSpaceSize(FloatSize(geometry.spaceSize().width(), space)); + geometry.setPhaseY(actualHeight ? actualHeight - roundToInt(computedYPosition + top) % actualHeight : 0); } else backgroundRepeatY = NoRepeatFill; } if (backgroundRepeatY == NoRepeatFill) { - LayoutUnit yOffset = top + computedYPosition; - if (yOffset > 0) - destinationRect.move(0, yOffset); - yOffset = std::min<LayoutUnit>(yOffset, 0); - phase.setHeight(-yOffset); - destinationRect.setHeight(tileSize.height() + yOffset); - spaceSize.setHeight(0); + int yOffset = fillLayer->backgroundYOrigin() == BottomEdge ? availableHeight - computedYPosition : computedYPosition; + geometry.setNoRepeatY(top + yOffset); + geometry.setSpaceSize(FloatSize(geometry.spaceSize().width(), 0)); } - if (fixedAttachment) { - LayoutPoint attachmentPoint = borderBoxRect.location(); - phase.expand(std::max<LayoutUnit>(attachmentPoint.x() - destinationRect.x(), 0), std::max<LayoutUnit>(attachmentPoint.y() - destinationRect.y(), 0)); - } + if (fixedAttachment) + geometry.useFixedAttachment(snappedPaintRect.location()); - destinationRect.intersect(borderBoxRect); - pixelSnapBackgroundImageGeometryForPainting(destinationRect, tileSize, phase, spaceSize, deviceScaleFactor); - return BackgroundImageGeometry(destinationRect, tileSize, phase, spaceSize, fixedAttachment); + geometry.clip(snappedPaintRect); + geometry.setDestOrigin(geometry.destRect().location()); } -void RenderBoxModelObject::getGeometryForBackgroundImage(const RenderLayerModelObject* paintContainer, const LayoutPoint& paintOffset, FloatRect& destRect, FloatSize& phase, FloatSize& tileSize) const +void RenderBoxModelObject::getGeometryForBackgroundImage(const RenderLayerModelObject* paintContainer, IntRect& destRect, IntPoint& phase, IntSize& tileSize) const { - LayoutRect paintRect(destRect); - BackgroundImageGeometry geometry = calculateBackgroundImageGeometry(paintContainer, *style().backgroundLayers(), paintOffset, paintRect); + const FillLayer* backgroundLayer = style().backgroundLayers(); + BackgroundImageGeometry geometry; + calculateBackgroundImageGeometry(paintContainer, backgroundLayer, destRect, geometry); phase = geometry.phase(); tileSize = geometry.tileSize(); destRect = geometry.destRect(); } -bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext& graphicsContext, const LayoutRect& rect, const RenderStyle& style, +static LayoutUnit computeBorderImageSide(Length borderSlice, LayoutUnit borderSide, LayoutUnit imageSide, LayoutUnit boxExtent, RenderView* renderView) +{ + if (borderSlice.isRelative()) + return borderSlice.value() * borderSide; + if (borderSlice.isAuto()) + return imageSide; + return valueForLength(borderSlice, boxExtent, renderView); +} + +bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, const LayoutRect& rect, const RenderStyle* style, const NinePieceImage& ninePieceImage, CompositeOperator op) { StyleImage* styleImage = ninePieceImage.image(); @@ -1258,26 +1244,236 @@ bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext& graphicsContext, if (!styleImage->isLoaded()) return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either. - if (!styleImage->canRender(this, style.effectiveZoom())) + if (!styleImage->canRender(this, style->effectiveZoom())) return false; // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function // doesn't have any understanding of the zoom that is in effect on the tile. - float deviceScaleFactor = document().deviceScaleFactor(); - LayoutRect rectWithOutsets = rect; - rectWithOutsets.expand(style.imageOutsets(ninePieceImage)); - LayoutRect destination = LayoutRect(snapRectToDevicePixels(rectWithOutsets, deviceScaleFactor)); + rectWithOutsets.expand(style->imageOutsets(ninePieceImage)); + IntRect borderImageRect = pixelSnappedIntRect(rectWithOutsets); - LayoutSize source = calculateImageIntrinsicDimensions(styleImage, destination.size(), DoNotScaleByEffectiveZoom); + IntSize imageSize = calculateImageIntrinsicDimensions(styleImage, borderImageRect.size(), DoNotScaleByEffectiveZoom); // If both values are ‘auto’ then the intrinsic width and/or height of the image should be used, if any. - styleImage->setContainerSizeForRenderer(this, source, style.effectiveZoom()); + styleImage->setContainerSizeForRenderer(this, imageSize, style->effectiveZoom()); + + int imageWidth = imageSize.width(); + int imageHeight = imageSize.height(); + RenderView* renderView = &view(); + + float imageScaleFactor = styleImage->imageScaleFactor(); + int topSlice = std::min<int>(imageHeight, valueForLength(ninePieceImage.imageSlices().top(), imageHeight)) * imageScaleFactor; + int rightSlice = std::min<int>(imageWidth, valueForLength(ninePieceImage.imageSlices().right(), imageWidth)) * imageScaleFactor; + int bottomSlice = std::min<int>(imageHeight, valueForLength(ninePieceImage.imageSlices().bottom(), imageHeight)) * imageScaleFactor; + int leftSlice = std::min<int>(imageWidth, valueForLength(ninePieceImage.imageSlices().left(), imageWidth)) * imageScaleFactor; + + ENinePieceImageRule hRule = ninePieceImage.horizontalRule(); + ENinePieceImageRule vRule = ninePieceImage.verticalRule(); + + int topWidth = computeBorderImageSide(ninePieceImage.borderSlices().top(), style->borderTopWidth(), topSlice, borderImageRect.height(), renderView); + int rightWidth = computeBorderImageSide(ninePieceImage.borderSlices().right(), style->borderRightWidth(), rightSlice, borderImageRect.width(), renderView); + int bottomWidth = computeBorderImageSide(ninePieceImage.borderSlices().bottom(), style->borderBottomWidth(), bottomSlice, borderImageRect.height(), renderView); + int leftWidth = computeBorderImageSide(ninePieceImage.borderSlices().left(), style->borderLeftWidth(), leftSlice, borderImageRect.width(), renderView); + + // Reduce the widths if they're too large. + // The spec says: Given Lwidth as the width of the border image area, Lheight as its height, and Wside as the border image width + // offset for the side, let f = min(Lwidth/(Wleft+Wright), Lheight/(Wtop+Wbottom)). If f < 1, then all W are reduced by + // multiplying them by f. + int borderSideWidth = std::max(1, leftWidth + rightWidth); + int borderSideHeight = std::max(1, topWidth + bottomWidth); + float borderSideScaleFactor = std::min((float)borderImageRect.width() / borderSideWidth, (float)borderImageRect.height() / borderSideHeight); + if (borderSideScaleFactor < 1) { + topWidth *= borderSideScaleFactor; + rightWidth *= borderSideScaleFactor; + bottomWidth *= borderSideScaleFactor; + leftWidth *= borderSideScaleFactor; + } + + bool drawLeft = leftSlice > 0 && leftWidth > 0; + bool drawTop = topSlice > 0 && topWidth > 0; + bool drawRight = rightSlice > 0 && rightWidth > 0; + bool drawBottom = bottomSlice > 0 && bottomWidth > 0; + bool drawMiddle = ninePieceImage.fill() && (imageWidth - leftSlice - rightSlice) > 0 && (borderImageRect.width() - leftWidth - rightWidth) > 0 + && (imageHeight - topSlice - bottomSlice) > 0 && (borderImageRect.height() - topWidth - bottomWidth) > 0; + + RefPtr<Image> image = styleImage->image(this, imageSize); + ColorSpace colorSpace = style->colorSpace(); + + float destinationWidth = borderImageRect.width() - leftWidth - rightWidth; + float destinationHeight = borderImageRect.height() - topWidth - bottomWidth; + + float sourceWidth = imageWidth - leftSlice - rightSlice; + float sourceHeight = imageHeight - topSlice - bottomSlice; + + float leftSideScale = drawLeft ? (float)leftWidth / leftSlice : 1; + float rightSideScale = drawRight ? (float)rightWidth / rightSlice : 1; + float topSideScale = drawTop ? (float)topWidth / topSlice : 1; + float bottomSideScale = drawBottom ? (float)bottomWidth / bottomSlice : 1; + + if (drawLeft) { + // Paint the top and bottom left corners. + + // The top left corner rect is (tx, ty, leftWidth, topWidth) + // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice) + if (drawTop) + graphicsContext->drawImage(image.get(), colorSpace, IntRect(borderImageRect.location(), IntSize(leftWidth, topWidth)), + LayoutRect(0, 0, leftSlice, topSlice), op, ImageOrientationDescription()); + + // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth) + // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice) + if (drawBottom) + graphicsContext->drawImage(image.get(), colorSpace, IntRect(borderImageRect.x(), borderImageRect.maxY() - bottomWidth, leftWidth, bottomWidth), + LayoutRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op, ImageOrientationDescription()); + + // Paint the left edge. + // Have to scale and tile into the border rect. + if (sourceHeight > 0) + graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(borderImageRect.x(), borderImageRect.y() + topWidth, leftWidth, + destinationHeight), + IntRect(0, topSlice, leftSlice, sourceHeight), + FloatSize(leftSideScale, leftSideScale), Image::StretchTile, (Image::TileRule)vRule, op); + } + + if (drawRight) { + // Paint the top and bottom right corners + // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth) + // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice) + if (drawTop) + graphicsContext->drawImage(image.get(), colorSpace, IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.y(), rightWidth, topWidth), + LayoutRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op, ImageOrientationDescription()); + + // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth) + // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice) + if (drawBottom) + graphicsContext->drawImage(image.get(), colorSpace, IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.maxY() - bottomWidth, rightWidth, bottomWidth), + LayoutRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op, ImageOrientationDescription()); + + // Paint the right edge. + if (sourceHeight > 0) + graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.y() + topWidth, rightWidth, + destinationHeight), + IntRect(imageWidth - rightSlice, topSlice, rightSlice, sourceHeight), + FloatSize(rightSideScale, rightSideScale), + Image::StretchTile, (Image::TileRule)vRule, op); + } + + // Paint the top edge. + if (drawTop && sourceWidth > 0) + graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(borderImageRect.x() + leftWidth, borderImageRect.y(), destinationWidth, topWidth), + IntRect(leftSlice, 0, sourceWidth, topSlice), + FloatSize(topSideScale, topSideScale), (Image::TileRule)hRule, Image::StretchTile, op); + + // Paint the bottom edge. + if (drawBottom && sourceWidth > 0) + graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(borderImageRect.x() + leftWidth, borderImageRect.maxY() - bottomWidth, + destinationWidth, bottomWidth), + IntRect(leftSlice, imageHeight - bottomSlice, sourceWidth, bottomSlice), + FloatSize(bottomSideScale, bottomSideScale), + (Image::TileRule)hRule, Image::StretchTile, op); + + // Paint the middle. + if (drawMiddle) { + FloatSize middleScaleFactor(1, 1); + if (drawTop) + middleScaleFactor.setWidth(topSideScale); + else if (drawBottom) + middleScaleFactor.setWidth(bottomSideScale); + if (drawLeft) + middleScaleFactor.setHeight(leftSideScale); + else if (drawRight) + middleScaleFactor.setHeight(rightSideScale); + + // For "stretch" rules, just override the scale factor and replace. We only had to do this for the + // center tile, since sides don't even use the scale factor unless they have a rule other than "stretch". + // The middle however can have "stretch" specified in one axis but not the other, so we have to + // correct the scale here. + if (hRule == StretchImageRule) + middleScaleFactor.setWidth(destinationWidth / sourceWidth); + + if (vRule == StretchImageRule) + middleScaleFactor.setHeight(destinationHeight / sourceHeight); + + graphicsContext->drawTiledImage(image.get(), colorSpace, + IntRect(borderImageRect.x() + leftWidth, borderImageRect.y() + topWidth, destinationWidth, destinationHeight), + IntRect(leftSlice, topSlice, sourceWidth, sourceHeight), + middleScaleFactor, (Image::TileRule)hRule, (Image::TileRule)vRule, op); + } - ninePieceImage.paint(graphicsContext, this, style, destination, source, deviceScaleFactor, op); return true; } +class BorderEdge { +public: + BorderEdge(int edgeWidth, const Color& edgeColor, EBorderStyle edgeStyle, bool edgeIsTransparent, bool edgeIsPresent = true) + : width(edgeWidth) + , color(edgeColor) + , style(edgeStyle) + , isTransparent(edgeIsTransparent) + , isPresent(edgeIsPresent) + { + if (style == DOUBLE && edgeWidth < 3) + style = SOLID; + } + + BorderEdge() + : width(0) + , style(BHIDDEN) + , isTransparent(false) + , isPresent(false) + { + } + + bool hasVisibleColorAndStyle() const { return style > BHIDDEN && !isTransparent; } + bool shouldRender() const { return isPresent && width && hasVisibleColorAndStyle(); } + bool presentButInvisible() const { return usedWidth() && !hasVisibleColorAndStyle(); } + bool obscuresBackgroundEdge(float scale) const + { + if (!isPresent || isTransparent || (width * scale) < 2 || color.hasAlpha() || style == BHIDDEN) + return false; + + if (style == DOTTED || style == DASHED) + return false; + + if (style == DOUBLE) + return width >= 5 * scale; // The outer band needs to be >= 2px wide at unit scale. + + return true; + } + bool obscuresBackground() const + { + if (!isPresent || isTransparent || color.hasAlpha() || style == BHIDDEN) + return false; + + if (style == DOTTED || style == DASHED || style == DOUBLE) + return false; + + return true; + } + + 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; +}; + static bool allCornersClippedOut(const RoundedRect& border, const LayoutRect& clipRect) { LayoutRect boundingRect = border.rect(); @@ -1314,6 +1510,37 @@ static bool borderWillArcInnerEdge(const LayoutSize& firstRadius, const FloatSiz return !firstRadius.isZero() || !secondRadius.isZero(); } +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) +{ + return static_cast<BorderEdgeFlag>(1 << side); +} + +static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) +{ + return flags & edgeFlagForSide(side); +} + +static inline bool includesAdjacentEdges(BorderEdgeFlags flags) +{ + return (flags & (TopBorderEdge | RightBorderEdge)) == (TopBorderEdge | RightBorderEdge) + || (flags & (RightBorderEdge | BottomBorderEdge)) == (RightBorderEdge | BottomBorderEdge) + || (flags & (BottomBorderEdge | LeftBorderEdge)) == (BottomBorderEdge | LeftBorderEdge) + || (flags & (LeftBorderEdge | TopBorderEdge)) == (LeftBorderEdge | TopBorderEdge); +} + +inline bool edgesShareColor(const BorderEdge& firstEdge, const BorderEdge& secondEdge) +{ + return firstEdge.color == secondEdge.color; +} + 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. @@ -1357,13 +1584,13 @@ static inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const if (!edgesShareColor(edges[side], edges[adjacentSide])) return false; - return !borderStyleHasUnmatchedColorsAtCorner(edges[side].style(), side, adjacentSide); + return !borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide); } static inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[]) { - if (!edges[side].color().hasAlpha()) + if (!edges[side].color.hasAlpha()) return false; if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) @@ -1372,7 +1599,7 @@ static inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSid if (!edgesShareColor(edges[side], edges[adjacentSide])) return true; - return borderStyleHasUnmatchedColorsAtCorner(edges[side].style(), side, adjacentSide); + return borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide); } // This assumes that we draw in order: top, bottom, left, right. @@ -1384,10 +1611,10 @@ static inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const Bor if (edges[adjacentSide].presentButInvisible()) return false; - if (!edgesShareColor(edges[side], edges[adjacentSide]) && edges[adjacentSide].color().hasAlpha()) + if (!edgesShareColor(edges[side], edges[adjacentSide]) && edges[adjacentSide].color.hasAlpha()) return false; - if (!borderStyleFillsBorderArea(edges[adjacentSide].style())) + if (!borderStyleFillsBorderArea(edges[adjacentSide].style)) return false; return true; @@ -1416,7 +1643,7 @@ static inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[], bool allowOverdraw) { - if ((edges[side].isTransparent() && edges[adjacentSide].isTransparent()) || !edges[adjacentSide].isPresent()) + if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edges[adjacentSide].isPresent) return false; if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges)) @@ -1425,18 +1652,18 @@ static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEd if (!edgesShareColor(edges[side], edges[adjacentSide])) return true; - if (borderStylesRequireMitre(side, adjacentSide, edges[side].style(), edges[adjacentSide].style())) + if (borderStylesRequireMitre(side, adjacentSide, edges[side].style, edges[adjacentSide].style)) return true; return false; } -void RenderBoxModelObject::paintOneBorderSide(GraphicsContext& graphicsContext, const RenderStyle& style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, - const LayoutRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge edges[], const Path* path, +void RenderBoxModelObject::paintOneBorderSide(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, + const IntRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge edges[], const Path* path, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor) { const BorderEdge& edgeToRender = edges[side]; - ASSERT(edgeToRender.widthForPainting()); + ASSERT(edgeToRender.width); const BorderEdge& adjacentEdge1 = edges[adjacentSide1]; const BorderEdge& adjacentEdge2 = edges[adjacentSide2]; @@ -1446,24 +1673,24 @@ void RenderBoxModelObject::paintOneBorderSide(GraphicsContext& graphicsContext, bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edges); bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edges); - const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.color(); + const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.color; if (path) { - GraphicsContextStateSaver stateSaver(graphicsContext); + GraphicsContextStateSaver stateSaver(*graphicsContext); if (innerBorder.isRenderable()) clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch); else clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, innerBorder, side, edges); - float thickness = std::max(std::max(edgeToRender.widthForPainting(), adjacentEdge1.widthForPainting()), adjacentEdge2.widthForPainting()); - drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, edgeToRender.widthForPainting(), thickness, side, style, - colorToPaint, edgeToRender.style(), bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); + float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.width), adjacentEdge2.width); + drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, edgeToRender.width, thickness, side, style, + colorToPaint, edgeToRender.style, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); } else { - bool clipForStyle = styleRequiresClipPolygon(edgeToRender.style()) && (mitreAdjacentSide1 || mitreAdjacentSide2); + bool clipForStyle = styleRequiresClipPolygon(edgeToRender.style) && (mitreAdjacentSide1 || mitreAdjacentSide2); bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1, edges) && mitreAdjacentSide1; bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2, edges) && mitreAdjacentSide2; bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2; - GraphicsContextStateSaver clipStateSaver(graphicsContext, shouldClip); + GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); if (shouldClip) { bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitreAdjacentSide1); bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitreAdjacentSide2); @@ -1472,14 +1699,16 @@ void RenderBoxModelObject::paintOneBorderSide(GraphicsContext& graphicsContext, mitreAdjacentSide1 = false; mitreAdjacentSide2 = false; } - drawLineForBoxSide(graphicsContext, sideRect, side, colorToPaint, edgeToRender.style(), mitreAdjacentSide1 ? adjacentEdge1.widthForPainting() : 0, mitreAdjacentSide2 ? adjacentEdge2.widthForPainting() : 0, antialias); + + drawLineForBoxSide(graphicsContext, sideRect.x(), sideRect.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.style, + mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? adjacentEdge2.width : 0, antialias); } } -static LayoutRect calculateSideRect(const RoundedRect& outerBorder, const BorderEdge edges[], int side) +static IntRect calculateSideRect(const RoundedRect& outerBorder, const BorderEdge edges[], int side) { - LayoutRect sideRect = outerBorder.rect(); - float width = edges[side].widthForPainting(); + IntRect sideRect = outerBorder.rect(); + int width = edges[side].width; if (side == BSTop) sideRect.setHeight(width); @@ -1493,7 +1722,7 @@ static LayoutRect calculateSideRect(const RoundedRect& outerBorder, const Border return sideRect; } -void RenderBoxModelObject::paintBorderSides(GraphicsContext& graphicsContext, const RenderStyle& style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, +void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeFlags edgeSet, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor) { @@ -1509,44 +1738,44 @@ void RenderBoxModelObject::paintBorderSides(GraphicsContext& graphicsContext, co // only depends on sideRect when painting solid borders. if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) { - LayoutRect sideRect = outerBorder.rect(); - sideRect.setHeight(edges[BSTop].widthForPainting() + innerBorderAdjustment.y()); + IntRect sideRect = outerBorder.rect(); + sideRect.setHeight(edges[BSTop].width + innerBorderAdjustment.y()); - 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 : nullptr, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + 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, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); } if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) { - LayoutRect sideRect = outerBorder.rect(); - sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].widthForPainting() - innerBorderAdjustment.y()); + IntRect sideRect = outerBorder.rect(); + sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width - innerBorderAdjustment.y()); - 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 : nullptr, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + 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, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); } if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) { - LayoutRect sideRect = outerBorder.rect(); - sideRect.setWidth(edges[BSLeft].widthForPainting() + innerBorderAdjustment.x()); + IntRect sideRect = outerBorder.rect(); + sideRect.setWidth(edges[BSLeft].width + innerBorderAdjustment.x()); - 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 : nullptr, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + 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, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); } if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) { - LayoutRect sideRect = outerBorder.rect(); - sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].widthForPainting() - innerBorderAdjustment.x()); + IntRect sideRect = outerBorder.rect(); + sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - innerBorderAdjustment.x()); - 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 : nullptr, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + 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, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); } } -void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext& graphicsContext, const RenderStyle& style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment, +void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias) { // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. // This is different from BoxSide enum order. - static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; + static BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; while (edgesToDraw) { // Find undrawn edges sharing a color. @@ -1560,10 +1789,10 @@ void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext& graphics bool includeEdge; if (!commonColorEdgeSet) { - commonColor = edges[currSide].color(); + commonColor = edges[currSide].color; includeEdge = true; } else - includeEdge = edges[currSide].color() == commonColor; + includeEdge = edges[currSide].color == commonColor; if (includeEdge) commonColorEdgeSet |= edgeFlagForSide(currSide); @@ -1571,38 +1800,34 @@ void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext& graphics bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) && commonColor.hasAlpha(); if (useTransparencyLayer) { - graphicsContext.beginTransparencyLayer(static_cast<float>(commonColor.alpha()) / 255); + graphicsContext->beginTransparencyLayer(static_cast<float>(commonColor.alpha()) / 255); commonColor = Color(commonColor.red(), commonColor.green(), commonColor.blue()); } paintBorderSides(graphicsContext, style, outerBorder, innerBorder, innerBorderAdjustment, edges, commonColorEdgeSet, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, &commonColor); if (useTransparencyLayer) - graphicsContext.endTransparencyLayer(); + graphicsContext->endTransparencyLayer(); edgesToDraw &= ~commonColorEdgeSet; } } -void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& rect, const RenderStyle& style, +void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& rect, const RenderStyle* style, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) { - GraphicsContext& graphicsContext = info.context(); - - if (graphicsContext.paintingDisabled()) - return; - - if (rect.isEmpty()) + GraphicsContext* graphicsContext = info.context; + // border-image is not affected by border-radius. + if (paintNinePieceImage(graphicsContext, rect, style, style->borderImage())) return; - // border-image is not affected by border-radius. - if (paintNinePieceImage(graphicsContext, rect, style, style.borderImage())) + if (graphicsContext->paintingDisabled()) return; BorderEdge edges[4]; - BorderEdge::getBorderEdgeInfo(edges, style, document().deviceScaleFactor(), includeLogicalLeftEdge, includeLogicalRightEdge); - RoundedRect outerBorder = style.getRoundedBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge); - RoundedRect innerBorder = style.getRoundedInnerBorderFor(borderInnerRectAdjustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogicalLeftEdge, includeLogicalRightEdge); + getBorderEdgeInfo(edges, style, includeLogicalLeftEdge, includeLogicalRightEdge); + RoundedRect outerBorder = style->getRoundedBorderFor(rect, &view(), includeLogicalLeftEdge, includeLogicalRightEdge); + RoundedRect innerBorder = style->getRoundedInnerBorderFor(borderInnerRectAdjustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogicalLeftEdge, includeLogicalRightEdge); bool haveAlphaColor = false; bool haveAllSolidEdges = true; @@ -1624,23 +1849,23 @@ void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& continue; } - if (!currEdge.widthForPainting()) { + if (!currEdge.width) { --numEdgesVisible; continue; } if (firstVisibleEdge == -1) firstVisibleEdge = i; - else if (currEdge.color() != edges[firstVisibleEdge].color()) + else if (currEdge.color != edges[firstVisibleEdge].color) allEdgesShareColor = false; - if (currEdge.color().hasAlpha()) + if (currEdge.color.hasAlpha()) haveAlphaColor = true; - if (currEdge.style() != SOLID) + if (currEdge.style != SOLID) haveAllSolidEdges = false; - if (currEdge.style() != DOUBLE) + if (currEdge.style != DOUBLE) haveAllDoubleEdges = false; } @@ -1649,7 +1874,6 @@ void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& if (haveAllSolidEdges && outerBorder.isRounded() && allCornersClippedOut(outerBorder, info.rect)) outerBorder.setRadii(RoundedRect::Radii()); - float deviceScaleFactor = document().deviceScaleFactor(); // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787 if ((haveAllSolidEdges || haveAllDoubleEdges) && allEdgesShareColor && innerBorder.isRenderable()) { // Fast path for drawing all solid edges and all unrounded double edges @@ -1657,18 +1881,17 @@ void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& && (haveAllSolidEdges || (!outerBorder.isRounded() && !innerBorder.isRounded()))) { Path path; - FloatRoundedRect pixelSnappedOuterBorder = outerBorder.pixelSnappedRoundedRectForPainting(deviceScaleFactor); - if (pixelSnappedOuterBorder.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer) - path.addRoundedRect(pixelSnappedOuterBorder); + if (outerBorder.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer) + path.addRoundedRect(outerBorder); else - path.addRect(pixelSnappedOuterBorder.rect()); + path.addRect(outerBorder.rect()); if (haveAllDoubleEdges) { - LayoutRect innerThirdRect = outerBorder.rect(); - LayoutRect outerThirdRect = outerBorder.rect(); + IntRect innerThirdRect = outerBorder.rect(); + IntRect outerThirdRect = outerBorder.rect(); for (int side = BSTop; side <= BSLeft; ++side) { - LayoutUnit outerWidth; - LayoutUnit innerWidth; + int outerWidth; + int innerWidth; edges[side].getDoubleBorderStripeWidths(outerWidth, innerWidth); if (side == BSTop) { @@ -1686,31 +1909,30 @@ void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& } } - FloatRoundedRect pixelSnappedOuterThird = outerBorder.pixelSnappedRoundedRectForPainting(deviceScaleFactor); - pixelSnappedOuterThird.setRect(snapRectToDevicePixels(outerThirdRect, deviceScaleFactor)); + RoundedRect outerThird = outerBorder; + RoundedRect innerThird = innerBorder; + innerThird.setRect(innerThirdRect); + outerThird.setRect(outerThirdRect); - if (pixelSnappedOuterThird.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer) - path.addRoundedRect(pixelSnappedOuterThird); + if (outerThird.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer) + path.addRoundedRect(outerThird); else - path.addRect(pixelSnappedOuterThird.rect()); + path.addRect(outerThird.rect()); - FloatRoundedRect pixelSnappedInnerThird = innerBorder.pixelSnappedRoundedRectForPainting(deviceScaleFactor); - pixelSnappedInnerThird.setRect(snapRectToDevicePixels(innerThirdRect, deviceScaleFactor)); - if (pixelSnappedInnerThird.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer) - path.addRoundedRect(pixelSnappedInnerThird); + if (innerThird.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer) + path.addRoundedRect(innerThird); else - path.addRect(pixelSnappedInnerThird.rect()); + path.addRect(innerThird.rect()); } - FloatRoundedRect pixelSnappedInnerBorder = innerBorder.pixelSnappedRoundedRectForPainting(deviceScaleFactor); - if (pixelSnappedInnerBorder.isRounded()) - path.addRoundedRect(pixelSnappedInnerBorder); + if (innerBorder.isRounded()) + path.addRoundedRect(innerBorder); else - path.addRect(pixelSnappedInnerBorder.rect()); + path.addRect(innerBorder.rect()); - graphicsContext.setFillRule(RULE_EVENODD); - graphicsContext.setFillColor(edges[firstVisibleEdge].color()); - graphicsContext.fillPath(path); + graphicsContext->setFillRule(RULE_EVENODD); + graphicsContext->setFillColor(edges[firstVisibleEdge].color, style->colorSpace()); + graphicsContext->fillPath(path); return; } // Avoid creating transparent layers @@ -1720,33 +1942,33 @@ void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& for (int i = BSTop; i <= BSLeft; ++i) { const BorderEdge& currEdge = edges[i]; if (currEdge.shouldRender()) { - LayoutRect sideRect = calculateSideRect(outerBorder, edges, i); + IntRect sideRect = calculateSideRect(outerBorder, edges, i); path.addRect(sideRect); } } - graphicsContext.setFillRule(RULE_NONZERO); - graphicsContext.setFillColor(edges[firstVisibleEdge].color()); - graphicsContext.fillPath(path); + graphicsContext->setFillRule(RULE_NONZERO); + graphicsContext->setFillColor(edges[firstVisibleEdge].color, style->colorSpace()); + graphicsContext->fillPath(path); return; } } bool clipToOuterBorder = outerBorder.isRounded(); - GraphicsContextStateSaver stateSaver(graphicsContext, clipToOuterBorder); + GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); if (clipToOuterBorder) { // Clip to the inner and outer radii rects. if (bleedAvoidance != BackgroundBleedUseTransparencyLayer) - graphicsContext.clipRoundedRect(outerBorder.pixelSnappedRoundedRectForPainting(deviceScaleFactor)); + graphicsContext->clipRoundedRect(outerBorder); // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787 // The inside will be clipped out later (in clipBorderSideForComplexInnerPath) if (innerBorder.isRenderable()) - graphicsContext.clipOutRoundedRect(innerBorder.pixelSnappedRoundedRectForPainting(deviceScaleFactor)); + graphicsContext->clipOutRoundedRect(innerBorder); } // If only one edge visible antialiasing doesn't create seams bool antialias = shouldAntialiasLines(graphicsContext) || numEdgesVisible == 1; - RoundedRect unadjustedInnerBorder = (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? style.getRoundedInnerBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge) : innerBorder; + RoundedRect unadjustedInnerBorder = (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? style->getRoundedInnerBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge) : innerBorder; IntPoint innerBorderAdjustment(innerBorder.rect().x() - unadjustedInnerBorder.rect().x(), innerBorder.rect().y() - unadjustedInnerBorder.rect().y()); if (haveAlphaColor) paintTranslucentBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias); @@ -1754,9 +1976,9 @@ void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& paintBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias); } -void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext& graphicsContext, const LayoutRect& borderRect, const Path& borderPath, const BorderEdge edges[], - float thickness, float drawThickness, BoxSide side, const RenderStyle& style, Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidance, - bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, const LayoutRect& borderRect, const Path& borderPath, const BorderEdge edges[], + float thickness, float drawThickness, BoxSide side, const RenderStyle* style, + Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) { if (thickness <= 0) return; @@ -1770,14 +1992,14 @@ void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext& graphicsContext, return; case DOTTED: case DASHED: { - graphicsContext.setStrokeColor(color); + 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); + 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 @@ -1800,47 +2022,47 @@ void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext& graphicsContext, DashArray lineDash; lineDash.append(dashLength); lineDash.append(gapLength); - graphicsContext.setLineDash(lineDash, dashLength); + 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); + graphicsContext->strokePath(borderPath); return; } case DOUBLE: { // Get the inner border rects for both the outer border line and the inner border line - LayoutUnit outerBorderTopWidth; - LayoutUnit innerBorderTopWidth; + int outerBorderTopWidth; + int innerBorderTopWidth; edges[BSTop].getDoubleBorderStripeWidths(outerBorderTopWidth, innerBorderTopWidth); - LayoutUnit outerBorderRightWidth; - LayoutUnit innerBorderRightWidth; + int outerBorderRightWidth; + int innerBorderRightWidth; edges[BSRight].getDoubleBorderStripeWidths(outerBorderRightWidth, innerBorderRightWidth); - LayoutUnit outerBorderBottomWidth; - LayoutUnit innerBorderBottomWidth; + int outerBorderBottomWidth; + int innerBorderBottomWidth; edges[BSBottom].getDoubleBorderStripeWidths(outerBorderBottomWidth, innerBorderBottomWidth); - LayoutUnit outerBorderLeftWidth; - LayoutUnit innerBorderLeftWidth; + int outerBorderLeftWidth; + int innerBorderLeftWidth; edges[BSLeft].getDoubleBorderStripeWidths(outerBorderLeftWidth, innerBorderLeftWidth); // Draw inner border line { - GraphicsContextStateSaver stateSaver(graphicsContext); - RoundedRect innerClip = style.getRoundedInnerBorderFor(borderRect, + GraphicsContextStateSaver stateSaver(*graphicsContext); + RoundedRect innerClip = style->getRoundedInnerBorderFor(borderRect, innerBorderTopWidth, innerBorderBottomWidth, innerBorderLeftWidth, innerBorderRightWidth, includeLogicalLeftEdge, includeLogicalRightEdge); - graphicsContext.clipRoundedRect(FloatRoundedRect(innerClip)); + graphicsContext->clipRoundedRect(innerClip); drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); } // Draw outer border line { - GraphicsContextStateSaver stateSaver(graphicsContext); + GraphicsContextStateSaver stateSaver(*graphicsContext); LayoutRect outerRect = borderRect; if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) { outerRect.inflate(1); @@ -1850,10 +2072,10 @@ void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext& graphicsContext, ++outerBorderRightWidth; } - RoundedRect outerClip = style.getRoundedInnerBorderFor(outerRect, + RoundedRect outerClip = style->getRoundedInnerBorderFor(outerRect, outerBorderTopWidth, outerBorderBottomWidth, outerBorderLeftWidth, outerBorderRightWidth, includeLogicalLeftEdge, includeLogicalRightEdge); - graphicsContext.clipOutRoundedRect(FloatRoundedRect(outerClip)); + graphicsContext->clipOutRoundedRect(outerClip); drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); } return; @@ -1875,31 +2097,35 @@ void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext& graphicsContext, drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s1, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); // Paint inner only - GraphicsContextStateSaver stateSaver(graphicsContext); - LayoutUnit topWidth = edges[BSTop].widthForPainting() / 2; - LayoutUnit bottomWidth = edges[BSBottom].widthForPainting() / 2; - LayoutUnit leftWidth = edges[BSLeft].widthForPainting() / 2; - LayoutUnit rightWidth = edges[BSRight].widthForPainting() / 2; + GraphicsContextStateSaver stateSaver(*graphicsContext); + LayoutUnit topWidth = edges[BSTop].usedWidth() / 2; + LayoutUnit bottomWidth = edges[BSBottom].usedWidth() / 2; + LayoutUnit leftWidth = edges[BSLeft].usedWidth() / 2; + LayoutUnit rightWidth = edges[BSRight].usedWidth() / 2; - RoundedRect clipRect = style.getRoundedInnerBorderFor(borderRect, + RoundedRect clipRect = style->getRoundedInnerBorderFor(borderRect, topWidth, bottomWidth, leftWidth, rightWidth, includeLogicalLeftEdge, includeLogicalRightEdge); - graphicsContext.clipRoundedRect(FloatRoundedRect(clipRect)); + graphicsContext->clipRoundedRect(clipRect); drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s2, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); return; } case INSET: + if (side == BSTop || side == BSLeft) + color = color.dark(); + break; case OUTSET: - calculateBorderStyleColor(borderStyle, side, color); + if (side == BSBottom || side == BSRight) + color = color.dark(); break; default: break; } - graphicsContext.setStrokeStyle(NoStroke); - graphicsContext.setFillColor(color); - graphicsContext.drawRect(snapRectToDevicePixels(borderRect, document().deviceScaleFactor())); + graphicsContext->setStrokeStyle(NoStroke); + graphicsContext->setFillColor(color, style->colorSpace()); + graphicsContext->drawRect(pixelSnappedIntRect(borderRect)); } static void findInnerVertex(const FloatPoint& outerCorner, const FloatPoint& innerCorner, const FloatPoint& centerPoint, FloatPoint& result) @@ -1917,14 +2143,15 @@ static void findInnerVertex(const FloatPoint& outerCorner, const FloatPoint& inn findIntersection(outerCorner, innerCorner, FloatPoint(0, centerPoint.y()), FloatPoint(100, centerPoint.y()), result); } -void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext& graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder, +void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder, BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches) { - float deviceScaleFactor = document().deviceScaleFactor(); - const FloatRect& outerRect = snapRectToDevicePixels(outerBorder.rect(), deviceScaleFactor); - const FloatRect& innerRect = snapRectToDevicePixels(innerBorder.rect(), deviceScaleFactor); + FloatPoint quad[4]; + + const LayoutRect& outerRect = outerBorder.rect(); + const LayoutRect& innerRect = innerBorder.rect(); - FloatPoint centerPoint(innerRect.location().x() + innerRect.width() / 2, innerRect.location().y() + innerRect.height() / 2); + 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. @@ -1940,14 +2167,12 @@ void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext& graphicsContex // 3 / \ 3 // 0----------------3 // - Vector<FloatPoint> quad; - quad.reserveInitialCapacity(4); switch (side) { case BSTop: - quad.uncheckedAppend(outerRect.minXMinYCorner()); - quad.uncheckedAppend(innerRect.minXMinYCorner()); - quad.uncheckedAppend(innerRect.maxXMinYCorner()); - quad.uncheckedAppend(outerRect.maxXMinYCorner()); + 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]); @@ -1957,10 +2182,10 @@ void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext& graphicsContex break; case BSLeft: - quad.uncheckedAppend(outerRect.minXMinYCorner()); - quad.uncheckedAppend(innerRect.minXMinYCorner()); - quad.uncheckedAppend(innerRect.minXMaxYCorner()); - quad.uncheckedAppend(outerRect.minXMaxYCorner()); + 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]); @@ -1970,10 +2195,10 @@ void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext& graphicsContex break; case BSBottom: - quad.uncheckedAppend(outerRect.minXMaxYCorner()); - quad.uncheckedAppend(innerRect.minXMaxYCorner()); - quad.uncheckedAppend(innerRect.maxXMaxYCorner()); - quad.uncheckedAppend(outerRect.maxXMaxYCorner()); + 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]); @@ -1983,10 +2208,10 @@ void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext& graphicsContex break; case BSRight: - quad.uncheckedAppend(outerRect.maxXMinYCorner()); - quad.uncheckedAppend(innerRect.maxXMinYCorner()); - quad.uncheckedAppend(innerRect.maxXMaxYCorner()); - quad.uncheckedAppend(outerRect.maxXMaxYCorner()); + 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]); @@ -1999,57 +2224,49 @@ void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext& graphicsContex // If the border matches both of its adjacent sides, don't anti-alias the clip, and // if neither side matches, anti-alias the clip. if (firstEdgeMatches == secondEdgeMatches) { - bool wasAntialiased = graphicsContext.shouldAntialias(); - graphicsContext.setShouldAntialias(!firstEdgeMatches); - graphicsContext.clipPath(Path::polygonPathFromPoints(quad), RULE_NONZERO); - graphicsContext.setShouldAntialias(wasAntialiased); + graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches); return; } // Square off the end which shouldn't be affected by antialiasing, and clip. - Vector<FloatPoint> firstQuad = { - quad[0], - quad[1], - side == BSTop || side == BSBottom ? FloatPoint(quad[3].x(), quad[2].y()) : FloatPoint(quad[2].x(), quad[3].y()), - quad[3] - }; - bool wasAntialiased = graphicsContext.shouldAntialias(); - graphicsContext.setShouldAntialias(!firstEdgeMatches); - graphicsContext.clipPath(Path::polygonPathFromPoints(firstQuad), RULE_NONZERO); - - Vector<FloatPoint> secondQuad = { - quad[0], - side == BSTop || side == BSBottom ? FloatPoint(quad[0].x(), quad[1].y()) : FloatPoint(quad[1].x(), quad[0].y()), - quad[2], - quad[3] - }; + FloatPoint firstQuad[4]; + firstQuad[0] = quad[0]; + firstQuad[1] = quad[1]; + firstQuad[2] = side == BSTop || side == BSBottom ? FloatPoint(quad[3].x(), quad[2].y()) + : FloatPoint(quad[2].x(), quad[3].y()); + firstQuad[3] = quad[3]; + graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches); + + FloatPoint secondQuad[4]; + secondQuad[0] = quad[0]; + secondQuad[1] = side == BSTop || side == BSBottom ? FloatPoint(quad[0].x(), quad[1].y()) + : FloatPoint(quad[1].x(), quad[0].y()); + secondQuad[2] = quad[2]; + secondQuad[3] = quad[3]; // Antialiasing affects the second side. - graphicsContext.setShouldAntialias(!secondEdgeMatches); - graphicsContext.clipPath(Path::polygonPathFromPoints(secondQuad), RULE_NONZERO); - - graphicsContext.setShouldAntialias(wasAntialiased); + graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches); } -static LayoutRect calculateSideRectIncludingInner(const RoundedRect& outerBorder, const BorderEdge edges[], BoxSide side) +static IntRect calculateSideRectIncludingInner(const RoundedRect& outerBorder, const BorderEdge edges[], BoxSide side) { - LayoutRect sideRect = outerBorder.rect(); - LayoutUnit width; + IntRect sideRect = outerBorder.rect(); + int width; switch (side) { case BSTop: - width = sideRect.height() - edges[BSBottom].widthForPainting(); + width = sideRect.height() - edges[BSBottom].width; sideRect.setHeight(width); break; case BSBottom: - width = sideRect.height() - edges[BSTop].widthForPainting(); + width = sideRect.height() - edges[BSTop].width; sideRect.shiftYEdgeTo(sideRect.maxY() - width); break; case BSLeft: - width = sideRect.width() - edges[BSRight].widthForPainting(); + width = sideRect.width() - edges[BSRight].width; sideRect.setWidth(width); break; case BSRight: - width = sideRect.width() - edges[BSLeft].widthForPainting(); + width = sideRect.width() - edges[BSLeft].width; sideRect.shiftXEdgeTo(sideRect.maxX() - width); break; } @@ -2063,7 +2280,7 @@ static RoundedRect calculateAdjustedInnerBorder(const RoundedRect&innerBorder, B // This function relies on the fact we only get radii not contained within each edge if one of the radii // for an edge is zero, so we can shift the arc towards the zero radius corner. RoundedRect::Radii newRadii = innerBorder.radii(); - LayoutRect newRect = innerBorder.rect(); + IntRect newRect = innerBorder.rect(); float overshoot; float maxRadii; @@ -2137,17 +2354,46 @@ static RoundedRect calculateAdjustedInnerBorder(const RoundedRect&innerBorder, B return RoundedRect(newRect, newRadii); } -void RenderBoxModelObject::clipBorderSideForComplexInnerPath(GraphicsContext& graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder, +void RenderBoxModelObject::clipBorderSideForComplexInnerPath(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder, BoxSide side, const class BorderEdge edges[]) { - graphicsContext.clip(calculateSideRectIncludingInner(outerBorder, edges, side)); - graphicsContext.clipOutRoundedRect(FloatRoundedRect(calculateAdjustedInnerBorder(innerBorder, side))); + graphicsContext->clip(calculateSideRectIncludingInner(outerBorder, edges, side)); + graphicsContext->clipOutRoundedRect(calculateAdjustedInnerBorder(innerBorder, side)); +} + +void RenderBoxModelObject::getBorderEdgeInfo(BorderEdge edges[], const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const +{ + bool horizontal = style->isHorizontalWritingMode(); + + edges[BSTop] = BorderEdge(style->borderTopWidth(), + style->visitedDependentColor(CSSPropertyBorderTopColor), + style->borderTopStyle(), + style->borderTopIsTransparent(), + horizontal || includeLogicalLeftEdge); + + edges[BSRight] = BorderEdge(style->borderRightWidth(), + style->visitedDependentColor(CSSPropertyBorderRightColor), + style->borderRightStyle(), + style->borderRightIsTransparent(), + !horizontal || includeLogicalRightEdge); + + edges[BSBottom] = BorderEdge(style->borderBottomWidth(), + style->visitedDependentColor(CSSPropertyBorderBottomColor), + style->borderBottomStyle(), + style->borderBottomIsTransparent(), + horizontal || includeLogicalRightEdge); + + edges[BSLeft] = BorderEdge(style->borderLeftWidth(), + style->visitedDependentColor(CSSPropertyBorderLeftColor), + style->borderLeftStyle(), + style->borderLeftIsTransparent(), + !horizontal || includeLogicalLeftEdge); } bool RenderBoxModelObject::borderObscuresBackgroundEdge(const FloatSize& contextScale) const { BorderEdge edges[4]; - BorderEdge::getBorderEdgeInfo(edges, style(), document().deviceScaleFactor()); + getBorderEdgeInfo(edges, &style()); for (int i = BSTop; i <= BSLeft; ++i) { const BorderEdge& currEdge = edges[i]; @@ -2170,7 +2416,7 @@ bool RenderBoxModelObject::borderObscuresBackground() const return false; BorderEdge edges[4]; - BorderEdge::getBorderEdgeInfo(edges, style(), document().deviceScaleFactor()); + getBorderEdgeInfo(edges, &style()); for (int i = BSTop; i <= BSLeft; ++i) { const BorderEdge& currEdge = edges[i]; @@ -2181,7 +2427,7 @@ bool RenderBoxModelObject::borderObscuresBackground() const return true; } -bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(const LayoutPoint&, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* inlineFlowBox) const +bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* inlineFlowBox) const { if (bleedAvoidance != BackgroundBleedNone) return false; @@ -2228,41 +2474,39 @@ bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(const LayoutPoin return true; } -static inline LayoutRect areaCastingShadowInHole(const LayoutRect& holeRect, int shadowExtent, int shadowSpread, const IntSize& shadowOffset) +static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowExtent, int shadowSpread, const IntSize& shadowOffset) { - LayoutRect bounds(holeRect); + IntRect bounds(holeRect); bounds.inflate(shadowExtent); if (shadowSpread < 0) bounds.inflate(-shadowSpread); - LayoutRect offsetBounds = bounds; + IntRect offsetBounds = bounds; offsetBounds.move(-shadowOffset); return unionRect(bounds, offsetBounds); } -void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRect, const RenderStyle& style, ShadowStyle shadowStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRect, const RenderStyle* s, ShadowStyle shadowStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) { // FIXME: Deal with border-image. Would be great to use border-image as a mask. - GraphicsContext& context = info.context(); - if (context.paintingDisabled() || !style.boxShadow()) + GraphicsContext* context = info.context; + if (context->paintingDisabled() || !s->boxShadow()) return; - RoundedRect border = (shadowStyle == Inset) ? style.getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge) - : style.getRoundedBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge); + RoundedRect border = (shadowStyle == Inset) + ? s->getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge) + : s->getRoundedBorderFor(paintRect, &view(), includeLogicalLeftEdge, includeLogicalRightEdge); - bool hasBorderRadius = style.hasBorderRadius(); - bool isHorizontal = style.isHorizontalWritingMode(); - float deviceScaleFactor = document().deviceScaleFactor(); + bool hasBorderRadius = s->hasBorderRadius(); + bool isHorizontal = s->isHorizontalWritingMode(); - bool hasOpaqueBackground = style.visitedDependentColor(CSSPropertyBackgroundColor).isValid() && style.visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255; - for (const ShadowData* shadow = style.boxShadow(); shadow; shadow = shadow->next()) { + 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) continue; - // FIXME: Add subpixel support for the shadow values. Soon after the shadow offset becomes fractional, - // all the early snappings here need to be pushed to the actual painting operations. IntSize shadowOffset(shadow->x(), shadow->y()); int shadowRadius = shadow->radius(); int shadowPaintingExtent = shadow->paintingExtent(); @@ -2279,48 +2523,50 @@ void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRec if (fillRect.isEmpty()) continue; - FloatRect pixelSnappedShadowRect = snapRectToDevicePixels(border.rect(), deviceScaleFactor); - pixelSnappedShadowRect.inflate(shadowPaintingExtent + shadowSpread); - pixelSnappedShadowRect.move(shadowOffset); + IntRect shadowRect(border.rect()); + shadowRect.inflate(shadowPaintingExtent + shadowSpread); + shadowRect.move(shadowOffset); - GraphicsContextStateSaver stateSaver(context); - context.clip(pixelSnappedShadowRect); + GraphicsContextStateSaver stateSaver(*context); + context->clip(shadowRect); // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not // bleed in (due to antialiasing) if the context is transformed. - IntSize extraOffset(roundToInt(paintRect.width()) + std::max(0, shadowOffset.width()) + shadowPaintingExtent + 2 * shadowSpread + 1, 0); + IntSize extraOffset(paintRect.pixelSnappedWidth() + std::max(0, shadowOffset.width()) + shadowPaintingExtent + 2 * shadowSpread + 1, 0); shadowOffset -= extraOffset; fillRect.move(extraOffset); if (shadow->isWebkitBoxShadow()) - context.setLegacyShadow(shadowOffset, shadowRadius, shadowColor); + context->setLegacyShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace()); else - context.setShadow(shadowOffset, shadowRadius, shadowColor); + context->setShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace()); - FloatRoundedRect rectToClipOut = border.pixelSnappedRoundedRectForPainting(deviceScaleFactor); - FloatRoundedRect pixelSnappedFillRect = fillRect.pixelSnappedRoundedRectForPainting(deviceScaleFactor); if (hasBorderRadius) { + RoundedRect rectToClipOut = border; + // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time // when painting the shadow. On the other hand, it introduces subpixel gaps along the // corners. Those are avoided by insetting the clipping path by one pixel. - if (hasOpaqueBackground) - rectToClipOut.inflateWithRadii(LayoutUnit::fromPixel(-1)); + if (hasOpaqueBackground) { + rectToClipOut.inflateWithRadii(-1); + } if (!rectToClipOut.isEmpty()) - context.clipOutRoundedRect(rectToClipOut); + context->clipOutRoundedRect(rectToClipOut); - RoundedRect influenceRect(LayoutRect(pixelSnappedShadowRect), border.radii()); + RoundedRect influenceRect(shadowRect, border.radii()); influenceRect.expandRadii(2 * shadowPaintingExtent + shadowSpread); - if (allCornersClippedOut(influenceRect, info.rect)) - context.fillRect(pixelSnappedFillRect.rect(), Color::black); + context->fillRect(fillRect.rect(), Color::black, s->colorSpace()); else { - pixelSnappedFillRect.expandRadii(shadowSpread); - if (!pixelSnappedFillRect.isRenderable()) - pixelSnappedFillRect.adjustRadii(); - context.fillRoundedRect(pixelSnappedFillRect, Color::black); + fillRect.expandRadii(shadowSpread); + if (!fillRect.isRenderable()) + fillRect.adjustRadii(); + context->fillRoundedRect(fillRect, Color::black, s->colorSpace()); } } else { + IntRect rectToClipOut = border.rect(); + // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time // when painting the shadow. On the other hand, it introduces subpixel gaps along the // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path @@ -2328,67 +2574,68 @@ void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRec if (hasOpaqueBackground) { // FIXME: The function to decide on the policy based on the transform should be a named function. // FIXME: It's not clear if this check is right. What about integral scale factors? - AffineTransform transform = context.getCTM(); + AffineTransform transform = context->getCTM(); if (transform.a() != 1 || (transform.d() != 1 && transform.d() != -1) || transform.b() || transform.c()) - rectToClipOut.inflate(LayoutUnit::fromPixel(-1).toFloat()); + rectToClipOut.inflate(-1); } if (!rectToClipOut.isEmpty()) - context.clipOut(rectToClipOut.rect()); - context.fillRect(pixelSnappedFillRect.rect(), Color::black); + context->clipOut(rectToClipOut); + context->fillRect(fillRect.rect(), Color::black, s->colorSpace()); } } else { // Inset shadow. - FloatRoundedRect pixelSnappedBorderRect = border.pixelSnappedRoundedRectForPainting(deviceScaleFactor); - FloatRect pixelSnappedHoleRect = pixelSnappedBorderRect.rect(); - pixelSnappedHoleRect.inflate(-shadowSpread); + IntRect holeRect(border.rect()); + holeRect.inflate(-shadowSpread); - if (pixelSnappedHoleRect.isEmpty()) { + if (holeRect.isEmpty()) { if (hasBorderRadius) - context.fillRoundedRect(pixelSnappedBorderRect, shadowColor); + context->fillRoundedRect(border, shadowColor, s->colorSpace()); else - context.fillRect(pixelSnappedBorderRect.rect(), shadowColor); + context->fillRect(border.rect(), shadowColor, s->colorSpace()); continue; } if (!includeLogicalLeftEdge) { if (isHorizontal) { - pixelSnappedHoleRect.move(-std::max(shadowOffset.width(), 0) - shadowPaintingExtent, 0); - pixelSnappedHoleRect.setWidth(pixelSnappedHoleRect.width() + std::max(shadowOffset.width(), 0) + shadowPaintingExtent); + holeRect.move(-std::max(shadowOffset.width(), 0) - shadowPaintingExtent, 0); + holeRect.setWidth(holeRect.width() + std::max(shadowOffset.width(), 0) + shadowPaintingExtent); } else { - pixelSnappedHoleRect.move(0, -std::max(shadowOffset.height(), 0) - shadowPaintingExtent); - pixelSnappedHoleRect.setHeight(pixelSnappedHoleRect.height() + std::max(shadowOffset.height(), 0) + shadowPaintingExtent); + holeRect.move(0, -std::max(shadowOffset.height(), 0) - shadowPaintingExtent); + holeRect.setHeight(holeRect.height() + std::max(shadowOffset.height(), 0) + shadowPaintingExtent); } } if (!includeLogicalRightEdge) { if (isHorizontal) - pixelSnappedHoleRect.setWidth(pixelSnappedHoleRect.width() - std::min(shadowOffset.width(), 0) + shadowPaintingExtent); + holeRect.setWidth(holeRect.width() - std::min(shadowOffset.width(), 0) + shadowPaintingExtent); else - pixelSnappedHoleRect.setHeight(pixelSnappedHoleRect.height() - std::min(shadowOffset.height(), 0) + shadowPaintingExtent); + holeRect.setHeight(holeRect.height() - std::min(shadowOffset.height(), 0) + shadowPaintingExtent); } Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255); - FloatRect pixelSnappedOuterRect = snapRectToDevicePixels(areaCastingShadowInHole(LayoutRect(pixelSnappedBorderRect.rect()), shadowPaintingExtent, shadowSpread, shadowOffset), deviceScaleFactor); - FloatRoundedRect pixelSnappedRoundedHole = FloatRoundedRect(pixelSnappedHoleRect, pixelSnappedBorderRect.radii()); + IntRect outerRect = areaCastingShadowInHole(border.rect(), shadowPaintingExtent, shadowSpread, shadowOffset); + RoundedRect roundedHole(holeRect, border.radii()); - GraphicsContextStateSaver stateSaver(context); + GraphicsContextStateSaver stateSaver(*context); if (hasBorderRadius) { - context.clipRoundedRect(pixelSnappedBorderRect); - pixelSnappedRoundedHole.shrinkRadii(shadowSpread); + Path path; + path.addRoundedRect(border); + context->clip(path); + roundedHole.shrinkRadii(shadowSpread); } else - context.clip(pixelSnappedBorderRect.rect()); + context->clip(border.rect()); - IntSize extraOffset(2 * roundToInt(paintRect.width()) + std::max(0, shadowOffset.width()) + shadowPaintingExtent - 2 * shadowSpread + 1, 0); - context.translate(extraOffset.width(), extraOffset.height()); + IntSize extraOffset(2 * paintRect.pixelSnappedWidth() + std::max(0, shadowOffset.width()) + shadowPaintingExtent - 2 * shadowSpread + 1, 0); + context->translate(extraOffset.width(), extraOffset.height()); shadowOffset -= extraOffset; if (shadow->isWebkitBoxShadow()) - context.setLegacyShadow(shadowOffset, shadowRadius, shadowColor); + context->setLegacyShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace()); else - context.setShadow(shadowOffset, shadowRadius, shadowColor); + context->setShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace()); - context.fillRectWithRoundedHole(pixelSnappedOuterRect, pixelSnappedRoundedHole, fillColor); + context->fillRectWithRoundedHole(outerRect, roundedHole, fillColor, s->colorSpace()); } } } @@ -2400,24 +2647,27 @@ LayoutUnit RenderBoxModelObject::containingBlockLogicalWidthForContent() const RenderBoxModelObject* RenderBoxModelObject::continuation() const { - if (!hasContinuation()) - return nullptr; - return continuationMap().get(this); + if (!continuationMap) + return 0; + return continuationMap->get(this); } void RenderBoxModelObject::setContinuation(RenderBoxModelObject* continuation) { - if (continuation) - continuationMap().set(this, continuation); - else if (hasContinuation()) - continuationMap().remove(this); - setHasContinuation(!!continuation); + if (continuation) { + if (!continuationMap) + continuationMap = new ContinuationMap; + continuationMap->set(this, continuation); + } else { + if (continuationMap) + continuationMap->remove(this); + } } RenderTextFragment* RenderBoxModelObject::firstLetterRemainingText() const { if (!firstLetterRemainingTextMap) - return nullptr; + return 0; return firstLetterRemainingTextMap->get(this); } @@ -2497,39 +2747,46 @@ LayoutRect RenderBoxModelObject::localCaretRectForEmptyElement(LayoutUnit width, return currentStyle.isHorizontalWritingMode() ? LayoutRect(x, y, caretWidth, height) : LayoutRect(y, x, height, caretWidth); } -bool RenderBoxModelObject::shouldAntialiasLines(GraphicsContext& context) +bool RenderBoxModelObject::shouldAntialiasLines(GraphicsContext* context) { // FIXME: We may want to not antialias when scaled by an integral value, // and we may want to antialias when translated by a non-integral value. - return !context.getCTM().isIdentityOrTranslationOrFlipped(); + return !context->getCTM().isIdentityOrTranslationOrFlipped(); } void RenderBoxModelObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const { - RenderElement* container = this->container(); - if (!container) + auto o = container(); + if (!o) return; - - // FIXME: This code is wrong for named flow threads since it only works for content in the first region. - // We also don't want to run it for multicolumn flow threads, since we can use our knowledge of column - // geometry to actually get a better result. + // The point inside a box that's inside a region has its coordinates relative to the region, // not the FlowThread that is its container in the RenderObject tree. - if (is<RenderBox>(*this) && container->isOutOfFlowRenderFlowThread()) { - RenderRegion* startRegion = nullptr; - RenderRegion* endRegion = nullptr; - if (downcast<RenderFlowThread>(*container).getRegionRangeForBox(downcast<RenderBox>(this), startRegion, endRegion)) - container = startRegion; + if (o->isRenderFlowThread() && isRenderBlock()) { + // FIXME (CSSREGIONS): switch to Box instead of Block when we'll have range information + // for boxes as well, not just for blocks. + RenderRegion* startRegion; + RenderRegion* endRegion; + toRenderFlowThread(o)->getRegionRangeForBox(toRenderBlock(this), startRegion, endRegion); + if (startRegion) + o = startRegion; } - container->mapAbsoluteToLocalPoint(mode, transformState); + o->mapAbsoluteToLocalPoint(mode, transformState); + + LayoutSize containerOffset = offsetFromContainer(o, LayoutPoint()); - LayoutSize containerOffset = offsetFromContainer(*container, LayoutPoint()); + if (!style().hasOutOfFlowPosition() && o->hasColumns()) { + RenderBlock* block = toRenderBlock(o); + LayoutPoint point(roundedLayoutPoint(transformState.mappedPoint())); + point -= containerOffset; + block->adjustForColumnRect(containerOffset, point); + } - bool preserve3D = mode & UseTransforms && (container->style().preserves3D() || style().preserves3D()); - if (mode & UseTransforms && shouldUseTransformFromContainer(container)) { + bool preserve3D = mode & UseTransforms && (o->style().preserves3D() || style().preserves3D()); + if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { TransformationMatrix t; - getTransformFromContainer(container, containerOffset, t); + getTransformFromContainer(o, containerOffset, t); transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); } else transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); @@ -2539,7 +2796,7 @@ void RenderBoxModelObject::moveChildTo(RenderBoxModelObject* toBoxModelObject, R { // We assume that callers have cleared their positioned objects list for child moves (!fullRemoveInsert) so the // positioned renderer maps don't become stale. It would be too slow to do the map lookup on each call. - ASSERT(!fullRemoveInsert || !is<RenderBlock>(*this) || !downcast<RenderBlock>(*this).hasPositionedObjects()); + ASSERT(!fullRemoveInsert || !isRenderBlock() || !toRenderBlock(this)->hasPositionedObjects()); ASSERT(this == child->parent()); ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); @@ -2560,32 +2817,16 @@ void RenderBoxModelObject::moveChildrenTo(RenderBoxModelObject* toBoxModelObject // This condition is rarely hit since this function is usually called on // anonymous blocks which can no longer carry positioned objects (see r120761) // or when fullRemoveInsert is false. - if (fullRemoveInsert && is<RenderBlock>(*this)) { - downcast<RenderBlock>(*this).removePositionedObjects(nullptr); - if (is<RenderBlockFlow>(*this)) - downcast<RenderBlockFlow>(*this).removeFloatingObjects(); + if (fullRemoveInsert && isRenderBlock()) { + toRenderBlock(this)->removePositionedObjects(0); + if (isRenderBlockFlow()) + toRenderBlockFlow(this)->removeFloatingObjects(); } ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); for (RenderObject* child = startChild; child && child != endChild; ) { // Save our next sibling as moveChildTo will clear it. RenderObject* nextSibling = child->nextSibling(); - - // Check to make sure we're not saving the firstLetter as the nextSibling. - // When the |child| object will be moved, its firstLetter will be recreated, - // so saving it now in nextSibling would let us with a destroyed object. - if (is<RenderTextFragment>(*child) && is<RenderText>(nextSibling)) { - RenderObject* firstLetterObj = nullptr; - if (RenderBlock* block = downcast<RenderTextFragment>(*child).blockForAccompanyingFirstLetter()) { - RenderElement* firstLetterContainer = nullptr; - block->getFirstLetter(firstLetterObj, firstLetterContainer, child); - } - - // This is the first letter, skip it. - if (firstLetterObj == nextSibling) - nextSibling = nextSibling->nextSibling(); - } - moveChildTo(toBoxModelObject, child, beforeChild, fullRemoveInsert); child = nextSibling; } |