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(m_render
Box)) { |
161 m_renderBox.paintRootBackgroundColor(paintInfo, rect, Color()); | 170 paintRootBackgroundColor(m_renderBox, 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 BoxPainter::paintFillLayerExtended(m_renderBox, paintInfo, c, fillLayer, rec
t, bleedAvoidance, 0, LayoutSize(), op, backgroundObject, skipBaseColor); |
| 188 } |
| 189 |
| 190 void BoxPainter::applyBoxShadowForBackground(GraphicsContext* context, RenderObj
ect& obj) |
| 191 { |
| 192 const ShadowList* shadowList = obj.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(RenderObject& obj, const Layout
Rect& borderRect, InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inli
neBoxHeight, |
| 217 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) |
| 218 { |
| 219 RoundedRect border = obj.style()->getRoundedBorderFor(borderRect, includeLog
icalLeftEdge, includeLogicalRightEdge); |
| 220 if (box && (box->nextLineBox() || box->prevLineBox())) { |
| 221 RoundedRect segmentBorder = obj.style()->getRoundedBorderFor(LayoutRect(
0, 0, inlineBoxWidth, inlineBoxHeight), includeLogicalLeftEdge, includeLogicalRi
ghtEdge); |
| 222 border.setRadii(segmentBorder.radii()); |
| 223 } |
| 224 |
| 225 return border; |
| 226 } |
| 227 |
| 228 RoundedRect BoxPainter::backgroundRoundedRectAdjustedForBleedAvoidance(RenderObj
ect& obj, GraphicsContext* context, const LayoutRect& borderRect, BackgroundBlee
dAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, bool i
ncludeLogicalLeftEdge, bool includeLogicalRightEdge) |
| 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 BoxPainter::getBackgroundRoundedRect(obj, shrinkRectByOnePixel(co
ntext, borderRect), box, boxSize.width(), boxSize.height(), includeLogicalLeftEd
ge, includeLogicalRightEdge); |
| 233 } |
| 234 if (bleedAvoidance == BackgroundBleedBackgroundOverBorder) |
| 235 return obj.style()->getRoundedInnerBorderFor(borderRect, includeLogicalL
eftEdge, includeLogicalRightEdge); |
| 236 |
| 237 return BoxPainter::getBackgroundRoundedRect(obj, borderRect, box, boxSize.wi
dth(), boxSize.height(), 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(RenderBoxModelObject& obj, const PaintIn
fo& paintInfo, const Color& color, const FillLayer& bgLayer, const LayoutRect& r
ect, |
| 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 = obj.style()->hasBorderRadius() && (includeLeftEdge |
| includeRightEdge); |
| 283 bool clippedWithLocalScrolling = obj.hasOverflowClip() && bgLayer.attachment
() == LocalBackgroundAttachment; |
| 284 bool isBorderFill = bgLayer.clip() == BorderFillBox; |
| 285 bool isDocumentElementRenderer = obj.isDocumentElement(); |
| 286 bool isBottomLayer = !bgLayer.next(); |
| 287 |
| 288 Color bgColor = color; |
| 289 StyleImage* bgImage = bgLayer.image(); |
| 290 bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(obj, obj.sty
le()->effectiveZoom()); |
| 291 |
| 292 bool forceBackgroundToWhite = false; |
| 293 if (obj.document().printing()) { |
| 294 if (obj.style()->printColorAdjust() == PrintColorAdjustEconomy) |
| 295 forceBackgroundToWhite = true; |
| 296 if (obj.document().settings() && obj.document().settings()->shouldPrintB
ackgrounds()) |
| 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 = obj.boxShadowShouldBeApplied
ToBackground(bleedAvoidance, box); |
| 324 GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAp
pliedToBackground); |
| 325 if (boxShadowShouldBeAppliedToBackground) |
| 326 BoxPainter::applyBoxShadowForBackground(context, obj); |
| 327 |
| 328 if (hasRoundedBorder && bleedAvoidance != BackgroundBleedClipBackground)
{ |
| 329 RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(
obj, context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightE
dge); |
| 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(obj, context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, in
cludeRightEdge) : getBackgroundRoundedRect(obj, rect, box, boxSize.width(), boxS
ize.height(), includeLeftEdge, includeRightEdge); |
| 350 |
| 351 // Clip to the padding or content boxes as necessary. |
| 352 if (bgLayer.clip() == ContentFillBox) { |
| 353 border = obj.style()->getRoundedInnerBorderFor(border.rect(), |
| 354 obj.paddingTop() + obj.borderTop(), obj.paddingBottom() + obj.bo
rderBottom(), |
| 355 obj.paddingLeft() + obj.borderLeft(), obj.paddingRight() + obj.b
orderRight(), includeLeftEdge, includeRightEdge); |
| 356 } else if (bgLayer.clip() == PaddingFillBox) { |
| 357 border = obj.style()->getRoundedInnerBorderFor(border.rect(), includ
eLeftEdge, includeRightEdge); |
| 358 } |
| 359 |
| 360 clipRoundedInnerRect(context, rect, border); |
| 361 } |
| 362 |
| 363 int bLeft = includeLeftEdge ? obj.borderLeft() : 0; |
| 364 int bRight = includeRightEdge ? obj.borderRight() : 0; |
| 365 LayoutUnit pLeft = includeLeftEdge ? obj.paddingLeft() : LayoutUnit(); |
| 366 LayoutUnit pRight = includeRightEdge ? obj.paddingRight() : LayoutUnit(); |
| 367 |
| 368 GraphicsContextStateSaver clipWithScrollingStateSaver(*context, clippedWithL
ocalScrolling); |
| 369 LayoutRect scrolledPaintRect = rect; |
| 370 if (clippedWithLocalScrolling) { |
| 371 // Clip to the overflow area. |
| 372 RenderBox* thisBox = toRenderBox(&obj); |
| 373 context->clip(thisBox->overflowClipRect(rect.location())); |
| 374 |
| 375 // Adjust the paint rect to reflect a scrolled content box with borders
at the ends. |
| 376 IntSize offset = thisBox->scrolledContentOffset(); |
| 377 scrolledPaintRect.move(-offset); |
| 378 scrolledPaintRect.setWidth(bLeft + thisBox->scrollWidth() + bRight); |
| 379 scrolledPaintRect.setHeight(thisBox->borderTop() + thisBox->scrollHeight
() + thisBox->borderBottom()); |
| 380 } |
| 381 |
| 382 GraphicsContextStateSaver backgroundClipStateSaver(*context, false); |
| 383 IntRect maskRect; |
| 384 |
| 385 switch (bgLayer.clip()) { |
| 386 case PaddingFillBox: |
| 387 case ContentFillBox: { |
| 388 if (clipToBorderRadius) |
| 389 break; |
| 390 |
| 391 // Clip to the padding or content boxes as necessary. |
| 392 bool includePadding = bgLayer.clip() == ContentFillBox; |
| 393 LayoutRect clipRect = LayoutRect(scrolledPaintRect.x() + bLeft + (includ
ePadding ? pLeft : LayoutUnit()), |
| 394 scrolledPaintRect.y() + obj.borderTop() + (includePadding ? obj.padd
ingTop() : LayoutUnit()), |
| 395 scrolledPaintRect.width() - bLeft - bRight - (includePadding ? pLeft
+ pRight : LayoutUnit()), |
| 396 scrolledPaintRect.height() - obj.borderTop() - obj.borderBottom() -
(includePadding ? obj.paddingTop() + obj.paddingBottom() : LayoutUnit())); |
| 397 backgroundClipStateSaver.save(); |
| 398 context->clip(clipRect); |
| 399 |
| 400 break; |
| 401 } |
| 402 case TextFillBox: { |
| 403 // First figure out how big the mask has to be. It should be no bigger t
han what we need |
| 404 // to actually render, so we should intersect the dirty rect with the bo
rder box of the background. |
| 405 maskRect = pixelSnappedIntRect(rect); |
| 406 maskRect.intersect(paintInfo.rect); |
| 407 |
| 408 // We draw the background into a separate layer, to be later masked with
yet another layer |
| 409 // holding the text content. |
| 410 backgroundClipStateSaver.save(); |
| 411 context->clip(maskRect); |
| 412 context->beginTransparencyLayer(1); |
| 413 |
| 414 break; |
| 415 } |
| 416 case BorderFillBox: |
| 417 break; |
| 418 default: |
| 419 ASSERT_NOT_REACHED(); |
| 420 break; |
| 421 } |
| 422 |
| 423 // Paint the color first underneath all images, culled if background image o
ccludes it. |
| 424 // FIXME: In the bgLayer->hasFiniteBounds() case, we could improve the culli
ng test |
| 425 // by verifying whether the background image covers the entire layout rect. |
| 426 if (isBottomLayer) { |
| 427 IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect)); |
| 428 bool boxShadowShouldBeAppliedToBackground = obj.boxShadowShouldBeApplied
ToBackground(bleedAvoidance, box); |
| 429 bool isOpaqueRoot = (isDocumentElementRenderer && !bgColor.hasAlpha()) |
| isDocumentElementWithOpaqueBackground(obj); |
| 430 if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage
|| !bgLayer.hasOpaqueImage(&obj) || !bgLayer.hasRepeatXY() || (isOpaqueRoot && !
toRenderBox(&obj)->height())) { |
| 431 if (!boxShadowShouldBeAppliedToBackground) |
| 432 backgroundRect.intersect(paintInfo.rect); |
| 433 |
| 434 GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShould
BeAppliedToBackground); |
| 435 if (boxShadowShouldBeAppliedToBackground) |
| 436 BoxPainter::applyBoxShadowForBackground(context, obj); |
| 437 |
| 438 if (isOpaqueRoot && !skipBaseColor) { |
| 439 paintRootBackgroundColor(obj, paintInfo, rect, bgColor); |
| 440 } else if (bgColor.alpha()) { |
| 441 context->fillRect(backgroundRect, bgColor, context->compositeOpe
ration()); |
| 442 } |
| 443 } |
| 444 } |
| 445 |
| 446 // no progressive loading of the background image |
| 447 if (shouldPaintBackgroundImage) { |
| 448 BackgroundImageGeometry geometry; |
| 449 calculateBackgroundImageGeometry(obj, paintInfo.paintContainer(), bgLaye
r, scrolledPaintRect, geometry, backgroundObject); |
| 450 geometry.clip(paintInfo.rect); |
| 451 if (!geometry.destRect().isEmpty()) { |
| 452 CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer.
composite() : op; |
| 453 RenderObject* clientForBackgroundImage = backgroundObject ? backgrou
ndObject : &obj; |
| 454 RefPtr<Image> image = bgImage->image(clientForBackgroundImage, geome
try.tileSize()); |
| 455 InterpolationQuality interpolationQuality = chooseInterpolationQuali
ty(obj, context, image.get(), &bgLayer, geometry.tileSize()); |
| 456 if (bgLayer.maskSourceType() == MaskLuminance) |
| 457 context->setColorFilter(ColorFilterLuminanceToAlpha); |
| 458 InterpolationQuality previousInterpolationQuality = context->imageIn
terpolationQuality(); |
| 459 context->setImageInterpolationQuality(interpolationQuality); |
| 460 context->drawTiledImage(image.get(), geometry.destRect(), geometry.r
elativePhase(), geometry.tileSize(), |
| 461 compositeOp, bgLayer.blendMode(), geometry.spaceSize()); |
| 462 context->setImageInterpolationQuality(previousInterpolationQuality); |
| 463 } |
| 464 } |
| 465 |
| 466 if (bgLayer.clip() == TextFillBox) { |
| 467 // Create the text mask layer. |
| 468 context->setCompositeOperation(CompositeDestinationIn); |
| 469 context->beginTransparencyLayer(1); |
| 470 |
| 471 // FIXME: Workaround for https://code.google.com/p/skia/issues/detail?id
=1291. |
| 472 context->clearRect(maskRect); |
| 473 |
| 474 // Now draw the text into the mask. We do this by painting using a speci
al paint phase that signals to |
| 475 // InlineTextBoxes that they should just add their contents to the clip. |
| 476 PaintInfo info(context, maskRect, PaintPhaseTextClip, PaintBehaviorForce
BlackText, 0); |
| 477 context->setCompositeOperation(CompositeSourceOver); |
| 478 if (box) { |
| 479 RootInlineBox& root = box->root(); |
| 480 box->paint(info, LayoutPoint(scrolledPaintRect.x() - box->x(), scrol
ledPaintRect.y() - box->y()), root.lineTop(), root.lineBottom()); |
| 481 } else { |
| 482 LayoutSize localOffset = obj.isBox() ? toRenderBox(&obj)->locationOf
fset() : LayoutSize(); |
| 483 obj.paint(info, scrolledPaintRect.location() - localOffset); |
| 484 } |
| 485 |
| 486 context->endLayer(); |
| 487 context->endLayer(); |
| 488 } |
179 } | 489 } |
180 | 490 |
181 void BoxPainter::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) | 491 void BoxPainter::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
182 { | 492 { |
183 if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->v
isibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) | 493 if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->v
isibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) |
184 return; | 494 return; |
185 | 495 |
186 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); | 496 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); |
187 paintMaskImages(paintInfo, paintRect); | 497 paintMaskImages(paintInfo, paintRect); |
188 } | 498 } |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 return; | 541 return; |
232 | 542 |
233 // We should never have this state in this function. A layer with a mask | 543 // 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. | 544 // should have always created its own backing if it became composited. |
235 ASSERT(m_renderBox.layer()->compositingState() != HasOwnBackingButPaintsInto
Ancestor); | 545 ASSERT(m_renderBox.layer()->compositingState() != HasOwnBackingButPaintsInto
Ancestor); |
236 | 546 |
237 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); | 547 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); |
238 paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black); | 548 paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black); |
239 } | 549 } |
240 | 550 |
| 551 void BoxPainter::paintRootBackgroundColor(RenderObject& obj, const PaintInfo& pa
intInfo, const LayoutRect& rect, const Color& bgColor) |
| 552 { |
| 553 GraphicsContext* context = paintInfo.context; |
| 554 if (rect.isEmpty()) |
| 555 return; |
| 556 |
| 557 ASSERT(obj.isDocumentElement()); |
| 558 |
| 559 IntRect backgroundRect(pixelSnappedIntRect(rect)); |
| 560 backgroundRect.intersect(paintInfo.rect); |
| 561 |
| 562 Color baseColor = obj.view()->frameView()->baseBackgroundColor(); |
| 563 bool shouldClearDocumentBackground = obj.document().settings() && obj.docume
nt().settings()->shouldClearDocumentBackground(); |
| 564 CompositeOperator operation = shouldClearDocumentBackground ? CompositeCopy
: context->compositeOperation(); |
| 565 |
| 566 // If we have an alpha go ahead and blend with the base background color. |
| 567 if (baseColor.alpha()) { |
| 568 if (bgColor.alpha()) |
| 569 baseColor = baseColor.blend(bgColor); |
| 570 context->fillRect(backgroundRect, baseColor, operation); |
| 571 } else if (bgColor.alpha()) { |
| 572 context->fillRect(backgroundRect, bgColor, operation); |
| 573 } else if (shouldClearDocumentBackground) { |
| 574 context->clearRect(backgroundRect); |
| 575 } |
| 576 } |
| 577 |
| 578 bool BoxPainter::isDocumentElementWithOpaqueBackground(RenderObject& obj) |
| 579 { |
| 580 if (!obj.isDocumentElement()) |
| 581 return false; |
| 582 |
| 583 // The background is opaque only if we're the root document, since iframes w
ith |
| 584 // no background in the child document should show the parent's background. |
| 585 bool isOpaque = true; |
| 586 Element* ownerElement = obj.document().ownerElement(); |
| 587 if (ownerElement) { |
| 588 if (!isHTMLFrameElement(*ownerElement)) { |
| 589 // Locate the <body> element using the DOM. This is easier than tryi
ng |
| 590 // to crawl around a render tree with potential :before/:after conte
nt and |
| 591 // anonymous blocks created by inline <body> tags etc. We can locate
the <body> |
| 592 // render object very easily via the DOM. |
| 593 HTMLElement* body = obj.document().body(); |
| 594 if (body) { |
| 595 // Can't scroll a frameset document anyway. |
| 596 isOpaque = body->hasTagName(HTMLNames::framesetTag); |
| 597 } else { |
| 598 // FIXME: SVG specific behavior should be in the SVG code. |
| 599 // SVG documents and XML documents with SVG root nodes are trans
parent. |
| 600 isOpaque = !obj.document().hasSVGRootNode(); |
| 601 } |
| 602 } |
| 603 } else if (obj.view()->frameView()) { |
| 604 isOpaque = !obj.view()->frameView()->isTransparent(); |
| 605 } |
| 606 |
| 607 return isOpaque; |
| 608 } |
| 609 |
| 610 static inline int getSpace(int areaSize, int tileSize) |
| 611 { |
| 612 int numberOfTiles = areaSize / tileSize; |
| 613 int space = -1; |
| 614 |
| 615 if (numberOfTiles > 1) |
| 616 space = lroundf((float)(areaSize - numberOfTiles * tileSize) / (numberOf
Tiles - 1)); |
| 617 |
| 618 return space; |
| 619 } |
| 620 |
| 621 void BoxPainter::calculateBackgroundImageGeometry(RenderBoxModelObject& obj, con
st RenderLayerModelObject* paintContainer, const FillLayer& fillLayer, const Lay
outRect& paintRect, |
| 622 BackgroundImageGeometry& geometry, RenderObject* backgroundObject) |
| 623 { |
| 624 LayoutUnit left = 0; |
| 625 LayoutUnit top = 0; |
| 626 IntSize positioningAreaSize; |
| 627 IntRect snappedPaintRect = pixelSnappedIntRect(paintRect); |
| 628 |
| 629 // Determine the background positioning area and set destRect to the backgro
und painting area. |
| 630 // destRect will be adjusted later if the background is non-repeating. |
| 631 // FIXME: transforms spec says that fixed backgrounds behave like scroll ins
ide transforms. |
| 632 bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment; |
| 633 |
| 634 if (RuntimeEnabledFeatures::fastMobileScrollingEnabled()) { |
| 635 // As a side effect of an optimization to blit on scroll, we do not hono
r the CSS |
| 636 // property "background-attachment: fixed" because it may result in rend
ering |
| 637 // artifacts. Note, these artifacts only appear if we are blitting on sc
roll of |
| 638 // a page that has fixed background images. |
| 639 fixedAttachment = false; |
| 640 } |
| 641 |
| 642 if (!fixedAttachment) { |
| 643 geometry.setDestRect(snappedPaintRect); |
| 644 |
| 645 LayoutUnit right = 0; |
| 646 LayoutUnit bottom = 0; |
| 647 // Scroll and Local. |
| 648 if (fillLayer.origin() != BorderFillBox) { |
| 649 left = obj.borderLeft(); |
| 650 right = obj.borderRight(); |
| 651 top = obj.borderTop(); |
| 652 bottom = obj.borderBottom(); |
| 653 if (fillLayer.origin() == ContentFillBox) { |
| 654 left += obj.paddingLeft(); |
| 655 right += obj.paddingRight(); |
| 656 top += obj.paddingTop(); |
| 657 bottom += obj.paddingBottom(); |
| 658 } |
| 659 } |
| 660 |
| 661 // The background of the box generated by the root element covers the en
tire canvas including |
| 662 // its margins. Since those were added in already, we have to factor the
m out when computing |
| 663 // the background positioning area. |
| 664 if (obj.isDocumentElement()) { |
| 665 positioningAreaSize = pixelSnappedIntSize(toRenderBox(&obj)->size()
- LayoutSize(left + right, top + bottom), toRenderBox(&obj)->location()); |
| 666 left += obj.marginLeft(); |
| 667 top += obj.marginTop(); |
| 668 } else { |
| 669 positioningAreaSize = pixelSnappedIntSize(paintRect.size() - LayoutS
ize(left + right, top + bottom), paintRect.location()); |
| 670 } |
| 671 } else { |
| 672 geometry.setHasNonLocalGeometry(); |
| 673 |
| 674 IntRect viewportRect = pixelSnappedIntRect(obj.viewRect()); |
| 675 if (fixedBackgroundPaintsInLocalCoordinates(obj)) |
| 676 viewportRect.setLocation(IntPoint()); |
| 677 else if (FrameView* frameView = obj.view()->frameView()) |
| 678 viewportRect.setLocation(IntPoint(frameView->scrollOffsetForFixedPos
ition())); |
| 679 |
| 680 if (paintContainer) { |
| 681 IntPoint absoluteContainerOffset = roundedIntPoint(paintContainer->l
ocalToAbsolute(FloatPoint())); |
| 682 viewportRect.moveBy(-absoluteContainerOffset); |
| 683 } |
| 684 |
| 685 geometry.setDestRect(pixelSnappedIntRect(viewportRect)); |
| 686 positioningAreaSize = geometry.destRect().size(); |
| 687 } |
| 688 |
| 689 const RenderObject* clientForBackgroundImage = backgroundObject ? background
Object : &obj; |
| 690 IntSize fillTileSize = calculateFillTileSize(obj, fillLayer, positioningArea
Size); |
| 691 fillLayer.image()->setContainerSizeForRenderer(clientForBackgroundImage, fil
lTileSize, obj.style()->effectiveZoom()); |
| 692 geometry.setTileSize(fillTileSize); |
| 693 |
| 694 EFillRepeat backgroundRepeatX = fillLayer.repeatX(); |
| 695 EFillRepeat backgroundRepeatY = fillLayer.repeatY(); |
| 696 int availableWidth = positioningAreaSize.width() - geometry.tileSize().width
(); |
| 697 int availableHeight = positioningAreaSize.height() - geometry.tileSize().hei
ght(); |
| 698 |
| 699 LayoutUnit computedXPosition = roundedMinimumValueForLength(fillLayer.xPosit
ion(), availableWidth); |
| 700 if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > 0 && fil
lTileSize.width() > 0) { |
| 701 long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.width() /
fillTileSize.width())); |
| 702 |
| 703 if (fillLayer.size().size.height().isAuto() && backgroundRepeatY != Roun
dFill) { |
| 704 fillTileSize.setHeight(fillTileSize.height() * positioningAreaSize.w
idth() / (nrTiles * fillTileSize.width())); |
| 705 } |
| 706 |
| 707 fillTileSize.setWidth(positioningAreaSize.width() / nrTiles); |
| 708 geometry.setTileSize(fillTileSize); |
| 709 geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().wid
th() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0); |
| 710 geometry.setSpaceSize(IntSize()); |
| 711 } |
| 712 |
| 713 LayoutUnit computedYPosition = roundedMinimumValueForLength(fillLayer.yPosit
ion(), availableHeight); |
| 714 if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > 0 && fi
llTileSize.height() > 0) { |
| 715 long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.height()
/ fillTileSize.height())); |
| 716 |
| 717 if (fillLayer.size().size.width().isAuto() && backgroundRepeatX != Round
Fill) { |
| 718 fillTileSize.setWidth(fillTileSize.width() * positioningAreaSize.hei
ght() / (nrTiles * fillTileSize.height())); |
| 719 } |
| 720 |
| 721 fillTileSize.setHeight(positioningAreaSize.height() / nrTiles); |
| 722 geometry.setTileSize(fillTileSize); |
| 723 geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().he
ight() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0)
; |
| 724 geometry.setSpaceSize(IntSize()); |
| 725 } |
| 726 |
| 727 if (backgroundRepeatX == RepeatFill) { |
| 728 geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().wid
th() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0); |
| 729 geometry.setSpaceSize(IntSize()); |
| 730 } else if (backgroundRepeatX == SpaceFill && fillTileSize.width() > 0) { |
| 731 int space = getSpace(positioningAreaSize.width(), geometry.tileSize().wi
dth()); |
| 732 int actualWidth = geometry.tileSize().width() + space; |
| 733 |
| 734 if (space >= 0) { |
| 735 computedXPosition = roundedMinimumValueForLength(Length(), available
Width); |
| 736 geometry.setSpaceSize(IntSize(space, 0)); |
| 737 geometry.setPhaseX(actualWidth ? actualWidth - roundToInt(computedXP
osition + left) % actualWidth : 0); |
| 738 } else { |
| 739 backgroundRepeatX = NoRepeatFill; |
| 740 } |
| 741 } |
| 742 if (backgroundRepeatX == NoRepeatFill) { |
| 743 int xOffset = fillLayer.backgroundXOrigin() == RightEdge ? availableWidt
h - computedXPosition : computedXPosition; |
| 744 geometry.setNoRepeatX(left + xOffset); |
| 745 geometry.setSpaceSize(IntSize(0, geometry.spaceSize().height())); |
| 746 } |
| 747 |
| 748 if (backgroundRepeatY == RepeatFill) { |
| 749 geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().he
ight() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0)
; |
| 750 geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0)); |
| 751 } else if (backgroundRepeatY == SpaceFill && fillTileSize.height() > 0) { |
| 752 int space = getSpace(positioningAreaSize.height(), geometry.tileSize().h
eight()); |
| 753 int actualHeight = geometry.tileSize().height() + space; |
| 754 |
| 755 if (space >= 0) { |
| 756 computedYPosition = roundedMinimumValueForLength(Length(), available
Height); |
| 757 geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), space)); |
| 758 geometry.setPhaseY(actualHeight ? actualHeight - roundToInt(computed
YPosition + top) % actualHeight : 0); |
| 759 } else { |
| 760 backgroundRepeatY = NoRepeatFill; |
| 761 } |
| 762 } |
| 763 if (backgroundRepeatY == NoRepeatFill) { |
| 764 int yOffset = fillLayer.backgroundYOrigin() == BottomEdge ? availableHei
ght - computedYPosition : computedYPosition; |
| 765 geometry.setNoRepeatY(top + yOffset); |
| 766 geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0)); |
| 767 } |
| 768 |
| 769 if (fixedAttachment) |
| 770 geometry.useFixedAttachment(snappedPaintRect.location()); |
| 771 |
| 772 geometry.clip(snappedPaintRect); |
| 773 geometry.setDestOrigin(geometry.destRect().location()); |
| 774 } |
| 775 |
| 776 InterpolationQuality BoxPainter::chooseInterpolationQuality(RenderBoxModelObject
& obj, GraphicsContext* context, Image* image, const void* layer, const LayoutSi
ze& size) |
| 777 { |
| 778 return ImageQualityController::imageQualityController()->chooseInterpolation
Quality(context, &obj, image, layer, size); |
| 779 } |
| 780 |
| 781 bool BoxPainter::fixedBackgroundPaintsInLocalCoordinates(const RenderObject& obj
) |
| 782 { |
| 783 if (!obj.isDocumentElement()) |
| 784 return false; |
| 785 |
| 786 if (obj.view()->frameView() && obj.view()->frameView()->paintBehavior() & Pa
intBehaviorFlattenCompositingLayers) |
| 787 return false; |
| 788 |
| 789 RenderLayer* rootLayer = obj.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 RenderBoxModelObject& obj, const
FillLayer& fillLayer, const IntSize& positioningAreaSize) |
| 803 { |
| 804 StyleImage* image = fillLayer.image(); |
| 805 EFillSizeType type = fillLayer.size().type; |
| 806 |
| 807 IntSize imageIntrinsicSize = obj.calculateImageIntrinsicDimensions(image, po
sitioningAreaSize, 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 |