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 if (!provider.validForDrawing()) {
25 return nullptr;
26 }
27
28 State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize);
29 if (state) {
30 if (nullptr == state->fPixmap.addr()) {
31 SkInPlaceDeleteCheck(state, storage);
32 state = nullptr;
33 }
34 }
35 return state;
36 }
37
38 ///////////////////////////////////////////////////////////////////////////////////////////////////
39
40 #include "SkBitmapCache.h"
41 #include "SkBitmapScaler.h"
42 #include "SkMipMap.h"
43 #include "SkResourceCache.h"
44
45 class SkDefaultBitmapControllerState : public SkBitmapController::State {
46 public:
47 SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality);
48
49 private:
50 SkBitmap fResultBitmap;
51 SkAutoTUnref<const SkMipMap> fCurrMip;
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
85 if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
86 fInvMatrix.hasPerspective())
87 {
88 return false; // can't handle the reqeust
89 }
90
91 SkScalar invScaleX = fInvMatrix.getScaleX();
92 SkScalar invScaleY = fInvMatrix.getScaleY();
93 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
94 SkSize scale;
95 if (!fInvMatrix.decomposeScale(&scale)) {
96 return false;
97 }
98 invScaleX = scale.width();
99 invScaleY = scale.height();
100 }
101 invScaleX = SkScalarAbs(invScaleX);
102 invScaleY = SkScalarAbs(invScaleY);
103
104 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
105 return false; // no need for HQ
106 }
107
108 if (invScaleX > 1 || invScaleY > 1) {
109 return false; // only use HQ when upsampling
110 }
111
112 const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
113 const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
114 const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);
115
116 if (!SkBitmapCache::FindWH(desc, &fResultBitmap)) {
117 SkBitmap orig;
118 if (!provider.asBitmap(&orig)) {
119 return false;
120 }
121 SkAutoPixmapUnlock src;
122 if (!orig.requestLock(&src)) {
123 return false;
124 }
125 if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
126 dstW, dstH, SkResourceCache::GetAllocator())) {
127 return false; // we failed to create fScaledBitmap
128 }
129
130 SkASSERT(fResultBitmap.getPixels());
131 fResultBitmap.setImmutable();
132 if (!provider.isVolatile()) {
133 if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
134 provider.notifyAddedToCache();
135 }
136 }
137 }
138
139 SkASSERT(fResultBitmap.getPixels());
140
141 fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
142 SkIntToScalar(dstH) / provider.height());
143 fQuality = kLow_SkFilterQuality;
144 return true;
145 }
146
147 /*
148 * Modulo internal errors, this should always succeed *if* the matrix is downscaling
149 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
150 */
processMediumRequest(const SkBitmapProvider & provider)151 bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
152 SkASSERT(fQuality <= kMedium_SkFilterQuality);
153 if (fQuality != kMedium_SkFilterQuality) {
154 return false;
155 }
156
157 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
158 // to a valid bitmap.
159 fQuality = kLow_SkFilterQuality;
160
161 SkSize invScaleSize;
162 if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
163 return false;
164 }
165
166 if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
167 fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc()));
168 if (nullptr == fCurrMip.get()) {
169 SkBitmap orig;
170 if (!provider.asBitmap(&orig)) {
171 return false;
172 }
173 fCurrMip.reset(SkMipMapCache::AddAndRef(orig));
174 if (nullptr == fCurrMip.get()) {
175 return false;
176 }
177 }
178 // diagnostic for a crasher...
179 if (nullptr == fCurrMip->data()) {
180 sk_throw();
181 }
182
183 const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
184 SkScalarInvert(invScaleSize.height()));
185 SkMipMap::Level level;
186 if (fCurrMip->extractLevel(scale, &level)) {
187 const SkSize& invScaleFixup = level.fScale;
188 fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
189
190 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
191 // that here, and not need to explicitly track it ourselves.
192 return fResultBitmap.installPixels(level.fPixmap);
193 } else {
194 // failed to extract, so release the mipmap
195 fCurrMip.reset(nullptr);
196 }
197 }
198 return false;
199 }
200
SkDefaultBitmapControllerState(const SkBitmapProvider & provider,const SkMatrix & inv,SkFilterQuality qual)201 SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
202 const SkMatrix& inv,
203 SkFilterQuality qual) {
204 fInvMatrix = inv;
205 fQuality = qual;
206
207 if (this->processHQRequest(provider) || this->processMediumRequest(provider)) {
208 SkASSERT(fResultBitmap.getPixels());
209 } else {
210 (void)provider.asBitmap(&fResultBitmap);
211 fResultBitmap.lockPixels();
212 // lock may fail to give us pixels
213 }
214 SkASSERT(fQuality <= kLow_SkFilterQuality);
215
216 // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
217 // and will destroy us if it is nullptr.
218 fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes(),
219 fResultBitmap.getColorTable());
220 }
221
onRequestBitmap(const SkBitmapProvider & bm,const SkMatrix & inverse,SkFilterQuality quality,void * storage,size_t size)222 SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
223 const SkMatrix& inverse,
224 SkFilterQuality quality,
225 void* storage, size_t size) {
226 return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality);
227 }
228
229