blob: 0cf4d6c4ea3a58f5da1f79b2ea54b7cbca360613 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 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 */
reed04617132014-08-21 09:46:49 -07007
8#include "SkBitmapCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkBitmapProcState.h"
10#include "SkColorPriv.h"
11#include "SkFilterProc.h"
12#include "SkPaint.h"
13#include "SkShader.h" // for tilemodes
digit@google.com3ada0ef2012-08-13 14:06:34 +000014#include "SkUtilsArm.h"
humper@google.com138ebc32013-07-19 20:20:04 +000015#include "SkBitmapScaler.h"
reed@google.comd94697c2013-07-24 14:31:33 +000016#include "SkMipMap.h"
reed@google.comcee9dcb2013-09-13 16:04:49 +000017#include "SkPixelRef.h"
commit-bot@chromium.orgf4491562014-05-28 17:30:02 +000018#include "SkImageEncoder.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019
digit@google.com3ada0ef2012-08-13 14:06:34 +000020#if !SK_ARM_NEON_IS_NONE
21// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
22extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
23extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
24extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
25extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
26extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
27extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
28extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
29extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
30#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000031
digit@google.com3ada0ef2012-08-13 14:06:34 +000032#define NAME_WRAP(x) x
33#include "SkBitmapProcState_filter.h"
34#include "SkBitmapProcState_procs.h"
reed@android.comb577b412009-10-27 17:49:32 +000035
reed@android.coma44b4cc2009-07-16 02:03:58 +000036///////////////////////////////////////////////////////////////////////////////
37
reed@google.comee056a82013-04-18 15:33:27 +000038// true iff the matrix contains, at most, scale and translate elements
39static bool matrix_only_scale_translate(const SkMatrix& m) {
40 return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask);
41}
42
reed@google.comc0e88e02012-10-17 21:11:56 +000043/**
44 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
45 * go ahead and treat it as if it were, so that subsequent code can go fast.
46 */
47static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
reed@google.comee056a82013-04-18 15:33:27 +000048 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000049
reed@google.comee056a82013-04-18 15:33:27 +000050 if (matrix.getType() & SkMatrix::kScale_Mask) {
51 SkRect src, dst;
52 bitmap.getBounds(&src);
reed@google.comf707adc2013-04-18 15:37:14 +000053
54 // Can't call mapRect(), since that will fix up inverted rectangles,
55 // e.g. when scale is negative, and we don't want to return true for
56 // those.
57 matrix.mapPoints(SkTCast<SkPoint*>(&dst),
58 SkTCast<const SkPoint*>(&src),
59 2);
reed@google.comee056a82013-04-18 15:33:27 +000060
61 // Now round all 4 edges to device space, and then compare the device
62 // width/height to the original. Note: we must map all 4 and subtract
63 // rather than map the "width" and compare, since we care about the
64 // phase (in pixel space) that any translate in the matrix might impart.
65 SkIRect idst;
66 dst.round(&idst);
67 return idst.width() == bitmap.width() && idst.height() == bitmap.height();
reed@google.comc0e88e02012-10-17 21:11:56 +000068 }
69 // if we got here, we're either kTranslate_Mask or identity
70 return true;
71}
72
73static bool just_trans_general(const SkMatrix& matrix) {
reed@google.comee056a82013-04-18 15:33:27 +000074 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000075
reed@google.comee056a82013-04-18 15:33:27 +000076 if (matrix.getType() & SkMatrix::kScale_Mask) {
reed@google.comc0e88e02012-10-17 21:11:56 +000077 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +000078
reed@google.comc0e88e02012-10-17 21:11:56 +000079 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
80 return false;
81 }
82 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
83 return false;
84 }
85 }
86 // if we got here, treat us as either kTranslate_Mask or identity
87 return true;
88}
89
90///////////////////////////////////////////////////////////////////////////////
91
reed@android.com8a1c16f2008-12-17 15:59:43 +000092static bool valid_for_filtering(unsigned dimension) {
93 // for filtering, width and height must fit in 14bits, since we use steal
94 // 2 bits from each to store our 4bit subpixel data
95 return (dimension & ~0x3FFF) == 0;
96}
97
reed@google.comd94697c2013-07-24 14:31:33 +000098static SkScalar effective_matrix_scale_sqrd(const SkMatrix& mat) {
reed@google.com9cfc83c2013-07-22 17:18:18 +000099 SkPoint v1, v2;
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000100
reed@google.com9cfc83c2013-07-22 17:18:18 +0000101 v1.fX = mat.getScaleX();
102 v1.fY = mat.getSkewY();
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000103
reed@google.com9cfc83c2013-07-22 17:18:18 +0000104 v2.fX = mat.getSkewX();
105 v2.fY = mat.getScaleY();
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000106
reed@google.com9cfc83c2013-07-22 17:18:18 +0000107 return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd());
108}
109
reed@google.comfa7fd802013-12-12 21:37:25 +0000110class AutoScaledCacheUnlocker {
111public:
112 AutoScaledCacheUnlocker(SkScaledImageCache::ID** idPtr) : fIDPtr(idPtr) {}
113 ~AutoScaledCacheUnlocker() {
114 if (fIDPtr && *fIDPtr) {
115 SkScaledImageCache::Unlock(*fIDPtr);
116 *fIDPtr = NULL;
117 }
118 }
119
120 // forgets the ID, so it won't call Unlock
121 void release() {
122 fIDPtr = NULL;
123 }
124
125private:
126 SkScaledImageCache::ID** fIDPtr;
127};
128#define AutoScaledCacheUnlocker(...) SK_REQUIRE_LOCAL_VAR(AutoScaledCacheUnlocker)
129
halcanary805ef152014-07-17 06:58:01 -0700130// Check to see that the size of the bitmap that would be produced by
131// scaling by the given inverted matrix is less than the maximum allowed.
132static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
133 size_t maximumAllocation
134 = SkScaledImageCache::GetSingleAllocationByteLimit();
135 if (0 == maximumAllocation) {
136 return true;
137 }
138 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
139 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
140 // Skip the division step:
141 return bm.info().getSafeSize(bm.info().minRowBytes())
142 < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY());
143}
144
humper@google.com9c96d4b2013-07-14 01:44:59 +0000145// TODO -- we may want to pass the clip into this function so we only scale
146// the portion of the image that we're going to need. This will complicate
147// the interface to the cache, but might be well worth it.
148
reed@google.comcee9dcb2013-09-13 16:04:49 +0000149bool SkBitmapProcState::possiblyScaleImage() {
reed@google.comfa7fd802013-12-12 21:37:25 +0000150 AutoScaledCacheUnlocker unlocker(&fScaledCacheID);
151
reed@google.comcee9dcb2013-09-13 16:04:49 +0000152 SkASSERT(NULL == fBitmap);
153 SkASSERT(NULL == fScaledCacheID);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000154
reed@google.com9cfc83c2013-07-22 17:18:18 +0000155 if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
reed@google.comcee9dcb2013-09-13 16:04:49 +0000156 return false;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000157 }
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000158 // Check to see if the transformation matrix is simple, and if we're
159 // doing high quality scaling. If so, do the bitmap scale here and
humper@google.com138ebc32013-07-19 20:20:04 +0000160 // remove the scaling component from the matrix.
humper@google.com9c96d4b2013-07-14 01:44:59 +0000161
reed@google.com9cfc83c2013-07-22 17:18:18 +0000162 if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
humper@google.com138ebc32013-07-19 20:20:04 +0000163 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
halcanary805ef152014-07-17 06:58:01 -0700164 kN32_SkColorType == fOrigBitmap.colorType() &&
165 cache_size_okay(fOrigBitmap, fInvMatrix)) {
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000166
reed@google.com602a1d72013-07-23 19:13:54 +0000167 SkScalar invScaleX = fInvMatrix.getScaleX();
168 SkScalar invScaleY = fInvMatrix.getScaleY();
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000169
humper8d9153f2014-08-08 12:06:13 -0700170 if (SkScalarNearlyEqual(invScaleX,1.0f) &&
171 SkScalarNearlyEqual(invScaleY,1.0f)) {
172 // short-circuit identity scaling; the output is supposed to
173 // be the same as the input, so we might as well go fast.
174
175 // Note(humper): We could also probably do this if the scales
176 // are close to -1 as well, since the flip doesn't require
177 // any fancy re-sampling...
178
179 // Set our filter level to low -- the only post-filtering this
180 // image might require is some interpolation if the translation
181 // is fractional.
182 fFilterLevel = SkPaint::kLow_FilterLevel;
183 return false;
184 }
185
reed04617132014-08-21 09:46:49 -0700186 fScaledCacheID = SkBitmapCache::FindAndLock(fOrigBitmap, invScaleX, invScaleY,
187 &fScaledBitmap);
reed@google.comfa7fd802013-12-12 21:37:25 +0000188 if (fScaledCacheID) {
189 fScaledBitmap.lockPixels();
190 if (!fScaledBitmap.getPixels()) {
191 fScaledBitmap.unlockPixels();
192 // found a purged entry (discardablememory?), release it
193 SkScaledImageCache::Unlock(fScaledCacheID);
194 fScaledCacheID = NULL;
195 // fall through to rebuild
196 }
197 }
198
reed@google.com602a1d72013-07-23 19:13:54 +0000199 if (NULL == fScaledCacheID) {
commit-bot@chromium.orgf4491562014-05-28 17:30:02 +0000200 float dest_width = fOrigBitmap.width() / invScaleX;
201 float dest_height = fOrigBitmap.height() / invScaleY;
202
reed@google.com602a1d72013-07-23 19:13:54 +0000203 // All the criteria are met; let's make a new bitmap.
humper@google.com138ebc32013-07-19 20:20:04 +0000204
reed@google.com1e182252013-07-24 20:10:42 +0000205 if (!SkBitmapScaler::Resize(&fScaledBitmap,
206 fOrigBitmap,
207 SkBitmapScaler::RESIZE_BEST,
208 dest_width,
209 dest_height,
reed@google.come4eb1222013-12-09 22:29:30 +0000210 SkScaledImageCache::GetAllocator())) {
reed@google.com1e182252013-07-24 20:10:42 +0000211 // we failed to create fScaledBitmap, so just return and let
212 // the scanline proc handle it.
reed@google.com23d0ab72013-10-15 20:14:00 +0000213 return false;
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000214
reed@google.com1e182252013-07-24 20:10:42 +0000215 }
commit-bot@chromium.orgf4491562014-05-28 17:30:02 +0000216
reed@google.comfa7fd802013-12-12 21:37:25 +0000217 SkASSERT(NULL != fScaledBitmap.getPixels());
reed04617132014-08-21 09:46:49 -0700218 fScaledCacheID = SkBitmapCache::AddAndLock(fOrigBitmap, invScaleX, invScaleY,
219 fScaledBitmap);
reed@google.comfa7fd802013-12-12 21:37:25 +0000220 if (!fScaledCacheID) {
221 fScaledBitmap.reset();
222 return false;
223 }
224 SkASSERT(NULL != fScaledBitmap.getPixels());
reed@google.com1ae6c2b2013-10-16 17:34:17 +0000225 }
226
reed@google.comfa7fd802013-12-12 21:37:25 +0000227 SkASSERT(NULL != fScaledBitmap.getPixels());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000228 fBitmap = &fScaledBitmap;
229
230 // set the inv matrix type to translate-only;
reed@google.com40039a32013-09-05 19:09:57 +0000231 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(),
232 fInvMatrix.getTranslateY() / fInvMatrix.getScaleY());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000233
humper8d9153f2014-08-08 12:06:13 -0700234 // Set our filter level to low -- the only post-filtering this
235 // image might require is some interpolation if the translation
236 // is fractional.
237 fFilterLevel = SkPaint::kLow_FilterLevel;
reed@google.comfa7fd802013-12-12 21:37:25 +0000238 unlocker.release();
reed@google.comcee9dcb2013-09-13 16:04:49 +0000239 return true;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000240 }
241
reed@google.com9cfc83c2013-07-22 17:18:18 +0000242 /*
reed@google.com9cfc83c2013-07-22 17:18:18 +0000243 * If High, then our special-case for scale-only did not take, and so we
244 * have to make a choice:
245 * 1. fall back on mipmaps + bilerp
246 * 2. fall back on scanline bicubic filter
247 * For now, we compute the "scale" value from the matrix, and have a
248 * threshold to decide when bicubic is better, and when mips are better.
249 * No doubt a fancier decision tree could be used uere.
250 *
251 * If Medium, then we just try to build a mipmap and select a level,
252 * setting the filter-level to kLow to signal that we just need bilerp
253 * to process the selected level.
254 */
humper@google.com9c96d4b2013-07-14 01:44:59 +0000255
reed@google.com9cfc83c2013-07-22 17:18:18 +0000256 SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000257
reed@google.com9cfc83c2013-07-22 17:18:18 +0000258 if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
259 // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller
260 // than this, then the mipmaps quality may be greater (certainly faster)
261 // so we only keep High quality if the scale is greater than this.
262 //
263 // Since we're dealing with the inverse, we compare against its inverse.
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000264 const SkScalar bicubicLimit = 4.0f;
reed@google.com9cfc83c2013-07-22 17:18:18 +0000265 const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
266 if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline
reed@google.comcee9dcb2013-09-13 16:04:49 +0000267 return false;
reed@google.com9cfc83c2013-07-22 17:18:18 +0000268 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000269
reed@google.com9cfc83c2013-07-22 17:18:18 +0000270 // else set the filter-level to Medium, since we're scaling down and
271 // want to reqeust mipmaps
272 fFilterLevel = SkPaint::kMedium_FilterLevel;
273 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000274
reed@google.com9cfc83c2013-07-22 17:18:18 +0000275 SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000276
reed@google.com9cfc83c2013-07-22 17:18:18 +0000277 /**
278 * Medium quality means use a mipmap for down-scaling, and just bilper
279 * for upscaling. Since we're examining the inverse matrix, we look for
280 * a scale > 1 to indicate down scaling by the CTM.
281 */
282 if (scaleSqd > SK_Scalar1) {
reed@google.comd94697c2013-07-24 14:31:33 +0000283 const SkMipMap* mip = NULL;
284
285 SkASSERT(NULL == fScaledCacheID);
reed04617132014-08-21 09:46:49 -0700286 fScaledCacheID = SkMipMapCache::FindAndLock(fOrigBitmap, &mip);
reed@google.comd94697c2013-07-24 14:31:33 +0000287 if (!fScaledCacheID) {
288 SkASSERT(NULL == mip);
289 mip = SkMipMap::Build(fOrigBitmap);
290 if (mip) {
reed04617132014-08-21 09:46:49 -0700291 fScaledCacheID = SkMipMapCache::AddAndLock(fOrigBitmap, mip);
commit-bot@chromium.org84f7a062014-04-17 16:46:07 +0000292 SkASSERT(mip->getRefCnt() > 1);
reed@google.comd94697c2013-07-24 14:31:33 +0000293 mip->unref(); // the cache took a ref
294 SkASSERT(fScaledCacheID);
295 }
296 } else {
297 SkASSERT(mip);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000298 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000299
reed@google.comd94697c2013-07-24 14:31:33 +0000300 if (mip) {
301 SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd));
302 SkMipMap::Level level;
303 if (mip->extractLevel(levelScale, &level)) {
304 SkScalar invScaleFixup = level.fScale;
305 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000306
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000307 SkImageInfo info = fOrigBitmap.info();
308 info.fWidth = level.fWidth;
309 info.fHeight = level.fHeight;
310 fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000311 fBitmap = &fScaledBitmap;
reed@google.comcee9dcb2013-09-13 16:04:49 +0000312 fFilterLevel = SkPaint::kLow_FilterLevel;
reed@google.comfa7fd802013-12-12 21:37:25 +0000313 unlocker.release();
reed@google.comcee9dcb2013-09-13 16:04:49 +0000314 return true;
reed@google.com9cfc83c2013-07-22 17:18:18 +0000315 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000316 }
317 }
318
reed@google.comcee9dcb2013-09-13 16:04:49 +0000319 return false;
320}
321
322static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) {
323 SkPixelRef* pr = src.pixelRef();
324 if (pr && pr->decodeInto(pow2, dst)) {
325 return true;
326 }
327
reed@google.comd94697c2013-07-24 14:31:33 +0000328 /*
reed@google.comcee9dcb2013-09-13 16:04:49 +0000329 * If decodeInto() fails, it is possibe that we have an old subclass that
330 * does not, or cannot, implement that. In that case we fall back to the
331 * older protocol of having the pixelRef handle the caching for us.
reed@google.comd94697c2013-07-24 14:31:33 +0000332 */
reed@google.comcee9dcb2013-09-13 16:04:49 +0000333 *dst = src;
334 dst->lockPixels();
335 return SkToBool(dst->getPixels());
336}
337
338bool SkBitmapProcState::lockBaseBitmap() {
reed@google.comfa7fd802013-12-12 21:37:25 +0000339 AutoScaledCacheUnlocker unlocker(&fScaledCacheID);
340
reed@google.comcee9dcb2013-09-13 16:04:49 +0000341 SkPixelRef* pr = fOrigBitmap.pixelRef();
342
reed@google.comfa7fd802013-12-12 21:37:25 +0000343 SkASSERT(NULL == fScaledCacheID);
344
reed@google.comcee9dcb2013-09-13 16:04:49 +0000345 if (pr->isLocked() || !pr->implementsDecodeInto()) {
346 // fast-case, no need to look in our cache
347 fScaledBitmap = fOrigBitmap;
reed@google.comfa7fd802013-12-12 21:37:25 +0000348 fScaledBitmap.lockPixels();
349 if (NULL == fScaledBitmap.getPixels()) {
350 return false;
351 }
reed@google.comcee9dcb2013-09-13 16:04:49 +0000352 } else {
reed04617132014-08-21 09:46:49 -0700353 fScaledCacheID = SkBitmapCache::FindAndLock(fOrigBitmap, 1, 1, &fScaledBitmap);
reed@google.comfa7fd802013-12-12 21:37:25 +0000354 if (fScaledCacheID) {
355 fScaledBitmap.lockPixels();
356 if (!fScaledBitmap.getPixels()) {
357 fScaledBitmap.unlockPixels();
358 // found a purged entry (discardablememory?), release it
359 SkScaledImageCache::Unlock(fScaledCacheID);
360 fScaledCacheID = NULL;
361 // fall through to rebuild
362 }
363 }
364
reed@google.comcee9dcb2013-09-13 16:04:49 +0000365 if (NULL == fScaledCacheID) {
366 if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) {
367 return false;
368 }
369
370 // TODO: if fScaled comes back at a different width/height than fOrig,
371 // we need to update the matrix we are using to sample from this guy.
372
reed04617132014-08-21 09:46:49 -0700373 fScaledCacheID = SkBitmapCache::AddAndLock(fOrigBitmap, 1, 1, fScaledBitmap);
reed@google.comcee9dcb2013-09-13 16:04:49 +0000374 if (!fScaledCacheID) {
375 fScaledBitmap.reset();
376 return false;
377 }
378 }
379 }
reed@google.comcee9dcb2013-09-13 16:04:49 +0000380 fBitmap = &fScaledBitmap;
reed@google.comfa7fd802013-12-12 21:37:25 +0000381 unlocker.release();
reed@google.comcee9dcb2013-09-13 16:04:49 +0000382 return true;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000383}
384
reed@google.com1e182252013-07-24 20:10:42 +0000385SkBitmapProcState::~SkBitmapProcState() {
386 if (fScaledCacheID) {
387 SkScaledImageCache::Unlock(fScaledCacheID);
388 }
389 SkDELETE(fBitmapFilter);
390}
391
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
reed@google.comcee9dcb2013-09-13 16:04:49 +0000393 SkASSERT(fOrigBitmap.width() && fOrigBitmap.height());
reed@android.coma44b4cc2009-07-16 02:03:58 +0000394
reed@google.comcee9dcb2013-09-13 16:04:49 +0000395 fBitmap = NULL;
reed@google.comef0e3192013-09-09 13:42:39 +0000396 fInvMatrix = inv;
reed@google.com9cfc83c2013-07-22 17:18:18 +0000397 fFilterLevel = paint.getFilterLevel();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398
reed@google.comfa7fd802013-12-12 21:37:25 +0000399 SkASSERT(NULL == fScaledCacheID);
400
humper@google.com9c96d4b2013-07-14 01:44:59 +0000401 // possiblyScaleImage will look to see if it can rescale the image as a
402 // preprocess; either by scaling up to the target size, or by selecting
403 // a nearby mipmap level. If it does, it will adjust the working
404 // matrix as well as the working bitmap. It may also adjust the filter
405 // quality to avoid re-filtering an already perfectly scaled image.
reed@google.comcee9dcb2013-09-13 16:04:49 +0000406 if (!this->possiblyScaleImage()) {
407 if (!this->lockBaseBitmap()) {
408 return false;
409 }
410 }
reed@google.comc294a972013-10-22 16:17:29 +0000411 // The above logic should have always assigned fBitmap, but in case it
412 // didn't, we check for that now...
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000413 // TODO(dominikg): Ask humper@ if we can just use an SkASSERT(fBitmap)?
reed@google.comc294a972013-10-22 16:17:29 +0000414 if (NULL == fBitmap) {
415 return false;
416 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000417
commit-bot@chromium.org84f7a062014-04-17 16:46:07 +0000418 // If we are "still" kMedium_FilterLevel, then the request was not fulfilled by possiblyScale,
419 // so we downgrade to kLow (so the rest of the sniffing code can assume that)
420 if (SkPaint::kMedium_FilterLevel == fFilterLevel) {
421 fFilterLevel = SkPaint::kLow_FilterLevel;
422 }
423
reed@google.comef0e3192013-09-09 13:42:39 +0000424 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
425 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
426 SkShader::kClamp_TileMode == fTileModeY;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000427
reed@google.comef0e3192013-09-09 13:42:39 +0000428 if (!(clampClamp || trivialMatrix)) {
429 fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
430 }
431
humper@google.com9c96d4b2013-07-14 01:44:59 +0000432 // Now that all possible changes to the matrix have taken place, check
433 // to see if we're really close to a no-scale matrix. If so, explicitly
434 // set it to be so. Subsequent code may inspect this matrix to choose
435 // a faster path in this case.
436
437 // This code will only execute if the matrix has some scale component;
438 // if it's already pure translate then we won't do this inversion.
439
440 if (matrix_only_scale_translate(fInvMatrix)) {
reed@google.comee056a82013-04-18 15:33:27 +0000441 SkMatrix forward;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000442 if (fInvMatrix.invert(&forward)) {
443 if (clampClamp ? just_trans_clamp(forward, *fBitmap)
reed@google.comee056a82013-04-18 15:33:27 +0000444 : just_trans_general(forward)) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000445 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
446 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000447 fInvMatrix.setTranslate(tx, ty);
reed@google.comce1f3cc2013-01-05 14:37:48 +0000448 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000449 }
450 }
451
humper@google.com9c96d4b2013-07-14 01:44:59 +0000452 fInvProc = fInvMatrix.getMapXYProc();
453 fInvType = fInvMatrix.getType();
454 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
455 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
456 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
457 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458
459 fAlphaScale = SkAlpha255To256(paint.getAlpha());
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000460
reed@android.com7a99eb12009-07-16 01:13:14 +0000461 fShaderProc32 = NULL;
462 fShaderProc16 = NULL;
463 fSampleProc32 = NULL;
464 fSampleProc16 = NULL;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000465
humper@google.com9c96d4b2013-07-14 01:44:59 +0000466 // recompute the triviality of the matrix here because we may have
467 // changed it!
468
469 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
470
reed@google.com9cfc83c2013-07-22 17:18:18 +0000471 if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000472 // If this is still set, that means we wanted HQ sampling
473 // but couldn't do it as a preprocess. Let's try to install
474 // the scanline version of the HQ sampler. If that process fails,
475 // downgrade to bilerp.
476
477 // NOTE: Might need to be careful here in the future when we want
478 // to have the platform proc have a shot at this; it's possible that
479 // the chooseBitmapFilterProc will fail to install a shader but a
480 // platform-specific one might succeed, so it might be premature here
481 // to fall back to bilerp. This needs thought.
482
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000483 if (!this->setBitmapFilterProcs()) {
reed@google.com9cfc83c2013-07-22 17:18:18 +0000484 fFilterLevel = SkPaint::kLow_FilterLevel;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000485 }
486 }
487
reed@google.com9cfc83c2013-07-22 17:18:18 +0000488 if (SkPaint::kLow_FilterLevel == fFilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000489 // Only try bilerp if the matrix is "interesting" and
490 // the image has a suitable size.
491
492 if (fInvType <= SkMatrix::kTranslate_Mask ||
reed@google.com9cfc83c2013-07-22 17:18:18 +0000493 !valid_for_filtering(fBitmap->width() | fBitmap->height())) {
494 fFilterLevel = SkPaint::kNone_FilterLevel;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000495 }
496 }
497
498 // At this point, we know exactly what kind of sampling the per-scanline
499 // shader will perform.
500
501 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000502 // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns NULL.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 if (NULL == fMatrixProc) {
504 return false;
505 }
506
507 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000508
reed00adc752014-07-12 21:10:52 -0700509 const SkAlphaType at = fBitmap->alphaType();
510
humper@google.com9c96d4b2013-07-14 01:44:59 +0000511 // No need to do this if we're doing HQ sampling; if filter quality is
512 // still set to HQ by the time we get here, then we must have installed
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000513 // the shader procs above and can skip all this.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514
reed@google.com9cfc83c2013-07-22 17:18:18 +0000515 if (fFilterLevel < SkPaint::kHigh_FilterLevel) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000516
humper@google.com9c96d4b2013-07-14 01:44:59 +0000517 int index = 0;
518 if (fAlphaScale < 256) { // note: this distinction is not used for D16
519 index |= 1;
reed@android.comaa9152a2009-07-17 21:24:56 +0000520 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000521 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
522 index |= 2;
523 }
reed@google.com9cfc83c2013-07-22 17:18:18 +0000524 if (fFilterLevel > SkPaint::kNone_FilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000525 index |= 4;
526 }
527 // bits 3,4,5 encoding the source bitmap format
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000528 switch (fBitmap->colorType()) {
529 case kN32_SkColorType:
reed00adc752014-07-12 21:10:52 -0700530 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
531 return false;
532 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000533 index |= 0;
534 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000535 case kRGB_565_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000536 index |= 8;
537 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000538 case kIndex_8_SkColorType:
reed00adc752014-07-12 21:10:52 -0700539 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
540 return false;
541 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000542 index |= 16;
543 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000544 case kARGB_4444_SkColorType:
reed00adc752014-07-12 21:10:52 -0700545 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
546 return false;
547 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000548 index |= 24;
549 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000550 case kAlpha_8_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000551 index |= 32;
552 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
553 break;
554 default:
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000555 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
humper@google.com9c96d4b2013-07-14 01:44:59 +0000556 return false;
557 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000558
humper@google.com9c96d4b2013-07-14 01:44:59 +0000559 #if !SK_ARM_NEON_IS_ALWAYS
560 static const SampleProc32 gSkBitmapProcStateSample32[] = {
561 S32_opaque_D32_nofilter_DXDY,
562 S32_alpha_D32_nofilter_DXDY,
563 S32_opaque_D32_nofilter_DX,
564 S32_alpha_D32_nofilter_DX,
565 S32_opaque_D32_filter_DXDY,
566 S32_alpha_D32_filter_DXDY,
567 S32_opaque_D32_filter_DX,
568 S32_alpha_D32_filter_DX,
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +0000569
humper@google.com9c96d4b2013-07-14 01:44:59 +0000570 S16_opaque_D32_nofilter_DXDY,
571 S16_alpha_D32_nofilter_DXDY,
572 S16_opaque_D32_nofilter_DX,
573 S16_alpha_D32_nofilter_DX,
574 S16_opaque_D32_filter_DXDY,
575 S16_alpha_D32_filter_DXDY,
576 S16_opaque_D32_filter_DX,
577 S16_alpha_D32_filter_DX,
578
579 SI8_opaque_D32_nofilter_DXDY,
580 SI8_alpha_D32_nofilter_DXDY,
581 SI8_opaque_D32_nofilter_DX,
582 SI8_alpha_D32_nofilter_DX,
583 SI8_opaque_D32_filter_DXDY,
584 SI8_alpha_D32_filter_DXDY,
585 SI8_opaque_D32_filter_DX,
586 SI8_alpha_D32_filter_DX,
587
588 S4444_opaque_D32_nofilter_DXDY,
589 S4444_alpha_D32_nofilter_DXDY,
590 S4444_opaque_D32_nofilter_DX,
591 S4444_alpha_D32_nofilter_DX,
592 S4444_opaque_D32_filter_DXDY,
593 S4444_alpha_D32_filter_DXDY,
594 S4444_opaque_D32_filter_DX,
595 S4444_alpha_D32_filter_DX,
596
597 // A8 treats alpha/opaque the same (equally efficient)
598 SA8_alpha_D32_nofilter_DXDY,
599 SA8_alpha_D32_nofilter_DXDY,
600 SA8_alpha_D32_nofilter_DX,
601 SA8_alpha_D32_nofilter_DX,
602 SA8_alpha_D32_filter_DXDY,
603 SA8_alpha_D32_filter_DXDY,
604 SA8_alpha_D32_filter_DX,
605 SA8_alpha_D32_filter_DX
606 };
607
608 static const SampleProc16 gSkBitmapProcStateSample16[] = {
609 S32_D16_nofilter_DXDY,
610 S32_D16_nofilter_DX,
611 S32_D16_filter_DXDY,
612 S32_D16_filter_DX,
613
614 S16_D16_nofilter_DXDY,
615 S16_D16_nofilter_DX,
616 S16_D16_filter_DXDY,
617 S16_D16_filter_DX,
618
619 SI8_D16_nofilter_DXDY,
620 SI8_D16_nofilter_DX,
621 SI8_D16_filter_DXDY,
622 SI8_D16_filter_DX,
623
624 // Don't support 4444 -> 565
625 NULL, NULL, NULL, NULL,
626 // Don't support A8 -> 565
627 NULL, NULL, NULL, NULL
628 };
629 #endif
630
631 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
632 index >>= 1; // shift away any opaque/alpha distinction
633 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
634
635 // our special-case shaderprocs
636 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
637 if (clampClamp) {
638 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
639 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
640 SkShader::kRepeat_TileMode == fTileModeY) {
641 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
642 }
643 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
644 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
645 }
646
647 if (NULL == fShaderProc32) {
648 fShaderProc32 = this->chooseShaderProc32();
649 }
humper@google.comb0889472013-07-09 21:37:14 +0000650 }
651
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000652 // see if our platform has any accelerated overrides
653 this->platformProcs();
reed@google.com4c69a062013-05-23 20:11:56 +0000654
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 return true;
656}
657
reed@google.com9a4c7462012-10-12 18:21:37 +0000658static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
659 int x, int y,
660 SkPMColor* SK_RESTRICT colors,
661 int count) {
662 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
663 SkASSERT(s.fInvKy == 0);
664 SkASSERT(count > 0 && colors != NULL);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000665 SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000666
reed@google.com9a4c7462012-10-12 18:21:37 +0000667 const int maxX = s.fBitmap->width() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000668 const int maxY = s.fBitmap->height() - 1;
669 int ix = s.fFilterOneX + x;
670 int iy = SkClampMax(s.fFilterOneY + y, maxY);
671#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000672 {
673 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000674 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com9a4c7462012-10-12 18:21:37 +0000675 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000676 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
677 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000678
reed@google.comf7698de2012-10-12 20:50:24 +0000679 SkASSERT(iy == iy2);
680 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000681 }
reed@google.comf7698de2012-10-12 20:50:24 +0000682#endif
683 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000684
reed@google.com9a4c7462012-10-12 18:21:37 +0000685 // clamp to the left
686 if (ix < 0) {
687 int n = SkMin32(-ix, count);
688 sk_memset32(colors, row[0], n);
689 count -= n;
690 if (0 == count) {
691 return;
692 }
693 colors += n;
694 SkASSERT(-ix == n);
695 ix = 0;
696 }
697 // copy the middle
698 if (ix <= maxX) {
699 int n = SkMin32(maxX - ix + 1, count);
700 memcpy(colors, row + ix, n * sizeof(SkPMColor));
701 count -= n;
702 if (0 == count) {
703 return;
704 }
705 colors += n;
706 }
707 SkASSERT(count > 0);
708 // clamp to the right
709 sk_memset32(colors, row[maxX], count);
710}
711
reed@google.coma8d99302012-10-16 20:23:25 +0000712static inline int sk_int_mod(int x, int n) {
713 SkASSERT(n > 0);
714 if ((unsigned)x >= (unsigned)n) {
715 if (x < 0) {
716 x = n + ~(~x % n);
717 } else {
718 x = x % n;
719 }
720 }
721 return x;
722}
723
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000724static inline int sk_int_mirror(int x, int n) {
725 x = sk_int_mod(x, 2 * n);
726 if (x >= n) {
727 x = n + ~(x - n);
728 }
729 return x;
730}
731
reed@google.coma8d99302012-10-16 20:23:25 +0000732static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
733 int x, int y,
734 SkPMColor* SK_RESTRICT colors,
735 int count) {
736 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
737 SkASSERT(s.fInvKy == 0);
738 SkASSERT(count > 0 && colors != NULL);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000739 SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000740
reed@google.coma8d99302012-10-16 20:23:25 +0000741 const int stopX = s.fBitmap->width();
742 const int stopY = s.fBitmap->height();
743 int ix = s.fFilterOneX + x;
744 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
745#ifdef SK_DEBUG
746 {
747 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000748 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.coma8d99302012-10-16 20:23:25 +0000749 SkIntToScalar(y) + SK_ScalarHalf, &pt);
750 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
751 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000752
reed@google.coma8d99302012-10-16 20:23:25 +0000753 SkASSERT(iy == iy2);
754 SkASSERT(ix == ix2);
755 }
756#endif
757 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
758
759 ix = sk_int_mod(ix, stopX);
760 for (;;) {
761 int n = SkMin32(stopX - ix, count);
762 memcpy(colors, row + ix, n * sizeof(SkPMColor));
763 count -= n;
764 if (0 == count) {
765 return;
766 }
767 colors += n;
768 ix = 0;
769 }
770}
771
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000772static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
773 int x, int y,
774 SkPMColor* SK_RESTRICT colors,
775 int count) {
776 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
777 SkASSERT(s.fInvKy == 0);
778 SkASSERT(count > 0 && colors != NULL);
779 SkASSERT(1 == s.fBitmap->width());
780
scroggo@google.comad511322013-02-22 15:50:37 +0000781 int iY0;
782 int iY1 SK_INIT_TO_AVOID_WARNING;
783 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000784
reed@google.com9cfc83c2013-07-22 17:18:18 +0000785 if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000786 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
787 uint32_t xy[2];
788
789 mproc(s, xy, 1, x, y);
790
791 iY0 = xy[0] >> 18;
792 iY1 = xy[0] & 0x3FFF;
793 iSubY = (xy[0] >> 14) & 0xF;
794 } else {
795 int yTemp;
796
797 if (s.fInvType > SkMatrix::kTranslate_Mask) {
798 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000799 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000800 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000801 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000802 &pt);
robertphillips@google.com1e305232013-01-22 20:29:16 +0000803 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000804 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000805 // bitmap's width and height. Since this method is going to do
806 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000807 if (SkShader::kClamp_TileMode != s.fTileModeX ||
808 SkShader::kClamp_TileMode != s.fTileModeY) {
809 yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
810 } else {
811 yTemp = SkScalarFloorToInt(pt.fY);
812 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000813 } else {
814 yTemp = s.fFilterOneY + y;
815 }
816
817 const int stopY = s.fBitmap->height();
818 switch (s.fTileModeY) {
819 case SkShader::kClamp_TileMode:
820 iY0 = SkClampMax(yTemp, stopY-1);
821 break;
822 case SkShader::kRepeat_TileMode:
823 iY0 = sk_int_mod(yTemp, stopY);
824 break;
825 case SkShader::kMirror_TileMode:
826 default:
827 iY0 = sk_int_mirror(yTemp, stopY);
828 break;
829 }
830
831#ifdef SK_DEBUG
832 {
833 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000834 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000835 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000836 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000837 &pt);
robertphillips@google.comd5077752013-01-23 00:36:02 +0000838 if (s.fInvType > SkMatrix::kTranslate_Mask &&
839 (SkShader::kClamp_TileMode != s.fTileModeX ||
840 SkShader::kClamp_TileMode != s.fTileModeY)) {
robertphillips@google.com1e305232013-01-22 20:29:16 +0000841 pt.fY *= s.fBitmap->height();
842 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000843 int iY2;
844
845 switch (s.fTileModeY) {
846 case SkShader::kClamp_TileMode:
847 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
848 break;
849 case SkShader::kRepeat_TileMode:
850 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
851 break;
852 case SkShader::kMirror_TileMode:
853 default:
854 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
855 break;
856 }
857
858 SkASSERT(iY0 == iY2);
859 }
860#endif
861 }
862
863 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
864 SkPMColor color;
865
reed@google.com9cfc83c2013-07-22 17:18:18 +0000866 if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000867 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
868
869 if (s.fAlphaScale < 256) {
870 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
871 } else {
872 Filter_32_opaque(iSubY, *row0, *row1, &color);
873 }
874 } else {
875 if (s.fAlphaScale < 256) {
876 color = SkAlphaMulQ(*row0, s.fAlphaScale);
877 } else {
878 color = *row0;
879 }
880 }
881
882 sk_memset32(colors, color, count);
883}
884
reed@google.com6bb92bc2012-11-20 19:45:16 +0000885static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
886 SkPMColor* SK_RESTRICT colors, int count) {
887 // if we get called, the matrix is too tricky, so we just draw nothing
888 sk_memset32(colors, 0, count);
889}
890
891bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000892 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000893 fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000894
895 /*
896 * if the translate is larger than our ints, we can get random results, or
897 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
898 * negate it.
899 */
900 const SkScalar too_big = SkIntToScalar(1 << 30);
901 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
902 return false;
903 }
904
reed@google.coma8d99302012-10-16 20:23:25 +0000905 // Since we know we're not filtered, we re-purpose these fields allow
906 // us to go from device -> src coordinates w/ just an integer add,
907 // rather than running through the inverse-matrix
908 fFilterOneX = SkScalarFloorToInt(pt.fX);
909 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000910 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000911}
912
reed@google.com9a4c7462012-10-12 18:21:37 +0000913SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000914
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000915 if (kN32_SkColorType != fBitmap->colorType()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000916 return NULL;
917 }
918
919 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
920
921 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
reed@google.com9cfc83c2013-07-22 17:18:18 +0000922 if (SkPaint::kNone_FilterLevel == fFilterLevel &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000923 fInvType <= SkMatrix::kTranslate_Mask &&
924 !this->setupForTranslate()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000925 return DoNothing_shaderproc;
926 }
927 return S32_D32_constX_shaderproc;
928 }
929
reed@google.com9a4c7462012-10-12 18:21:37 +0000930 if (fAlphaScale < 256) {
931 return NULL;
932 }
933 if (fInvType > SkMatrix::kTranslate_Mask) {
934 return NULL;
935 }
reed@google.com9cfc83c2013-07-22 17:18:18 +0000936 if (SkPaint::kNone_FilterLevel != fFilterLevel) {
reed@google.com9a4c7462012-10-12 18:21:37 +0000937 return NULL;
938 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000939
reed@google.coma8d99302012-10-16 20:23:25 +0000940 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
941 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
942
943 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000944 if (this->setupForTranslate()) {
945 return Clamp_S32_D32_nofilter_trans_shaderproc;
946 }
947 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000948 }
reed@google.coma8d99302012-10-16 20:23:25 +0000949 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000950 if (this->setupForTranslate()) {
951 return Repeat_S32_D32_nofilter_trans_shaderproc;
952 }
953 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000954 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000955 return NULL;
956}
957
reed@android.com4c128c42009-08-14 13:54:37 +0000958///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000959
960#ifdef SK_DEBUG
961
962static void check_scale_nofilter(uint32_t bitmapXY[], int count,
963 unsigned mx, unsigned my) {
964 unsigned y = *bitmapXY++;
965 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000966
reed@google.com9fe287b2012-03-27 15:54:28 +0000967 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
968 for (int i = 0; i < count; ++i) {
969 SkASSERT(xptr[i] < mx);
970 }
971}
972
973static void check_scale_filter(uint32_t bitmapXY[], int count,
974 unsigned mx, unsigned my) {
975 uint32_t YY = *bitmapXY++;
976 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000977 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000978 SkASSERT(y0 < my);
979 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000980
reed@google.com9fe287b2012-03-27 15:54:28 +0000981 for (int i = 0; i < count; ++i) {
982 uint32_t XX = bitmapXY[i];
983 unsigned x0 = XX >> 18;
984 unsigned x1 = XX & 0x3FFF;
985 SkASSERT(x0 < mx);
986 SkASSERT(x1 < mx);
987 }
988}
989
990static void check_affine_nofilter(uint32_t bitmapXY[], int count,
991 unsigned mx, unsigned my) {
992 for (int i = 0; i < count; ++i) {
993 uint32_t XY = bitmapXY[i];
994 unsigned x = XY & 0xFFFF;
995 unsigned y = XY >> 16;
996 SkASSERT(x < mx);
997 SkASSERT(y < my);
998 }
999}
1000
1001static void check_affine_filter(uint32_t bitmapXY[], int count,
1002 unsigned mx, unsigned my) {
1003 for (int i = 0; i < count; ++i) {
1004 uint32_t YY = *bitmapXY++;
1005 unsigned y0 = YY >> 18;
1006 unsigned y1 = YY & 0x3FFF;
1007 SkASSERT(y0 < my);
1008 SkASSERT(y1 < my);
1009
1010 uint32_t XX = *bitmapXY++;
1011 unsigned x0 = XX >> 18;
1012 unsigned x1 = XX & 0x3FFF;
1013 SkASSERT(x0 < mx);
1014 SkASSERT(x1 < mx);
1015 }
1016}
1017
1018void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
1019 uint32_t bitmapXY[], int count,
1020 int x, int y) {
1021 SkASSERT(bitmapXY);
1022 SkASSERT(count > 0);
1023
1024 state.fMatrixProc(state, bitmapXY, count, x, y);
1025
1026 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
1027
1028 // There are four formats possible:
1029 // scale -vs- affine
1030 // filter -vs- nofilter
1031 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
reed@google.com9cfc83c2013-07-22 17:18:18 +00001032 proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_scale_filter : check_scale_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +00001033 } else {
reed@google.com9cfc83c2013-07-22 17:18:18 +00001034 proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_affine_filter : check_affine_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +00001035 }
1036 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
1037}
1038
1039SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
1040 return DebugMatrixProc;
1041}
1042
1043#endif
1044
1045///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +00001046/*
1047 The storage requirements for the different matrix procs are as follows,
1048 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001049
reed@android.com4c128c42009-08-14 13:54:37 +00001050 scale/translate nofilter Y(4bytes) + N * X
1051 affine/perspective nofilter N * (X Y)
1052 scale/translate filter Y Y + N * (X X)
1053 affine/perspective filter N * (Y Y X X)
1054 */
1055int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
1056 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +00001057
1058 size &= ~3; // only care about 4-byte aligned chunks
1059 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
1060 size -= 4; // the shared Y (or YY) coordinate
1061 if (size < 0) {
1062 size = 0;
1063 }
reed@android.com258cb222010-04-14 13:36:33 +00001064 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +00001065 } else {
reed@android.com258cb222010-04-14 13:36:33 +00001066 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +00001067 }
1068
reed@google.com9cfc83c2013-07-22 17:18:18 +00001069 if (fFilterLevel != SkPaint::kNone_FilterLevel) {
reed@android.com258cb222010-04-14 13:36:33 +00001070 size >>= 1;
1071 }
1072
1073 return size;
reed@android.com4c128c42009-08-14 13:54:37 +00001074}