blob: f23a85a88a1e0b6bee91882f71d7dbb186b08b60 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/paint/SVGRootPainter.h"
#include "core/layout/svg/LayoutSVGRoot.h"
#include "core/layout/svg/SVGLayoutSupport.h"
#include "core/paint/BoxPainter.h"
#include "core/paint/ObjectPaintProperties.h"
#include "core/paint/PaintInfo.h"
#include "core/paint/PaintTiming.h"
#include "core/paint/SVGPaintContext.h"
#include "core/paint/TransformRecorder.h"
#include "core/svg/SVGSVGElement.h"
#include "platform/graphics/paint/ClipRecorder.h"
#include "platform/graphics/paint/ScopedPaintChunkProperties.h"
#include "wtf/Optional.h"
namespace blink {
void SVGRootPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
// Pixel-snap to match BoxPainter's alignment.
const IntRect adjustedRect = pixelSnappedIntRect(paintOffset, m_layoutSVGRoot.size());
// An empty viewport disables rendering.
if (adjustedRect.isEmpty())
return;
// SVG outlines are painted during PaintPhaseForeground.
if (shouldPaintSelfOutline(paintInfo.phase))
return;
// An empty viewBox also disables rendering.
// (http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute)
SVGSVGElement* svg = toSVGSVGElement(m_layoutSVGRoot.node());
ASSERT(svg);
if (svg->hasEmptyViewBox())
return;
PaintInfo paintInfoBeforeFiltering(paintInfo);
Optional<ScopedPaintChunkProperties> propertyScope;
if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
const auto* objectProperties = m_layoutSVGRoot.objectPaintProperties();
// If a transform exists, we can rely on a paint layer existing to apply it.
DCHECK(!objectProperties || !objectProperties->transform() || m_layoutSVGRoot.hasLayer());
if (objectProperties && objectProperties->svgLocalToBorderBoxTransform()) {
auto& paintController = paintInfoBeforeFiltering.context.getPaintController();
PaintChunkProperties properties(paintController.currentPaintChunkProperties());
properties.transform = objectProperties->svgLocalToBorderBoxTransform();
propertyScope.emplace(paintController, properties);
}
}
// Apply initial viewport clip.
Optional<ClipRecorder> clipRecorder;
if (m_layoutSVGRoot.shouldApplyViewportClip()) {
// TODO(pdr): Clip the paint info cull rect here.
clipRecorder.emplace(paintInfoBeforeFiltering.context, m_layoutSVGRoot, paintInfoBeforeFiltering.displayItemTypeForClipping(), pixelSnappedIntRect(m_layoutSVGRoot.overflowClipRect(paintOffset)));
}
// Convert from container offsets (html layoutObjects) to a relative transform (svg layoutObjects).
// Transform from our paint container's coordinate system to our local coords.
AffineTransform paintOffsetToBorderBox =
AffineTransform::translation(adjustedRect.x(), adjustedRect.y());
// Compensate for size snapping.
paintOffsetToBorderBox.scale(
adjustedRect.width() / m_layoutSVGRoot.size().width().toFloat(),
adjustedRect.height() / m_layoutSVGRoot.size().height().toFloat());
paintOffsetToBorderBox.multiply(m_layoutSVGRoot.localToBorderBoxTransform());
paintInfoBeforeFiltering.updateCullRect(paintOffsetToBorderBox);
TransformRecorder transformRecorder(paintInfoBeforeFiltering.context, m_layoutSVGRoot, paintOffsetToBorderBox);
SVGPaintContext paintContext(m_layoutSVGRoot, paintInfoBeforeFiltering);
if (paintContext.paintInfo().phase == PaintPhaseForeground && !paintContext.applyClipMaskAndFilterIfNecessary())
return;
BoxPainter(m_layoutSVGRoot).paint(paintContext.paintInfo(), LayoutPoint());
PaintTiming& timing = PaintTiming::from(m_layoutSVGRoot.node()->document().topDocument());
timing.markFirstContentfulPaint();
}
} // namespace blink