1 /*
2 * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
3 * Copyright (C) 2006 Apple Computer, 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>
9 *
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.
14 *
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.
19 *
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.
24 */
25
26 #include "config.h"
27
28 #if ENABLE(SVG)
29 #include "RenderSVGImage.h"
30
31 #include "Attr.h"
32 #include "FloatConversion.h"
33 #include "FloatQuad.h"
34 #include "GraphicsContext.h"
35 #include "PointerEventsHitRules.h"
36 #include "RenderImageResource.h"
37 #include "RenderLayer.h"
38 #include "RenderSVGResourceContainer.h"
39 #include "RenderSVGResourceFilter.h"
40 #include "SVGImageElement.h"
41 #include "SVGLength.h"
42 #include "SVGPreserveAspectRatio.h"
43 #include "SVGRenderSupport.h"
44 #include "SVGResources.h"
45
46 namespace WebCore {
47
RenderSVGImage(SVGImageElement * impl)48 RenderSVGImage::RenderSVGImage(SVGImageElement* impl)
49 : RenderSVGModelObject(impl)
50 , m_updateCachedRepaintRect(true)
51 , m_needsTransformUpdate(true)
52 , m_imageResource(RenderImageResource::create())
53 {
54 m_imageResource->initialize(this);
55 }
56
~RenderSVGImage()57 RenderSVGImage::~RenderSVGImage()
58 {
59 m_imageResource->shutdown();
60 }
61
layout()62 void RenderSVGImage::layout()
63 {
64 ASSERT(needsLayout());
65
66 LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
67 SVGImageElement* image = static_cast<SVGImageElement*>(node());
68
69 bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_updateCachedRepaintRect;
70 if (m_needsTransformUpdate) {
71 m_localTransform = image->animatedLocalTransform();
72 m_needsTransformUpdate = false;
73 }
74
75 if (m_updateCachedRepaintRect) {
76 m_repaintBoundingBox = m_objectBoundingBox;
77 SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
78 m_updateCachedRepaintRect = false;
79 }
80
81 // Invalidate all resources of this client if our layout changed.
82 if (m_everHadLayout && selfNeedsLayout())
83 SVGResourcesCache::clientLayoutChanged(this);
84
85 // If our bounds changed, notify the parents.
86 if (transformOrBoundariesUpdate)
87 RenderSVGModelObject::setNeedsBoundariesUpdate();
88
89 repainter.repaintAfterLayout();
90 setNeedsLayout(false);
91 }
92
updateFromElement()93 void RenderSVGImage::updateFromElement()
94 {
95 SVGImageElement* image = static_cast<SVGImageElement*>(node());
96
97 FloatRect oldBoundaries = m_objectBoundingBox;
98 m_objectBoundingBox = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image));
99 if (m_objectBoundingBox != oldBoundaries) {
100 m_updateCachedRepaintRect = true;
101 setNeedsLayout(true);
102 }
103 RenderSVGModelObject::updateFromElement();
104 }
105
paint(PaintInfo & paintInfo,int,int)106 void RenderSVGImage::paint(PaintInfo& paintInfo, int, int)
107 {
108 if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || !m_imageResource->hasImage())
109 return;
110
111 FloatRect boundingBox = repaintRectInLocalCoordinates();
112 if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
113 return;
114
115 PaintInfo childPaintInfo(paintInfo);
116 bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
117 if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
118 GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
119 childPaintInfo.applyTransform(m_localTransform);
120
121 if (childPaintInfo.phase == PaintPhaseForeground) {
122 PaintInfo savedInfo(childPaintInfo);
123
124 if (SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo)) {
125 RefPtr<Image> image = m_imageResource->image();
126 FloatRect destRect = m_objectBoundingBox;
127 FloatRect srcRect(0, 0, image->width(), image->height());
128
129 SVGImageElement* imageElement = static_cast<SVGImageElement*>(node());
130 if (imageElement->preserveAspectRatio().align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE)
131 imageElement->preserveAspectRatio().transformRect(destRect, srcRect);
132
133 childPaintInfo.context->drawImage(image.get(), ColorSpaceDeviceRGB, destRect, srcRect);
134 }
135
136 SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, savedInfo.context);
137 }
138
139 if (drawsOutline)
140 paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()),
141 static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()));
142 }
143 }
144
nodeAtFloatPoint(const HitTestRequest & request,HitTestResult & result,const FloatPoint & pointInParent,HitTestAction hitTestAction)145 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
146 {
147 // We only draw in the forground phase, so we only hit-test then.
148 if (hitTestAction != HitTestForeground)
149 return false;
150
151 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents());
152 bool isVisible = (style()->visibility() == VISIBLE);
153 if (isVisible || !hitRules.requireVisible) {
154 FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
155
156 if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
157 return false;
158
159 if (hitRules.canHitFill) {
160 if (m_objectBoundingBox.contains(localPoint)) {
161 updateHitTestResult(result, roundedIntPoint(localPoint));
162 return true;
163 }
164 }
165 }
166
167 return false;
168 }
169
imageChanged(WrappedImagePtr,const IntRect *)170 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
171 {
172 // The image resource defaults to nullImage until the resource arrives.
173 // This empty image may be cached by SVG resources which must be invalidated.
174 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this))
175 resources->removeClientFromCache(this);
176
177 // Eventually notify parent resources, that we've changed.
178 RenderSVGResource::markForLayoutAndParentResourceInvalidation(this, false);
179
180 repaint();
181 }
182
addFocusRingRects(Vector<IntRect> & rects,int,int)183 void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, int, int)
184 {
185 // this is called from paint() after the localTransform has already been applied
186 IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates());
187 if (!contentRect.isEmpty())
188 rects.append(contentRect);
189 }
190
191 } // namespace WebCore
192
193 #endif // ENABLE(SVG)
194