blob: 92e55e876ad4349b28ed4fe43043e1bfe5b7900e [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#define NAME_WRAP(x) x
28#include "SkBitmapProcState_filter.h"
29#include "SkBitmapProcState_procs.h"
reed@android.comb577b412009-10-27 17:49:32 +000030
reed@android.coma44b4cc2009-07-16 02:03:58 +000031///////////////////////////////////////////////////////////////////////////////
32
reed@google.comc0e88e02012-10-17 21:11:56 +000033/**
34 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
35 * go ahead and treat it as if it were, so that subsequent code can go fast.
36 */
37static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
38 SkMatrix::TypeMask mask = matrix.getType();
39
40 if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
41 return false;
42 }
43 if (mask & SkMatrix::kScale_Mask) {
44 SkScalar sx = matrix[SkMatrix::kMScaleX];
45 SkScalar sy = matrix[SkMatrix::kMScaleY];
46 int w = bitmap.width();
47 int h = bitmap.height();
48 int sw = SkScalarRound(SkScalarMul(sx, SkIntToScalar(w)));
49 int sh = SkScalarRound(SkScalarMul(sy, SkIntToScalar(h)));
50 return sw == w && sh == h;
51 }
52 // if we got here, we're either kTranslate_Mask or identity
53 return true;
54}
55
56static bool just_trans_general(const SkMatrix& matrix) {
57 SkMatrix::TypeMask mask = matrix.getType();
58
59 if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
60 return false;
61 }
62 if (mask & SkMatrix::kScale_Mask) {
63 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +000064
reed@google.comc0e88e02012-10-17 21:11:56 +000065 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
66 return false;
67 }
68 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
69 return false;
70 }
71 }
72 // if we got here, treat us as either kTranslate_Mask or identity
73 return true;
74}
75
76///////////////////////////////////////////////////////////////////////////////
77
reed@android.com8a1c16f2008-12-17 15:59:43 +000078static bool valid_for_filtering(unsigned dimension) {
79 // for filtering, width and height must fit in 14bits, since we use steal
80 // 2 bits from each to store our 4bit subpixel data
81 return (dimension & ~0x3FFF) == 0;
82}
83
84bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
85 if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
86 return false;
87 }
reed@android.coma44b4cc2009-07-16 02:03:58 +000088
reed@android.com07d1f002009-08-13 19:35:48 +000089 const SkMatrix* m;
90 bool trivial_matrix = (inv.getType() & ~SkMatrix::kTranslate_Mask) == 0;
91 bool clamp_clamp = SkShader::kClamp_TileMode == fTileModeX &&
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +000092 SkShader::kClamp_TileMode == fTileModeY;
reed@android.com07d1f002009-08-13 19:35:48 +000093
94 if (clamp_clamp || trivial_matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 m = &inv;
96 } else {
97 fUnitInvMatrix = inv;
98 fUnitInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
99 m = &fUnitInvMatrix;
100 }
reed@android.com07d1f002009-08-13 19:35:48 +0000101
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 fBitmap = &fOrigBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 if (fOrigBitmap.hasMipMap()) {
104 int shift = fOrigBitmap.extractMipLevel(&fMipBitmap,
105 SkScalarToFixed(m->getScaleX()),
106 SkScalarToFixed(m->getSkewY()));
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 if (shift > 0) {
109 if (m != &fUnitInvMatrix) {
110 fUnitInvMatrix = *m;
111 m = &fUnitInvMatrix;
112 }
113
114 SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
115 fUnitInvMatrix.postScale(scale, scale);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 // now point here instead of fOrigBitmap
118 fBitmap = &fMipBitmap;
119 }
120 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121
reed@google.comc0e88e02012-10-17 21:11:56 +0000122 // wack our matrix to exactly no-scale, if we're really close to begin with
123 {
124 bool fixupMatrix = clamp_clamp ?
125 just_trans_clamp(*m, *fBitmap) : just_trans_general(*m);
126 if (fixupMatrix) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000127#ifdef SK_IGNORE_TRANS_CLAMP_FIX
reed@google.comc0e88e02012-10-17 21:11:56 +0000128 if (m != &fUnitInvMatrix) { // can't mutate the original
129 fUnitInvMatrix = inv;
130 m = &fUnitInvMatrix;
131 }
132 fUnitInvMatrix.set(SkMatrix::kMScaleX, SK_Scalar1);
133 fUnitInvMatrix.set(SkMatrix::kMScaleY, SK_Scalar1);
reed@google.comce1f3cc2013-01-05 14:37:48 +0000134#else
135 // If we can be treated just like translate, construct that inverse
136 // such that we landed in the proper place. Given that m may have
137 // some slight scale, we have to invert it to compute this new
138 // matrix.
139 SkMatrix forward;
140 if (m->invert(&forward)) {
141 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
142 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
143 fUnitInvMatrix.setTranslate(tx, ty);
144 m = &fUnitInvMatrix;
145 // now the following code will sniff m, and decide to take the
146 // fast case (since m is purely translate).
147 }
148#endif
reed@google.comc0e88e02012-10-17 21:11:56 +0000149 }
150 }
151
152 // Below this point, we should never refer to the inv parameter, since we
153 // may be using a munged version for "our" inverse.
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000154
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 fInvMatrix = m;
156 fInvProc = m->getMapXYProc();
157 fInvType = m->getType();
158 fInvSx = SkScalarToFixed(m->getScaleX());
reed@google.com4bc0a9d2012-03-07 21:47:41 +0000159 fInvSxFractionalInt = SkScalarToFractionalInt(m->getScaleX());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 fInvKy = SkScalarToFixed(m->getSkewY());
reed@google.com411215a2012-03-08 20:13:46 +0000161 fInvKyFractionalInt = SkScalarToFractionalInt(m->getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162
163 fAlphaScale = SkAlpha255To256(paint.getAlpha());
164
165 // pick-up filtering from the paint, but only if the matrix is
166 // more complex than identity/translate (i.e. no need to pay the cost
167 // of filtering if we're not scaled etc.).
168 // note: we explicitly check inv, since m might be scaled due to unitinv
169 // trickery, but we don't want to see that for this test
170 fDoFilter = paint.isFilterBitmap() &&
reed@google.comc0e88e02012-10-17 21:11:56 +0000171 (fInvType > SkMatrix::kTranslate_Mask &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 valid_for_filtering(fBitmap->width() | fBitmap->height()));
173
reed@android.com7a99eb12009-07-16 01:13:14 +0000174 fShaderProc32 = NULL;
175 fShaderProc16 = NULL;
176 fSampleProc32 = NULL;
177 fSampleProc16 = NULL;
reed@android.com48534f92009-07-16 20:53:26 +0000178
reed@android.com07d1f002009-08-13 19:35:48 +0000179 fMatrixProc = this->chooseMatrixProc(trivial_matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 if (NULL == fMatrixProc) {
181 return false;
182 }
183
184 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 int index = 0;
187 if (fAlphaScale < 256) { // note: this distinction is not used for D16
188 index |= 1;
189 }
190 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
191 index |= 2;
192 }
193 if (fDoFilter) {
194 index |= 4;
195 }
196 // bits 3,4,5 encoding the source bitmap format
197 switch (fBitmap->config()) {
198 case SkBitmap::kARGB_8888_Config:
199 index |= 0;
200 break;
201 case SkBitmap::kRGB_565_Config:
202 index |= 8;
203 break;
204 case SkBitmap::kIndex8_Config:
205 index |= 16;
206 break;
207 case SkBitmap::kARGB_4444_Config:
208 index |= 24;
209 break;
210 case SkBitmap::kA8_Config:
211 index |= 32;
212 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
reed@android.com3469c762009-02-24 19:03:20 +0000213 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 default:
215 return false;
216 }
217
digit@google.com3ada0ef2012-08-13 14:06:34 +0000218#if !SK_ARM_NEON_IS_ALWAYS
219 static const SampleProc32 gSkBitmapProcStateSample32[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 S32_opaque_D32_nofilter_DXDY,
221 S32_alpha_D32_nofilter_DXDY,
222 S32_opaque_D32_nofilter_DX,
223 S32_alpha_D32_nofilter_DX,
224 S32_opaque_D32_filter_DXDY,
225 S32_alpha_D32_filter_DXDY,
226 S32_opaque_D32_filter_DX,
227 S32_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000228
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 S16_opaque_D32_nofilter_DXDY,
230 S16_alpha_D32_nofilter_DXDY,
231 S16_opaque_D32_nofilter_DX,
232 S16_alpha_D32_nofilter_DX,
233 S16_opaque_D32_filter_DXDY,
234 S16_alpha_D32_filter_DXDY,
235 S16_opaque_D32_filter_DX,
236 S16_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 SI8_opaque_D32_nofilter_DXDY,
239 SI8_alpha_D32_nofilter_DXDY,
240 SI8_opaque_D32_nofilter_DX,
241 SI8_alpha_D32_nofilter_DX,
242 SI8_opaque_D32_filter_DXDY,
243 SI8_alpha_D32_filter_DXDY,
244 SI8_opaque_D32_filter_DX,
245 SI8_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 S4444_opaque_D32_nofilter_DXDY,
248 S4444_alpha_D32_nofilter_DXDY,
249 S4444_opaque_D32_nofilter_DX,
250 S4444_alpha_D32_nofilter_DX,
251 S4444_opaque_D32_filter_DXDY,
252 S4444_alpha_D32_filter_DXDY,
253 S4444_opaque_D32_filter_DX,
254 S4444_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000255
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000256 // A8 treats alpha/opaque the same (equally efficient)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 SA8_alpha_D32_nofilter_DXDY,
258 SA8_alpha_D32_nofilter_DXDY,
259 SA8_alpha_D32_nofilter_DX,
260 SA8_alpha_D32_nofilter_DX,
261 SA8_alpha_D32_filter_DXDY,
262 SA8_alpha_D32_filter_DXDY,
263 SA8_alpha_D32_filter_DX,
264 SA8_alpha_D32_filter_DX
265 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000266
digit@google.com3ada0ef2012-08-13 14:06:34 +0000267 static const SampleProc16 gSkBitmapProcStateSample16[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 S32_D16_nofilter_DXDY,
269 S32_D16_nofilter_DX,
270 S32_D16_filter_DXDY,
271 S32_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000272
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 S16_D16_nofilter_DXDY,
274 S16_D16_nofilter_DX,
275 S16_D16_filter_DXDY,
276 S16_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000277
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 SI8_D16_nofilter_DXDY,
279 SI8_D16_nofilter_DX,
280 SI8_D16_filter_DXDY,
281 SI8_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 // Don't support 4444 -> 565
284 NULL, NULL, NULL, NULL,
285 // Don't support A8 -> 565
286 NULL, NULL, NULL, NULL
287 };
digit@google.com3ada0ef2012-08-13 14:06:34 +0000288#endif
reed@android.com48534f92009-07-16 20:53:26 +0000289
digit@google.com3ada0ef2012-08-13 14:06:34 +0000290 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 index >>= 1; // shift away any opaque/alpha distinction
digit@google.com3ada0ef2012-08-13 14:06:34 +0000292 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293
reed@android.coma44b4cc2009-07-16 02:03:58 +0000294 // our special-case shaderprocs
digit@google.com3ada0ef2012-08-13 14:06:34 +0000295 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
reed@android.comaa9152a2009-07-17 21:24:56 +0000296 if (clamp_clamp) {
digit@google.com3ada0ef2012-08-13 14:06:34 +0000297 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
reed@android.comaa9152a2009-07-17 21:24:56 +0000298 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
299 SkShader::kRepeat_TileMode == fTileModeY) {
digit@google.com3ada0ef2012-08-13 14:06:34 +0000300 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
reed@android.comaa9152a2009-07-17 21:24:56 +0000301 }
digit@google.com3ada0ef2012-08-13 14:06:34 +0000302 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clamp_clamp) {
303 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
reed@android.coma44b4cc2009-07-16 02:03:58 +0000304 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000305
reed@google.com9a4c7462012-10-12 18:21:37 +0000306 if (NULL == fShaderProc32) {
307 fShaderProc32 = this->chooseShaderProc32();
308 }
309
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000310 // see if our platform has any accelerated overrides
311 this->platformProcs();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 return true;
313}
314
reed@google.com9a4c7462012-10-12 18:21:37 +0000315static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
316 int x, int y,
317 SkPMColor* SK_RESTRICT colors,
318 int count) {
319 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
320 SkASSERT(s.fInvKy == 0);
321 SkASSERT(count > 0 && colors != NULL);
322 SkASSERT(!s.fDoFilter);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000323
reed@google.com9a4c7462012-10-12 18:21:37 +0000324 const int maxX = s.fBitmap->width() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000325 const int maxY = s.fBitmap->height() - 1;
326 int ix = s.fFilterOneX + x;
327 int iy = SkClampMax(s.fFilterOneY + y, maxY);
328#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000329 {
330 SkPoint pt;
331 s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
332 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000333 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
334 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000335
reed@google.comf7698de2012-10-12 20:50:24 +0000336 SkASSERT(iy == iy2);
337 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000338 }
reed@google.comf7698de2012-10-12 20:50:24 +0000339#endif
340 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000341
reed@google.com9a4c7462012-10-12 18:21:37 +0000342 // clamp to the left
343 if (ix < 0) {
344 int n = SkMin32(-ix, count);
345 sk_memset32(colors, row[0], n);
346 count -= n;
347 if (0 == count) {
348 return;
349 }
350 colors += n;
351 SkASSERT(-ix == n);
352 ix = 0;
353 }
354 // copy the middle
355 if (ix <= maxX) {
356 int n = SkMin32(maxX - ix + 1, count);
357 memcpy(colors, row + ix, n * sizeof(SkPMColor));
358 count -= n;
359 if (0 == count) {
360 return;
361 }
362 colors += n;
363 }
364 SkASSERT(count > 0);
365 // clamp to the right
366 sk_memset32(colors, row[maxX], count);
367}
368
reed@google.coma8d99302012-10-16 20:23:25 +0000369static inline int sk_int_mod(int x, int n) {
370 SkASSERT(n > 0);
371 if ((unsigned)x >= (unsigned)n) {
372 if (x < 0) {
373 x = n + ~(~x % n);
374 } else {
375 x = x % n;
376 }
377 }
378 return x;
379}
380
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000381static inline int sk_int_mirror(int x, int n) {
382 x = sk_int_mod(x, 2 * n);
383 if (x >= n) {
384 x = n + ~(x - n);
385 }
386 return x;
387}
388
reed@google.coma8d99302012-10-16 20:23:25 +0000389static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
390 int x, int y,
391 SkPMColor* SK_RESTRICT colors,
392 int count) {
393 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
394 SkASSERT(s.fInvKy == 0);
395 SkASSERT(count > 0 && colors != NULL);
396 SkASSERT(!s.fDoFilter);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000397
reed@google.coma8d99302012-10-16 20:23:25 +0000398 const int stopX = s.fBitmap->width();
399 const int stopY = s.fBitmap->height();
400 int ix = s.fFilterOneX + x;
401 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
402#ifdef SK_DEBUG
403 {
404 SkPoint pt;
405 s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
406 SkIntToScalar(y) + SK_ScalarHalf, &pt);
407 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
408 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000409
reed@google.coma8d99302012-10-16 20:23:25 +0000410 SkASSERT(iy == iy2);
411 SkASSERT(ix == ix2);
412 }
413#endif
414 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
415
416 ix = sk_int_mod(ix, stopX);
417 for (;;) {
418 int n = SkMin32(stopX - ix, count);
419 memcpy(colors, row + ix, n * sizeof(SkPMColor));
420 count -= n;
421 if (0 == count) {
422 return;
423 }
424 colors += n;
425 ix = 0;
426 }
427}
428
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000429static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
430 int x, int y,
431 SkPMColor* SK_RESTRICT colors,
432 int count) {
433 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
434 SkASSERT(s.fInvKy == 0);
435 SkASSERT(count > 0 && colors != NULL);
436 SkASSERT(1 == s.fBitmap->width());
437
438 int iY0, iY1, iSubY;
439
440 if (s.fDoFilter) {
441 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
442 uint32_t xy[2];
443
444 mproc(s, xy, 1, x, y);
445
446 iY0 = xy[0] >> 18;
447 iY1 = xy[0] & 0x3FFF;
448 iSubY = (xy[0] >> 14) & 0xF;
449 } else {
450 int yTemp;
451
452 if (s.fInvType > SkMatrix::kTranslate_Mask) {
453 SkPoint pt;
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000454 s.fInvProc(*s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000455 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000456 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000457 &pt);
robertphillips@google.com1e305232013-01-22 20:29:16 +0000458 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000459 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000460 // bitmap's width and height. Since this method is going to do
461 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000462 if (SkShader::kClamp_TileMode != s.fTileModeX ||
463 SkShader::kClamp_TileMode != s.fTileModeY) {
464 yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
465 } else {
466 yTemp = SkScalarFloorToInt(pt.fY);
467 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000468 } else {
469 yTemp = s.fFilterOneY + y;
470 }
471
472 const int stopY = s.fBitmap->height();
473 switch (s.fTileModeY) {
474 case SkShader::kClamp_TileMode:
475 iY0 = SkClampMax(yTemp, stopY-1);
476 break;
477 case SkShader::kRepeat_TileMode:
478 iY0 = sk_int_mod(yTemp, stopY);
479 break;
480 case SkShader::kMirror_TileMode:
481 default:
482 iY0 = sk_int_mirror(yTemp, stopY);
483 break;
484 }
485
486#ifdef SK_DEBUG
487 {
488 SkPoint pt;
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000489 s.fInvProc(*s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000490 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000491 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000492 &pt);
robertphillips@google.comd5077752013-01-23 00:36:02 +0000493 if (s.fInvType > SkMatrix::kTranslate_Mask &&
494 (SkShader::kClamp_TileMode != s.fTileModeX ||
495 SkShader::kClamp_TileMode != s.fTileModeY)) {
robertphillips@google.com1e305232013-01-22 20:29:16 +0000496 pt.fY *= s.fBitmap->height();
497 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000498 int iY2;
499
500 switch (s.fTileModeY) {
501 case SkShader::kClamp_TileMode:
502 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
503 break;
504 case SkShader::kRepeat_TileMode:
505 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
506 break;
507 case SkShader::kMirror_TileMode:
508 default:
509 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
510 break;
511 }
512
513 SkASSERT(iY0 == iY2);
514 }
515#endif
516 }
517
518 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
519 SkPMColor color;
520
521 if (s.fDoFilter) {
522 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
523
524 if (s.fAlphaScale < 256) {
525 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
526 } else {
527 Filter_32_opaque(iSubY, *row0, *row1, &color);
528 }
529 } else {
530 if (s.fAlphaScale < 256) {
531 color = SkAlphaMulQ(*row0, s.fAlphaScale);
532 } else {
533 color = *row0;
534 }
535 }
536
537 sk_memset32(colors, color, count);
538}
539
reed@google.com6bb92bc2012-11-20 19:45:16 +0000540static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
541 SkPMColor* SK_RESTRICT colors, int count) {
542 // if we get called, the matrix is too tricky, so we just draw nothing
543 sk_memset32(colors, 0, count);
544}
545
546bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000547 SkPoint pt;
548 fInvProc(*fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000549
550 /*
551 * if the translate is larger than our ints, we can get random results, or
552 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
553 * negate it.
554 */
555 const SkScalar too_big = SkIntToScalar(1 << 30);
556 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
557 return false;
558 }
559
reed@google.coma8d99302012-10-16 20:23:25 +0000560 // Since we know we're not filtered, we re-purpose these fields allow
561 // us to go from device -> src coordinates w/ just an integer add,
562 // rather than running through the inverse-matrix
563 fFilterOneX = SkScalarFloorToInt(pt.fX);
564 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000565 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000566}
567
reed@google.com9a4c7462012-10-12 18:21:37 +0000568SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000569
570 if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
571 return NULL;
572 }
573
robertphillips@google.com90b17c72013-01-21 21:17:16 +0000574#ifndef SK_IGNORE_1XN_BITMAP_OPT
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000575 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
576
577 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
578 if (!fDoFilter && fInvType <= SkMatrix::kTranslate_Mask && !this->setupForTranslate()) {
579 return DoNothing_shaderproc;
580 }
581 return S32_D32_constX_shaderproc;
582 }
robertphillips@google.com90b17c72013-01-21 21:17:16 +0000583#endif
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000584
reed@google.com9a4c7462012-10-12 18:21:37 +0000585 if (fAlphaScale < 256) {
586 return NULL;
587 }
588 if (fInvType > SkMatrix::kTranslate_Mask) {
589 return NULL;
590 }
591 if (fDoFilter) {
592 return NULL;
593 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000594
reed@google.coma8d99302012-10-16 20:23:25 +0000595 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
596 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
597
598 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000599 if (this->setupForTranslate()) {
600 return Clamp_S32_D32_nofilter_trans_shaderproc;
601 }
602 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000603 }
reed@google.coma8d99302012-10-16 20:23:25 +0000604 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000605 if (this->setupForTranslate()) {
606 return Repeat_S32_D32_nofilter_trans_shaderproc;
607 }
608 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000609 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000610 return NULL;
611}
612
reed@android.com4c128c42009-08-14 13:54:37 +0000613///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000614
615#ifdef SK_DEBUG
616
617static void check_scale_nofilter(uint32_t bitmapXY[], int count,
618 unsigned mx, unsigned my) {
619 unsigned y = *bitmapXY++;
620 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000621
reed@google.com9fe287b2012-03-27 15:54:28 +0000622 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
623 for (int i = 0; i < count; ++i) {
624 SkASSERT(xptr[i] < mx);
625 }
626}
627
628static void check_scale_filter(uint32_t bitmapXY[], int count,
629 unsigned mx, unsigned my) {
630 uint32_t YY = *bitmapXY++;
631 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000632 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000633 SkASSERT(y0 < my);
634 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000635
reed@google.com9fe287b2012-03-27 15:54:28 +0000636 for (int i = 0; i < count; ++i) {
637 uint32_t XX = bitmapXY[i];
638 unsigned x0 = XX >> 18;
639 unsigned x1 = XX & 0x3FFF;
640 SkASSERT(x0 < mx);
641 SkASSERT(x1 < mx);
642 }
643}
644
645static void check_affine_nofilter(uint32_t bitmapXY[], int count,
646 unsigned mx, unsigned my) {
647 for (int i = 0; i < count; ++i) {
648 uint32_t XY = bitmapXY[i];
649 unsigned x = XY & 0xFFFF;
650 unsigned y = XY >> 16;
651 SkASSERT(x < mx);
652 SkASSERT(y < my);
653 }
654}
655
656static void check_affine_filter(uint32_t bitmapXY[], int count,
657 unsigned mx, unsigned my) {
658 for (int i = 0; i < count; ++i) {
659 uint32_t YY = *bitmapXY++;
660 unsigned y0 = YY >> 18;
661 unsigned y1 = YY & 0x3FFF;
662 SkASSERT(y0 < my);
663 SkASSERT(y1 < my);
664
665 uint32_t XX = *bitmapXY++;
666 unsigned x0 = XX >> 18;
667 unsigned x1 = XX & 0x3FFF;
668 SkASSERT(x0 < mx);
669 SkASSERT(x1 < mx);
670 }
671}
672
673void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
674 uint32_t bitmapXY[], int count,
675 int x, int y) {
676 SkASSERT(bitmapXY);
677 SkASSERT(count > 0);
678
679 state.fMatrixProc(state, bitmapXY, count, x, y);
680
681 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
682
683 // There are four formats possible:
684 // scale -vs- affine
685 // filter -vs- nofilter
686 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
687 proc = state.fDoFilter ? check_scale_filter : check_scale_nofilter;
688 } else {
689 proc = state.fDoFilter ? check_affine_filter : check_affine_nofilter;
690 }
691 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
692}
693
694SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
695 return DebugMatrixProc;
696}
697
698#endif
699
700///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000701/*
702 The storage requirements for the different matrix procs are as follows,
703 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000704
reed@android.com4c128c42009-08-14 13:54:37 +0000705 scale/translate nofilter Y(4bytes) + N * X
706 affine/perspective nofilter N * (X Y)
707 scale/translate filter Y Y + N * (X X)
708 affine/perspective filter N * (Y Y X X)
709 */
710int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
711 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000712
713 size &= ~3; // only care about 4-byte aligned chunks
714 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
715 size -= 4; // the shared Y (or YY) coordinate
716 if (size < 0) {
717 size = 0;
718 }
reed@android.com258cb222010-04-14 13:36:33 +0000719 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000720 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000721 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000722 }
723
reed@android.com258cb222010-04-14 13:36:33 +0000724 if (fDoFilter) {
725 size >>= 1;
726 }
727
728 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000729}
730