blob: 7b88ab644a9ecca1d5e694f0d979bc70d246d860 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
reed04617132014-08-21 09:46:49 -07007
8#include "SkBitmapCache.h"
reed64045422015-06-04 06:31:31 -07009#include "SkBitmapController.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkBitmapProcState.h"
11#include "SkColorPriv.h"
12#include "SkFilterProc.h"
13#include "SkPaint.h"
14#include "SkShader.h" // for tilemodes
digit@google.com3ada0ef2012-08-13 14:06:34 +000015#include "SkUtilsArm.h"
humper@google.com138ebc32013-07-19 20:20:04 +000016#include "SkBitmapScaler.h"
reed@google.comd94697c2013-07-24 14:31:33 +000017#include "SkMipMap.h"
reed@google.comcee9dcb2013-09-13 16:04:49 +000018#include "SkPixelRef.h"
commit-bot@chromium.orgf4491562014-05-28 17:30:02 +000019#include "SkImageEncoder.h"
reed011f39a2014-08-28 13:35:23 -070020#include "SkResourceCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021
mtklein809ccf32016-05-05 10:58:39 -070022#if defined(SK_ARM_HAS_NEON)
digit@google.com3ada0ef2012-08-13 14:06:34 +000023// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
digit@google.com3ada0ef2012-08-13 14:06:34 +000024extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
25extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
herbc7a784c2015-12-18 09:52:15 -080026extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const void *, int, int, uint16_t*, int);
27extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const void *, int, int, uint16_t*, int);
digit@google.com3ada0ef2012-08-13 14:06:34 +000028extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
herbc7a784c2015-12-18 09:52:15 -080029extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const void *, int, int, uint32_t*, int);
30extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const void*, int, int, uint32_t*, int);
digit@google.com3ada0ef2012-08-13 14:06:34 +000031#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000032
herbc7a784c2015-12-18 09:52:15 -080033extern void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void*, int, int, uint32_t*, int);
mtklein14e4d392014-10-23 14:35:01 -070034
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
reed05a56472016-03-02 09:49:02 -080039SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmapProvider& provider,
reed6644d932016-06-10 11:41:47 -070040 SkShader::TileMode tmx, SkShader::TileMode tmy,
Brian Osman7b8400d2016-11-08 17:08:54 -050041 SkDestinationSurfaceColorMode colorMode)
reed7a4d8472015-09-15 13:33:58 -070042 : fProvider(provider)
reed05a56472016-03-02 09:49:02 -080043 , fTileModeX(tmx)
44 , fTileModeY(tmy)
Brian Osman7b8400d2016-11-08 17:08:54 -050045 , fColorMode(colorMode)
reed7a4d8472015-09-15 13:33:58 -070046 , fBMState(nullptr)
reed05a56472016-03-02 09:49:02 -080047{}
reed7a4d8472015-09-15 13:33:58 -070048
reed05a56472016-03-02 09:49:02 -080049SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmap& bm,
reed6644d932016-06-10 11:41:47 -070050 SkShader::TileMode tmx, SkShader::TileMode tmy,
Brian Osman7b8400d2016-11-08 17:08:54 -050051 SkDestinationSurfaceColorMode colorMode)
reed7a4d8472015-09-15 13:33:58 -070052 : fProvider(SkBitmapProvider(bm))
reed05a56472016-03-02 09:49:02 -080053 , fTileModeX(tmx)
54 , fTileModeY(tmy)
Brian Osman7b8400d2016-11-08 17:08:54 -050055 ,fColorMode(colorMode)
reed7a4d8472015-09-15 13:33:58 -070056 , fBMState(nullptr)
reed05a56472016-03-02 09:49:02 -080057{}
reed64045422015-06-04 06:31:31 -070058
reed05a56472016-03-02 09:49:02 -080059SkBitmapProcInfo::~SkBitmapProcInfo() {
reed64045422015-06-04 06:31:31 -070060 SkInPlaceDeleteCheck(fBMState, fBMStateStorage.get());
61}
62
reed@android.coma44b4cc2009-07-16 02:03:58 +000063///////////////////////////////////////////////////////////////////////////////
64
fmalita544da7a2016-11-03 14:43:53 -070065// true iff the matrix has a scale and no more than an optional translate.
reed@google.comee056a82013-04-18 15:33:27 +000066static bool matrix_only_scale_translate(const SkMatrix& m) {
fmalita544da7a2016-11-03 14:43:53 -070067 return (m.getType() & ~SkMatrix::kTranslate_Mask) == SkMatrix::kScale_Mask;
reed@google.comee056a82013-04-18 15:33:27 +000068}
69
reed@google.comc0e88e02012-10-17 21:11:56 +000070/**
71 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
72 * go ahead and treat it as if it were, so that subsequent code can go fast.
73 */
reedad7ae6c2015-06-04 14:12:25 -070074static bool just_trans_clamp(const SkMatrix& matrix, const SkPixmap& pixmap) {
reed@google.comee056a82013-04-18 15:33:27 +000075 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000076
fmalita544da7a2016-11-03 14:43:53 -070077 SkRect dst;
78 SkRect src = SkRect::Make(pixmap.bounds());
reed@google.comf707adc2013-04-18 15:37:14 +000079
fmalita544da7a2016-11-03 14:43:53 -070080 // Can't call mapRect(), since that will fix up inverted rectangles,
81 // e.g. when scale is negative, and we don't want to return true for
82 // those.
83 matrix.mapPoints(SkTCast<SkPoint*>(&dst),
84 SkTCast<const SkPoint*>(&src),
85 2);
reed@google.comee056a82013-04-18 15:33:27 +000086
fmalita544da7a2016-11-03 14:43:53 -070087 // Now round all 4 edges to device space, and then compare the device
88 // width/height to the original. Note: we must map all 4 and subtract
89 // rather than map the "width" and compare, since we care about the
90 // phase (in pixel space) that any translate in the matrix might impart.
91 SkIRect idst;
92 dst.round(&idst);
93 return idst.width() == pixmap.width() && idst.height() == pixmap.height();
reed@google.comc0e88e02012-10-17 21:11:56 +000094}
95
96static bool just_trans_general(const SkMatrix& matrix) {
reed@google.comee056a82013-04-18 15:33:27 +000097 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000098
fmalita544da7a2016-11-03 14:43:53 -070099 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000100
fmalita544da7a2016-11-03 14:43:53 -0700101 return SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)
102 && SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol);
reed@google.comc0e88e02012-10-17 21:11:56 +0000103}
104
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105static bool valid_for_filtering(unsigned dimension) {
106 // for filtering, width and height must fit in 14bits, since we use steal
107 // 2 bits from each to store our 4bit subpixel data
108 return (dimension & ~0x3FFF) == 0;
109}
110
reed05a56472016-03-02 09:49:02 -0800111bool SkBitmapProcInfo::init(const SkMatrix& inv, const SkPaint& paint) {
reed2252eda2016-01-20 11:04:40 -0800112 const int origW = fProvider.info().width();
113 const int origH = fProvider.info().height();
reed05a56472016-03-02 09:49:02 -0800114
115 fPixmap.reset();
116 fInvMatrix = inv;
117 fFilterQuality = paint.getFilterQuality();
118
reed2252eda2016-01-20 11:04:40 -0800119 bool allow_ignore_fractional_translate = true; // historical default
reed05a56472016-03-02 09:49:02 -0800120 if (kMedium_SkFilterQuality == fFilterQuality) {
reed2252eda2016-01-20 11:04:40 -0800121 allow_ignore_fractional_translate = false;
122 }
reed2252eda2016-01-20 11:04:40 -0800123
Brian Osman7b8400d2016-11-08 17:08:54 -0500124 SkDefaultBitmapController controller(fColorMode);
reed7a4d8472015-09-15 13:33:58 -0700125 fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(),
reed64045422015-06-04 06:31:31 -0700126 fBMStateStorage.get(), fBMStateStorage.size());
reedad7ae6c2015-06-04 14:12:25 -0700127 // Note : we allow the controller to return an empty (zero-dimension) result. Should we?
halcanary96fcdcc2015-08-27 07:41:13 -0700128 if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) {
reed64045422015-06-04 06:31:31 -0700129 return false;
reedf7094c42015-01-16 12:05:19 -0800130 }
reedad7ae6c2015-06-04 14:12:25 -0700131 fPixmap = fBMState->pixmap();
reed64045422015-06-04 06:31:31 -0700132 fInvMatrix = fBMState->invMatrix();
herb6eff52a2016-03-23 09:00:33 -0700133 fRealInvMatrix = fBMState->invMatrix();
reed05a56472016-03-02 09:49:02 -0800134 fPaintColor = paint.getColor();
135 fFilterQuality = fBMState->quality();
reedad7ae6c2015-06-04 14:12:25 -0700136 SkASSERT(fPixmap.addr());
reed05a56472016-03-02 09:49:02 -0800137
reed@google.comef0e3192013-09-09 13:42:39 +0000138 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
139 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
140 SkShader::kClamp_TileMode == fTileModeY;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000141
humper535e3b22014-10-27 10:32:06 -0700142 // Most of the scanline procs deal with "unit" texture coordinates, as this
143 // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate
144 // those, we divide the matrix by its dimensions here.
145 //
146 // We don't do this if we're either trivial (can ignore the matrix) or clamping
147 // in both X and Y since clamping to width,height is just as easy as to 0xFFFF.
148
fmalitab1c7f882016-11-03 11:42:49 -0700149 // Note that we cannot ignore the matrix when allow_ignore_fractional_translate is false.
150
151 if (!(clampClamp || (trivialMatrix && allow_ignore_fractional_translate))) {
reedad7ae6c2015-06-04 14:12:25 -0700152 fInvMatrix.postIDiv(fPixmap.width(), fPixmap.height());
reed@google.comef0e3192013-09-09 13:42:39 +0000153 }
154
humper@google.com9c96d4b2013-07-14 01:44:59 +0000155 // Now that all possible changes to the matrix have taken place, check
156 // to see if we're really close to a no-scale matrix. If so, explicitly
157 // set it to be so. Subsequent code may inspect this matrix to choose
158 // a faster path in this case.
159
160 // This code will only execute if the matrix has some scale component;
161 // if it's already pure translate then we won't do this inversion.
162
163 if (matrix_only_scale_translate(fInvMatrix)) {
reed@google.comee056a82013-04-18 15:33:27 +0000164 SkMatrix forward;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000165 if (fInvMatrix.invert(&forward)) {
reed2252eda2016-01-20 11:04:40 -0800166 if ((clampClamp && allow_ignore_fractional_translate)
reed05a56472016-03-02 09:49:02 -0800167 ? just_trans_clamp(forward, fPixmap)
168 : just_trans_general(forward)) {
reedf2e2ad02016-01-19 13:33:32 -0800169 fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY());
reed@google.comce1f3cc2013-01-05 14:37:48 +0000170 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000171 }
172 }
173
reed05a56472016-03-02 09:49:02 -0800174 fInvType = fInvMatrix.getType();
humper@google.com9c96d4b2013-07-14 01:44:59 +0000175
reed2252eda2016-01-20 11:04:40 -0800176 // If our target pixmap is the same as the original, then we revert back to legacy behavior
177 // and allow the code to ignore fractional translate.
178 //
179 // The width/height check allows allow_ignore_fractional_translate to stay false if we
180 // previously set it that way (e.g. we started in kMedium).
181 //
182 if (fPixmap.width() == origW && fPixmap.height() == origH) {
183 allow_ignore_fractional_translate = true;
184 }
185
reed05a56472016-03-02 09:49:02 -0800186 if (kLow_SkFilterQuality == fFilterQuality && allow_ignore_fractional_translate) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000187 // Only try bilerp if the matrix is "interesting" and
188 // the image has a suitable size.
189
190 if (fInvType <= SkMatrix::kTranslate_Mask ||
reedad7ae6c2015-06-04 14:12:25 -0700191 !valid_for_filtering(fPixmap.width() | fPixmap.height()))
reedf7094c42015-01-16 12:05:19 -0800192 {
reed05a56472016-03-02 09:49:02 -0800193 fFilterQuality = kNone_SkFilterQuality;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000194 }
195 }
herb6eff52a2016-03-23 09:00:33 -0700196
reed05a56472016-03-02 09:49:02 -0800197 return true;
reed26feeba2015-01-14 12:28:22 -0800198}
humper@google.com9c96d4b2013-07-14 01:44:59 +0000199
reed05a56472016-03-02 09:49:02 -0800200/*
201 * Analyze filter-quality and matrix, and decide how to implement that.
202 *
203 * In general, we cascade down the request level [ High ... None ]
204 * - for a given level, if we can fulfill it, fine, else
205 * - else we downgrade to the next lower level and try again.
206 * We can always fulfill requests for Low and None
207 * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
208 * and may be removed.
209 */
210bool SkBitmapProcState::chooseProcs() {
211 fInvProc = fInvMatrix.getMapXYProc();
212 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
213 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
214 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
215 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
216
217 fAlphaScale = SkAlpha255To256(SkColorGetA(fPaintColor));
218
219 fShaderProc32 = nullptr;
220 fShaderProc16 = nullptr;
221 fSampleProc32 = nullptr;
222
223 const bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
224 const bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
225 SkShader::kClamp_TileMode == fTileModeY;
226
227 return this->chooseScanlineProcs(trivialMatrix, clampClamp);
228}
229
230bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000231 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
halcanary96fcdcc2015-08-27 07:41:13 -0700232 // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns nullptr.
233 if (nullptr == fMatrixProc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 return false;
235 }
236
237 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000238
reedad7ae6c2015-06-04 14:12:25 -0700239 const SkAlphaType at = fPixmap.alphaType();
reed00adc752014-07-12 21:10:52 -0700240
humper@google.com9c96d4b2013-07-14 01:44:59 +0000241 // No need to do this if we're doing HQ sampling; if filter quality is
242 // still set to HQ by the time we get here, then we must have installed
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000243 // the shader procs above and can skip all this.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
reed05a56472016-03-02 09:49:02 -0800245 if (fFilterQuality < kHigh_SkFilterQuality) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000246
humper@google.com9c96d4b2013-07-14 01:44:59 +0000247 int index = 0;
248 if (fAlphaScale < 256) { // note: this distinction is not used for D16
249 index |= 1;
reed@android.comaa9152a2009-07-17 21:24:56 +0000250 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000251 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
252 index |= 2;
253 }
reed05a56472016-03-02 09:49:02 -0800254 if (fFilterQuality > kNone_SkFilterQuality) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000255 index |= 4;
256 }
257 // bits 3,4,5 encoding the source bitmap format
reedad7ae6c2015-06-04 14:12:25 -0700258 switch (fPixmap.colorType()) {
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000259 case kN32_SkColorType:
reed00adc752014-07-12 21:10:52 -0700260 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
261 return false;
262 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000263 index |= 0;
264 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000265 case kRGB_565_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000266 index |= 8;
267 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000268 case kIndex_8_SkColorType:
reed00adc752014-07-12 21:10:52 -0700269 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
270 return false;
271 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000272 index |= 16;
273 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000274 case kARGB_4444_SkColorType:
reed00adc752014-07-12 21:10:52 -0700275 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
276 return false;
277 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000278 index |= 24;
279 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000280 case kAlpha_8_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000281 index |= 32;
reed05a56472016-03-02 09:49:02 -0800282 fPaintPMColor = SkPreMultiplyColor(fPaintColor);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000283 break;
reed0c9b1a82015-03-17 17:44:06 -0700284 case kGray_8_SkColorType:
285 index |= 40;
reed05a56472016-03-02 09:49:02 -0800286 fPaintPMColor = SkPreMultiplyColor(fPaintColor);
reed0c9b1a82015-03-17 17:44:06 -0700287 break;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000288 default:
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000289 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
humper@google.com9c96d4b2013-07-14 01:44:59 +0000290 return false;
291 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000292
mtklein809ccf32016-05-05 10:58:39 -0700293#if !defined(SK_ARM_HAS_NEON)
humper@google.com9c96d4b2013-07-14 01:44:59 +0000294 static const SampleProc32 gSkBitmapProcStateSample32[] = {
295 S32_opaque_D32_nofilter_DXDY,
296 S32_alpha_D32_nofilter_DXDY,
297 S32_opaque_D32_nofilter_DX,
298 S32_alpha_D32_nofilter_DX,
299 S32_opaque_D32_filter_DXDY,
300 S32_alpha_D32_filter_DXDY,
301 S32_opaque_D32_filter_DX,
302 S32_alpha_D32_filter_DX,
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +0000303
humper@google.com9c96d4b2013-07-14 01:44:59 +0000304 S16_opaque_D32_nofilter_DXDY,
305 S16_alpha_D32_nofilter_DXDY,
306 S16_opaque_D32_nofilter_DX,
307 S16_alpha_D32_nofilter_DX,
308 S16_opaque_D32_filter_DXDY,
309 S16_alpha_D32_filter_DXDY,
310 S16_opaque_D32_filter_DX,
311 S16_alpha_D32_filter_DX,
312
313 SI8_opaque_D32_nofilter_DXDY,
314 SI8_alpha_D32_nofilter_DXDY,
315 SI8_opaque_D32_nofilter_DX,
316 SI8_alpha_D32_nofilter_DX,
317 SI8_opaque_D32_filter_DXDY,
318 SI8_alpha_D32_filter_DXDY,
319 SI8_opaque_D32_filter_DX,
320 SI8_alpha_D32_filter_DX,
321
322 S4444_opaque_D32_nofilter_DXDY,
323 S4444_alpha_D32_nofilter_DXDY,
324 S4444_opaque_D32_nofilter_DX,
325 S4444_alpha_D32_nofilter_DX,
326 S4444_opaque_D32_filter_DXDY,
327 S4444_alpha_D32_filter_DXDY,
328 S4444_opaque_D32_filter_DX,
329 S4444_alpha_D32_filter_DX,
herb6eff52a2016-03-23 09:00:33 -0700330
humper@google.com9c96d4b2013-07-14 01:44:59 +0000331 // A8 treats alpha/opaque the same (equally efficient)
332 SA8_alpha_D32_nofilter_DXDY,
333 SA8_alpha_D32_nofilter_DXDY,
334 SA8_alpha_D32_nofilter_DX,
335 SA8_alpha_D32_nofilter_DX,
336 SA8_alpha_D32_filter_DXDY,
337 SA8_alpha_D32_filter_DXDY,
338 SA8_alpha_D32_filter_DX,
reed0c9b1a82015-03-17 17:44:06 -0700339 SA8_alpha_D32_filter_DX,
herb6eff52a2016-03-23 09:00:33 -0700340
reed0c9b1a82015-03-17 17:44:06 -0700341 // todo: possibly specialize on opaqueness
342 SG8_alpha_D32_nofilter_DXDY,
343 SG8_alpha_D32_nofilter_DXDY,
344 SG8_alpha_D32_nofilter_DX,
345 SG8_alpha_D32_nofilter_DX,
346 SG8_alpha_D32_filter_DXDY,
347 SG8_alpha_D32_filter_DXDY,
348 SG8_alpha_D32_filter_DX,
349 SG8_alpha_D32_filter_DX
humper@google.com9c96d4b2013-07-14 01:44:59 +0000350 };
reed26feeba2015-01-14 12:28:22 -0800351#endif
humper@google.com9c96d4b2013-07-14 01:44:59 +0000352
353 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
humper@google.com9c96d4b2013-07-14 01:44:59 +0000354
355 // our special-case shaderprocs
reed4e5a7582016-01-05 05:10:33 -0800356 if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000357 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
mtklein14e4d392014-10-23 14:35:01 -0700358 } else if (S32_opaque_D32_nofilter_DX == fSampleProc32 && clampClamp) {
359 fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000360 }
361
halcanary96fcdcc2015-08-27 07:41:13 -0700362 if (nullptr == fShaderProc32) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000363 fShaderProc32 = this->chooseShaderProc32();
364 }
humper@google.comb0889472013-07-09 21:37:14 +0000365 }
mtklein96d68b72015-02-20 12:40:40 -0800366
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000367 // see if our platform has any accelerated overrides
368 this->platformProcs();
mtklein96d68b72015-02-20 12:40:40 -0800369
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 return true;
371}
372
herbc7a784c2015-12-18 09:52:15 -0800373static void Clamp_S32_D32_nofilter_trans_shaderproc(const void* sIn,
reed@google.com9a4c7462012-10-12 18:21:37 +0000374 int x, int y,
375 SkPMColor* SK_RESTRICT colors,
376 int count) {
herbc7a784c2015-12-18 09:52:15 -0800377 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
reed@google.com9a4c7462012-10-12 18:21:37 +0000378 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
379 SkASSERT(s.fInvKy == 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700380 SkASSERT(count > 0 && colors != nullptr);
reed05a56472016-03-02 09:49:02 -0800381 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000382
reedad7ae6c2015-06-04 14:12:25 -0700383 const int maxX = s.fPixmap.width() - 1;
384 const int maxY = s.fPixmap.height() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000385 int ix = s.fFilterOneX + x;
386 int iy = SkClampMax(s.fFilterOneY + y, maxY);
reedad7ae6c2015-06-04 14:12:25 -0700387 const SkPMColor* row = s.fPixmap.addr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000388
reed@google.com9a4c7462012-10-12 18:21:37 +0000389 // clamp to the left
390 if (ix < 0) {
391 int n = SkMin32(-ix, count);
392 sk_memset32(colors, row[0], n);
393 count -= n;
394 if (0 == count) {
395 return;
396 }
397 colors += n;
398 SkASSERT(-ix == n);
399 ix = 0;
400 }
401 // copy the middle
402 if (ix <= maxX) {
403 int n = SkMin32(maxX - ix + 1, count);
404 memcpy(colors, row + ix, n * sizeof(SkPMColor));
405 count -= n;
406 if (0 == count) {
407 return;
408 }
409 colors += n;
410 }
411 SkASSERT(count > 0);
412 // clamp to the right
413 sk_memset32(colors, row[maxX], count);
414}
415
reed@google.coma8d99302012-10-16 20:23:25 +0000416static inline int sk_int_mod(int x, int n) {
417 SkASSERT(n > 0);
418 if ((unsigned)x >= (unsigned)n) {
419 if (x < 0) {
420 x = n + ~(~x % n);
421 } else {
422 x = x % n;
423 }
424 }
425 return x;
426}
427
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000428static inline int sk_int_mirror(int x, int n) {
429 x = sk_int_mod(x, 2 * n);
430 if (x >= n) {
431 x = n + ~(x - n);
432 }
433 return x;
434}
435
herbc7a784c2015-12-18 09:52:15 -0800436static void Repeat_S32_D32_nofilter_trans_shaderproc(const void* sIn,
reed@google.coma8d99302012-10-16 20:23:25 +0000437 int x, int y,
438 SkPMColor* SK_RESTRICT colors,
439 int count) {
herbc7a784c2015-12-18 09:52:15 -0800440 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
reed@google.coma8d99302012-10-16 20:23:25 +0000441 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
442 SkASSERT(s.fInvKy == 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700443 SkASSERT(count > 0 && colors != nullptr);
reed05a56472016-03-02 09:49:02 -0800444 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000445
reedad7ae6c2015-06-04 14:12:25 -0700446 const int stopX = s.fPixmap.width();
447 const int stopY = s.fPixmap.height();
reed@google.coma8d99302012-10-16 20:23:25 +0000448 int ix = s.fFilterOneX + x;
449 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
reedad7ae6c2015-06-04 14:12:25 -0700450 const SkPMColor* row = s.fPixmap.addr32(0, iy);
reed@google.coma8d99302012-10-16 20:23:25 +0000451
452 ix = sk_int_mod(ix, stopX);
453 for (;;) {
454 int n = SkMin32(stopX - ix, count);
455 memcpy(colors, row + ix, n * sizeof(SkPMColor));
456 count -= n;
457 if (0 == count) {
458 return;
459 }
460 colors += n;
461 ix = 0;
462 }
463}
464
herbc7a784c2015-12-18 09:52:15 -0800465static void S32_D32_constX_shaderproc(const void* sIn,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000466 int x, int y,
467 SkPMColor* SK_RESTRICT colors,
468 int count) {
herbc7a784c2015-12-18 09:52:15 -0800469 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000470 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
471 SkASSERT(s.fInvKy == 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700472 SkASSERT(count > 0 && colors != nullptr);
reedad7ae6c2015-06-04 14:12:25 -0700473 SkASSERT(1 == s.fPixmap.width());
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000474
scroggo@google.comad511322013-02-22 15:50:37 +0000475 int iY0;
476 int iY1 SK_INIT_TO_AVOID_WARNING;
477 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000478
reed05a56472016-03-02 09:49:02 -0800479 if (kNone_SkFilterQuality != s.fFilterQuality) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000480 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
481 uint32_t xy[2];
482
483 mproc(s, xy, 1, x, y);
484
485 iY0 = xy[0] >> 18;
486 iY1 = xy[0] & 0x3FFF;
487 iSubY = (xy[0] >> 14) & 0xF;
488 } else {
489 int yTemp;
490
491 if (s.fInvType > SkMatrix::kTranslate_Mask) {
fmalitaeb543072016-02-02 10:17:24 -0800492 const SkBitmapProcStateAutoMapper mapper(s, x, y);
493
robertphillips@google.com1e305232013-01-22 20:29:16 +0000494 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000495 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000496 // bitmap's width and height. Since this method is going to do
497 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000498 if (SkShader::kClamp_TileMode != s.fTileModeX ||
499 SkShader::kClamp_TileMode != s.fTileModeY) {
fmalitabe5cfa92016-02-03 10:21:33 -0800500 yTemp = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height());
robertphillips@google.comd5077752013-01-23 00:36:02 +0000501 } else {
fmalitabe5cfa92016-02-03 10:21:33 -0800502 yTemp = mapper.intY();
robertphillips@google.comd5077752013-01-23 00:36:02 +0000503 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000504 } else {
505 yTemp = s.fFilterOneY + y;
506 }
507
reedad7ae6c2015-06-04 14:12:25 -0700508 const int stopY = s.fPixmap.height();
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000509 switch (s.fTileModeY) {
510 case SkShader::kClamp_TileMode:
511 iY0 = SkClampMax(yTemp, stopY-1);
512 break;
513 case SkShader::kRepeat_TileMode:
514 iY0 = sk_int_mod(yTemp, stopY);
515 break;
516 case SkShader::kMirror_TileMode:
517 default:
518 iY0 = sk_int_mirror(yTemp, stopY);
519 break;
520 }
521
fmalitad2a42712016-02-01 04:48:39 -0800522#ifdef SK_DEBUG
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000523 {
fmalitaeb543072016-02-02 10:17:24 -0800524 const SkBitmapProcStateAutoMapper mapper(s, x, y);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000525 int iY2;
fmalitaeb543072016-02-02 10:17:24 -0800526
527 if (s.fInvType > SkMatrix::kTranslate_Mask &&
528 (SkShader::kClamp_TileMode != s.fTileModeX ||
529 SkShader::kClamp_TileMode != s.fTileModeY)) {
fmalitabe5cfa92016-02-03 10:21:33 -0800530 iY2 = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height());
fmalitad2a42712016-02-01 04:48:39 -0800531 } else {
fmalitabe5cfa92016-02-03 10:21:33 -0800532 iY2 = mapper.intY();
fmalitad2a42712016-02-01 04:48:39 -0800533 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000534
535 switch (s.fTileModeY) {
536 case SkShader::kClamp_TileMode:
fmalitad2a42712016-02-01 04:48:39 -0800537 iY2 = SkClampMax(iY2, stopY-1);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000538 break;
539 case SkShader::kRepeat_TileMode:
fmalitad2a42712016-02-01 04:48:39 -0800540 iY2 = sk_int_mod(iY2, stopY);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000541 break;
542 case SkShader::kMirror_TileMode:
543 default:
fmalitad2a42712016-02-01 04:48:39 -0800544 iY2 = sk_int_mirror(iY2, stopY);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000545 break;
546 }
547
548 SkASSERT(iY0 == iY2);
549 }
550#endif
551 }
552
reedad7ae6c2015-06-04 14:12:25 -0700553 const SkPMColor* row0 = s.fPixmap.addr32(0, iY0);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000554 SkPMColor color;
555
reed05a56472016-03-02 09:49:02 -0800556 if (kNone_SkFilterQuality != s.fFilterQuality) {
reedad7ae6c2015-06-04 14:12:25 -0700557 const SkPMColor* row1 = s.fPixmap.addr32(0, iY1);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000558
559 if (s.fAlphaScale < 256) {
560 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
561 } else {
562 Filter_32_opaque(iSubY, *row0, *row1, &color);
563 }
564 } else {
565 if (s.fAlphaScale < 256) {
566 color = SkAlphaMulQ(*row0, s.fAlphaScale);
567 } else {
568 color = *row0;
569 }
570 }
571
572 sk_memset32(colors, color, count);
573}
574
herbc7a784c2015-12-18 09:52:15 -0800575static void DoNothing_shaderproc(const void*, int x, int y,
reed@google.com6bb92bc2012-11-20 19:45:16 +0000576 SkPMColor* SK_RESTRICT colors, int count) {
577 // if we get called, the matrix is too tricky, so we just draw nothing
578 sk_memset32(colors, 0, count);
579}
580
581bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000582 SkPoint pt;
fmalita8a8eb522016-01-30 18:56:34 -0800583 const SkBitmapProcStateAutoMapper mapper(*this, 0, 0, &pt);
fmalita653c12d2016-01-30 10:06:46 -0800584
reed@google.com6bb92bc2012-11-20 19:45:16 +0000585 /*
586 * if the translate is larger than our ints, we can get random results, or
587 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
588 * negate it.
589 */
fmalita8a8eb522016-01-30 18:56:34 -0800590 const SkScalar too_big = SkIntToScalar(1 << 30);
591 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000592 return false;
593 }
594
reed@google.coma8d99302012-10-16 20:23:25 +0000595 // Since we know we're not filtered, we re-purpose these fields allow
596 // us to go from device -> src coordinates w/ just an integer add,
597 // rather than running through the inverse-matrix
fmalitabe5cfa92016-02-03 10:21:33 -0800598 fFilterOneX = mapper.intX();
599 fFilterOneY = mapper.intY();
fmalita653c12d2016-01-30 10:06:46 -0800600
reed@google.com6bb92bc2012-11-20 19:45:16 +0000601 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000602}
603
reed@google.com9a4c7462012-10-12 18:21:37 +0000604SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000605
reedad7ae6c2015-06-04 14:12:25 -0700606 if (kN32_SkColorType != fPixmap.colorType()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700607 return nullptr;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000608 }
609
610 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
611
reedad7ae6c2015-06-04 14:12:25 -0700612 if (1 == fPixmap.width() && 0 == (fInvType & ~kMask)) {
reed05a56472016-03-02 09:49:02 -0800613 if (kNone_SkFilterQuality == fFilterQuality &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000614 fInvType <= SkMatrix::kTranslate_Mask &&
615 !this->setupForTranslate()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000616 return DoNothing_shaderproc;
617 }
618 return S32_D32_constX_shaderproc;
619 }
620
reed@google.com9a4c7462012-10-12 18:21:37 +0000621 if (fAlphaScale < 256) {
halcanary96fcdcc2015-08-27 07:41:13 -0700622 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000623 }
624 if (fInvType > SkMatrix::kTranslate_Mask) {
halcanary96fcdcc2015-08-27 07:41:13 -0700625 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000626 }
reed05a56472016-03-02 09:49:02 -0800627 if (kNone_SkFilterQuality != fFilterQuality) {
halcanary96fcdcc2015-08-27 07:41:13 -0700628 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000629 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000630
reed@google.coma8d99302012-10-16 20:23:25 +0000631 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
632 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
633
634 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000635 if (this->setupForTranslate()) {
636 return Clamp_S32_D32_nofilter_trans_shaderproc;
637 }
638 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000639 }
reed@google.coma8d99302012-10-16 20:23:25 +0000640 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000641 if (this->setupForTranslate()) {
642 return Repeat_S32_D32_nofilter_trans_shaderproc;
643 }
644 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000645 }
halcanary96fcdcc2015-08-27 07:41:13 -0700646 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000647}
648
reed@android.com4c128c42009-08-14 13:54:37 +0000649///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000650
651#ifdef SK_DEBUG
652
653static void check_scale_nofilter(uint32_t bitmapXY[], int count,
654 unsigned mx, unsigned my) {
655 unsigned y = *bitmapXY++;
656 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000657
reed@google.com9fe287b2012-03-27 15:54:28 +0000658 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
659 for (int i = 0; i < count; ++i) {
660 SkASSERT(xptr[i] < mx);
661 }
662}
663
664static void check_scale_filter(uint32_t bitmapXY[], int count,
665 unsigned mx, unsigned my) {
666 uint32_t YY = *bitmapXY++;
667 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000668 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000669 SkASSERT(y0 < my);
670 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000671
reed@google.com9fe287b2012-03-27 15:54:28 +0000672 for (int i = 0; i < count; ++i) {
673 uint32_t XX = bitmapXY[i];
674 unsigned x0 = XX >> 18;
675 unsigned x1 = XX & 0x3FFF;
676 SkASSERT(x0 < mx);
677 SkASSERT(x1 < mx);
678 }
679}
680
681static void check_affine_nofilter(uint32_t bitmapXY[], int count,
682 unsigned mx, unsigned my) {
683 for (int i = 0; i < count; ++i) {
684 uint32_t XY = bitmapXY[i];
685 unsigned x = XY & 0xFFFF;
686 unsigned y = XY >> 16;
687 SkASSERT(x < mx);
688 SkASSERT(y < my);
689 }
690}
691
692static void check_affine_filter(uint32_t bitmapXY[], int count,
693 unsigned mx, unsigned my) {
694 for (int i = 0; i < count; ++i) {
695 uint32_t YY = *bitmapXY++;
696 unsigned y0 = YY >> 18;
697 unsigned y1 = YY & 0x3FFF;
698 SkASSERT(y0 < my);
699 SkASSERT(y1 < my);
700
701 uint32_t XX = *bitmapXY++;
702 unsigned x0 = XX >> 18;
703 unsigned x1 = XX & 0x3FFF;
704 SkASSERT(x0 < mx);
705 SkASSERT(x1 < mx);
706 }
707}
708
709void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
710 uint32_t bitmapXY[], int count,
711 int x, int y) {
712 SkASSERT(bitmapXY);
713 SkASSERT(count > 0);
714
715 state.fMatrixProc(state, bitmapXY, count, x, y);
716
717 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
718
719 // There are four formats possible:
720 // scale -vs- affine
721 // filter -vs- nofilter
722 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
reed05a56472016-03-02 09:49:02 -0800723 proc = state.fFilterQuality != kNone_SkFilterQuality ?
724 check_scale_filter : check_scale_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000725 } else {
reed05a56472016-03-02 09:49:02 -0800726 proc = state.fFilterQuality != kNone_SkFilterQuality ?
727 check_affine_filter : check_affine_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000728 }
reedad7ae6c2015-06-04 14:12:25 -0700729 proc(bitmapXY, count, state.fPixmap.width(), state.fPixmap.height());
reed@google.com9fe287b2012-03-27 15:54:28 +0000730}
731
732SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
733 return DebugMatrixProc;
734}
735
736#endif
737
738///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000739/*
740 The storage requirements for the different matrix procs are as follows,
741 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000742
reed@android.com4c128c42009-08-14 13:54:37 +0000743 scale/translate nofilter Y(4bytes) + N * X
744 affine/perspective nofilter N * (X Y)
745 scale/translate filter Y Y + N * (X X)
746 affine/perspective filter N * (Y Y X X)
747 */
748int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
749 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000750
751 size &= ~3; // only care about 4-byte aligned chunks
752 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
753 size -= 4; // the shared Y (or YY) coordinate
754 if (size < 0) {
755 size = 0;
756 }
reed@android.com258cb222010-04-14 13:36:33 +0000757 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000758 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000759 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000760 }
761
reed05a56472016-03-02 09:49:02 -0800762 if (fFilterQuality != kNone_SkFilterQuality) {
reed@android.com258cb222010-04-14 13:36:33 +0000763 size >>= 1;
764 }
765
766 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000767}
mtklein14e4d392014-10-23 14:35:01 -0700768
769///////////////////////
770
herbc7a784c2015-12-18 09:52:15 -0800771void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, int y,
mtklein14e4d392014-10-23 14:35:01 -0700772 SkPMColor* SK_RESTRICT dst, int count) {
herbc7a784c2015-12-18 09:52:15 -0800773 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
mtklein14e4d392014-10-23 14:35:01 -0700774 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
775 SkMatrix::kScale_Mask)) == 0);
776
reedad7ae6c2015-06-04 14:12:25 -0700777 const unsigned maxX = s.fPixmap.width() - 1;
mtklein14e4d392014-10-23 14:35:01 -0700778 SkFractionalInt fx;
779 int dstY;
780 {
fmalitab3a83582016-01-04 10:28:11 -0800781 const SkBitmapProcStateAutoMapper mapper(s, x, y);
reedad7ae6c2015-06-04 14:12:25 -0700782 const unsigned maxY = s.fPixmap.height() - 1;
fmalitabe5cfa92016-02-03 10:21:33 -0800783 dstY = SkClampMax(mapper.intY(), maxY);
784 fx = mapper.fractionalIntX();
mtklein14e4d392014-10-23 14:35:01 -0700785 }
786
reedad7ae6c2015-06-04 14:12:25 -0700787 const SkPMColor* SK_RESTRICT src = s.fPixmap.addr32(0, dstY);
mtklein14e4d392014-10-23 14:35:01 -0700788 const SkFractionalInt dx = s.fInvSxFractionalInt;
789
790 // Check if we're safely inside [0...maxX] so no need to clamp each computed index.
791 //
792 if ((uint64_t)SkFractionalIntToInt(fx) <= maxX &&
793 (uint64_t)SkFractionalIntToInt(fx + dx * (count - 1)) <= maxX)
794 {
795 int count4 = count >> 2;
796 for (int i = 0; i < count4; ++i) {
797 SkPMColor src0 = src[SkFractionalIntToInt(fx)]; fx += dx;
798 SkPMColor src1 = src[SkFractionalIntToInt(fx)]; fx += dx;
799 SkPMColor src2 = src[SkFractionalIntToInt(fx)]; fx += dx;
800 SkPMColor src3 = src[SkFractionalIntToInt(fx)]; fx += dx;
801 dst[0] = src0;
802 dst[1] = src1;
803 dst[2] = src2;
804 dst[3] = src3;
805 dst += 4;
806 }
807 for (int i = (count4 << 2); i < count; ++i) {
808 unsigned index = SkFractionalIntToInt(fx);
809 SkASSERT(index <= maxX);
810 *dst++ = src[index];
811 fx += dx;
812 }
813 } else {
814 for (int i = 0; i < count; ++i) {
815 dst[i] = src[SkClampMax(SkFractionalIntToInt(fx), maxX)];
816 fx += dx;
817 }
818 }
819}