OLD | NEW |
---|---|
(Empty) | |
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 "SkMatrix.h" | |
11 | |
12 static bool valid_for_drawing(const SkBitmap& bm) { | |
13 if (0 == bm.width() || 0 == bm.height()) { | |
14 return false; // nothing to draw | |
15 } | |
16 if (NULL == bm.pixelRef()) { | |
17 return false; // no pixels to read | |
18 } | |
19 if (bm.getTexture()) { | |
20 // we can handle texture (ugh) since lockPixels will perform a read-back | |
21 return true; | |
22 } | |
23 if (kIndex_8_SkColorType == bm.colorType()) { | |
24 SkAutoLockPixels alp(bm); // but we need to call it before getColorTable () is safe. | |
25 if (!bm.getColorTable()) { | |
26 return false; | |
27 } | |
28 } | |
29 return true; | |
30 } | |
31 | |
32 bool SkBitmapController::requestBitmap(const SkBitmap& inBM, const SkMatrix& inv , | |
33 SkFilterQuality inQuality, SkBitmap* outB M, SkMatrix* outInv, | |
34 SkFilterQuality* outQuality) { | |
35 if (!valid_for_drawing(inBM)) { | |
36 return false; | |
37 } | |
38 return this->onRequestBitmap(inBM, inv, inQuality, outBM, outInv, outQuality ); | |
39 } | |
40 | |
41 //////////////////////////////////////////////////////////////////////////////// /////////////////// | |
42 | |
43 #include "SkBitmapCache.h" | |
44 #include "SkBitmapScaler.h" | |
45 #include "SkMipMap.h" | |
46 #include "SkResourceCache.h" | |
47 | |
48 class SkDefaultBitmapControllerState { | |
49 public: | |
50 SkDefaultBitmapControllerState(const SkBitmap& src, const SkMatrix& inv, SkF ilterQuality qual) | |
51 : fOrigBitmap(src), fInvMatrix(inv), fFilterLevel(qual) | |
52 {} | |
53 | |
54 const SkBitmap& fOrigBitmap; | |
55 SkMatrix fInvMatrix; | |
56 SkFilterQuality fFilterLevel; | |
57 | |
58 SkBitmap fScaledBitmap; | |
59 SkAutoTUnref<const SkMipMap> fCurrMip; | |
60 | |
61 bool processHQRequest(); | |
62 bool processMediumRequest(); | |
63 }; | |
64 | |
65 // Check to see that the size of the bitmap that would be produced by | |
66 // scaling by the given inverted matrix is less than the maximum allowed. | |
67 static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) { | |
68 size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByte Limit(); | |
69 if (0 == maximumAllocation) { | |
70 return true; | |
71 } | |
72 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY); | |
73 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize); | |
74 // Skip the division step: | |
75 const size_t size = bm.info().getSafeSize(bm.info().minRowBytes()); | |
76 return size < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY()); | |
77 } | |
78 | |
79 /* | |
80 * High quality is implemented by performing up-right scale-only filtering and then | |
81 * using bilerp for any remaining transformations. | |
82 */ | |
83 bool SkDefaultBitmapControllerState::processHQRequest() { | |
84 SkASSERT(kHigh_SkFilterQuality == fFilterLevel); | |
85 | |
86 // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap | |
87 // to a valid bitmap. If we succeed, we will set this to Low instead. | |
88 fFilterLevel = kMedium_SkFilterQuality; | |
89 | |
90 if (kN32_SkColorType != fOrigBitmap.colorType() || !cache_size_okay(fOrigBit map, fInvMatrix) || | |
91 fInvMatrix.hasPerspective()) | |
92 { | |
93 return false; // can't handle the reqeust | |
94 } | |
95 | |
96 SkScalar invScaleX = fInvMatrix.getScaleX(); | |
97 SkScalar invScaleY = fInvMatrix.getScaleY(); | |
98 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) { | |
99 SkSize scale; | |
100 if (!fInvMatrix.decomposeScale(&scale)) { | |
101 return false; | |
102 } | |
103 invScaleX = scale.width(); | |
104 invScaleY = scale.height(); | |
105 } | |
106 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) { | |
107 return false; // no need for HQ | |
108 } | |
109 | |
110 SkScalar trueDestWidth = fOrigBitmap.width() / invScaleX; | |
111 SkScalar trueDestHeight = fOrigBitmap.height() / invScaleY; | |
112 SkScalar roundedDestWidth = SkScalarRoundToScalar(trueDestWidth); | |
113 SkScalar roundedDestHeight = SkScalarRoundToScalar(trueDestHeight); | |
114 | |
115 if (!SkBitmapCache::Find(fOrigBitmap, roundedDestWidth, roundedDestHeight, & fScaledBitmap)) { | |
116 if (!SkBitmapScaler::Resize(&fScaledBitmap, | |
117 fOrigBitmap, | |
118 SkBitmapScaler::RESIZE_BEST, | |
119 roundedDestWidth, | |
120 roundedDestHeight, | |
121 SkResourceCache::GetAllocator())) { | |
122 return false; // we failed to create fScaledBitmap | |
123 } | |
124 | |
125 SkASSERT(fScaledBitmap.getPixels()); | |
126 fScaledBitmap.setImmutable(); | |
127 SkBitmapCache::Add(fOrigBitmap, roundedDestWidth, roundedDestHeight, fSc aledBitmap); | |
128 } | |
129 | |
130 SkASSERT(fScaledBitmap.getPixels()); | |
131 | |
132 fInvMatrix.postScale(roundedDestWidth / fOrigBitmap.width(), | |
133 roundedDestHeight / fOrigBitmap.height()); | |
134 fFilterLevel = kLow_SkFilterQuality; | |
135 return true; | |
136 } | |
137 | |
138 /* | |
139 * Modulo internal errors, this should always succeed *if* the matrix is downsc aling | |
140 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscalin g) | |
141 */ | |
142 bool SkDefaultBitmapControllerState::processMediumRequest() { | |
143 SkASSERT(kMedium_SkFilterQuality == fFilterLevel); | |
144 | |
145 // Our default return state is to downgrade the request to Low, w/ or w/o se tting fBitmap | |
146 // to a valid bitmap. | |
147 fFilterLevel = kLow_SkFilterQuality; | |
148 | |
149 SkSize invScaleSize; | |
150 if (!fInvMatrix.decomposeScale(&invScaleSize, NULL)) { | |
151 return false; | |
152 } | |
153 SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height( )); | |
154 | |
155 if (invScale > SK_Scalar1) { | |
156 fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap)); | |
157 if (NULL == fCurrMip.get()) { | |
158 fCurrMip.reset(SkMipMapCache::AddAndRef(fOrigBitmap)); | |
159 if (NULL == fCurrMip.get()) { | |
160 return false; | |
161 } | |
162 } | |
163 // diagnostic for a crasher... | |
164 if (NULL == fCurrMip->data()) { | |
165 sk_throw(); | |
166 } | |
167 | |
168 SkScalar levelScale = SkScalarInvert(invScale); | |
169 SkMipMap::Level level; | |
170 if (fCurrMip->extractLevel(levelScale, &level)) { | |
171 SkScalar invScaleFixup = level.fScale; | |
172 fInvMatrix.postScale(invScaleFixup, invScaleFixup); | |
173 | |
174 const SkImageInfo info = fOrigBitmap.info().makeWH(level.fWidth, lev el.fHeight); | |
175 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install | |
176 // that here, and not need to explicitly track it ourselves. | |
177 fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes); | |
scroggo
2015/06/02 13:24:53
Should we check the return value? (Even just to as
reed1
2015/06/02 14:07:46
Done.
| |
178 return true; | |
179 } else { | |
180 // failed to extract, so release the mipmap | |
181 fCurrMip.reset(NULL); | |
182 } | |
183 } | |
184 return false; | |
185 } | |
186 | |
187 bool SkDefaultBitmapController::onRequestBitmap(const SkBitmap& inBM, const SkMa trix& inInverse, | |
188 SkFilterQuality inQuality, SkBit map* outBM, | |
189 SkMatrix* outInverse, SkFilterQu ality* outQuality) { | |
190 SkDefaultBitmapControllerState state(inBM, inInverse, inQuality); | |
191 | |
192 if (!state.processHQRequest() && !state.processHQRequest()) { | |
scroggo
2015/06/02 13:24:54
Two questions:
- if inQuality is not kHigh, won't
reed1
2015/06/02 14:07:46
Done.
| |
193 state.fScaledBitmap = inBM; | |
194 } | |
195 SkASSERT(state.fFilterLevel <= kLow_SkFilterQuality); | |
196 *outBM = state.fScaledBitmap; | |
197 *outInverse = inInverse; | |
198 *outQuality = state.fFilterLevel; | |
199 return true; | |
200 } | |
201 | |
OLD | NEW |