| Index: Source/core/paint/BoxPainter.cpp
|
| diff --git a/Source/core/paint/BoxPainter.cpp b/Source/core/paint/BoxPainter.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c495937249fbb70ca7cc2725ccd5053d76e5a642
|
| --- /dev/null
|
| +++ b/Source/core/paint/BoxPainter.cpp
|
| @@ -0,0 +1,241 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "config.h"
|
| +#include "core/paint/BoxPainter.h"
|
| +
|
| +#include "core/rendering/BoxDecorationData.h"
|
| +#include "core/rendering/PaintInfo.h"
|
| +#include "core/rendering/RenderBox.h"
|
| +#include "core/rendering/RenderLayer.h"
|
| +#include "core/rendering/RenderTable.h"
|
| +#include "core/rendering/RenderTheme.h"
|
| +#include "core/rendering/RenderView.h"
|
| +#include "platform/geometry/LayoutPoint.h"
|
| +#include "platform/graphics/GraphicsContextStateSaver.h"
|
| +
|
| +namespace blink {
|
| +
|
| +void BoxPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
|
| +{
|
| + LayoutPoint adjustedPaintOffset = paintOffset + m_renderBox.location();
|
| + // default implementation. Just pass paint through to the children
|
| + PaintInfo childInfo(paintInfo);
|
| + childInfo.updatePaintingRootForChildren(&m_renderBox);
|
| + for (RenderObject* child = m_renderBox.slowFirstChild(); child; child = child->nextSibling())
|
| + child->paint(childInfo, adjustedPaintOffset);
|
| +}
|
| +
|
| +void BoxPainter::paintBoxDecorationBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
|
| +{
|
| + if (!paintInfo.shouldPaintWithinRoot(&m_renderBox))
|
| + return;
|
| +
|
| + LayoutRect paintRect = m_renderBox.borderBoxRect();
|
| + paintRect.moveBy(paintOffset);
|
| + paintBoxDecorationBackgroundWithRect(paintInfo, paintOffset, paintRect);
|
| +}
|
| +
|
| +void BoxPainter::paintBoxDecorationBackgroundWithRect(PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutRect& paintRect)
|
| +{
|
| + RenderStyle* style = m_renderBox.style();
|
| + BoxDecorationData boxDecorationData(*style, m_renderBox.canRenderBorderImage(), m_renderBox.backgroundHasOpaqueTopLayer(), paintInfo.context);
|
| +
|
| + // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
|
| + // custom shadows of their own.
|
| + if (!m_renderBox.boxShadowShouldBeAppliedToBackground(boxDecorationData.bleedAvoidance))
|
| + m_renderBox.paintBoxShadow(paintInfo, paintRect, style, Normal);
|
| +
|
| + GraphicsContextStateSaver stateSaver(*paintInfo.context, false);
|
| + if (boxDecorationData.bleedAvoidance == BackgroundBleedClipBackground) {
|
| + stateSaver.save();
|
| + RoundedRect border = style->getRoundedBorderFor(paintRect);
|
| + paintInfo.context->clipRoundedRect(border);
|
| + }
|
| +
|
| + // If we have a native theme appearance, paint that before painting our background.
|
| + // The theme will tell us whether or not we should also paint the CSS background.
|
| + IntRect snappedPaintRect(pixelSnappedIntRect(paintRect));
|
| + bool themePainted = boxDecorationData.hasAppearance && !RenderTheme::theme().paint(&m_renderBox, paintInfo, snappedPaintRect);
|
| + if (!themePainted) {
|
| + if (boxDecorationData.bleedAvoidance == BackgroundBleedBackgroundOverBorder)
|
| + m_renderBox.paintBorder(paintInfo, paintRect, style, boxDecorationData.bleedAvoidance);
|
| +
|
| + paintBackground(paintInfo, paintRect, boxDecorationData.backgroundColor, boxDecorationData.bleedAvoidance);
|
| +
|
| + if (boxDecorationData.hasAppearance)
|
| + RenderTheme::theme().paintDecorations(&m_renderBox, paintInfo, snappedPaintRect);
|
| + }
|
| + m_renderBox.paintBoxShadow(paintInfo, paintRect, style, Inset);
|
| +
|
| + // The theme will tell us whether or not we should also paint the CSS border.
|
| + if (boxDecorationData.hasBorder && boxDecorationData.bleedAvoidance != BackgroundBleedBackgroundOverBorder
|
| + && (!boxDecorationData.hasAppearance || (!themePainted && RenderTheme::theme().paintBorderOnly(&m_renderBox, paintInfo, snappedPaintRect)))
|
| + && !(m_renderBox.isTable() && toRenderTable(&m_renderBox)->collapseBorders()))
|
| + m_renderBox.paintBorder(paintInfo, paintRect, style, boxDecorationData.bleedAvoidance);
|
| +}
|
| +
|
| +static bool skipBodyBackground(const RenderBox* bodyElementRenderer)
|
| +{
|
| + ASSERT(bodyElementRenderer->isBody());
|
| + // The <body> only paints its background if the root element has defined a background independent of the body,
|
| + // or if the <body>'s parent is not the document element's renderer (e.g. inside SVG foreignObject).
|
| + RenderObject* documentElementRenderer = bodyElementRenderer->document().documentElement()->renderer();
|
| + return documentElementRenderer
|
| + && !documentElementRenderer->hasBackground()
|
| + && (documentElementRenderer == bodyElementRenderer->parent());
|
| +}
|
| +
|
| +void BoxPainter::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, const Color& backgroundColor, BackgroundBleedAvoidance bleedAvoidance)
|
| +{
|
| + if (m_renderBox.isDocumentElement()) {
|
| + paintRootBoxFillLayers(paintInfo);
|
| + return;
|
| + }
|
| + if (m_renderBox.isBody() && skipBodyBackground(&m_renderBox))
|
| + return;
|
| + if (m_renderBox.boxDecorationBackgroundIsKnownToBeObscured())
|
| + return;
|
| + paintFillLayers(paintInfo, backgroundColor, m_renderBox.style()->backgroundLayers(), paintRect, bleedAvoidance);
|
| +}
|
| +
|
| +void BoxPainter::paintRootBoxFillLayers(const PaintInfo& paintInfo)
|
| +{
|
| + if (paintInfo.skipRootBackground())
|
| + return;
|
| +
|
| + RenderObject* rootBackgroundRenderer = m_renderBox.rendererForRootBackground();
|
| +
|
| + const FillLayer& bgLayer = rootBackgroundRenderer->style()->backgroundLayers();
|
| + Color bgColor = rootBackgroundRenderer->resolveColor(CSSPropertyBackgroundColor);
|
| +
|
| + paintFillLayers(paintInfo, bgColor, bgLayer, m_renderBox.view()->backgroundRect(&m_renderBox), BackgroundBleedNone, CompositeSourceOver, rootBackgroundRenderer);
|
| +}
|
| +
|
| +void BoxPainter::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect,
|
| + BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject)
|
| +{
|
| + Vector<const FillLayer*, 8> layers;
|
| + const FillLayer* curLayer = &fillLayer;
|
| + bool shouldDrawBackgroundInSeparateBuffer = false;
|
| + bool isBottomLayerOccluded = false;
|
| + while (curLayer) {
|
| + layers.append(curLayer);
|
| + // Stop traversal when an opaque layer is encountered.
|
| + // FIXME : It would be possible for the following occlusion culling test to be more aggressive
|
| + // on layers with no repeat by testing whether the image covers the layout rect.
|
| + // Testing that here would imply duplicating a lot of calculations that are currently done in
|
| + // RenderBoxModelObject::paintFillLayerExtended. A more efficient solution might be to move
|
| + // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here
|
| + // and pass it down.
|
| +
|
| + if (!shouldDrawBackgroundInSeparateBuffer && curLayer->blendMode() != WebBlendModeNormal)
|
| + shouldDrawBackgroundInSeparateBuffer = true;
|
| +
|
| + // The clipOccludesNextLayers condition must be evaluated first to avoid short-circuiting.
|
| + if (curLayer->clipOccludesNextLayers(curLayer == &fillLayer)
|
| + && curLayer->hasOpaqueImage(&m_renderBox)
|
| + && curLayer->image()->canRender(m_renderBox, m_renderBox.style()->effectiveZoom())
|
| + && curLayer->hasRepeatXY()
|
| + && curLayer->blendMode() == WebBlendModeNormal
|
| + && !m_renderBox.boxShadowShouldBeAppliedToBackground(bleedAvoidance))
|
| + break;
|
| + curLayer = curLayer->next();
|
| + }
|
| +
|
| + if (layers.size() > 0 && (**layers.rbegin()).next())
|
| + isBottomLayerOccluded = true;
|
| +
|
| + GraphicsContext* context = paintInfo.context;
|
| + if (!context)
|
| + shouldDrawBackgroundInSeparateBuffer = false;
|
| +
|
| + bool skipBaseColor = false;
|
| + if (shouldDrawBackgroundInSeparateBuffer) {
|
| + bool isBaseColorVisible = !isBottomLayerOccluded && c.hasAlpha();
|
| +
|
| + // Paint the document's base background color outside the transparency layer,
|
| + // so that the background images don't blend with this color: http://crbug.com/389039.
|
| + if (isBaseColorVisible && m_renderBox.isDocumentElementWithOpaqueBackground()) {
|
| + m_renderBox.paintRootBackgroundColor(paintInfo, rect, Color());
|
| + skipBaseColor = true;
|
| + }
|
| + context->beginTransparencyLayer(1);
|
| + }
|
| +
|
| + Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend();
|
| + for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it)
|
| + paintFillLayer(paintInfo, c, **it, rect, bleedAvoidance, op, backgroundObject, skipBaseColor);
|
| +
|
| + if (shouldDrawBackgroundInSeparateBuffer)
|
| + context->endLayer();
|
| +}
|
| +
|
| +void BoxPainter::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect,
|
| + BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject, bool skipBaseColor)
|
| +{
|
| + m_renderBox.paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoidance, 0, LayoutSize(), op, backgroundObject, skipBaseColor);
|
| +}
|
| +
|
| +void BoxPainter::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
|
| +{
|
| + if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
|
| + return;
|
| +
|
| + LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size());
|
| + paintMaskImages(paintInfo, paintRect);
|
| +}
|
| +
|
| +void BoxPainter::paintMaskImages(const PaintInfo& paintInfo, const LayoutRect& paintRect)
|
| +{
|
| + // Figure out if we need to push a transparency layer to render our mask.
|
| + bool pushTransparencyLayer = false;
|
| + bool compositedMask = m_renderBox.hasLayer() && m_renderBox.layer()->hasCompositedMask();
|
| + bool flattenCompositingLayers = m_renderBox.view()->frameView() && m_renderBox.view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers;
|
| + CompositeOperator compositeOp = CompositeSourceOver;
|
| +
|
| + bool allMaskImagesLoaded = true;
|
| +
|
| + if (!compositedMask || flattenCompositingLayers) {
|
| + pushTransparencyLayer = true;
|
| + StyleImage* maskBoxImage = m_renderBox.style()->maskBoxImage().image();
|
| + const FillLayer& maskLayers = m_renderBox.style()->maskLayers();
|
| +
|
| + // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content.
|
| + if (maskBoxImage)
|
| + allMaskImagesLoaded &= maskBoxImage->isLoaded();
|
| +
|
| + allMaskImagesLoaded &= maskLayers.imagesAreLoaded();
|
| +
|
| + paintInfo.context->setCompositeOperation(CompositeDestinationIn);
|
| + paintInfo.context->beginTransparencyLayer(1);
|
| + compositeOp = CompositeSourceOver;
|
| + }
|
| +
|
| + if (allMaskImagesLoaded) {
|
| + paintFillLayers(paintInfo, Color::transparent, m_renderBox.style()->maskLayers(), paintRect, BackgroundBleedNone, compositeOp);
|
| + m_renderBox.paintNinePieceImage(paintInfo.context, paintRect, m_renderBox.style(), m_renderBox.style()->maskBoxImage(), compositeOp);
|
| + }
|
| +
|
| + if (pushTransparencyLayer)
|
| + paintInfo.context->endLayer();
|
| +}
|
| +
|
| +void BoxPainter::paintClippingMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
|
| +{
|
| + if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseClippingMask)
|
| + return;
|
| +
|
| + if (!m_renderBox.layer() || m_renderBox.layer()->compositingState() != PaintsIntoOwnBacking)
|
| + return;
|
| +
|
| + // We should never have this state in this function. A layer with a mask
|
| + // should have always created its own backing if it became composited.
|
| + ASSERT(layer()->compositingState() != HasOwnBackingButPaintsIntoAncestor);
|
| +
|
| + LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size());
|
| + paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black);
|
| +}
|
| +
|
| +} // namespace blink
|
|
|