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 childPaintInfo.context->save();
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 childPaintInfo.context->restore();
144 }
145 }
146
nodeAtFloatPoint(const HitTestRequest & request,HitTestResult & result,const FloatPoint & pointInParent,HitTestAction hitTestAction)147 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
148 {
149 // We only draw in the forground phase, so we only hit-test then.
150 if (hitTestAction != HitTestForeground)
151 return false;
152
153 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents());
154 bool isVisible = (style()->visibility() == VISIBLE);
155 if (isVisible || !hitRules.requireVisible) {
156 FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
157
158 if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
159 return false;
160
161 if (hitRules.canHitFill) {
162 if (m_objectBoundingBox.contains(localPoint)) {
163 updateHitTestResult(result, roundedIntPoint(localPoint));
164 return true;
165 }
166 }
167 }
168
169 return false;
170 }
171
imageChanged(WrappedImagePtr,const IntRect *)172 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
173 {
174 // The image resource defaults to nullImage until the resource arrives.
175 // This empty image may be cached by SVG resources which must be invalidated.
176 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this))
177 resources->removeClientFromCache(this);
178
179 // Eventually notify parent resources, that we've changed.
180 RenderSVGResource::markForLayoutAndParentResourceInvalidation(this, false);
181
182 repaint();
183 }
184
addFocusRingRects(Vector<IntRect> & rects,int,int)185 void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, int, int)
186 {
187 // this is called from paint() after the localTransform has already been applied
188 IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates());
189 if (!contentRect.isEmpty())
190 rects.append(contentRect);
191 }
192
193 } // namespace WebCore
194
195 #endif // ENABLE(SVG)
196