blob: 314fdd74c6b1b2d6df83b8ef9fbb537cbefef700 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
reed04617132014-08-21 09:46:49 -07007
8#include "SkBitmapCache.h"
reed64045422015-06-04 06:31:31 -07009#include "SkBitmapController.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkBitmapProcState.h"
11#include "SkColorPriv.h"
12#include "SkFilterProc.h"
13#include "SkPaint.h"
14#include "SkShader.h" // for tilemodes
digit@google.com3ada0ef2012-08-13 14:06:34 +000015#include "SkUtilsArm.h"
humper@google.com138ebc32013-07-19 20:20:04 +000016#include "SkBitmapScaler.h"
reed@google.comd94697c2013-07-24 14:31:33 +000017#include "SkMipMap.h"
reed@google.comcee9dcb2013-09-13 16:04:49 +000018#include "SkPixelRef.h"
commit-bot@chromium.orgf4491562014-05-28 17:30:02 +000019#include "SkImageEncoder.h"
reed011f39a2014-08-28 13:35:23 -070020#include "SkResourceCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021
digit@google.com3ada0ef2012-08-13 14:06:34 +000022#if !SK_ARM_NEON_IS_NONE
23// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
24extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
25extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
26extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
27extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
28extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
29extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
30extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
31extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
32#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000033
mtklein14e4d392014-10-23 14:35:01 -070034extern void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const SkBitmapProcState&, int, int, uint32_t*, int);
35
digit@google.com3ada0ef2012-08-13 14:06:34 +000036#define NAME_WRAP(x) x
37#include "SkBitmapProcState_filter.h"
38#include "SkBitmapProcState_procs.h"
reed@android.comb577b412009-10-27 17:49:32 +000039
reed64045422015-06-04 06:31:31 -070040SkBitmapProcState::SkBitmapProcState() : fBMState(NULL) {}
41
42SkBitmapProcState::~SkBitmapProcState() {
43 SkInPlaceDeleteCheck(fBMState, fBMStateStorage.get());
44}
45
reed@android.coma44b4cc2009-07-16 02:03:58 +000046///////////////////////////////////////////////////////////////////////////////
47
reed@google.comee056a82013-04-18 15:33:27 +000048// true iff the matrix contains, at most, scale and translate elements
49static bool matrix_only_scale_translate(const SkMatrix& m) {
50 return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask);
51}
52
reed@google.comc0e88e02012-10-17 21:11:56 +000053/**
54 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
55 * go ahead and treat it as if it were, so that subsequent code can go fast.
56 */
57static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
reed@google.comee056a82013-04-18 15:33:27 +000058 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000059
reed@google.comee056a82013-04-18 15:33:27 +000060 if (matrix.getType() & SkMatrix::kScale_Mask) {
61 SkRect src, dst;
62 bitmap.getBounds(&src);
reed@google.comf707adc2013-04-18 15:37:14 +000063
64 // Can't call mapRect(), since that will fix up inverted rectangles,
65 // e.g. when scale is negative, and we don't want to return true for
66 // those.
67 matrix.mapPoints(SkTCast<SkPoint*>(&dst),
68 SkTCast<const SkPoint*>(&src),
69 2);
reed@google.comee056a82013-04-18 15:33:27 +000070
71 // Now round all 4 edges to device space, and then compare the device
72 // width/height to the original. Note: we must map all 4 and subtract
73 // rather than map the "width" and compare, since we care about the
74 // phase (in pixel space) that any translate in the matrix might impart.
75 SkIRect idst;
76 dst.round(&idst);
77 return idst.width() == bitmap.width() && idst.height() == bitmap.height();
reed@google.comc0e88e02012-10-17 21:11:56 +000078 }
79 // if we got here, we're either kTranslate_Mask or identity
80 return true;
81}
82
83static bool just_trans_general(const SkMatrix& matrix) {
reed@google.comee056a82013-04-18 15:33:27 +000084 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000085
reed@google.comee056a82013-04-18 15:33:27 +000086 if (matrix.getType() & SkMatrix::kScale_Mask) {
reed@google.comc0e88e02012-10-17 21:11:56 +000087 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +000088
reed@google.comc0e88e02012-10-17 21:11:56 +000089 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
90 return false;
91 }
92 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
93 return false;
94 }
95 }
96 // if we got here, treat us as either kTranslate_Mask or identity
97 return true;
98}
99
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100static bool valid_for_filtering(unsigned dimension) {
101 // for filtering, width and height must fit in 14bits, since we use steal
102 // 2 bits from each to store our 4bit subpixel data
103 return (dimension & ~0x3FFF) == 0;
104}
105
reedf7094c42015-01-16 12:05:19 -0800106/*
107 * Analyze filter-quality and matrix, and decide how to implement that.
108 *
109 * In general, we cascade down the request level [ High ... None ]
110 * - for a given level, if we can fulfill it, fine, else
111 * - else we downgrade to the next lower level and try again.
112 * We can always fulfill requests for Low and None
113 * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
114 * and may be removed.
115 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
reed@google.comcee9dcb2013-09-13 16:04:49 +0000117 fBitmap = NULL;
reed@google.comef0e3192013-09-09 13:42:39 +0000118 fInvMatrix = inv;
reed93a12152015-03-16 10:08:34 -0700119 fFilterLevel = paint.getFilterQuality();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120
reed64045422015-06-04 06:31:31 -0700121 SkDefaultBitmapController controller;
122 fBMState = controller.requestBitmap(fOrigBitmap, inv, paint.getFilterQuality(),
123 fBMStateStorage.get(), fBMStateStorage.size());
124 if (NULL == fBMState) {
125 return false;
reedf7094c42015-01-16 12:05:19 -0800126 }
reed64045422015-06-04 06:31:31 -0700127 fBitmap = &fBMState->lockedBitmap();
128 fInvMatrix = fBMState->invMatrix();
129 fFilterLevel = fBMState->quality();
130 SkASSERT(fBitmap->getPixels());
131
reed@google.comef0e3192013-09-09 13:42:39 +0000132 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
133 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
134 SkShader::kClamp_TileMode == fTileModeY;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000135
humper535e3b22014-10-27 10:32:06 -0700136 // Most of the scanline procs deal with "unit" texture coordinates, as this
137 // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate
138 // those, we divide the matrix by its dimensions here.
139 //
140 // We don't do this if we're either trivial (can ignore the matrix) or clamping
141 // in both X and Y since clamping to width,height is just as easy as to 0xFFFF.
142
143 if (!(clampClamp || trivialMatrix)) {
144 fInvMatrix.postIDiv(fBitmap->width(), fBitmap->height());
reed@google.comef0e3192013-09-09 13:42:39 +0000145 }
146
humper@google.com9c96d4b2013-07-14 01:44:59 +0000147 // Now that all possible changes to the matrix have taken place, check
148 // to see if we're really close to a no-scale matrix. If so, explicitly
149 // set it to be so. Subsequent code may inspect this matrix to choose
150 // a faster path in this case.
151
152 // This code will only execute if the matrix has some scale component;
153 // if it's already pure translate then we won't do this inversion.
154
155 if (matrix_only_scale_translate(fInvMatrix)) {
reed@google.comee056a82013-04-18 15:33:27 +0000156 SkMatrix forward;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000157 if (fInvMatrix.invert(&forward)) {
158 if (clampClamp ? just_trans_clamp(forward, *fBitmap)
reedf7094c42015-01-16 12:05:19 -0800159 : just_trans_general(forward)) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000160 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
161 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000162 fInvMatrix.setTranslate(tx, ty);
reed@google.comce1f3cc2013-01-05 14:37:48 +0000163 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000164 }
165 }
166
humper@google.com9c96d4b2013-07-14 01:44:59 +0000167 fInvProc = fInvMatrix.getMapXYProc();
168 fInvType = fInvMatrix.getType();
169 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
170 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
171 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
172 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173
174 fAlphaScale = SkAlpha255To256(paint.getAlpha());
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000175
reed@android.com7a99eb12009-07-16 01:13:14 +0000176 fShaderProc32 = NULL;
177 fShaderProc16 = NULL;
178 fSampleProc32 = NULL;
179 fSampleProc16 = NULL;
mtklein96d68b72015-02-20 12:40:40 -0800180
humper@google.com9c96d4b2013-07-14 01:44:59 +0000181 // recompute the triviality of the matrix here because we may have
182 // changed it!
183
184 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
185
reed93a12152015-03-16 10:08:34 -0700186 if (kLow_SkFilterQuality == fFilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000187 // Only try bilerp if the matrix is "interesting" and
188 // the image has a suitable size.
189
190 if (fInvType <= SkMatrix::kTranslate_Mask ||
reedf7094c42015-01-16 12:05:19 -0800191 !valid_for_filtering(fBitmap->width() | fBitmap->height()))
192 {
reed93a12152015-03-16 10:08:34 -0700193 fFilterLevel = kNone_SkFilterQuality;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000194 }
195 }
196
reed26feeba2015-01-14 12:28:22 -0800197 return this->chooseScanlineProcs(trivialMatrix, clampClamp, paint);
198}
humper@google.com9c96d4b2013-07-14 01:44:59 +0000199
reed26feeba2015-01-14 12:28:22 -0800200bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp,
201 const SkPaint& paint) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000202 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000203 // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns NULL.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 if (NULL == fMatrixProc) {
205 return false;
206 }
207
208 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000209
reed00adc752014-07-12 21:10:52 -0700210 const SkAlphaType at = fBitmap->alphaType();
211
humper@google.com9c96d4b2013-07-14 01:44:59 +0000212 // No need to do this if we're doing HQ sampling; if filter quality is
213 // still set to HQ by the time we get here, then we must have installed
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000214 // the shader procs above and can skip all this.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215
reed93a12152015-03-16 10:08:34 -0700216 if (fFilterLevel < kHigh_SkFilterQuality) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000217
humper@google.com9c96d4b2013-07-14 01:44:59 +0000218 int index = 0;
219 if (fAlphaScale < 256) { // note: this distinction is not used for D16
220 index |= 1;
reed@android.comaa9152a2009-07-17 21:24:56 +0000221 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000222 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
223 index |= 2;
224 }
reed93a12152015-03-16 10:08:34 -0700225 if (fFilterLevel > kNone_SkFilterQuality) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000226 index |= 4;
227 }
228 // bits 3,4,5 encoding the source bitmap format
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000229 switch (fBitmap->colorType()) {
230 case kN32_SkColorType:
reed00adc752014-07-12 21:10:52 -0700231 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
232 return false;
233 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000234 index |= 0;
235 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000236 case kRGB_565_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000237 index |= 8;
238 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000239 case kIndex_8_SkColorType:
reed00adc752014-07-12 21:10:52 -0700240 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
241 return false;
242 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000243 index |= 16;
244 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000245 case kARGB_4444_SkColorType:
reed00adc752014-07-12 21:10:52 -0700246 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
247 return false;
248 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000249 index |= 24;
250 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000251 case kAlpha_8_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000252 index |= 32;
253 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
254 break;
reed0c9b1a82015-03-17 17:44:06 -0700255 case kGray_8_SkColorType:
256 index |= 40;
257 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
258 break;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000259 default:
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000260 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
humper@google.com9c96d4b2013-07-14 01:44:59 +0000261 return false;
262 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000263
reed26feeba2015-01-14 12:28:22 -0800264#if !SK_ARM_NEON_IS_ALWAYS
humper@google.com9c96d4b2013-07-14 01:44:59 +0000265 static const SampleProc32 gSkBitmapProcStateSample32[] = {
266 S32_opaque_D32_nofilter_DXDY,
267 S32_alpha_D32_nofilter_DXDY,
268 S32_opaque_D32_nofilter_DX,
269 S32_alpha_D32_nofilter_DX,
270 S32_opaque_D32_filter_DXDY,
271 S32_alpha_D32_filter_DXDY,
272 S32_opaque_D32_filter_DX,
273 S32_alpha_D32_filter_DX,
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +0000274
humper@google.com9c96d4b2013-07-14 01:44:59 +0000275 S16_opaque_D32_nofilter_DXDY,
276 S16_alpha_D32_nofilter_DXDY,
277 S16_opaque_D32_nofilter_DX,
278 S16_alpha_D32_nofilter_DX,
279 S16_opaque_D32_filter_DXDY,
280 S16_alpha_D32_filter_DXDY,
281 S16_opaque_D32_filter_DX,
282 S16_alpha_D32_filter_DX,
283
284 SI8_opaque_D32_nofilter_DXDY,
285 SI8_alpha_D32_nofilter_DXDY,
286 SI8_opaque_D32_nofilter_DX,
287 SI8_alpha_D32_nofilter_DX,
288 SI8_opaque_D32_filter_DXDY,
289 SI8_alpha_D32_filter_DXDY,
290 SI8_opaque_D32_filter_DX,
291 SI8_alpha_D32_filter_DX,
292
293 S4444_opaque_D32_nofilter_DXDY,
294 S4444_alpha_D32_nofilter_DXDY,
295 S4444_opaque_D32_nofilter_DX,
296 S4444_alpha_D32_nofilter_DX,
297 S4444_opaque_D32_filter_DXDY,
298 S4444_alpha_D32_filter_DXDY,
299 S4444_opaque_D32_filter_DX,
300 S4444_alpha_D32_filter_DX,
reed0c9b1a82015-03-17 17:44:06 -0700301
humper@google.com9c96d4b2013-07-14 01:44:59 +0000302 // A8 treats alpha/opaque the same (equally efficient)
303 SA8_alpha_D32_nofilter_DXDY,
304 SA8_alpha_D32_nofilter_DXDY,
305 SA8_alpha_D32_nofilter_DX,
306 SA8_alpha_D32_nofilter_DX,
307 SA8_alpha_D32_filter_DXDY,
308 SA8_alpha_D32_filter_DXDY,
309 SA8_alpha_D32_filter_DX,
reed0c9b1a82015-03-17 17:44:06 -0700310 SA8_alpha_D32_filter_DX,
311
312 // todo: possibly specialize on opaqueness
313 SG8_alpha_D32_nofilter_DXDY,
314 SG8_alpha_D32_nofilter_DXDY,
315 SG8_alpha_D32_nofilter_DX,
316 SG8_alpha_D32_nofilter_DX,
317 SG8_alpha_D32_filter_DXDY,
318 SG8_alpha_D32_filter_DXDY,
319 SG8_alpha_D32_filter_DX,
320 SG8_alpha_D32_filter_DX
humper@google.com9c96d4b2013-07-14 01:44:59 +0000321 };
322
323 static const SampleProc16 gSkBitmapProcStateSample16[] = {
324 S32_D16_nofilter_DXDY,
325 S32_D16_nofilter_DX,
326 S32_D16_filter_DXDY,
327 S32_D16_filter_DX,
328
329 S16_D16_nofilter_DXDY,
330 S16_D16_nofilter_DX,
331 S16_D16_filter_DXDY,
332 S16_D16_filter_DX,
333
334 SI8_D16_nofilter_DXDY,
335 SI8_D16_nofilter_DX,
336 SI8_D16_filter_DXDY,
337 SI8_D16_filter_DX,
338
339 // Don't support 4444 -> 565
340 NULL, NULL, NULL, NULL,
341 // Don't support A8 -> 565
reed0c9b1a82015-03-17 17:44:06 -0700342 NULL, NULL, NULL, NULL,
343 // Don't support G8 -> 565 (but we could)
humper@google.com9c96d4b2013-07-14 01:44:59 +0000344 NULL, NULL, NULL, NULL
345 };
reed26feeba2015-01-14 12:28:22 -0800346#endif
humper@google.com9c96d4b2013-07-14 01:44:59 +0000347
348 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
349 index >>= 1; // shift away any opaque/alpha distinction
350 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
351
352 // our special-case shaderprocs
353 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
354 if (clampClamp) {
355 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
356 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
357 SkShader::kRepeat_TileMode == fTileModeY) {
358 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
359 }
360 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
361 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
mtklein14e4d392014-10-23 14:35:01 -0700362 } else if (S32_opaque_D32_nofilter_DX == fSampleProc32 && clampClamp) {
363 fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000364 }
365
366 if (NULL == fShaderProc32) {
367 fShaderProc32 = this->chooseShaderProc32();
368 }
humper@google.comb0889472013-07-09 21:37:14 +0000369 }
mtklein96d68b72015-02-20 12:40:40 -0800370
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000371 // see if our platform has any accelerated overrides
372 this->platformProcs();
mtklein96d68b72015-02-20 12:40:40 -0800373
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 return true;
375}
376
reed@google.com9a4c7462012-10-12 18:21:37 +0000377static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
378 int x, int y,
379 SkPMColor* SK_RESTRICT colors,
380 int count) {
381 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
382 SkASSERT(s.fInvKy == 0);
383 SkASSERT(count > 0 && colors != NULL);
reed93a12152015-03-16 10:08:34 -0700384 SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000385
reed@google.com9a4c7462012-10-12 18:21:37 +0000386 const int maxX = s.fBitmap->width() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000387 const int maxY = s.fBitmap->height() - 1;
388 int ix = s.fFilterOneX + x;
389 int iy = SkClampMax(s.fFilterOneY + y, maxY);
390#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000391 {
392 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000393 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com9a4c7462012-10-12 18:21:37 +0000394 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000395 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
396 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000397
reed@google.comf7698de2012-10-12 20:50:24 +0000398 SkASSERT(iy == iy2);
399 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000400 }
reed@google.comf7698de2012-10-12 20:50:24 +0000401#endif
402 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000403
reed@google.com9a4c7462012-10-12 18:21:37 +0000404 // clamp to the left
405 if (ix < 0) {
406 int n = SkMin32(-ix, count);
407 sk_memset32(colors, row[0], n);
408 count -= n;
409 if (0 == count) {
410 return;
411 }
412 colors += n;
413 SkASSERT(-ix == n);
414 ix = 0;
415 }
416 // copy the middle
417 if (ix <= maxX) {
418 int n = SkMin32(maxX - ix + 1, count);
419 memcpy(colors, row + ix, n * sizeof(SkPMColor));
420 count -= n;
421 if (0 == count) {
422 return;
423 }
424 colors += n;
425 }
426 SkASSERT(count > 0);
427 // clamp to the right
428 sk_memset32(colors, row[maxX], count);
429}
430
reed@google.coma8d99302012-10-16 20:23:25 +0000431static inline int sk_int_mod(int x, int n) {
432 SkASSERT(n > 0);
433 if ((unsigned)x >= (unsigned)n) {
434 if (x < 0) {
435 x = n + ~(~x % n);
436 } else {
437 x = x % n;
438 }
439 }
440 return x;
441}
442
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000443static inline int sk_int_mirror(int x, int n) {
444 x = sk_int_mod(x, 2 * n);
445 if (x >= n) {
446 x = n + ~(x - n);
447 }
448 return x;
449}
450
reed@google.coma8d99302012-10-16 20:23:25 +0000451static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
452 int x, int y,
453 SkPMColor* SK_RESTRICT colors,
454 int count) {
455 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
456 SkASSERT(s.fInvKy == 0);
457 SkASSERT(count > 0 && colors != NULL);
reed93a12152015-03-16 10:08:34 -0700458 SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000459
reed@google.coma8d99302012-10-16 20:23:25 +0000460 const int stopX = s.fBitmap->width();
461 const int stopY = s.fBitmap->height();
462 int ix = s.fFilterOneX + x;
463 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
464#ifdef SK_DEBUG
465 {
466 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000467 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.coma8d99302012-10-16 20:23:25 +0000468 SkIntToScalar(y) + SK_ScalarHalf, &pt);
469 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
470 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000471
reed@google.coma8d99302012-10-16 20:23:25 +0000472 SkASSERT(iy == iy2);
473 SkASSERT(ix == ix2);
474 }
475#endif
476 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
477
478 ix = sk_int_mod(ix, stopX);
479 for (;;) {
480 int n = SkMin32(stopX - ix, count);
481 memcpy(colors, row + ix, n * sizeof(SkPMColor));
482 count -= n;
483 if (0 == count) {
484 return;
485 }
486 colors += n;
487 ix = 0;
488 }
489}
490
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000491static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
492 int x, int y,
493 SkPMColor* SK_RESTRICT colors,
494 int count) {
495 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
496 SkASSERT(s.fInvKy == 0);
497 SkASSERT(count > 0 && colors != NULL);
498 SkASSERT(1 == s.fBitmap->width());
499
scroggo@google.comad511322013-02-22 15:50:37 +0000500 int iY0;
501 int iY1 SK_INIT_TO_AVOID_WARNING;
502 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000503
reed93a12152015-03-16 10:08:34 -0700504 if (kNone_SkFilterQuality != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000505 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
506 uint32_t xy[2];
507
508 mproc(s, xy, 1, x, y);
509
510 iY0 = xy[0] >> 18;
511 iY1 = xy[0] & 0x3FFF;
512 iSubY = (xy[0] >> 14) & 0xF;
513 } else {
514 int yTemp;
515
516 if (s.fInvType > SkMatrix::kTranslate_Mask) {
517 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000518 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000519 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000520 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000521 &pt);
robertphillips@google.com1e305232013-01-22 20:29:16 +0000522 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000523 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000524 // bitmap's width and height. Since this method is going to do
525 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000526 if (SkShader::kClamp_TileMode != s.fTileModeX ||
527 SkShader::kClamp_TileMode != s.fTileModeY) {
528 yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
529 } else {
530 yTemp = SkScalarFloorToInt(pt.fY);
531 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000532 } else {
533 yTemp = s.fFilterOneY + y;
534 }
535
536 const int stopY = s.fBitmap->height();
537 switch (s.fTileModeY) {
538 case SkShader::kClamp_TileMode:
539 iY0 = SkClampMax(yTemp, stopY-1);
540 break;
541 case SkShader::kRepeat_TileMode:
542 iY0 = sk_int_mod(yTemp, stopY);
543 break;
544 case SkShader::kMirror_TileMode:
545 default:
546 iY0 = sk_int_mirror(yTemp, stopY);
547 break;
548 }
549
550#ifdef SK_DEBUG
551 {
552 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000553 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000554 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000555 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000556 &pt);
robertphillips@google.comd5077752013-01-23 00:36:02 +0000557 if (s.fInvType > SkMatrix::kTranslate_Mask &&
558 (SkShader::kClamp_TileMode != s.fTileModeX ||
559 SkShader::kClamp_TileMode != s.fTileModeY)) {
robertphillips@google.com1e305232013-01-22 20:29:16 +0000560 pt.fY *= s.fBitmap->height();
561 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000562 int iY2;
563
564 switch (s.fTileModeY) {
565 case SkShader::kClamp_TileMode:
566 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
567 break;
568 case SkShader::kRepeat_TileMode:
569 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
570 break;
571 case SkShader::kMirror_TileMode:
572 default:
573 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
574 break;
575 }
576
577 SkASSERT(iY0 == iY2);
578 }
579#endif
580 }
581
582 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
583 SkPMColor color;
584
reed93a12152015-03-16 10:08:34 -0700585 if (kNone_SkFilterQuality != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000586 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
587
588 if (s.fAlphaScale < 256) {
589 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
590 } else {
591 Filter_32_opaque(iSubY, *row0, *row1, &color);
592 }
593 } else {
594 if (s.fAlphaScale < 256) {
595 color = SkAlphaMulQ(*row0, s.fAlphaScale);
596 } else {
597 color = *row0;
598 }
599 }
600
601 sk_memset32(colors, color, count);
602}
603
reed@google.com6bb92bc2012-11-20 19:45:16 +0000604static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
605 SkPMColor* SK_RESTRICT colors, int count) {
606 // if we get called, the matrix is too tricky, so we just draw nothing
607 sk_memset32(colors, 0, count);
608}
609
610bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000611 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000612 fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000613
614 /*
615 * if the translate is larger than our ints, we can get random results, or
616 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
617 * negate it.
618 */
619 const SkScalar too_big = SkIntToScalar(1 << 30);
620 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
621 return false;
622 }
623
reed@google.coma8d99302012-10-16 20:23:25 +0000624 // Since we know we're not filtered, we re-purpose these fields allow
625 // us to go from device -> src coordinates w/ just an integer add,
626 // rather than running through the inverse-matrix
627 fFilterOneX = SkScalarFloorToInt(pt.fX);
628 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000629 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000630}
631
reed@google.com9a4c7462012-10-12 18:21:37 +0000632SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000633
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000634 if (kN32_SkColorType != fBitmap->colorType()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000635 return NULL;
636 }
637
638 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
639
640 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
reed93a12152015-03-16 10:08:34 -0700641 if (kNone_SkFilterQuality == fFilterLevel &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000642 fInvType <= SkMatrix::kTranslate_Mask &&
643 !this->setupForTranslate()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000644 return DoNothing_shaderproc;
645 }
646 return S32_D32_constX_shaderproc;
647 }
648
reed@google.com9a4c7462012-10-12 18:21:37 +0000649 if (fAlphaScale < 256) {
650 return NULL;
651 }
652 if (fInvType > SkMatrix::kTranslate_Mask) {
653 return NULL;
654 }
reed93a12152015-03-16 10:08:34 -0700655 if (kNone_SkFilterQuality != fFilterLevel) {
reed@google.com9a4c7462012-10-12 18:21:37 +0000656 return NULL;
657 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000658
reed@google.coma8d99302012-10-16 20:23:25 +0000659 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
660 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
661
662 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000663 if (this->setupForTranslate()) {
664 return Clamp_S32_D32_nofilter_trans_shaderproc;
665 }
666 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000667 }
reed@google.coma8d99302012-10-16 20:23:25 +0000668 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000669 if (this->setupForTranslate()) {
670 return Repeat_S32_D32_nofilter_trans_shaderproc;
671 }
672 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000673 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000674 return NULL;
675}
676
reed@android.com4c128c42009-08-14 13:54:37 +0000677///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000678
679#ifdef SK_DEBUG
680
681static void check_scale_nofilter(uint32_t bitmapXY[], int count,
682 unsigned mx, unsigned my) {
683 unsigned y = *bitmapXY++;
684 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000685
reed@google.com9fe287b2012-03-27 15:54:28 +0000686 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
687 for (int i = 0; i < count; ++i) {
688 SkASSERT(xptr[i] < mx);
689 }
690}
691
692static void check_scale_filter(uint32_t bitmapXY[], int count,
693 unsigned mx, unsigned my) {
694 uint32_t YY = *bitmapXY++;
695 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000696 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000697 SkASSERT(y0 < my);
698 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000699
reed@google.com9fe287b2012-03-27 15:54:28 +0000700 for (int i = 0; i < count; ++i) {
701 uint32_t XX = bitmapXY[i];
702 unsigned x0 = XX >> 18;
703 unsigned x1 = XX & 0x3FFF;
704 SkASSERT(x0 < mx);
705 SkASSERT(x1 < mx);
706 }
707}
708
709static void check_affine_nofilter(uint32_t bitmapXY[], int count,
710 unsigned mx, unsigned my) {
711 for (int i = 0; i < count; ++i) {
712 uint32_t XY = bitmapXY[i];
713 unsigned x = XY & 0xFFFF;
714 unsigned y = XY >> 16;
715 SkASSERT(x < mx);
716 SkASSERT(y < my);
717 }
718}
719
720static void check_affine_filter(uint32_t bitmapXY[], int count,
721 unsigned mx, unsigned my) {
722 for (int i = 0; i < count; ++i) {
723 uint32_t YY = *bitmapXY++;
724 unsigned y0 = YY >> 18;
725 unsigned y1 = YY & 0x3FFF;
726 SkASSERT(y0 < my);
727 SkASSERT(y1 < my);
728
729 uint32_t XX = *bitmapXY++;
730 unsigned x0 = XX >> 18;
731 unsigned x1 = XX & 0x3FFF;
732 SkASSERT(x0 < mx);
733 SkASSERT(x1 < mx);
734 }
735}
736
737void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
738 uint32_t bitmapXY[], int count,
739 int x, int y) {
740 SkASSERT(bitmapXY);
741 SkASSERT(count > 0);
742
743 state.fMatrixProc(state, bitmapXY, count, x, y);
744
745 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
746
747 // There are four formats possible:
748 // scale -vs- affine
749 // filter -vs- nofilter
750 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
reed93a12152015-03-16 10:08:34 -0700751 proc = state.fFilterLevel != kNone_SkFilterQuality ? check_scale_filter : check_scale_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000752 } else {
reed93a12152015-03-16 10:08:34 -0700753 proc = state.fFilterLevel != kNone_SkFilterQuality ? check_affine_filter : check_affine_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000754 }
755 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
756}
757
758SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
759 return DebugMatrixProc;
760}
761
762#endif
763
764///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000765/*
766 The storage requirements for the different matrix procs are as follows,
767 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000768
reed@android.com4c128c42009-08-14 13:54:37 +0000769 scale/translate nofilter Y(4bytes) + N * X
770 affine/perspective nofilter N * (X Y)
771 scale/translate filter Y Y + N * (X X)
772 affine/perspective filter N * (Y Y X X)
773 */
774int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
775 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000776
777 size &= ~3; // only care about 4-byte aligned chunks
778 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
779 size -= 4; // the shared Y (or YY) coordinate
780 if (size < 0) {
781 size = 0;
782 }
reed@android.com258cb222010-04-14 13:36:33 +0000783 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000784 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000785 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000786 }
787
reed93a12152015-03-16 10:08:34 -0700788 if (fFilterLevel != kNone_SkFilterQuality) {
reed@android.com258cb222010-04-14 13:36:33 +0000789 size >>= 1;
790 }
791
792 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000793}
mtklein14e4d392014-10-23 14:35:01 -0700794
795///////////////////////
796
797void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const SkBitmapProcState& s, int x, int y,
798 SkPMColor* SK_RESTRICT dst, int count) {
799 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
800 SkMatrix::kScale_Mask)) == 0);
801
802 const unsigned maxX = s.fBitmap->width() - 1;
803 SkFractionalInt fx;
804 int dstY;
805 {
806 SkPoint pt;
807 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf,
808 &pt);
809 fx = SkScalarToFractionalInt(pt.fY);
810 const unsigned maxY = s.fBitmap->height() - 1;
811 dstY = SkClampMax(SkFractionalIntToInt(fx), maxY);
812 fx = SkScalarToFractionalInt(pt.fX);
813 }
814
815 const SkPMColor* SK_RESTRICT src = s.fBitmap->getAddr32(0, dstY);
816 const SkFractionalInt dx = s.fInvSxFractionalInt;
817
818 // Check if we're safely inside [0...maxX] so no need to clamp each computed index.
819 //
820 if ((uint64_t)SkFractionalIntToInt(fx) <= maxX &&
821 (uint64_t)SkFractionalIntToInt(fx + dx * (count - 1)) <= maxX)
822 {
823 int count4 = count >> 2;
824 for (int i = 0; i < count4; ++i) {
825 SkPMColor src0 = src[SkFractionalIntToInt(fx)]; fx += dx;
826 SkPMColor src1 = src[SkFractionalIntToInt(fx)]; fx += dx;
827 SkPMColor src2 = src[SkFractionalIntToInt(fx)]; fx += dx;
828 SkPMColor src3 = src[SkFractionalIntToInt(fx)]; fx += dx;
829 dst[0] = src0;
830 dst[1] = src1;
831 dst[2] = src2;
832 dst[3] = src3;
833 dst += 4;
834 }
835 for (int i = (count4 << 2); i < count; ++i) {
836 unsigned index = SkFractionalIntToInt(fx);
837 SkASSERT(index <= maxX);
838 *dst++ = src[index];
839 fx += dx;
840 }
841 } else {
842 for (int i = 0; i < count; ++i) {
843 dst[i] = src[SkClampMax(SkFractionalIntToInt(fx), maxX)];
844 fx += dx;
845 }
846 }
847}
848