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 "config.h" | 5 #include "config.h" |
6 #include "core/paint/BoxPainter.h" | 6 #include "core/paint/BoxPainter.h" |
7 | 7 |
| 8 #include "core/HTMLNames.h" |
| 9 #include "core/frame/Settings.h" |
| 10 #include "core/html/HTMLFrameOwnerElement.h" |
| 11 #include "core/paint/BackgroundImageGeometry.h" |
8 #include "core/paint/BoxDecorationData.h" | 12 #include "core/paint/BoxDecorationData.h" |
| 13 #include "core/rendering/ImageQualityController.h" |
9 #include "core/rendering/PaintInfo.h" | 14 #include "core/rendering/PaintInfo.h" |
10 #include "core/rendering/RenderBox.h" | 15 #include "core/rendering/RenderBox.h" |
| 16 #include "core/rendering/RenderBoxModelObject.h" |
11 #include "core/rendering/RenderLayer.h" | 17 #include "core/rendering/RenderLayer.h" |
12 #include "core/rendering/RenderTable.h" | 18 #include "core/rendering/RenderTable.h" |
13 #include "core/rendering/RenderTheme.h" | 19 #include "core/rendering/RenderTheme.h" |
14 #include "core/rendering/RenderView.h" | 20 #include "core/rendering/RenderView.h" |
| 21 #include "core/rendering/compositing/CompositedLayerMapping.h" |
| 22 #include "core/rendering/style/ShadowList.h" |
| 23 #include "platform/LengthFunctions.h" |
15 #include "platform/geometry/LayoutPoint.h" | 24 #include "platform/geometry/LayoutPoint.h" |
16 #include "platform/graphics/GraphicsContextStateSaver.h" | 25 #include "platform/graphics/GraphicsContextStateSaver.h" |
17 | 26 |
18 namespace blink { | 27 namespace blink { |
19 | 28 |
20 void BoxPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) | 29 void BoxPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
21 { | 30 { |
22 LayoutPoint adjustedPaintOffset = paintOffset + m_renderBox.location(); | 31 LayoutPoint adjustedPaintOffset = paintOffset + m_renderBox.location(); |
23 // default implementation. Just pass paint through to the children | 32 // default implementation. Just pass paint through to the children |
24 PaintInfo childInfo(paintInfo); | 33 PaintInfo childInfo(paintInfo); |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 GraphicsContext* context = paintInfo.context; | 159 GraphicsContext* context = paintInfo.context; |
151 if (!context) | 160 if (!context) |
152 shouldDrawBackgroundInSeparateBuffer = false; | 161 shouldDrawBackgroundInSeparateBuffer = false; |
153 | 162 |
154 bool skipBaseColor = false; | 163 bool skipBaseColor = false; |
155 if (shouldDrawBackgroundInSeparateBuffer) { | 164 if (shouldDrawBackgroundInSeparateBuffer) { |
156 bool isBaseColorVisible = !isBottomLayerOccluded && c.hasAlpha(); | 165 bool isBaseColorVisible = !isBottomLayerOccluded && c.hasAlpha(); |
157 | 166 |
158 // Paint the document's base background color outside the transparency l
ayer, | 167 // Paint the document's base background color outside the transparency l
ayer, |
159 // so that the background images don't blend with this color: http://crb
ug.com/389039. | 168 // so that the background images don't blend with this color: http://crb
ug.com/389039. |
160 if (isBaseColorVisible && m_renderBox.isDocumentElementWithOpaqueBackgro
und()) { | 169 if (isBaseColorVisible && isDocumentElementWithOpaqueBackground()) { |
161 m_renderBox.paintRootBackgroundColor(paintInfo, rect, Color()); | 170 paintRootBackgroundColor(paintInfo, rect, Color()); |
162 skipBaseColor = true; | 171 skipBaseColor = true; |
163 } | 172 } |
164 context->beginTransparencyLayer(1); | 173 context->beginTransparencyLayer(1); |
165 } | 174 } |
166 | 175 |
167 Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend(); | 176 Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend(); |
168 for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin();
it != topLayer; ++it) | 177 for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin();
it != topLayer; ++it) |
169 paintFillLayer(paintInfo, c, **it, rect, bleedAvoidance, op, backgroundO
bject, skipBaseColor); | 178 paintFillLayer(paintInfo, c, **it, rect, bleedAvoidance, op, backgroundO
bject, skipBaseColor); |
170 | 179 |
171 if (shouldDrawBackgroundInSeparateBuffer) | 180 if (shouldDrawBackgroundInSeparateBuffer) |
172 context->endLayer(); | 181 context->endLayer(); |
173 } | 182 } |
174 | 183 |
175 void BoxPainter::paintFillLayer(const PaintInfo& paintInfo, const Color& c, cons
t FillLayer& fillLayer, const LayoutRect& rect, | 184 void BoxPainter::paintFillLayer(const PaintInfo& paintInfo, const Color& c, cons
t FillLayer& fillLayer, const LayoutRect& rect, |
176 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject*
backgroundObject, bool skipBaseColor) | 185 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject*
backgroundObject, bool skipBaseColor) |
177 { | 186 { |
178 m_renderBox.paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoid
ance, 0, LayoutSize(), op, backgroundObject, skipBaseColor); | 187 paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoidance, 0, Lay
outSize(), op, backgroundObject, skipBaseColor); |
| 188 } |
| 189 |
| 190 void BoxPainter::applyBoxShadowForBackground(GraphicsContext* context) |
| 191 { |
| 192 const ShadowList* shadowList = m_renderBox.style()->boxShadow(); |
| 193 ASSERT(shadowList); |
| 194 for (size_t i = shadowList->shadows().size(); i--; ) { |
| 195 const ShadowData& boxShadow = shadowList->shadows()[i]; |
| 196 if (boxShadow.style() != Normal) |
| 197 continue; |
| 198 FloatSize shadowOffset(boxShadow.x(), boxShadow.y()); |
| 199 context->setShadow(shadowOffset, boxShadow.blur(), boxShadow.color(), |
| 200 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::Shad
owIgnoresAlpha); |
| 201 return; |
| 202 } |
| 203 } |
| 204 |
| 205 // FIXME: See crbug.com/382491. The use of getCTM in this context is incorrect b
ecause the matrix returned does not |
| 206 // include scales applied at raster time, such as the device zoom. |
| 207 static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, const LayoutRec
t& rect) |
| 208 { |
| 209 LayoutRect shrunkRect = rect; |
| 210 AffineTransform transform = context->getCTM(); |
| 211 shrunkRect.inflateX(-static_cast<LayoutUnit>(ceil(1 / transform.xScale()))); |
| 212 shrunkRect.inflateY(-static_cast<LayoutUnit>(ceil(1 / transform.yScale()))); |
| 213 return shrunkRect; |
| 214 } |
| 215 |
| 216 RoundedRect BoxPainter::getBackgroundRoundedRect(const LayoutRect& borderRect, I
nlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight, |
| 217 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const |
| 218 { |
| 219 RoundedRect border = m_renderBox.style()->getRoundedBorderFor(borderRect, in
cludeLogicalLeftEdge, includeLogicalRightEdge); |
| 220 if (box && (box->nextLineBox() || box->prevLineBox())) { |
| 221 RoundedRect segmentBorder = m_renderBox.style()->getRoundedBorderFor(Lay
outRect(0, 0, inlineBoxWidth, inlineBoxHeight), includeLogicalLeftEdge, includeL
ogicalRightEdge); |
| 222 border.setRadii(segmentBorder.radii()); |
| 223 } |
| 224 |
| 225 return border; |
| 226 } |
| 227 |
| 228 RoundedRect BoxPainter::backgroundRoundedRectAdjustedForBleedAvoidance(GraphicsC
ontext* context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvo
idance, InlineFlowBox* box, const LayoutSize& boxSize, bool includeLogicalLeftEd
ge, bool includeLogicalRightEdge) const |
| 229 { |
| 230 if (bleedAvoidance == BackgroundBleedShrinkBackground) { |
| 231 // We shrink the rectangle by one pixel on each side because the bleed i
s one pixel maximum. |
| 232 return getBackgroundRoundedRect(shrinkRectByOnePixel(context, borderRect
), box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogica
lRightEdge); |
| 233 } |
| 234 if (bleedAvoidance == BackgroundBleedBackgroundOverBorder) |
| 235 return m_renderBox.style()->getRoundedInnerBorderFor(borderRect, include
LogicalLeftEdge, includeLogicalRightEdge); |
| 236 |
| 237 return getBackgroundRoundedRect(borderRect, box, boxSize.width(), boxSize.he
ight(), includeLogicalLeftEdge, includeLogicalRightEdge); |
| 238 } |
| 239 |
| 240 void BoxPainter::clipRoundedInnerRect(GraphicsContext * context, const LayoutRec
t& rect, const RoundedRect& clipRect) |
| 241 { |
| 242 if (clipRect.isRenderable()) { |
| 243 context->clipRoundedRect(clipRect); |
| 244 } else { |
| 245 // We create a rounded rect for each of the corners and clip it, while m
aking sure we clip opposing corners together. |
| 246 if (!clipRect.radii().topLeft().isEmpty() || !clipRect.radii().bottomRig
ht().isEmpty()) { |
| 247 IntRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.max
X() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y()); |
| 248 RoundedRect::Radii topCornerRadii; |
| 249 topCornerRadii.setTopLeft(clipRect.radii().topLeft()); |
| 250 context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); |
| 251 |
| 252 IntRect bottomCorner(rect.x(), rect.y(), clipRect.rect().maxX() - re
ct.x(), clipRect.rect().maxY() - rect.y()); |
| 253 RoundedRect::Radii bottomCornerRadii; |
| 254 bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight()); |
| 255 context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii
)); |
| 256 } |
| 257 |
| 258 if (!clipRect.radii().topRight().isEmpty() || !clipRect.radii().bottomLe
ft().isEmpty()) { |
| 259 IntRect topCorner(rect.x(), clipRect.rect().y(), clipRect.rect().max
X() - rect.x(), rect.maxY() - clipRect.rect().y()); |
| 260 RoundedRect::Radii topCornerRadii; |
| 261 topCornerRadii.setTopRight(clipRect.radii().topRight()); |
| 262 context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); |
| 263 |
| 264 IntRect bottomCorner(clipRect.rect().x(), rect.y(), rect.maxX() - cl
ipRect.rect().x(), clipRect.rect().maxY() - rect.y()); |
| 265 RoundedRect::Radii bottomCornerRadii; |
| 266 bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft()); |
| 267 context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii
)); |
| 268 } |
| 269 } |
| 270 } |
| 271 |
| 272 void BoxPainter::paintFillLayerExtended(const PaintInfo& paintInfo, const Color&
color, const FillLayer& bgLayer, const LayoutRect& rect, |
| 273 BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSiz
e& boxSize, CompositeOperator op, RenderObject* backgroundObject, bool skipBaseC
olor) |
| 274 { |
| 275 GraphicsContext* context = paintInfo.context; |
| 276 if (rect.isEmpty()) |
| 277 return; |
| 278 |
| 279 bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true; |
| 280 bool includeRightEdge = box ? box->includeLogicalRightEdge() : true; |
| 281 |
| 282 bool hasRoundedBorder = m_renderBox.style()->hasBorderRadius() && (includeLe
ftEdge || includeRightEdge); |
| 283 bool clippedWithLocalScrolling = m_renderBox.hasOverflowClip() && bgLayer.at
tachment() == LocalBackgroundAttachment; |
| 284 bool isBorderFill = bgLayer.clip() == BorderFillBox; |
| 285 bool isDocumentElementRenderer = m_renderBox.isDocumentElement(); |
| 286 bool isBottomLayer = !bgLayer.next(); |
| 287 |
| 288 Color bgColor = color; |
| 289 StyleImage* bgImage = bgLayer.image(); |
| 290 bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(m_renderBox,
m_renderBox.style()->effectiveZoom()); |
| 291 |
| 292 bool forceBackgroundToWhite = false; |
| 293 if (m_renderBox.document().printing()) { |
| 294 if (m_renderBox.style()->printColorAdjust() == PrintColorAdjustEconomy) |
| 295 forceBackgroundToWhite = true; |
| 296 if (m_renderBox.document().settings() && m_renderBox.document().settings
()->shouldPrintBackgrounds()) |
| 297 forceBackgroundToWhite = false; |
| 298 } |
| 299 |
| 300 // When printing backgrounds is disabled or using economy mode, |
| 301 // change existing background colors and images to a solid white background. |
| 302 // If there's no bg color or image, leave it untouched to avoid affecting tr
ansparency. |
| 303 // We don't try to avoid loading the background images, because this style f
lag is only set |
| 304 // when printing, and at that point we've already loaded the background imag
es anyway. (To avoid |
| 305 // loading the background images we'd have to do this check when applying st
yles rather than |
| 306 // while rendering.) |
| 307 if (forceBackgroundToWhite) { |
| 308 // Note that we can't reuse this variable below because the bgColor migh
t be changed |
| 309 bool shouldPaintBackgroundColor = isBottomLayer && bgColor.alpha(); |
| 310 if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) { |
| 311 bgColor = Color::white; |
| 312 shouldPaintBackgroundImage = false; |
| 313 } |
| 314 } |
| 315 |
| 316 bool colorVisible = bgColor.alpha(); |
| 317 |
| 318 // Fast path for drawing simple color backgrounds. |
| 319 if (!isDocumentElementRenderer && !clippedWithLocalScrolling && !shouldPaint
BackgroundImage && isBorderFill && isBottomLayer) { |
| 320 if (!colorVisible) |
| 321 return; |
| 322 |
| 323 bool boxShadowShouldBeAppliedToBackground = m_renderBox.boxShadowShouldB
eAppliedToBackground(bleedAvoidance, box); |
| 324 GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAp
pliedToBackground); |
| 325 if (boxShadowShouldBeAppliedToBackground) |
| 326 applyBoxShadowForBackground(context); |
| 327 |
| 328 if (hasRoundedBorder && bleedAvoidance != BackgroundBleedClipBackground)
{ |
| 329 RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(
context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge); |
| 330 if (border.isRenderable()) { |
| 331 context->fillRoundedRect(border, bgColor); |
| 332 } else { |
| 333 context->save(); |
| 334 clipRoundedInnerRect(context, rect, border); |
| 335 context->fillRect(border.rect(), bgColor); |
| 336 context->restore(); |
| 337 } |
| 338 } else { |
| 339 context->fillRect(pixelSnappedIntRect(rect), bgColor); |
| 340 } |
| 341 |
| 342 return; |
| 343 } |
| 344 |
| 345 // BorderFillBox radius clipping is taken care of by BackgroundBleedClipBack
ground |
| 346 bool clipToBorderRadius = hasRoundedBorder && !(isBorderFill && bleedAvoidan
ce == BackgroundBleedClipBackground); |
| 347 GraphicsContextStateSaver clipToBorderStateSaver(*context, clipToBorderRadiu
s); |
| 348 if (clipToBorderRadius) { |
| 349 RoundedRect border = isBorderFill ? backgroundRoundedRectAdjustedForBlee
dAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, include
RightEdge) : getBackgroundRoundedRect(rect, box, boxSize.width(), boxSize.height
(), includeLeftEdge, includeRightEdge); |
| 350 |
| 351 // Clip to the padding or content boxes as necessary. |
| 352 if (bgLayer.clip() == ContentFillBox) { |
| 353 border = m_renderBox.style()->getRoundedInnerBorderFor(border.rect()
, |
| 354 m_renderBox.paddingTop() + m_renderBox.borderTop(), m_renderBox.
paddingBottom() + m_renderBox.borderBottom(), |
| 355 m_renderBox.paddingLeft() + m_renderBox.borderLeft(), m_renderBo
x.paddingRight() + m_renderBox.borderRight(), includeLeftEdge, includeRightEdge)
; |
| 356 } else if (bgLayer.clip() == PaddingFillBox) { |
| 357 border = m_renderBox.style()->getRoundedInnerBorderFor(border.rect()
, includeLeftEdge, includeRightEdge); |
| 358 } |
| 359 |
| 360 clipRoundedInnerRect(context, rect, border); |
| 361 } |
| 362 |
| 363 int bLeft = includeLeftEdge ? m_renderBox.borderLeft() : 0; |
| 364 int bRight = includeRightEdge ? m_renderBox.borderRight() : 0; |
| 365 LayoutUnit pLeft = includeLeftEdge ? m_renderBox.paddingLeft() : LayoutUnit(
); |
| 366 LayoutUnit pRight = includeRightEdge ? m_renderBox.paddingRight() : LayoutUn
it(); |
| 367 |
| 368 GraphicsContextStateSaver clipWithScrollingStateSaver(*context, clippedWithL
ocalScrolling); |
| 369 LayoutRect scrolledPaintRect = rect; |
| 370 if (clippedWithLocalScrolling) { |
| 371 // Clip to the overflow area. |
| 372 context->clip(m_renderBox.overflowClipRect(rect.location())); |
| 373 |
| 374 // Adjust the paint rect to reflect a scrolled content box with borders
at the ends. |
| 375 IntSize offset = m_renderBox.scrolledContentOffset(); |
| 376 scrolledPaintRect.move(-offset); |
| 377 scrolledPaintRect.setWidth(bLeft + m_renderBox.scrollWidth() + bRight); |
| 378 scrolledPaintRect.setHeight(m_renderBox.borderTop() + m_renderBox.scroll
Height() + m_renderBox.borderBottom()); |
| 379 } |
| 380 |
| 381 GraphicsContextStateSaver backgroundClipStateSaver(*context, false); |
| 382 IntRect maskRect; |
| 383 |
| 384 switch (bgLayer.clip()) { |
| 385 case PaddingFillBox: |
| 386 case ContentFillBox: { |
| 387 if (clipToBorderRadius) |
| 388 break; |
| 389 |
| 390 // Clip to the padding or content boxes as necessary. |
| 391 bool includePadding = bgLayer.clip() == ContentFillBox; |
| 392 LayoutRect clipRect = LayoutRect(scrolledPaintRect.x() + bLeft + (includ
ePadding ? pLeft : LayoutUnit()), |
| 393 scrolledPaintRect.y() + m_renderBox.borderTop() + (includePadding ?
m_renderBox.paddingTop() : LayoutUnit()), |
| 394 scrolledPaintRect.width() - bLeft - bRight - (includePadding ? pLeft
+ pRight : LayoutUnit()), |
| 395 scrolledPaintRect.height() - m_renderBox.borderTop() - m_renderBox.b
orderBottom() - (includePadding ? m_renderBox.paddingTop() + m_renderBox.padding
Bottom() : LayoutUnit())); |
| 396 backgroundClipStateSaver.save(); |
| 397 context->clip(clipRect); |
| 398 |
| 399 break; |
| 400 } |
| 401 case TextFillBox: { |
| 402 // First figure out how big the mask has to be. It should be no bigger t
han what we need |
| 403 // to actually render, so we should intersect the dirty rect with the bo
rder box of the background. |
| 404 maskRect = pixelSnappedIntRect(rect); |
| 405 maskRect.intersect(paintInfo.rect); |
| 406 |
| 407 // We draw the background into a separate layer, to be later masked with
yet another layer |
| 408 // holding the text content. |
| 409 backgroundClipStateSaver.save(); |
| 410 context->clip(maskRect); |
| 411 context->beginTransparencyLayer(1); |
| 412 |
| 413 break; |
| 414 } |
| 415 case BorderFillBox: |
| 416 break; |
| 417 default: |
| 418 ASSERT_NOT_REACHED(); |
| 419 break; |
| 420 } |
| 421 |
| 422 // Paint the color first underneath all images, culled if background image o
ccludes it. |
| 423 // FIXME: In the bgLayer->hasFiniteBounds() case, we could improve the culli
ng test |
| 424 // by verifying whether the background image covers the entire layout rect. |
| 425 if (isBottomLayer) { |
| 426 IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect)); |
| 427 bool boxShadowShouldBeAppliedToBackground = m_renderBox.boxShadowShouldB
eAppliedToBackground(bleedAvoidance, box); |
| 428 bool isOpaqueRoot = (isDocumentElementRenderer && !bgColor.hasAlpha()) |
| isDocumentElementWithOpaqueBackground(); |
| 429 if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage
|| !bgLayer.hasOpaqueImage(&m_renderBox) || !bgLayer.hasRepeatXY() || (isOpaqueR
oot && m_renderBox.height())) { |
| 430 if (!boxShadowShouldBeAppliedToBackground) |
| 431 backgroundRect.intersect(paintInfo.rect); |
| 432 |
| 433 GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShould
BeAppliedToBackground); |
| 434 if (boxShadowShouldBeAppliedToBackground) |
| 435 applyBoxShadowForBackground(context); |
| 436 |
| 437 if (isOpaqueRoot && !skipBaseColor) { |
| 438 paintRootBackgroundColor(paintInfo, rect, bgColor); |
| 439 } else if (bgColor.alpha()) { |
| 440 context->fillRect(backgroundRect, bgColor, context->compositeOpe
ration()); |
| 441 } |
| 442 } |
| 443 } |
| 444 |
| 445 // no progressive loading of the background image |
| 446 if (shouldPaintBackgroundImage) { |
| 447 BackgroundImageGeometry geometry; |
| 448 calculateBackgroundImageGeometry(paintInfo.paintContainer(), bgLayer, sc
rolledPaintRect, geometry, backgroundObject); |
| 449 geometry.clip(paintInfo.rect); |
| 450 if (!geometry.destRect().isEmpty()) { |
| 451 CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer.
composite() : op; |
| 452 RenderObject* clientForBackgroundImage = backgroundObject ? backgrou
ndObject : &m_renderBox; |
| 453 RefPtr<Image> image = bgImage->image(clientForBackgroundImage, geome
try.tileSize()); |
| 454 InterpolationQuality interpolationQuality = chooseInterpolationQuali
ty(context, image.get(), &bgLayer, geometry.tileSize()); |
| 455 if (bgLayer.maskSourceType() == MaskLuminance) |
| 456 context->setColorFilter(ColorFilterLuminanceToAlpha); |
| 457 InterpolationQuality previousInterpolationQuality = context->imageIn
terpolationQuality(); |
| 458 context->setImageInterpolationQuality(interpolationQuality); |
| 459 context->drawTiledImage(image.get(), geometry.destRect(), geometry.r
elativePhase(), geometry.tileSize(), |
| 460 compositeOp, bgLayer.blendMode(), geometry.spaceSize()); |
| 461 context->setImageInterpolationQuality(previousInterpolationQuality); |
| 462 } |
| 463 } |
| 464 |
| 465 if (bgLayer.clip() == TextFillBox) { |
| 466 // Create the text mask layer. |
| 467 context->setCompositeOperation(CompositeDestinationIn); |
| 468 context->beginTransparencyLayer(1); |
| 469 |
| 470 // FIXME: Workaround for https://code.google.com/p/skia/issues/detail?id
=1291. |
| 471 context->clearRect(maskRect); |
| 472 |
| 473 // Now draw the text into the mask. We do this by painting using a speci
al paint phase that signals to |
| 474 // InlineTextBoxes that they should just add their contents to the clip. |
| 475 PaintInfo info(context, maskRect, PaintPhaseTextClip, PaintBehaviorForce
BlackText, 0); |
| 476 context->setCompositeOperation(CompositeSourceOver); |
| 477 if (box) { |
| 478 RootInlineBox& root = box->root(); |
| 479 box->paint(info, LayoutPoint(scrolledPaintRect.x() - box->x(), scrol
ledPaintRect.y() - box->y()), root.lineTop(), root.lineBottom()); |
| 480 } else { |
| 481 LayoutSize localOffset = m_renderBox.locationOffset(); |
| 482 paint(info, scrolledPaintRect.location() - localOffset); |
| 483 } |
| 484 |
| 485 context->endLayer(); |
| 486 context->endLayer(); |
| 487 } |
179 } | 488 } |
180 | 489 |
181 void BoxPainter::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) | 490 void BoxPainter::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
182 { | 491 { |
183 if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->v
isibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) | 492 if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->v
isibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) |
184 return; | 493 return; |
185 | 494 |
186 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); | 495 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); |
187 paintMaskImages(paintInfo, paintRect); | 496 paintMaskImages(paintInfo, paintRect); |
188 } | 497 } |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 return; | 540 return; |
232 | 541 |
233 // We should never have this state in this function. A layer with a mask | 542 // We should never have this state in this function. A layer with a mask |
234 // should have always created its own backing if it became composited. | 543 // should have always created its own backing if it became composited. |
235 ASSERT(m_renderBox.layer()->compositingState() != HasOwnBackingButPaintsInto
Ancestor); | 544 ASSERT(m_renderBox.layer()->compositingState() != HasOwnBackingButPaintsInto
Ancestor); |
236 | 545 |
237 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); | 546 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); |
238 paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black); | 547 paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black); |
239 } | 548 } |
240 | 549 |
| 550 void BoxPainter::paintRootBackgroundColor(const PaintInfo& paintInfo, const Layo
utRect& rect, const Color& bgColor) |
| 551 { |
| 552 GraphicsContext* context = paintInfo.context; |
| 553 if (rect.isEmpty()) |
| 554 return; |
| 555 |
| 556 ASSERT(isDocumentElement()); |
| 557 |
| 558 IntRect backgroundRect(pixelSnappedIntRect(rect)); |
| 559 backgroundRect.intersect(paintInfo.rect); |
| 560 |
| 561 Color baseColor = m_renderBox.view()->frameView()->baseBackgroundColor(); |
| 562 bool shouldClearDocumentBackground = m_renderBox.document().settings() && m_
renderBox.document().settings()->shouldClearDocumentBackground(); |
| 563 CompositeOperator operation = shouldClearDocumentBackground ? CompositeCopy
: context->compositeOperation(); |
| 564 |
| 565 // If we have an alpha go ahead and blend with the base background color. |
| 566 if (baseColor.alpha()) { |
| 567 if (bgColor.alpha()) |
| 568 baseColor = baseColor.blend(bgColor); |
| 569 context->fillRect(backgroundRect, baseColor, operation); |
| 570 } else if (bgColor.alpha()) { |
| 571 context->fillRect(backgroundRect, bgColor, operation); |
| 572 } else if (shouldClearDocumentBackground) { |
| 573 context->clearRect(backgroundRect); |
| 574 } |
| 575 } |
| 576 |
| 577 bool BoxPainter::isDocumentElementWithOpaqueBackground() const |
| 578 { |
| 579 if (!m_renderBox.isDocumentElement()) |
| 580 return false; |
| 581 |
| 582 // The background is opaque only if we're the root document, since iframes w
ith |
| 583 // no background in the child document should show the parent's background. |
| 584 bool isOpaque = true; |
| 585 Element* ownerElement = m_renderBox.document().ownerElement(); |
| 586 if (ownerElement) { |
| 587 if (!isHTMLFrameElement(*ownerElement)) { |
| 588 // Locate the <body> element using the DOM. This is easier than tryi
ng |
| 589 // to crawl around a render tree with potential :before/:after conte
nt and |
| 590 // anonymous blocks created by inline <body> tags etc. We can locate
the <body> |
| 591 // render object very easily via the DOM. |
| 592 HTMLElement* body = m_renderBox.document().body(); |
| 593 if (body) { |
| 594 // Can't scroll a frameset document anyway. |
| 595 isOpaque = body->hasTagName(HTMLNames::framesetTag); |
| 596 } else { |
| 597 // FIXME: SVG specific behavior should be in the SVG code. |
| 598 // SVG documents and XML documents with SVG root nodes are trans
parent. |
| 599 isOpaque = !m_renderBox.document().hasSVGRootNode(); |
| 600 } |
| 601 } |
| 602 } else if (m_renderBox.view()->frameView()) { |
| 603 isOpaque = !m_renderBox.view()->frameView()->isTransparent(); |
| 604 } |
| 605 |
| 606 return isOpaque; |
| 607 } |
| 608 |
| 609 static inline int getSpace(int areaSize, int tileSize) |
| 610 { |
| 611 int numberOfTiles = areaSize / tileSize; |
| 612 int space = -1; |
| 613 |
| 614 if (numberOfTiles > 1) |
| 615 space = lroundf((float)(areaSize - numberOfTiles * tileSize) / (numberOf
Tiles - 1)); |
| 616 |
| 617 return space; |
| 618 } |
| 619 |
| 620 void BoxPainter::calculateBackgroundImageGeometry(const RenderLayerModelObject*
paintContainer, const FillLayer& fillLayer, const LayoutRect& paintRect, |
| 621 BackgroundImageGeometry& geometry, RenderObject* backgroundObject) const |
| 622 { |
| 623 LayoutUnit left = 0; |
| 624 LayoutUnit top = 0; |
| 625 IntSize positioningAreaSize; |
| 626 IntRect snappedPaintRect = pixelSnappedIntRect(paintRect); |
| 627 |
| 628 // Determine the background positioning area and set destRect to the backgro
und painting area. |
| 629 // destRect will be adjusted later if the background is non-repeating. |
| 630 // FIXME: transforms spec says that fixed backgrounds behave like scroll ins
ide transforms. |
| 631 bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment; |
| 632 |
| 633 if (RuntimeEnabledFeatures::fastMobileScrollingEnabled()) { |
| 634 // As a side effect of an optimization to blit on scroll, we do not hono
r the CSS |
| 635 // property "background-attachment: fixed" because it may result in rend
ering |
| 636 // artifacts. Note, these artifacts only appear if we are blitting on sc
roll of |
| 637 // a page that has fixed background images. |
| 638 fixedAttachment = false; |
| 639 } |
| 640 |
| 641 if (!fixedAttachment) { |
| 642 geometry.setDestRect(snappedPaintRect); |
| 643 |
| 644 LayoutUnit right = 0; |
| 645 LayoutUnit bottom = 0; |
| 646 // Scroll and Local. |
| 647 if (fillLayer.origin() != BorderFillBox) { |
| 648 left = m_renderBox.borderLeft(); |
| 649 right = m_renderBox.borderRight(); |
| 650 top = m_renderBox.borderTop(); |
| 651 bottom = m_renderBox.borderBottom(); |
| 652 if (fillLayer.origin() == ContentFillBox) { |
| 653 left += m_renderBox.paddingLeft(); |
| 654 right += m_renderBox.paddingRight(); |
| 655 top += m_renderBox.paddingTop(); |
| 656 bottom += m_renderBox.paddingBottom(); |
| 657 } |
| 658 } |
| 659 |
| 660 // The background of the box generated by the root element covers the en
tire canvas including |
| 661 // its margins. Since those were added in already, we have to factor the
m out when computing |
| 662 // the background positioning area. |
| 663 if (m_renderBox.isDocumentElement()) { |
| 664 positioningAreaSize = pixelSnappedIntSize(m_renderBox.size() - Layou
tSize(left + right, top + bottom), m_renderBox.location()); |
| 665 left += m_renderBox.marginLeft(); |
| 666 top += m_renderBox.marginTop(); |
| 667 } else { |
| 668 positioningAreaSize = pixelSnappedIntSize(paintRect.size() - LayoutS
ize(left + right, top + bottom), paintRect.location()); |
| 669 } |
| 670 } else { |
| 671 geometry.setHasNonLocalGeometry(); |
| 672 |
| 673 IntRect viewportRect = pixelSnappedIntRect(m_renderBox.viewRect()); |
| 674 if (fixedBackgroundPaintsInLocalCoordinates()) |
| 675 viewportRect.setLocation(IntPoint()); |
| 676 else if (FrameView* frameView = m_renderBox.view()->frameView()) |
| 677 viewportRect.setLocation(IntPoint(frameView->scrollOffsetForFixedPos
ition())); |
| 678 |
| 679 if (paintContainer) { |
| 680 IntPoint absoluteContainerOffset = roundedIntPoint(paintContainer->l
ocalToAbsolute(FloatPoint())); |
| 681 viewportRect.moveBy(-absoluteContainerOffset); |
| 682 } |
| 683 |
| 684 geometry.setDestRect(pixelSnappedIntRect(viewportRect)); |
| 685 positioningAreaSize = geometry.destRect().size(); |
| 686 } |
| 687 |
| 688 const RenderObject* clientForBackgroundImage = backgroundObject ? background
Object : &m_renderBox; |
| 689 IntSize fillTileSize = calculateFillTileSize(fillLayer, positioningAreaSize)
; |
| 690 fillLayer.image()->setContainerSizeForRenderer(clientForBackgroundImage, fil
lTileSize, m_renderBox.style()->effectiveZoom()); |
| 691 geometry.setTileSize(fillTileSize); |
| 692 |
| 693 EFillRepeat backgroundRepeatX = fillLayer.repeatX(); |
| 694 EFillRepeat backgroundRepeatY = fillLayer.repeatY(); |
| 695 int availableWidth = positioningAreaSize.width() - geometry.tileSize().width
(); |
| 696 int availableHeight = positioningAreaSize.height() - geometry.tileSize().hei
ght(); |
| 697 |
| 698 LayoutUnit computedXPosition = roundedMinimumValueForLength(fillLayer.xPosit
ion(), availableWidth); |
| 699 if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > 0 && fil
lTileSize.width() > 0) { |
| 700 long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.width() /
fillTileSize.width())); |
| 701 |
| 702 if (fillLayer.size().size.height().isAuto() && backgroundRepeatY != Roun
dFill) { |
| 703 fillTileSize.setHeight(fillTileSize.height() * positioningAreaSize.w
idth() / (nrTiles * fillTileSize.width())); |
| 704 } |
| 705 |
| 706 fillTileSize.setWidth(positioningAreaSize.width() / nrTiles); |
| 707 geometry.setTileSize(fillTileSize); |
| 708 geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().wid
th() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0); |
| 709 geometry.setSpaceSize(IntSize()); |
| 710 } |
| 711 |
| 712 LayoutUnit computedYPosition = roundedMinimumValueForLength(fillLayer.yPosit
ion(), availableHeight); |
| 713 if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > 0 && fi
llTileSize.height() > 0) { |
| 714 long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.height()
/ fillTileSize.height())); |
| 715 |
| 716 if (fillLayer.size().size.width().isAuto() && backgroundRepeatX != Round
Fill) { |
| 717 fillTileSize.setWidth(fillTileSize.width() * positioningAreaSize.hei
ght() / (nrTiles * fillTileSize.height())); |
| 718 } |
| 719 |
| 720 fillTileSize.setHeight(positioningAreaSize.height() / nrTiles); |
| 721 geometry.setTileSize(fillTileSize); |
| 722 geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().he
ight() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0)
; |
| 723 geometry.setSpaceSize(IntSize()); |
| 724 } |
| 725 |
| 726 if (backgroundRepeatX == RepeatFill) { |
| 727 geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().wid
th() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0); |
| 728 geometry.setSpaceSize(IntSize()); |
| 729 } else if (backgroundRepeatX == SpaceFill && fillTileSize.width() > 0) { |
| 730 int space = getSpace(positioningAreaSize.width(), geometry.tileSize().wi
dth()); |
| 731 int actualWidth = geometry.tileSize().width() + space; |
| 732 |
| 733 if (space >= 0) { |
| 734 computedXPosition = roundedMinimumValueForLength(Length(), available
Width); |
| 735 geometry.setSpaceSize(IntSize(space, 0)); |
| 736 geometry.setPhaseX(actualWidth ? actualWidth - roundToInt(computedXP
osition + left) % actualWidth : 0); |
| 737 } else { |
| 738 backgroundRepeatX = NoRepeatFill; |
| 739 } |
| 740 } |
| 741 if (backgroundRepeatX == NoRepeatFill) { |
| 742 int xOffset = fillLayer.backgroundXOrigin() == RightEdge ? availableWidt
h - computedXPosition : computedXPosition; |
| 743 geometry.setNoRepeatX(left + xOffset); |
| 744 geometry.setSpaceSize(IntSize(0, geometry.spaceSize().height())); |
| 745 } |
| 746 |
| 747 if (backgroundRepeatY == RepeatFill) { |
| 748 geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().he
ight() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0)
; |
| 749 geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0)); |
| 750 } else if (backgroundRepeatY == SpaceFill && fillTileSize.height() > 0) { |
| 751 int space = getSpace(positioningAreaSize.height(), geometry.tileSize().h
eight()); |
| 752 int actualHeight = geometry.tileSize().height() + space; |
| 753 |
| 754 if (space >= 0) { |
| 755 computedYPosition = roundedMinimumValueForLength(Length(), available
Height); |
| 756 geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), space)); |
| 757 geometry.setPhaseY(actualHeight ? actualHeight - roundToInt(computed
YPosition + top) % actualHeight : 0); |
| 758 } else { |
| 759 backgroundRepeatY = NoRepeatFill; |
| 760 } |
| 761 } |
| 762 if (backgroundRepeatY == NoRepeatFill) { |
| 763 int yOffset = fillLayer.backgroundYOrigin() == BottomEdge ? availableHei
ght - computedYPosition : computedYPosition; |
| 764 geometry.setNoRepeatY(top + yOffset); |
| 765 geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0)); |
| 766 } |
| 767 |
| 768 if (fixedAttachment) |
| 769 geometry.useFixedAttachment(snappedPaintRect.location()); |
| 770 |
| 771 geometry.clip(snappedPaintRect); |
| 772 geometry.setDestOrigin(geometry.destRect().location()); |
| 773 } |
| 774 |
| 775 |
| 776 InterpolationQuality BoxPainter::chooseInterpolationQuality(GraphicsContext* con
text, Image* image, const void* layer, const LayoutSize& size) |
| 777 { |
| 778 return ImageQualityController::imageQualityController()->chooseInterpolation
Quality(context, &m_renderBox, image, layer, size); |
| 779 } |
| 780 |
| 781 bool BoxPainter::fixedBackgroundPaintsInLocalCoordinates() const |
| 782 { |
| 783 if (!m_renderBox.isDocumentElement()) |
| 784 return false; |
| 785 |
| 786 if (m_renderBox.view()->frameView() && m_renderBox.view()->frameView()->pain
tBehavior() & PaintBehaviorFlattenCompositingLayers) |
| 787 return false; |
| 788 |
| 789 RenderLayer* rootLayer = m_renderBox.view()->layer(); |
| 790 if (!rootLayer || rootLayer->compositingState() == NotComposited) |
| 791 return false; |
| 792 |
| 793 return rootLayer->compositedLayerMapping()->backgroundLayerPaintsFixedRootBa
ckground(); |
| 794 } |
| 795 |
| 796 static inline void applySubPixelHeuristicForTileSize(LayoutSize& tileSize, const
IntSize& positioningAreaSize) |
| 797 { |
| 798 tileSize.setWidth(positioningAreaSize.width() - tileSize.width() <= 1 ? tile
Size.width().ceil() : tileSize.width().floor()); |
| 799 tileSize.setHeight(positioningAreaSize.height() - tileSize.height() <= 1 ? t
ileSize.height().ceil() : tileSize.height().floor()); |
| 800 } |
| 801 |
| 802 IntSize BoxPainter::calculateFillTileSize(const FillLayer& fillLayer, const IntS
ize& positioningAreaSize) const |
| 803 { |
| 804 StyleImage* image = fillLayer.image(); |
| 805 EFillSizeType type = fillLayer.size().type; |
| 806 |
| 807 IntSize imageIntrinsicSize = m_renderBox.calculateImageIntrinsicDimensions(i
mage, positioningAreaSize, RenderBoxModelObject::ScaleByEffectiveZoom); |
| 808 imageIntrinsicSize.scale(1 / image->imageScaleFactor(), 1 / image->imageScal
eFactor()); |
| 809 switch (type) { |
| 810 case SizeLength: { |
| 811 LayoutSize tileSize = positioningAreaSize; |
| 812 |
| 813 Length layerWidth = fillLayer.size().size.width(); |
| 814 Length layerHeight = fillLayer.size().size.height(); |
| 815 |
| 816 if (layerWidth.isFixed()) |
| 817 tileSize.setWidth(layerWidth.value()); |
| 818 else if (layerWidth.isPercent()) |
| 819 tileSize.setWidth(valueForLength(layerWidth, positioningAreaSize.wid
th())); |
| 820 |
| 821 if (layerHeight.isFixed()) |
| 822 tileSize.setHeight(layerHeight.value()); |
| 823 else if (layerHeight.isPercent()) |
| 824 tileSize.setHeight(valueForLength(layerHeight, positioningAreaSize.h
eight())); |
| 825 |
| 826 applySubPixelHeuristicForTileSize(tileSize, positioningAreaSize); |
| 827 |
| 828 // If one of the values is auto we have to use the appropriate |
| 829 // scale to maintain our aspect ratio. |
| 830 if (layerWidth.isAuto() && !layerHeight.isAuto()) { |
| 831 if (imageIntrinsicSize.height()) |
| 832 tileSize.setWidth(imageIntrinsicSize.width() * tileSize.height()
/ imageIntrinsicSize.height()); |
| 833 } else if (!layerWidth.isAuto() && layerHeight.isAuto()) { |
| 834 if (imageIntrinsicSize.width()) |
| 835 tileSize.setHeight(imageIntrinsicSize.height() * tileSize.width(
) / imageIntrinsicSize.width()); |
| 836 } else if (layerWidth.isAuto() && layerHeight.isAuto()) { |
| 837 // If both width and height are auto, use the image's intrinsic size
. |
| 838 tileSize = imageIntrinsicSize; |
| 839 } |
| 840 |
| 841 tileSize.clampNegativeToZero(); |
| 842 return flooredIntSize(tileSize); |
| 843 } |
| 844 case SizeNone: { |
| 845 // If both values are ‘auto’ then the intrinsic width and/or height of t
he image should be used, if any. |
| 846 if (!imageIntrinsicSize.isEmpty()) |
| 847 return imageIntrinsicSize; |
| 848 |
| 849 // If the image has neither an intrinsic width nor an intrinsic height,
its size is determined as for ‘contain’. |
| 850 type = Contain; |
| 851 } |
| 852 case Contain: |
| 853 case Cover: { |
| 854 float horizontalScaleFactor = imageIntrinsicSize.width() |
| 855 ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSi
ze.width() : 1; |
| 856 float verticalScaleFactor = imageIntrinsicSize.height() |
| 857 ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicS
ize.height() : 1; |
| 858 float scaleFactor = type == Contain ? std::min(horizontalScaleFactor, ve
rticalScaleFactor) : std::max(horizontalScaleFactor, verticalScaleFactor); |
| 859 return IntSize(std::max(1l, lround(imageIntrinsicSize.width() * scaleFac
tor)), std::max(1l, lround(imageIntrinsicSize.height() * scaleFactor))); |
| 860 } |
| 861 } |
| 862 |
| 863 ASSERT_NOT_REACHED(); |
| 864 return IntSize(); |
| 865 } |
| 866 |
241 } // namespace blink | 867 } // namespace blink |
OLD | NEW |