RefPtr<Range> Frame::rangeForPoint(const IntPoint& framePoint) { VisiblePosition position = visiblePositionForPoint(framePoint); if (position.isNull()) return nullptr; Position deepPosition = position.deepEquivalent(); Text* containerText = deepPosition.containerText(); if (!containerText || !containerText->renderer() || containerText->renderer()->style().userSelect() == SELECT_NONE) return nullptr; VisiblePosition previous = position.previous(); if (previous.isNotNull()) { RefPtr<Range> previousCharacterRange = makeRange(previous, position); LayoutRect rect = editor().firstRectForRange(previousCharacterRange.get()); if (rect.contains(framePoint)) return previousCharacterRange; } VisiblePosition next = position.next(); if (RefPtr<Range> nextCharacterRange = makeRange(position, next)) { LayoutRect rect = editor().firstRectForRange(nextCharacterRange.get()); if (rect.contains(framePoint)) return nextCharacterRange; } return nullptr; }
static void deflateIfOverlapped(LayoutRect& a, LayoutRect& b) { if (!a.intersects(b) || a.contains(b) || b.contains(a)) return; LayoutUnit deflateFactor = -fudgeFactor(); // Avoid negative width or height values. if ((a.width() + 2 * deflateFactor > 0) && (a.height() + 2 * deflateFactor > 0)) a.inflate(deflateFactor); if ((b.width() + 2 * deflateFactor > 0) && (b.height() + 2 * deflateFactor > 0)) b.inflate(deflateFactor); }
AccessibilityObject* AccessibilityListBox::elementAccessibilityHitTest(const LayoutPoint& point) const { // the internal HTMLSelectElement methods for returning a listbox option at a point // ignore optgroup elements. if (!m_renderer) return 0; Node* node = m_renderer->node(); if (!node) return 0; LayoutRect parentRect = boundingBoxRect(); AccessibilityObject* listBoxOption = 0; unsigned length = m_children.size(); for (unsigned i = 0; i < length; i++) { LayoutRect rect = toRenderListBox(m_renderer)->itemBoundingBoxRect(parentRect.location(), i); // The cast to HTMLElement below is safe because the only other possible listItem type // would be a WMLElement, but WML builds don't use accessibility features at all. if (rect.contains(point)) { listBoxOption = m_children[i].get(); break; } } if (listBoxOption && !listBoxOption->accessibilityIsIgnored()) return listBoxOption; return axObjectCache()->getOrCreate(m_renderer); }
Node* TreeScope::nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint) { auto* frame = documentScope().frame(); auto* view = documentScope().view(); if (!frame || !view) return nullptr; float scaleFactor = frame->pageZoomFactor() * frame->frameScaleFactor(); LayoutPoint contentsPoint = clientPoint; contentsPoint.scale(scaleFactor); contentsPoint.moveBy(view->contentsScrollPosition()); LayoutRect visibleRect; #if PLATFORM(IOS) visibleRect = view->unobscuredContentRect(); #else visibleRect = view->visibleContentRect(); #endif if (!visibleRect.contains(contentsPoint)) return nullptr; HitTestResult result(contentsPoint); documentScope().renderView()->hitTest(HitTestRequest(), result); if (localPoint) *localPoint = result.localPoint(); return result.innerNode(); }
bool RenderFlowThread::hitTestFlowThreadPortionInRegion(RenderRegion* region, LayoutRect flowThreadPortionRect, LayoutRect flowThreadPortionOverflowRect, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const { LayoutRect regionClippingRect = computeRegionClippingRect(accumulatedOffset, flowThreadPortionRect, flowThreadPortionOverflowRect); if (!regionClippingRect.contains(locationInContainer.point())) return false; LayoutSize renderFlowThreadOffset; if (style()->isFlippedBlocksWritingMode()) { LayoutRect flippedFlowThreadPortionRect(flowThreadPortionRect); flipForWritingMode(flippedFlowThreadPortionRect); renderFlowThreadOffset = accumulatedOffset - flippedFlowThreadPortionRect.location(); } else renderFlowThreadOffset = accumulatedOffset - flowThreadPortionRect.location(); // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView. HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping); // Make a new temporary HitTestLocation in the new region. HitTestLocation newHitTestLocation(locationInContainer, -renderFlowThreadOffset, region); bool isPointInsideFlowThread = layer()->hitTest(newRequest, newHitTestLocation, result); // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate // space? Right now it's staying in the RenderFlowThread's coordinate space, which may end up being ok. We will know more when we get around to // patching positionForPoint. return isPointInsideFlowThread; }
void RenderHTMLCanvas::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { GraphicsContext* context = paintInfo.context; LayoutRect contentRect = contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = replacedContentRect(); paintRect.moveBy(paintOffset); bool clip = !contentRect.contains(paintRect); if (clip) { // Not allowed to overflow the content box. paintInfo.context->save(); paintInfo.context->clip(pixelSnappedIntRect(contentRect)); } // FIXME: InterpolationNone should be used if ImageRenderingOptimizeContrast is set. // See bug for more details: crbug.com/353716. InterpolationQuality interpolationQuality = style()->imageRendering() == ImageRenderingOptimizeContrast ? InterpolationLow : CanvasDefaultInterpolationQuality; InterpolationQuality previousInterpolationQuality = context->imageInterpolationQuality(); context->setImageInterpolationQuality(interpolationQuality); toHTMLCanvasElement(node())->paint(context, paintRect); context->setImageInterpolationQuality(previousInterpolationQuality); if (clip) context->restore(); }
void HTMLCanvasPainter::paintReplaced(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { GraphicsContext* context = paintInfo.context; LayoutRect contentRect = m_renderHTMLCanvas.contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = m_renderHTMLCanvas.replacedContentRect(); paintRect.moveBy(paintOffset); bool clip = !contentRect.contains(paintRect); if (clip) { context->save(); context->clip(contentRect); } // FIXME: InterpolationNone should be used if ImageRenderingOptimizeContrast is set. // See bug for more details: crbug.com/353716. InterpolationQuality interpolationQuality = m_renderHTMLCanvas.style()->imageRendering() == ImageRenderingOptimizeContrast ? InterpolationLow : CanvasDefaultInterpolationQuality; if (m_renderHTMLCanvas.style()->imageRendering() == ImageRenderingPixelated) interpolationQuality = InterpolationNone; InterpolationQuality previousInterpolationQuality = context->imageInterpolationQuality(); context->setImageInterpolationQuality(interpolationQuality); toHTMLCanvasElement(m_renderHTMLCanvas.node())->paint(context, paintRect); context->setImageInterpolationQuality(previousInterpolationQuality); if (clip) context->restore(); }
void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { MediaPlayer* mediaPlayer = mediaElement()->player(); bool displayingPoster = videoElement()->shouldDisplayPosterImage(); if (!displayingPoster && !mediaPlayer) return; LayoutRect rect = videoBox(); if (rect.isEmpty()) return; rect.moveBy(paintOffset); LayoutRect contentRect = contentBoxRect(); contentRect.moveBy(paintOffset); GraphicsContext* context = paintInfo.context; bool clip = !contentRect.contains(rect); if (clip) { context->save(); context->clip(contentRect); } if (displayingPoster) paintIntoRect(context, rect); else if ((document().view() && document().view()->paintBehavior() & PaintBehaviorFlattenCompositingLayers) || !acceleratedRenderingInUse()) mediaPlayer->paint(context, pixelSnappedIntRect(rect)); if (clip) context->restore(); }
void RenderHTMLCanvas::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { GraphicsContext* context = paintInfo.context; LayoutRect contentRect = contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = replacedContentRect(); paintRect.moveBy(paintOffset); bool clip = !contentRect.contains(paintRect); if (clip) { // Not allowed to overflow the content box. paintInfo.context->save(); paintInfo.context->clip(pixelSnappedIntRect(contentRect)); } if (Frame* frame = this->frame()) { if (Page* page = frame->page()) { if (paintInfo.phase == PaintPhaseForeground) page->addRelevantRepaintedObject(this, intersection(paintRect, contentRect)); } } bool useLowQualityScale = style()->imageRendering() == ImageRenderingOptimizeContrast; toHTMLCanvasElement(node())->paint(context, paintRect, useLowQualityScale); if (clip) context->restore(); }
bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect) { // If it is not a rect-based hit test, this method has to be no-op. // Return false, so the hit test stops. if (!isRectBasedTest()) return false; // If node is null, return true so the hit test can continue. if (!node) return true; if (!request.allowsShadowContent()) node = node->shadowAncestorNode(); mutableRectBasedTestResult().add(node); bool regionFilled = rect.contains(locationInContainer.boundingBox()); // FIXME: This code (incorrectly) attempts to correct for culled inline nodes. See https://bugs.webkit.org/show_bug.cgi?id=85849. if (node->renderer()->isInline() && !regionFilled) { for (RenderObject* curr = node->renderer()->parent(); curr; curr = curr->parent()) { if (!curr->isRenderInline()) break; // We need to make sure the nodes for culled inlines get included. RenderInline* currInline = toRenderInline(curr); if (currInline->alwaysCreateLineBoxes()) break; if (currInline->visibleToHitTesting() && currInline->node()) mutableRectBasedTestResult().add(currInline->node()->shadowAncestorNode()); } } return !regionFilled; }
ScrollAnchor::ExamineResult ScrollAnchor::examine( const LayoutObject* candidate) const { if (candidate->isLayoutInline()) return ExamineResult(Continue); // Anonymous blocks are not in the DOM tree and it may be hard for // developers to reason about the anchor node. if (candidate->isAnonymous()) return ExamineResult(Continue); if (!candidate->isText() && !candidate->isBox()) return ExamineResult(Skip); if (!candidateMayMoveWithScroller(candidate, m_scroller)) return ExamineResult(Skip); if (candidate->style()->overflowAnchor() == AnchorNone) return ExamineResult(Skip); LayoutRect candidateRect = relativeBounds(candidate, m_scroller); LayoutRect visibleRect = scrollerLayoutBoxItem(m_scroller).overflowClipRect(LayoutPoint()); bool occupiesSpace = candidateRect.width() > 0 && candidateRect.height() > 0; if (occupiesSpace && visibleRect.intersects(candidateRect)) { return ExamineResult( visibleRect.contains(candidateRect) ? Return : Constrain, cornerToAnchor(m_scroller)); } else { return ExamineResult(Skip); } }
bool LayoutImage::updateImageLoadingPriorities() { if (!m_imageResource || !m_imageResource->cachedImage() || m_imageResource->cachedImage()->isLoaded()) return false; LayoutRect viewBounds = viewRect(); LayoutRect objectBounds = LayoutRect(absoluteContentBox()); // The object bounds might be empty right now, so intersects will fail since it doesn't deal // with empty rects. Use LayoutRect::contains in that case. bool isVisible; if (!objectBounds.isEmpty()) isVisible = viewBounds.intersects(objectBounds); else isVisible = viewBounds.contains(objectBounds); ResourceLoadPriorityOptimizer::VisibilityStatus status = isVisible ? ResourceLoadPriorityOptimizer::Visible : ResourceLoadPriorityOptimizer::NotVisible; LayoutRect screenArea; if (!objectBounds.isEmpty()) { screenArea = viewBounds; screenArea.intersect(objectBounds); } ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->notifyImageResourceVisibility(m_imageResource->cachedImage(), status, screenArea); return true; }
BoxClipper::BoxClipper(const LayoutBox& box, const PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset, ContentsClipBehavior contentsClipBehavior) : m_box(box) , m_paintInfo(paintInfo) , m_clipType(DisplayItem::UninitializedType) { ASSERT(m_paintInfo.phase != PaintPhaseSelfBlockBackgroundOnly && m_paintInfo.phase != PaintPhaseSelfOutlineOnly); if (m_paintInfo.phase == PaintPhaseMask) return; if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { const auto* objectProperties = m_box.objectPaintProperties(); if (objectProperties && objectProperties->overflowClip()) { PaintChunkProperties properties(paintInfo.context.getPaintController().currentPaintChunkProperties()); properties.clip = objectProperties->overflowClip(); m_scopedClipProperty.emplace(paintInfo.context.getPaintController(), properties); } return; } bool isControlClip = m_box.hasControlClip(); bool isOverflowOrContainmentClip = (m_box.hasOverflowClip() && !m_box.layer()->isSelfPaintingLayer()) || m_box.style()->containsPaint(); if (!isControlClip && !isOverflowOrContainmentClip) return; LayoutRect clipRect = isControlClip ? m_box.controlClipRect(accumulatedOffset) : m_box.overflowClipRect(accumulatedOffset); FloatRoundedRect clipRoundedRect(0, 0, 0, 0); bool hasBorderRadius = m_box.style()->hasBorderRadius(); if (hasBorderRadius) clipRoundedRect = m_box.style()->getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, m_box.size())); // Selection does not affect visual overflow, so this optimization is invalid if selection // is present. if (contentsClipBehavior == SkipContentsClipIfPossible && box.getSelectionState() == SelectionNone) { LayoutRect contentsVisualOverflow = m_box.contentsVisualOverflowRect(); if (contentsVisualOverflow.isEmpty()) return; LayoutRect conservativeClipRect = clipRect; if (hasBorderRadius) conservativeClipRect.intersect(LayoutRect(clipRoundedRect.radiusCenterRect())); conservativeClipRect.moveBy(-accumulatedOffset); if (m_box.hasLayer()) conservativeClipRect.move(m_box.scrolledContentOffset()); if (conservativeClipRect.contains(contentsVisualOverflow)) return; } if (!m_paintInfo.context.getPaintController().displayItemConstructionIsDisabled()) { m_clipType = m_paintInfo.displayItemTypeForClipping(); Vector<FloatRoundedRect> roundedRects; if (hasBorderRadius) roundedRects.append(clipRoundedRect); m_paintInfo.context.getPaintController().createAndAppend<ClipDisplayItem>(m_box, m_clipType, pixelSnappedIntRect(clipRect), roundedRects); } }
PassRefPtr<Range> Frame::rangeForPoint(const LayoutPoint& framePoint) { VisiblePosition position = visiblePositionForPoint(framePoint); if (position.isNull()) return 0; VisiblePosition previous = position.previous(); if (previous.isNotNull()) { RefPtr<Range> previousCharacterRange = makeRange(previous, position); LayoutRect rect = editor()->firstRectForRange(previousCharacterRange.get()); if (rect.contains(framePoint)) return previousCharacterRange.release(); } VisiblePosition next = position.next(); if (RefPtr<Range> nextCharacterRange = makeRange(position, next)) { LayoutRect rect = editor()->firstRectForRange(nextCharacterRange.get()); if (rect.contains(framePoint)) return nextCharacterRange.release(); } return 0; }
ListBasedHitTestBehavior HitTestResult::addNodeToListBasedTestResult(Node* node, const HitTestLocation& location, const LayoutRect& rect) { // If not a list-based test, stop testing because the hit has been found. if (!hitTestRequest().listBased()) return StopHitTesting; if (!node) return ContinueHitTesting; mutableListBasedTestResult().add(node); if (hitTestRequest().penetratingList()) return ContinueHitTesting; return rect.contains(LayoutRect(location.boundingBox())) ? StopHitTesting : ContinueHitTesting; }
bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect) { // If it is not a rect-based hit test, this method has to be no-op. // Return false, so the hit test stops. if (!isRectBasedTest()) return false; // If node is null, return true so the hit test can continue. if (!node) return true; mutableRectBasedTestResult().add(node); bool regionFilled = rect.contains(locationInContainer.boundingBox()); return !regionFilled; }
void ImagePainter::paintIntoRect(GraphicsContext& context, const LayoutRect& destRect, const LayoutRect& contentRect) { if (!m_layoutImage.imageResource()->hasImage() || m_layoutImage.imageResource()->errorOccurred()) return; // FIXME: should we just ASSERT these conditions? (audit all // callers). IntRect pixelSnappedDestRect = pixelSnappedIntRect(destRect); if (pixelSnappedDestRect.isEmpty()) return; RefPtr<Image> image = m_layoutImage.imageResource()->image( pixelSnappedDestRect.size(), m_layoutImage.style()->effectiveZoom()); if (!image || image->isNull()) return; // FIXME: why is interpolation quality selection not included in the // Instrumentation reported cost of drawing an image? InterpolationQuality interpolationQuality = BoxPainter::chooseInterpolationQuality( m_layoutImage, image.get(), image.get(), LayoutSize(pixelSnappedDestRect.size())); FloatRect srcRect = image->rect(); // If the content rect requires clipping, adjust |srcRect| and // |pixelSnappedDestRect| over using a clip. if (!contentRect.contains(destRect)) { IntRect pixelSnappedContentRect = pixelSnappedIntRect(contentRect); pixelSnappedContentRect.intersect(pixelSnappedDestRect); if (pixelSnappedContentRect.isEmpty()) return; srcRect = mapRect(pixelSnappedContentRect, pixelSnappedDestRect, srcRect); pixelSnappedDestRect = pixelSnappedContentRect; } TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data", InspectorPaintImageEvent::data(m_layoutImage)); InterpolationQuality previousInterpolationQuality = context.imageInterpolationQuality(); context.setImageInterpolationQuality(interpolationQuality); context.drawImage( image.get(), pixelSnappedDestRect, &srcRect, SkXfermode::kSrcOver_Mode, LayoutObject::shouldRespectImageOrientation(&m_layoutImage)); context.setImageInterpolationQuality(previousInterpolationQuality); }
void ImagePainter::paintReplaced(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = m_layoutImage.contentWidth(); LayoutUnit cHeight = m_layoutImage.contentHeight(); GraphicsContext* context = paintInfo.context; if (!m_layoutImage.imageResource()->hasImage()) { if (paintInfo.phase == PaintPhaseSelection) return; if (cWidth > 2 && cHeight > 2) { // Draw an outline rect where the image should be. IntRect paintRect = pixelSnappedIntRect(LayoutRect(paintOffset.x() + m_layoutImage.borderLeft() + m_layoutImage.paddingLeft(), paintOffset.y() + m_layoutImage.borderTop() + m_layoutImage.paddingTop(), cWidth, cHeight)); LayoutObjectDrawingRecorder drawingRecorder(*context, m_layoutImage, paintInfo.phase, paintRect); if (drawingRecorder.canUseCachedDrawing()) return; context->setStrokeStyle(SolidStroke); context->setStrokeColor(Color::lightGray); context->setFillColor(Color::transparent); context->drawRect(paintRect); } } else if (cWidth > 0 && cHeight > 0) { LayoutRect contentRect = m_layoutImage.contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = m_layoutImage.replacedContentRect(); paintRect.moveBy(paintOffset); LayoutObjectDrawingRecorder drawingRecorder(*context, m_layoutImage, paintInfo.phase, contentRect); if (drawingRecorder.canUseCachedDrawing()) return; bool clip = !contentRect.contains(paintRect); if (clip) { context->save(); context->clip(contentRect); } paintIntoRect(context, paintRect); if (clip) context->restore(); } }
BoxClipper::BoxClipper(const LayoutBox& box, const PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset, ContentsClipBehavior contentsClipBehavior) : m_box(box) , m_paintInfo(paintInfo) , m_clipType(DisplayItem::UninitializedType) { if (m_paintInfo.phase == PaintPhaseSelfBlockBackground || m_paintInfo.phase == PaintPhaseSelfOutline || m_paintInfo.phase == PaintPhaseMask) return; bool isControlClip = m_box.hasControlClip(); bool isOverflowOrContainmentClip = (m_box.hasOverflowClip() && !m_box.layer()->isSelfPaintingLayer()) || m_box.style()->containsPaint(); if (!isControlClip && !isOverflowOrContainmentClip) return; LayoutRect clipRect = isControlClip ? m_box.controlClipRect(accumulatedOffset) : m_box.overflowClipRect(accumulatedOffset); FloatRoundedRect clipRoundedRect(0, 0, 0, 0); bool hasBorderRadius = m_box.style()->hasBorderRadius(); if (hasBorderRadius) clipRoundedRect = m_box.style()->getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, m_box.size())); if (contentsClipBehavior == SkipContentsClipIfPossible) { LayoutRect contentsVisualOverflow = m_box.contentsVisualOverflowRect(); if (contentsVisualOverflow.isEmpty()) return; LayoutRect conservativeClipRect = clipRect; if (hasBorderRadius) conservativeClipRect.intersect(LayoutRect(clipRoundedRect.radiusCenterRect())); conservativeClipRect.moveBy(-accumulatedOffset); if (m_box.hasLayer()) conservativeClipRect.move(m_box.scrolledContentOffset()); if (conservativeClipRect.contains(contentsVisualOverflow)) return; } if (!m_paintInfo.context.paintController().displayItemConstructionIsDisabled()) { m_clipType = m_paintInfo.displayItemTypeForClipping(); Vector<FloatRoundedRect> roundedRects; if (hasBorderRadius) roundedRects.append(clipRoundedRect); m_paintInfo.context.paintController().createAndAppend<ClipDisplayItem>(m_box, m_clipType, pixelSnappedIntRect(clipRect), roundedRects); } }
void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { MediaPlayer* mediaPlayer = videoElement().player(); bool displayingPoster = videoElement().shouldDisplayPosterImage(); Page* page = frame().page(); if (!displayingPoster && !mediaPlayer) { if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); return; } LayoutRect rect = videoBox(); if (rect.isEmpty()) { if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); return; } rect.moveBy(paintOffset); if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantRepaintedObject(this, rect); LayoutRect contentRect = contentBoxRect(); contentRect.moveBy(paintOffset); GraphicsContext& context = paintInfo.context(); bool clip = !contentRect.contains(rect); GraphicsContextStateSaver stateSaver(context, clip); if (clip) context.clip(contentRect); if (displayingPoster) paintIntoRect(context, rect); else if (!videoElement().isFullscreen() || !mediaPlayer->supportsAcceleratedRendering()) { if (view().frameView().paintBehavior() & PaintBehaviorFlattenCompositingLayers) mediaPlayer->paintCurrentFrameInContext(context, rect); else mediaPlayer->paint(context, rect); } }
void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { GraphicsContext* context = paintInfo.context; if (m_imageResource->hasImage() && contentWidth() > 0 && contentHeight() > 0) { LayoutRect contentRect = contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = replacedContentRect(); paintRect.moveBy(paintOffset); bool clip = !contentRect.contains(paintRect); if (clip) { context->save(); context->clip(contentRect); } paintIntoRect(context, paintRect); if (clip) context->restore(); } }
void ImagePainter::paintReplaced(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = m_layoutImage.contentWidth(); LayoutUnit cHeight = m_layoutImage.contentHeight(); GraphicsContext* context = paintInfo.context; if (!m_layoutImage.imageResource()->hasImage()) { if (paintInfo.phase == PaintPhaseSelection) return; if (cWidth > 2 && cHeight > 2) { if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*context, m_layoutImage, paintInfo.phase, paintOffset)) return; // Draw an outline rect where the image should be. IntRect paintRect = pixelSnappedIntRect(LayoutRect(paintOffset.x() + m_layoutImage.borderLeft() + m_layoutImage.paddingLeft(), paintOffset.y() + m_layoutImage.borderTop() + m_layoutImage.paddingTop(), cWidth, cHeight)); LayoutObjectDrawingRecorder drawingRecorder(*context, m_layoutImage, paintInfo.phase, paintRect, paintOffset); context->setStrokeStyle(SolidStroke); context->setStrokeColor(Color::lightGray); context->setFillColor(Color::transparent); context->drawRect(paintRect); } } else if (cWidth > 0 && cHeight > 0) { LayoutRect contentRect = m_layoutImage.contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = m_layoutImage.replacedContentRect(); paintRect.moveBy(paintOffset); Optional<ClipRecorder> clipRecorder; if (!contentRect.contains(paintRect)) { // TODO(fmalita): can we get rid of this clip and adjust the image src/dst rect instead? clipRecorder.emplace(*context, m_layoutImage, paintInfo.displayItemTypeForClipping(), contentRect); } if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*context, m_layoutImage, paintInfo.phase, paintOffset)) return; LayoutObjectDrawingRecorder drawingRecorder(*context, m_layoutImage, paintInfo.phase, contentRect, paintOffset); paintIntoRect(context, paintRect); } }
void VideoPainter::paintReplaced(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { WebMediaPlayer* mediaPlayer = m_layoutVideo.mediaElement()->webMediaPlayer(); bool displayingPoster = m_layoutVideo.videoElement()->shouldDisplayPosterImage(); if (!displayingPoster && !mediaPlayer) return; LayoutRect rect(m_layoutVideo.videoBox()); if (rect.isEmpty()) return; rect.moveBy(paintOffset); GraphicsContext& context = paintInfo.context; LayoutRect contentRect = m_layoutVideo.contentBoxRect(); contentRect.moveBy(paintOffset); Optional<ClipRecorder> clipRecorder; if (!contentRect.contains(rect)) { clipRecorder.emplace(context, m_layoutVideo, paintInfo.displayItemTypeForClipping(), contentRect); } if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, m_layoutVideo, paintInfo.phase, paintOffset)) return; LayoutObjectDrawingRecorder drawingRecorder(context, m_layoutVideo, paintInfo.phase, contentRect, paintOffset); // Video frames are only painted in software for printing or capturing node images via web APIs. bool forceSoftwareVideoPaint = paintInfo.globalPaintFlags() & GlobalPaintFlattenCompositingLayers; if (displayingPoster || !forceSoftwareVideoPaint) { // This will display the poster image, if one is present, and otherwise paint nothing. ImagePainter(m_layoutVideo).paintIntoRect(context, rect); } else { SkPaint videoPaint = context.fillPaint(); videoPaint.setColor(SK_ColorBLACK); m_layoutVideo.videoElement()->paintCurrentFrame(context.canvas(), pixelSnappedIntRect(rect), &videoPaint); } }
void RenderHTMLCanvas::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { GraphicsContext* context = paintInfo.context; LayoutRect contentRect = contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = replacedContentRect(intrinsicSize()); paintRect.moveBy(paintOffset); // Not allowed to overflow the content box. bool clip = !contentRect.contains(paintRect); GraphicsContextStateSaver stateSaver(*paintInfo.context, clip); if (clip) paintInfo.context->clip(snappedIntRect(contentRect)); if (Page* page = frame().page()) { if (paintInfo.phase == PaintPhaseForeground) page->addRelevantRepaintedObject(this, intersection(paintRect, contentRect)); } bool useLowQualityScale = style().imageRendering() == ImageRenderingCrispEdges || style().imageRendering() == ImageRenderingOptimizeSpeed; canvasElement().paint(context, paintRect, useLowQualityScale); }
void VideoPainter::paintReplaced(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { WebMediaPlayer* mediaPlayer = m_layoutVideo.mediaElement()->webMediaPlayer(); bool displayingPoster = m_layoutVideo.videoElement()->shouldDisplayPosterImage(); if (!displayingPoster && !mediaPlayer) return; LayoutRect rect(m_layoutVideo.videoBox()); if (rect.isEmpty()) return; rect.moveBy(paintOffset); GraphicsContext* context = paintInfo.context; if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*context, m_layoutVideo, paintInfo.phase)) return; LayoutRect contentRect = m_layoutVideo.contentBoxRect(); contentRect.moveBy(paintOffset); LayoutObjectDrawingRecorder drawingRecorder(*context, m_layoutVideo, paintInfo.phase, contentRect); bool clip = !contentRect.contains(rect); if (clip) { context->save(); context->clip(contentRect); } if (displayingPoster) { ImagePainter(m_layoutVideo).paintIntoRect(context, rect); } else if ((m_layoutVideo.document().view() && m_layoutVideo.document().view()->paintBehavior() & PaintBehaviorFlattenCompositingLayers) || !m_layoutVideo.acceleratedRenderingInUse()) { SkPaint videoPaint = context->fillPaint(); videoPaint.setColor(SK_ColorBLACK); m_layoutVideo.videoElement()->paintCurrentFrame(context->canvas(), pixelSnappedIntRect(rect), &videoPaint); } if (clip) context->restore(); }
void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = contentWidth(); LayoutUnit cHeight = contentHeight(); LayoutUnit leftBorder = borderLeft(); LayoutUnit topBorder = borderTop(); LayoutUnit leftPad = paddingLeft(); LayoutUnit topPad = paddingTop(); GraphicsContext& context = paintInfo.context(); float deviceScaleFactor = document().deviceScaleFactor(); Page* page = frame().page(); if (!imageResource().hasImage() || imageResource().errorOccurred()) { if (paintInfo.phase == PaintPhaseSelection) return; if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); if (cWidth > 2 && cHeight > 2) { LayoutUnit borderWidth = LayoutUnit(1 / deviceScaleFactor); // Draw an outline rect where the image should be. context.setStrokeStyle(SolidStroke); context.setStrokeColor(Color::lightGray, style().colorSpace()); context.setFillColor(Color::transparent, style().colorSpace()); context.drawRect(snapRectToDevicePixels(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight), deviceScaleFactor), borderWidth); bool errorPictureDrawn = false; LayoutSize imageOffset; // When calculating the usable dimensions, exclude the pixels of // the ouline rect so the error image/alt text doesn't draw on it. LayoutUnit usableWidth = cWidth - 2 * borderWidth; LayoutUnit usableHeight = cHeight - 2 * borderWidth; RefPtr<Image> image = imageResource().image(); if (imageResource().errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) { // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution. std::pair<Image*, float> brokenImageAndImageScaleFactor = imageResource().cachedImage()->brokenImage(document().deviceScaleFactor()); image = brokenImageAndImageScaleFactor.first; FloatSize imageSize = image->size(); imageSize.scale(1 / brokenImageAndImageScaleFactor.second); // Center the error image, accounting for border and padding. LayoutUnit centerX = (usableWidth - imageSize.width()) / 2; if (centerX < 0) centerX = 0; LayoutUnit centerY = (usableHeight - imageSize.height()) / 2; if (centerY < 0) centerY = 0; imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth); ImageOrientationDescription orientationDescription(shouldRespectImageOrientation()); #if ENABLE(CSS_IMAGE_ORIENTATION) orientationDescription.setImageOrientationEnum(style().imageOrientation()); #endif context.drawImage(*image, style().colorSpace(), snapRectToDevicePixels(LayoutRect(paintOffset + imageOffset, imageSize), deviceScaleFactor), orientationDescription); errorPictureDrawn = true; } if (!m_altText.isEmpty()) { String text = document().displayStringModifiedByEncoding(m_altText); context.setFillColor(style().visitedDependentColor(CSSPropertyColor), style().colorSpace()); const FontCascade& font = style().fontCascade(); const FontMetrics& fontMetrics = font.fontMetrics(); LayoutUnit ascent = fontMetrics.ascent(); LayoutPoint altTextOffset = paintOffset; altTextOffset.move(leftBorder + leftPad + (paddingWidth / 2) - borderWidth, topBorder + topPad + ascent + (paddingHeight / 2) - borderWidth); // Only draw the alt text if it'll fit within the content box, // and only if it fits above the error image. TextRun textRun = RenderBlock::constructTextRun(this, font, text, style()); LayoutUnit textWidth = font.width(textRun); if (errorPictureDrawn) { if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height()) context.drawText(font, textRun, altTextOffset); } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height()) context.drawText(font, textRun, altTextOffset); } } } else if (imageResource().hasImage() && cWidth > 0 && cHeight > 0) { RefPtr<Image> img = imageResource().image(cWidth, cHeight); if (!img || img->isNull()) { if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); return; } LayoutRect contentBoxRect = this->contentBoxRect(); contentBoxRect.moveBy(paintOffset); LayoutRect replacedContentRect = this->replacedContentRect(intrinsicSize()); replacedContentRect.moveBy(paintOffset); bool clip = !contentBoxRect.contains(replacedContentRect); GraphicsContextStateSaver stateSaver(context, clip); if (clip) context.clip(contentBoxRect); paintIntoRect(context, snapRectToDevicePixels(replacedContentRect, deviceScaleFactor)); if (cachedImage() && page && paintInfo.phase == PaintPhaseForeground) { // For now, count images as unpainted if they are still progressively loading. We may want // to refine this in the future to account for the portion of the image that has painted. LayoutRect visibleRect = intersection(replacedContentRect, contentBoxRect); if (cachedImage()->isLoading()) page->addRelevantUnpaintedObject(this, visibleRect); else page->addRelevantRepaintedObject(this, visibleRect); } } }
void ImagePainter::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = m_renderImage.contentWidth(); LayoutUnit cHeight = m_renderImage.contentHeight(); GraphicsContext* context = paintInfo.context; if (!m_renderImage.imageResource()->hasImage() || m_renderImage.imageResource()->errorOccurred()) { if (paintInfo.phase == PaintPhaseSelection) return; if (cWidth > 2 && cHeight > 2) { const int borderWidth = 1; LayoutUnit leftBorder = m_renderImage.borderLeft(); LayoutUnit topBorder = m_renderImage.borderTop(); LayoutUnit leftPad = m_renderImage.paddingLeft(); LayoutUnit topPad = m_renderImage.paddingTop(); // Draw an outline rect where the image should be. IntRect paintRect = pixelSnappedIntRect(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight)); DrawingRecorder recorder(context, &m_renderImage, paintInfo.phase, paintRect); context->setStrokeStyle(SolidStroke); context->setStrokeColor(Color::lightGray); context->setFillColor(Color::transparent); context->drawRect(paintRect); bool errorPictureDrawn = false; LayoutSize imageOffset; // When calculating the usable dimensions, exclude the pixels of // the ouline rect so the error image/alt text doesn't draw on it. LayoutUnit usableWidth = cWidth - 2 * borderWidth; LayoutUnit usableHeight = cHeight - 2 * borderWidth; RefPtr<Image> image = m_renderImage.imageResource()->image(); if (m_renderImage.imageResource()->errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) { float deviceScaleFactor = blink::deviceScaleFactor(m_renderImage.frame()); // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution. pair<Image*, float> brokenImageAndImageScaleFactor = ImageResource::brokenImage(deviceScaleFactor); image = brokenImageAndImageScaleFactor.first; IntSize imageSize = image->size(); imageSize.scale(1 / brokenImageAndImageScaleFactor.second); // Center the error image, accounting for border and padding. LayoutUnit centerX = (usableWidth - imageSize.width()) / 2; if (centerX < 0) centerX = 0; LayoutUnit centerY = (usableHeight - imageSize.height()) / 2; if (centerY < 0) centerY = 0; imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth); context->drawImage(image.get(), pixelSnappedIntRect(LayoutRect(paintOffset + imageOffset, imageSize)), CompositeSourceOver, m_renderImage.shouldRespectImageOrientation()); errorPictureDrawn = true; } if (!m_renderImage.altText().isEmpty()) { const Font& font = m_renderImage.style()->font(); const FontMetrics& fontMetrics = font.fontMetrics(); LayoutUnit ascent = fontMetrics.ascent(); LayoutPoint textRectOrigin = paintOffset; textRectOrigin.move(leftBorder + leftPad + (RenderImage::paddingWidth / 2) - borderWidth, topBorder + topPad + (RenderImage::paddingHeight / 2) - borderWidth); LayoutPoint textOrigin(textRectOrigin.x(), textRectOrigin.y() + ascent); // Only draw the alt text if it'll fit within the content box, // and only if it fits above the error image. TextRun textRun = constructTextRun(&m_renderImage, font, m_renderImage.altText(), m_renderImage.style(), TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, DefaultTextRunFlags | RespectDirection); float textWidth = font.width(textRun); TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = FloatRect(textRectOrigin, FloatSize(textWidth, fontMetrics.height())); context->setFillColor(m_renderImage.resolveColor(CSSPropertyColor)); if (textRun.direction() == RTL) { int availableWidth = cWidth - static_cast<int>(RenderImage::paddingWidth); textOrigin.move(availableWidth - ceilf(textWidth), 0); } if (errorPictureDrawn) { if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height()) context->drawBidiText(font, textRunPaintInfo, textOrigin); } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height()) { context->drawBidiText(font, textRunPaintInfo, textOrigin); } } } } else if (m_renderImage.imageResource()->hasImage() && cWidth > 0 && cHeight > 0) { LayoutRect contentRect = m_renderImage.contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = m_renderImage.replacedContentRect(); paintRect.moveBy(paintOffset); DrawingRecorder recorder(context, &m_renderImage, paintInfo.phase, contentRect); bool clip = !contentRect.contains(paintRect); if (clip) { context->save(); context->clip(contentRect); } paintIntoRect(context, paintRect); if (clip) context->restore(); } }