blob: ab2321254b1e3f4619c1688317fe10b465e1adbe [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
reed04617132014-08-21 09:46:49 -07007
8#include "SkBitmapCache.h"
reed64045422015-06-04 06:31:31 -07009#include "SkBitmapController.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkBitmapProcState.h"
11#include "SkColorPriv.h"
12#include "SkFilterProc.h"
13#include "SkPaint.h"
14#include "SkShader.h" // for tilemodes
digit@google.com3ada0ef2012-08-13 14:06:34 +000015#include "SkUtilsArm.h"
humper@google.com138ebc32013-07-19 20:20:04 +000016#include "SkBitmapScaler.h"
reed@google.comd94697c2013-07-24 14:31:33 +000017#include "SkMipMap.h"
reed@google.comcee9dcb2013-09-13 16:04:49 +000018#include "SkPixelRef.h"
commit-bot@chromium.orgf4491562014-05-28 17:30:02 +000019#include "SkImageEncoder.h"
reed011f39a2014-08-28 13:35:23 -070020#include "SkResourceCache.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021
digit@google.com3ada0ef2012-08-13 14:06:34 +000022#if !SK_ARM_NEON_IS_NONE
23// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
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,
40 SkShader::TileMode tmx, SkShader::TileMode tmy)
reed7a4d8472015-09-15 13:33:58 -070041 : fProvider(provider)
reed05a56472016-03-02 09:49:02 -080042 , fTileModeX(tmx)
43 , fTileModeY(tmy)
reed7a4d8472015-09-15 13:33:58 -070044 , fBMState(nullptr)
reed05a56472016-03-02 09:49:02 -080045{}
reed7a4d8472015-09-15 13:33:58 -070046
reed05a56472016-03-02 09:49:02 -080047SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmap& bm,
48 SkShader::TileMode tmx, SkShader::TileMode tmy)
reed7a4d8472015-09-15 13:33:58 -070049 : fProvider(SkBitmapProvider(bm))
reed05a56472016-03-02 09:49:02 -080050 , fTileModeX(tmx)
51 , fTileModeY(tmy)
reed7a4d8472015-09-15 13:33:58 -070052 , fBMState(nullptr)
reed05a56472016-03-02 09:49:02 -080053{}
reed64045422015-06-04 06:31:31 -070054
reed05a56472016-03-02 09:49:02 -080055SkBitmapProcInfo::~SkBitmapProcInfo() {
reed64045422015-06-04 06:31:31 -070056 SkInPlaceDeleteCheck(fBMState, fBMStateStorage.get());
57}
58
reed@android.coma44b4cc2009-07-16 02:03:58 +000059///////////////////////////////////////////////////////////////////////////////
60
reed@google.comee056a82013-04-18 15:33:27 +000061// true iff the matrix contains, at most, scale and translate elements
62static bool matrix_only_scale_translate(const SkMatrix& m) {
63 return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask);
64}
65
reed@google.comc0e88e02012-10-17 21:11:56 +000066/**
67 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
68 * go ahead and treat it as if it were, so that subsequent code can go fast.
69 */
reedad7ae6c2015-06-04 14:12:25 -070070static bool just_trans_clamp(const SkMatrix& matrix, const SkPixmap& pixmap) {
reed@google.comee056a82013-04-18 15:33:27 +000071 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000072
reed@google.comee056a82013-04-18 15:33:27 +000073 if (matrix.getType() & SkMatrix::kScale_Mask) {
reedad7ae6c2015-06-04 14:12:25 -070074 SkRect dst;
75 SkRect src = SkRect::Make(pixmap.bounds());
reed@google.comf707adc2013-04-18 15:37:14 +000076
77 // Can't call mapRect(), since that will fix up inverted rectangles,
78 // e.g. when scale is negative, and we don't want to return true for
79 // those.
80 matrix.mapPoints(SkTCast<SkPoint*>(&dst),
81 SkTCast<const SkPoint*>(&src),
82 2);
reed@google.comee056a82013-04-18 15:33:27 +000083
84 // Now round all 4 edges to device space, and then compare the device
85 // width/height to the original. Note: we must map all 4 and subtract
86 // rather than map the "width" and compare, since we care about the
87 // phase (in pixel space) that any translate in the matrix might impart.
88 SkIRect idst;
89 dst.round(&idst);
reedad7ae6c2015-06-04 14:12:25 -070090 return idst.width() == pixmap.width() && idst.height() == pixmap.height();
reed@google.comc0e88e02012-10-17 21:11:56 +000091 }
92 // if we got here, we're either kTranslate_Mask or identity
93 return true;
94}
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
reed@google.comee056a82013-04-18 15:33:27 +000099 if (matrix.getType() & SkMatrix::kScale_Mask) {
reed@google.comc0e88e02012-10-17 21:11:56 +0000100 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000101
reed@google.comc0e88e02012-10-17 21:11:56 +0000102 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
103 return false;
104 }
105 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
106 return false;
107 }
108 }
109 // if we got here, treat us as either kTranslate_Mask or identity
110 return true;
111}
112
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113static bool valid_for_filtering(unsigned dimension) {
114 // for filtering, width and height must fit in 14bits, since we use steal
115 // 2 bits from each to store our 4bit subpixel data
116 return (dimension & ~0x3FFF) == 0;
117}
118
reed05a56472016-03-02 09:49:02 -0800119bool SkBitmapProcInfo::init(const SkMatrix& inv, const SkPaint& paint) {
reed2252eda2016-01-20 11:04:40 -0800120 const int origW = fProvider.info().width();
121 const int origH = fProvider.info().height();
reed05a56472016-03-02 09:49:02 -0800122
123 fPixmap.reset();
124 fInvMatrix = inv;
125 fFilterQuality = paint.getFilterQuality();
126
reed2252eda2016-01-20 11:04:40 -0800127 bool allow_ignore_fractional_translate = true; // historical default
reed05a56472016-03-02 09:49:02 -0800128 if (kMedium_SkFilterQuality == fFilterQuality) {
reed2252eda2016-01-20 11:04:40 -0800129 allow_ignore_fractional_translate = false;
130 }
reed2252eda2016-01-20 11:04:40 -0800131
reed64045422015-06-04 06:31:31 -0700132 SkDefaultBitmapController controller;
reed7a4d8472015-09-15 13:33:58 -0700133 fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(),
reed64045422015-06-04 06:31:31 -0700134 fBMStateStorage.get(), fBMStateStorage.size());
reedad7ae6c2015-06-04 14:12:25 -0700135 // Note : we allow the controller to return an empty (zero-dimension) result. Should we?
halcanary96fcdcc2015-08-27 07:41:13 -0700136 if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) {
reed64045422015-06-04 06:31:31 -0700137 return false;
reedf7094c42015-01-16 12:05:19 -0800138 }
reedad7ae6c2015-06-04 14:12:25 -0700139 fPixmap = fBMState->pixmap();
reed64045422015-06-04 06:31:31 -0700140 fInvMatrix = fBMState->invMatrix();
herb6eff52a2016-03-23 09:00:33 -0700141 fRealInvMatrix = fBMState->invMatrix();
reed05a56472016-03-02 09:49:02 -0800142 fPaintColor = paint.getColor();
143 fFilterQuality = fBMState->quality();
reedad7ae6c2015-06-04 14:12:25 -0700144 SkASSERT(fPixmap.addr());
reed05a56472016-03-02 09:49:02 -0800145
reed@google.comef0e3192013-09-09 13:42:39 +0000146 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
147 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
148 SkShader::kClamp_TileMode == fTileModeY;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000149
humper535e3b22014-10-27 10:32:06 -0700150 // Most of the scanline procs deal with "unit" texture coordinates, as this
151 // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate
152 // those, we divide the matrix by its dimensions here.
153 //
154 // We don't do this if we're either trivial (can ignore the matrix) or clamping
155 // in both X and Y since clamping to width,height is just as easy as to 0xFFFF.
156
157 if (!(clampClamp || trivialMatrix)) {
reedad7ae6c2015-06-04 14:12:25 -0700158 fInvMatrix.postIDiv(fPixmap.width(), fPixmap.height());
reed@google.comef0e3192013-09-09 13:42:39 +0000159 }
160
humper@google.com9c96d4b2013-07-14 01:44:59 +0000161 // Now that all possible changes to the matrix have taken place, check
162 // to see if we're really close to a no-scale matrix. If so, explicitly
163 // set it to be so. Subsequent code may inspect this matrix to choose
164 // a faster path in this case.
165
166 // This code will only execute if the matrix has some scale component;
167 // if it's already pure translate then we won't do this inversion.
168
169 if (matrix_only_scale_translate(fInvMatrix)) {
reed@google.comee056a82013-04-18 15:33:27 +0000170 SkMatrix forward;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000171 if (fInvMatrix.invert(&forward)) {
reed2252eda2016-01-20 11:04:40 -0800172 if ((clampClamp && allow_ignore_fractional_translate)
reed05a56472016-03-02 09:49:02 -0800173 ? just_trans_clamp(forward, fPixmap)
174 : just_trans_general(forward)) {
reedf2e2ad02016-01-19 13:33:32 -0800175 fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY());
reed@google.comce1f3cc2013-01-05 14:37:48 +0000176 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000177 }
178 }
179
reed05a56472016-03-02 09:49:02 -0800180 fInvType = fInvMatrix.getType();
humper@google.com9c96d4b2013-07-14 01:44:59 +0000181
reed2252eda2016-01-20 11:04:40 -0800182 // If our target pixmap is the same as the original, then we revert back to legacy behavior
183 // and allow the code to ignore fractional translate.
184 //
185 // The width/height check allows allow_ignore_fractional_translate to stay false if we
186 // previously set it that way (e.g. we started in kMedium).
187 //
188 if (fPixmap.width() == origW && fPixmap.height() == origH) {
189 allow_ignore_fractional_translate = true;
190 }
191
reed05a56472016-03-02 09:49:02 -0800192 if (kLow_SkFilterQuality == fFilterQuality && allow_ignore_fractional_translate) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000193 // Only try bilerp if the matrix is "interesting" and
194 // the image has a suitable size.
195
196 if (fInvType <= SkMatrix::kTranslate_Mask ||
reedad7ae6c2015-06-04 14:12:25 -0700197 !valid_for_filtering(fPixmap.width() | fPixmap.height()))
reedf7094c42015-01-16 12:05:19 -0800198 {
reed05a56472016-03-02 09:49:02 -0800199 fFilterQuality = kNone_SkFilterQuality;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000200 }
201 }
herb6eff52a2016-03-23 09:00:33 -0700202
reed05a56472016-03-02 09:49:02 -0800203 return true;
reed26feeba2015-01-14 12:28:22 -0800204}
humper@google.com9c96d4b2013-07-14 01:44:59 +0000205
reed05a56472016-03-02 09:49:02 -0800206/*
207 * Analyze filter-quality and matrix, and decide how to implement that.
208 *
209 * In general, we cascade down the request level [ High ... None ]
210 * - for a given level, if we can fulfill it, fine, else
211 * - else we downgrade to the next lower level and try again.
212 * We can always fulfill requests for Low and None
213 * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
214 * and may be removed.
215 */
216bool SkBitmapProcState::chooseProcs() {
217 fInvProc = fInvMatrix.getMapXYProc();
218 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
219 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
220 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
221 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
222
223 fAlphaScale = SkAlpha255To256(SkColorGetA(fPaintColor));
224
225 fShaderProc32 = nullptr;
226 fShaderProc16 = nullptr;
227 fSampleProc32 = nullptr;
228
229 const bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
230 const bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
231 SkShader::kClamp_TileMode == fTileModeY;
232
233 return this->chooseScanlineProcs(trivialMatrix, clampClamp);
234}
235
236bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000237 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
halcanary96fcdcc2015-08-27 07:41:13 -0700238 // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns nullptr.
239 if (nullptr == fMatrixProc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 return false;
241 }
242
243 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000244
reedad7ae6c2015-06-04 14:12:25 -0700245 const SkAlphaType at = fPixmap.alphaType();
reed00adc752014-07-12 21:10:52 -0700246
humper@google.com9c96d4b2013-07-14 01:44:59 +0000247 // No need to do this if we're doing HQ sampling; if filter quality is
248 // still set to HQ by the time we get here, then we must have installed
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000249 // the shader procs above and can skip all this.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
reed05a56472016-03-02 09:49:02 -0800251 if (fFilterQuality < kHigh_SkFilterQuality) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000252
humper@google.com9c96d4b2013-07-14 01:44:59 +0000253 int index = 0;
254 if (fAlphaScale < 256) { // note: this distinction is not used for D16
255 index |= 1;
reed@android.comaa9152a2009-07-17 21:24:56 +0000256 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000257 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
258 index |= 2;
259 }
reed05a56472016-03-02 09:49:02 -0800260 if (fFilterQuality > kNone_SkFilterQuality) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000261 index |= 4;
262 }
263 // bits 3,4,5 encoding the source bitmap format
reedad7ae6c2015-06-04 14:12:25 -0700264 switch (fPixmap.colorType()) {
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000265 case kN32_SkColorType:
reed00adc752014-07-12 21:10:52 -0700266 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
267 return false;
268 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000269 index |= 0;
270 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000271 case kRGB_565_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000272 index |= 8;
273 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000274 case kIndex_8_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 |= 16;
279 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000280 case kARGB_4444_SkColorType:
reed00adc752014-07-12 21:10:52 -0700281 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
282 return false;
283 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000284 index |= 24;
285 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000286 case kAlpha_8_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000287 index |= 32;
reed05a56472016-03-02 09:49:02 -0800288 fPaintPMColor = SkPreMultiplyColor(fPaintColor);
humper@google.com9c96d4b2013-07-14 01:44:59 +0000289 break;
reed0c9b1a82015-03-17 17:44:06 -0700290 case kGray_8_SkColorType:
291 index |= 40;
reed05a56472016-03-02 09:49:02 -0800292 fPaintPMColor = SkPreMultiplyColor(fPaintColor);
reed0c9b1a82015-03-17 17:44:06 -0700293 break;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000294 default:
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000295 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
humper@google.com9c96d4b2013-07-14 01:44:59 +0000296 return false;
297 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000298
reed26feeba2015-01-14 12:28:22 -0800299#if !SK_ARM_NEON_IS_ALWAYS
humper@google.com9c96d4b2013-07-14 01:44:59 +0000300 static const SampleProc32 gSkBitmapProcStateSample32[] = {
301 S32_opaque_D32_nofilter_DXDY,
302 S32_alpha_D32_nofilter_DXDY,
303 S32_opaque_D32_nofilter_DX,
304 S32_alpha_D32_nofilter_DX,
305 S32_opaque_D32_filter_DXDY,
306 S32_alpha_D32_filter_DXDY,
307 S32_opaque_D32_filter_DX,
308 S32_alpha_D32_filter_DX,
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +0000309
humper@google.com9c96d4b2013-07-14 01:44:59 +0000310 S16_opaque_D32_nofilter_DXDY,
311 S16_alpha_D32_nofilter_DXDY,
312 S16_opaque_D32_nofilter_DX,
313 S16_alpha_D32_nofilter_DX,
314 S16_opaque_D32_filter_DXDY,
315 S16_alpha_D32_filter_DXDY,
316 S16_opaque_D32_filter_DX,
317 S16_alpha_D32_filter_DX,
318
319 SI8_opaque_D32_nofilter_DXDY,
320 SI8_alpha_D32_nofilter_DXDY,
321 SI8_opaque_D32_nofilter_DX,
322 SI8_alpha_D32_nofilter_DX,
323 SI8_opaque_D32_filter_DXDY,
324 SI8_alpha_D32_filter_DXDY,
325 SI8_opaque_D32_filter_DX,
326 SI8_alpha_D32_filter_DX,
327
328 S4444_opaque_D32_nofilter_DXDY,
329 S4444_alpha_D32_nofilter_DXDY,
330 S4444_opaque_D32_nofilter_DX,
331 S4444_alpha_D32_nofilter_DX,
332 S4444_opaque_D32_filter_DXDY,
333 S4444_alpha_D32_filter_DXDY,
334 S4444_opaque_D32_filter_DX,
335 S4444_alpha_D32_filter_DX,
herb6eff52a2016-03-23 09:00:33 -0700336
humper@google.com9c96d4b2013-07-14 01:44:59 +0000337 // A8 treats alpha/opaque the same (equally efficient)
338 SA8_alpha_D32_nofilter_DXDY,
339 SA8_alpha_D32_nofilter_DXDY,
340 SA8_alpha_D32_nofilter_DX,
341 SA8_alpha_D32_nofilter_DX,
342 SA8_alpha_D32_filter_DXDY,
343 SA8_alpha_D32_filter_DXDY,
344 SA8_alpha_D32_filter_DX,
reed0c9b1a82015-03-17 17:44:06 -0700345 SA8_alpha_D32_filter_DX,
herb6eff52a2016-03-23 09:00:33 -0700346
reed0c9b1a82015-03-17 17:44:06 -0700347 // todo: possibly specialize on opaqueness
348 SG8_alpha_D32_nofilter_DXDY,
349 SG8_alpha_D32_nofilter_DXDY,
350 SG8_alpha_D32_nofilter_DX,
351 SG8_alpha_D32_nofilter_DX,
352 SG8_alpha_D32_filter_DXDY,
353 SG8_alpha_D32_filter_DXDY,
354 SG8_alpha_D32_filter_DX,
355 SG8_alpha_D32_filter_DX
humper@google.com9c96d4b2013-07-14 01:44:59 +0000356 };
reed26feeba2015-01-14 12:28:22 -0800357#endif
humper@google.com9c96d4b2013-07-14 01:44:59 +0000358
359 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
360 index >>= 1; // shift away any opaque/alpha distinction
humper@google.com9c96d4b2013-07-14 01:44:59 +0000361
362 // our special-case shaderprocs
reed4e5a7582016-01-05 05:10:33 -0800363 if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000364 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
mtklein14e4d392014-10-23 14:35:01 -0700365 } else if (S32_opaque_D32_nofilter_DX == fSampleProc32 && clampClamp) {
366 fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000367 }
368
halcanary96fcdcc2015-08-27 07:41:13 -0700369 if (nullptr == fShaderProc32) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000370 fShaderProc32 = this->chooseShaderProc32();
371 }
humper@google.comb0889472013-07-09 21:37:14 +0000372 }
mtklein96d68b72015-02-20 12:40:40 -0800373
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000374 // see if our platform has any accelerated overrides
375 this->platformProcs();
mtklein96d68b72015-02-20 12:40:40 -0800376
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 return true;
378}
379
herbc7a784c2015-12-18 09:52:15 -0800380static void Clamp_S32_D32_nofilter_trans_shaderproc(const void* sIn,
reed@google.com9a4c7462012-10-12 18:21:37 +0000381 int x, int y,
382 SkPMColor* SK_RESTRICT colors,
383 int count) {
herbc7a784c2015-12-18 09:52:15 -0800384 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
reed@google.com9a4c7462012-10-12 18:21:37 +0000385 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
386 SkASSERT(s.fInvKy == 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700387 SkASSERT(count > 0 && colors != nullptr);
reed05a56472016-03-02 09:49:02 -0800388 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000389
reedad7ae6c2015-06-04 14:12:25 -0700390 const int maxX = s.fPixmap.width() - 1;
391 const int maxY = s.fPixmap.height() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000392 int ix = s.fFilterOneX + x;
393 int iy = SkClampMax(s.fFilterOneY + y, maxY);
reedad7ae6c2015-06-04 14:12:25 -0700394 const SkPMColor* row = s.fPixmap.addr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000395
reed@google.com9a4c7462012-10-12 18:21:37 +0000396 // clamp to the left
397 if (ix < 0) {
398 int n = SkMin32(-ix, count);
399 sk_memset32(colors, row[0], n);
400 count -= n;
401 if (0 == count) {
402 return;
403 }
404 colors += n;
405 SkASSERT(-ix == n);
406 ix = 0;
407 }
408 // copy the middle
409 if (ix <= maxX) {
410 int n = SkMin32(maxX - ix + 1, count);
411 memcpy(colors, row + ix, n * sizeof(SkPMColor));
412 count -= n;
413 if (0 == count) {
414 return;
415 }
416 colors += n;
417 }
418 SkASSERT(count > 0);
419 // clamp to the right
420 sk_memset32(colors, row[maxX], count);
421}
422
reed@google.coma8d99302012-10-16 20:23:25 +0000423static inline int sk_int_mod(int x, int n) {
424 SkASSERT(n > 0);
425 if ((unsigned)x >= (unsigned)n) {
426 if (x < 0) {
427 x = n + ~(~x % n);
428 } else {
429 x = x % n;
430 }
431 }
432 return x;
433}
434
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000435static inline int sk_int_mirror(int x, int n) {
436 x = sk_int_mod(x, 2 * n);
437 if (x >= n) {
438 x = n + ~(x - n);
439 }
440 return x;
441}
442
herbc7a784c2015-12-18 09:52:15 -0800443static void Repeat_S32_D32_nofilter_trans_shaderproc(const void* sIn,
reed@google.coma8d99302012-10-16 20:23:25 +0000444 int x, int y,
445 SkPMColor* SK_RESTRICT colors,
446 int count) {
herbc7a784c2015-12-18 09:52:15 -0800447 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
reed@google.coma8d99302012-10-16 20:23:25 +0000448 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
449 SkASSERT(s.fInvKy == 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700450 SkASSERT(count > 0 && colors != nullptr);
reed05a56472016-03-02 09:49:02 -0800451 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000452
reedad7ae6c2015-06-04 14:12:25 -0700453 const int stopX = s.fPixmap.width();
454 const int stopY = s.fPixmap.height();
reed@google.coma8d99302012-10-16 20:23:25 +0000455 int ix = s.fFilterOneX + x;
456 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
reedad7ae6c2015-06-04 14:12:25 -0700457 const SkPMColor* row = s.fPixmap.addr32(0, iy);
reed@google.coma8d99302012-10-16 20:23:25 +0000458
459 ix = sk_int_mod(ix, stopX);
460 for (;;) {
461 int n = SkMin32(stopX - ix, count);
462 memcpy(colors, row + ix, n * sizeof(SkPMColor));
463 count -= n;
464 if (0 == count) {
465 return;
466 }
467 colors += n;
468 ix = 0;
469 }
470}
471
herbc7a784c2015-12-18 09:52:15 -0800472static void S32_D32_constX_shaderproc(const void* sIn,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000473 int x, int y,
474 SkPMColor* SK_RESTRICT colors,
475 int count) {
herbc7a784c2015-12-18 09:52:15 -0800476 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000477 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
478 SkASSERT(s.fInvKy == 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700479 SkASSERT(count > 0 && colors != nullptr);
reedad7ae6c2015-06-04 14:12:25 -0700480 SkASSERT(1 == s.fPixmap.width());
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000481
scroggo@google.comad511322013-02-22 15:50:37 +0000482 int iY0;
483 int iY1 SK_INIT_TO_AVOID_WARNING;
484 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000485
reed05a56472016-03-02 09:49:02 -0800486 if (kNone_SkFilterQuality != s.fFilterQuality) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000487 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
488 uint32_t xy[2];
489
490 mproc(s, xy, 1, x, y);
491
492 iY0 = xy[0] >> 18;
493 iY1 = xy[0] & 0x3FFF;
494 iSubY = (xy[0] >> 14) & 0xF;
495 } else {
496 int yTemp;
497
498 if (s.fInvType > SkMatrix::kTranslate_Mask) {
fmalitaeb543072016-02-02 10:17:24 -0800499 const SkBitmapProcStateAutoMapper mapper(s, x, y);
500
robertphillips@google.com1e305232013-01-22 20:29:16 +0000501 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000502 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000503 // bitmap's width and height. Since this method is going to do
504 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000505 if (SkShader::kClamp_TileMode != s.fTileModeX ||
506 SkShader::kClamp_TileMode != s.fTileModeY) {
fmalitabe5cfa92016-02-03 10:21:33 -0800507 yTemp = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height());
robertphillips@google.comd5077752013-01-23 00:36:02 +0000508 } else {
fmalitabe5cfa92016-02-03 10:21:33 -0800509 yTemp = mapper.intY();
robertphillips@google.comd5077752013-01-23 00:36:02 +0000510 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000511 } else {
512 yTemp = s.fFilterOneY + y;
513 }
514
reedad7ae6c2015-06-04 14:12:25 -0700515 const int stopY = s.fPixmap.height();
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000516 switch (s.fTileModeY) {
517 case SkShader::kClamp_TileMode:
518 iY0 = SkClampMax(yTemp, stopY-1);
519 break;
520 case SkShader::kRepeat_TileMode:
521 iY0 = sk_int_mod(yTemp, stopY);
522 break;
523 case SkShader::kMirror_TileMode:
524 default:
525 iY0 = sk_int_mirror(yTemp, stopY);
526 break;
527 }
528
fmalitad2a42712016-02-01 04:48:39 -0800529#ifdef SK_DEBUG
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000530 {
fmalitaeb543072016-02-02 10:17:24 -0800531 const SkBitmapProcStateAutoMapper mapper(s, x, y);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000532 int iY2;
fmalitaeb543072016-02-02 10:17:24 -0800533
534 if (s.fInvType > SkMatrix::kTranslate_Mask &&
535 (SkShader::kClamp_TileMode != s.fTileModeX ||
536 SkShader::kClamp_TileMode != s.fTileModeY)) {
fmalitabe5cfa92016-02-03 10:21:33 -0800537 iY2 = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height());
fmalitad2a42712016-02-01 04:48:39 -0800538 } else {
fmalitabe5cfa92016-02-03 10:21:33 -0800539 iY2 = mapper.intY();
fmalitad2a42712016-02-01 04:48:39 -0800540 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000541
542 switch (s.fTileModeY) {
543 case SkShader::kClamp_TileMode:
fmalitad2a42712016-02-01 04:48:39 -0800544 iY2 = SkClampMax(iY2, stopY-1);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000545 break;
546 case SkShader::kRepeat_TileMode:
fmalitad2a42712016-02-01 04:48:39 -0800547 iY2 = sk_int_mod(iY2, stopY);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000548 break;
549 case SkShader::kMirror_TileMode:
550 default:
fmalitad2a42712016-02-01 04:48:39 -0800551 iY2 = sk_int_mirror(iY2, stopY);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000552 break;
553 }
554
555 SkASSERT(iY0 == iY2);
556 }
557#endif
558 }
559
reedad7ae6c2015-06-04 14:12:25 -0700560 const SkPMColor* row0 = s.fPixmap.addr32(0, iY0);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000561 SkPMColor color;
562
reed05a56472016-03-02 09:49:02 -0800563 if (kNone_SkFilterQuality != s.fFilterQuality) {
reedad7ae6c2015-06-04 14:12:25 -0700564 const SkPMColor* row1 = s.fPixmap.addr32(0, iY1);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000565
566 if (s.fAlphaScale < 256) {
567 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
568 } else {
569 Filter_32_opaque(iSubY, *row0, *row1, &color);
570 }
571 } else {
572 if (s.fAlphaScale < 256) {
573 color = SkAlphaMulQ(*row0, s.fAlphaScale);
574 } else {
575 color = *row0;
576 }
577 }
578
579 sk_memset32(colors, color, count);
580}
581
herbc7a784c2015-12-18 09:52:15 -0800582static void DoNothing_shaderproc(const void*, int x, int y,
reed@google.com6bb92bc2012-11-20 19:45:16 +0000583 SkPMColor* SK_RESTRICT colors, int count) {
584 // if we get called, the matrix is too tricky, so we just draw nothing
585 sk_memset32(colors, 0, count);
586}
587
588bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000589 SkPoint pt;
fmalita8a8eb522016-01-30 18:56:34 -0800590 const SkBitmapProcStateAutoMapper mapper(*this, 0, 0, &pt);
fmalita653c12d2016-01-30 10:06:46 -0800591
reed@google.com6bb92bc2012-11-20 19:45:16 +0000592 /*
593 * if the translate is larger than our ints, we can get random results, or
594 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
595 * negate it.
596 */
fmalita8a8eb522016-01-30 18:56:34 -0800597 const SkScalar too_big = SkIntToScalar(1 << 30);
598 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000599 return false;
600 }
601
reed@google.coma8d99302012-10-16 20:23:25 +0000602 // Since we know we're not filtered, we re-purpose these fields allow
603 // us to go from device -> src coordinates w/ just an integer add,
604 // rather than running through the inverse-matrix
fmalitabe5cfa92016-02-03 10:21:33 -0800605 fFilterOneX = mapper.intX();
606 fFilterOneY = mapper.intY();
fmalita653c12d2016-01-30 10:06:46 -0800607
reed@google.com6bb92bc2012-11-20 19:45:16 +0000608 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000609}
610
reed@google.com9a4c7462012-10-12 18:21:37 +0000611SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000612
reedad7ae6c2015-06-04 14:12:25 -0700613 if (kN32_SkColorType != fPixmap.colorType()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700614 return nullptr;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000615 }
616
617 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
618
reedad7ae6c2015-06-04 14:12:25 -0700619 if (1 == fPixmap.width() && 0 == (fInvType & ~kMask)) {
reed05a56472016-03-02 09:49:02 -0800620 if (kNone_SkFilterQuality == fFilterQuality &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000621 fInvType <= SkMatrix::kTranslate_Mask &&
622 !this->setupForTranslate()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000623 return DoNothing_shaderproc;
624 }
625 return S32_D32_constX_shaderproc;
626 }
627
reed@google.com9a4c7462012-10-12 18:21:37 +0000628 if (fAlphaScale < 256) {
halcanary96fcdcc2015-08-27 07:41:13 -0700629 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000630 }
631 if (fInvType > SkMatrix::kTranslate_Mask) {
halcanary96fcdcc2015-08-27 07:41:13 -0700632 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000633 }
reed05a56472016-03-02 09:49:02 -0800634 if (kNone_SkFilterQuality != fFilterQuality) {
halcanary96fcdcc2015-08-27 07:41:13 -0700635 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000636 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000637
reed@google.coma8d99302012-10-16 20:23:25 +0000638 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
639 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
640
641 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000642 if (this->setupForTranslate()) {
643 return Clamp_S32_D32_nofilter_trans_shaderproc;
644 }
645 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000646 }
reed@google.coma8d99302012-10-16 20:23:25 +0000647 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000648 if (this->setupForTranslate()) {
649 return Repeat_S32_D32_nofilter_trans_shaderproc;
650 }
651 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000652 }
halcanary96fcdcc2015-08-27 07:41:13 -0700653 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000654}
655
reed@android.com4c128c42009-08-14 13:54:37 +0000656///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000657
658#ifdef SK_DEBUG
659
660static void check_scale_nofilter(uint32_t bitmapXY[], int count,
661 unsigned mx, unsigned my) {
662 unsigned y = *bitmapXY++;
663 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000664
reed@google.com9fe287b2012-03-27 15:54:28 +0000665 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
666 for (int i = 0; i < count; ++i) {
667 SkASSERT(xptr[i] < mx);
668 }
669}
670
671static void check_scale_filter(uint32_t bitmapXY[], int count,
672 unsigned mx, unsigned my) {
673 uint32_t YY = *bitmapXY++;
674 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000675 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000676 SkASSERT(y0 < my);
677 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000678
reed@google.com9fe287b2012-03-27 15:54:28 +0000679 for (int i = 0; i < count; ++i) {
680 uint32_t XX = bitmapXY[i];
681 unsigned x0 = XX >> 18;
682 unsigned x1 = XX & 0x3FFF;
683 SkASSERT(x0 < mx);
684 SkASSERT(x1 < mx);
685 }
686}
687
688static void check_affine_nofilter(uint32_t bitmapXY[], int count,
689 unsigned mx, unsigned my) {
690 for (int i = 0; i < count; ++i) {
691 uint32_t XY = bitmapXY[i];
692 unsigned x = XY & 0xFFFF;
693 unsigned y = XY >> 16;
694 SkASSERT(x < mx);
695 SkASSERT(y < my);
696 }
697}
698
699static void check_affine_filter(uint32_t bitmapXY[], int count,
700 unsigned mx, unsigned my) {
701 for (int i = 0; i < count; ++i) {
702 uint32_t YY = *bitmapXY++;
703 unsigned y0 = YY >> 18;
704 unsigned y1 = YY & 0x3FFF;
705 SkASSERT(y0 < my);
706 SkASSERT(y1 < my);
707
708 uint32_t XX = *bitmapXY++;
709 unsigned x0 = XX >> 18;
710 unsigned x1 = XX & 0x3FFF;
711 SkASSERT(x0 < mx);
712 SkASSERT(x1 < mx);
713 }
714}
715
716void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
717 uint32_t bitmapXY[], int count,
718 int x, int y) {
719 SkASSERT(bitmapXY);
720 SkASSERT(count > 0);
721
722 state.fMatrixProc(state, bitmapXY, count, x, y);
723
724 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
725
726 // There are four formats possible:
727 // scale -vs- affine
728 // filter -vs- nofilter
729 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
reed05a56472016-03-02 09:49:02 -0800730 proc = state.fFilterQuality != kNone_SkFilterQuality ?
731 check_scale_filter : check_scale_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000732 } else {
reed05a56472016-03-02 09:49:02 -0800733 proc = state.fFilterQuality != kNone_SkFilterQuality ?
734 check_affine_filter : check_affine_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000735 }
reedad7ae6c2015-06-04 14:12:25 -0700736 proc(bitmapXY, count, state.fPixmap.width(), state.fPixmap.height());
reed@google.com9fe287b2012-03-27 15:54:28 +0000737}
738
739SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
740 return DebugMatrixProc;
741}
742
743#endif
744
745///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000746/*
747 The storage requirements for the different matrix procs are as follows,
748 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000749
reed@android.com4c128c42009-08-14 13:54:37 +0000750 scale/translate nofilter Y(4bytes) + N * X
751 affine/perspective nofilter N * (X Y)
752 scale/translate filter Y Y + N * (X X)
753 affine/perspective filter N * (Y Y X X)
754 */
755int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
756 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000757
758 size &= ~3; // only care about 4-byte aligned chunks
759 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
760 size -= 4; // the shared Y (or YY) coordinate
761 if (size < 0) {
762 size = 0;
763 }
reed@android.com258cb222010-04-14 13:36:33 +0000764 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000765 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000766 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000767 }
768
reed05a56472016-03-02 09:49:02 -0800769 if (fFilterQuality != kNone_SkFilterQuality) {
reed@android.com258cb222010-04-14 13:36:33 +0000770 size >>= 1;
771 }
772
773 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000774}
mtklein14e4d392014-10-23 14:35:01 -0700775
776///////////////////////
777
herbc7a784c2015-12-18 09:52:15 -0800778void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, int y,
mtklein14e4d392014-10-23 14:35:01 -0700779 SkPMColor* SK_RESTRICT dst, int count) {
herbc7a784c2015-12-18 09:52:15 -0800780 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
mtklein14e4d392014-10-23 14:35:01 -0700781 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
782 SkMatrix::kScale_Mask)) == 0);
783
reedad7ae6c2015-06-04 14:12:25 -0700784 const unsigned maxX = s.fPixmap.width() - 1;
mtklein14e4d392014-10-23 14:35:01 -0700785 SkFractionalInt fx;
786 int dstY;
787 {
fmalitab3a83582016-01-04 10:28:11 -0800788 const SkBitmapProcStateAutoMapper mapper(s, x, y);
reedad7ae6c2015-06-04 14:12:25 -0700789 const unsigned maxY = s.fPixmap.height() - 1;
fmalitabe5cfa92016-02-03 10:21:33 -0800790 dstY = SkClampMax(mapper.intY(), maxY);
791 fx = mapper.fractionalIntX();
mtklein14e4d392014-10-23 14:35:01 -0700792 }
793
reedad7ae6c2015-06-04 14:12:25 -0700794 const SkPMColor* SK_RESTRICT src = s.fPixmap.addr32(0, dstY);
mtklein14e4d392014-10-23 14:35:01 -0700795 const SkFractionalInt dx = s.fInvSxFractionalInt;
796
797 // Check if we're safely inside [0...maxX] so no need to clamp each computed index.
798 //
799 if ((uint64_t)SkFractionalIntToInt(fx) <= maxX &&
800 (uint64_t)SkFractionalIntToInt(fx + dx * (count - 1)) <= maxX)
801 {
802 int count4 = count >> 2;
803 for (int i = 0; i < count4; ++i) {
804 SkPMColor src0 = src[SkFractionalIntToInt(fx)]; fx += dx;
805 SkPMColor src1 = src[SkFractionalIntToInt(fx)]; fx += dx;
806 SkPMColor src2 = src[SkFractionalIntToInt(fx)]; fx += dx;
807 SkPMColor src3 = src[SkFractionalIntToInt(fx)]; fx += dx;
808 dst[0] = src0;
809 dst[1] = src1;
810 dst[2] = src2;
811 dst[3] = src3;
812 dst += 4;
813 }
814 for (int i = (count4 << 2); i < count; ++i) {
815 unsigned index = SkFractionalIntToInt(fx);
816 SkASSERT(index <= maxX);
817 *dst++ = src[index];
818 fx += dx;
819 }
820 } else {
821 for (int i = 0; i < count; ++i) {
822 dst[i] = src[SkClampMax(SkFractionalIntToInt(fx), maxX)];
823 fx += dx;
824 }
825 }
826}
fmalita3e6be162015-12-18 09:36:18 -0800827