blob: 79dcc9a1c029bcf1e73a5e9b69a43dd896900061 [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014
digit@google.com3ada0ef2012-08-13 14:06:34 +000015#if !SK_ARM_NEON_IS_NONE
16// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
17extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
18extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
19extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
20extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
21extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
22extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
23extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
24extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
25#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000026
digit@google.com3ada0ef2012-08-13 14:06:34 +000027#if !SK_ARM_NEON_IS_ALWAYS
28#define NAME_WRAP(x) x
29#include "SkBitmapProcState_filter.h"
30#include "SkBitmapProcState_procs.h"
31#endif
reed@android.comb577b412009-10-27 17:49:32 +000032
reed@android.coma44b4cc2009-07-16 02:03:58 +000033///////////////////////////////////////////////////////////////////////////////
34
reed@google.comc0e88e02012-10-17 21:11:56 +000035/**
36 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
37 * go ahead and treat it as if it were, so that subsequent code can go fast.
38 */
39static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
40 SkMatrix::TypeMask mask = matrix.getType();
41
42 if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
43 return false;
44 }
45 if (mask & SkMatrix::kScale_Mask) {
46 SkScalar sx = matrix[SkMatrix::kMScaleX];
47 SkScalar sy = matrix[SkMatrix::kMScaleY];
48 int w = bitmap.width();
49 int h = bitmap.height();
50 int sw = SkScalarRound(SkScalarMul(sx, SkIntToScalar(w)));
51 int sh = SkScalarRound(SkScalarMul(sy, SkIntToScalar(h)));
52 return sw == w && sh == h;
53 }
54 // if we got here, we're either kTranslate_Mask or identity
55 return true;
56}
57
58static bool just_trans_general(const SkMatrix& matrix) {
59 SkMatrix::TypeMask mask = matrix.getType();
60
61 if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
62 return false;
63 }
64 if (mask & SkMatrix::kScale_Mask) {
65 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +000066
reed@google.comc0e88e02012-10-17 21:11:56 +000067 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
68 return false;
69 }
70 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
71 return false;
72 }
73 }
74 // if we got here, treat us as either kTranslate_Mask or identity
75 return true;
76}
77
78///////////////////////////////////////////////////////////////////////////////
79
reed@android.com8a1c16f2008-12-17 15:59:43 +000080static bool valid_for_filtering(unsigned dimension) {
81 // for filtering, width and height must fit in 14bits, since we use steal
82 // 2 bits from each to store our 4bit subpixel data
83 return (dimension & ~0x3FFF) == 0;
84}
85
86bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
87 if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
88 return false;
89 }
reed@android.coma44b4cc2009-07-16 02:03:58 +000090
reed@android.com07d1f002009-08-13 19:35:48 +000091 const SkMatrix* m;
92 bool trivial_matrix = (inv.getType() & ~SkMatrix::kTranslate_Mask) == 0;
93 bool clamp_clamp = SkShader::kClamp_TileMode == fTileModeX &&
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +000094 SkShader::kClamp_TileMode == fTileModeY;
reed@android.com07d1f002009-08-13 19:35:48 +000095
96 if (clamp_clamp || trivial_matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 m = &inv;
98 } else {
99 fUnitInvMatrix = inv;
100 fUnitInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
101 m = &fUnitInvMatrix;
102 }
reed@android.com07d1f002009-08-13 19:35:48 +0000103
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 fBitmap = &fOrigBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 if (fOrigBitmap.hasMipMap()) {
106 int shift = fOrigBitmap.extractMipLevel(&fMipBitmap,
107 SkScalarToFixed(m->getScaleX()),
108 SkScalarToFixed(m->getSkewY()));
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000109
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 if (shift > 0) {
111 if (m != &fUnitInvMatrix) {
112 fUnitInvMatrix = *m;
113 m = &fUnitInvMatrix;
114 }
115
116 SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
117 fUnitInvMatrix.postScale(scale, scale);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000118
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 // now point here instead of fOrigBitmap
120 fBitmap = &fMipBitmap;
121 }
122 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
reed@google.comc0e88e02012-10-17 21:11:56 +0000124 // wack our matrix to exactly no-scale, if we're really close to begin with
125 {
126 bool fixupMatrix = clamp_clamp ?
127 just_trans_clamp(*m, *fBitmap) : just_trans_general(*m);
128 if (fixupMatrix) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000129#ifdef SK_IGNORE_TRANS_CLAMP_FIX
reed@google.comc0e88e02012-10-17 21:11:56 +0000130 if (m != &fUnitInvMatrix) { // can't mutate the original
131 fUnitInvMatrix = inv;
132 m = &fUnitInvMatrix;
133 }
134 fUnitInvMatrix.set(SkMatrix::kMScaleX, SK_Scalar1);
135 fUnitInvMatrix.set(SkMatrix::kMScaleY, SK_Scalar1);
reed@google.comce1f3cc2013-01-05 14:37:48 +0000136#else
137 // If we can be treated just like translate, construct that inverse
138 // such that we landed in the proper place. Given that m may have
139 // some slight scale, we have to invert it to compute this new
140 // matrix.
141 SkMatrix forward;
142 if (m->invert(&forward)) {
143 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
144 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
145 fUnitInvMatrix.setTranslate(tx, ty);
146 m = &fUnitInvMatrix;
147 // now the following code will sniff m, and decide to take the
148 // fast case (since m is purely translate).
149 }
150#endif
reed@google.comc0e88e02012-10-17 21:11:56 +0000151 }
152 }
153
154 // Below this point, we should never refer to the inv parameter, since we
155 // may be using a munged version for "our" inverse.
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000156
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 fInvMatrix = m;
158 fInvProc = m->getMapXYProc();
159 fInvType = m->getType();
160 fInvSx = SkScalarToFixed(m->getScaleX());
reed@google.com4bc0a9d2012-03-07 21:47:41 +0000161 fInvSxFractionalInt = SkScalarToFractionalInt(m->getScaleX());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 fInvKy = SkScalarToFixed(m->getSkewY());
reed@google.com411215a2012-03-08 20:13:46 +0000163 fInvKyFractionalInt = SkScalarToFractionalInt(m->getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164
165 fAlphaScale = SkAlpha255To256(paint.getAlpha());
166
167 // pick-up filtering from the paint, but only if the matrix is
168 // more complex than identity/translate (i.e. no need to pay the cost
169 // of filtering if we're not scaled etc.).
170 // note: we explicitly check inv, since m might be scaled due to unitinv
171 // trickery, but we don't want to see that for this test
172 fDoFilter = paint.isFilterBitmap() &&
reed@google.comc0e88e02012-10-17 21:11:56 +0000173 (fInvType > SkMatrix::kTranslate_Mask &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 valid_for_filtering(fBitmap->width() | fBitmap->height()));
175
reed@android.com7a99eb12009-07-16 01:13:14 +0000176 fShaderProc32 = NULL;
177 fShaderProc16 = NULL;
178 fSampleProc32 = NULL;
179 fSampleProc16 = NULL;
reed@android.com48534f92009-07-16 20:53:26 +0000180
reed@android.com07d1f002009-08-13 19:35:48 +0000181 fMatrixProc = this->chooseMatrixProc(trivial_matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 if (NULL == fMatrixProc) {
183 return false;
184 }
185
186 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000187
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 int index = 0;
189 if (fAlphaScale < 256) { // note: this distinction is not used for D16
190 index |= 1;
191 }
192 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
193 index |= 2;
194 }
195 if (fDoFilter) {
196 index |= 4;
197 }
198 // bits 3,4,5 encoding the source bitmap format
199 switch (fBitmap->config()) {
200 case SkBitmap::kARGB_8888_Config:
201 index |= 0;
202 break;
203 case SkBitmap::kRGB_565_Config:
204 index |= 8;
205 break;
206 case SkBitmap::kIndex8_Config:
207 index |= 16;
208 break;
209 case SkBitmap::kARGB_4444_Config:
210 index |= 24;
211 break;
212 case SkBitmap::kA8_Config:
213 index |= 32;
214 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
reed@android.com3469c762009-02-24 19:03:20 +0000215 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 default:
217 return false;
218 }
219
digit@google.com3ada0ef2012-08-13 14:06:34 +0000220#if !SK_ARM_NEON_IS_ALWAYS
221 static const SampleProc32 gSkBitmapProcStateSample32[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 S32_opaque_D32_nofilter_DXDY,
223 S32_alpha_D32_nofilter_DXDY,
224 S32_opaque_D32_nofilter_DX,
225 S32_alpha_D32_nofilter_DX,
226 S32_opaque_D32_filter_DXDY,
227 S32_alpha_D32_filter_DXDY,
228 S32_opaque_D32_filter_DX,
229 S32_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000230
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 S16_opaque_D32_nofilter_DXDY,
232 S16_alpha_D32_nofilter_DXDY,
233 S16_opaque_D32_nofilter_DX,
234 S16_alpha_D32_nofilter_DX,
235 S16_opaque_D32_filter_DXDY,
236 S16_alpha_D32_filter_DXDY,
237 S16_opaque_D32_filter_DX,
238 S16_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 SI8_opaque_D32_nofilter_DXDY,
241 SI8_alpha_D32_nofilter_DXDY,
242 SI8_opaque_D32_nofilter_DX,
243 SI8_alpha_D32_nofilter_DX,
244 SI8_opaque_D32_filter_DXDY,
245 SI8_alpha_D32_filter_DXDY,
246 SI8_opaque_D32_filter_DX,
247 SI8_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000248
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 S4444_opaque_D32_nofilter_DXDY,
250 S4444_alpha_D32_nofilter_DXDY,
251 S4444_opaque_D32_nofilter_DX,
252 S4444_alpha_D32_nofilter_DX,
253 S4444_opaque_D32_filter_DXDY,
254 S4444_alpha_D32_filter_DXDY,
255 S4444_opaque_D32_filter_DX,
256 S4444_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000257
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000258 // A8 treats alpha/opaque the same (equally efficient)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 SA8_alpha_D32_nofilter_DXDY,
260 SA8_alpha_D32_nofilter_DXDY,
261 SA8_alpha_D32_nofilter_DX,
262 SA8_alpha_D32_nofilter_DX,
263 SA8_alpha_D32_filter_DXDY,
264 SA8_alpha_D32_filter_DXDY,
265 SA8_alpha_D32_filter_DX,
266 SA8_alpha_D32_filter_DX
267 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000268
digit@google.com3ada0ef2012-08-13 14:06:34 +0000269 static const SampleProc16 gSkBitmapProcStateSample16[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 S32_D16_nofilter_DXDY,
271 S32_D16_nofilter_DX,
272 S32_D16_filter_DXDY,
273 S32_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000274
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 S16_D16_nofilter_DXDY,
276 S16_D16_nofilter_DX,
277 S16_D16_filter_DXDY,
278 S16_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000279
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 SI8_D16_nofilter_DXDY,
281 SI8_D16_nofilter_DX,
282 SI8_D16_filter_DXDY,
283 SI8_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000284
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 // Don't support 4444 -> 565
286 NULL, NULL, NULL, NULL,
287 // Don't support A8 -> 565
288 NULL, NULL, NULL, NULL
289 };
digit@google.com3ada0ef2012-08-13 14:06:34 +0000290#endif
reed@android.com48534f92009-07-16 20:53:26 +0000291
digit@google.com3ada0ef2012-08-13 14:06:34 +0000292 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 index >>= 1; // shift away any opaque/alpha distinction
digit@google.com3ada0ef2012-08-13 14:06:34 +0000294 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295
reed@android.coma44b4cc2009-07-16 02:03:58 +0000296 // our special-case shaderprocs
digit@google.com3ada0ef2012-08-13 14:06:34 +0000297 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
reed@android.comaa9152a2009-07-17 21:24:56 +0000298 if (clamp_clamp) {
digit@google.com3ada0ef2012-08-13 14:06:34 +0000299 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
reed@android.comaa9152a2009-07-17 21:24:56 +0000300 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
301 SkShader::kRepeat_TileMode == fTileModeY) {
digit@google.com3ada0ef2012-08-13 14:06:34 +0000302 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
reed@android.comaa9152a2009-07-17 21:24:56 +0000303 }
digit@google.com3ada0ef2012-08-13 14:06:34 +0000304 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clamp_clamp) {
305 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
reed@android.coma44b4cc2009-07-16 02:03:58 +0000306 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000307
reed@google.com9a4c7462012-10-12 18:21:37 +0000308 if (NULL == fShaderProc32) {
309 fShaderProc32 = this->chooseShaderProc32();
310 }
311
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000312 // see if our platform has any accelerated overrides
313 this->platformProcs();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 return true;
315}
316
reed@google.com9a4c7462012-10-12 18:21:37 +0000317static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
318 int x, int y,
319 SkPMColor* SK_RESTRICT colors,
320 int count) {
321 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
322 SkASSERT(s.fInvKy == 0);
323 SkASSERT(count > 0 && colors != NULL);
324 SkASSERT(!s.fDoFilter);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000325
reed@google.com9a4c7462012-10-12 18:21:37 +0000326 const int maxX = s.fBitmap->width() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000327 const int maxY = s.fBitmap->height() - 1;
328 int ix = s.fFilterOneX + x;
329 int iy = SkClampMax(s.fFilterOneY + y, maxY);
330#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000331 {
332 SkPoint pt;
333 s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
334 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000335 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
336 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000337
reed@google.comf7698de2012-10-12 20:50:24 +0000338 SkASSERT(iy == iy2);
339 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000340 }
reed@google.comf7698de2012-10-12 20:50:24 +0000341#endif
342 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000343
reed@google.com9a4c7462012-10-12 18:21:37 +0000344 // clamp to the left
345 if (ix < 0) {
346 int n = SkMin32(-ix, count);
347 sk_memset32(colors, row[0], n);
348 count -= n;
349 if (0 == count) {
350 return;
351 }
352 colors += n;
353 SkASSERT(-ix == n);
354 ix = 0;
355 }
356 // copy the middle
357 if (ix <= maxX) {
358 int n = SkMin32(maxX - ix + 1, count);
359 memcpy(colors, row + ix, n * sizeof(SkPMColor));
360 count -= n;
361 if (0 == count) {
362 return;
363 }
364 colors += n;
365 }
366 SkASSERT(count > 0);
367 // clamp to the right
368 sk_memset32(colors, row[maxX], count);
369}
370
reed@google.coma8d99302012-10-16 20:23:25 +0000371static inline int sk_int_mod(int x, int n) {
372 SkASSERT(n > 0);
373 if ((unsigned)x >= (unsigned)n) {
374 if (x < 0) {
375 x = n + ~(~x % n);
376 } else {
377 x = x % n;
378 }
379 }
380 return x;
381}
382
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000383static inline int sk_int_mirror(int x, int n) {
384 x = sk_int_mod(x, 2 * n);
385 if (x >= n) {
386 x = n + ~(x - n);
387 }
388 return x;
389}
390
reed@google.coma8d99302012-10-16 20:23:25 +0000391static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
392 int x, int y,
393 SkPMColor* SK_RESTRICT colors,
394 int count) {
395 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
396 SkASSERT(s.fInvKy == 0);
397 SkASSERT(count > 0 && colors != NULL);
398 SkASSERT(!s.fDoFilter);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000399
reed@google.coma8d99302012-10-16 20:23:25 +0000400 const int stopX = s.fBitmap->width();
401 const int stopY = s.fBitmap->height();
402 int ix = s.fFilterOneX + x;
403 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
404#ifdef SK_DEBUG
405 {
406 SkPoint pt;
407 s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
408 SkIntToScalar(y) + SK_ScalarHalf, &pt);
409 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
410 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000411
reed@google.coma8d99302012-10-16 20:23:25 +0000412 SkASSERT(iy == iy2);
413 SkASSERT(ix == ix2);
414 }
415#endif
416 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
417
418 ix = sk_int_mod(ix, stopX);
419 for (;;) {
420 int n = SkMin32(stopX - ix, count);
421 memcpy(colors, row + ix, n * sizeof(SkPMColor));
422 count -= n;
423 if (0 == count) {
424 return;
425 }
426 colors += n;
427 ix = 0;
428 }
429}
430
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000431static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
432 int x, int y,
433 SkPMColor* SK_RESTRICT colors,
434 int count) {
435 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
436 SkASSERT(s.fInvKy == 0);
437 SkASSERT(count > 0 && colors != NULL);
438 SkASSERT(1 == s.fBitmap->width());
439
440 int iY0, iY1, iSubY;
441
442 if (s.fDoFilter) {
443 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
444 uint32_t xy[2];
445
446 mproc(s, xy, 1, x, y);
447
448 iY0 = xy[0] >> 18;
449 iY1 = xy[0] & 0x3FFF;
450 iSubY = (xy[0] >> 14) & 0xF;
451 } else {
452 int yTemp;
453
454 if (s.fInvType > SkMatrix::kTranslate_Mask) {
455 SkPoint pt;
456 s.fInvProc(*s.fInvMatrix,
457 SkIntToScalar(x) + SK_ScalarHalf,
458 SkIntToScalar(y) + SK_ScalarHalf,
459 &pt);
460 yTemp = SkScalarFloorToInt(pt.fY);
461 } else {
462 yTemp = s.fFilterOneY + y;
463 }
464
465 const int stopY = s.fBitmap->height();
466 switch (s.fTileModeY) {
467 case SkShader::kClamp_TileMode:
468 iY0 = SkClampMax(yTemp, stopY-1);
469 break;
470 case SkShader::kRepeat_TileMode:
471 iY0 = sk_int_mod(yTemp, stopY);
472 break;
473 case SkShader::kMirror_TileMode:
474 default:
475 iY0 = sk_int_mirror(yTemp, stopY);
476 break;
477 }
478
479#ifdef SK_DEBUG
480 {
481 SkPoint pt;
482 s.fInvProc(*s.fInvMatrix,
483 SkIntToScalar(x) + SK_ScalarHalf,
484 SkIntToScalar(y) + SK_ScalarHalf,
485 &pt);
486 int iY2;
487
488 switch (s.fTileModeY) {
489 case SkShader::kClamp_TileMode:
490 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
491 break;
492 case SkShader::kRepeat_TileMode:
493 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
494 break;
495 case SkShader::kMirror_TileMode:
496 default:
497 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
498 break;
499 }
500
501 SkASSERT(iY0 == iY2);
502 }
503#endif
504 }
505
506 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
507 SkPMColor color;
508
509 if (s.fDoFilter) {
510 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
511
512 if (s.fAlphaScale < 256) {
513 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
514 } else {
515 Filter_32_opaque(iSubY, *row0, *row1, &color);
516 }
517 } else {
518 if (s.fAlphaScale < 256) {
519 color = SkAlphaMulQ(*row0, s.fAlphaScale);
520 } else {
521 color = *row0;
522 }
523 }
524
525 sk_memset32(colors, color, count);
526}
527
reed@google.com6bb92bc2012-11-20 19:45:16 +0000528static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
529 SkPMColor* SK_RESTRICT colors, int count) {
530 // if we get called, the matrix is too tricky, so we just draw nothing
531 sk_memset32(colors, 0, count);
532}
533
534bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000535 SkPoint pt;
536 fInvProc(*fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000537
538 /*
539 * if the translate is larger than our ints, we can get random results, or
540 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
541 * negate it.
542 */
543 const SkScalar too_big = SkIntToScalar(1 << 30);
544 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
545 return false;
546 }
547
reed@google.coma8d99302012-10-16 20:23:25 +0000548 // Since we know we're not filtered, we re-purpose these fields allow
549 // us to go from device -> src coordinates w/ just an integer add,
550 // rather than running through the inverse-matrix
551 fFilterOneX = SkScalarFloorToInt(pt.fX);
552 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000553 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000554}
555
reed@google.com9a4c7462012-10-12 18:21:37 +0000556SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000557
558 if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
559 return NULL;
560 }
561
562 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
563
564 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
565 if (!fDoFilter && fInvType <= SkMatrix::kTranslate_Mask && !this->setupForTranslate()) {
566 return DoNothing_shaderproc;
567 }
568 return S32_D32_constX_shaderproc;
569 }
570
reed@google.com9a4c7462012-10-12 18:21:37 +0000571 if (fAlphaScale < 256) {
572 return NULL;
573 }
574 if (fInvType > SkMatrix::kTranslate_Mask) {
575 return NULL;
576 }
577 if (fDoFilter) {
578 return NULL;
579 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000580
reed@google.coma8d99302012-10-16 20:23:25 +0000581 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
582 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
583
584 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000585 if (this->setupForTranslate()) {
586 return Clamp_S32_D32_nofilter_trans_shaderproc;
587 }
588 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000589 }
reed@google.coma8d99302012-10-16 20:23:25 +0000590 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000591 if (this->setupForTranslate()) {
592 return Repeat_S32_D32_nofilter_trans_shaderproc;
593 }
594 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000595 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000596 return NULL;
597}
598
reed@android.com4c128c42009-08-14 13:54:37 +0000599///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000600
601#ifdef SK_DEBUG
602
603static void check_scale_nofilter(uint32_t bitmapXY[], int count,
604 unsigned mx, unsigned my) {
605 unsigned y = *bitmapXY++;
606 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000607
reed@google.com9fe287b2012-03-27 15:54:28 +0000608 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
609 for (int i = 0; i < count; ++i) {
610 SkASSERT(xptr[i] < mx);
611 }
612}
613
614static void check_scale_filter(uint32_t bitmapXY[], int count,
615 unsigned mx, unsigned my) {
616 uint32_t YY = *bitmapXY++;
617 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000618 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000619 SkASSERT(y0 < my);
620 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000621
reed@google.com9fe287b2012-03-27 15:54:28 +0000622 for (int i = 0; i < count; ++i) {
623 uint32_t XX = bitmapXY[i];
624 unsigned x0 = XX >> 18;
625 unsigned x1 = XX & 0x3FFF;
626 SkASSERT(x0 < mx);
627 SkASSERT(x1 < mx);
628 }
629}
630
631static void check_affine_nofilter(uint32_t bitmapXY[], int count,
632 unsigned mx, unsigned my) {
633 for (int i = 0; i < count; ++i) {
634 uint32_t XY = bitmapXY[i];
635 unsigned x = XY & 0xFFFF;
636 unsigned y = XY >> 16;
637 SkASSERT(x < mx);
638 SkASSERT(y < my);
639 }
640}
641
642static void check_affine_filter(uint32_t bitmapXY[], int count,
643 unsigned mx, unsigned my) {
644 for (int i = 0; i < count; ++i) {
645 uint32_t YY = *bitmapXY++;
646 unsigned y0 = YY >> 18;
647 unsigned y1 = YY & 0x3FFF;
648 SkASSERT(y0 < my);
649 SkASSERT(y1 < my);
650
651 uint32_t XX = *bitmapXY++;
652 unsigned x0 = XX >> 18;
653 unsigned x1 = XX & 0x3FFF;
654 SkASSERT(x0 < mx);
655 SkASSERT(x1 < mx);
656 }
657}
658
659void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
660 uint32_t bitmapXY[], int count,
661 int x, int y) {
662 SkASSERT(bitmapXY);
663 SkASSERT(count > 0);
664
665 state.fMatrixProc(state, bitmapXY, count, x, y);
666
667 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
668
669 // There are four formats possible:
670 // scale -vs- affine
671 // filter -vs- nofilter
672 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
673 proc = state.fDoFilter ? check_scale_filter : check_scale_nofilter;
674 } else {
675 proc = state.fDoFilter ? check_affine_filter : check_affine_nofilter;
676 }
677 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
678}
679
680SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
681 return DebugMatrixProc;
682}
683
684#endif
685
686///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000687/*
688 The storage requirements for the different matrix procs are as follows,
689 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000690
reed@android.com4c128c42009-08-14 13:54:37 +0000691 scale/translate nofilter Y(4bytes) + N * X
692 affine/perspective nofilter N * (X Y)
693 scale/translate filter Y Y + N * (X X)
694 affine/perspective filter N * (Y Y X X)
695 */
696int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
697 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000698
699 size &= ~3; // only care about 4-byte aligned chunks
700 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
701 size -= 4; // the shared Y (or YY) coordinate
702 if (size < 0) {
703 size = 0;
704 }
reed@android.com258cb222010-04-14 13:36:33 +0000705 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000706 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000707 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000708 }
709
reed@android.com258cb222010-04-14 13:36:33 +0000710 if (fDoFilter) {
711 size >>= 1;
712 }
713
714 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000715}
716