blob: 69c2ea19be30a6e7a97b5a1a6d5481e1917300da [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkBitmapProcState.h"
10#include "SkColorPriv.h"
11#include "SkFilterProc.h"
12#include "SkPaint.h"
13#include "SkShader.h" // for tilemodes
digit@google.com3ada0ef2012-08-13 14:06:34 +000014#include "SkUtilsArm.h"
humper@google.com138ebc32013-07-19 20:20:04 +000015#include "SkBitmapScaler.h"
reed@google.comd94697c2013-07-24 14:31:33 +000016#include "SkMipMap.h"
reed@google.comcee9dcb2013-09-13 16:04:49 +000017#include "SkPixelRef.h"
commit-bot@chromium.orgf4491562014-05-28 17:30:02 +000018#include "SkImageEncoder.h"
reed011f39a2014-08-28 13:35:23 -070019#include "SkResourceCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020
digit@google.com3ada0ef2012-08-13 14:06:34 +000021#if !SK_ARM_NEON_IS_NONE
22// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
23extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
24extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
25extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
26extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
27extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
28extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
29extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
30extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
31#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000032
mtklein14e4d392014-10-23 14:35:01 -070033extern void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const SkBitmapProcState&, int, int, uint32_t*, int);
34
digit@google.com3ada0ef2012-08-13 14:06:34 +000035#define NAME_WRAP(x) x
36#include "SkBitmapProcState_filter.h"
37#include "SkBitmapProcState_procs.h"
reed@android.comb577b412009-10-27 17:49:32 +000038
reed@android.coma44b4cc2009-07-16 02:03:58 +000039///////////////////////////////////////////////////////////////////////////////
40
reed@google.comee056a82013-04-18 15:33:27 +000041// true iff the matrix contains, at most, scale and translate elements
42static bool matrix_only_scale_translate(const SkMatrix& m) {
43 return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask);
44}
45
reed@google.comc0e88e02012-10-17 21:11:56 +000046/**
47 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
48 * go ahead and treat it as if it were, so that subsequent code can go fast.
49 */
50static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
reed@google.comee056a82013-04-18 15:33:27 +000051 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000052
reed@google.comee056a82013-04-18 15:33:27 +000053 if (matrix.getType() & SkMatrix::kScale_Mask) {
54 SkRect src, dst;
55 bitmap.getBounds(&src);
reed@google.comf707adc2013-04-18 15:37:14 +000056
57 // Can't call mapRect(), since that will fix up inverted rectangles,
58 // e.g. when scale is negative, and we don't want to return true for
59 // those.
60 matrix.mapPoints(SkTCast<SkPoint*>(&dst),
61 SkTCast<const SkPoint*>(&src),
62 2);
reed@google.comee056a82013-04-18 15:33:27 +000063
64 // Now round all 4 edges to device space, and then compare the device
65 // width/height to the original. Note: we must map all 4 and subtract
66 // rather than map the "width" and compare, since we care about the
67 // phase (in pixel space) that any translate in the matrix might impart.
68 SkIRect idst;
69 dst.round(&idst);
70 return idst.width() == bitmap.width() && idst.height() == bitmap.height();
reed@google.comc0e88e02012-10-17 21:11:56 +000071 }
72 // if we got here, we're either kTranslate_Mask or identity
73 return true;
74}
75
76static bool just_trans_general(const SkMatrix& matrix) {
reed@google.comee056a82013-04-18 15:33:27 +000077 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000078
reed@google.comee056a82013-04-18 15:33:27 +000079 if (matrix.getType() & SkMatrix::kScale_Mask) {
reed@google.comc0e88e02012-10-17 21:11:56 +000080 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +000081
reed@google.comc0e88e02012-10-17 21:11:56 +000082 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
83 return false;
84 }
85 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
86 return false;
87 }
88 }
89 // if we got here, treat us as either kTranslate_Mask or identity
90 return true;
91}
92
93///////////////////////////////////////////////////////////////////////////////
94
reed@android.com8a1c16f2008-12-17 15:59:43 +000095static bool valid_for_filtering(unsigned dimension) {
96 // for filtering, width and height must fit in 14bits, since we use steal
97 // 2 bits from each to store our 4bit subpixel data
98 return (dimension & ~0x3FFF) == 0;
99}
100
reed0c2da0c2015-02-19 18:39:49 -0800101static SkScalar effective_matrix_scale(const SkMatrix& mat) {
102 SkScalar dx = SkVector::Length(mat.getScaleX(), mat.getSkewY());
103 SkScalar dy = SkVector::Length(mat.getSkewX(), mat.getScaleY());
104#ifdef SK_SUPPORT_LEGACY_MIPMAP_EFFECTIVE_SCALE
105 return SkMaxScalar(dx, dy);
106#else
107 return SkScalarSqrt(dx * dy);
108#endif
reed@google.com9cfc83c2013-07-22 17:18:18 +0000109}
110
halcanary805ef152014-07-17 06:58:01 -0700111// Check to see that the size of the bitmap that would be produced by
112// scaling by the given inverted matrix is less than the maximum allowed.
113static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
reed1d9e80f2015-01-26 11:24:37 -0800114 size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit();
halcanary805ef152014-07-17 06:58:01 -0700115 if (0 == maximumAllocation) {
116 return true;
117 }
118 // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
119 // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
120 // Skip the division step:
121 return bm.info().getSafeSize(bm.info().minRowBytes())
122 < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY());
123}
124
reedf7094c42015-01-16 12:05:19 -0800125/*
126 * Extract the "best" scale factors from a matrix.
127 */
128static bool extract_scale(const SkMatrix& matrix, SkVector* scale) {
129 SkASSERT(!matrix.hasPerspective());
130 SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
131 SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]);
132 if (!SkScalarIsFinite(sx) || !SkScalarIsFinite(sy) ||
133 SkScalarNearlyZero(sx) || SkScalarNearlyZero(sy))
134 {
135 return false;
136 }
137 scale->set(sx, sy);
138 return true;
139}
140
141/*
142 * High quality is implemented by performing up-right scale-only filtering and then
143 * using bilerp for any remaining transformations.
144 */
145void SkBitmapProcState::processHQRequest() {
reed93a12152015-03-16 10:08:34 -0700146 SkASSERT(kHigh_SkFilterQuality == fFilterLevel);
reedf7094c42015-01-16 12:05:19 -0800147
148 // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
149 // to a valid bitmap. If we succeed, we will set this to Low instead.
reed93a12152015-03-16 10:08:34 -0700150 fFilterLevel = kMedium_SkFilterQuality;
reedf7094c42015-01-16 12:05:19 -0800151
152 if (kN32_SkColorType != fOrigBitmap.colorType() || !cache_size_okay(fOrigBitmap, fInvMatrix) ||
153 fInvMatrix.hasPerspective())
154 {
155 return; // can't handle the reqeust
156 }
157
158 SkScalar invScaleX = fInvMatrix.getScaleX();
159 SkScalar invScaleY = fInvMatrix.getScaleY();
160 if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
161 SkVector scale;
162 if (!extract_scale(fInvMatrix, &scale)) {
163 return; // can't find suitable scale factors
164 }
165 invScaleX = scale.x();
166 invScaleY = scale.y();
167 }
168 if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
169 return; // no need for HQ
170 }
171
172 SkScalar trueDestWidth = fOrigBitmap.width() / invScaleX;
173 SkScalar trueDestHeight = fOrigBitmap.height() / invScaleY;
174 SkScalar roundedDestWidth = SkScalarRoundToScalar(trueDestWidth);
175 SkScalar roundedDestHeight = SkScalarRoundToScalar(trueDestHeight);
176
177 if (!SkBitmapCache::Find(fOrigBitmap, roundedDestWidth, roundedDestHeight, &fScaledBitmap)) {
178 if (!SkBitmapScaler::Resize(&fScaledBitmap,
179 fOrigBitmap,
180 SkBitmapScaler::RESIZE_BEST,
181 roundedDestWidth,
182 roundedDestHeight,
183 SkResourceCache::GetAllocator())) {
184 return; // we failed to create fScaledBitmap
185 }
186
187 SkASSERT(fScaledBitmap.getPixels());
188 fScaledBitmap.setImmutable();
189 SkBitmapCache::Add(fOrigBitmap, roundedDestWidth, roundedDestHeight, fScaledBitmap);
190 }
191
192 SkASSERT(fScaledBitmap.getPixels());
193 fBitmap = &fScaledBitmap;
194
195 fInvMatrix.postScale(roundedDestWidth / fOrigBitmap.width(),
196 roundedDestHeight / fOrigBitmap.height());
reed93a12152015-03-16 10:08:34 -0700197 fFilterLevel = kLow_SkFilterQuality;
reedf7094c42015-01-16 12:05:19 -0800198}
199
200/*
201 * Modulo internal errors, this should always succeed *if* the matrix is downscaling
202 * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
203 */
204void SkBitmapProcState::processMediumRequest() {
reed93a12152015-03-16 10:08:34 -0700205 SkASSERT(kMedium_SkFilterQuality == fFilterLevel);
reedf7094c42015-01-16 12:05:19 -0800206
207 // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
208 // to a valid bitmap.
reed93a12152015-03-16 10:08:34 -0700209 fFilterLevel = kLow_SkFilterQuality;
reedf7094c42015-01-16 12:05:19 -0800210
reed0c2da0c2015-02-19 18:39:49 -0800211 SkScalar invScale = effective_matrix_scale(fInvMatrix);
reedf7094c42015-01-16 12:05:19 -0800212
reed0c2da0c2015-02-19 18:39:49 -0800213 if (invScale > SK_Scalar1) {
reedf7094c42015-01-16 12:05:19 -0800214 fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap));
215 if (NULL == fCurrMip.get()) {
216 fCurrMip.reset(SkMipMapCache::AddAndRef(fOrigBitmap));
217 if (NULL == fCurrMip.get()) {
218 return;
219 }
220 }
221 // diagnostic for a crasher...
222 if (NULL == fCurrMip->data()) {
223 sk_throw();
224 }
225
reed0c2da0c2015-02-19 18:39:49 -0800226 SkScalar levelScale = SkScalarInvert(invScale);
reedf7094c42015-01-16 12:05:19 -0800227 SkMipMap::Level level;
228 if (fCurrMip->extractLevel(levelScale, &level)) {
229 SkScalar invScaleFixup = level.fScale;
230 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
231
232 const SkImageInfo info = fOrigBitmap.info().makeWH(level.fWidth, level.fHeight);
233 // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
234 // that here, and not need to explicitly track it ourselves.
235 fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes);
236 fBitmap = &fScaledBitmap;
237 } else {
238 // failed to extract, so release the mipmap
239 fCurrMip.reset(NULL);
240 }
241 }
242}
243
reed@google.comcee9dcb2013-09-13 16:04:49 +0000244bool SkBitmapProcState::lockBaseBitmap() {
mtklein96d68b72015-02-20 12:40:40 -0800245 // TODO(reed): use bitmap cache here?
246 fScaledBitmap = fOrigBitmap;
247 fScaledBitmap.lockPixels();
248 if (NULL == fScaledBitmap.getPixels()) {
249 return false;
reed@google.comcee9dcb2013-09-13 16:04:49 +0000250 }
reed@google.comcee9dcb2013-09-13 16:04:49 +0000251 fBitmap = &fScaledBitmap;
252 return true;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000253}
254
reed89dee422014-12-04 08:06:17 -0800255static bool valid_for_drawing(const SkBitmap& bm) {
256 if (0 == bm.width() || 0 == bm.height()) {
257 return false; // nothing to draw
258 }
259 if (NULL == bm.pixelRef()) {
260 return false; // no pixels to read
261 }
262 if (bm.getTexture()) {
263 // we can handle texture (ugh) since lockPixels will perform a read-back
264 return true;
265 }
266 if (kIndex_8_SkColorType == bm.colorType()) {
267 SkAutoLockPixels alp(bm); // but we need to call it before getColorTable() is safe.
268 if (!bm.getColorTable()) {
269 return false;
270 }
271 }
272 return true;
273}
274
reedf7094c42015-01-16 12:05:19 -0800275/*
276 * Analyze filter-quality and matrix, and decide how to implement that.
277 *
278 * In general, we cascade down the request level [ High ... None ]
279 * - for a given level, if we can fulfill it, fine, else
280 * - else we downgrade to the next lower level and try again.
281 * We can always fulfill requests for Low and None
282 * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
283 * and may be removed.
284 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
reed89dee422014-12-04 08:06:17 -0800286 if (!valid_for_drawing(fOrigBitmap)) {
287 return false;
288 }
reed@android.coma44b4cc2009-07-16 02:03:58 +0000289
reed@google.comcee9dcb2013-09-13 16:04:49 +0000290 fBitmap = NULL;
reed@google.comef0e3192013-09-09 13:42:39 +0000291 fInvMatrix = inv;
reed93a12152015-03-16 10:08:34 -0700292 fFilterLevel = paint.getFilterQuality();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293
reed93a12152015-03-16 10:08:34 -0700294 if (kHigh_SkFilterQuality == fFilterLevel) {
reedf7094c42015-01-16 12:05:19 -0800295 this->processHQRequest();
296 }
reed93a12152015-03-16 10:08:34 -0700297 SkASSERT(fFilterLevel < kHigh_SkFilterQuality);
reedf7094c42015-01-16 12:05:19 -0800298
reed93a12152015-03-16 10:08:34 -0700299 if (kMedium_SkFilterQuality == fFilterLevel) {
reedf7094c42015-01-16 12:05:19 -0800300 this->processMediumRequest();
301 }
reed93a12152015-03-16 10:08:34 -0700302 SkASSERT(fFilterLevel < kMedium_SkFilterQuality);
reedf7094c42015-01-16 12:05:19 -0800303
304 if (NULL == fBitmap) {
305 if (!this->lockBaseBitmap()) {
306 return false;
307 }
308 }
309 SkASSERT(fBitmap);
commit-bot@chromium.org84f7a062014-04-17 16:46:07 +0000310
reed@google.comef0e3192013-09-09 13:42:39 +0000311 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
312 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
313 SkShader::kClamp_TileMode == fTileModeY;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000314
humper535e3b22014-10-27 10:32:06 -0700315 // Most of the scanline procs deal with "unit" texture coordinates, as this
316 // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate
317 // those, we divide the matrix by its dimensions here.
318 //
319 // We don't do this if we're either trivial (can ignore the matrix) or clamping
320 // in both X and Y since clamping to width,height is just as easy as to 0xFFFF.
321
322 if (!(clampClamp || trivialMatrix)) {
323 fInvMatrix.postIDiv(fBitmap->width(), fBitmap->height());
reed@google.comef0e3192013-09-09 13:42:39 +0000324 }
325
humper@google.com9c96d4b2013-07-14 01:44:59 +0000326 // Now that all possible changes to the matrix have taken place, check
327 // to see if we're really close to a no-scale matrix. If so, explicitly
328 // set it to be so. Subsequent code may inspect this matrix to choose
329 // a faster path in this case.
330
331 // This code will only execute if the matrix has some scale component;
332 // if it's already pure translate then we won't do this inversion.
333
334 if (matrix_only_scale_translate(fInvMatrix)) {
reed@google.comee056a82013-04-18 15:33:27 +0000335 SkMatrix forward;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000336 if (fInvMatrix.invert(&forward)) {
337 if (clampClamp ? just_trans_clamp(forward, *fBitmap)
reedf7094c42015-01-16 12:05:19 -0800338 : just_trans_general(forward)) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000339 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
340 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000341 fInvMatrix.setTranslate(tx, ty);
reed@google.comce1f3cc2013-01-05 14:37:48 +0000342 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000343 }
344 }
345
humper@google.com9c96d4b2013-07-14 01:44:59 +0000346 fInvProc = fInvMatrix.getMapXYProc();
347 fInvType = fInvMatrix.getType();
348 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
349 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
350 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
351 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352
353 fAlphaScale = SkAlpha255To256(paint.getAlpha());
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000354
reed@android.com7a99eb12009-07-16 01:13:14 +0000355 fShaderProc32 = NULL;
356 fShaderProc16 = NULL;
357 fSampleProc32 = NULL;
358 fSampleProc16 = NULL;
mtklein96d68b72015-02-20 12:40:40 -0800359
humper@google.com9c96d4b2013-07-14 01:44:59 +0000360 // recompute the triviality of the matrix here because we may have
361 // changed it!
362
363 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
364
reed93a12152015-03-16 10:08:34 -0700365 if (kLow_SkFilterQuality == fFilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000366 // Only try bilerp if the matrix is "interesting" and
367 // the image has a suitable size.
368
369 if (fInvType <= SkMatrix::kTranslate_Mask ||
reedf7094c42015-01-16 12:05:19 -0800370 !valid_for_filtering(fBitmap->width() | fBitmap->height()))
371 {
reed93a12152015-03-16 10:08:34 -0700372 fFilterLevel = kNone_SkFilterQuality;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000373 }
374 }
375
reed26feeba2015-01-14 12:28:22 -0800376 return this->chooseScanlineProcs(trivialMatrix, clampClamp, paint);
377}
humper@google.com9c96d4b2013-07-14 01:44:59 +0000378
reed26feeba2015-01-14 12:28:22 -0800379bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp,
380 const SkPaint& paint) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000381 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000382 // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns NULL.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 if (NULL == fMatrixProc) {
384 return false;
385 }
386
387 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000388
reed00adc752014-07-12 21:10:52 -0700389 const SkAlphaType at = fBitmap->alphaType();
390
humper@google.com9c96d4b2013-07-14 01:44:59 +0000391 // No need to do this if we're doing HQ sampling; if filter quality is
392 // still set to HQ by the time we get here, then we must have installed
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000393 // the shader procs above and can skip all this.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394
reed93a12152015-03-16 10:08:34 -0700395 if (fFilterLevel < kHigh_SkFilterQuality) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000396
humper@google.com9c96d4b2013-07-14 01:44:59 +0000397 int index = 0;
398 if (fAlphaScale < 256) { // note: this distinction is not used for D16
399 index |= 1;
reed@android.comaa9152a2009-07-17 21:24:56 +0000400 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000401 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
402 index |= 2;
403 }
reed93a12152015-03-16 10:08:34 -0700404 if (fFilterLevel > kNone_SkFilterQuality) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000405 index |= 4;
406 }
407 // bits 3,4,5 encoding the source bitmap format
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000408 switch (fBitmap->colorType()) {
409 case kN32_SkColorType:
reed00adc752014-07-12 21:10:52 -0700410 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
411 return false;
412 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000413 index |= 0;
414 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000415 case kRGB_565_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000416 index |= 8;
417 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000418 case kIndex_8_SkColorType:
reed00adc752014-07-12 21:10:52 -0700419 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
420 return false;
421 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000422 index |= 16;
423 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000424 case kARGB_4444_SkColorType:
reed00adc752014-07-12 21:10:52 -0700425 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
426 return false;
427 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000428 index |= 24;
429 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000430 case kAlpha_8_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000431 index |= 32;
432 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
433 break;
reed0c9b1a82015-03-17 17:44:06 -0700434 case kGray_8_SkColorType:
435 index |= 40;
436 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
437 break;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000438 default:
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000439 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
humper@google.com9c96d4b2013-07-14 01:44:59 +0000440 return false;
441 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000442
reed26feeba2015-01-14 12:28:22 -0800443#if !SK_ARM_NEON_IS_ALWAYS
humper@google.com9c96d4b2013-07-14 01:44:59 +0000444 static const SampleProc32 gSkBitmapProcStateSample32[] = {
445 S32_opaque_D32_nofilter_DXDY,
446 S32_alpha_D32_nofilter_DXDY,
447 S32_opaque_D32_nofilter_DX,
448 S32_alpha_D32_nofilter_DX,
449 S32_opaque_D32_filter_DXDY,
450 S32_alpha_D32_filter_DXDY,
451 S32_opaque_D32_filter_DX,
452 S32_alpha_D32_filter_DX,
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +0000453
humper@google.com9c96d4b2013-07-14 01:44:59 +0000454 S16_opaque_D32_nofilter_DXDY,
455 S16_alpha_D32_nofilter_DXDY,
456 S16_opaque_D32_nofilter_DX,
457 S16_alpha_D32_nofilter_DX,
458 S16_opaque_D32_filter_DXDY,
459 S16_alpha_D32_filter_DXDY,
460 S16_opaque_D32_filter_DX,
461 S16_alpha_D32_filter_DX,
462
463 SI8_opaque_D32_nofilter_DXDY,
464 SI8_alpha_D32_nofilter_DXDY,
465 SI8_opaque_D32_nofilter_DX,
466 SI8_alpha_D32_nofilter_DX,
467 SI8_opaque_D32_filter_DXDY,
468 SI8_alpha_D32_filter_DXDY,
469 SI8_opaque_D32_filter_DX,
470 SI8_alpha_D32_filter_DX,
471
472 S4444_opaque_D32_nofilter_DXDY,
473 S4444_alpha_D32_nofilter_DXDY,
474 S4444_opaque_D32_nofilter_DX,
475 S4444_alpha_D32_nofilter_DX,
476 S4444_opaque_D32_filter_DXDY,
477 S4444_alpha_D32_filter_DXDY,
478 S4444_opaque_D32_filter_DX,
479 S4444_alpha_D32_filter_DX,
reed0c9b1a82015-03-17 17:44:06 -0700480
humper@google.com9c96d4b2013-07-14 01:44:59 +0000481 // A8 treats alpha/opaque the same (equally efficient)
482 SA8_alpha_D32_nofilter_DXDY,
483 SA8_alpha_D32_nofilter_DXDY,
484 SA8_alpha_D32_nofilter_DX,
485 SA8_alpha_D32_nofilter_DX,
486 SA8_alpha_D32_filter_DXDY,
487 SA8_alpha_D32_filter_DXDY,
488 SA8_alpha_D32_filter_DX,
reed0c9b1a82015-03-17 17:44:06 -0700489 SA8_alpha_D32_filter_DX,
490
491 // todo: possibly specialize on opaqueness
492 SG8_alpha_D32_nofilter_DXDY,
493 SG8_alpha_D32_nofilter_DXDY,
494 SG8_alpha_D32_nofilter_DX,
495 SG8_alpha_D32_nofilter_DX,
496 SG8_alpha_D32_filter_DXDY,
497 SG8_alpha_D32_filter_DXDY,
498 SG8_alpha_D32_filter_DX,
499 SG8_alpha_D32_filter_DX
humper@google.com9c96d4b2013-07-14 01:44:59 +0000500 };
501
502 static const SampleProc16 gSkBitmapProcStateSample16[] = {
503 S32_D16_nofilter_DXDY,
504 S32_D16_nofilter_DX,
505 S32_D16_filter_DXDY,
506 S32_D16_filter_DX,
507
508 S16_D16_nofilter_DXDY,
509 S16_D16_nofilter_DX,
510 S16_D16_filter_DXDY,
511 S16_D16_filter_DX,
512
513 SI8_D16_nofilter_DXDY,
514 SI8_D16_nofilter_DX,
515 SI8_D16_filter_DXDY,
516 SI8_D16_filter_DX,
517
518 // Don't support 4444 -> 565
519 NULL, NULL, NULL, NULL,
520 // Don't support A8 -> 565
reed0c9b1a82015-03-17 17:44:06 -0700521 NULL, NULL, NULL, NULL,
522 // Don't support G8 -> 565 (but we could)
humper@google.com9c96d4b2013-07-14 01:44:59 +0000523 NULL, NULL, NULL, NULL
524 };
reed26feeba2015-01-14 12:28:22 -0800525#endif
humper@google.com9c96d4b2013-07-14 01:44:59 +0000526
527 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
528 index >>= 1; // shift away any opaque/alpha distinction
529 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
530
531 // our special-case shaderprocs
532 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
533 if (clampClamp) {
534 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
535 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
536 SkShader::kRepeat_TileMode == fTileModeY) {
537 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
538 }
539 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
540 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
mtklein14e4d392014-10-23 14:35:01 -0700541 } else if (S32_opaque_D32_nofilter_DX == fSampleProc32 && clampClamp) {
542 fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000543 }
544
545 if (NULL == fShaderProc32) {
546 fShaderProc32 = this->chooseShaderProc32();
547 }
humper@google.comb0889472013-07-09 21:37:14 +0000548 }
mtklein96d68b72015-02-20 12:40:40 -0800549
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000550 // see if our platform has any accelerated overrides
551 this->platformProcs();
mtklein96d68b72015-02-20 12:40:40 -0800552
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 return true;
554}
555
reed@google.com9a4c7462012-10-12 18:21:37 +0000556static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
557 int x, int y,
558 SkPMColor* SK_RESTRICT colors,
559 int count) {
560 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
561 SkASSERT(s.fInvKy == 0);
562 SkASSERT(count > 0 && colors != NULL);
reed93a12152015-03-16 10:08:34 -0700563 SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000564
reed@google.com9a4c7462012-10-12 18:21:37 +0000565 const int maxX = s.fBitmap->width() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000566 const int maxY = s.fBitmap->height() - 1;
567 int ix = s.fFilterOneX + x;
568 int iy = SkClampMax(s.fFilterOneY + y, maxY);
569#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000570 {
571 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000572 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com9a4c7462012-10-12 18:21:37 +0000573 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000574 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
575 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000576
reed@google.comf7698de2012-10-12 20:50:24 +0000577 SkASSERT(iy == iy2);
578 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000579 }
reed@google.comf7698de2012-10-12 20:50:24 +0000580#endif
581 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000582
reed@google.com9a4c7462012-10-12 18:21:37 +0000583 // clamp to the left
584 if (ix < 0) {
585 int n = SkMin32(-ix, count);
586 sk_memset32(colors, row[0], n);
587 count -= n;
588 if (0 == count) {
589 return;
590 }
591 colors += n;
592 SkASSERT(-ix == n);
593 ix = 0;
594 }
595 // copy the middle
596 if (ix <= maxX) {
597 int n = SkMin32(maxX - ix + 1, count);
598 memcpy(colors, row + ix, n * sizeof(SkPMColor));
599 count -= n;
600 if (0 == count) {
601 return;
602 }
603 colors += n;
604 }
605 SkASSERT(count > 0);
606 // clamp to the right
607 sk_memset32(colors, row[maxX], count);
608}
609
reed@google.coma8d99302012-10-16 20:23:25 +0000610static inline int sk_int_mod(int x, int n) {
611 SkASSERT(n > 0);
612 if ((unsigned)x >= (unsigned)n) {
613 if (x < 0) {
614 x = n + ~(~x % n);
615 } else {
616 x = x % n;
617 }
618 }
619 return x;
620}
621
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000622static inline int sk_int_mirror(int x, int n) {
623 x = sk_int_mod(x, 2 * n);
624 if (x >= n) {
625 x = n + ~(x - n);
626 }
627 return x;
628}
629
reed@google.coma8d99302012-10-16 20:23:25 +0000630static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
631 int x, int y,
632 SkPMColor* SK_RESTRICT colors,
633 int count) {
634 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
635 SkASSERT(s.fInvKy == 0);
636 SkASSERT(count > 0 && colors != NULL);
reed93a12152015-03-16 10:08:34 -0700637 SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000638
reed@google.coma8d99302012-10-16 20:23:25 +0000639 const int stopX = s.fBitmap->width();
640 const int stopY = s.fBitmap->height();
641 int ix = s.fFilterOneX + x;
642 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
643#ifdef SK_DEBUG
644 {
645 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000646 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.coma8d99302012-10-16 20:23:25 +0000647 SkIntToScalar(y) + SK_ScalarHalf, &pt);
648 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
649 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000650
reed@google.coma8d99302012-10-16 20:23:25 +0000651 SkASSERT(iy == iy2);
652 SkASSERT(ix == ix2);
653 }
654#endif
655 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
656
657 ix = sk_int_mod(ix, stopX);
658 for (;;) {
659 int n = SkMin32(stopX - ix, count);
660 memcpy(colors, row + ix, n * sizeof(SkPMColor));
661 count -= n;
662 if (0 == count) {
663 return;
664 }
665 colors += n;
666 ix = 0;
667 }
668}
669
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000670static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
671 int x, int y,
672 SkPMColor* SK_RESTRICT colors,
673 int count) {
674 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
675 SkASSERT(s.fInvKy == 0);
676 SkASSERT(count > 0 && colors != NULL);
677 SkASSERT(1 == s.fBitmap->width());
678
scroggo@google.comad511322013-02-22 15:50:37 +0000679 int iY0;
680 int iY1 SK_INIT_TO_AVOID_WARNING;
681 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000682
reed93a12152015-03-16 10:08:34 -0700683 if (kNone_SkFilterQuality != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000684 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
685 uint32_t xy[2];
686
687 mproc(s, xy, 1, x, y);
688
689 iY0 = xy[0] >> 18;
690 iY1 = xy[0] & 0x3FFF;
691 iSubY = (xy[0] >> 14) & 0xF;
692 } else {
693 int yTemp;
694
695 if (s.fInvType > SkMatrix::kTranslate_Mask) {
696 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000697 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000698 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000699 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000700 &pt);
robertphillips@google.com1e305232013-01-22 20:29:16 +0000701 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000702 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000703 // bitmap's width and height. Since this method is going to do
704 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000705 if (SkShader::kClamp_TileMode != s.fTileModeX ||
706 SkShader::kClamp_TileMode != s.fTileModeY) {
707 yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
708 } else {
709 yTemp = SkScalarFloorToInt(pt.fY);
710 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000711 } else {
712 yTemp = s.fFilterOneY + y;
713 }
714
715 const int stopY = s.fBitmap->height();
716 switch (s.fTileModeY) {
717 case SkShader::kClamp_TileMode:
718 iY0 = SkClampMax(yTemp, stopY-1);
719 break;
720 case SkShader::kRepeat_TileMode:
721 iY0 = sk_int_mod(yTemp, stopY);
722 break;
723 case SkShader::kMirror_TileMode:
724 default:
725 iY0 = sk_int_mirror(yTemp, stopY);
726 break;
727 }
728
729#ifdef SK_DEBUG
730 {
731 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000732 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000733 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000734 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000735 &pt);
robertphillips@google.comd5077752013-01-23 00:36:02 +0000736 if (s.fInvType > SkMatrix::kTranslate_Mask &&
737 (SkShader::kClamp_TileMode != s.fTileModeX ||
738 SkShader::kClamp_TileMode != s.fTileModeY)) {
robertphillips@google.com1e305232013-01-22 20:29:16 +0000739 pt.fY *= s.fBitmap->height();
740 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000741 int iY2;
742
743 switch (s.fTileModeY) {
744 case SkShader::kClamp_TileMode:
745 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
746 break;
747 case SkShader::kRepeat_TileMode:
748 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
749 break;
750 case SkShader::kMirror_TileMode:
751 default:
752 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
753 break;
754 }
755
756 SkASSERT(iY0 == iY2);
757 }
758#endif
759 }
760
761 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
762 SkPMColor color;
763
reed93a12152015-03-16 10:08:34 -0700764 if (kNone_SkFilterQuality != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000765 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
766
767 if (s.fAlphaScale < 256) {
768 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
769 } else {
770 Filter_32_opaque(iSubY, *row0, *row1, &color);
771 }
772 } else {
773 if (s.fAlphaScale < 256) {
774 color = SkAlphaMulQ(*row0, s.fAlphaScale);
775 } else {
776 color = *row0;
777 }
778 }
779
780 sk_memset32(colors, color, count);
781}
782
reed@google.com6bb92bc2012-11-20 19:45:16 +0000783static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
784 SkPMColor* SK_RESTRICT colors, int count) {
785 // if we get called, the matrix is too tricky, so we just draw nothing
786 sk_memset32(colors, 0, count);
787}
788
789bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000790 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000791 fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000792
793 /*
794 * if the translate is larger than our ints, we can get random results, or
795 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
796 * negate it.
797 */
798 const SkScalar too_big = SkIntToScalar(1 << 30);
799 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
800 return false;
801 }
802
reed@google.coma8d99302012-10-16 20:23:25 +0000803 // Since we know we're not filtered, we re-purpose these fields allow
804 // us to go from device -> src coordinates w/ just an integer add,
805 // rather than running through the inverse-matrix
806 fFilterOneX = SkScalarFloorToInt(pt.fX);
807 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000808 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000809}
810
reed@google.com9a4c7462012-10-12 18:21:37 +0000811SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000812
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000813 if (kN32_SkColorType != fBitmap->colorType()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000814 return NULL;
815 }
816
817 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
818
819 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
reed93a12152015-03-16 10:08:34 -0700820 if (kNone_SkFilterQuality == fFilterLevel &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000821 fInvType <= SkMatrix::kTranslate_Mask &&
822 !this->setupForTranslate()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000823 return DoNothing_shaderproc;
824 }
825 return S32_D32_constX_shaderproc;
826 }
827
reed@google.com9a4c7462012-10-12 18:21:37 +0000828 if (fAlphaScale < 256) {
829 return NULL;
830 }
831 if (fInvType > SkMatrix::kTranslate_Mask) {
832 return NULL;
833 }
reed93a12152015-03-16 10:08:34 -0700834 if (kNone_SkFilterQuality != fFilterLevel) {
reed@google.com9a4c7462012-10-12 18:21:37 +0000835 return NULL;
836 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000837
reed@google.coma8d99302012-10-16 20:23:25 +0000838 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
839 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
840
841 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000842 if (this->setupForTranslate()) {
843 return Clamp_S32_D32_nofilter_trans_shaderproc;
844 }
845 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000846 }
reed@google.coma8d99302012-10-16 20:23:25 +0000847 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000848 if (this->setupForTranslate()) {
849 return Repeat_S32_D32_nofilter_trans_shaderproc;
850 }
851 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000852 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000853 return NULL;
854}
855
reed@android.com4c128c42009-08-14 13:54:37 +0000856///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000857
858#ifdef SK_DEBUG
859
860static void check_scale_nofilter(uint32_t bitmapXY[], int count,
861 unsigned mx, unsigned my) {
862 unsigned y = *bitmapXY++;
863 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000864
reed@google.com9fe287b2012-03-27 15:54:28 +0000865 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
866 for (int i = 0; i < count; ++i) {
867 SkASSERT(xptr[i] < mx);
868 }
869}
870
871static void check_scale_filter(uint32_t bitmapXY[], int count,
872 unsigned mx, unsigned my) {
873 uint32_t YY = *bitmapXY++;
874 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000875 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000876 SkASSERT(y0 < my);
877 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000878
reed@google.com9fe287b2012-03-27 15:54:28 +0000879 for (int i = 0; i < count; ++i) {
880 uint32_t XX = bitmapXY[i];
881 unsigned x0 = XX >> 18;
882 unsigned x1 = XX & 0x3FFF;
883 SkASSERT(x0 < mx);
884 SkASSERT(x1 < mx);
885 }
886}
887
888static void check_affine_nofilter(uint32_t bitmapXY[], int count,
889 unsigned mx, unsigned my) {
890 for (int i = 0; i < count; ++i) {
891 uint32_t XY = bitmapXY[i];
892 unsigned x = XY & 0xFFFF;
893 unsigned y = XY >> 16;
894 SkASSERT(x < mx);
895 SkASSERT(y < my);
896 }
897}
898
899static void check_affine_filter(uint32_t bitmapXY[], int count,
900 unsigned mx, unsigned my) {
901 for (int i = 0; i < count; ++i) {
902 uint32_t YY = *bitmapXY++;
903 unsigned y0 = YY >> 18;
904 unsigned y1 = YY & 0x3FFF;
905 SkASSERT(y0 < my);
906 SkASSERT(y1 < my);
907
908 uint32_t XX = *bitmapXY++;
909 unsigned x0 = XX >> 18;
910 unsigned x1 = XX & 0x3FFF;
911 SkASSERT(x0 < mx);
912 SkASSERT(x1 < mx);
913 }
914}
915
916void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
917 uint32_t bitmapXY[], int count,
918 int x, int y) {
919 SkASSERT(bitmapXY);
920 SkASSERT(count > 0);
921
922 state.fMatrixProc(state, bitmapXY, count, x, y);
923
924 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
925
926 // There are four formats possible:
927 // scale -vs- affine
928 // filter -vs- nofilter
929 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
reed93a12152015-03-16 10:08:34 -0700930 proc = state.fFilterLevel != kNone_SkFilterQuality ? check_scale_filter : check_scale_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000931 } else {
reed93a12152015-03-16 10:08:34 -0700932 proc = state.fFilterLevel != kNone_SkFilterQuality ? check_affine_filter : check_affine_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000933 }
934 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
935}
936
937SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
938 return DebugMatrixProc;
939}
940
941#endif
942
943///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000944/*
945 The storage requirements for the different matrix procs are as follows,
946 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000947
reed@android.com4c128c42009-08-14 13:54:37 +0000948 scale/translate nofilter Y(4bytes) + N * X
949 affine/perspective nofilter N * (X Y)
950 scale/translate filter Y Y + N * (X X)
951 affine/perspective filter N * (Y Y X X)
952 */
953int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
954 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000955
956 size &= ~3; // only care about 4-byte aligned chunks
957 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
958 size -= 4; // the shared Y (or YY) coordinate
959 if (size < 0) {
960 size = 0;
961 }
reed@android.com258cb222010-04-14 13:36:33 +0000962 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000963 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000964 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000965 }
966
reed93a12152015-03-16 10:08:34 -0700967 if (fFilterLevel != kNone_SkFilterQuality) {
reed@android.com258cb222010-04-14 13:36:33 +0000968 size >>= 1;
969 }
970
971 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000972}
mtklein14e4d392014-10-23 14:35:01 -0700973
974///////////////////////
975
976void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const SkBitmapProcState& s, int x, int y,
977 SkPMColor* SK_RESTRICT dst, int count) {
978 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
979 SkMatrix::kScale_Mask)) == 0);
980
981 const unsigned maxX = s.fBitmap->width() - 1;
982 SkFractionalInt fx;
983 int dstY;
984 {
985 SkPoint pt;
986 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf,
987 &pt);
988 fx = SkScalarToFractionalInt(pt.fY);
989 const unsigned maxY = s.fBitmap->height() - 1;
990 dstY = SkClampMax(SkFractionalIntToInt(fx), maxY);
991 fx = SkScalarToFractionalInt(pt.fX);
992 }
993
994 const SkPMColor* SK_RESTRICT src = s.fBitmap->getAddr32(0, dstY);
995 const SkFractionalInt dx = s.fInvSxFractionalInt;
996
997 // Check if we're safely inside [0...maxX] so no need to clamp each computed index.
998 //
999 if ((uint64_t)SkFractionalIntToInt(fx) <= maxX &&
1000 (uint64_t)SkFractionalIntToInt(fx + dx * (count - 1)) <= maxX)
1001 {
1002 int count4 = count >> 2;
1003 for (int i = 0; i < count4; ++i) {
1004 SkPMColor src0 = src[SkFractionalIntToInt(fx)]; fx += dx;
1005 SkPMColor src1 = src[SkFractionalIntToInt(fx)]; fx += dx;
1006 SkPMColor src2 = src[SkFractionalIntToInt(fx)]; fx += dx;
1007 SkPMColor src3 = src[SkFractionalIntToInt(fx)]; fx += dx;
1008 dst[0] = src0;
1009 dst[1] = src1;
1010 dst[2] = src2;
1011 dst[3] = src3;
1012 dst += 4;
1013 }
1014 for (int i = (count4 << 2); i < count; ++i) {
1015 unsigned index = SkFractionalIntToInt(fx);
1016 SkASSERT(index <= maxX);
1017 *dst++ = src[index];
1018 fx += dx;
1019 }
1020 } else {
1021 for (int i = 0; i < count; ++i) {
1022 dst[i] = src[SkClampMax(SkFractionalIntToInt(fx), maxX)];
1023 fx += dx;
1024 }
1025 }
1026}
1027