blob: a1eff9f2a782b8e7d05fdb957998fc8174ba8c63 [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@android.com8a1c16f2008-12-17 15:59:43 +000035static bool valid_for_filtering(unsigned dimension) {
36 // for filtering, width and height must fit in 14bits, since we use steal
37 // 2 bits from each to store our 4bit subpixel data
38 return (dimension & ~0x3FFF) == 0;
39}
40
41bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
42 if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
43 return false;
44 }
reed@android.coma44b4cc2009-07-16 02:03:58 +000045
reed@android.com07d1f002009-08-13 19:35:48 +000046 const SkMatrix* m;
47 bool trivial_matrix = (inv.getType() & ~SkMatrix::kTranslate_Mask) == 0;
48 bool clamp_clamp = SkShader::kClamp_TileMode == fTileModeX &&
49 SkShader::kClamp_TileMode == fTileModeY;
50
51 if (clamp_clamp || trivial_matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 m = &inv;
53 } else {
54 fUnitInvMatrix = inv;
55 fUnitInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
56 m = &fUnitInvMatrix;
57 }
reed@android.com07d1f002009-08-13 19:35:48 +000058
reed@android.com8a1c16f2008-12-17 15:59:43 +000059 fBitmap = &fOrigBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 if (fOrigBitmap.hasMipMap()) {
61 int shift = fOrigBitmap.extractMipLevel(&fMipBitmap,
62 SkScalarToFixed(m->getScaleX()),
63 SkScalarToFixed(m->getSkewY()));
rmistry@google.comfbfcd562012-08-23 18:09:54 +000064
reed@android.com8a1c16f2008-12-17 15:59:43 +000065 if (shift > 0) {
66 if (m != &fUnitInvMatrix) {
67 fUnitInvMatrix = *m;
68 m = &fUnitInvMatrix;
69 }
70
71 SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
72 fUnitInvMatrix.postScale(scale, scale);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000073
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 // now point here instead of fOrigBitmap
75 fBitmap = &fMipBitmap;
76 }
77 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000078
79 fInvMatrix = m;
80 fInvProc = m->getMapXYProc();
81 fInvType = m->getType();
82 fInvSx = SkScalarToFixed(m->getScaleX());
reed@google.com4bc0a9d2012-03-07 21:47:41 +000083 fInvSxFractionalInt = SkScalarToFractionalInt(m->getScaleX());
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 fInvKy = SkScalarToFixed(m->getSkewY());
reed@google.com411215a2012-03-08 20:13:46 +000085 fInvKyFractionalInt = SkScalarToFractionalInt(m->getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +000086
87 fAlphaScale = SkAlpha255To256(paint.getAlpha());
88
89 // pick-up filtering from the paint, but only if the matrix is
90 // more complex than identity/translate (i.e. no need to pay the cost
91 // of filtering if we're not scaled etc.).
92 // note: we explicitly check inv, since m might be scaled due to unitinv
93 // trickery, but we don't want to see that for this test
94 fDoFilter = paint.isFilterBitmap() &&
95 (inv.getType() > SkMatrix::kTranslate_Mask &&
96 valid_for_filtering(fBitmap->width() | fBitmap->height()));
97
reed@android.com7a99eb12009-07-16 01:13:14 +000098 fShaderProc32 = NULL;
99 fShaderProc16 = NULL;
100 fSampleProc32 = NULL;
101 fSampleProc16 = NULL;
reed@android.com48534f92009-07-16 20:53:26 +0000102
reed@android.com07d1f002009-08-13 19:35:48 +0000103 fMatrixProc = this->chooseMatrixProc(trivial_matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 if (NULL == fMatrixProc) {
105 return false;
106 }
107
108 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000109
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 int index = 0;
111 if (fAlphaScale < 256) { // note: this distinction is not used for D16
112 index |= 1;
113 }
114 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
115 index |= 2;
116 }
117 if (fDoFilter) {
118 index |= 4;
119 }
120 // bits 3,4,5 encoding the source bitmap format
121 switch (fBitmap->config()) {
122 case SkBitmap::kARGB_8888_Config:
123 index |= 0;
124 break;
125 case SkBitmap::kRGB_565_Config:
126 index |= 8;
127 break;
128 case SkBitmap::kIndex8_Config:
129 index |= 16;
130 break;
131 case SkBitmap::kARGB_4444_Config:
132 index |= 24;
133 break;
134 case SkBitmap::kA8_Config:
135 index |= 32;
136 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
reed@android.com3469c762009-02-24 19:03:20 +0000137 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 default:
139 return false;
140 }
141
digit@google.com3ada0ef2012-08-13 14:06:34 +0000142#if !SK_ARM_NEON_IS_ALWAYS
143 static const SampleProc32 gSkBitmapProcStateSample32[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 S32_opaque_D32_nofilter_DXDY,
145 S32_alpha_D32_nofilter_DXDY,
146 S32_opaque_D32_nofilter_DX,
147 S32_alpha_D32_nofilter_DX,
148 S32_opaque_D32_filter_DXDY,
149 S32_alpha_D32_filter_DXDY,
150 S32_opaque_D32_filter_DX,
151 S32_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000152
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 S16_opaque_D32_nofilter_DXDY,
154 S16_alpha_D32_nofilter_DXDY,
155 S16_opaque_D32_nofilter_DX,
156 S16_alpha_D32_nofilter_DX,
157 S16_opaque_D32_filter_DXDY,
158 S16_alpha_D32_filter_DXDY,
159 S16_opaque_D32_filter_DX,
160 S16_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000161
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 SI8_opaque_D32_nofilter_DXDY,
163 SI8_alpha_D32_nofilter_DXDY,
164 SI8_opaque_D32_nofilter_DX,
165 SI8_alpha_D32_nofilter_DX,
166 SI8_opaque_D32_filter_DXDY,
167 SI8_alpha_D32_filter_DXDY,
168 SI8_opaque_D32_filter_DX,
169 SI8_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 S4444_opaque_D32_nofilter_DXDY,
172 S4444_alpha_D32_nofilter_DXDY,
173 S4444_opaque_D32_nofilter_DX,
174 S4444_alpha_D32_nofilter_DX,
175 S4444_opaque_D32_filter_DXDY,
176 S4444_alpha_D32_filter_DXDY,
177 S4444_opaque_D32_filter_DX,
178 S4444_alpha_D32_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 // A8 treats alpha/opauqe the same (equally efficient)
181 SA8_alpha_D32_nofilter_DXDY,
182 SA8_alpha_D32_nofilter_DXDY,
183 SA8_alpha_D32_nofilter_DX,
184 SA8_alpha_D32_nofilter_DX,
185 SA8_alpha_D32_filter_DXDY,
186 SA8_alpha_D32_filter_DXDY,
187 SA8_alpha_D32_filter_DX,
188 SA8_alpha_D32_filter_DX
189 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000190
digit@google.com3ada0ef2012-08-13 14:06:34 +0000191 static const SampleProc16 gSkBitmapProcStateSample16[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 S32_D16_nofilter_DXDY,
193 S32_D16_nofilter_DX,
194 S32_D16_filter_DXDY,
195 S32_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000196
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 S16_D16_nofilter_DXDY,
198 S16_D16_nofilter_DX,
199 S16_D16_filter_DXDY,
200 S16_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000201
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 SI8_D16_nofilter_DXDY,
203 SI8_D16_nofilter_DX,
204 SI8_D16_filter_DXDY,
205 SI8_D16_filter_DX,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000206
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 // Don't support 4444 -> 565
208 NULL, NULL, NULL, NULL,
209 // Don't support A8 -> 565
210 NULL, NULL, NULL, NULL
211 };
digit@google.com3ada0ef2012-08-13 14:06:34 +0000212#endif
reed@android.com48534f92009-07-16 20:53:26 +0000213
digit@google.com3ada0ef2012-08-13 14:06:34 +0000214 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 index >>= 1; // shift away any opaque/alpha distinction
digit@google.com3ada0ef2012-08-13 14:06:34 +0000216 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217
reed@android.coma44b4cc2009-07-16 02:03:58 +0000218 // our special-case shaderprocs
digit@google.com3ada0ef2012-08-13 14:06:34 +0000219 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
reed@android.comaa9152a2009-07-17 21:24:56 +0000220 if (clamp_clamp) {
digit@google.com3ada0ef2012-08-13 14:06:34 +0000221 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
reed@android.comaa9152a2009-07-17 21:24:56 +0000222 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
223 SkShader::kRepeat_TileMode == fTileModeY) {
digit@google.com3ada0ef2012-08-13 14:06:34 +0000224 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
reed@android.comaa9152a2009-07-17 21:24:56 +0000225 }
digit@google.com3ada0ef2012-08-13 14:06:34 +0000226 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clamp_clamp) {
227 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
reed@android.coma44b4cc2009-07-16 02:03:58 +0000228 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000229
reed@google.com9a4c7462012-10-12 18:21:37 +0000230 if (NULL == fShaderProc32) {
231 fShaderProc32 = this->chooseShaderProc32();
232 }
233
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000234 // see if our platform has any accelerated overrides
235 this->platformProcs();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 return true;
237}
238
reed@google.com9a4c7462012-10-12 18:21:37 +0000239static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
240 int x, int y,
241 SkPMColor* SK_RESTRICT colors,
242 int count) {
243 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
244 SkASSERT(s.fInvKy == 0);
245 SkASSERT(count > 0 && colors != NULL);
246 SkASSERT(!s.fDoFilter);
247
248 const int maxX = s.fBitmap->width() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000249 const int maxY = s.fBitmap->height() - 1;
250 int ix = s.fFilterOneX + x;
251 int iy = SkClampMax(s.fFilterOneY + y, maxY);
252#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000253 {
254 SkPoint pt;
255 s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
256 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000257 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
258 int ix2 = SkScalarFloorToInt(pt.fX);
259
260 SkASSERT(iy == iy2);
261 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000262 }
reed@google.comf7698de2012-10-12 20:50:24 +0000263#endif
264 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
reed@google.com9a4c7462012-10-12 18:21:37 +0000265
266 // clamp to the left
267 if (ix < 0) {
268 int n = SkMin32(-ix, count);
269 sk_memset32(colors, row[0], n);
270 count -= n;
271 if (0 == count) {
272 return;
273 }
274 colors += n;
275 SkASSERT(-ix == n);
276 ix = 0;
277 }
278 // copy the middle
279 if (ix <= maxX) {
280 int n = SkMin32(maxX - ix + 1, count);
281 memcpy(colors, row + ix, n * sizeof(SkPMColor));
282 count -= n;
283 if (0 == count) {
284 return;
285 }
286 colors += n;
287 }
288 SkASSERT(count > 0);
289 // clamp to the right
290 sk_memset32(colors, row[maxX], count);
291}
292
293SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
294 if (fAlphaScale < 256) {
295 return NULL;
296 }
297 if (fInvType > SkMatrix::kTranslate_Mask) {
298 return NULL;
299 }
300 if (fDoFilter) {
301 return NULL;
302 }
303 if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
304 return NULL;
305 }
306
307 if (SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY) {
reed@google.comf7698de2012-10-12 20:50:24 +0000308 SkPoint pt;
309 fInvProc(*fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
310 // Since we know we're not filtered, we re-purpose these fields allow
311 // us to go from device -> src coordinates w/ just an integer add,
312 // rather than running through the inverse-matrix
313 fFilterOneX = SkScalarFloorToInt(pt.fX);
314 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com9a4c7462012-10-12 18:21:37 +0000315 return Clamp_S32_D32_nofilter_trans_shaderproc;
316 }
317 return NULL;
318}
319
reed@android.com4c128c42009-08-14 13:54:37 +0000320///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000321
322#ifdef SK_DEBUG
323
324static void check_scale_nofilter(uint32_t bitmapXY[], int count,
325 unsigned mx, unsigned my) {
326 unsigned y = *bitmapXY++;
327 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000328
reed@google.com9fe287b2012-03-27 15:54:28 +0000329 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
330 for (int i = 0; i < count; ++i) {
331 SkASSERT(xptr[i] < mx);
332 }
333}
334
335static void check_scale_filter(uint32_t bitmapXY[], int count,
336 unsigned mx, unsigned my) {
337 uint32_t YY = *bitmapXY++;
338 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000339 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000340 SkASSERT(y0 < my);
341 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000342
reed@google.com9fe287b2012-03-27 15:54:28 +0000343 for (int i = 0; i < count; ++i) {
344 uint32_t XX = bitmapXY[i];
345 unsigned x0 = XX >> 18;
346 unsigned x1 = XX & 0x3FFF;
347 SkASSERT(x0 < mx);
348 SkASSERT(x1 < mx);
349 }
350}
351
352static void check_affine_nofilter(uint32_t bitmapXY[], int count,
353 unsigned mx, unsigned my) {
354 for (int i = 0; i < count; ++i) {
355 uint32_t XY = bitmapXY[i];
356 unsigned x = XY & 0xFFFF;
357 unsigned y = XY >> 16;
358 SkASSERT(x < mx);
359 SkASSERT(y < my);
360 }
361}
362
363static void check_affine_filter(uint32_t bitmapXY[], int count,
364 unsigned mx, unsigned my) {
365 for (int i = 0; i < count; ++i) {
366 uint32_t YY = *bitmapXY++;
367 unsigned y0 = YY >> 18;
368 unsigned y1 = YY & 0x3FFF;
369 SkASSERT(y0 < my);
370 SkASSERT(y1 < my);
371
372 uint32_t XX = *bitmapXY++;
373 unsigned x0 = XX >> 18;
374 unsigned x1 = XX & 0x3FFF;
375 SkASSERT(x0 < mx);
376 SkASSERT(x1 < mx);
377 }
378}
379
380void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
381 uint32_t bitmapXY[], int count,
382 int x, int y) {
383 SkASSERT(bitmapXY);
384 SkASSERT(count > 0);
385
386 state.fMatrixProc(state, bitmapXY, count, x, y);
387
388 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
389
390 // There are four formats possible:
391 // scale -vs- affine
392 // filter -vs- nofilter
393 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
394 proc = state.fDoFilter ? check_scale_filter : check_scale_nofilter;
395 } else {
396 proc = state.fDoFilter ? check_affine_filter : check_affine_nofilter;
397 }
398 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
399}
400
401SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
402 return DebugMatrixProc;
403}
404
405#endif
406
407///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000408/*
409 The storage requirements for the different matrix procs are as follows,
410 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000411
reed@android.com4c128c42009-08-14 13:54:37 +0000412 scale/translate nofilter Y(4bytes) + N * X
413 affine/perspective nofilter N * (X Y)
414 scale/translate filter Y Y + N * (X X)
415 affine/perspective filter N * (Y Y X X)
416 */
417int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
418 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000419
420 size &= ~3; // only care about 4-byte aligned chunks
421 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
422 size -= 4; // the shared Y (or YY) coordinate
423 if (size < 0) {
424 size = 0;
425 }
reed@android.com258cb222010-04-14 13:36:33 +0000426 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000427 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000428 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000429 }
430
reed@android.com258cb222010-04-14 13:36:33 +0000431 if (fDoFilter) {
432 size >>= 1;
433 }
434
435 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000436}
437