Chromium Code Reviews| Index: Source/core/paint/NinePieceImageGrid.cpp |
| diff --git a/Source/core/paint/NinePieceImageGrid.cpp b/Source/core/paint/NinePieceImageGrid.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..bd95f946f0b76fdf417195eb6470ea0aadc496d8 |
| --- /dev/null |
| +++ b/Source/core/paint/NinePieceImageGrid.cpp |
| @@ -0,0 +1,254 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "config.h" |
| +#include "core/paint/NinePieceImageGrid.h" |
| + |
| +#include "core/style/ComputedStyle.h" |
| +#include "core/style/NinePieceImage.h" |
| +#include "platform/LengthFunctions.h" |
| +#include "platform/geometry/FloatSize.h" |
| +#include "platform/geometry/IntSize.h" |
| + |
| +namespace blink { |
| + |
| +static LayoutUnit computeEdgeWidth(const BorderImageLength& borderSlice, int borderSide, const LayoutUnit& imageSide, |
| + const LayoutUnit& boxExtent) |
| +{ |
| + if (borderSlice.isNumber()) |
| + return borderSlice.number() * borderSide; |
| + if (borderSlice.length().isAuto()) |
| + return imageSide; |
| + return valueForLength(borderSlice.length(), boxExtent); |
| +} |
| + |
| +static int computeEdgeSlice(const Length& slice, LayoutUnit maximum) |
| +{ |
| + return std::min<int>(maximum, valueForLength(slice, maximum)); |
| +} |
| + |
| +NinePieceImageGrid::NinePieceImageGrid(const NinePieceImage& ninePieceImage, IntSize imageSize, IntRect borderImageArea, |
| + const IntRectOutsets& borderWidths) |
| + : m_borderImageArea(borderImageArea) |
| + , m_imageSize(imageSize) |
| + , m_horizontalTileRule((Image::TileRule)ninePieceImage.horizontalRule()) |
| + , m_verticalTileRule((Image::TileRule)ninePieceImage.verticalRule()) |
| + , m_fill(ninePieceImage.fill()) |
| +{ |
| + StyleImage* styleImage = ninePieceImage.image(); |
| + ASSERT(styleImage); |
| + |
| + float imageScaleFactor = styleImage->imageScaleFactor(); |
| + m_top.slice = computeEdgeSlice(ninePieceImage.imageSlices().top(), imageSize.height()) * imageScaleFactor; |
| + m_right.slice = computeEdgeSlice(ninePieceImage.imageSlices().right(), imageSize.width()) * imageScaleFactor; |
| + m_bottom.slice = computeEdgeSlice(ninePieceImage.imageSlices().bottom(), imageSize.height()) * imageScaleFactor; |
| + m_left.slice = computeEdgeSlice(ninePieceImage.imageSlices().left(), imageSize.width()) * imageScaleFactor; |
| + |
| + m_top.width = computeEdgeWidth(ninePieceImage.borderSlices().top(), borderWidths.top(), m_top.slice, |
| + borderImageArea.height()); |
| + m_right.width = computeEdgeWidth(ninePieceImage.borderSlices().right(), borderWidths.right(), m_right.slice, |
| + borderImageArea.width()); |
| + m_bottom.width = computeEdgeWidth(ninePieceImage.borderSlices().bottom(), borderWidths.bottom(), m_bottom.slice, |
| + borderImageArea.height()); |
| + m_left.width = computeEdgeWidth(ninePieceImage.borderSlices().left(), borderWidths.left(), m_left.slice, |
| + borderImageArea.width()); |
| + |
| + // 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, m_left.width + m_right.width); |
| + int borderSideHeight = std::max(1, m_top.width + m_bottom.width); |
| + float borderSideScaleFactor = std::min((float)borderImageArea.width() / borderSideWidth, |
| + (float)borderImageArea.height() / borderSideHeight); |
| + if (borderSideScaleFactor < 1) { |
| + m_top.width *= borderSideScaleFactor; |
| + m_right.width *= borderSideScaleFactor; |
| + m_bottom.width *= borderSideScaleFactor; |
| + m_left.width *= borderSideScaleFactor; |
| + } |
| +} |
| + |
| +static FloatRect subrect(IntRect rect, float offsetX, float offsetY, float width, float height) |
|
fs
2015/06/24 13:19:10
Maybe a short comment on the behavior of this func
davve
2015/06/24 13:35:25
Done.
|
| +{ |
| + float baseX = rect.x(); |
| + if (offsetX < 0) |
| + baseX = rect.maxX(); |
| + |
| + float baseY = rect.y(); |
| + if (offsetY < 0) |
| + baseY = rect.maxY(); |
| + |
| + return FloatRect(baseX + offsetX, baseY + offsetY, width, height); |
| +} |
| + |
| +static FloatRect subrect(IntSize size, float offsetX, float offsetY, float width, float height) |
| +{ |
| + return subrect(IntRect(IntPoint(), size), offsetX, offsetY, width, height); |
| +} |
| + |
| +static inline void setCornerPiece(NinePieceImageGrid::NinePieceDrawInfo& drawInfo, bool isDrawable, |
| + const FloatRect& source, const FloatRect& destination) |
| +{ |
| + drawInfo.isDrawable = isDrawable; |
| + if (drawInfo.isDrawable) { |
| + drawInfo.source = source; |
| + drawInfo.destination = destination; |
| + } |
| +} |
| + |
| +void NinePieceImageGrid::setDrawInfoCorner(NinePieceDrawInfo& drawInfo, NinePiece piece) const |
| +{ |
| + switch (piece) { |
| + case TopLeftPiece: |
| + setCornerPiece(drawInfo, m_top.draw() && m_left.draw(), |
| + subrect(m_imageSize, 0, 0, m_left.slice, m_top.slice), |
| + subrect(m_borderImageArea, 0, 0, m_left.width, m_top.width)); |
| + break; |
| + case BottomLeftPiece: |
| + setCornerPiece(drawInfo, m_bottom.draw() && m_left.draw(), |
| + subrect(m_imageSize, 0, -m_bottom.slice, m_left.slice, m_bottom.slice), |
| + subrect(m_borderImageArea, 0, -m_bottom.width, m_left.width, m_bottom.width)); |
| + break; |
| + case TopRightPiece: |
| + setCornerPiece(drawInfo, m_top.draw() && m_right.draw(), |
| + subrect(m_imageSize, -m_right.slice, 0, m_right.slice, m_top.slice), |
| + subrect(m_borderImageArea, -m_right.width, 0, m_right.width, m_top.width)); |
| + break; |
| + case BottomRightPiece: |
| + setCornerPiece(drawInfo, m_bottom.draw() && m_right.draw(), |
| + subrect(m_imageSize, -m_right.slice, -m_bottom.slice, m_right.slice, m_bottom.slice), |
| + subrect(m_borderImageArea, -m_right.width, -m_bottom.width, m_right.width, m_bottom.width)); |
| + break; |
| + default: |
| + ASSERT_NOT_REACHED(); |
| + break; |
| + } |
| +} |
| + |
| +static inline void setHorizontalEdge(NinePieceImageGrid::NinePieceDrawInfo& drawInfo, |
| + const NinePieceImageGrid::Edge& edge, const FloatRect& source, const FloatRect& destination, |
| + Image::TileRule tileRule) |
| +{ |
| + drawInfo.isDrawable = edge.draw() && source.width() > 0; |
| + if (drawInfo.isDrawable) { |
| + drawInfo.source = source; |
| + drawInfo.destination = destination; |
| + drawInfo.tileScale = FloatSize(edge.scale(), edge.scale()); |
| + drawInfo.tileRule = { tileRule, Image::StretchTile }; |
| + } |
| +} |
| + |
| +static inline void setVerticalEdge(NinePieceImageGrid::NinePieceDrawInfo& drawInfo, |
| + const NinePieceImageGrid::Edge& edge, const FloatRect& source, const FloatRect& destination, |
| + Image::TileRule tileRule) |
| +{ |
| + drawInfo.isDrawable = edge.draw() && source.height() > 0; |
| + if (drawInfo.isDrawable) { |
| + drawInfo.source = source; |
| + drawInfo.destination = destination; |
| + drawInfo.tileScale = FloatSize(edge.scale(), edge.scale()); |
| + drawInfo.tileRule = { Image::StretchTile, tileRule }; |
| + } |
| +} |
| + |
| +static IntSize subsize(const IntSize& size, int horizontalInset, int verticalInset) |
|
fs
2015/06/24 13:19:10
I think there's an operator overload for this (if
davve
2015/06/24 13:35:25
Nice, I like the operator overload.
|
| +{ |
| + return IntSize(size.width() - horizontalInset, size.height() - verticalInset); |
| +} |
| + |
| +void NinePieceImageGrid::setDrawInfoEdge(NinePieceDrawInfo& drawInfo, NinePiece piece) const |
| +{ |
| + IntSize edgeSourceSize = subsize(m_imageSize, m_left.slice + m_right.slice, m_top.slice + m_bottom.slice); |
| + IntSize edgeDestinationSize = |
| + subsize(m_borderImageArea.size(), m_left.width + m_right.width, m_top.width + m_bottom.width); |
| + |
| + switch (piece) { |
| + case LeftPiece: |
| + setVerticalEdge(drawInfo, m_left, |
| + subrect(m_imageSize, 0, m_top.slice, m_left.slice, edgeSourceSize.height()), |
| + subrect(m_borderImageArea, 0, m_top.width, m_left.width, edgeDestinationSize.height()), |
| + m_verticalTileRule); |
| + break; |
| + case RightPiece: |
| + setVerticalEdge(drawInfo, m_right, |
| + subrect(m_imageSize, -m_right.slice, m_top.slice, m_right.slice, edgeSourceSize.height()), |
| + subrect(m_borderImageArea, -m_right.width, m_top.width, m_right.width, edgeDestinationSize.height()), |
| + m_verticalTileRule); |
| + break; |
| + case TopPiece: |
| + setHorizontalEdge(drawInfo, m_top, |
| + subrect(m_imageSize, m_left.slice, 0, edgeSourceSize.width(), m_top.slice), |
| + subrect(m_borderImageArea, m_left.width, 0, edgeDestinationSize.width(), m_top.width), |
| + m_horizontalTileRule); |
| + break; |
| + case BottomPiece: |
| + setHorizontalEdge(drawInfo, m_bottom, |
| + subrect(m_imageSize, m_left.slice, -m_bottom.slice, edgeSourceSize.width(), m_bottom.slice), |
| + subrect(m_borderImageArea, m_left.width, -m_bottom.width, edgeDestinationSize.width(), m_bottom.width), |
| + m_horizontalTileRule); |
| + break; |
| + default: |
| + ASSERT_NOT_REACHED(); |
| + break; |
| + } |
| +} |
| + |
| +void NinePieceImageGrid::setDrawInfoMiddle(NinePieceDrawInfo& drawInfo) const |
| +{ |
| + IntSize sourceSize = subsize(m_imageSize, m_left.slice + m_right.slice, m_top.slice + m_bottom.slice); |
| + IntSize destinationSize = |
| + subsize(m_borderImageArea.size(), m_left.width + m_right.width, m_top.width + m_bottom.width); |
| + |
| + drawInfo.isDrawable = m_fill && !sourceSize.isEmpty() && !destinationSize.isEmpty(); |
| + if (!drawInfo.isDrawable) |
| + return; |
| + |
| + drawInfo.source = subrect(m_imageSize, m_left.slice, m_top.slice, sourceSize.width(), sourceSize.height()); |
| + drawInfo.destination = subrect(m_borderImageArea, m_left.width, m_top.width, |
| + destinationSize.width(), destinationSize.height()); |
| + |
| + FloatSize middleScaleFactor(1, 1); |
| + |
| + if (m_top.draw()) |
| + middleScaleFactor.setWidth(m_top.scale()); |
| + else if (m_bottom.draw()) |
| + middleScaleFactor.setWidth(m_bottom.scale()); |
| + |
| + if (m_left.draw()) |
| + middleScaleFactor.setHeight(m_left.scale()); |
| + else if (m_right.draw()) |
| + middleScaleFactor.setHeight(m_right.scale()); |
| + |
| + if (!sourceSize.isEmpty()) { |
| + // For "stretch" rules, just override the scale factor and replace. We only have 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 (m_horizontalTileRule == (Image::TileRule)StretchImageRule) |
| + middleScaleFactor.setWidth((float) destinationSize.width() / sourceSize.width()); |
| + |
| + if (m_verticalTileRule == (Image::TileRule)StretchImageRule) |
| + middleScaleFactor.setHeight((float) destinationSize.height() / sourceSize.height()); |
| + } |
| + |
| + drawInfo.tileScale = middleScaleFactor; |
| + drawInfo.tileRule = { m_horizontalTileRule, m_verticalTileRule }; |
| +} |
| + |
| +NinePieceImageGrid::NinePieceDrawInfo NinePieceImageGrid::getNinePieceDrawInfo(NinePiece piece) const |
| +{ |
| + NinePieceDrawInfo drawInfo; |
| + drawInfo.isCornerPiece = |
| + piece == TopLeftPiece || piece == TopRightPiece || piece == BottomLeftPiece || piece == BottomRightPiece; |
| + |
| + if (drawInfo.isCornerPiece) |
| + setDrawInfoCorner(drawInfo, piece); |
| + else if (piece != MiddlePiece) |
| + setDrawInfoEdge(drawInfo, piece); |
| + else |
| + setDrawInfoMiddle(drawInfo); |
| + |
| + return drawInfo; |
| +} |
| + |
| +} // namespace blink |