1/*
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 */
7
8#include "include/core/SkImageEncoder.h"
9#include "include/core/SkPaint.h"
10#include "include/core/SkShader.h"
11#include "include/private/SkColorData.h"
12#include "include/private/SkMacros.h"
13#include "src/core/SkBitmapCache.h"
14#include "src/core/SkBitmapController.h"
15#include "src/core/SkBitmapProcState.h"
16#include "src/core/SkMipMap.h"
17#include "src/core/SkOpts.h"
18#include "src/core/SkResourceCache.h"
19#include "src/core/SkUtils.h"
20
21// One-stop-shop shader for,
22// - nearest-neighbor sampling (_nofilter_),
23// - clamp tiling in X and Y both (Clamp_),
24// - with at most a scale and translate matrix (_DX_),
25// - and no extra alpha applied (_opaque_),
26// - sampling from 8888 (_S32_) and drawing to 8888 (_S32_).
27static void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, int y,
28 SkPMColor* dst, int count) {
29 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
30 SkASSERT(s.fInvMatrix.isScaleTranslate());
31 SkASSERT(s.fAlphaScale == 256);
32
33 const unsigned maxX = s.fPixmap.width() - 1;
34 SkFractionalInt fx;
35 int dstY;
36 {
37 const SkBitmapProcStateAutoMapper mapper(s, x, y);
38 const unsigned maxY = s.fPixmap.height() - 1;
39 dstY = SkTPin<int>(mapper.intY(), 0, maxY);
40 fx = mapper.fractionalIntX();
41 }
42
43 const SkPMColor* src = s.fPixmap.addr32(0, dstY);
44 const SkFractionalInt dx = s.fInvSxFractionalInt;
45
46 // Check if we're safely inside [0...maxX] so no need to clamp each computed index.
47 //
48 if ((uint64_t)SkFractionalIntToInt(fx) <= maxX &&
49 (uint64_t)SkFractionalIntToInt(fx + dx * (count - 1)) <= maxX)
50 {
51 int count4 = count >> 2;
52 for (int i = 0; i < count4; ++i) {
53 SkPMColor src0 = src[SkFractionalIntToInt(fx)]; fx += dx;
54 SkPMColor src1 = src[SkFractionalIntToInt(fx)]; fx += dx;
55 SkPMColor src2 = src[SkFractionalIntToInt(fx)]; fx += dx;
56 SkPMColor src3 = src[SkFractionalIntToInt(fx)]; fx += dx;
57 dst[0] = src0;
58 dst[1] = src1;
59 dst[2] = src2;
60 dst[3] = src3;
61 dst += 4;
62 }
63 for (int i = (count4 << 2); i < count; ++i) {
64 unsigned index = SkFractionalIntToInt(fx);
65 SkASSERT(index <= maxX);
66 *dst++ = src[index];
67 fx += dx;
68 }
69 } else {
70 for (int i = 0; i < count; ++i) {
71 dst[i] = src[SkTPin<int>(SkFractionalIntToInt(fx), 0, maxX)];
72 fx += dx;
73 }
74 }
75}
76
77static void S32_alpha_D32_nofilter_DX(const SkBitmapProcState& s,
78 const uint32_t* xy, int count, SkPMColor* colors) {
79 SkASSERT(count > 0 && colors != nullptr);
80 SkASSERT(s.fInvMatrix.isScaleTranslate());
81 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
82 SkASSERT(4 == s.fPixmap.info().bytesPerPixel());
83 SkASSERT(s.fAlphaScale <= 256);
84
85 // xy is a 32-bit y-coordinate, followed by 16-bit x-coordinates.
86 unsigned y = *xy++;
87 SkASSERT(y < (unsigned)s.fPixmap.height());
88
89 auto row = (const SkPMColor*)( (const char*)s.fPixmap.addr() + y * s.fPixmap.rowBytes() );
90
91 if (1 == s.fPixmap.width()) {
92 sk_memset32(colors, SkAlphaMulQ(row[0], s.fAlphaScale), count);
93 return;
94 }
95
96 // Step 4 xs == 2 uint32_t at a time.
97 while (count >= 4) {
98 uint32_t x01 = *xy++,
99 x23 = *xy++;
100
101 SkPMColor p0 = row[UNPACK_PRIMARY_SHORT (x01)];
102 SkPMColor p1 = row[UNPACK_SECONDARY_SHORT(x01)];
103 SkPMColor p2 = row[UNPACK_PRIMARY_SHORT (x23)];
104 SkPMColor p3 = row[UNPACK_SECONDARY_SHORT(x23)];
105
106 *colors++ = SkAlphaMulQ(p0, s.fAlphaScale);
107 *colors++ = SkAlphaMulQ(p1, s.fAlphaScale);
108 *colors++ = SkAlphaMulQ(p2, s.fAlphaScale);
109 *colors++ = SkAlphaMulQ(p3, s.fAlphaScale);
110
111 count -= 4;
112 }
113
114 // Step 1 x == 1 uint16_t at a time.
115 auto x = (const uint16_t*)xy;
116 while (count --> 0) {
117 *colors++ = SkAlphaMulQ(row[*x++], s.fAlphaScale);
118 }
119}
120
121static void S32_alpha_D32_nofilter_DXDY(const SkBitmapProcState& s,
122 const uint32_t* xy, int count, SkPMColor* colors) {
123 SkASSERT(count > 0 && colors != nullptr);
124 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
125 SkASSERT(4 == s.fPixmap.info().bytesPerPixel());
126 SkASSERT(s.fAlphaScale <= 256);
127
128 auto src = (const char*)s.fPixmap.addr();
129 size_t rb = s.fPixmap.rowBytes();
130
131 while (count --> 0) {
132 uint32_t XY = *xy++,
133 x = XY & 0xffff,
134 y = XY >> 16;
135 SkASSERT(x < (unsigned)s.fPixmap.width ());
136 SkASSERT(y < (unsigned)s.fPixmap.height());
137 *colors++ = ((const SkPMColor*)(src + y*rb))[x];
138 }
139}
140
141SkBitmapProcInfo::SkBitmapProcInfo(const SkImage_Base* image, SkTileMode tmx, SkTileMode tmy)
142 : fImage(image)
143 , fTileModeX(tmx)
144 , fTileModeY(tmy)
145 , fBMState(nullptr)
146{}
147
148SkBitmapProcInfo::~SkBitmapProcInfo() {}
149
150
151// true iff the matrix has a scale and no more than an optional translate.
152static bool matrix_only_scale_translate(const SkMatrix& m) {
153 return (m.getType() & ~SkMatrix::kTranslate_Mask) == SkMatrix::kScale_Mask;
154}
155
156/**
157 * For the purposes of drawing bitmaps, if a matrix is "almost" translate
158 * go ahead and treat it as if it were, so that subsequent code can go fast.
159 */
160static bool just_trans_general(const SkMatrix& matrix) {
161 SkASSERT(matrix_only_scale_translate(matrix));
162
163 const SkScalar tol = SK_Scalar1 / 32768;
164
165 return SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)
166 && SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol);
167}
168
169/**
170 * Determine if the matrix can be treated as integral-only-translate,
171 * for the purpose of filtering.
172 */
173static bool just_trans_integral(const SkMatrix& m) {
174 static constexpr SkScalar tol = SK_Scalar1 / 256;
175
176 return m.getType() <= SkMatrix::kTranslate_Mask
177 && SkScalarNearlyEqual(m.getTranslateX(), SkScalarRoundToScalar(m.getTranslateX()), tol)
178 && SkScalarNearlyEqual(m.getTranslateY(), SkScalarRoundToScalar(m.getTranslateY()), tol);
179}
180
181static bool valid_for_filtering(unsigned dimension) {
182 // for filtering, width and height must fit in 14bits, since we use steal
183 // 2 bits from each to store our 4bit subpixel data
184 return (dimension & ~0x3FFF) == 0;
185}
186
187bool SkBitmapProcInfo::init(const SkMatrix& inv, const SkPaint& paint) {
188 SkASSERT(!inv.hasPerspective());
189 SkASSERT(SkOpts::S32_alpha_D32_filter_DXDY || inv.isScaleTranslate());
190
191 fPixmap.reset();
192 fInvMatrix = inv;
193 fFilterQuality = paint.getFilterQuality();
194
195 fBMState = SkBitmapController::RequestBitmap(fImage, inv, paint.getFilterQuality(), &fAlloc);
196
197 // Note : we allow the controller to return an empty (zero-dimension) result. Should we?
198 if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) {
199 return false;
200 }
201 fPixmap = fBMState->pixmap();
202 fInvMatrix = fBMState->invMatrix();
203 fPaintColor = paint.getColor();
204 fFilterQuality = fBMState->quality();
205 SkASSERT(fFilterQuality <= kLow_SkFilterQuality);
206 SkASSERT(fPixmap.addr());
207
208 bool integral_translate_only = just_trans_integral(fInvMatrix);
209 if (!integral_translate_only) {
210 // Most of the scanline procs deal with "unit" texture coordinates, as this
211 // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate
212 // those, we divide the matrix by its dimensions here.
213 //
214 // We don't do this if we're either trivial (can ignore the matrix) or clamping
215 // in both X and Y since clamping to width,height is just as easy as to 0xFFFF.
216
217 if (fTileModeX != SkTileMode::kClamp || fTileModeY != SkTileMode::kClamp) {
218 SkMatrixPriv::PostIDiv(&fInvMatrix, fPixmap.width(), fPixmap.height());
219 }
220
221 // Now that all possible changes to the matrix have taken place, check
222 // to see if we're really close to a no-scale matrix. If so, explicitly
223 // set it to be so. Subsequent code may inspect this matrix to choose
224 // a faster path in this case.
225
226 // This code will only execute if the matrix has some scale component;
227 // if it's already pure translate then we won't do this inversion.
228
229 if (matrix_only_scale_translate(fInvMatrix)) {
230 SkMatrix forward;
231 if (fInvMatrix.invert(&forward) && just_trans_general(forward)) {
232 fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY());
233 }
234 }
235
236 // Recompute the flag after matrix adjustments.
237 integral_translate_only = just_trans_integral(fInvMatrix);
238 }
239
240 if (kLow_SkFilterQuality == fFilterQuality &&
241 (!valid_for_filtering(fPixmap.width() | fPixmap.height()) ||
242 integral_translate_only)) {
243 fFilterQuality = kNone_SkFilterQuality;
244 }
245
246 return true;
247}
248
249/*
250 * Analyze filter-quality and matrix, and decide how to implement that.
251 *
252 * In general, we cascade down the request level [ High ... None ]
253 * - for a given level, if we can fulfill it, fine, else
254 * - else we downgrade to the next lower level and try again.
255 * We can always fulfill requests for Low and None
256 * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
257 * and may be removed.
258 */
259bool SkBitmapProcState::chooseProcs() {
260 SkASSERT(!fInvMatrix.hasPerspective());
261 SkASSERT(SkOpts::S32_alpha_D32_filter_DXDY || fInvMatrix.isScaleTranslate());
262 SkASSERT(fPixmap.colorType() == kN32_SkColorType);
263 SkASSERT(fPixmap.alphaType() == kPremul_SkAlphaType ||
264 fPixmap.alphaType() == kOpaque_SkAlphaType);
265
266 SkASSERT(fTileModeX != SkTileMode::kDecal);
267 SkASSERT(fFilterQuality < kHigh_SkFilterQuality);
268
269 fInvProc = SkMatrixPriv::GetMapXYProc(fInvMatrix);
270 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
271 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY ());
272
273 fAlphaScale = SkAlpha255To256(SkColorGetA(fPaintColor));
274
275 bool translate_only = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
276 fMatrixProc = this->chooseMatrixProc(translate_only);
277 SkASSERT(fMatrixProc);
278
279 const bool filter = fFilterQuality > kNone_SkFilterQuality;
280 if (fInvMatrix.isScaleTranslate()) {
281 fSampleProc32 = filter ? SkOpts::S32_alpha_D32_filter_DX : S32_alpha_D32_nofilter_DX ;
282 } else {
283 fSampleProc32 = filter ? SkOpts::S32_alpha_D32_filter_DXDY : S32_alpha_D32_nofilter_DXDY;
284 }
285 SkASSERT(fSampleProc32);
286
287 // our special-case shaderprocs
288 // TODO: move this one into chooseShaderProc32() or pull all that in here.
289 if (fAlphaScale == 256
290 && fFilterQuality == kNone_SkFilterQuality
291 && SkTileMode::kClamp == fTileModeX
292 && SkTileMode::kClamp == fTileModeY
293 && fInvMatrix.isScaleTranslate()) {
294 fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc;
295 } else {
296 fShaderProc32 = this->chooseShaderProc32();
297 }
298
299 return true;
300}
301
302static void Clamp_S32_D32_nofilter_trans_shaderproc(const void* sIn,
303 int x, int y,
304 SkPMColor* colors,
305 int count) {
306 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
307 SkASSERT(s.fInvMatrix.isTranslate());
308 SkASSERT(count > 0 && colors != nullptr);
309 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
310
311 const int maxX = s.fPixmap.width() - 1;
312 const int maxY = s.fPixmap.height() - 1;
313 int ix = s.fFilterOneX + x;
314 int iy = SkTPin(s.fFilterOneY + y, 0, maxY);
315 const SkPMColor* row = s.fPixmap.addr32(0, iy);
316
317 // clamp to the left
318 if (ix < 0) {
319 int n = std::min(-ix, count);
320 sk_memset32(colors, row[0], n);
321 count -= n;
322 if (0 == count) {
323 return;
324 }
325 colors += n;
326 SkASSERT(-ix == n);
327 ix = 0;
328 }
329 // copy the middle
330 if (ix <= maxX) {
331 int n = std::min(maxX - ix + 1, count);
332 memcpy(colors, row + ix, n * sizeof(SkPMColor));
333 count -= n;
334 if (0 == count) {
335 return;
336 }
337 colors += n;
338 }
339 SkASSERT(count > 0);
340 // clamp to the right
341 sk_memset32(colors, row[maxX], count);
342}
343
344static inline int sk_int_mod(int x, int n) {
345 SkASSERT(n > 0);
346 if ((unsigned)x >= (unsigned)n) {
347 if (x < 0) {
348 x = n + ~(~x % n);
349 } else {
350 x = x % n;
351 }
352 }
353 return x;
354}
355
356static inline int sk_int_mirror(int x, int n) {
357 x = sk_int_mod(x, 2 * n);
358 if (x >= n) {
359 x = n + ~(x - n);
360 }
361 return x;
362}
363
364static void Repeat_S32_D32_nofilter_trans_shaderproc(const void* sIn,
365 int x, int y,
366 SkPMColor* colors,
367 int count) {
368 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
369 SkASSERT(s.fInvMatrix.isTranslate());
370 SkASSERT(count > 0 && colors != nullptr);
371 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality);
372
373 const int stopX = s.fPixmap.width();
374 const int stopY = s.fPixmap.height();
375 int ix = s.fFilterOneX + x;
376 int iy = sk_int_mod(s.fFilterOneY + y, stopY);
377 const SkPMColor* row = s.fPixmap.addr32(0, iy);
378
379 ix = sk_int_mod(ix, stopX);
380 for (;;) {
381 int n = std::min(stopX - ix, count);
382 memcpy(colors, row + ix, n * sizeof(SkPMColor));
383 count -= n;
384 if (0 == count) {
385 return;
386 }
387 colors += n;
388 ix = 0;
389 }
390}
391
392static inline void filter_32_alpha(unsigned t,
393 SkPMColor color0,
394 SkPMColor color1,
395 SkPMColor* dstColor,
396 unsigned alphaScale) {
397 SkASSERT((unsigned)t <= 0xF);
398 SkASSERT(alphaScale <= 256);
399
400 const uint32_t mask = 0xFF00FF;
401
402 int scale = 256 - 16*t;
403 uint32_t lo = (color0 & mask) * scale;
404 uint32_t hi = ((color0 >> 8) & mask) * scale;
405
406 scale = 16*t;
407 lo += (color1 & mask) * scale;
408 hi += ((color1 >> 8) & mask) * scale;
409
410 // TODO: if (alphaScale < 256) ...
411 lo = ((lo >> 8) & mask) * alphaScale;
412 hi = ((hi >> 8) & mask) * alphaScale;
413
414 *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
415}
416
417static void S32_D32_constX_shaderproc(const void* sIn,
418 int x, int y,
419 SkPMColor* colors,
420 int count) {
421 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn);
422 SkASSERT(s.fInvMatrix.isScaleTranslate());
423 SkASSERT(count > 0 && colors != nullptr);
424 SkASSERT(1 == s.fPixmap.width());
425
426 int iY0;
427 int iY1 SK_INIT_TO_AVOID_WARNING;
428 int iSubY SK_INIT_TO_AVOID_WARNING;
429
430 if (kNone_SkFilterQuality != s.fFilterQuality) {
431 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
432 uint32_t xy[2];
433
434 mproc(s, xy, 1, x, y);
435
436 iY0 = xy[0] >> 18;
437 iY1 = xy[0] & 0x3FFF;
438 iSubY = (xy[0] >> 14) & 0xF;
439 } else {
440 int yTemp;
441
442 if (s.fInvMatrix.isTranslate()) {
443 yTemp = s.fFilterOneY + y;
444 } else{
445 const SkBitmapProcStateAutoMapper mapper(s, x, y);
446
447 // When the matrix has a scale component the setup code in
448 // chooseProcs multiples the inverse matrix by the inverse of the
449 // bitmap's width and height. Since this method is going to do
450 // its own tiling and sampling we need to undo that here.
451 if (SkTileMode::kClamp != s.fTileModeX || SkTileMode::kClamp != s.fTileModeY) {
452 yTemp = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height());
453 } else {
454 yTemp = mapper.intY();
455 }
456 }
457
458 const int stopY = s.fPixmap.height();
459 switch (s.fTileModeY) {
460 case SkTileMode::kClamp:
461 iY0 = SkTPin(yTemp, 0, stopY-1);
462 break;
463 case SkTileMode::kRepeat:
464 iY0 = sk_int_mod(yTemp, stopY);
465 break;
466 case SkTileMode::kMirror:
467 default:
468 iY0 = sk_int_mirror(yTemp, stopY);
469 break;
470 }
471
472#ifdef SK_DEBUG
473 {
474 const SkBitmapProcStateAutoMapper mapper(s, x, y);
475 int iY2;
476
477 if (!s.fInvMatrix.isTranslate() &&
478 (SkTileMode::kClamp != s.fTileModeX || SkTileMode::kClamp != s.fTileModeY)) {
479 iY2 = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height());
480 } else {
481 iY2 = mapper.intY();
482 }
483
484 switch (s.fTileModeY) {
485 case SkTileMode::kClamp:
486 iY2 = SkTPin(iY2, 0, stopY-1);
487 break;
488 case SkTileMode::kRepeat:
489 iY2 = sk_int_mod(iY2, stopY);
490 break;
491 case SkTileMode::kMirror:
492 default:
493 iY2 = sk_int_mirror(iY2, stopY);
494 break;
495 }
496
497 SkASSERT(iY0 == iY2);
498 }
499#endif
500 }
501
502 const SkPMColor* row0 = s.fPixmap.addr32(0, iY0);
503 SkPMColor color;
504
505 if (kNone_SkFilterQuality != s.fFilterQuality) {
506 const SkPMColor* row1 = s.fPixmap.addr32(0, iY1);
507 filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
508 } else {
509 if (s.fAlphaScale < 256) {
510 color = SkAlphaMulQ(*row0, s.fAlphaScale);
511 } else {
512 color = *row0;
513 }
514 }
515
516 sk_memset32(colors, color, count);
517}
518
519static void DoNothing_shaderproc(const void*, int x, int y,
520 SkPMColor* colors, int count) {
521 // if we get called, the matrix is too tricky, so we just draw nothing
522 sk_memset32(colors, 0, count);
523}
524
525bool SkBitmapProcState::setupForTranslate() {
526 SkPoint pt;
527 const SkBitmapProcStateAutoMapper mapper(*this, 0, 0, &pt);
528
529 /*
530 * if the translate is larger than our ints, we can get random results, or
531 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't
532 * negate it.
533 */
534 const SkScalar too_big = SkIntToScalar(1 << 30);
535 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
536 return false;
537 }
538
539 // Since we know we're not filtered, we re-purpose these fields allow
540 // us to go from device -> src coordinates w/ just an integer add,
541 // rather than running through the inverse-matrix
542 fFilterOneX = mapper.intX();
543 fFilterOneY = mapper.intY();
544
545 return true;
546}
547
548SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
549
550 if (kN32_SkColorType != fPixmap.colorType()) {
551 return nullptr;
552 }
553
554 if (1 == fPixmap.width() && fInvMatrix.isScaleTranslate()) {
555 if (kNone_SkFilterQuality == fFilterQuality &&
556 fInvMatrix.isTranslate() &&
557 !this->setupForTranslate()) {
558 return DoNothing_shaderproc;
559 }
560 return S32_D32_constX_shaderproc;
561 }
562
563 if (fAlphaScale < 256) {
564 return nullptr;
565 }
566 if (!fInvMatrix.isTranslate()) {
567 return nullptr;
568 }
569 if (kNone_SkFilterQuality != fFilterQuality) {
570 return nullptr;
571 }
572
573 SkTileMode tx = fTileModeX;
574 SkTileMode ty = fTileModeY;
575
576 if (SkTileMode::kClamp == tx && SkTileMode::kClamp == ty) {
577 if (this->setupForTranslate()) {
578 return Clamp_S32_D32_nofilter_trans_shaderproc;
579 }
580 return DoNothing_shaderproc;
581 }
582 if (SkTileMode::kRepeat == tx && SkTileMode::kRepeat == ty) {
583 if (this->setupForTranslate()) {
584 return Repeat_S32_D32_nofilter_trans_shaderproc;
585 }
586 return DoNothing_shaderproc;
587 }
588 return nullptr;
589}
590
591#ifdef SK_DEBUG
592
593static void check_scale_nofilter(uint32_t bitmapXY[], int count,
594 unsigned mx, unsigned my) {
595 unsigned y = *bitmapXY++;
596 SkASSERT(y < my);
597
598 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
599 for (int i = 0; i < count; ++i) {
600 SkASSERT(xptr[i] < mx);
601 }
602}
603
604static void check_scale_filter(uint32_t bitmapXY[], int count,
605 unsigned mx, unsigned my) {
606 uint32_t YY = *bitmapXY++;
607 unsigned y0 = YY >> 18;
608 unsigned y1 = YY & 0x3FFF;
609 SkASSERT(y0 < my);
610 SkASSERT(y1 < my);
611
612 for (int i = 0; i < count; ++i) {
613 uint32_t XX = bitmapXY[i];
614 unsigned x0 = XX >> 18;
615 unsigned x1 = XX & 0x3FFF;
616 SkASSERT(x0 < mx);
617 SkASSERT(x1 < mx);
618 }
619}
620
621static void check_affine_nofilter(uint32_t bitmapXY[], int count, unsigned mx, unsigned my) {
622 for (int i = 0; i < count; ++i) {
623 uint32_t XY = bitmapXY[i];
624 unsigned x = XY & 0xFFFF;
625 unsigned y = XY >> 16;
626 SkASSERT(x < mx);
627 SkASSERT(y < my);
628 }
629}
630
631static void check_affine_filter(uint32_t bitmapXY[], int count, unsigned mx, unsigned my) {
632 for (int i = 0; i < count; ++i) {
633 uint32_t YY = *bitmapXY++;
634 unsigned y0 = YY >> 18;
635 unsigned y1 = YY & 0x3FFF;
636 SkASSERT(y0 < my);
637 SkASSERT(y1 < my);
638
639 uint32_t XX = *bitmapXY++;
640 unsigned x0 = XX >> 18;
641 unsigned x1 = XX & 0x3FFF;
642 SkASSERT(x0 < mx);
643 SkASSERT(x1 < mx);
644 }
645}
646
647void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
648 uint32_t bitmapXY[], int count,
649 int x, int y) {
650 SkASSERT(bitmapXY);
651 SkASSERT(count > 0);
652
653 state.fMatrixProc(state, bitmapXY, count, x, y);
654
655 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
656
657 const bool filter = state.fFilterQuality > kNone_SkFilterQuality;
658 if (state.fInvMatrix.isScaleTranslate()) {
659 proc = filter ? check_scale_filter : check_scale_nofilter;
660 } else {
661 proc = filter ? check_affine_filter : check_affine_nofilter;
662 }
663
664 proc(bitmapXY, count, state.fPixmap.width(), state.fPixmap.height());
665}
666
667SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
668 return DebugMatrixProc;
669}
670
671#endif
672
673/*
674 The storage requirements for the different matrix procs are as follows,
675 where each X or Y is 2 bytes, and N is the number of pixels/elements:
676
677 scale/translate nofilter Y(4bytes) + N * X
678 affine/perspective nofilter N * (X Y)
679 scale/translate filter Y Y + N * (X X)
680 affine filter N * (Y Y X X)
681 */
682int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
683 int32_t size = static_cast<int32_t>(bufferSize);
684
685 size &= ~3; // only care about 4-byte aligned chunks
686 if (fInvMatrix.isScaleTranslate()) {
687 size -= 4; // the shared Y (or YY) coordinate
688 if (size < 0) {
689 size = 0;
690 }
691 size >>= 1;
692 } else {
693 size >>= 2;
694 }
695
696 if (fFilterQuality != kNone_SkFilterQuality) {
697 size >>= 1;
698 }
699
700 return size;
701}
702
703