2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5 Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
6 Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
25 #include "CachedImage.h"
27 #include "BitmapImage.h"
28 #include "MemoryCache.h"
29 #include "CachedResourceClient.h"
30 #include "CachedResourceClientWalker.h"
31 #include "CachedResourceLoader.h"
33 #include "FrameLoaderClient.h"
34 #include "FrameLoaderTypes.h"
35 #include "FrameView.h"
36 #include "RenderObject.h"
38 #include "SharedBuffer.h"
39 #include "SubresourceLoader.h"
40 #include <wtf/CurrentTime.h>
41 #include <wtf/StdLibExtras.h>
42 #include <wtf/Vector.h>
45 #include "PDFDocumentImage.h"
56 CachedImage::CachedImage(const ResourceRequest& resourceRequest)
57 : CachedResource(resourceRequest, ImageResource)
59 , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
60 , m_shouldPaintBrokenImage(true)
65 CachedImage::CachedImage(Image* image)
66 : CachedResource(ResourceRequest(), ImageResource)
68 , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
69 , m_shouldPaintBrokenImage(true)
75 CachedImage::~CachedImage()
79 void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*)
81 ASSERT(!hasClients());
85 void CachedImage::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
87 if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages())
88 CachedResource::load(cachedResourceLoader, options);
93 void CachedImage::removeClientForRenderer(RenderObject* renderer)
97 m_svgImageCache->removeRendererFromCache(renderer);
99 removeClient(renderer);
102 void CachedImage::didAddClient(CachedResourceClient* c)
104 if (m_decodedDataDeletionTimer.isActive())
105 m_decodedDataDeletionTimer.stop();
107 if (m_data && !m_image && !errorOccurred()) {
109 m_image->setData(m_data, true);
112 ASSERT(c->resourceClientType() == CachedImageClient::expectedType());
113 if (m_image && !m_image->isNull())
114 static_cast<CachedImageClient*>(c)->imageChanged(this);
116 CachedResource::didAddClient(c);
119 void CachedImage::allClientsRemoved()
121 if (m_image && !errorOccurred())
122 m_image->resetAnimation();
123 if (double interval = memoryCache()->deadDecodedDataDeletionInterval())
124 m_decodedDataDeletionTimer.startOneShot(interval);
127 pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const
129 if (deviceScaleFactor >= 2) {
130 DEFINE_STATIC_LOCAL(Image*, brokenImageHiRes, (Image::loadPlatformResource("missingImage@2x").leakRef()));
131 return make_pair(brokenImageHiRes, 2);
134 DEFINE_STATIC_LOCAL(Image*, brokenImageLoRes, (Image::loadPlatformResource("missingImage").leakRef()));
135 return make_pair(brokenImageLoRes, 1);
138 bool CachedImage::willPaintBrokenImage() const
140 return errorOccurred() && m_shouldPaintBrokenImage;
144 inline Image* CachedImage::lookupOrCreateImageForRenderer(const RenderObject* renderer)
148 if (!m_image->isSVGImage())
149 return m_image.get();
150 Image* useImage = m_svgImageCache->lookupOrCreateBitmapImageForRenderer(renderer);
151 if (useImage == Image::nullImage())
152 return m_image.get();
156 inline Image* CachedImage::lookupOrCreateImageForRenderer(const RenderObject*)
158 return m_image.get();
162 Image* CachedImage::image()
164 ASSERT(!isPurgeable());
166 if (errorOccurred() && m_shouldPaintBrokenImage) {
167 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
168 // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage()
169 // when they need the real, deviceScaleFactor-appropriate broken image icon.
170 return brokenImage(1).first;
174 return m_image.get();
176 return Image::nullImage();
179 Image* CachedImage::imageForRenderer(const RenderObject* renderer)
181 ASSERT(!isPurgeable());
183 if (errorOccurred() && m_shouldPaintBrokenImage) {
184 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
185 // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage()
186 // when they need the real, deviceScaleFactor-appropriate broken image icon.
187 return brokenImage(1).first;
191 return lookupOrCreateImageForRenderer(renderer);
193 return Image::nullImage();
196 void CachedImage::setContainerSizeForRenderer(const RenderObject* renderer, const IntSize& containerSize, float containerZoom)
198 if (!m_image || containerSize.isEmpty())
201 if (!m_image->isSVGImage()) {
202 m_image->setContainerSize(containerSize);
205 m_svgImageCache->setRequestedSizeAndZoom(renderer, SVGImageCache::SizeAndZoom(containerSize, containerZoom));
207 UNUSED_PARAM(renderer);
208 m_image->setContainerSize(containerSize);
212 bool CachedImage::usesImageContainerSize() const
215 return m_image->usesContainerSize();
220 bool CachedImage::imageHasRelativeWidth() const
223 return m_image->hasRelativeWidth();
228 bool CachedImage::imageHasRelativeHeight() const
231 return m_image->hasRelativeHeight();
236 IntSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier)
238 ASSERT(!isPurgeable());
243 if (m_image->isSVGImage()) {
244 // SVGImages already includes the zooming in its intrinsic size.
245 SVGImageCache::SizeAndZoom sizeAndZoom = m_svgImageCache->requestedSizeAndZoom(renderer);
246 if (sizeAndZoom.size.isEmpty())
247 return m_image->size();
248 if (sizeAndZoom.zoom == 1)
249 return sizeAndZoom.size;
250 if (multiplier == 1) {
251 // Consumer wants unscaled coordinates.
252 sizeAndZoom.size.setWidth(sizeAndZoom.size.width() / sizeAndZoom.zoom);
253 sizeAndZoom.size.setHeight(sizeAndZoom.size.height() / sizeAndZoom.zoom);
254 return sizeAndZoom.size;
256 return sizeAndZoom.size;
260 if (multiplier == 1.0f)
261 return m_image->size();
263 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
264 bool hasWidth = m_image->size().width() > 0;
265 bool hasHeight = m_image->size().height() > 0;
266 int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier);
267 int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier);
269 width = max(1, width);
271 height = max(1, height);
272 return IntSize(width, height);
275 void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
278 m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
281 void CachedImage::notifyObservers(const IntRect* changeRect)
283 CachedResourceClientWalker<CachedImageClient> w(m_clients);
284 while (CachedImageClient* c = w.next())
285 c->imageChanged(this, changeRect);
288 void CachedImage::checkShouldPaintBrokenImage()
290 if (!m_loader || m_loader->reachedTerminalState())
293 m_shouldPaintBrokenImage = m_loader->frameLoader()->client()->shouldPaintBrokenImage(m_resourceRequest.url());
296 void CachedImage::clear()
298 destroyDecodedData();
300 m_svgImageCache.clear();
306 inline void CachedImage::createImage()
308 // Create the image if it doesn't yet exist.
311 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
312 if (m_response.mimeType() == "application/pdf") {
313 m_image = PDFDocumentImage::create();
318 if (m_response.mimeType() == "image/svg+xml") {
319 RefPtr<SVGImage> svgImage = SVGImage::create(this);
320 m_svgImageCache = SVGImageCache::create(svgImage.get());
321 m_image = svgImage.release();
325 m_image = BitmapImage::create(this);
328 size_t CachedImage::maximumDecodedImageSize()
330 if (!m_loader || m_loader->reachedTerminalState())
332 Settings* settings = m_loader->frameLoader()->frame()->settings();
333 return settings ? settings->maximumDecodedImageSize() : 0;
336 void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
342 bool sizeAvailable = false;
344 // Have the image update its data from its internal buffer.
345 // It will not do anything now, but will delay decoding until
346 // queried for info (like size or specific image frames).
347 sizeAvailable = m_image->setData(m_data, allDataReceived);
349 // Go ahead and tell our observers to try to draw if we have either
350 // received all the data or the size is known. Each chunk from the
351 // network causes observers to repaint, which will force that chunk
353 #if ENABLE(TIZEN_PREVENT_INCREMENTAL_IMAGE_RENDERING_WORKAROUND)
354 if (/*sizeAvailable ||*/ allDataReceived) {
356 if (sizeAvailable || allDataReceived) {
358 size_t maxDecodedImageSize = maximumDecodedImageSize();
359 IntSize s = m_image->size();
360 size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check
361 if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) {
362 error(errorOccurred() ? status() : DecodeError);
364 memoryCache()->remove(this);
368 // It would be nice to only redraw the decoded band of the image, but with the current design
369 // (decoding delayed until painting) that seems hard.
373 setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
376 if (allDataReceived) {
382 void CachedImage::error(CachedResource::Status status)
384 checkShouldPaintBrokenImage();
387 ASSERT(errorOccurred());
394 void CachedImage::destroyDecodedData()
396 bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
397 if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
398 // Image refs the data buffer so we should not make it purgeable while the image is alive.
399 // Invoking addClient() will reconstruct the image object.
402 if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
404 } else if (m_image && !errorOccurred())
405 m_image->destroyDecodedData();
408 void CachedImage::decodedSizeChanged(const Image* image, int delta)
410 if (!image || image != m_image)
413 setDecodedSize(decodedSize() + delta);
416 void CachedImage::didDraw(const Image* image)
418 if (!image || image != m_image)
421 double timeStamp = FrameView::currentPaintTimeStamp();
422 if (!timeStamp) // If didDraw is called outside of a Frame paint.
423 timeStamp = currentTime();
425 CachedResource::didAccessDecodedData(timeStamp);
428 bool CachedImage::shouldPauseAnimation(const Image* image)
430 if (!image || image != m_image)
433 CachedResourceClientWalker<CachedImageClient> w(m_clients);
434 while (CachedImageClient* c = w.next()) {
435 if (c->willRenderImage(this))
442 void CachedImage::animationAdvanced(const Image* image)
444 if (!image || image != m_image)
449 void CachedImage::changedInRect(const Image* image, const IntRect& rect)
451 if (!image || image != m_image)
454 // We have to update the cached ImageBuffers if the underlying content changed.
455 if (image->isSVGImage()) {
456 m_svgImageCache->imageContentChanged();
460 notifyObservers(&rect);
463 } // namespace WebCore