1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "core/paint/NinePieceImageGrid.h"
8 #include "core/style/ComputedStyle.h"
9 #include "core/style/NinePieceImage.h"
10 #include "platform/LengthFunctions.h"
11 #include "platform/geometry/FloatSize.h"
12 #include "platform/geometry/IntSize.h"
16 static LayoutUnit
computeEdgeWidth(const BorderImageLength
& borderSlice
, int borderSide
, const LayoutUnit
& imageSide
,
17 const LayoutUnit
& boxExtent
)
19 if (borderSlice
.isNumber())
20 return borderSlice
.number() * borderSide
;
21 if (borderSlice
.length().isAuto())
23 return valueForLength(borderSlice
.length(), boxExtent
);
26 static int computeEdgeSlice(const Length
& slice
, LayoutUnit maximum
)
28 return std::min
<int>(maximum
, valueForLength(slice
, maximum
));
31 NinePieceImageGrid::NinePieceImageGrid(const NinePieceImage
& ninePieceImage
, IntSize imageSize
, IntRect borderImageArea
,
32 const IntRectOutsets
& borderWidths
)
33 : m_borderImageArea(borderImageArea
)
34 , m_imageSize(imageSize
)
35 , m_horizontalTileRule((Image::TileRule
)ninePieceImage
.horizontalRule())
36 , m_verticalTileRule((Image::TileRule
)ninePieceImage
.verticalRule())
37 , m_fill(ninePieceImage
.fill())
39 StyleImage
* styleImage
= ninePieceImage
.image();
42 float imageScaleFactor
= styleImage
->imageScaleFactor();
43 m_top
.slice
= computeEdgeSlice(ninePieceImage
.imageSlices().top(), imageSize
.height()) * imageScaleFactor
;
44 m_right
.slice
= computeEdgeSlice(ninePieceImage
.imageSlices().right(), imageSize
.width()) * imageScaleFactor
;
45 m_bottom
.slice
= computeEdgeSlice(ninePieceImage
.imageSlices().bottom(), imageSize
.height()) * imageScaleFactor
;
46 m_left
.slice
= computeEdgeSlice(ninePieceImage
.imageSlices().left(), imageSize
.width()) * imageScaleFactor
;
48 m_top
.width
= computeEdgeWidth(ninePieceImage
.borderSlices().top(), borderWidths
.top(), m_top
.slice
,
49 borderImageArea
.height());
50 m_right
.width
= computeEdgeWidth(ninePieceImage
.borderSlices().right(), borderWidths
.right(), m_right
.slice
,
51 borderImageArea
.width());
52 m_bottom
.width
= computeEdgeWidth(ninePieceImage
.borderSlices().bottom(), borderWidths
.bottom(), m_bottom
.slice
,
53 borderImageArea
.height());
54 m_left
.width
= computeEdgeWidth(ninePieceImage
.borderSlices().left(), borderWidths
.left(), m_left
.slice
,
55 borderImageArea
.width());
57 // The spec says: Given Lwidth as the width of the border image area, Lheight as its height, and Wside as the border
58 // image width offset for the side, let f = min(Lwidth/(Wleft+Wright), Lheight/(Wtop+Wbottom)). If f < 1, then all W
59 // are reduced by multiplying them by f.
60 int borderSideWidth
= std::max(1, m_left
.width
+ m_right
.width
);
61 int borderSideHeight
= std::max(1, m_top
.width
+ m_bottom
.width
);
62 float borderSideScaleFactor
= std::min((float)borderImageArea
.width() / borderSideWidth
,
63 (float)borderImageArea
.height() / borderSideHeight
);
64 if (borderSideScaleFactor
< 1) {
65 m_top
.width
*= borderSideScaleFactor
;
66 m_right
.width
*= borderSideScaleFactor
;
67 m_bottom
.width
*= borderSideScaleFactor
;
68 m_left
.width
*= borderSideScaleFactor
;
72 // Given a rectangle, construct a subrectangle using offset, width and height. Negative offsets are relative to the
73 // extent of the given rectangle.
74 static FloatRect
subrect(IntRect rect
, float offsetX
, float offsetY
, float width
, float height
)
76 float baseX
= rect
.x();
80 float baseY
= rect
.y();
84 return FloatRect(baseX
+ offsetX
, baseY
+ offsetY
, width
, height
);
87 static FloatRect
subrect(IntSize size
, float offsetX
, float offsetY
, float width
, float height
)
89 return subrect(IntRect(IntPoint(), size
), offsetX
, offsetY
, width
, height
);
92 static inline void setCornerPiece(NinePieceImageGrid::NinePieceDrawInfo
& drawInfo
, bool isDrawable
,
93 const FloatRect
& source
, const FloatRect
& destination
)
95 drawInfo
.isDrawable
= isDrawable
;
96 if (drawInfo
.isDrawable
) {
97 drawInfo
.source
= source
;
98 drawInfo
.destination
= destination
;
102 void NinePieceImageGrid::setDrawInfoCorner(NinePieceDrawInfo
& drawInfo
, NinePiece piece
) const
106 setCornerPiece(drawInfo
, m_top
.isDrawable() && m_left
.isDrawable(),
107 subrect(m_imageSize
, 0, 0, m_left
.slice
, m_top
.slice
),
108 subrect(m_borderImageArea
, 0, 0, m_left
.width
, m_top
.width
));
110 case BottomLeftPiece
:
111 setCornerPiece(drawInfo
, m_bottom
.isDrawable() && m_left
.isDrawable(),
112 subrect(m_imageSize
, 0, -m_bottom
.slice
, m_left
.slice
, m_bottom
.slice
),
113 subrect(m_borderImageArea
, 0, -m_bottom
.width
, m_left
.width
, m_bottom
.width
));
116 setCornerPiece(drawInfo
, m_top
.isDrawable() && m_right
.isDrawable(),
117 subrect(m_imageSize
, -m_right
.slice
, 0, m_right
.slice
, m_top
.slice
),
118 subrect(m_borderImageArea
, -m_right
.width
, 0, m_right
.width
, m_top
.width
));
120 case BottomRightPiece
:
121 setCornerPiece(drawInfo
, m_bottom
.isDrawable() && m_right
.isDrawable(),
122 subrect(m_imageSize
, -m_right
.slice
, -m_bottom
.slice
, m_right
.slice
, m_bottom
.slice
),
123 subrect(m_borderImageArea
, -m_right
.width
, -m_bottom
.width
, m_right
.width
, m_bottom
.width
));
126 ASSERT_NOT_REACHED();
131 static inline void setHorizontalEdge(NinePieceImageGrid::NinePieceDrawInfo
& drawInfo
,
132 const NinePieceImageGrid::Edge
& edge
, const FloatRect
& source
, const FloatRect
& destination
,
133 Image::TileRule tileRule
)
135 drawInfo
.isDrawable
= edge
.isDrawable() && source
.width() > 0;
136 if (drawInfo
.isDrawable
) {
137 drawInfo
.source
= source
;
138 drawInfo
.destination
= destination
;
139 drawInfo
.tileScale
= FloatSize(edge
.scale(), edge
.scale());
140 drawInfo
.tileRule
= { tileRule
, Image::StretchTile
};
144 static inline void setVerticalEdge(NinePieceImageGrid::NinePieceDrawInfo
& drawInfo
,
145 const NinePieceImageGrid::Edge
& edge
, const FloatRect
& source
, const FloatRect
& destination
,
146 Image::TileRule tileRule
)
148 drawInfo
.isDrawable
= edge
.isDrawable() && source
.height() > 0;
149 if (drawInfo
.isDrawable
) {
150 drawInfo
.source
= source
;
151 drawInfo
.destination
= destination
;
152 drawInfo
.tileScale
= FloatSize(edge
.scale(), edge
.scale());
153 drawInfo
.tileRule
= { Image::StretchTile
, tileRule
};
157 void NinePieceImageGrid::setDrawInfoEdge(NinePieceDrawInfo
& drawInfo
, NinePiece piece
) const
159 IntSize edgeSourceSize
= m_imageSize
- IntSize(m_left
.slice
+ m_right
.slice
, m_top
.slice
+ m_bottom
.slice
);
160 IntSize edgeDestinationSize
= m_borderImageArea
.size() - IntSize(m_left
.width
+ m_right
.width
, m_top
.width
+ m_bottom
.width
);
164 setVerticalEdge(drawInfo
, m_left
,
165 subrect(m_imageSize
, 0, m_top
.slice
, m_left
.slice
, edgeSourceSize
.height()),
166 subrect(m_borderImageArea
, 0, m_top
.width
, m_left
.width
, edgeDestinationSize
.height()),
170 setVerticalEdge(drawInfo
, m_right
,
171 subrect(m_imageSize
, -m_right
.slice
, m_top
.slice
, m_right
.slice
, edgeSourceSize
.height()),
172 subrect(m_borderImageArea
, -m_right
.width
, m_top
.width
, m_right
.width
, edgeDestinationSize
.height()),
176 setHorizontalEdge(drawInfo
, m_top
,
177 subrect(m_imageSize
, m_left
.slice
, 0, edgeSourceSize
.width(), m_top
.slice
),
178 subrect(m_borderImageArea
, m_left
.width
, 0, edgeDestinationSize
.width(), m_top
.width
),
179 m_horizontalTileRule
);
182 setHorizontalEdge(drawInfo
, m_bottom
,
183 subrect(m_imageSize
, m_left
.slice
, -m_bottom
.slice
, edgeSourceSize
.width(), m_bottom
.slice
),
184 subrect(m_borderImageArea
, m_left
.width
, -m_bottom
.width
, edgeDestinationSize
.width(), m_bottom
.width
),
185 m_horizontalTileRule
);
188 ASSERT_NOT_REACHED();
193 void NinePieceImageGrid::setDrawInfoMiddle(NinePieceDrawInfo
& drawInfo
) const
195 IntSize sourceSize
= m_imageSize
- IntSize(m_left
.slice
+ m_right
.slice
, m_top
.slice
+ m_bottom
.slice
);
196 IntSize destinationSize
=
197 m_borderImageArea
.size() - IntSize(m_left
.width
+ m_right
.width
, m_top
.width
+ m_bottom
.width
);
199 drawInfo
.isDrawable
= m_fill
&& !sourceSize
.isEmpty() && !destinationSize
.isEmpty();
200 if (!drawInfo
.isDrawable
)
203 drawInfo
.source
= subrect(m_imageSize
, m_left
.slice
, m_top
.slice
, sourceSize
.width(), sourceSize
.height());
204 drawInfo
.destination
= subrect(m_borderImageArea
, m_left
.width
, m_top
.width
,
205 destinationSize
.width(), destinationSize
.height());
207 FloatSize
middleScaleFactor(1, 1);
209 if (m_top
.isDrawable())
210 middleScaleFactor
.setWidth(m_top
.scale());
211 else if (m_bottom
.isDrawable())
212 middleScaleFactor
.setWidth(m_bottom
.scale());
214 if (m_left
.isDrawable())
215 middleScaleFactor
.setHeight(m_left
.scale());
216 else if (m_right
.isDrawable())
217 middleScaleFactor
.setHeight(m_right
.scale());
219 if (!sourceSize
.isEmpty()) {
220 // For "stretch" rules, just override the scale factor and replace. We only have to do this for the center tile,
221 // since sides don't even use the scale factor unless they have a rule other than "stretch". The middle however
222 // can have "stretch" specified in one axis but not the other, so we have to correct the scale here.
223 if (m_horizontalTileRule
== (Image::TileRule
)StretchImageRule
)
224 middleScaleFactor
.setWidth((float) destinationSize
.width() / sourceSize
.width());
226 if (m_verticalTileRule
== (Image::TileRule
)StretchImageRule
)
227 middleScaleFactor
.setHeight((float) destinationSize
.height() / sourceSize
.height());
230 drawInfo
.tileScale
= middleScaleFactor
;
231 drawInfo
.tileRule
= { m_horizontalTileRule
, m_verticalTileRule
};
234 NinePieceImageGrid::NinePieceDrawInfo
NinePieceImageGrid::getNinePieceDrawInfo(NinePiece piece
) const
236 NinePieceDrawInfo drawInfo
;
237 drawInfo
.isCornerPiece
=
238 piece
== TopLeftPiece
|| piece
== TopRightPiece
|| piece
== BottomLeftPiece
|| piece
== BottomRightPiece
;
240 if (drawInfo
.isCornerPiece
)
241 setDrawInfoCorner(drawInfo
, piece
);
242 else if (piece
!= MiddlePiece
)
243 setDrawInfoEdge(drawInfo
, piece
);
245 setDrawInfoMiddle(drawInfo
);