FilterPainter::FilterPainter(DeprecatedPaintLayer& layer, GraphicsContext* context, const LayoutPoint& offsetFromRoot, const ClipRect& clipRect, DeprecatedPaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed) : m_filterInProgress(false) , m_context(context) , m_layoutObject(layer.layoutObject()) { if (!layer.filterEffectBuilder() || !layer.paintsWithFilters()) return; ASSERT(layer.filterInfo()); SkiaImageFilterBuilder builder; RefPtrWillBeRawPtr<FilterEffect> lastEffect = layer.filterEffectBuilder()->lastEffect(); lastEffect->determineFilterPrimitiveSubregion(MapRectForward); RefPtr<SkImageFilter> imageFilter = builder.build(lastEffect.get(), ColorSpaceDeviceRGB); if (!imageFilter) return; if (!rootRelativeBoundsComputed) { rootRelativeBounds = layer.physicalBoundingBoxIncludingReflectionAndStackingChildren(paintingInfo.rootLayer, offsetFromRoot); rootRelativeBoundsComputed = true; } // 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. // Subsequent code should not clip to the dirty rect, since we've already // done it above, and doing it later will defeat the outsets. paintingInfo.clipToDirtyRect = false; if (clipRect.rect() != paintingInfo.paintDirtyRect || clipRect.hasRadius()) { m_clipRecorder = adoptPtr(new LayerClipRecorder(*context, *layer.layoutObject(), DisplayItem::ClipLayerFilter, clipRect, &paintingInfo, LayoutPoint(), paintFlags)); } ASSERT(m_layoutObject); if (RuntimeEnabledFeatures::slimmingPaintEnabled()) { ASSERT(context->displayItemList()); if (!context->displayItemList()->displayItemConstructionIsDisabled()) { FilterOperations filterOperations(layer.computeFilterOperations(m_layoutObject->styleRef())); OwnPtr<WebFilterOperations> webFilterOperations = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations()); builder.buildFilterOperations(filterOperations, webFilterOperations.get()); // FIXME: It's possible to have empty WebFilterOperations here even // though the SkImageFilter produced above is non-null, since the // layer's FilterEffectBuilder can have a stale representation of // the layer's filter. See crbug.com/502026. if (webFilterOperations->isEmpty()) return; context->displayItemList()->createAndAppend<BeginFilterDisplayItem>(*m_layoutObject, imageFilter, rootRelativeBounds, webFilterOperations.release()); } } else { BeginFilterDisplayItem filterDisplayItem(*m_layoutObject, imageFilter, rootRelativeBounds); filterDisplayItem.replay(*context); } m_filterInProgress = true; }
void DeprecatedPaintLayerPainter::paintChildren(unsigned childrenToVisit, GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { if (!m_paintLayer.hasSelfPaintingLayerDescendant()) return; #if ENABLE(ASSERT) LayerListMutationDetector mutationChecker(m_paintLayer.stackingNode()); #endif IntSize scrollOffsetAccumulation = paintingInfo.scrollOffsetAccumulation; if (m_paintLayer.layoutObject()->hasOverflowClip()) scrollOffsetAccumulation += m_paintLayer.layoutBox()->scrolledContentOffset(); DeprecatedPaintLayerStackingNodeIterator iterator(*m_paintLayer.stackingNode(), childrenToVisit); while (DeprecatedPaintLayerStackingNode* child = iterator.next()) { DeprecatedPaintLayerPainter childPainter(*child->layer()); // If this Layer should paint into its own backing or a grouped backing, that will be done via CompositedDeprecatedPaintLayerMapping::paintContents() // and CompositedDeprecatedPaintLayerMapping::doPaintTask(). if (!childPainter.shouldPaintLayerInSoftwareMode(paintingInfo.globalPaintFlags(), paintFlags)) continue; DeprecatedPaintLayerPaintingInfo childPaintingInfo = paintingInfo; childPaintingInfo.scrollOffsetAccumulation = scrollOffsetAccumulation; // Rare case: accumulate scroll offset of non-stacking-context ancestors up to m_paintLayer. for (DeprecatedPaintLayer* parentLayer = child->layer()->parent(); parentLayer != &m_paintLayer; parentLayer = parentLayer->parent()) { if (parentLayer->layoutObject()->hasOverflowClip()) childPaintingInfo.scrollOffsetAccumulation += parentLayer->layoutBox()->scrolledContentOffset(); } childPainter.paintLayer(context, childPaintingInfo, paintFlags); } }
ClipPathHelper(GraphicsContext* context, const DeprecatedPaintLayer& paintLayer, const DeprecatedPaintLayerPaintingInfo& paintingInfo, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed, const LayoutPoint& offsetFromRoot, PaintLayerFlags paintFlags) : m_resourceClipper(0), m_paintLayer(paintLayer), m_context(context) { const ComputedStyle& style = paintLayer.layoutObject()->styleRef(); // 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. if (!paintLayer.layoutObject()->hasClipPath() || (paintLayer.needsCompositedScrolling() && !(paintFlags & PaintLayerPaintingChildClippingMaskPhase))) return; m_clipperState = SVGClipPainter::ClipperNotApplied; ASSERT(style.clipPath()); if (style.clipPath()->type() == ClipPathOperation::SHAPE) { ShapeClipPathOperation* clipPath = toShapeClipPathOperation(style.clipPath()); if (clipPath->isValid()) { if (!rootRelativeBoundsComputed) { rootRelativeBounds = paintLayer.physicalBoundingBoxIncludingReflectionAndStackingChildren(paintingInfo.rootLayer, offsetFromRoot); rootRelativeBoundsComputed = true; } m_clipPathRecorder.emplace(*context, *paintLayer.layoutObject(), clipPath->path(rootRelativeBounds)); } } else if (style.clipPath()->type() == ClipPathOperation::REFERENCE) { ReferenceClipPathOperation* referenceClipPathOperation = toReferenceClipPathOperation(style.clipPath()); Document& document = paintLayer.layoutObject()->document(); // FIXME: It doesn't work with forward or external SVG references (https://bugs.webkit.org/show_bug.cgi?id=90405) Element* element = document.getElementById(referenceClipPathOperation->fragment()); if (isSVGClipPathElement(element) && element->layoutObject()) { if (!rootRelativeBoundsComputed) { rootRelativeBounds = paintLayer.physicalBoundingBoxIncludingReflectionAndStackingChildren(paintingInfo.rootLayer, offsetFromRoot); rootRelativeBoundsComputed = true; } m_resourceClipper = toLayoutSVGResourceClipper(toLayoutSVGResourceContainer(element->layoutObject())); if (!SVGClipPainter(*m_resourceClipper).applyClippingToContext(*paintLayer.layoutObject(), rootRelativeBounds, paintingInfo.paintDirtyRect, context, m_clipperState)) { // No need to post-apply the clipper if this failed. m_resourceClipper = 0; } } } }
void CompositingInputsUpdater::updateRecursive(DeprecatedPaintLayer* layer, UpdateType updateType, AncestorInfo info) { if (!layer->childNeedsCompositingInputsUpdate() && updateType != ForceUpdate) return; m_geometryMap.pushMappingsToAncestor(layer, layer->parent()); if (layer->hasCompositedDeprecatedPaintLayerMapping()) info.enclosingCompositedLayer = layer; if (layer->needsCompositingInputsUpdate()) { if (info.enclosingCompositedLayer) info.enclosingCompositedLayer->compositedDeprecatedPaintLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree); updateType = ForceUpdate; } if (updateType == ForceUpdate) { DeprecatedPaintLayer::AncestorDependentCompositingInputs properties; if (!layer->isRootLayer()) { properties.clippedAbsoluteBoundingBox = enclosingIntRect(m_geometryMap.absoluteRect(layer->boundingBoxForCompositingOverlapTest())); // FIXME: Setting the absBounds to 1x1 instead of 0x0 makes very little sense, // but removing this code will make JSGameBench sad. // See https://codereview.chromium.org/13912020/ if (properties.clippedAbsoluteBoundingBox.isEmpty()) properties.clippedAbsoluteBoundingBox.setSize(IntSize(1, 1)); IntRect clipRect = pixelSnappedIntRect(layer->clipper().backgroundClipRect(ClipRectsContext(m_rootLayer, AbsoluteClipRects)).rect()); properties.clippedAbsoluteBoundingBox.intersect(clipRect); const DeprecatedPaintLayer* parent = layer->parent(); properties.opacityAncestor = parent->isTransparent() ? parent : parent->opacityAncestor(); properties.transformAncestor = parent->hasTransformRelatedProperty() ? parent : parent->transformAncestor(); properties.filterAncestor = parent->hasFilter() ? parent : parent->filterAncestor(); bool layerIsFixedPosition = layer->layoutObject()->style()->position() == FixedPosition; properties.nearestFixedPositionLayer = layerIsFixedPosition ? layer : parent->nearestFixedPositionLayer(); if (info.hasAncestorWithClipOrOverflowClip) { const DeprecatedPaintLayer* parentLayerOnClippingContainerChain = findParentLayerOnClippingContainerChain(layer); const bool parentHasClipOrOverflowClip = parentLayerOnClippingContainerChain->layoutObject()->hasClipOrOverflowClip(); properties.clippingContainer = parentHasClipOrOverflowClip ? parentLayerOnClippingContainerChain->layoutObject() : parentLayerOnClippingContainerChain->clippingContainer(); } if (info.lastScrollingAncestor) { const LayoutObject* containingBlock = layer->layoutObject()->containingBlock(); const DeprecatedPaintLayer* parentLayerOnContainingBlockChain = findParentLayerOnContainingBlockChain(containingBlock); properties.ancestorScrollingLayer = parentLayerOnContainingBlockChain->ancestorScrollingLayer(); if (parentLayerOnContainingBlockChain->scrollsOverflow()) properties.ancestorScrollingLayer = parentLayerOnContainingBlockChain; if (layer->layoutObject()->isOutOfFlowPositioned() && !layer->subtreeIsInvisible()) { const DeprecatedPaintLayer* clippingLayer = properties.clippingContainer ? properties.clippingContainer->enclosingLayer() : layer->compositor()->rootLayer(); if (hasClippedStackingAncestor(layer, clippingLayer)) properties.clipParent = clippingLayer; } if (layer->stackingNode()->isTreatedAsStackingContextForPainting() && properties.ancestorScrollingLayer && !info.ancestorStackingContext->layoutObject()->isDescendantOf(properties.ancestorScrollingLayer->layoutObject())) properties.scrollParent = properties.ancestorScrollingLayer; } } properties.hasAncestorWithClipPath = info.hasAncestorWithClipPath; layer->updateAncestorDependentCompositingInputs(properties); } if (layer->stackingNode()->isStackingContext()) info.ancestorStackingContext = layer; if (layer->scrollsOverflow()) info.lastScrollingAncestor = layer; if (layer->layoutObject()->hasClipOrOverflowClip()) info.hasAncestorWithClipOrOverflowClip = true; if (layer->layoutObject()->hasClipPath()) info.hasAncestorWithClipPath = true; DeprecatedPaintLayer::DescendantDependentCompositingInputs descendantProperties; for (DeprecatedPaintLayer* child = layer->firstChild(); child; child = child->nextSibling()) { updateRecursive(child, updateType, info); descendantProperties.hasDescendantWithClipPath |= child->hasDescendantWithClipPath() || child->layoutObject()->hasClipPath(); descendantProperties.hasNonIsolatedDescendantWithBlendMode |= (!child->stackingNode()->isStackingContext() && child->hasNonIsolatedDescendantWithBlendMode()) || child->layoutObject()->style()->hasBlendMode(); } layer->updateDescendantDependentCompositingInputs(descendantProperties); layer->didUpdateCompositingInputs(); m_geometryMap.popMappingsToAncestor(layer->parent()); }
void FramePainter::paintContents(GraphicsContext* context, const IntRect& rect) { Document* document = m_frameView.frame().document(); #ifndef NDEBUG bool fillWithRed; if (document->printing()) fillWithRed = false; // Printing, don't fill with red (can't remember why). else if (m_frameView.frame().owner()) fillWithRed = false; // Subframe, don't fill with red. else if (m_frameView.isTransparent()) fillWithRed = false; // Transparent, don't fill with red. else if (m_frameView.paintBehavior() & PaintBehaviorSelectionOnly) fillWithRed = false; // Selections are transparent, don't fill with red. else if (m_frameView.nodeToDraw()) fillWithRed = false; // Element images are transparent, don't fill with red. else fillWithRed = true; if (fillWithRed) { IntRect contentRect(IntPoint(), m_frameView.contentsSize()); DrawingRecorder drawingRecorder(*context, *m_frameView.layoutView(), DisplayItem::DebugRedFill, contentRect); if (!drawingRecorder.canUseCachedDrawing()) context->fillRect(contentRect, Color(0xFF, 0, 0)); } #endif LayoutView* layoutView = m_frameView.layoutView(); if (!layoutView) { WTF_LOG_ERROR("called FramePainter::paint with nil renderer"); return; } RELEASE_ASSERT(!m_frameView.needsLayout()); ASSERT(document->lifecycle().state() >= DocumentLifecycle::CompositingClean); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "Paint", "data", InspectorPaintEvent::data(layoutView, LayoutRect(rect), 0)); bool isTopLevelPainter = !s_inPaintContents; s_inPaintContents = true; FontCachePurgePreventer fontCachePurgePreventer; PaintBehavior oldPaintBehavior = m_frameView.paintBehavior(); if (FrameView* parentView = m_frameView.parentFrameView()) { if (parentView->paintBehavior() & PaintBehaviorFlattenCompositingLayers) m_frameView.setPaintBehavior(m_frameView.paintBehavior() | PaintBehaviorFlattenCompositingLayers); } if (m_frameView.paintBehavior() == PaintBehaviorNormal) document->markers().invalidateRenderedRectsForMarkersInRect(LayoutRect(rect)); if (document->printing()) m_frameView.setPaintBehavior(m_frameView.paintBehavior() | PaintBehaviorFlattenCompositingLayers); ASSERT(!m_frameView.isPainting()); m_frameView.setIsPainting(true); // m_frameView.nodeToDraw() is used to draw only one element (and its descendants) LayoutObject* renderer = m_frameView.nodeToDraw() ? m_frameView.nodeToDraw()->layoutObject() : 0; DeprecatedPaintLayer* rootLayer = layoutView->layer(); #if ENABLE(ASSERT) layoutView->assertSubtreeIsLaidOut(); LayoutObject::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(*rootLayer->layoutObject()); #endif DeprecatedPaintLayerPainter layerPainter(*rootLayer); float deviceScaleFactor = blink::deviceScaleFactor(rootLayer->layoutObject()->frame()); context->setDeviceScaleFactor(deviceScaleFactor); layerPainter.paint(context, LayoutRect(rect), m_frameView.paintBehavior(), renderer); if (rootLayer->containsDirtyOverlayScrollbars()) layerPainter.paintOverlayScrollbars(context, LayoutRect(rect), m_frameView.paintBehavior(), renderer); m_frameView.setIsPainting(false); m_frameView.setPaintBehavior(oldPaintBehavior); m_frameView.setLastPaintTime(currentTime()); // Regions may have changed as a result of the visibility/z-index of element changing. if (document->annotatedRegionsDirty()) m_frameView.updateAnnotatedRegions(); if (isTopLevelPainter) { // Everything that happens after paintContents completions is considered // to be part of the next frame. memoryCache()->updateFramePaintTimestamp(); s_inPaintContents = false; } InspectorInstrumentation::didPaint(layoutView, 0, context, LayoutRect(rect)); }
void DeprecatedPaintLayerPainter::paintLayerWithTransform(GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { TransformationMatrix layerTransform = m_paintLayer.renderableTransform(paintingInfo.globalPaintFlags()); // If the transform can't be inverted, then don't paint anything. if (!layerTransform.isInvertible()) return; // FIXME: We should make sure that we don't walk past paintingInfo.rootLayer here. // m_paintLayer may be the "root", and then we should avoid looking at its parent. DeprecatedPaintLayer* parentLayer = m_paintLayer.parent(); ClipRect ancestorBackgroundClipRect; if (parentLayer) { // Calculate the clip rectangle that the ancestors establish. ClipRectsContext clipRectsContext(paintingInfo.rootLayer, (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize); if (shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()) == IgnoreOverflowClip) clipRectsContext.setIgnoreOverflowClip(); ancestorBackgroundClipRect = m_paintLayer.clipper().backgroundClipRect(clipRectsContext); } DeprecatedPaintLayer* paginationLayer = m_paintLayer.enclosingPaginationLayer(); DeprecatedPaintLayerFragments fragments; if (paginationLayer) { // FIXME: This is a mess. Look closely at this code and the code in Layer and fix any // issues in it & refactor to make it obvious from code structure what it does and that it's // correct. ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects; ShouldRespectOverflowClip respectOverflowClip = shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()); // Calculate the transformed bounding box in the current coordinate space, to figure out // which fragmentainers (e.g. columns) we need to visit. LayoutRect transformedExtent = DeprecatedPaintLayer::transparencyClipBox(&m_paintLayer, paginationLayer, DeprecatedPaintLayer::PaintingTransparencyClipBox, DeprecatedPaintLayer::RootOfTransparencyClipBox, paintingInfo.globalPaintFlags()); // FIXME: we don't check if paginationLayer is within paintingInfo.rootLayer here. paginationLayer->collectFragments(fragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, 0, paintingInfo.subPixelAccumulation, &transformedExtent); } else { // We don't need to collect any fragments in the regular way here. We have already // calculated a clip rectangle for the ancestry if it was needed, and clipping this // layer is something that can be done further down the path, when the transform has // been applied. DeprecatedPaintLayerFragment fragment; fragment.backgroundRect = paintingInfo.paintDirtyRect; fragments.append(fragment); } bool needsScope = fragments.size() > 1; for (const auto& fragment : fragments) { Optional<ScopeRecorder> scopeRecorder; if (needsScope) scopeRecorder.emplace(*context); Optional<LayerClipRecorder> clipRecorder; if (parentLayer) { ClipRect clipRectForFragment(ancestorBackgroundClipRect); clipRectForFragment.moveBy(fragment.paginationOffset); clipRectForFragment.intersect(fragment.backgroundRect); if (clipRectForFragment.isEmpty()) continue; if (needsToClip(paintingInfo, clipRectForFragment)) clipRecorder.emplace(*context, *parentLayer->layoutObject(), DisplayItem::ClipLayerParent, clipRectForFragment, &paintingInfo, fragment.paginationOffset, paintFlags); } paintFragmentByApplyingTransform(context, paintingInfo, paintFlags, fragment.paginationOffset); } }