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