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 "LayoutRepainter.h"
36#include "PointerEventsHitRules.h"
37#include "RenderImageResource.h"
38#include "RenderLayer.h"
39#include "RenderSVGResourceContainer.h"
40#include "RenderSVGResourceFilter.h"
41#include "SVGImageElement.h"
42#include "SVGLength.h"
43#include "SVGPreserveAspectRatio.h"
44#include "SVGRenderingContext.h"
45#include "SVGResources.h"
46#include "SVGResourcesCache.h"
47#include <wtf/StackStats.h>
48
49namespace WebCore {
50
51RenderSVGImage::RenderSVGImage(SVGImageElement* impl)
52    : RenderSVGModelObject(impl)
53    , m_needsBoundariesUpdate(true)
54    , m_needsTransformUpdate(true)
55    , m_imageResource(RenderImageResource::create())
56{
57    m_imageResource->initialize(this);
58}
59
60RenderSVGImage::~RenderSVGImage()
61{
62    m_imageResource->shutdown();
63}
64
65bool RenderSVGImage::updateImageViewport()
66{
67    SVGImageElement* image = static_cast<SVGImageElement*>(node());
68    FloatRect oldBoundaries = m_objectBoundingBox;
69
70    SVGLengthContext lengthContext(image);
71    m_objectBoundingBox = FloatRect(image->x().value(lengthContext), image->y().value(lengthContext), image->width().value(lengthContext), image->height().value(lengthContext));
72
73    if (oldBoundaries == m_objectBoundingBox)
74        return false;
75
76    m_imageResource->setContainerSizeForRenderer(enclosingIntRect(m_objectBoundingBox).size());
77    m_needsBoundariesUpdate = true;
78    return true;
79}
80
81void RenderSVGImage::layout()
82{
83    StackStats::LayoutCheckPoint layoutCheckPoint;
84    ASSERT(needsLayout());
85
86    LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
87    updateImageViewport();
88
89    bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate;
90    if (m_needsTransformUpdate) {
91        m_localTransform = static_cast<SVGImageElement*>(node())->animatedLocalTransform();
92        m_needsTransformUpdate = false;
93    }
94
95    if (m_needsBoundariesUpdate) {
96        m_repaintBoundingBoxExcludingShadow = m_objectBoundingBox;
97        SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBoxExcludingShadow);
98
99        m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
100        SVGRenderSupport::intersectRepaintRectWithShadows(this, m_repaintBoundingBox);
101
102        m_needsBoundariesUpdate = false;
103    }
104
105    // Invalidate all resources of this client if our layout changed.
106    if (everHadLayout() && selfNeedsLayout())
107        SVGResourcesCache::clientLayoutChanged(this);
108
109    // If our bounds changed, notify the parents.
110    if (transformOrBoundariesUpdate)
111        RenderSVGModelObject::setNeedsBoundariesUpdate();
112
113    repainter.repaintAfterLayout();
114    setNeedsLayout(false);
115}
116
117void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&)
118{
119    if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || !m_imageResource->hasImage())
120        return;
121
122    FloatRect boundingBox = repaintRectInLocalCoordinates();
123    if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
124        return;
125
126    PaintInfo childPaintInfo(paintInfo);
127    bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
128    if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
129        GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
130        childPaintInfo.applyTransform(m_localTransform);
131
132        if (childPaintInfo.phase == PaintPhaseForeground) {
133            SVGRenderingContext renderingContext(this, childPaintInfo);
134
135            if (renderingContext.isRenderingPrepared()) {
136                if (style()->svgStyle()->bufferedRendering() == BR_STATIC  && renderingContext.bufferForeground(m_bufferedForeground))
137                    return;
138
139                paintForeground(childPaintInfo);
140            }
141        }
142
143        if (drawsOutline)
144            paintOutline(childPaintInfo.context, IntRect(boundingBox));
145    }
146}
147
148void RenderSVGImage::paintForeground(PaintInfo& paintInfo)
149{
150    RefPtr<Image> image = m_imageResource->image();
151    FloatRect destRect = m_objectBoundingBox;
152    FloatRect srcRect(0, 0, image->width(), image->height());
153
154    SVGImageElement* imageElement = static_cast<SVGImageElement*>(node());
155    imageElement->preserveAspectRatio().transformRect(destRect, srcRect);
156
157    paintInfo.context->drawImage(image.get(), ColorSpaceDeviceRGB, destRect, srcRect);
158}
159
160void RenderSVGImage::invalidateBufferedForeground()
161{
162    m_bufferedForeground.clear();
163}
164
165bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
166{
167    // We only draw in the forground phase, so we only hit-test then.
168    if (hitTestAction != HitTestForeground)
169        return false;
170
171    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents());
172    bool isVisible = (style()->visibility() == VISIBLE);
173    if (isVisible || !hitRules.requireVisible) {
174        FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
175
176        if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
177            return false;
178
179        if (hitRules.canHitFill) {
180            if (m_objectBoundingBox.contains(localPoint)) {
181                updateHitTestResult(result, roundedLayoutPoint(localPoint));
182                return true;
183            }
184        }
185    }
186
187    return false;
188}
189
190void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
191{
192    // The image resource defaults to nullImage until the resource arrives.
193    // This empty image may be cached by SVG resources which must be invalidated.
194    if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this))
195        resources->removeClientFromCache(this);
196
197    // Eventually notify parent resources, that we've changed.
198    RenderSVGResource::markForLayoutAndParentResourceInvalidation(this, false);
199
200    // Update the SVGImageCache sizeAndScales entry in case image loading finished after layout.
201    // (https://bugs.webkit.org/show_bug.cgi?id=99489)
202    m_objectBoundingBox = FloatRect();
203    updateImageViewport();
204
205    invalidateBufferedForeground();
206
207    repaint();
208}
209
210void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&)
211{
212    // this is called from paint() after the localTransform has already been applied
213    IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates());
214    if (!contentRect.isEmpty())
215        rects.append(contentRect);
216}
217
218} // namespace WebCore
219
220#endif // ENABLE(SVG)
221