blob: 9f370c08989afe300c285ac5155bce0fff762339 [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.comee056a82013-04-18 15:33:27 +000033// true iff the matrix contains, at most, scale and translate elements
34static bool matrix_only_scale_translate(const SkMatrix& m) {
35 return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask);
36}
37
reed@google.comc0e88e02012-10-17 21:11:56 +000038/**
39 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
40 * go ahead and treat it as if it were, so that subsequent code can go fast.
41 */
42static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
reed@google.comee056a82013-04-18 15:33:27 +000043 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000044
reed@google.comee056a82013-04-18 15:33:27 +000045 if (matrix.getType() & SkMatrix::kScale_Mask) {
46 SkRect src, dst;
47 bitmap.getBounds(&src);
reed@google.comf707adc2013-04-18 15:37:14 +000048
49 // Can't call mapRect(), since that will fix up inverted rectangles,
50 // e.g. when scale is negative, and we don't want to return true for
51 // those.
52 matrix.mapPoints(SkTCast<SkPoint*>(&dst),
53 SkTCast<const SkPoint*>(&src),
54 2);
reed@google.comee056a82013-04-18 15:33:27 +000055
56 // Now round all 4 edges to device space, and then compare the device
57 // width/height to the original. Note: we must map all 4 and subtract
58 // rather than map the "width" and compare, since we care about the
59 // phase (in pixel space) that any translate in the matrix might impart.
60 SkIRect idst;
61 dst.round(&idst);
62 return idst.width() == bitmap.width() && idst.height() == bitmap.height();
reed@google.comc0e88e02012-10-17 21:11:56 +000063 }
64 // if we got here, we're either kTranslate_Mask or identity
65 return true;
66}
67
68static bool just_trans_general(const SkMatrix& matrix) {
reed@google.comee056a82013-04-18 15:33:27 +000069 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000070
reed@google.comee056a82013-04-18 15:33:27 +000071 if (matrix.getType() & SkMatrix::kScale_Mask) {
reed@google.comc0e88e02012-10-17 21:11:56 +000072 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +000073
reed@google.comc0e88e02012-10-17 21:11:56 +000074 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
75 return false;
76 }
77 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
78 return false;
79 }
80 }
81 // if we got here, treat us as either kTranslate_Mask or identity
82 return true;
83}
84
85///////////////////////////////////////////////////////////////////////////////
86
reed@android.com8a1c16f2008-12-17 15:59:43 +000087static bool valid_for_filtering(unsigned dimension) {
88 // for filtering, width and height must fit in 14bits, since we use steal
89 // 2 bits from each to store our 4bit subpixel data
90 return (dimension & ~0x3FFF) == 0;
91}
92
93bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
94 if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
95 return false;
96 }
reed@android.coma44b4cc2009-07-16 02:03:58 +000097
reed@android.com07d1f002009-08-13 19:35:48 +000098 const SkMatrix* m;
99 bool trivial_matrix = (inv.getType() & ~SkMatrix::kTranslate_Mask) == 0;
100 bool clamp_clamp = SkShader::kClamp_TileMode == fTileModeX &&
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000101 SkShader::kClamp_TileMode == fTileModeY;
reed@android.com07d1f002009-08-13 19:35:48 +0000102
103 if (clamp_clamp || trivial_matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 m = &inv;
105 } else {
106 fUnitInvMatrix = inv;
107 fUnitInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
108 m = &fUnitInvMatrix;
109 }
reed@android.com07d1f002009-08-13 19:35:48 +0000110
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 fBitmap = &fOrigBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 if (fOrigBitmap.hasMipMap()) {
113 int shift = fOrigBitmap.extractMipLevel(&fMipBitmap,
114 SkScalarToFixed(m->getScaleX()),
115 SkScalarToFixed(m->getSkewY()));
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 if (shift > 0) {
118 if (m != &fUnitInvMatrix) {
119 fUnitInvMatrix = *m;
120 m = &fUnitInvMatrix;
121 }
122
123 SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
124 fUnitInvMatrix.postScale(scale, scale);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000125
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 // now point here instead of fOrigBitmap
127 fBitmap = &fMipBitmap;
128 }
129 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130
reed@google.comc0e88e02012-10-17 21:11:56 +0000131 // wack our matrix to exactly no-scale, if we're really close to begin with
reed@google.comee056a82013-04-18 15:33:27 +0000132 if (matrix_only_scale_translate(*m)) {
133 SkMatrix forward;
134 if (m->invert(&forward)) {
135 if (clamp_clamp ? just_trans_clamp(forward, *fBitmap)
136 : just_trans_general(forward)) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000137 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
138 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
139 fUnitInvMatrix.setTranslate(tx, ty);
140 m = &fUnitInvMatrix;
141 // now the following code will sniff m, and decide to take the
142 // fast case (since m is purely translate).
143 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000144 }
145 }
146
147 // Below this point, we should never refer to the inv parameter, since we
148 // may be using a munged version for "our" inverse.
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000149
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 fInvMatrix = m;
151 fInvProc = m->getMapXYProc();
152 fInvType = m->getType();
153 fInvSx = SkScalarToFixed(m->getScaleX());
reed@google.com4bc0a9d2012-03-07 21:47:41 +0000154 fInvSxFractionalInt = SkScalarToFractionalInt(m->getScaleX());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 fInvKy = SkScalarToFixed(m->getSkewY());
reed@google.com411215a2012-03-08 20:13:46 +0000156 fInvKyFractionalInt = SkScalarToFractionalInt(m->getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157
158 fAlphaScale = SkAlpha255To256(paint.getAlpha());
159
160 // pick-up filtering from the paint, but only if the matrix is
161 // more complex than identity/translate (i.e. no need to pay the cost
162 // of filtering if we're not scaled etc.).
163 // note: we explicitly check inv, since m might be scaled due to unitinv
164 // trickery, but we don't want to see that for this test
165 fDoFilter = paint.isFilterBitmap() &&
reed@google.comc0e88e02012-10-17 21:11:56 +0000166 (fInvType > SkMatrix::kTranslate_Mask &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 valid_for_filtering(fBitmap->width() | fBitmap->height()));
168
reed@android.com7a99eb12009-07-16 01:13:14 +0000169 fShaderProc32 = NULL;
170 fShaderProc16 = NULL;
171 fSampleProc32 = NULL;
172 fSampleProc16 = NULL;
reed@android.com48534f92009-07-16 20:53:26 +0000173
reed@android.com07d1f002009-08-13 19:35:48 +0000174 fMatrixProc = this->chooseMatrixProc(trivial_matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 if (NULL == fMatrixProc) {
176 return false;
177 }
178
179 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000180
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 int index = 0;
182 if (fAlphaScale < 256) { // note: this distinction is not used for D16
183 index |= 1;
184 }
185 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
186 index |= 2;
187 }
188 if (fDoFilter) {
189 index |= 4;
190 }
191 // bits 3,4,5 encoding the source bitmap format
192 switch (fBitmap->config()) {
193 case SkBitmap::kARGB_8888_Config:
194 index |= 0;
195 break;
196 case SkBitmap::kRGB_565_Config:
197 index |= 8;
198 break;
199 case SkBitmap::kIndex8_Config:
200 index |= 16;
201 break;
202 case SkBitmap::kARGB_4444_Config:
203 index |= 24;
204 break;
205 case SkBitmap::kA8_Config:
206 index |= 32;
207 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
reed@android.com3469c762009-02-24 19:03:20 +0000208 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 default:
210 return false;
211 }
212
digit@google.com3ada0ef2012-08-13 14:06:34 +0000213#if !SK_ARM_NEON_IS_ALWAYS
214 static const SampleProc32 gSkBitmapProcStateSample32[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 S32_opaque_D32_nofilter_DXDY,
216 S32_alpha_D32_nofilter_DXDY,
217 S32_opaque_D32_nofilter_DX,
218 S32_alpha_D32_nofilter_DX,
219 S32_opaque_D32_filter_DXDY,
220 S32_alpha_D32_filter_DXDY,
221 S32_opaque_D32_filter_DX,
222 S32_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000223
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 S16_opaque_D32_nofilter_DXDY,
225 S16_alpha_D32_nofilter_DXDY,
226 S16_opaque_D32_nofilter_DX,
227 S16_alpha_D32_nofilter_DX,
228 S16_opaque_D32_filter_DXDY,
229 S16_alpha_D32_filter_DXDY,
230 S16_opaque_D32_filter_DX,
231 S16_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000232
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 SI8_opaque_D32_nofilter_DXDY,
234 SI8_alpha_D32_nofilter_DXDY,
235 SI8_opaque_D32_nofilter_DX,
236 SI8_alpha_D32_nofilter_DX,
237 SI8_opaque_D32_filter_DXDY,
238 SI8_alpha_D32_filter_DXDY,
239 SI8_opaque_D32_filter_DX,
240 SI8_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000241
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 S4444_opaque_D32_nofilter_DXDY,
243 S4444_alpha_D32_nofilter_DXDY,
244 S4444_opaque_D32_nofilter_DX,
245 S4444_alpha_D32_nofilter_DX,
246 S4444_opaque_D32_filter_DXDY,
247 S4444_alpha_D32_filter_DXDY,
248 S4444_opaque_D32_filter_DX,
249 S4444_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000250
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000251 // A8 treats alpha/opaque the same (equally efficient)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 SA8_alpha_D32_nofilter_DXDY,
253 SA8_alpha_D32_nofilter_DXDY,
254 SA8_alpha_D32_nofilter_DX,
255 SA8_alpha_D32_nofilter_DX,
256 SA8_alpha_D32_filter_DXDY,
257 SA8_alpha_D32_filter_DXDY,
258 SA8_alpha_D32_filter_DX,
259 SA8_alpha_D32_filter_DX
260 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000261
digit@google.com3ada0ef2012-08-13 14:06:34 +0000262 static const SampleProc16 gSkBitmapProcStateSample16[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 S32_D16_nofilter_DXDY,
264 S32_D16_nofilter_DX,
265 S32_D16_filter_DXDY,
266 S32_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000267
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 S16_D16_nofilter_DXDY,
269 S16_D16_nofilter_DX,
270 S16_D16_filter_DXDY,
271 S16_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000272
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 SI8_D16_nofilter_DXDY,
274 SI8_D16_nofilter_DX,
275 SI8_D16_filter_DXDY,
276 SI8_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000277
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 // Don't support 4444 -> 565
279 NULL, NULL, NULL, NULL,
280 // Don't support A8 -> 565
281 NULL, NULL, NULL, NULL
282 };
digit@google.com3ada0ef2012-08-13 14:06:34 +0000283#endif
reed@android.com48534f92009-07-16 20:53:26 +0000284
digit@google.com3ada0ef2012-08-13 14:06:34 +0000285 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 index >>= 1; // shift away any opaque/alpha distinction
digit@google.com3ada0ef2012-08-13 14:06:34 +0000287 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288
reed@android.coma44b4cc2009-07-16 02:03:58 +0000289 // our special-case shaderprocs
digit@google.com3ada0ef2012-08-13 14:06:34 +0000290 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
reed@android.comaa9152a2009-07-17 21:24:56 +0000291 if (clamp_clamp) {
digit@google.com3ada0ef2012-08-13 14:06:34 +0000292 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
reed@android.comaa9152a2009-07-17 21:24:56 +0000293 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
294 SkShader::kRepeat_TileMode == fTileModeY) {
digit@google.com3ada0ef2012-08-13 14:06:34 +0000295 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
reed@android.comaa9152a2009-07-17 21:24:56 +0000296 }
digit@google.com3ada0ef2012-08-13 14:06:34 +0000297 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clamp_clamp) {
298 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
reed@android.coma44b4cc2009-07-16 02:03:58 +0000299 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000300
reed@google.com9a4c7462012-10-12 18:21:37 +0000301 if (NULL == fShaderProc32) {
302 fShaderProc32 = this->chooseShaderProc32();
303 }
reed@google.com4c69a062013-05-23 20:11:56 +0000304
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000305 // see if our platform has any accelerated overrides
306 this->platformProcs();
reed@google.com4c69a062013-05-23 20:11:56 +0000307
308 if (NULL == fShaderProc32) {
309 fShaderProc32 = this->chooseBicubicFilterProc(paint);
310 }
311
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
scroggo@google.comad511322013-02-22 15:50:37 +0000438 int iY0;
439 int iY1 SK_INIT_TO_AVOID_WARNING;
440 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000441
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;
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000456 s.fInvProc(*s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000457 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000458 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000459 &pt);
robertphillips@google.com1e305232013-01-22 20:29:16 +0000460 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000461 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000462 // bitmap's width and height. Since this method is going to do
463 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000464 if (SkShader::kClamp_TileMode != s.fTileModeX ||
465 SkShader::kClamp_TileMode != s.fTileModeY) {
466 yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
467 } else {
468 yTemp = SkScalarFloorToInt(pt.fY);
469 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000470 } else {
471 yTemp = s.fFilterOneY + y;
472 }
473
474 const int stopY = s.fBitmap->height();
475 switch (s.fTileModeY) {
476 case SkShader::kClamp_TileMode:
477 iY0 = SkClampMax(yTemp, stopY-1);
478 break;
479 case SkShader::kRepeat_TileMode:
480 iY0 = sk_int_mod(yTemp, stopY);
481 break;
482 case SkShader::kMirror_TileMode:
483 default:
484 iY0 = sk_int_mirror(yTemp, stopY);
485 break;
486 }
487
488#ifdef SK_DEBUG
489 {
490 SkPoint pt;
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000491 s.fInvProc(*s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000492 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000493 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000494 &pt);
robertphillips@google.comd5077752013-01-23 00:36:02 +0000495 if (s.fInvType > SkMatrix::kTranslate_Mask &&
496 (SkShader::kClamp_TileMode != s.fTileModeX ||
497 SkShader::kClamp_TileMode != s.fTileModeY)) {
robertphillips@google.com1e305232013-01-22 20:29:16 +0000498 pt.fY *= s.fBitmap->height();
499 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000500 int iY2;
501
502 switch (s.fTileModeY) {
503 case SkShader::kClamp_TileMode:
504 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
505 break;
506 case SkShader::kRepeat_TileMode:
507 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
508 break;
509 case SkShader::kMirror_TileMode:
510 default:
511 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
512 break;
513 }
514
515 SkASSERT(iY0 == iY2);
516 }
517#endif
518 }
519
520 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
521 SkPMColor color;
522
523 if (s.fDoFilter) {
524 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
525
526 if (s.fAlphaScale < 256) {
527 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
528 } else {
529 Filter_32_opaque(iSubY, *row0, *row1, &color);
530 }
531 } else {
532 if (s.fAlphaScale < 256) {
533 color = SkAlphaMulQ(*row0, s.fAlphaScale);
534 } else {
535 color = *row0;
536 }
537 }
538
539 sk_memset32(colors, color, count);
540}
541
reed@google.com6bb92bc2012-11-20 19:45:16 +0000542static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
543 SkPMColor* SK_RESTRICT colors, int count) {
544 // if we get called, the matrix is too tricky, so we just draw nothing
545 sk_memset32(colors, 0, count);
546}
547
548bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000549 SkPoint pt;
550 fInvProc(*fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000551
552 /*
553 * if the translate is larger than our ints, we can get random results, or
554 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
555 * negate it.
556 */
557 const SkScalar too_big = SkIntToScalar(1 << 30);
558 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
559 return false;
560 }
561
reed@google.coma8d99302012-10-16 20:23:25 +0000562 // Since we know we're not filtered, we re-purpose these fields allow
563 // us to go from device -> src coordinates w/ just an integer add,
564 // rather than running through the inverse-matrix
565 fFilterOneX = SkScalarFloorToInt(pt.fX);
566 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000567 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000568}
569
reed@google.com9a4c7462012-10-12 18:21:37 +0000570SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000571
572 if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
573 return NULL;
574 }
575
576 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
577
578 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
579 if (!fDoFilter && fInvType <= SkMatrix::kTranslate_Mask && !this->setupForTranslate()) {
580 return DoNothing_shaderproc;
581 }
582 return S32_D32_constX_shaderproc;
583 }
584
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}