Infinite repaint loop with SVGImageCache and deferred repaint timers
[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 "FrameView.h"
25 #include "GraphicsContext.h"
26 #include "ImageBuffer.h"
27 #include "RenderSVGRoot.h"
28 #include "SVGImage.h"
29
30 namespace WebCore {
31
32 SVGImageCache::SVGImageCache(SVGImage* svgImage)
33     : m_svgImage(svgImage)
34     , m_redrawTimer(this, &SVGImageCache::redrawTimerFired)
35 {
36     ASSERT(m_svgImage);
37 }
38
39 SVGImageCache::~SVGImageCache()
40 {
41     m_sizeAndZoomMap.clear();
42
43     ImageDataMap::iterator end = m_imageDataMap.end();
44     for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it)
45         delete it->second.buffer;
46
47     m_imageDataMap.clear();
48 }
49
50 void SVGImageCache::removeRendererFromCache(const RenderObject* renderer)
51 {
52     ASSERT(renderer);
53     m_sizeAndZoomMap.remove(renderer);
54
55     ImageDataMap::iterator it = m_imageDataMap.find(renderer);
56     if (it == m_imageDataMap.end())
57         return;
58
59     delete it->second.buffer;
60     m_imageDataMap.remove(it);
61 }
62
63 void SVGImageCache::setRequestedSizeAndZoom(const RenderObject* renderer, const SizeAndZoom& sizeAndZoom)
64 {
65     ASSERT(renderer);
66     ASSERT(!sizeAndZoom.size.isEmpty());
67     m_sizeAndZoomMap.set(renderer, sizeAndZoom);
68 }
69
70 SVGImageCache::SizeAndZoom SVGImageCache::requestedSizeAndZoom(const RenderObject* renderer) const
71 {
72     ASSERT(renderer);
73     SizeAndZoomMap::const_iterator it = m_sizeAndZoomMap.find(renderer);
74     if (it == m_sizeAndZoomMap.end())
75         return SizeAndZoom();
76     return it->second;
77 }
78
79 void SVGImageCache::imageContentChanged()
80 {
81     ImageDataMap::iterator end = m_imageDataMap.end();
82     for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it)
83         it->second.imageNeedsUpdate = true;
84
85     // If we're in the middle of layout, start redrawing dirty
86     // images on a timer; otherwise it's safe to draw immediately.
87     
88     FrameView* frameView = m_svgImage->frameView();
89     if (frameView && frameView->needsLayout()) {
90         if (!m_redrawTimer.isActive())
91             m_redrawTimer.startOneShot(0);
92     } else
93         redraw();
94 }
95
96 void SVGImageCache::redraw()
97 {
98     ImageDataMap::iterator end = m_imageDataMap.end();
99     for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it) {
100         ImageData& data = it->second;
101         if (!data.imageNeedsUpdate)
102             continue;
103         // If the content changed we redraw using our existing ImageBuffer.
104         ASSERT(data.buffer);
105         ASSERT(data.image);
106         m_svgImage->drawSVGToImageBuffer(data.buffer, data.sizeAndZoom.size, data.sizeAndZoom.zoom, SVGImage::ClearImageBuffer);
107         data.image = data.buffer->copyImage(CopyBackingStore);
108         data.imageNeedsUpdate = false;
109     }
110     ASSERT(m_svgImage->imageObserver());
111     m_svgImage->imageObserver()->animationAdvanced(m_svgImage);
112 }
113
114 void SVGImageCache::redrawTimerFired(Timer<SVGImageCache>*)
115 {
116     redraw();
117 }
118
119 Image* SVGImageCache::lookupOrCreateBitmapImageForRenderer(const RenderObject* renderer)
120 {
121     ASSERT(renderer);
122
123     // The cache needs to know the size of the renderer before querying an image for it.
124     SizeAndZoomMap::iterator sizeIt = m_sizeAndZoomMap.find(renderer);
125     if (sizeIt == m_sizeAndZoomMap.end())
126         return Image::nullImage();
127
128     IntSize size = sizeIt->second.size;
129     float zoom = sizeIt->second.zoom;
130     ASSERT(!size.isEmpty());
131
132     // Lookup image for renderer in cache and eventually update it.
133     ImageDataMap::iterator it = m_imageDataMap.find(renderer);
134     if (it != m_imageDataMap.end()) {
135         ImageData& data = it->second;
136
137         // Common case: image size & zoom remained the same.
138         if (data.sizeAndZoom.size == size && data.sizeAndZoom.zoom == zoom)
139             return data.image.get();
140
141         // If the image size for the renderer changed, we have to delete the buffer, remove the item from the cache and recreate it.
142         delete data.buffer;
143         m_imageDataMap.remove(it);
144     }
145
146     // Create and cache new image and image buffer at requested size.
147     OwnPtr<ImageBuffer> newBuffer = ImageBuffer::create(size);
148     if (!newBuffer)
149         return Image::nullImage();
150
151     m_svgImage->drawSVGToImageBuffer(newBuffer.get(), size, zoom, SVGImage::DontClearImageBuffer);
152
153     RefPtr<Image> newImage = newBuffer->copyImage(CopyBackingStore);
154     Image* newImagePtr = newImage.get();
155     ASSERT(newImagePtr);
156
157     m_imageDataMap.add(renderer, ImageData(newBuffer.leakPtr(), newImage.release(), sizeIt->second));
158     return newImagePtr;
159 }
160
161 } // namespace WebCore
162
163 #endif // ENABLE(SVG)