Line data Source code
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 :
20 141 : SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider,
21 : const SkMatrix& inv,
22 : SkFilterQuality quality,
23 : void* storage, size_t storageSize) {
24 141 : State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize);
25 141 : if (state) {
26 141 : if (nullptr == state->fPixmap.addr()) {
27 0 : SkInPlaceDeleteCheck(state, storage);
28 0 : state = nullptr;
29 : }
30 : }
31 141 : return state;
32 : }
33 :
34 : ///////////////////////////////////////////////////////////////////////////////////////////////////
35 :
36 : #include "SkBitmapCache.h"
37 : #include "SkBitmapScaler.h"
38 : #include "SkMipMap.h"
39 : #include "SkResourceCache.h"
40 :
41 423 : 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.
59 0 : static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMatrix& invMat) {
60 0 : size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit();
61 0 : if (0 == maximumAllocation) {
62 0 : return true;
63 : }
64 : // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
65 : // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
66 : // Skip the division step:
67 0 : const size_t size = provider.info().getSafeSize(provider.info().minRowBytes());
68 0 : SkScalar invScaleSqr = invMat.getScaleX() * invMat.getScaleY();
69 0 : 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 : */
76 141 : bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) {
77 141 : if (fQuality != kHigh_SkFilterQuality) {
78 141 : 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 0 : fQuality = kMedium_SkFilterQuality;
84 : #ifdef SK_USE_MIP_FOR_DOWNSCALE_HQ
85 : return false;
86 : #endif
87 :
88 0 : if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
89 0 : fInvMatrix.hasPerspective())
90 : {
91 0 : return false; // can't handle the reqeust
92 : }
93 :
94 0 : SkScalar invScaleX = fInvMatrix.getScaleX();
95 0 : SkScalar invScaleY = fInvMatrix.getScaleY();
96 0 : if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
97 : SkSize scale;
98 0 : if (!fInvMatrix.decomposeScale(&scale)) {
99 0 : return false;
100 : }
101 0 : invScaleX = scale.width();
102 0 : invScaleY = scale.height();
103 : }
104 0 : invScaleX = SkScalarAbs(invScaleX);
105 0 : invScaleY = SkScalarAbs(invScaleY);
106 :
107 0 : if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
108 0 : return false; // no need for HQ
109 : }
110 :
111 0 : if (invScaleX > 1 || invScaleY > 1) {
112 0 : return false; // only use HQ when upsampling
113 : }
114 :
115 : // If the shader can natively handle HQ filtering, let it do it.
116 0 : if (fCanShadeHQ) {
117 0 : fQuality = kHigh_SkFilterQuality;
118 0 : SkAssertResult(provider.asBitmap(&fResultBitmap));
119 0 : fResultBitmap.lockPixels();
120 0 : return true;
121 : }
122 :
123 0 : const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
124 0 : const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
125 0 : const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);
126 :
127 0 : if (!SkBitmapCache::Find(desc, &fResultBitmap)) {
128 0 : SkBitmap orig;
129 0 : if (!provider.asBitmap(&orig)) {
130 0 : return false;
131 : }
132 0 : SkAutoPixmapUnlock src;
133 0 : if (!orig.requestLock(&src)) {
134 0 : return false;
135 : }
136 :
137 0 : SkPixmap dst;
138 0 : SkBitmapCache::RecPtr rec;
139 0 : const SkImageInfo info = SkImageInfo::MakeN32(desc.fScaledWidth, desc.fScaledHeight,
140 0 : src.pixmap().alphaType());
141 0 : if (provider.isVolatile()) {
142 0 : if (!fResultBitmap.tryAllocPixels(info)) {
143 0 : return false;
144 : }
145 0 : SkASSERT(fResultBitmap.getPixels());
146 0 : fResultBitmap.peekPixels(&dst);
147 0 : fResultBitmap.setImmutable(); // a little cheat, as we haven't resized yet, but ok
148 : } else {
149 0 : rec = SkBitmapCache::Alloc(desc, info, &dst);
150 0 : if (!rec) {
151 0 : return false;
152 : }
153 : }
154 0 : if (!SkBitmapScaler::Resize(dst, src.pixmap(), kHQ_RESIZE_METHOD)) {
155 0 : return false; // we failed to create fScaledBitmap
156 : }
157 0 : if (rec) {
158 0 : SkBitmapCache::Add(std::move(rec), &fResultBitmap);
159 0 : SkASSERT(fResultBitmap.getPixels());
160 0 : provider.notifyAddedToCache();
161 : }
162 : }
163 :
164 0 : SkASSERT(fResultBitmap.getPixels());
165 0 : SkASSERT(fResultBitmap.isImmutable());
166 :
167 0 : fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
168 0 : SkIntToScalar(dstH) / provider.height());
169 0 : fQuality = kLow_SkFilterQuality;
170 0 : return true;
171 : }
172 :
173 : /*
174 : * Modulo internal errors, this should always succeed *if* the matrix is downscaling
175 : * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
176 : */
177 141 : bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
178 141 : SkASSERT(fQuality <= kMedium_SkFilterQuality);
179 141 : if (fQuality != kMedium_SkFilterQuality) {
180 141 : return false;
181 : }
182 :
183 : // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
184 : // to a valid bitmap.
185 0 : fQuality = kLow_SkFilterQuality;
186 :
187 : SkSize invScaleSize;
188 0 : if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
189 0 : return false;
190 : }
191 :
192 0 : SkDestinationSurfaceColorMode colorMode = provider.dstColorSpace()
193 0 : ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
194 0 : : SkDestinationSurfaceColorMode::kLegacy;
195 0 : if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
196 0 : fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc(), colorMode));
197 0 : if (nullptr == fCurrMip.get()) {
198 0 : SkBitmap orig;
199 0 : if (!provider.asBitmap(&orig)) {
200 0 : return false;
201 : }
202 0 : fCurrMip.reset(SkMipMapCache::AddAndRef(orig, colorMode));
203 0 : if (nullptr == fCurrMip.get()) {
204 0 : return false;
205 : }
206 : }
207 : // diagnostic for a crasher...
208 0 : if (nullptr == fCurrMip->data()) {
209 0 : sk_throw();
210 : }
211 :
212 0 : const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
213 0 : SkScalarInvert(invScaleSize.height()));
214 0 : SkMipMap::Level level;
215 0 : if (fCurrMip->extractLevel(scale, &level)) {
216 0 : const SkSize& invScaleFixup = level.fScale;
217 0 : fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
218 :
219 : // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
220 : // that here, and not need to explicitly track it ourselves.
221 0 : return fResultBitmap.installPixels(level.fPixmap);
222 : } else {
223 : // failed to extract, so release the mipmap
224 0 : fCurrMip.reset(nullptr);
225 : }
226 : }
227 0 : return false;
228 : }
229 :
230 141 : SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
231 : const SkMatrix& inv,
232 : SkFilterQuality qual,
233 141 : bool canShadeHQ) {
234 141 : fInvMatrix = inv;
235 141 : fQuality = qual;
236 141 : fCanShadeHQ = canShadeHQ;
237 :
238 141 : bool processed = this->processHQRequest(provider) || this->processMediumRequest(provider);
239 :
240 141 : if (processed) {
241 0 : SkASSERT(fResultBitmap.getPixels());
242 : } else {
243 141 : (void)provider.asBitmap(&fResultBitmap);
244 141 : fResultBitmap.lockPixels();
245 : // lock may fail to give us pixels
246 : }
247 141 : SkASSERT(fCanShadeHQ || fQuality <= kLow_SkFilterQuality);
248 :
249 : // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
250 : // and will destroy us if it is nullptr.
251 141 : fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes(),
252 141 : fResultBitmap.getColorTable());
253 141 : }
254 :
255 141 : SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
256 : const SkMatrix& inverse,
257 : SkFilterQuality quality,
258 : void* storage, size_t size) {
259 141 : return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size,
260 141 : bm, inverse, quality, fCanShadeHQ);
261 : }
|