2 * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
3 * Copyright (C) 2006 Apple Inc.
4 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
5 * Copyright (C) 2007, 2008, 2009 Rob Buis <buis@kde.org>
6 * Copyright (C) 2009 Google, Inc.
7 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
8 * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 #include "RenderSVGImage.h"
29 #include "FloatQuad.h"
30 #include "GraphicsContext.h"
31 #include "HitTestResult.h"
32 #include "LayoutRepainter.h"
33 #include "PointerEventsHitRules.h"
34 #include "RenderImageResource.h"
35 #include "RenderLayer.h"
36 #include "RenderSVGResource.h"
37 #include "RenderSVGResourceFilter.h"
38 #include "SVGImageElement.h"
39 #include "SVGRenderingContext.h"
40 #include "SVGResources.h"
41 #include "SVGResourcesCache.h"
42 #include <wtf/StackStats.h>
46 RenderSVGImage::RenderSVGImage(SVGImageElement& element, RenderStyle&& style)
47 : RenderSVGModelObject(element, WTFMove(style))
48 , m_needsBoundariesUpdate(true)
49 , m_needsTransformUpdate(true)
50 , m_imageResource(std::make_unique<RenderImageResource>())
52 imageResource().initialize(this);
55 RenderSVGImage::~RenderSVGImage()
59 void RenderSVGImage::willBeDestroyed()
61 imageResource().shutdown();
62 RenderSVGModelObject::willBeDestroyed();
65 SVGImageElement& RenderSVGImage::imageElement() const
67 return downcast<SVGImageElement>(RenderSVGModelObject::element());
70 bool RenderSVGImage::updateImageViewport()
72 FloatRect oldBoundaries = m_objectBoundingBox;
73 bool updatedViewport = false;
75 SVGLengthContext lengthContext(&imageElement());
76 m_objectBoundingBox = FloatRect(imageElement().x().value(lengthContext), imageElement().y().value(lengthContext), imageElement().width().value(lengthContext), imageElement().height().value(lengthContext));
78 // Images with preserveAspectRatio=none should force non-uniform scaling. This can be achieved
79 // by setting the image's container size to its intrinsic size.
80 // See: http://www.w3.org/TR/SVG/single-page.html, 7.8 The ‘preserveAspectRatio’ attribute.
81 if (imageElement().preserveAspectRatio().align() == SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_NONE) {
82 if (CachedImage* cachedImage = imageResource().cachedImage()) {
83 LayoutSize intrinsicSize = cachedImage->imageSizeForRenderer(0, style().effectiveZoom());
84 if (intrinsicSize != imageResource().imageSize(style().effectiveZoom())) {
85 imageResource().setContainerSizeForRenderer(roundedIntSize(intrinsicSize));
86 updatedViewport = true;
91 if (oldBoundaries != m_objectBoundingBox) {
93 imageResource().setContainerSizeForRenderer(enclosingIntRect(m_objectBoundingBox).size());
94 updatedViewport = true;
95 m_needsBoundariesUpdate = true;
98 return updatedViewport;
101 void RenderSVGImage::layout()
103 StackStats::LayoutCheckPoint layoutCheckPoint;
104 ASSERT(needsLayout());
106 LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(*this) && selfNeedsLayout());
107 updateImageViewport();
109 bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate;
110 if (m_needsTransformUpdate) {
111 m_localTransform = imageElement().animatedLocalTransform();
112 m_needsTransformUpdate = false;
115 if (m_needsBoundariesUpdate) {
116 m_repaintBoundingBoxExcludingShadow = m_objectBoundingBox;
117 SVGRenderSupport::intersectRepaintRectWithResources(*this, m_repaintBoundingBoxExcludingShadow);
119 m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
120 SVGRenderSupport::intersectRepaintRectWithShadows(*this, m_repaintBoundingBox);
122 m_needsBoundariesUpdate = false;
125 // Invalidate all resources of this client if our layout changed.
126 if (everHadLayout() && selfNeedsLayout())
127 SVGResourcesCache::clientLayoutChanged(*this);
129 // If our bounds changed, notify the parents.
130 if (transformOrBoundariesUpdate)
131 RenderSVGModelObject::setNeedsBoundariesUpdate();
133 repainter.repaintAfterLayout();
137 void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&)
139 if (paintInfo.context().paintingDisabled() || paintInfo.phase != PaintPhaseForeground
140 || style().visibility() == HIDDEN || !imageResource().cachedImage())
143 FloatRect boundingBox = repaintRectInLocalCoordinates();
144 if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
147 PaintInfo childPaintInfo(paintInfo);
148 GraphicsContextStateSaver stateSaver(childPaintInfo.context());
149 childPaintInfo.applyTransform(m_localTransform);
151 if (childPaintInfo.phase == PaintPhaseForeground) {
152 SVGRenderingContext renderingContext(*this, childPaintInfo);
154 if (renderingContext.isRenderingPrepared()) {
155 if (style().svgStyle().bufferedRendering() == BR_STATIC && renderingContext.bufferForeground(m_bufferedForeground))
158 paintForeground(childPaintInfo);
162 if (style().outlineWidth())
163 paintOutline(childPaintInfo, IntRect(boundingBox));
166 void RenderSVGImage::paintForeground(PaintInfo& paintInfo)
168 RefPtr<Image> image = imageResource().image();
172 FloatRect destRect = m_objectBoundingBox;
173 FloatRect srcRect(0, 0, image->width(), image->height());
175 imageElement().preserveAspectRatio().transformRect(destRect, srcRect);
177 paintInfo.context().drawImage(*image, destRect, srcRect);
180 void RenderSVGImage::invalidateBufferedForeground()
182 m_bufferedForeground.reset();
185 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
187 // We only draw in the forground phase, so we only hit-test then.
188 if (hitTestAction != HitTestForeground)
191 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style().pointerEvents());
192 bool isVisible = (style().visibility() == VISIBLE);
193 if (isVisible || !hitRules.requireVisible) {
194 FloatPoint localPoint = localToParentTransform().inverse().value_or(AffineTransform()).mapPoint(pointInParent);
196 if (!SVGRenderSupport::pointInClippingArea(*this, localPoint))
199 if (hitRules.canHitFill) {
200 if (m_objectBoundingBox.contains(localPoint)) {
201 updateHitTestResult(result, LayoutPoint(localPoint));
202 if (result.addNodeToListBasedTestResult(&imageElement(), request, localPoint) == HitTestProgress::Stop)
211 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
213 // The image resource defaults to nullImage until the resource arrives.
214 // This empty image may be cached by SVG resources which must be invalidated.
215 if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this))
216 resources->removeClientFromCache(*this);
218 // Eventually notify parent resources, that we've changed.
219 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*this, false);
221 // Update the SVGImageCache sizeAndScales entry in case image loading finished after layout.
222 // (https://bugs.webkit.org/show_bug.cgi?id=99489)
223 m_objectBoundingBox = FloatRect();
224 updateImageViewport();
226 invalidateBufferedForeground();
231 void RenderSVGImage::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
233 // this is called from paint() after the localTransform has already been applied
234 LayoutRect contentRect = LayoutRect(repaintRectInLocalCoordinates());
235 if (!contentRect.isEmpty())
236 rects.append(contentRect);
239 } // namespace WebCore