blob: 220eb56316eacf4c6a532aceea89c5943d27b9f8 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkBitmapProcState.h"
9#include "SkColorPriv.h"
10#include "SkFilterProc.h"
11#include "SkPaint.h"
12#include "SkShader.h" // for tilemodes
digit@google.com3ada0ef2012-08-13 14:06:34 +000013#include "SkUtilsArm.h"
humper@google.com138ebc32013-07-19 20:20:04 +000014#include "SkBitmapScaler.h"
reed@google.comd94697c2013-07-24 14:31:33 +000015#include "SkMipMap.h"
reed@google.comcee9dcb2013-09-13 16:04:49 +000016#include "SkPixelRef.h"
reed@google.com602a1d72013-07-23 19:13:54 +000017#include "SkScaledImageCache.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
humper@google.com9c96d4b2013-07-14 01:44:59 +0000130// TODO -- we may want to pass the clip into this function so we only scale
131// the portion of the image that we're going to need. This will complicate
132// the interface to the cache, but might be well worth it.
133
reed@google.comcee9dcb2013-09-13 16:04:49 +0000134bool SkBitmapProcState::possiblyScaleImage() {
reed@google.comfa7fd802013-12-12 21:37:25 +0000135 AutoScaledCacheUnlocker unlocker(&fScaledCacheID);
136
reed@google.comcee9dcb2013-09-13 16:04:49 +0000137 SkASSERT(NULL == fBitmap);
138 SkASSERT(NULL == fScaledCacheID);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000139
reed@google.com9cfc83c2013-07-22 17:18:18 +0000140 if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
reed@google.comcee9dcb2013-09-13 16:04:49 +0000141 return false;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000142 }
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000143
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000144 // Check to see if the transformation matrix is simple, and if we're
145 // doing high quality scaling. If so, do the bitmap scale here and
humper@google.com138ebc32013-07-19 20:20:04 +0000146 // remove the scaling component from the matrix.
humper@google.com9c96d4b2013-07-14 01:44:59 +0000147
reed@google.com9cfc83c2013-07-22 17:18:18 +0000148 if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
humper@google.com138ebc32013-07-19 20:20:04 +0000149 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000150 fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) {
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000151
reed@google.com602a1d72013-07-23 19:13:54 +0000152 SkScalar invScaleX = fInvMatrix.getScaleX();
153 SkScalar invScaleY = fInvMatrix.getScaleY();
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000154
reed@google.com602a1d72013-07-23 19:13:54 +0000155 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap,
156 invScaleX, invScaleY,
157 &fScaledBitmap);
reed@google.comfa7fd802013-12-12 21:37:25 +0000158 if (fScaledCacheID) {
159 fScaledBitmap.lockPixels();
160 if (!fScaledBitmap.getPixels()) {
161 fScaledBitmap.unlockPixels();
162 // found a purged entry (discardablememory?), release it
163 SkScaledImageCache::Unlock(fScaledCacheID);
164 fScaledCacheID = NULL;
165 // fall through to rebuild
166 }
167 }
168
reed@google.com602a1d72013-07-23 19:13:54 +0000169 if (NULL == fScaledCacheID) {
commit-bot@chromium.orgf4491562014-05-28 17:30:02 +0000170 float dest_width = fOrigBitmap.width() / invScaleX;
171 float dest_height = fOrigBitmap.height() / invScaleY;
172
173#ifdef SK_IGNORE_CORRECT_HIGH_QUALITY_IMAGE_SCALE
174 dest_width = SkScalarCeilToScalar(dest_width);
175 dest_height = SkScalarCeilToScalar(dest_height);
176#endif
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000177
reed@google.com602a1d72013-07-23 19:13:54 +0000178 // All the criteria are met; let's make a new bitmap.
humper@google.com138ebc32013-07-19 20:20:04 +0000179
reed@google.come15d9ec2013-09-06 12:57:45 +0000180 SkConvolutionProcs simd;
181 sk_bzero(&simd, sizeof(simd));
182 this->platformConvolutionProcs(&simd);
183
reed@google.com1e182252013-07-24 20:10:42 +0000184 if (!SkBitmapScaler::Resize(&fScaledBitmap,
185 fOrigBitmap,
186 SkBitmapScaler::RESIZE_BEST,
187 dest_width,
188 dest_height,
reed@google.come4eb1222013-12-09 22:29:30 +0000189 simd,
190 SkScaledImageCache::GetAllocator())) {
reed@google.com1e182252013-07-24 20:10:42 +0000191 // we failed to create fScaledBitmap, so just return and let
192 // the scanline proc handle it.
reed@google.com23d0ab72013-10-15 20:14:00 +0000193 return false;
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000194
reed@google.com1e182252013-07-24 20:10:42 +0000195 }
commit-bot@chromium.orgf4491562014-05-28 17:30:02 +0000196
reed@google.comfa7fd802013-12-12 21:37:25 +0000197 SkASSERT(NULL != fScaledBitmap.getPixels());
reed@google.com602a1d72013-07-23 19:13:54 +0000198 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap,
199 invScaleX,
200 invScaleY,
201 fScaledBitmap);
reed@google.comfa7fd802013-12-12 21:37:25 +0000202 if (!fScaledCacheID) {
203 fScaledBitmap.reset();
204 return false;
205 }
206 SkASSERT(NULL != fScaledBitmap.getPixels());
reed@google.com1ae6c2b2013-10-16 17:34:17 +0000207 }
208
reed@google.comfa7fd802013-12-12 21:37:25 +0000209 SkASSERT(NULL != fScaledBitmap.getPixels());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000210 fBitmap = &fScaledBitmap;
211
212 // set the inv matrix type to translate-only;
reed@google.com40039a32013-09-05 19:09:57 +0000213 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(),
214 fInvMatrix.getTranslateY() / fInvMatrix.getScaleY());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000215
216 // no need for any further filtering; we just did it!
reed@google.com9cfc83c2013-07-22 17:18:18 +0000217 fFilterLevel = SkPaint::kNone_FilterLevel;
reed@google.comfa7fd802013-12-12 21:37:25 +0000218 unlocker.release();
reed@google.comcee9dcb2013-09-13 16:04:49 +0000219 return true;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000220 }
221
reed@google.com9cfc83c2013-07-22 17:18:18 +0000222 /*
reed@google.com9cfc83c2013-07-22 17:18:18 +0000223 * If High, then our special-case for scale-only did not take, and so we
224 * have to make a choice:
225 * 1. fall back on mipmaps + bilerp
226 * 2. fall back on scanline bicubic filter
227 * For now, we compute the "scale" value from the matrix, and have a
228 * threshold to decide when bicubic is better, and when mips are better.
229 * No doubt a fancier decision tree could be used uere.
230 *
231 * If Medium, then we just try to build a mipmap and select a level,
232 * setting the filter-level to kLow to signal that we just need bilerp
233 * to process the selected level.
234 */
humper@google.com9c96d4b2013-07-14 01:44:59 +0000235
reed@google.com9cfc83c2013-07-22 17:18:18 +0000236 SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000237
reed@google.com9cfc83c2013-07-22 17:18:18 +0000238 if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
239 // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller
240 // than this, then the mipmaps quality may be greater (certainly faster)
241 // so we only keep High quality if the scale is greater than this.
242 //
243 // Since we're dealing with the inverse, we compare against its inverse.
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000244 const SkScalar bicubicLimit = 4.0f;
reed@google.com9cfc83c2013-07-22 17:18:18 +0000245 const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
246 if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline
reed@google.comcee9dcb2013-09-13 16:04:49 +0000247 return false;
reed@google.com9cfc83c2013-07-22 17:18:18 +0000248 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000249
reed@google.com9cfc83c2013-07-22 17:18:18 +0000250 // else set the filter-level to Medium, since we're scaling down and
251 // want to reqeust mipmaps
252 fFilterLevel = SkPaint::kMedium_FilterLevel;
253 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000254
reed@google.com9cfc83c2013-07-22 17:18:18 +0000255 SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000256
reed@google.com9cfc83c2013-07-22 17:18:18 +0000257 /**
258 * Medium quality means use a mipmap for down-scaling, and just bilper
259 * for upscaling. Since we're examining the inverse matrix, we look for
260 * a scale > 1 to indicate down scaling by the CTM.
261 */
262 if (scaleSqd > SK_Scalar1) {
reed@google.comd94697c2013-07-24 14:31:33 +0000263 const SkMipMap* mip = NULL;
264
265 SkASSERT(NULL == fScaledCacheID);
266 fScaledCacheID = SkScaledImageCache::FindAndLockMip(fOrigBitmap, &mip);
267 if (!fScaledCacheID) {
268 SkASSERT(NULL == mip);
269 mip = SkMipMap::Build(fOrigBitmap);
270 if (mip) {
271 fScaledCacheID = SkScaledImageCache::AddAndLockMip(fOrigBitmap,
272 mip);
commit-bot@chromium.org84f7a062014-04-17 16:46:07 +0000273 SkASSERT(mip->getRefCnt() > 1);
reed@google.comd94697c2013-07-24 14:31:33 +0000274 mip->unref(); // the cache took a ref
275 SkASSERT(fScaledCacheID);
276 }
277 } else {
278 SkASSERT(mip);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000279 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000280
reed@google.comd94697c2013-07-24 14:31:33 +0000281 if (mip) {
282 SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd));
283 SkMipMap::Level level;
284 if (mip->extractLevel(levelScale, &level)) {
285 SkScalar invScaleFixup = level.fScale;
286 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000287
reed@google.comd94697c2013-07-24 14:31:33 +0000288 fScaledBitmap.setConfig(fOrigBitmap.config(),
289 level.fWidth, level.fHeight,
290 level.fRowBytes);
291 fScaledBitmap.setPixels(level.fPixels);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000292 fBitmap = &fScaledBitmap;
reed@google.comcee9dcb2013-09-13 16:04:49 +0000293 fFilterLevel = SkPaint::kLow_FilterLevel;
reed@google.comfa7fd802013-12-12 21:37:25 +0000294 unlocker.release();
reed@google.comcee9dcb2013-09-13 16:04:49 +0000295 return true;
reed@google.com9cfc83c2013-07-22 17:18:18 +0000296 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000297 }
298 }
299
reed@google.comcee9dcb2013-09-13 16:04:49 +0000300 return false;
301}
302
303static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) {
304 SkPixelRef* pr = src.pixelRef();
305 if (pr && pr->decodeInto(pow2, dst)) {
306 return true;
307 }
308
reed@google.comd94697c2013-07-24 14:31:33 +0000309 /*
reed@google.comcee9dcb2013-09-13 16:04:49 +0000310 * If decodeInto() fails, it is possibe that we have an old subclass that
311 * does not, or cannot, implement that. In that case we fall back to the
312 * older protocol of having the pixelRef handle the caching for us.
reed@google.comd94697c2013-07-24 14:31:33 +0000313 */
reed@google.comcee9dcb2013-09-13 16:04:49 +0000314 *dst = src;
315 dst->lockPixels();
316 return SkToBool(dst->getPixels());
317}
318
319bool SkBitmapProcState::lockBaseBitmap() {
reed@google.comfa7fd802013-12-12 21:37:25 +0000320 AutoScaledCacheUnlocker unlocker(&fScaledCacheID);
321
reed@google.comcee9dcb2013-09-13 16:04:49 +0000322 SkPixelRef* pr = fOrigBitmap.pixelRef();
323
reed@google.comfa7fd802013-12-12 21:37:25 +0000324 SkASSERT(NULL == fScaledCacheID);
325
reed@google.comcee9dcb2013-09-13 16:04:49 +0000326 if (pr->isLocked() || !pr->implementsDecodeInto()) {
327 // fast-case, no need to look in our cache
328 fScaledBitmap = fOrigBitmap;
reed@google.comfa7fd802013-12-12 21:37:25 +0000329 fScaledBitmap.lockPixels();
330 if (NULL == fScaledBitmap.getPixels()) {
331 return false;
332 }
reed@google.comcee9dcb2013-09-13 16:04:49 +0000333 } else {
334 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap,
335 SK_Scalar1, SK_Scalar1,
336 &fScaledBitmap);
reed@google.comfa7fd802013-12-12 21:37:25 +0000337 if (fScaledCacheID) {
338 fScaledBitmap.lockPixels();
339 if (!fScaledBitmap.getPixels()) {
340 fScaledBitmap.unlockPixels();
341 // found a purged entry (discardablememory?), release it
342 SkScaledImageCache::Unlock(fScaledCacheID);
343 fScaledCacheID = NULL;
344 // fall through to rebuild
345 }
346 }
347
reed@google.comcee9dcb2013-09-13 16:04:49 +0000348 if (NULL == fScaledCacheID) {
349 if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) {
350 return false;
351 }
352
353 // TODO: if fScaled comes back at a different width/height than fOrig,
354 // we need to update the matrix we are using to sample from this guy.
355
356 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap,
357 SK_Scalar1, SK_Scalar1,
358 fScaledBitmap);
359 if (!fScaledCacheID) {
360 fScaledBitmap.reset();
361 return false;
362 }
363 }
364 }
reed@google.comcee9dcb2013-09-13 16:04:49 +0000365 fBitmap = &fScaledBitmap;
reed@google.comfa7fd802013-12-12 21:37:25 +0000366 unlocker.release();
reed@google.comcee9dcb2013-09-13 16:04:49 +0000367 return true;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000368}
369
reed@google.com1e182252013-07-24 20:10:42 +0000370SkBitmapProcState::~SkBitmapProcState() {
371 if (fScaledCacheID) {
372 SkScaledImageCache::Unlock(fScaledCacheID);
373 }
374 SkDELETE(fBitmapFilter);
375}
376
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
reed@google.comcee9dcb2013-09-13 16:04:49 +0000378 SkASSERT(fOrigBitmap.width() && fOrigBitmap.height());
reed@android.coma44b4cc2009-07-16 02:03:58 +0000379
reed@google.comcee9dcb2013-09-13 16:04:49 +0000380 fBitmap = NULL;
reed@google.comef0e3192013-09-09 13:42:39 +0000381 fInvMatrix = inv;
reed@google.com9cfc83c2013-07-22 17:18:18 +0000382 fFilterLevel = paint.getFilterLevel();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383
reed@google.comfa7fd802013-12-12 21:37:25 +0000384 SkASSERT(NULL == fScaledCacheID);
385
humper@google.com9c96d4b2013-07-14 01:44:59 +0000386 // possiblyScaleImage will look to see if it can rescale the image as a
387 // preprocess; either by scaling up to the target size, or by selecting
388 // a nearby mipmap level. If it does, it will adjust the working
389 // matrix as well as the working bitmap. It may also adjust the filter
390 // quality to avoid re-filtering an already perfectly scaled image.
reed@google.comcee9dcb2013-09-13 16:04:49 +0000391 if (!this->possiblyScaleImage()) {
392 if (!this->lockBaseBitmap()) {
393 return false;
394 }
395 }
reed@google.comc294a972013-10-22 16:17:29 +0000396 // The above logic should have always assigned fBitmap, but in case it
397 // didn't, we check for that now...
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000398 // TODO(dominikg): Ask humper@ if we can just use an SkASSERT(fBitmap)?
reed@google.comc294a972013-10-22 16:17:29 +0000399 if (NULL == fBitmap) {
400 return false;
401 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000402
commit-bot@chromium.org84f7a062014-04-17 16:46:07 +0000403 // If we are "still" kMedium_FilterLevel, then the request was not fulfilled by possiblyScale,
404 // so we downgrade to kLow (so the rest of the sniffing code can assume that)
405 if (SkPaint::kMedium_FilterLevel == fFilterLevel) {
406 fFilterLevel = SkPaint::kLow_FilterLevel;
407 }
408
reed@google.comef0e3192013-09-09 13:42:39 +0000409 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
410 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
411 SkShader::kClamp_TileMode == fTileModeY;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000412
reed@google.comef0e3192013-09-09 13:42:39 +0000413 if (!(clampClamp || trivialMatrix)) {
414 fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
415 }
416
humper@google.com9c96d4b2013-07-14 01:44:59 +0000417 // Now that all possible changes to the matrix have taken place, check
418 // to see if we're really close to a no-scale matrix. If so, explicitly
419 // set it to be so. Subsequent code may inspect this matrix to choose
420 // a faster path in this case.
421
422 // This code will only execute if the matrix has some scale component;
423 // if it's already pure translate then we won't do this inversion.
424
425 if (matrix_only_scale_translate(fInvMatrix)) {
reed@google.comee056a82013-04-18 15:33:27 +0000426 SkMatrix forward;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000427 if (fInvMatrix.invert(&forward)) {
428 if (clampClamp ? just_trans_clamp(forward, *fBitmap)
reed@google.comee056a82013-04-18 15:33:27 +0000429 : just_trans_general(forward)) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000430 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
431 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000432 fInvMatrix.setTranslate(tx, ty);
reed@google.comce1f3cc2013-01-05 14:37:48 +0000433 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000434 }
435 }
436
humper@google.com9c96d4b2013-07-14 01:44:59 +0000437 fInvProc = fInvMatrix.getMapXYProc();
438 fInvType = fInvMatrix.getType();
439 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
440 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
441 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
442 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443
444 fAlphaScale = SkAlpha255To256(paint.getAlpha());
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000445
reed@android.com7a99eb12009-07-16 01:13:14 +0000446 fShaderProc32 = NULL;
447 fShaderProc16 = NULL;
448 fSampleProc32 = NULL;
449 fSampleProc16 = NULL;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000450
humper@google.com9c96d4b2013-07-14 01:44:59 +0000451 // recompute the triviality of the matrix here because we may have
452 // changed it!
453
454 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
455
reed@google.com9cfc83c2013-07-22 17:18:18 +0000456 if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000457 // If this is still set, that means we wanted HQ sampling
458 // but couldn't do it as a preprocess. Let's try to install
459 // the scanline version of the HQ sampler. If that process fails,
460 // downgrade to bilerp.
461
462 // NOTE: Might need to be careful here in the future when we want
463 // to have the platform proc have a shot at this; it's possible that
464 // the chooseBitmapFilterProc will fail to install a shader but a
465 // platform-specific one might succeed, so it might be premature here
466 // to fall back to bilerp. This needs thought.
467
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000468 if (!this->setBitmapFilterProcs()) {
reed@google.com9cfc83c2013-07-22 17:18:18 +0000469 fFilterLevel = SkPaint::kLow_FilterLevel;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000470 }
471 }
472
reed@google.com9cfc83c2013-07-22 17:18:18 +0000473 if (SkPaint::kLow_FilterLevel == fFilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000474 // Only try bilerp if the matrix is "interesting" and
475 // the image has a suitable size.
476
477 if (fInvType <= SkMatrix::kTranslate_Mask ||
reed@google.com9cfc83c2013-07-22 17:18:18 +0000478 !valid_for_filtering(fBitmap->width() | fBitmap->height())) {
479 fFilterLevel = SkPaint::kNone_FilterLevel;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000480 }
481 }
482
483 // At this point, we know exactly what kind of sampling the per-scanline
484 // shader will perform.
485
486 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000487 // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns NULL.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 if (NULL == fMatrixProc) {
489 return false;
490 }
491
492 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000493
humper@google.com9c96d4b2013-07-14 01:44:59 +0000494 // No need to do this if we're doing HQ sampling; if filter quality is
495 // still set to HQ by the time we get here, then we must have installed
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000496 // the shader procs above and can skip all this.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497
reed@google.com9cfc83c2013-07-22 17:18:18 +0000498 if (fFilterLevel < SkPaint::kHigh_FilterLevel) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000499
humper@google.com9c96d4b2013-07-14 01:44:59 +0000500 int index = 0;
501 if (fAlphaScale < 256) { // note: this distinction is not used for D16
502 index |= 1;
reed@android.comaa9152a2009-07-17 21:24:56 +0000503 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000504 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
505 index |= 2;
506 }
reed@google.com9cfc83c2013-07-22 17:18:18 +0000507 if (fFilterLevel > SkPaint::kNone_FilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000508 index |= 4;
509 }
510 // bits 3,4,5 encoding the source bitmap format
511 switch (fBitmap->config()) {
512 case SkBitmap::kARGB_8888_Config:
513 index |= 0;
514 break;
515 case SkBitmap::kRGB_565_Config:
516 index |= 8;
517 break;
518 case SkBitmap::kIndex8_Config:
519 index |= 16;
520 break;
521 case SkBitmap::kARGB_4444_Config:
522 index |= 24;
523 break;
524 case SkBitmap::kA8_Config:
525 index |= 32;
526 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
527 break;
528 default:
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000529 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
humper@google.com9c96d4b2013-07-14 01:44:59 +0000530 return false;
531 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000532
humper@google.com9c96d4b2013-07-14 01:44:59 +0000533 #if !SK_ARM_NEON_IS_ALWAYS
534 static const SampleProc32 gSkBitmapProcStateSample32[] = {
535 S32_opaque_D32_nofilter_DXDY,
536 S32_alpha_D32_nofilter_DXDY,
537 S32_opaque_D32_nofilter_DX,
538 S32_alpha_D32_nofilter_DX,
539 S32_opaque_D32_filter_DXDY,
540 S32_alpha_D32_filter_DXDY,
541 S32_opaque_D32_filter_DX,
542 S32_alpha_D32_filter_DX,
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +0000543
humper@google.com9c96d4b2013-07-14 01:44:59 +0000544 S16_opaque_D32_nofilter_DXDY,
545 S16_alpha_D32_nofilter_DXDY,
546 S16_opaque_D32_nofilter_DX,
547 S16_alpha_D32_nofilter_DX,
548 S16_opaque_D32_filter_DXDY,
549 S16_alpha_D32_filter_DXDY,
550 S16_opaque_D32_filter_DX,
551 S16_alpha_D32_filter_DX,
552
553 SI8_opaque_D32_nofilter_DXDY,
554 SI8_alpha_D32_nofilter_DXDY,
555 SI8_opaque_D32_nofilter_DX,
556 SI8_alpha_D32_nofilter_DX,
557 SI8_opaque_D32_filter_DXDY,
558 SI8_alpha_D32_filter_DXDY,
559 SI8_opaque_D32_filter_DX,
560 SI8_alpha_D32_filter_DX,
561
562 S4444_opaque_D32_nofilter_DXDY,
563 S4444_alpha_D32_nofilter_DXDY,
564 S4444_opaque_D32_nofilter_DX,
565 S4444_alpha_D32_nofilter_DX,
566 S4444_opaque_D32_filter_DXDY,
567 S4444_alpha_D32_filter_DXDY,
568 S4444_opaque_D32_filter_DX,
569 S4444_alpha_D32_filter_DX,
570
571 // A8 treats alpha/opaque the same (equally efficient)
572 SA8_alpha_D32_nofilter_DXDY,
573 SA8_alpha_D32_nofilter_DXDY,
574 SA8_alpha_D32_nofilter_DX,
575 SA8_alpha_D32_nofilter_DX,
576 SA8_alpha_D32_filter_DXDY,
577 SA8_alpha_D32_filter_DXDY,
578 SA8_alpha_D32_filter_DX,
579 SA8_alpha_D32_filter_DX
580 };
581
582 static const SampleProc16 gSkBitmapProcStateSample16[] = {
583 S32_D16_nofilter_DXDY,
584 S32_D16_nofilter_DX,
585 S32_D16_filter_DXDY,
586 S32_D16_filter_DX,
587
588 S16_D16_nofilter_DXDY,
589 S16_D16_nofilter_DX,
590 S16_D16_filter_DXDY,
591 S16_D16_filter_DX,
592
593 SI8_D16_nofilter_DXDY,
594 SI8_D16_nofilter_DX,
595 SI8_D16_filter_DXDY,
596 SI8_D16_filter_DX,
597
598 // Don't support 4444 -> 565
599 NULL, NULL, NULL, NULL,
600 // Don't support A8 -> 565
601 NULL, NULL, NULL, NULL
602 };
603 #endif
604
605 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
606 index >>= 1; // shift away any opaque/alpha distinction
607 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
608
609 // our special-case shaderprocs
610 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
611 if (clampClamp) {
612 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
613 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
614 SkShader::kRepeat_TileMode == fTileModeY) {
615 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
616 }
617 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
618 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
619 }
620
621 if (NULL == fShaderProc32) {
622 fShaderProc32 = this->chooseShaderProc32();
623 }
humper@google.comb0889472013-07-09 21:37:14 +0000624 }
625
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000626 // see if our platform has any accelerated overrides
627 this->platformProcs();
reed@google.com4c69a062013-05-23 20:11:56 +0000628
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 return true;
630}
631
reed@google.com9a4c7462012-10-12 18:21:37 +0000632static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
633 int x, int y,
634 SkPMColor* SK_RESTRICT colors,
635 int count) {
636 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
637 SkASSERT(s.fInvKy == 0);
638 SkASSERT(count > 0 && colors != NULL);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000639 SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000640
reed@google.com9a4c7462012-10-12 18:21:37 +0000641 const int maxX = s.fBitmap->width() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000642 const int maxY = s.fBitmap->height() - 1;
643 int ix = s.fFilterOneX + x;
644 int iy = SkClampMax(s.fFilterOneY + y, maxY);
645#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000646 {
647 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000648 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com9a4c7462012-10-12 18:21:37 +0000649 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000650 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
651 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000652
reed@google.comf7698de2012-10-12 20:50:24 +0000653 SkASSERT(iy == iy2);
654 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000655 }
reed@google.comf7698de2012-10-12 20:50:24 +0000656#endif
657 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000658
reed@google.com9a4c7462012-10-12 18:21:37 +0000659 // clamp to the left
660 if (ix < 0) {
661 int n = SkMin32(-ix, count);
662 sk_memset32(colors, row[0], n);
663 count -= n;
664 if (0 == count) {
665 return;
666 }
667 colors += n;
668 SkASSERT(-ix == n);
669 ix = 0;
670 }
671 // copy the middle
672 if (ix <= maxX) {
673 int n = SkMin32(maxX - ix + 1, count);
674 memcpy(colors, row + ix, n * sizeof(SkPMColor));
675 count -= n;
676 if (0 == count) {
677 return;
678 }
679 colors += n;
680 }
681 SkASSERT(count > 0);
682 // clamp to the right
683 sk_memset32(colors, row[maxX], count);
684}
685
reed@google.coma8d99302012-10-16 20:23:25 +0000686static inline int sk_int_mod(int x, int n) {
687 SkASSERT(n > 0);
688 if ((unsigned)x >= (unsigned)n) {
689 if (x < 0) {
690 x = n + ~(~x % n);
691 } else {
692 x = x % n;
693 }
694 }
695 return x;
696}
697
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000698static inline int sk_int_mirror(int x, int n) {
699 x = sk_int_mod(x, 2 * n);
700 if (x >= n) {
701 x = n + ~(x - n);
702 }
703 return x;
704}
705
reed@google.coma8d99302012-10-16 20:23:25 +0000706static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
707 int x, int y,
708 SkPMColor* SK_RESTRICT colors,
709 int count) {
710 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
711 SkASSERT(s.fInvKy == 0);
712 SkASSERT(count > 0 && colors != NULL);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000713 SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000714
reed@google.coma8d99302012-10-16 20:23:25 +0000715 const int stopX = s.fBitmap->width();
716 const int stopY = s.fBitmap->height();
717 int ix = s.fFilterOneX + x;
718 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
719#ifdef SK_DEBUG
720 {
721 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000722 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.coma8d99302012-10-16 20:23:25 +0000723 SkIntToScalar(y) + SK_ScalarHalf, &pt);
724 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
725 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000726
reed@google.coma8d99302012-10-16 20:23:25 +0000727 SkASSERT(iy == iy2);
728 SkASSERT(ix == ix2);
729 }
730#endif
731 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
732
733 ix = sk_int_mod(ix, stopX);
734 for (;;) {
735 int n = SkMin32(stopX - ix, count);
736 memcpy(colors, row + ix, n * sizeof(SkPMColor));
737 count -= n;
738 if (0 == count) {
739 return;
740 }
741 colors += n;
742 ix = 0;
743 }
744}
745
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000746static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
747 int x, int y,
748 SkPMColor* SK_RESTRICT colors,
749 int count) {
750 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
751 SkASSERT(s.fInvKy == 0);
752 SkASSERT(count > 0 && colors != NULL);
753 SkASSERT(1 == s.fBitmap->width());
754
scroggo@google.comad511322013-02-22 15:50:37 +0000755 int iY0;
756 int iY1 SK_INIT_TO_AVOID_WARNING;
757 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000758
reed@google.com9cfc83c2013-07-22 17:18:18 +0000759 if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000760 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
761 uint32_t xy[2];
762
763 mproc(s, xy, 1, x, y);
764
765 iY0 = xy[0] >> 18;
766 iY1 = xy[0] & 0x3FFF;
767 iSubY = (xy[0] >> 14) & 0xF;
768 } else {
769 int yTemp;
770
771 if (s.fInvType > SkMatrix::kTranslate_Mask) {
772 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000773 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000774 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000775 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000776 &pt);
robertphillips@google.com1e305232013-01-22 20:29:16 +0000777 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000778 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000779 // bitmap's width and height. Since this method is going to do
780 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000781 if (SkShader::kClamp_TileMode != s.fTileModeX ||
782 SkShader::kClamp_TileMode != s.fTileModeY) {
783 yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
784 } else {
785 yTemp = SkScalarFloorToInt(pt.fY);
786 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000787 } else {
788 yTemp = s.fFilterOneY + y;
789 }
790
791 const int stopY = s.fBitmap->height();
792 switch (s.fTileModeY) {
793 case SkShader::kClamp_TileMode:
794 iY0 = SkClampMax(yTemp, stopY-1);
795 break;
796 case SkShader::kRepeat_TileMode:
797 iY0 = sk_int_mod(yTemp, stopY);
798 break;
799 case SkShader::kMirror_TileMode:
800 default:
801 iY0 = sk_int_mirror(yTemp, stopY);
802 break;
803 }
804
805#ifdef SK_DEBUG
806 {
807 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000808 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000809 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000810 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000811 &pt);
robertphillips@google.comd5077752013-01-23 00:36:02 +0000812 if (s.fInvType > SkMatrix::kTranslate_Mask &&
813 (SkShader::kClamp_TileMode != s.fTileModeX ||
814 SkShader::kClamp_TileMode != s.fTileModeY)) {
robertphillips@google.com1e305232013-01-22 20:29:16 +0000815 pt.fY *= s.fBitmap->height();
816 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000817 int iY2;
818
819 switch (s.fTileModeY) {
820 case SkShader::kClamp_TileMode:
821 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
822 break;
823 case SkShader::kRepeat_TileMode:
824 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
825 break;
826 case SkShader::kMirror_TileMode:
827 default:
828 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
829 break;
830 }
831
832 SkASSERT(iY0 == iY2);
833 }
834#endif
835 }
836
837 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
838 SkPMColor color;
839
reed@google.com9cfc83c2013-07-22 17:18:18 +0000840 if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000841 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
842
843 if (s.fAlphaScale < 256) {
844 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
845 } else {
846 Filter_32_opaque(iSubY, *row0, *row1, &color);
847 }
848 } else {
849 if (s.fAlphaScale < 256) {
850 color = SkAlphaMulQ(*row0, s.fAlphaScale);
851 } else {
852 color = *row0;
853 }
854 }
855
856 sk_memset32(colors, color, count);
857}
858
reed@google.com6bb92bc2012-11-20 19:45:16 +0000859static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
860 SkPMColor* SK_RESTRICT colors, int count) {
861 // if we get called, the matrix is too tricky, so we just draw nothing
862 sk_memset32(colors, 0, count);
863}
864
865bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000866 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000867 fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000868
869 /*
870 * if the translate is larger than our ints, we can get random results, or
871 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
872 * negate it.
873 */
874 const SkScalar too_big = SkIntToScalar(1 << 30);
875 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
876 return false;
877 }
878
reed@google.coma8d99302012-10-16 20:23:25 +0000879 // Since we know we're not filtered, we re-purpose these fields allow
880 // us to go from device -> src coordinates w/ just an integer add,
881 // rather than running through the inverse-matrix
882 fFilterOneX = SkScalarFloorToInt(pt.fX);
883 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000884 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000885}
886
reed@google.com9a4c7462012-10-12 18:21:37 +0000887SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000888
889 if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
890 return NULL;
891 }
892
893 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
894
895 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
reed@google.com9cfc83c2013-07-22 17:18:18 +0000896 if (SkPaint::kNone_FilterLevel == fFilterLevel &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000897 fInvType <= SkMatrix::kTranslate_Mask &&
898 !this->setupForTranslate()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000899 return DoNothing_shaderproc;
900 }
901 return S32_D32_constX_shaderproc;
902 }
903
reed@google.com9a4c7462012-10-12 18:21:37 +0000904 if (fAlphaScale < 256) {
905 return NULL;
906 }
907 if (fInvType > SkMatrix::kTranslate_Mask) {
908 return NULL;
909 }
reed@google.com9cfc83c2013-07-22 17:18:18 +0000910 if (SkPaint::kNone_FilterLevel != fFilterLevel) {
reed@google.com9a4c7462012-10-12 18:21:37 +0000911 return NULL;
912 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000913
reed@google.coma8d99302012-10-16 20:23:25 +0000914 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
915 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
916
917 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000918 if (this->setupForTranslate()) {
919 return Clamp_S32_D32_nofilter_trans_shaderproc;
920 }
921 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000922 }
reed@google.coma8d99302012-10-16 20:23:25 +0000923 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000924 if (this->setupForTranslate()) {
925 return Repeat_S32_D32_nofilter_trans_shaderproc;
926 }
927 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000928 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000929 return NULL;
930}
931
reed@android.com4c128c42009-08-14 13:54:37 +0000932///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000933
934#ifdef SK_DEBUG
935
936static void check_scale_nofilter(uint32_t bitmapXY[], int count,
937 unsigned mx, unsigned my) {
938 unsigned y = *bitmapXY++;
939 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000940
reed@google.com9fe287b2012-03-27 15:54:28 +0000941 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
942 for (int i = 0; i < count; ++i) {
943 SkASSERT(xptr[i] < mx);
944 }
945}
946
947static void check_scale_filter(uint32_t bitmapXY[], int count,
948 unsigned mx, unsigned my) {
949 uint32_t YY = *bitmapXY++;
950 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000951 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000952 SkASSERT(y0 < my);
953 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000954
reed@google.com9fe287b2012-03-27 15:54:28 +0000955 for (int i = 0; i < count; ++i) {
956 uint32_t XX = bitmapXY[i];
957 unsigned x0 = XX >> 18;
958 unsigned x1 = XX & 0x3FFF;
959 SkASSERT(x0 < mx);
960 SkASSERT(x1 < mx);
961 }
962}
963
964static void check_affine_nofilter(uint32_t bitmapXY[], int count,
965 unsigned mx, unsigned my) {
966 for (int i = 0; i < count; ++i) {
967 uint32_t XY = bitmapXY[i];
968 unsigned x = XY & 0xFFFF;
969 unsigned y = XY >> 16;
970 SkASSERT(x < mx);
971 SkASSERT(y < my);
972 }
973}
974
975static void check_affine_filter(uint32_t bitmapXY[], int count,
976 unsigned mx, unsigned my) {
977 for (int i = 0; i < count; ++i) {
978 uint32_t YY = *bitmapXY++;
979 unsigned y0 = YY >> 18;
980 unsigned y1 = YY & 0x3FFF;
981 SkASSERT(y0 < my);
982 SkASSERT(y1 < my);
983
984 uint32_t XX = *bitmapXY++;
985 unsigned x0 = XX >> 18;
986 unsigned x1 = XX & 0x3FFF;
987 SkASSERT(x0 < mx);
988 SkASSERT(x1 < mx);
989 }
990}
991
992void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
993 uint32_t bitmapXY[], int count,
994 int x, int y) {
995 SkASSERT(bitmapXY);
996 SkASSERT(count > 0);
997
998 state.fMatrixProc(state, bitmapXY, count, x, y);
999
1000 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
1001
1002 // There are four formats possible:
1003 // scale -vs- affine
1004 // filter -vs- nofilter
1005 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
reed@google.com9cfc83c2013-07-22 17:18:18 +00001006 proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_scale_filter : check_scale_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +00001007 } else {
reed@google.com9cfc83c2013-07-22 17:18:18 +00001008 proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_affine_filter : check_affine_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +00001009 }
1010 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
1011}
1012
1013SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
1014 return DebugMatrixProc;
1015}
1016
1017#endif
1018
1019///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +00001020/*
1021 The storage requirements for the different matrix procs are as follows,
1022 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001023
reed@android.com4c128c42009-08-14 13:54:37 +00001024 scale/translate nofilter Y(4bytes) + N * X
1025 affine/perspective nofilter N * (X Y)
1026 scale/translate filter Y Y + N * (X X)
1027 affine/perspective filter N * (Y Y X X)
1028 */
1029int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
1030 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +00001031
1032 size &= ~3; // only care about 4-byte aligned chunks
1033 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
1034 size -= 4; // the shared Y (or YY) coordinate
1035 if (size < 0) {
1036 size = 0;
1037 }
reed@android.com258cb222010-04-14 13:36:33 +00001038 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +00001039 } else {
reed@android.com258cb222010-04-14 13:36:33 +00001040 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +00001041 }
1042
reed@google.com9cfc83c2013-07-22 17:18:18 +00001043 if (fFilterLevel != SkPaint::kNone_FilterLevel) {
reed@android.com258cb222010-04-14 13:36:33 +00001044 size >>= 1;
1045 }
1046
1047 return size;
reed@android.com4c128c42009-08-14 13:54:37 +00001048}