OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "config.h" |
| 6 #include "core/paint/NinePieceImageGrid.h" |
| 7 |
| 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" |
| 13 |
| 14 namespace blink { |
| 15 |
| 16 static LayoutUnit computeEdgeWidth(const BorderImageLength& borderSlice, int bor
derSide, const LayoutUnit& imageSide, |
| 17 const LayoutUnit& boxExtent) |
| 18 { |
| 19 if (borderSlice.isNumber()) |
| 20 return borderSlice.number() * borderSide; |
| 21 if (borderSlice.length().isAuto()) |
| 22 return imageSide; |
| 23 return valueForLength(borderSlice.length(), boxExtent); |
| 24 } |
| 25 |
| 26 static int computeEdgeSlice(const Length& slice, LayoutUnit maximum) |
| 27 { |
| 28 return std::min<int>(maximum, valueForLength(slice, maximum)); |
| 29 } |
| 30 |
| 31 NinePieceImageGrid::NinePieceImageGrid(const NinePieceImage& ninePieceImage, Int
Size 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()) |
| 38 { |
| 39 StyleImage* styleImage = ninePieceImage.image(); |
| 40 ASSERT(styleImage); |
| 41 |
| 42 float imageScaleFactor = styleImage->imageScaleFactor(); |
| 43 m_top.slice = computeEdgeSlice(ninePieceImage.imageSlices().top(), imageSize
.height()) * imageScaleFactor; |
| 44 m_right.slice = computeEdgeSlice(ninePieceImage.imageSlices().right(), image
Size.width()) * imageScaleFactor; |
| 45 m_bottom.slice = computeEdgeSlice(ninePieceImage.imageSlices().bottom(), ima
geSize.height()) * imageScaleFactor; |
| 46 m_left.slice = computeEdgeSlice(ninePieceImage.imageSlices().left(), imageSi
ze.width()) * imageScaleFactor; |
| 47 |
| 48 m_top.width = computeEdgeWidth(ninePieceImage.borderSlices().top(), borderWi
dths.top(), m_top.slice, |
| 49 borderImageArea.height()); |
| 50 m_right.width = computeEdgeWidth(ninePieceImage.borderSlices().right(), bord
erWidths.right(), m_right.slice, |
| 51 borderImageArea.width()); |
| 52 m_bottom.width = computeEdgeWidth(ninePieceImage.borderSlices().bottom(), bo
rderWidths.bottom(), m_bottom.slice, |
| 53 borderImageArea.height()); |
| 54 m_left.width = computeEdgeWidth(ninePieceImage.borderSlices().left(), border
Widths.left(), m_left.slice, |
| 55 borderImageArea.width()); |
| 56 |
| 57 // The spec says: Given Lwidth as the width of the border image area, Lheigh
t as its height, and Wside as the border |
| 58 // image width offset for the side, let f = min(Lwidth/(Wleft+Wright), Lheig
ht/(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() / bord
erSideWidth, |
| 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; |
| 69 } |
| 70 } |
| 71 |
| 72 // Given a rectangle, construct a subrectangle using offset, width and height. N
egative 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) |
| 75 { |
| 76 float baseX = rect.x(); |
| 77 if (offsetX < 0) |
| 78 baseX = rect.maxX(); |
| 79 |
| 80 float baseY = rect.y(); |
| 81 if (offsetY < 0) |
| 82 baseY = rect.maxY(); |
| 83 |
| 84 return FloatRect(baseX + offsetX, baseY + offsetY, width, height); |
| 85 } |
| 86 |
| 87 static FloatRect subrect(IntSize size, float offsetX, float offsetY, float width
, float height) |
| 88 { |
| 89 return subrect(IntRect(IntPoint(), size), offsetX, offsetY, width, height); |
| 90 } |
| 91 |
| 92 static inline void setCornerPiece(NinePieceImageGrid::NinePieceDrawInfo& drawInf
o, bool isDrawable, |
| 93 const FloatRect& source, const FloatRect& destination) |
| 94 { |
| 95 drawInfo.isDrawable = isDrawable; |
| 96 if (drawInfo.isDrawable) { |
| 97 drawInfo.source = source; |
| 98 drawInfo.destination = destination; |
| 99 } |
| 100 } |
| 101 |
| 102 void NinePieceImageGrid::setDrawInfoCorner(NinePieceDrawInfo& drawInfo, NinePiec
e piece) const |
| 103 { |
| 104 switch (piece) { |
| 105 case TopLeftPiece: |
| 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)); |
| 109 break; |
| 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.slic
e), |
| 113 subrect(m_borderImageArea, 0, -m_bottom.width, m_left.width, m_botto
m.width)); |
| 114 break; |
| 115 case TopRightPiece: |
| 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.w
idth)); |
| 119 break; |
| 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)); |
| 124 break; |
| 125 default: |
| 126 ASSERT_NOT_REACHED(); |
| 127 break; |
| 128 } |
| 129 } |
| 130 |
| 131 static inline void setHorizontalEdge(NinePieceImageGrid::NinePieceDrawInfo& draw
Info, |
| 132 const NinePieceImageGrid::Edge& edge, const FloatRect& source, const FloatRe
ct& destination, |
| 133 Image::TileRule tileRule) |
| 134 { |
| 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 }; |
| 141 } |
| 142 } |
| 143 |
| 144 static inline void setVerticalEdge(NinePieceImageGrid::NinePieceDrawInfo& drawIn
fo, |
| 145 const NinePieceImageGrid::Edge& edge, const FloatRect& source, const FloatRe
ct& destination, |
| 146 Image::TileRule tileRule) |
| 147 { |
| 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 }; |
| 154 } |
| 155 } |
| 156 |
| 157 void NinePieceImageGrid::setDrawInfoEdge(NinePieceDrawInfo& drawInfo, NinePiece
piece) const |
| 158 { |
| 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.widt
h + m_right.width, m_top.width + m_bottom.width); |
| 161 |
| 162 switch (piece) { |
| 163 case LeftPiece: |
| 164 setVerticalEdge(drawInfo, m_left, |
| 165 subrect(m_imageSize, 0, m_top.slice, m_left.slice, edgeSourceSize.he
ight()), |
| 166 subrect(m_borderImageArea, 0, m_top.width, m_left.width, edgeDestina
tionSize.height()), |
| 167 m_verticalTileRule); |
| 168 break; |
| 169 case RightPiece: |
| 170 setVerticalEdge(drawInfo, m_right, |
| 171 subrect(m_imageSize, -m_right.slice, m_top.slice, m_right.slice, edg
eSourceSize.height()), |
| 172 subrect(m_borderImageArea, -m_right.width, m_top.width, m_right.widt
h, edgeDestinationSize.height()), |
| 173 m_verticalTileRule); |
| 174 break; |
| 175 case TopPiece: |
| 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.widt
h(), m_top.width), |
| 179 m_horizontalTileRule); |
| 180 break; |
| 181 case BottomPiece: |
| 182 setHorizontalEdge(drawInfo, m_bottom, |
| 183 subrect(m_imageSize, m_left.slice, -m_bottom.slice, edgeSourceSize.w
idth(), m_bottom.slice), |
| 184 subrect(m_borderImageArea, m_left.width, -m_bottom.width, edgeDestin
ationSize.width(), m_bottom.width), |
| 185 m_horizontalTileRule); |
| 186 break; |
| 187 default: |
| 188 ASSERT_NOT_REACHED(); |
| 189 break; |
| 190 } |
| 191 } |
| 192 |
| 193 void NinePieceImageGrid::setDrawInfoMiddle(NinePieceDrawInfo& drawInfo) const |
| 194 { |
| 195 IntSize sourceSize = m_imageSize - IntSize(m_left.slice + m_right.slice, m_t
op.slice + m_bottom.slice); |
| 196 IntSize destinationSize = |
| 197 m_borderImageArea.size() - IntSize(m_left.width + m_right.width, m_top.w
idth + m_bottom.width); |
| 198 |
| 199 drawInfo.isDrawable = m_fill && !sourceSize.isEmpty() && !destinationSize.is
Empty(); |
| 200 if (!drawInfo.isDrawable) |
| 201 return; |
| 202 |
| 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()); |
| 206 |
| 207 FloatSize middleScaleFactor(1, 1); |
| 208 |
| 209 if (m_top.isDrawable()) |
| 210 middleScaleFactor.setWidth(m_top.scale()); |
| 211 else if (m_bottom.isDrawable()) |
| 212 middleScaleFactor.setWidth(m_bottom.scale()); |
| 213 |
| 214 if (m_left.isDrawable()) |
| 215 middleScaleFactor.setHeight(m_left.scale()); |
| 216 else if (m_right.isDrawable()) |
| 217 middleScaleFactor.setHeight(m_right.scale()); |
| 218 |
| 219 if (!sourceSize.isEmpty()) { |
| 220 // For "stretch" rules, just override the scale factor and replace. We o
nly have to do this for the center tile, |
| 221 // since sides don't even use the scale factor unless they have a rule o
ther than "stretch". The middle however |
| 222 // can have "stretch" specified in one axis but not the other, so we hav
e to correct the scale here. |
| 223 if (m_horizontalTileRule == (Image::TileRule)StretchImageRule) |
| 224 middleScaleFactor.setWidth((float) destinationSize.width() / sourceS
ize.width()); |
| 225 |
| 226 if (m_verticalTileRule == (Image::TileRule)StretchImageRule) |
| 227 middleScaleFactor.setHeight((float) destinationSize.height() / sourc
eSize.height()); |
| 228 } |
| 229 |
| 230 drawInfo.tileScale = middleScaleFactor; |
| 231 drawInfo.tileRule = { m_horizontalTileRule, m_verticalTileRule }; |
| 232 } |
| 233 |
| 234 NinePieceImageGrid::NinePieceDrawInfo NinePieceImageGrid::getNinePieceDrawInfo(N
inePiece piece) const |
| 235 { |
| 236 NinePieceDrawInfo drawInfo; |
| 237 drawInfo.isCornerPiece = |
| 238 piece == TopLeftPiece || piece == TopRightPiece || piece == BottomLeftPi
ece || piece == BottomRightPiece; |
| 239 |
| 240 if (drawInfo.isCornerPiece) |
| 241 setDrawInfoCorner(drawInfo, piece); |
| 242 else if (piece != MiddlePiece) |
| 243 setDrawInfoEdge(drawInfo, piece); |
| 244 else |
| 245 setDrawInfoMiddle(drawInfo); |
| 246 |
| 247 return drawInfo; |
| 248 } |
| 249 |
| 250 } // namespace blink |
OLD | NEW |