void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutPoint adjustedPaintOffset = paintOffset + location(); Widget* widget = this->widget(); RELEASE_ASSERT(widget); // Tell the widget to paint now. This is the only time the widget is allowed // to paint itself. That way it will composite properly with z-indexed layers. IntPoint widgetLocation = widget->frameRect().location(); IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()), roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop())); IntRect paintRect = paintInfo.rect; IntSize widgetPaintOffset = paintLocation - widgetLocation; // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. if (!widgetPaintOffset.isZero()) { paintInfo.context->translate(widgetPaintOffset.width(), widgetPaintOffset.height()); paintRect.move(-widgetPaintOffset); } widget->paint(paintInfo.context, paintRect); if (!widgetPaintOffset.isZero()) paintInfo.context->translate(-widgetPaintOffset.width(), -widgetPaintOffset.height()); }
void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { IntPoint contentPaintOffset = roundedIntPoint(paintOffset + location() + contentBoxRect().location()); // Tell the widget to paint now. This is the only time the widget is allowed // to paint itself. That way it will composite properly with z-indexed layers. LayoutRect paintRect = paintInfo.rect; IntPoint widgetLocation = m_widget->frameRect().location(); IntSize widgetPaintOffset = contentPaintOffset - widgetLocation; // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. if (!widgetPaintOffset.isZero()) { paintInfo.context().translate(widgetPaintOffset); paintRect.move(-widgetPaintOffset); } // FIXME: Remove repaintrect encolsing/integral snapping when RenderWidget becomes device pixel snapped. m_widget->paint(paintInfo.context(), snappedIntRect(paintRect)); if (!widgetPaintOffset.isZero()) paintInfo.context().translate(-widgetPaintOffset); if (is<FrameView>(*m_widget)) { FrameView& frameView = downcast<FrameView>(*m_widget); bool runOverlapTests = !frameView.useSlowRepaintsIfNotOverlapped(); if (paintInfo.overlapTestRequests && runOverlapTests) { ASSERT(!paintInfo.overlapTestRequests->contains(this)); paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); } } }
void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutPoint adjustedPaintOffset = paintOffset + location(); // Tell the widget to paint now. This is the only time the widget is allowed // to paint itself. That way it will composite properly with z-indexed layers. IntPoint widgetLocation = m_widget->frameRect().location(); IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()), roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop())); IntRect paintRect = paintInfo.rect; IntSize widgetPaintOffset = paintLocation - widgetLocation; // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. if (!widgetPaintOffset.isZero()) { paintInfo.context->translate(widgetPaintOffset); paintRect.move(-widgetPaintOffset); } m_widget->paint(paintInfo.context, paintRect); if (!widgetPaintOffset.isZero()) paintInfo.context->translate(-widgetPaintOffset); if (m_widget->isFrameView()) { FrameView* frameView = toFrameView(m_widget.get()); bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants(); if (paintInfo.overlapTestRequests && runOverlapTests) { ASSERT(!paintInfo.overlapTestRequests->contains(this)); paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); } } }
void BackingStore::scroll(const IntRect& scrollRect, const IntSize& scrollOffset) { if (scrollOffset.isZero()) return; m_pixmap.scroll(scrollOffset.width(), scrollOffset.height(), scrollRect); }
void ScrollAnchor::adjust() { if (!m_queued) return; m_queued = false; DCHECK(m_scroller); if (!m_anchorObject) return; IntSize adjustment = computeAdjustment(); if (adjustment.isZero()) return; if (m_scrollAnchorDisablingStyleChanged) { // Note that we only clear if the adjustment would have been non-zero. // This minimizes redundant calls to findAnchor. // TODO(skobes): add UMA metric for this. clearSelf(); DEFINE_STATIC_LOCAL(EnumerationHistogram, suppressedBySanaclapHistogram, ("Layout.ScrollAnchor.SuppressedBySanaclap", 2)); suppressedBySanaclapHistogram.count(1); return; } m_scroller->setScrollOffset( m_scroller->getScrollOffset() + FloatSize(adjustment), AnchoringScroll); // Update UMA metric. DEFINE_STATIC_LOCAL(EnumerationHistogram, adjustedOffsetHistogram, ("Layout.ScrollAnchor.AdjustedScrollOffset", 2)); adjustedOffsetHistogram.count(1); UseCounter::count(scrollerLayoutBox(m_scroller)->document(), UseCounter::ScrollAnchored); }
bool CCVideoLayerImpl::reserveTextures(const VideoFrameChromium* frame, GC3Denum format, LayerRendererChromium* layerRenderer) { if (frame->planes() > MaxPlanes) return false; int maxTextureSize = layerRenderer->capabilities().maxTextureSize; for (unsigned plane = 0; plane < frame->planes(); ++plane) { IntSize requiredTextureSize = frame->requiredTextureSize(plane); // If the renderer cannot handle this large of a texture, return false. // FIXME: Remove this test when tiled layers are implemented. if (requiredTextureSize.isZero() || requiredTextureSize.width() > maxTextureSize || requiredTextureSize.height() > maxTextureSize) return false; if (!m_textures[plane].m_texture) { m_textures[plane].m_texture = ManagedTexture::create(layerRenderer->renderSurfaceTextureManager()); if (!m_textures[plane].m_texture) return false; m_textures[plane].m_visibleSize = IntSize(); } else { // The renderSurfaceTextureManager may have been destroyed and recreated since the last frame, so pass the new one. // This is a no-op if the TextureManager is still around. m_textures[plane].m_texture->setTextureManager(layerRenderer->renderSurfaceTextureManager()); } if (m_textures[plane].m_texture->size() != requiredTextureSize) m_textures[plane].m_visibleSize = computeVisibleSize(frame, plane); if (!m_textures[plane].m_texture->reserve(requiredTextureSize, format)) return false; } return true; }
bool VideoLayerChromium::reserveTextures(const VideoFrameChromium* frame, GC3Denum textureFormat) { ASSERT(layerTreeHost()); ASSERT(frame); ASSERT(textureFormat != GraphicsContext3D::INVALID_VALUE); int maxTextureSize = layerTreeHost()->layerRendererCapabilities().maxTextureSize; for (unsigned plane = 0; plane < frame->planes(); plane++) { IntSize requiredTextureSize = frame->requiredTextureSize(plane); // If the renderer cannot handle this large of a texture, return false. // FIXME: Remove this test when tiled layers are implemented. if (requiredTextureSize.isZero() || requiredTextureSize.width() > maxTextureSize || requiredTextureSize.height() > maxTextureSize) return false; if (!m_textures[plane].m_texture) return false; if (m_textures[plane].m_texture->size() != requiredTextureSize) m_textures[plane].m_visibleSize = computeVisibleSize(frame, plane); m_textures[plane].m_texture->reserve(requiredTextureSize, textureFormat); } return true; }
void WebPage::gestureDidScroll(const IntSize& size) { ASSERT_ARG(size, !size.isZero()); if (!m_gestureTargetNode || !m_gestureTargetNode->renderer() || !m_gestureTargetNode->renderer()->enclosingLayer()) return; Scrollbar* verticalScrollbar = 0; if (Frame* frame = m_page->mainFrame()) { if (ScrollView* view = frame->view()) verticalScrollbar = view->verticalScrollbar(); } m_gestureTargetNode->renderer()->enclosingLayer()->scrollByRecursively(size); bool gestureReachedScrollingLimit = verticalScrollbar && scrollbarAtTopOrBottomOfDocument(verticalScrollbar); // FIXME: We really only want to update this state if the state was updated via scrolling the main frame, // not scrolling something in a main frame when the main frame had already reached its scrolling limit. if (gestureReachedScrollingLimit == m_gestureReachedScrollingLimit) return; send(Messages::WebPageProxy::SetGestureReachedScrollingLimit(gestureReachedScrollingLimit)); m_gestureReachedScrollingLimit = gestureReachedScrollingLimit; }
void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, SkXfermode::Mode compositeOp, const FloatRect& destRect, const IntSize& repeatSpacing) { TRACE_EVENT0("skia", "Image::drawPattern"); SkBitmap bitmap; if (!bitmapForCurrentFrame(&bitmap)) return; FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, bitmap.width(), bitmap.height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix localMatrix; // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the pattern. If WebKit wants // a shifted image, it will shift it from there using the localMatrix. const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. localMatrix.preScale(scale.width(), scale.height()); SkBitmap bitmapToPaint; bitmap.extractSubset(&bitmapToPaint, enclosingIntRect(normSrcRect)); if (!repeatSpacing.isZero()) { SkScalar ctmScaleX = 1.0; SkScalar ctmScaleY = 1.0; if (!RuntimeEnabledFeatures::slimmingPaintEnabled()) { AffineTransform ctm = context->getCTM(); ctmScaleX = ctm.xScale(); ctmScaleY = ctm.yScale(); } bitmapToPaint = createBitmapWithSpace( bitmapToPaint, repeatSpacing.width() * ctmScaleX / scale.width(), repeatSpacing.height() * ctmScaleY / scale.height()); } RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(bitmapToPaint, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap); { SkPaint paint; int initialSaveCount = context->preparePaintForDrawRectToRect(&paint, floatSrcRect, destRect, compositeOp, !bitmap.isOpaque(), isLazyDecoded, bitmap.isImmutable()); paint.setShader(shader.get()); context->drawRect(destRect, paint); context->canvas()->restoreToCount(initialSaveCount); } if (isLazyDecoded) PlatformInstrumentation::didDrawLazyPixelRef(bitmap.getGenerationID()); }
void GraphicsLayerTextureMapper::didCommitScrollOffset(const IntSize& offset) { if (offset.isZero()) return; m_committedScrollOffset = offset; notifyChange(CommittedScrollOffsetChange); }
void BackingStore::scroll(const IntRect& scrollRect, const IntSize& scrollOffset) { if (scrollOffset.isZero()) return; ASSERT(m_backingStore); m_backingStore->scroll(scrollRect, scrollOffset); }
void DrawingAreaProxy::setSize(const IntSize& size, const IntSize& scrollOffset) { if (m_size == size && scrollOffset.isZero()) return; m_size = size; m_scrollOffset += scrollOffset; sizeDidChange(); }
void CoordinatedGraphicsLayer::commitScrollOffset(const IntSize& offset) { if (!isScrollable() || offset.isZero()) return; m_scrollableArea->notifyScrollPositionChanged(m_scrollableArea->scrollPosition() + offset); m_layerState.committedScrollOffset += offset; m_layerState.committedScrollOffsetChanged = true; didChangeLayerState(); }
void MediaDocument::mediaElementNaturalSizeChanged(const IntSize& newSize) { if (ownerElement()) return; if (newSize.isZero()) return; if (page()) page()->chrome().client().imageOrMediaDocumentSizeChanged(newSize); }
void AutoscrollController::updateDragAndDrop(Node* dropTargetNode, const IntPoint& eventPosition, double eventTime) { if (!dropTargetNode || !dropTargetNode->layoutObject()) { stopAutoscroll(); return; } if (m_autoscrollLayoutObject && m_autoscrollLayoutObject->frame() != dropTargetNode->layoutObject()->frame()) return; dropTargetNode->layoutObject() ->frameView() ->updateAllLifecyclePhasesExceptPaint(); LayoutBox* scrollable = LayoutBox::findAutoscrollable(dropTargetNode->layoutObject()); if (!scrollable) { stopAutoscroll(); return; } Page* page = scrollable->frame() ? scrollable->frame()->page() : nullptr; if (!page) { stopAutoscroll(); return; } IntSize offset = scrollable->calculateAutoscrollDirection(eventPosition); if (offset.isZero()) { stopAutoscroll(); return; } m_dragAndDropAutoscrollReferencePosition = eventPosition + offset; if (m_autoscrollType == NoAutoscroll) { m_autoscrollType = AutoscrollForDragAndDrop; m_autoscrollLayoutObject = scrollable; m_dragAndDropAutoscrollStartTime = eventTime; UseCounter::count(m_page->mainFrame(), UseCounter::DragAndDropScrollStart); startAutoscroll(); } else if (m_autoscrollLayoutObject != scrollable) { m_dragAndDropAutoscrollStartTime = eventTime; m_autoscrollLayoutObject = scrollable; } }
void PaintPropertyTreeBuilder::updateScrollAndScrollTranslation( const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (object.hasOverflowClip()) { const LayoutBox& box = toLayoutBox(object); const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea(); IntSize scrollOffset = box.scrolledContentOffset(); if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) { TransformationMatrix matrix = TransformationMatrix().translate( -scrollOffset.width(), -scrollOffset.height()); object.getMutableForPainting() .ensurePaintProperties() .updateScrollTranslation( context.current.transform, matrix, FloatPoint3D(), context.current.shouldFlattenInheritedTransform, context.current.renderingContextID); IntSize scrollClip = scrollableArea->visibleContentRect().size(); IntSize scrollBounds = scrollableArea->contentsSize(); bool userScrollableHorizontal = scrollableArea->userInputScrollable(HorizontalScrollbar); bool userScrollableVertical = scrollableArea->userInputScrollable(VerticalScrollbar); object.getMutableForPainting().ensurePaintProperties().updateScroll( context.current.scroll, object.paintProperties()->scrollTranslation(), scrollClip, scrollBounds, userScrollableHorizontal, userScrollableVertical); } else { // Ensure pre-existing properties are cleared when there is no // scrolling. auto* properties = object.getMutableForPainting().paintProperties(); if (properties) { properties->clearScrollTranslation(); properties->clearScroll(); } } } if (object.paintProperties() && object.paintProperties()->scroll()) { context.current.transform = object.paintProperties()->scrollTranslation(); const auto* scroll = object.paintProperties()->scroll(); // TODO(pdr): Remove this const cast. context.current.scroll = const_cast<ScrollPaintPropertyNode*>(scroll); context.current.shouldFlattenInheritedTransform = false; } }
void CCLayerTreeHost::applyScrollAndScale(const CCScrollAndScaleSet& info) { if (!m_rootLayer) return; LayerChromium* rootScrollLayer = findFirstScrollableLayer(m_rootLayer.get()); IntSize rootScrollDelta; for (size_t i = 0; i < info.scrolls.size(); ++i) { LayerChromium* layer = CCLayerTreeHostCommon::findLayerInSubtree(m_rootLayer.get(), info.scrolls[i].layerId); if (!layer) continue; if (layer == rootScrollLayer) rootScrollDelta += info.scrolls[i].scrollDelta; else layer->scrollBy(info.scrolls[i].scrollDelta); } if (!rootScrollDelta.isZero() || info.pageScaleDelta != 1) m_client->applyScrollAndScale(rootScrollDelta, info.pageScaleDelta); }
void AutoscrollController::updateDragAndDrop(Node* dropTargetNode, const IntPoint& eventPosition, double eventTime) { if (!dropTargetNode || !dropTargetNode->renderer()) { stopAutoscroll(); return; } if (m_autoscrollRenderer && m_autoscrollRenderer->frame() != dropTargetNode->renderer()->frame()) return; RenderBox* scrollable = RenderBox::findAutoscrollable(dropTargetNode->renderer()); if (!scrollable) { stopAutoscroll(); return; } Page* page = scrollable->frame() ? scrollable->frame()->page() : 0; if (!page) { stopAutoscroll(); return; } IntSize offset = scrollable->calculateAutoscrollDirection(eventPosition); if (offset.isZero()) { stopAutoscroll(); return; } m_dragAndDropAutoscrollReferencePosition = eventPosition + offset; if (m_autoscrollType == NoAutoscroll) { m_autoscrollType = AutoscrollForDragAndDrop; m_autoscrollRenderer = scrollable; m_dragAndDropAutoscrollStartTime = eventTime; startAutoscroll(); } else if (m_autoscrollRenderer != scrollable) { m_dragAndDropAutoscrollStartTime = eventTime; m_autoscrollRenderer = scrollable; } }
void PaintPropertyTreeBuilder::updateScrollAndScrollTranslation( const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (object.hasOverflowClip()) { const LayoutBox& box = toLayoutBox(object); const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea(); IntSize scrollOffset = box.scrolledContentOffset(); if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) { TransformationMatrix matrix = TransformationMatrix().translate( -scrollOffset.width(), -scrollOffset.height()); context.current.transform = object.getMutableForPainting() .ensurePaintProperties() .updateScrollTranslation( context.current.transform, matrix, FloatPoint3D(), context.current.shouldFlattenInheritedTransform, context.current.renderingContextID); IntSize scrollClip = scrollableArea->visibleContentRect().size(); IntSize scrollBounds = scrollableArea->contentsSize(); bool userScrollableHorizontal = scrollableArea->userInputScrollable(HorizontalScrollbar); bool userScrollableVertical = scrollableArea->userInputScrollable(VerticalScrollbar); context.current.scroll = object.getMutableForPainting().ensurePaintProperties().updateScroll( context.current.scroll, context.current.transform, scrollClip, scrollBounds, userScrollableHorizontal, userScrollableVertical); context.current.shouldFlattenInheritedTransform = false; return; } } if (auto* properties = object.getMutableForPainting().paintProperties()) { properties->clearScrollTranslation(); properties->clearScroll(); } }
bool VideoLayerChromium::allocateTexturesIfNeeded(GraphicsContext3D* context, VideoFrameChromium* frame, unsigned textureFormat) { ASSERT(context); ASSERT(frame); for (unsigned plane = 0; plane < frame->planes(); plane++) { IntSize planeTextureSize = frame->requiredTextureSize(plane); // If the renderer cannot handle this large of a texture, return false. // FIXME: Remove this test when tiled layers are implemented. if (!layerRenderer()->checkTextureSize(planeTextureSize)) return false; if (!m_textures[plane]) m_textures[plane] = layerRenderer()->createLayerTexture(); if (!planeTextureSize.isZero() && planeTextureSize != m_textureSizes[plane]) { allocateTexture(context, m_textures[plane], planeTextureSize, textureFormat); m_textureSizes[plane] = planeTextureSize; m_frameSizes[plane] = IntSize(frame->width(), frame->height()); } } return true; }
void BlockPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (RuntimeEnabledFeatures::slimmingPaintOffsetCachingEnabled() && m_layoutBlock.childrenInline() && !paintInfo.context.paintController().skippingCache()) { if (m_layoutBlock.paintOffsetChanged(paintOffset)) { LineBoxListPainter(m_layoutBlock.lineBoxes()).invalidateLineBoxPaintOffsets(paintInfo); paintInfo.context.paintController().invalidatePaintOffset(m_layoutBlock); } // Set previousPaintOffset here in case that m_layoutBlock paints nothing and no // LayoutObjectDrawingRecorder updates its previousPaintOffset. // TODO(wangxianzhu): Integrate paint offset checking into new paint invalidation. m_layoutBlock.mutableForPainting().setPreviousPaintOffset(paintOffset); } const PaintPhase paintPhase = paintInfo.phase; if ((paintPhase == PaintPhaseSelfBlockBackground || paintPhase == PaintPhaseBlockBackground) && m_layoutBlock.style()->visibility() == VISIBLE && m_layoutBlock.hasBoxDecorationBackground()) m_layoutBlock.paintBoxDecorationBackground(paintInfo, paintOffset); if (paintPhase == PaintPhaseMask && m_layoutBlock.style()->visibility() == VISIBLE) { m_layoutBlock.paintMask(paintInfo, paintOffset); return; } if (paintPhase == PaintPhaseClippingMask && m_layoutBlock.style()->visibility() == VISIBLE) { BoxPainter(m_layoutBlock).paintClippingMask(paintInfo, paintOffset); return; } // FIXME: When Skia supports annotation rect covering (https://code.google.com/p/skia/issues/detail?id=3872), // this rect may be covered by foreground and descendant drawings. Then we may need a dedicated paint phase. if (paintPhase == PaintPhaseForeground && paintInfo.isPrinting()) ObjectPainter(m_layoutBlock).addPDFURLRectIfNeeded(paintInfo, paintOffset); { Optional<ScrollRecorder> scrollRecorder; Optional<PaintInfo> scrolledPaintInfo; if (m_layoutBlock.hasOverflowClip()) { IntSize scrollOffset = m_layoutBlock.scrolledContentOffset(); if (m_layoutBlock.layer()->scrollsOverflow() || !scrollOffset.isZero()) { scrollRecorder.emplace(paintInfo.context, m_layoutBlock, paintPhase, scrollOffset); scrolledPaintInfo.emplace(paintInfo); AffineTransform transform; transform.translate(-scrollOffset.width(), -scrollOffset.height()); scrolledPaintInfo->updateCullRect(transform); } } // We're done. We don't bother painting any children. if (paintPhase == PaintPhaseSelfBlockBackground || paintInfo.paintRootBackgroundOnly()) return; const PaintInfo& contentsPaintInfo = scrolledPaintInfo ? *scrolledPaintInfo : paintInfo; if (paintPhase != PaintPhaseSelfOutline) paintContents(contentsPaintInfo, paintOffset); if (paintPhase == PaintPhaseForeground && !paintInfo.isPrinting()) m_layoutBlock.paintSelection(contentsPaintInfo, paintOffset); // Fill in gaps in selection on lines and between blocks. if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) m_layoutBlock.paintFloats(contentsPaintInfo, paintOffset); } if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && m_layoutBlock.style()->hasOutline() && m_layoutBlock.style()->visibility() == VISIBLE) ObjectPainter(m_layoutBlock).paintOutline(paintInfo, paintOffset); // If the caret's node's layout object's containing block is this block, and the paint action is PaintPhaseForeground, // then paint the caret. if (paintPhase == PaintPhaseForeground && m_layoutBlock.hasCaret() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutBlock, DisplayItem::Caret, paintOffset)) { LayoutRect bounds = m_layoutBlock.visualOverflowRect(); bounds.moveBy(paintOffset); LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutBlock, DisplayItem::Caret, bounds, paintOffset); paintCarets(paintInfo, paintOffset); } }
void BlockPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { const PaintPhase paintPhase = paintInfo.phase; if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && m_layoutBlock.style()->visibility() == VISIBLE && m_layoutBlock.hasBoxDecorationBackground()) m_layoutBlock.paintBoxDecorationBackground(paintInfo, paintOffset); if (paintPhase == PaintPhaseMask && m_layoutBlock.style()->visibility() == VISIBLE) { m_layoutBlock.paintMask(paintInfo, paintOffset); return; } if (paintPhase == PaintPhaseClippingMask && m_layoutBlock.style()->visibility() == VISIBLE) { BoxPainter(m_layoutBlock).paintClippingMask(paintInfo, paintOffset); return; } // FIXME: When Skia supports annotation rect covering (https://code.google.com/p/skia/issues/detail?id=3872), // this rect may be covered by foreground and descendant drawings. Then we may need a dedicated paint phase. if (paintPhase == PaintPhaseForeground && paintInfo.isPrinting()) ObjectPainter(m_layoutBlock).addPDFURLRectIfNeeded(paintInfo, paintOffset); { Optional<ScrollRecorder> scrollRecorder; Optional<PaintInfo> scrolledPaintInfo; if (m_layoutBlock.hasOverflowClip()) { IntSize scrollOffset = m_layoutBlock.scrolledContentOffset(); if (m_layoutBlock.layer()->scrollsOverflow() || !scrollOffset.isZero()) { scrollRecorder.emplace(*paintInfo.context, m_layoutBlock, paintPhase, scrollOffset); scrolledPaintInfo.emplace(paintInfo); scrolledPaintInfo->rect.move(scrollOffset); } } // We're done. We don't bother painting any children. if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackgroundOnly()) return; const PaintInfo& contentsPaintInfo = scrolledPaintInfo ? *scrolledPaintInfo : paintInfo; if (paintPhase != PaintPhaseSelfOutline) paintContents(contentsPaintInfo, paintOffset); if (paintPhase == PaintPhaseForeground && !paintInfo.isPrinting()) m_layoutBlock.paintSelection(contentsPaintInfo, paintOffset); // Fill in gaps in selection on lines and between blocks. if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) m_layoutBlock.paintFloats(contentsPaintInfo, paintOffset, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); } if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && m_layoutBlock.style()->hasOutline() && m_layoutBlock.style()->visibility() == VISIBLE) { // Don't paint focus ring for anonymous block continuation because the // inline element having outline-style:auto paints the whole focus ring. if (!m_layoutBlock.style()->outlineStyleIsAuto() || !m_layoutBlock.isAnonymousBlockContinuation()) ObjectPainter(m_layoutBlock).paintOutline(paintInfo, LayoutRect(paintOffset, m_layoutBlock.size()), visualOverflowRectWithPaintOffset(m_layoutBlock, paintOffset)); } if (paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines) paintContinuationOutlines(paintInfo, paintOffset); // If the caret's node's layout object's containing block is this block, and the paint action is PaintPhaseForeground, // then paint the caret. if (paintPhase == PaintPhaseForeground && hasCaret() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutBlock, DisplayItem::Caret)) { LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutBlock, DisplayItem::Caret, visualOverflowRectWithPaintOffset(m_layoutBlock, paintOffset)); paintCarets(paintInfo, paintOffset); } }
void BlockPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { const PaintPhase paintPhase = paintInfo.phase; if (shouldPaintSelfBlockBackground(paintPhase)) { if (m_layoutBlock.style()->visibility() == EVisibility::Visible && m_layoutBlock.hasBoxDecorationBackground()) m_layoutBlock.paintBoxDecorationBackground(paintInfo, paintOffset); // We're done. We don't bother painting any children. if (paintPhase == PaintPhaseSelfBlockBackgroundOnly) return; } if (paintInfo.paintRootBackgroundOnly()) return; if (paintPhase == PaintPhaseMask && m_layoutBlock.style()->visibility() == EVisibility::Visible) { m_layoutBlock.paintMask(paintInfo, paintOffset); return; } if (paintPhase == PaintPhaseClippingMask && m_layoutBlock.style()->visibility() == EVisibility::Visible) { BoxPainter(m_layoutBlock).paintClippingMask(paintInfo, paintOffset); return; } if (paintPhase == PaintPhaseForeground && paintInfo.isPrinting()) ObjectPainter(m_layoutBlock).addPDFURLRectIfNeeded(paintInfo, paintOffset); if (paintPhase != PaintPhaseSelfOutlineOnly) { Optional<ScopedPaintChunkProperties> m_scopedScrollProperty; Optional<ScrollRecorder> scrollRecorder; Optional<PaintInfo> scrolledPaintInfo; if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { const auto* objectProperties = m_layoutBlock.paintProperties(); if (auto* scroll = objectProperties ? objectProperties->scroll() : nullptr) { PaintChunkProperties properties(paintInfo.context.getPaintController() .currentPaintChunkProperties()); auto* scrollTranslation = objectProperties->scrollTranslation(); DCHECK(scrollTranslation); properties.transform = scrollTranslation; properties.scroll = scroll; m_scopedScrollProperty.emplace( paintInfo.context.getPaintController(), m_layoutBlock, DisplayItem::paintPhaseToDrawingType(paintPhase), properties); scrolledPaintInfo.emplace(paintInfo); scrolledPaintInfo->updateCullRect( scrollTranslation->matrix().toAffineTransform()); } } else if (m_layoutBlock.hasOverflowClip()) { IntSize scrollOffset = m_layoutBlock.scrolledContentOffset(); if (m_layoutBlock.layer()->scrollsOverflow() || !scrollOffset.isZero()) { scrollRecorder.emplace(paintInfo.context, m_layoutBlock, paintPhase, scrollOffset); scrolledPaintInfo.emplace(paintInfo); AffineTransform transform; transform.translate(-scrollOffset.width(), -scrollOffset.height()); scrolledPaintInfo->updateCullRect(transform); } } const PaintInfo& contentsPaintInfo = scrolledPaintInfo ? *scrolledPaintInfo : paintInfo; if (m_layoutBlock.isLayoutBlockFlow()) { BlockFlowPainter blockFlowPainter(toLayoutBlockFlow(m_layoutBlock)); blockFlowPainter.paintContents(contentsPaintInfo, paintOffset); if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) blockFlowPainter.paintFloats(contentsPaintInfo, paintOffset); } else { paintContents(contentsPaintInfo, paintOffset); } } if (shouldPaintSelfOutline(paintPhase)) ObjectPainter(m_layoutBlock).paintOutline(paintInfo, paintOffset); // If the caret's node's layout object's containing block is this block, and // the paint action is PaintPhaseForeground, then paint the caret. if (paintPhase == PaintPhaseForeground && m_layoutBlock.hasCaret()) paintCarets(paintInfo, paintOffset); }
void NativeImageSkia::drawPattern( GraphicsContext* context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect, WebBlendMode blendMode, const IntSize& repeatSpacing) const { FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix totalMatrix = context->getTotalMatrix(); AffineTransform ctm = context->getCTM(); SkScalar ctmScaleX = ctm.xScale(); SkScalar ctmScaleY = ctm.yScale(); totalMatrix.preScale(scale.width(), scale.height()); // Figure out what size the bitmap will be in the destination. The // destination rect is the bounds of the pattern, we need to use the // matrix to see how big it will be. SkRect destRectTarget; totalMatrix.mapRect(&destRectTarget, normSrcRect); float destBitmapWidth = SkScalarToFloat(destRectTarget.width()); float destBitmapHeight = SkScalarToFloat(destRectTarget.height()); bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); // Compute the resampling mode. InterpolationQuality resampling; if (context->isAccelerated()) resampling = InterpolationLow; else if (isLazyDecoded) resampling = InterpolationHigh; else resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete()); resampling = limitInterpolationQuality(context, resampling); SkMatrix localMatrix; // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the pattern. If WebKit wants // a shifted image, it will shift it from there using the localMatrix. const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); RefPtr<SkShader> shader; SkFilterQuality filterLevel = static_cast<SkFilterQuality>(resampling); // Bicubic filter is only applied to defer-decoded images, see // NativeImageSkia::draw for details. if (resampling == InterpolationHigh && !isLazyDecoded) { // Do nice resampling. filterLevel = kNone_SkFilterQuality; float scaleX = destBitmapWidth / normSrcRect.width(); float scaleY = destBitmapHeight / normSrcRect.height(); SkRect scaledSrcRect; // Since we are resizing the bitmap, we need to remove the scale // applied to the pixels in the bitmap shader. This means we need // CTM * localMatrix to have identity scale. Since we // can't modify CTM (or the rectangle will be drawn in the wrong // place), we must set localMatrix's scale to the inverse of // CTM scale. localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1); // The image fragment generated here is not exactly what is // requested. The scale factor used is approximated and image // fragment is slightly larger to align to integer // boundaries. SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect); if (repeatSpacing.isZero()) { shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); } else { shader = adoptRef(SkShader::CreateBitmapShader( createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); } } else { // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. localMatrix.preScale(scale.width(), scale.height()); // No need to resample before drawing. SkBitmap srcSubset; bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect)); if (repeatSpacing.isZero()) { shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); } else { shader = adoptRef(SkShader::CreateBitmapShader( createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); } } SkPaint paint; paint.setShader(shader.get()); paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode)); paint.setColorFilter(context->colorFilter()); paint.setFilterQuality(filterLevel); context->drawRect(destRect, paint); }
void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) { if (!shouldPaint(paintInfo, tx, ty)) return; tx += x(); ty += y(); if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) paintBoxDecorations(paintInfo, tx, ty); if (paintInfo.phase == PaintPhaseMask) { paintMask(paintInfo, tx, ty); return; } if (!m_frameView || paintInfo.phase != PaintPhaseForeground || style()->visibility() != VISIBLE) return; #if PLATFORM(MAC) if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true); #endif if (style()->hasBorderRadius()) { IntRect borderRect = IntRect(tx, ty, width(), height()); if (borderRect.isEmpty()) return; // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. paintInfo.context->save(); IntSize topLeft, topRight, bottomLeft, bottomRight; style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); } if (m_widget) { // Tell the widget to paint now. This is the only time the widget is allowed // to paint itself. That way it will composite properly with z-indexed layers. if (m_substituteImage) paintInfo.context->drawImage(m_substituteImage.get(), style()->colorSpace(), m_widget->frameRect()); else { IntPoint widgetLocation = m_widget->frameRect().location(); IntPoint paintLocation(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); IntRect paintRect = paintInfo.rect; IntSize paintOffset = paintLocation - widgetLocation; // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. if (!paintOffset.isZero()) { paintInfo.context->translate(paintOffset); paintRect.move(-paintOffset); } m_widget->paint(paintInfo.context, paintRect); if (!paintOffset.isZero()) paintInfo.context->translate(-paintOffset); } if (m_widget->isFrameView() && paintInfo.overlapTestRequests && !static_cast<FrameView*>(m_widget.get())->useSlowRepaintsIfNotOverlapped()) { ASSERT(!paintInfo.overlapTestRequests->contains(this)); paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); } } if (style()->hasBorderRadius()) paintInfo.context->restore(); // Paint a partially transparent wash over selected widgets. if (isSelected() && !document()->printing()) { // FIXME: selectionRect() is in absolute, not painting coordinates. paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace()); } }
void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!shouldPaint(paintInfo, paintOffset)) return; LayoutPoint adjustedPaintOffset = paintOffset + location(); if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) paintBoxDecorations(paintInfo, adjustedPaintOffset); if (paintInfo.phase == PaintPhaseMask) { paintMask(paintInfo, adjustedPaintOffset); return; } if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && hasOutline()) paintOutline(paintInfo.context, LayoutRect(adjustedPaintOffset, size())); if (!m_frameView || paintInfo.phase != PaintPhaseForeground) return; #if PLATFORM(MAC) if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) paintCustomHighlight(paintOffset, style()->highlight(), true); #endif if (style()->hasBorderRadius()) { LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size()); if (borderRect.isEmpty()) return; // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. paintInfo.context->save(); RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(borderRect, paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true); clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect); } if (m_widget) { // Tell the widget to paint now. This is the only time the widget is allowed // to paint itself. That way it will composite properly with z-indexed layers. IntPoint widgetLocation = m_widget->frameRect().location(); IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()), roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop())); IntRect paintRect = paintInfo.rect; IntSize widgetPaintOffset = paintLocation - widgetLocation; // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. if (!widgetPaintOffset.isZero()) { paintInfo.context->translate(widgetPaintOffset); paintRect.move(-widgetPaintOffset); } m_widget->paint(paintInfo.context, paintRect); if (!widgetPaintOffset.isZero()) paintInfo.context->translate(-widgetPaintOffset); if (m_widget->isFrameView()) { FrameView* frameView = static_cast<FrameView*>(m_widget.get()); bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants(); if (paintInfo.overlapTestRequests && runOverlapTests) { ASSERT(!paintInfo.overlapTestRequests->contains(this)); paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); } } } if (style()->hasBorderRadius()) paintInfo.context->restore(); // Paint a partially transparent wash over selected widgets. if (isSelected() && !document()->printing()) { // FIXME: selectionRect() is in absolute, not painting coordinates. paintInfo.context->fillRect(pixelSnappedIntRect(selectionRect()), selectionBackgroundColor(), style()->colorSpace()); } }
LayoutRect RenderNamedFlowThread::decorationsClipRectForBoxInNamedFlowFragment(const RenderBox& box, RenderNamedFlowFragment& fragment) const { LayoutRect visualOverflowRect = fragment.visualOverflowRectForBox(&box); LayoutUnit initialLogicalX = style().isHorizontalWritingMode() ? visualOverflowRect.x() : visualOverflowRect.y(); // The visual overflow rect returned by visualOverflowRectForBox is already flipped but the // RenderRegion::rectFlowPortionForBox method expects it unflipped. flipForWritingModeLocalCoordinates(visualOverflowRect); visualOverflowRect = fragment.rectFlowPortionForBox(&box, visualOverflowRect); // Now flip it again. flipForWritingModeLocalCoordinates(visualOverflowRect); // Take the scrolled offset of this object's parents into consideration. IntSize scrolledContentOffset; RenderBlock* containingBlock = box.containingBlock(); while (containingBlock) { if (containingBlock->isRenderNamedFlowThread()) { // We've reached the flow thread, take the scrolled offset of the region into consideration. ASSERT(containingBlock == this); scrolledContentOffset += fragment.fragmentContainer().scrolledContentOffset(); break; } scrolledContentOffset += containingBlock->scrolledContentOffset(); containingBlock = containingBlock->containingBlock(); } if (!scrolledContentOffset.isZero()) { if (style().isFlippedBlocksWritingMode()) scrolledContentOffset = -scrolledContentOffset; visualOverflowRect.inflateX(scrolledContentOffset.width()); visualOverflowRect.inflateY(scrolledContentOffset.height()); } // Layers are in physical coordinates so the origin must be moved to the physical top-left of the flowthread. if (style().isFlippedBlocksWritingMode()) { if (style().isHorizontalWritingMode()) visualOverflowRect.moveBy(LayoutPoint(0, height())); else visualOverflowRect.moveBy(LayoutPoint(width(), 0)); } const RenderBox* iterBox = &box; while (iterBox && iterBox != this) { RenderBlock* containerBlock = iterBox->containingBlock(); // FIXME: This doesn't work properly with flipped writing modes. // https://bugs.webkit.org/show_bug.cgi?id=125149 if (iterBox->isPositioned()) { // For positioned elements, just use the layer's absolute bounding box. visualOverflowRect.moveBy(iterBox->layer()->absoluteBoundingBox().location()); break; } LayoutRect currentBoxRect = iterBox->frameRect(); if (iterBox->style().isFlippedBlocksWritingMode()) { if (iterBox->style().isHorizontalWritingMode()) currentBoxRect.setY(currentBoxRect.height() - currentBoxRect.maxY()); else currentBoxRect.setX(currentBoxRect.width() - currentBoxRect.maxX()); } if (containerBlock->style().writingMode() != iterBox->style().writingMode()) iterBox->flipForWritingMode(currentBoxRect); visualOverflowRect.moveBy(currentBoxRect.location()); iterBox = containerBlock; } // Since the purpose of this method is to make sure the borders of a fragmented // element don't overflow the region in the fragmentation direction, there's no // point in restricting the clipping rect on the logical X axis. // This also saves us the trouble of handling percent-based widths and margins // since the absolute bounding box of a positioned element would not contain // the correct coordinates relative to the region we're interested in, but rather // relative to the actual flow thread. if (style().isHorizontalWritingMode()) { if (initialLogicalX < visualOverflowRect.x()) visualOverflowRect.shiftXEdgeTo(initialLogicalX); if (visualOverflowRect.width() < frameRect().width()) visualOverflowRect.setWidth(frameRect().width()); } else { if (initialLogicalX < visualOverflowRect.y()) visualOverflowRect.shiftYEdgeTo(initialLogicalX); if (visualOverflowRect.height() < frameRect().height()) visualOverflowRect.setHeight(frameRect().height()); } return visualOverflowRect; }