SVGImageCache leaks image data
[WebKit-https.git] / Source / WebCore / svg / graphics / SVGImageCache.cpp
1 /*
2  * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "SVGImageCache.h"
22
23 #if ENABLE(SVG)
24 #include "CachedImage.h"
25 #include "FrameView.h"
26 #include "GraphicsContext.h"
27 #include "ImageBuffer.h"
28 #include "RenderSVGRoot.h"
29 #include "SVGImage.h"
30
31 namespace WebCore {
32
33 SVGImageCache::SVGImageCache(SVGImage* svgImage)
34     : m_svgImage(svgImage)
35     , m_redrawTimer(this, &SVGImageCache::redrawTimerFired)
36 {
37     ASSERT(m_svgImage);
38 }
39
40 SVGImageCache::~SVGImageCache()
41 {
42     m_sizeAndScalesMap.clear();
43
44     ImageDataMap::iterator end = m_imageDataMap.end();
45     for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it) {
46         // Checks if the client (it->first) is still valid. The client should remove itself from this
47         // cache before its end of life, otherwise the following ASSERT will crash on pure virtual
48         // function call or a general crash.
49         ASSERT(it->first->resourceClientType() == CachedImageClient::expectedType());
50         delete it->second.buffer;
51     }
52
53     m_imageDataMap.clear();
54 }
55
56 void SVGImageCache::removeClientFromCache(const CachedImageClient* client)
57 {
58     ASSERT(client);
59     m_sizeAndScalesMap.remove(client);
60
61     ImageDataMap::iterator it = m_imageDataMap.find(client);
62     if (it == m_imageDataMap.end())
63         return;
64
65     delete it->second.buffer;
66     m_imageDataMap.remove(it);
67 }
68
69 void SVGImageCache::setRequestedSizeAndScales(const CachedImageClient* client, const SizeAndScales& sizeAndScales)
70 {
71     ASSERT(client);
72     ASSERT(!sizeAndScales.size.isEmpty());
73     m_sizeAndScalesMap.set(client, sizeAndScales);
74 }
75
76 SVGImageCache::SizeAndScales SVGImageCache::requestedSizeAndScales(const CachedImageClient* client) const
77 {
78     ASSERT(client);
79     SizeAndScalesMap::const_iterator it = m_sizeAndScalesMap.find(client);
80     if (it == m_sizeAndScalesMap.end())
81         return SizeAndScales();
82     return it->second;
83 }
84
85 void SVGImageCache::imageContentChanged()
86 {
87     ImageDataMap::iterator end = m_imageDataMap.end();
88     for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it)
89         it->second.imageNeedsUpdate = true;
90
91     // If we're in the middle of layout, start redrawing dirty
92     // images on a timer; otherwise it's safe to draw immediately.
93     FrameView* frameView = m_svgImage->frameView();
94     if (frameView && (frameView->needsLayout() || frameView->isInLayout())) {
95         if (!m_redrawTimer.isActive())
96             m_redrawTimer.startOneShot(0);
97     } else
98        redraw();
99 }
100
101 void SVGImageCache::redraw()
102 {
103     ImageDataMap::iterator end = m_imageDataMap.end();
104     for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it) {
105         ImageData& data = it->second;
106         if (!data.imageNeedsUpdate)
107             continue;
108         // If the content changed we redraw using our existing ImageBuffer.
109         ASSERT(data.buffer);
110         ASSERT(data.image);
111         m_svgImage->drawSVGToImageBuffer(data.buffer, data.sizeAndScales.size, data.sizeAndScales.zoom, data.sizeAndScales.scale, SVGImage::ClearImageBuffer);
112         data.image = data.buffer->copyImage(CopyBackingStore);
113         data.imageNeedsUpdate = false;
114     }
115     ASSERT(m_svgImage->imageObserver());
116     m_svgImage->imageObserver()->animationAdvanced(m_svgImage);
117 }
118
119 void SVGImageCache::redrawTimerFired(Timer<SVGImageCache>*)
120 {
121     // We have no guarantee that the frame does not require layout when the timer fired.
122     // So be sure to check again in case it is still not safe to run redraw.
123     FrameView* frameView = m_svgImage->frameView();
124     if (frameView && (frameView->needsLayout() || frameView->isInLayout())) {
125         if (!m_redrawTimer.isActive())
126             m_redrawTimer.startOneShot(0);
127     } else
128        redraw();
129 }
130
131 Image* SVGImageCache::lookupOrCreateBitmapImageForClient(const CachedImageClient* client)
132 {
133     ASSERT(client);
134
135     // The cache needs to know the size of the client before querying an image for it.
136     SizeAndScalesMap::iterator sizeIt = m_sizeAndScalesMap.find(client);
137     if (sizeIt == m_sizeAndScalesMap.end())
138         return Image::nullImage();
139
140     IntSize size = sizeIt->second.size;
141     float zoom = sizeIt->second.zoom;
142     float scale = sizeIt->second.scale;
143     ASSERT(!size.isEmpty());
144
145     // Lookup image for client in cache and eventually update it.
146     ImageDataMap::iterator it = m_imageDataMap.find(client);
147     if (it != m_imageDataMap.end()) {
148         ImageData& data = it->second;
149
150         // Common case: image size & zoom remained the same.
151         if (data.sizeAndScales.size == size && data.sizeAndScales.zoom == zoom && data.sizeAndScales.scale == scale)
152             return data.image.get();
153
154         // If the image size for the client changed, we have to delete the buffer, remove the item from the cache and recreate it.
155         delete data.buffer;
156         m_imageDataMap.remove(it);
157     }
158
159     FloatSize scaledSize(size);
160     scaledSize.scale(scale);
161
162     // Create and cache new image and image buffer at requested size.
163     OwnPtr<ImageBuffer> newBuffer = ImageBuffer::create(expandedIntSize(scaledSize), 1);
164     if (!newBuffer)
165         return Image::nullImage();
166
167     m_svgImage->drawSVGToImageBuffer(newBuffer.get(), size, zoom, scale, SVGImage::DontClearImageBuffer);
168
169     RefPtr<Image> newImage = newBuffer->copyImage(CopyBackingStore);
170     Image* newImagePtr = newImage.get();
171     ASSERT(newImagePtr);
172
173     m_imageDataMap.add(client, ImageData(newBuffer.leakPtr(), newImage.release(), sizeIt->second));
174     return newImagePtr;
175 }
176
177 } // namespace WebCore
178
179 #endif // ENABLE(SVG)