| Index: sky/engine/core/rendering/RenderBox.cpp
|
| diff --git a/sky/engine/core/rendering/RenderBox.cpp b/sky/engine/core/rendering/RenderBox.cpp
|
| index 313eae4cbe970e645a8f2f54bb0fda08639c735c..ca32089cbfda1ce153c3dadbf240d3a1dacc7c97 100644
|
| --- a/sky/engine/core/rendering/RenderBox.cpp
|
| +++ b/sky/engine/core/rendering/RenderBox.cpp
|
| @@ -37,6 +37,7 @@
|
| #include "sky/engine/core/html/HTMLElement.h"
|
| #include "sky/engine/core/page/EventHandler.h"
|
| #include "sky/engine/core/page/Page.h"
|
| +#include "sky/engine/core/rendering/FilterEffectRenderer.h"
|
| #include "sky/engine/core/rendering/HitTestResult.h"
|
| #include "sky/engine/core/rendering/PaintInfo.h"
|
| #include "sky/engine/core/rendering/RenderFlexibleBox.h"
|
| @@ -388,11 +389,216 @@ bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result
|
|
|
| // --------------------- painting stuff -------------------------------
|
|
|
| -void RenderBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
|
| +static inline bool compareZIndex(RenderBox* first, RenderBox* second)
|
| +{
|
| + return first->style()->zIndex() < second->style()->zIndex();
|
| +}
|
| +
|
| +void RenderBox::paintLayer(GraphicsContext* context, RenderLayer* rootLayer, const IntRect& rect)
|
| +{
|
| + // If this layer is totally invisible then there is nothing to paint.
|
| + // TODO(ojan): Return false from isSelfPainting and then ASSERT(!opacity()) here.
|
| + if (!opacity())
|
| + return;
|
| +
|
| + LayerPaintingInfo paintingInfo(rootLayer, rect, LayoutSize());
|
| +
|
| + if (!layer()->paintsWithTransform()) {
|
| + paintLayerContents(context, paintingInfo, rect);
|
| + return;
|
| + }
|
| +
|
| + TransformationMatrix layerTransform = layer()->renderableTransform();
|
| + // If the transform can't be inverted, then don't paint anything.
|
| + if (!layerTransform.isInvertible())
|
| + return;
|
| +
|
| + // If we have a transparency layer enclosing us and we are the root of a transform, then we need to establish the transparency
|
| + // layer from the parent now, assuming there is a parent
|
| + if (layer()->isTransparent()) {
|
| + // TODO(ojan): This should ASSERT(layer()->parent()) instead of branching since the
|
| + // RenderView can't be transformed in sky.
|
| + if (layer()->parent())
|
| + layer()->parent()->beginTransparencyLayers(context, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, paintingInfo.subPixelAccumulation);
|
| + else
|
| + layer()->beginTransparencyLayers(context, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, paintingInfo.subPixelAccumulation);
|
| + }
|
| +
|
| + // Make sure the parent's clip rects have been calculated.
|
| + ClipRect clipRect;
|
| + // TODO(ojan): This should ASSERT(layer()->parent()) instead of branching since the
|
| + // RenderView can't be transformed in sky.
|
| + if (layer()->parent()) {
|
| + ClipRectsContext clipRectsContext(paintingInfo.rootLayer, PaintingClipRects);
|
| + clipRect = layer()->clipper().backgroundClipRect(clipRectsContext);
|
| + clipRect.intersect(paintingInfo.paintDirtyRect);
|
| +
|
| + // Push the parent coordinate space's clip.
|
| + layer()->parent()->clipToRect(paintingInfo, context, clipRect);
|
| + }
|
| +
|
| + // This involves subtracting out the position of the layer in our current coordinate space, but preserving
|
| + // the accumulated error for sub-pixel layout.
|
| + LayoutPoint delta;
|
| + layer()->convertToLayerCoords(paintingInfo.rootLayer, delta);
|
| + TransformationMatrix transform(layer()->renderableTransform());
|
| + IntPoint roundedDelta = roundedIntPoint(delta);
|
| + transform.translateRight(roundedDelta.x(), roundedDelta.y());
|
| + LayoutSize adjustedSubPixelAccumulation = paintingInfo.subPixelAccumulation + (delta - roundedDelta);
|
| +
|
| + // Apply the transform.
|
| + GraphicsContextStateSaver stateSaver(*context, false);
|
| + if (!transform.isIdentity()) {
|
| + stateSaver.save();
|
| + context->concatCTM(transform.toAffineTransform());
|
| + }
|
| +
|
| + // Now do a paint with the root layer shifted to be us.
|
| + LayerPaintingInfo transformedPaintingInfo(layer(), enclosingIntRect(transform.inverse().mapRect(paintingInfo.paintDirtyRect)),
|
| + adjustedSubPixelAccumulation);
|
| + paintLayerContents(context, transformedPaintingInfo, rect);
|
| +
|
| + // Restore the clip.
|
| + // TODO(ojan): This should ASSERT(layer()->parent()) instead of branching since the
|
| + // RenderView can't be transformed in sky.
|
| + if (layer()->parent())
|
| + layer()->parent()->restoreClip(context, paintingInfo.paintDirtyRect, clipRect);
|
| +}
|
| +
|
| +void RenderBox::paintLayerContents(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, const IntRect& rect)
|
| +{
|
| + float deviceScaleFactor = blink::deviceScaleFactor(frame());
|
| + context->setDeviceScaleFactor(deviceScaleFactor);
|
| +
|
| + GraphicsContext* transparencyLayerContext = context;
|
| +
|
| + LayoutPoint offsetFromRoot;
|
| + layer()->convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot);
|
| +
|
| + LayoutRect rootRelativeBounds;
|
| + bool rootRelativeBoundsComputed = false;
|
| +
|
| + // Apply clip-path to context.
|
| + GraphicsContextStateSaver clipStateSaver(*context, false);
|
| +
|
| + // Clip-path, like border radius, must not be applied to the contents of a composited-scrolling container.
|
| + // It must, however, still be applied to the mask layer, so that the compositor can properly mask the
|
| + // scrolling contents and scrollbars.
|
| + // TODO(ojan): This style null check doesn't make sense. We should always have a style.
|
| + if (hasClipPath() && style()) {
|
| + ASSERT(style()->clipPath());
|
| + if (style()->clipPath()->type() == ClipPathOperation::SHAPE) {
|
| + ShapeClipPathOperation* clipPath = toShapeClipPathOperation(style()->clipPath());
|
| + if (clipPath->isValid()) {
|
| + clipStateSaver.save();
|
| +
|
| + if (!rootRelativeBoundsComputed) {
|
| + rootRelativeBounds = layer()->physicalBoundingBoxIncludingReflectionAndStackingChildren(paintingInfo.rootLayer, offsetFromRoot);
|
| + rootRelativeBoundsComputed = true;
|
| + }
|
| +
|
| + context->clipPath(clipPath->path(rootRelativeBounds), clipPath->windRule());
|
| + }
|
| + }
|
| + }
|
| +
|
| + LayerPaintingInfo localPaintingInfo(paintingInfo);
|
| + FilterEffectRendererHelper filterPainter(layer()->filterRenderer() && layer()->paintsWithFilters());
|
| +
|
| + LayoutRect layerBounds;
|
| + // FIXME(sky): Remove foregroundRect. It's unused.
|
| + ClipRect contentRect, foregroundRect;
|
| + ClipRectsContext clipRectsContext(localPaintingInfo.rootLayer, PaintingClipRects, localPaintingInfo.subPixelAccumulation);
|
| + layer()->clipper().calculateRects(clipRectsContext, localPaintingInfo.paintDirtyRect,
|
| + layerBounds, contentRect, foregroundRect,
|
| + &offsetFromRoot);
|
| +
|
| + bool shouldPaintContent = layer()->intersectsDamageRect(layerBounds, contentRect.rect(), localPaintingInfo.rootLayer, &offsetFromRoot);
|
| +
|
| + bool haveTransparency = layer()->isTransparent();
|
| +
|
| + if (filterPainter.haveFilterEffect()) {
|
| + ASSERT(layer()->filterInfo());
|
| +
|
| + if (!rootRelativeBoundsComputed)
|
| + rootRelativeBounds = layer()->physicalBoundingBoxIncludingReflectionAndStackingChildren(paintingInfo.rootLayer, offsetFromRoot);
|
| +
|
| + if (filterPainter.prepareFilterEffect(layer(), rootRelativeBounds, paintingInfo.paintDirtyRect)) {
|
| + // Rewire the old context to a memory buffer, so that we can capture the contents of the layer.
|
| + // NOTE: We saved the old context in the "transparencyLayerContext" local variable, to be able to start a transparency layer
|
| + // on the original context and avoid duplicating "beginFilterEffect" after each transparency layer call. Also, note that
|
| + // beginTransparencyLayers will only create a single lazy transparency layer, even though it is called twice in this method.
|
| + // With deferred filters, we don't need a separate context, but we do need to do transparency and clipping before starting
|
| + // filter processing.
|
| + // FIXME: when the legacy path is removed, remove the transparencyLayerContext as well.
|
| + if (haveTransparency) {
|
| + // If we have a filter and transparency, we have to eagerly start a transparency layer here, rather than risk a child layer lazily starts one after filter processing.
|
| + layer()->beginTransparencyLayers(context, localPaintingInfo.rootLayer, paintingInfo.paintDirtyRect, paintingInfo.subPixelAccumulation);
|
| + }
|
| + // We'll handle clipping to the dirty rect before filter rasterization.
|
| + // Filter processing will automatically expand the clip rect and the offscreen to accommodate any filter outsets.
|
| + // FIXME: It is incorrect to just clip to the damageRect here once multiple fragments are involved.
|
| + layer()->clipToRect(localPaintingInfo, context, contentRect);
|
| + // Subsequent code should not clip to the dirty rect, since we've already
|
| + // done it above, and doing it later will defeat the outsets.
|
| + localPaintingInfo.clipToDirtyRect = false;
|
| +
|
| + context = filterPainter.beginFilterEffect(context);
|
| + }
|
| + }
|
| +
|
| + LayoutPoint layerLocation = toPoint(layerBounds.location() - location() + localPaintingInfo.subPixelAccumulation);
|
| +
|
| + if (shouldPaintContent) {
|
| + bool contentRectIsEmpty = contentRect.isEmpty();
|
| +
|
| + // Begin transparency if we have something to paint.
|
| + if (haveTransparency && !contentRectIsEmpty)
|
| + layer()->beginTransparencyLayers(transparencyLayerContext, localPaintingInfo.rootLayer, paintingInfo.paintDirtyRect, localPaintingInfo.subPixelAccumulation);
|
| +
|
| + // Optimize clipping for the single fragment case.
|
| + bool shouldClip = localPaintingInfo.clipToDirtyRect && !contentRectIsEmpty;
|
| + if (shouldClip)
|
| + layer()->clipToRect(localPaintingInfo, context, contentRect);
|
| +
|
| + // TODO(ojan): We probably should have already set shouldPaintContent to false if the rect is empty.
|
| + if (!contentRectIsEmpty) {
|
| + Vector<RenderBox*> layers;
|
| +
|
| + PaintInfo paintInfo(context, pixelSnappedIntRect(contentRect.rect()), localPaintingInfo.rootLayer->renderer());
|
| + paint(paintInfo, layerLocation, layers);
|
| +
|
| + std::stable_sort(layers.begin(), layers.end(), compareZIndex);
|
| + for (auto& box : layers) {
|
| + box->paintLayer(context, paintingInfo.rootLayer, rect);
|
| + }
|
| + }
|
| +
|
| + if (shouldClip)
|
| + layer()->restoreClip(context, localPaintingInfo.paintDirtyRect, contentRect);
|
| + }
|
| +
|
| + if (filterPainter.hasStartedFilterEffect()) {
|
| + context = filterPainter.applyFilterEffect();
|
| + layer()->restoreClip(transparencyLayerContext, localPaintingInfo.paintDirtyRect, contentRect);
|
| + }
|
| +
|
| + // Make sure that we now use the original transparency context.
|
| + ASSERT(transparencyLayerContext == context);
|
| +
|
| + // End our transparency layer
|
| + if (haveTransparency && layer()->usedTransparency()) {
|
| + context->endLayer();
|
| + context->restore();
|
| + layer()->clearUsedTransparency();
|
| + }
|
| +}
|
| +
|
| +void RenderBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector<RenderBox*>& layers)
|
| {
|
| LayoutPoint adjustedPaintOffset = paintOffset + location();
|
| for (RenderObject* child = slowFirstChild(); child; child = child->nextSibling())
|
| - child->paint(paintInfo, adjustedPaintOffset);
|
| + child->paint(paintInfo, adjustedPaintOffset, layers);
|
| }
|
|
|
| void RenderBox::paintRootBoxFillLayers(const PaintInfo& paintInfo)
|
|
|