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..b59320dcaa719ad9b4f8285df0a6cdf38b6fd002 |
--- /dev/null |
+++ b/Source/core/paint/NinePieceImageGrid.cpp |
@@ -0,0 +1,250 @@ |
+// 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; |
+ } |
+} |
+ |
+// Given a rectangle, construct a subrectangle using offset, width and height. Negative offsets are relative to the |
+// extent of the given rectangle. |
+static FloatRect subrect(IntRect rect, float offsetX, float offsetY, float width, float height) |
+{ |
+ 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.isDrawable() && m_left.isDrawable(), |
+ 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.isDrawable() && m_left.isDrawable(), |
+ 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.isDrawable() && m_right.isDrawable(), |
+ 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.isDrawable() && m_right.isDrawable(), |
+ 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.isDrawable() && 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.isDrawable() && source.height() > 0; |
+ if (drawInfo.isDrawable) { |
+ drawInfo.source = source; |
+ drawInfo.destination = destination; |
+ drawInfo.tileScale = FloatSize(edge.scale(), edge.scale()); |
+ drawInfo.tileRule = { Image::StretchTile, tileRule }; |
+ } |
+} |
+ |
+void NinePieceImageGrid::setDrawInfoEdge(NinePieceDrawInfo& drawInfo, NinePiece piece) const |
+{ |
+ IntSize edgeSourceSize = m_imageSize - IntSize(m_left.slice + m_right.slice, m_top.slice + m_bottom.slice); |
+ IntSize edgeDestinationSize = m_borderImageArea.size() - IntSize(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 = m_imageSize - IntSize(m_left.slice + m_right.slice, m_top.slice + m_bottom.slice); |
+ IntSize destinationSize = |
+ m_borderImageArea.size() - IntSize(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.isDrawable()) |
+ middleScaleFactor.setWidth(m_top.scale()); |
+ else if (m_bottom.isDrawable()) |
+ middleScaleFactor.setWidth(m_bottom.scale()); |
+ |
+ if (m_left.isDrawable()) |
+ middleScaleFactor.setHeight(m_left.scale()); |
+ else if (m_right.isDrawable()) |
+ 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 |