OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "config.h" |
| 6 #include "core/paint/BoxPainter.h" |
| 7 |
| 8 #include "core/paint/BoxDecorationData.h" |
| 9 #include "core/rendering/PaintInfo.h" |
| 10 #include "core/rendering/RenderBox.h" |
| 11 #include "core/rendering/RenderLayer.h" |
| 12 #include "core/rendering/RenderTable.h" |
| 13 #include "core/rendering/RenderTheme.h" |
| 14 #include "core/rendering/RenderView.h" |
| 15 #include "platform/geometry/LayoutPoint.h" |
| 16 #include "platform/graphics/GraphicsContextStateSaver.h" |
| 17 |
| 18 namespace blink { |
| 19 |
| 20 void BoxPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| 21 { |
| 22 LayoutPoint adjustedPaintOffset = paintOffset + m_renderBox.location(); |
| 23 // default implementation. Just pass paint through to the children |
| 24 PaintInfo childInfo(paintInfo); |
| 25 childInfo.updatePaintingRootForChildren(&m_renderBox); |
| 26 for (RenderObject* child = m_renderBox.slowFirstChild(); child; child = chil
d->nextSibling()) |
| 27 child->paint(childInfo, adjustedPaintOffset); |
| 28 } |
| 29 |
| 30 void BoxPainter::paintBoxDecorationBackground(PaintInfo& paintInfo, const Layout
Point& paintOffset) |
| 31 { |
| 32 if (!paintInfo.shouldPaintWithinRoot(&m_renderBox)) |
| 33 return; |
| 34 |
| 35 LayoutRect paintRect = m_renderBox.borderBoxRect(); |
| 36 paintRect.moveBy(paintOffset); |
| 37 paintBoxDecorationBackgroundWithRect(paintInfo, paintOffset, paintRect); |
| 38 } |
| 39 |
| 40 void BoxPainter::paintBoxDecorationBackgroundWithRect(PaintInfo& paintInfo, cons
t LayoutPoint& paintOffset, const LayoutRect& paintRect) |
| 41 { |
| 42 RenderStyle* style = m_renderBox.style(); |
| 43 BoxDecorationData boxDecorationData(*style, m_renderBox.canRenderBorderImage
(), m_renderBox.backgroundHasOpaqueTopLayer(), paintInfo.context); |
| 44 |
| 45 // FIXME: Should eventually give the theme control over whether the box shad
ow should paint, since controls could have |
| 46 // custom shadows of their own. |
| 47 if (!m_renderBox.boxShadowShouldBeAppliedToBackground(boxDecorationData.blee
dAvoidance())) |
| 48 m_renderBox.paintBoxShadow(paintInfo, paintRect, style, Normal); |
| 49 |
| 50 GraphicsContextStateSaver stateSaver(*paintInfo.context, false); |
| 51 if (boxDecorationData.bleedAvoidance() == BackgroundBleedClipBackground) { |
| 52 stateSaver.save(); |
| 53 RoundedRect border = style->getRoundedBorderFor(paintRect); |
| 54 paintInfo.context->clipRoundedRect(border); |
| 55 } |
| 56 |
| 57 // If we have a native theme appearance, paint that before painting our back
ground. |
| 58 // The theme will tell us whether or not we should also paint the CSS backgr
ound. |
| 59 IntRect snappedPaintRect(pixelSnappedIntRect(paintRect)); |
| 60 bool themePainted = boxDecorationData.hasAppearance && !RenderTheme::theme()
.paint(&m_renderBox, paintInfo, snappedPaintRect); |
| 61 if (!themePainted) { |
| 62 if (boxDecorationData.bleedAvoidance() == BackgroundBleedBackgroundOverB
order) |
| 63 m_renderBox.paintBorder(paintInfo, paintRect, style, boxDecorationDa
ta.bleedAvoidance()); |
| 64 |
| 65 paintBackground(paintInfo, paintRect, boxDecorationData.backgroundColor,
boxDecorationData.bleedAvoidance()); |
| 66 |
| 67 if (boxDecorationData.hasAppearance) |
| 68 RenderTheme::theme().paintDecorations(&m_renderBox, paintInfo, snapp
edPaintRect); |
| 69 } |
| 70 m_renderBox.paintBoxShadow(paintInfo, paintRect, style, Inset); |
| 71 |
| 72 // The theme will tell us whether or not we should also paint the CSS border
. |
| 73 if (boxDecorationData.hasBorder && boxDecorationData.bleedAvoidance() != Bac
kgroundBleedBackgroundOverBorder |
| 74 && (!boxDecorationData.hasAppearance || (!themePainted && RenderTheme::t
heme().paintBorderOnly(&m_renderBox, paintInfo, snappedPaintRect))) |
| 75 && !(m_renderBox.isTable() && toRenderTable(&m_renderBox)->collapseBorde
rs())) |
| 76 m_renderBox.paintBorder(paintInfo, paintRect, style, boxDecorationData.b
leedAvoidance()); |
| 77 } |
| 78 |
| 79 static bool skipBodyBackground(const RenderBox* bodyElementRenderer) |
| 80 { |
| 81 ASSERT(bodyElementRenderer->isBody()); |
| 82 // The <body> only paints its background if the root element has defined a b
ackground independent of the body, |
| 83 // or if the <body>'s parent is not the document element's renderer (e.g. in
side SVG foreignObject). |
| 84 RenderObject* documentElementRenderer = bodyElementRenderer->document().docu
mentElement()->renderer(); |
| 85 return documentElementRenderer |
| 86 && !documentElementRenderer->hasBackground() |
| 87 && (documentElementRenderer == bodyElementRenderer->parent()); |
| 88 } |
| 89 |
| 90 void BoxPainter::paintBackground(const PaintInfo& paintInfo, const LayoutRect& p
aintRect, const Color& backgroundColor, BackgroundBleedAvoidance bleedAvoidance) |
| 91 { |
| 92 if (m_renderBox.isDocumentElement()) { |
| 93 paintRootBoxFillLayers(paintInfo); |
| 94 return; |
| 95 } |
| 96 if (m_renderBox.isBody() && skipBodyBackground(&m_renderBox)) |
| 97 return; |
| 98 if (m_renderBox.boxDecorationBackgroundIsKnownToBeObscured()) |
| 99 return; |
| 100 paintFillLayers(paintInfo, backgroundColor, m_renderBox.style()->backgroundL
ayers(), paintRect, bleedAvoidance); |
| 101 } |
| 102 |
| 103 void BoxPainter::paintRootBoxFillLayers(const PaintInfo& paintInfo) |
| 104 { |
| 105 if (paintInfo.skipRootBackground()) |
| 106 return; |
| 107 |
| 108 RenderObject* rootBackgroundRenderer = m_renderBox.rendererForRootBackground
(); |
| 109 |
| 110 const FillLayer& bgLayer = rootBackgroundRenderer->style()->backgroundLayers
(); |
| 111 Color bgColor = rootBackgroundRenderer->resolveColor(CSSPropertyBackgroundCo
lor); |
| 112 |
| 113 paintFillLayers(paintInfo, bgColor, bgLayer, m_renderBox.view()->backgroundR
ect(&m_renderBox), BackgroundBleedNone, CompositeSourceOver, rootBackgroundRende
rer); |
| 114 } |
| 115 |
| 116 void BoxPainter::paintFillLayers(const PaintInfo& paintInfo, const Color& c, con
st FillLayer& fillLayer, const LayoutRect& rect, |
| 117 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject*
backgroundObject) |
| 118 { |
| 119 Vector<const FillLayer*, 8> layers; |
| 120 const FillLayer* curLayer = &fillLayer; |
| 121 bool shouldDrawBackgroundInSeparateBuffer = false; |
| 122 bool isBottomLayerOccluded = false; |
| 123 while (curLayer) { |
| 124 layers.append(curLayer); |
| 125 // Stop traversal when an opaque layer is encountered. |
| 126 // FIXME : It would be possible for the following occlusion culling test
to be more aggressive |
| 127 // on layers with no repeat by testing whether the image covers the layo
ut rect. |
| 128 // Testing that here would imply duplicating a lot of calculations that
are currently done in |
| 129 // RenderBoxModelObject::paintFillLayerExtended. A more efficient soluti
on might be to move |
| 130 // the layer recursion into paintFillLayerExtended, or to compute the la
yer geometry here |
| 131 // and pass it down. |
| 132 |
| 133 if (!shouldDrawBackgroundInSeparateBuffer && curLayer->blendMode() != We
bBlendModeNormal) |
| 134 shouldDrawBackgroundInSeparateBuffer = true; |
| 135 |
| 136 // The clipOccludesNextLayers condition must be evaluated first to avoid
short-circuiting. |
| 137 if (curLayer->clipOccludesNextLayers(curLayer == &fillLayer) |
| 138 && curLayer->hasOpaqueImage(&m_renderBox) |
| 139 && curLayer->image()->canRender(m_renderBox, m_renderBox.style()->ef
fectiveZoom()) |
| 140 && curLayer->hasRepeatXY() |
| 141 && curLayer->blendMode() == WebBlendModeNormal |
| 142 && !m_renderBox.boxShadowShouldBeAppliedToBackground(bleedAvoidance)
) |
| 143 break; |
| 144 curLayer = curLayer->next(); |
| 145 } |
| 146 |
| 147 if (layers.size() > 0 && (**layers.rbegin()).next()) |
| 148 isBottomLayerOccluded = true; |
| 149 |
| 150 GraphicsContext* context = paintInfo.context; |
| 151 if (!context) |
| 152 shouldDrawBackgroundInSeparateBuffer = false; |
| 153 |
| 154 bool skipBaseColor = false; |
| 155 if (shouldDrawBackgroundInSeparateBuffer) { |
| 156 bool isBaseColorVisible = !isBottomLayerOccluded && c.hasAlpha(); |
| 157 |
| 158 // 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. |
| 160 if (isBaseColorVisible && m_renderBox.isDocumentElementWithOpaqueBackgro
und()) { |
| 161 m_renderBox.paintRootBackgroundColor(paintInfo, rect, Color()); |
| 162 skipBaseColor = true; |
| 163 } |
| 164 context->beginTransparencyLayer(1); |
| 165 } |
| 166 |
| 167 Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend(); |
| 168 for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin();
it != topLayer; ++it) |
| 169 paintFillLayer(paintInfo, c, **it, rect, bleedAvoidance, op, backgroundO
bject, skipBaseColor); |
| 170 |
| 171 if (shouldDrawBackgroundInSeparateBuffer) |
| 172 context->endLayer(); |
| 173 } |
| 174 |
| 175 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) |
| 177 { |
| 178 m_renderBox.paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoid
ance, 0, LayoutSize(), op, backgroundObject, skipBaseColor); |
| 179 } |
| 180 |
| 181 void BoxPainter::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| 182 { |
| 183 if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->v
isibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) |
| 184 return; |
| 185 |
| 186 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); |
| 187 paintMaskImages(paintInfo, paintRect); |
| 188 } |
| 189 |
| 190 void BoxPainter::paintMaskImages(const PaintInfo& paintInfo, const LayoutRect& p
aintRect) |
| 191 { |
| 192 // Figure out if we need to push a transparency layer to render our mask. |
| 193 bool pushTransparencyLayer = false; |
| 194 bool compositedMask = m_renderBox.hasLayer() && m_renderBox.layer()->hasComp
ositedMask(); |
| 195 bool flattenCompositingLayers = m_renderBox.view()->frameView() && m_renderB
ox.view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers; |
| 196 CompositeOperator compositeOp = CompositeSourceOver; |
| 197 |
| 198 bool allMaskImagesLoaded = true; |
| 199 |
| 200 if (!compositedMask || flattenCompositingLayers) { |
| 201 pushTransparencyLayer = true; |
| 202 StyleImage* maskBoxImage = m_renderBox.style()->maskBoxImage().image(); |
| 203 const FillLayer& maskLayers = m_renderBox.style()->maskLayers(); |
| 204 |
| 205 // Don't render a masked element until all the mask images have loaded,
to prevent a flash of unmasked content. |
| 206 if (maskBoxImage) |
| 207 allMaskImagesLoaded &= maskBoxImage->isLoaded(); |
| 208 |
| 209 allMaskImagesLoaded &= maskLayers.imagesAreLoaded(); |
| 210 |
| 211 paintInfo.context->setCompositeOperation(CompositeDestinationIn); |
| 212 paintInfo.context->beginTransparencyLayer(1); |
| 213 compositeOp = CompositeSourceOver; |
| 214 } |
| 215 |
| 216 if (allMaskImagesLoaded) { |
| 217 paintFillLayers(paintInfo, Color::transparent, m_renderBox.style()->mask
Layers(), paintRect, BackgroundBleedNone, compositeOp); |
| 218 m_renderBox.paintNinePieceImage(paintInfo.context, paintRect, m_renderBo
x.style(), m_renderBox.style()->maskBoxImage(), compositeOp); |
| 219 } |
| 220 |
| 221 if (pushTransparencyLayer) |
| 222 paintInfo.context->endLayer(); |
| 223 } |
| 224 |
| 225 void BoxPainter::paintClippingMask(PaintInfo& paintInfo, const LayoutPoint& pain
tOffset) |
| 226 { |
| 227 if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->v
isibility() != VISIBLE || paintInfo.phase != PaintPhaseClippingMask) |
| 228 return; |
| 229 |
| 230 if (!m_renderBox.layer() || m_renderBox.layer()->compositingState() != Paint
sIntoOwnBacking) |
| 231 return; |
| 232 |
| 233 // 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. |
| 235 ASSERT(m_renderBox.layer()->compositingState() != HasOwnBackingButPaintsInto
Ancestor); |
| 236 |
| 237 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); |
| 238 paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black); |
| 239 } |
| 240 |
| 241 } // namespace blink |
OLD | NEW |