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