OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 Research In Motion Limited. All rights reserved. | 2 * Copyright (C) 2011 Research In Motion Limited. All rights reserved. |
3 * Copyright (C) 2013 Apple Inc. All rights reserved. | 3 * Copyright (C) 2013 Apple Inc. All rights reserved. |
4 * | 4 * |
5 * This library is free software; you can redistribute it and/or | 5 * This library is free software; you can redistribute it and/or |
6 * modify it under the terms of the GNU Library General Public | 6 * modify it under the terms of the GNU Library General Public |
7 * License as published by the Free Software Foundation; either | 7 * License as published by the Free Software Foundation; either |
8 * version 2 of the License, or (at your option) any later version. | 8 * version 2 of the License, or (at your option) any later version. |
9 * | 9 * |
10 * This library is distributed in the hope that it will be useful, | 10 * This library is distributed in the hope that it will be useful, |
(...skipping 11 matching lines...) Expand all Loading... |
22 #include "SVGImageCache.h" | 22 #include "SVGImageCache.h" |
23 | 23 |
24 #if ENABLE(SVG) | 24 #if ENABLE(SVG) |
25 #include "CachedImage.h" | 25 #include "CachedImage.h" |
26 #include "FrameView.h" | 26 #include "FrameView.h" |
27 #include "GraphicsContext.h" | 27 #include "GraphicsContext.h" |
28 #include "ImageBuffer.h" | 28 #include "ImageBuffer.h" |
29 #include "Page.h" | 29 #include "Page.h" |
30 #include "RenderSVGRoot.h" | 30 #include "RenderSVGRoot.h" |
31 #include "SVGImage.h" | 31 #include "SVGImage.h" |
| 32 #include "SVGImageForContainer.h" |
32 | 33 |
33 namespace WebCore { | 34 namespace WebCore { |
34 | 35 |
35 static const int timeToKeepCachedBitmapsAfterLastUseInSeconds = 30; | |
36 | |
37 SVGImageCache::SVGImageCache(SVGImage* svgImage) | 36 SVGImageCache::SVGImageCache(SVGImage* svgImage) |
38 : m_svgImage(svgImage) | 37 : m_svgImage(svgImage) |
39 , m_redrawTimer(this, &SVGImageCache::redrawTimerFired) | |
40 , m_cacheClearTimer(this, &SVGImageCache::cacheClearTimerFired, timeToKeepCa
chedBitmapsAfterLastUseInSeconds) | |
41 { | 38 { |
42 ASSERT(m_svgImage); | 39 ASSERT(m_svgImage); |
43 } | 40 } |
44 | 41 |
45 SVGImageCache::~SVGImageCache() | 42 SVGImageCache::~SVGImageCache() |
46 { | 43 { |
47 m_sizeAndScalesMap.clear(); | 44 m_imageForContainerMap.clear(); |
48 clearBitmapCache(); | |
49 } | |
50 | |
51 void SVGImageCache::clearBitmapCache() | |
52 { | |
53 ImageDataMap::iterator end = m_imageDataMap.end(); | |
54 for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it) { | |
55 // Checks if the client (it->key) is still valid. The client should remo
ve itself from this | |
56 // cache before its end of life, otherwise the following ASSERT will cra
sh on pure virtual | |
57 // function call or a general crash. | |
58 ASSERT(it->key->resourceClientType() == CachedImageClient::expectedType(
)); | |
59 delete it->value.buffer; | |
60 } | |
61 | |
62 m_imageDataMap.clear(); | |
63 } | 45 } |
64 | 46 |
65 void SVGImageCache::removeClientFromCache(const CachedImageClient* client) | 47 void SVGImageCache::removeClientFromCache(const CachedImageClient* client) |
66 { | 48 { |
67 ASSERT(client); | 49 ASSERT(client); |
68 m_sizeAndScalesMap.remove(client); | |
69 | 50 |
70 ImageDataMap::iterator it = m_imageDataMap.find(client); | 51 if (m_imageForContainerMap.contains(client)) |
71 if (it == m_imageDataMap.end()) | 52 m_imageForContainerMap.remove(client); |
72 return; | |
73 | |
74 delete it->value.buffer; | |
75 m_imageDataMap.remove(it); | |
76 } | 53 } |
77 | 54 |
78 void SVGImageCache::setContainerSizeForRenderer(const CachedImageClient* client,
const IntSize& containerSize, float containerZoom) | 55 void SVGImageCache::setContainerSizeForRenderer(const CachedImageClient* client,
const IntSize& containerSize, float containerZoom) |
79 { | 56 { |
80 ASSERT(client); | 57 ASSERT(client); |
81 ASSERT(!containerSize.isEmpty()); | 58 ASSERT(!containerSize.isEmpty()); |
82 | 59 |
83 FloatSize containerSizeWithoutZoom(containerSize); | 60 FloatSize containerSizeWithoutZoom(containerSize); |
84 containerSizeWithoutZoom.scale(1 / containerZoom); | 61 containerSizeWithoutZoom.scale(1 / containerZoom); |
85 m_sizeAndScalesMap.set(client, SizeAndScales(containerSizeWithoutZoom, conta
inerZoom)); | 62 |
| 63 ImageForContainerMap::iterator imageIt = m_imageForContainerMap.find(client)
; |
| 64 if (imageIt == m_imageForContainerMap.end()) { |
| 65 RefPtr<SVGImageForContainer> image = SVGImageForContainer::create(m_svgI
mage, containerSizeWithoutZoom, 1, containerZoom); |
| 66 m_imageForContainerMap.set(client, image); |
| 67 } else { |
| 68 imageIt->value->setSize(containerSizeWithoutZoom); |
| 69 imageIt->value->setZoom(containerZoom); |
| 70 } |
86 } | 71 } |
87 | 72 |
88 IntSize SVGImageCache::imageSizeForRenderer(const RenderObject* renderer) const | 73 IntSize SVGImageCache::imageSizeForRenderer(const RenderObject* renderer) const |
89 { | 74 { |
90 IntSize imageSize = m_svgImage->size(); | 75 IntSize imageSize = m_svgImage->size(); |
91 | |
92 if (!renderer) | 76 if (!renderer) |
93 return imageSize; | 77 return imageSize; |
94 SizeAndScalesMap::const_iterator it = m_sizeAndScalesMap.find(renderer); | 78 |
95 if (it == m_sizeAndScalesMap.end()) | 79 ImageForContainerMap::const_iterator it = m_imageForContainerMap.find(render
er); |
| 80 if (it == m_imageForContainerMap.end()) |
96 return imageSize; | 81 return imageSize; |
97 | 82 |
98 SizeAndScales sizeAndScales = it->value; | 83 RefPtr<SVGImageForContainer> image = it->value; |
99 if (!sizeAndScales.size.isEmpty()) { | 84 FloatSize size = image->containerSize(); |
100 float scale = sizeAndScales.scale; | 85 if (!size.isEmpty()) { |
101 if (!scale) { | 86 size.scale(image->zoom()); |
102 Page* page = renderer->document()->page(); | 87 imageSize.setWidth(size.width()); |
103 scale = page->deviceScaleFactor() * page->pageScaleFactor(); | 88 imageSize.setHeight(size.height()); |
104 } | 89 } |
105 | 90 |
106 imageSize.setWidth(scale * sizeAndScales.size.width()); | |
107 imageSize.setHeight(scale * sizeAndScales.size.height()); | |
108 } | |
109 return imageSize; | 91 return imageSize; |
110 } | 92 } |
111 | 93 |
112 void SVGImageCache::imageContentChanged() | 94 // FIXME: This doesn't take into account the animation timeline so animations wi
ll not |
113 { | 95 // restart on page load, nor will two animations in different pages have differe
nt timelines. |
114 ImageDataMap::iterator end = m_imageDataMap.end(); | 96 Image* SVGImageCache::imageForRenderer(const RenderObject* renderer) |
115 for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it) | |
116 it->value.imageNeedsUpdate = true; | |
117 | |
118 // Always redraw on a timer because this method may be invoked from destruct
ors of things we are intending to draw. | |
119 if (!m_redrawTimer.isActive()) | |
120 m_redrawTimer.startOneShot(0); | |
121 } | |
122 | |
123 void SVGImageCache::redraw() | |
124 { | |
125 ImageDataMap::iterator end = m_imageDataMap.end(); | |
126 for (ImageDataMap::iterator it = m_imageDataMap.begin(); it != end; ++it) { | |
127 ImageData& data = it->value; | |
128 if (!data.imageNeedsUpdate) | |
129 continue; | |
130 // If the content changed we redraw using our existing ImageBuffer. | |
131 ASSERT(data.buffer); | |
132 ASSERT(data.image); | |
133 m_svgImage->drawSVGToImageBuffer(data.buffer, data.sizeAndScales.size, d
ata.sizeAndScales.zoom * data.sizeAndScales.scale, SVGImage::ClearImageBuffer); | |
134 data.image = data.buffer->copyImage(CopyBackingStore); | |
135 data.imageNeedsUpdate = false; | |
136 } | |
137 ASSERT(m_svgImage->imageObserver()); | |
138 m_svgImage->imageObserver()->animationAdvanced(m_svgImage); | |
139 } | |
140 | |
141 void SVGImageCache::redrawTimerFired(Timer<SVGImageCache>*) | |
142 { | |
143 // We have no guarantee that the frame does not require layout when the time
r fired. | |
144 // So be sure to check again in case it is still not safe to run redraw. | |
145 FrameView* frameView = m_svgImage->frameView(); | |
146 if (frameView && (frameView->needsLayout() || frameView->isInLayout())) { | |
147 if (!m_redrawTimer.isActive()) | |
148 m_redrawTimer.startOneShot(0); | |
149 } else | |
150 redraw(); | |
151 } | |
152 | |
153 void SVGImageCache::cacheClearTimerFired(DeferrableOneShotTimer<SVGImageCache>*) | |
154 { | |
155 clearBitmapCache(); | |
156 } | |
157 | |
158 Image* SVGImageCache::lookupOrCreateBitmapImageForRenderer(const RenderObject* r
enderer) | |
159 { | 97 { |
160 if (!renderer) | 98 if (!renderer) |
161 return Image::nullImage(); | 99 return Image::nullImage(); |
162 | 100 |
163 const CachedImageClient* client = renderer; | |
164 | |
165 // The cache needs to know the size of the renderer before querying an image
for it. | 101 // The cache needs to know the size of the renderer before querying an image
for it. |
166 SizeAndScalesMap::iterator sizeIt = m_sizeAndScalesMap.find(renderer); | 102 ImageForContainerMap::iterator it = m_imageForContainerMap.find(renderer); |
167 if (sizeIt == m_sizeAndScalesMap.end()) | 103 if (it == m_imageForContainerMap.end()) |
168 return Image::nullImage(); | 104 return Image::nullImage(); |
169 | 105 |
170 FloatSize size = sizeIt->value.size; | 106 RefPtr<SVGImageForContainer> image = it->value; |
171 float zoom = sizeIt->value.zoom; | 107 ASSERT(!image->containerSize().isEmpty()); |
172 float scale = sizeIt->value.scale; | |
173 | 108 |
174 // FIXME (85335): This needs to take CSS transform scale into account as wel
l. | 109 // FIXME: Set the page scale in setContainerSizeForRenderer instead of looki
ng it up here. |
175 Page* page = renderer->document()->page(); | 110 Page* page = renderer->document()->page(); |
176 if (!scale) | 111 image->setPageScale(page->deviceScaleFactor() * page->pageScaleFactor()); |
177 scale = page->deviceScaleFactor() * page->pageScaleFactor(); | |
178 | 112 |
179 ASSERT(!size.isEmpty()); | 113 return image.get(); |
180 | |
181 // (Re)schedule the oneshot timer to throw out all the cached ImageBuffers i
f they remain unused for too long. | |
182 m_cacheClearTimer.restart(); | |
183 | |
184 // Lookup image for client in cache and eventually update it. | |
185 ImageDataMap::iterator it = m_imageDataMap.find(client); | |
186 if (it != m_imageDataMap.end()) { | |
187 ImageData& data = it->value; | |
188 | |
189 // Common case: image size & zoom remained the same. | |
190 if (data.sizeAndScales.size == size && data.sizeAndScales.zoom == zoom &
& data.sizeAndScales.scale == scale) | |
191 return data.image.get(); | |
192 | |
193 // If the image size for the client changed, we have to delete the buffe
r, remove the item from the cache and recreate it. | |
194 delete data.buffer; | |
195 m_imageDataMap.remove(it); | |
196 } | |
197 | |
198 FloatSize scaledSize(size); | |
199 scaledSize.scale(scale * zoom); | |
200 | |
201 // Create and cache new image and image buffer at requested size. | |
202 OwnPtr<ImageBuffer> newBuffer = ImageBuffer::create(expandedIntSize(scaledSi
ze), 1); | |
203 if (!newBuffer) | |
204 return Image::nullImage(); | |
205 | |
206 m_svgImage->drawSVGToImageBuffer(newBuffer.get(), size, scale * zoom, SVGIma
ge::DontClearImageBuffer); | |
207 | |
208 RefPtr<Image> newImage = newBuffer->copyImage(CopyBackingStore); | |
209 Image* newImagePtr = newImage.get(); | |
210 ASSERT(newImagePtr); | |
211 | |
212 m_imageDataMap.add(client, ImageData(newBuffer.leakPtr(), newImage.release()
, SizeAndScales(size, zoom, scale))); | |
213 return newImagePtr; | |
214 } | 114 } |
215 | 115 |
216 } // namespace WebCore | 116 } // namespace WebCore |
217 | 117 |
218 #endif // ENABLE(SVG) | 118 #endif // ENABLE(SVG) |
OLD | NEW |