Index: Source/core/paint/BoxPainter.cpp |
diff --git a/Source/core/paint/BoxPainter.cpp b/Source/core/paint/BoxPainter.cpp |
index 23c02de8e83c4958db7ba929610ca4ea1a52e75d..4e8cd240aa6f97510879643cac08ba7859262758 100644 |
--- a/Source/core/paint/BoxPainter.cpp |
+++ b/Source/core/paint/BoxPainter.cpp |
@@ -440,7 +440,7 @@ void BoxPainter::paintFillLayerExtended(LayoutBoxModelObject& obj, const PaintIn |
BackgroundImageGeometry geometry; |
if (bgImage) |
- calculateBackgroundImageGeometry(obj, paintInfo.paintContainer(), paintInfo.globalPaintFlags(), bgLayer, scrolledPaintRect, geometry, backgroundObject); |
+ geometry.calculate(obj, paintInfo.paintContainer(), paintInfo.globalPaintFlags(), bgLayer, scrolledPaintRect, backgroundObject); |
bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(obj, obj.style()->effectiveZoom()); |
// Paint the color first underneath all images, culled if background image occludes it. |
@@ -570,297 +570,11 @@ void BoxPainter::paintClippingMask(const PaintInfo& paintInfo, const LayoutPoint |
paintInfo.context->fillRect(paintRect, Color::black); |
} |
-// Return the amount of space to leave between image tiles for the background-repeat: space property. |
-static inline int getSpaceBetweenImageTiles(int areaSize, int tileSize) |
-{ |
- int numberOfTiles = areaSize / tileSize; |
- int space = -1; |
- |
- if (numberOfTiles > 1) { |
- // Spec doesn't specify rounding, so use the same method as for background-repeat: round. |
- space = lroundf((areaSize - numberOfTiles * tileSize) / (float)(numberOfTiles - 1)); |
- } |
- |
- return space; |
-} |
- |
-void BoxPainter::calculateBackgroundImageGeometry(const LayoutBoxModelObject& obj, const LayoutBoxModelObject* paintContainer, const GlobalPaintFlags globalPaintFlags, const FillLayer& fillLayer, const LayoutRect& paintRect, |
- BackgroundImageGeometry& geometry, LayoutObject* backgroundObject) |
-{ |
- LayoutUnit left = 0; |
- LayoutUnit top = 0; |
- IntSize positioningAreaSize; |
- IntRect snappedPaintRect = pixelSnappedIntRect(paintRect); |
- bool isLayoutView = obj.isLayoutView(); |
- const LayoutBox* rootBox = nullptr; |
- if (isLayoutView) { |
- // It is only possible reach here when root element has a box. |
- Element* documentElement = obj.document().documentElement(); |
- ASSERT(documentElement); |
- ASSERT(documentElement->layoutObject()); |
- ASSERT(documentElement->layoutObject()->isBox()); |
- rootBox = toLayoutBox(documentElement->layoutObject()); |
- } |
- const LayoutBoxModelObject& positioningBox = isLayoutView ? static_cast<const LayoutBoxModelObject&>(*rootBox) : obj; |
- |
- // 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. |
- bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment; |
- |
- if (RuntimeEnabledFeatures::fastMobileScrollingEnabled()) { |
- // 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; |
- } |
- |
- if (!fixedAttachment) { |
- geometry.setDestRect(snappedPaintRect); |
- |
- LayoutUnit right = 0; |
- LayoutUnit bottom = 0; |
- // Scroll and Local. |
- if (fillLayer.origin() != BorderFillBox) { |
- left = positioningBox.borderLeft(); |
- right = positioningBox.borderRight(); |
- top = positioningBox.borderTop(); |
- bottom = positioningBox.borderBottom(); |
- if (fillLayer.origin() == ContentFillBox) { |
- left += positioningBox.paddingLeft(); |
- right += positioningBox.paddingRight(); |
- top += positioningBox.paddingTop(); |
- bottom += positioningBox.paddingBottom(); |
- } |
- } |
- |
- if (isLayoutView) { |
- // The background of the box generated by the root element covers the entire canvas and will |
- // be painted by the view object, but the we should still use the root element box for |
- // positioning. |
- positioningAreaSize = pixelSnappedIntSize(rootBox->size() - LayoutSize(left + right, top + bottom), rootBox->location()); |
- // The input paint rect is specified in root element local coordinate (i.e. a transform |
- // is applied on the context for painting), and is expanded to cover the whole canvas. |
- // Since left/top is relative to the paint rect, we need to offset them back. |
- left -= paintRect.x(); |
- top -= paintRect.y(); |
- } else { |
- positioningAreaSize = pixelSnappedIntSize(paintRect.size() - LayoutSize(left + right, top + bottom), paintRect.location()); |
- } |
- } else { |
- geometry.setHasNonLocalGeometry(); |
- |
- IntRect viewportRect = pixelSnappedIntRect(obj.viewRect()); |
- if (fixedBackgroundPaintsInLocalCoordinates(obj, globalPaintFlags)) |
- viewportRect.setLocation(IntPoint()); |
- else if (FrameView* frameView = obj.view()->frameView()) |
- viewportRect.setLocation(frameView->scrollPosition()); |
- |
- if (paintContainer) { |
- IntPoint absoluteContainerOffset = roundedIntPoint(paintContainer->localToAbsolute(FloatPoint())); |
- viewportRect.moveBy(-absoluteContainerOffset); |
- } |
- |
- geometry.setDestRect(viewportRect); |
- positioningAreaSize = geometry.destRect().size(); |
- } |
- |
- const LayoutObject* clientForBackgroundImage = backgroundObject ? backgroundObject : &obj; |
- IntSize fillTileSize = calculateFillTileSize(positioningBox, fillLayer, positioningAreaSize); |
- fillLayer.image()->setContainerSizeForLayoutObject(clientForBackgroundImage, fillTileSize, obj.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(); |
- |
- LayoutUnit computedXPosition = roundedMinimumValueForLength(fillLayer.xPosition(), availableWidth); |
- if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > 0 && fillTileSize.width() > 0) { |
- long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.width() / fillTileSize.width())); |
- |
- // Round tile size per css3-background spec. |
- fillTileSize.setWidth(lroundf(positioningAreaSize.width() / (float)nrTiles)); |
- |
- // Maintain aspect ratio if background-size: auto is set |
- if (fillLayer.size().size.height().isAuto() && backgroundRepeatY != RoundFill) { |
- fillTileSize.setHeight(fillTileSize.height() * positioningAreaSize.width() / (nrTiles * fillTileSize.width())); |
- } |
- |
- geometry.setTileSize(fillTileSize); |
- geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0); |
- geometry.setSpaceSize(IntSize()); |
- } |
- |
- LayoutUnit computedYPosition = roundedMinimumValueForLength(fillLayer.yPosition(), availableHeight); |
- if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > 0 && fillTileSize.height() > 0) { |
- long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.height() / fillTileSize.height())); |
- |
- // Round tile size per css3-background spec. |
- fillTileSize.setHeight(lroundf(positioningAreaSize.height() / (float)nrTiles)); |
- |
- // Maintain aspect ratio if background-size: auto is set |
- if (fillLayer.size().size.width().isAuto() && backgroundRepeatX != RoundFill) { |
- fillTileSize.setWidth(fillTileSize.width() * positioningAreaSize.height() / (nrTiles * fillTileSize.height())); |
- } |
- |
- geometry.setTileSize(fillTileSize); |
- geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0); |
- geometry.setSpaceSize(IntSize()); |
- } |
- |
- if (backgroundRepeatX == RepeatFill) { |
- int xOffset = fillLayer.backgroundXOrigin() == RightEdge ? availableWidth - computedXPosition : computedXPosition; |
- geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(xOffset + left) % geometry.tileSize().width() : 0); |
- geometry.setSpaceSize(IntSize()); |
- } else if (backgroundRepeatX == SpaceFill && fillTileSize.width() > 0) { |
- int space = getSpaceBetweenImageTiles(positioningAreaSize.width(), geometry.tileSize().width()); |
- int actualWidth = geometry.tileSize().width() + space; |
- |
- if (space >= 0) { |
- computedXPosition = roundedMinimumValueForLength(Length(), availableWidth); |
- geometry.setSpaceSize(IntSize(space, 0)); |
- geometry.setPhaseX(actualWidth ? actualWidth - roundToInt(computedXPosition + left) % actualWidth : 0); |
- } else { |
- backgroundRepeatX = NoRepeatFill; |
- } |
- } |
- if (backgroundRepeatX == NoRepeatFill) { |
- int xOffset = fillLayer.backgroundXOrigin() == RightEdge ? availableWidth - computedXPosition : computedXPosition; |
- geometry.setNoRepeatX(left + xOffset); |
- geometry.setSpaceSize(IntSize(0, geometry.spaceSize().height())); |
- } |
- |
- if (backgroundRepeatY == RepeatFill) { |
- int yOffset = fillLayer.backgroundYOrigin() == BottomEdge ? availableHeight - computedYPosition : computedYPosition; |
- geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(yOffset + top) % geometry.tileSize().height() : 0); |
- geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0)); |
- } else if (backgroundRepeatY == SpaceFill && fillTileSize.height() > 0) { |
- int space = getSpaceBetweenImageTiles(positioningAreaSize.height(), geometry.tileSize().height()); |
- int actualHeight = geometry.tileSize().height() + space; |
- |
- if (space >= 0) { |
- computedYPosition = roundedMinimumValueForLength(Length(), availableHeight); |
- geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), space)); |
- geometry.setPhaseY(actualHeight ? actualHeight - roundToInt(computedYPosition + top) % actualHeight : 0); |
- } else { |
- backgroundRepeatY = NoRepeatFill; |
- } |
- } |
- if (backgroundRepeatY == NoRepeatFill) { |
- int yOffset = fillLayer.backgroundYOrigin() == BottomEdge ? availableHeight - computedYPosition : computedYPosition; |
- geometry.setNoRepeatY(top + yOffset); |
- geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0)); |
- } |
- |
- if (fixedAttachment) |
- geometry.useFixedAttachment(snappedPaintRect.location()); |
- |
- geometry.clip(snappedPaintRect); |
-} |
- |
InterpolationQuality BoxPainter::chooseInterpolationQuality(LayoutObject& obj, GraphicsContext* context, Image* image, const void* layer, const LayoutSize& size) |
{ |
return ImageQualityController::imageQualityController()->chooseInterpolationQuality(context, &obj, image, layer, size); |
} |
-bool BoxPainter::fixedBackgroundPaintsInLocalCoordinates(const LayoutObject& obj, const GlobalPaintFlags globalPaintFlags) |
-{ |
- if (!obj.isLayoutView()) |
- return false; |
- |
- const LayoutView& view = toLayoutView(obj); |
- |
- if (globalPaintFlags & GlobalPaintFlattenCompositingLayers) |
- return false; |
- |
- DeprecatedPaintLayer* rootLayer = view.layer(); |
- if (!rootLayer || rootLayer->compositingState() == NotComposited) |
- return false; |
- |
- return rootLayer->compositedDeprecatedPaintLayerMapping()->backgroundLayerPaintsFixedRootBackground(); |
-} |
- |
-static inline void applySubPixelHeuristicForTileSize(LayoutSize& tileSize, const IntSize& positioningAreaSize) |
-{ |
- 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()); |
-} |
- |
-IntSize BoxPainter::calculateFillTileSize(const LayoutBoxModelObject& obj, const FillLayer& fillLayer, const IntSize& positioningAreaSize) |
-{ |
- StyleImage* image = fillLayer.image(); |
- EFillSizeType type = fillLayer.size().type; |
- |
- IntSize imageIntrinsicSize = obj.calculateImageIntrinsicDimensions(image, positioningAreaSize, LayoutBoxModelObject::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(); |
- |
- if (layerWidth.isFixed()) |
- tileSize.setWidth(layerWidth.value()); |
- else if (layerWidth.hasPercent()) |
- tileSize.setWidth(valueForLength(layerWidth, positioningAreaSize.width())); |
- |
- if (layerHeight.isFixed()) |
- tileSize.setHeight(layerHeight.value()); |
- else if (layerHeight.hasPercent()) |
- 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()) { |
- if (imageIntrinsicSize.height()) { |
- LayoutUnit adjustedWidth = imageIntrinsicSize.width() * tileSize.height() / imageIntrinsicSize.height(); |
- if (imageIntrinsicSize.width() >= 1 && adjustedWidth < 1) |
- adjustedWidth = 1; |
- tileSize.setWidth(adjustedWidth); |
- } |
- } else if (!layerWidth.isAuto() && layerHeight.isAuto()) { |
- if (imageIntrinsicSize.width()) { |
- LayoutUnit adjustedHeight = imageIntrinsicSize.height() * tileSize.width() / imageIntrinsicSize.width(); |
- if (imageIntrinsicSize.height() >= 1 && adjustedHeight < 1) |
- adjustedHeight = 1; |
- tileSize.setHeight(adjustedHeight); |
- } |
- } else if (layerWidth.isAuto() && layerHeight.isAuto()) { |
- // If both width and height are auto, use the image's intrinsic size. |
- tileSize = LayoutSize(imageIntrinsicSize); |
- } |
- |
- tileSize.clampNegativeToZero(); |
- 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. |
- if (!imageIntrinsicSize.isEmpty()) |
- return imageIntrinsicSize; |
- |
- // If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for 'contain'. |
- type = Contain; |
- } |
- case Contain: |
- case Cover: { |
- 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); |
- return IntSize(std::max(1l, lround(imageIntrinsicSize.width() * scaleFactor)), std::max(1l, lround(imageIntrinsicSize.height() * scaleFactor))); |
- } |
- } |
- |
- ASSERT_NOT_REACHED(); |
- return IntSize(); |
-} |
- |
bool BoxPainter::paintNinePieceImage(LayoutBoxModelObject& obj, GraphicsContext* graphicsContext, const LayoutRect& rect, const ComputedStyle& style, const NinePieceImage& ninePieceImage, SkXfermode::Mode op) |
{ |
NinePieceImagePainter ninePieceImagePainter(obj); |