OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/paint/BackgroundImageGeometry.h" | 5 #include "core/paint/BackgroundImageGeometry.h" |
6 | 6 |
7 #include "core/frame/FrameView.h" | 7 #include "core/frame/FrameView.h" |
8 #include "core/layout/LayoutBox.h" | 8 #include "core/layout/LayoutBox.h" |
9 #include "core/layout/LayoutBoxModelObject.h" | 9 #include "core/layout/LayoutBoxModelObject.h" |
10 #include "core/layout/LayoutView.h" | 10 #include "core/layout/LayoutView.h" |
11 #include "core/layout/compositing/CompositedLayerMapping.h" | 11 #include "core/layout/compositing/CompositedLayerMapping.h" |
12 #include "core/paint/PaintLayer.h" | 12 #include "core/paint/PaintLayer.h" |
13 #include "platform/LayoutUnit.h" | 13 #include "platform/LayoutUnit.h" |
14 #include "platform/geometry/LayoutRect.h" | 14 #include "platform/geometry/LayoutRect.h" |
15 | 15 |
16 namespace blink { | 16 namespace blink { |
17 | 17 |
18 namespace { | 18 namespace { |
19 | 19 |
20 // Return the amount of space to leave between image tiles for the background-re
peat: space property. | 20 // Return the amount of space to leave between image tiles for the |
| 21 // background-repeat: space property. |
21 inline LayoutUnit getSpaceBetweenImageTiles(LayoutUnit areaSize, | 22 inline LayoutUnit getSpaceBetweenImageTiles(LayoutUnit areaSize, |
22 LayoutUnit tileSize) { | 23 LayoutUnit tileSize) { |
23 int numberOfTiles = (areaSize / tileSize).toInt(); | 24 int numberOfTiles = (areaSize / tileSize).toInt(); |
24 LayoutUnit space(-1); | 25 LayoutUnit space(-1); |
25 | 26 |
26 if (numberOfTiles > 1) { | 27 if (numberOfTiles > 1) { |
27 // Spec doesn't specify rounding, so use the same method as for background-r
epeat: round. | 28 // Spec doesn't specify rounding, so use the same method as for |
| 29 // background-repeat: round. |
28 space = (areaSize - numberOfTiles * tileSize) / (numberOfTiles - 1); | 30 space = (areaSize - numberOfTiles * tileSize) / (numberOfTiles - 1); |
29 } | 31 } |
30 | 32 |
31 return space; | 33 return space; |
32 } | 34 } |
33 | 35 |
34 bool fixedBackgroundPaintsInLocalCoordinates( | 36 bool fixedBackgroundPaintsInLocalCoordinates( |
35 const LayoutObject& obj, | 37 const LayoutObject& obj, |
36 const GlobalPaintFlags globalPaintFlags) { | 38 const GlobalPaintFlags globalPaintFlags) { |
37 if (!obj.isLayoutView()) | 39 if (!obj.isLayoutView()) |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 } | 101 } |
100 } else if (layerWidth.isAuto() && layerHeight.isAuto()) { | 102 } else if (layerWidth.isAuto() && layerHeight.isAuto()) { |
101 // If both width and height are auto, use the image's intrinsic size. | 103 // If both width and height are auto, use the image's intrinsic size. |
102 tileSize = imageIntrinsicSize; | 104 tileSize = imageIntrinsicSize; |
103 } | 105 } |
104 | 106 |
105 tileSize.clampNegativeToZero(); | 107 tileSize.clampNegativeToZero(); |
106 return tileSize; | 108 return tileSize; |
107 } | 109 } |
108 case SizeNone: { | 110 case SizeNone: { |
109 // If both values are 'auto' then the intrinsic width and/or height of the
image should be used, if any. | 111 // If both values are 'auto' then the intrinsic width and/or height of the |
| 112 // image should be used, if any. |
110 if (!imageIntrinsicSize.isEmpty()) | 113 if (!imageIntrinsicSize.isEmpty()) |
111 return imageIntrinsicSize; | 114 return imageIntrinsicSize; |
112 | 115 |
113 // If the image has neither an intrinsic width nor an intrinsic height, it
s size is determined as for 'contain'. | 116 // If the image has neither an intrinsic width nor an intrinsic height, |
| 117 // its size is determined as for 'contain'. |
114 type = Contain; | 118 type = Contain; |
115 } | 119 } |
116 case Contain: | 120 case Contain: |
117 case Cover: { | 121 case Cover: { |
118 float horizontalScaleFactor = | 122 float horizontalScaleFactor = |
119 imageIntrinsicSize.width() | 123 imageIntrinsicSize.width() |
120 ? positioningAreaSize.width().toFloat() / | 124 ? positioningAreaSize.width().toFloat() / |
121 imageIntrinsicSize.width() | 125 imageIntrinsicSize.width() |
122 : 1.0f; | 126 : 1.0f; |
123 float verticalScaleFactor = imageIntrinsicSize.height() | 127 float verticalScaleFactor = imageIntrinsicSize.height() |
124 ? positioningAreaSize.height().toFloat() / | 128 ? positioningAreaSize.height().toFloat() / |
125 imageIntrinsicSize.height() | 129 imageIntrinsicSize.height() |
126 : 1.0f; | 130 : 1.0f; |
127 // Force the dimension that determines the size to exactly match the | 131 // Force the dimension that determines the size to exactly match the |
128 // positioningAreaSize in that dimension, so that rounding of floating poi
nt | 132 // positioningAreaSize in that dimension, so that rounding of floating |
129 // approximation to LayoutUnit do not shrink the image to smaller than the | 133 // point approximation to LayoutUnit do not shrink the image to smaller |
130 // positioningAreaSize. | 134 // than the positioningAreaSize. |
131 if (type == Contain) { | 135 if (type == Contain) { |
132 if (horizontalScaleFactor < verticalScaleFactor) | 136 if (horizontalScaleFactor < verticalScaleFactor) |
133 return LayoutSize( | 137 return LayoutSize( |
134 positioningAreaSize.width(), | 138 positioningAreaSize.width(), |
135 LayoutUnit(std::max( | 139 LayoutUnit(std::max( |
136 1.0f, imageIntrinsicSize.height() * horizontalScaleFactor))); | 140 1.0f, imageIntrinsicSize.height() * horizontalScaleFactor))); |
137 return LayoutSize(LayoutUnit(std::max(1.0f, imageIntrinsicSize.width() * | 141 return LayoutSize(LayoutUnit(std::max(1.0f, imageIntrinsicSize.width() * |
138 verticalScaleFactor)), | 142 verticalScaleFactor)), |
139 positioningAreaSize.height()); | 143 positioningAreaSize.height()); |
140 } | 144 } |
(...skipping 21 matching lines...) Expand all Loading... |
162 for (const LayoutBlock* block = object.containingBlock(); block; | 166 for (const LayoutBlock* block = object.containingBlock(); block; |
163 block = block->containingBlock()) { | 167 block = block->containingBlock()) { |
164 if (block->hasOverflowClip()) | 168 if (block->hasOverflowClip()) |
165 result += block->scrolledContentOffset(); | 169 result += block->scrolledContentOffset(); |
166 if (block == container) | 170 if (block == container) |
167 break; | 171 break; |
168 } | 172 } |
169 return result; | 173 return result; |
170 } | 174 } |
171 | 175 |
172 // When we match the sub-pixel fraction of the destination rect in a dimension,
we | 176 // When we match the sub-pixel fraction of the destination rect in a dimension, |
173 // snap the same way. This commonly occurs when the background is meant to fill
the | 177 // we snap the same way. This commonly occurs when the background is meant to |
174 // padding box but there's a border (which in Blink is always stored as an integ
er). | 178 // fill the padding box but there's a border (which in Blink is always stored as |
175 // Otherwise we floor to avoid growing our tile size. Often these tiles are from
a | 179 // an integer). Otherwise we floor to avoid growing our tile size. Often these |
176 // sprite map, and bleeding adjacent sprites is visually worse than clipping the | 180 // tiles are from a sprite map, and bleeding adjacent sprites is visually worse |
177 // intended one. | 181 // than clipping the intended one. |
178 LayoutSize applySubPixelHeuristicToImageSize(const LayoutSize& size, | 182 LayoutSize applySubPixelHeuristicToImageSize(const LayoutSize& size, |
179 const LayoutRect& destination) { | 183 const LayoutRect& destination) { |
180 LayoutSize snappedSize = | 184 LayoutSize snappedSize = |
181 LayoutSize(size.width().fraction() == destination.width().fraction() | 185 LayoutSize(size.width().fraction() == destination.width().fraction() |
182 ? snapSizeToPixel(size.width(), destination.x()) | 186 ? snapSizeToPixel(size.width(), destination.x()) |
183 : size.width().floor(), | 187 : size.width().floor(), |
184 size.height().fraction() == destination.height().fraction() | 188 size.height().fraction() == destination.height().fraction() |
185 ? snapSizeToPixel(size.height(), destination.y()) | 189 ? snapSizeToPixel(size.height(), destination.y()) |
186 : size.height().floor()); | 190 : size.height().floor()); |
187 return snappedSize; | 191 return snappedSize; |
(...skipping 15 matching lines...) Expand all Loading... |
203 setPhaseY(LayoutUnit(-std::min(roundedOffset, 0))); | 207 setPhaseY(LayoutUnit(-std::min(roundedOffset, 0))); |
204 m_destRect.setHeight(m_tileSize.height() + std::min(roundedOffset, 0)); | 208 m_destRect.setHeight(m_tileSize.height() + std::min(roundedOffset, 0)); |
205 setSpaceSize(LayoutSize(spaceSize().width(), LayoutUnit())); | 209 setSpaceSize(LayoutSize(spaceSize().width(), LayoutUnit())); |
206 } | 210 } |
207 | 211 |
208 void BackgroundImageGeometry::setRepeatX(const FillLayer& fillLayer, | 212 void BackgroundImageGeometry::setRepeatX(const FillLayer& fillLayer, |
209 LayoutUnit unsnappedTileWidth, | 213 LayoutUnit unsnappedTileWidth, |
210 LayoutUnit snappedAvailableWidth, | 214 LayoutUnit snappedAvailableWidth, |
211 LayoutUnit unsnappedAvailableWidth, | 215 LayoutUnit unsnappedAvailableWidth, |
212 LayoutUnit extraOffset) { | 216 LayoutUnit extraOffset) { |
213 // We would like to identify the phase as a fraction of the image size in the
absence of snapping, | 217 // We would like to identify the phase as a fraction of the image size in the |
214 // then re-apply it to the snapped values. This is to handle large positions. | 218 // absence of snapping, then re-apply it to the snapped values. This is to |
| 219 // handle large positions. |
215 if (unsnappedTileWidth) { | 220 if (unsnappedTileWidth) { |
216 LayoutUnit computedXPosition = roundedMinimumValueForLength( | 221 LayoutUnit computedXPosition = roundedMinimumValueForLength( |
217 fillLayer.xPosition(), unsnappedAvailableWidth); | 222 fillLayer.xPosition(), unsnappedAvailableWidth); |
218 if (fillLayer.backgroundXOrigin() == RightEdge) { | 223 if (fillLayer.backgroundXOrigin() == RightEdge) { |
219 float numberOfTilesInPosition = | 224 float numberOfTilesInPosition = |
220 (snappedAvailableWidth - computedXPosition + extraOffset).toFloat() / | 225 (snappedAvailableWidth - computedXPosition + extraOffset).toFloat() / |
221 unsnappedTileWidth.toFloat(); | 226 unsnappedTileWidth.toFloat(); |
222 float fractionalPositionWithinTile = | 227 float fractionalPositionWithinTile = |
223 numberOfTilesInPosition - truncf(numberOfTilesInPosition); | 228 numberOfTilesInPosition - truncf(numberOfTilesInPosition); |
224 setPhaseX(LayoutUnit( | 229 setPhaseX(LayoutUnit( |
(...skipping 11 matching lines...) Expand all Loading... |
236 setPhaseX(LayoutUnit()); | 241 setPhaseX(LayoutUnit()); |
237 } | 242 } |
238 setSpaceSize(LayoutSize(LayoutUnit(), spaceSize().height())); | 243 setSpaceSize(LayoutSize(LayoutUnit(), spaceSize().height())); |
239 } | 244 } |
240 | 245 |
241 void BackgroundImageGeometry::setRepeatY(const FillLayer& fillLayer, | 246 void BackgroundImageGeometry::setRepeatY(const FillLayer& fillLayer, |
242 LayoutUnit unsnappedTileHeight, | 247 LayoutUnit unsnappedTileHeight, |
243 LayoutUnit snappedAvailableHeight, | 248 LayoutUnit snappedAvailableHeight, |
244 LayoutUnit unsnappedAvailableHeight, | 249 LayoutUnit unsnappedAvailableHeight, |
245 LayoutUnit extraOffset) { | 250 LayoutUnit extraOffset) { |
246 // We would like to identify the phase as a fraction of the image size in the
absence of snapping, | 251 // We would like to identify the phase as a fraction of the image size in the |
247 // then re-apply it to the snapped values. This is to handle large positions. | 252 // absence of snapping, then re-apply it to the snapped values. This is to |
| 253 // handle large positions. |
248 if (unsnappedTileHeight) { | 254 if (unsnappedTileHeight) { |
249 LayoutUnit computedYPosition = roundedMinimumValueForLength( | 255 LayoutUnit computedYPosition = roundedMinimumValueForLength( |
250 fillLayer.yPosition(), unsnappedAvailableHeight); | 256 fillLayer.yPosition(), unsnappedAvailableHeight); |
251 if (fillLayer.backgroundYOrigin() == BottomEdge) { | 257 if (fillLayer.backgroundYOrigin() == BottomEdge) { |
252 float numberOfTilesInPosition = | 258 float numberOfTilesInPosition = |
253 (snappedAvailableHeight - computedYPosition + extraOffset).toFloat() / | 259 (snappedAvailableHeight - computedYPosition + extraOffset).toFloat() / |
254 unsnappedTileHeight.toFloat(); | 260 unsnappedTileHeight.toFloat(); |
255 float fractionalPositionWithinTile = | 261 float fractionalPositionWithinTile = |
256 numberOfTilesInPosition - truncf(numberOfTilesInPosition); | 262 numberOfTilesInPosition - truncf(numberOfTilesInPosition); |
257 setPhaseY(LayoutUnit( | 263 setPhaseY(LayoutUnit( |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
322 // It is only possible reach here when root element has a box. | 328 // It is only possible reach here when root element has a box. |
323 Element* documentElement = obj.document().documentElement(); | 329 Element* documentElement = obj.document().documentElement(); |
324 DCHECK(documentElement); | 330 DCHECK(documentElement); |
325 DCHECK(documentElement->layoutObject()); | 331 DCHECK(documentElement->layoutObject()); |
326 DCHECK(documentElement->layoutObject()->isBox()); | 332 DCHECK(documentElement->layoutObject()->isBox()); |
327 rootBox = toLayoutBox(documentElement->layoutObject()); | 333 rootBox = toLayoutBox(documentElement->layoutObject()); |
328 } | 334 } |
329 const LayoutBoxModelObject& positioningBox = | 335 const LayoutBoxModelObject& positioningBox = |
330 isLayoutView ? static_cast<const LayoutBoxModelObject&>(*rootBox) : obj; | 336 isLayoutView ? static_cast<const LayoutBoxModelObject&>(*rootBox) : obj; |
331 | 337 |
332 // Determine the background positioning area and set destRect to the backgroun
d painting area. | 338 // Determine the background positioning area and set destRect to the |
333 // destRect will be adjusted later if the background is non-repeating. | 339 // background painting area. destRect will be adjusted later if the |
334 // FIXME: transforms spec says that fixed backgrounds behave like scroll insid
e transforms. | 340 // background is non-repeating. |
| 341 // FIXME: transforms spec says that fixed backgrounds behave like scroll |
| 342 // inside transforms. |
335 bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment; | 343 bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment; |
336 | 344 |
337 if (RuntimeEnabledFeatures::fastMobileScrollingEnabled()) { | 345 if (RuntimeEnabledFeatures::fastMobileScrollingEnabled()) { |
338 // As a side effect of an optimization to blit on scroll, we do not honor th
e CSS | 346 // As a side effect of an optimization to blit on scroll, we do not honor |
339 // property "background-attachment: fixed" because it may result in renderin
g | 347 // the CSS property "background-attachment: fixed" because it may result in |
340 // artifacts. Note, these artifacts only appear if we are blitting on scroll
of | 348 // rendering artifacts. Note, these artifacts only appear if we are blitting |
341 // a page that has fixed background images. | 349 // on scroll of a page that has fixed background images. |
342 fixedAttachment = false; | 350 fixedAttachment = false; |
343 } | 351 } |
344 | 352 |
345 if (!fixedAttachment) { | 353 if (!fixedAttachment) { |
346 setDestRect(paintRect); | 354 setDestRect(paintRect); |
347 | 355 |
348 LayoutUnit right; | 356 LayoutUnit right; |
349 LayoutUnit bottom; | 357 LayoutUnit bottom; |
350 // Scroll and Local. | 358 // Scroll and Local. |
351 if (fillLayer.origin() != BorderFillBox) { | 359 if (fillLayer.origin() != BorderFillBox) { |
352 left = LayoutUnit(positioningBox.borderLeft()); | 360 left = LayoutUnit(positioningBox.borderLeft()); |
353 right = LayoutUnit(positioningBox.borderRight()); | 361 right = LayoutUnit(positioningBox.borderRight()); |
354 top = LayoutUnit(positioningBox.borderTop()); | 362 top = LayoutUnit(positioningBox.borderTop()); |
355 bottom = LayoutUnit(positioningBox.borderBottom()); | 363 bottom = LayoutUnit(positioningBox.borderBottom()); |
356 if (fillLayer.origin() == ContentFillBox) { | 364 if (fillLayer.origin() == ContentFillBox) { |
357 left += positioningBox.paddingLeft(); | 365 left += positioningBox.paddingLeft(); |
358 right += positioningBox.paddingRight(); | 366 right += positioningBox.paddingRight(); |
359 top += positioningBox.paddingTop(); | 367 top += positioningBox.paddingTop(); |
360 bottom += positioningBox.paddingBottom(); | 368 bottom += positioningBox.paddingBottom(); |
361 } | 369 } |
362 } | 370 } |
363 | 371 |
364 if (isLayoutView) { | 372 if (isLayoutView) { |
365 // The background of the box generated by the root element covers the enti
re canvas and will | 373 // The background of the box generated by the root element covers the |
366 // be painted by the view object, but the we should still use the root ele
ment box for | 374 // entire canvas and will be painted by the view object, but the we should |
367 // positioning. | 375 // still use the root element box for positioning. |
368 positioningAreaSize = | 376 positioningAreaSize = |
369 rootBox->size() - LayoutSize(left + right, top + bottom), | 377 rootBox->size() - LayoutSize(left + right, top + bottom), |
370 rootBox->location(); | 378 rootBox->location(); |
371 // The input paint rect is specified in root element local coordinate (i.e
. a transform | 379 // The input paint rect is specified in root element local coordinate |
372 // is applied on the context for painting), and is expanded to cover the w
hole canvas. | 380 // (i.e. a transform is applied on the context for painting), and is |
373 // Since left/top is relative to the paint rect, we need to offset them ba
ck. | 381 // expanded to cover the whole canvas. Since left/top is relative to the |
| 382 // paint rect, we need to offset them back. |
374 left -= paintRect.x(); | 383 left -= paintRect.x(); |
375 top -= paintRect.y(); | 384 top -= paintRect.y(); |
376 } else { | 385 } else { |
377 positioningAreaSize = | 386 positioningAreaSize = |
378 paintRect.size() - LayoutSize(left + right, top + bottom); | 387 paintRect.size() - LayoutSize(left + right, top + bottom); |
379 } | 388 } |
380 } else { | 389 } else { |
381 setHasNonLocalGeometry(); | 390 setHasNonLocalGeometry(); |
382 | 391 |
383 LayoutRect viewportRect = obj.viewRect(); | 392 LayoutRect viewportRect = obj.viewRect(); |
(...skipping 11 matching lines...) Expand all Loading... |
395 if (paintContainer) | 404 if (paintContainer) |
396 viewportRect.moveBy( | 405 viewportRect.moveBy( |
397 LayoutPoint(-paintContainer->localToAbsolute(FloatPoint()))); | 406 LayoutPoint(-paintContainer->localToAbsolute(FloatPoint()))); |
398 | 407 |
399 setDestRect(viewportRect); | 408 setDestRect(viewportRect); |
400 positioningAreaSize = destRect().size(); | 409 positioningAreaSize = destRect().size(); |
401 } | 410 } |
402 | 411 |
403 LayoutSize fillTileSize( | 412 LayoutSize fillTileSize( |
404 calculateFillTileSize(positioningBox, fillLayer, positioningAreaSize)); | 413 calculateFillTileSize(positioningBox, fillLayer, positioningAreaSize)); |
405 // It's necessary to apply the heuristic here prior to any further calculation
s to avoid | 414 // It's necessary to apply the heuristic here prior to any further |
406 // incorrectly using sub-pixel values that won't be present in the painted til
e. | 415 // calculations to avoid incorrectly using sub-pixel values that won't be |
| 416 // present in the painted tile. |
407 setTileSize(applySubPixelHeuristicToImageSize(fillTileSize, m_destRect)); | 417 setTileSize(applySubPixelHeuristicToImageSize(fillTileSize, m_destRect)); |
408 | 418 |
409 EFillRepeat backgroundRepeatX = fillLayer.repeatX(); | 419 EFillRepeat backgroundRepeatX = fillLayer.repeatX(); |
410 EFillRepeat backgroundRepeatY = fillLayer.repeatY(); | 420 EFillRepeat backgroundRepeatY = fillLayer.repeatY(); |
411 LayoutUnit unsnappedAvailableWidth = | 421 LayoutUnit unsnappedAvailableWidth = |
412 positioningAreaSize.width() - fillTileSize.width(); | 422 positioningAreaSize.width() - fillTileSize.width(); |
413 LayoutUnit unsnappedAvailableHeight = | 423 LayoutUnit unsnappedAvailableHeight = |
414 positioningAreaSize.height() - fillTileSize.height(); | 424 positioningAreaSize.height() - fillTileSize.height(); |
415 positioningAreaSize = | 425 positioningAreaSize = |
416 LayoutSize(snapSizeToPixel(positioningAreaSize.width(), m_destRect.x()), | 426 LayoutSize(snapSizeToPixel(positioningAreaSize.width(), m_destRect.x()), |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
512 useFixedAttachment(paintRect.location()); | 522 useFixedAttachment(paintRect.location()); |
513 | 523 |
514 // Clip the final output rect to the paint rect | 524 // Clip the final output rect to the paint rect |
515 m_destRect.intersect(paintRect); | 525 m_destRect.intersect(paintRect); |
516 | 526 |
517 // Snap as-yet unsnapped values. | 527 // Snap as-yet unsnapped values. |
518 setDestRect(LayoutRect(pixelSnappedIntRect(m_destRect))); | 528 setDestRect(LayoutRect(pixelSnappedIntRect(m_destRect))); |
519 } | 529 } |
520 | 530 |
521 } // namespace blink | 531 } // namespace blink |
OLD | NEW |