1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkBitmap.h"
9 #include "SkBitmapController.h"
10 #include "SkBitmapProvider.h"
11 #include "SkMatrix.h"
12 #include "SkPixelRef.h"
13 #include "SkTemplates.h"
14
15 // RESIZE_LANCZOS3 is another good option, but chrome prefers mitchell at the moment
16 #define kHQ_RESIZE_METHOD SkBitmapScaler::RESIZE_MITCHELL
17
18 ///////////////////////////////////////////////////////////////////////////////////////////////////
19
requestBitmap(const SkBitmapProvider & provider,const SkMatrix & inv,SkFilterQuality quality,void * storage,size_t storageSize)20 SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider,
21 const SkMatrix& inv,
22 SkFilterQuality quality,
23 void* storage, size_t storageSize) {
24 State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize);
25 if (state) {
26 if (nullptr == state->fPixmap.addr()) {
27 SkInPlaceDeleteCheck(state, storage);
28 state = nullptr;
29 }
30 }
31 return state;
32 }
33
34 ///////////////////////////////////////////////////////////////////////////////////////////////////
35
36 #include "SkBitmapCache.h"
37 #include "SkBitmapScaler.h"
38 #include "SkMipMap.h"
39 #include "SkResourceCache.h"
40
41 class SkDefaultBitmapControllerState : public SkBitmapController::State {
42 public:
43 SkDefaultBitmapControllerState(const SkBitmapProvider&,
44 const SkMatrix& inv,
45 SkFilterQuality,
46 bool canShadeHQ);
47
48 private:
49 SkBitmap fResultBitmap;
50 sk_sp<const SkMipMap> fCurrMip;
51 bool fCanShadeHQ;
52
53 bool processHQRequest(const SkBitmapProvider&);
54 bool processMediumRequest(const SkBitmapProvider&);
55 };
56
57 // Check to see that the size of the bitmap that would be produced by
58 // scaling by the given inverted matrix is less than the maximum allowed.
cache_size_okay(const SkBitmapProvider & provider,const SkMatrix & invMat)59 static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMatrix& invMat) {
60 size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit();
61 if (0 == maximumAllocation) {
62 return true;
63 }
64 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
65 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
66 // Skip the division step:
67 const size_t size = provider.info().getSafeSize(provider.info().minRowBytes());
68 SkScalar invScaleSqr = invMat.getScaleX() * invMat.getScaleY();
69 return size < (maximumAllocation * SkScalarAbs(invScaleSqr));
70 }
71
72 /*
73 * High quality is implemented by performing up-right scale-only filtering and then
74 * using bilerp for any remaining transformations.
75 */
processHQRequest(const SkBitmapProvider & provider)76 bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) {
77 if (fQuality != kHigh_SkFilterQuality) {
78 return false;
79 }
80
81 // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
82 // to a valid bitmap. If we succeed, we will set this to Low instead.
83 fQuality = kMedium_SkFilterQuality;
84 #ifdef SK_USE_MIP_FOR_DOWNSCALE_HQ
85 return false;
86 #endif
87
88 if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
89 fInvMatrix.hasPerspective())
90 {
91 return false; // can't handle the reqeust
92 }
93
94 SkScalar invScaleX = fInvMatrix.getScaleX();
95 SkScalar invScaleY = fInvMatrix.getScaleY();
96 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
97 SkSize scale;
98 if (!fInvMatrix.decomposeScale(&scale)) {
99 return false;
100 }
101 invScaleX = scale.width();
102 invScaleY = scale.height();
103 }
104 invScaleX = SkScalarAbs(invScaleX);
105 invScaleY = SkScalarAbs(invScaleY);
106
107 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
108 return false; // no need for HQ
109 }
110
111 if (invScaleX > 1 || invScaleY > 1) {
112 return false; // only use HQ when upsampling
113 }
114
115 // If the shader can natively handle HQ filtering, let it do it.
116 if (fCanShadeHQ) {
117 fQuality = kHigh_SkFilterQuality;
118 SkAssertResult(provider.asBitmap(&fResultBitmap));
119 fResultBitmap.lockPixels();
120 return true;
121 }
122
123 const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
124 const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
125 const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);
126
127 if (!SkBitmapCache::Find(desc, &fResultBitmap)) {
128 SkBitmap orig;
129 if (!provider.asBitmap(&orig)) {
130 return false;
131 }
132 SkAutoPixmapUnlock src;
133 if (!orig.requestLock(&src)) {
134 return false;
135 }
136 if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
137 dstW, dstH, SkResourceCache::GetAllocator())) {
138 return false; // we failed to create fScaledBitmap
139 }
140
141 SkASSERT(fResultBitmap.getPixels());
142 fResultBitmap.setImmutable();
143 if (!provider.isVolatile()) {
144 if (SkBitmapCache::Add(desc, fResultBitmap)) {
145 provider.notifyAddedToCache();
146 }
147 }
148 }
149
150 SkASSERT(fResultBitmap.getPixels());
151
152 fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
153 SkIntToScalar(dstH) / provider.height());
154 fQuality = kLow_SkFilterQuality;
155 return true;
156 }
157
158 /*
159 * Modulo internal errors, this should always succeed *if* the matrix is downscaling
160 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
161 */
processMediumRequest(const SkBitmapProvider & provider)162 bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
163 SkASSERT(fQuality <= kMedium_SkFilterQuality);
164 if (fQuality != kMedium_SkFilterQuality) {
165 return false;
166 }
167
168 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
169 // to a valid bitmap.
170 fQuality = kLow_SkFilterQuality;
171
172 SkSize invScaleSize;
173 if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
174 return false;
175 }
176
177 SkDestinationSurfaceColorMode colorMode = provider.dstColorSpace()
178 ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
179 : SkDestinationSurfaceColorMode::kLegacy;
180 if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
181 fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc(), colorMode));
182 if (nullptr == fCurrMip.get()) {
183 SkBitmap orig;
184 if (!provider.asBitmap(&orig)) {
185 return false;
186 }
187 fCurrMip.reset(SkMipMapCache::AddAndRef(orig, colorMode));
188 if (nullptr == fCurrMip.get()) {
189 return false;
190 }
191 }
192 // diagnostic for a crasher...
193 if (nullptr == fCurrMip->data()) {
194 sk_throw();
195 }
196
197 const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
198 SkScalarInvert(invScaleSize.height()));
199 SkMipMap::Level level;
200 if (fCurrMip->extractLevel(scale, &level)) {
201 const SkSize& invScaleFixup = level.fScale;
202 fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
203
204 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
205 // that here, and not need to explicitly track it ourselves.
206 return fResultBitmap.installPixels(level.fPixmap);
207 } else {
208 // failed to extract, so release the mipmap
209 fCurrMip.reset(nullptr);
210 }
211 }
212 return false;
213 }
214
SkDefaultBitmapControllerState(const SkBitmapProvider & provider,const SkMatrix & inv,SkFilterQuality qual,bool canShadeHQ)215 SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
216 const SkMatrix& inv,
217 SkFilterQuality qual,
218 bool canShadeHQ) {
219 fInvMatrix = inv;
220 fQuality = qual;
221 fCanShadeHQ = canShadeHQ;
222
223 bool processed = this->processHQRequest(provider) || this->processMediumRequest(provider);
224
225 if (processed) {
226 SkASSERT(fResultBitmap.getPixels());
227 } else {
228 (void)provider.asBitmap(&fResultBitmap);
229 fResultBitmap.lockPixels();
230 // lock may fail to give us pixels
231 }
232 SkASSERT(fCanShadeHQ || fQuality <= kLow_SkFilterQuality);
233
234 // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
235 // and will destroy us if it is nullptr.
236 fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes(),
237 fResultBitmap.getColorTable());
238 }
239
onRequestBitmap(const SkBitmapProvider & bm,const SkMatrix & inverse,SkFilterQuality quality,void * storage,size_t size)240 SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
241 const SkMatrix& inverse,
242 SkFilterQuality quality,
243 void* storage, size_t size) {
244 return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size,
245 bm, inverse, quality, fCanShadeHQ);
246 }
247