1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2008 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkShader.h" 9cb93a386Sopenharmony_ci#include "include/private/SkTPin.h" 10cb93a386Sopenharmony_ci#include "include/private/SkTo.h" 11cb93a386Sopenharmony_ci#include "src/core/SkBitmapProcState.h" 12cb93a386Sopenharmony_ci#include "src/core/SkOpts.h" 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ci/* 15cb93a386Sopenharmony_ci * The decal_ functions require that 16cb93a386Sopenharmony_ci * 1. dx > 0 17cb93a386Sopenharmony_ci * 2. [fx, fx+dx, fx+2dx, fx+3dx, ... fx+(count-1)dx] are all <= maxX 18cb93a386Sopenharmony_ci * 19cb93a386Sopenharmony_ci * In addition, we use SkFractionalInt to keep more fractional precision than 20cb93a386Sopenharmony_ci * just SkFixed, so we will abort the decal_ call if dx is very small, since 21cb93a386Sopenharmony_ci * the decal_ function just operates on SkFixed. If that were changed, we could 22cb93a386Sopenharmony_ci * skip the very_small test here. 23cb93a386Sopenharmony_ci */ 24cb93a386Sopenharmony_cistatic inline bool can_truncate_to_fixed_for_decal(SkFixed fx, 25cb93a386Sopenharmony_ci SkFixed dx, 26cb93a386Sopenharmony_ci int count, unsigned max) { 27cb93a386Sopenharmony_ci SkASSERT(count > 0); 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ci // if decal_ kept SkFractionalInt precision, this would just be dx <= 0 30cb93a386Sopenharmony_ci // I just made up the 1/256. Just don't want to perceive accumulated error 31cb93a386Sopenharmony_ci // if we truncate frDx and lose its low bits. 32cb93a386Sopenharmony_ci if (dx <= SK_Fixed1 / 256) { 33cb93a386Sopenharmony_ci return false; 34cb93a386Sopenharmony_ci } 35cb93a386Sopenharmony_ci 36cb93a386Sopenharmony_ci // Note: it seems the test should be (fx <= max && lastFx <= max); but 37cb93a386Sopenharmony_ci // historically it's been a strict inequality check, and changing produces 38cb93a386Sopenharmony_ci // unexpected diffs. Further investigation is needed. 39cb93a386Sopenharmony_ci 40cb93a386Sopenharmony_ci // We cast to unsigned so we don't have to check for negative values, which 41cb93a386Sopenharmony_ci // will now appear as very large positive values, and thus fail our test! 42cb93a386Sopenharmony_ci if ((unsigned)SkFixedFloorToInt(fx) >= max) { 43cb93a386Sopenharmony_ci return false; 44cb93a386Sopenharmony_ci } 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ci // Promote to 64bit (48.16) to avoid overflow. 47cb93a386Sopenharmony_ci const uint64_t lastFx = fx + sk_64_mul(dx, count - 1); 48cb93a386Sopenharmony_ci 49cb93a386Sopenharmony_ci return SkTFitsIn<int32_t>(lastFx) && (unsigned)SkFixedFloorToInt(SkTo<int32_t>(lastFx)) < max; 50cb93a386Sopenharmony_ci} 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ci// When not filtering, we store 32-bit y, 16-bit x, 16-bit x, 16-bit x, ... 53cb93a386Sopenharmony_ci// When filtering we write out 32-bit encodings, pairing 14.4 x0 with 14-bit x1. 54cb93a386Sopenharmony_ci 55cb93a386Sopenharmony_ci// The clamp routines may try to fall into one of these unclamped decal fast-paths. 56cb93a386Sopenharmony_ci// (Only clamp works in the right coordinate space to check for decal.) 57cb93a386Sopenharmony_cistatic void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) { 58cb93a386Sopenharmony_ci // can_truncate_to_fixed_for_decal() checked only that stepping fx+=dx count-1 59cb93a386Sopenharmony_ci // times doesn't overflow fx, so we take unusual care not to step count times. 60cb93a386Sopenharmony_ci for (; count > 2; count -= 2) { 61cb93a386Sopenharmony_ci *dst++ = pack_two_shorts( (fx + 0) >> 16, 62cb93a386Sopenharmony_ci (fx + dx) >> 16); 63cb93a386Sopenharmony_ci fx += dx+dx; 64cb93a386Sopenharmony_ci } 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci SkASSERT(count <= 2); 67cb93a386Sopenharmony_ci switch (count) { 68cb93a386Sopenharmony_ci case 2: ((uint16_t*)dst)[1] = SkToU16((fx + dx) >> 16); [[fallthrough]]; 69cb93a386Sopenharmony_ci case 1: ((uint16_t*)dst)[0] = SkToU16((fx + 0) >> 16); 70cb93a386Sopenharmony_ci } 71cb93a386Sopenharmony_ci} 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ci// A generic implementation for unfiltered scale+translate, templated on tiling method. 74cb93a386Sopenharmony_citemplate <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), bool tryDecal> 75cb93a386Sopenharmony_cistatic void nofilter_scale(const SkBitmapProcState& s, 76cb93a386Sopenharmony_ci uint32_t xy[], int count, int x, int y) { 77cb93a386Sopenharmony_ci SkASSERT(s.fInvMatrix.isScaleTranslate()); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci // Write out our 32-bit y, and get our intial fx. 80cb93a386Sopenharmony_ci SkFractionalInt fx; 81cb93a386Sopenharmony_ci { 82cb93a386Sopenharmony_ci const SkBitmapProcStateAutoMapper mapper(s, x, y); 83cb93a386Sopenharmony_ci *xy++ = tiley(mapper.fixedY(), s.fPixmap.height() - 1); 84cb93a386Sopenharmony_ci fx = mapper.fractionalIntX(); 85cb93a386Sopenharmony_ci } 86cb93a386Sopenharmony_ci 87cb93a386Sopenharmony_ci const unsigned maxX = s.fPixmap.width() - 1; 88cb93a386Sopenharmony_ci if (0 == maxX) { 89cb93a386Sopenharmony_ci // If width == 1, all the x-values must refer to that pixel, and must be zero. 90cb93a386Sopenharmony_ci memset(xy, 0, count * sizeof(uint16_t)); 91cb93a386Sopenharmony_ci return; 92cb93a386Sopenharmony_ci } 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci const SkFractionalInt dx = s.fInvSxFractionalInt; 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_ci if (tryDecal) { 97cb93a386Sopenharmony_ci const SkFixed fixedFx = SkFractionalIntToFixed(fx); 98cb93a386Sopenharmony_ci const SkFixed fixedDx = SkFractionalIntToFixed(dx); 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_ci if (can_truncate_to_fixed_for_decal(fixedFx, fixedDx, count, maxX)) { 101cb93a386Sopenharmony_ci decal_nofilter_scale(xy, fixedFx, fixedDx, count); 102cb93a386Sopenharmony_ci return; 103cb93a386Sopenharmony_ci } 104cb93a386Sopenharmony_ci } 105cb93a386Sopenharmony_ci 106cb93a386Sopenharmony_ci // Remember, each x-coordinate is 16-bit. 107cb93a386Sopenharmony_ci for (; count >= 2; count -= 2) { 108cb93a386Sopenharmony_ci *xy++ = pack_two_shorts(tilex(SkFractionalIntToFixed(fx ), maxX), 109cb93a386Sopenharmony_ci tilex(SkFractionalIntToFixed(fx + dx), maxX)); 110cb93a386Sopenharmony_ci fx += dx+dx; 111cb93a386Sopenharmony_ci } 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_ci auto xx = (uint16_t*)xy; 114cb93a386Sopenharmony_ci while (count --> 0) { 115cb93a386Sopenharmony_ci *xx++ = tilex(SkFractionalIntToFixed(fx), maxX); 116cb93a386Sopenharmony_ci fx += dx; 117cb93a386Sopenharmony_ci } 118cb93a386Sopenharmony_ci} 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_citemplate <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int)> 121cb93a386Sopenharmony_cistatic void nofilter_affine(const SkBitmapProcState& s, 122cb93a386Sopenharmony_ci uint32_t xy[], int count, int x, int y) { 123cb93a386Sopenharmony_ci SkASSERT(!s.fInvMatrix.hasPerspective()); 124cb93a386Sopenharmony_ci 125cb93a386Sopenharmony_ci const SkBitmapProcStateAutoMapper mapper(s, x, y); 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_ci SkFractionalInt fx = mapper.fractionalIntX(), 128cb93a386Sopenharmony_ci fy = mapper.fractionalIntY(), 129cb93a386Sopenharmony_ci dx = s.fInvSxFractionalInt, 130cb93a386Sopenharmony_ci dy = s.fInvKyFractionalInt; 131cb93a386Sopenharmony_ci int maxX = s.fPixmap.width () - 1, 132cb93a386Sopenharmony_ci maxY = s.fPixmap.height() - 1; 133cb93a386Sopenharmony_ci 134cb93a386Sopenharmony_ci while (count --> 0) { 135cb93a386Sopenharmony_ci *xy++ = (tiley(SkFractionalIntToFixed(fy), maxY) << 16) 136cb93a386Sopenharmony_ci | (tilex(SkFractionalIntToFixed(fx), maxX) ); 137cb93a386Sopenharmony_ci fx += dx; 138cb93a386Sopenharmony_ci fy += dy; 139cb93a386Sopenharmony_ci } 140cb93a386Sopenharmony_ci} 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_ci// used when both tilex and tiley are clamp 143cb93a386Sopenharmony_ci// Extract the high four fractional bits from fx, the lerp parameter when filtering. 144cb93a386Sopenharmony_cistatic unsigned extract_low_bits_clamp_clamp(SkFixed fx, int /*max*/) { 145cb93a386Sopenharmony_ci // If we're already scaled up to by max like clamp/decal, 146cb93a386Sopenharmony_ci // just grab the high four fractional bits. 147cb93a386Sopenharmony_ci return (fx >> 12) & 0xf; 148cb93a386Sopenharmony_ci} 149cb93a386Sopenharmony_ci 150cb93a386Sopenharmony_ci//used when one of tilex and tiley is not clamp 151cb93a386Sopenharmony_cistatic unsigned extract_low_bits_general(SkFixed fx, int max) { 152cb93a386Sopenharmony_ci // In repeat or mirror fx is in [0,1], so scale up by max first. 153cb93a386Sopenharmony_ci // TODO: remove the +1 here and the -1 at the call sites... 154cb93a386Sopenharmony_ci return extract_low_bits_clamp_clamp((fx & 0xffff) * (max+1), max); 155cb93a386Sopenharmony_ci} 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_citemplate <unsigned (*tile)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int)> 158cb93a386Sopenharmony_cistatic uint32_t pack(SkFixed f, unsigned max, SkFixed one) { 159cb93a386Sopenharmony_ci uint32_t packed = tile(f, max); // low coordinate in high bits 160cb93a386Sopenharmony_ci packed = (packed << 4) | extract_low_bits(f, max); // (lerp weight _is_ coord fractional part) 161cb93a386Sopenharmony_ci packed = (packed << 14) | tile((f + one), max); // high coordinate in low bits 162cb93a386Sopenharmony_ci return packed; 163cb93a386Sopenharmony_ci} 164cb93a386Sopenharmony_ci 165cb93a386Sopenharmony_citemplate <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int), bool tryDecal> 166cb93a386Sopenharmony_cistatic void filter_scale(const SkBitmapProcState& s, 167cb93a386Sopenharmony_ci uint32_t xy[], int count, int x, int y) { 168cb93a386Sopenharmony_ci SkASSERT(s.fInvMatrix.isScaleTranslate()); 169cb93a386Sopenharmony_ci 170cb93a386Sopenharmony_ci const unsigned maxX = s.fPixmap.width() - 1; 171cb93a386Sopenharmony_ci const SkFractionalInt dx = s.fInvSxFractionalInt; 172cb93a386Sopenharmony_ci SkFractionalInt fx; 173cb93a386Sopenharmony_ci { 174cb93a386Sopenharmony_ci const SkBitmapProcStateAutoMapper mapper(s, x, y); 175cb93a386Sopenharmony_ci const unsigned maxY = s.fPixmap.height() - 1; 176cb93a386Sopenharmony_ci // compute our two Y values up front 177cb93a386Sopenharmony_ci *xy++ = pack<tiley, extract_low_bits>(mapper.fixedY(), maxY, s.fFilterOneY); 178cb93a386Sopenharmony_ci // now initialize fx 179cb93a386Sopenharmony_ci fx = mapper.fractionalIntX(); 180cb93a386Sopenharmony_ci } 181cb93a386Sopenharmony_ci 182cb93a386Sopenharmony_ci // For historical reasons we check both ends are < maxX rather than <= maxX. 183cb93a386Sopenharmony_ci // TODO: try changing this? See also can_truncate_to_fixed_for_decal(). 184cb93a386Sopenharmony_ci if (tryDecal && 185cb93a386Sopenharmony_ci (unsigned)SkFractionalIntToInt(fx ) < maxX && 186cb93a386Sopenharmony_ci (unsigned)SkFractionalIntToInt(fx + dx*(count-1)) < maxX) { 187cb93a386Sopenharmony_ci while (count --> 0) { 188cb93a386Sopenharmony_ci SkFixed fixedFx = SkFractionalIntToFixed(fx); 189cb93a386Sopenharmony_ci SkASSERT((fixedFx >> (16 + 14)) == 0); 190cb93a386Sopenharmony_ci *xy++ = (fixedFx >> 12 << 14) | ((fixedFx >> 16) + 1); 191cb93a386Sopenharmony_ci fx += dx; 192cb93a386Sopenharmony_ci } 193cb93a386Sopenharmony_ci return; 194cb93a386Sopenharmony_ci } 195cb93a386Sopenharmony_ci 196cb93a386Sopenharmony_ci while (count --> 0) { 197cb93a386Sopenharmony_ci *xy++ = pack<tilex, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, s.fFilterOneX); 198cb93a386Sopenharmony_ci fx += dx; 199cb93a386Sopenharmony_ci } 200cb93a386Sopenharmony_ci} 201cb93a386Sopenharmony_ci 202cb93a386Sopenharmony_citemplate <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int)> 203cb93a386Sopenharmony_cistatic void filter_affine(const SkBitmapProcState& s, 204cb93a386Sopenharmony_ci uint32_t xy[], int count, int x, int y) { 205cb93a386Sopenharmony_ci SkASSERT(!s.fInvMatrix.hasPerspective()); 206cb93a386Sopenharmony_ci 207cb93a386Sopenharmony_ci const SkBitmapProcStateAutoMapper mapper(s, x, y); 208cb93a386Sopenharmony_ci 209cb93a386Sopenharmony_ci SkFixed oneX = s.fFilterOneX, 210cb93a386Sopenharmony_ci oneY = s.fFilterOneY; 211cb93a386Sopenharmony_ci 212cb93a386Sopenharmony_ci SkFractionalInt fx = mapper.fractionalIntX(), 213cb93a386Sopenharmony_ci fy = mapper.fractionalIntY(), 214cb93a386Sopenharmony_ci dx = s.fInvSxFractionalInt, 215cb93a386Sopenharmony_ci dy = s.fInvKyFractionalInt; 216cb93a386Sopenharmony_ci unsigned maxX = s.fPixmap.width () - 1, 217cb93a386Sopenharmony_ci maxY = s.fPixmap.height() - 1; 218cb93a386Sopenharmony_ci while (count --> 0) { 219cb93a386Sopenharmony_ci *xy++ = pack<tiley, extract_low_bits>(SkFractionalIntToFixed(fy), maxY, oneY); 220cb93a386Sopenharmony_ci *xy++ = pack<tilex, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, oneX); 221cb93a386Sopenharmony_ci 222cb93a386Sopenharmony_ci fy += dy; 223cb93a386Sopenharmony_ci fx += dx; 224cb93a386Sopenharmony_ci } 225cb93a386Sopenharmony_ci} 226cb93a386Sopenharmony_ci 227cb93a386Sopenharmony_ci// Helper to ensure that when we shift down, we do it w/o sign-extension 228cb93a386Sopenharmony_ci// so the caller doesn't have to manually mask off the top 16 bits. 229cb93a386Sopenharmony_cistatic inline unsigned SK_USHIFT16(unsigned x) { 230cb93a386Sopenharmony_ci return x >> 16; 231cb93a386Sopenharmony_ci} 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_cistatic unsigned repeat(SkFixed fx, int max) { 234cb93a386Sopenharmony_ci SkASSERT(max < 65535); 235cb93a386Sopenharmony_ci return SK_USHIFT16((unsigned)(fx & 0xFFFF) * (max + 1)); 236cb93a386Sopenharmony_ci} 237cb93a386Sopenharmony_cistatic unsigned mirror(SkFixed fx, int max) { 238cb93a386Sopenharmony_ci SkASSERT(max < 65535); 239cb93a386Sopenharmony_ci // s is 0xFFFFFFFF if we're on an odd interval, or 0 if an even interval 240cb93a386Sopenharmony_ci SkFixed s = SkLeftShift(fx, 15) >> 31; 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_ci // This should be exactly the same as repeat(fx ^ s, max) from here on. 243cb93a386Sopenharmony_ci return SK_USHIFT16( ((fx ^ s) & 0xFFFF) * (max + 1) ); 244cb93a386Sopenharmony_ci} 245cb93a386Sopenharmony_ci 246cb93a386Sopenharmony_cistatic unsigned clamp(SkFixed fx, int max) { 247cb93a386Sopenharmony_ci return SkTPin(fx >> 16, 0, max); 248cb93a386Sopenharmony_ci} 249cb93a386Sopenharmony_ci 250cb93a386Sopenharmony_cistatic const SkBitmapProcState::MatrixProc ClampX_ClampY_Procs[] = { 251cb93a386Sopenharmony_ci nofilter_scale <clamp, clamp, true>, filter_scale <clamp, clamp, extract_low_bits_clamp_clamp, true>, 252cb93a386Sopenharmony_ci nofilter_affine<clamp, clamp>, filter_affine<clamp, clamp, extract_low_bits_clamp_clamp>, 253cb93a386Sopenharmony_ci}; 254cb93a386Sopenharmony_cistatic const SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs[] = { 255cb93a386Sopenharmony_ci nofilter_scale <repeat, repeat, false>, filter_scale <repeat, repeat, extract_low_bits_general, false>, 256cb93a386Sopenharmony_ci nofilter_affine<repeat, repeat>, filter_affine<repeat, repeat, extract_low_bits_general> 257cb93a386Sopenharmony_ci}; 258cb93a386Sopenharmony_cistatic const SkBitmapProcState::MatrixProc MirrorX_MirrorY_Procs[] = { 259cb93a386Sopenharmony_ci nofilter_scale <mirror, mirror, false>, filter_scale <mirror, mirror, extract_low_bits_general, false>, 260cb93a386Sopenharmony_ci nofilter_affine<mirror, mirror>, filter_affine<mirror, mirror, extract_low_bits_general>, 261cb93a386Sopenharmony_ci}; 262cb93a386Sopenharmony_ci 263cb93a386Sopenharmony_ci 264cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 265cb93a386Sopenharmony_ci// This next chunk has some specializations for unfiltered translate-only matrices. 266cb93a386Sopenharmony_ci 267cb93a386Sopenharmony_cistatic inline U16CPU int_clamp(int x, int n) { 268cb93a386Sopenharmony_ci if (x < 0) { x = 0; } 269cb93a386Sopenharmony_ci if (x >= n) { x = n - 1; } 270cb93a386Sopenharmony_ci return x; 271cb93a386Sopenharmony_ci} 272cb93a386Sopenharmony_ci 273cb93a386Sopenharmony_ci/* returns 0...(n-1) given any x (positive or negative). 274cb93a386Sopenharmony_ci 275cb93a386Sopenharmony_ci As an example, if n (which is always positive) is 5... 276cb93a386Sopenharmony_ci 277cb93a386Sopenharmony_ci x: -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 278cb93a386Sopenharmony_ci returns: 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 279cb93a386Sopenharmony_ci */ 280cb93a386Sopenharmony_cistatic inline int sk_int_mod(int x, int n) { 281cb93a386Sopenharmony_ci SkASSERT(n > 0); 282cb93a386Sopenharmony_ci if ((unsigned)x >= (unsigned)n) { 283cb93a386Sopenharmony_ci if (x < 0) { 284cb93a386Sopenharmony_ci x = n + ~(~x % n); 285cb93a386Sopenharmony_ci } else { 286cb93a386Sopenharmony_ci x = x % n; 287cb93a386Sopenharmony_ci } 288cb93a386Sopenharmony_ci } 289cb93a386Sopenharmony_ci return x; 290cb93a386Sopenharmony_ci} 291cb93a386Sopenharmony_ci 292cb93a386Sopenharmony_cistatic inline U16CPU int_repeat(int x, int n) { 293cb93a386Sopenharmony_ci return sk_int_mod(x, n); 294cb93a386Sopenharmony_ci} 295cb93a386Sopenharmony_ci 296cb93a386Sopenharmony_cistatic inline U16CPU int_mirror(int x, int n) { 297cb93a386Sopenharmony_ci x = sk_int_mod(x, 2 * n); 298cb93a386Sopenharmony_ci if (x >= n) { 299cb93a386Sopenharmony_ci x = n + ~(x - n); 300cb93a386Sopenharmony_ci } 301cb93a386Sopenharmony_ci return x; 302cb93a386Sopenharmony_ci} 303cb93a386Sopenharmony_ci 304cb93a386Sopenharmony_cistatic void fill_sequential(uint16_t xptr[], int pos, int count) { 305cb93a386Sopenharmony_ci while (count --> 0) { 306cb93a386Sopenharmony_ci *xptr++ = pos++; 307cb93a386Sopenharmony_ci } 308cb93a386Sopenharmony_ci} 309cb93a386Sopenharmony_ci 310cb93a386Sopenharmony_cistatic void fill_backwards(uint16_t xptr[], int pos, int count) { 311cb93a386Sopenharmony_ci while (count --> 0) { 312cb93a386Sopenharmony_ci SkASSERT(pos >= 0); 313cb93a386Sopenharmony_ci *xptr++ = pos--; 314cb93a386Sopenharmony_ci } 315cb93a386Sopenharmony_ci} 316cb93a386Sopenharmony_ci 317cb93a386Sopenharmony_citemplate< U16CPU (tiley)(int x, int n) > 318cb93a386Sopenharmony_cistatic void clampx_nofilter_trans(const SkBitmapProcState& s, 319cb93a386Sopenharmony_ci uint32_t xy[], int count, int x, int y) { 320cb93a386Sopenharmony_ci SkASSERT(s.fInvMatrix.isTranslate()); 321cb93a386Sopenharmony_ci 322cb93a386Sopenharmony_ci const SkBitmapProcStateAutoMapper mapper(s, x, y); 323cb93a386Sopenharmony_ci *xy++ = tiley(mapper.intY(), s.fPixmap.height()); 324cb93a386Sopenharmony_ci int xpos = mapper.intX(); 325cb93a386Sopenharmony_ci 326cb93a386Sopenharmony_ci const int width = s.fPixmap.width(); 327cb93a386Sopenharmony_ci if (1 == width) { 328cb93a386Sopenharmony_ci // all of the following X values must be 0 329cb93a386Sopenharmony_ci memset(xy, 0, count * sizeof(uint16_t)); 330cb93a386Sopenharmony_ci return; 331cb93a386Sopenharmony_ci } 332cb93a386Sopenharmony_ci 333cb93a386Sopenharmony_ci uint16_t* xptr = reinterpret_cast<uint16_t*>(xy); 334cb93a386Sopenharmony_ci int n; 335cb93a386Sopenharmony_ci 336cb93a386Sopenharmony_ci // fill before 0 as needed 337cb93a386Sopenharmony_ci if (xpos < 0) { 338cb93a386Sopenharmony_ci n = -xpos; 339cb93a386Sopenharmony_ci if (n > count) { 340cb93a386Sopenharmony_ci n = count; 341cb93a386Sopenharmony_ci } 342cb93a386Sopenharmony_ci memset(xptr, 0, n * sizeof(uint16_t)); 343cb93a386Sopenharmony_ci count -= n; 344cb93a386Sopenharmony_ci if (0 == count) { 345cb93a386Sopenharmony_ci return; 346cb93a386Sopenharmony_ci } 347cb93a386Sopenharmony_ci xptr += n; 348cb93a386Sopenharmony_ci xpos = 0; 349cb93a386Sopenharmony_ci } 350cb93a386Sopenharmony_ci 351cb93a386Sopenharmony_ci // fill in 0..width-1 if needed 352cb93a386Sopenharmony_ci if (xpos < width) { 353cb93a386Sopenharmony_ci n = width - xpos; 354cb93a386Sopenharmony_ci if (n > count) { 355cb93a386Sopenharmony_ci n = count; 356cb93a386Sopenharmony_ci } 357cb93a386Sopenharmony_ci fill_sequential(xptr, xpos, n); 358cb93a386Sopenharmony_ci count -= n; 359cb93a386Sopenharmony_ci if (0 == count) { 360cb93a386Sopenharmony_ci return; 361cb93a386Sopenharmony_ci } 362cb93a386Sopenharmony_ci xptr += n; 363cb93a386Sopenharmony_ci } 364cb93a386Sopenharmony_ci 365cb93a386Sopenharmony_ci // fill the remaining with the max value 366cb93a386Sopenharmony_ci sk_memset16(xptr, width - 1, count); 367cb93a386Sopenharmony_ci} 368cb93a386Sopenharmony_ci 369cb93a386Sopenharmony_citemplate< U16CPU (tiley)(int x, int n) > 370cb93a386Sopenharmony_cistatic void repeatx_nofilter_trans(const SkBitmapProcState& s, 371cb93a386Sopenharmony_ci uint32_t xy[], int count, int x, int y) { 372cb93a386Sopenharmony_ci SkASSERT(s.fInvMatrix.isTranslate()); 373cb93a386Sopenharmony_ci 374cb93a386Sopenharmony_ci const SkBitmapProcStateAutoMapper mapper(s, x, y); 375cb93a386Sopenharmony_ci *xy++ = tiley(mapper.intY(), s.fPixmap.height()); 376cb93a386Sopenharmony_ci int xpos = mapper.intX(); 377cb93a386Sopenharmony_ci 378cb93a386Sopenharmony_ci const int width = s.fPixmap.width(); 379cb93a386Sopenharmony_ci if (1 == width) { 380cb93a386Sopenharmony_ci // all of the following X values must be 0 381cb93a386Sopenharmony_ci memset(xy, 0, count * sizeof(uint16_t)); 382cb93a386Sopenharmony_ci return; 383cb93a386Sopenharmony_ci } 384cb93a386Sopenharmony_ci 385cb93a386Sopenharmony_ci uint16_t* xptr = reinterpret_cast<uint16_t*>(xy); 386cb93a386Sopenharmony_ci int start = sk_int_mod(xpos, width); 387cb93a386Sopenharmony_ci int n = width - start; 388cb93a386Sopenharmony_ci if (n > count) { 389cb93a386Sopenharmony_ci n = count; 390cb93a386Sopenharmony_ci } 391cb93a386Sopenharmony_ci fill_sequential(xptr, start, n); 392cb93a386Sopenharmony_ci xptr += n; 393cb93a386Sopenharmony_ci count -= n; 394cb93a386Sopenharmony_ci 395cb93a386Sopenharmony_ci while (count >= width) { 396cb93a386Sopenharmony_ci fill_sequential(xptr, 0, width); 397cb93a386Sopenharmony_ci xptr += width; 398cb93a386Sopenharmony_ci count -= width; 399cb93a386Sopenharmony_ci } 400cb93a386Sopenharmony_ci 401cb93a386Sopenharmony_ci if (count > 0) { 402cb93a386Sopenharmony_ci fill_sequential(xptr, 0, count); 403cb93a386Sopenharmony_ci } 404cb93a386Sopenharmony_ci} 405cb93a386Sopenharmony_ci 406cb93a386Sopenharmony_citemplate< U16CPU (tiley)(int x, int n) > 407cb93a386Sopenharmony_cistatic void mirrorx_nofilter_trans(const SkBitmapProcState& s, 408cb93a386Sopenharmony_ci uint32_t xy[], int count, int x, int y) { 409cb93a386Sopenharmony_ci SkASSERT(s.fInvMatrix.isTranslate()); 410cb93a386Sopenharmony_ci 411cb93a386Sopenharmony_ci const SkBitmapProcStateAutoMapper mapper(s, x, y); 412cb93a386Sopenharmony_ci *xy++ = tiley(mapper.intY(), s.fPixmap.height()); 413cb93a386Sopenharmony_ci int xpos = mapper.intX(); 414cb93a386Sopenharmony_ci 415cb93a386Sopenharmony_ci const int width = s.fPixmap.width(); 416cb93a386Sopenharmony_ci if (1 == width) { 417cb93a386Sopenharmony_ci // all of the following X values must be 0 418cb93a386Sopenharmony_ci memset(xy, 0, count * sizeof(uint16_t)); 419cb93a386Sopenharmony_ci return; 420cb93a386Sopenharmony_ci } 421cb93a386Sopenharmony_ci 422cb93a386Sopenharmony_ci uint16_t* xptr = reinterpret_cast<uint16_t*>(xy); 423cb93a386Sopenharmony_ci // need to know our start, and our initial phase (forward or backward) 424cb93a386Sopenharmony_ci bool forward; 425cb93a386Sopenharmony_ci int n; 426cb93a386Sopenharmony_ci int start = sk_int_mod(xpos, 2 * width); 427cb93a386Sopenharmony_ci if (start >= width) { 428cb93a386Sopenharmony_ci start = width + ~(start - width); 429cb93a386Sopenharmony_ci forward = false; 430cb93a386Sopenharmony_ci n = start + 1; // [start .. 0] 431cb93a386Sopenharmony_ci } else { 432cb93a386Sopenharmony_ci forward = true; 433cb93a386Sopenharmony_ci n = width - start; // [start .. width) 434cb93a386Sopenharmony_ci } 435cb93a386Sopenharmony_ci if (n > count) { 436cb93a386Sopenharmony_ci n = count; 437cb93a386Sopenharmony_ci } 438cb93a386Sopenharmony_ci if (forward) { 439cb93a386Sopenharmony_ci fill_sequential(xptr, start, n); 440cb93a386Sopenharmony_ci } else { 441cb93a386Sopenharmony_ci fill_backwards(xptr, start, n); 442cb93a386Sopenharmony_ci } 443cb93a386Sopenharmony_ci forward = !forward; 444cb93a386Sopenharmony_ci xptr += n; 445cb93a386Sopenharmony_ci count -= n; 446cb93a386Sopenharmony_ci 447cb93a386Sopenharmony_ci while (count >= width) { 448cb93a386Sopenharmony_ci if (forward) { 449cb93a386Sopenharmony_ci fill_sequential(xptr, 0, width); 450cb93a386Sopenharmony_ci } else { 451cb93a386Sopenharmony_ci fill_backwards(xptr, width - 1, width); 452cb93a386Sopenharmony_ci } 453cb93a386Sopenharmony_ci forward = !forward; 454cb93a386Sopenharmony_ci xptr += width; 455cb93a386Sopenharmony_ci count -= width; 456cb93a386Sopenharmony_ci } 457cb93a386Sopenharmony_ci 458cb93a386Sopenharmony_ci if (count > 0) { 459cb93a386Sopenharmony_ci if (forward) { 460cb93a386Sopenharmony_ci fill_sequential(xptr, 0, count); 461cb93a386Sopenharmony_ci } else { 462cb93a386Sopenharmony_ci fill_backwards(xptr, width - 1, count); 463cb93a386Sopenharmony_ci } 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci} 466cb93a386Sopenharmony_ci 467cb93a386Sopenharmony_ci 468cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 469cb93a386Sopenharmony_ci// The main entry point to the file, choosing between everything above. 470cb93a386Sopenharmony_ci 471cb93a386Sopenharmony_ciSkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool translate_only_matrix) { 472cb93a386Sopenharmony_ci SkASSERT(!fInvMatrix.hasPerspective()); 473cb93a386Sopenharmony_ci SkASSERT(fTileModeX != SkTileMode::kDecal); 474cb93a386Sopenharmony_ci 475cb93a386Sopenharmony_ci if( fTileModeX == fTileModeY ) { 476cb93a386Sopenharmony_ci // Check for our special case translate methods when there is no scale/affine/perspective. 477cb93a386Sopenharmony_ci if (translate_only_matrix && !fBilerp) { 478cb93a386Sopenharmony_ci switch (fTileModeX) { 479cb93a386Sopenharmony_ci default: SkASSERT(false); [[fallthrough]]; 480cb93a386Sopenharmony_ci case SkTileMode::kClamp: return clampx_nofilter_trans<int_clamp>; 481cb93a386Sopenharmony_ci case SkTileMode::kRepeat: return repeatx_nofilter_trans<int_repeat>; 482cb93a386Sopenharmony_ci case SkTileMode::kMirror: return mirrorx_nofilter_trans<int_mirror>; 483cb93a386Sopenharmony_ci } 484cb93a386Sopenharmony_ci } 485cb93a386Sopenharmony_ci 486cb93a386Sopenharmony_ci // The arrays are all [ nofilter, filter ]. 487cb93a386Sopenharmony_ci int index = fBilerp ? 1 : 0; 488cb93a386Sopenharmony_ci if (!fInvMatrix.isScaleTranslate()) { 489cb93a386Sopenharmony_ci index |= 2; 490cb93a386Sopenharmony_ci } 491cb93a386Sopenharmony_ci 492cb93a386Sopenharmony_ci if (fTileModeX == SkTileMode::kClamp) { 493cb93a386Sopenharmony_ci // clamp gets special version of filterOne, working in non-normalized space (allowing decal) 494cb93a386Sopenharmony_ci fFilterOneX = SK_Fixed1; 495cb93a386Sopenharmony_ci fFilterOneY = SK_Fixed1; 496cb93a386Sopenharmony_ci return ClampX_ClampY_Procs[index]; 497cb93a386Sopenharmony_ci } 498cb93a386Sopenharmony_ci 499cb93a386Sopenharmony_ci // all remaining procs use this form for filterOne, putting them into normalized space. 500cb93a386Sopenharmony_ci fFilterOneX = SK_Fixed1 / fPixmap.width(); 501cb93a386Sopenharmony_ci fFilterOneY = SK_Fixed1 / fPixmap.height(); 502cb93a386Sopenharmony_ci 503cb93a386Sopenharmony_ci if (fTileModeX == SkTileMode::kRepeat) { 504cb93a386Sopenharmony_ci return RepeatX_RepeatY_Procs[index]; 505cb93a386Sopenharmony_ci } 506cb93a386Sopenharmony_ci 507cb93a386Sopenharmony_ci return MirrorX_MirrorY_Procs[index]; 508cb93a386Sopenharmony_ci } 509cb93a386Sopenharmony_ci 510cb93a386Sopenharmony_ci SkASSERT(fTileModeX == fTileModeY); 511cb93a386Sopenharmony_ci return nullptr; 512cb93a386Sopenharmony_ci} 513