Skia OOM error when upscaling small subsets of images by large quantities
[WebKit-https.git] / Source / WebCore / platform / graphics / skia / NativeImageSkia.cpp
1 /*
2  * Copyright (c) 2008, Google Inc. All rights reserved.
3  * 
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  * 
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  * 
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #include "skia/ext/image_operations.h"
34
35 #include "NativeImageSkia.h"
36 #include "GraphicsContext3D.h"
37 #include "SkiaUtils.h"
38
39 #if PLATFORM(CHROMIUM)
40 #include "TraceEvent.h"
41 #endif
42
43 namespace WebCore {
44
45 NativeImageSkia::NativeImageSkia()
46     : m_resizeRequests(0)
47 {
48 }
49
50 NativeImageSkia::NativeImageSkia(const SkBitmap& other)
51     : m_image(other),
52       m_resizeRequests(0)
53 {
54 }
55
56 NativeImageSkia::~NativeImageSkia()
57 {
58 }
59
60 int NativeImageSkia::decodedSize() const
61 {
62     return m_image.getSize() + m_resizedImage.getSize();
63 }
64
65 bool NativeImageSkia::hasResizedBitmap(const SkIRect& srcSubset, int destWidth, int destHeight) const
66 {
67     return m_cachedImageInfo.isEqual(srcSubset, destWidth, destHeight) && !m_resizedImage.empty();
68 }
69
70 SkBitmap NativeImageSkia::resizedBitmap(const SkIRect& srcSubset,
71                                         int destWidth,
72                                         int destHeight,
73                                         const SkIRect& destVisibleSubset) const
74 {
75 #if PLATFORM(CHROMIUM)
76     TRACE_EVENT("NativeImageSkia::resizedBitmap", const_cast<NativeImageSkia*>(this), 0);
77 #endif
78     if (!hasResizedBitmap(srcSubset, destWidth, destHeight)) {
79         bool shouldCache = isDataComplete()
80             && shouldCacheResampling(srcSubset, destWidth, destHeight, destVisibleSubset);
81
82         SkBitmap subset;
83         m_image.extractSubset(&subset, srcSubset);
84         if (!shouldCache) {
85 #if PLATFORM(CHROMIUM)
86             TRACE_EVENT("nonCachedResize", const_cast<NativeImageSkia*>(this), 0);
87 #endif
88             // Just resize the visible subset and return it.
89             SkBitmap resizedImage = skia::ImageOperations::Resize(subset, skia::ImageOperations::RESIZE_LANCZOS3, destWidth, destHeight, destVisibleSubset);
90             resizedImage.setImmutable();
91             return resizedImage;
92         } else {
93 #if PLATFORM(CHROMIUM)
94             TRACE_EVENT("cachedResize", const_cast<NativeImageSkia*>(this), 0);
95 #endif
96             m_resizedImage = skia::ImageOperations::Resize(subset, skia::ImageOperations::RESIZE_LANCZOS3, destWidth, destHeight);
97         }
98         m_resizedImage.setImmutable();
99     }
100
101     SkBitmap visibleBitmap;
102     m_resizedImage.extractSubset(&visibleBitmap, destVisibleSubset);
103     return visibleBitmap;
104 }
105
106 bool NativeImageSkia::shouldCacheResampling(const SkIRect& srcSubset,
107                                             int destWidth,
108                                             int destHeight,
109                                             const SkIRect& destVisibleSubset) const
110 {
111     // Check whether the requested dimensions match previous request.
112     bool matchesPreviousRequest = m_cachedImageInfo.isEqual(srcSubset, destWidth, destHeight);
113     if (matchesPreviousRequest)
114         ++m_resizeRequests;
115     else {
116         m_cachedImageInfo.set(srcSubset, destWidth, destHeight);
117         m_resizeRequests = 0;
118         // Reset m_resizedImage now, because we don't distinguish between the
119         // last requested resize info and m_resizedImage's resize info.
120         m_resizedImage.reset();
121     }
122
123     // We can not cache incomplete frames. This might be a good optimization in
124     // the future, were we know how much of the frame has been decoded, so when
125     // we incrementally draw more of the image, we only have to resample the
126     // parts that are changed.
127     if (!isDataComplete())
128         return false;
129
130     // If the destination bitmap is excessively large, we'll never allow caching.
131     static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL;
132     if ((static_cast<unsigned long long>(destWidth) * static_cast<unsigned long long>(destHeight)) > kLargeBitmapSize)
133         return false;
134
135     // If the destination bitmap is small, we'll always allow caching, since
136     // there is not very much penalty for computing it and it may come in handy.
137     static const int kSmallBitmapSize = 4096;
138     if (destWidth * destHeight <= kSmallBitmapSize)
139         return true;
140
141     // If "too many" requests have been made for this bitmap, we assume that
142     // many more will be made as well, and we'll go ahead and cache it.
143     static const int kManyRequestThreshold = 4;
144     if (m_resizeRequests >= kManyRequestThreshold)
145         return true;
146
147     // If more than 1/4 of the resized image is visible, it's worth caching.
148     int destVisibleSize = destVisibleSubset.width() * destVisibleSubset.height();
149     return (destVisibleSize > (destWidth * destHeight) / 4);
150 }
151
152 NativeImageSkia::CachedImageInfo::CachedImageInfo()
153 {
154     srcSubset.setEmpty();
155 }
156
157 bool NativeImageSkia::CachedImageInfo::isEqual(const SkIRect& otherSrcSubset, int width, int height) const
158 {
159     return srcSubset == otherSrcSubset
160         && requestSize.width() == width
161         && requestSize.height() == height;
162 }
163
164 void NativeImageSkia::CachedImageInfo::set(const SkIRect& otherSrcSubset, int width, int height)
165 {
166     srcSubset = otherSrcSubset;
167     requestSize.setWidth(width);
168     requestSize.setHeight(height);
169 }
170
171 } // namespace WebCore