blob: c8164a35c79109e398fe45b81726c181b02b246b [file] [log] [blame]
reed64045422015-06-04 06:31:31 -07001/*
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 "SkMatrix.h"
11
12///////////////////////////////////////////////////////////////////////////////////////////////////
13
14static bool valid_for_drawing(const SkBitmap& bm) {
15 if (0 == bm.width() || 0 == bm.height()) {
16 return false; // nothing to draw
17 }
18 if (NULL == bm.pixelRef()) {
19 return false; // no pixels to read
20 }
21 if (bm.getTexture()) {
22 // we can handle texture (ugh) since lockPixels will perform a read-back
23 return true;
24 }
25 if (kIndex_8_SkColorType == bm.colorType()) {
26 SkAutoLockPixels alp(bm); // but we need to call it before getColorTable() is safe.
27 if (!bm.getColorTable()) {
28 return false;
29 }
30 }
31 return true;
32}
33
34SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmap& bm,
35 const SkMatrix& inv,
36 SkFilterQuality quality,
37 void* storage, size_t storageSize) {
38
39 if (!valid_for_drawing(bm)) {
40 return NULL;
41 }
42
43 State* state = this->onRequestBitmap(bm, inv, quality, storage, storageSize);
44 if (state) {
reedad7ae6c2015-06-04 14:12:25 -070045 if (NULL == state->fPixmap.addr()) {
46 SkInPlaceDeleteCheck(state, storage);
47 state = NULL;
reed64045422015-06-04 06:31:31 -070048 }
49 }
50 return state;
51}
52
53///////////////////////////////////////////////////////////////////////////////////////////////////
54
55#include "SkBitmapCache.h"
56#include "SkBitmapScaler.h"
57#include "SkMipMap.h"
58#include "SkResourceCache.h"
59
60class SkDefaultBitmapControllerState : public SkBitmapController::State {
61public:
62 SkDefaultBitmapControllerState(const SkBitmap& src, const SkMatrix& inv, SkFilterQuality qual);
63
64private:
reedad7ae6c2015-06-04 14:12:25 -070065 SkBitmap fResultBitmap;
reed64045422015-06-04 06:31:31 -070066 SkAutoTUnref<const SkMipMap> fCurrMip;
67
68 bool processHQRequest(const SkBitmap& orig);
69 bool processMediumRequest(const SkBitmap& orig);
70};
71
72// Check to see that the size of the bitmap that would be produced by
73// scaling by the given inverted matrix is less than the maximum allowed.
74static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
75 size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit();
76 if (0 == maximumAllocation) {
77 return true;
78 }
79 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
80 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
81 // Skip the division step:
82 const size_t size = bm.info().getSafeSize(bm.info().minRowBytes());
83 return size < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY());
84}
85
86/*
87 * High quality is implemented by performing up-right scale-only filtering and then
88 * using bilerp for any remaining transformations.
89 */
90bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap) {
91 if (fQuality != kHigh_SkFilterQuality) {
92 return false;
93 }
94
95 // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
96 // to a valid bitmap. If we succeed, we will set this to Low instead.
97 fQuality = kMedium_SkFilterQuality;
98
99 if (kN32_SkColorType != origBitmap.colorType() || !cache_size_okay(origBitmap, fInvMatrix) ||
100 fInvMatrix.hasPerspective())
101 {
102 return false; // can't handle the reqeust
103 }
104
105 SkScalar invScaleX = fInvMatrix.getScaleX();
106 SkScalar invScaleY = fInvMatrix.getScaleY();
107 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
108 SkSize scale;
109 if (!fInvMatrix.decomposeScale(&scale)) {
110 return false;
111 }
112 invScaleX = scale.width();
113 invScaleY = scale.height();
114 }
115 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
116 return false; // no need for HQ
117 }
118
119 SkScalar trueDestWidth = origBitmap.width() / invScaleX;
120 SkScalar trueDestHeight = origBitmap.height() / invScaleY;
121 SkScalar roundedDestWidth = SkScalarRoundToScalar(trueDestWidth);
122 SkScalar roundedDestHeight = SkScalarRoundToScalar(trueDestHeight);
123
reedad7ae6c2015-06-04 14:12:25 -0700124 if (!SkBitmapCache::Find(origBitmap, roundedDestWidth, roundedDestHeight, &fResultBitmap)) {
reed3c834322015-06-12 07:09:59 -0700125 SkAutoPixmapUnlock src;
126 if (!origBitmap.requestLock(&src)) {
127 return false;
128 }
129 if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), SkBitmapScaler::RESIZE_BEST,
130 roundedDestWidth, roundedDestHeight,
reed64045422015-06-04 06:31:31 -0700131 SkResourceCache::GetAllocator())) {
132 return false; // we failed to create fScaledBitmap
133 }
134
reedad7ae6c2015-06-04 14:12:25 -0700135 SkASSERT(fResultBitmap.getPixels());
136 fResultBitmap.setImmutable();
137 SkBitmapCache::Add(origBitmap, roundedDestWidth, roundedDestHeight, fResultBitmap);
reed64045422015-06-04 06:31:31 -0700138 }
139
reedad7ae6c2015-06-04 14:12:25 -0700140 SkASSERT(fResultBitmap.getPixels());
reed64045422015-06-04 06:31:31 -0700141
142 fInvMatrix.postScale(roundedDestWidth / origBitmap.width(),
143 roundedDestHeight / origBitmap.height());
144 fQuality = kLow_SkFilterQuality;
145 return true;
146}
147
148/*
149 * Modulo internal errors, this should always succeed *if* the matrix is downscaling
150 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
151 */
152bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBitmap) {
153 SkASSERT(fQuality <= kMedium_SkFilterQuality);
154 if (fQuality != kMedium_SkFilterQuality) {
155 return false;
156 }
157
158 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
159 // to a valid bitmap.
160 fQuality = kLow_SkFilterQuality;
161
162 SkSize invScaleSize;
163 if (!fInvMatrix.decomposeScale(&invScaleSize, NULL)) {
164 return false;
165 }
166 SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height());
167
168 if (invScale > SK_Scalar1) {
169 fCurrMip.reset(SkMipMapCache::FindAndRef(origBitmap));
170 if (NULL == fCurrMip.get()) {
171 fCurrMip.reset(SkMipMapCache::AddAndRef(origBitmap));
172 if (NULL == fCurrMip.get()) {
173 return false;
174 }
175 }
176 // diagnostic for a crasher...
177 if (NULL == fCurrMip->data()) {
178 sk_throw();
179 }
180
181 SkScalar levelScale = SkScalarInvert(invScale);
182 SkMipMap::Level level;
183 if (fCurrMip->extractLevel(levelScale, &level)) {
184 SkScalar invScaleFixup = level.fScale;
185 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
186
187 const SkImageInfo info = origBitmap.info().makeWH(level.fWidth, level.fHeight);
188 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
189 // that here, and not need to explicitly track it ourselves.
reedad7ae6c2015-06-04 14:12:25 -0700190 return fResultBitmap.installPixels(info, level.fPixels, level.fRowBytes);
reed64045422015-06-04 06:31:31 -0700191 } else {
192 // failed to extract, so release the mipmap
193 fCurrMip.reset(NULL);
194 }
195 }
196 return false;
197}
198
199SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmap& src,
200 const SkMatrix& inv,
201 SkFilterQuality qual) {
202 fInvMatrix = inv;
203 fQuality = qual;
204
reedad7ae6c2015-06-04 14:12:25 -0700205 if (this->processHQRequest(src) || this->processMediumRequest(src)) {
206 SkASSERT(fResultBitmap.getPixels());
207 } else {
208 fResultBitmap = src;
209 fResultBitmap.lockPixels();
210 // lock may fail to give us pixels
reed64045422015-06-04 06:31:31 -0700211 }
212 SkASSERT(fQuality <= kLow_SkFilterQuality);
reedad7ae6c2015-06-04 14:12:25 -0700213
214 // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
215 // and will destroy us if it is NULL.
216 fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes(),
217 fResultBitmap.getColorTable());
reed64045422015-06-04 06:31:31 -0700218}
219
220SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmap& bm,
221 const SkMatrix& inverse,
222 SkFilterQuality quality,
223 void* storage, size_t size) {
224 return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality);
225}
226