blob: c85a5fb9926e934451636a6709a09ec2d3316325 [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
24extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
25extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
26extern void S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
herbc7a784c2015-12-18 09:52:15 -080027extern void Clamp_S16_D16_filter_DX_shaderproc_neon(const void *, int, int, uint16_t*, int);
28extern void Repeat_S16_D16_filter_DX_shaderproc_neon(const void *, int, int, uint16_t*, int);
digit@google.com3ada0ef2012-08-13 14:06:34 +000029extern void SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
herbc7a784c2015-12-18 09:52:15 -080030extern void SI8_opaque_D32_filter_DX_shaderproc_neon(const void *, int, int, uint32_t*, int);
31extern void Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const void*, int, int, uint32_t*, int);
digit@google.com3ada0ef2012-08-13 14:06:34 +000032#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000033
herbc7a784c2015-12-18 09:52:15 -080034extern void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void*, int, int, uint32_t*, int);
mtklein14e4d392014-10-23 14:35:01 -070035
digit@google.com3ada0ef2012-08-13 14:06:34 +000036#define NAME_WRAP(x) x
37#include "SkBitmapProcState_filter.h"
38#include "SkBitmapProcState_procs.h"
reed@android.comb577b412009-10-27 17:49:32 +000039
reed7a4d8472015-09-15 13:33:58 -070040SkBitmapProcState::SkBitmapProcState(const SkBitmapProvider& provider,
41 SkShader::TileMode tmx, SkShader::TileMode tmy)
42 : fProvider(provider)
43 , fBMState(nullptr)
44{
45 fTileModeX = tmx;
46 fTileModeY = tmy;
47}
48
49SkBitmapProcState::SkBitmapProcState(const SkBitmap& bm,
50 SkShader::TileMode tmx, SkShader::TileMode tmy)
51 : fProvider(SkBitmapProvider(bm))
52 , fBMState(nullptr)
53{
54 fTileModeX = tmx;
55 fTileModeY = tmy;
56}
reed64045422015-06-04 06:31:31 -070057
58SkBitmapProcState::~SkBitmapProcState() {
59 SkInPlaceDeleteCheck(fBMState, fBMStateStorage.get());
60}
61
reed@android.coma44b4cc2009-07-16 02:03:58 +000062///////////////////////////////////////////////////////////////////////////////
63
reed@google.comee056a82013-04-18 15:33:27 +000064// true iff the matrix contains, at most, scale and translate elements
65static bool matrix_only_scale_translate(const SkMatrix& m) {
66 return m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask);
67}
68
reed@google.comc0e88e02012-10-17 21:11:56 +000069/**
70 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
71 * go ahead and treat it as if it were, so that subsequent code can go fast.
72 */
reedad7ae6c2015-06-04 14:12:25 -070073static bool just_trans_clamp(const SkMatrix& matrix, const SkPixmap& pixmap) {
reed@google.comee056a82013-04-18 15:33:27 +000074 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +000075
reed@google.comee056a82013-04-18 15:33:27 +000076 if (matrix.getType() & SkMatrix::kScale_Mask) {
reedad7ae6c2015-06-04 14:12:25 -070077 SkRect dst;
78 SkRect src = SkRect::Make(pixmap.bounds());
reed@google.comf707adc2013-04-18 15:37:14 +000079
80 // 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
87 // 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);
reedad7ae6c2015-06-04 14:12:25 -070093 return idst.width() == pixmap.width() && idst.height() == pixmap.height();
reed@google.comc0e88e02012-10-17 21:11:56 +000094 }
95 // if we got here, we're either kTranslate_Mask or identity
96 return true;
97}
98
99static bool just_trans_general(const SkMatrix& matrix) {
reed@google.comee056a82013-04-18 15:33:27 +0000100 SkASSERT(matrix_only_scale_translate(matrix));
reed@google.comc0e88e02012-10-17 21:11:56 +0000101
reed@google.comee056a82013-04-18 15:33:27 +0000102 if (matrix.getType() & SkMatrix::kScale_Mask) {
reed@google.comc0e88e02012-10-17 21:11:56 +0000103 const SkScalar tol = SK_Scalar1 / 32768;
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000104
reed@google.comc0e88e02012-10-17 21:11:56 +0000105 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
106 return false;
107 }
108 if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
109 return false;
110 }
111 }
112 // if we got here, treat us as either kTranslate_Mask or identity
113 return true;
114}
115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116static bool valid_for_filtering(unsigned dimension) {
117 // for filtering, width and height must fit in 14bits, since we use steal
118 // 2 bits from each to store our 4bit subpixel data
119 return (dimension & ~0x3FFF) == 0;
120}
121
reedf7094c42015-01-16 12:05:19 -0800122/*
123 * Analyze filter-quality and matrix, and decide how to implement that.
124 *
125 * In general, we cascade down the request level [ High ... None ]
126 * - for a given level, if we can fulfill it, fine, else
127 * - else we downgrade to the next lower level and try again.
128 * We can always fulfill requests for Low and None
129 * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
130 * and may be removed.
131 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
reedad7ae6c2015-06-04 14:12:25 -0700133 fPixmap.reset();
reed@google.comef0e3192013-09-09 13:42:39 +0000134 fInvMatrix = inv;
reed93a12152015-03-16 10:08:34 -0700135 fFilterLevel = paint.getFilterQuality();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136
reed64045422015-06-04 06:31:31 -0700137 SkDefaultBitmapController controller;
reed7a4d8472015-09-15 13:33:58 -0700138 fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(),
reed64045422015-06-04 06:31:31 -0700139 fBMStateStorage.get(), fBMStateStorage.size());
reedad7ae6c2015-06-04 14:12:25 -0700140 // Note : we allow the controller to return an empty (zero-dimension) result. Should we?
halcanary96fcdcc2015-08-27 07:41:13 -0700141 if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) {
reed64045422015-06-04 06:31:31 -0700142 return false;
reedf7094c42015-01-16 12:05:19 -0800143 }
reedad7ae6c2015-06-04 14:12:25 -0700144 fPixmap = fBMState->pixmap();
reed64045422015-06-04 06:31:31 -0700145 fInvMatrix = fBMState->invMatrix();
146 fFilterLevel = fBMState->quality();
reedad7ae6c2015-06-04 14:12:25 -0700147 SkASSERT(fPixmap.addr());
reed64045422015-06-04 06:31:31 -0700148
reed@google.comef0e3192013-09-09 13:42:39 +0000149 bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
150 bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
151 SkShader::kClamp_TileMode == fTileModeY;
skia.committer@gmail.com9a070f22013-09-10 07:01:44 +0000152
humper535e3b22014-10-27 10:32:06 -0700153 // Most of the scanline procs deal with "unit" texture coordinates, as this
154 // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate
155 // those, we divide the matrix by its dimensions here.
156 //
157 // We don't do this if we're either trivial (can ignore the matrix) or clamping
158 // in both X and Y since clamping to width,height is just as easy as to 0xFFFF.
159
160 if (!(clampClamp || trivialMatrix)) {
reedad7ae6c2015-06-04 14:12:25 -0700161 fInvMatrix.postIDiv(fPixmap.width(), fPixmap.height());
reed@google.comef0e3192013-09-09 13:42:39 +0000162 }
163
humper@google.com9c96d4b2013-07-14 01:44:59 +0000164 // Now that all possible changes to the matrix have taken place, check
165 // to see if we're really close to a no-scale matrix. If so, explicitly
166 // set it to be so. Subsequent code may inspect this matrix to choose
167 // a faster path in this case.
168
169 // This code will only execute if the matrix has some scale component;
170 // if it's already pure translate then we won't do this inversion.
171
172 if (matrix_only_scale_translate(fInvMatrix)) {
reed@google.comee056a82013-04-18 15:33:27 +0000173 SkMatrix forward;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000174 if (fInvMatrix.invert(&forward)) {
reedad7ae6c2015-06-04 14:12:25 -0700175 if (clampClamp ? just_trans_clamp(forward, fPixmap)
reedf7094c42015-01-16 12:05:19 -0800176 : just_trans_general(forward)) {
reed@google.comce1f3cc2013-01-05 14:37:48 +0000177 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
178 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
humper@google.com9c96d4b2013-07-14 01:44:59 +0000179 fInvMatrix.setTranslate(tx, ty);
reed@google.comce1f3cc2013-01-05 14:37:48 +0000180 }
reed@google.comc0e88e02012-10-17 21:11:56 +0000181 }
182 }
183
humper@google.com9c96d4b2013-07-14 01:44:59 +0000184 fInvProc = fInvMatrix.getMapXYProc();
185 fInvType = fInvMatrix.getType();
186 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX());
187 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
188 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY());
189 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190
191 fAlphaScale = SkAlpha255To256(paint.getAlpha());
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000192
halcanary96fcdcc2015-08-27 07:41:13 -0700193 fShaderProc32 = nullptr;
194 fShaderProc16 = nullptr;
195 fSampleProc32 = nullptr;
196 fSampleProc16 = nullptr;
mtklein96d68b72015-02-20 12:40:40 -0800197
humper@google.com9c96d4b2013-07-14 01:44:59 +0000198 // recompute the triviality of the matrix here because we may have
199 // changed it!
200
201 trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
202
reed93a12152015-03-16 10:08:34 -0700203 if (kLow_SkFilterQuality == fFilterLevel) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000204 // Only try bilerp if the matrix is "interesting" and
205 // the image has a suitable size.
206
207 if (fInvType <= SkMatrix::kTranslate_Mask ||
reedad7ae6c2015-06-04 14:12:25 -0700208 !valid_for_filtering(fPixmap.width() | fPixmap.height()))
reedf7094c42015-01-16 12:05:19 -0800209 {
reed93a12152015-03-16 10:08:34 -0700210 fFilterLevel = kNone_SkFilterQuality;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000211 }
212 }
213
reed26feeba2015-01-14 12:28:22 -0800214 return this->chooseScanlineProcs(trivialMatrix, clampClamp, paint);
215}
humper@google.com9c96d4b2013-07-14 01:44:59 +0000216
reed26feeba2015-01-14 12:28:22 -0800217bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp,
218 const SkPaint& paint) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000219 fMatrixProc = this->chooseMatrixProc(trivialMatrix);
halcanary96fcdcc2015-08-27 07:41:13 -0700220 // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns nullptr.
221 if (nullptr == fMatrixProc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 return false;
223 }
224
225 ///////////////////////////////////////////////////////////////////////
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000226
reedad7ae6c2015-06-04 14:12:25 -0700227 const SkAlphaType at = fPixmap.alphaType();
reed00adc752014-07-12 21:10:52 -0700228
humper@google.com9c96d4b2013-07-14 01:44:59 +0000229 // No need to do this if we're doing HQ sampling; if filter quality is
230 // still set to HQ by the time we get here, then we must have installed
mtklein@google.com0dc546c2013-08-26 16:21:35 +0000231 // the shader procs above and can skip all this.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232
reed93a12152015-03-16 10:08:34 -0700233 if (fFilterLevel < kHigh_SkFilterQuality) {
skia.committer@gmail.comfa1bd5f2013-07-13 07:00:56 +0000234
humper@google.com9c96d4b2013-07-14 01:44:59 +0000235 int index = 0;
236 if (fAlphaScale < 256) { // note: this distinction is not used for D16
237 index |= 1;
reed@android.comaa9152a2009-07-17 21:24:56 +0000238 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000239 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
240 index |= 2;
241 }
reed93a12152015-03-16 10:08:34 -0700242 if (fFilterLevel > kNone_SkFilterQuality) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000243 index |= 4;
244 }
245 // bits 3,4,5 encoding the source bitmap format
reedad7ae6c2015-06-04 14:12:25 -0700246 switch (fPixmap.colorType()) {
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000247 case kN32_SkColorType:
reed00adc752014-07-12 21:10:52 -0700248 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
249 return false;
250 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000251 index |= 0;
252 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000253 case kRGB_565_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000254 index |= 8;
255 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000256 case kIndex_8_SkColorType:
reed00adc752014-07-12 21:10:52 -0700257 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
258 return false;
259 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000260 index |= 16;
261 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000262 case kARGB_4444_SkColorType:
reed00adc752014-07-12 21:10:52 -0700263 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
264 return false;
265 }
humper@google.com9c96d4b2013-07-14 01:44:59 +0000266 index |= 24;
267 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000268 case kAlpha_8_SkColorType:
humper@google.com9c96d4b2013-07-14 01:44:59 +0000269 index |= 32;
270 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
271 break;
reed0c9b1a82015-03-17 17:44:06 -0700272 case kGray_8_SkColorType:
273 index |= 40;
274 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
275 break;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000276 default:
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000277 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
humper@google.com9c96d4b2013-07-14 01:44:59 +0000278 return false;
279 }
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000280
reed26feeba2015-01-14 12:28:22 -0800281#if !SK_ARM_NEON_IS_ALWAYS
humper@google.com9c96d4b2013-07-14 01:44:59 +0000282 static const SampleProc32 gSkBitmapProcStateSample32[] = {
283 S32_opaque_D32_nofilter_DXDY,
284 S32_alpha_D32_nofilter_DXDY,
285 S32_opaque_D32_nofilter_DX,
286 S32_alpha_D32_nofilter_DX,
287 S32_opaque_D32_filter_DXDY,
288 S32_alpha_D32_filter_DXDY,
289 S32_opaque_D32_filter_DX,
290 S32_alpha_D32_filter_DX,
skia.committer@gmail.com3e2345a2013-05-24 07:01:26 +0000291
humper@google.com9c96d4b2013-07-14 01:44:59 +0000292 S16_opaque_D32_nofilter_DXDY,
293 S16_alpha_D32_nofilter_DXDY,
294 S16_opaque_D32_nofilter_DX,
295 S16_alpha_D32_nofilter_DX,
296 S16_opaque_D32_filter_DXDY,
297 S16_alpha_D32_filter_DXDY,
298 S16_opaque_D32_filter_DX,
299 S16_alpha_D32_filter_DX,
300
301 SI8_opaque_D32_nofilter_DXDY,
302 SI8_alpha_D32_nofilter_DXDY,
303 SI8_opaque_D32_nofilter_DX,
304 SI8_alpha_D32_nofilter_DX,
305 SI8_opaque_D32_filter_DXDY,
306 SI8_alpha_D32_filter_DXDY,
307 SI8_opaque_D32_filter_DX,
308 SI8_alpha_D32_filter_DX,
309
310 S4444_opaque_D32_nofilter_DXDY,
311 S4444_alpha_D32_nofilter_DXDY,
312 S4444_opaque_D32_nofilter_DX,
313 S4444_alpha_D32_nofilter_DX,
314 S4444_opaque_D32_filter_DXDY,
315 S4444_alpha_D32_filter_DXDY,
316 S4444_opaque_D32_filter_DX,
317 S4444_alpha_D32_filter_DX,
reed0c9b1a82015-03-17 17:44:06 -0700318
humper@google.com9c96d4b2013-07-14 01:44:59 +0000319 // A8 treats alpha/opaque the same (equally efficient)
320 SA8_alpha_D32_nofilter_DXDY,
321 SA8_alpha_D32_nofilter_DXDY,
322 SA8_alpha_D32_nofilter_DX,
323 SA8_alpha_D32_nofilter_DX,
324 SA8_alpha_D32_filter_DXDY,
325 SA8_alpha_D32_filter_DXDY,
326 SA8_alpha_D32_filter_DX,
reed0c9b1a82015-03-17 17:44:06 -0700327 SA8_alpha_D32_filter_DX,
328
329 // todo: possibly specialize on opaqueness
330 SG8_alpha_D32_nofilter_DXDY,
331 SG8_alpha_D32_nofilter_DXDY,
332 SG8_alpha_D32_nofilter_DX,
333 SG8_alpha_D32_nofilter_DX,
334 SG8_alpha_D32_filter_DXDY,
335 SG8_alpha_D32_filter_DXDY,
336 SG8_alpha_D32_filter_DX,
337 SG8_alpha_D32_filter_DX
humper@google.com9c96d4b2013-07-14 01:44:59 +0000338 };
339
340 static const SampleProc16 gSkBitmapProcStateSample16[] = {
341 S32_D16_nofilter_DXDY,
342 S32_D16_nofilter_DX,
343 S32_D16_filter_DXDY,
344 S32_D16_filter_DX,
345
346 S16_D16_nofilter_DXDY,
347 S16_D16_nofilter_DX,
348 S16_D16_filter_DXDY,
349 S16_D16_filter_DX,
350
351 SI8_D16_nofilter_DXDY,
352 SI8_D16_nofilter_DX,
353 SI8_D16_filter_DXDY,
354 SI8_D16_filter_DX,
355
356 // Don't support 4444 -> 565
halcanary96fcdcc2015-08-27 07:41:13 -0700357 nullptr, nullptr, nullptr, nullptr,
humper@google.com9c96d4b2013-07-14 01:44:59 +0000358 // Don't support A8 -> 565
halcanary96fcdcc2015-08-27 07:41:13 -0700359 nullptr, nullptr, nullptr, nullptr,
reed0c9b1a82015-03-17 17:44:06 -0700360 // Don't support G8 -> 565 (but we could)
halcanary96fcdcc2015-08-27 07:41:13 -0700361 nullptr, nullptr, nullptr, nullptr
humper@google.com9c96d4b2013-07-14 01:44:59 +0000362 };
reed26feeba2015-01-14 12:28:22 -0800363#endif
humper@google.com9c96d4b2013-07-14 01:44:59 +0000364
365 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
366 index >>= 1; // shift away any opaque/alpha distinction
367 fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
368
369 // our special-case shaderprocs
370 if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
371 if (clampClamp) {
372 fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
373 } else if (SkShader::kRepeat_TileMode == fTileModeX &&
374 SkShader::kRepeat_TileMode == fTileModeY) {
375 fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
376 }
377 } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
378 fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
mtklein14e4d392014-10-23 14:35:01 -0700379 } else if (S32_opaque_D32_nofilter_DX == fSampleProc32 && clampClamp) {
380 fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000381 }
382
halcanary96fcdcc2015-08-27 07:41:13 -0700383 if (nullptr == fShaderProc32) {
humper@google.com9c96d4b2013-07-14 01:44:59 +0000384 fShaderProc32 = this->chooseShaderProc32();
385 }
humper@google.comb0889472013-07-09 21:37:14 +0000386 }
mtklein96d68b72015-02-20 12:40:40 -0800387
reed@android.comc9a1d4b2009-08-03 15:05:55 +0000388 // see if our platform has any accelerated overrides
389 this->platformProcs();
mtklein96d68b72015-02-20 12:40:40 -0800390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 return true;
392}
393
herbc7a784c2015-12-18 09:52:15 -0800394static void Clamp_S32_D32_nofilter_trans_shaderproc(const void* sIn,
reed@google.com9a4c7462012-10-12 18:21:37 +0000395 int x, int y,
396 SkPMColor* SK_RESTRICT colors,
397 int count) {
herbc7a784c2015-12-18 09:52:15 -0800398 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
reed@google.com9a4c7462012-10-12 18:21:37 +0000399 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
400 SkASSERT(s.fInvKy == 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700401 SkASSERT(count > 0 && colors != nullptr);
reed93a12152015-03-16 10:08:34 -0700402 SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000403
reedad7ae6c2015-06-04 14:12:25 -0700404 const int maxX = s.fPixmap.width() - 1;
405 const int maxY = s.fPixmap.height() - 1;
reed@google.comf7698de2012-10-12 20:50:24 +0000406 int ix = s.fFilterOneX + x;
407 int iy = SkClampMax(s.fFilterOneY + y, maxY);
408#ifdef SK_DEBUG
reed@google.com9a4c7462012-10-12 18:21:37 +0000409 {
410 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000411 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.com9a4c7462012-10-12 18:21:37 +0000412 SkIntToScalar(y) + SK_ScalarHalf, &pt);
reed@google.comf7698de2012-10-12 20:50:24 +0000413 int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
414 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000415
reed@google.comf7698de2012-10-12 20:50:24 +0000416 SkASSERT(iy == iy2);
417 SkASSERT(ix == ix2);
reed@google.com9a4c7462012-10-12 18:21:37 +0000418 }
reed@google.comf7698de2012-10-12 20:50:24 +0000419#endif
reedad7ae6c2015-06-04 14:12:25 -0700420 const SkPMColor* row = s.fPixmap.addr32(0, iy);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000421
reed@google.com9a4c7462012-10-12 18:21:37 +0000422 // clamp to the left
423 if (ix < 0) {
424 int n = SkMin32(-ix, count);
425 sk_memset32(colors, row[0], n);
426 count -= n;
427 if (0 == count) {
428 return;
429 }
430 colors += n;
431 SkASSERT(-ix == n);
432 ix = 0;
433 }
434 // copy the middle
435 if (ix <= maxX) {
436 int n = SkMin32(maxX - ix + 1, count);
437 memcpy(colors, row + ix, n * sizeof(SkPMColor));
438 count -= n;
439 if (0 == count) {
440 return;
441 }
442 colors += n;
443 }
444 SkASSERT(count > 0);
445 // clamp to the right
446 sk_memset32(colors, row[maxX], count);
447}
448
reed@google.coma8d99302012-10-16 20:23:25 +0000449static inline int sk_int_mod(int x, int n) {
450 SkASSERT(n > 0);
451 if ((unsigned)x >= (unsigned)n) {
452 if (x < 0) {
453 x = n + ~(~x % n);
454 } else {
455 x = x % n;
456 }
457 }
458 return x;
459}
460
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000461static inline int sk_int_mirror(int x, int n) {
462 x = sk_int_mod(x, 2 * n);
463 if (x >= n) {
464 x = n + ~(x - n);
465 }
466 return x;
467}
468
herbc7a784c2015-12-18 09:52:15 -0800469static void Repeat_S32_D32_nofilter_trans_shaderproc(const void* sIn,
reed@google.coma8d99302012-10-16 20:23:25 +0000470 int x, int y,
471 SkPMColor* SK_RESTRICT colors,
472 int count) {
herbc7a784c2015-12-18 09:52:15 -0800473 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
reed@google.coma8d99302012-10-16 20:23:25 +0000474 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
475 SkASSERT(s.fInvKy == 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700476 SkASSERT(count > 0 && colors != nullptr);
reed93a12152015-03-16 10:08:34 -0700477 SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000478
reedad7ae6c2015-06-04 14:12:25 -0700479 const int stopX = s.fPixmap.width();
480 const int stopY = s.fPixmap.height();
reed@google.coma8d99302012-10-16 20:23:25 +0000481 int ix = s.fFilterOneX + x;
482 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
483#ifdef SK_DEBUG
484 {
485 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000486 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
reed@google.coma8d99302012-10-16 20:23:25 +0000487 SkIntToScalar(y) + SK_ScalarHalf, &pt);
488 int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
489 int ix2 = SkScalarFloorToInt(pt.fX);
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000490
reed@google.coma8d99302012-10-16 20:23:25 +0000491 SkASSERT(iy == iy2);
492 SkASSERT(ix == ix2);
493 }
494#endif
reedad7ae6c2015-06-04 14:12:25 -0700495 const SkPMColor* row = s.fPixmap.addr32(0, iy);
reed@google.coma8d99302012-10-16 20:23:25 +0000496
497 ix = sk_int_mod(ix, stopX);
498 for (;;) {
499 int n = SkMin32(stopX - ix, count);
500 memcpy(colors, row + ix, n * sizeof(SkPMColor));
501 count -= n;
502 if (0 == count) {
503 return;
504 }
505 colors += n;
506 ix = 0;
507 }
508}
509
herbc7a784c2015-12-18 09:52:15 -0800510static void S32_D32_constX_shaderproc(const void* sIn,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000511 int x, int y,
512 SkPMColor* SK_RESTRICT colors,
513 int count) {
herbc7a784c2015-12-18 09:52:15 -0800514 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000515 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
516 SkASSERT(s.fInvKy == 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700517 SkASSERT(count > 0 && colors != nullptr);
reedad7ae6c2015-06-04 14:12:25 -0700518 SkASSERT(1 == s.fPixmap.width());
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000519
scroggo@google.comad511322013-02-22 15:50:37 +0000520 int iY0;
521 int iY1 SK_INIT_TO_AVOID_WARNING;
522 int iSubY SK_INIT_TO_AVOID_WARNING;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000523
reed93a12152015-03-16 10:08:34 -0700524 if (kNone_SkFilterQuality != s.fFilterLevel) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000525 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
526 uint32_t xy[2];
527
528 mproc(s, xy, 1, x, y);
529
530 iY0 = xy[0] >> 18;
531 iY1 = xy[0] & 0x3FFF;
532 iSubY = (xy[0] >> 14) & 0xF;
533 } else {
534 int yTemp;
535
536 if (s.fInvType > SkMatrix::kTranslate_Mask) {
537 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000538 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000539 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000540 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000541 &pt);
robertphillips@google.com1e305232013-01-22 20:29:16 +0000542 // When the matrix has a scale component the setup code in
skia.committer@gmail.com98ded842013-01-23 07:06:17 +0000543 // chooseProcs multiples the inverse matrix by the inverse of the
robertphillips@google.com1e305232013-01-22 20:29:16 +0000544 // bitmap's width and height. Since this method is going to do
545 // its own tiling and sampling we need to undo that here.
robertphillips@google.comd5077752013-01-23 00:36:02 +0000546 if (SkShader::kClamp_TileMode != s.fTileModeX ||
547 SkShader::kClamp_TileMode != s.fTileModeY) {
reedad7ae6c2015-06-04 14:12:25 -0700548 yTemp = SkScalarFloorToInt(pt.fY * s.fPixmap.height());
robertphillips@google.comd5077752013-01-23 00:36:02 +0000549 } else {
550 yTemp = SkScalarFloorToInt(pt.fY);
551 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000552 } else {
553 yTemp = s.fFilterOneY + y;
554 }
555
reedad7ae6c2015-06-04 14:12:25 -0700556 const int stopY = s.fPixmap.height();
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000557 switch (s.fTileModeY) {
558 case SkShader::kClamp_TileMode:
559 iY0 = SkClampMax(yTemp, stopY-1);
560 break;
561 case SkShader::kRepeat_TileMode:
562 iY0 = sk_int_mod(yTemp, stopY);
563 break;
564 case SkShader::kMirror_TileMode:
565 default:
566 iY0 = sk_int_mirror(yTemp, stopY);
567 break;
568 }
569
570#ifdef SK_DEBUG
571 {
572 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000573 s.fInvProc(s.fInvMatrix,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000574 SkIntToScalar(x) + SK_ScalarHalf,
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000575 SkIntToScalar(y) + SK_ScalarHalf,
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000576 &pt);
robertphillips@google.comd5077752013-01-23 00:36:02 +0000577 if (s.fInvType > SkMatrix::kTranslate_Mask &&
578 (SkShader::kClamp_TileMode != s.fTileModeX ||
579 SkShader::kClamp_TileMode != s.fTileModeY)) {
reedad7ae6c2015-06-04 14:12:25 -0700580 pt.fY *= s.fPixmap.height();
robertphillips@google.com1e305232013-01-22 20:29:16 +0000581 }
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000582 int iY2;
583
584 switch (s.fTileModeY) {
585 case SkShader::kClamp_TileMode:
586 iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
587 break;
588 case SkShader::kRepeat_TileMode:
589 iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
590 break;
591 case SkShader::kMirror_TileMode:
592 default:
593 iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
594 break;
595 }
596
597 SkASSERT(iY0 == iY2);
598 }
599#endif
600 }
601
reedad7ae6c2015-06-04 14:12:25 -0700602 const SkPMColor* row0 = s.fPixmap.addr32(0, iY0);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000603 SkPMColor color;
604
reed93a12152015-03-16 10:08:34 -0700605 if (kNone_SkFilterQuality != s.fFilterLevel) {
reedad7ae6c2015-06-04 14:12:25 -0700606 const SkPMColor* row1 = s.fPixmap.addr32(0, iY1);
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000607
608 if (s.fAlphaScale < 256) {
609 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
610 } else {
611 Filter_32_opaque(iSubY, *row0, *row1, &color);
612 }
613 } else {
614 if (s.fAlphaScale < 256) {
615 color = SkAlphaMulQ(*row0, s.fAlphaScale);
616 } else {
617 color = *row0;
618 }
619 }
620
621 sk_memset32(colors, color, count);
622}
623
herbc7a784c2015-12-18 09:52:15 -0800624static void DoNothing_shaderproc(const void*, int x, int y,
reed@google.com6bb92bc2012-11-20 19:45:16 +0000625 SkPMColor* SK_RESTRICT colors, int count) {
626 // if we get called, the matrix is too tricky, so we just draw nothing
627 sk_memset32(colors, 0, count);
628}
629
630bool SkBitmapProcState::setupForTranslate() {
reed@google.coma8d99302012-10-16 20:23:25 +0000631 SkPoint pt;
humper@google.com9c96d4b2013-07-14 01:44:59 +0000632 fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000633
634 /*
635 * if the translate is larger than our ints, we can get random results, or
636 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
637 * negate it.
638 */
639 const SkScalar too_big = SkIntToScalar(1 << 30);
640 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
641 return false;
642 }
643
reed@google.coma8d99302012-10-16 20:23:25 +0000644 // Since we know we're not filtered, we re-purpose these fields allow
645 // us to go from device -> src coordinates w/ just an integer add,
646 // rather than running through the inverse-matrix
647 fFilterOneX = SkScalarFloorToInt(pt.fX);
648 fFilterOneY = SkScalarFloorToInt(pt.fY);
reed@google.com6bb92bc2012-11-20 19:45:16 +0000649 return true;
reed@google.coma8d99302012-10-16 20:23:25 +0000650}
651
reed@google.com9a4c7462012-10-12 18:21:37 +0000652SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000653
reedad7ae6c2015-06-04 14:12:25 -0700654 if (kN32_SkColorType != fPixmap.colorType()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700655 return nullptr;
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000656 }
657
658 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
659
reedad7ae6c2015-06-04 14:12:25 -0700660 if (1 == fPixmap.width() && 0 == (fInvType & ~kMask)) {
reed93a12152015-03-16 10:08:34 -0700661 if (kNone_SkFilterQuality == fFilterLevel &&
humper@google.com9c96d4b2013-07-14 01:44:59 +0000662 fInvType <= SkMatrix::kTranslate_Mask &&
663 !this->setupForTranslate()) {
robertphillips@google.com8b8bf4d2013-01-18 16:38:40 +0000664 return DoNothing_shaderproc;
665 }
666 return S32_D32_constX_shaderproc;
667 }
668
reed@google.com9a4c7462012-10-12 18:21:37 +0000669 if (fAlphaScale < 256) {
halcanary96fcdcc2015-08-27 07:41:13 -0700670 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000671 }
672 if (fInvType > SkMatrix::kTranslate_Mask) {
halcanary96fcdcc2015-08-27 07:41:13 -0700673 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000674 }
reed93a12152015-03-16 10:08:34 -0700675 if (kNone_SkFilterQuality != fFilterLevel) {
halcanary96fcdcc2015-08-27 07:41:13 -0700676 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000677 }
reed@google.com9a4c7462012-10-12 18:21:37 +0000678
reed@google.coma8d99302012-10-16 20:23:25 +0000679 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
680 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
681
682 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000683 if (this->setupForTranslate()) {
684 return Clamp_S32_D32_nofilter_trans_shaderproc;
685 }
686 return DoNothing_shaderproc;
reed@google.com9a4c7462012-10-12 18:21:37 +0000687 }
reed@google.coma8d99302012-10-16 20:23:25 +0000688 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
reed@google.com6bb92bc2012-11-20 19:45:16 +0000689 if (this->setupForTranslate()) {
690 return Repeat_S32_D32_nofilter_trans_shaderproc;
691 }
692 return DoNothing_shaderproc;
reed@google.coma8d99302012-10-16 20:23:25 +0000693 }
halcanary96fcdcc2015-08-27 07:41:13 -0700694 return nullptr;
reed@google.com9a4c7462012-10-12 18:21:37 +0000695}
696
reed@android.com4c128c42009-08-14 13:54:37 +0000697///////////////////////////////////////////////////////////////////////////////
reed@google.com9fe287b2012-03-27 15:54:28 +0000698
699#ifdef SK_DEBUG
700
701static void check_scale_nofilter(uint32_t bitmapXY[], int count,
702 unsigned mx, unsigned my) {
703 unsigned y = *bitmapXY++;
704 SkASSERT(y < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000705
reed@google.com9fe287b2012-03-27 15:54:28 +0000706 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
707 for (int i = 0; i < count; ++i) {
708 SkASSERT(xptr[i] < mx);
709 }
710}
711
712static void check_scale_filter(uint32_t bitmapXY[], int count,
713 unsigned mx, unsigned my) {
714 uint32_t YY = *bitmapXY++;
715 unsigned y0 = YY >> 18;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000716 unsigned y1 = YY & 0x3FFF;
reed@google.com9fe287b2012-03-27 15:54:28 +0000717 SkASSERT(y0 < my);
718 SkASSERT(y1 < my);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000719
reed@google.com9fe287b2012-03-27 15:54:28 +0000720 for (int i = 0; i < count; ++i) {
721 uint32_t XX = bitmapXY[i];
722 unsigned x0 = XX >> 18;
723 unsigned x1 = XX & 0x3FFF;
724 SkASSERT(x0 < mx);
725 SkASSERT(x1 < mx);
726 }
727}
728
729static void check_affine_nofilter(uint32_t bitmapXY[], int count,
730 unsigned mx, unsigned my) {
731 for (int i = 0; i < count; ++i) {
732 uint32_t XY = bitmapXY[i];
733 unsigned x = XY & 0xFFFF;
734 unsigned y = XY >> 16;
735 SkASSERT(x < mx);
736 SkASSERT(y < my);
737 }
738}
739
740static void check_affine_filter(uint32_t bitmapXY[], int count,
741 unsigned mx, unsigned my) {
742 for (int i = 0; i < count; ++i) {
743 uint32_t YY = *bitmapXY++;
744 unsigned y0 = YY >> 18;
745 unsigned y1 = YY & 0x3FFF;
746 SkASSERT(y0 < my);
747 SkASSERT(y1 < my);
748
749 uint32_t XX = *bitmapXY++;
750 unsigned x0 = XX >> 18;
751 unsigned x1 = XX & 0x3FFF;
752 SkASSERT(x0 < mx);
753 SkASSERT(x1 < mx);
754 }
755}
756
757void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
758 uint32_t bitmapXY[], int count,
759 int x, int y) {
760 SkASSERT(bitmapXY);
761 SkASSERT(count > 0);
762
763 state.fMatrixProc(state, bitmapXY, count, x, y);
764
765 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
766
767 // There are four formats possible:
768 // scale -vs- affine
769 // filter -vs- nofilter
770 if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
reed93a12152015-03-16 10:08:34 -0700771 proc = state.fFilterLevel != kNone_SkFilterQuality ? check_scale_filter : check_scale_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000772 } else {
reed93a12152015-03-16 10:08:34 -0700773 proc = state.fFilterLevel != kNone_SkFilterQuality ? check_affine_filter : check_affine_nofilter;
reed@google.com9fe287b2012-03-27 15:54:28 +0000774 }
reedad7ae6c2015-06-04 14:12:25 -0700775 proc(bitmapXY, count, state.fPixmap.width(), state.fPixmap.height());
reed@google.com9fe287b2012-03-27 15:54:28 +0000776}
777
778SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
779 return DebugMatrixProc;
780}
781
782#endif
783
784///////////////////////////////////////////////////////////////////////////////
reed@android.com4c128c42009-08-14 13:54:37 +0000785/*
786 The storage requirements for the different matrix procs are as follows,
787 where each X or Y is 2 bytes, and N is the number of pixels/elements:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000788
reed@android.com4c128c42009-08-14 13:54:37 +0000789 scale/translate nofilter Y(4bytes) + N * X
790 affine/perspective nofilter N * (X Y)
791 scale/translate filter Y Y + N * (X X)
792 affine/perspective filter N * (Y Y X X)
793 */
794int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
795 int32_t size = static_cast<int32_t>(bufferSize);
reed@android.com4c128c42009-08-14 13:54:37 +0000796
797 size &= ~3; // only care about 4-byte aligned chunks
798 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
799 size -= 4; // the shared Y (or YY) coordinate
800 if (size < 0) {
801 size = 0;
802 }
reed@android.com258cb222010-04-14 13:36:33 +0000803 size >>= 1;
reed@android.com4c128c42009-08-14 13:54:37 +0000804 } else {
reed@android.com258cb222010-04-14 13:36:33 +0000805 size >>= 2;
reed@android.com4c128c42009-08-14 13:54:37 +0000806 }
807
reed93a12152015-03-16 10:08:34 -0700808 if (fFilterLevel != kNone_SkFilterQuality) {
reed@android.com258cb222010-04-14 13:36:33 +0000809 size >>= 1;
810 }
811
812 return size;
reed@android.com4c128c42009-08-14 13:54:37 +0000813}
mtklein14e4d392014-10-23 14:35:01 -0700814
815///////////////////////
816
herbc7a784c2015-12-18 09:52:15 -0800817void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, int y,
mtklein14e4d392014-10-23 14:35:01 -0700818 SkPMColor* SK_RESTRICT dst, int count) {
herbc7a784c2015-12-18 09:52:15 -0800819 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
mtklein14e4d392014-10-23 14:35:01 -0700820 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
821 SkMatrix::kScale_Mask)) == 0);
822
reedad7ae6c2015-06-04 14:12:25 -0700823 const unsigned maxX = s.fPixmap.width() - 1;
mtklein14e4d392014-10-23 14:35:01 -0700824 SkFractionalInt fx;
825 int dstY;
826 {
fmalita3e6be162015-12-18 09:36:18 -0800827 SkPoint pt;
828 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf,
829 &pt);
830 fx = SkScalarToFractionalInt(pt.fY)
831 + bitmap_sampler_inv_bias(s.fInvMatrix.getScaleY());
reedad7ae6c2015-06-04 14:12:25 -0700832 const unsigned maxY = s.fPixmap.height() - 1;
fmalita3e6be162015-12-18 09:36:18 -0800833 dstY = SkClampMax(SkFractionalIntToInt(fx), maxY);
834 fx = SkScalarToFractionalInt(pt.fX)
835 + bitmap_sampler_inv_bias(s.fInvMatrix.getScaleX());
mtklein14e4d392014-10-23 14:35:01 -0700836 }
837
reedad7ae6c2015-06-04 14:12:25 -0700838 const SkPMColor* SK_RESTRICT src = s.fPixmap.addr32(0, dstY);
mtklein14e4d392014-10-23 14:35:01 -0700839 const SkFractionalInt dx = s.fInvSxFractionalInt;
840
841 // Check if we're safely inside [0...maxX] so no need to clamp each computed index.
842 //
843 if ((uint64_t)SkFractionalIntToInt(fx) <= maxX &&
844 (uint64_t)SkFractionalIntToInt(fx + dx * (count - 1)) <= maxX)
845 {
846 int count4 = count >> 2;
847 for (int i = 0; i < count4; ++i) {
848 SkPMColor src0 = src[SkFractionalIntToInt(fx)]; fx += dx;
849 SkPMColor src1 = src[SkFractionalIntToInt(fx)]; fx += dx;
850 SkPMColor src2 = src[SkFractionalIntToInt(fx)]; fx += dx;
851 SkPMColor src3 = src[SkFractionalIntToInt(fx)]; fx += dx;
852 dst[0] = src0;
853 dst[1] = src1;
854 dst[2] = src2;
855 dst[3] = src3;
856 dst += 4;
857 }
858 for (int i = (count4 << 2); i < count; ++i) {
859 unsigned index = SkFractionalIntToInt(fx);
860 SkASSERT(index <= maxX);
861 *dst++ = src[index];
862 fx += dx;
863 }
864 } else {
865 for (int i = 0; i < count; ++i) {
866 dst[i] = src[SkClampMax(SkFractionalIntToInt(fx), maxX)];
867 fx += dx;
868 }
869 }
870}
fmalita3e6be162015-12-18 09:36:18 -0800871