blob: c1874a9f255623df96769277cff56e8ae10511f3 [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"
humper@google.com138ebc32013-07-19 20:20:04 +000014#include "SkBitmapScaler.h"
reed@google.comd94697c2013-07-24 14:31:33 +000015#include "SkMipMap.h"
reed@google.com602a1d72013-07-23 19:13:54 +000016#include "SkScaledImageCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
digit@google.com3ada0ef2012-08-13 14:06:34 +000018#if !SK_ARM_NEON_IS_NONE
19// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
20extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
21extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
22extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
23extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
24extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
25extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
26extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
27extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
28#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
digit@google.com3ada0ef2012-08-13 14:06:34 +000030#define NAME_WRAP(x) x
31#include "SkBitmapProcState_filter.h"
32#include "SkBitmapProcState_procs.h"
reed@android.comb577b412009-10-27 17:49:32 +000033
reed@android.coma44b4cc2009-07-16 02:03:58 +000034///////////////////////////////////////////////////////////////////////////////
35
reed@google.comee056a82013-04-18 15:33:27 +000036// true iff the matrix contains, at most, scale and translate elements
37static bool matrix_only_scale_translate(const SkMatrix& m) {
38 return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask);
39}
40
reed@google.comc0e88e02012-10-17 21:11:56 +000041/**
42 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
43 * go ahead and treat it as if it were, so that subsequent code can go fast.
44 */
45static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
reed@google.comee056a82013-04-18 15:33:27 +000046 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000047
reed@google.comee056a82013-04-18 15:33:27 +000048 if (matrix.getType() & SkMatrix::kScale_Mask) {
49 SkRect src, dst;
50 bitmap.getBounds(&src);
reed@google.comf707adc2013-04-18 15:37:14 +000051
52 // Can't call mapRect(), since that will fix up inverted rectangles,
53 // e.g. when scale is negative, and we don't want to return true for
54 // those.
55 matrix.mapPoints(SkTCast<SkPoint*>(&dst),
56 SkTCast<const SkPoint*>(&src),
57 2);
reed@google.comee056a82013-04-18 15:33:27 +000058
59 // Now round all 4 edges to device space, and then compare the device
60 // width/height to the original. Note: we must map all 4 and subtract
61 // rather than map the "width" and compare, since we care about the
62 // phase (in pixel space) that any translate in the matrix might impart.
63 SkIRect idst;
64 dst.round(&idst);
65 return idst.width() == bitmap.width() && idst.height() == bitmap.height();
reed@google.comc0e88e02012-10-17 21:11:56 +000066 }
67 // if we got here, we're either kTranslate_Mask or identity
68 return true;
69}
70
71static bool just_trans_general(const SkMatrix& matrix) {
reed@google.comee056a82013-04-18 15:33:27 +000072 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000073
reed@google.comee056a82013-04-18 15:33:27 +000074 if (matrix.getType() & SkMatrix::kScale_Mask) {
reed@google.comc0e88e02012-10-17 21:11:56 +000075 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +000076
reed@google.comc0e88e02012-10-17 21:11:56 +000077 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
78 return false;
79 }
80 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
81 return false;
82 }
83 }
84 // if we got here, treat us as either kTranslate_Mask or identity
85 return true;
86}
87
88///////////////////////////////////////////////////////////////////////////////
89
reed@android.com8a1c16f2008-12-17 15:59:43 +000090static bool valid_for_filtering(unsigned dimension) {
91 // for filtering, width and height must fit in 14bits, since we use steal
92 // 2 bits from each to store our 4bit subpixel data
93 return (dimension & ~0x3FFF) == 0;
94}
95
reed@google.comd94697c2013-07-24 14:31:33 +000096static SkScalar effective_matrix_scale_sqrd(const SkMatrix& mat) {
reed@google.com9cfc83c2013-07-22 17:18:18 +000097 SkPoint v1, v2;
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +000098
reed@google.com9cfc83c2013-07-22 17:18:18 +000099 v1.fX = mat.getScaleX();
100 v1.fY = mat.getSkewY();
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000101
reed@google.com9cfc83c2013-07-22 17:18:18 +0000102 v2.fX = mat.getSkewX();
103 v2.fY = mat.getScaleY();
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000104
reed@google.com9cfc83c2013-07-22 17:18:18 +0000105 return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd());
106}
107
humper@google.com9c96d4b2013-07-14 01:44:59 +0000108// TODO -- we may want to pass the clip into this function so we only scale
109// the portion of the image that we're going to need. This will complicate
110// the interface to the cache, but might be well worth it.
111
112void SkBitmapProcState::possiblyScaleImage() {
113
reed@google.com9cfc83c2013-07-22 17:18:18 +0000114 if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
115 // none or low (bilerp) does not need to look any further
humper@google.com9c96d4b2013-07-14 01:44:59 +0000116 return;
117 }
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000118
humper@google.com138ebc32013-07-19 20:20:04 +0000119 // STEP 1: Highest quality direct scale?
humper@google.com9c96d4b2013-07-14 01:44:59 +0000120
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000121 // Check to see if the transformation matrix is simple, and if we're
122 // doing high quality scaling. If so, do the bitmap scale here and
humper@google.com138ebc32013-07-19 20:20:04 +0000123 // remove the scaling component from the matrix.
humper@google.com9c96d4b2013-07-14 01:44:59 +0000124
reed@google.com9cfc83c2013-07-22 17:18:18 +0000125 if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
humper@google.com138ebc32013-07-19 20:20:04 +0000126 fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000127 fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) {
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000128
reed@google.com602a1d72013-07-23 19:13:54 +0000129 SkScalar invScaleX = fInvMatrix.getScaleX();
130 SkScalar invScaleY = fInvMatrix.getScaleY();
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000131
reed@google.com602a1d72013-07-23 19:13:54 +0000132 SkASSERT(NULL == fScaledCacheID);
133 fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap,
134 invScaleX, invScaleY,
135 &fScaledBitmap);
136 if (NULL == fScaledCacheID) {
137 int dest_width = SkScalarCeilToInt(fOrigBitmap.width() / invScaleX);
138 int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / invScaleY);
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000139
reed@google.com602a1d72013-07-23 19:13:54 +0000140 // All the criteria are met; let's make a new bitmap.
humper@google.com138ebc32013-07-19 20:20:04 +0000141
reed@google.come15d9ec2013-09-06 12:57:45 +0000142 SkConvolutionProcs simd;
143 sk_bzero(&simd, sizeof(simd));
144 this->platformConvolutionProcs(&simd);
145
reed@google.com1e182252013-07-24 20:10:42 +0000146 if (!SkBitmapScaler::Resize(&fScaledBitmap,
147 fOrigBitmap,
148 SkBitmapScaler::RESIZE_BEST,
149 dest_width,
150 dest_height,
reed@google.comfed04b32013-09-05 20:31:17 +0000151 simd)) {
reed@google.com1e182252013-07-24 20:10:42 +0000152 // we failed to create fScaledBitmap, so just return and let
153 // the scanline proc handle it.
154 return;
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000155
reed@google.com1e182252013-07-24 20:10:42 +0000156 }
reed@google.com602a1d72013-07-23 19:13:54 +0000157 fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap,
158 invScaleX,
159 invScaleY,
160 fScaledBitmap);
161 }
humper@google.com138ebc32013-07-19 20:20:04 +0000162 fScaledBitmap.lockPixels();
skia.committer@gmail.com1f3c7382013-07-20 07:00:58 +0000163
humper@google.com9c96d4b2013-07-14 01:44:59 +0000164 fBitmap = &fScaledBitmap;
165
166 // set the inv matrix type to translate-only;
167
reed@google.com40039a32013-09-05 19:09:57 +0000168 fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(),
169 fInvMatrix.getTranslateY() / fInvMatrix.getScaleY());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000170
171 // no need for any further filtering; we just did it!
172
reed@google.com9cfc83c2013-07-22 17:18:18 +0000173 fFilterLevel = SkPaint::kNone_FilterLevel;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000174
175 return;
176 }
177
reed@google.com9cfc83c2013-07-22 17:18:18 +0000178 /*
179 * If we get here, the caller has requested either Med or High filter-level
180 *
181 * If High, then our special-case for scale-only did not take, and so we
182 * have to make a choice:
183 * 1. fall back on mipmaps + bilerp
184 * 2. fall back on scanline bicubic filter
185 * For now, we compute the "scale" value from the matrix, and have a
186 * threshold to decide when bicubic is better, and when mips are better.
187 * No doubt a fancier decision tree could be used uere.
188 *
189 * If Medium, then we just try to build a mipmap and select a level,
190 * setting the filter-level to kLow to signal that we just need bilerp
191 * to process the selected level.
192 */
humper@google.com9c96d4b2013-07-14 01:44:59 +0000193
reed@google.com9cfc83c2013-07-22 17:18:18 +0000194 SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000195
reed@google.com9cfc83c2013-07-22 17:18:18 +0000196 if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
197 // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller
198 // than this, then the mipmaps quality may be greater (certainly faster)
199 // so we only keep High quality if the scale is greater than this.
200 //
201 // Since we're dealing with the inverse, we compare against its inverse.
202 const SkScalar bicubicLimit = SkFloatToScalar(4.0f);
203 const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
204 if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline
205 return;
206 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000207
reed@google.com9cfc83c2013-07-22 17:18:18 +0000208 // else set the filter-level to Medium, since we're scaling down and
209 // want to reqeust mipmaps
210 fFilterLevel = SkPaint::kMedium_FilterLevel;
211 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000212
reed@google.com9cfc83c2013-07-22 17:18:18 +0000213 SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000214
reed@google.com9cfc83c2013-07-22 17:18:18 +0000215 /**
216 * Medium quality means use a mipmap for down-scaling, and just bilper
217 * for upscaling. Since we're examining the inverse matrix, we look for
218 * a scale > 1 to indicate down scaling by the CTM.
219 */
220 if (scaleSqd > SK_Scalar1) {
reed@google.comd94697c2013-07-24 14:31:33 +0000221 const SkMipMap* mip = NULL;
222
223 SkASSERT(NULL == fScaledCacheID);
224 fScaledCacheID = SkScaledImageCache::FindAndLockMip(fOrigBitmap, &mip);
225 if (!fScaledCacheID) {
226 SkASSERT(NULL == mip);
227 mip = SkMipMap::Build(fOrigBitmap);
228 if (mip) {
229 fScaledCacheID = SkScaledImageCache::AddAndLockMip(fOrigBitmap,
230 mip);
231 mip->unref(); // the cache took a ref
232 SkASSERT(fScaledCacheID);
233 }
234 } else {
235 SkASSERT(mip);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000236 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000237
reed@google.comd94697c2013-07-24 14:31:33 +0000238 if (mip) {
239 SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd));
240 SkMipMap::Level level;
241 if (mip->extractLevel(levelScale, &level)) {
242 SkScalar invScaleFixup = level.fScale;
243 fInvMatrix.postScale(invScaleFixup, invScaleFixup);
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000244
reed@google.comd94697c2013-07-24 14:31:33 +0000245 fScaledBitmap.setConfig(fOrigBitmap.config(),
246 level.fWidth, level.fHeight,
247 level.fRowBytes);
248 fScaledBitmap.setPixels(level.fPixels);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000249 fBitmap = &fScaledBitmap;
250 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000251 }
252 }
253
reed@google.comd94697c2013-07-24 14:31:33 +0000254 /*
255 * At this point, we may or may not have built a mipmap. Regardless, we
256 * now fall back on Low so will bilerp whatever fBitmap now points at.
257 */
reed@google.com9cfc83c2013-07-22 17:18:18 +0000258 fFilterLevel = SkPaint::kLow_FilterLevel;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000259}
260
261void SkBitmapProcState::endContext() {
262 SkDELETE(fBitmapFilter);
263 fBitmapFilter = NULL;
264 fScaledBitmap.reset();
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000265
reed@google.com602a1d72013-07-23 19:13:54 +0000266 if (fScaledCacheID) {
267 SkScaledImageCache::Unlock(fScaledCacheID);
268 fScaledCacheID = NULL;
269 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000270}
271
reed@google.com1e182252013-07-24 20:10:42 +0000272SkBitmapProcState::~SkBitmapProcState() {
273 if (fScaledCacheID) {
274 SkScaledImageCache::Unlock(fScaledCacheID);
275 }
276 SkDELETE(fBitmapFilter);
277}
278
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
280 if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
281 return false;
282 }
reed@android.coma44b4cc2009-07-16 02:03:58 +0000283
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 fBitmap = &fOrigBitmap;
reed@google.comef0e3192013-09-09 13:42:39 +0000285 fInvMatrix = inv;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000286
humper@google.com9c96d4b2013-07-14 01:44:59 +0000287 // initialize our filter quality to the one requested by the caller.
288 // We may downgrade it later if we determine that we either don't need
289 // or can't provide as high a quality filtering as the user requested.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000290
reed@google.com9cfc83c2013-07-22 17:18:18 +0000291 fFilterLevel = paint.getFilterLevel();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292
humper@google.com9c96d4b2013-07-14 01:44:59 +0000293#ifndef SK_IGNORE_IMAGE_PRESCALE
294 // possiblyScaleImage will look to see if it can rescale the image as a
295 // preprocess; either by scaling up to the target size, or by selecting
296 // a nearby mipmap level. If it does, it will adjust the working
297 // matrix as well as the working bitmap. It may also adjust the filter
298 // quality to avoid re-filtering an already perfectly scaled image.
299
300 this->possiblyScaleImage();
301#endif
302
reed@google.comef0e3192013-09-09 13:42:39 +0000303 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
304 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
305 SkShader::kClamp_TileMode == fTileModeY;
306
307 if (!(clampClamp || trivialMatrix)) {
308 fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
309 }
310
humper@google.com9c96d4b2013-07-14 01:44:59 +0000311 // Now that all possible changes to the matrix have taken place, check
312 // to see if we're really close to a no-scale matrix. If so, explicitly
313 // set it to be so. Subsequent code may inspect this matrix to choose
314 // a faster path in this case.
315
316 // This code will only execute if the matrix has some scale component;
317 // if it's already pure translate then we won't do this inversion.
318
319 if (matrix_only_scale_translate(fInvMatrix)) {
reed@google.comee056a82013-04-18 15:33:27 +0000320 SkMatrix forward;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000321 if (fInvMatrix.invert(&forward)) {
322 if (clampClamp ? just_trans_clamp(forward, *fBitmap)
reed@google.comee056a82013-04-18 15:33:27 +0000323 : just_trans_general(forward)) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000324 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
325 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000326 fInvMatrix.setTranslate(tx, ty);
327
reed@google.comce1f3cc2013-01-05 14:37:48 +0000328 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000329 }
330 }
331
humper@google.com9c96d4b2013-07-14 01:44:59 +0000332 fInvProc = fInvMatrix.getMapXYProc();
333 fInvType = fInvMatrix.getType();
334 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
335 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
336 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
337 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338
339 fAlphaScale = SkAlpha255To256(paint.getAlpha());
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000340
reed@android.com7a99eb12009-07-16 01:13:14 +0000341 fShaderProc32 = NULL;
342 fShaderProc16 = NULL;
343 fSampleProc32 = NULL;
344 fSampleProc16 = NULL;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000345
humper@google.com9c96d4b2013-07-14 01:44:59 +0000346 // recompute the triviality of the matrix here because we may have
347 // changed it!
348
349 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
350
reed@google.com9cfc83c2013-07-22 17:18:18 +0000351 if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000352 // If this is still set, that means we wanted HQ sampling
353 // but couldn't do it as a preprocess. Let's try to install
354 // the scanline version of the HQ sampler. If that process fails,
355 // downgrade to bilerp.
356
357 // NOTE: Might need to be careful here in the future when we want
358 // to have the platform proc have a shot at this; it's possible that
359 // the chooseBitmapFilterProc will fail to install a shader but a
360 // platform-specific one might succeed, so it might be premature here
361 // to fall back to bilerp. This needs thought.
362
363 SkASSERT(fInvType > SkMatrix::kTranslate_Mask);
364
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000365 if (!this->setBitmapFilterProcs()) {
reed@google.com9cfc83c2013-07-22 17:18:18 +0000366 fFilterLevel = SkPaint::kLow_FilterLevel;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000367 }
368 }
369
reed@google.com9cfc83c2013-07-22 17:18:18 +0000370 if (SkPaint::kLow_FilterLevel == fFilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000371 // Only try bilerp if the matrix is "interesting" and
372 // the image has a suitable size.
373
374 if (fInvType <= SkMatrix::kTranslate_Mask ||
reed@google.com9cfc83c2013-07-22 17:18:18 +0000375 !valid_for_filtering(fBitmap->width() | fBitmap->height())) {
376 fFilterLevel = SkPaint::kNone_FilterLevel;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000377 }
378 }
379
380 // At this point, we know exactly what kind of sampling the per-scanline
381 // shader will perform.
382
383 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 if (NULL == fMatrixProc) {
385 return false;
386 }
387
388 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000389
humper@google.com9c96d4b2013-07-14 01:44:59 +0000390 // No need to do this if we're doing HQ sampling; if filter quality is
391 // still set to HQ by the time we get here, then we must have installed
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000392 // the shader procs above and can skip all this.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393
reed@google.com9cfc83c2013-07-22 17:18:18 +0000394 if (fFilterLevel < SkPaint::kHigh_FilterLevel) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000395
humper@google.com9c96d4b2013-07-14 01:44:59 +0000396 int index = 0;
397 if (fAlphaScale < 256) { // note: this distinction is not used for D16
398 index |= 1;
reed@android.comaa9152a2009-07-17 21:24:56 +0000399 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000400 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
401 index |= 2;
402 }
reed@google.com9cfc83c2013-07-22 17:18:18 +0000403 if (fFilterLevel > SkPaint::kNone_FilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000404 index |= 4;
405 }
406 // bits 3,4,5 encoding the source bitmap format
407 switch (fBitmap->config()) {
408 case SkBitmap::kARGB_8888_Config:
409 index |= 0;
410 break;
411 case SkBitmap::kRGB_565_Config:
412 index |= 8;
413 break;
414 case SkBitmap::kIndex8_Config:
415 index |= 16;
416 break;
417 case SkBitmap::kARGB_4444_Config:
418 index |= 24;
419 break;
420 case SkBitmap::kA8_Config:
421 index |= 32;
422 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
423 break;
424 default:
425 return false;
426 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000427
humper@google.com9c96d4b2013-07-14 01:44:59 +0000428 #if !SK_ARM_NEON_IS_ALWAYS
429 static const SampleProc32 gSkBitmapProcStateSample32[] = {
430 S32_opaque_D32_nofilter_DXDY,
431 S32_alpha_D32_nofilter_DXDY,
432 S32_opaque_D32_nofilter_DX,
433 S32_alpha_D32_nofilter_DX,
434 S32_opaque_D32_filter_DXDY,
435 S32_alpha_D32_filter_DXDY,
436 S32_opaque_D32_filter_DX,
437 S32_alpha_D32_filter_DX,
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +0000438
humper@google.com9c96d4b2013-07-14 01:44:59 +0000439 S16_opaque_D32_nofilter_DXDY,
440 S16_alpha_D32_nofilter_DXDY,
441 S16_opaque_D32_nofilter_DX,
442 S16_alpha_D32_nofilter_DX,
443 S16_opaque_D32_filter_DXDY,
444 S16_alpha_D32_filter_DXDY,
445 S16_opaque_D32_filter_DX,
446 S16_alpha_D32_filter_DX,
447
448 SI8_opaque_D32_nofilter_DXDY,
449 SI8_alpha_D32_nofilter_DXDY,
450 SI8_opaque_D32_nofilter_DX,
451 SI8_alpha_D32_nofilter_DX,
452 SI8_opaque_D32_filter_DXDY,
453 SI8_alpha_D32_filter_DXDY,
454 SI8_opaque_D32_filter_DX,
455 SI8_alpha_D32_filter_DX,
456
457 S4444_opaque_D32_nofilter_DXDY,
458 S4444_alpha_D32_nofilter_DXDY,
459 S4444_opaque_D32_nofilter_DX,
460 S4444_alpha_D32_nofilter_DX,
461 S4444_opaque_D32_filter_DXDY,
462 S4444_alpha_D32_filter_DXDY,
463 S4444_opaque_D32_filter_DX,
464 S4444_alpha_D32_filter_DX,
465
466 // A8 treats alpha/opaque the same (equally efficient)
467 SA8_alpha_D32_nofilter_DXDY,
468 SA8_alpha_D32_nofilter_DXDY,
469 SA8_alpha_D32_nofilter_DX,
470 SA8_alpha_D32_nofilter_DX,
471 SA8_alpha_D32_filter_DXDY,
472 SA8_alpha_D32_filter_DXDY,
473 SA8_alpha_D32_filter_DX,
474 SA8_alpha_D32_filter_DX
475 };
476
477 static const SampleProc16 gSkBitmapProcStateSample16[] = {
478 S32_D16_nofilter_DXDY,
479 S32_D16_nofilter_DX,
480 S32_D16_filter_DXDY,
481 S32_D16_filter_DX,
482
483 S16_D16_nofilter_DXDY,
484 S16_D16_nofilter_DX,
485 S16_D16_filter_DXDY,
486 S16_D16_filter_DX,
487
488 SI8_D16_nofilter_DXDY,
489 SI8_D16_nofilter_DX,
490 SI8_D16_filter_DXDY,
491 SI8_D16_filter_DX,
492
493 // Don't support 4444 -> 565
494 NULL, NULL, NULL, NULL,
495 // Don't support A8 -> 565
496 NULL, NULL, NULL, NULL
497 };
498 #endif
499
500 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
501 index >>= 1; // shift away any opaque/alpha distinction
502 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
503
504 // our special-case shaderprocs
505 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
506 if (clampClamp) {
507 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
508 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
509 SkShader::kRepeat_TileMode == fTileModeY) {
510 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
511 }
512 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
513 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
514 }
515
516 if (NULL == fShaderProc32) {
517 fShaderProc32 = this->chooseShaderProc32();
518 }
humper@google.comb0889472013-07-09 21:37:14 +0000519 }
520
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000521 // see if our platform has any accelerated overrides
522 this->platformProcs();
reed@google.com4c69a062013-05-23 20:11:56 +0000523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 return true;
525}
526
reed@google.com9a4c7462012-10-12 18:21:37 +0000527static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
528 int x, int y,
529 SkPMColor* SK_RESTRICT colors,
530 int count) {
531 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
532 SkASSERT(s.fInvKy == 0);
533 SkASSERT(count > 0 && colors != NULL);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000534 SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000535
reed@google.com9a4c7462012-10-12 18:21:37 +0000536 const int maxX = s.fBitmap->width() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000537 const int maxY = s.fBitmap->height() - 1;
538 int ix = s.fFilterOneX + x;
539 int iy = SkClampMax(s.fFilterOneY + y, maxY);
540#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000541 {
542 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000543 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com9a4c7462012-10-12 18:21:37 +0000544 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000545 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
546 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000547
reed@google.comf7698de2012-10-12 20:50:24 +0000548 SkASSERT(iy == iy2);
549 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000550 }
reed@google.comf7698de2012-10-12 20:50:24 +0000551#endif
552 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000553
reed@google.com9a4c7462012-10-12 18:21:37 +0000554 // clamp to the left
555 if (ix < 0) {
556 int n = SkMin32(-ix, count);
557 sk_memset32(colors, row[0], n);
558 count -= n;
559 if (0 == count) {
560 return;
561 }
562 colors += n;
563 SkASSERT(-ix == n);
564 ix = 0;
565 }
566 // copy the middle
567 if (ix <= maxX) {
568 int n = SkMin32(maxX - ix + 1, count);
569 memcpy(colors, row + ix, n * sizeof(SkPMColor));
570 count -= n;
571 if (0 == count) {
572 return;
573 }
574 colors += n;
575 }
576 SkASSERT(count > 0);
577 // clamp to the right
578 sk_memset32(colors, row[maxX], count);
579}
580
reed@google.coma8d99302012-10-16 20:23:25 +0000581static inline int sk_int_mod(int x, int n) {
582 SkASSERT(n > 0);
583 if ((unsigned)x >= (unsigned)n) {
584 if (x < 0) {
585 x = n + ~(~x % n);
586 } else {
587 x = x % n;
588 }
589 }
590 return x;
591}
592
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000593static inline int sk_int_mirror(int x, int n) {
594 x = sk_int_mod(x, 2 * n);
595 if (x >= n) {
596 x = n + ~(x - n);
597 }
598 return x;
599}
600
reed@google.coma8d99302012-10-16 20:23:25 +0000601static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
602 int x, int y,
603 SkPMColor* SK_RESTRICT colors,
604 int count) {
605 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
606 SkASSERT(s.fInvKy == 0);
607 SkASSERT(count > 0 && colors != NULL);
reed@google.com9cfc83c2013-07-22 17:18:18 +0000608 SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000609
reed@google.coma8d99302012-10-16 20:23:25 +0000610 const int stopX = s.fBitmap->width();
611 const int stopY = s.fBitmap->height();
612 int ix = s.fFilterOneX + x;
613 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
614#ifdef SK_DEBUG
615 {
616 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000617 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.coma8d99302012-10-16 20:23:25 +0000618 SkIntToScalar(y) + SK_ScalarHalf, &pt);
619 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
620 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000621
reed@google.coma8d99302012-10-16 20:23:25 +0000622 SkASSERT(iy == iy2);
623 SkASSERT(ix == ix2);
624 }
625#endif
626 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
627
628 ix = sk_int_mod(ix, stopX);
629 for (;;) {
630 int n = SkMin32(stopX - ix, count);
631 memcpy(colors, row + ix, n * sizeof(SkPMColor));
632 count -= n;
633 if (0 == count) {
634 return;
635 }
636 colors += n;
637 ix = 0;
638 }
639}
640
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000641static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
642 int x, int y,
643 SkPMColor* SK_RESTRICT colors,
644 int count) {
645 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
646 SkASSERT(s.fInvKy == 0);
647 SkASSERT(count > 0 && colors != NULL);
648 SkASSERT(1 == s.fBitmap->width());
649
scroggo@google.comad511322013-02-22 15:50:37 +0000650 int iY0;
651 int iY1 SK_INIT_TO_AVOID_WARNING;
652 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000653
reed@google.com9cfc83c2013-07-22 17:18:18 +0000654 if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000655 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
656 uint32_t xy[2];
657
658 mproc(s, xy, 1, x, y);
659
660 iY0 = xy[0] >> 18;
661 iY1 = xy[0] & 0x3FFF;
662 iSubY = (xy[0] >> 14) & 0xF;
663 } else {
664 int yTemp;
665
666 if (s.fInvType > SkMatrix::kTranslate_Mask) {
667 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000668 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000669 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000670 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000671 &pt);
robertphillips@google.com1e305232013-01-22 20:29:16 +0000672 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000673 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000674 // bitmap's width and height. Since this method is going to do
675 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000676 if (SkShader::kClamp_TileMode != s.fTileModeX ||
677 SkShader::kClamp_TileMode != s.fTileModeY) {
678 yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
679 } else {
680 yTemp = SkScalarFloorToInt(pt.fY);
681 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000682 } else {
683 yTemp = s.fFilterOneY + y;
684 }
685
686 const int stopY = s.fBitmap->height();
687 switch (s.fTileModeY) {
688 case SkShader::kClamp_TileMode:
689 iY0 = SkClampMax(yTemp, stopY-1);
690 break;
691 case SkShader::kRepeat_TileMode:
692 iY0 = sk_int_mod(yTemp, stopY);
693 break;
694 case SkShader::kMirror_TileMode:
695 default:
696 iY0 = sk_int_mirror(yTemp, stopY);
697 break;
698 }
699
700#ifdef SK_DEBUG
701 {
702 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000703 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000704 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000705 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000706 &pt);
robertphillips@google.comd5077752013-01-23 00:36:02 +0000707 if (s.fInvType > SkMatrix::kTranslate_Mask &&
708 (SkShader::kClamp_TileMode != s.fTileModeX ||
709 SkShader::kClamp_TileMode != s.fTileModeY)) {
robertphillips@google.com1e305232013-01-22 20:29:16 +0000710 pt.fY *= s.fBitmap->height();
711 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000712 int iY2;
713
714 switch (s.fTileModeY) {
715 case SkShader::kClamp_TileMode:
716 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
717 break;
718 case SkShader::kRepeat_TileMode:
719 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
720 break;
721 case SkShader::kMirror_TileMode:
722 default:
723 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
724 break;
725 }
726
727 SkASSERT(iY0 == iY2);
728 }
729#endif
730 }
731
732 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
733 SkPMColor color;
734
reed@google.com9cfc83c2013-07-22 17:18:18 +0000735 if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000736 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
737
738 if (s.fAlphaScale < 256) {
739 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
740 } else {
741 Filter_32_opaque(iSubY, *row0, *row1, &color);
742 }
743 } else {
744 if (s.fAlphaScale < 256) {
745 color = SkAlphaMulQ(*row0, s.fAlphaScale);
746 } else {
747 color = *row0;
748 }
749 }
750
751 sk_memset32(colors, color, count);
752}
753
reed@google.com6bb92bc2012-11-20 19:45:16 +0000754static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
755 SkPMColor* SK_RESTRICT colors, int count) {
756 // if we get called, the matrix is too tricky, so we just draw nothing
757 sk_memset32(colors, 0, count);
758}
759
760bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000761 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000762 fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000763
764 /*
765 * if the translate is larger than our ints, we can get random results, or
766 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
767 * negate it.
768 */
769 const SkScalar too_big = SkIntToScalar(1 << 30);
770 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
771 return false;
772 }
773
reed@google.coma8d99302012-10-16 20:23:25 +0000774 // Since we know we're not filtered, we re-purpose these fields allow
775 // us to go from device -> src coordinates w/ just an integer add,
776 // rather than running through the inverse-matrix
777 fFilterOneX = SkScalarFloorToInt(pt.fX);
778 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000779 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000780}
781
reed@google.com9a4c7462012-10-12 18:21:37 +0000782SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000783
784 if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
785 return NULL;
786 }
787
788 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
789
790 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
reed@google.com9cfc83c2013-07-22 17:18:18 +0000791 if (SkPaint::kNone_FilterLevel == fFilterLevel &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000792 fInvType <= SkMatrix::kTranslate_Mask &&
793 !this->setupForTranslate()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000794 return DoNothing_shaderproc;
795 }
796 return S32_D32_constX_shaderproc;
797 }
798
reed@google.com9a4c7462012-10-12 18:21:37 +0000799 if (fAlphaScale < 256) {
800 return NULL;
801 }
802 if (fInvType > SkMatrix::kTranslate_Mask) {
803 return NULL;
804 }
reed@google.com9cfc83c2013-07-22 17:18:18 +0000805 if (SkPaint::kNone_FilterLevel != fFilterLevel) {
reed@google.com9a4c7462012-10-12 18:21:37 +0000806 return NULL;
807 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000808
reed@google.coma8d99302012-10-16 20:23:25 +0000809 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
810 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
811
812 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000813 if (this->setupForTranslate()) {
814 return Clamp_S32_D32_nofilter_trans_shaderproc;
815 }
816 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000817 }
reed@google.coma8d99302012-10-16 20:23:25 +0000818 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000819 if (this->setupForTranslate()) {
820 return Repeat_S32_D32_nofilter_trans_shaderproc;
821 }
822 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000823 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000824 return NULL;
825}
826
reed@android.com4c128c42009-08-14 13:54:37 +0000827///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000828
829#ifdef SK_DEBUG
830
831static void check_scale_nofilter(uint32_t bitmapXY[], int count,
832 unsigned mx, unsigned my) {
833 unsigned y = *bitmapXY++;
834 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000835
reed@google.com9fe287b2012-03-27 15:54:28 +0000836 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
837 for (int i = 0; i < count; ++i) {
838 SkASSERT(xptr[i] < mx);
839 }
840}
841
842static void check_scale_filter(uint32_t bitmapXY[], int count,
843 unsigned mx, unsigned my) {
844 uint32_t YY = *bitmapXY++;
845 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000846 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000847 SkASSERT(y0 < my);
848 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000849
reed@google.com9fe287b2012-03-27 15:54:28 +0000850 for (int i = 0; i < count; ++i) {
851 uint32_t XX = bitmapXY[i];
852 unsigned x0 = XX >> 18;
853 unsigned x1 = XX & 0x3FFF;
854 SkASSERT(x0 < mx);
855 SkASSERT(x1 < mx);
856 }
857}
858
859static void check_affine_nofilter(uint32_t bitmapXY[], int count,
860 unsigned mx, unsigned my) {
861 for (int i = 0; i < count; ++i) {
862 uint32_t XY = bitmapXY[i];
863 unsigned x = XY & 0xFFFF;
864 unsigned y = XY >> 16;
865 SkASSERT(x < mx);
866 SkASSERT(y < my);
867 }
868}
869
870static void check_affine_filter(uint32_t bitmapXY[], int count,
871 unsigned mx, unsigned my) {
872 for (int i = 0; i < count; ++i) {
873 uint32_t YY = *bitmapXY++;
874 unsigned y0 = YY >> 18;
875 unsigned y1 = YY & 0x3FFF;
876 SkASSERT(y0 < my);
877 SkASSERT(y1 < my);
878
879 uint32_t XX = *bitmapXY++;
880 unsigned x0 = XX >> 18;
881 unsigned x1 = XX & 0x3FFF;
882 SkASSERT(x0 < mx);
883 SkASSERT(x1 < mx);
884 }
885}
886
887void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
888 uint32_t bitmapXY[], int count,
889 int x, int y) {
890 SkASSERT(bitmapXY);
891 SkASSERT(count > 0);
892
893 state.fMatrixProc(state, bitmapXY, count, x, y);
894
895 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
896
897 // There are four formats possible:
898 // scale -vs- affine
899 // filter -vs- nofilter
900 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
reed@google.com9cfc83c2013-07-22 17:18:18 +0000901 proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_scale_filter : check_scale_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000902 } else {
reed@google.com9cfc83c2013-07-22 17:18:18 +0000903 proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_affine_filter : check_affine_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000904 }
905 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
906}
907
908SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
909 return DebugMatrixProc;
910}
911
912#endif
913
914///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000915/*
916 The storage requirements for the different matrix procs are as follows,
917 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000918
reed@android.com4c128c42009-08-14 13:54:37 +0000919 scale/translate nofilter Y(4bytes) + N * X
920 affine/perspective nofilter N * (X Y)
921 scale/translate filter Y Y + N * (X X)
922 affine/perspective filter N * (Y Y X X)
923 */
924int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
925 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000926
927 size &= ~3; // only care about 4-byte aligned chunks
928 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
929 size -= 4; // the shared Y (or YY) coordinate
930 if (size < 0) {
931 size = 0;
932 }
reed@android.com258cb222010-04-14 13:36:33 +0000933 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000934 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000935 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000936 }
937
reed@google.com9cfc83c2013-07-22 17:18:18 +0000938 if (fFilterLevel != SkPaint::kNone_FilterLevel) {
reed@android.com258cb222010-04-14 13:36:33 +0000939 size >>= 1;
940 }
941
942 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000943}