| 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 |