Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(640)

Unified Diff: Source/core/paint/NinePieceImageGrid.cpp

Issue 1180053009: Rewrite nine piece image painting code (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Address review comments Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/paint/NinePieceImageGrid.h ('k') | Source/core/paint/NinePieceImageGridTest.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « Source/core/paint/NinePieceImageGrid.h ('k') | Source/core/paint/NinePieceImageGridTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698