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