• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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