blob: 61972886da5a20fb7a0f0e2bf5a90c87cf29a2fc [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkBitmapProcState.h"
9#include "SkColorPriv.h"
10#include "SkFilterProc.h"
11#include "SkPaint.h"
12#include "SkShader.h" // for tilemodes
digit@google.com3ada0ef2012-08-13 14:06:34 +000013#include "SkUtilsArm.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014
digit@google.com3ada0ef2012-08-13 14:06:34 +000015#if !SK_ARM_NEON_IS_NONE
16// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
17extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
18extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
19extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
20extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
21extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
22extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
23extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
24extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
25#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000026
digit@google.com3ada0ef2012-08-13 14:06:34 +000027#define NAME_WRAP(x) x
28#include "SkBitmapProcState_filter.h"
29#include "SkBitmapProcState_procs.h"
reed@android.comb577b412009-10-27 17:49:32 +000030
reed@android.coma44b4cc2009-07-16 02:03:58 +000031///////////////////////////////////////////////////////////////////////////////
32
reed@google.comee056a82013-04-18 15:33:27 +000033// true iff the matrix contains, at most, scale and translate elements
34static bool matrix_only_scale_translate(const SkMatrix& m) {
35 return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask);
36}
37
reed@google.comc0e88e02012-10-17 21:11:56 +000038/**
39 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
40 * go ahead and treat it as if it were, so that subsequent code can go fast.
41 */
42static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
reed@google.comee056a82013-04-18 15:33:27 +000043 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000044
reed@google.comee056a82013-04-18 15:33:27 +000045 if (matrix.getType() & SkMatrix::kScale_Mask) {
46 SkRect src, dst;
47 bitmap.getBounds(&src);
reed@google.comf707adc2013-04-18 15:37:14 +000048
49 // Can't call mapRect(), since that will fix up inverted rectangles,
50 // e.g. when scale is negative, and we don't want to return true for
51 // those.
52 matrix.mapPoints(SkTCast<SkPoint*>(&dst),
53 SkTCast<const SkPoint*>(&src),
54 2);
reed@google.comee056a82013-04-18 15:33:27 +000055
56 // Now round all 4 edges to device space, and then compare the device
57 // width/height to the original. Note: we must map all 4 and subtract
58 // rather than map the "width" and compare, since we care about the
59 // phase (in pixel space) that any translate in the matrix might impart.
60 SkIRect idst;
61 dst.round(&idst);
62 return idst.width() == bitmap.width() && idst.height() == bitmap.height();
reed@google.comc0e88e02012-10-17 21:11:56 +000063 }
64 // if we got here, we're either kTranslate_Mask or identity
65 return true;
66}
67
68static bool just_trans_general(const SkMatrix& matrix) {
reed@google.comee056a82013-04-18 15:33:27 +000069 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000070
reed@google.comee056a82013-04-18 15:33:27 +000071 if (matrix.getType() & SkMatrix::kScale_Mask) {
reed@google.comc0e88e02012-10-17 21:11:56 +000072 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +000073
reed@google.comc0e88e02012-10-17 21:11:56 +000074 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
75 return false;
76 }
77 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
78 return false;
79 }
80 }
81 // if we got here, treat us as either kTranslate_Mask or identity
82 return true;
83}
84
85///////////////////////////////////////////////////////////////////////////////
86
reed@android.com8a1c16f2008-12-17 15:59:43 +000087static bool valid_for_filtering(unsigned dimension) {
88 // for filtering, width and height must fit in 14bits, since we use steal
89 // 2 bits from each to store our 4bit subpixel data
90 return (dimension & ~0x3FFF) == 0;
91}
92
humper@google.comd64d8a92013-07-12 21:14:33 +000093// TODO -- we may want to pass the clip into this function so we only scale
94// the portion of the image that we're going to need. This will complicate
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +000095// the interface to the cache, but might be well worth it.
humper@google.comd64d8a92013-07-12 21:14:33 +000096
97void SkBitmapProcState::possiblyScaleImage() {
98
99 if (fFilterQuality != kHQ_BitmapFilter) {
100 return;
101 }
102
103 // STEP 1: UPSAMPLE?
104
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000105 // Check to see if the transformation matrix is scaling up, and if
106 // the matrix is simple, and if we're doing high quality scaling.
humper@google.comd64d8a92013-07-12 21:14:33 +0000107 // If so, do the bitmap scale here and remove the scaling component from the matrix.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000108
humper@google.comd64d8a92013-07-12 21:14:33 +0000109 if (fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
110 (fInvMatrix.getScaleX() < 1 || fInvMatrix.getScaleY() < 1) &&
111 fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000112
humper@google.comd64d8a92013-07-12 21:14:33 +0000113 // All the criteria are met; let's make a new bitmap.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000114 fScaledBitmap.setConfig(SkBitmap::kARGB_8888_Config,
115 (int)(fOrigBitmap.width() / fInvMatrix.getScaleX()),
humper@google.comd64d8a92013-07-12 21:14:33 +0000116 (int)(fOrigBitmap.height() / fInvMatrix.getScaleY()));
117 fScaledBitmap.allocPixels();
118 fOrigBitmap.scale(&fScaledBitmap);
119 fBitmap = &fScaledBitmap;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000120
humper@google.comd64d8a92013-07-12 21:14:33 +0000121 // set the inv matrix type to translate-only;
122
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000123 fInvMatrix.setTranslate( 1/fInvMatrix.getScaleX() * fInvMatrix.getTranslateX(),
humper@google.comd64d8a92013-07-12 21:14:33 +0000124 1/fInvMatrix.getScaleY() * fInvMatrix.getTranslateY() );
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000125
humper@google.comd64d8a92013-07-12 21:14:33 +0000126 // no need for any further filtering; we just did it!
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000127
humper@google.comd64d8a92013-07-12 21:14:33 +0000128 fFilterQuality = kNone_BitmapFilter;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000129
humper@google.comd64d8a92013-07-12 21:14:33 +0000130 return;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000131 }
132
133 if (!fOrigBitmap.hasMipMap()) {
134
humper@google.comd64d8a92013-07-12 21:14:33 +0000135 // STEP 2: DOWNSAMPLE
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000136
humper@google.comd64d8a92013-07-12 21:14:33 +0000137 // Check to see if the transformation matrix is scaling *down*.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000138 // If so, automatically build mipmaps.
139
humper@google.comd64d8a92013-07-12 21:14:33 +0000140 SkPoint v1, v2;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000141
142 // conservatively estimate if the matrix is scaling down by seeing
humper@google.comd64d8a92013-07-12 21:14:33 +0000143 // what its upper left 2x2 portion does to two unit vectors.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000144
humper@google.comd64d8a92013-07-12 21:14:33 +0000145 v1.fX = fInvMatrix.getScaleX();
146 v1.fY = fInvMatrix.getSkewY();
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000147
humper@google.comd64d8a92013-07-12 21:14:33 +0000148 v2.fX = fInvMatrix.getSkewX();
149 v2.fY = fInvMatrix.getScaleY();
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000150
humper@google.comd64d8a92013-07-12 21:14:33 +0000151 if (v1.fX * v1.fX + v1.fY * v1.fY > 1 ||
152 v2.fX * v2.fX + v2.fY * v2.fY > 1) {
153 fOrigBitmap.buildMipMap();
154
155 // Now that we've built the mipmaps and we know we're downsampling,
156 // downgrade to bilinear interpolation for the mip level.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000157
humper@google.comd64d8a92013-07-12 21:14:33 +0000158 fFilterQuality = kBilerp_BitmapFilter;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000159 }
160 }
161
humper@google.comd64d8a92013-07-12 21:14:33 +0000162 if (fOrigBitmap.hasMipMap()) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000163
humper@google.comd64d8a92013-07-12 21:14:33 +0000164 // STEP 3: We've got mipmaps, let's choose the closest level as our render
165 // source and adjust the matrix accordingly.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000166
humper@google.comd64d8a92013-07-12 21:14:33 +0000167 int shift = fOrigBitmap.extractMipLevel(&fScaledBitmap,
168 SkScalarToFixed(fInvMatrix.getScaleX()),
169 SkScalarToFixed(fInvMatrix.getSkewY()));
170
171 if (shift > 0) {
172 SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
173 fInvMatrix.postScale(scale, scale);
174 fBitmap = &fScaledBitmap;
175 }
176 }
177}
178
179void SkBitmapProcState::endContext() {
180 SkDELETE(fBitmapFilter);
181 fBitmapFilter = NULL;
182 fScaledBitmap.reset();
183}
184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
186 if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
187 return false;
188 }
reed@android.coma44b4cc2009-07-16 02:03:58 +0000189
humper@google.comd64d8a92013-07-12 21:14:33 +0000190 bool trivialMatrix = (inv.getType() & ~SkMatrix::kTranslate_Mask) == 0;
191 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000192 SkShader::kClamp_TileMode == fTileModeY;
reed@android.com07d1f002009-08-13 19:35:48 +0000193
humper@google.comd64d8a92013-07-12 21:14:33 +0000194 fInvMatrix = inv;
195 if (!(clampClamp || trivialMatrix)) {
196 fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 }
reed@android.com07d1f002009-08-13 19:35:48 +0000198
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 fBitmap = &fOrigBitmap;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000200
humper@google.comd64d8a92013-07-12 21:14:33 +0000201 // initialize our filter quality to the one requested by the caller.
202 // We may downgrade it later if we determine that we either don't need
203 // or can't provide as high a quality filtering as the user requested.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000204
humper@google.comd64d8a92013-07-12 21:14:33 +0000205 fFilterQuality = kNone_BitmapFilter;
206 if (paint.isFilterBitmap()) {
207 if (paint.getFlags() & SkPaint::kHighQualityFilterBitmap_Flag) {
208 fFilterQuality = kHQ_BitmapFilter;
209 } else {
210 fFilterQuality = kBilerp_BitmapFilter;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 }
212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213
humper@google.comd64d8a92013-07-12 21:14:33 +0000214#ifndef SK_IGNORE_IMAGE_PRESCALE
215 // possiblyScaleImage will look to see if it can rescale the image as a
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000216 // preprocess; either by scaling up to the target size, or by selecting
humper@google.comd64d8a92013-07-12 21:14:33 +0000217 // a nearby mipmap level. If it does, it will adjust the working
218 // matrix as well as the working bitmap. It may also adjust the filter
219 // quality to avoid re-filtering an already perfectly scaled image.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000220
humper@google.comd64d8a92013-07-12 21:14:33 +0000221 this->possiblyScaleImage();
222#endif
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000223
humper@google.comd64d8a92013-07-12 21:14:33 +0000224 // Now that all possible changes to the matrix have taken place, check
225 // to see if we're really close to a no-scale matrix. If so, explicitly
226 // set it to be so. Subsequent code may inspect this matrix to choose
227 // a faster path in this case.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000228
humper@google.comd64d8a92013-07-12 21:14:33 +0000229 // This code will only execute if the matrix has some scale component;
230 // if it's already pure translate then we won't do this inversion.
231
232 if (matrix_only_scale_translate(fInvMatrix)) {
reed@google.comee056a82013-04-18 15:33:27 +0000233 SkMatrix forward;
humper@google.comd64d8a92013-07-12 21:14:33 +0000234 if (fInvMatrix.invert(&forward)) {
235 if (clampClamp ? just_trans_clamp(forward, *fBitmap)
reed@google.comee056a82013-04-18 15:33:27 +0000236 : just_trans_general(forward)) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000237 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
238 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
humper@google.comd64d8a92013-07-12 21:14:33 +0000239 fInvMatrix.setTranslate(tx, ty);
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000240
reed@google.comce1f3cc2013-01-05 14:37:48 +0000241 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000242 }
243 }
244
humper@google.comd64d8a92013-07-12 21:14:33 +0000245 fInvProc = fInvMatrix.getMapXYProc();
246 fInvType = fInvMatrix.getType();
247 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
248 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
249 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
250 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251
252 fAlphaScale = SkAlpha255To256(paint.getAlpha());
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000253
reed@android.com7a99eb12009-07-16 01:13:14 +0000254 fShaderProc32 = NULL;
255 fShaderProc16 = NULL;
256 fSampleProc32 = NULL;
257 fSampleProc16 = NULL;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000258
humper@google.comd64d8a92013-07-12 21:14:33 +0000259 // recompute the triviality of the matrix here because we may have
260 // changed it!
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000261
humper@google.comd64d8a92013-07-12 21:14:33 +0000262 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
reed@android.com48534f92009-07-16 20:53:26 +0000263
humper@google.comd64d8a92013-07-12 21:14:33 +0000264 if (kHQ_BitmapFilter == fFilterQuality) {
265 // If this is still set, that means we wanted HQ sampling
266 // but couldn't do it as a preprocess. Let's try to install
267 // the scanline version of the HQ sampler. If that process fails,
268 // downgrade to bilerp.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000269
humper@google.comd64d8a92013-07-12 21:14:33 +0000270 // NOTE: Might need to be careful here in the future when we want
271 // to have the platform proc have a shot at this; it's possible that
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000272 // the chooseBitmapFilterProc will fail to install a shader but a
273 // platform-specific one might succeed, so it might be premature here
humper@google.comd64d8a92013-07-12 21:14:33 +0000274 // to fall back to bilerp. This needs thought.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000275
humper@google.comd64d8a92013-07-12 21:14:33 +0000276 SkASSERT(fInvType > SkMatrix::kTranslate_Mask);
277
278 fShaderProc32 = this->chooseBitmapFilterProc();
279 if (!fShaderProc32) {
280 fFilterQuality = kBilerp_BitmapFilter;
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000281 }
humper@google.comd64d8a92013-07-12 21:14:33 +0000282 }
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000283
humper@google.comd64d8a92013-07-12 21:14:33 +0000284 if (kBilerp_BitmapFilter == fFilterQuality) {
285 // Only try bilerp if the matrix is "interesting" and
286 // the image has a suitable size.
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000287
humper@google.comd64d8a92013-07-12 21:14:33 +0000288 if (fInvType < SkMatrix::kTranslate_Mask ||
289 !valid_for_filtering(fBitmap->width() | fBitmap->height())) {
290 fFilterQuality = kNone_BitmapFilter;
291 }
292 }
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000293
humper@google.comd64d8a92013-07-12 21:14:33 +0000294 // At this point, we know exactly what kind of sampling the per-scanline
295 // shader will perform.
296
297 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 if (NULL == fMatrixProc) {
299 return false;
300 }
301
302 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000303
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000304 // No need to do this if we're doing HQ sampling; if filter quality is
305 // still set to HQ by the time we get here, then we must have installed
humper@google.comd64d8a92013-07-12 21:14:33 +0000306 // the shader proc above and can skip all this.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307
humper@google.comd64d8a92013-07-12 21:14:33 +0000308 if (fFilterQuality < kHQ_BitmapFilter) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000309
humper@google.comd64d8a92013-07-12 21:14:33 +0000310 int index = 0;
311 if (fAlphaScale < 256) { // note: this distinction is not used for D16
312 index |= 1;
reed@android.comaa9152a2009-07-17 21:24:56 +0000313 }
humper@google.comd64d8a92013-07-12 21:14:33 +0000314 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
315 index |= 2;
316 }
317 if (fFilterQuality != kNone_BitmapFilter) {
318 index |= 4;
319 }
320 // bits 3,4,5 encoding the source bitmap format
321 switch (fBitmap->config()) {
322 case SkBitmap::kARGB_8888_Config:
323 index |= 0;
324 break;
325 case SkBitmap::kRGB_565_Config:
326 index |= 8;
327 break;
328 case SkBitmap::kIndex8_Config:
329 index |= 16;
330 break;
331 case SkBitmap::kARGB_4444_Config:
332 index |= 24;
333 break;
334 case SkBitmap::kA8_Config:
335 index |= 32;
336 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
337 break;
338 default:
339 return false;
340 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000341
humper@google.comd64d8a92013-07-12 21:14:33 +0000342 #if !SK_ARM_NEON_IS_ALWAYS
343 static const SampleProc32 gSkBitmapProcStateSample32[] = {
344 S32_opaque_D32_nofilter_DXDY,
345 S32_alpha_D32_nofilter_DXDY,
346 S32_opaque_D32_nofilter_DX,
347 S32_alpha_D32_nofilter_DX,
348 S32_opaque_D32_filter_DXDY,
349 S32_alpha_D32_filter_DXDY,
350 S32_opaque_D32_filter_DX,
351 S32_alpha_D32_filter_DX,
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +0000352
humper@google.comd64d8a92013-07-12 21:14:33 +0000353 S16_opaque_D32_nofilter_DXDY,
354 S16_alpha_D32_nofilter_DXDY,
355 S16_opaque_D32_nofilter_DX,
356 S16_alpha_D32_nofilter_DX,
357 S16_opaque_D32_filter_DXDY,
358 S16_alpha_D32_filter_DXDY,
359 S16_opaque_D32_filter_DX,
360 S16_alpha_D32_filter_DX,
361
362 SI8_opaque_D32_nofilter_DXDY,
363 SI8_alpha_D32_nofilter_DXDY,
364 SI8_opaque_D32_nofilter_DX,
365 SI8_alpha_D32_nofilter_DX,
366 SI8_opaque_D32_filter_DXDY,
367 SI8_alpha_D32_filter_DXDY,
368 SI8_opaque_D32_filter_DX,
369 SI8_alpha_D32_filter_DX,
370
371 S4444_opaque_D32_nofilter_DXDY,
372 S4444_alpha_D32_nofilter_DXDY,
373 S4444_opaque_D32_nofilter_DX,
374 S4444_alpha_D32_nofilter_DX,
375 S4444_opaque_D32_filter_DXDY,
376 S4444_alpha_D32_filter_DXDY,
377 S4444_opaque_D32_filter_DX,
378 S4444_alpha_D32_filter_DX,
379
380 // A8 treats alpha/opaque the same (equally efficient)
381 SA8_alpha_D32_nofilter_DXDY,
382 SA8_alpha_D32_nofilter_DXDY,
383 SA8_alpha_D32_nofilter_DX,
384 SA8_alpha_D32_nofilter_DX,
385 SA8_alpha_D32_filter_DXDY,
386 SA8_alpha_D32_filter_DXDY,
387 SA8_alpha_D32_filter_DX,
388 SA8_alpha_D32_filter_DX
389 };
390
391 static const SampleProc16 gSkBitmapProcStateSample16[] = {
392 S32_D16_nofilter_DXDY,
393 S32_D16_nofilter_DX,
394 S32_D16_filter_DXDY,
395 S32_D16_filter_DX,
396
397 S16_D16_nofilter_DXDY,
398 S16_D16_nofilter_DX,
399 S16_D16_filter_DXDY,
400 S16_D16_filter_DX,
401
402 SI8_D16_nofilter_DXDY,
403 SI8_D16_nofilter_DX,
404 SI8_D16_filter_DXDY,
405 SI8_D16_filter_DX,
406
407 // Don't support 4444 -> 565
408 NULL, NULL, NULL, NULL,
409 // Don't support A8 -> 565
410 NULL, NULL, NULL, NULL
411 };
412 #endif
413
414 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
415 index >>= 1; // shift away any opaque/alpha distinction
416 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
417
418 // our special-case shaderprocs
419 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
420 if (clampClamp) {
421 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
422 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
423 SkShader::kRepeat_TileMode == fTileModeY) {
424 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
425 }
426 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
427 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
428 }
429
430 if (NULL == fShaderProc32) {
431 fShaderProc32 = this->chooseShaderProc32();
432 }
humper@google.comb0889472013-07-09 21:37:14 +0000433 }
434
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000435 // see if our platform has any accelerated overrides
436 this->platformProcs();
reed@google.com4c69a062013-05-23 20:11:56 +0000437
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 return true;
439}
440
reed@google.com9a4c7462012-10-12 18:21:37 +0000441static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
442 int x, int y,
443 SkPMColor* SK_RESTRICT colors,
444 int count) {
445 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
446 SkASSERT(s.fInvKy == 0);
447 SkASSERT(count > 0 && colors != NULL);
humper@google.comd64d8a92013-07-12 21:14:33 +0000448 SkASSERT(SkBitmapProcState::kNone_BitmapFilter == s.fFilterQuality);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000449
reed@google.com9a4c7462012-10-12 18:21:37 +0000450 const int maxX = s.fBitmap->width() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000451 const int maxY = s.fBitmap->height() - 1;
452 int ix = s.fFilterOneX + x;
453 int iy = SkClampMax(s.fFilterOneY + y, maxY);
454#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000455 {
456 SkPoint pt;
humper@google.comd64d8a92013-07-12 21:14:33 +0000457 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com9a4c7462012-10-12 18:21:37 +0000458 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000459 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
460 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000461
reed@google.comf7698de2012-10-12 20:50:24 +0000462 SkASSERT(iy == iy2);
463 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000464 }
reed@google.comf7698de2012-10-12 20:50:24 +0000465#endif
466 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000467
reed@google.com9a4c7462012-10-12 18:21:37 +0000468 // clamp to the left
469 if (ix < 0) {
470 int n = SkMin32(-ix, count);
471 sk_memset32(colors, row[0], n);
472 count -= n;
473 if (0 == count) {
474 return;
475 }
476 colors += n;
477 SkASSERT(-ix == n);
478 ix = 0;
479 }
480 // copy the middle
481 if (ix <= maxX) {
482 int n = SkMin32(maxX - ix + 1, count);
483 memcpy(colors, row + ix, n * sizeof(SkPMColor));
484 count -= n;
485 if (0 == count) {
486 return;
487 }
488 colors += n;
489 }
490 SkASSERT(count > 0);
491 // clamp to the right
492 sk_memset32(colors, row[maxX], count);
493}
494
reed@google.coma8d99302012-10-16 20:23:25 +0000495static inline int sk_int_mod(int x, int n) {
496 SkASSERT(n > 0);
497 if ((unsigned)x >= (unsigned)n) {
498 if (x < 0) {
499 x = n + ~(~x % n);
500 } else {
501 x = x % n;
502 }
503 }
504 return x;
505}
506
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000507static inline int sk_int_mirror(int x, int n) {
508 x = sk_int_mod(x, 2 * n);
509 if (x >= n) {
510 x = n + ~(x - n);
511 }
512 return x;
513}
514
reed@google.coma8d99302012-10-16 20:23:25 +0000515static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
516 int x, int y,
517 SkPMColor* SK_RESTRICT colors,
518 int count) {
519 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
520 SkASSERT(s.fInvKy == 0);
521 SkASSERT(count > 0 && colors != NULL);
humper@google.comd64d8a92013-07-12 21:14:33 +0000522 SkASSERT(SkBitmapProcState::kNone_BitmapFilter == s.fFilterQuality);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000523
reed@google.coma8d99302012-10-16 20:23:25 +0000524 const int stopX = s.fBitmap->width();
525 const int stopY = s.fBitmap->height();
526 int ix = s.fFilterOneX + x;
527 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
528#ifdef SK_DEBUG
529 {
530 SkPoint pt;
humper@google.comd64d8a92013-07-12 21:14:33 +0000531 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.coma8d99302012-10-16 20:23:25 +0000532 SkIntToScalar(y) + SK_ScalarHalf, &pt);
533 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
534 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000535
reed@google.coma8d99302012-10-16 20:23:25 +0000536 SkASSERT(iy == iy2);
537 SkASSERT(ix == ix2);
538 }
539#endif
540 const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
541
542 ix = sk_int_mod(ix, stopX);
543 for (;;) {
544 int n = SkMin32(stopX - ix, count);
545 memcpy(colors, row + ix, n * sizeof(SkPMColor));
546 count -= n;
547 if (0 == count) {
548 return;
549 }
550 colors += n;
551 ix = 0;
552 }
553}
554
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000555static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
556 int x, int y,
557 SkPMColor* SK_RESTRICT colors,
558 int count) {
559 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
560 SkASSERT(s.fInvKy == 0);
561 SkASSERT(count > 0 && colors != NULL);
562 SkASSERT(1 == s.fBitmap->width());
563
scroggo@google.comad511322013-02-22 15:50:37 +0000564 int iY0;
565 int iY1 SK_INIT_TO_AVOID_WARNING;
566 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000567
humper@google.comd64d8a92013-07-12 21:14:33 +0000568 if (s.fFilterQuality != SkBitmapProcState::kNone_BitmapFilter) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000569 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
570 uint32_t xy[2];
571
572 mproc(s, xy, 1, x, y);
573
574 iY0 = xy[0] >> 18;
575 iY1 = xy[0] & 0x3FFF;
576 iSubY = (xy[0] >> 14) & 0xF;
577 } else {
578 int yTemp;
579
580 if (s.fInvType > SkMatrix::kTranslate_Mask) {
581 SkPoint pt;
humper@google.comd64d8a92013-07-12 21:14:33 +0000582 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000583 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000584 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000585 &pt);
robertphillips@google.com1e305232013-01-22 20:29:16 +0000586 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000587 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000588 // bitmap's width and height. Since this method is going to do
589 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000590 if (SkShader::kClamp_TileMode != s.fTileModeX ||
591 SkShader::kClamp_TileMode != s.fTileModeY) {
592 yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
593 } else {
594 yTemp = SkScalarFloorToInt(pt.fY);
595 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000596 } else {
597 yTemp = s.fFilterOneY + y;
598 }
599
600 const int stopY = s.fBitmap->height();
601 switch (s.fTileModeY) {
602 case SkShader::kClamp_TileMode:
603 iY0 = SkClampMax(yTemp, stopY-1);
604 break;
605 case SkShader::kRepeat_TileMode:
606 iY0 = sk_int_mod(yTemp, stopY);
607 break;
608 case SkShader::kMirror_TileMode:
609 default:
610 iY0 = sk_int_mirror(yTemp, stopY);
611 break;
612 }
613
614#ifdef SK_DEBUG
615 {
616 SkPoint pt;
humper@google.comd64d8a92013-07-12 21:14:33 +0000617 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000618 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000619 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000620 &pt);
robertphillips@google.comd5077752013-01-23 00:36:02 +0000621 if (s.fInvType > SkMatrix::kTranslate_Mask &&
622 (SkShader::kClamp_TileMode != s.fTileModeX ||
623 SkShader::kClamp_TileMode != s.fTileModeY)) {
robertphillips@google.com1e305232013-01-22 20:29:16 +0000624 pt.fY *= s.fBitmap->height();
625 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000626 int iY2;
627
628 switch (s.fTileModeY) {
629 case SkShader::kClamp_TileMode:
630 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
631 break;
632 case SkShader::kRepeat_TileMode:
633 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
634 break;
635 case SkShader::kMirror_TileMode:
636 default:
637 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
638 break;
639 }
640
641 SkASSERT(iY0 == iY2);
642 }
643#endif
644 }
645
646 const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
647 SkPMColor color;
648
humper@google.comd64d8a92013-07-12 21:14:33 +0000649 if (s.fFilterQuality != SkBitmapProcState::kNone_BitmapFilter) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000650 const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
651
652 if (s.fAlphaScale < 256) {
653 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
654 } else {
655 Filter_32_opaque(iSubY, *row0, *row1, &color);
656 }
657 } else {
658 if (s.fAlphaScale < 256) {
659 color = SkAlphaMulQ(*row0, s.fAlphaScale);
660 } else {
661 color = *row0;
662 }
663 }
664
665 sk_memset32(colors, color, count);
666}
667
reed@google.com6bb92bc2012-11-20 19:45:16 +0000668static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
669 SkPMColor* SK_RESTRICT colors, int count) {
670 // if we get called, the matrix is too tricky, so we just draw nothing
671 sk_memset32(colors, 0, count);
672}
673
674bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000675 SkPoint pt;
humper@google.comd64d8a92013-07-12 21:14:33 +0000676 fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000677
678 /*
679 * if the translate is larger than our ints, we can get random results, or
680 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
681 * negate it.
682 */
683 const SkScalar too_big = SkIntToScalar(1 << 30);
684 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
685 return false;
686 }
687
reed@google.coma8d99302012-10-16 20:23:25 +0000688 // Since we know we're not filtered, we re-purpose these fields allow
689 // us to go from device -> src coordinates w/ just an integer add,
690 // rather than running through the inverse-matrix
691 fFilterOneX = SkScalarFloorToInt(pt.fX);
692 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000693 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000694}
695
reed@google.com9a4c7462012-10-12 18:21:37 +0000696SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000697
698 if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
699 return NULL;
700 }
701
702 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
703
704 if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000705 if (kNone_BitmapFilter == fFilterQuality &&
706 fInvType <= SkMatrix::kTranslate_Mask &&
humper@google.comd64d8a92013-07-12 21:14:33 +0000707 !this->setupForTranslate()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000708 return DoNothing_shaderproc;
709 }
710 return S32_D32_constX_shaderproc;
711 }
712
reed@google.com9a4c7462012-10-12 18:21:37 +0000713 if (fAlphaScale < 256) {
714 return NULL;
715 }
716 if (fInvType > SkMatrix::kTranslate_Mask) {
717 return NULL;
718 }
humper@google.comd64d8a92013-07-12 21:14:33 +0000719 if (fFilterQuality != kNone_BitmapFilter) {
reed@google.com9a4c7462012-10-12 18:21:37 +0000720 return NULL;
721 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000722
reed@google.coma8d99302012-10-16 20:23:25 +0000723 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
724 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
725
726 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000727 if (this->setupForTranslate()) {
728 return Clamp_S32_D32_nofilter_trans_shaderproc;
729 }
730 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000731 }
reed@google.coma8d99302012-10-16 20:23:25 +0000732 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000733 if (this->setupForTranslate()) {
734 return Repeat_S32_D32_nofilter_trans_shaderproc;
735 }
736 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000737 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000738 return NULL;
739}
740
reed@android.com4c128c42009-08-14 13:54:37 +0000741///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000742
743#ifdef SK_DEBUG
744
745static void check_scale_nofilter(uint32_t bitmapXY[], int count,
746 unsigned mx, unsigned my) {
747 unsigned y = *bitmapXY++;
748 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000749
reed@google.com9fe287b2012-03-27 15:54:28 +0000750 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
751 for (int i = 0; i < count; ++i) {
752 SkASSERT(xptr[i] < mx);
753 }
754}
755
756static void check_scale_filter(uint32_t bitmapXY[], int count,
757 unsigned mx, unsigned my) {
758 uint32_t YY = *bitmapXY++;
759 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000760 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000761 SkASSERT(y0 < my);
762 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000763
reed@google.com9fe287b2012-03-27 15:54:28 +0000764 for (int i = 0; i < count; ++i) {
765 uint32_t XX = bitmapXY[i];
766 unsigned x0 = XX >> 18;
767 unsigned x1 = XX & 0x3FFF;
768 SkASSERT(x0 < mx);
769 SkASSERT(x1 < mx);
770 }
771}
772
773static void check_affine_nofilter(uint32_t bitmapXY[], int count,
774 unsigned mx, unsigned my) {
775 for (int i = 0; i < count; ++i) {
776 uint32_t XY = bitmapXY[i];
777 unsigned x = XY & 0xFFFF;
778 unsigned y = XY >> 16;
779 SkASSERT(x < mx);
780 SkASSERT(y < my);
781 }
782}
783
784static void check_affine_filter(uint32_t bitmapXY[], int count,
785 unsigned mx, unsigned my) {
786 for (int i = 0; i < count; ++i) {
787 uint32_t YY = *bitmapXY++;
788 unsigned y0 = YY >> 18;
789 unsigned y1 = YY & 0x3FFF;
790 SkASSERT(y0 < my);
791 SkASSERT(y1 < my);
792
793 uint32_t XX = *bitmapXY++;
794 unsigned x0 = XX >> 18;
795 unsigned x1 = XX & 0x3FFF;
796 SkASSERT(x0 < mx);
797 SkASSERT(x1 < mx);
798 }
799}
800
801void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
802 uint32_t bitmapXY[], int count,
803 int x, int y) {
804 SkASSERT(bitmapXY);
805 SkASSERT(count > 0);
806
807 state.fMatrixProc(state, bitmapXY, count, x, y);
808
809 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
810
811 // There are four formats possible:
812 // scale -vs- affine
813 // filter -vs- nofilter
814 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
humper@google.comd64d8a92013-07-12 21:14:33 +0000815 proc = state.fFilterQuality != kNone_BitmapFilter ? check_scale_filter : check_scale_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000816 } else {
humper@google.comd64d8a92013-07-12 21:14:33 +0000817 proc = state.fFilterQuality != kNone_BitmapFilter ? check_affine_filter : check_affine_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000818 }
819 proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
820}
821
822SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
823 return DebugMatrixProc;
824}
825
826#endif
827
828///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000829/*
830 The storage requirements for the different matrix procs are as follows,
831 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000832
reed@android.com4c128c42009-08-14 13:54:37 +0000833 scale/translate nofilter Y(4bytes) + N * X
834 affine/perspective nofilter N * (X Y)
835 scale/translate filter Y Y + N * (X X)
836 affine/perspective filter N * (Y Y X X)
837 */
838int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
839 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000840
841 size &= ~3; // only care about 4-byte aligned chunks
842 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
843 size -= 4; // the shared Y (or YY) coordinate
844 if (size < 0) {
845 size = 0;
846 }
reed@android.com258cb222010-04-14 13:36:33 +0000847 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000848 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000849 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000850 }
851
humper@google.comd64d8a92013-07-12 21:14:33 +0000852 if (fFilterQuality != kNone_BitmapFilter) {
reed@android.com258cb222010-04-14 13:36:33 +0000853 size >>= 1;
854 }
855
856 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000857}