mistress
stb_image_resize2.h
1/* stb_image_resize2 - v2.50 - public domain image resizing
2
3 by Jeff Roberts (v2) and Jorge L Rodriguez
4 http://github.com/nothings/stb
5
6 Can be threaded with the extended API. SSE2, AVX, Neon and WASM SIMD support. Only
7 scaling and translation is supported, no rotations or shears.
8
9 COMPILING & LINKING
10 In one C/C++ file that #includes this file, do this:
11 #define STB_IMAGE_RESIZE_IMPLEMENTATION
12 before the #include. That will create the implementation in that file.
13
14 EASY API CALLS:
15 Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation, clamps to edge.
16
17 stbir_resize_uint8_srgb( input_pixels, input_w, input_h, input_stride_in_bytes,
18 output_pixels, output_w, output_h, output_stride_in_bytes,
19 pixel_layout_enum )
20
21 stbir_resize_uint8_linear( input_pixels, input_w, input_h, input_stride_in_bytes,
22 output_pixels, output_w, output_h, output_stride_in_bytes,
23 pixel_layout_enum )
24
25 stbir_resize_float_linear( input_pixels, input_w, input_h, input_stride_in_bytes,
26 output_pixels, output_w, output_h, output_stride_in_bytes,
27 pixel_layout_enum )
28
29 If you pass NULL or zero for the output_pixels, we will allocate the output buffer
30 for you and return it from the function (free with free() or STBIR_FREE).
31 As a special case, XX_stride_in_bytes of 0 means packed continuously in memory.
32
33 API LEVELS
34 There are three levels of API - easy-to-use, medium-complexity and extended-complexity.
35
36 See the "header file" section of the source for API documentation.
37
38 ADDITIONAL DOCUMENTATION
39
40 MEMORY ALLOCATION
41 By default, we use malloc and free for memory allocation. To override the
42 memory allocation, before the implementation #include, add a:
43
44 #define STBIR_MALLOC(size,user_data) ...
45 #define STBIR_FREE(ptr,user_data) ...
46
47 Each resize makes exactly one call to malloc/free (unless you use the
48 extended API where you can do one allocation for many resizes). Under
49 address sanitizer, we do separate allocations to find overread/writes.
50
51 PERFORMANCE
52 This library was written with an emphasis on performance. When testing
53 stb_image_resize with RGBA, the fastest mode is STBIR_4CHANNEL with
54 STBIR_TYPE_UINT8 pixels and CLAMPed edges (which is what many other resize
55 libs do by default). Also, make sure SIMD is turned on of course (default
56 for 64-bit targets). Avoid WRAP edge mode if you want the fastest speed.
57
58 This library also comes with profiling built-in. If you define STBIR_PROFILE,
59 you can use the advanced API and get low-level profiling information by
60 calling stbir_resize_extended_profile_info() or stbir_resize_split_profile_info()
61 after a resize.
62
63 SIMD
64 Most of the routines have optimized SSE2, AVX, AVX-512, NEON and WASM versions.
65
66 On Microsoft compilers, we automatically turn on SIMD for 64-bit x64 and
67 ARM; for 32-bit x86 and ARM, you select SIMD mode by defining STBIR_SSE2 or
68 STBIR_NEON.
69
70 For AVX and AVX2, we auto-select it by detecting the /arch:AVX
71 or /arch:AVX2 switches. You can also always manually turn SSE2, AVX or AVX2
72 support on by defining STBIR_SSE2, STBIR_AVX or STBIR_AVX2.
73
74 For AVX-512, we turn it on by /arch:AVX512 (or -march=x86-64-v4, or the
75 -mavx512f -mavx512bw -mavx512vl trio on clang and GCC), or by defining
76 STBIR_AVX512. It's mostly useful on Zen 5 or modern Intel cores that don't
77 downclock on use. Best on very large resizes.
78
79 On Linux, SSE2 and Neon is on by default for 64-bit x64 or ARM64. For 32-bit,
80 we select x86 SIMD mode by whether you have -msse2, -mavx or -mavx2 enabled
81 on the command line. For 32-bit ARM, you must pass -mfpu=neon-vfpv4 for both
82 clang and GCC, but GCC also requires an additional -mfp16-format=ieee to
83 automatically enable NEON.
84
85 On x86 platforms, you can also define STBIR_FP16C to turn on FP16C instructions
86 for converting back and forth to half-floats. This is autoselected when we
87 are using AVX2. Clang and GCC also require the -mf16c switch. ARM always uses
88 the built-in half float hardware NEON instructions.
89
90 You can also tell us to use multiply-add instructions with STBIR_USE_FMA.
91 Because x86 doesn't always have fma, we turn it off by default to maintain
92 determinism across all platforms. If you don't care about non-FMA determinism
93 and are willing to restrict yourself to more recent x86 CPUs (around the AVX
94 timeframe), then fma will give you around a 15% speedup.
95
96 You can force off SIMD in all cases by defining STBIR_NO_SIMD. You can turn
97 off AVX, AVX2 or AVX-512 specifically with STBIR_NO_AVX, STBIR_NO_AVX2 or
98 STBIR_NO_AVX512. AVX is 10% to 40% faster, and AVX2 is generally another 12%.
99
100 ALPHA CHANNEL
101 Most of the resizing functions provide the ability to control how the alpha
102 channel of an image is processed.
103
104 When alpha represents transparency, it is important that when combining
105 colors with filtering, the pixels should not be treated equally; they
106 should use a weighted average based on their alpha values. For example,
107 if a pixel is 1% opaque bright green and another pixel is 99% opaque
108 black and you average them, the average will be 50% opaque, but the
109 unweighted average and will be a middling green color, while the weighted
110 average will be nearly black. This means the unweighted version introduced
111 green energy that didn't exist in the source image.
112
113 (If you want to know why this makes sense, you can work out the math for
114 the following: consider what happens if you alpha composite a source image
115 over a fixed color and then average the output, vs. if you average the
116 source image pixels and then composite that over the same fixed color.
117 Only the weighted average produces the same result as the ground truth
118 composite-then-average result.)
119
120 Therefore, it is in general best to "alpha weight" the pixels when applying
121 filters to them. This essentially means multiplying the colors by the alpha
122 values before combining them, and then dividing by the alpha value at the
123 end.
124
125 The computer graphics industry introduced a technique called "premultiplied
126 alpha" or "associated alpha" in which image colors are stored in image files
127 already multiplied by their alpha. This saves some math when compositing,
128 and also avoids the need to divide by the alpha at the end (which is quite
129 inefficient). However, while premultiplied alpha is common in the movie CGI
130 industry, it is not commonplace in other industries like videogames, and most
131 consumer file formats are generally expected to contain not-premultiplied
132 colors. For example, Photoshop saves PNG files "unpremultiplied", and web
133 browsers like Chrome and Firefox expect PNG images to be unpremultiplied.
134
135 Note that there are three possibilities that might describe your image
136 and resize expectation:
137
138 1. images are not premultiplied, alpha weighting is desired
139 2. images are not premultiplied, alpha weighting is not desired
140 3. images are premultiplied
141
142 Both case #2 and case #3 require the exact same math: no alpha weighting
143 should be applied or removed. Only case 1 requires extra math operations;
144 the other two cases can be handled identically.
145
146 stb_image_resize expects case #1 by default, applying alpha weighting to
147 images, expecting the input images to be unpremultiplied. This is what the
148 COLOR+ALPHA buffer types tell the resizer to do.
149
150 When you use the pixel layouts STBIR_RGBA, STBIR_BGRA, STBIR_ARGB,
151 STBIR_ABGR, STBIR_RA, or STBIR_AR you are telling us that the pixels are
152 non-premultiplied. In these cases, the resizer will alpha weight the colors
153 (effectively creating the premultiplied image), do the filtering, and then
154 convert back to non-premult on exit.
155
156 When you use the pixel layouts STBIR_RGBA_PM, STBIR_BGRA_PM, STBIR_ARGB_PM,
157 STBIR_ABGR_PM, STBIR_RA_PM or STBIR_AR_PM, you are telling that the pixels
158 ARE premultiplied. In this case, the resizer doesn't have to do the
159 premultipling - it can filter directly on the input. This about twice as
160 fast as the non-premultiplied case, so it's the right option if your data is
161 already setup correctly.
162
163 When you use the pixel layout STBIR_4CHANNEL or STBIR_2CHANNEL, you are
164 telling us that there is no channel that represents transparency; it may be
165 RGB and some unrelated fourth channel that has been stored in the alpha
166 channel, but it is actually not alpha. No special processing will be
167 performed.
168
169 The difference between the generic 4 or 2 channel layouts, and the
170 specialized _PM versions is with the _PM versions you are telling us that
171 the data *is* alpha, just don't premultiply it. That's important when
172 using SRGB pixel formats, we need to know where the alpha is, because
173 it is converted linearly (rather than with the SRGB converters).
174
175 Because alpha weighting produces the same effect as premultiplying, you
176 even have the option with non-premultiplied inputs to let the resizer
177 produce a premultiplied output. Because the intially computed alpha-weighted
178 output image is effectively premultiplied, this is actually more performant
179 than the normal path which un-premultiplies the output image as a final step.
180
181 Finally, when converting both in and out of non-premulitplied space (for
182 example, when using STBIR_RGBA), we go to somewhat heroic measures to
183 ensure that areas with zero alpha value pixels get something reasonable
184 in the RGB values. If you don't care about the RGB values of zero alpha
185 pixels, you can call the stbir_set_non_pm_alpha_speed_over_quality()
186 function - this runs a premultiplied resize about 25% faster. That said,
187 when you really care about speed, using premultiplied pixels for both in
188 and out (STBIR_RGBA_PM, etc) much faster than both of these premultiplied
189 options.
190
191 PIXEL LAYOUT CONVERSION
192 The resizer can convert from some pixel layouts to others. When using the
193 stbir_set_pixel_layouts(), you can, for example, specify STBIR_RGBA
194 on input, and STBIR_ARGB on output, and it will re-organize the channels
195 during the resize. Currently, you can only convert between two pixel
196 layouts with the same number of channels.
197
198 DETERMINISM
199 We commit to being deterministic (from x64 to ARM to scalar to SIMD, etc).
200 This requires compiling with fast-math off (using at least /fp:precise).
201 Also, you must turn off fp-contracting (which turns mult+adds into fmas)!
202 We attempt to do this with pragmas, but with Clang, you usually want to add
203 -ffp-contract=off to the command line as well.
204
205 For 32-bit x86, you must use SSE and SSE2 codegen for determinism. That is,
206 if the scalar x87 unit gets used at all, we immediately lose determinism.
207 On Microsoft Visual Studio 2008 and earlier, from what we can tell there is
208 no way to be deterministic in 32-bit x86 (some x87 always leaks in, even
209 with fp:strict). On 32-bit x86 GCC, determinism requires both -msse2 and
210 -fpmath=sse.
211
212 Note that we will not be deterministic with float data containing NaNs -
213 the NaNs will propagate differently on different SIMD and platforms.
214
215 If you turn on STBIR_USE_FMA, then we will be deterministic with other
216 fma targets, but we will differ from non-fma targets (this is unavoidable,
217 because a fma isn't simply an add with a mult - it also introduces a
218 rounding difference compared to non-fma instruction sequences.
219
220 FLOAT PIXEL FORMAT RANGE
221 Any range of values can be used for the non-alpha float data that you pass
222 in (0 to 1, -1 to 1, whatever). However, if you are inputting float values
223 but *outputting* bytes or shorts, you must use a range of 0 to 1 so that we
224 scale back properly. The alpha channel must also be 0 to 1 for any format
225 that does premultiplication prior to resizing.
226
227 Note also that with float output, using filters with negative lobes, the
228 output filtered values might go slightly out of range. You can define
229 STBIR_FLOAT_LOW_CLAMP and/or STBIR_FLOAT_HIGH_CLAMP to specify the range
230 to clamp to on output, if that's important.
231
232 MAX/MIN SCALE FACTORS
233 Images can be up to 2^30 pixels wide or high (dimensions are ints, and
234 the internal pointer math is done in size_t).
235
236 The positions of the filter taps are calculated in stbir_real (double by
237 default), which is accurate all the way to that limit. When the scale
238 works out to an exact fraction (which it does for most whole image
239 resizes), the positions are calculated with 64-bit integer math and are
240 exact at any size.
241
242 If you are on a part without hardware doubles, you can define stbir_real
243 to float before you #include. Positions are then accurate to about 2^20
244 pixels and are only off by half a pixel at 2^24, so for extreme resizes
245 on those parts, do it in multiple stages (using float intermediate
246 buffers).
247
248 HUGE IMAGES AND TILING
249 Image strides are 64-bit, so the buffers themselves can be as large as
250 memory allows; widths and heights are limited to 2^30 (ints). To resize
251 an image larger than that in either direction, resize it in tiles: for
252 each output tile, point the resize at a piece of the input (the 64-bit
253 strides reach anywhere in the buffer) with the output tile's size, and
254 set the input region the tile maps to, in the piece's own coordinates,
255 with stbir_set_input_subrect. Two things keep the tiles seamless:
256
257 - the piece must extend past the region by the filter's reach on every
258 side that isn't a real image edge (the filter's support in input
259 pixels: support x input / output when downsampling, support when
260 upsampling, plus a pixel of slack), because the edge modes act at
261 the piece's edges - a tap that runs off the piece is clamped or
262 wrapped there, as if the image ended. WRAP is the awkward one: a
263 tile at a real edge needs the pixels from the opposite edge of the
264 whole image, which a piece of the buffer can't reach, so you would
265 have to copy them into place next to the piece - it is simpler to
266 use another edge mode when tiling.
267
268 - the region must be exactly the tile's share of the whole mapping
269 (tile size x whole input / whole output, at the tile's offset), so
270 every tile has the whole resize's scale and phase - a region drawn
271 from rounded numbers shifts that tile's samples against its
272 neighbors.
273
274 A region that reaches past the source is clipped to it (the output
275 pixels that would map outside are not written).
276
277 FLIPPED IMAGES
278 Stride is just the delta from one scanline to the next. This means you can
279 use a negative stride to handle inverted images (point to the final
280 scanline and use a negative stride). You can invert the input or output,
281 using negative strides.
282
283 DEFAULT FILTERS
284 For functions which don't provide explicit control over what filters to
285 use, you can change the compile-time defaults with:
286
287 #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something
288 #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something
289
290 See stbir_filter in the header-file section for the list of filters.
291
292 NEW FILTERS
293 A number of 1D filter kernels are supplied. For a list of supported
294 filters, see the stbir_filter enum. You can install your own filters by
295 using the stbir_set_filter_callbacks_new function.
296
297 PROGRESS
298 For interactive use with slow resize operations, you can use the
299 scanline callbacks in the extended API. It would have to be a *very* large
300 image resample to need progress though - we're very fast.
301
302 ASSERT
303 Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
304
305 CACHE SIZE
306 A resize is run in column slices sized so that its working memory fits
307 the L2 cache. By default, the L2 size is read with cpuid on x86 (and a L2
308 of a megabyte or more is treated as 512 KB, to keep the internal memory
309 down), and taken as 256 KB on other cpus. If that isn't right for you,
310 set stbir_l2_cache_size_override (bytes) before resizing, and it is used
311 as is; or define STBIR_ASK_WINDOWS_FOR_L2_SIZE, STBIR_ASK_LINUX_FOR_L2_SIZE
312 or STBIR_ASK_MACOS_FOR_L2_SIZE before the implementation include to ask
313 the OS instead of cpuid (see the comment above them for the headers each
314 one needs). Usually the defaults will just work fine.
315
316 THREADING
317 To resize on several threads, call stbir_build_samplers_with_splits on
318 one thread with the number of threads you'd like, then call
319 stbir_resize_extended_split on each thread with its index (and a count
320 of 1), and stbir_free_samplers when they're all done. The build returns
321 the number of splits to actually use: it is limited to how many ways
322 the resize can be split, and then to the count the build estimates
323 will still speed things up (a small image or very narrow rows don't
324 get faster with more threads, they get slower). The estimate's constants
325 are statics in the implementation that you can override if you need to.
326
327 FUTURE TODOS
328 * Add pixel layout conversions for sensible different channel counts
329 (maybe, 1->3/4, 3->4, 4->1, 3->1).
330 * Convert the reincludes to macros when we know they aren't changing.
331 * Most of our code is internally function pointers, should we compile
332 all the SIMD stuff always and dynamically dispatch?
333
334 CONTRIBUTORS
335 Jeff Roberts: 2.0 implementation, optimizations, SIMD
336 Martins Mozeiko: NEON simd, WASM simd, clang and GCC whisperer
337 Fabian Giesen: half float and srgb converters
338 Sean Barrett: API design, optimizations
339 Jorge L Rodriguez: Original 1.0 implementation
340 Aras Pranckevicius: bugfixes
341 Nathan Reed: warning fixes for 1.0
342
343 REVISIONS
344 2.50 (2026-10-07) Big performance release, especially for large and/or
345 threaded resizes! New stuff:
346 - Resizes now run in column slices sized to the L2 cache
347 (auto-detected, or set stbir_l2_cache_size_override),
348 which is 1.6-3x faster on large resizes (more when
349 threaded) and uses far less temp memory.
350 - Coefficient sets for rational scales are now shared,
351 using much less memory for these resizes (and faster).
352 - Rational downsamples now get faster dedicated kernels
353 (20-30% faster).
354 - Added horizontal gather kernels up to 17 taps, so
355 4-tap filters stay on the fast gather path down to
356 4:1 (20-30% faster than scatter path). Useful in mip
357 chain generation loops.
358 - For each resize, we adjust the offset of the internal
359 buffers to avoid 4K aliasing with the input buffer
360 (avoids random 40% slowdowns).
361 - New model for estimating vertical or horizontal first
362 mode. Much better at predicting best direction (lots
363 of resizes faster 30%-40% with this change).
364 - Added a model of thread addition improvement - if you
365 ask for 32 threads for example, the model will only use
366 the thread count that will improve performance (some
367 users were estimating this themselves - this should
368 be more accurate).
369 - 1 channel downsizes 30-40% faster (SIMD across samples).
370 - Added some new AVX kernels and optimizations.
371 - Added AVX-512 support - useful mostly just for Zen 5
372 and recent Intel cores and large resamples.
373 - Bunch of cleanup and exactness in coefficient building,
374 along with new higher precision filter callbacks - this
375 moves our image resize limits up to 2^30.
376 - Added several new filters - more Mitchell variants,
377 some Lanczos, and the Muratori filter.
378 - Point sampling now actually does point sampling when
379 downsampling (previously only upsamples were point
380 sampled, down was averaged).
381 - Added 64-bit strides so that you can resize over the
382 2^30 with manual tiling (calling resize multiple
383 times over the 64-bit area).
384 - Windows ARM64EC support (thanks navvyswethgraphics)
385 - Lots of little bugfixes in unusual corner cases.
386 NOTE THAT 2.50 IS NOT deterministic with prior versions
387 (among other improvements: coefficients calculated more
388 precisely, new v-first vs h-first decisions, etc).
389
390 2.18 (2026-03-25) fixed coefficient calculation when skipping a coefficient off
391 the left side of the window, added non-aligned access safe
392 memcpy mode for scalar path, fixed various typos, and fixed
393 define error in the float clamp output mode.
394 2.17 (2025-10-25) silly format bug in easy-to-use APIs.
395 2.16 (2025-10-21) fixed the easy-to-use APIs to allow inverted bitmaps (negative
396 strides), fix vertical filter kernel callback, fix threaded
397 gather buffer priming (and assert).
398 (thanks adipose, TainZerL, and Harrison Green)
399 2.15 (2025-07-17) fixed an assert in debug mode when using floats with input
400 callbacks, work around GCC warning when adding to null ptr
401 (thanks Johannes Spohr and Pyry Kovanen).
402 2.14 (2025-05-09) fixed a bug using downsampling gather horizontal first, and
403 scatter with vertical first.
404 2.13 (2025-02-27) fixed a bug when using input callbacks, turned off simd for
405 tiny-c, fixed some variables that should have been static,
406 fixes a bug when calculating temp memory with resizes that
407 exceed 2GB of temp memory (very large resizes).
408 2.12 (2024-10-18) fix incorrect use of user_data with STBIR_FREE
409 2.11 (2024-09-08) fix harmless asan warnings in 2-channel and 3-channel mode
410 with AVX-2, fix some weird scaling edge conditions with
411 point sample mode.
412 2.10 (2024-07-27) fix the defines GCC and mingw for loop unroll control,
413 fix MSVC 32-bit arm half float routines.
414 2.09 (2024-06-19) fix the defines for 32-bit ARM GCC builds (was selecting
415 hardware half floats).
416 2.08 (2024-06-10) fix for RGB->BGR three channel flips and add SIMD (thanks
417 to Ryan Salsbury), fix for sub-rect resizes, use the
418 pragmas to control unrolling when they are available.
419 2.07 (2024-05-24) fix for slow final split during threaded conversions of very
420 wide scanlines when downsampling (caused by extra input
421 converting), fix for wide scanline resamples with many
422 splits (int overflow), fix GCC warning.
423 2.06 (2024-02-10) fix for identical width/height 3x or more down-scaling
424 undersampling a single row on rare resize ratios (about 1%).
425 2.05 (2024-02-07) fix for 2 pixel to 1 pixel resizes with wrap (thanks Aras),
426 fix for output callback (thanks Julien Koenen).
427 2.04 (2023-11-17) fix for rare AVX bug, shadowed symbol (thanks Nikola Smiljanic).
428 2.03 (2023-11-01) ASAN and TSAN warnings fixed, minor tweaks.
429 2.00 (2023-10-10) mostly new source: new api, optimizations, simd, vertical-first, etc
430 2x-5x faster without simd, 4x-12x faster with simd,
431 in some cases, 20x to 40x faster esp resizing large to very small.
432 0.96 (2019-03-04) fixed warnings
433 0.95 (2017-07-23) fixed warnings
434 0.94 (2017-03-18) fixed warnings
435 0.93 (2017-03-03) fixed bug with certain combinations of heights
436 0.92 (2017-01-02) fix integer overflow on large (>2GB) images
437 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions
438 0.90 (2014-09-17) first released version
439
440 LICENSE
441 See end of file for license information.
442*/
443
444#if !defined(STB_IMAGE_RESIZE_DO_HORIZONTALS) && !defined(STB_IMAGE_RESIZE_DO_VERTICALS) && !defined(STB_IMAGE_RESIZE_DO_CODERS) // for internal re-includes
445
446#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE2_H
447#define STBIR_INCLUDE_STB_IMAGE_RESIZE2_H
448
449#include <stddef.h>
450#ifdef _MSC_VER
451typedef unsigned char stbir_uint8;
452typedef unsigned short stbir_uint16;
453typedef unsigned int stbir_uint32;
454typedef unsigned __int64 stbir_uint64;
455typedef __int64 stbir_int64;
456#else
457#include <stdint.h>
458typedef uint8_t stbir_uint8;
459typedef uint16_t stbir_uint16;
460typedef uint32_t stbir_uint32;
461typedef uint64_t stbir_uint64;
462typedef int64_t stbir_int64;
463#endif
464
465// we use this type for high precision floating point work - it's normally just a double
466// but you can define it to float, if you don't have double support (some old embedded
467// platforms).
468#ifndef stbir_real
469#define stbir_real double
470#endif
471
472#ifndef STBIRDEF
473#ifdef STB_IMAGE_RESIZE_STATIC
474#define STBIRDEF static
475#else
476#ifdef __cplusplus
477#define STBIRDEF extern "C"
478#else
479#define STBIRDEF extern
480#endif
481#endif
482#endif
483
484//////////////////////////////////////////////////////////////////////////////
485//// start "header file" ///////////////////////////////////////////////////
486//
487// Easy-to-use API:
488//
489// * stride is the offset between successive rows of image data
490// in memory, in bytes (64-bit, so a buffer can be larger than 2 GB).
491// specify 0 for packed continuously in memory
492// * colorspace is linear or sRGB as specified by function name
493// * Uses the default filters
494// * Uses edge mode clamped
495// * returned result is 1 for success or 0 in case of an error.
496
497
498// stbir_pixel_layout specifies:
499// number of channels
500// order of channels
501// whether color is premultiplied by alpha
502// for back compatibility, you can cast the old channel count to an stbir_pixel_layout
503typedef enum
504{
505 STBIR_1CHANNEL = 1,
506 STBIR_2CHANNEL = 2,
507 STBIR_RGB = 3, // 3-chan, with order specified (for channel flipping)
508 STBIR_BGR = 0, // 3-chan, with order specified (for channel flipping)
509 STBIR_4CHANNEL = 5,
510
511 STBIR_RGBA = 4, // alpha formats, where alpha is NOT premultiplied into color channels
512 STBIR_BGRA = 6,
513 STBIR_ARGB = 7,
514 STBIR_ABGR = 8,
515 STBIR_RA = 9,
516 STBIR_AR = 10,
517
518 STBIR_RGBA_PM = 11, // alpha formats, where alpha is premultiplied into color channels
519 STBIR_BGRA_PM = 12,
520 STBIR_ARGB_PM = 13,
521 STBIR_ABGR_PM = 14,
522 STBIR_RA_PM = 15,
523 STBIR_AR_PM = 16,
524
525 STBIR_RGBA_NO_AW = 11, // alpha formats, where NO alpha weighting is applied at all!
526 STBIR_BGRA_NO_AW = 12, // these are just synonyms for the _PM flags (which also do
527 STBIR_ARGB_NO_AW = 13, // no alpha weighting). These names just make it more clear
528 STBIR_ABGR_NO_AW = 14, // for some folks).
529 STBIR_RA_NO_AW = 15,
530 STBIR_AR_NO_AW = 16,
531
532} stbir_pixel_layout;
533
534//===============================================================
535// Simple-complexity API
536//
537// If output_pixels is NULL (0), then we will allocate the buffer and return it to you.
538//--------------------------------
539
540STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, stbir_int64 input_stride_in_bytes,
541 unsigned char *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes,
542 stbir_pixel_layout pixel_type );
543
544STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, stbir_int64 input_stride_in_bytes,
545 unsigned char *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes,
546 stbir_pixel_layout pixel_type );
547
548STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, stbir_int64 input_stride_in_bytes,
549 float *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes,
550 stbir_pixel_layout pixel_type );
551//===============================================================
552
553//===============================================================
554// Medium-complexity API
555//
556// This extends the easy-to-use API as follows:
557//
558// * Can specify the datatype - U8, U8_SRGB, U16, FLOAT, HALF_FLOAT
559// * Edge wrap can selected explicitly
560// * Filter can be selected explicitly
561//--------------------------------
562
563typedef enum
564{
565 STBIR_EDGE_CLAMP = 0,
566 STBIR_EDGE_REFLECT = 1,
567 STBIR_EDGE_WRAP = 2, // this edge mode is slower and uses more memory
568 STBIR_EDGE_ZERO = 3,
569} stbir_edge;
570
571typedef enum
572{
573 STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses
574 STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios
575 STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering
576 STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque
577 STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline (the default when upsampling)
578 STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 (the default when downsampling)
579 STBIR_FILTER_POINT_SAMPLE = 6, // Simple point sampling
580 STBIR_FILTER_MITCHELL_QUARTER = 7, // Mitchell-Netrevalli with B=1/4, C=3/8 (Robidoux), a little sharper than Mitchell
581 STBIR_FILTER_MITCHELL_SIXTH = 8, // Mitchell-Netrevalli with B=1/6, C=5/12, sharper still
582 STBIR_FILTER_MITCHELL_NEG_SIXTH = 9, // Mitchell-Netrevalli with B=-1/6, C=7/12, over-sharpening
583 STBIR_FILTER_MITCHELL_NEG_THIRD = 10, // Mitchell-Netrevalli with B=-1/3, C=2/3, more over-sharpening
584 STBIR_FILTER_LANCZOS4 = 11, // Lanczos with 2 lobes (4 taps at 1:1), sharp, some ringing on hard edges
585 STBIR_FILTER_LANCZOS5 = 12, // Lanczos with 2.5 lobes (5 taps at 1:1), very sharp, often ringy
586 STBIR_FILTER_MURATORI6 = 13, // Casey Muratori's Bink 2 kernel (1 -4 19 19 -4 1)/32: sharper than Catmull-Rom, stable under repeated half pixel shifts when using 8-bit pixels
587 STBIR_FILTER_OTHER = 14, // User callback specified (keep last - the built-ins are everything below it)
588} stbir_filter;
589
590typedef enum
591{
592 STBIR_TYPE_UINT8 = 0,
593 STBIR_TYPE_UINT8_SRGB = 1,
594 STBIR_TYPE_UINT8_SRGB_ALPHA = 2, // alpha channel, when present, should also be SRGB (this is very unusual)
595 STBIR_TYPE_UINT16 = 3,
596 STBIR_TYPE_FLOAT = 4,
597 STBIR_TYPE_HALF_FLOAT = 5
598} stbir_datatype;
599
600// medium api
601STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, stbir_int64 input_stride_in_bytes,
602 void *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes,
603 stbir_pixel_layout pixel_layout, stbir_datatype data_type,
604 stbir_edge edge, stbir_filter filter );
605//===============================================================
606
607
608//===============================================================
609// Extended-complexity API
610//
611// This API exposes all resize functionality.
612//
613// * Separate filter types for each axis
614// * Separate edge modes for each axis
615// * Separate input and output data types
616// * Can specify regions with subpixel correctness
617// * Can specify alpha flags
618// * Can specify a memory callback
619// * Can specify a callback data type for pixel input and output
620// * Can be threaded for a single resize
621// * Can be used to resize many frames without recalculating the sampler info
622//
623// Use this API as follows:
624// 1) Call the stbir_resize_init function on a local STBIR_RESIZE structure
625// 2) Call any of the stbir_set functions
626// 3) Optionally call stbir_build_samplers() if you are going to resample multiple times
627// with the same input and output dimensions (like resizing video frames)
628// 4) Resample by calling stbir_resize_extended().
629// 5) Call stbir_free_samplers() if you called stbir_build_samplers()
630//--------------------------------
631
632
633// Types:
634
635// INPUT CALLBACK: this callback is used for input scanlines
636typedef void const * stbir_input_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context );
637
638// OUTPUT CALLBACK: this callback is used for output scanlines (or pieces of scanlines - see below)
639typedef void stbir_output_callback( void const * output_ptr, int num_pixels, int x, int y, void * context );
640
641// callbacks for user installed filters (there are old and new versions, please use the new versions from now on)
642typedef float stbir__kernel_callback( float x, float scale, void * user_data );
643typedef float stbir__support_callback( float scale, void * user_data );
644typedef stbir_real stbir__kernel_callback_new( stbir_real x, stbir_real scale, void * user_data );
645typedef stbir_real stbir__support_callback_new( stbir_real scale, void * user_data );
646typedef stbir_real stbir__radius_callback_new( stbir_real scale, stbir_real inv_scale, int is_upsample, void * user_data );
647
648// internal structure with precomputed scaling
649typedef struct stbir__info stbir__info;
650
651typedef struct STBIR_RESIZE // use the stbir_resize_init and stbir_override functions to set these values for future compatibility
652{
653 void * user_data;
654 void const * input_pixels;
655 int input_w, input_h;
656 stbir_real input_s0, input_t0, input_s1, input_t1;
657 int input_s_n0, input_s_n1, input_s_d; // the input region as fractions, when it was set in pixels: s0 = n0/d, s1 = n1/d (d is 0 when set as reals)
658 int input_t_n0, input_t_n1, input_t_d;
659 stbir_input_callback * input_cb;
660 void * output_pixels;
661 int output_w, output_h;
662 int output_subx, output_suby, output_subw, output_subh;
663 stbir_output_callback * output_cb;
664 stbir_int64 input_stride_in_bytes;
665 stbir_int64 output_stride_in_bytes;
666 int splits;
667 int fast_alpha;
668 int needs_rebuild;
669 int called_alloc;
670 stbir_pixel_layout input_pixel_layout_public;
671 stbir_pixel_layout output_pixel_layout_public;
672 stbir_datatype input_data_type;
673 stbir_datatype output_data_type;
674 stbir_filter horizontal_filter, vertical_filter;
675 stbir_edge horizontal_edge, vertical_edge;
676 stbir__kernel_callback * horizontal_filter_kernel; stbir__support_callback * horizontal_filter_support; // old callback form
677 stbir__kernel_callback * vertical_filter_kernel; stbir__support_callback * vertical_filter_support;
678 stbir__kernel_callback_new * horizontal_filter_kernel_new; stbir__support_callback_new * horizontal_filter_support_new; stbir__radius_callback_new * horizontal_filter_radius;
679 stbir__kernel_callback_new * vertical_filter_kernel_new; stbir__support_callback_new * vertical_filter_support_new; stbir__radius_callback_new * vertical_filter_radius;
680 stbir__info * samplers;
681} STBIR_RESIZE;
682
683// extended complexity api
684
685
686// First off, you must ALWAYS call stbir_resize_init on your resize structure before any of the other calls!
687STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize,
688 const void *input_pixels, int input_w, int input_h, stbir_int64 input_stride_in_bytes, // stride can be zero
689 void *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes, // stride can be zero
690 stbir_pixel_layout pixel_layout, stbir_datatype data_type );
691
692//===============================================================
693// You can update these parameters any time after resize_init and there is no cost
694//--------------------------------
695
696STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type );
697STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ); // no callbacks by default
698STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ); // pass back STBIR_RESIZE* by default
699STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, stbir_int64 input_stride_in_bytes, void * output_pixels, stbir_int64 output_stride_in_bytes );
700
701//===============================================================
702
703
704//===============================================================
705// If you call any of these functions, you will trigger a sampler rebuild!
706//--------------------------------
707
708STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ); // sets new buffer layouts
709STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ); // CLAMP by default
710
711STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ); // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default
712
713// Your own filters: per axis, a kernel callback and one of two ways to give its extent (a support or a radius - both
714// on one axis returns 0; all null keeps the built-in filter). All in stbir_real (double unless user overridden to float).
715// the resizer places the taps in stbir_real and rounds each weight to float once, into the coefficient table.
716//
717// The kernel returns the weight at x, in the filter's own pixels (output pixels when downsampling, input pixels when
718// upsampling), centered on zero. It is also passed the ratio <= 1 (output / input downsampling, input / output
719// upsampling), for kernels like the box whose shape depends on it.
720//
721// The support returns the filter's radius in those same filter pixels, and is passed the same ratio. For most kernels
722// this is a constant (2 for the cubics, 1 for the triangle); the resizer converts it to input pixels itself.
723//
724// The radius is the alternative for a kernel whose extent depends on the scale: it returns the radius in INPUT pixels,
725// from scale, inv_scale and the resizer's own up / down decision (is_upsample, 1:1 included), so an exact ratio gets
726// an exact tap count (the built-in box is 0.5 * inv_scale + 0.5).
727//
728// static stbir_real my_kernel( stbir_real x, stbir_real scale, void * user ) { ... }
729// static stbir_real my_support( stbir_real scale, void * user ) { return 2.0; }
730// stbir_set_filter_callbacks_new( &resize, my_kernel, my_support, 0, my_kernel, my_support, 0 );
731STBIRDEF int stbir_set_filter_callbacks_new( STBIR_RESIZE * resize, stbir__kernel_callback_new * horizontal_filter, stbir__support_callback_new * horizontal_support, stbir__radius_callback_new * horizontal_radius, stbir__kernel_callback_new * vertical_filter, stbir__support_callback_new * vertical_support, stbir__radius_callback_new * vertical_radius );
732
733// Old version of the filter callbacks (for backwards compatibility) - please use stbir_set_filter_callbacks_new for new code.
734STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support );
735
736STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets both sub-regions (full regions by default)
737STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, stbir_real s0, stbir_real t0, stbir_real s1, stbir_real t1 ); // sets input sub-region (full region by default). a region past the image is clipped to it: the output pixels mapping outside aren't written
738STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets output sub-region (full region by default)
739
740// when inputting AND outputting non-premultiplied alpha pixels, we use a slower but higher quality technique
741// that fills the zero alpha pixel's RGB values with something plausible. If you don't care about areas of
742// zero alpha, you can call this function to get about a 25% speed improvement for STBIR_RGBA to STBIR_RGBA
743// types of resizes.
744STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality );
745//===============================================================
746
747
748//===============================================================
749// You can call build_samplers to prebuild all the internal data we need to resample.
750// Then, if you call resize_extended many times with the same resize, you only pay the
751// cost once.
752// If you do call build_samplers, you MUST call free_samplers eventually.
753//--------------------------------
754
755// This builds the samplers and does one allocation
756STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize );
757
758// You MUST call this, if you call stbir_build_samplers or stbir_build_samplers_with_splits
759STBIRDEF void stbir_free_samplers( STBIR_RESIZE * resize );
760//===============================================================
761
762
763// And this is the main function to perform the resize synchronously on one thread.
764STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize );
765
766
767//===============================================================
768// Use these functions for multithreading.
769// 1) You call stbir_build_samplers_with_splits first on the main thread
770// 2) Then stbir_resize_extended_split on each thread
771// 3) stbir_free_samplers when done on the main thread
772//--------------------------------
773
774// This will build samplers for threading.
775// You can pass in the number of threads you'd like to use (try_splits).
776// It returns the number of splits (threads) that you can call it with.
777// It might be less: when the image can't be split up that many ways, or when
778// the build's estimate says more threads wouldn't help.
779
780STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int try_splits );
781
782// This function does a split of the resizing (you call this fuction for each
783// split, on multiple threads). A split is a piece of the output resize pixel space.
784
785// Note that you MUST call stbir_build_samplers_with_splits before stbir_resize_extended_split!
786
787// Usually, you will always call stbir_resize_extended_split with split_start as the thread_index
788// and "1" for the split_count.
789// But, if you have a weird situation where you MIGHT want 8 threads, but sometimes
790// only 4 threads, you can use 0,2,4,6 for the split_start's and use "2" for the
791// split_count each time to turn in into a 4 thread resize. (This is unusual).
792
793STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count );
794//===============================================================
795
796
797//===============================================================
798// Pixel Callbacks info:
799//--------------------------------
800
801// The input callback is super flexible - it calls you with the input address
802// of the start of the input row (based on the stride and base pointer), and the
803// x position and pixel count that it needs from that row. It gives you an
804// optional_output pointer that you can fill, or you can just return your own
805// pointer into your own data.
806//
807// You can also do conversion from non-supported data types if necessary - in
808// this case, you ignore the input_ptr and just use the x and y parameters to
809// calculate your own input_ptr based on the size of each non-supported pixel.
810// (Something like the third example below.)
811//
812// You can also install just an input or just an output callback by setting the
813// callback that you don't want to zero.
814//
815// First example, progress: (getting a callback that you can monitor the progress):
816// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context )
817// {
818// percentage_done = y / input_height;
819// return ( (char*) input_ptr ) + x * pixel_width_in_bytes; // use buffer from call (input_ptr is the row start)
820// }
821//
822// Next example, copying: (copy from some other buffer or stream):
823// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context )
824// {
825// CopyOrStreamData( optional_output, other_data_src, num_pixels * pixel_width_in_bytes );
826// return optional_output; // return the optional buffer that we filled
827// }
828//
829// Third example, input another buffer without copying: (zero-copy from other buffer):
830// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context )
831// {
832// void * pixels = ( (char*) other_image_base ) + ( y * other_image_stride ) + ( x * other_pixel_width_in_bytes );
833// return pixels; // return pointer to your data without copying
834// }
835//
836//
837// The output callback is considerably simpler - it just calls you so that you can dump
838// out the output pixels. You can also convert to other output types here if you want.
839//
840// Note that the resizer can process the output in column slices (to stay in cache), so
841// you are called with pieces of scanlines: num_pixels pixels starting at output pixel x
842// on output row y. x and y are always in the full output image's pixel space (so with an
843// output sub-rect, they include the sub-rect offset). The pieces of a scanline are not
844// delivered in order, so if you want whole scanlines, assemble them yourself with x and y.
845//
846// Simple example:
847// void my_output( void const * output_ptr, int num_pixels, int x, int y, void * context )
848// {
849// memcpy( ( (char*) my_output_image ) + y * my_output_stride + x * pixel_width_in_bytes, output_ptr, num_pixels * pixel_width_in_bytes );
850// }
851//===============================================================
852
853
854
855//===============================================================
856// optional built-in profiling API
857//--------------------------------
858
859#ifdef STBIR_PROFILE
860
861typedef struct STBIR_PROFILE_INFO
862{
863 stbir_uint64 total_clocks;
864
865 // how many clocks spent (of total_clocks) in the various resize routines, along with a string description
866 // there are "resize_count" number of zones
867 stbir_uint64 clocks[ 8 ];
868 char const ** descriptions;
869
870 // count of clocks and descriptions
871 stbir_uint32 count;
872} STBIR_PROFILE_INFO;
873
874// use after calling stbir_resize_extended (or stbir_build_samplers or stbir_build_samplers_with_splits)
875STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize );
876
877// use after calling stbir_resize_extended
878STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize );
879
880// use after calling stbir_resize_extended_split
881STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize, int split_start, int split_num );
882
883//===============================================================
884
885#endif
886
887
888//// end header file /////////////////////////////////////////////////////
889#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE2_H
890
891#if defined(STB_IMAGE_RESIZE_IMPLEMENTATION) || defined(STB_IMAGE_RESIZE2_IMPLEMENTATION)
892
893#ifndef STBIR_ASSERT
894#include <assert.h>
895#define STBIR_ASSERT(x) assert(x)
896#endif
897
898#ifndef STBIR_MALLOC
899#include <stdlib.h>
900#define STBIR_MALLOC(size,user_data) ((void)(user_data), malloc(size))
901#define STBIR_FREE(ptr,user_data) ((void)(user_data), free(ptr))
902// (we used the comma operator to evaluate user_data, to avoid "unused parameter" warnings)
903#endif
904
905#ifdef _MSC_VER
906
907#define stbir__inline __forceinline
908
909#else
910
911#define stbir__inline __inline__
912
913// Clang address sanitizer
914#if defined(__has_feature)
915 #if __has_feature(address_sanitizer) || __has_feature(memory_sanitizer)
916 #ifndef STBIR__SEPARATE_ALLOCATIONS
917 #define STBIR__SEPARATE_ALLOCATIONS
918 #endif
919 #endif
920#endif
921
922#endif
923
924// GCC and MSVC
925#if defined(__SANITIZE_ADDRESS__)
926 #ifndef STBIR__SEPARATE_ALLOCATIONS
927 #define STBIR__SEPARATE_ALLOCATIONS
928 #endif
929#endif
930
931// Always turn off automatic FMA use - use STBIR_USE_FMA if you want.
932// Otherwise, this is a determinism disaster.
933#ifndef STBIR_DONT_CHANGE_FP_CONTRACT // override in case you don't want this behavior
934#if defined(_MSC_VER) && !defined(__clang__)
935#if _MSC_VER > 1200
936#pragma fp_contract(off)
937#endif
938#elif defined(__GNUC__) && !defined(__clang__)
939#pragma GCC optimize("fp-contract=off")
940#else
941#pragma STDC FP_CONTRACT OFF
942#endif
943#endif
944
945#ifdef _MSC_VER
946#define STBIR__UNUSED(v) (void)(v)
947#else
948#define STBIR__UNUSED(v) (void)sizeof(v)
949#endif
950
951#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0]))
952
953
954#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE
955#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
956#endif
957
958#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE
959#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL
960#endif
961
962
963#ifndef STBIR__HEADER_FILENAME
964#define STBIR__HEADER_FILENAME "stb_image_resize2.h"
965#endif
966
967// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types
968// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible
969typedef enum
970{
971 STBIRI_1CHANNEL = 0,
972 STBIRI_2CHANNEL = 1,
973 STBIRI_RGB = 2,
974 STBIRI_BGR = 3,
975 STBIRI_4CHANNEL = 4,
976
977 STBIRI_RGBA = 5,
978 STBIRI_BGRA = 6,
979 STBIRI_ARGB = 7,
980 STBIRI_ABGR = 8,
981 STBIRI_RA = 9,
982 STBIRI_AR = 10,
983
984 STBIRI_RGBA_PM = 11,
985 STBIRI_BGRA_PM = 12,
986 STBIRI_ARGB_PM = 13,
987 STBIRI_ABGR_PM = 14,
988 STBIRI_RA_PM = 15,
989 STBIRI_AR_PM = 16,
990} stbir_internal_pixel_layout;
991
992// define the public pixel layouts to not compile inside the implementation (to avoid accidental use)
993#define STBIR_BGR bad_dont_use_in_implementation
994#define STBIR_1CHANNEL STBIR_BGR
995#define STBIR_2CHANNEL STBIR_BGR
996#define STBIR_RGB STBIR_BGR
997#define STBIR_RGBA STBIR_BGR
998#define STBIR_4CHANNEL STBIR_BGR
999#define STBIR_BGRA STBIR_BGR
1000#define STBIR_ARGB STBIR_BGR
1001#define STBIR_ABGR STBIR_BGR
1002#define STBIR_RA STBIR_BGR
1003#define STBIR_AR STBIR_BGR
1004#define STBIR_RGBA_PM STBIR_BGR
1005#define STBIR_BGRA_PM STBIR_BGR
1006#define STBIR_ARGB_PM STBIR_BGR
1007#define STBIR_ABGR_PM STBIR_BGR
1008#define STBIR_RA_PM STBIR_BGR
1009#define STBIR_AR_PM STBIR_BGR
1010
1011// must match stbir_datatype
1012static unsigned char stbir__type_size[] = {
1013 1,1,1,2,4,2 // STBIR_TYPE_UINT8,STBIR_TYPE_UINT8_SRGB,STBIR_TYPE_UINT8_SRGB_ALPHA,STBIR_TYPE_UINT16,STBIR_TYPE_FLOAT,STBIR_TYPE_HALF_FLOAT
1014};
1015
1016// When gathering, the contributors are which source pixels contribute.
1017// When scattering, the contributors are which destination pixels are contributed to.
1018typedef struct
1019{
1020 int n0; // First contributing pixel
1021 int n1; // Last contributing pixel
1022} stbir__contributors;
1023
1024typedef struct
1025{
1026 int lowest; // First sample index for whole filter
1027 int highest; // Last sample index for whole filter
1028 int widest; // widest single set of samples for an output
1029} stbir__filter_extent_info;
1030
1031typedef struct stbir__scale_info
1032{
1033 int input_full_size;
1034 int output_sub_size;
1035 stbir_real scale;
1036 stbir_real inv_scale;
1037 stbir_real pixel_shift; // starting shift in output pixel space (in pixels)
1038 int scale_is_rational;
1039 stbir_uint32 scale_numerator, scale_denominator;
1040 stbir_real tap_scale_in, tap_scale_out; // 1 / 2N and 1 / 2D for stbir__tap_offset
1041 int positions_exact; // the tap positions come from the exact fraction and an integer shift (stbir__tap_offset)
1042 int shift_pixels; // pixel_shift as that integer
1043 int approximated_drift; // the fraction approximates the scale: how far its far edge can land from the float chain's, in input pixels (0: exact)
1044} stbir__scale_info;
1045
1046typedef struct
1047{
1048 stbir__contributors * contributors;
1049 float* coefficients;
1050 stbir__scale_info scale_info;
1051 stbir_filter filter_enum;
1052 stbir__kernel_callback_new * filter_kernel_new; // the kernel: the built-in's or the user's, or
1053 stbir__radius_callback_new * filter_radius; // the filter's extent, one of the three: a radius in input pixels (the box) or
1054 stbir__support_callback_new * filter_support_new; // a support in filter pixels (the other built-ins); the user's, or the built-in's,
1055 stbir__kernel_callback * filter_kernel_old; // an old back compat float kernel
1056 stbir__support_callback * filter_support_old; // or an old back compat support function
1057 stbir_edge edge;
1058 int coefficient_width;
1059 int entry_floats; // horizontal only: floats per packed column entry (STBIR__COLUMN_ENTRY_FLOATS of coefficient_width)
1060
1061 // polyphase sharing (see stbir__plan_polyphase_sharing). The coefficient sets for the repeating part of a rational scale
1062 // are stored once. 0 = every output has its own set.
1063 int shared_period; // coefficient sets in the repeating block
1064 int shared_prologue; // outputs with their own coefficient sets before it
1065 int shared_epilogue; // outputs with their own coefficient sets after it
1066 int shared_block_n0; // n0 of the block's first output (output P), and how far n0 moves per pass over the block
1067 int shared_block_shift;
1068 int shared_epilogue_n0; // n0 of the first epilogue output
1069 int filter_pixel_width;
1070 int filter_pixel_margin;
1071 int num_contributors;
1072 size_t contributors_size;
1073 size_t coefficients_size;
1074 stbir__filter_extent_info extent_info;
1075 int is_gather; // 0 = scatter, 1 = gather with scale >= 1, 2 = gather with scale < 1
1076} stbir__sampler;
1077
1078// horizontal routines combine the contribs and coeffs into one structure - coefficients is actually an array of width floats.
1079typedef struct
1080{
1081 int advance; // how far the running decode pointer moves for the NEXT column, in floats (already times the channel count)
1082 int width; // how many pixels this column reads (n1 - n0 + 1) - only the n-coefficient kernels look at it
1083 float coefficients[ 1 ]; // width floats long, with a zero pad after for the widths whose kernels load one float past
1084} stbir__column_entry;
1085
1086// a column slice is a run of output columns that the whole vertical/horizontal loop is run over at once (instead of
1087// full scanlines), so the ring buffer and the temp buffers stay in cache. the coefficients are shared by all the
1088// slices, each slice just starts at a different offset into them.
1089typedef struct
1090{
1091 int x0; // first output pixel of the slice
1092 int width; // output pixels in the slice
1093 int in_n0, in_n1; // input pixels decoded for the slice (in the horizontal sampler's coordinate space - can hang off either edge with WRAP)
1094 int first_n0; // input pixel output x0 starts reading at
1095} stbir__column_slice;
1096
1097typedef struct
1098{
1099#ifdef STBIR_PROFILE
1100 union
1101 {
1102 struct { stbir_uint64 total, looping, vertical, horizontal, decode, encode, alpha, unalpha; } named;
1103 stbir_uint64 array[8];
1104 } profile;
1105 stbir_uint64 * current_zone_excluded_ptr;
1106#endif
1107 float* decode_buffer;
1108
1109 int ring_buffer_first_scanline;
1110 int ring_buffer_last_scanline;
1111 int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer
1112 int start_output_y, end_output_y;
1113 int start_input_y, end_input_y; // used in scatter only
1114 int first_column_slice, num_column_slices; // the column slices this split runs: all of them when the splits are bands of rows, its own range when they are columns
1115
1116 #ifdef STBIR__SEPARATE_ALLOCATIONS
1117 float** ring_buffers; // one pointer for each ring buffer
1118 #else
1119 float* ring_buffer; // one big buffer that we index into
1120 #endif
1121
1122 float* vertical_buffer;
1123
1124 // where the buffers above were allocated. they each have a page of slack, and get repositioned within it at
1125 // resize time to keep them clear of 4K aliasing with the input rows (see stbir__4K_alias_skew_buffers)
1126 void* decode_buffer_alloc;
1127 void* ring_buffer_alloc;
1128 void* vertical_buffer_alloc;
1129
1130 char no_cache_straddle[64];
1131} stbir__per_split_info;
1132
1133typedef float * stbir__decode_pixels_func( float * decode, ptrdiff_t width_times_channels, void const * input );
1134typedef void stbir__alpha_weight_func( float * decode_buffer, ptrdiff_t width_times_channels );
1135typedef void stbir__horizontal_gather_channels_func( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer,
1136 stbir__column_entry const * column, int entry_stride );
1137typedef void stbir__alpha_unweight_func(float * encode_buffer, ptrdiff_t width_times_channels );
1138typedef void stbir__encode_pixels_func( void * output, ptrdiff_t width_times_channels, float const * encode );
1139
1140struct stbir__info
1141{
1142#ifdef STBIR_PROFILE
1143 union
1144 {
1145 struct { stbir_uint64 total, build, alloc, horizontal, vertical, cleanup, scatter; } named;
1146 stbir_uint64 array[7];
1147 } profile;
1148 stbir_uint64 * current_zone_excluded_ptr;
1149#endif
1150 stbir__sampler horizontal;
1151 stbir__sampler vertical;
1152
1153 void const * input_data;
1154 void * output_data;
1155
1156 stbir_int64 input_stride_bytes;
1157 stbir_int64 output_stride_bytes;
1158 size_t ring_buffer_length_bytes; // The length of an individual entry in the ring buffer (there are ring_buffer_num_entries of them)
1159 int ring_buffer_num_entries; // Total number of entries in the ring buffer.
1160
1161 stbir_datatype input_type;
1162 stbir_datatype output_type;
1163
1164 stbir_input_callback * in_pixels_cb;
1165 void * user_data;
1166 stbir_output_callback * out_pixels_cb;
1167
1168 stbir__contributors conservative_extents; // input pixel range that covers everything the horizontal filter can touch (decode buffers are sized from this)
1169
1170 void * alloced_mem;
1171 stbir__per_split_info * split_info; // by default 1, but there will be N of these allocated based on the thread init you did
1172 stbir__column_slice * column_slices; // the output columns, left to right (the last slice can be narrower)
1173 int num_column_slices;
1174 int split_cols, split_rows; // the split grid: splits = split_cols column groups x split_rows row bands (see stbir__split_mode)
1175
1176 stbir__decode_pixels_func * decode_pixels;
1177 stbir__alpha_weight_func * alpha_weight;
1178 stbir__horizontal_gather_channels_func * horizontal_gather_channels;
1179 stbir__horizontal_gather_channels_func * horizontal_gather_period1; // for the run over a polyphase period 1 set (integer downsamples: one coefficient set for every output), or null
1180 stbir__alpha_unweight_func * alpha_unweight;
1181 stbir__encode_pixels_func * encode_pixels;
1182
1183 int alloc_ring_buffer_num_entries; // Number of entries in the ring buffer that will be allocated
1184 int splits; // count of splits
1185
1186 stbir_internal_pixel_layout input_pixel_layout_internal;
1187 stbir_internal_pixel_layout output_pixel_layout_internal;
1188
1189 int input_color_and_type;
1190 int input_callback_offset_bytes; // where the input callback's temp buffer sits relative to the decode target (see STBIR_INPUT_CALLBACK_PADDING)
1191 int offset_x, offset_y; // offset within output_data
1192 int vertical_first;
1193 int channels;
1194 int effective_channels; // same as channels, except on RGBA/ARGB (7), or XA/AX (3)
1195 size_t alloced_total;
1196};
1197
1198
1199#define stbir__max_uint8_as_float 255.0f
1200#define stbir__max_uint16_as_float 65535.0f
1201#define stbir__max_uint8_as_float_inverted 3.9215689e-03f // (1.0f/255.0f)
1202#define stbir__max_uint16_as_float_inverted 1.5259022e-05f // (1.0f/65535.0f)
1203
1204// an alpha below this isn't divided by when un-premultiplying - alpha is effectively zero
1205#define stbir__alpha_too_small_to_divide ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20))
1206
1207// a coefficient set whose sum is within this of 1 isn't renormalized: scaling by 1 / sum would move every float
1208// coefficient by less than half an ulp ( 2^-25 of itself ), so each would round straight back - the loop is a no-op
1209#define stbir__renorm_tolerance ( (stbir_real)1.0 / ( 1 << 26 ) )
1210
1211// a kernel value this small (either sign) is dropped before the coefficients are normalized
1212#define stbir__too_small_coefficient ( (stbir_real)1.0 / ( 1 << 26 ) )
1213
1214// a coefficient set whose sum is under this (either sign) is zeroed instead of normalized
1215#define stbir__too_small_coefficient_sum ( (stbir_real)1.0 / ( 1 << 16 ) )
1216
1217// min/max friendly
1218#define STBIR_CLAMP(x, xmin, xmax) for(;;) { \
1219 if ( (x) < (xmin) ) (x) = (xmin); \
1220 if ( (x) > (xmax) ) (x) = (xmax); \
1221 break; \
1222}
1223
1224static stbir__inline int stbir__min(int a, int b)
1225{
1226 return a < b ? a : b;
1227}
1228
1229static stbir__inline int stbir__max(int a, int b)
1230{
1231 return a > b ? a : b;
1232}
1233
1234static float stbir__srgb_uchar_to_linear_float[256] = {
1235 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f,
1236 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f,
1237 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f,
1238 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f,
1239 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f,
1240 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f,
1241 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f,
1242 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f,
1243 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f,
1244 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f,
1245 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f,
1246 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f,
1247 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f,
1248 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f,
1249 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f,
1250 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f,
1251 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f,
1252 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f,
1253 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f,
1254 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f,
1255 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f,
1256 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f,
1257 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f,
1258 0.982251f, 0.991102f, 1.0f
1259};
1260
1261typedef union
1262{
1263 unsigned int u;
1264 float f;
1265} stbir__FP32;
1266
1267// From https://gist.github.com/rygorous/2203834
1268
1269// the SIMD sRGB encoders index this with a float's bits munged into 912..1015, so this biases it back to the table size
1270#define STBIR__SRGB8_TAB4_BIAS ((127-13)*8)
1271#define STBIR__SRGB8_TAB4( index ) fp32_to_srgb8_tab4[ (ptrdiff_t)(index) - STBIR__SRGB8_TAB4_BIAS ]
1272static const stbir_uint32 fp32_to_srgb8_tab4[104] = {
1273 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
1274 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
1275 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
1276 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
1277 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
1278 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
1279 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
1280 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
1281 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
1282 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
1283 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
1284 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
1285 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
1286};
1287
1288static stbir__inline stbir_uint8 stbir__linear_to_srgb_uchar(float in)
1289{
1290 static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps
1291 static const stbir__FP32 minval = { (127-13) << 23 };
1292 stbir_uint32 tab,bias,scale,t;
1293 stbir__FP32 f;
1294
1295 // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively.
1296 // The tests are carefully written so that NaNs map to 0, same as in the reference
1297 // implementation.
1298 if (!(in > minval.f)) // written this way to catch NaNs
1299 return 0;
1300 if (in > almostone.f)
1301 return 255;
1302
1303 // Do the table lookup and unpack bias, scale
1304 f.f = in;
1305 tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20];
1306 bias = (tab >> 16) << 9;
1307 scale = tab & 0xffff;
1308
1309 // Grab next-highest mantissa bits and perform linear interpolation
1310 t = (f.u >> 12) & 0xff;
1311 return (unsigned char) ((bias + scale*t) >> 16);
1312}
1313
1314#ifndef STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT
1315#define STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT 128 // when downsampling and <= 128 scanlines of buffering, use gather (scatter above that - 32 or more to 1 downsampling)
1316#endif
1317
1318
1319// every scanline buffer carries this many extra floats past the end. we right justify into the buffer
1320// so the conversion can read ahead of the writes. also gives the horizontal kernels a one-float overread
1321#define STBIR_INPUT_CALLBACK_PADDING 16
1322
1323#ifdef _M_IX86_FP
1324#if ( _M_IX86_FP >= 2 )
1325#ifndef STBIR_SSE
1326#define STBIR_SSE
1327#endif
1328#endif
1329#endif
1330
1331#ifdef __TINYC__
1332 // tiny c has no intrinsics yet - this can become a version check if they add them
1333 #define STBIR_NO_SIMD
1334#endif
1335
1336// (ARM64EC defines _M_X64 and _M_AMD64 as well as _M_ARM64EC, so the x64 tests have to leave it out - it is NEON below)
1337#if ( defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(_M_AMD64) || defined(__SSE2__) || defined(STBIR_SSE) || defined(STBIR_SSE2) ) && !defined(_M_ARM64EC)
1338 #ifndef STBIR_SSE2
1339 #define STBIR_SSE2
1340 #endif
1341 // AVX-512: the foundation plus the byte/word (BW) and 128/256-bit (VL) extensions. /arch:AVX512 on microsoft,
1342 // -mavx512f -mavx512bw -mavx512vl (or -march=x86-64-v4) on clang and gcc. Also implies AVX2
1343 #if ( defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512VL__) ) || defined(STBIR_AVX512)
1344 #ifdef STBIR_NO_AVX512
1345 #undef STBIR_AVX512
1346 #else
1347 #ifndef STBIR_AVX512
1348 #define STBIR_AVX512
1349 #endif
1350 #ifndef STBIR_AVX2
1351 #define STBIR_AVX2
1352 #endif
1353 #endif
1354 #endif
1355 #if defined(__AVX__) || defined(STBIR_AVX2)
1356 #ifndef STBIR_AVX
1357 #ifndef STBIR_NO_AVX
1358 #define STBIR_AVX
1359 #endif
1360 #endif
1361 #endif
1362 #if defined(__AVX2__) || defined(STBIR_AVX2)
1363 #ifndef STBIR_NO_AVX2
1364 #ifndef STBIR_AVX2
1365 #define STBIR_AVX2
1366 #endif
1367 #if defined( _MSC_VER ) && !defined(__clang__)
1368 #ifndef STBIR_FP16C // FP16C instructions are on all AVX2 cpus, so we can autoselect it here on microsoft - clang needs -mf16c
1369 #define STBIR_FP16C
1370 #endif
1371 #endif
1372 #endif
1373 #endif
1374 #ifdef __F16C__
1375 #ifndef STBIR_FP16C // turn on FP16C instructions if the define is set (for clang and gcc)
1376 #define STBIR_FP16C
1377 #endif
1378 #endif
1379#endif
1380
1381// 64-bit ARM (MSVC's ARM64EC included): the NEON code has a few AArch64 only forms
1382#if defined( _M_ARM64 ) || defined( _M_ARM64EC ) || defined( __aarch64__ ) || defined( __arm64__ )
1383#define STBIR__ARM64
1384#endif
1385
1386#if defined( STBIR__ARM64 ) || ((__ARM_NEON_FP & 4) != 0) || defined(__ARM_NEON__)
1387#ifndef STBIR_NEON
1388#define STBIR_NEON
1389#endif
1390#endif
1391
1392#if defined(_M_ARM) || defined(__arm__)
1393#ifdef STBIR_USE_FMA
1394#undef STBIR_USE_FMA // no FMA for 32-bit arm on MSVC
1395#endif
1396#endif
1397
1398#if defined(__wasm__) && defined(__wasm_simd128__)
1399#ifndef STBIR_WASM
1400#define STBIR_WASM
1401#endif
1402#endif
1403
1404// restrict pointers for the output pointers, other loop and unroll control
1405#if defined( _MSC_VER ) && !defined(__clang__)
1406 #define STBIR_STREAMOUT_PTR( star ) star __restrict
1407 #define STBIR_NO_UNROLL( ptr ) __assume(ptr) // this oddly keeps msvc from unrolling a loop
1408 #if _MSC_VER >= 1900
1409 #define STBIR_NO_UNROLL_LOOP_START __pragma(loop( no_vector ))
1410 #else
1411 #define STBIR_NO_UNROLL_LOOP_START
1412 #endif
1413#elif defined( __clang__ )
1414 #define STBIR_STREAMOUT_PTR( star ) star __restrict__
1415 #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr))
1416 #if ( __clang_major__ >= 4 ) || ( ( __clang_major__ >= 3 ) && ( __clang_minor__ >= 5 ) )
1417 #define STBIR_NO_UNROLL_LOOP_START _Pragma("clang loop unroll(disable)") _Pragma("clang loop vectorize(disable)")
1418 #else
1419 #define STBIR_NO_UNROLL_LOOP_START
1420 #endif
1421#elif defined( __GNUC__ )
1422 #define STBIR_STREAMOUT_PTR( star ) star __restrict__
1423 #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr))
1424 #if __GNUC__ >= 14
1425 #define STBIR_NO_UNROLL_LOOP_START _Pragma("GCC unroll 0") _Pragma("GCC novector")
1426 #else
1427 #define STBIR_NO_UNROLL_LOOP_START
1428 #endif
1429 #define STBIR_NO_UNROLL_LOOP_START_INF_FOR
1430#else
1431 #define STBIR_STREAMOUT_PTR( star ) star
1432 #define STBIR_NO_UNROLL( ptr )
1433 #define STBIR_NO_UNROLL_LOOP_START
1434#endif
1435
1436#ifndef STBIR_NO_UNROLL_LOOP_START_INF_FOR
1437#define STBIR_NO_UNROLL_LOOP_START_INF_FOR STBIR_NO_UNROLL_LOOP_START
1438#endif
1439
1440#ifdef STBIR_NO_SIMD // force simd off for whatever reason
1441
1442// force simd off overrides everything else, so clear it all
1443
1444#ifdef STBIR_SSE2
1445#undef STBIR_SSE2
1446#endif
1447
1448#ifdef STBIR_AVX
1449#undef STBIR_AVX
1450#endif
1451
1452#ifdef STBIR_NEON
1453#undef STBIR_NEON
1454#endif
1455
1456#ifdef STBIR_AVX2
1457#undef STBIR_AVX2
1458#endif
1459
1460#ifdef STBIR_FP16C
1461#undef STBIR_FP16C
1462#endif
1463
1464#ifdef STBIR_WASM
1465#undef STBIR_WASM
1466#endif
1467
1468#ifdef STBIR_SIMD
1469#undef STBIR_SIMD
1470#endif
1471
1472#else // STBIR_SIMD
1473
1474#ifdef STBIR_SSE2
1475 #include <emmintrin.h>
1476
1477 #define stbir__simdf __m128
1478 #define stbir__simdi __m128i
1479
1480 #define stbir_simdi_castf( reg ) _mm_castps_si128(reg)
1481 #define stbir_simdf_casti( reg ) _mm_castsi128_ps(reg)
1482
1483 #define stbir__simdf_load( reg, ptr ) (reg) = _mm_loadu_ps( (float const*)(ptr) )
1484 #define stbir__simdi_load( reg, ptr ) (reg) = _mm_loadu_si128 ( (stbir__simdi const*)(ptr) )
1485 #define stbir__simdf_load1( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf)
1486 #define stbir__simdf_load1z( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values must be zero
1487 #define stbir__simdf_frep4( fvar ) _mm_set_ps1( fvar )
1488 #define stbir__simdf_load1frep4( out, fvar ) (out) = _mm_set_ps1( fvar )
1489 #define stbir__simdf_load2( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values can be random (not denormal or nan for perf)
1490 #define stbir__simdf_load2z( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values must be zero
1491 #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = _mm_castpd_ps(_mm_loadh_pd( _mm_castps_pd(reg), (double*)(ptr) ))
1492 // pairwise add of two registers: out = ( a0+a1, a2+a3, b0+b1, b2+b3 ), and the halves version: ( a0+a2, a1+a3, b0+b2, b1+b3 )
1493 #define stbir__simdf_hadd2( out, a, b ) (out) = _mm_add_ps( _mm_shuffle_ps( a, b, (0<<0)+(2<<2)+(0<<4)+(2<<6) ), _mm_shuffle_ps( a, b, (1<<0)+(3<<2)+(1<<4)+(3<<6) ) )
1494 #define stbir__simdf_addhalves2( out, a, b ) (out) = _mm_add_ps( _mm_movelh_ps( a, b ), _mm_movehl_ps( b, a ) )
1495
1496 #define stbir__simdf_zeroP() _mm_setzero_ps()
1497 #define stbir__simdf_zero( reg ) (reg) = _mm_setzero_ps()
1498
1499 #define stbir__simdf_store( ptr, reg ) _mm_storeu_ps( (float*)(ptr), reg )
1500 #define stbir__simdf_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), reg )
1501 #define stbir__simdf_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), _mm_castps_si128(reg) )
1502 #define stbir__simdf_store2h( ptr, reg ) _mm_storeh_pd( (double*)(ptr), _mm_castps_pd(reg) )
1503
1504 #define stbir__simdi_store( ptr, reg ) _mm_storeu_si128( (__m128i*)(ptr), reg )
1505 #define stbir__simdi_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), (reg) )
1506
1507 #define stbir__prefetch( ptr ) _mm_prefetch((char*)(ptr), _MM_HINT_T0 )
1508
1509 #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \
1510 { \
1511 stbir__simdi zero = _mm_setzero_si128(); \
1512 out2 = _mm_unpacklo_epi8( ireg, zero ); \
1513 out3 = _mm_unpackhi_epi8( ireg, zero ); \
1514 out0 = _mm_unpacklo_epi16( out2, zero ); \
1515 out1 = _mm_unpackhi_epi16( out2, zero ); \
1516 out2 = _mm_unpacklo_epi16( out3, zero ); \
1517 out3 = _mm_unpackhi_epi16( out3, zero ); \
1518 }
1519
1520
1521 #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \
1522 { \
1523 stbir__simdi zero = _mm_setzero_si128(); \
1524 out0 = _mm_unpacklo_epi16( ireg, zero ); \
1525 out1 = _mm_unpackhi_epi16( ireg, zero ); \
1526 }
1527
1528 #define stbir__simdf_convert_float_to_i32( i, f ) (i) = _mm_cvttps_epi32(f)
1529 #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),_mm_setzero_ps()))))
1530 #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps()))))
1531
1532 #define stbir__simdi_to_int( i ) _mm_cvtsi128_si32(i)
1533 #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = _mm_cvtepi32_ps( ireg )
1534 #define stbir__simdf_add( out, reg0, reg1 ) (out) = _mm_add_ps( reg0, reg1 )
1535 #define stbir__simdf_mult( out, reg0, reg1 ) (out) = _mm_mul_ps( reg0, reg1 )
1536 #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = _mm_mul_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) )
1537 #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = _mm_mul_ss( reg, _mm_load_ss( (float const*)(ptr) ) )
1538 #define stbir__simdf_add_mem( out, reg, ptr ) (out) = _mm_add_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) )
1539
1540 #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd
1541 #include <immintrin.h>
1542 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_fmadd_ps( mul1, mul2, add )
1543 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ps( mul, _mm_loadu_ps( (float const*)(ptr) ), add )
1544 #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ss( mul, _mm_load_ss( (float const*)(ptr) ), add )
1545 #else
1546 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_add_ps( add, _mm_mul_ps( mul1, mul2 ) )
1547 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_add_ps( add, _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ) )
1548 #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_add_ss( add, _mm_mul_ss( mul, _mm_load_ss( (float const*)(ptr) ) ) )
1549 #endif
1550
1551 #define stbir__simdf_add1( out, reg0, reg1 ) (out) = _mm_add_ss( reg0, reg1 )
1552
1553 #define stbir__simdf_and( out, reg0, reg1 ) (out) = _mm_and_ps( reg0, reg1 )
1554
1555 #define stbir__simdf_min( out, reg0, reg1 ) (out) = _mm_min_ps( reg0, reg1 )
1556 #define stbir__simdf_max( out, reg0, reg1 ) (out) = _mm_max_ps( reg0, reg1 )
1557
1558 #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (3<<0) + (0<<2) + (1<<4) + (2<<6) ) )
1559 #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (2<<0) + (3<<2) + (0<<4) + (1<<6) ) )
1560
1561 static const stbir__simdf STBIR_zeroones = { 0.0f,1.0f,0.0f,1.0f };
1562 static const stbir__simdf STBIR_onezeros = { 1.0f,0.0f,1.0f,0.0f };
1563 #define stbir__simdf_aaa1( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movehl_ps( ones, alp ) ), (1<<0) + (1<<2) + (1<<4) + (2<<6) ) )
1564 #define stbir__simdf_1aaa( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movelh_ps( ones, alp ) ), (0<<0) + (2<<2) + (2<<4) + (2<<6) ) )
1565 #define stbir__simdf_a1a1( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_srli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_zeroones )
1566 #define stbir__simdf_1a1a( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_slli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_onezeros )
1567
1568 #define stbir__simdf_swiz( reg, one, two, three, four ) _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( reg ), (one<<0) + (two<<2) + (three<<4) + (four<<6) ) )
1569
1570 #define stbir__simdi_and( out, reg0, reg1 ) (out) = _mm_and_si128( reg0, reg1 )
1571 #define stbir__simdi_or( out, reg0, reg1 ) (out) = _mm_or_si128( reg0, reg1 )
1572 #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = _mm_madd_epi16( reg0, reg1 )
1573
1574 #define stbir__simdf_pack_to_8bytes(out,aa,bb) \
1575 { \
1576 stbir__simdf af,bf; \
1577 stbir__simdi a,b; \
1578 af = _mm_min_ps( aa, STBIR_max_uint8_as_float ); \
1579 bf = _mm_min_ps( bb, STBIR_max_uint8_as_float ); \
1580 af = _mm_max_ps( af, _mm_setzero_ps() ); \
1581 bf = _mm_max_ps( bf, _mm_setzero_ps() ); \
1582 a = _mm_cvttps_epi32( af ); \
1583 b = _mm_cvttps_epi32( bf ); \
1584 a = _mm_packs_epi32( a, b ); \
1585 out = _mm_packus_epi16( a, a ); \
1586 }
1587
1588 #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \
1589 stbir__simdf_load( o0, (ptr) ); \
1590 stbir__simdf_load( o1, (ptr)+4 ); \
1591 stbir__simdf_load( o2, (ptr)+8 ); \
1592 stbir__simdf_load( o3, (ptr)+12 ); \
1593 { \
1594 __m128 tmp0, tmp1, tmp2, tmp3; \
1595 tmp0 = _mm_unpacklo_ps(o0, o1); \
1596 tmp2 = _mm_unpacklo_ps(o2, o3); \
1597 tmp1 = _mm_unpackhi_ps(o0, o1); \
1598 tmp3 = _mm_unpackhi_ps(o2, o3); \
1599 o0 = _mm_movelh_ps(tmp0, tmp2); \
1600 o1 = _mm_movehl_ps(tmp2, tmp0); \
1601 o2 = _mm_movelh_ps(tmp1, tmp3); \
1602 o3 = _mm_movehl_ps(tmp3, tmp1); \
1603 }
1604
1605 #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \
1606 r0 = _mm_packs_epi32( r0, r1 ); \
1607 r2 = _mm_packs_epi32( r2, r3 ); \
1608 r1 = _mm_unpacklo_epi16( r0, r2 ); \
1609 r3 = _mm_unpackhi_epi16( r0, r2 ); \
1610 r0 = _mm_unpacklo_epi16( r1, r3 ); \
1611 r2 = _mm_unpackhi_epi16( r1, r3 ); \
1612 r0 = _mm_packus_epi16( r0, r2 ); \
1613 stbir__simdi_store( ptr, r0 ); \
1614
1615 #define stbir__simdi_32shr( out, reg, imm ) out = _mm_srli_epi32( reg, imm )
1616
1617 #if defined(_MSC_VER) && !defined(__clang__)
1618 // msvc inits with 8 bytes
1619 #define STBIR__CONST_32_TO_8( v ) (char)(unsigned char)((v)&255),(char)(unsigned char)(((v)>>8)&255),(char)(unsigned char)(((v)>>16)&255),(char)(unsigned char)(((v)>>24)&255)
1620 #define STBIR__CONST_4_32i( v ) STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v )
1621 #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) STBIR__CONST_32_TO_8( v0 ), STBIR__CONST_32_TO_8( v1 ), STBIR__CONST_32_TO_8( v2 ), STBIR__CONST_32_TO_8( v3 )
1622 #else
1623 // everything else inits with long long's
1624 #define STBIR__CONST_4_32i( v ) (long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v))),(long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v)))
1625 #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) (long long)((((stbir_uint64)(stbir_uint32)(v1))<<32)|((stbir_uint64)(stbir_uint32)(v0))),(long long)((((stbir_uint64)(stbir_uint32)(v3))<<32)|((stbir_uint64)(stbir_uint32)(v2)))
1626 #endif
1627
1628 #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x }
1629 #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { STBIR__CONST_4_32i(x) }
1630 #define STBIR__SIMDI_CONST4(var, a, b, c, d) stbir__simdi var = { STBIR__CONST_4d_32i(a, b, c, d) }
1631 #define STBIR__CONSTF(var) (var)
1632 #define STBIR__CONSTI(var) (var)
1633
1634 // an arbitrary byte permute (pshufb) needs SSSE3
1635 #if defined(STBIR_AVX) || defined(__SSSE3__) || defined(__SSE4_1__)
1636 #include <tmmintrin.h>
1637 #define STBIR_SIMD_BYTE_SHUFFLE
1638 #define stbir__simdi_shuffle_bytes( out, reg, table ) (out) = _mm_shuffle_epi8( (reg), (table) )
1639 #endif
1640
1641 #if defined(STBIR_AVX) || defined(__SSE4_1__)
1642 #include <smmintrin.h>
1643 #define stbir__simdf_pack_to_8words(out,reg0,reg1) out = _mm_packus_epi32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())), _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())))
1644 #else
1645 static STBIR__SIMDI_CONST(stbir__s32_32768, 32768);
1646 static STBIR__SIMDI_CONST(stbir__s16_32768, ((32768<<16)|32768));
1647
1648 #define stbir__simdf_pack_to_8words(out,reg0,reg1) \
1649 { \
1650 stbir__simdi tmp0,tmp1; \
1651 tmp0 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \
1652 tmp1 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \
1653 tmp0 = _mm_sub_epi32( tmp0, stbir__s32_32768 ); \
1654 tmp1 = _mm_sub_epi32( tmp1, stbir__s32_32768 ); \
1655 out = _mm_packs_epi32( tmp0, tmp1 ); \
1656 out = _mm_sub_epi16( out, stbir__s16_32768 ); \
1657 }
1658
1659 #endif
1660
1661 #define STBIR_SIMD
1662
1663 // if we detect AVX, set the simd8 defines
1664 #ifdef STBIR_AVX
1665 #include <immintrin.h>
1666 #define STBIR_SIMD8
1667 #define stbir__simdf8 __m256
1668 #define stbir__simdi8 __m256i
1669 #define stbir__simdf8_load( out, ptr ) (out) = _mm256_loadu_ps( (float const *)(ptr) )
1670 #define stbir__simdi8_load( out, ptr ) (out) = _mm256_loadu_si256( (__m256i const *)(ptr) )
1671 #define stbir__simdf8_mult( out, a, b ) (out) = _mm256_mul_ps( (a), (b) )
1672 #define stbir__simdf8_store( ptr, out ) _mm256_storeu_ps( (float*)(ptr), out )
1673 #define stbir__simdi8_store( ptr, reg ) _mm256_storeu_si256( (__m256i*)(ptr), reg )
1674 #define stbir__simdf8_frep8( fval ) _mm256_set1_ps( fval )
1675
1676 #define stbir__simdf8_min( out, reg0, reg1 ) (out) = _mm256_min_ps( reg0, reg1 )
1677 #define stbir__simdf8_max( out, reg0, reg1 ) (out) = _mm256_max_ps( reg0, reg1 )
1678
1679 #define stbir__simdf8_add4halves( out, bot4, top8 ) (out) = _mm_add_ps( bot4, _mm256_extractf128_ps( top8, 1 ) )
1680 #define stbir__simdf8_mult_mem( out, reg, ptr ) (out) = _mm256_mul_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) )
1681 #define stbir__simdf8_add_mem( out, reg, ptr ) (out) = _mm256_add_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) )
1682 #define stbir__simdf8_add( out, a, b ) (out) = _mm256_add_ps( a, b )
1683 #define stbir__simdf8_load1b( out, ptr ) (out) = _mm256_broadcast_ss( ptr )
1684 #define stbir__simdf_load1rep4( out, ptr ) (out) = _mm_broadcast_ss( ptr ) // avx load instruction
1685
1686 #define stbir__simdi8_convert_i32_to_float(out, ireg) (out) = _mm256_cvtepi32_ps( ireg )
1687 #define stbir__simdf8_convert_float_to_i32( i, f ) (i) = _mm256_cvttps_epi32(f)
1688
1689 #define stbir__simdf8_bot4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (0<<0)+(2<<4) )
1690 #define stbir__simdf8_top4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (1<<0)+(3<<4) )
1691
1692 #define stbir__simdf8_gettop4( reg ) _mm256_extractf128_ps(reg,1)
1693
1694 #ifdef STBIR_AVX2
1695
1696 // vpmovzxbd takes eight bytes to eight dwords in one shuffle, so sixteen bytes are three (the high eight moved down first)
1697 #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \
1698 { \
1699 out0 = _mm256_cvtepu8_epi32( ireg ); \
1700 out1 = _mm256_cvtepu8_epi32( _mm_unpackhi_epi64( ireg, ireg ) ); \
1701 }
1702
1703 #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \
1704 { \
1705 stbir__simdi8 t; \
1706 stbir__simdf8 af,bf; \
1707 stbir__simdi8 a,b; \
1708 af = _mm256_min_ps( aa, STBIR_max_uint8_as_float8 ); \
1709 bf = _mm256_min_ps( bb, STBIR_max_uint8_as_float8 ); \
1710 af = _mm256_max_ps( af, _mm256_setzero_ps() ); \
1711 bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \
1712 a = _mm256_cvttps_epi32( af ); \
1713 b = _mm256_cvttps_epi32( bf ); \
1714 t = _mm256_permute4x64_epi64( _mm256_packs_epi32( a, b ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \
1715 out = _mm256_castsi256_si128( _mm256_permute4x64_epi64( _mm256_packus_epi16( t, t ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ) ); \
1716 }
1717
1718 #define stbir__simdi8_expand_u16_to_u32(out,ireg) out = _mm256_cvtepu16_epi32( ireg );
1719
1720 // a byte permute of 32 bytes by one 16 byte table, per 128-bit lane (vpshufb ymm)
1721 #define stbir__simdi8_shuffle_bytes( out, reg, table ) (out) = _mm256_shuffle_epi8( (reg), _mm256_broadcastsi128_si256( table ) )
1722
1723 #define stbir__simdf8_pack_to_16words(out,aa,bb) \
1724 { \
1725 stbir__simdf8 af,bf; \
1726 stbir__simdi8 a,b; \
1727 af = _mm256_min_ps( aa, STBIR_max_uint16_as_float8 ); \
1728 bf = _mm256_min_ps( bb, STBIR_max_uint16_as_float8 ); \
1729 af = _mm256_max_ps( af, _mm256_setzero_ps() ); \
1730 bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \
1731 a = _mm256_cvttps_epi32( af ); \
1732 b = _mm256_cvttps_epi32( bf ); \
1733 (out) = _mm256_permute4x64_epi64( _mm256_packus_epi32(a, b), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \
1734 }
1735
1736 #else
1737
1738 #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \
1739 { \
1740 stbir__simdi a,zero = _mm_setzero_si128(); \
1741 a = _mm_unpacklo_epi8( ireg, zero ); \
1742 out0 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \
1743 a = _mm_unpackhi_epi8( ireg, zero ); \
1744 out1 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \
1745 }
1746
1747 #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \
1748 { \
1749 stbir__simdi t; \
1750 stbir__simdf8 af,bf; \
1751 stbir__simdi8 a,b; \
1752 af = _mm256_min_ps( aa, STBIR_max_uint8_as_float8 ); \
1753 bf = _mm256_min_ps( bb, STBIR_max_uint8_as_float8 ); \
1754 af = _mm256_max_ps( af, _mm256_setzero_ps() ); \
1755 bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \
1756 a = _mm256_cvttps_epi32( af ); \
1757 b = _mm256_cvttps_epi32( bf ); \
1758 out = _mm_packs_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \
1759 out = _mm_packus_epi16( out, out ); \
1760 t = _mm_packs_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \
1761 t = _mm_packus_epi16( t, t ); \
1762 out = _mm_castps_si128( _mm_shuffle_ps( _mm_castsi128_ps(out), _mm_castsi128_ps(t), (0<<0)+(1<<2)+(0<<4)+(1<<6) ) ); \
1763 }
1764
1765 #define stbir__simdi8_expand_u16_to_u32(out,ireg) \
1766 { \
1767 stbir__simdi a,b,zero = _mm_setzero_si128(); \
1768 a = _mm_unpacklo_epi16( ireg, zero ); \
1769 b = _mm_unpackhi_epi16( ireg, zero ); \
1770 out = _mm256_insertf128_si256( _mm256_castsi128_si256( a ), b, 1 ); \
1771 }
1772
1773 #define stbir__simdf8_pack_to_16words(out,aa,bb) \
1774 { \
1775 stbir__simdi t0,t1; \
1776 stbir__simdf8 af,bf; \
1777 stbir__simdi8 a,b; \
1778 af = _mm256_min_ps( aa, STBIR_max_uint16_as_float8 ); \
1779 bf = _mm256_min_ps( bb, STBIR_max_uint16_as_float8 ); \
1780 af = _mm256_max_ps( af, _mm256_setzero_ps() ); \
1781 bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \
1782 a = _mm256_cvttps_epi32( af ); \
1783 b = _mm256_cvttps_epi32( bf ); \
1784 t0 = _mm_packus_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \
1785 t1 = _mm_packus_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \
1786 out = _mm256_setr_m128i( t0, t1 ); \
1787 }
1788
1789 #endif
1790
1791 static __m256i stbir_00001111 = { STBIR__CONST_4d_32i( 0, 0, 0, 0 ), STBIR__CONST_4d_32i( 1, 1, 1, 1 ) };
1792 #define stbir__simdf8_0123to00001111( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00001111 )
1793
1794 static __m256i stbir_22223333 = { STBIR__CONST_4d_32i( 2, 2, 2, 2 ), STBIR__CONST_4d_32i( 3, 3, 3, 3 ) };
1795 #define stbir__simdf8_0123to22223333( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_22223333 )
1796
1797 #define stbir__simdf8_0123to2222( out, in ) (out) = stbir__simdf_swiz(_mm256_castps256_ps128(in), 2,2,2,2 )
1798
1799 #define stbir__simdf8_load4b( out, ptr ) (out) = _mm256_broadcast_ps( (__m128 const *)(ptr) )
1800
1801 static __m256i stbir_00112233 = { STBIR__CONST_4d_32i( 0, 0, 1, 1 ), STBIR__CONST_4d_32i( 2, 2, 3, 3 ) };
1802 #define stbir__simdf8_0123to00112233( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00112233 )
1803 #define stbir__simdf8_add4( out, a8, b ) (out) = _mm256_add_ps( a8, _mm256_insertf128_ps( _mm256_setzero_ps(), b, 0 ) )
1804
1805 static __m256i stbir_load6 = { STBIR__CONST_4_32i( 0x80000000 ), STBIR__CONST_4d_32i( 0x80000000, 0x80000000, 0, 0 ) };
1806 #define stbir__simdf8_load6z( out, ptr ) (out) = _mm256_maskload_ps( ptr, stbir_load6 )
1807
1808 #define stbir__simdf8_0123to00000000( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(0<<4)+(0<<6) )
1809 #define stbir__simdf8_0123to11111111( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(1<<4)+(1<<6) )
1810 #define stbir__simdf8_0123to22222222( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(2<<2)+(2<<4)+(2<<6) )
1811 #define stbir__simdf8_0123to33333333( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(3<<2)+(3<<4)+(3<<6) )
1812 #define stbir__simdf8_0123to21032103( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(1<<2)+(0<<4)+(3<<6) )
1813 #define stbir__simdf8_0123to32103210( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(2<<2)+(1<<4)+(0<<6) )
1814 #define stbir__simdf8_0123to12301230( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(2<<2)+(3<<4)+(0<<6) )
1815 #define stbir__simdf8_0123to10321032( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(0<<2)+(3<<4)+(2<<6) )
1816 #define stbir__simdf8_0123to30123012( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(0<<2)+(1<<4)+(2<<6) )
1817
1818 #define stbir__simdf8_0123to11331133( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(3<<4)+(3<<6) )
1819 #define stbir__simdf8_0123to00220022( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(2<<4)+(2<<6) )
1820
1821 #define stbir__simdf8_aaa1( out, alp, ones ) do { (out) = _mm256_blend_ps( alp, ones, (1<<0)+(1<<1)+(1<<2)+(0<<3)+(1<<4)+(1<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (3<<0) + (3<<2) + (3<<4) + (0<<6) ); } while(0)
1822 #define stbir__simdf8_1aaa( out, alp, ones ) do { (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(1<<2)+(1<<3)+(0<<4)+(1<<5)+(1<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (0<<4) + (0<<6) ); } while(0)
1823 #define stbir__simdf8_a1a1( out, alp, ones) do { (out) = _mm256_blend_ps( alp, ones, (1<<0)+(0<<1)+(1<<2)+(0<<3)+(1<<4)+(0<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ); } while(0)
1824 #define stbir__simdf8_1a1a( out, alp, ones) do { (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(0<<2)+(1<<3)+(0<<4)+(1<<5)+(0<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ); } while(0)
1825
1826 #define stbir__simdf8_zero( reg ) (reg) = _mm256_setzero_ps()
1827
1828 #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd
1829 #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_fmadd_ps( mul1, mul2, add )
1830 #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_fmadd_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ), add )
1831 #define stbir__simdf8_madd_mem4( out, add, mul, ptr )(out) = _mm256_fmadd_ps( _mm256_setr_m128( mul, _mm_setzero_ps() ), _mm256_setr_m128( _mm_loadu_ps( (float const*)(ptr) ), _mm_setzero_ps() ), add )
1832 #else
1833 #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul1, mul2 ) )
1834 #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ) ) )
1835 #define stbir__simdf8_madd_mem4( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_setr_m128( _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ), _mm_setzero_ps() ) )
1836 #endif
1837 #define stbir__if_simdf8_cast_to_simdf4( val ) _mm256_castps256_ps128( val )
1838
1839 static const stbir__simdf8 STBIR_max_uint16_as_float_inverted8 = { stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted };
1840 static const stbir__simdf8 STBIR_max_uint8_as_float_inverted8 = { stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted };
1841 static const stbir__simdf8 STBIR_ones8 = { 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 };
1842 static const stbir__simdf8 STBIR_simd_point58 = { 0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5 };
1843 static const stbir__simdf8 STBIR_max_uint8_as_float8 = { stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float, stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float };
1844 static const stbir__simdf8 STBIR_max_uint16_as_float8 = { stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float, stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float };
1845
1846 // the 16-wide set (AVX-512) for the X-wide code.
1847 #ifdef STBIR_AVX512
1848 #define STBIR_SIMD16
1849 #define stbir__simdf16 __m512
1850 #define stbir__simdi16 __m512i
1851 #define stbir__simdf16_load( out, ptr ) (out) = _mm512_loadu_ps( (float const *)(ptr) )
1852 #define stbir__simdi16_load( out, ptr ) (out) = _mm512_loadu_si512( (void const *)(ptr) )
1853 #define stbir__simdf16_store( ptr, reg ) _mm512_storeu_ps( (float*)(ptr), reg )
1854 #define stbir__simdi16_store( ptr, reg ) _mm512_storeu_si512( (void*)(ptr), reg )
1855 #define stbir__simdf16_mult( out, a, b ) (out) = _mm512_mul_ps( (a), (b) )
1856 #define stbir__simdf16_add_mem( out, reg, ptr ) (out) = _mm512_add_ps( reg, _mm512_loadu_ps( (float const*)(ptr) ) )
1857 #define stbir__simdf16_min( out, a, b ) (out) = _mm512_min_ps( a, b )
1858 #define stbir__simdf16_max( out, a, b ) (out) = _mm512_max_ps( a, b )
1859 #define stbir__simdf16_zero( reg ) (reg) = _mm512_setzero_ps()
1860 #define stbir__simdf16_frep16( fval ) _mm512_set1_ps( fval )
1861 #define stbir__simdi16_convert_i32_to_float( out, ireg ) (out) = _mm512_cvtepi32_ps( ireg )
1862 #define stbir__simdf16_convert_float_to_i32( i, f ) (i) = _mm512_cvttps_epi32( f )
1863 #define stbir__simdf16_cast_to_simdf4( val ) _mm512_castps512_ps128( val )
1864
1865 #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd
1866 #define stbir__simdf16_madd( out, add, mul1, mul2 ) (out) = _mm512_fmadd_ps( mul1, mul2, add )
1867 #define stbir__simdf16_madd_mem( out, add, mul, ptr ) (out) = _mm512_fmadd_ps( mul, _mm512_loadu_ps( (float const*)(ptr) ), add )
1868 #else
1869 #define stbir__simdf16_madd( out, add, mul1, mul2 ) (out) = _mm512_add_ps( add, _mm512_mul_ps( mul1, mul2 ) )
1870 #define stbir__simdf16_madd_mem( out, add, mul, ptr ) (out) = _mm512_add_ps( add, _mm512_mul_ps( mul, _mm512_loadu_ps( (float const*)(ptr) ) ) )
1871 #endif
1872
1873 // the per lane shuffles: one 4-wide order, applied in every lane
1874 #define stbir__simdf16_0123to0123( out, in ) (out) = (in)
1875 #define stbir__simdf16_0123to1230( out, in ) (out) = _mm512_shuffle_ps( in, in, (1<<0)+(2<<2)+(3<<4)+(0<<6) )
1876 #define stbir__simdf16_0123to2103( out, in ) (out) = _mm512_shuffle_ps( in, in, (2<<0)+(1<<2)+(0<<4)+(3<<6) )
1877 #define stbir__simdf16_0123to3210( out, in ) (out) = _mm512_shuffle_ps( in, in, (3<<0)+(2<<2)+(1<<4)+(0<<6) )
1878 #define stbir__simdf16_0123to1032( out, in ) (out) = _mm512_shuffle_ps( in, in, (1<<0)+(0<<2)+(3<<4)+(2<<6) )
1879 #define stbir__simdf16_0123to3012( out, in ) (out) = _mm512_shuffle_ps( in, in, (3<<0)+(0<<2)+(1<<4)+(2<<6) )
1880
1881 // the alpha patterns: blend the ones in (the mask repeats per lane), then shuffle each lane
1882 #define stbir__simdf16_aaa1( out, alp, ones ) do { (out) = _mm512_mask_blend_ps( (__mmask16)0x7777, alp, ones ); (out) = _mm512_shuffle_ps( out, out, (3<<0)+(3<<2)+(3<<4)+(0<<6) ); } while(0)
1883 #define stbir__simdf16_1aaa( out, alp, ones ) do { (out) = _mm512_mask_blend_ps( (__mmask16)0xeeee, alp, ones ); (out) = _mm512_shuffle_ps( out, out, (1<<0)+(0<<2)+(0<<4)+(0<<6) ); } while(0)
1884 #define stbir__simdf16_a1a1( out, alp, ones ) do { (out) = _mm512_mask_blend_ps( (__mmask16)0x5555, alp, ones ); (out) = _mm512_shuffle_ps( out, out, (1<<0)+(0<<2)+(3<<4)+(2<<6) ); } while(0)
1885 #define stbir__simdf16_1a1a( out, alp, ones ) do { (out) = _mm512_mask_blend_ps( (__mmask16)0xaaaa, alp, ones ); (out) = _mm512_shuffle_ps( out, out, (1<<0)+(0<<2)+(3<<4)+(2<<6) ); } while(0)
1886
1887 // integer conversions: sixteen bytes or words to sixteen dwords in one op, and the saturating narrows back
1888 #define stbir__simdi16_expand_u8_to_u32( out, ireg ) (out) = _mm512_cvtepu8_epi32( ireg )
1889 #define stbir__simdi16_expand_u16_to_u32( out, ireg ) (out) = _mm512_cvtepu16_epi32( ireg )
1890
1891 #define stbir__simdf16_pack_to_32bytes( out, aa, bb ) \
1892 { \
1893 stbir__simdf16 af, bf; \
1894 af = _mm512_min_ps( aa, STBIR_max_uint8_as_float16 ); \
1895 bf = _mm512_min_ps( bb, STBIR_max_uint8_as_float16 ); \
1896 af = _mm512_max_ps( af, _mm512_setzero_ps() ); \
1897 bf = _mm512_max_ps( bf, _mm512_setzero_ps() ); \
1898 (out) = _mm256_inserti128_si256( _mm256_castsi128_si256( _mm512_cvtusepi32_epi8( _mm512_cvttps_epi32( af ) ) ), _mm512_cvtusepi32_epi8( _mm512_cvttps_epi32( bf ) ), 1 ); \
1899 }
1900
1901 // the pack interleaves the two inputs per 128-bit lane, the 64-bit permute puts them back in order
1902 static const stbir__simdi16 stbir__pack_lanes_in_order16 = { STBIR__CONST_4d_32i( 0,0, 2,0 ), STBIR__CONST_4d_32i( 4,0, 6,0 ), STBIR__CONST_4d_32i( 1,0, 3,0 ), STBIR__CONST_4d_32i( 5,0, 7,0 ) }; // 64-bit indexes 0 2 4 6 1 3 5 7
1903 #define stbir__simdf16_pack_to_32words( out, aa, bb ) \
1904 { \
1905 stbir__simdf16 af, bf; \
1906 af = _mm512_min_ps( aa, STBIR_max_uint16_as_float16 ); \
1907 bf = _mm512_min_ps( bb, STBIR_max_uint16_as_float16 ); \
1908 af = _mm512_max_ps( af, _mm512_setzero_ps() ); \
1909 bf = _mm512_max_ps( bf, _mm512_setzero_ps() ); \
1910 (out) = _mm512_permutexvar_epi64( stbir__pack_lanes_in_order16, _mm512_packus_epi32( _mm512_cvttps_epi32( af ), _mm512_cvttps_epi32( bf ) ) ); \
1911 }
1912
1913 static const stbir__simdf16 STBIR_ones16 = { 1.0f,1.0f,1.0f,1.0f, 1.0f,1.0f,1.0f,1.0f, 1.0f,1.0f,1.0f,1.0f, 1.0f,1.0f,1.0f,1.0f };
1914 static const stbir__simdf16 STBIR_simd_point516 = { 0.5f,0.5f,0.5f,0.5f, 0.5f,0.5f,0.5f,0.5f, 0.5f,0.5f,0.5f,0.5f, 0.5f,0.5f,0.5f,0.5f };
1915 static const stbir__simdf16 STBIR_max_uint8_as_float16 = { 255.0f,255.0f,255.0f,255.0f, 255.0f,255.0f,255.0f,255.0f, 255.0f,255.0f,255.0f,255.0f, 255.0f,255.0f,255.0f,255.0f };
1916 static const stbir__simdf16 STBIR_max_uint16_as_float16 = { 65535.0f,65535.0f,65535.0f,65535.0f, 65535.0f,65535.0f,65535.0f,65535.0f, 65535.0f,65535.0f,65535.0f,65535.0f, 65535.0f,65535.0f,65535.0f,65535.0f };
1917 static const stbir__simdf16 STBIR_max_uint8_as_float_inverted16 = { stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted, stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted, stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted, stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted };
1918 static const stbir__simdf16 STBIR_max_uint16_as_float_inverted16 = { stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted, stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted, stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted, stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted };
1919 #endif // STBIR_AVX512
1920
1921 #endif
1922
1923#elif defined(STBIR_NEON)
1924
1925 #include <arm_neon.h>
1926
1927 #define stbir__simdf float32x4_t
1928 #define stbir__simdi uint32x4_t
1929
1930 #define stbir_simdi_castf( reg ) vreinterpretq_u32_f32(reg)
1931 #define stbir_simdf_casti( reg ) vreinterpretq_f32_u32(reg)
1932
1933 #define stbir__simdf_load( reg, ptr ) (reg) = vld1q_f32( (float const*)(ptr) )
1934 #define stbir__simdi_load( reg, ptr ) (reg) = vld1q_u32( (uint32_t const*)(ptr) )
1935 #define stbir__simdf_load1( out, ptr ) (out) = vld1q_lane_f32( (float const*)(ptr), vdupq_n_f32(0), 0 ) // top values zero, like SSE (a replicated inf would turn into nan against the zero coefficient lanes)
1936 #define stbir__simdf_load1z( out, ptr ) (out) = vld1q_lane_f32( (float const*)(ptr), vdupq_n_f32(0), 0 ) // top values must be zero
1937 #define stbir__simdf_frep4( fvar ) vdupq_n_f32( fvar )
1938 #define stbir__simdf_load1frep4( out, fvar ) (out) = vdupq_n_f32( fvar )
1939 #define stbir__simdf_load2( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values can be random (not denormal or nan for perf)
1940 #define stbir__simdf_load2z( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values must be zero
1941 #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = vcombine_f32( vget_low_f32(reg), vld1_f32( (float const*)(ptr) ) )
1942 // pairwise add of two registers: out = ( a0+a1, a2+a3, b0+b1, b2+b3 ), and the halves version: ( a0+a2, a1+a3, b0+b2, b1+b3 )
1943 #define stbir__simdf_hadd2( out, a, b ) (out) = vcombine_f32( vpadd_f32( vget_low_f32( a ), vget_high_f32( a ) ), vpadd_f32( vget_low_f32( b ), vget_high_f32( b ) ) )
1944 #define stbir__simdf_addhalves2( out, a, b ) (out) = vaddq_f32( vcombine_f32( vget_low_f32( a ), vget_low_f32( b ) ), vcombine_f32( vget_high_f32( a ), vget_high_f32( b ) ) )
1945
1946 #define stbir__simdf_zeroP() vdupq_n_f32(0)
1947 #define stbir__simdf_zero( reg ) (reg) = vdupq_n_f32(0)
1948
1949 #define stbir__simdf_store( ptr, reg ) vst1q_f32( (float*)(ptr), reg )
1950 #define stbir__simdf_store1( ptr, reg ) vst1q_lane_f32( (float*)(ptr), reg, 0)
1951 #define stbir__simdf_store2( ptr, reg ) vst1_f32( (float*)(ptr), vget_low_f32(reg) )
1952 #define stbir__simdf_store2h( ptr, reg ) vst1_f32( (float*)(ptr), vget_high_f32(reg) )
1953
1954 #define stbir__simdi_store( ptr, reg ) vst1q_u32( (uint32_t*)(ptr), reg )
1955 #define stbir__simdi_store2( ptr, reg ) vst1_u32( (uint32_t*)(ptr), vget_low_u32(reg) )
1956
1957 #define stbir__prefetch( ptr )
1958
1959 #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \
1960 { \
1961 uint16x8_t l = vmovl_u8( vget_low_u8 ( vreinterpretq_u8_u32(ireg) ) ); \
1962 uint16x8_t h = vmovl_u8( vget_high_u8( vreinterpretq_u8_u32(ireg) ) ); \
1963 out0 = vmovl_u16( vget_low_u16 ( l ) ); \
1964 out1 = vmovl_u16( vget_high_u16( l ) ); \
1965 out2 = vmovl_u16( vget_low_u16 ( h ) ); \
1966 out3 = vmovl_u16( vget_high_u16( h ) ); \
1967 }
1968
1969
1970 #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \
1971 { \
1972 uint16x8_t tmp = vreinterpretq_u16_u32(ireg); \
1973 out0 = vmovl_u16( vget_low_u16 ( tmp ) ); \
1974 out1 = vmovl_u16( vget_high_u16( tmp ) ); \
1975 }
1976
1977 #define stbir__simdf_convert_float_to_i32( i, f ) (i) = vreinterpretq_u32_s32( vcvtq_s32_f32(f) )
1978 #define stbir__simdi_to_int( i ) (int)vgetq_lane_u32(i, 0)
1979 #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),vdupq_n_f32(0))), 0))
1980 #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),vdupq_n_f32(0))), 0))
1981 #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = vcvtq_f32_s32( vreinterpretq_s32_u32(ireg) )
1982 #define stbir__simdf_add( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 )
1983 #define stbir__simdf_mult( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 )
1984 #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_f32( (float const*)(ptr) ) )
1985 #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) )
1986 #define stbir__simdf_add_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_f32( (float const*)(ptr) ) )
1987
1988 #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd (and also x64 no madd to arm madd)
1989 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 )
1990 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_f32( (float const*)(ptr) ) )
1991 #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_dup_f32( (float const*)(ptr) ) )
1992 #else
1993 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) )
1994 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_f32( (float const*)(ptr) ) ) )
1995 #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_dup_f32( (float const*)(ptr) ) ) )
1996 #endif
1997
1998 #define stbir__simdf_add1( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 )
1999
2000 #define stbir__simdf_and( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) )
2001
2002 // min / max with SSE's nan rule
2003 #if defined( STBIR__ARM64 )
2004 #define stbir__simdf_min( out, reg0, reg1 ) (out) = vminnmq_f32( reg0, reg1 )
2005 #define stbir__simdf_max( out, reg0, reg1 ) (out) = vmaxnmq_f32( reg0, reg1 )
2006 #else
2007 #define stbir__simdf_min( out, reg0, reg1 ) (out) = vbslq_f32( vcltq_f32( reg0, reg1 ), reg0, reg1 )
2008 #define stbir__simdf_max( out, reg0, reg1 ) (out) = vbslq_f32( vcgtq_f32( reg0, reg1 ), reg0, reg1 )
2009 #endif
2010
2011 #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 3 )
2012 #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 2 )
2013
2014 #define stbir__simdf_a1a1( out, alp, ones ) (out) = vzipq_f32(vuzpq_f32(alp, alp).val[1], ones).val[0]
2015 #define stbir__simdf_1a1a( out, alp, ones ) (out) = vzipq_f32(ones, vuzpq_f32(alp, alp).val[0]).val[0]
2016
2017 #if defined( STBIR__ARM64 )
2018
2019 #define stbir__simdf_aaa1( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3, ones, 3)
2020 #define stbir__simdf_1aaa( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0, ones, 0)
2021
2022 #if defined( _MSC_VER ) && !defined(__clang__)
2023 #define stbir_make16(a,b,c,d) vcombine_u8( \
2024 vcreate_u8( (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \
2025 ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56)), \
2026 vcreate_u8( (4*c+0) | ((4*c+1)<<8) | ((4*c+2)<<16) | ((4*c+3)<<24) | \
2027 ((stbir_uint64)(4*d+0)<<32) | ((stbir_uint64)(4*d+1)<<40) | ((stbir_uint64)(4*d+2)<<48) | ((stbir_uint64)(4*d+3)<<56) ) )
2028
2029 static stbir__inline uint8x16x2_t stbir_make16x2(float32x4_t rega,float32x4_t regb)
2030 {
2031 uint8x16x2_t r = { vreinterpretq_u8_f32(rega), vreinterpretq_u8_f32(regb) };
2032 return r;
2033 }
2034 #else
2035 #define stbir_make16(a,b,c,d) (uint8x16_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3,4*c+0,4*c+1,4*c+2,4*c+3,4*d+0,4*d+1,4*d+2,4*d+3}
2036 #define stbir_make16x2(a,b) (uint8x16x2_t){{vreinterpretq_u8_f32(a),vreinterpretq_u8_f32(b)}}
2037 #endif
2038
2039 #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vqtbl1q_u8( vreinterpretq_u8_f32(reg), stbir_make16(one, two, three, four) ) )
2040 #define stbir__simdf_swiz2( rega, regb, one, two, three, four ) vreinterpretq_f32_u8( vqtbl2q_u8( stbir_make16x2(rega,regb), stbir_make16(one, two, three, four) ) )
2041
2042 // an arbitrary byte permute
2043 #define STBIR_SIMD_BYTE_SHUFFLE
2044 #define stbir__simdi_shuffle_bytes( out, reg, table ) (out) = vreinterpretq_u32_u8( vqtbl1q_u8( vreinterpretq_u8_u32( reg ), vreinterpretq_u8_u32( table ) ) )
2045
2046 #define stbir__simdi_16madd( out, reg0, reg1 ) \
2047 { \
2048 int16x8_t r0 = vreinterpretq_s16_u32(reg0); \
2049 int16x8_t r1 = vreinterpretq_s16_u32(reg1); \
2050 int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \
2051 int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \
2052 (out) = vreinterpretq_u32_s32( vpaddq_s32(tmp0, tmp1) ); \
2053 }
2054
2055 #else
2056
2057 #define stbir__simdf_aaa1( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3)
2058 #define stbir__simdf_1aaa( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0)
2059
2060 #if defined( _MSC_VER ) && !defined(__clang__)
2061 static stbir__inline uint8x8x2_t stbir_make8x2(float32x4_t reg)
2062 {
2063 uint8x8x2_t r = { { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } };
2064 return r;
2065 }
2066 #define stbir_make8(a,b) vcreate_u8( \
2067 (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \
2068 ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56) )
2069 #else
2070 #define stbir_make8x2(reg) (uint8x8x2_t){ { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } }
2071 #define stbir_make8(a,b) (uint8x8_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3}
2072 #endif
2073
2074 #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vcombine_u8( \
2075 vtbl2_u8( stbir_make8x2( reg ), stbir_make8( one, two ) ), \
2076 vtbl2_u8( stbir_make8x2( reg ), stbir_make8( three, four ) ) ) )
2077
2078 #define stbir__simdi_16madd( out, reg0, reg1 ) \
2079 { \
2080 int16x8_t r0 = vreinterpretq_s16_u32(reg0); \
2081 int16x8_t r1 = vreinterpretq_s16_u32(reg1); \
2082 int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \
2083 int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \
2084 int32x2_t out0 = vpadd_s32( vget_low_s32(tmp0), vget_high_s32(tmp0) ); \
2085 int32x2_t out1 = vpadd_s32( vget_low_s32(tmp1), vget_high_s32(tmp1) ); \
2086 (out) = vreinterpretq_u32_s32( vcombine_s32(out0, out1) ); \
2087 }
2088
2089 #endif
2090
2091 #define stbir__simdi_and( out, reg0, reg1 ) (out) = vandq_u32( reg0, reg1 )
2092 #define stbir__simdi_or( out, reg0, reg1 ) (out) = vorrq_u32( reg0, reg1 )
2093
2094 #define stbir__simdf_pack_to_8bytes(out,aa,bb) \
2095 { \
2096 float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \
2097 float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \
2098 int16x4_t ai = vqmovn_s32( vcvtq_s32_f32( af ) ); \
2099 int16x4_t bi = vqmovn_s32( vcvtq_s32_f32( bf ) ); \
2100 uint8x8_t out8 = vqmovun_s16( vcombine_s16(ai, bi) ); \
2101 out = vreinterpretq_u32_u8( vcombine_u8(out8, out8) ); \
2102 }
2103
2104 #define stbir__simdf_pack_to_8words(out,aa,bb) \
2105 { \
2106 float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \
2107 float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \
2108 int32x4_t ai = vcvtq_s32_f32( af ); \
2109 int32x4_t bi = vcvtq_s32_f32( bf ); \
2110 out = vreinterpretq_u32_u16( vcombine_u16(vqmovun_s32(ai), vqmovun_s32(bi)) ); \
2111 }
2112
2113 #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \
2114 { \
2115 int16x4x2_t tmp0 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r0)), vqmovn_s32(vreinterpretq_s32_u32(r2)) ); \
2116 int16x4x2_t tmp1 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r1)), vqmovn_s32(vreinterpretq_s32_u32(r3)) ); \
2117 uint8x8x2_t out = \
2118 { { \
2119 vqmovun_s16( vcombine_s16(tmp0.val[0], tmp0.val[1]) ), \
2120 vqmovun_s16( vcombine_s16(tmp1.val[0], tmp1.val[1]) ), \
2121 } }; \
2122 vst2_u8(ptr, out); \
2123 }
2124
2125 #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \
2126 { \
2127 float32x4x4_t tmp = vld4q_f32(ptr); \
2128 o0 = tmp.val[0]; \
2129 o1 = tmp.val[1]; \
2130 o2 = tmp.val[2]; \
2131 o3 = tmp.val[3]; \
2132 }
2133
2134 #define stbir__simdi_32shr( out, reg, imm ) out = vshrq_n_u32( reg, imm )
2135
2136 #if defined( _MSC_VER ) && !defined(__clang__)
2137 #define STBIR__SIMDF_CONST(var, x) __declspec(align(8)) float var[] = { x, x, x, x }
2138 #define STBIR__SIMDI_CONST(var, x) __declspec(align(8)) uint32_t var[] = { x, x, x, x }
2139 #define STBIR__SIMDI_CONST4(var, a, b, c, d) __declspec(align(8)) uint32_t var[] = { a, b, c, d }
2140 #define STBIR__CONSTF(var) (*(const float32x4_t*)var)
2141 #define STBIR__CONSTI(var) (*(const uint32x4_t*)var)
2142 #else
2143 #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x }
2144 #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x }
2145 #define STBIR__SIMDI_CONST4(var, a, b, c, d) stbir__simdi var = { a, b, c, d }
2146 #define STBIR__CONSTF(var) (var)
2147 #define STBIR__CONSTI(var) (var)
2148 #endif
2149
2150 #define STBIR_SIMD
2151
2152#elif defined(STBIR_WASM)
2153
2154 #include <wasm_simd128.h>
2155
2156 #define stbir__simdf v128_t
2157 #define stbir__simdi v128_t
2158
2159 #define stbir_simdi_castf( reg ) (reg)
2160 #define stbir_simdf_casti( reg ) (reg)
2161
2162 #define stbir__simdf_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) )
2163 #define stbir__simdi_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) )
2164 #define stbir__simdf_load1( out, ptr ) (out) = wasm_v128_load32_zero( (void const*)(ptr) ) // top values zero, like SSE (a replicated inf would turn into nan against the zero coefficient lanes)
2165 #define stbir__simdf_load1z( out, ptr ) (out) = wasm_v128_load32_zero( (void const*)(ptr) ) // top values must be zero
2166 #define stbir__simdf_frep4( fvar ) wasm_f32x4_splat( fvar )
2167 #define stbir__simdf_load1frep4( out, fvar ) (out) = wasm_f32x4_splat( fvar )
2168 #define stbir__simdf_load2( out, ptr ) (out) = wasm_v128_load64_zero( (void const*)(ptr) ) // top values zero, like SSE
2169 #define stbir__simdf_load2z( out, ptr ) (out) = wasm_v128_load64_zero( (void const*)(ptr) ) // top values must be zero
2170 #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = wasm_v128_load64_lane( (void const*)(ptr), reg, 1 )
2171// pairwise add of two registers: out = ( a0+a1, a2+a3, b0+b1, b2+b3 ), and the halves version: ( a0+a2, a1+a3, b0+b2, b1+b3 )
2172#define stbir__simdf_hadd2( out, a, b ) (out) = wasm_f32x4_add( wasm_i32x4_shuffle( a, b, 0, 2, 4, 6 ), wasm_i32x4_shuffle( a, b, 1, 3, 5, 7 ) )
2173#define stbir__simdf_addhalves2( out, a, b ) (out) = wasm_f32x4_add( wasm_i32x4_shuffle( a, b, 0, 1, 4, 5 ), wasm_i32x4_shuffle( a, b, 2, 3, 6, 7 ) )
2174
2175 #define stbir__simdf_zeroP() wasm_f32x4_const_splat(0)
2176 #define stbir__simdf_zero( reg ) (reg) = wasm_f32x4_const_splat(0)
2177
2178 #define stbir__simdf_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg )
2179 #define stbir__simdf_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 )
2180 #define stbir__simdf_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 )
2181 #define stbir__simdf_store2h( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 1 )
2182
2183 #define stbir__simdi_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg )
2184 #define stbir__simdi_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 )
2185
2186 #define stbir__prefetch( ptr )
2187
2188 #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \
2189 { \
2190 v128_t l = wasm_u16x8_extend_low_u8x16 ( ireg ); \
2191 v128_t h = wasm_u16x8_extend_high_u8x16( ireg ); \
2192 out0 = wasm_u32x4_extend_low_u16x8 ( l ); \
2193 out1 = wasm_u32x4_extend_high_u16x8( l ); \
2194 out2 = wasm_u32x4_extend_low_u16x8 ( h ); \
2195 out3 = wasm_u32x4_extend_high_u16x8( h ); \
2196 }
2197
2198
2199 #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \
2200 { \
2201 out0 = wasm_u32x4_extend_low_u16x8 ( ireg ); \
2202 out1 = wasm_u32x4_extend_high_u16x8( ireg ); \
2203 }
2204
2205 #define stbir__simdf_convert_float_to_i32( i, f ) (i) = wasm_i32x4_trunc_sat_f32x4(f)
2206 #define stbir__simdi_to_int( i ) wasm_i32x4_extract_lane(i, 0)
2207 #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint8_as_float),wasm_f32x4_const_splat(0))), 0))
2208 #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint16_as_float),wasm_f32x4_const_splat(0))), 0))
2209 #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = wasm_f32x4_convert_i32x4(ireg)
2210 #define stbir__simdf_add( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 )
2211 #define stbir__simdf_mult( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 )
2212 #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load( (void const*)(ptr) ) )
2213 #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load32_splat( (void const*)(ptr) ) )
2214 #define stbir__simdf_add_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load( (void const*)(ptr) ) )
2215
2216 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) )
2217 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load( (void const*)(ptr) ) ) )
2218 #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load32_splat( (void const*)(ptr) ) ) )
2219
2220 #define stbir__simdf_add1( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 )
2221
2222 #define stbir__simdf_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 )
2223
2224 #define stbir__simdf_min( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 )
2225 #define stbir__simdf_max( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 )
2226
2227 #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 3, 4, 5, -1 )
2228 #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 2, 3, 4, -1 )
2229
2230 #define stbir__simdf_aaa1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 3, 3, 3, 4)
2231 #define stbir__simdf_1aaa(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 0, 0)
2232 #define stbir__simdf_a1a1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 1, 4, 3, 4)
2233 #define stbir__simdf_1a1a(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 4, 2)
2234
2235 #define stbir__simdf_swiz( reg, one, two, three, four ) wasm_i32x4_shuffle(reg, reg, one, two, three, four)
2236
2237 // an arbitrary byte permute
2238 #define STBIR_SIMD_BYTE_SHUFFLE
2239 #define stbir__simdi_shuffle_bytes( out, reg, table ) (out) = wasm_i8x16_swizzle( reg, table )
2240
2241 #define stbir__simdi_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 )
2242 #define stbir__simdi_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 )
2243 #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = wasm_i32x4_dot_i16x8( reg0, reg1 )
2244
2245 #define stbir__simdf_pack_to_8bytes(out,aa,bb) \
2246 { \
2247 v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \
2248 v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \
2249 v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \
2250 v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \
2251 v128_t out16 = wasm_i16x8_narrow_i32x4( ai, bi ); \
2252 out = wasm_u8x16_narrow_i16x8( out16, out16 ); \
2253 }
2254
2255 #define stbir__simdf_pack_to_8words(out,aa,bb) \
2256 { \
2257 v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \
2258 v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \
2259 v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \
2260 v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \
2261 out = wasm_u16x8_narrow_i32x4( ai, bi ); \
2262 }
2263
2264 #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \
2265 { \
2266 v128_t tmp0 = wasm_i16x8_narrow_i32x4(r0, r1); \
2267 v128_t tmp1 = wasm_i16x8_narrow_i32x4(r2, r3); \
2268 v128_t tmp = wasm_u8x16_narrow_i16x8(tmp0, tmp1); \
2269 tmp = wasm_i8x16_shuffle(tmp, tmp, 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); \
2270 wasm_v128_store( (void*)(ptr), tmp); \
2271 }
2272
2273 #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \
2274 { \
2275 v128_t t0 = wasm_v128_load( ptr ); \
2276 v128_t t1 = wasm_v128_load( ptr+4 ); \
2277 v128_t t2 = wasm_v128_load( ptr+8 ); \
2278 v128_t t3 = wasm_v128_load( ptr+12 ); \
2279 v128_t s0 = wasm_i32x4_shuffle(t0, t1, 0, 4, 2, 6); \
2280 v128_t s1 = wasm_i32x4_shuffle(t0, t1, 1, 5, 3, 7); \
2281 v128_t s2 = wasm_i32x4_shuffle(t2, t3, 0, 4, 2, 6); \
2282 v128_t s3 = wasm_i32x4_shuffle(t2, t3, 1, 5, 3, 7); \
2283 o0 = wasm_i32x4_shuffle(s0, s2, 0, 1, 4, 5); \
2284 o1 = wasm_i32x4_shuffle(s1, s3, 0, 1, 4, 5); \
2285 o2 = wasm_i32x4_shuffle(s0, s2, 2, 3, 6, 7); \
2286 o3 = wasm_i32x4_shuffle(s1, s3, 2, 3, 6, 7); \
2287 }
2288
2289 #define stbir__simdi_32shr( out, reg, imm ) out = wasm_u32x4_shr( reg, imm )
2290
2291 typedef float stbir__f32x4 __attribute__((__vector_size__(16), __aligned__(16)));
2292 #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = (v128_t)(stbir__f32x4){ x, x, x, x }
2293 #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x }
2294 #define STBIR__SIMDI_CONST4(var, a, b, c, d) stbir__simdi var = { a, b, c, d }
2295 #define STBIR__CONSTF(var) (var)
2296 #define STBIR__CONSTI(var) (var)
2297
2298 #define STBIR_SIMD
2299
2300#endif // SSE2/NEON/WASM
2301
2302#endif // NO SIMD
2303
2304// the X-wide names
2305#if defined(STBIR_SIMD16)
2306 #define STBIR__U16_DECODE_BLOCK 16 // words per block in the u16 decoders (one 32 byte load)
2307 #define stbir__simdfX stbir__simdf16
2308 #define stbir__simdiX stbir__simdi16
2309 #define stbir__simdfX_load stbir__simdf16_load
2310 #define stbir__simdiX_load stbir__simdi16_load
2311 #define stbir__simdfX_mult stbir__simdf16_mult
2312 #define stbir__simdfX_add_mem stbir__simdf16_add_mem
2313 #define stbir__simdfX_madd_mem stbir__simdf16_madd_mem
2314 #define stbir__simdfX_store stbir__simdf16_store
2315 #define stbir__simdiX_store stbir__simdi16_store
2316 #define stbir__simdf_frepX stbir__simdf16_frep16
2317 #define stbir__simdfX_madd stbir__simdf16_madd
2318 #define stbir__simdfX_min stbir__simdf16_min
2319 #define stbir__simdfX_max stbir__simdf16_max
2320 #define stbir__simdfX_aaa1 stbir__simdf16_aaa1
2321 #define stbir__simdfX_1aaa stbir__simdf16_1aaa
2322 #define stbir__simdfX_a1a1 stbir__simdf16_a1a1
2323 #define stbir__simdfX_1a1a stbir__simdf16_1a1a
2324 #define stbir__simdfX_convert_float_to_i32 stbir__simdf16_convert_float_to_i32
2325 #define stbir__simdfX_pack_to_words stbir__simdf16_pack_to_32words
2326 #define stbir__simdfX_zero stbir__simdf16_zero
2327 #define STBIR_onesX STBIR_ones16
2328 #define STBIR_max_uint8_as_floatX STBIR_max_uint8_as_float16
2329 #define STBIR_max_uint16_as_floatX STBIR_max_uint16_as_float16
2330 #define STBIR_simd_point5X STBIR_simd_point516
2331 #define stbir__simdfX_float_count 16
2332 #define stbir__simdfX_cast_to_simdf4( val ) stbir__simdf16_cast_to_simdf4( val )
2333 #define stbir__simdfX_0123to1230 stbir__simdf16_0123to1230
2334 #define stbir__simdfX_0123to2103 stbir__simdf16_0123to2103
2335#elif defined(STBIR_SIMD8)
2336 #define STBIR__U16_DECODE_BLOCK 8
2337 #define stbir__simdfX stbir__simdf8
2338 #define stbir__simdiX stbir__simdi8
2339 #define stbir__simdfX_load stbir__simdf8_load
2340 #define stbir__simdiX_load stbir__simdi8_load
2341 #define stbir__simdfX_mult stbir__simdf8_mult
2342 #define stbir__simdfX_add_mem stbir__simdf8_add_mem
2343 #define stbir__simdfX_madd_mem stbir__simdf8_madd_mem
2344 #define stbir__simdfX_store stbir__simdf8_store
2345 #define stbir__simdiX_store stbir__simdi8_store
2346 #define stbir__simdf_frepX stbir__simdf8_frep8
2347 #define stbir__simdfX_madd stbir__simdf8_madd
2348 #define stbir__simdfX_min stbir__simdf8_min
2349 #define stbir__simdfX_max stbir__simdf8_max
2350 #define stbir__simdfX_aaa1 stbir__simdf8_aaa1
2351 #define stbir__simdfX_1aaa stbir__simdf8_1aaa
2352 #define stbir__simdfX_a1a1 stbir__simdf8_a1a1
2353 #define stbir__simdfX_1a1a stbir__simdf8_1a1a
2354 #define stbir__simdfX_convert_float_to_i32 stbir__simdf8_convert_float_to_i32
2355 #define stbir__simdfX_pack_to_words stbir__simdf8_pack_to_16words
2356 #define stbir__simdfX_zero stbir__simdf8_zero
2357 #define STBIR_onesX STBIR_ones8
2358 #define STBIR_max_uint8_as_floatX STBIR_max_uint8_as_float8
2359 #define STBIR_max_uint16_as_floatX STBIR_max_uint16_as_float8
2360 #define STBIR_simd_point5X STBIR_simd_point58
2361 #define stbir__simdfX_float_count 8
2362 #define stbir__simdfX_cast_to_simdf4( val ) stbir__if_simdf8_cast_to_simdf4( val )
2363 #define stbir__simdfX_0123to1230 stbir__simdf8_0123to12301230
2364 #define stbir__simdfX_0123to2103 stbir__simdf8_0123to21032103
2365#else
2366 #define STBIR__U16_DECODE_BLOCK 8
2367 #define stbir__simdfX stbir__simdf
2368 #define stbir__simdiX stbir__simdi
2369 #define stbir__simdfX_load stbir__simdf_load
2370 #define stbir__simdiX_load stbir__simdi_load
2371 #define stbir__simdfX_mult stbir__simdf_mult
2372 #define stbir__simdfX_add_mem stbir__simdf_add_mem
2373 #define stbir__simdfX_madd_mem stbir__simdf_madd_mem
2374 #define stbir__simdfX_store stbir__simdf_store
2375 #define stbir__simdiX_store stbir__simdi_store
2376 #define stbir__simdf_frepX stbir__simdf_frep4
2377 #define stbir__simdfX_madd stbir__simdf_madd
2378 #define stbir__simdfX_min stbir__simdf_min
2379 #define stbir__simdfX_max stbir__simdf_max
2380 #define stbir__simdfX_aaa1 stbir__simdf_aaa1
2381 #define stbir__simdfX_1aaa stbir__simdf_1aaa
2382 #define stbir__simdfX_a1a1 stbir__simdf_a1a1
2383 #define stbir__simdfX_1a1a stbir__simdf_1a1a
2384 #define stbir__simdfX_convert_float_to_i32 stbir__simdf_convert_float_to_i32
2385 #define stbir__simdfX_pack_to_words stbir__simdf_pack_to_8words
2386 #define stbir__simdfX_zero stbir__simdf_zero
2387 #define STBIR_onesX STBIR__CONSTF(STBIR_ones)
2388 #define STBIR_simd_point5X STBIR__CONSTF(STBIR_simd_point5)
2389 #define STBIR_max_uint8_as_floatX STBIR__CONSTF(STBIR_max_uint8_as_float)
2390 #define STBIR_max_uint16_as_floatX STBIR__CONSTF(STBIR_max_uint16_as_float)
2391 #define stbir__simdfX_float_count 4
2392 #define stbir__simdfX_cast_to_simdf4( val ) ( val )
2393 #define stbir__if_simdf8_cast_to_simdf4( val ) ( val )
2394 #define stbir__simdfX_0123to1230 stbir__simdf_0123to1230
2395 #define stbir__simdfX_0123to2103 stbir__simdf_0123to2103
2396#endif
2397
2398
2399#if defined(STBIR_NEON) && !defined(_M_ARM) && !defined(__arm__)
2400
2401 #if defined( _MSC_VER ) && !defined(__clang__)
2402 typedef __int16 stbir__FP16;
2403 #else
2404 typedef float16_t stbir__FP16;
2405 #endif
2406
2407#else // no NEON, or 32-bit ARM for MSVC
2408
2409 typedef union stbir__FP16
2410 {
2411 unsigned short u;
2412 } stbir__FP16;
2413
2414#endif
2415
2416#if (!defined(STBIR_NEON) && !defined(STBIR_FP16C)) || (defined(STBIR_NEON) && defined(_M_ARM)) || (defined(STBIR_NEON) && defined(__arm__))
2417
2418 // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668
2419
2420 static stbir__inline float stbir__half_to_float( stbir__FP16 h )
2421 {
2422 static const stbir__FP32 magic = { (254 - 15) << 23 };
2423 static const stbir__FP32 was_infnan = { (127 + 16) << 23 };
2424 stbir__FP32 o;
2425
2426 o.u = (h.u & 0x7fff) << 13; // exponent/mantissa bits
2427 o.f *= magic.f; // exponent adjust
2428 if (o.f >= was_infnan.f) // make sure Inf/NaN survive
2429 o.u |= 255 << 23;
2430 o.u |= (h.u & 0x8000) << 16; // sign bit
2431 return o.f;
2432 }
2433
2434 static stbir__inline stbir__FP16 stbir__float_to_half(float val)
2435 {
2436 stbir__FP32 f32infty = { 255 << 23 };
2437 stbir__FP32 f16max = { (127 + 16) << 23 };
2438 stbir__FP32 denorm_magic = { ((127 - 15) + (23 - 10) + 1) << 23 };
2439 unsigned int sign_mask = 0x80000000u;
2440 stbir__FP16 o = { 0 };
2441 stbir__FP32 f;
2442 unsigned int sign;
2443
2444 f.f = val;
2445 sign = f.u & sign_mask;
2446 f.u ^= sign;
2447
2448 if (f.u >= f16max.u) // result is Inf or NaN (all exponent bits set)
2449 o.u = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf
2450 else // (De)normalized number or zero
2451 {
2452 if (f.u < (113 << 23)) // resulting FP16 is subnormal or zero
2453 {
2454 // use a magic value to align our 10 mantissa bits at the bottom of
2455 // the float. as long as FP addition is round-to-nearest-even this
2456 // just works.
2457 f.f += denorm_magic.f;
2458 // and one integer subtract of the bias later, we have our final float!
2459 o.u = (unsigned short) ( f.u - denorm_magic.u );
2460 }
2461 else
2462 {
2463 unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd
2464 // update exponent, rounding bias part 1
2465 f.u = f.u + ((15u - 127) << 23) + 0xfff;
2466 // rounding bias part 2
2467 f.u += mant_odd;
2468 // take the bits!
2469 o.u = (unsigned short) ( f.u >> 13 );
2470 }
2471 }
2472
2473 o.u |= sign >> 16;
2474 return o;
2475 }
2476
2477#endif
2478
2479
2480#if defined(STBIR_FP16C)
2481
2482 #include <immintrin.h>
2483
2484 static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input)
2485 {
2486 _mm256_storeu_ps( (float*)output, _mm256_cvtph_ps( _mm_loadu_si128( (__m128i const* )input ) ) );
2487 }
2488
2489 static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input)
2490 {
2491 _mm_storeu_si128( (__m128i*)output, _mm256_cvtps_ph( _mm256_loadu_ps( input ), 0 ) );
2492 }
2493
2494 static stbir__inline float stbir__half_to_float( stbir__FP16 h )
2495 {
2496 return _mm_cvtss_f32( _mm_cvtph_ps( _mm_cvtsi32_si128( (int)h.u ) ) );
2497 }
2498
2499 static stbir__inline stbir__FP16 stbir__float_to_half( float f )
2500 {
2501 stbir__FP16 h;
2502 h.u = (unsigned short) _mm_cvtsi128_si32( _mm_cvtps_ph( _mm_set_ss( f ), 0 ) );
2503 return h;
2504 }
2505
2506#elif defined(STBIR_SSE2)
2507
2508 // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668
2509 stbir__inline static void stbir__half_to_float_SIMD(float * output, void const * input)
2510 {
2511 static const STBIR__SIMDI_CONST(mask_nosign, 0x7fff);
2512 static const STBIR__SIMDI_CONST(smallest_normal, 0x0400);
2513 static const STBIR__SIMDI_CONST(infinity, 0x7c00);
2514 static const STBIR__SIMDI_CONST(expadjust_normal, (127 - 15) << 23);
2515 static const STBIR__SIMDI_CONST(magic_denorm, 113 << 23);
2516
2517 __m128i i = _mm_loadu_si128 ( (__m128i const*)(input) );
2518 __m128i h = _mm_unpacklo_epi16 ( i, _mm_setzero_si128() );
2519 __m128i mnosign = STBIR__CONSTI(mask_nosign);
2520 __m128i eadjust = STBIR__CONSTI(expadjust_normal);
2521 __m128i smallest = STBIR__CONSTI(smallest_normal);
2522 __m128i infty = STBIR__CONSTI(infinity);
2523 __m128i expmant = _mm_and_si128(mnosign, h);
2524 __m128i justsign = _mm_xor_si128(h, expmant);
2525 __m128i b_notinfnan = _mm_cmpgt_epi32(infty, expmant);
2526 __m128i b_isdenorm = _mm_cmpgt_epi32(smallest, expmant);
2527 __m128i shifted = _mm_slli_epi32(expmant, 13);
2528 __m128i adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust);
2529 __m128i adjusted = _mm_add_epi32(eadjust, shifted);
2530 __m128i den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm));
2531 __m128i adjusted2 = _mm_add_epi32(adjusted, adj_infnan);
2532 __m128 den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm);
2533 __m128 adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm));
2534 __m128 adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2));
2535 __m128 adjusted5 = _mm_or_ps(adjusted3, adjusted4);
2536 __m128i sign = _mm_slli_epi32(justsign, 16);
2537 __m128 final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign));
2538 stbir__simdf_store( output + 0, final );
2539
2540 h = _mm_unpackhi_epi16 ( i, _mm_setzero_si128() );
2541 expmant = _mm_and_si128(mnosign, h);
2542 justsign = _mm_xor_si128(h, expmant);
2543 b_notinfnan = _mm_cmpgt_epi32(infty, expmant);
2544 b_isdenorm = _mm_cmpgt_epi32(smallest, expmant);
2545 shifted = _mm_slli_epi32(expmant, 13);
2546 adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust);
2547 adjusted = _mm_add_epi32(eadjust, shifted);
2548 den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm));
2549 adjusted2 = _mm_add_epi32(adjusted, adj_infnan);
2550 den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm);
2551 adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm));
2552 adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2));
2553 adjusted5 = _mm_or_ps(adjusted3, adjusted4);
2554 sign = _mm_slli_epi32(justsign, 16);
2555 final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign));
2556 stbir__simdf_store( output + 4, final );
2557
2558 // ~38 SSE2 ops for 8 values
2559 }
2560
2561 // Fabian's round-to-nearest-even float to half
2562 // ~48 SSE2 ops for 8 output
2563 stbir__inline static void stbir__float_to_half_SIMD(void * output, float const * input)
2564 {
2565 static const STBIR__SIMDI_CONST(mask_sign, 0x80000000u);
2566 static const STBIR__SIMDI_CONST(c_f16max, (127 + 16) << 23); // all FP32 values >=this round to +inf
2567 static const STBIR__SIMDI_CONST(c_nanbit, 0x200);
2568 static const STBIR__SIMDI_CONST(c_infty_as_fp16, 0x7c00);
2569 static const STBIR__SIMDI_CONST(c_min_normal, (127 - 14) << 23); // smallest FP32 that yields a normalized FP16
2570 static const STBIR__SIMDI_CONST(c_subnorm_magic, ((127 - 15) + (23 - 10) + 1) << 23);
2571 static const STBIR__SIMDI_CONST(c_normal_bias, 0xfff - ((127 - 15) << 23)); // adjust exponent and add mantissa rounding
2572
2573 __m128 f = _mm_loadu_ps(input);
2574 __m128 msign = _mm_castsi128_ps(STBIR__CONSTI(mask_sign));
2575 __m128 justsign = _mm_and_ps(msign, f);
2576 __m128 absf = _mm_xor_ps(f, justsign);
2577 __m128i absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit)
2578 __m128i f16max = STBIR__CONSTI(c_f16max);
2579 __m128 b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN?
2580 __m128i b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special?
2581 __m128i nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), STBIR__CONSTI(c_nanbit));
2582 __m128i inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials
2583
2584 __m128i min_normal = STBIR__CONSTI(c_min_normal);
2585 __m128i b_issub = _mm_cmpgt_epi32(min_normal, absf_int);
2586
2587 // "result is subnormal" path
2588 __m128 subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa
2589 __m128i subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias
2590
2591 // "result is normal" path
2592 __m128i mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign
2593 __m128i mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0
2594
2595 __m128i round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias));
2596 __m128i round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE)
2597 __m128i normal = _mm_srli_epi32(round2, 13); // rounded result
2598
2599 // combine the two non-specials
2600 __m128i nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal));
2601
2602 // merge in specials as well
2603 __m128i joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan));
2604
2605 __m128i sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16);
2606 __m128i final2, final= _mm_or_si128(joined, sign_shift);
2607
2608 f = _mm_loadu_ps(input+4);
2609 justsign = _mm_and_ps(msign, f);
2610 absf = _mm_xor_ps(f, justsign);
2611 absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit)
2612 b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN?
2613 b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special?
2614 nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), STBIR__CONSTI(c_nanbit));
2615 inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials
2616
2617 b_issub = _mm_cmpgt_epi32(min_normal, absf_int);
2618
2619 // "result is subnormal" path
2620 subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa
2621 subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias
2622
2623 // "result is normal" path
2624 mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign
2625 mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0
2626
2627 round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias));
2628 round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE)
2629 normal = _mm_srli_epi32(round2, 13); // rounded result
2630
2631 // combine the two non-specials
2632 nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal));
2633
2634 // merge in specials as well
2635 joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan));
2636
2637 sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16);
2638 final2 = _mm_or_si128(joined, sign_shift);
2639 final = _mm_packs_epi32(final, final2);
2640 stbir__simdi_store( output,final );
2641 }
2642
2643#elif defined(STBIR_NEON) && defined(_MSC_VER) && defined(STBIR__ARM64) && !defined(__clang__) // 64-bit ARM on MSVC (not clang)
2644
2645 static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input)
2646 {
2647 float16x4_t in0 = vld1_f16(input + 0);
2648 float16x4_t in1 = vld1_f16(input + 4);
2649 vst1q_f32(output + 0, vcvt_f32_f16(in0));
2650 vst1q_f32(output + 4, vcvt_f32_f16(in1));
2651 }
2652
2653 static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input)
2654 {
2655 float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0));
2656 float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4));
2657 vst1_f16(output+0, out0);
2658 vst1_f16(output+4, out1);
2659 }
2660
2661 static stbir__inline float stbir__half_to_float( stbir__FP16 h )
2662 {
2663 return vgetq_lane_f32(vcvt_f32_f16(vld1_dup_f16(&h)), 0);
2664 }
2665
2666 static stbir__inline stbir__FP16 stbir__float_to_half( float f )
2667 {
2668 return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0).n16_u16[0];
2669 }
2670
2671#elif defined(STBIR_NEON) && defined( STBIR__ARM64 ) // 64-bit ARM
2672
2673 static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input)
2674 {
2675 float16x8_t in = vld1q_f16(input);
2676 vst1q_f32(output + 0, vcvt_f32_f16(vget_low_f16(in)));
2677 vst1q_f32(output + 4, vcvt_f32_f16(vget_high_f16(in)));
2678 }
2679
2680 static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input)
2681 {
2682 float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0));
2683 float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4));
2684 vst1q_f16(output, vcombine_f16(out0, out1));
2685 }
2686
2687 static stbir__inline float stbir__half_to_float( stbir__FP16 h )
2688 {
2689 return vgetq_lane_f32(vcvt_f32_f16(vdup_n_f16(h)), 0);
2690 }
2691
2692 static stbir__inline stbir__FP16 stbir__float_to_half( float f )
2693 {
2694 return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0);
2695 }
2696
2697#elif defined(STBIR_WASM) || (defined(STBIR_NEON) && (defined(_MSC_VER) || defined(_M_ARM) || defined(__arm__))) // WASM or 32-bit ARM on MSVC/clang
2698
2699 static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input)
2700 {
2701 int i;
2702 for (i=0; i<8; i++)
2703 {
2704 output[i] = stbir__half_to_float(input[i]);
2705 }
2706 }
2707 static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input)
2708 {
2709 int i;
2710 for (i=0; i<8; i++)
2711 {
2712 output[i] = stbir__float_to_half(input[i]);
2713 }
2714 }
2715
2716#endif
2717
2718
2719#ifdef STBIR_SIMD
2720
2721#define stbir__simdf_0123to3333( out, reg ) (out) = stbir__simdf_swiz( reg, 3,3,3,3 )
2722#define stbir__simdf_0123to2222( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,2,2 )
2723#define stbir__simdf_0123to1111( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,1,1 )
2724#define stbir__simdf_0123to0000( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,0 )
2725#define stbir__simdf_0123to0001( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,1 )
2726#define stbir__simdf_0123to1122( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,2,2 )
2727#define stbir__simdf_0123to2333( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,3,3 )
2728#define stbir__simdf_0123to1230( out, reg ) (out) = stbir__simdf_swiz( reg, 1,2,3,0 )
2729#define stbir__simdf_0123to2103( out, reg ) (out) = stbir__simdf_swiz( reg, 2,1,0,3 )
2730#define stbir__simdf_0123to3210( out, reg ) (out) = stbir__simdf_swiz( reg, 3,2,1,0 )
2731#define stbir__simdf_0123to2301( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,0,1 )
2732#define stbir__simdf_0123to3012( out, reg ) (out) = stbir__simdf_swiz( reg, 3,0,1,2 )
2733#define stbir__simdf_0123to0011( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,1,1 )
2734#define stbir__simdf_0123to2233( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,3,3 )
2735#define stbir__simdf_0123to1133( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,3,3 )
2736#define stbir__simdf_0123to0022( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,2 )
2737#define stbir__simdf_0123to1032( out, reg ) (out) = stbir__simdf_swiz( reg, 1,0,3,2 )
2738
2739typedef union stbir__simdi_u32
2740{
2741 stbir_uint32 m128i_u32[4];
2742 int m128i_i32[4];
2743 stbir__simdi m128i_i128;
2744} stbir__simdi_u32;
2745
2746static const int STBIR_mask[9] = { 0,0,0,-1,-1,-1,0,0,0 };
2747
2748static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float, stbir__max_uint8_as_float);
2749static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float, stbir__max_uint16_as_float);
2750static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float_inverted, stbir__max_uint8_as_float_inverted);
2751static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float_inverted, stbir__max_uint16_as_float_inverted);
2752
2753static const STBIR__SIMDF_CONST(STBIR_simd_point5, 0.5f);
2754static const STBIR__SIMDF_CONST(STBIR_ones, 1.0f);
2755static const STBIR__SIMDI_CONST(STBIR_almost_zero, (127 - 13) << 23);
2756static const STBIR__SIMDI_CONST(STBIR_almost_one, 0x3f7fffff);
2757static const STBIR__SIMDI_CONST(STBIR_mantissa_mask, 0xff);
2758static const STBIR__SIMDI_CONST(STBIR_topscale, 0x02000000);
2759
2760// Basically, in simd mode, we unroll the proper amount, and we don't want
2761// the non-simd remnant loops to be unroll because they only run a few times
2762// Adding this switch saves about 5K on clang which is Captain Unroll the 3rd.
2763#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star )
2764#define STBIR_SIMD_NO_UNROLL(ptr) STBIR_NO_UNROLL(ptr)
2765#define STBIR_SIMD_NO_UNROLL_LOOP_START STBIR_NO_UNROLL_LOOP_START
2766#define STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR STBIR_NO_UNROLL_LOOP_START_INF_FOR
2767
2768#ifdef STBIR_MEMCPY
2769#undef STBIR_MEMCPY
2770#endif
2771#define STBIR_MEMCPY stbir_simd_memcpy
2772
2773// override normal use of memcpy with much simpler copy (faster and smaller with our sized copies)
2774static void stbir_simd_memcpy( void * dest, void const * src, size_t bytes )
2775{
2776 char STBIR_SIMD_STREAMOUT_PTR (*) d = (char*) dest;
2777 char STBIR_SIMD_STREAMOUT_PTR( * ) d_end = ((char*) dest) + bytes;
2778 char const * s = (char const*) src;
2779
2780 // check overlaps
2781 STBIR_ASSERT( ( ( d >= ( (char*)src) + bytes ) ) || ( ( d + bytes ) <= (char*)src ) );
2782
2783 if ( bytes < (16*stbir__simdfX_float_count) )
2784 {
2785 if ( bytes < 16 )
2786 {
2787 if ( bytes )
2788 {
2789 STBIR_SIMD_NO_UNROLL_LOOP_START
2790 do
2791 {
2792 STBIR_SIMD_NO_UNROLL(d);
2793 d[ 0 ] = s[ 0 ];
2794 ++d;
2795 ++s;
2796 } while ( d < d_end );
2797 }
2798 }
2799 else
2800 {
2801 stbir__simdf x;
2802 // do one unaligned to get us aligned for the stream out below
2803 stbir__simdf_load( x, s );
2804 stbir__simdf_store( d, x );
2805 s += ( ( ( (size_t)d ) + 16 ) & ~15 ) - (size_t)d;
2806 d = (char*)( ( ( (size_t)d ) + 16 ) & ~15 );
2807
2808 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
2809 for(;;)
2810 {
2811 STBIR_SIMD_NO_UNROLL(d);
2812
2813 if ( d > ( d_end - 16 ) )
2814 {
2815 if ( d == d_end )
2816 return;
2817 s -= d - ( d_end - 16 );
2818 d = d_end - 16;
2819 }
2820
2821 stbir__simdf_load( x, s );
2822 stbir__simdf_store( d, x );
2823 d += 16;
2824 s += 16;
2825 }
2826 }
2827 }
2828 else
2829 {
2830 stbir__simdfX x0,x1,x2,x3;
2831
2832 // do one unaligned to get us aligned for the stream out below
2833 stbir__simdfX_load( x0, s + 0*stbir__simdfX_float_count );
2834 stbir__simdfX_load( x1, s + 4*stbir__simdfX_float_count );
2835 stbir__simdfX_load( x2, s + 8*stbir__simdfX_float_count );
2836 stbir__simdfX_load( x3, s + 12*stbir__simdfX_float_count );
2837 stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 );
2838 stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 );
2839 stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 );
2840 stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 );
2841 s += ( ( ( (size_t)d ) + (16*stbir__simdfX_float_count) ) & ~((16*stbir__simdfX_float_count)-1) ) - (size_t)d;
2842 d = (char*)( ( ( (size_t)d ) + (16*stbir__simdfX_float_count) ) & ~((16*stbir__simdfX_float_count)-1) );
2843
2844 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
2845 for(;;)
2846 {
2847 STBIR_SIMD_NO_UNROLL(d);
2848
2849 if ( d > ( d_end - (16*stbir__simdfX_float_count) ) )
2850 {
2851 if ( d == d_end )
2852 return;
2853 s -= d - ( d_end - (16*stbir__simdfX_float_count) );
2854 d = d_end - (16*stbir__simdfX_float_count);
2855 }
2856
2857 stbir__simdfX_load( x0, s + 0*stbir__simdfX_float_count );
2858 stbir__simdfX_load( x1, s + 4*stbir__simdfX_float_count );
2859 stbir__simdfX_load( x2, s + 8*stbir__simdfX_float_count );
2860 stbir__simdfX_load( x3, s + 12*stbir__simdfX_float_count );
2861 stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 );
2862 stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 );
2863 stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 );
2864 stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 );
2865 d += (16*stbir__simdfX_float_count);
2866 s += (16*stbir__simdfX_float_count);
2867 }
2868 }
2869}
2870
2871// float copy that is specifically intentionally overlapping (src is below dest by less than the count, so a forward
2872// copy replicates the first floats through the rest.
2873static void stbir_overlapping_memcpy( float * dest, float const * src, size_t count )
2874{
2875 float STBIR_SIMD_STREAMOUT_PTR (*) sd = (float*) src;
2876 float STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((float*) src) + count;
2877 ptrdiff_t ofs_to_dest = dest - src;
2878
2879 if ( ofs_to_dest >= 4 ) // is the overlap more than 4 floats away?
2880 {
2881 float STBIR_SIMD_STREAMOUT_PTR( * ) s_end4 = ((float*) src) + (count&~(size_t)3);
2882 STBIR_SIMD_NO_UNROLL_LOOP_START
2883 do
2884 {
2885 stbir__simdf x;
2886 STBIR_SIMD_NO_UNROLL(sd);
2887 stbir__simdf_load( x, sd );
2888 stbir__simdf_store( ( sd + ofs_to_dest ), x );
2889 sd += 4;
2890 } while ( sd < s_end4 );
2891
2892 if ( sd == s_end )
2893 return;
2894 }
2895
2896 do
2897 {
2898 STBIR_SIMD_NO_UNROLL(sd);
2899 sd[ ofs_to_dest ] = sd[ 0 ];
2900 ++sd;
2901 } while ( sd < s_end );
2902}
2903
2904#else // no SSE2
2905
2906// when in scalar mode, we let unrolling happen, so this macro just does the __restrict
2907#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star )
2908#define STBIR_SIMD_NO_UNROLL(ptr)
2909#define STBIR_SIMD_NO_UNROLL_LOOP_START
2910#define STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
2911
2912#endif // SSE2
2913
2914
2915#ifdef STBIR_PROFILE
2916
2917#ifndef STBIR_PROFILE_FUNC
2918
2919#if ( defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(__SSE2__) || defined(STBIR_SSE) || defined( _M_IX86_FP ) || defined(__i386) || defined( __i386__ ) || defined( _M_IX86 ) || defined( _X86_ ) ) && !defined(_M_ARM64EC)
2920
2921#ifdef _MSC_VER
2922
2923 STBIRDEF stbir_uint64 __rdtsc();
2924 #define STBIR_PROFILE_FUNC() __rdtsc()
2925
2926#else // non msvc
2927
2928 static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC()
2929 {
2930 stbir_uint32 lo, hi;
2931 asm volatile ("rdtsc" : "=a" (lo), "=d" (hi) );
2932 return ( ( (stbir_uint64) hi ) << 32 ) | ( (stbir_uint64) lo );
2933 }
2934
2935#endif // msvc
2936
2937#elif defined( STBIR__ARM64 ) || defined(__ARM_NEON__)
2938
2939#if defined( _MSC_VER ) && !defined(__clang__)
2940
2941 #include <intrin.h> // _ReadStatusReg, ARM64_SYSREG
2942 #ifndef ARM64_CNTVCT // winnt.h defines it; without windows.h build the same encoding (CNTVCT_EL0, the generic timer counter)
2943 #define ARM64_CNTVCT ARM64_SYSREG(3,3,14,0,2)
2944 #endif
2945 #define STBIR_PROFILE_FUNC() _ReadStatusReg(ARM64_CNTVCT)
2946
2947#else
2948
2949 static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC()
2950 {
2951 stbir_uint64 tsc;
2952 asm volatile("mrs %0, cntvct_el0" : "=r" (tsc));
2953 return tsc;
2954 }
2955
2956#endif
2957
2958#else // x64, arm
2959
2960#error Unknown platform for profiling.
2961
2962#endif // x64, arm
2963
2964#endif // STBIR_PROFILE_FUNC
2965
2966#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO ,stbir__per_split_info * split_info
2967#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO ,split_info
2968
2969#define STBIR_ONLY_PROFILE_BUILD_GET_INFO ,stbir__info * profile_info
2970#define STBIR_ONLY_PROFILE_BUILD_SET_INFO ,profile_info
2971
2972// super light-weight micro profiler
2973#define STBIR_PROFILE_START_ll( info, wh ) { stbir_uint64 wh##thiszonetime = STBIR_PROFILE_FUNC(); stbir_uint64 * wh##save_parent_excluded_ptr = info->current_zone_excluded_ptr; stbir_uint64 wh##current_zone_excluded = 0; info->current_zone_excluded_ptr = &wh##current_zone_excluded;
2974#define STBIR_PROFILE_END_ll( info, wh ) wh##thiszonetime = STBIR_PROFILE_FUNC() - wh##thiszonetime; info->profile.named.wh += wh##thiszonetime - wh##current_zone_excluded; *wh##save_parent_excluded_ptr += wh##thiszonetime; info->current_zone_excluded_ptr = wh##save_parent_excluded_ptr; }
2975#define STBIR_PROFILE_FIRST_START_ll( info, wh ) { int zone; info->current_zone_excluded_ptr = &info->profile.named.total; for(zone=0;zone<(int)STBIR__ARRAY_SIZE(info->profile.array);zone++) info->profile.array[zone]=0; } STBIR_PROFILE_START_ll( info, wh );
2976#define STBIR_PROFILE_CLEAR_EXTRAS_ll( info, num ) { int extra; for(extra=1;extra<(num);extra++) { int zone; for(zone=0;zone<(int)STBIR__ARRAY_SIZE((info)->profile.array);zone++) (info)[extra].profile.array[zone]=0; } }
2977
2978// for thread data
2979#define STBIR_PROFILE_START( wh ) STBIR_PROFILE_START_ll( split_info, wh )
2980#define STBIR_PROFILE_END( wh ) STBIR_PROFILE_END_ll( split_info, wh )
2981#define STBIR_PROFILE_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( split_info, wh )
2982#define STBIR_PROFILE_CLEAR_EXTRAS() STBIR_PROFILE_CLEAR_EXTRAS_ll( split_info, split_count )
2983#define STBIR_PROFILE_NEST_IN_FIRST( extra ) { if ( ( extra ) != split_info ) ( extra )->current_zone_excluded_ptr = split_info->current_zone_excluded_ptr; } // an extra split's zones count against the first split's open zone
2984
2985// for build data
2986#define STBIR_PROFILE_BUILD_START( wh ) STBIR_PROFILE_START_ll( profile_info, wh )
2987#define STBIR_PROFILE_BUILD_END( wh ) STBIR_PROFILE_END_ll( profile_info, wh )
2988#define STBIR_PROFILE_BUILD_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( profile_info, wh )
2989#define STBIR_PROFILE_BUILD_CLEAR( info ) { int zone; for(zone=0;zone<(int)STBIR__ARRAY_SIZE(info->profile.array);zone++) info->profile.array[zone]=0; }
2990
2991#else // no profile
2992
2993#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO
2994#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO
2995
2996#define STBIR_ONLY_PROFILE_BUILD_GET_INFO
2997#define STBIR_ONLY_PROFILE_BUILD_SET_INFO
2998
2999#define STBIR_PROFILE_START( wh )
3000#define STBIR_PROFILE_END( wh )
3001#define STBIR_PROFILE_FIRST_START( wh )
3002#define STBIR_PROFILE_CLEAR_EXTRAS( )
3003#define STBIR_PROFILE_NEST_IN_FIRST( extra )
3004
3005#define STBIR_PROFILE_BUILD_START( wh )
3006#define STBIR_PROFILE_BUILD_END( wh )
3007#define STBIR_PROFILE_BUILD_FIRST_START( wh )
3008#define STBIR_PROFILE_BUILD_CLEAR( info )
3009
3010#endif // stbir_profile
3011
3012#ifndef STBIR_MEMCPY
3013// For memcpy
3014#include <string.h>
3015#define STBIR_MEMCPY( dest, src, len ) memcpy( dest, src, len )
3016#endif
3017
3018#ifndef STBIR_SIMD
3019
3020// float copy that is specifically intentionally overlapping (src is below dest by less than the count, so a forward
3021// copy replicates the first floats through the rest.
3022static void stbir_overlapping_memcpy( float * dest, float const * src, size_t count )
3023{
3024 float STBIR_SIMD_STREAMOUT_PTR (*) sd = (float*) src;
3025 float STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((float*) src) + count;
3026 ptrdiff_t ofs_to_dest = dest - src;
3027
3028 if ( ofs_to_dest >= 2 ) // is the overlap more than 2 floats away
3029 {
3030 float STBIR_SIMD_STREAMOUT_PTR( * ) s_end2 = ((float*) src) + (count&~(size_t)1);
3031
3032 STBIR_NO_UNROLL_LOOP_START
3033 do
3034 {
3035 float a,b;
3036 STBIR_NO_UNROLL(sd);
3037 a = sd[0];
3038 b = sd[1];
3039 sd[ ofs_to_dest ] = a;
3040 sd[ ofs_to_dest + 1 ] = b;
3041 sd += 2;
3042 } while ( sd < s_end2 );
3043
3044 if ( sd == s_end )
3045 return;
3046 }
3047
3048 STBIR_NO_UNROLL_LOOP_START
3049 do
3050 {
3051 STBIR_NO_UNROLL(sd);
3052 sd[ ofs_to_dest ] = sd[ 0 ];
3053 ++sd;
3054 } while ( sd < s_end );
3055}
3056
3057#endif
3058
3059// the built-in kernels (all in stbir_real; the generator rounds the weight to float once, on the way into the
3060// coefficient table). The box filter as the overlap of a pixel with the output pixel's footprint: 1 within
3061// 0.5 - scale / 2 of the center, 0 beyond 0.5 + scale / 2, a ramp between.
3062static stbir_real stbir__filter_trapezoid(stbir_real x, stbir_real scale, void * user_data)
3063{
3064 stbir_real halfscale = scale * (stbir_real)0.5;
3065 stbir_real t = (stbir_real)0.5 + halfscale;
3066 stbir_real r = (stbir_real)0.5 - halfscale;
3067 STBIR_ASSERT(scale <= (stbir_real)1.0);
3068 STBIR__UNUSED(user_data);
3069
3070 if ( x < (stbir_real) 0.0 ) x = -x;
3071
3072 if ( x >= t )
3073 return (stbir_real)0.0;
3074 if ( x <= r )
3075 return (stbir_real)1.0;
3076 return ( t - x ) / scale;
3077}
3078
3079// the box's extent depends on the scale, so it is given as a radius (in input pixels): one multiply, so an integer
3080// ratio's tap count is exact
3081static stbir_real stbir__radius_trapezoid(stbir_real scale, stbir_real inv_scale, int is_upsample, void * user_data)
3082{
3083 STBIR__UNUSED(scale);
3084 STBIR__UNUSED(is_upsample);
3085 STBIR__UNUSED(user_data);
3086 return (stbir_real)0.5 * inv_scale + (stbir_real)0.5; // ( 0.5 + scale / 2 ) output pixels downsampling, 0.5 + inv_scale / 2 input pixels upsampling: the same number
3087}
3088
3089static stbir_real stbir__filter_triangle(stbir_real x, stbir_real s, void * user_data)
3090{
3091 STBIR__UNUSED(s);
3092 STBIR__UNUSED(user_data);
3093
3094 if ( x < (stbir_real) 0.0 ) x = -x;
3095
3096 if (x <= (stbir_real) 1.0)
3097 return (stbir_real) 1.0 - x;
3098 else
3099 return (stbir_real) 0.0;
3100}
3101
3102static stbir_real stbir__filter_point(stbir_real x, stbir_real s, void * user_data)
3103{
3104 STBIR__UNUSED(x);
3105 STBIR__UNUSED(s);
3106 STBIR__UNUSED(user_data);
3107
3108 return (stbir_real) 1.0;
3109}
3110
3111// the Mitchell-Netrevalli cubics with C = (1-B)/2: B=1 is the cubic b-spline, 1/3 Mitchell, 0 Catmull-Rom
3112static stbir__inline stbir_real stbir__mitchell_netrevalli(stbir_real x, stbir_real B)
3113{
3114 if ( x < (stbir_real) 0.0 ) x = -x;
3115
3116 if (x < (stbir_real) 1.0)
3117 return (((stbir_real) 6.0 - (stbir_real) 2.0*B) + x*x*(((stbir_real) 9.0 - (stbir_real) 6.0*B)*x - ((stbir_real) 15.0 - (stbir_real) 9.0*B)))/(stbir_real) 6.0;
3118 else if (x < (stbir_real) 2.0)
3119 return (((stbir_real) 12.0 - (stbir_real) 4.0*B) + x*(((stbir_real) 12.0*B - (stbir_real) 24.0) + x*(((stbir_real) 15.0 - (stbir_real) 9.0*B) + ((stbir_real) 2.0*B - (stbir_real) 3.0)*x)))/(stbir_real) 6.0;
3120
3121 return (stbir_real) 0.0;
3122}
3123
3124static stbir_real stbir__filter_cubic(stbir_real x, stbir_real s, void * user_data)
3125{
3126 STBIR__UNUSED(s);
3127 STBIR__UNUSED(user_data);
3128
3129 return stbir__mitchell_netrevalli( x, (stbir_real) 1.0 );
3130}
3131
3132// Catmull-Rom is the family at B=0, written out (it's the upsampling default)
3133static stbir_real stbir__filter_catmullrom(stbir_real x, stbir_real s, void * user_data)
3134{
3135 STBIR__UNUSED(s);
3136 STBIR__UNUSED(user_data);
3137
3138 if ( x < (stbir_real) 0.0 ) x = -x;
3139
3140 if (x < (stbir_real) 1.0)
3141 return (stbir_real) 1.0 - x*x*((stbir_real) 2.5 - (stbir_real) 1.5*x);
3142 else if (x < (stbir_real) 2.0)
3143 return (stbir_real) 2.0 - x*((stbir_real) 4.0 + x*((stbir_real) 0.5*x - (stbir_real) 2.5));
3144
3145 return (stbir_real) 0.0;
3146}
3147
3148static stbir_real stbir__filter_mitchell(stbir_real x, stbir_real s, void * user_data)
3149{
3150 STBIR__UNUSED(s);
3151 STBIR__UNUSED(user_data);
3152
3153 return stbir__mitchell_netrevalli( x, (stbir_real) 1.0 / (stbir_real) 3.0 );
3154}
3155
3156static stbir_real stbir__filter_mitchell_quarter(stbir_real x, stbir_real s, void * user_data)
3157{
3158 STBIR__UNUSED(s);
3159 STBIR__UNUSED(user_data);
3160
3161 return stbir__mitchell_netrevalli( x, (stbir_real) 0.25 );
3162}
3163
3164static stbir_real stbir__filter_mitchell_sixth(stbir_real x, stbir_real s, void * user_data)
3165{
3166 STBIR__UNUSED(s);
3167 STBIR__UNUSED(user_data);
3168
3169 return stbir__mitchell_netrevalli( x, (stbir_real) 1.0 / (stbir_real) 6.0 );
3170}
3171
3172static stbir_real stbir__filter_mitchell_neg_sixth(stbir_real x, stbir_real s, void * user_data)
3173{
3174 STBIR__UNUSED(s);
3175 STBIR__UNUSED(user_data);
3176
3177 return stbir__mitchell_netrevalli( x, (stbir_real) -1.0 / (stbir_real) 6.0 );
3178}
3179
3180static stbir_real stbir__filter_mitchell_neg_third(stbir_real x, stbir_real s, void * user_data)
3181{
3182 STBIR__UNUSED(s);
3183 STBIR__UNUSED(user_data);
3184
3185 return stbir__mitchell_netrevalli( x, (stbir_real) -1.0 / (stbir_real) 3.0 );
3186}
3187
3188// sin(pi x) / (pi x), done here so the coefficients match on every platform (no libm). sin(pi x) flips sign at every
3189// whole x, so reduce to the nearest whole and run the series on [-pi/2, pi/2] (good to ~1e-9).
3190static stbir_real stbir__sinc_pi(stbir_real x)
3191{
3192 stbir_real const pi = (stbir_real) 3.14159265358979323846;
3193 stbir_real u, u2, s;
3194 int n;
3195
3196 if ( x < (stbir_real) 0.0 ) x = -x;
3197
3198 if (x < (stbir_real) 1e-4)
3199 {
3200 stbir_real px = pi * x;
3201 return (stbir_real) 1.0 - px*px / (stbir_real) 6.0;
3202 }
3203
3204 n = (int) ( x + (stbir_real) 0.5 );
3205 u = pi * ( x - (stbir_real) n );
3206 u2 = u*u;
3207 s = (stbir_real) 1.0 - u2 / (stbir_real) 156.0; // the series from the inside out: 1 - u^2 / ( 2k ( 2k + 1 ) )
3208 s = (stbir_real) 1.0 - u2 / (stbir_real) 110.0 * s;
3209 s = (stbir_real) 1.0 - u2 / (stbir_real) 72.0 * s;
3210 s = (stbir_real) 1.0 - u2 / (stbir_real) 42.0 * s;
3211 s = (stbir_real) 1.0 - u2 / (stbir_real) 20.0 * s;
3212 s = (stbir_real) 1.0 - u2 / (stbir_real) 6.0 * s;
3213 s = u * s;
3214 if ( n & 1 ) s = -s;
3215
3216 return s / ( pi * x );
3217}
3218
3219// Lanczos: a sinc windowed by a sinc stretched to the support
3220static stbir_real stbir__filter_lanczos(stbir_real x, stbir_real support)
3221{
3222 if ( x < (stbir_real) 0.0 ) x = -x;
3223
3224 if (x < support)
3225 return stbir__sinc_pi( x ) * stbir__sinc_pi( x / support );
3226
3227 return (stbir_real) 0.0;
3228}
3229
3230static stbir_real stbir__filter_lanczos4(stbir_real x, stbir_real s, void * user_data)
3231{
3232 STBIR__UNUSED(s);
3233 STBIR__UNUSED(user_data);
3234
3235 return stbir__filter_lanczos( x, (stbir_real) 2.0 );
3236}
3237
3238static stbir_real stbir__filter_lanczos5(stbir_real x, stbir_real s, void * user_data)
3239{
3240 STBIR__UNUSED(s);
3241 STBIR__UNUSED(user_data);
3242
3243 return stbir__filter_lanczos( x, (stbir_real) 2.5 );
3244}
3245
3246// Casey Muratori's Bink 2 half pixel kernel ( 1 -4 19 19 -4 1 ) / 32: a Catmull-Rom spline through knots every half
3247// pixel (1 at 0, the weights at the half pixels, 0 at the whole ones), padded at both ends so the four points stay in the table
3248static stbir_real stbir__filter_muratori6(stbir_real x, stbir_real s, void * user_data)
3249{
3250 static stbir_real const knots[9] = { (stbir_real) 19.0/(stbir_real) 32.0, (stbir_real) 1.0, (stbir_real) 19.0/(stbir_real) 32.0, (stbir_real) 0.0, (stbir_real) -4.0/(stbir_real) 32.0, (stbir_real) 0.0, (stbir_real) 1.0/(stbir_real) 32.0, (stbir_real) 0.0, (stbir_real) 0.0 };
3251 stbir_real t, p0, p1, p2, p3;
3252 int i;
3253 STBIR__UNUSED(s);
3254 STBIR__UNUSED(user_data);
3255
3256 if ( x < (stbir_real) 0.0 ) x = -x;
3257
3258 if ( x >= (stbir_real) 3.0 )
3259 return (stbir_real) 0.0;
3260
3261 t = x * (stbir_real) 2.0;
3262 i = (int) t;
3263 t -= (stbir_real) i;
3264
3265 p0 = knots[i];
3266 p1 = knots[i+1];
3267 p2 = knots[i+2];
3268 p3 = knots[i+3];
3269
3270 return (stbir_real) 0.5*((stbir_real) 2.0*p1 + t*((p2 - p0) + t*(((stbir_real) 2.0*p0 - (stbir_real) 5.0*p1 + (stbir_real) 4.0*p2 - p3) + t*(((stbir_real) 3.0*p1 - (stbir_real) 3.0*p2) + (p3 - p0)))));
3271}
3272
3273// point sampling takes the one nearest input pixel in both directions.
3274static stbir_real stbir__radius_point(stbir_real scale, stbir_real inv_scale, int is_upsample, void * user_data)
3275{
3276 STBIR__UNUSED(scale);
3277 STBIR__UNUSED(inv_scale);
3278 STBIR__UNUSED(is_upsample);
3279 STBIR__UNUSED(user_data);
3280 return (stbir_real)0.5;
3281}
3282
3283// the other built-ins' supports: the kernel's radius in filter pixels
3284
3285static stbir_real stbir__support_one(stbir_real scale, void * user_data)
3286{
3287 STBIR__UNUSED(scale);
3288 STBIR__UNUSED(user_data);
3289 return (stbir_real)1.0;
3290}
3291
3292static stbir_real stbir__support_two(stbir_real scale, void * user_data)
3293{
3294 STBIR__UNUSED(scale);
3295 STBIR__UNUSED(user_data);
3296 return (stbir_real)2.0;
3297}
3298
3299static stbir_real stbir__support_two_and_half(stbir_real scale, void * user_data)
3300{
3301 STBIR__UNUSED(scale);
3302 STBIR__UNUSED(user_data);
3303 return (stbir_real)2.5;
3304}
3305
3306static stbir_real stbir__support_three(stbir_real scale, void * user_data)
3307{
3308 STBIR__UNUSED(scale);
3309 STBIR__UNUSED(user_data);
3310 return (stbir_real)3.0;
3311}
3312
3313// the built-in filters, indexed by stbir_filter (everything below STBIR_FILTER_OTHER
3314static stbir__kernel_callback_new * stbir__builtin_kernels[STBIR_FILTER_OTHER] =
3315{
3316 0, // STBIR_FILTER_DEFAULT (resolved before the lookup)
3317 stbir__filter_trapezoid, // STBIR_FILTER_BOX
3318 stbir__filter_triangle, // STBIR_FILTER_TRIANGLE
3319 stbir__filter_cubic, // STBIR_FILTER_CUBICBSPLINE
3320 stbir__filter_catmullrom, // STBIR_FILTER_CATMULLROM
3321 stbir__filter_mitchell, // STBIR_FILTER_MITCHELL
3322 stbir__filter_point, // STBIR_FILTER_POINT_SAMPLE
3323 stbir__filter_mitchell_quarter, // STBIR_FILTER_MITCHELL_QUARTER
3324 stbir__filter_mitchell_sixth, // STBIR_FILTER_MITCHELL_SIXTH
3325 stbir__filter_mitchell_neg_sixth, // STBIR_FILTER_MITCHELL_NEG_SIXTH
3326 stbir__filter_mitchell_neg_third, // STBIR_FILTER_MITCHELL_NEG_THIRD
3327 stbir__filter_lanczos4, // STBIR_FILTER_LANCZOS4
3328 stbir__filter_lanczos5, // STBIR_FILTER_LANCZOS5
3329 stbir__filter_muratori6, // STBIR_FILTER_MURATORI6
3330};
3331
3332static stbir__support_callback_new * stbir__builtin_supports[STBIR_FILTER_OTHER] =
3333{
3334 0, // STBIR_FILTER_DEFAULT
3335 0, // STBIR_FILTER_BOX (a radius)
3336 stbir__support_one, // STBIR_FILTER_TRIANGLE
3337 stbir__support_two, // STBIR_FILTER_CUBICBSPLINE
3338 stbir__support_two, // STBIR_FILTER_CATMULLROM
3339 stbir__support_two, // STBIR_FILTER_MITCHELL
3340 0, // STBIR_FILTER_POINT_SAMPLE (a radius)
3341 stbir__support_two, // STBIR_FILTER_MITCHELL_QUARTER
3342 stbir__support_two, // STBIR_FILTER_MITCHELL_SIXTH
3343 stbir__support_two, // STBIR_FILTER_MITCHELL_NEG_SIXTH
3344 stbir__support_two, // STBIR_FILTER_MITCHELL_NEG_THIRD
3345 stbir__support_two, // STBIR_FILTER_LANCZOS4
3346 stbir__support_two_and_half, // STBIR_FILTER_LANCZOS5
3347 stbir__support_three, // STBIR_FILTER_MURATORI6
3348};
3349
3350static stbir__radius_callback_new * stbir__builtin_radii[STBIR_FILTER_OTHER] =
3351{
3352 0, // STBIR_FILTER_DEFAULT
3353 stbir__radius_trapezoid, // STBIR_FILTER_BOX
3354 0, 0, 0, 0, // STBIR_FILTER_TRIANGLE .. STBIR_FILTER_MITCHELL (supports)
3355 stbir__radius_point, // STBIR_FILTER_POINT_SAMPLE
3356 0, 0, 0, 0, 0, 0, 0, // STBIR_FILTER_MITCHELL_QUARTER .. STBIR_FILTER_MURATORI6 (supports)
3357};
3358
3359// upsample (1:1 included), and a 1:1 with the output on the input's pixel grid (a copy)
3360static int stbir__scale_is_upsample( stbir__scale_info const * scale_info )
3361{
3362 return ( scale_info->scale >= (stbir_real) 1.0 );
3363}
3364
3365static int stbir__scale_is_copy( stbir__scale_info const * scale_info )
3366{
3367 return ( scale_info->positions_exact ) && ( scale_info->scale_numerator == scale_info->scale_denominator );
3368}
3369
3370// the sum under which a coefficient set is zeroed
3371static stbir_real stbir__too_small_coefficient_sum_for( stbir__scale_info const * scale_info )
3372{
3373 if ( stbir__scale_is_upsample( scale_info ) )
3374 return stbir__too_small_coefficient_sum;
3375 return stbir__too_small_coefficient_sum * scale_info->scale;
3376}
3377
3378// floor and ceil without math.h (the header doesn't use it), either sign, for pixel coordinates
3379// and tap counts - the range is known to fit an int
3380static int stbir__floor_to_int( stbir_real x )
3381{
3382 int i = (int) x;
3383 return ( (stbir_real)i > x ) ? i - 1 : i;
3384}
3385
3386static int stbir__ceil_to_int( stbir_real x )
3387{
3388 int i = (int) x;
3389 return ( (stbir_real)i < x ) ? i + 1 : i;
3390}
3391
3392// the filter's radius in input pixels. a radius callback gives it directly. a support callback gives the radius in
3393// filter pixels (called with the ratio <= 1): input pixels when upsampling, output pixels when downsampling, so it
3394// is scaled by inv_scale there. the old float form is converted the same way.
3395static stbir_real stbir__filter_radius_in_input_pixels( stbir__radius_callback_new * radius, stbir__support_callback_new * support_new, stbir__support_callback * support_old, stbir__scale_info const * scale_info, void * user_data )
3396{
3397 int is_upsample = stbir__scale_is_upsample( scale_info );
3398 stbir_real r;
3399
3400 if ( radius )
3401 r = radius( scale_info->scale, scale_info->inv_scale, is_upsample, user_data );
3402 else if ( is_upsample )
3403 {
3404 if ( support_new )
3405 r = support_new( scale_info->inv_scale, user_data );
3406 else
3407 r = (stbir_real) support_old( (float) scale_info->inv_scale, user_data );
3408 }
3409 else
3410 {
3411 if ( support_new )
3412 r = support_new( scale_info->scale, user_data );
3413 else
3414 r = (stbir_real) support_old( (float) scale_info->scale, user_data );
3415 r *= scale_info->inv_scale;
3416 }
3417
3418 // at least half an input pixel (point sampling's), so every output has a tap: the downsample generator walks the
3419 // inputs and gives each output the ones within reach. a user filter's might be under; the built-ins never are
3420 if ( !( r >= (stbir_real)0.5 ) )
3421 r = (stbir_real)0.5;
3422
3423 return r;
3424}
3425
3426static stbir_real stbir__filter_radius_in_output_pixels( stbir__radius_callback_new * radius, stbir__support_callback_new * support_new, stbir__support_callback * support_old, stbir__scale_info const * scale_info, void * user_data )
3427{
3428 return stbir__filter_radius_in_input_pixels( radius, support_new, support_old, scale_info, user_data ) / scale_info->inv_scale;
3429}
3430
3431// This is the maximum number of input samples that can affect an output sample
3432// with the given filter from the output pixel's perspective
3433static int stbir__get_filter_pixel_width( stbir__radius_callback_new * radius, stbir__support_callback_new * support_new, stbir__support_callback * support_old, stbir__scale_info const * scale_info, void * user_data)
3434{
3435 STBIR_ASSERT( ( radius != 0 ) || ( support_new != 0 ) || ( support_old != 0 ) );
3436 return stbir__ceil_to_int( (stbir_real)2.0 * stbir__filter_radius_in_input_pixels( radius, support_new, support_old, scale_info, user_data ) );
3437}
3438
3439// this is how many coefficients per run of the filter (which is different from the filter_pixel_width depending on if we are scattering or gathering)
3440static int stbir__get_coefficient_width(stbir__sampler * samp, int is_gather, void * user_data)
3441{
3442 stbir__scale_info const * scale_info = &samp->scale_info;
3443
3444 switch( is_gather )
3445 {
3446 case 1:
3447 case 2:
3448 // one of slack: a tap exactly on the far edge is in the range (stbir__exact_pixel_range), one more than ceil( 2 x radius )
3449 return stbir__ceil_to_int( (stbir_real)2.0 * stbir__filter_radius_in_input_pixels( samp->filter_radius, samp->filter_support_new, samp->filter_support_old, scale_info, user_data ) ) + 1;
3450 case 0:
3451 return stbir__ceil_to_int( (stbir_real)2.0 * stbir__filter_radius_in_output_pixels( samp->filter_radius, samp->filter_support_new, samp->filter_support_old, scale_info, user_data ) );
3452 default:
3453 STBIR_ASSERT( (is_gather >= 0 ) && (is_gather <= 2 ) );
3454 return 0;
3455 }
3456}
3457
3458static int stbir__get_contributors(stbir__sampler * samp, int is_gather)
3459{
3460 if (is_gather)
3461 return samp->scale_info.output_sub_size;
3462 else
3463 return (samp->scale_info.input_full_size + samp->filter_pixel_margin * 2);
3464}
3465
3466static int stbir__edge_zero_full( int n, int max )
3467{
3468 STBIR__UNUSED(n);
3469 STBIR__UNUSED(max);
3470 return 0; // NOTREACHED
3471}
3472
3473static int stbir__edge_clamp_full( int n, int max )
3474{
3475 if (n < 0)
3476 return 0;
3477
3478 if (n >= max)
3479 return max - 1;
3480
3481 return n; // NOTREACHED
3482}
3483
3484static int stbir__edge_reflect_full( int n, int max )
3485{
3486 if (n < 0)
3487 {
3488 if (n > -max)
3489 return -n;
3490 else
3491 return max - 1;
3492 }
3493
3494 if (n >= max)
3495 {
3496 if ( ( n - max ) >= max ) // (as n >= 2 max, without the overflow)
3497 return 0;
3498 else
3499 return ( max - 1 ) - ( n - max );
3500 }
3501
3502 return n; // NOTREACHED
3503}
3504
3505static int stbir__edge_wrap_full( int n, int max )
3506{
3507 if (n >= 0)
3508 return (n % max);
3509 else
3510 {
3511 int m = (-n) % max;
3512
3513 if (m != 0)
3514 m = max - m;
3515
3516 return (m);
3517 }
3518}
3519
3520typedef int stbir__edge_wrap_func( int n, int max );
3521static stbir__edge_wrap_func * stbir__edge_wrap_slow[] =
3522{
3523 stbir__edge_clamp_full, // STBIR_EDGE_CLAMP
3524 stbir__edge_reflect_full, // STBIR_EDGE_REFLECT
3525 stbir__edge_wrap_full, // STBIR_EDGE_WRAP
3526 stbir__edge_zero_full, // STBIR_EDGE_ZERO
3527};
3528
3529stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max)
3530{
3531 // avoid per-pixel switch
3532 if (n >= 0 && n < max)
3533 return n;
3534 return stbir__edge_wrap_slow[edge]( n, max );
3535}
3536
3537// the ranges' edge handling: in pixel ranges may hang off the image with WRAP (the decoder wraps them, within a width
3538// each side), out pixel ranges are clipped to the output
3539static void stbir__clip_in_pixel_range( int first, int last, int input_size, stbir_edge edge, int * first_pixel, int * last_pixel )
3540{
3541 if ( last < first )
3542 last = first; // point sample mode can span a value *right* at 0.5, and cause these to cross
3543 if ( edge == STBIR_EDGE_WRAP )
3544 {
3545 if ( first < -input_size )
3546 first = -input_size;
3547 if ( ( last - input_size ) >= input_size ) // (as last >= 2 input_size, without the overflow)
3548 last = ( input_size - 1 ) + input_size;
3549 }
3550 *first_pixel = first;
3551 *last_pixel = last;
3552}
3553
3554static void stbir__clip_out_pixel_range( int first, int last, int out_size, int * first_pixel, int * last_pixel )
3555{
3556 if ( first < 0 )
3557 first = 0;
3558 if ( last >= out_size )
3559 last = out_size - 1;
3560 *first_pixel = first;
3561 *last_pixel = last;
3562}
3563
3564// The pixels within support of the center numer / den (quotient kept apart, only the remainder meets the real math).
3565// On the far edge is in, on the near edge out; stbir__tie_epsilon lifts the tie above rounding noise (point sampling
3566// hits both edges at every even ratio). The output range and the real chain ranges below use the same rule.
3567#define stbir__tie_epsilon ( ( sizeof( stbir_real ) == 4 ) ? (stbir_real)( 1.0 / ( 1 << 18 ) ) : (stbir_real)( 1.0 / ( (stbir_int64)1 << 36 ) ) )
3568
3569// The input pixels within out_filter_radius (output pixels) of an output pixel's center.
3570static void stbir__calculate_in_pixel_range( int * first_pixel, int * last_pixel, stbir_real out_pixel_center, stbir_real out_filter_radius, stbir_real inv_scale, stbir_real out_shift, int input_size, stbir_edge edge )
3571{
3572 stbir_real in_pixel_influence_lowerbound = ( out_pixel_center - out_filter_radius + out_shift ) * inv_scale;
3573 stbir_real in_pixel_influence_upperbound = ( out_pixel_center + out_filter_radius + out_shift ) * inv_scale;
3574 stbir__clip_in_pixel_range( stbir__floor_to_int( in_pixel_influence_lowerbound + (stbir_real)0.5 + stbir__tie_epsilon ), stbir__floor_to_int( in_pixel_influence_upperbound - (stbir_real)0.5 + stbir__tie_epsilon ), input_size, edge, first_pixel, last_pixel );
3575}
3576
3577// Output pixel o's center against input pixel i's (in input pixels for upsamples, output pixels for downsamples). With an
3578// exact scale N/D and integer shift it is ( ( 2o + 1 + 2 shift ) D - ( 2i + 1 ) N ) / 2N (or 2D): integers until one
3579// multiply by the precomputed 1 / 2N (or 1 / 2D), exact to float at any coordinate.
3580static stbir_real stbir__tap_offset( stbir__scale_info const * scale_info, int o, int i, int in_input_pixels )
3581{
3582 if ( scale_info->positions_exact )
3583 {
3584 // (the difference is at most the filter's width times 2N or 2D: exact in a double, a float ulp in a float)
3585 stbir_int64 numer = ( 2 * (stbir_int64)o + 1 + 2 * (stbir_int64)scale_info->shift_pixels ) * (stbir_int64)scale_info->scale_denominator
3586 - ( 2 * (stbir_int64)i + 1 ) * (stbir_int64)scale_info->scale_numerator;
3587 return (stbir_real)numer * ( in_input_pixels ? scale_info->tap_scale_in : scale_info->tap_scale_out );
3588 }
3589 if ( in_input_pixels )
3590 return ( ( (stbir_real)o + (stbir_real)0.5 + scale_info->pixel_shift ) * scale_info->inv_scale ) - ( (stbir_real)i + (stbir_real)0.5 );
3591 return ( (stbir_real)o + (stbir_real)0.5 ) - ( ( (stbir_real)i + (stbir_real)0.5 ) * scale_info->scale - scale_info->pixel_shift );
3592}
3593
3594// the kernel's weight at x (in filter pixels): through the new form directly, or an old float kernel's floats
3595static stbir_real stbir__kernel_weight( stbir__kernel_callback_new * kernel_new, stbir__kernel_callback * kernel_old, stbir_real x, stbir_real scale, void * user_data )
3596{
3597 if ( kernel_new )
3598 return kernel_new( x, scale, user_data );
3599 return (stbir_real) kernel_old( (float) x, (float) scale, user_data );
3600}
3601
3602static void stbir__exact_pixel_range( stbir_int64 numer, stbir_int64 den, stbir_real den_reciprocal, stbir_real support, int * first, int * last )
3603{
3604 int q = (int)( numer / den );
3605 stbir_real f = (stbir_real)( numer % den ) * den_reciprocal; // ( -1, 1 ): the same sign as numer
3606 *first = q + stbir__floor_to_int( f - support + (stbir_real)0.5 + stbir__tie_epsilon );
3607 *last = q + stbir__floor_to_int( f + support - (stbir_real)0.5 + stbir__tie_epsilon );
3608}
3609
3610static void stbir__exact_output_range( stbir_int64 numer, stbir_int64 den, stbir_real den_reciprocal, stbir_real support, int * first, int * last )
3611{
3612 int q = (int)( numer / den );
3613 stbir_real f = (stbir_real)( numer % den ) * den_reciprocal;
3614 *first = q + stbir__floor_to_int( f - support - (stbir_real)0.5 - stbir__tie_epsilon ) + 1; // ceil, a tie exactly on it included
3615 *last = q + stbir__floor_to_int( f + support - (stbir_real)0.5 - stbir__tie_epsilon ); // a tie exactly on it excluded
3616}
3617
3618// The input pixels within the filter's support of output pixel o: out_filter_radius is the support in output pixels for
3619// the real chain, support the same in input pixels (the filter's own, for upsamples) for the exact positions - then
3620// the center is the one stbir__tap_offset weights from.
3621static void stbir__output_in_pixel_range( stbir__scale_info const * scale_info, int o, stbir_real out_filter_radius, stbir_real support, stbir_edge edge, int * first_pixel, int * last_pixel )
3622{
3623 if ( scale_info->positions_exact )
3624 {
3625 stbir_int64 numer = ( 2 * (stbir_int64)o + 1 + 2 * (stbir_int64)scale_info->shift_pixels ) * (stbir_int64)scale_info->scale_denominator; // output o's center in input pixels, over 2N
3626 int first, last;
3627 stbir__exact_pixel_range( numer, 2 * (stbir_int64)scale_info->scale_numerator, scale_info->tap_scale_in, support, &first, &last );
3628 stbir__clip_in_pixel_range( first, last, scale_info->input_full_size, edge, first_pixel, last_pixel );
3629 }
3630 else
3631 stbir__calculate_in_pixel_range( first_pixel, last_pixel, (stbir_real)o + (stbir_real)0.5, out_filter_radius, scale_info->inv_scale, scale_info->pixel_shift, scale_info->input_full_size, edge );
3632}
3633
3634static void stbir__calculate_coefficients_for_gather_upsample( stbir_real out_filter_radius, stbir_real support, stbir__kernel_callback_new * kernel_new, stbir__kernel_callback * kernel_old, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float* coefficient_group, int coefficient_width, stbir_edge edge, void * user_data )
3635{
3636 int n, end;
3637 int numerator = scale_info->scale_numerator;
3638 int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) );
3639
3640 // Looping through out pixels
3641 end = num_contributors; if ( polyphase ) end = numerator;
3642 for (n = 0; n < end; n++)
3643 {
3644 int i;
3645 int last_non_zero;
3646 int in_first_pixel, in_last_pixel;
3647
3648 stbir__output_in_pixel_range( scale_info, n, out_filter_radius, support, edge, &in_first_pixel, &in_last_pixel );
3649
3650 // make sure we never generate a range larger than our precalculated coeff width
3651 // this only happens in point sample mode, but it's a good safe thing to do anyway
3652 if ( ( in_last_pixel - in_first_pixel + 1 ) > coefficient_width )
3653 in_last_pixel = in_first_pixel + coefficient_width - 1;
3654
3655 last_non_zero = -1;
3656 for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
3657 {
3658 stbir_real weight = stbir__kernel_weight( kernel_new, kernel_old, stbir__tap_offset( scale_info, n, i + in_first_pixel, 1 ), scale_info->inv_scale, user_data );
3659 float coeff = (float) weight;
3660
3661 if ( ( weight < stbir__too_small_coefficient ) && ( weight > -stbir__too_small_coefficient ) )
3662 {
3663 if ( i == 0 ) // if we're at the front, just eat zero contributors
3664 {
3665 STBIR_ASSERT ( ( in_last_pixel - in_first_pixel ) != 0 ); // there should be at least one contrib
3666 ++in_first_pixel;
3667 i--;
3668 continue;
3669 }
3670 coeff = 0; // make sure is fully zero (should keep denormals away)
3671 }
3672 else
3673 last_non_zero = i;
3674
3675 coefficient_group[i] = coeff;
3676 }
3677
3678 in_last_pixel = last_non_zero+in_first_pixel; // kills trailing zeros
3679 contributors->n0 = in_first_pixel;
3680 contributors->n1 = in_last_pixel;
3681
3682 STBIR_ASSERT(contributors->n1 >= contributors->n0);
3683
3684 ++contributors;
3685 coefficient_group += coefficient_width;
3686 }
3687}
3688
3689static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs, int new_pixel, float new_coeff, int max_width )
3690{
3691 if ( contribs->n1 < contribs->n0 ) // this first clause should never happen, but handle in case
3692 {
3693 contribs->n0 = contribs->n1 = new_pixel;
3694 coeffs[0] = new_coeff;
3695 }
3696 else if ( new_pixel <= contribs->n1 ) // before the end
3697 {
3698 if ( new_pixel < contribs->n0 ) // before the front?
3699 {
3700 if ( ( contribs->n1 - new_pixel + 1 ) <= max_width )
3701 {
3702 int j, o = contribs->n0 - new_pixel;
3703 for ( j = contribs->n1 - contribs->n0 ; j >= 0 ; j-- )
3704 coeffs[ j + o ] = coeffs[ j ];
3705 for ( j = 1 ; j < o ; j++ )
3706 coeffs[ j ] = 0;
3707 coeffs[ 0 ] = new_coeff;
3708 contribs->n0 = new_pixel;
3709 }
3710 }
3711 else
3712 {
3713 // add new weight to existing coeff if already there
3714 coeffs[ new_pixel - contribs->n0 ] += new_coeff;
3715 }
3716 }
3717 else
3718 {
3719 if ( ( new_pixel - contribs->n0 + 1 ) <= max_width )
3720 {
3721 int j, e = new_pixel - contribs->n0;
3722 for( j = ( contribs->n1 - contribs->n0 ) + 1 ; j < e ; j++ ) // clear in-betweens coeffs if there are any
3723 coeffs[j] = 0;
3724
3725 coeffs[ e ] = new_coeff;
3726 contribs->n1 = new_pixel;
3727 }
3728 }
3729}
3730
3731// The output pixels within in_pixels_radius (input pixels) of an input pixel's center.
3732static void stbir__calculate_out_pixel_range( int * first_pixel, int * last_pixel, stbir_real in_pixel_center, stbir_real in_pixels_radius, stbir_real scale, stbir_real out_shift, int out_size )
3733{
3734 stbir_real out_pixel_influence_lowerbound = ( in_pixel_center - in_pixels_radius ) * scale - out_shift;
3735 stbir_real out_pixel_influence_upperbound = ( in_pixel_center + in_pixels_radius ) * scale - out_shift;
3736 stbir__clip_out_pixel_range( stbir__floor_to_int( out_pixel_influence_lowerbound - (stbir_real)0.5 - stbir__tie_epsilon ) + 1, stbir__floor_to_int( out_pixel_influence_upperbound - (stbir_real)0.5 - stbir__tie_epsilon ), out_size, first_pixel, last_pixel );
3737}
3738
3739// The output pixels within the filter's support of input pixel i, one pixel at a time - the same ways as
3740// stbir__output_in_pixel_range: in_pixels_radius is the support in input pixels for the real chain, support the
3741// same in output pixels for the exact positions.
3742static void stbir__input_out_pixel_range( stbir__scale_info const * scale_info, int i, stbir_real in_pixels_radius, stbir_real support, int * first_pixel, int * last_pixel )
3743{
3744 if ( scale_info->positions_exact )
3745 {
3746 stbir_int64 numer = ( 2 * (stbir_int64)i + 1 ) * (stbir_int64)scale_info->scale_numerator - 2 * (stbir_int64)scale_info->shift_pixels * (stbir_int64)scale_info->scale_denominator; // input i's center in output pixels, over 2D
3747 int first, last;
3748 stbir__exact_output_range( numer, 2 * (stbir_int64)scale_info->scale_denominator, scale_info->tap_scale_out, support, &first, &last );
3749 stbir__clip_out_pixel_range( first, last, scale_info->output_sub_size, first_pixel, last_pixel );
3750 }
3751 else
3752 stbir__calculate_out_pixel_range( first_pixel, last_pixel, (stbir_real)i + (stbir_real)0.5, in_pixels_radius, scale_info->scale, scale_info->pixel_shift, scale_info->output_sub_size );
3753}
3754
3755static void stbir__calculate_coefficients_for_gather_downsample( int start, int end, stbir_real in_pixels_radius, stbir_real support, stbir__kernel_callback_new * kernel_new, stbir__kernel_callback * kernel_old, stbir__scale_info * scale_info, int coefficient_width, int num_contributors, stbir__contributors * contributors, float * coefficient_group, void * user_data )
3756{
3757 int in_pixel;
3758 int i;
3759 int first_out_inited = -1;
3760 int out_size = scale_info->output_sub_size;
3761 int numerator = scale_info->scale_numerator;
3762 int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < out_size ) );
3763
3764 STBIR__UNUSED(num_contributors);
3765
3766 // Loop through the input pixels
3767 for (in_pixel = start; in_pixel < end; in_pixel++)
3768 {
3769 int out_first_pixel, out_last_pixel;
3770
3771 stbir__input_out_pixel_range( scale_info, in_pixel, in_pixels_radius, support, &out_first_pixel, &out_last_pixel );
3772
3773 if ( out_first_pixel > out_last_pixel )
3774 continue;
3775
3776 // clamp or exit if we are using polyphase filtering, and the limit is up
3777 if ( polyphase )
3778 {
3779 // when polyphase, you only have to do coeffs up to the numerator count
3780 if ( out_first_pixel >= numerator )
3781 break;
3782
3783 // don't do any extra work, clamp last pixel at numerator too
3784 if ( out_last_pixel >= numerator )
3785 out_last_pixel = numerator - 1;
3786 }
3787
3788 for (i = 0; i <= out_last_pixel - out_first_pixel; i++)
3789 {
3790 stbir_real weight = stbir__kernel_weight( kernel_new, kernel_old, stbir__tap_offset( scale_info, i + out_first_pixel, in_pixel, 0 ), scale_info->scale, user_data );
3791 float coeff = 0.0f;
3792
3793 if ( ( weight >= stbir__too_small_coefficient ) || ( weight <= -stbir__too_small_coefficient ) )
3794 coeff = (float) ( weight * scale_info->scale );
3795
3796 {
3797 int out = i + out_first_pixel;
3798 float * coeffs = coefficient_group + (size_t)out * coefficient_width;
3799 stbir__contributors * contribs = contributors + out;
3800
3801 // is this the first time this output pixel has been seen? Init it.
3802 if ( out > first_out_inited )
3803 {
3804 STBIR_ASSERT( out == ( first_out_inited + 1 ) ); // ensure we have only advanced one at time
3805 first_out_inited = out;
3806 contribs->n0 = in_pixel;
3807 contribs->n1 = in_pixel;
3808 coeffs[0] = coeff;
3809 }
3810 else
3811 {
3812 // insert on end (always in order)
3813 if ( coeffs[0] == 0.0f ) // if the first coefficient is zero, then zap it for this coeff
3814 {
3815 STBIR_ASSERT( ( in_pixel - contribs->n0 ) == 1 ); // ensure that when we zap, we're at the 2nd pos
3816 contribs->n0 = in_pixel;
3817 }
3818 contribs->n1 = in_pixel;
3819 STBIR_ASSERT( ( in_pixel - contribs->n0 ) < coefficient_width );
3820 coeffs[in_pixel - contribs->n0] = coeff;
3821 }
3822 }
3823 }
3824 }
3825}
3826
3827// fold a contributor's out of range taps back into the image for the edge mode, trim its trailing zero weights,
3828// and fold its extents into lowest/highest/widest
3829static stbir__inline void stbir__fold_and_trim_contributor( stbir_edge edge, stbir__contributors * contributor, float * coefficient_group, int coefficient_width, int input_size, stbir__filter_extent_info * extents )
3830{
3831 int input_last_pixel = input_size - 1;
3832 int i;
3833
3834 if ( edge == STBIR_EDGE_ZERO )
3835 {
3836 // In zero edge mode, just remove the out of bounds pixels completely (their weights are accounted for now).
3837 if ( contributor->n1 > input_last_pixel )
3838 contributor->n1 = input_last_pixel;
3839
3840 if ( contributor->n0 < 0 )
3841 {
3842 int skip = -contributor->n0;
3843 int remaining;
3844
3845 contributor->n0 = 0;
3846
3847 // Move the weights down.
3848 remaining = contributor->n1 - contributor->n0 + 1;
3849 for (i = 0; i < remaining; i++)
3850 coefficient_group[i] = coefficient_group[i + skip];
3851 }
3852 }
3853 else if ( ( edge == STBIR_EDGE_CLAMP ) || ( edge == STBIR_EDGE_REFLECT ) )
3854 {
3855 // For clamp and reflect, calculate the true in bounds pixel (based on the edge type) and add the weight to it.
3856
3857 // Right hand side first.
3858 if ( contributor->n1 > input_last_pixel )
3859 {
3860 int first = contributor->n0;
3861 int last = contributor->n1;
3862 contributor->n1 = input_last_pixel;
3863 for (i = input_size; i <= last; i++)
3864 stbir__insert_coeff( contributor, coefficient_group, stbir__edge_wrap_slow[edge]( i, input_size ), coefficient_group[i - first], coefficient_width );
3865 }
3866
3867 // Then the left.
3868 if ( contributor->n0 < 0 )
3869 {
3870 int first = contributor->n0;
3871 float * coeff = coefficient_group - ( first + 1 ); // the weight of pixel -1
3872 float first_coeff;
3873
3874 // Reinsert the weights at their reflected or clamped pixel (insert accumulates if the pixel is already there).
3875 for (i = -1; i > first; i--)
3876 stbir__insert_coeff( contributor, coefficient_group, stbir__edge_wrap_slow[edge]( i, input_size ), *coeff--, coefficient_width );
3877
3878 // Save the first one, since we didn't do it yet (i == n0) - there might be too many coeffs to hold before we shrink.
3879 first_coeff = coeff[0];
3880
3881 // Slide all the coeffs down (we accumulated them into the positive pixels) and reset the first pixel.
3882 contributor->n0 = 0;
3883 for (i = 0; i <= contributor->n1; i++)
3884 coefficient_group[i] = coefficient_group[i - first];
3885
3886 // Now that we have shrunk the contributor, we can insert the first one safely.
3887 stbir__insert_coeff( contributor, coefficient_group, stbir__edge_wrap_slow[edge]( first, input_size ), first_coeff, coefficient_width );
3888 }
3889 }
3890
3891 // Trim trailing zero coefficients, re-zero the unused tail, and widen the extents to this contributor.
3892 if ( contributor->n0 <= contributor->n1 )
3893 {
3894 int count = contributor->n1 - contributor->n0 + 1;
3895 while ( count && ( coefficient_group[count - 1] == 0.0f ) )
3896 --count;
3897
3898 contributor->n1 = contributor->n0 + count - 1;
3899
3900 if ( count > 0 )
3901 {
3902 if ( contributor->n0 < extents->lowest )
3903 extents->lowest = contributor->n0;
3904 if ( contributor->n1 > extents->highest )
3905 extents->highest = contributor->n1;
3906 if ( count > extents->widest )
3907 extents->widest = count;
3908 }
3909
3910 for (i = count; i < coefficient_width; i++)
3911 coefficient_group[i] = 0.0f;
3912 }
3913}
3914
3915static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter_extent_info* filter_info, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float * coefficients, int coefficient_width )
3916{
3917 int input_size = scale_info->input_full_size;
3918 int numerator = scale_info->scale_numerator;
3919 int denominator = scale_info->scale_denominator;
3920 int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) );
3921 int calculated = polyphase ? numerator : num_contributors; // when polyphase, only the first numerator outputs were calculated
3922 stbir_real too_small_sum = stbir__too_small_coefficient_sum_for( scale_info );
3923 int n, i;
3924
3925 // Make the sum of each output's coefficients 1.
3926 for (n = 0; n < calculated; n++)
3927 {
3928 stbir__contributors * contributor = contributors + n;
3929 float * coefficient_group = coefficients + (size_t)n * coefficient_width;
3930 stbir_real total_filter = 0;
3931 int last = contributor->n1 - contributor->n0;
3932
3933 for (i = 0; i <= last; i++)
3934 {
3935 total_filter += (stbir_real) coefficient_group[i];
3936 STBIR_ASSERT( ( coefficient_group[i] >= -2.0f ) && ( coefficient_group[i] <= 2.0f ) ); // check for wonky weights
3937 }
3938
3939 if ( ( total_filter < too_small_sum ) && ( total_filter > -too_small_sum ) )
3940 {
3941 // All coeffs are extremely small, just zero it.
3942 contributor->n1 = contributor->n0;
3943 coefficient_group[0] = 0.0f;
3944 }
3945 else if ( ( total_filter < ( (stbir_real)1.0 - stbir__renorm_tolerance ) ) || ( total_filter > ( (stbir_real)1.0 + stbir__renorm_tolerance ) ) )
3946 {
3947 stbir_real filter_scale = (stbir_real)1.0 / total_filter;
3948 for (i = 0; i <= last; i++)
3949 coefficient_group[i] = (float) ( coefficient_group[i] * filter_scale );
3950 }
3951 }
3952
3953 // Polyphase: the rest of the outputs are the first numerator's, moved along by the denominator once per period.
3954 if ( polyphase )
3955 {
3956 for (n = numerator; n < num_contributors; n++)
3957 {
3958 contributors[n].n0 = contributors[n - numerator].n0 + denominator;
3959 contributors[n].n1 = contributors[n - numerator].n1 + denominator;
3960 }
3961 stbir_overlapping_memcpy( coefficients + (size_t)numerator * coefficient_width, coefficients, (size_t)( num_contributors - numerator ) * coefficient_width );
3962 }
3963
3964 filter_info->lowest = 0x7fffffff;
3965 filter_info->highest = -0x7fffffff;
3966 filter_info->widest = -1;
3967 for (n = 0; n < num_contributors; n++)
3968 stbir__fold_and_trim_contributor( edge, contributors + n, coefficients + (size_t)n * coefficient_width, coefficient_width, input_size, filter_info );
3969}
3970
3971// how many input pixels the horizontal kernel that gets selected for this widest will read
3972static int stbir__horizontal_read_extent( stbir__contributors const * contribs, int widest )
3973{
3974 int mod, extent;
3975
3976 if ( widest <= 17 )
3977 return widest;
3978
3979 mod = widest & 3;
3980 extent = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod;
3981 if ( extent < ( 8 + mod ) )
3982 extent = 8 + mod;
3983 return extent;
3984}
3985
3986// the SIMD kernels load a remnant of three coefficients as four, so widths of 4n+3 get a fourth, zero coefficient
3987#define STBIR__COEFFICIENT_PAD( widest ) ( ( (widest) & 3 ) == 3 )
3988#define STBIR__COLUMN_ENTRY_FLOATS( widest ) ( 2 + (widest) + STBIR__COEFFICIENT_PAD( widest ) ) // the entry's size in floats
3989
3990// Repack the coefficient groups from coefficient_width floats each to the run time layout: a stbir__column_entry,
3991// two ints (filled in by stbir__fill_column_entries) followed by widest coefficients.
3992static void stbir__pack_column_entries( int num_entries, float * coefficients, int coefficient_width, int widest )
3993{
3994 int entry_floats = STBIR__COLUMN_ENTRY_FLOATS( widest );
3995 int i, j;
3996
3997 for (i = 0; i < num_entries; i++)
3998 {
3999 // When the entries grow they move up, so do the last one first; otherwise they move down (or stay), first one first.
4000 int n = ( entry_floats > coefficient_width ) ? ( num_entries - 1 - i ) : i;
4001 float * coefficient_group = coefficients + (size_t)n * coefficient_width;
4002 float * entry_coefficients = coefficients + (size_t)n * entry_floats + 2;
4003
4004 if ( entry_coefficients > coefficient_group )
4005 {
4006 for (j = widest - 1; j >= 0; j--)
4007 entry_coefficients[j] = coefficient_group[j];
4008 }
4009 else
4010 {
4011 for (j = 0; j < widest; j++)
4012 entry_coefficients[j] = coefficient_group[j];
4013 }
4014
4015 // The kernels that load one float past the coefficients see a zero.
4016 if ( STBIR__COEFFICIENT_PAD( widest ) )
4017 entry_coefficients[widest] = 0.0f;
4018 }
4019}
4020
4021// the horizontal kernels read a fixed number of pixels from each contributor's n0 (see stbir__horizontal_read_extent),
4022// so contributors near the right edge get moved back (zero weights in front) to keep those reads inside the
4023// decoded range.
4024static void stbir__clip_contributor_reads( int num_contributors, stbir__contributors* contributors, float * coefficients, int stride, int widest, int row0, int row1 )
4025{
4026 int row_end = row1 + 1;
4027 STBIR__UNUSED( row0 ); // only used in an assert
4028
4029 // the fixed width kernels (up to 17 taps) read exactly widest pixels from n0 whatever a contributor's own width is,
4030 // and the generic ones read in fours (stbir__horizontal_read_extent). So we make sure we never read outside the
4031 // decode buffer, by possibly moving the sample area back into the scanline, and putting zero weights first.
4032 // we start on the right edge and check until we're well past the possible
4033 // clip area (2*widest).
4034 {
4035 stbir__contributors * contribs = contributors + num_contributors - 1;
4036 float * coeffs = coefficients + (size_t)stride * ( num_contributors - 1 );
4037
4038 // go until no chance of clipping (this is usually less than 8 lops)
4039 while ( ( contribs >= contributors ) && ( ( contribs->n0 + widest*2 ) >= row_end ) )
4040 {
4041 // might we clip??
4042 if ( ( contribs->n0 + widest ) > row_end )
4043 {
4044 // past 17 taps the generic loops run, and they read to this contributor's own width rounded up to a four,
4045 // not a fixed widest amount - so calculate this
4046 int stop_range = stbir__horizontal_read_extent( contribs, widest );
4047
4048 // now see if we still clip with the refined range
4049 if ( ( contribs->n0 + stop_range ) > row_end )
4050 {
4051 int new_n0 = row_end - stop_range;
4052 int num = contribs->n1 - contribs->n0 + 1;
4053 int backup = contribs->n0 - new_n0;
4054 float * from_co = coeffs + num - 1;
4055 float * to_co = from_co + backup;
4056
4057 STBIR_ASSERT( ( new_n0 >= row0 ) && ( new_n0 < contribs->n0 ) );
4058
4059 // move the coeffs over
4060 while( num )
4061 {
4062 *to_co-- = *from_co--;
4063 --num;
4064 }
4065 // zero new positions
4066 while ( to_co >= coeffs )
4067 *to_co-- = 0;
4068 // set new start point (backing up by less than the mod-4 slack keeps the read extent the same, so this can't clip again)
4069 contribs->n0 = new_n0;
4070 STBIR_ASSERT( ( contribs->n0 + stbir__horizontal_read_extent( contribs, widest ) ) <= row_end );
4071 }
4072 }
4073 --contribs;
4074 coeffs -= stride;
4075 }
4076 }
4077}
4078
4079// pack the coefficient sets into column entries and clip the edge reads
4080static int stbir__pack_coefficients( int num_contributors, stbir__contributors* contributors, float * coefficients, int coefficient_width, int widest, int row0, int row1 )
4081{
4082 stbir__pack_column_entries( num_contributors, coefficients, coefficient_width, widest );
4083 stbir__clip_contributor_reads( num_contributors, contributors, coefficients + 2, STBIR__COLUMN_ENTRY_FLOATS( widest ), widest, row0, row1 );
4084 return widest;
4085}
4086
4087// For polyphase resizing (rational scale resizing), there is a ton of coefficent sharing across the
4088// horizontal scanline. We store the data like this (you need the pro and epi for clamp modes):
4089// [ prologue scanline coeffs ][ big run of repeating coeffs ][ epilogue scanline coeffs ]
4090
4091// Bounds on how many outputs at each edge have taps outside the image (before folding), from the filter geometry.
4092static void stbir__polyphase_edge_bounds( stbir__sampler const * samp, void * user_data, int * out_prologue, int * out_epilogue )
4093{
4094 stbir__scale_info const * scale_info = &samp->scale_info;
4095 int num_contributors = samp->num_contributors;
4096 stbir_real in_pixels_radius;
4097 int prologue = 0;
4098 int epilogue = 0;
4099 int n;
4100
4101 // the reach of an output's taps, plus how far an approximated fraction's copies can drift from these real positions
4102 in_pixels_radius = stbir__filter_radius_in_input_pixels( samp->filter_radius, samp->filter_support_new, samp->filter_support_old, scale_info, user_data ) + (stbir_real)scale_info->approximated_drift;
4103
4104 // Nothing gets folded with wrap.
4105 if ( samp->edge != STBIR_EDGE_WRAP )
4106 {
4107 // Looping through out pixels from the left edge, until one reads nothing before the image
4108 for (n = 0; n < num_contributors; n++)
4109 {
4110 stbir_real in_first_pixel = ( (stbir_real)n + (stbir_real)0.5 + scale_info->pixel_shift ) * scale_info->inv_scale - in_pixels_radius;
4111 if ( stbir__floor_to_int( in_first_pixel + (stbir_real)0.5 ) >= 0 )
4112 break;
4113 ++prologue;
4114 }
4115
4116 // And from the right edge, until one reads nothing past it
4117 for (n = num_contributors - 1; n >= 0; n--)
4118 {
4119 stbir_real in_last_pixel = ( (stbir_real)n + (stbir_real)0.5 + scale_info->pixel_shift ) * scale_info->inv_scale + in_pixels_radius;
4120 if ( stbir__floor_to_int( in_last_pixel - (stbir_real)0.5 ) < scale_info->input_full_size )
4121 break;
4122 ++epilogue;
4123 }
4124
4125 // A pixel of slack each way: this is the real chain, the generators' positions can be exact.
4126 ++prologue;
4127 ++epilogue;
4128 }
4129
4130 *out_prologue = prologue;
4131 *out_epilogue = epilogue;
4132}
4133
4134// for polyphase (rational) scales, we want to unroll the sharing a bit just so we aren't
4135// constantly breaking out of a tight loop to back up the coeff sharing.
4136#define STBIR__POLYPHASE_MIN_BLOCK 256
4137
4138// decide whether this (gather) sampler shares its polyphase coefficient sets, and size its tables accordingly.
4139// called before the allocation pass, so it can only use the sampler's geometry.
4140static void stbir__plan_polyphase_sharing( stbir__sampler * samp, int reads_get_clipped, void * user_data )
4141{
4142 int num_contributors = samp->num_contributors;
4143 int numerator = (int) samp->scale_info.scale_numerator;
4144 int prologue, epilogue, period, repeating;
4145
4146 samp->shared_period = 0;
4147 samp->shared_prologue = 0;
4148 samp->shared_epilogue = 0;
4149
4150 if ( ( !samp->is_gather ) || ( !samp->scale_info.scale_is_rational ) || ( numerator >= num_contributors ) )
4151 return;
4152
4153 stbir__polyphase_edge_bounds( samp, user_data, &prologue, &epilogue );
4154
4155 // the prologue is whole periods, so an output's phase is its index mod numerator
4156 prologue = ( ( prologue + numerator - 1 ) / numerator ) * numerator;
4157
4158 // the horizontal's clip (stbir__clip_contributor_reads) can also move outputs within 2*widest input pixels of the
4159 // right edge: 2*widest*scale outputs, bounded with the coefficient width since widest isn't known yet
4160 if ( reads_get_clipped )
4161 epilogue += (int)( (stbir_real)2.0 * (stbir_real)samp->coefficient_width * samp->scale_info.scale ) + 2;
4162
4163 // the block is one period of sets (one set at period 1, run with an entry stride of zero). the horizontal walks a
4164 // row through it, so its block is padded out to keep the kernel calls long; the vertical fetches a set per row by index
4165 period = numerator;
4166 if ( ( numerator > 1 ) && ( reads_get_clipped ) )
4167 {
4168 while ( period < STBIR__POLYPHASE_MIN_BLOCK )
4169 period += numerator;
4170 }
4171
4172 // not enough repeating outputs to be worth it
4173 repeating = num_contributors - prologue - epilogue;
4174 if ( repeating < period )
4175 return;
4176
4177 samp->shared_period = period;
4178 samp->shared_prologue = prologue;
4179 samp->shared_epilogue = epilogue;
4180 samp->coefficients_size = (size_t)( prologue + period + epilogue ) * STBIR__COLUMN_ENTRY_FLOATS( samp->coefficient_width ) * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING;
4181 samp->contributors_size = (size_t)( prologue + period + epilogue + 1 ) * sizeof(stbir__contributors); // one per coefficient set (build time only)
4182}
4183
4184// the shared layout keeps contributors for just the outputs that have their own coefficient sets, in set order:
4185// [ P prologue outputs ][ block outputs ][ E epilogue outputs ].
4186static void stbir__shared_output_range( stbir__sampler const * samp, int x, int * n0, int * n1 )
4187{
4188 int prologue = samp->shared_prologue;
4189 int period = samp->shared_period;
4190 int epilogue = samp->shared_epilogue;
4191 int num_contributors = samp->num_contributors;
4192 stbir__contributors const * contributor;
4193 int shift = 0;
4194
4195 if ( x >= num_contributors - epilogue )
4196 contributor = samp->contributors + prologue + period + ( x - ( num_contributors - epilogue ) );
4197 else if ( x >= prologue )
4198 {
4199 contributor = samp->contributors + prologue + ( ( x - prologue ) % period );
4200 shift = ( ( x - prologue ) / period ) * ( period / (int) samp->scale_info.scale_numerator ) * (int) samp->scale_info.scale_denominator;
4201 }
4202 else
4203 contributor = samp->contributors + x;
4204
4205 *n0 = contributor->n0 + shift;
4206 *n1 = contributor->n1 + shift;
4207}
4208
4209// n0/n1 of output x for either layout
4210static void stbir__output_range( stbir__sampler const * samp, int x, int * n0, int * n1 )
4211{
4212 if ( samp->shared_period )
4213 stbir__shared_output_range( samp, x, n0, n1 );
4214 else
4215 {
4216 *n0 = samp->contributors[ x ].n0;
4217 *n1 = samp->contributors[ x ].n1;
4218 }
4219}
4220
4221// The shared layout's version of stbir__cleanup_gathered_coefficients.
4222static void stbir__cleanup_shared_coefficients( stbir__sampler * samp )
4223{
4224 stbir_edge edge = samp->edge;
4225 stbir__scale_info const * scale_info = &samp->scale_info;
4226 int input_size = scale_info->input_full_size;
4227 int num_contributors = samp->num_contributors;
4228 int coefficient_width = samp->coefficient_width;
4229 int numerator = (int) scale_info->scale_numerator;
4230 int denominator = (int) scale_info->scale_denominator;
4231 int prologue = samp->shared_prologue;
4232 int period = samp->shared_period;
4233 int epilogue = samp->shared_epilogue;
4234 stbir__contributors * contributors = samp->contributors;
4235 float * coefficients = samp->coefficients;
4236 float * phase_coefficients = coefficients + (size_t)prologue * coefficient_width;
4237 stbir__filter_extent_info * extents = &samp->extent_info;
4238 stbir_real too_small_sum = stbir__too_small_coefficient_sum_for( &samp->scale_info );
4239 int n, i;
4240
4241 // Make the sum of each phase's coefficients 1 (as the plain path does for its first numerator outputs).
4242 for (n = 0; n < numerator; n++)
4243 {
4244 stbir__contributors * contributor = contributors + n;
4245 float * coefficient_group = phase_coefficients + (size_t)n * coefficient_width;
4246 stbir_real total_filter = 0;
4247 int last = contributor->n1 - contributor->n0;
4248
4249 for (i = 0; i <= last; i++)
4250 {
4251 total_filter += (stbir_real) coefficient_group[i];
4252 STBIR_ASSERT( ( coefficient_group[i] >= -2.0f ) && ( coefficient_group[i] <= 2.0f ) ); // check for wonky weights
4253 }
4254
4255 if ( ( total_filter < too_small_sum ) && ( total_filter > -too_small_sum ) )
4256 {
4257 // All coeffs are extremely small, just zero it.
4258 contributor->n1 = contributor->n0;
4259 coefficient_group[0] = 0.0f;
4260 }
4261 else if ( ( total_filter < ( (stbir_real)1.0 - stbir__renorm_tolerance ) ) || ( total_filter > ( (stbir_real)1.0 + stbir__renorm_tolerance ) ) )
4262 {
4263 stbir_real filter_scale = (stbir_real)1.0 / total_filter;
4264 for (i = 0; i <= last; i++)
4265 coefficient_group[i] = (float) ( coefficient_group[i] * filter_scale );
4266 }
4267 }
4268
4269 // The contributors for the prologue and period outputs (0..prologue+period-1) are the phase's, moved along by the
4270 // denominator once per period. The epilogue outputs (the last epilogue of the image) follow them.
4271 for (n = numerator; n < prologue + period; n++)
4272 {
4273 contributors[n].n0 = contributors[n - numerator].n0 + denominator;
4274 contributors[n].n1 = contributors[n - numerator].n1 + denominator;
4275 }
4276 for (n = 0; n < epilogue; n++)
4277 {
4278 int out_pixel = num_contributors - epilogue + n;
4279 contributors[prologue + period + n].n0 = contributors[out_pixel % numerator].n0 + ( out_pixel / numerator ) * denominator;
4280 contributors[prologue + period + n].n1 = contributors[out_pixel % numerator].n1 + ( out_pixel / numerator ) * denominator;
4281 }
4282
4283 // The period is the phases repeated; the prologue and epilogue groups start out as copies of their phase.
4284 for (n = numerator; n < period; n++)
4285 STBIR_MEMCPY( phase_coefficients + (size_t)n * coefficient_width, phase_coefficients + (size_t)( n % numerator ) * coefficient_width, coefficient_width * sizeof(float) );
4286 for (n = 0; n < prologue; n++)
4287 STBIR_MEMCPY( coefficients + (size_t)n * coefficient_width, phase_coefficients + (size_t)( n % numerator ) * coefficient_width, coefficient_width * sizeof(float) );
4288 for (n = 0; n < epilogue; n++)
4289 STBIR_MEMCPY( coefficients + (size_t)( prologue + period + n ) * coefficient_width, phase_coefficients + (size_t)( ( num_contributors - epilogue + n ) % numerator ) * coefficient_width, coefficient_width * sizeof(float) );
4290
4291 // Fold and trim every group. The period's outputs are never at an edge (that is what the prologue and epilogue are
4292 // for), so for them this is just the trim, which is the same for every copy of a phase - the period stays periodic.
4293 extents->lowest = 0x7fffffff;
4294 extents->highest = -0x7fffffff;
4295 extents->widest = -1;
4296 for (n = 0; n < prologue + period + epilogue; n++)
4297 {
4298 STBIR_ASSERT( ( n < prologue ) || ( n >= prologue + period ) || ( edge == STBIR_EDGE_WRAP ) || ( ( contributors[n].n0 >= 0 ) && ( contributors[n].n1 < input_size ) ) );
4299 stbir__fold_and_trim_contributor( edge, contributors + n, coefficients + (size_t)n * coefficient_width, coefficient_width, input_size, extents );
4300 }
4301
4302 // lowest and highest are over every output: the extremes are in the prologue and first period, and the last period
4303 // and epilogue.
4304 {
4305 int n0, n1;
4306 stbir__shared_output_range( samp, num_contributors - epilogue - 1, &n0, &n1 );
4307 if ( n1 > extents->highest )
4308 extents->highest = n1;
4309 }
4310}
4311
4312// the shared layout's version of stbir__pack_coefficients
4313static int stbir__pack_shared_coefficients( stbir__sampler * samp, int row0, int row1 )
4314{
4315 int num_contributors = samp->num_contributors;
4316 int widest = samp->extent_info.widest;
4317 int entry_floats = STBIR__COLUMN_ENTRY_FLOATS( widest );
4318 int prologue = samp->shared_prologue;
4319 int period = samp->shared_period;
4320 int epilogue = samp->shared_epilogue;
4321 int n0, n1;
4322
4323 stbir__pack_column_entries( prologue + period + epilogue, samp->coefficients, samp->coefficient_width, widest );
4324 stbir__clip_contributor_reads( epilogue, samp->contributors + prologue + period, samp->coefficients + (size_t)( prologue + period ) * entry_floats + 2, entry_floats, widest, row0, row1 );
4325
4326 // the clipping never needed to reach past the epilogue (the last periodic output is where it would have gone next)
4327 stbir__shared_output_range( samp, num_contributors - epilogue - 1, &n0, &n1 );
4328 STBIR_ASSERT( ( n0 + widest * 2 ) < row1 + 1 );
4329 return widest;
4330}
4331
4332// the packed column entry at an index (the entries are entry_floats apart)
4333static stbir__column_entry * stbir__column_entry_at( stbir__sampler const * samp, int index )
4334{
4335 return (stbir__column_entry *)( samp->coefficients + (size_t)index * samp->entry_floats );
4336}
4337
4338// fill in the column entries' advance and width from the build time contributors (after the entries have been
4339// packed, and after the contributors have been read for the last time - the column slice table). returns 0 when
4340// an advance doesn't fit the entry's int - a horizontal downsample of around 2^29:1 at four channels
4341static int stbir__fill_column_entries( stbir__sampler * samp, int channels )
4342{
4343 stbir__contributors const * contributors = samp->contributors;
4344 int num_entries = samp->shared_period ? ( samp->shared_prologue + samp->shared_period + samp->shared_epilogue ) : samp->num_contributors;
4345 int i;
4346
4347 if ( samp->shared_period )
4348 {
4349 int prologue = samp->shared_prologue;
4350 int period = samp->shared_period;
4351 samp->shared_block_n0 = contributors[prologue].n0;
4352 samp->shared_block_shift = ( period / (int) samp->scale_info.scale_numerator ) * (int) samp->scale_info.scale_denominator;
4353 samp->shared_epilogue_n0 = contributors[prologue + period].n0;
4354 }
4355
4356 // each entry says how far the decode pointer moves to the NEXT column. the block's last entry says how far to the
4357 // block's first column one pass on (the wrap)
4358 for (i = 0; i < num_entries; i++)
4359 {
4360 stbir__column_entry * entry = stbir__column_entry_at( samp, i );
4361 int advance;
4362
4363 if ( ( samp->shared_period ) && ( i == samp->shared_prologue + samp->shared_period - 1 ) )
4364 advance = ( samp->shared_block_n0 + samp->shared_block_shift ) - contributors[i].n0;
4365 else
4366 advance = ( i + 1 < num_entries ) ? ( contributors[i + 1].n0 - contributors[i].n0 ) : 0;
4367
4368 if ( ( (stbir_int64)advance * channels <= -0x7fffffff ) || ( (stbir_int64)advance * channels >= 0x7fffffff ) )
4369 return 0; // error
4370
4371 entry->advance = advance * channels;
4372 entry->width = contributors[i].n1 - contributors[i].n0 + 1;
4373 }
4374 return 1;
4375}
4376
4377// which coefficient set (column entry) output x uses, for either layout
4378static int stbir__output_entry_index( stbir__sampler const * samp, int x )
4379{
4380 if ( samp->shared_period )
4381 {
4382 int prologue = samp->shared_prologue;
4383 int period = samp->shared_period;
4384 int epilogue = samp->shared_epilogue;
4385 int num_contributors = samp->num_contributors;
4386
4387 if ( x >= num_contributors - epilogue )
4388 return prologue + period + ( x - ( num_contributors - epilogue ) );
4389 if ( x >= prologue )
4390 return prologue + ( ( x - prologue ) % period );
4391 }
4392 return x;
4393}
4394
4395static float const * stbir__output_coefficients( stbir__sampler const * samp, int x )
4396{
4397 return samp->coefficients + (size_t)stbir__output_entry_index( samp, x ) * samp->coefficient_width;
4398}
4399
4400// output x's packed column entry
4401static stbir__column_entry const * stbir__output_column_entry( stbir__sampler const * samp, int x )
4402{
4403 return stbir__column_entry_at( samp, stbir__output_entry_index( samp, x ) );
4404}
4405
4406// Vertical scatter functions - used when we are downsampling around 32:1 or more (horizontal is always gather)
4407// advice-wise, if you are downsampling this amount, then you'd better off doing the downsample in a couple steps
4408static void stbir__calculate_coefficients_scatter( stbir__kernel_callback_new * kernel_new, stbir__kernel_callback * kernel_old, stbir__scale_info const * scale_info, int in_pixel, int out_first_pixel, int out_last_pixel, stbir__contributors* contributor, float* coefficient_group, void * user_data )
4409{
4410 int i;
4411
4412 contributor->n0 = out_first_pixel;
4413 contributor->n1 = out_last_pixel;
4414
4415 for (i = 0; i <= out_last_pixel - out_first_pixel; i++)
4416 {
4417 stbir_real weight = stbir__kernel_weight( kernel_new, kernel_old, stbir__tap_offset( scale_info, i + out_first_pixel, in_pixel, 0 ), scale_info->scale, user_data );
4418 float coeff = 0.0f;
4419
4420 if ( ( weight >= stbir__too_small_coefficient ) || ( weight <= -stbir__too_small_coefficient ) )
4421 coeff = (float) ( weight * scale_info->scale );
4422
4423 coefficient_group[i] = coeff;
4424 }
4425}
4426
4427// Normalize each output's coefficients: summed and scaled in stbir_real, each then rounded to float
4428static void stbir__normalize_scatter_coefficients( stbir__contributors* contributors, float* coefficients, int coefficient_width, int num_contributors, int output_size, stbir_real too_small_sum )
4429{
4430 int i, j, first = 0;
4431
4432 for (i = 0; i < output_size; i++)
4433 {
4434 stbir_real total = 0;
4435 stbir_real filter_scale;
4436
4437 while ( ( first < num_contributors ) && ( contributors[first].n1 < i ) )
4438 first++;
4439
4440 for (j = first; j < num_contributors; j++)
4441 {
4442 if (i >= contributors[j].n0 && i <= contributors[j].n1)
4443 total += (stbir_real) coefficients[ (size_t)j * coefficient_width + i - contributors[j].n0 ];
4444 else if (i < contributors[j].n0)
4445 break;
4446 }
4447
4448 if ( ( total < too_small_sum ) && ( total > -too_small_sum ) )
4449 filter_scale = 0; // all coeffs are extremely small, just zero them
4450 else if ( ( total < ( (stbir_real)1.0 - stbir__renorm_tolerance ) ) || ( total > ( (stbir_real)1.0 + stbir__renorm_tolerance ) ) )
4451 filter_scale = (stbir_real)1.0 / total;
4452 else
4453 continue;
4454
4455 for (j = first; j < num_contributors; j++)
4456 {
4457 if (i >= contributors[j].n0 && i <= contributors[j].n1)
4458 coefficients[ (size_t)j * coefficient_width + i - contributors[j].n0 ] = (float) ( coefficients[ (size_t)j * coefficient_width + i - contributors[j].n0 ] * filter_scale );
4459 else if (i < contributors[j].n0)
4460 break;
4461 }
4462 }
4463}
4464
4465// Polyphase: only the first numerator outputs were calculated. From first_copy on, each contributor is the one a
4466// denominator before it with its outputs moved along by the numerator (calculated ones keep their outputs, the copies follow).
4467static void stbir__replicate_scatter_coefficients( stbir__contributors* contributors, float* coefficients, int coefficient_width, int num_contributors, int first_copy, int calculated_end, int numerator, int denominator, int output_size )
4468{
4469 int j, i;
4470
4471 for (j = first_copy; j < num_contributors; j++)
4472 {
4473 stbir__contributors* contributor = contributors + j;
4474 stbir__contributors* source = contributors + j - denominator;
4475 float* coefficient_group = coefficients + (size_t)j * coefficient_width;
4476 float* source_group = coefficients + (size_t)( j - denominator ) * coefficient_width;
4477
4478 if ( j >= calculated_end )
4479 {
4480 contributor->n0 = 0;
4481 contributor->n1 = -1;
4482
4483 // the whole source fits: copy it
4484 if ( source->n1 + numerator < output_size )
4485 {
4486 contributor->n0 = source->n0 + numerator;
4487 contributor->n1 = source->n1 + numerator;
4488 STBIR_MEMCPY( coefficient_group, source_group, (size_t)( source->n1 - source->n0 + 1 ) * sizeof(float) );
4489 continue;
4490 }
4491 }
4492
4493 // append the source's outputs that are in range
4494 for (i = 0; i <= source->n1 - source->n0; i++)
4495 {
4496 int out = source->n0 + i + numerator;
4497 if ( out >= output_size )
4498 break;
4499 if ( contributor->n1 < contributor->n0 )
4500 {
4501 contributor->n0 = contributor->n1 = out;
4502 coefficient_group[0] = source_group[i];
4503 }
4504 else
4505 stbir__insert_coeff( contributor, coefficient_group, out, source_group[i], coefficient_width );
4506 }
4507 }
4508}
4509
4510// The out of range input scanlines: clamp and reflect add their weights to the scanlines they map to (right edge
4511// ascending, then left edge descending - the gather's order), zero drops them, wrap keeps them.
4512static void stbir__fold_scatter_edges( stbir__sampler * samp, int coefficient_width )
4513{
4514 stbir_edge edge = samp->edge;
4515 int input_size = samp->scale_info.input_full_size;
4516 int filter_pixel_margin = samp->filter_pixel_margin;
4517 stbir__contributors* contributors = samp->contributors;
4518 float* coefficients = samp->coefficients;
4519 int n, i;
4520
4521 if ( ( edge == STBIR_EDGE_CLAMP ) || ( edge == STBIR_EDGE_REFLECT ) )
4522 {
4523 int pass;
4524 for (pass = 0; pass < 2; pass++)
4525 {
4526 int from = pass ? -1 : input_size;
4527 int to = pass ? -filter_pixel_margin - 1 : input_size + filter_pixel_margin;
4528 int step = pass ? -1 : 1;
4529 for (n = from; n != to; n += step)
4530 {
4531 stbir__contributors* contributor = contributors + n + filter_pixel_margin;
4532 float* coefficient_group = coefficients + (size_t)( n + filter_pixel_margin ) * coefficient_width;
4533 int folded = stbir__edge_wrap_slow[edge]( n, input_size ) + filter_pixel_margin;
4534
4535 for (i = 0; i <= contributor->n1 - contributor->n0; i++)
4536 stbir__insert_coeff( contributors + folded, coefficients + (size_t)folded * coefficient_width, contributor->n0 + i, coefficient_group[i], coefficient_width );
4537
4538 contributor->n0 = 0;
4539 contributor->n1 = -1;
4540 }
4541 }
4542 }
4543 else if ( edge == STBIR_EDGE_ZERO )
4544 {
4545 for (n = 0; n < filter_pixel_margin; n++)
4546 {
4547 contributors[n].n0 = 0;
4548 contributors[n].n1 = -1;
4549 contributors[input_size + 2 * filter_pixel_margin - 1 - n].n0 = 0;
4550 contributors[input_size + 2 * filter_pixel_margin - 1 - n].n1 = -1;
4551 }
4552 }
4553}
4554
4555// Optimize step - skip zero coefficients at either end, and pack the groups from the build width down to the final width
4556// (an output past the width is dropped). Also notes the first and last input scanlines with any weight.
4557static void stbir__pack_scatter_coefficients( stbir__sampler * samp, int build_width )
4558{
4559 int coefficient_width = samp->coefficient_width;
4560 int filter_pixel_margin = samp->filter_pixel_margin;
4561 int num_contributors = samp->num_contributors;
4562 stbir__contributors* contributors = samp->contributors;
4563 float* coefficients = samp->coefficients;
4564 int j, i;
4565
4566 samp->extent_info.lowest = 0x7fffffff;
4567 samp->extent_info.highest = -0x7fffffff;
4568 samp->extent_info.widest = coefficient_width;
4569
4570 for (j = 0; j < num_contributors; j++)
4571 {
4572 stbir__contributors* contributor = contributors + j;
4573 float const* source_group = coefficients + (size_t)j * build_width;
4574 float* coefficient_group = coefficients + (size_t)j * coefficient_width; // at or before the source, so packing forward in place is safe
4575 int range = contributor->n1 - contributor->n0 + 1;
4576 int skip = 0;
4577
4578 while ( ( skip < range ) && ( source_group[skip] == 0.0f ) )
4579 skip++;
4580 range -= skip;
4581 if ( range > coefficient_width )
4582 range = coefficient_width;
4583 while ( ( range > 0 ) && ( source_group[skip + range - 1] == 0.0f ) )
4584 range--;
4585
4586 for (i = 0; i < range; i++)
4587 coefficient_group[i] = source_group[i + skip];
4588
4589 if ( range <= 0 )
4590 {
4591 contributor->n0 = 0;
4592 contributor->n1 = -1;
4593 continue;
4594 }
4595 contributor->n0 += skip;
4596 contributor->n1 = contributor->n0 + range - 1;
4597
4598 if ( j - filter_pixel_margin < samp->extent_info.lowest )
4599 samp->extent_info.lowest = j - filter_pixel_margin;
4600 samp->extent_info.highest = j - filter_pixel_margin;
4601 }
4602}
4603
4604static void stbir__calculate_scatter_filters( stbir__sampler * samp, stbir_real in_pixels_radius, stbir_real support, void * user_data )
4605{
4606 stbir__scale_info const * scale_info = &samp->scale_info;
4607 int output_size = scale_info->output_sub_size;
4608 int filter_pixel_margin = samp->filter_pixel_margin;
4609 int num_contributors = samp->num_contributors;
4610 int build_width = samp->coefficient_width + 1;
4611 int polyphase = ( scale_info->scale_is_rational ) && ( (int)scale_info->scale_numerator < output_size );
4612 int numerator = polyphase ? (int)scale_info->scale_numerator : output_size;
4613 int calculated_end = num_contributors; // the first contributor with nothing calculated
4614 int first_weighted = -1; // the first contributor with any weight
4615 int n;
4616
4617 // Looping through in pixels
4618 for (n = 0; n < num_contributors; n++)
4619 {
4620 int out_first_pixel, out_last_pixel;
4621
4622 stbir__input_out_pixel_range( scale_info, n - filter_pixel_margin, in_pixels_radius, support, &out_first_pixel, &out_last_pixel );
4623
4624 // when polyphase, you only have to do coeffs up to the numerator count
4625 if ( out_first_pixel >= numerator )
4626 {
4627 calculated_end = n;
4628 break;
4629 }
4630 if ( out_last_pixel >= numerator )
4631 out_last_pixel = numerator - 1;
4632
4633 stbir__calculate_coefficients_scatter( samp->filter_kernel_new, samp->filter_kernel_old, scale_info, n - filter_pixel_margin, out_first_pixel, out_last_pixel, samp->contributors + n, samp->coefficients + (size_t)n * build_width, user_data );
4634
4635 if ( ( first_weighted < 0 ) && ( out_last_pixel >= out_first_pixel ) )
4636 first_weighted = n;
4637 }
4638 for (n = calculated_end; n < num_contributors; n++)
4639 {
4640 samp->contributors[n].n0 = 0;
4641 samp->contributors[n].n1 = -1;
4642 }
4643
4644 stbir__normalize_scatter_coefficients( samp->contributors, samp->coefficients, build_width, calculated_end, numerator, stbir__too_small_coefficient_sum_for( scale_info ) );
4645
4646 // the period is the rational's, which can be a little off the true one, so the copies start where it says
4647 if ( ( polyphase ) && ( first_weighted >= 0 ) )
4648 stbir__replicate_scatter_coefficients( samp->contributors, samp->coefficients, build_width, num_contributors, first_weighted + (int)scale_info->scale_denominator, calculated_end, numerator, (int)scale_info->scale_denominator, output_size );
4649
4650 stbir__fold_scatter_edges( samp, build_width );
4651 stbir__pack_scatter_coefficients( samp, build_width );
4652}
4653
4654static void stbir__calculate_filters( stbir__sampler * samp, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO )
4655{
4656 stbir_real scale = samp->scale_info.scale;
4657 stbir__kernel_callback_new * kernel_new = samp->filter_kernel_new;
4658 stbir__kernel_callback * kernel_old = samp->filter_kernel_old;
4659 stbir_real in_pixels_radius = stbir__filter_radius_in_input_pixels( samp->filter_radius, samp->filter_support_new, samp->filter_support_old, &samp->scale_info, user_data );
4660 stbir_real inv_scale = samp->scale_info.inv_scale;
4661 int input_full_size = samp->scale_info.input_full_size;
4662 int gather_num_contributors = samp->num_contributors;
4663 stbir__contributors* gather_contributors = samp->contributors;
4664 float * gather_coeffs = samp->coefficients;
4665 int gather_coefficient_width = samp->coefficient_width;
4666
4667 // with shared polyphase sets the generated phases go after the prologue sets
4668 if ( samp->shared_period )
4669 gather_coeffs += (size_t)samp->shared_prologue * gather_coefficient_width;
4670
4671 switch ( samp->is_gather )
4672 {
4673 case 1: // gather upsample
4674 {
4675 stbir_real filter_support = in_pixels_radius; // the filter's own pixels are input pixels
4676 stbir_real out_pixels_radius = filter_support * scale;
4677
4678 stbir__calculate_coefficients_for_gather_upsample( out_pixels_radius, filter_support, kernel_new, kernel_old, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width, samp->edge, user_data );
4679
4680 STBIR_PROFILE_BUILD_START( cleanup );
4681 if ( samp->shared_period )
4682 stbir__cleanup_shared_coefficients( samp );
4683 else
4684 stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width );
4685 STBIR_PROFILE_BUILD_END( cleanup );
4686 }
4687 break;
4688
4689 case 0: // scatter downsample (only on vertical)
4690 case 2: // gather downsample
4691 {
4692 stbir_real filter_support = in_pixels_radius / inv_scale; // in output pixels
4693 int filter_pixel_margin = samp->filter_pixel_margin;
4694 int input_end = input_full_size + filter_pixel_margin;
4695
4696 if ( !samp->is_gather )
4697 {
4698 STBIR_PROFILE_BUILD_START( scatter );
4699 stbir__calculate_scatter_filters( samp, in_pixels_radius, filter_support, user_data );
4700 STBIR_PROFILE_BUILD_END( scatter );
4701 break;
4702 }
4703
4704 stbir__calculate_coefficients_for_gather_downsample( -filter_pixel_margin, input_end, in_pixels_radius, filter_support, kernel_new, kernel_old, &samp->scale_info, gather_coefficient_width, gather_num_contributors, gather_contributors, gather_coeffs, user_data );
4705
4706 STBIR_PROFILE_BUILD_START( cleanup );
4707 if ( samp->shared_period )
4708 stbir__cleanup_shared_coefficients( samp );
4709 else
4710 stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width );
4711 STBIR_PROFILE_BUILD_END( cleanup );
4712 }
4713 break;
4714 }
4715}
4716
4717
4718
4719//========================================================================================================
4720// scanline decoders and encoders
4721
4722#define stbir__coder_min_num 1
4723#define STB_IMAGE_RESIZE_DO_CODERS
4724#include STBIR__HEADER_FILENAME
4725
4726#define stbir__decode_suffix BGRA
4727#define stbir__decode_swizzle
4728#define stbir__decode_order0 2
4729#define stbir__decode_order1 1
4730#define stbir__decode_order2 0
4731#define stbir__decode_order3 3
4732#define stbir__encode_order0 2
4733#define stbir__encode_order1 1
4734#define stbir__encode_order2 0
4735#define stbir__encode_order3 3
4736#define stbir__coder_min_num 4
4737#define STB_IMAGE_RESIZE_DO_CODERS
4738#include STBIR__HEADER_FILENAME
4739
4740#define stbir__decode_suffix ARGB
4741#define stbir__decode_swizzle
4742#define stbir__decode_order0 1
4743#define stbir__decode_order1 2
4744#define stbir__decode_order2 3
4745#define stbir__decode_order3 0
4746#define stbir__encode_order0 3
4747#define stbir__encode_order1 0
4748#define stbir__encode_order2 1
4749#define stbir__encode_order3 2
4750#define stbir__coder_min_num 4
4751#define STB_IMAGE_RESIZE_DO_CODERS
4752#include STBIR__HEADER_FILENAME
4753
4754#define stbir__decode_suffix ABGR
4755#define stbir__decode_swizzle
4756#define stbir__decode_order0 3
4757#define stbir__decode_order1 2
4758#define stbir__decode_order2 1
4759#define stbir__decode_order3 0
4760#define stbir__encode_order0 3
4761#define stbir__encode_order1 2
4762#define stbir__encode_order2 1
4763#define stbir__encode_order3 0
4764#define stbir__coder_min_num 4
4765#define STB_IMAGE_RESIZE_DO_CODERS
4766#include STBIR__HEADER_FILENAME
4767
4768#define stbir__decode_suffix AR
4769#define stbir__decode_swizzle
4770#define stbir__decode_order0 1
4771#define stbir__decode_order1 0
4772#define stbir__decode_order2 3
4773#define stbir__decode_order3 2
4774#define stbir__encode_order0 1
4775#define stbir__encode_order1 0
4776#define stbir__encode_order2 3
4777#define stbir__encode_order3 2
4778#define stbir__coder_min_num 2
4779#define STB_IMAGE_RESIZE_DO_CODERS
4780#include STBIR__HEADER_FILENAME
4781
4782
4783// fancy alpha means we expand to keep both premultipied and non-premultiplied color channels
4784static void stbir__fancy_alpha_weight_4ch( float * out_buffer, ptrdiff_t width_times_channels )
4785{
4786 float STBIR_STREAMOUT_PTR(*) out = out_buffer;
4787 float const * end_decode = out_buffer + ( width_times_channels / 4 ) * 7; // decode buffer aligned to end of out_buffer
4788 float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels;
4789
4790 // fancy alpha is stored internally as R G B A Rpm Gpm Bpm
4791
4792 #ifdef STBIR_SIMD
4793
4794 #ifdef STBIR_SIMD8
4795 decode += 16;
4796 STBIR_NO_UNROLL_LOOP_START
4797 while ( decode <= end_decode )
4798 {
4799 stbir__simdf8 d0,d1,a0,a1,p0,p1;
4800 STBIR_NO_UNROLL(decode);
4801 stbir__simdf8_load( d0, decode-16 );
4802 stbir__simdf8_load( d1, decode-16+8 );
4803 stbir__simdf8_0123to33333333( a0, d0 );
4804 stbir__simdf8_0123to33333333( a1, d1 );
4805 stbir__simdf8_mult( p0, a0, d0 );
4806 stbir__simdf8_mult( p1, a1, d1 );
4807 stbir__simdf8_bot4s( a0, d0, p0 );
4808 stbir__simdf8_bot4s( a1, d1, p1 );
4809 stbir__simdf8_top4s( d0, d0, p0 );
4810 stbir__simdf8_top4s( d1, d1, p1 );
4811 stbir__simdf8_store ( out, a0 );
4812 stbir__simdf8_store ( out+7, d0 );
4813 stbir__simdf8_store ( out+14, a1 );
4814 stbir__simdf8_store ( out+21, d1 );
4815 decode += 16;
4816 out += 28;
4817 }
4818 decode -= 16;
4819 #else
4820 decode += 8;
4821 STBIR_NO_UNROLL_LOOP_START
4822 while ( decode <= end_decode )
4823 {
4824 stbir__simdf d0,a0,d1,a1,p0,p1;
4825 STBIR_NO_UNROLL(decode);
4826 stbir__simdf_load( d0, decode-8 );
4827 stbir__simdf_load( d1, decode-8+4 );
4828 stbir__simdf_0123to3333( a0, d0 );
4829 stbir__simdf_0123to3333( a1, d1 );
4830 stbir__simdf_mult( p0, a0, d0 );
4831 stbir__simdf_mult( p1, a1, d1 );
4832 stbir__simdf_store ( out, d0 );
4833 stbir__simdf_store ( out+4, p0 );
4834 stbir__simdf_store ( out+7, d1 );
4835 stbir__simdf_store ( out+7+4, p1 );
4836 decode += 8;
4837 out += 14;
4838 }
4839 decode -= 8;
4840 #endif
4841
4842 // might be one last odd pixel
4843 #ifdef STBIR_SIMD8
4844 STBIR_NO_UNROLL_LOOP_START
4845 while ( decode < end_decode )
4846 #else
4847 if ( decode < end_decode )
4848 #endif
4849 {
4850 stbir__simdf d,a,p;
4851 STBIR_NO_UNROLL(decode);
4852 stbir__simdf_load( d, decode );
4853 stbir__simdf_0123to3333( a, d );
4854 stbir__simdf_mult( p, a, d );
4855 stbir__simdf_store ( out, d );
4856 stbir__simdf_store ( out+4, p );
4857 decode += 4;
4858 out += 7;
4859 }
4860
4861 #else
4862
4863 while( decode < end_decode )
4864 {
4865 float r = decode[0], g = decode[1], b = decode[2], alpha = decode[3];
4866 out[0] = r;
4867 out[1] = g;
4868 out[2] = b;
4869 out[3] = alpha;
4870 out[4] = r * alpha;
4871 out[5] = g * alpha;
4872 out[6] = b * alpha;
4873 out += 7;
4874 decode += 4;
4875 }
4876
4877 #endif
4878}
4879
4880static void stbir__fancy_alpha_weight_2ch( float * out_buffer, ptrdiff_t width_times_channels )
4881{
4882 float STBIR_STREAMOUT_PTR(*) out = out_buffer;
4883 float const * end_decode = out_buffer + ( width_times_channels / 2 ) * 3;
4884 float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels;
4885
4886 // for fancy alpha, turns into: [X A Xpm][X A Xpm],etc
4887
4888 #ifdef STBIR_SIMD
4889
4890 decode += 8;
4891 if ( decode <= end_decode )
4892 {
4893 STBIR_NO_UNROLL_LOOP_START
4894 do {
4895 #ifdef STBIR_SIMD8
4896 stbir__simdf8 d0,a0,p0;
4897 STBIR_NO_UNROLL(decode);
4898 stbir__simdf8_load( d0, decode-8 );
4899 stbir__simdf8_0123to11331133( p0, d0 );
4900 stbir__simdf8_0123to00220022( a0, d0 );
4901 stbir__simdf8_mult( p0, p0, a0 );
4902
4903 stbir__simdf_store2( out, stbir__if_simdf8_cast_to_simdf4( d0 ) );
4904 stbir__simdf_store( out+2, stbir__if_simdf8_cast_to_simdf4( p0 ) );
4905 stbir__simdf_store2h( out+3, stbir__if_simdf8_cast_to_simdf4( d0 ) );
4906
4907 stbir__simdf_store2( out+6, stbir__simdf8_gettop4( d0 ) );
4908 stbir__simdf_store( out+8, stbir__simdf8_gettop4( p0 ) );
4909 stbir__simdf_store2h( out+9, stbir__simdf8_gettop4( d0 ) );
4910 #else
4911 stbir__simdf d0,a0,d1,a1,p0,p1;
4912 STBIR_NO_UNROLL(decode);
4913 stbir__simdf_load( d0, decode-8 );
4914 stbir__simdf_load( d1, decode-8+4 );
4915 stbir__simdf_0123to1133( p0, d0 );
4916 stbir__simdf_0123to1133( p1, d1 );
4917 stbir__simdf_0123to0022( a0, d0 );
4918 stbir__simdf_0123to0022( a1, d1 );
4919 stbir__simdf_mult( p0, p0, a0 );
4920 stbir__simdf_mult( p1, p1, a1 );
4921
4922 stbir__simdf_store2( out, d0 );
4923 stbir__simdf_store( out+2, p0 );
4924 stbir__simdf_store2h( out+3, d0 );
4925
4926 stbir__simdf_store2( out+6, d1 );
4927 stbir__simdf_store( out+8, p1 );
4928 stbir__simdf_store2h( out+9, d1 );
4929 #endif
4930 decode += 8;
4931 out += 12;
4932 } while ( decode <= end_decode );
4933 }
4934 decode -= 8;
4935 #endif
4936
4937 STBIR_SIMD_NO_UNROLL_LOOP_START
4938 while( decode < end_decode )
4939 {
4940 float x = decode[0], y = decode[1];
4941 STBIR_SIMD_NO_UNROLL(decode);
4942 out[0] = x;
4943 out[1] = y;
4944 out[2] = x * y;
4945 out += 3;
4946 decode += 2;
4947 }
4948}
4949
4950static void stbir__fancy_alpha_unweight_4ch( float * encode_buffer, ptrdiff_t width_times_channels )
4951{
4952 float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer;
4953 float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer;
4954 float const * end_output = encode_buffer + width_times_channels;
4955
4956 // fancy RGBA is stored internally as R G B A Rpm Gpm Bpm
4957
4958 STBIR_SIMD_NO_UNROLL_LOOP_START
4959 do {
4960 float alpha = input[3];
4961#ifdef STBIR_SIMD
4962 stbir__simdf i,ia;
4963 STBIR_SIMD_NO_UNROLL(encode);
4964 if ( alpha < stbir__alpha_too_small_to_divide )
4965 {
4966 stbir__simdf_load( i, input );
4967 stbir__simdf_store( encode, i );
4968 }
4969 else
4970 {
4971 stbir__simdf_load1frep4( ia, 1.0f / alpha );
4972 stbir__simdf_load( i, input+4 );
4973 stbir__simdf_mult( i, i, ia );
4974 stbir__simdf_store( encode, i );
4975 encode[3] = alpha;
4976 }
4977#else
4978 if ( alpha < stbir__alpha_too_small_to_divide )
4979 {
4980 encode[0] = input[0];
4981 encode[1] = input[1];
4982 encode[2] = input[2];
4983 }
4984 else
4985 {
4986 float ialpha = 1.0f / alpha;
4987 encode[0] = input[4] * ialpha;
4988 encode[1] = input[5] * ialpha;
4989 encode[2] = input[6] * ialpha;
4990 }
4991 encode[3] = alpha;
4992#endif
4993
4994 input += 7;
4995 encode += 4;
4996 } while ( encode < end_output );
4997}
4998
4999// format: [X A Xpm][X A Xpm] etc
5000static void stbir__fancy_alpha_unweight_2ch( float * encode_buffer, ptrdiff_t width_times_channels )
5001{
5002 float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer;
5003 float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer;
5004 float const * end_output = encode_buffer + width_times_channels;
5005
5006 do {
5007 float alpha = input[1];
5008 encode[0] = input[0];
5009 if ( alpha >= stbir__alpha_too_small_to_divide )
5010 encode[0] = input[2] / alpha;
5011 encode[1] = alpha;
5012
5013 input += 3;
5014 encode += 2;
5015 } while ( encode < end_output );
5016}
5017
5018static void stbir__simple_alpha_weight_4ch( float * decode_buffer, ptrdiff_t width_times_channels )
5019{
5020 float STBIR_STREAMOUT_PTR(*) decode = decode_buffer;
5021 float const * end_decode = decode_buffer + width_times_channels;
5022
5023 #ifdef STBIR_SIMD
5024 {
5025 decode += 2 * stbir__simdfX_float_count;
5026 STBIR_NO_UNROLL_LOOP_START
5027 while ( decode <= end_decode )
5028 {
5029 stbir__simdfX d0,a0,d1,a1;
5030 STBIR_NO_UNROLL(decode);
5031 stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count );
5032 stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count );
5033 stbir__simdfX_aaa1( a0, d0, STBIR_onesX );
5034 stbir__simdfX_aaa1( a1, d1, STBIR_onesX );
5035 stbir__simdfX_mult( d0, d0, a0 );
5036 stbir__simdfX_mult( d1, d1, a1 );
5037 stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 );
5038 stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 );
5039 decode += 2 * stbir__simdfX_float_count;
5040 }
5041 decode -= 2 * stbir__simdfX_float_count;
5042
5043 // few last pixels remnants
5044 #ifdef STBIR_SIMD8
5045 STBIR_NO_UNROLL_LOOP_START
5046 while ( decode < end_decode )
5047 #else
5048 if ( decode < end_decode )
5049 #endif
5050 {
5051 stbir__simdf d,a;
5052 stbir__simdf_load( d, decode );
5053 stbir__simdf_aaa1( a, d, STBIR__CONSTF(STBIR_ones) );
5054 stbir__simdf_mult( d, d, a );
5055 stbir__simdf_store ( decode, d );
5056 decode += 4;
5057 }
5058 }
5059
5060 #else
5061
5062 while( decode < end_decode )
5063 {
5064 float alpha = decode[3];
5065 decode[0] *= alpha;
5066 decode[1] *= alpha;
5067 decode[2] *= alpha;
5068 decode += 4;
5069 }
5070
5071 #endif
5072}
5073
5074static void stbir__simple_alpha_weight_2ch( float * decode_buffer, ptrdiff_t width_times_channels )
5075{
5076 float STBIR_STREAMOUT_PTR(*) decode = decode_buffer;
5077 float const * end_decode = decode_buffer + width_times_channels;
5078
5079 #ifdef STBIR_SIMD
5080 decode += 2 * stbir__simdfX_float_count;
5081 STBIR_NO_UNROLL_LOOP_START
5082 while ( decode <= end_decode )
5083 {
5084 stbir__simdfX d0,a0,d1,a1;
5085 STBIR_NO_UNROLL(decode);
5086 stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count );
5087 stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count );
5088 stbir__simdfX_a1a1( a0, d0, STBIR_onesX );
5089 stbir__simdfX_a1a1( a1, d1, STBIR_onesX );
5090 stbir__simdfX_mult( d0, d0, a0 );
5091 stbir__simdfX_mult( d1, d1, a1 );
5092 stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 );
5093 stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 );
5094 decode += 2 * stbir__simdfX_float_count;
5095 }
5096 decode -= 2 * stbir__simdfX_float_count;
5097 #endif
5098
5099 STBIR_SIMD_NO_UNROLL_LOOP_START
5100 while( decode < end_decode )
5101 {
5102 float alpha = decode[1];
5103 STBIR_SIMD_NO_UNROLL(decode);
5104 decode[0] *= alpha;
5105 decode += 2;
5106 }
5107}
5108
5109static void stbir__simple_alpha_unweight_4ch( float * encode_buffer, ptrdiff_t width_times_channels )
5110{
5111 float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer;
5112 float const * end_output = encode_buffer + width_times_channels;
5113
5114 STBIR_SIMD_NO_UNROLL_LOOP_START
5115 do {
5116 float alpha = encode[3];
5117
5118#ifdef STBIR_SIMD
5119 stbir__simdf i,ia;
5120 STBIR_SIMD_NO_UNROLL(encode);
5121 if ( alpha >= stbir__alpha_too_small_to_divide )
5122 {
5123 stbir__simdf_load1frep4( ia, 1.0f / alpha );
5124 stbir__simdf_load( i, encode );
5125 stbir__simdf_mult( i, i, ia );
5126 stbir__simdf_store( encode, i );
5127 encode[3] = alpha;
5128 }
5129#else
5130 if ( alpha >= stbir__alpha_too_small_to_divide )
5131 {
5132 float ialpha = 1.0f / alpha;
5133 encode[0] *= ialpha;
5134 encode[1] *= ialpha;
5135 encode[2] *= ialpha;
5136 }
5137#endif
5138 encode += 4;
5139 } while ( encode < end_output );
5140}
5141
5142static void stbir__simple_alpha_unweight_2ch( float * encode_buffer, ptrdiff_t width_times_channels )
5143{
5144 float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer;
5145 float const * end_output = encode_buffer + width_times_channels;
5146
5147 do {
5148 float alpha = encode[1];
5149 if ( alpha >= stbir__alpha_too_small_to_divide )
5150 encode[0] /= alpha;
5151 encode += 2;
5152 } while ( encode < end_output );
5153}
5154
5155
5156// only used in RGB->BGR or BGR->RGB
5157static void stbir__simple_flip_3ch( float * decode_buffer, ptrdiff_t width_times_channels )
5158{
5159 float STBIR_STREAMOUT_PTR(*) decode = decode_buffer;
5160 float const * end_decode = decode_buffer + width_times_channels;
5161
5162#ifdef STBIR_SIMD
5163 #ifdef stbir__simdf_swiz2 // do we have two argument swizzles?
5164 end_decode -= 12;
5165 STBIR_NO_UNROLL_LOOP_START
5166 while( decode <= end_decode )
5167 {
5168 // on arm64 8 instructions, no overlapping stores
5169 stbir__simdf a,b,c,na,nb;
5170 STBIR_SIMD_NO_UNROLL(decode);
5171 stbir__simdf_load( a, decode );
5172 stbir__simdf_load( b, decode+4 );
5173 stbir__simdf_load( c, decode+8 );
5174
5175 na = stbir__simdf_swiz2( a, b, 2, 1, 0, 5 );
5176 b = stbir__simdf_swiz2( a, b, 4, 3, 6, 7 );
5177 nb = stbir__simdf_swiz2( b, c, 0, 1, 4, 3 );
5178 c = stbir__simdf_swiz2( b, c, 2, 7, 6, 5 );
5179
5180 stbir__simdf_store( decode, na );
5181 stbir__simdf_store( decode+4, nb );
5182 stbir__simdf_store( decode+8, c );
5183 decode += 12;
5184 }
5185 end_decode += 12;
5186 #else
5187 end_decode -= 24;
5188 STBIR_NO_UNROLL_LOOP_START
5189 while( decode <= end_decode )
5190 {
5191 // 26 instructions on x64
5192 stbir__simdf a,b,c,d,e,f,g;
5193 float i21, i23;
5194 STBIR_SIMD_NO_UNROLL(decode);
5195 stbir__simdf_load( a, decode );
5196 stbir__simdf_load( b, decode+3 );
5197 stbir__simdf_load( c, decode+6 );
5198 stbir__simdf_load( d, decode+9 );
5199 stbir__simdf_load( e, decode+12 );
5200 stbir__simdf_load( f, decode+15 );
5201 stbir__simdf_load( g, decode+18 );
5202
5203 a = stbir__simdf_swiz( a, 2, 1, 0, 3 );
5204 b = stbir__simdf_swiz( b, 2, 1, 0, 3 );
5205 c = stbir__simdf_swiz( c, 2, 1, 0, 3 );
5206 d = stbir__simdf_swiz( d, 2, 1, 0, 3 );
5207 e = stbir__simdf_swiz( e, 2, 1, 0, 3 );
5208 f = stbir__simdf_swiz( f, 2, 1, 0, 3 );
5209 g = stbir__simdf_swiz( g, 2, 1, 0, 3 );
5210
5211 // stores overlap, need to be in order,
5212 stbir__simdf_store( decode, a );
5213 i21 = decode[21];
5214 stbir__simdf_store( decode+3, b );
5215 i23 = decode[23];
5216 stbir__simdf_store( decode+6, c );
5217 stbir__simdf_store( decode+9, d );
5218 stbir__simdf_store( decode+12, e );
5219 stbir__simdf_store( decode+15, f );
5220 stbir__simdf_store( decode+18, g );
5221 decode[21] = i23;
5222 decode[23] = i21;
5223 decode += 24;
5224 }
5225 end_decode += 24;
5226 #endif
5227#else
5228 end_decode -= 12;
5229 STBIR_NO_UNROLL_LOOP_START
5230 while( decode <= end_decode )
5231 {
5232 // 16 instructions
5233 float t0,t1,t2,t3;
5234 STBIR_NO_UNROLL(decode);
5235 t0 = decode[0];
5236 t1 = decode[3];
5237 t2 = decode[6];
5238 t3 = decode[9];
5239 decode[0] = decode[2];
5240 decode[3] = decode[5];
5241 decode[6] = decode[8];
5242 decode[9] = decode[11];
5243 decode[2] = t0;
5244 decode[5] = t1;
5245 decode[8] = t2;
5246 decode[11] = t3;
5247 decode += 12;
5248 }
5249 end_decode += 12;
5250#endif
5251
5252 STBIR_NO_UNROLL_LOOP_START
5253 while( decode < end_decode )
5254 {
5255 float t = decode[0];
5256 STBIR_NO_UNROLL(decode);
5257 decode[0] = decode[2];
5258 decode[2] = t;
5259 decode += 3;
5260 }
5261}
5262
5263
5264// decodes the input pixels that a column slice needs for input scanline n into output_buffer
5265// most of the complexity in this function is for wrap mode, we need to grab from multiple
5266// points in the input buffer
5267static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float * output_buffer, stbir__column_slice const * slice STBIR_ONLY_PROFILE_GET_SPLIT_INFO )
5268{
5269 int channels = stbir_info->channels;
5270 int effective_channels = stbir_info->effective_channels;
5271 int input_sample_in_bytes = stbir__type_size[stbir_info->input_type] * channels;
5272 stbir_edge edge_horizontal = stbir_info->horizontal.edge;
5273 stbir_edge edge_vertical = stbir_info->vertical.edge;
5274 int input_full_size = stbir_info->horizontal.scale_info.input_full_size;
5275 int row = stbir__edge_wrap(edge_vertical, n, stbir_info->vertical.scale_info.input_full_size);
5276 const void* input_plane_data = ( (char *) stbir_info->input_data ) + (ptrdiff_t)( row * stbir_info->input_stride_bytes );
5277 float * full_decode_buffer = output_buffer - (ptrdiff_t)slice->in_n0 * effective_channels; // decode buffer is indexed in input pixel space, so back it up to input pixel zero (the products are 64-bit: a tile of a huge input can start past 2^31 / channels)
5278 float * last_decoded = output_buffer;
5279 int run_n0[3], run_n1[3], run_input_x[3];
5280 int num_runs = 0, r;
5281 int n0 = slice->in_n0, n1 = slice->in_n1;
5282
5283 // if we are on edge_zero, and we get in here with an out of bounds n, then the calculate filters has failed
5284 STBIR_ASSERT( !(edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->vertical.scale_info.input_full_size)) );
5285
5286 if ( edge_horizontal == STBIR_EDGE_WRAP )
5287 {
5288 // the margins are never wider than the scanline (stbir__set_sampler clamps them), so each side wraps exactly once
5289 STBIR_ASSERT( n0 >= -input_full_size );
5290 if ( n1 > ( ( input_full_size - 1 ) + input_full_size ) )
5291 n1 = ( input_full_size - 1 ) + input_full_size; // the conservative range can poke one past what the filter can reach, that pixel just gets zeroed below ( 2 x 2^30 - 1, written so it can't overflow )
5292
5293 if ( n0 < 0 )
5294 {
5295 run_n0[num_runs] = n0;
5296 run_n1[num_runs] = ( n1 < -1 ) ? n1 : -1;
5297 run_input_x[num_runs] = n0 + input_full_size;
5298 ++num_runs;
5299 }
5300 if ( ( n1 >= 0 ) && ( n0 < input_full_size ) )
5301 {
5302 run_n0[num_runs] = ( n0 > 0 ) ? n0 : 0;
5303 run_n1[num_runs] = ( n1 < ( input_full_size - 1 ) ) ? n1 : ( input_full_size - 1 );
5304 run_input_x[num_runs] = run_n0[num_runs];
5305 ++num_runs;
5306 }
5307 if ( n1 >= input_full_size )
5308 {
5309 run_n0[num_runs] = ( n0 > input_full_size ) ? n0 : input_full_size;
5310 run_n1[num_runs] = n1;
5311 run_input_x[num_runs] = run_n0[num_runs] - input_full_size;
5312 ++num_runs;
5313 }
5314 }
5315 else
5316 {
5317 STBIR_ASSERT( ( n0 >= 0 ) && ( n1 < input_full_size ) );
5318 run_n0[0] = n0;
5319 run_n1[0] = n1;
5320 run_input_x[0] = n0;
5321 num_runs = 1;
5322 }
5323
5324 for( r = 0 ; r < num_runs ; r++ )
5325 {
5326 int width = run_n1[r] + 1 - run_n0[r];
5327 ptrdiff_t width_times_channels = (ptrdiff_t)width * channels;
5328 float * decode_buffer = full_decode_buffer + (ptrdiff_t)run_n0[r] * effective_channels;
5329 float * end_decode = full_decode_buffer + (ptrdiff_t)( run_n1[r] + 1 ) * effective_channels;
5330 void const * input_data;
5331
5332 // read directly out of input plane by default
5333 input_data = ( (char*)input_plane_data ) + (ptrdiff_t)run_input_x[r] * input_sample_in_bytes;
5334
5335 // if we have an input callback, call it to get the input data
5336 if ( stbir_info->in_pixels_cb )
5337 {
5338 // call the callback with a temp buffer (that they can choose to use or not). the temp is just right aligned memory in the decode_buffer itself
5339 input_data = stbir_info->in_pixels_cb( ( (char*) end_decode ) - ( (ptrdiff_t)width * input_sample_in_bytes ) + stbir_info->input_callback_offset_bytes, input_plane_data, width, run_input_x[r], row, stbir_info->user_data );
5340 }
5341
5342 STBIR_PROFILE_START( decode );
5343 // convert the pixels info the float decode_buffer, (we index from end_decode, so that when channels<effective_channels, we are right justified in the buffer)
5344 last_decoded = stbir_info->decode_pixels( (float*)end_decode - width_times_channels, width_times_channels, input_data );
5345 STBIR_PROFILE_END( decode );
5346
5347 if (stbir_info->alpha_weight)
5348 {
5349 STBIR_PROFILE_START( alpha );
5350 stbir_info->alpha_weight( decode_buffer, width_times_channels );
5351 STBIR_PROFILE_END( alpha );
5352 }
5353 }
5354
5355 // the horizontal kernels sometimes read a few samples past the input end, so
5356 // zero the extra to avoid NaN (has to happen after decoding, since the input
5357 // buffer might overwrite them. Should just be a couple clears.
5358 {
5359 float * range_end = full_decode_buffer + (ptrdiff_t)( slice->in_n1 + 1 ) * effective_channels + 2;
5360 while ( last_decoded < range_end )
5361 *last_decoded++ = 0.0f;
5362 }
5363}
5364
5365// advance to the next set of coeffs and contrib info
5366#define stbir__next_contributor() \
5367 decode_buffer += column->advance; \
5368 column = ( (stbir__column_entry const *)( ( (char const *)(column) ) + (entry_stride) ) );
5369
5370
5371//=================
5372// Do 1 channel horizontal routines
5373
5374#ifdef STBIR_SIMD
5375
5376#define stbir__1_coeff_only() \
5377 stbir__simdf tot,c; \
5378 STBIR_SIMD_NO_UNROLL(decode); \
5379 stbir__simdf_load1( c, hc ); \
5380 stbir__simdf_mult1_mem( tot, c, decode );
5381
5382#define stbir__2_coeff_only() \
5383 stbir__simdf tot,c,d; \
5384 STBIR_SIMD_NO_UNROLL(decode); \
5385 stbir__simdf_load2z( c, hc ); \
5386 stbir__simdf_load2( d, decode ); \
5387 stbir__simdf_mult( tot, c, d ); \
5388 stbir__simdf_0123to1230( c, tot ); \
5389 stbir__simdf_add1( tot, tot, c );
5390
5391#define stbir__3_coeff_only() \
5392 stbir__simdf tot,c,t; \
5393 STBIR_SIMD_NO_UNROLL(decode); \
5394 stbir__simdf_load( c, hc ); \
5395 stbir__simdf_mult_mem( tot, c, decode ); \
5396 stbir__simdf_0123to1230( c, tot ); \
5397 stbir__simdf_0123to2301( t, tot ); \
5398 stbir__simdf_add1( tot, tot, c ); \
5399 stbir__simdf_add1( tot, tot, t );
5400
5401#define stbir__store_output_tiny() \
5402 stbir__simdf_store1( output, tot ); \
5403 stbir__next_contributor(); \
5404 output += 1;
5405
5406#define stbir__4_coeff_start() \
5407 stbir__simdf tot,c; \
5408 STBIR_SIMD_NO_UNROLL(decode); \
5409 stbir__simdf_load( c, hc ); \
5410 stbir__simdf_mult_mem( tot, c, decode ); \
5411
5412#define stbir__4_coeff_continue_from_4( ofs ) \
5413 STBIR_SIMD_NO_UNROLL(decode); \
5414 stbir__simdf_load( c, hc + (ofs) ); \
5415 stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) );
5416
5417#define stbir__1_coeff_remnant( ofs ) \
5418 { stbir__simdf d; \
5419 stbir__simdf_load1z( c, hc + (ofs) ); \
5420 stbir__simdf_load1( d, decode + (ofs) ); \
5421 stbir__simdf_madd( tot, tot, d, c ); }
5422
5423#define stbir__2_coeff_remnant( ofs ) \
5424 { stbir__simdf d; \
5425 stbir__simdf_load2z( c, hc+(ofs) ); \
5426 stbir__simdf_load2( d, decode+(ofs) ); \
5427 stbir__simdf_madd( tot, tot, d, c ); }
5428
5429#define stbir__3_coeff_setup() \
5430 stbir__simdf mask; \
5431 stbir__simdf_load( mask, STBIR_mask + 3 );
5432
5433#define stbir__3_coeff_remnant( ofs ) \
5434 stbir__simdf_load( c, hc+(ofs) ); \
5435 stbir__simdf_and( c, c, mask ); \
5436 stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) );
5437
5438// four outputs per iteration for one channel - do 4 1-channel versions
5439#define stbir__x4_setup() \
5440 stbir__column_entry const * e1 = (stbir__column_entry const *)( (char const *)column + entry_stride ); \
5441 stbir__column_entry const * e2 = (stbir__column_entry const *)( (char const *)e1 + entry_stride ); \
5442 stbir__column_entry const * e3 = (stbir__column_entry const *)( (char const *)e2 + entry_stride ); \
5443 float const * d0 = decode_buffer, * d1 = d0 + column->advance, * d2 = d1 + e1->advance, * d3 = d2 + e2->advance; \
5444 float const * h0 = column->coefficients, * h1 = e1->coefficients, * h2 = e2->coefficients, * h3 = e3->coefficients; \
5445 stbir__simdf a0, a1, a2, a3, c;
5446
5447#define stbir__x4_start() \
5448 do { \
5449 stbir__simdf_load( c, h0 ); \
5450 stbir__simdf_mult_mem( a0, c, d0 ); \
5451 stbir__simdf_load( c, h1 ); \
5452 stbir__simdf_mult_mem( a1, c, d1 ); \
5453 stbir__simdf_load( c, h2 ); \
5454 stbir__simdf_mult_mem( a2, c, d2 ); \
5455 stbir__simdf_load( c, h3 ); \
5456 stbir__simdf_mult_mem( a3, c, d3 ); \
5457 } while(0)
5458
5459#define stbir__x4_cont( ofs ) \
5460 do { \
5461 stbir__simdf_load( c, h0 + (ofs) ); \
5462 stbir__simdf_madd_mem( a0, a0, c, d0 + (ofs) ); \
5463 stbir__simdf_load( c, h1 + (ofs) ); \
5464 stbir__simdf_madd_mem( a1, a1, c, d1 + (ofs) ); \
5465 stbir__simdf_load( c, h2 + (ofs) ); \
5466 stbir__simdf_madd_mem( a2, a2, c, d2 + (ofs) ); \
5467 stbir__simdf_load( c, h3 + (ofs) ); \
5468 stbir__simdf_madd_mem( a3, a3, c, d3 + (ofs) ); \
5469 } while(0)
5470
5471#define stbir__x4_rem1( ofs ) \
5472 do { \
5473 stbir__simdf d; \
5474 stbir__simdf_load1z( c, h0 + (ofs) ); \
5475 stbir__simdf_load1( d, d0 + (ofs) ); \
5476 stbir__simdf_madd( a0, a0, d, c ); \
5477 stbir__simdf_load1z( c, h1 + (ofs) ); \
5478 stbir__simdf_load1( d, d1 + (ofs) ); \
5479 stbir__simdf_madd( a1, a1, d, c ); \
5480 stbir__simdf_load1z( c, h2 + (ofs) ); \
5481 stbir__simdf_load1( d, d2 + (ofs) ); \
5482 stbir__simdf_madd( a2, a2, d, c ); \
5483 stbir__simdf_load1z( c, h3 + (ofs) ); \
5484 stbir__simdf_load1( d, d3 + (ofs) ); \
5485 stbir__simdf_madd( a3, a3, d, c ); \
5486 } while(0)
5487
5488#define stbir__x4_rem2( ofs ) \
5489 do { \
5490 stbir__simdf d; \
5491 stbir__simdf_load2z( c, h0 + (ofs) ); \
5492 stbir__simdf_load2( d, d0 + (ofs) ); \
5493 stbir__simdf_madd( a0, a0, d, c ); \
5494 stbir__simdf_load2z( c, h1 + (ofs) ); \
5495 stbir__simdf_load2( d, d1 + (ofs) ); \
5496 stbir__simdf_madd( a1, a1, d, c ); \
5497 stbir__simdf_load2z( c, h2 + (ofs) ); \
5498 stbir__simdf_load2( d, d2 + (ofs) ); \
5499 stbir__simdf_madd( a2, a2, d, c ); \
5500 stbir__simdf_load2z( c, h3 + (ofs) ); \
5501 stbir__simdf_load2( d, d3 + (ofs) ); \
5502 stbir__simdf_madd( a3, a3, d, c ); \
5503 } while(0)
5504
5505// widths of 4n+3 carry a zero after their last coefficient (STBIR__COEFFICIENT_PAD), so three is a full four
5506#define stbir__x4_rem3( ofs ) stbir__x4_cont( ofs )
5507
5508// two coefficients: ( d0 d0+1 | d1 d1+1 ) * ( c0 c0+1 | c1 c1+1 ), twice, then one pairwise add gives the four outputs
5509#define stbir__x4_2taps() \
5510 do { \
5511 stbir__simdf_load2( c, h0 ); \
5512 stbir__simdf_load2hmerge( c, c, h1 ); \
5513 stbir__simdf_load2( a0, d0 ); \
5514 stbir__simdf_load2hmerge( a0, a0, d1 ); \
5515 stbir__simdf_mult( a0, a0, c ); \
5516 stbir__simdf_load2( a2, h2 ); \
5517 stbir__simdf_load2hmerge( a2, a2, h3 ); \
5518 stbir__simdf_load2( a3, d2 ); \
5519 stbir__simdf_load2hmerge( a3, a3, d3 ); \
5520 stbir__simdf_mult( a1, a3, a2 ); \
5521 stbir__simdf_hadd2( a0, a0, a1 ); \
5522 } while(0)
5523
5524// the same association as the per pixel path's reduction, ( l0 + l2 ) + ( l1 + l3 ), so the results match it bit for bit
5525#define stbir__x4_finish() \
5526 do { \
5527 stbir__simdf_addhalves2( a0, a0, a1 ); \
5528 stbir__simdf_addhalves2( a2, a2, a3 ); \
5529 stbir__simdf_hadd2( a0, a0, a2 ); \
5530 } while(0)
5531
5532// the three coefficient per pixel path reduces as ( l0 + l1 ) + l2, which the pairwise tree reproduces (lane 3 is the zero pad)
5533#define stbir__x4_finish3() \
5534 do { \
5535 stbir__simdf_hadd2( a0, a0, a1 ); \
5536 stbir__simdf_hadd2( a2, a2, a3 ); \
5537 stbir__simdf_hadd2( a0, a0, a2 ); \
5538 } while(0)
5539
5540#define stbir__x4_store() \
5541 do { \
5542 stbir__simdf_store( output, a0 ); \
5543 decode_buffer = d3 + e3->advance; \
5544 column = (stbir__column_entry const *)( (char const *)e3 + entry_stride ); \
5545 output += 4; \
5546 } while(0)
5547
5548// polyphase period 1 for 1 channels (when have exact downsamples 2:1, 3:1, etc - common for mipmaps)
5549#define stbir__period1_setup_4() stbir__simdf P0; stbir__simdf_load( P0, hc );
5550#define stbir__period1_setup_5() stbir__simdf P0, PR; stbir__simdf_load( P0, hc ); stbir__simdf_load1z( PR, hc + 4 );
5551#define stbir__period1_setup_8() stbir__simdf P0, P1; stbir__simdf_load( P0, hc ); stbir__simdf_load( P1, hc + 4 );
5552#define stbir__period1_setup_9() stbir__simdf P0, P1, PR; stbir__simdf_load( P0, hc ); stbir__simdf_load( P1, hc + 4 ); stbir__simdf_load1z( PR, hc + 8 );
5553#define stbir__period1_setup_11() stbir__simdf P0, P1, PR3; stbir__simdf_load( P0, hc ); stbir__simdf_load( P1, hc + 4 ); stbir__simdf_load( PR3, hc + 8 ); // the fourth of PR3 is the 4n+3 zero pad
5554#define stbir__period1_setup_12() stbir__simdf P0, P1, P2; stbir__simdf_load( P0, hc ); stbir__simdf_load( P1, hc + 4 ); stbir__simdf_load( P2, hc + 8 );
5555#define stbir__period1_setup_13() stbir__simdf PR; stbir__period1_setup_12() stbir__simdf_load1z( PR, hc + 12 );
5556#define stbir__period1_setup_16() stbir__simdf P3; stbir__period1_setup_12() stbir__simdf_load( P3, hc + 12 );
5557#define stbir__period1_setup_17() stbir__simdf PR; stbir__period1_setup_16() stbir__simdf_load1z( PR, hc + 16 );
5558
5559#define stbir__period1_4_coeff_start() \
5560 stbir__simdf tot, c; \
5561 STBIR_SIMD_NO_UNROLL(decode); \
5562 stbir__simdf_mult_mem( tot, P0, decode );
5563
5564#define stbir__period1_4_coeff_continue() \
5565 stbir__simdf_madd_mem( tot, tot, P1, decode + 4 );
5566#define stbir__period1_4_coeff_continue2() \
5567 stbir__simdf_madd_mem( tot, tot, P2, decode + 8 );
5568#define stbir__period1_4_coeff_continue3() \
5569 stbir__simdf_madd_mem( tot, tot, P3, decode + 12 );
5570
5571#define stbir__period1_1_coeff_remnant( ofs ) \
5572 do { \
5573 stbir__simdf d; \
5574 stbir__simdf_load1( d, decode + (ofs) ); \
5575 stbir__simdf_madd( tot, tot, d, PR ); \
5576 } while(0)
5577#define stbir__period1_3_coeff_remnant( ofs ) \
5578 stbir__simdf_madd_mem( tot, tot, PR3, decode + (ofs) );
5579
5580#define stbir__period1_store_output() \
5581 stbir__simdf_0123to2301( c, tot ); \
5582 stbir__simdf_add( tot, tot, c ); \
5583 stbir__simdf_0123to1230( c, tot ); \
5584 stbir__simdf_add1( tot, tot, c ); \
5585 stbir__simdf_store1( output, tot ); \
5586 decode_buffer += advance; \
5587 output += 1;
5588
5589// the four outputs' decode pointers are one base plus multiples of the (constant) advance
5590#define stbir__period1_x4_setup() \
5591 float const * d0 = decode_buffer; \
5592 int const dofs1 = advance, dofs2 = advance * 2, dofs3 = advance * 3; \
5593 stbir__simdf a0, a1, a2, a3;
5594
5595#define stbir__period1_x4_start() \
5596 do { \
5597 stbir__simdf_mult_mem( a0, P0, d0 ); \
5598 stbir__simdf_mult_mem( a1, P0, d0 + dofs1 ); \
5599 stbir__simdf_mult_mem( a2, P0, d0 + dofs2 ); \
5600 stbir__simdf_mult_mem( a3, P0, d0 + dofs3 ); \
5601 } while(0)
5602
5603// the next four coefficients, for each of the four outputs
5604#define stbir__period1_x4_next( P, ofs ) \
5605 do { \
5606 stbir__simdf_madd_mem( a0, a0, P, d0 + (ofs) ); \
5607 stbir__simdf_madd_mem( a1, a1, P, d0 + dofs1 + (ofs) ); \
5608 stbir__simdf_madd_mem( a2, a2, P, d0 + dofs2 + (ofs) ); \
5609 stbir__simdf_madd_mem( a3, a3, P, d0 + dofs3 + (ofs) ); \
5610 } while(0)
5611#define stbir__period1_x4_continue() stbir__period1_x4_next( P1, 4 )
5612#define stbir__period1_x4_continue2() stbir__period1_x4_next( P2, 8 )
5613#define stbir__period1_x4_continue3() stbir__period1_x4_next( P3, 12 )
5614#define stbir__period1_x4_remnant3( ofs ) stbir__period1_x4_next( PR3, ofs )
5615
5616#define stbir__period1_x4_remnant( ofs ) \
5617 do { \
5618 stbir__simdf d; \
5619 stbir__simdf_load1( d, d0 + (ofs) ); \
5620 stbir__simdf_madd( a0, a0, d, PR ); \
5621 stbir__simdf_load1( d, d0 + dofs1 + (ofs) ); \
5622 stbir__simdf_madd( a1, a1, d, PR ); \
5623 stbir__simdf_load1( d, d0 + dofs2 + (ofs) ); \
5624 stbir__simdf_madd( a2, a2, d, PR ); \
5625 stbir__simdf_load1( d, d0 + dofs3 + (ofs) ); \
5626 stbir__simdf_madd( a3, a3, d, PR ); \
5627 } while(0)
5628
5629#define stbir__period1_x4_store() \
5630 do { \
5631 stbir__simdf_addhalves2( a0, a0, a1 ); \
5632 stbir__simdf_addhalves2( a2, a2, a3 ); \
5633 stbir__simdf_hadd2( a0, a0, a2 ); \
5634 stbir__simdf_store( output, a0 ); \
5635 decode_buffer = d0 + dofs3 + advance; \
5636 output += 4; \
5637 } while(0)
5638
5639#define stbir__store_output() \
5640 stbir__simdf_0123to2301( c, tot ); \
5641 stbir__simdf_add( tot, tot, c ); \
5642 stbir__simdf_0123to1230( c, tot ); \
5643 stbir__simdf_add1( tot, tot, c ); \
5644 stbir__simdf_store1( output, tot ); \
5645 stbir__next_contributor(); \
5646 output += 1;
5647
5648#else
5649
5650#define stbir__1_coeff_only() \
5651 float tot; \
5652 tot = decode[0]*hc[0];
5653
5654#define stbir__2_coeff_only() \
5655 float tot; \
5656 tot = decode[0] * hc[0]; \
5657 tot += decode[1] * hc[1];
5658
5659#define stbir__3_coeff_only() \
5660 float tot; \
5661 tot = decode[0] * hc[0]; \
5662 tot += decode[1] * hc[1]; \
5663 tot += decode[2] * hc[2];
5664
5665#define stbir__store_output_tiny() \
5666 output[0] = tot; \
5667 stbir__next_contributor(); \
5668 output += 1;
5669
5670#define stbir__4_coeff_start() \
5671 float tot0,tot1,tot2,tot3; \
5672 tot0 = decode[0] * hc[0]; \
5673 tot1 = decode[1] * hc[1]; \
5674 tot2 = decode[2] * hc[2]; \
5675 tot3 = decode[3] * hc[3];
5676
5677#define stbir__4_coeff_continue_from_4( ofs ) \
5678 tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \
5679 tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \
5680 tot2 += decode[2+(ofs)] * hc[2+(ofs)]; \
5681 tot3 += decode[3+(ofs)] * hc[3+(ofs)];
5682
5683#define stbir__1_coeff_remnant( ofs ) \
5684 tot0 += decode[0+(ofs)] * hc[0+(ofs)];
5685
5686#define stbir__2_coeff_remnant( ofs ) \
5687 tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \
5688 tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \
5689
5690#define stbir__3_coeff_remnant( ofs ) \
5691 tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \
5692 tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \
5693 tot2 += decode[2+(ofs)] * hc[2+(ofs)];
5694
5695#define stbir__store_output() \
5696 output[0] = (tot0+tot2)+(tot1+tot3); \
5697 stbir__next_contributor(); \
5698 output += 1;
5699
5700#endif
5701
5702#define STBIR__horizontal_channels 1
5703#define STB_IMAGE_RESIZE_DO_HORIZONTALS
5704#include STBIR__HEADER_FILENAME
5705
5706
5707//=================
5708// Do 2 channel horizontal routines
5709
5710#ifdef STBIR_SIMD
5711
5712#define stbir__1_coeff_only() \
5713 stbir__simdf tot,c,d; \
5714 STBIR_SIMD_NO_UNROLL(decode); \
5715 stbir__simdf_load1z( c, hc ); \
5716 stbir__simdf_0123to0011( c, c ); \
5717 stbir__simdf_load2( d, decode ); \
5718 stbir__simdf_mult( tot, d, c );
5719
5720#define stbir__2_coeff_only() \
5721 stbir__simdf tot,c; \
5722 STBIR_SIMD_NO_UNROLL(decode); \
5723 stbir__simdf_load2( c, hc ); \
5724 stbir__simdf_0123to0011( c, c ); \
5725 stbir__simdf_mult_mem( tot, c, decode );
5726
5727#define stbir__3_coeff_only() \
5728 stbir__simdf tot,c,cs,d; \
5729 STBIR_SIMD_NO_UNROLL(decode); \
5730 stbir__simdf_load( cs, hc ); \
5731 stbir__simdf_0123to0011( c, cs ); \
5732 stbir__simdf_mult_mem( tot, c, decode ); \
5733 stbir__simdf_0123to2222( c, cs ); \
5734 stbir__simdf_load2z( d, decode+4 ); \
5735 stbir__simdf_madd( tot, tot, d, c );
5736
5737#define stbir__store_output_tiny() \
5738 stbir__simdf_0123to2301( c, tot ); \
5739 stbir__simdf_add( tot, tot, c ); \
5740 stbir__simdf_store2( output, tot ); \
5741 stbir__next_contributor(); \
5742 output += 2;
5743
5744// polyphase period 1 for 2 channels (when have exact downsamples 2:1, 3:1, etc - common for mipmaps)
5745#define stbir__period1_setup_4() stbir__simdf P0a, P0b, cs; stbir__simdf_load( cs, hc ); stbir__simdf_0123to0011( P0a, cs ); stbir__simdf_0123to2233( P0b, cs );
5746#define stbir__period1_setup_5() stbir__simdf PR; stbir__period1_setup_4() stbir__simdf_load1z( cs, hc + 4 ); stbir__simdf_0123to0011( PR, cs );
5747#define stbir__period1_setup_8() stbir__simdf P1a, P1b; stbir__period1_setup_4() stbir__simdf_load( cs, hc + 4 ); stbir__simdf_0123to0011( P1a, cs ); stbir__simdf_0123to2233( P1b, cs );
5748#define stbir__period1_setup_9() stbir__simdf PR; stbir__period1_setup_8() stbir__simdf_load1z( cs, hc + 8 ); stbir__simdf_0123to0011( PR, cs );
5749#define stbir__period1_setup_11() stbir__simdf PR3a, PR3b; stbir__period1_setup_8() stbir__simdf_load( cs, hc + 8 ); stbir__simdf_0123to0011( PR3a, cs ); stbir__simdf_0123to2222( PR3b, cs );
5750#define stbir__period1_setup_12() stbir__simdf P2a, P2b; stbir__period1_setup_8() stbir__simdf_load( cs, hc + 8 ); stbir__simdf_0123to0011( P2a, cs ); stbir__simdf_0123to2233( P2b, cs );
5751#define stbir__period1_setup_13() stbir__simdf PR; stbir__period1_setup_12() stbir__simdf_load1z( cs, hc + 12 ); stbir__simdf_0123to0011( PR, cs );
5752#define stbir__period1_setup_16() stbir__simdf P3a, P3b; stbir__period1_setup_12() stbir__simdf_load( cs, hc + 12 ); stbir__simdf_0123to0011( P3a, cs ); stbir__simdf_0123to2233( P3b, cs );
5753#define stbir__period1_setup_17() stbir__simdf PR; stbir__period1_setup_16() stbir__simdf_load1z( cs, hc + 16 ); stbir__simdf_0123to0011( PR, cs );
5754
5755#define stbir__period1_4_coeff_start() \
5756 stbir__simdf tot0, tot1; \
5757 STBIR_SIMD_NO_UNROLL(decode); \
5758 stbir__simdf_mult_mem( tot0, P0a, decode ); \
5759 stbir__simdf_mult_mem( tot1, P0b, decode + 4 );
5760
5761#define stbir__period1_4_coeff_continue() \
5762 stbir__simdf_madd_mem( tot0, tot0, P1a, decode + 8 ); \
5763 stbir__simdf_madd_mem( tot1, tot1, P1b, decode + 12 );
5764#define stbir__period1_4_coeff_continue2() \
5765 stbir__simdf_madd_mem( tot0, tot0, P2a, decode + 16 ); \
5766 stbir__simdf_madd_mem( tot1, tot1, P2b, decode + 20 );
5767#define stbir__period1_4_coeff_continue3() \
5768 stbir__simdf_madd_mem( tot0, tot0, P3a, decode + 24 ); \
5769 stbir__simdf_madd_mem( tot1, tot1, P3b, decode + 28 );
5770
5771#define stbir__period1_1_coeff_remnant( ofs ) \
5772 { stbir__simdf d; \
5773 stbir__simdf_load2( d, decode + (ofs) * 2 ); \
5774 stbir__simdf_madd( tot0, tot0, d, PR ); }
5775#define stbir__period1_3_coeff_remnant( ofs ) \
5776 { stbir__simdf d; \
5777 stbir__simdf_madd_mem( tot0, tot0, PR3a, decode + (ofs) * 2 ); \
5778 stbir__simdf_load2z( d, decode + (ofs) * 2 + 4 ); \
5779 stbir__simdf_madd( tot1, tot1, d, PR3b ); }
5780
5781#define stbir__period1_store_output() \
5782 stbir__simdf_add( tot0, tot0, tot1 ); \
5783 stbir__simdf_0123to2301( tot1, tot0 ); \
5784 stbir__simdf_add( tot0, tot0, tot1 ); \
5785 stbir__simdf_store2( output, tot0 ); \
5786 decode_buffer += advance; \
5787 output += 2;
5788
5789#ifdef STBIR_SIMD8
5790
5791#define stbir__4_coeff_start() \
5792 stbir__simdf8 tot0,c,cs; \
5793 STBIR_SIMD_NO_UNROLL(decode); \
5794 stbir__simdf8_load4b( cs, hc ); \
5795 stbir__simdf8_0123to00112233( c, cs ); \
5796 stbir__simdf8_mult_mem( tot0, c, decode );
5797
5798#define stbir__4_coeff_continue_from_4( ofs ) \
5799 STBIR_SIMD_NO_UNROLL(decode); \
5800 stbir__simdf8_load4b( cs, hc + (ofs) ); \
5801 stbir__simdf8_0123to00112233( c, cs ); \
5802 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*2 );
5803
5804#define stbir__1_coeff_remnant( ofs ) \
5805 { stbir__simdf t,d; \
5806 stbir__simdf_load1z( t, hc + (ofs) ); \
5807 stbir__simdf_load2( d, decode + (ofs) * 2 ); \
5808 stbir__simdf_0123to0011( t, t ); \
5809 stbir__simdf_mult( t, t, d ); \
5810 stbir__simdf8_add4( tot0, tot0, t ); }
5811
5812#define stbir__2_coeff_remnant( ofs ) \
5813 { stbir__simdf t; \
5814 stbir__simdf_load2( t, hc + (ofs) ); \
5815 stbir__simdf_0123to0011( t, t ); \
5816 stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \
5817 stbir__simdf8_add4( tot0, tot0, t ); }
5818
5819#define stbir__3_coeff_remnant( ofs ) \
5820 { stbir__simdf8 d; \
5821 stbir__simdf8_load4b( cs, hc + (ofs) ); \
5822 stbir__simdf8_0123to00112233( c, cs ); \
5823 stbir__simdf8_load6z( d, decode+(ofs)*2 ); \
5824 stbir__simdf8_madd( tot0, tot0, c, d ); }
5825
5826#define stbir__store_output() \
5827 { stbir__simdf t,d; \
5828 stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \
5829 stbir__simdf_0123to2301( d, t ); \
5830 stbir__simdf_add( t, t, d ); \
5831 stbir__simdf_store2( output, t ); \
5832 stbir__next_contributor(); \
5833 output += 2; }
5834
5835#else
5836
5837#define stbir__4_coeff_start() \
5838 stbir__simdf tot0,tot1,c,cs; \
5839 STBIR_SIMD_NO_UNROLL(decode); \
5840 stbir__simdf_load( cs, hc ); \
5841 stbir__simdf_0123to0011( c, cs ); \
5842 stbir__simdf_mult_mem( tot0, c, decode ); \
5843 stbir__simdf_0123to2233( c, cs ); \
5844 stbir__simdf_mult_mem( tot1, c, decode+4 );
5845
5846#define stbir__4_coeff_continue_from_4( ofs ) \
5847 STBIR_SIMD_NO_UNROLL(decode); \
5848 stbir__simdf_load( cs, hc + (ofs) ); \
5849 stbir__simdf_0123to0011( c, cs ); \
5850 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \
5851 stbir__simdf_0123to2233( c, cs ); \
5852 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*2+4 );
5853
5854#define stbir__1_coeff_remnant( ofs ) \
5855 { stbir__simdf d; \
5856 stbir__simdf_load1z( cs, hc + (ofs) ); \
5857 stbir__simdf_0123to0011( c, cs ); \
5858 stbir__simdf_load2( d, decode + (ofs) * 2 ); \
5859 stbir__simdf_madd( tot0, tot0, d, c ); }
5860
5861#define stbir__2_coeff_remnant( ofs ) \
5862 stbir__simdf_load2( cs, hc + (ofs) ); \
5863 stbir__simdf_0123to0011( c, cs ); \
5864 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 );
5865
5866#define stbir__3_coeff_remnant( ofs ) \
5867 { stbir__simdf d; \
5868 stbir__simdf_load( cs, hc + (ofs) ); \
5869 stbir__simdf_0123to0011( c, cs ); \
5870 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \
5871 stbir__simdf_0123to2222( c, cs ); \
5872 stbir__simdf_load2z( d, decode + (ofs) * 2 + 4 ); \
5873 stbir__simdf_madd( tot1, tot1, d, c ); }
5874
5875#define stbir__store_output() \
5876 stbir__simdf_add( tot0, tot0, tot1 ); \
5877 stbir__simdf_0123to2301( c, tot0 ); \
5878 stbir__simdf_add( tot0, tot0, c ); \
5879 stbir__simdf_store2( output, tot0 ); \
5880 stbir__next_contributor(); \
5881 output += 2;
5882
5883#endif
5884
5885#else
5886
5887#define stbir__1_coeff_only() \
5888 float tota,totb,c; \
5889 c = hc[0]; \
5890 tota = decode[0]*c; \
5891 totb = decode[1]*c;
5892
5893#define stbir__2_coeff_only() \
5894 float tota,totb,c; \
5895 c = hc[0]; \
5896 tota = decode[0]*c; \
5897 totb = decode[1]*c; \
5898 c = hc[1]; \
5899 tota += decode[2]*c; \
5900 totb += decode[3]*c;
5901
5902// this weird order of add matches the simd
5903#define stbir__3_coeff_only() \
5904 float tota,totb,c; \
5905 c = hc[0]; \
5906 tota = decode[0]*c; \
5907 totb = decode[1]*c; \
5908 c = hc[2]; \
5909 tota += decode[4]*c; \
5910 totb += decode[5]*c; \
5911 c = hc[1]; \
5912 tota += decode[2]*c; \
5913 totb += decode[3]*c;
5914
5915#define stbir__store_output_tiny() \
5916 output[0] = tota; \
5917 output[1] = totb; \
5918 stbir__next_contributor(); \
5919 output += 2;
5920
5921#define stbir__4_coeff_start() \
5922 float tota0,tota1,tota2,tota3,totb0,totb1,totb2,totb3,c; \
5923 c = hc[0]; \
5924 tota0 = decode[0]*c; \
5925 totb0 = decode[1]*c; \
5926 c = hc[1]; \
5927 tota1 = decode[2]*c; \
5928 totb1 = decode[3]*c; \
5929 c = hc[2]; \
5930 tota2 = decode[4]*c; \
5931 totb2 = decode[5]*c; \
5932 c = hc[3]; \
5933 tota3 = decode[6]*c; \
5934 totb3 = decode[7]*c;
5935
5936#define stbir__4_coeff_continue_from_4( ofs ) \
5937 c = hc[0+(ofs)]; \
5938 tota0 += decode[0+(ofs)*2]*c; \
5939 totb0 += decode[1+(ofs)*2]*c; \
5940 c = hc[1+(ofs)]; \
5941 tota1 += decode[2+(ofs)*2]*c; \
5942 totb1 += decode[3+(ofs)*2]*c; \
5943 c = hc[2+(ofs)]; \
5944 tota2 += decode[4+(ofs)*2]*c; \
5945 totb2 += decode[5+(ofs)*2]*c; \
5946 c = hc[3+(ofs)]; \
5947 tota3 += decode[6+(ofs)*2]*c; \
5948 totb3 += decode[7+(ofs)*2]*c;
5949
5950#define stbir__1_coeff_remnant( ofs ) \
5951 c = hc[0+(ofs)]; \
5952 tota0 += decode[0+(ofs)*2] * c; \
5953 totb0 += decode[1+(ofs)*2] * c;
5954
5955#define stbir__2_coeff_remnant( ofs ) \
5956 c = hc[0+(ofs)]; \
5957 tota0 += decode[0+(ofs)*2] * c; \
5958 totb0 += decode[1+(ofs)*2] * c; \
5959 c = hc[1+(ofs)]; \
5960 tota1 += decode[2+(ofs)*2] * c; \
5961 totb1 += decode[3+(ofs)*2] * c;
5962
5963#define stbir__3_coeff_remnant( ofs ) \
5964 c = hc[0+(ofs)]; \
5965 tota0 += decode[0+(ofs)*2] * c; \
5966 totb0 += decode[1+(ofs)*2] * c; \
5967 c = hc[1+(ofs)]; \
5968 tota1 += decode[2+(ofs)*2] * c; \
5969 totb1 += decode[3+(ofs)*2] * c; \
5970 c = hc[2+(ofs)]; \
5971 tota2 += decode[4+(ofs)*2] * c; \
5972 totb2 += decode[5+(ofs)*2] * c;
5973
5974#define stbir__store_output() \
5975 output[0] = (tota0+tota2)+(tota1+tota3); \
5976 output[1] = (totb0+totb2)+(totb1+totb3); \
5977 stbir__next_contributor(); \
5978 output += 2;
5979
5980#endif
5981
5982#define STBIR__horizontal_channels 2
5983#define STB_IMAGE_RESIZE_DO_HORIZONTALS
5984#include STBIR__HEADER_FILENAME
5985
5986
5987//=================
5988// Do 3 channel horizontal routines
5989
5990#ifdef STBIR_SIMD
5991
5992#define stbir__1_coeff_only() \
5993 stbir__simdf tot,c,d; \
5994 STBIR_SIMD_NO_UNROLL(decode); \
5995 stbir__simdf_load1z( c, hc ); \
5996 stbir__simdf_0123to0001( c, c ); \
5997 stbir__simdf_load( d, decode ); \
5998 stbir__simdf_mult( tot, d, c );
5999
6000#define stbir__2_coeff_only() \
6001 stbir__simdf tot,c,cs,d; \
6002 STBIR_SIMD_NO_UNROLL(decode); \
6003 stbir__simdf_load2( cs, hc ); \
6004 stbir__simdf_0123to0000( c, cs ); \
6005 stbir__simdf_load( d, decode ); \
6006 stbir__simdf_mult( tot, d, c ); \
6007 stbir__simdf_0123to1111( c, cs ); \
6008 stbir__simdf_load( d, decode+3 ); \
6009 stbir__simdf_madd( tot, tot, d, c );
6010
6011#define stbir__3_coeff_only() \
6012 stbir__simdf tot,c,d,cs; \
6013 STBIR_SIMD_NO_UNROLL(decode); \
6014 stbir__simdf_load( cs, hc ); \
6015 stbir__simdf_0123to0000( c, cs ); \
6016 stbir__simdf_load( d, decode ); \
6017 stbir__simdf_mult( tot, d, c ); \
6018 stbir__simdf_0123to1111( c, cs ); \
6019 stbir__simdf_load( d, decode+3 ); \
6020 stbir__simdf_madd( tot, tot, d, c ); \
6021 stbir__simdf_0123to2222( c, cs ); \
6022 stbir__simdf_load( d, decode+6 ); \
6023 stbir__simdf_madd( tot, tot, d, c );
6024
6025#define stbir__store_output_tiny() \
6026 stbir__simdf_store2( output, tot ); \
6027 stbir__simdf_0123to2301( tot, tot ); \
6028 stbir__simdf_store1( output+2, tot ); \
6029 stbir__next_contributor(); \
6030 output += 3;
6031
6032#ifdef STBIR_SIMD8
6033
6034// we're loading from the XXXYYY decode by -1 to get the XXXYYY into different halves of the AVX reg fyi
6035#define stbir__4_coeff_start() \
6036 stbir__simdf8 tot0,tot1,c,cs; stbir__simdf t; \
6037 STBIR_SIMD_NO_UNROLL(decode); \
6038 stbir__simdf8_load4b( cs, hc ); \
6039 stbir__simdf8_0123to00001111( c, cs ); \
6040 stbir__simdf8_mult_mem( tot0, c, decode - 1 ); \
6041 stbir__simdf8_0123to22223333( c, cs ); \
6042 stbir__simdf8_mult_mem( tot1, c, decode+6 - 1 );
6043
6044#define stbir__4_coeff_continue_from_4( ofs ) \
6045 STBIR_SIMD_NO_UNROLL(decode); \
6046 stbir__simdf8_load4b( cs, hc + (ofs) ); \
6047 stbir__simdf8_0123to00001111( c, cs ); \
6048 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \
6049 stbir__simdf8_0123to22223333( c, cs ); \
6050 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*3 + 6 - 1 );
6051
6052#define stbir__1_coeff_remnant( ofs ) \
6053 STBIR_SIMD_NO_UNROLL(decode); \
6054 stbir__simdf_load1rep4( t, hc + (ofs) ); \
6055 stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*3 - 1 );
6056
6057#define stbir__2_coeff_remnant( ofs ) \
6058 STBIR_SIMD_NO_UNROLL(decode); \
6059 stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \
6060 stbir__simdf8_0123to22223333( c, cs ); \
6061 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 );
6062
6063 #define stbir__3_coeff_remnant( ofs ) \
6064 STBIR_SIMD_NO_UNROLL(decode); \
6065 stbir__simdf8_load4b( cs, hc + (ofs) ); \
6066 stbir__simdf8_0123to00001111( c, cs ); \
6067 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \
6068 stbir__simdf8_0123to2222( t, cs ); \
6069 stbir__simdf8_madd_mem4( tot1, tot1, t, decode+(ofs)*3 + 6 - 1 );
6070
6071// polyphase period 1 for 3 channels (when have exact downsamples 2:1, 3:1, etc - common for mipmaps)
6072#define stbir__period1_setup_4() stbir__simdf8 P01, P23, cs; stbir__simdf8_load4b( cs, hc ); stbir__simdf8_0123to00001111( P01, cs ); stbir__simdf8_0123to22223333( P23, cs );
6073#define stbir__period1_setup_5() stbir__simdf PR; stbir__period1_setup_4() stbir__simdf_load1rep4( PR, hc + 4 );
6074#define stbir__period1_setup_8() stbir__simdf8 P45, P67; stbir__period1_setup_4() stbir__simdf8_load4b( cs, hc + 4 ); stbir__simdf8_0123to00001111( P45, cs ); stbir__simdf8_0123to22223333( P67, cs );
6075#define stbir__period1_setup_9() stbir__simdf PR; stbir__period1_setup_8() stbir__simdf_load1rep4( PR, hc + 8 );
6076#define stbir__period1_setup_11() stbir__simdf8 P89; stbir__simdf PR; stbir__period1_setup_8() stbir__simdf8_load4b( cs, hc + 8 ); stbir__simdf8_0123to00001111( P89, cs ); stbir__simdf8_0123to2222( PR, cs );
6077#define stbir__period1_setup_12() stbir__simdf8 P89, PAB; stbir__period1_setup_8() stbir__simdf8_load4b( cs, hc + 8 ); stbir__simdf8_0123to00001111( P89, cs ); stbir__simdf8_0123to22223333( PAB, cs );
6078#define stbir__period1_setup_13() stbir__simdf PR; stbir__period1_setup_12() stbir__simdf_load1rep4( PR, hc + 12 );
6079#define stbir__period1_setup_16() stbir__simdf8 PCD, PEF; stbir__period1_setup_12() stbir__simdf8_load4b( cs, hc + 12 ); stbir__simdf8_0123to00001111( PCD, cs ); stbir__simdf8_0123to22223333( PEF, cs );
6080#define stbir__period1_setup_17() stbir__simdf PR; stbir__period1_setup_16() stbir__simdf_load1rep4( PR, hc + 16 );
6081
6082#define stbir__period1_4_coeff_start() \
6083 stbir__simdf8 tot0, tot1; stbir__simdf t; \
6084 STBIR_SIMD_NO_UNROLL(decode); \
6085 stbir__simdf8_mult_mem( tot0, P01, decode - 1 ); \
6086 stbir__simdf8_mult_mem( tot1, P23, decode + 6 - 1 );
6087
6088#define stbir__period1_4_coeff_continue() \
6089 stbir__simdf8_madd_mem( tot0, tot0, P45, decode + 12 - 1 ); \
6090 stbir__simdf8_madd_mem( tot1, tot1, P67, decode + 18 - 1 );
6091#define stbir__period1_4_coeff_continue2() \
6092 stbir__simdf8_madd_mem( tot0, tot0, P89, decode + 24 - 1 ); \
6093 stbir__simdf8_madd_mem( tot1, tot1, PAB, decode + 30 - 1 );
6094#define stbir__period1_4_coeff_continue3() \
6095 stbir__simdf8_madd_mem( tot0, tot0, PCD, decode + 36 - 1 ); \
6096 stbir__simdf8_madd_mem( tot1, tot1, PEF, decode + 42 - 1 );
6097
6098#define stbir__period1_1_coeff_remnant( ofs ) \
6099 stbir__simdf8_madd_mem4( tot0, tot0, PR, decode + (ofs) * 3 - 1 );
6100#define stbir__period1_3_coeff_remnant( ofs ) \
6101 stbir__simdf8_madd_mem( tot0, tot0, P89, decode + (ofs) * 3 - 1 ); \
6102 stbir__simdf8_madd_mem4( tot1, tot1, PR, decode + (ofs) * 3 + 6 - 1 );
6103
6104#define stbir__period1_store_output() \
6105 stbir__simdf8_add( tot0, tot0, tot1 ); \
6106 stbir__simdf_0123to1230( t, stbir__if_simdf8_cast_to_simdf4( tot0 ) ); \
6107 stbir__simdf8_add4halves( t, t, tot0 ); \
6108 decode_buffer += advance; \
6109 output += 3; \
6110 if ( output < output_end ) \
6111 { \
6112 stbir__simdf_store( output-3, t ); \
6113 continue; \
6114 } \
6115 { stbir__simdf tt; stbir__simdf_0123to2301( tt, t ); \
6116 stbir__simdf_store2( output-3, t ); \
6117 stbir__simdf_store1( output+2-3, tt ); } \
6118 break;
6119
6120#define stbir__store_output() \
6121 stbir__simdf8_add( tot0, tot0, tot1 ); \
6122 stbir__simdf_0123to1230( t, stbir__if_simdf8_cast_to_simdf4( tot0 ) ); \
6123 stbir__simdf8_add4halves( t, t, tot0 ); \
6124 stbir__next_contributor(); \
6125 output += 3; \
6126 if ( output < output_end ) \
6127 { \
6128 stbir__simdf_store( output-3, t ); \
6129 continue; \
6130 } \
6131 { stbir__simdf tt; stbir__simdf_0123to2301( tt, t ); \
6132 stbir__simdf_store2( output-3, t ); \
6133 stbir__simdf_store1( output+2-3, tt ); } \
6134 break;
6135
6136
6137#else
6138
6139#define stbir__4_coeff_start() \
6140 stbir__simdf tot0,tot1,tot2,c,cs; \
6141 STBIR_SIMD_NO_UNROLL(decode); \
6142 stbir__simdf_load( cs, hc ); \
6143 stbir__simdf_0123to0001( c, cs ); \
6144 stbir__simdf_mult_mem( tot0, c, decode ); \
6145 stbir__simdf_0123to1122( c, cs ); \
6146 stbir__simdf_mult_mem( tot1, c, decode+4 ); \
6147 stbir__simdf_0123to2333( c, cs ); \
6148 stbir__simdf_mult_mem( tot2, c, decode+8 );
6149
6150#define stbir__4_coeff_continue_from_4( ofs ) \
6151 STBIR_SIMD_NO_UNROLL(decode); \
6152 stbir__simdf_load( cs, hc + (ofs) ); \
6153 stbir__simdf_0123to0001( c, cs ); \
6154 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \
6155 stbir__simdf_0123to1122( c, cs ); \
6156 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \
6157 stbir__simdf_0123to2333( c, cs ); \
6158 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*3+8 );
6159
6160#define stbir__1_coeff_remnant( ofs ) \
6161 STBIR_SIMD_NO_UNROLL(decode); \
6162 stbir__simdf_load1z( c, hc + (ofs) ); \
6163 stbir__simdf_0123to0001( c, c ); \
6164 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 );
6165
6166#define stbir__2_coeff_remnant( ofs ) \
6167 { stbir__simdf d; \
6168 STBIR_SIMD_NO_UNROLL(decode); \
6169 stbir__simdf_load2z( cs, hc + (ofs) ); \
6170 stbir__simdf_0123to0001( c, cs ); \
6171 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \
6172 stbir__simdf_0123to1122( c, cs ); \
6173 stbir__simdf_load2z( d, decode+(ofs)*3+4 ); \
6174 stbir__simdf_madd( tot1, tot1, c, d ); }
6175
6176#define stbir__3_coeff_remnant( ofs ) \
6177 { stbir__simdf d; \
6178 STBIR_SIMD_NO_UNROLL(decode); \
6179 stbir__simdf_load( cs, hc + (ofs) ); \
6180 stbir__simdf_0123to0001( c, cs ); \
6181 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \
6182 stbir__simdf_0123to1122( c, cs ); \
6183 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \
6184 stbir__simdf_0123to2222( c, cs ); \
6185 stbir__simdf_load1z( d, decode+(ofs)*3+8 ); \
6186 stbir__simdf_madd( tot2, tot2, c, d ); }
6187
6188// polyphase period 1 for 3 channels (when have exact downsamples 2:1, 3:1, etc - common for mipmaps)
6189#define stbir__period1_setup_4() stbir__simdf P0a, P0b, P0c, cs; stbir__simdf_load( cs, hc ); stbir__simdf_0123to0001( P0a, cs ); stbir__simdf_0123to1122( P0b, cs ); stbir__simdf_0123to2333( P0c, cs );
6190#define stbir__period1_setup_5() stbir__simdf PR; stbir__period1_setup_4() stbir__simdf_load1z( cs, hc + 4 ); stbir__simdf_0123to0001( PR, cs );
6191#define stbir__period1_setup_8() stbir__simdf P1a, P1b, P1c; stbir__period1_setup_4() stbir__simdf_load( cs, hc + 4 ); stbir__simdf_0123to0001( P1a, cs ); stbir__simdf_0123to1122( P1b, cs ); stbir__simdf_0123to2333( P1c, cs );
6192#define stbir__period1_setup_9() stbir__simdf PR; stbir__period1_setup_8() stbir__simdf_load1z( cs, hc + 8 ); stbir__simdf_0123to0001( PR, cs );
6193#define stbir__period1_setup_11() stbir__simdf PR3a, PR3b, PR3c; stbir__period1_setup_8() stbir__simdf_load( cs, hc + 8 ); stbir__simdf_0123to0001( PR3a, cs ); stbir__simdf_0123to1122( PR3b, cs ); stbir__simdf_0123to2222( PR3c, cs );
6194#define stbir__period1_setup_12() stbir__simdf P2a, P2b, P2c; stbir__period1_setup_8() stbir__simdf_load( cs, hc + 8 ); stbir__simdf_0123to0001( P2a, cs ); stbir__simdf_0123to1122( P2b, cs ); stbir__simdf_0123to2333( P2c, cs );
6195#define stbir__period1_setup_13() stbir__simdf PR; stbir__period1_setup_12() stbir__simdf_load1z( cs, hc + 12 ); stbir__simdf_0123to0001( PR, cs );
6196#define stbir__period1_setup_16() stbir__simdf P3a, P3b, P3c; stbir__period1_setup_12() stbir__simdf_load( cs, hc + 12 ); stbir__simdf_0123to0001( P3a, cs ); stbir__simdf_0123to1122( P3b, cs ); stbir__simdf_0123to2333( P3c, cs );
6197#define stbir__period1_setup_17() stbir__simdf PR; stbir__period1_setup_16() stbir__simdf_load1z( cs, hc + 16 ); stbir__simdf_0123to0001( PR, cs );
6198
6199#define stbir__period1_4_coeff_start() \
6200 stbir__simdf tot0, tot1, tot2, c; \
6201 STBIR_SIMD_NO_UNROLL(decode); \
6202 stbir__simdf_mult_mem( tot0, P0a, decode ); \
6203 stbir__simdf_mult_mem( tot1, P0b, decode + 4 ); \
6204 stbir__simdf_mult_mem( tot2, P0c, decode + 8 );
6205
6206#define stbir__period1_4_coeff_continue() \
6207 stbir__simdf_madd_mem( tot0, tot0, P1a, decode + 12 ); \
6208 stbir__simdf_madd_mem( tot1, tot1, P1b, decode + 16 ); \
6209 stbir__simdf_madd_mem( tot2, tot2, P1c, decode + 20 );
6210#define stbir__period1_4_coeff_continue2() \
6211 stbir__simdf_madd_mem( tot0, tot0, P2a, decode + 24 ); \
6212 stbir__simdf_madd_mem( tot1, tot1, P2b, decode + 28 ); \
6213 stbir__simdf_madd_mem( tot2, tot2, P2c, decode + 32 );
6214#define stbir__period1_4_coeff_continue3() \
6215 stbir__simdf_madd_mem( tot0, tot0, P3a, decode + 36 ); \
6216 stbir__simdf_madd_mem( tot1, tot1, P3b, decode + 40 ); \
6217 stbir__simdf_madd_mem( tot2, tot2, P3c, decode + 44 );
6218
6219#define stbir__period1_1_coeff_remnant( ofs ) \
6220 stbir__simdf_madd_mem( tot0, tot0, PR, decode + (ofs) * 3 );
6221#define stbir__period1_3_coeff_remnant( ofs ) \
6222 { stbir__simdf d; \
6223 stbir__simdf_madd_mem( tot0, tot0, PR3a, decode + (ofs) * 3 ); \
6224 stbir__simdf_madd_mem( tot1, tot1, PR3b, decode + (ofs) * 3 + 4 ); \
6225 stbir__simdf_load1z( d, decode + (ofs) * 3 + 8 ); \
6226 stbir__simdf_madd( tot2, tot2, PR3c, d ); }
6227
6228#define stbir__period1_store_output() \
6229 stbir__simdf_0123ABCDto3ABx( c, tot0, tot1 ); \
6230 stbir__simdf_0123ABCDto23Ax( cs, tot1, tot2 ); \
6231 stbir__simdf_0123to1230( tot2, tot2 ); \
6232 stbir__simdf_add( tot0, tot0, cs ); \
6233 stbir__simdf_add( c, c, tot2 ); \
6234 stbir__simdf_add( tot0, tot0, c ); \
6235 decode_buffer += advance; \
6236 output += 3; \
6237 if ( output < output_end ) \
6238 { \
6239 stbir__simdf_store( output-3, tot0 ); \
6240 continue; \
6241 } \
6242 stbir__simdf_0123to2301( tot1, tot0 ); \
6243 stbir__simdf_store2( output-3, tot0 ); \
6244 stbir__simdf_store1( output+2-3, tot1 ); \
6245 break;
6246
6247#define stbir__store_output() \
6248 stbir__simdf_0123ABCDto3ABx( c, tot0, tot1 ); \
6249 stbir__simdf_0123ABCDto23Ax( cs, tot1, tot2 ); \
6250 stbir__simdf_0123to1230( tot2, tot2 ); \
6251 stbir__simdf_add( tot0, tot0, cs ); \
6252 stbir__simdf_add( c, c, tot2 ); \
6253 stbir__simdf_add( tot0, tot0, c ); \
6254 stbir__next_contributor(); \
6255 output += 3; \
6256 if ( output < output_end ) \
6257 { \
6258 stbir__simdf_store( output-3, tot0 ); \
6259 continue; \
6260 } \
6261 stbir__simdf_0123to2301( tot1, tot0 ); \
6262 stbir__simdf_store2( output-3, tot0 ); \
6263 stbir__simdf_store1( output+2-3, tot1 ); \
6264 break;
6265
6266#endif
6267
6268#else
6269
6270#define stbir__1_coeff_only() \
6271 float tot0, tot1, tot2, c; \
6272 c = hc[0]; \
6273 tot0 = decode[0]*c; \
6274 tot1 = decode[1]*c; \
6275 tot2 = decode[2]*c;
6276
6277#define stbir__2_coeff_only() \
6278 float tot0, tot1, tot2, c; \
6279 c = hc[0]; \
6280 tot0 = decode[0]*c; \
6281 tot1 = decode[1]*c; \
6282 tot2 = decode[2]*c; \
6283 c = hc[1]; \
6284 tot0 += decode[3]*c; \
6285 tot1 += decode[4]*c; \
6286 tot2 += decode[5]*c;
6287
6288#define stbir__3_coeff_only() \
6289 float tot0, tot1, tot2, c; \
6290 c = hc[0]; \
6291 tot0 = decode[0]*c; \
6292 tot1 = decode[1]*c; \
6293 tot2 = decode[2]*c; \
6294 c = hc[1]; \
6295 tot0 += decode[3]*c; \
6296 tot1 += decode[4]*c; \
6297 tot2 += decode[5]*c; \
6298 c = hc[2]; \
6299 tot0 += decode[6]*c; \
6300 tot1 += decode[7]*c; \
6301 tot2 += decode[8]*c;
6302
6303#define stbir__store_output_tiny() \
6304 output[0] = tot0; \
6305 output[1] = tot1; \
6306 output[2] = tot2; \
6307 stbir__next_contributor(); \
6308 output += 3;
6309
6310#define stbir__4_coeff_start() \
6311 float tota0,tota1,tota2,totb0,totb1,totb2,totc0,totc1,totc2,totd0,totd1,totd2,c; \
6312 c = hc[0]; \
6313 tota0 = decode[0]*c; \
6314 tota1 = decode[1]*c; \
6315 tota2 = decode[2]*c; \
6316 c = hc[1]; \
6317 totb0 = decode[3]*c; \
6318 totb1 = decode[4]*c; \
6319 totb2 = decode[5]*c; \
6320 c = hc[2]; \
6321 totc0 = decode[6]*c; \
6322 totc1 = decode[7]*c; \
6323 totc2 = decode[8]*c; \
6324 c = hc[3]; \
6325 totd0 = decode[9]*c; \
6326 totd1 = decode[10]*c; \
6327 totd2 = decode[11]*c;
6328
6329#define stbir__4_coeff_continue_from_4( ofs ) \
6330 c = hc[0+(ofs)]; \
6331 tota0 += decode[0+(ofs)*3]*c; \
6332 tota1 += decode[1+(ofs)*3]*c; \
6333 tota2 += decode[2+(ofs)*3]*c; \
6334 c = hc[1+(ofs)]; \
6335 totb0 += decode[3+(ofs)*3]*c; \
6336 totb1 += decode[4+(ofs)*3]*c; \
6337 totb2 += decode[5+(ofs)*3]*c; \
6338 c = hc[2+(ofs)]; \
6339 totc0 += decode[6+(ofs)*3]*c; \
6340 totc1 += decode[7+(ofs)*3]*c; \
6341 totc2 += decode[8+(ofs)*3]*c; \
6342 c = hc[3+(ofs)]; \
6343 totd0 += decode[9+(ofs)*3]*c; \
6344 totd1 += decode[10+(ofs)*3]*c; \
6345 totd2 += decode[11+(ofs)*3]*c;
6346
6347#define stbir__1_coeff_remnant( ofs ) \
6348 c = hc[0+(ofs)]; \
6349 tota0 += decode[0+(ofs)*3]*c; \
6350 tota1 += decode[1+(ofs)*3]*c; \
6351 tota2 += decode[2+(ofs)*3]*c;
6352
6353#define stbir__2_coeff_remnant( ofs ) \
6354 c = hc[0+(ofs)]; \
6355 tota0 += decode[0+(ofs)*3]*c; \
6356 tota1 += decode[1+(ofs)*3]*c; \
6357 tota2 += decode[2+(ofs)*3]*c; \
6358 c = hc[1+(ofs)]; \
6359 totb0 += decode[3+(ofs)*3]*c; \
6360 totb1 += decode[4+(ofs)*3]*c; \
6361 totb2 += decode[5+(ofs)*3]*c; \
6362
6363#define stbir__3_coeff_remnant( ofs ) \
6364 c = hc[0+(ofs)]; \
6365 tota0 += decode[0+(ofs)*3]*c; \
6366 tota1 += decode[1+(ofs)*3]*c; \
6367 tota2 += decode[2+(ofs)*3]*c; \
6368 c = hc[1+(ofs)]; \
6369 totb0 += decode[3+(ofs)*3]*c; \
6370 totb1 += decode[4+(ofs)*3]*c; \
6371 totb2 += decode[5+(ofs)*3]*c; \
6372 c = hc[2+(ofs)]; \
6373 totc0 += decode[6+(ofs)*3]*c; \
6374 totc1 += decode[7+(ofs)*3]*c; \
6375 totc2 += decode[8+(ofs)*3]*c;
6376
6377#define stbir__store_output() \
6378 output[0] = (tota0+totc0)+(totb0+totd0); \
6379 output[1] = (tota1+totc1)+(totb1+totd1); \
6380 output[2] = (tota2+totc2)+(totb2+totd2); \
6381 stbir__next_contributor(); \
6382 output += 3;
6383
6384#endif
6385
6386#define STBIR__horizontal_channels 3
6387#define STB_IMAGE_RESIZE_DO_HORIZONTALS
6388#include STBIR__HEADER_FILENAME
6389
6390//=================
6391// Do 4 channel horizontal routines
6392
6393#ifdef STBIR_SIMD
6394
6395#define stbir__1_coeff_only() \
6396 stbir__simdf tot,c; \
6397 STBIR_SIMD_NO_UNROLL(decode); \
6398 stbir__simdf_load1( c, hc ); \
6399 stbir__simdf_0123to0000( c, c ); \
6400 stbir__simdf_mult_mem( tot, c, decode );
6401
6402#define stbir__2_coeff_only() \
6403 stbir__simdf tot,c,cs; \
6404 STBIR_SIMD_NO_UNROLL(decode); \
6405 stbir__simdf_load2( cs, hc ); \
6406 stbir__simdf_0123to0000( c, cs ); \
6407 stbir__simdf_mult_mem( tot, c, decode ); \
6408 stbir__simdf_0123to1111( c, cs ); \
6409 stbir__simdf_madd_mem( tot, tot, c, decode+4 );
6410
6411#define stbir__3_coeff_only() \
6412 stbir__simdf tot,c,cs; \
6413 STBIR_SIMD_NO_UNROLL(decode); \
6414 stbir__simdf_load( cs, hc ); \
6415 stbir__simdf_0123to0000( c, cs ); \
6416 stbir__simdf_mult_mem( tot, c, decode ); \
6417 stbir__simdf_0123to1111( c, cs ); \
6418 stbir__simdf_madd_mem( tot, tot, c, decode+4 ); \
6419 stbir__simdf_0123to2222( c, cs ); \
6420 stbir__simdf_madd_mem( tot, tot, c, decode+8 );
6421
6422#define stbir__store_output_tiny() \
6423 stbir__simdf_store( output, tot ); \
6424 stbir__next_contributor(); \
6425 output += 4;
6426
6427#ifdef STBIR_SIMD8
6428
6429#define stbir__4_coeff_start() \
6430 stbir__simdf8 tot0,c,cs; stbir__simdf t; \
6431 STBIR_SIMD_NO_UNROLL(decode); \
6432 stbir__simdf8_load4b( cs, hc ); \
6433 stbir__simdf8_0123to00001111( c, cs ); \
6434 stbir__simdf8_mult_mem( tot0, c, decode ); \
6435 stbir__simdf8_0123to22223333( c, cs ); \
6436 stbir__simdf8_madd_mem( tot0, tot0, c, decode+8 );
6437
6438#define stbir__4_coeff_continue_from_4( ofs ) \
6439 STBIR_SIMD_NO_UNROLL(decode); \
6440 stbir__simdf8_load4b( cs, hc + (ofs) ); \
6441 stbir__simdf8_0123to00001111( c, cs ); \
6442 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
6443 stbir__simdf8_0123to22223333( c, cs ); \
6444 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 );
6445
6446#define stbir__1_coeff_remnant( ofs ) \
6447 STBIR_SIMD_NO_UNROLL(decode); \
6448 stbir__simdf_load1rep4( t, hc + (ofs) ); \
6449 stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4 );
6450
6451#define stbir__2_coeff_remnant( ofs ) \
6452 STBIR_SIMD_NO_UNROLL(decode); \
6453 stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \
6454 stbir__simdf8_0123to22223333( c, cs ); \
6455 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 );
6456
6457 #define stbir__3_coeff_remnant( ofs ) \
6458 STBIR_SIMD_NO_UNROLL(decode); \
6459 stbir__simdf8_load4b( cs, hc + (ofs) ); \
6460 stbir__simdf8_0123to00001111( c, cs ); \
6461 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
6462 stbir__simdf8_0123to2222( t, cs ); \
6463 stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4+8 );
6464
6465// polyphase period 1 for 4 channels (when have exact downsamples 2:1, 3:1, etc - common for mipmaps)
6466#define stbir__period1_setup_4() stbir__simdf8 P01, P23, cs; stbir__simdf8_load4b( cs, hc ); stbir__simdf8_0123to00001111( P01, cs ); stbir__simdf8_0123to22223333( P23, cs );
6467#define stbir__period1_setup_5() stbir__simdf PR; stbir__period1_setup_4() stbir__simdf_load1rep4( PR, hc + 4 );
6468#define stbir__period1_setup_8() stbir__simdf8 P45, P67; stbir__period1_setup_4() stbir__simdf8_load4b( cs, hc + 4 ); stbir__simdf8_0123to00001111( P45, cs ); stbir__simdf8_0123to22223333( P67, cs );
6469#define stbir__period1_setup_9() stbir__simdf PR; stbir__period1_setup_8() stbir__simdf_load1rep4( PR, hc + 8 );
6470#define stbir__period1_setup_11() stbir__simdf8 P89; stbir__simdf PR; stbir__period1_setup_8() stbir__simdf8_load4b( cs, hc + 8 ); stbir__simdf8_0123to00001111( P89, cs ); stbir__simdf8_0123to2222( PR, cs );
6471#define stbir__period1_setup_12() stbir__simdf8 P89, PAB; stbir__period1_setup_8() stbir__simdf8_load4b( cs, hc + 8 ); stbir__simdf8_0123to00001111( P89, cs ); stbir__simdf8_0123to22223333( PAB, cs );
6472#define stbir__period1_setup_13() stbir__simdf PR; stbir__period1_setup_12() stbir__simdf_load1rep4( PR, hc + 12 );
6473#define stbir__period1_setup_16() stbir__simdf8 PCD, PEF; stbir__period1_setup_12() stbir__simdf8_load4b( cs, hc + 12 ); stbir__simdf8_0123to00001111( PCD, cs ); stbir__simdf8_0123to22223333( PEF, cs );
6474#define stbir__period1_setup_17() stbir__simdf PR; stbir__period1_setup_16() stbir__simdf_load1rep4( PR, hc + 16 );
6475
6476#define stbir__period1_4_coeff_start() \
6477 stbir__simdf8 tot0; stbir__simdf t; \
6478 STBIR_SIMD_NO_UNROLL(decode); \
6479 stbir__simdf8_mult_mem( tot0, P01, decode ); \
6480 stbir__simdf8_madd_mem( tot0, tot0, P23, decode + 8 );
6481
6482#define stbir__period1_4_coeff_continue() \
6483 stbir__simdf8_madd_mem( tot0, tot0, P45, decode + 16 ); \
6484 stbir__simdf8_madd_mem( tot0, tot0, P67, decode + 24 );
6485#define stbir__period1_4_coeff_continue2() \
6486 stbir__simdf8_madd_mem( tot0, tot0, P89, decode + 32 ); \
6487 stbir__simdf8_madd_mem( tot0, tot0, PAB, decode + 40 );
6488#define stbir__period1_4_coeff_continue3() \
6489 stbir__simdf8_madd_mem( tot0, tot0, PCD, decode + 48 ); \
6490 stbir__simdf8_madd_mem( tot0, tot0, PEF, decode + 56 );
6491
6492#define stbir__period1_1_coeff_remnant( ofs ) \
6493 stbir__simdf8_madd_mem4( tot0, tot0, PR, decode + (ofs) * 4 );
6494#define stbir__period1_3_coeff_remnant( ofs ) \
6495 stbir__simdf8_madd_mem( tot0, tot0, P89, decode + (ofs) * 4 ); \
6496 stbir__simdf8_madd_mem4( tot0, tot0, PR, decode + (ofs) * 4 + 8 );
6497
6498#define stbir__period1_store_output() \
6499 stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \
6500 stbir__simdf_store( output, t ); \
6501 decode_buffer += advance; \
6502 output += 4;
6503
6504#define stbir__store_output() \
6505 stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \
6506 stbir__simdf_store( output, t ); \
6507 stbir__next_contributor(); \
6508 output += 4;
6509
6510#else
6511
6512#define stbir__4_coeff_start() \
6513 stbir__simdf tot0,tot1,c,cs; \
6514 STBIR_SIMD_NO_UNROLL(decode); \
6515 stbir__simdf_load( cs, hc ); \
6516 stbir__simdf_0123to0000( c, cs ); \
6517 stbir__simdf_mult_mem( tot0, c, decode ); \
6518 stbir__simdf_0123to1111( c, cs ); \
6519 stbir__simdf_mult_mem( tot1, c, decode+4 ); \
6520 stbir__simdf_0123to2222( c, cs ); \
6521 stbir__simdf_madd_mem( tot0, tot0, c, decode+8 ); \
6522 stbir__simdf_0123to3333( c, cs ); \
6523 stbir__simdf_madd_mem( tot1, tot1, c, decode+12 );
6524
6525#define stbir__4_coeff_continue_from_4( ofs ) \
6526 STBIR_SIMD_NO_UNROLL(decode); \
6527 stbir__simdf_load( cs, hc + (ofs) ); \
6528 stbir__simdf_0123to0000( c, cs ); \
6529 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
6530 stbir__simdf_0123to1111( c, cs ); \
6531 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \
6532 stbir__simdf_0123to2222( c, cs ); \
6533 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); \
6534 stbir__simdf_0123to3333( c, cs ); \
6535 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+12 );
6536
6537#define stbir__1_coeff_remnant( ofs ) \
6538 STBIR_SIMD_NO_UNROLL(decode); \
6539 stbir__simdf_load1( c, hc + (ofs) ); \
6540 stbir__simdf_0123to0000( c, c ); \
6541 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 );
6542
6543#define stbir__2_coeff_remnant( ofs ) \
6544 STBIR_SIMD_NO_UNROLL(decode); \
6545 stbir__simdf_load2( cs, hc + (ofs) ); \
6546 stbir__simdf_0123to0000( c, cs ); \
6547 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
6548 stbir__simdf_0123to1111( c, cs ); \
6549 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 );
6550
6551#define stbir__3_coeff_remnant( ofs ) \
6552 STBIR_SIMD_NO_UNROLL(decode); \
6553 stbir__simdf_load( cs, hc + (ofs) ); \
6554 stbir__simdf_0123to0000( c, cs ); \
6555 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
6556 stbir__simdf_0123to1111( c, cs ); \
6557 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \
6558 stbir__simdf_0123to2222( c, cs ); \
6559 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 );
6560
6561// polyphase period 1 for 4 channels (when have exact downsamples 2:1, 3:1, etc - common for mipmaps)
6562#define stbir__period1_setup_4() stbir__simdf P0, P1, P2, P3; stbir__simdf_load1frep4( P0, hc[0] ); stbir__simdf_load1frep4( P1, hc[1] ); stbir__simdf_load1frep4( P2, hc[2] ); stbir__simdf_load1frep4( P3, hc[3] );
6563#define stbir__period1_setup_5() stbir__simdf PR; stbir__period1_setup_4() stbir__simdf_load1frep4( PR, hc[4] );
6564#define stbir__period1_setup_8() stbir__simdf P4, P5, P6, P7; stbir__period1_setup_4() stbir__simdf_load1frep4( P4, hc[4] ); stbir__simdf_load1frep4( P5, hc[5] ); stbir__simdf_load1frep4( P6, hc[6] ); stbir__simdf_load1frep4( P7, hc[7] );
6565#define stbir__period1_setup_9() stbir__simdf PR; stbir__period1_setup_8() stbir__simdf_load1frep4( PR, hc[8] );
6566#define stbir__period1_setup_11() stbir__simdf P8, P9, P10; stbir__period1_setup_8() stbir__simdf_load1frep4( P8, hc[8] ); stbir__simdf_load1frep4( P9, hc[9] ); stbir__simdf_load1frep4( P10, hc[10] );
6567#define stbir__period1_setup_12() stbir__simdf P8, P9, P10, P11; stbir__period1_setup_8() stbir__simdf_load1frep4( P8, hc[8] ); stbir__simdf_load1frep4( P9, hc[9] ); stbir__simdf_load1frep4( P10, hc[10] ); stbir__simdf_load1frep4( P11, hc[11] );
6568#define stbir__period1_setup_13() stbir__simdf PR; stbir__period1_setup_12() stbir__simdf_load1frep4( PR, hc[12] );
6569#define stbir__period1_setup_16() stbir__simdf P12, P13, P14, P15; stbir__period1_setup_12() stbir__simdf_load1frep4( P12, hc[12] ); stbir__simdf_load1frep4( P13, hc[13] ); stbir__simdf_load1frep4( P14, hc[14] ); stbir__simdf_load1frep4( P15, hc[15] );
6570#define stbir__period1_setup_17() stbir__simdf PR; stbir__period1_setup_16() stbir__simdf_load1frep4( PR, hc[16] );
6571
6572#define stbir__period1_4_coeff_start() \
6573 stbir__simdf tot0, tot1; \
6574 STBIR_SIMD_NO_UNROLL(decode); \
6575 stbir__simdf_mult_mem( tot0, P0, decode ); \
6576 stbir__simdf_mult_mem( tot1, P1, decode + 4 ); \
6577 stbir__simdf_madd_mem( tot0, tot0, P2, decode + 8 ); \
6578 stbir__simdf_madd_mem( tot1, tot1, P3, decode + 12 );
6579
6580#define stbir__period1_4_coeff_continue() \
6581 stbir__simdf_madd_mem( tot0, tot0, P4, decode + 16 ); \
6582 stbir__simdf_madd_mem( tot1, tot1, P5, decode + 20 ); \
6583 stbir__simdf_madd_mem( tot0, tot0, P6, decode + 24 ); \
6584 stbir__simdf_madd_mem( tot1, tot1, P7, decode + 28 );
6585#define stbir__period1_4_coeff_continue2() \
6586 stbir__simdf_madd_mem( tot0, tot0, P8, decode + 32 ); \
6587 stbir__simdf_madd_mem( tot1, tot1, P9, decode + 36 ); \
6588 stbir__simdf_madd_mem( tot0, tot0, P10, decode + 40 ); \
6589 stbir__simdf_madd_mem( tot1, tot1, P11, decode + 44 );
6590#define stbir__period1_4_coeff_continue3() \
6591 stbir__simdf_madd_mem( tot0, tot0, P12, decode + 48 ); \
6592 stbir__simdf_madd_mem( tot1, tot1, P13, decode + 52 ); \
6593 stbir__simdf_madd_mem( tot0, tot0, P14, decode + 56 ); \
6594 stbir__simdf_madd_mem( tot1, tot1, P15, decode + 60 );
6595
6596#define stbir__period1_1_coeff_remnant( ofs ) \
6597 stbir__simdf_madd_mem( tot0, tot0, PR, decode + (ofs) * 4 );
6598#define stbir__period1_3_coeff_remnant( ofs ) \
6599 stbir__simdf_madd_mem( tot0, tot0, P8, decode + (ofs) * 4 ); \
6600 stbir__simdf_madd_mem( tot1, tot1, P9, decode + (ofs) * 4 + 4 ); \
6601 stbir__simdf_madd_mem( tot0, tot0, P10, decode + (ofs) * 4 + 8 );
6602
6603#define stbir__period1_store_output() \
6604 stbir__simdf_add( tot0, tot0, tot1 ); \
6605 stbir__simdf_store( output, tot0 ); \
6606 decode_buffer += advance; \
6607 output += 4;
6608
6609#define stbir__store_output() \
6610 stbir__simdf_add( tot0, tot0, tot1 ); \
6611 stbir__simdf_store( output, tot0 ); \
6612 stbir__next_contributor(); \
6613 output += 4;
6614
6615#endif
6616
6617#else
6618
6619#define stbir__1_coeff_only() \
6620 float p0,p1,p2,p3,c; \
6621 STBIR_SIMD_NO_UNROLL(decode); \
6622 c = hc[0]; \
6623 p0 = decode[0] * c; \
6624 p1 = decode[1] * c; \
6625 p2 = decode[2] * c; \
6626 p3 = decode[3] * c;
6627
6628#define stbir__2_coeff_only() \
6629 float p0,p1,p2,p3,c; \
6630 STBIR_SIMD_NO_UNROLL(decode); \
6631 c = hc[0]; \
6632 p0 = decode[0] * c; \
6633 p1 = decode[1] * c; \
6634 p2 = decode[2] * c; \
6635 p3 = decode[3] * c; \
6636 c = hc[1]; \
6637 p0 += decode[4] * c; \
6638 p1 += decode[5] * c; \
6639 p2 += decode[6] * c; \
6640 p3 += decode[7] * c;
6641
6642#define stbir__3_coeff_only() \
6643 float p0,p1,p2,p3,c; \
6644 STBIR_SIMD_NO_UNROLL(decode); \
6645 c = hc[0]; \
6646 p0 = decode[0] * c; \
6647 p1 = decode[1] * c; \
6648 p2 = decode[2] * c; \
6649 p3 = decode[3] * c; \
6650 c = hc[1]; \
6651 p0 += decode[4] * c; \
6652 p1 += decode[5] * c; \
6653 p2 += decode[6] * c; \
6654 p3 += decode[7] * c; \
6655 c = hc[2]; \
6656 p0 += decode[8] * c; \
6657 p1 += decode[9] * c; \
6658 p2 += decode[10] * c; \
6659 p3 += decode[11] * c;
6660
6661#define stbir__store_output_tiny() \
6662 output[0] = p0; \
6663 output[1] = p1; \
6664 output[2] = p2; \
6665 output[3] = p3; \
6666 stbir__next_contributor(); \
6667 output += 4;
6668
6669#define stbir__4_coeff_start() \
6670 float x0,x1,x2,x3,y0,y1,y2,y3,c; \
6671 STBIR_SIMD_NO_UNROLL(decode); \
6672 c = hc[0]; \
6673 x0 = decode[0] * c; \
6674 x1 = decode[1] * c; \
6675 x2 = decode[2] * c; \
6676 x3 = decode[3] * c; \
6677 c = hc[1]; \
6678 y0 = decode[4] * c; \
6679 y1 = decode[5] * c; \
6680 y2 = decode[6] * c; \
6681 y3 = decode[7] * c; \
6682 c = hc[2]; \
6683 x0 += decode[8] * c; \
6684 x1 += decode[9] * c; \
6685 x2 += decode[10] * c; \
6686 x3 += decode[11] * c; \
6687 c = hc[3]; \
6688 y0 += decode[12] * c; \
6689 y1 += decode[13] * c; \
6690 y2 += decode[14] * c; \
6691 y3 += decode[15] * c;
6692
6693#define stbir__4_coeff_continue_from_4( ofs ) \
6694 STBIR_SIMD_NO_UNROLL(decode); \
6695 c = hc[0+(ofs)]; \
6696 x0 += decode[0+(ofs)*4] * c; \
6697 x1 += decode[1+(ofs)*4] * c; \
6698 x2 += decode[2+(ofs)*4] * c; \
6699 x3 += decode[3+(ofs)*4] * c; \
6700 c = hc[1+(ofs)]; \
6701 y0 += decode[4+(ofs)*4] * c; \
6702 y1 += decode[5+(ofs)*4] * c; \
6703 y2 += decode[6+(ofs)*4] * c; \
6704 y3 += decode[7+(ofs)*4] * c; \
6705 c = hc[2+(ofs)]; \
6706 x0 += decode[8+(ofs)*4] * c; \
6707 x1 += decode[9+(ofs)*4] * c; \
6708 x2 += decode[10+(ofs)*4] * c; \
6709 x3 += decode[11+(ofs)*4] * c; \
6710 c = hc[3+(ofs)]; \
6711 y0 += decode[12+(ofs)*4] * c; \
6712 y1 += decode[13+(ofs)*4] * c; \
6713 y2 += decode[14+(ofs)*4] * c; \
6714 y3 += decode[15+(ofs)*4] * c;
6715
6716#define stbir__1_coeff_remnant( ofs ) \
6717 STBIR_SIMD_NO_UNROLL(decode); \
6718 c = hc[0+(ofs)]; \
6719 x0 += decode[0+(ofs)*4] * c; \
6720 x1 += decode[1+(ofs)*4] * c; \
6721 x2 += decode[2+(ofs)*4] * c; \
6722 x3 += decode[3+(ofs)*4] * c;
6723
6724#define stbir__2_coeff_remnant( ofs ) \
6725 STBIR_SIMD_NO_UNROLL(decode); \
6726 c = hc[0+(ofs)]; \
6727 x0 += decode[0+(ofs)*4] * c; \
6728 x1 += decode[1+(ofs)*4] * c; \
6729 x2 += decode[2+(ofs)*4] * c; \
6730 x3 += decode[3+(ofs)*4] * c; \
6731 c = hc[1+(ofs)]; \
6732 y0 += decode[4+(ofs)*4] * c; \
6733 y1 += decode[5+(ofs)*4] * c; \
6734 y2 += decode[6+(ofs)*4] * c; \
6735 y3 += decode[7+(ofs)*4] * c;
6736
6737#define stbir__3_coeff_remnant( ofs ) \
6738 STBIR_SIMD_NO_UNROLL(decode); \
6739 c = hc[0+(ofs)]; \
6740 x0 += decode[0+(ofs)*4] * c; \
6741 x1 += decode[1+(ofs)*4] * c; \
6742 x2 += decode[2+(ofs)*4] * c; \
6743 x3 += decode[3+(ofs)*4] * c; \
6744 c = hc[1+(ofs)]; \
6745 y0 += decode[4+(ofs)*4] * c; \
6746 y1 += decode[5+(ofs)*4] * c; \
6747 y2 += decode[6+(ofs)*4] * c; \
6748 y3 += decode[7+(ofs)*4] * c; \
6749 c = hc[2+(ofs)]; \
6750 x0 += decode[8+(ofs)*4] * c; \
6751 x1 += decode[9+(ofs)*4] * c; \
6752 x2 += decode[10+(ofs)*4] * c; \
6753 x3 += decode[11+(ofs)*4] * c;
6754
6755#define stbir__store_output() \
6756 output[0] = x0 + y0; \
6757 output[1] = x1 + y1; \
6758 output[2] = x2 + y2; \
6759 output[3] = x3 + y3; \
6760 stbir__next_contributor(); \
6761 output += 4;
6762
6763#endif
6764
6765#define STBIR__horizontal_channels 4
6766#define STB_IMAGE_RESIZE_DO_HORIZONTALS
6767#include STBIR__HEADER_FILENAME
6768
6769
6770//=================
6771// Do 7 channel horizontal routines
6772
6773#ifdef STBIR_SIMD
6774
6775#define stbir__1_coeff_only() \
6776 stbir__simdf tot0,tot1,c; \
6777 STBIR_SIMD_NO_UNROLL(decode); \
6778 stbir__simdf_load1( c, hc ); \
6779 stbir__simdf_0123to0000( c, c ); \
6780 stbir__simdf_mult_mem( tot0, c, decode ); \
6781 stbir__simdf_mult_mem( tot1, c, decode+3 );
6782
6783#define stbir__2_coeff_only() \
6784 stbir__simdf tot0,tot1,c,cs; \
6785 STBIR_SIMD_NO_UNROLL(decode); \
6786 stbir__simdf_load2( cs, hc ); \
6787 stbir__simdf_0123to0000( c, cs ); \
6788 stbir__simdf_mult_mem( tot0, c, decode ); \
6789 stbir__simdf_mult_mem( tot1, c, decode+3 ); \
6790 stbir__simdf_0123to1111( c, cs ); \
6791 stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \
6792 stbir__simdf_madd_mem( tot1, tot1, c,decode+10 );
6793
6794#define stbir__3_coeff_only() \
6795 stbir__simdf tot0,tot1,c,cs; \
6796 STBIR_SIMD_NO_UNROLL(decode); \
6797 stbir__simdf_load( cs, hc ); \
6798 stbir__simdf_0123to0000( c, cs ); \
6799 stbir__simdf_mult_mem( tot0, c, decode ); \
6800 stbir__simdf_mult_mem( tot1, c, decode+3 ); \
6801 stbir__simdf_0123to1111( c, cs ); \
6802 stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \
6803 stbir__simdf_madd_mem( tot1, tot1, c, decode+10 ); \
6804 stbir__simdf_0123to2222( c, cs ); \
6805 stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \
6806 stbir__simdf_madd_mem( tot1, tot1, c, decode+17 );
6807
6808#define stbir__store_output_tiny() \
6809 stbir__simdf_store( output+3, tot1 ); \
6810 stbir__simdf_store( output, tot0 ); \
6811 stbir__next_contributor(); \
6812 output += 7;
6813
6814#ifdef STBIR_SIMD8
6815
6816#define stbir__4_coeff_start() \
6817 stbir__simdf8 tot0,tot1,c,cs; \
6818 STBIR_SIMD_NO_UNROLL(decode); \
6819 stbir__simdf8_load4b( cs, hc ); \
6820 stbir__simdf8_0123to00000000( c, cs ); \
6821 stbir__simdf8_mult_mem( tot0, c, decode ); \
6822 stbir__simdf8_0123to11111111( c, cs ); \
6823 stbir__simdf8_mult_mem( tot1, c, decode+7 ); \
6824 stbir__simdf8_0123to22222222( c, cs ); \
6825 stbir__simdf8_madd_mem( tot0, tot0, c, decode+14 ); \
6826 stbir__simdf8_0123to33333333( c, cs ); \
6827 stbir__simdf8_madd_mem( tot1, tot1, c, decode+21 );
6828
6829#define stbir__4_coeff_continue_from_4( ofs ) \
6830 STBIR_SIMD_NO_UNROLL(decode); \
6831 stbir__simdf8_load4b( cs, hc + (ofs) ); \
6832 stbir__simdf8_0123to00000000( c, cs ); \
6833 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
6834 stbir__simdf8_0123to11111111( c, cs ); \
6835 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \
6836 stbir__simdf8_0123to22222222( c, cs ); \
6837 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \
6838 stbir__simdf8_0123to33333333( c, cs ); \
6839 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+21 );
6840
6841#define stbir__1_coeff_remnant( ofs ) \
6842 STBIR_SIMD_NO_UNROLL(decode); \
6843 stbir__simdf8_load1b( c, hc + (ofs) ); \
6844 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 );
6845
6846#define stbir__2_coeff_remnant( ofs ) \
6847 STBIR_SIMD_NO_UNROLL(decode); \
6848 stbir__simdf8_load1b( c, hc + (ofs) ); \
6849 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
6850 stbir__simdf8_load1b( c, hc + (ofs)+1 ); \
6851 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 );
6852
6853#define stbir__3_coeff_remnant( ofs ) \
6854 STBIR_SIMD_NO_UNROLL(decode); \
6855 stbir__simdf8_load4b( cs, hc + (ofs) ); \
6856 stbir__simdf8_0123to00000000( c, cs ); \
6857 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
6858 stbir__simdf8_0123to11111111( c, cs ); \
6859 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \
6860 stbir__simdf8_0123to22222222( c, cs ); \
6861 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 );
6862
6863// polyphase period 1 for 7 channels (when have exact downsamples 2:1, 3:1, etc - common for mipmaps)
6864#define stbir__period1_setup_4() stbir__simdf8 P0, P1, P2, P3; stbir__simdf8_load1b( P0, hc ); stbir__simdf8_load1b( P1, hc + 1 ); stbir__simdf8_load1b( P2, hc + 2 ); stbir__simdf8_load1b( P3, hc + 3 );
6865#define stbir__period1_setup_5() stbir__simdf8 PR; stbir__period1_setup_4() stbir__simdf8_load1b( PR, hc + 4 );
6866#define stbir__period1_setup_8() stbir__simdf8 P4, P5, P6, P7; stbir__period1_setup_4() stbir__simdf8_load1b( P4, hc + 4 ); stbir__simdf8_load1b( P5, hc + 5 ); stbir__simdf8_load1b( P6, hc + 6 ); stbir__simdf8_load1b( P7, hc + 7 );
6867#define stbir__period1_setup_9() stbir__simdf8 PR; stbir__period1_setup_8() stbir__simdf8_load1b( PR, hc + 8 );
6868#define stbir__period1_setup_11() stbir__simdf8 P8, P9, P10; stbir__period1_setup_8() stbir__simdf8_load1b( P8, hc + 8 ); stbir__simdf8_load1b( P9, hc + 9 ); stbir__simdf8_load1b( P10, hc + 10 );
6869#define stbir__period1_setup_12() stbir__simdf8 P8, P9, P10, P11; stbir__period1_setup_8() stbir__simdf8_load1b( P8, hc + 8 ); stbir__simdf8_load1b( P9, hc + 9 ); stbir__simdf8_load1b( P10, hc + 10 ); stbir__simdf8_load1b( P11, hc + 11 );
6870#define stbir__period1_setup_13() stbir__simdf8 PR; stbir__period1_setup_12() stbir__simdf8_load1b( PR, hc + 12 );
6871#define stbir__period1_setup_16() stbir__simdf8 P12, P13, P14, P15; stbir__period1_setup_12() stbir__simdf8_load1b( P12, hc + 12 ); stbir__simdf8_load1b( P13, hc + 13 ); stbir__simdf8_load1b( P14, hc + 14 ); stbir__simdf8_load1b( P15, hc + 15 );
6872#define stbir__period1_setup_17() stbir__simdf8 PR; stbir__period1_setup_16() stbir__simdf8_load1b( PR, hc + 16 );
6873
6874#define stbir__period1_4_coeff_start() \
6875 stbir__simdf8 tot0, tot1; \
6876 STBIR_SIMD_NO_UNROLL(decode); \
6877 stbir__simdf8_mult_mem( tot0, P0, decode ); \
6878 stbir__simdf8_mult_mem( tot1, P1, decode + 7 ); \
6879 stbir__simdf8_madd_mem( tot0, tot0, P2, decode + 14 ); \
6880 stbir__simdf8_madd_mem( tot1, tot1, P3, decode + 21 );
6881
6882#define stbir__period1_4_coeff_continue() \
6883 stbir__simdf8_madd_mem( tot0, tot0, P4, decode + 28 ); \
6884 stbir__simdf8_madd_mem( tot1, tot1, P5, decode + 35 ); \
6885 stbir__simdf8_madd_mem( tot0, tot0, P6, decode + 42 ); \
6886 stbir__simdf8_madd_mem( tot1, tot1, P7, decode + 49 );
6887#define stbir__period1_4_coeff_continue2() \
6888 stbir__simdf8_madd_mem( tot0, tot0, P8, decode + 56 ); \
6889 stbir__simdf8_madd_mem( tot1, tot1, P9, decode + 63 ); \
6890 stbir__simdf8_madd_mem( tot0, tot0, P10, decode + 70 ); \
6891 stbir__simdf8_madd_mem( tot1, tot1, P11, decode + 77 );
6892#define stbir__period1_4_coeff_continue3() \
6893 stbir__simdf8_madd_mem( tot0, tot0, P12, decode + 84 ); \
6894 stbir__simdf8_madd_mem( tot1, tot1, P13, decode + 91 ); \
6895 stbir__simdf8_madd_mem( tot0, tot0, P14, decode + 98 ); \
6896 stbir__simdf8_madd_mem( tot1, tot1, P15, decode + 105 );
6897
6898#define stbir__period1_1_coeff_remnant( ofs ) \
6899 stbir__simdf8_madd_mem( tot0, tot0, PR, decode + (ofs) * 7 );
6900#define stbir__period1_3_coeff_remnant( ofs ) \
6901 stbir__simdf8_madd_mem( tot0, tot0, P8, decode + (ofs) * 7 ); \
6902 stbir__simdf8_madd_mem( tot1, tot1, P9, decode + (ofs) * 7 + 7 ); \
6903 stbir__simdf8_madd_mem( tot0, tot0, P10, decode + (ofs) * 7 + 14 );
6904
6905#define stbir__period1_store_output() \
6906 stbir__simdf8_add( tot0, tot0, tot1 ); \
6907 decode_buffer += advance; \
6908 output += 7; \
6909 if ( output < output_end ) \
6910 { \
6911 stbir__simdf8_store( output-7, tot0 ); \
6912 continue; \
6913 } \
6914 stbir__simdf_store( output-7+3, stbir__simdf_swiz(stbir__simdf8_gettop4(tot0),0,0,1,2) ); \
6915 stbir__simdf_store( output-7, stbir__if_simdf8_cast_to_simdf4(tot0) ); \
6916 break;
6917
6918#define stbir__store_output() \
6919 stbir__simdf8_add( tot0, tot0, tot1 ); \
6920 stbir__next_contributor(); \
6921 output += 7; \
6922 if ( output < output_end ) \
6923 { \
6924 stbir__simdf8_store( output-7, tot0 ); \
6925 continue; \
6926 } \
6927 stbir__simdf_store( output-7+3, stbir__simdf_swiz(stbir__simdf8_gettop4(tot0),0,0,1,2) ); \
6928 stbir__simdf_store( output-7, stbir__if_simdf8_cast_to_simdf4(tot0) ); \
6929 break;
6930
6931#else
6932
6933#define stbir__4_coeff_start() \
6934 stbir__simdf tot0,tot1,tot2,tot3,c,cs; \
6935 STBIR_SIMD_NO_UNROLL(decode); \
6936 stbir__simdf_load( cs, hc ); \
6937 stbir__simdf_0123to0000( c, cs ); \
6938 stbir__simdf_mult_mem( tot0, c, decode ); \
6939 stbir__simdf_mult_mem( tot1, c, decode+3 ); \
6940 stbir__simdf_0123to1111( c, cs ); \
6941 stbir__simdf_mult_mem( tot2, c, decode+7 ); \
6942 stbir__simdf_mult_mem( tot3, c, decode+10 ); \
6943 stbir__simdf_0123to2222( c, cs ); \
6944 stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \
6945 stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); \
6946 stbir__simdf_0123to3333( c, cs ); \
6947 stbir__simdf_madd_mem( tot2, tot2, c, decode+21 ); \
6948 stbir__simdf_madd_mem( tot3, tot3, c, decode+24 );
6949
6950#define stbir__4_coeff_continue_from_4( ofs ) \
6951 STBIR_SIMD_NO_UNROLL(decode); \
6952 stbir__simdf_load( cs, hc + (ofs) ); \
6953 stbir__simdf_0123to0000( c, cs ); \
6954 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
6955 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \
6956 stbir__simdf_0123to1111( c, cs ); \
6957 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \
6958 stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \
6959 stbir__simdf_0123to2222( c, cs ); \
6960 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \
6961 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); \
6962 stbir__simdf_0123to3333( c, cs ); \
6963 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+21 ); \
6964 stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+24 );
6965
6966#define stbir__1_coeff_remnant( ofs ) \
6967 STBIR_SIMD_NO_UNROLL(decode); \
6968 stbir__simdf_load1( c, hc + (ofs) ); \
6969 stbir__simdf_0123to0000( c, c ); \
6970 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
6971 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \
6972
6973#define stbir__2_coeff_remnant( ofs ) \
6974 STBIR_SIMD_NO_UNROLL(decode); \
6975 stbir__simdf_load2( cs, hc + (ofs) ); \
6976 stbir__simdf_0123to0000( c, cs ); \
6977 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
6978 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \
6979 stbir__simdf_0123to1111( c, cs ); \
6980 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \
6981 stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 );
6982
6983#define stbir__3_coeff_remnant( ofs ) \
6984 STBIR_SIMD_NO_UNROLL(decode); \
6985 stbir__simdf_load( cs, hc + (ofs) ); \
6986 stbir__simdf_0123to0000( c, cs ); \
6987 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
6988 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \
6989 stbir__simdf_0123to1111( c, cs ); \
6990 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \
6991 stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \
6992 stbir__simdf_0123to2222( c, cs ); \
6993 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \
6994 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 );
6995
6996// polyphase period 1 for 7 channels (when have exact downsamples 2:1, 3:1, etc - common for mipmaps)
6997#define stbir__period1_setup_4() stbir__simdf P0, P1, P2, P3; stbir__simdf_load1frep4( P0, hc[0] ); stbir__simdf_load1frep4( P1, hc[1] ); stbir__simdf_load1frep4( P2, hc[2] ); stbir__simdf_load1frep4( P3, hc[3] );
6998#define stbir__period1_setup_5() stbir__simdf PR; stbir__period1_setup_4() stbir__simdf_load1frep4( PR, hc[4] );
6999#define stbir__period1_setup_8() stbir__simdf P4, P5, P6, P7; stbir__period1_setup_4() stbir__simdf_load1frep4( P4, hc[4] ); stbir__simdf_load1frep4( P5, hc[5] ); stbir__simdf_load1frep4( P6, hc[6] ); stbir__simdf_load1frep4( P7, hc[7] );
7000#define stbir__period1_setup_9() stbir__simdf PR; stbir__period1_setup_8() stbir__simdf_load1frep4( PR, hc[8] );
7001#define stbir__period1_setup_11() stbir__simdf P8, P9, P10; stbir__period1_setup_8() stbir__simdf_load1frep4( P8, hc[8] ); stbir__simdf_load1frep4( P9, hc[9] ); stbir__simdf_load1frep4( P10, hc[10] );
7002#define stbir__period1_setup_12() stbir__simdf P8, P9, P10, P11; stbir__period1_setup_8() stbir__simdf_load1frep4( P8, hc[8] ); stbir__simdf_load1frep4( P9, hc[9] ); stbir__simdf_load1frep4( P10, hc[10] ); stbir__simdf_load1frep4( P11, hc[11] );
7003#define stbir__period1_setup_13() stbir__simdf PR; stbir__period1_setup_12() stbir__simdf_load1frep4( PR, hc[12] );
7004#define stbir__period1_setup_16() stbir__simdf P12, P13, P14, P15; stbir__period1_setup_12() stbir__simdf_load1frep4( P12, hc[12] ); stbir__simdf_load1frep4( P13, hc[13] ); stbir__simdf_load1frep4( P14, hc[14] ); stbir__simdf_load1frep4( P15, hc[15] );
7005#define stbir__period1_setup_17() stbir__simdf PR; stbir__period1_setup_16() stbir__simdf_load1frep4( PR, hc[16] );
7006
7007#define stbir__period1_4_coeff_start() \
7008 stbir__simdf tot0, tot1, tot2, tot3; \
7009 STBIR_SIMD_NO_UNROLL(decode); \
7010 stbir__simdf_mult_mem( tot0, P0, decode ); \
7011 stbir__simdf_mult_mem( tot1, P0, decode + 3 ); \
7012 stbir__simdf_mult_mem( tot2, P1, decode + 7 ); \
7013 stbir__simdf_mult_mem( tot3, P1, decode + 10 ); \
7014 stbir__simdf_madd_mem( tot0, tot0, P2, decode + 14 ); \
7015 stbir__simdf_madd_mem( tot1, tot1, P2, decode + 17 ); \
7016 stbir__simdf_madd_mem( tot2, tot2, P3, decode + 21 ); \
7017 stbir__simdf_madd_mem( tot3, tot3, P3, decode + 24 );
7018
7019#define stbir__period1_4_coeff_continue() \
7020 stbir__simdf_madd_mem( tot0, tot0, P4, decode + 28 ); \
7021 stbir__simdf_madd_mem( tot1, tot1, P4, decode + 31 ); \
7022 stbir__simdf_madd_mem( tot2, tot2, P5, decode + 35 ); \
7023 stbir__simdf_madd_mem( tot3, tot3, P5, decode + 38 ); \
7024 stbir__simdf_madd_mem( tot0, tot0, P6, decode + 42 ); \
7025 stbir__simdf_madd_mem( tot1, tot1, P6, decode + 45 ); \
7026 stbir__simdf_madd_mem( tot2, tot2, P7, decode + 49 ); \
7027 stbir__simdf_madd_mem( tot3, tot3, P7, decode + 52 );
7028#define stbir__period1_4_coeff_continue2() \
7029 stbir__simdf_madd_mem( tot0, tot0, P8, decode + 56 ); \
7030 stbir__simdf_madd_mem( tot1, tot1, P8, decode + 59 ); \
7031 stbir__simdf_madd_mem( tot2, tot2, P9, decode + 63 ); \
7032 stbir__simdf_madd_mem( tot3, tot3, P9, decode + 66 ); \
7033 stbir__simdf_madd_mem( tot0, tot0, P10, decode + 70 ); \
7034 stbir__simdf_madd_mem( tot1, tot1, P10, decode + 73 ); \
7035 stbir__simdf_madd_mem( tot2, tot2, P11, decode + 77 ); \
7036 stbir__simdf_madd_mem( tot3, tot3, P11, decode + 80 );
7037#define stbir__period1_4_coeff_continue3() \
7038 stbir__simdf_madd_mem( tot0, tot0, P12, decode + 84 ); \
7039 stbir__simdf_madd_mem( tot1, tot1, P12, decode + 87 ); \
7040 stbir__simdf_madd_mem( tot2, tot2, P13, decode + 91 ); \
7041 stbir__simdf_madd_mem( tot3, tot3, P13, decode + 94 ); \
7042 stbir__simdf_madd_mem( tot0, tot0, P14, decode + 98 ); \
7043 stbir__simdf_madd_mem( tot1, tot1, P14, decode + 101 ); \
7044 stbir__simdf_madd_mem( tot2, tot2, P15, decode + 105 ); \
7045 stbir__simdf_madd_mem( tot3, tot3, P15, decode + 108 );
7046
7047#define stbir__period1_1_coeff_remnant( ofs ) \
7048 stbir__simdf_madd_mem( tot0, tot0, PR, decode + (ofs) * 7 ); \
7049 stbir__simdf_madd_mem( tot1, tot1, PR, decode + (ofs) * 7 + 3 );
7050#define stbir__period1_3_coeff_remnant( ofs ) \
7051 stbir__simdf_madd_mem( tot0, tot0, P8, decode + (ofs) * 7 ); \
7052 stbir__simdf_madd_mem( tot1, tot1, P8, decode + (ofs) * 7 + 3 ); \
7053 stbir__simdf_madd_mem( tot2, tot2, P9, decode + (ofs) * 7 + 7 ); \
7054 stbir__simdf_madd_mem( tot3, tot3, P9, decode + (ofs) * 7 + 10 ); \
7055 stbir__simdf_madd_mem( tot0, tot0, P10, decode + (ofs) * 7 + 14 ); \
7056 stbir__simdf_madd_mem( tot1, tot1, P10, decode + (ofs) * 7 + 17 );
7057
7058#define stbir__period1_store_output() \
7059 stbir__simdf_add( tot0, tot0, tot2 ); \
7060 stbir__simdf_add( tot1, tot1, tot3 ); \
7061 stbir__simdf_store( output+3, tot1 ); \
7062 stbir__simdf_store( output, tot0 ); \
7063 decode_buffer += advance; \
7064 output += 7;
7065
7066#define stbir__store_output() \
7067 stbir__simdf_add( tot0, tot0, tot2 ); \
7068 stbir__simdf_add( tot1, tot1, tot3 ); \
7069 stbir__simdf_store( output+3, tot1 ); \
7070 stbir__simdf_store( output, tot0 ); \
7071 stbir__next_contributor(); \
7072 output += 7;
7073
7074#endif
7075
7076#else
7077
7078#define stbir__1_coeff_only() \
7079 float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \
7080 c = hc[0]; \
7081 tot0 = decode[0]*c; \
7082 tot1 = decode[1]*c; \
7083 tot2 = decode[2]*c; \
7084 tot3 = decode[3]*c; \
7085 tot4 = decode[4]*c; \
7086 tot5 = decode[5]*c; \
7087 tot6 = decode[6]*c;
7088
7089#define stbir__2_coeff_only() \
7090 float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \
7091 c = hc[0]; \
7092 tot0 = decode[0]*c; \
7093 tot1 = decode[1]*c; \
7094 tot2 = decode[2]*c; \
7095 tot3 = decode[3]*c; \
7096 tot4 = decode[4]*c; \
7097 tot5 = decode[5]*c; \
7098 tot6 = decode[6]*c; \
7099 c = hc[1]; \
7100 tot0 += decode[7]*c; \
7101 tot1 += decode[8]*c; \
7102 tot2 += decode[9]*c; \
7103 tot3 += decode[10]*c; \
7104 tot4 += decode[11]*c; \
7105 tot5 += decode[12]*c; \
7106 tot6 += decode[13]*c; \
7107
7108#define stbir__3_coeff_only() \
7109 float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \
7110 c = hc[0]; \
7111 tot0 = decode[0]*c; \
7112 tot1 = decode[1]*c; \
7113 tot2 = decode[2]*c; \
7114 tot3 = decode[3]*c; \
7115 tot4 = decode[4]*c; \
7116 tot5 = decode[5]*c; \
7117 tot6 = decode[6]*c; \
7118 c = hc[1]; \
7119 tot0 += decode[7]*c; \
7120 tot1 += decode[8]*c; \
7121 tot2 += decode[9]*c; \
7122 tot3 += decode[10]*c; \
7123 tot4 += decode[11]*c; \
7124 tot5 += decode[12]*c; \
7125 tot6 += decode[13]*c; \
7126 c = hc[2]; \
7127 tot0 += decode[14]*c; \
7128 tot1 += decode[15]*c; \
7129 tot2 += decode[16]*c; \
7130 tot3 += decode[17]*c; \
7131 tot4 += decode[18]*c; \
7132 tot5 += decode[19]*c; \
7133 tot6 += decode[20]*c; \
7134
7135#define stbir__store_output_tiny() \
7136 output[0] = tot0; \
7137 output[1] = tot1; \
7138 output[2] = tot2; \
7139 output[3] = tot3; \
7140 output[4] = tot4; \
7141 output[5] = tot5; \
7142 output[6] = tot6; \
7143 stbir__next_contributor(); \
7144 output += 7;
7145
7146#define stbir__4_coeff_start() \
7147 float x0,x1,x2,x3,x4,x5,x6,y0,y1,y2,y3,y4,y5,y6,c; \
7148 STBIR_SIMD_NO_UNROLL(decode); \
7149 c = hc[0]; \
7150 x0 = decode[0] * c; \
7151 x1 = decode[1] * c; \
7152 x2 = decode[2] * c; \
7153 x3 = decode[3] * c; \
7154 x4 = decode[4] * c; \
7155 x5 = decode[5] * c; \
7156 x6 = decode[6] * c; \
7157 c = hc[1]; \
7158 y0 = decode[7] * c; \
7159 y1 = decode[8] * c; \
7160 y2 = decode[9] * c; \
7161 y3 = decode[10] * c; \
7162 y4 = decode[11] * c; \
7163 y5 = decode[12] * c; \
7164 y6 = decode[13] * c; \
7165 c = hc[2]; \
7166 x0 += decode[14] * c; \
7167 x1 += decode[15] * c; \
7168 x2 += decode[16] * c; \
7169 x3 += decode[17] * c; \
7170 x4 += decode[18] * c; \
7171 x5 += decode[19] * c; \
7172 x6 += decode[20] * c; \
7173 c = hc[3]; \
7174 y0 += decode[21] * c; \
7175 y1 += decode[22] * c; \
7176 y2 += decode[23] * c; \
7177 y3 += decode[24] * c; \
7178 y4 += decode[25] * c; \
7179 y5 += decode[26] * c; \
7180 y6 += decode[27] * c;
7181
7182#define stbir__4_coeff_continue_from_4( ofs ) \
7183 STBIR_SIMD_NO_UNROLL(decode); \
7184 c = hc[0+(ofs)]; \
7185 x0 += decode[0+(ofs)*7] * c; \
7186 x1 += decode[1+(ofs)*7] * c; \
7187 x2 += decode[2+(ofs)*7] * c; \
7188 x3 += decode[3+(ofs)*7] * c; \
7189 x4 += decode[4+(ofs)*7] * c; \
7190 x5 += decode[5+(ofs)*7] * c; \
7191 x6 += decode[6+(ofs)*7] * c; \
7192 c = hc[1+(ofs)]; \
7193 y0 += decode[7+(ofs)*7] * c; \
7194 y1 += decode[8+(ofs)*7] * c; \
7195 y2 += decode[9+(ofs)*7] * c; \
7196 y3 += decode[10+(ofs)*7] * c; \
7197 y4 += decode[11+(ofs)*7] * c; \
7198 y5 += decode[12+(ofs)*7] * c; \
7199 y6 += decode[13+(ofs)*7] * c; \
7200 c = hc[2+(ofs)]; \
7201 x0 += decode[14+(ofs)*7] * c; \
7202 x1 += decode[15+(ofs)*7] * c; \
7203 x2 += decode[16+(ofs)*7] * c; \
7204 x3 += decode[17+(ofs)*7] * c; \
7205 x4 += decode[18+(ofs)*7] * c; \
7206 x5 += decode[19+(ofs)*7] * c; \
7207 x6 += decode[20+(ofs)*7] * c; \
7208 c = hc[3+(ofs)]; \
7209 y0 += decode[21+(ofs)*7] * c; \
7210 y1 += decode[22+(ofs)*7] * c; \
7211 y2 += decode[23+(ofs)*7] * c; \
7212 y3 += decode[24+(ofs)*7] * c; \
7213 y4 += decode[25+(ofs)*7] * c; \
7214 y5 += decode[26+(ofs)*7] * c; \
7215 y6 += decode[27+(ofs)*7] * c;
7216
7217#define stbir__1_coeff_remnant( ofs ) \
7218 STBIR_SIMD_NO_UNROLL(decode); \
7219 c = hc[0+(ofs)]; \
7220 x0 += decode[0+(ofs)*7] * c; \
7221 x1 += decode[1+(ofs)*7] * c; \
7222 x2 += decode[2+(ofs)*7] * c; \
7223 x3 += decode[3+(ofs)*7] * c; \
7224 x4 += decode[4+(ofs)*7] * c; \
7225 x5 += decode[5+(ofs)*7] * c; \
7226 x6 += decode[6+(ofs)*7] * c; \
7227
7228#define stbir__2_coeff_remnant( ofs ) \
7229 STBIR_SIMD_NO_UNROLL(decode); \
7230 c = hc[0+(ofs)]; \
7231 x0 += decode[0+(ofs)*7] * c; \
7232 x1 += decode[1+(ofs)*7] * c; \
7233 x2 += decode[2+(ofs)*7] * c; \
7234 x3 += decode[3+(ofs)*7] * c; \
7235 x4 += decode[4+(ofs)*7] * c; \
7236 x5 += decode[5+(ofs)*7] * c; \
7237 x6 += decode[6+(ofs)*7] * c; \
7238 c = hc[1+(ofs)]; \
7239 y0 += decode[7+(ofs)*7] * c; \
7240 y1 += decode[8+(ofs)*7] * c; \
7241 y2 += decode[9+(ofs)*7] * c; \
7242 y3 += decode[10+(ofs)*7] * c; \
7243 y4 += decode[11+(ofs)*7] * c; \
7244 y5 += decode[12+(ofs)*7] * c; \
7245 y6 += decode[13+(ofs)*7] * c; \
7246
7247#define stbir__3_coeff_remnant( ofs ) \
7248 STBIR_SIMD_NO_UNROLL(decode); \
7249 c = hc[0+(ofs)]; \
7250 x0 += decode[0+(ofs)*7] * c; \
7251 x1 += decode[1+(ofs)*7] * c; \
7252 x2 += decode[2+(ofs)*7] * c; \
7253 x3 += decode[3+(ofs)*7] * c; \
7254 x4 += decode[4+(ofs)*7] * c; \
7255 x5 += decode[5+(ofs)*7] * c; \
7256 x6 += decode[6+(ofs)*7] * c; \
7257 c = hc[1+(ofs)]; \
7258 y0 += decode[7+(ofs)*7] * c; \
7259 y1 += decode[8+(ofs)*7] * c; \
7260 y2 += decode[9+(ofs)*7] * c; \
7261 y3 += decode[10+(ofs)*7] * c; \
7262 y4 += decode[11+(ofs)*7] * c; \
7263 y5 += decode[12+(ofs)*7] * c; \
7264 y6 += decode[13+(ofs)*7] * c; \
7265 c = hc[2+(ofs)]; \
7266 x0 += decode[14+(ofs)*7] * c; \
7267 x1 += decode[15+(ofs)*7] * c; \
7268 x2 += decode[16+(ofs)*7] * c; \
7269 x3 += decode[17+(ofs)*7] * c; \
7270 x4 += decode[18+(ofs)*7] * c; \
7271 x5 += decode[19+(ofs)*7] * c; \
7272 x6 += decode[20+(ofs)*7] * c; \
7273
7274#define stbir__store_output() \
7275 output[0] = x0 + y0; \
7276 output[1] = x1 + y1; \
7277 output[2] = x2 + y2; \
7278 output[3] = x3 + y3; \
7279 output[4] = x4 + y4; \
7280 output[5] = x5 + y5; \
7281 output[6] = x6 + y6; \
7282 stbir__next_contributor(); \
7283 output += 7;
7284
7285#endif
7286
7287#define STBIR__horizontal_channels 7
7288#define STB_IMAGE_RESIZE_DO_HORIZONTALS
7289#include STBIR__HEADER_FILENAME
7290
7291
7292// include all of the vertical resamplers (both scatter and gather versions)
7293
7294#define STBIR__vertical_channels 1
7295#define STB_IMAGE_RESIZE_DO_VERTICALS
7296#include STBIR__HEADER_FILENAME
7297
7298#define STBIR__vertical_channels 1
7299#define STB_IMAGE_RESIZE_DO_VERTICALS
7300#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
7301#include STBIR__HEADER_FILENAME
7302
7303#define STBIR__vertical_channels 2
7304#define STB_IMAGE_RESIZE_DO_VERTICALS
7305#include STBIR__HEADER_FILENAME
7306
7307#define STBIR__vertical_channels 2
7308#define STB_IMAGE_RESIZE_DO_VERTICALS
7309#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
7310#include STBIR__HEADER_FILENAME
7311
7312#define STBIR__vertical_channels 3
7313#define STB_IMAGE_RESIZE_DO_VERTICALS
7314#include STBIR__HEADER_FILENAME
7315
7316#define STBIR__vertical_channels 3
7317#define STB_IMAGE_RESIZE_DO_VERTICALS
7318#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
7319#include STBIR__HEADER_FILENAME
7320
7321#define STBIR__vertical_channels 4
7322#define STB_IMAGE_RESIZE_DO_VERTICALS
7323#include STBIR__HEADER_FILENAME
7324
7325#define STBIR__vertical_channels 4
7326#define STB_IMAGE_RESIZE_DO_VERTICALS
7327#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
7328#include STBIR__HEADER_FILENAME
7329
7330#define STBIR__vertical_channels 5
7331#define STB_IMAGE_RESIZE_DO_VERTICALS
7332#include STBIR__HEADER_FILENAME
7333
7334#define STBIR__vertical_channels 5
7335#define STB_IMAGE_RESIZE_DO_VERTICALS
7336#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
7337#include STBIR__HEADER_FILENAME
7338
7339#define STBIR__vertical_channels 6
7340#define STB_IMAGE_RESIZE_DO_VERTICALS
7341#include STBIR__HEADER_FILENAME
7342
7343#define STBIR__vertical_channels 6
7344#define STB_IMAGE_RESIZE_DO_VERTICALS
7345#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
7346#include STBIR__HEADER_FILENAME
7347
7348#define STBIR__vertical_channels 7
7349#define STB_IMAGE_RESIZE_DO_VERTICALS
7350#include STBIR__HEADER_FILENAME
7351
7352#define STBIR__vertical_channels 7
7353#define STB_IMAGE_RESIZE_DO_VERTICALS
7354#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
7355#include STBIR__HEADER_FILENAME
7356
7357#define STBIR__vertical_channels 8
7358#define STB_IMAGE_RESIZE_DO_VERTICALS
7359#include STBIR__HEADER_FILENAME
7360
7361#define STBIR__vertical_channels 8
7362#define STB_IMAGE_RESIZE_DO_VERTICALS
7363#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
7364#include STBIR__HEADER_FILENAME
7365
7366typedef void STBIR_VERTICAL_GATHERFUNC( float * output, float const * coeffs, float const ** inputs, float const * input0_end );
7367
7368static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers[ 8 ] =
7369{
7370 stbir__vertical_gather_with_1_coeffs,stbir__vertical_gather_with_2_coeffs,stbir__vertical_gather_with_3_coeffs,stbir__vertical_gather_with_4_coeffs,stbir__vertical_gather_with_5_coeffs,stbir__vertical_gather_with_6_coeffs,stbir__vertical_gather_with_7_coeffs,stbir__vertical_gather_with_8_coeffs
7371};
7372
7373static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers_continues[ 8 ] =
7374{
7375 stbir__vertical_gather_with_1_coeffs_cont,stbir__vertical_gather_with_2_coeffs_cont,stbir__vertical_gather_with_3_coeffs_cont,stbir__vertical_gather_with_4_coeffs_cont,stbir__vertical_gather_with_5_coeffs_cont,stbir__vertical_gather_with_6_coeffs_cont,stbir__vertical_gather_with_7_coeffs_cont,stbir__vertical_gather_with_8_coeffs_cont
7376};
7377
7378typedef void STBIR_VERTICAL_SCATTERFUNC( float ** outputs, float const * coeffs, float const * input, float const * input_end );
7379
7380static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_sets[ 8 ] =
7381{
7382 stbir__vertical_scatter_with_1_coeffs,stbir__vertical_scatter_with_2_coeffs,stbir__vertical_scatter_with_3_coeffs,stbir__vertical_scatter_with_4_coeffs,stbir__vertical_scatter_with_5_coeffs,stbir__vertical_scatter_with_6_coeffs,stbir__vertical_scatter_with_7_coeffs,stbir__vertical_scatter_with_8_coeffs
7383};
7384
7385static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_blends[ 8 ] =
7386{
7387 stbir__vertical_scatter_with_1_coeffs_cont,stbir__vertical_scatter_with_2_coeffs_cont,stbir__vertical_scatter_with_3_coeffs_cont,stbir__vertical_scatter_with_4_coeffs_cont,stbir__vertical_scatter_with_5_coeffs_cont,stbir__vertical_scatter_with_6_coeffs_cont,stbir__vertical_scatter_with_7_coeffs_cont,stbir__vertical_scatter_with_8_coeffs_cont
7388};
7389
7390
7391// pointer to an output pixel, relative to the output region (offset_x/offset_y are already folded into output_data)
7392static void * stbir__output_pixel_ptr( stbir__info const * stbir_info, int x, int y )
7393{
7394 return ( (char*) stbir_info->output_data ) + (ptrdiff_t)( y * stbir_info->output_stride_bytes ) + ( (size_t)x * (size_t)stbir_info->channels * (size_t)stbir__type_size[ stbir_info->output_type ] );
7395}
7396
7397static void stbir__encode_scanline( stbir__info const * stbir_info, float * encode_buffer, int row, stbir__column_slice const * slice STBIR_ONLY_PROFILE_GET_SPLIT_INFO )
7398{
7399 int num_pixels = slice->width;
7400 int channels = stbir_info->channels;
7401 ptrdiff_t width_times_channels = (ptrdiff_t)num_pixels * channels;
7402 void * output_buffer_data = stbir__output_pixel_ptr( stbir_info, slice->x0, row );
7403 void * output_buffer;
7404
7405 // un-alpha weight if we need to
7406 if ( stbir_info->alpha_unweight )
7407 {
7408 STBIR_PROFILE_START( unalpha );
7409 stbir_info->alpha_unweight( encode_buffer, width_times_channels );
7410 STBIR_PROFILE_END( unalpha );
7411 }
7412
7413 // write directly into output by default
7414 output_buffer = output_buffer_data;
7415
7416 // if we have an output callback, we first convert the decode buffer in place (and then hand that to the callback)
7417 if ( stbir_info->out_pixels_cb )
7418 output_buffer = encode_buffer;
7419
7420 STBIR_PROFILE_START( encode );
7421 // convert into the output buffer
7422 stbir_info->encode_pixels( output_buffer, width_times_channels, encode_buffer );
7423 STBIR_PROFILE_END( encode );
7424
7425 // if we have an output callback, call it to send the data (x and y are in full output image space, not region relative)
7426 if ( stbir_info->out_pixels_cb )
7427 stbir_info->out_pixels_cb( output_buffer, num_pixels, stbir_info->offset_x + slice->x0, stbir_info->offset_y + row, stbir_info->user_data );
7428}
7429
7430
7431// Get the ring buffer pointer for an index
7432static float* stbir__get_ring_buffer_entry(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int index )
7433{
7434 STBIR_ASSERT( index < stbir_info->ring_buffer_num_entries );
7435
7436 #ifdef STBIR__SEPARATE_ALLOCATIONS
7437 return split_info->ring_buffers[ index ];
7438 #else
7439 return (float*) ( ( (char*) split_info->ring_buffer ) + ( (size_t) index * stbir_info->ring_buffer_length_bytes ) );
7440 #endif
7441}
7442
7443// Get the specified scan line from the ring buffer
7444static float* stbir__get_ring_buffer_scanline(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int get_scanline)
7445{
7446 int ring_buffer_index = (split_info->ring_buffer_begin_index + (get_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries;
7447 return stbir__get_ring_buffer_entry( stbir_info, split_info, ring_buffer_index );
7448}
7449
7450// the horizontal pass over one slice's output pixels.
7451static void stbir__resample_horizontal_gather(stbir__info const * stbir_info, float* output_buffer, float const * input_buffer, stbir__column_slice const * slice STBIR_ONLY_PROFILE_GET_SPLIT_INFO )
7452{
7453 stbir__sampler const * horizontal = &stbir_info->horizontal;
7454 int channels = stbir_info->effective_channels;
7455 int point_copy = ( horizontal->filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( stbir__scale_is_copy( &horizontal->scale_info ) );
7456 int prologue = horizontal->shared_prologue;
7457 int period = horizontal->shared_period;
7458 int epilogue_start = horizontal->num_contributors - horizontal->shared_epilogue;
7459 int x = slice->x0, x_end = slice->x0 + slice->width;
7460 int n0 = slice->first_n0;
7461
7462 STBIR_PROFILE_START( horizontal );
7463 while ( x < x_end )
7464 {
7465 float const * decode = input_buffer + (ptrdiff_t)( n0 - slice->in_n0 ) * channels; // input_buffer holds input pixel in_n0 at index zero
7466 int run_end = x_end;
7467 int entry_stride = horizontal->entry_floats * (int) sizeof( float );
7468
7469 if ( ( period ) && ( x < epilogue_start ) )
7470 {
7471 if ( period == 1 )
7472 {
7473 // one set for the whole periodic region: the prologue is its own run, then the region is one run over that set
7474 run_end = ( x < prologue ) ? prologue : epilogue_start;
7475 if ( x >= prologue )
7476 entry_stride = 0;
7477 }
7478 else
7479 {
7480 // to the end of this pass over the block (from the prologue, the first pass), but not into the epilogue
7481 int pass = ( x < prologue ) ? 0 : ( ( x - prologue ) / period );
7482 run_end = prologue + ( pass + 1 ) * period;
7483 if ( run_end > epilogue_start )
7484 run_end = epilogue_start;
7485 }
7486 if ( run_end > x_end )
7487 run_end = x_end;
7488 }
7489
7490 if ( point_copy )
7491 STBIR_MEMCPY( output_buffer, decode, ( run_end - x ) * sizeof( float ) * channels );
7492 else if ( ( entry_stride == 0 ) && ( stbir_info->horizontal_gather_period1 ) )
7493 stbir_info->horizontal_gather_period1( output_buffer, run_end - x, decode, stbir__output_column_entry( horizontal, x ), 0 );
7494 else
7495 stbir_info->horizontal_gather_channels( output_buffer, run_end - x, decode, stbir__output_column_entry( horizontal, x ), entry_stride );
7496 output_buffer += (ptrdiff_t)( run_end - x ) * channels;
7497
7498 // where the next run starts reading: the epilogue, or a block boundary
7499 if ( run_end == epilogue_start )
7500 n0 = horizontal->shared_epilogue_n0;
7501 else if ( period )
7502 n0 = horizontal->shared_block_n0 + ( ( run_end - prologue ) / period ) * horizontal->shared_block_shift;
7503 x = run_end;
7504 }
7505 STBIR_PROFILE_END( horizontal );
7506}
7507
7508// how many floats wide the buffer that the vertical pass works on is (decoded input pixels when vertical
7509// first, horizontally resampled output pixels otherwise)
7510static ptrdiff_t stbir__vertical_width_times_channels( stbir__info const * stbir_info, stbir__column_slice const * slice )
7511{
7512 int width = ( stbir_info->vertical_first ) ? ( slice->in_n1 - slice->in_n0 + 1 ) : slice->width;
7513 return (ptrdiff_t)stbir_info->effective_channels * width;
7514}
7515
7516static void stbir__resample_vertical_gather(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n, int contrib_n0, int contrib_n1, float const * vertical_coefficients, stbir__column_slice const * slice )
7517{
7518 float* encode_buffer = split_info->vertical_buffer;
7519 float* decode_buffer = split_info->decode_buffer;
7520 int vertical_first = stbir_info->vertical_first;
7521 ptrdiff_t width_times_channels = stbir__vertical_width_times_channels( stbir_info, slice );
7522
7523 STBIR_ASSERT( stbir_info->vertical.is_gather );
7524
7525 // loop over the contributing scanlines and scale into the buffer
7526 STBIR_PROFILE_START( vertical );
7527 {
7528 int k = 0, total = contrib_n1 - contrib_n0 + 1;
7529 STBIR_ASSERT( total > 0 );
7530 do {
7531 float const * inputs[8];
7532 int i, cnt = total;
7533 if ( cnt > 8 )
7534 cnt = 8;
7535 for( i = 0 ; i < cnt ; i++ )
7536 inputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+contrib_n0 );
7537
7538 // call the N scanlines at a time function (up to 8 scanlines of blending at once)
7539 ((k==0)?stbir__vertical_gathers:stbir__vertical_gathers_continues)[cnt-1]( (vertical_first) ? decode_buffer : encode_buffer, vertical_coefficients + k, inputs, inputs[0] + width_times_channels );
7540 k += cnt;
7541 total -= cnt;
7542 } while ( total );
7543 }
7544 STBIR_PROFILE_END( vertical );
7545
7546 if ( vertical_first )
7547 {
7548 // Now resample the gathered vertical data in the horizontal axis into the encode buffer
7549 decode_buffer[ width_times_channels ] = 0.0f; // clear two over for horizontals with a remnant of 3
7550 decode_buffer[ width_times_channels+1 ] = 0.0f;
7551 stbir__resample_horizontal_gather(stbir_info, encode_buffer, decode_buffer, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7552 }
7553
7554 stbir__encode_scanline( stbir_info, encode_buffer, n, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7555}
7556
7557static void stbir__decode_and_resample_for_vertical_gather_loop(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n, stbir__column_slice const * slice)
7558{
7559 int ring_buffer_index;
7560 float* ring_buffer;
7561
7562 // Decode the nth scanline from the source image into the decode buffer.
7563 stbir__decode_scanline( stbir_info, n, split_info->decode_buffer, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7564
7565 // update new end scanline
7566 split_info->ring_buffer_last_scanline = n;
7567
7568 // get ring buffer
7569 ring_buffer_index = (split_info->ring_buffer_begin_index + (split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries;
7570 ring_buffer = stbir__get_ring_buffer_entry(stbir_info, split_info, ring_buffer_index);
7571
7572 // Now resample it into the ring buffer.
7573 stbir__resample_horizontal_gather( stbir_info, ring_buffer, split_info->decode_buffer, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7574
7575 // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling.
7576}
7577
7578// A trimmed zero coefficient can make a row's range start later than the next row's ((4,4) then (3,6)), and the ring only
7579// loads forward. So look ahead up to max_look rows from y for the lowest first scanline; *out_row is which row had it.
7580static int stbir__lowest_scanline_needed( stbir__sampler const * vertical, int y, int max_look, int * out_row )
7581{
7582 int start_n0, lowest_n0, n0, n1, j;
7583
7584 stbir__output_range( vertical, y, &start_n0, &n1 );
7585 lowest_n0 = start_n0;
7586 *out_row = 0;
7587 for (j = 1; j <= max_look; j++)
7588 {
7589 stbir__output_range( vertical, y + j, &n0, &n1 );
7590 if ( n0 > start_n0 )
7591 break;
7592 if ( n0 < lowest_n0 )
7593 {
7594 lowest_n0 = n0;
7595 *out_row = j;
7596 }
7597 }
7598 return lowest_n0;
7599}
7600
7601static void stbir__vertical_gather_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count, stbir__column_slice const * slice )
7602{
7603 stbir__sampler const * vertical = &stbir_info->vertical;
7604 int y, start_output_y, end_output_y;
7605
7606 STBIR_ASSERT( vertical->is_gather );
7607
7608 start_output_y = split_info->start_output_y;
7609 end_output_y = split_info[split_count-1].end_output_y;
7610
7611 // initialize the ring buffer for gathering: start it at the lowest scanline the first rows need
7612 {
7613 int look = vertical->filter_pixel_margin * 3, unused;
7614 if ( look > ( end_output_y - start_output_y - 1 ) )
7615 look = end_output_y - start_output_y - 1;
7616 split_info->ring_buffer_begin_index = 0;
7617 split_info->ring_buffer_first_scanline = stbir__lowest_scanline_needed( vertical, start_output_y, look, &unused );
7618 split_info->ring_buffer_last_scanline = split_info->ring_buffer_first_scanline - 1; // means "empty"
7619 }
7620
7621 for (y = start_output_y; y < end_output_y; y++)
7622 {
7623 int in_first_scanline, in_last_scanline;
7624 float const * vertical_coefficients = stbir__output_coefficients( vertical, y );
7625
7626 stbir__output_range( vertical, y, &in_first_scanline, &in_last_scanline );
7627
7628 // rows with a zero weight at either end aren't worth decoding (the pack's shifted sets when the vertical shares the
7629 // horizontal's tables, kernels that are exactly zero at their support's edge)
7630 while ( ( in_first_scanline < in_last_scanline ) && ( vertical_coefficients[0] == 0.0f ) )
7631 {
7632 ++in_first_scanline;
7633 ++vertical_coefficients;
7634 }
7635 while ( ( in_last_scanline > in_first_scanline ) && ( vertical_coefficients[ in_last_scanline - in_first_scanline ] == 0.0f ) )
7636 --in_last_scanline;
7637
7638 // make sure the indexing hasn't broken
7639 STBIR_ASSERT( in_first_scanline >= split_info->ring_buffer_first_scanline );
7640
7641 // Load in new scanlines
7642 while (in_last_scanline > split_info->ring_buffer_last_scanline)
7643 {
7644 STBIR_ASSERT( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) <= stbir_info->ring_buffer_num_entries );
7645
7646 // make sure there was room in the ring buffer when we add new scanlines
7647 if ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries )
7648 {
7649 split_info->ring_buffer_first_scanline++;
7650 split_info->ring_buffer_begin_index++;
7651 }
7652
7653 if ( stbir_info->vertical_first )
7654 {
7655 float * ring_buffer = stbir__get_ring_buffer_scanline( stbir_info, split_info, ++split_info->ring_buffer_last_scanline );
7656 // Decode the nth scanline from the source image into the decode buffer.
7657 stbir__decode_scanline( stbir_info, split_info->ring_buffer_last_scanline, ring_buffer, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7658 }
7659 else
7660 {
7661 stbir__decode_and_resample_for_vertical_gather_loop(stbir_info, split_info, split_info->ring_buffer_last_scanline + 1, slice);
7662 }
7663 }
7664
7665 // Now all buffers should be ready to write a row of vertical sampling, so do it.
7666 stbir__resample_vertical_gather(stbir_info, split_info, y, in_first_scanline, in_last_scanline, vertical_coefficients, slice );
7667 }
7668}
7669
7670#define STBIR__FLOAT_EMPTY_MARKER 3.0e+38F
7671#define STBIR__FLOAT_BUFFER_IS_EMPTY(ptr) ((ptr)[0]==STBIR__FLOAT_EMPTY_MARKER)
7672
7673static void stbir__encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info, stbir__column_slice const * slice)
7674{
7675 // evict a scanline out into the output buffer
7676 float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index );
7677
7678 // dump the scanline out
7679 stbir__encode_scanline( stbir_info, ring_buffer_entry, split_info->ring_buffer_first_scanline, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7680
7681 // mark it as empty
7682 ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER;
7683
7684 // advance the first scanline
7685 split_info->ring_buffer_first_scanline++;
7686 if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries )
7687 split_info->ring_buffer_begin_index = 0;
7688}
7689
7690static void stbir__horizontal_resample_and_encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info, stbir__column_slice const * slice)
7691{
7692 // evict a scanline out into the output buffer
7693
7694 float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index );
7695
7696 // Now resample it into the buffer.
7697 stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, ring_buffer_entry, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7698
7699 // dump the scanline out
7700 stbir__encode_scanline( stbir_info, split_info->vertical_buffer, split_info->ring_buffer_first_scanline, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7701
7702 // mark it as empty
7703 ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER;
7704
7705 // advance the first scanline
7706 split_info->ring_buffer_first_scanline++;
7707 if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries )
7708 split_info->ring_buffer_begin_index = 0;
7709}
7710
7711static void stbir__resample_vertical_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n0, int n1, float const * vertical_coefficients, float const * vertical_buffer, float const * vertical_buffer_end )
7712{
7713 STBIR_ASSERT( !stbir_info->vertical.is_gather );
7714
7715 STBIR_PROFILE_START( vertical );
7716 {
7717 int k = 0, total = n1 - n0 + 1;
7718 STBIR_ASSERT( total > 0 );
7719 do {
7720 float * outputs[8];
7721 int i, n = total; if ( n > 8 ) n = 8;
7722 for( i = 0 ; i < n ; i++ )
7723 {
7724 outputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+n0 );
7725 if ( ( i ) && ( STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[i] ) != STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ) ) ) // make sure runs are of the same type
7726 {
7727 n = i;
7728 break;
7729 }
7730 }
7731 // call the scatter to N scanlines at a time function (up to 8 scanlines of scattering at once)
7732 ((STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ))?stbir__vertical_scatter_sets:stbir__vertical_scatter_blends)[n-1]( outputs, vertical_coefficients + k, vertical_buffer, vertical_buffer_end );
7733 k += n;
7734 total -= n;
7735 } while ( total );
7736 }
7737
7738 STBIR_PROFILE_END( vertical );
7739}
7740
7741typedef void stbir__handle_scanline_for_scatter_func(stbir__info const * stbir_info, stbir__per_split_info* split_info, stbir__column_slice const * slice);
7742
7743static void stbir__vertical_scatter_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count, stbir__column_slice const * slice )
7744{
7745 int y, start_output_y, end_output_y, start_input_y, end_input_y;
7746 stbir__contributors* vertical_contributors = stbir_info->vertical.contributors;
7747 float const * vertical_coefficients = stbir_info->vertical.coefficients;
7748 stbir__handle_scanline_for_scatter_func * handle_scanline_for_scatter;
7749 void * scanline_scatter_buffer;
7750 void * scanline_scatter_buffer_end;
7751 int on_first_input_y, last_input_y;
7752 ptrdiff_t width_times_channels = stbir__vertical_width_times_channels( stbir_info, slice );
7753
7754 STBIR_ASSERT( !stbir_info->vertical.is_gather );
7755
7756 start_output_y = split_info->start_output_y;
7757 end_output_y = split_info[split_count-1].end_output_y; // may do multiple split counts
7758
7759 start_input_y = split_info->start_input_y;
7760 end_input_y = split_info[split_count-1].end_input_y;
7761
7762 // adjust for starting offset start_input_y
7763 y = start_input_y + stbir_info->vertical.filter_pixel_margin;
7764 vertical_contributors += y ;
7765 vertical_coefficients += (size_t)stbir_info->vertical.coefficient_width * y;
7766
7767 if ( stbir_info->vertical_first )
7768 {
7769 handle_scanline_for_scatter = stbir__horizontal_resample_and_encode_first_scanline_from_scatter;
7770 scanline_scatter_buffer = split_info->decode_buffer;
7771 }
7772 else
7773 {
7774 handle_scanline_for_scatter = stbir__encode_first_scanline_from_scatter;
7775 scanline_scatter_buffer = split_info->vertical_buffer;
7776 }
7777 scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * width_times_channels;
7778
7779 // initialize the ring buffer for scattering
7780 split_info->ring_buffer_first_scanline = start_output_y;
7781 split_info->ring_buffer_last_scanline = -1;
7782 split_info->ring_buffer_begin_index = -1;
7783
7784 // mark all the buffers as empty to start
7785 for( y = 0 ; y < stbir_info->ring_buffer_num_entries ; y++ )
7786 {
7787 float * decode_buffer = stbir__get_ring_buffer_entry( stbir_info, split_info, y );
7788 decode_buffer[ width_times_channels ] = 0.0f; // clear two over for horizontals with a remnant of 3
7789 decode_buffer[ width_times_channels+1 ] = 0.0f;
7790 decode_buffer[0] = STBIR__FLOAT_EMPTY_MARKER; // only used on scatter
7791 }
7792
7793 // do the loop in input space
7794 on_first_input_y = 1;
7795 last_input_y = start_input_y;
7796 for (y = start_input_y ; y < end_input_y; y++)
7797 {
7798 int out_first_scanline, out_last_scanline;
7799
7800 out_first_scanline = vertical_contributors->n0;
7801 out_last_scanline = vertical_contributors->n1;
7802
7803 // this input row is ours if the output rows it contributes to overlap the split's rows at all
7804 if ( ( out_last_scanline >= out_first_scanline ) && ( out_last_scanline >= start_output_y ) && ( out_first_scanline < end_output_y ) )
7805 {
7806 float const * coefficients = vertical_coefficients;
7807
7808 // keep track of the range actually seen for the next resize
7809 last_input_y = y;
7810 if ( ( on_first_input_y ) && ( y > start_input_y ) )
7811 split_info->start_input_y = y;
7812 on_first_input_y = 0;
7813
7814 // clip the region
7815 if ( out_first_scanline < start_output_y )
7816 {
7817 coefficients += start_output_y - out_first_scanline;
7818 out_first_scanline = start_output_y;
7819 }
7820
7821 if ( out_last_scanline >= end_output_y )
7822 out_last_scanline = end_output_y - 1;
7823
7824 STBIR_ASSERT( out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries ); // (the ring is capped to the band's rows, so after the clip)
7825
7826 // if very first scanline, init the index
7827 if (split_info->ring_buffer_begin_index < 0)
7828 split_info->ring_buffer_begin_index = out_first_scanline - start_output_y;
7829
7830 STBIR_ASSERT( split_info->ring_buffer_begin_index <= out_first_scanline );
7831
7832 // Decode the nth scanline from the source image into the decode buffer.
7833 stbir__decode_scanline( stbir_info, y, split_info->decode_buffer, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7834
7835 // When horizontal first, we resample horizontally into the vertical buffer before we scatter it out
7836 if ( !stbir_info->vertical_first )
7837 stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, split_info->decode_buffer, slice STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
7838
7839 // Now it's sitting in the buffer ready to be distributed into the ring buffers.
7840
7841 // evict from the ring buffer if it is full and this row needs a new entry
7842 if ( ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) &&
7843 ( out_last_scanline > split_info->ring_buffer_last_scanline ) )
7844 handle_scanline_for_scatter( stbir_info, split_info, slice );
7845
7846 // Now the horizontal buffer is ready to write to all ring buffer rows, so do it.
7847 stbir__resample_vertical_scatter(stbir_info, split_info, out_first_scanline, out_last_scanline, coefficients, (float*)scanline_scatter_buffer, (float*)scanline_scatter_buffer_end );
7848
7849 // update the end of the buffer
7850 if ( out_last_scanline > split_info->ring_buffer_last_scanline )
7851 split_info->ring_buffer_last_scanline = out_last_scanline;
7852 }
7853 ++vertical_contributors;
7854 vertical_coefficients += stbir_info->vertical.coefficient_width;
7855 }
7856
7857 // now evict the scanlines that are left over in the ring buffer
7858 while ( split_info->ring_buffer_first_scanline < end_output_y )
7859 handle_scanline_for_scatter(stbir_info, split_info, slice);
7860
7861 // update the end_input_y if we do multiple resizes with the same data
7862 ++last_input_y;
7863 for( y = 0 ; y < split_count; y++ )
7864 if ( split_info[y].end_input_y > last_input_y )
7865 split_info[y].end_input_y = last_input_y;
7866}
7867
7868
7869static void stbir__set_sampler(stbir__sampler * samp, stbir_filter filter, stbir__kernel_callback_new * kernel_new, stbir__support_callback_new * support_new, stbir__radius_callback_new * radius, stbir__kernel_callback * kernel_old, stbir__support_callback * support_old, stbir_edge edge, stbir__scale_info * scale_info, int always_gather, void * user_data )
7870{
7871 // set filter
7872 if (filter == 0)
7873 {
7874 filter = STBIR_DEFAULT_FILTER_DOWNSAMPLE; // default to downsample
7875 if ( stbir__scale_is_upsample( scale_info ) )
7876 {
7877 if ( stbir__scale_is_copy( scale_info ) )
7878 filter = STBIR_FILTER_POINT_SAMPLE;
7879 else
7880 filter = STBIR_DEFAULT_FILTER_UPSAMPLE;
7881 }
7882 }
7883 samp->filter_enum = filter;
7884
7885 STBIR_ASSERT(samp->filter_enum != 0);
7886 STBIR_ASSERT((unsigned)samp->filter_enum < STBIR_FILTER_OTHER);
7887 samp->filter_kernel_new = stbir__builtin_kernels[ filter ];
7888 samp->filter_kernel_old = 0;
7889 samp->filter_support_new = stbir__builtin_supports[ filter ];
7890 samp->filter_radius = stbir__builtin_radii[ filter ];
7891 samp->filter_support_old = 0;
7892
7893 // a user filter is a kernel with a support or a radius (stbir_set_filter_callbacks_new refuses both), or an old
7894 // float kernel with an old float support (stbir_set_filter_callbacks)
7895 if ( ( kernel_new && ( support_new || radius ) ) || ( kernel_old && support_old ) )
7896 {
7897 samp->filter_kernel_new = kernel_new;
7898 samp->filter_kernel_old = kernel_old;
7899 samp->filter_support_new = support_new;
7900 samp->filter_radius = radius;
7901 samp->filter_support_old = support_old;
7902 samp->filter_enum = STBIR_FILTER_OTHER;
7903 }
7904
7905 samp->edge = edge;
7906 samp->filter_pixel_width = stbir__get_filter_pixel_width( samp->filter_radius, samp->filter_support_new, samp->filter_support_old, scale_info, user_data );
7907 // Gather is always better, but in extreme downsamples, you have to most or all of the data in memory
7908 // For horizontal, we always have all the pixels, so we always use gather here (always_gather==1).
7909 // For vertical, we use gather if scaling up (which means we will have samp->filter_pixel_width
7910 // scanlines in memory at once).
7911 samp->is_gather = 0;
7912 if ( stbir__scale_is_upsample( scale_info ) )
7913 samp->is_gather = 1;
7914 else if ( ( always_gather ) || ( samp->filter_pixel_width <= STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT ) )
7915 samp->is_gather = 2;
7916
7917 // pre calculate stuff based on the above
7918 samp->coefficient_width = stbir__get_coefficient_width(samp, samp->is_gather, user_data);
7919
7920 // filter_pixel_width is the conservative size in pixels of input that affect an output pixel.
7921 // In rare cases (only with 2 pix to 1 pix with the default filters), it's possible that the
7922 // filter will extend before or after the scanline beyond just one extra entire copy of the
7923 // scanline (we would hit the edge twice). We don't let you do that, so we clamp the total
7924 // width to 3x the total of input pixel (once for the scanline, once for the left side
7925 // overhang, and once for the right side). We only do this for edge mode, since the other
7926 // modes can just re-edge clamp back in again.
7927 if ( edge == STBIR_EDGE_WRAP )
7928 if ( ( samp->filter_pixel_width / 3 ) > scale_info->input_full_size )
7929 samp->filter_pixel_width = scale_info->input_full_size * 3;
7930
7931 // This is how much to expand buffers to account for filters seeking outside
7932 // the image boundaries.
7933 samp->filter_pixel_margin = samp->filter_pixel_width / 2;
7934
7935 // filter_pixel_margin is the amount that this filter can overhang on just one side of either
7936 // end of the scanline (left or the right). Since we only allow you to overhang 1 scanline's
7937 // worth of pixels, we clamp this one side of overhang to the input scanline size. Again,
7938 // this clamping only happens in rare cases with the default filters (2 pix to 1 pix).
7939 if ( edge == STBIR_EDGE_WRAP )
7940 if ( samp->filter_pixel_margin > scale_info->input_full_size )
7941 samp->filter_pixel_margin = scale_info->input_full_size;
7942
7943 samp->num_contributors = stbir__get_contributors(samp, samp->is_gather);
7944
7945 samp->entry_floats = 0;
7946 samp->shared_period = 0;
7947 samp->shared_prologue = 0;
7948 samp->shared_epilogue = 0;
7949 samp->contributors_size = (size_t)samp->num_contributors * sizeof(stbir__contributors);
7950 samp->coefficients_size = (size_t)samp->num_contributors * STBIR__COLUMN_ENTRY_FLOATS( samp->coefficient_width ) * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING; // room for the horizontal's column entry ints, plus padding
7951}
7952
7953// Walks input pixels from start toward end (step is -1 or 1) and returns the last one that contributes to any output,
7954// or fallback if none did. When the output region is only a pixel or two wide the starting guess can be past the
7955// whole region, so a non contributing pixel only stops the walk once contributing ones have been seen.
7956static int stbir__last_contributing_input( int start, int end, int step, int fallback, stbir_real in_pixels_radius, stbir_real support, stbir__scale_info const * scale_info )
7957{
7958 int n = start;
7959 int last = fallback;
7960 int seen = 0;
7961
7962 while ( ( step < 0 ) ? ( n >= end ) : ( n <= end ) )
7963 {
7964 int out_first_pixel, out_last_pixel;
7965 stbir__input_out_pixel_range( scale_info, n, in_pixels_radius, support, &out_first_pixel, &out_last_pixel );
7966 if ( out_first_pixel > out_last_pixel )
7967 {
7968 if ( seen )
7969 break;
7970 }
7971 else
7972 {
7973 last = n;
7974 seen = 1;
7975 }
7976 n += step;
7977 }
7978 return last;
7979}
7980
7981static void stbir__get_conservative_extents( stbir__sampler * samp, stbir__contributors * range, void * user_data )
7982{
7983 stbir_real scale = samp->scale_info.scale;
7984 stbir_real in_pixels_radius = stbir__filter_radius_in_input_pixels( samp->filter_radius, samp->filter_support_new, samp->filter_support_old, &samp->scale_info, user_data );
7985 int input_full_size = samp->scale_info.input_full_size;
7986 stbir_edge edge = samp->edge;
7987 stbir_real inv_scale = samp->scale_info.inv_scale;
7988
7989 STBIR_ASSERT( samp->is_gather != 0 );
7990
7991 if ( samp->is_gather == 1 )
7992 {
7993 int in_first_pixel, in_last_pixel;
7994 stbir_real filter_support = in_pixels_radius;
7995 stbir_real out_filter_radius = filter_support * scale;
7996
7997 stbir__output_in_pixel_range( &samp->scale_info, 0, out_filter_radius, filter_support, edge, &in_first_pixel, &in_last_pixel );
7998 range->n0 = in_first_pixel;
7999 stbir__output_in_pixel_range( &samp->scale_info, samp->scale_info.output_sub_size - 1, out_filter_radius, filter_support, edge, &in_first_pixel, &in_last_pixel );
8000 range->n1 = in_last_pixel;
8001 }
8002 else if ( samp->is_gather == 2 ) // downsample gather, refine
8003 {
8004 stbir_real filter_support = in_pixels_radius / inv_scale; // in output pixels
8005 int filter_pixel_margin = samp->filter_pixel_margin;
8006 int output_sub_size = samp->scale_info.output_sub_size;
8007 int in_first_pixel, in_last_pixel;
8008
8009 // get a conservative area of the input range
8010 stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, (stbir_real)0.0, (stbir_real)0.0, samp->scale_info.inv_scale, samp->scale_info.pixel_shift, input_full_size, edge );
8011 range->n0 = in_first_pixel;
8012 stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, (stbir_real)output_sub_size, (stbir_real)0.0, samp->scale_info.inv_scale, samp->scale_info.pixel_shift, input_full_size, edge );
8013 range->n1 = in_last_pixel;
8014
8015 // walk down through the margin from the start of the area to find the bottom, and up from its end to find the top
8016 // (the coefficient generator never goes past input_full_size + filter_pixel_margin - 1)
8017 range->n0 = stbir__last_contributing_input( range->n0 + 1, -filter_pixel_margin, -1, range->n0, in_pixels_radius, filter_support, &samp->scale_info );
8018 range->n1 = stbir__last_contributing_input( range->n1 - 1, input_full_size + filter_pixel_margin - 1, 1, range->n1, in_pixels_radius, filter_support, &samp->scale_info );
8019 }
8020
8021 // when polyphase, the coefficient generator only calculates the first numerator outputs and then copies them
8022 // forward shifted by denominator (exact integer math). the top of the range above was calculated directly at the
8023 // far end of the image in floating point, which can round the other way, so widen by one - and when the fraction is
8024 // an approximation of the scale, the copies drift from the real positions by up to approximated_drift input pixels.
8025 if ( ( samp->scale_info.scale_is_rational ) && ( (int)samp->scale_info.scale_numerator < samp->scale_info.output_sub_size ) )
8026 range->n1 += 1 + samp->scale_info.approximated_drift;
8027
8028 // with WRAP the range is allowed to hang off either edge (those pixels get decoded from the other end of
8029 // the scanline), the other modes fold everything back into the scanline so clamp them to it
8030 if ( samp->edge != STBIR_EDGE_WRAP )
8031 {
8032 // for reflect, the margin taps get folded back into the image, and on tiny inputs we
8033 // land beyond, so widen to include them
8034 if ( samp->edge == STBIR_EDGE_REFLECT )
8035 {
8036 if ( range->n0 < 0 )
8037 range->n1 = stbir__max( range->n1, stbir__min( -range->n0, input_full_size - 1 ) );
8038 if ( range->n1 >= input_full_size )
8039 range->n0 = stbir__min( range->n0, stbir__max( 0, ( input_full_size - 1 ) - ( range->n1 - input_full_size ) ) ); // ( 2 x size - n1 - 1, written so it can't overflow at 2^30 )
8040 }
8041
8042 // for non-edge-wrap modes, we never read over the edge, so clamp
8043 if ( range->n0 < 0 )
8044 range->n0 = 0;
8045 if ( range->n1 >= input_full_size )
8046 range->n1 = input_full_size - 1;
8047 }
8048}
8049
8050static void stbir__get_split_info( stbir__per_split_info* split_info, int splits, int output_height, int vertical_pixel_margin, int input_full_height, int is_gather, stbir__sampler const * vertical )
8051{
8052 int start_y = 0;
8053 int remaining = output_height;
8054 int i;
8055
8056 for (i = 0; i < splits; i++)
8057 {
8058 int rows = remaining / ( splits - i );
8059
8060 split_info[i].start_output_y = start_y;
8061 split_info[i].end_output_y = start_y + rows;
8062
8063 // Don't start a split on a row whose range starts later than a following row's (see stbir__lowest_scanline_needed):
8064 // move the rows up to that one into the previous split, so it ends with the unusual row instead of this one
8065 // starting on it. Only this split's rows are looked at (the next split's first row is its own problem).
8066 if ( ( is_gather ) && ( i ) && ( rows > 1 ) )
8067 {
8068 int look = vertical_pixel_margin * 3;
8069 int move;
8070
8071 if ( look > ( rows - 1 ) )
8072 look = ( rows - 1 );
8073 stbir__lowest_scanline_needed( vertical, start_y, look, &move );
8074 split_info[i-1].end_output_y += move;
8075 split_info[i].start_output_y += move;
8076 }
8077
8078 start_y += rows;
8079 remaining -= rows;
8080
8081 // scatter range (updated to minimum as you run it)
8082 split_info[i].start_input_y = -vertical_pixel_margin;
8083 split_info[i].end_input_y = input_full_height + vertical_pixel_margin;
8084 }
8085}
8086
8087static void stbir__free_internal_mem( stbir__info *info )
8088{
8089 #define STBIR__FREE_AND_CLEAR( ptr ) { if ( ptr ) { void * p = (ptr); (ptr) = 0; STBIR_FREE( p, info->user_data); } }
8090
8091 if ( info )
8092 {
8093 #ifndef STBIR__SEPARATE_ALLOCATIONS
8094 STBIR__FREE_AND_CLEAR( info->alloced_mem );
8095 #else
8096 // a build whose allocation failed part way comes through here too: info and its pointer arrays are zeroed when
8097 // allocated, so every pointer is either an allocation or null
8098 int i,j;
8099
8100 if ( info->split_info )
8101 {
8102 for( i = 0 ; i < info->splits ; i++ )
8103 {
8104 stbir__per_split_info * split = info->split_info + i;
8105
8106 if ( split->ring_buffers )
8107 {
8108 for( j = 0 ; j < info->alloc_ring_buffer_num_entries ; j++ )
8109 {
8110 float * ring_buffer = split->ring_buffers[j];
8111 if ( ring_buffer )
8112 {
8113 #ifdef STBIR_SIMD8
8114 if ( info->effective_channels == 3 )
8115 --ring_buffer; // avx in 3 channel mode needs one float at the start of the buffer
8116 #endif
8117 STBIR_FREE( ring_buffer, info->user_data );
8118 }
8119 }
8120 STBIR__FREE_AND_CLEAR( split->ring_buffers );
8121 }
8122
8123 if ( split->decode_buffer_alloc )
8124 {
8125 #ifdef STBIR_SIMD8
8126 if ( info->effective_channels == 3 )
8127 split->decode_buffer_alloc = ( (float*)split->decode_buffer_alloc ) - 1; // avx in 3 channel mode needs one float at the start of the buffer
8128 #endif
8129 STBIR__FREE_AND_CLEAR( split->decode_buffer_alloc );
8130 }
8131 STBIR__FREE_AND_CLEAR( split->vertical_buffer_alloc );
8132 }
8133 STBIR__FREE_AND_CLEAR( info->split_info );
8134 }
8135 STBIR__FREE_AND_CLEAR( info->column_slices );
8136 if ( info->vertical.contributors != info->horizontal.contributors ) // (when shared, the vertical's coefficients point two ints into the horizontal's)
8137 {
8138 STBIR__FREE_AND_CLEAR( info->vertical.coefficients );
8139 STBIR__FREE_AND_CLEAR( info->vertical.contributors );
8140 }
8141 STBIR__FREE_AND_CLEAR( info->horizontal.coefficients );
8142 STBIR__FREE_AND_CLEAR( info->horizontal.contributors );
8143 STBIR__FREE_AND_CLEAR( info->alloced_mem );
8144 STBIR_FREE( info, info->user_data );
8145 #endif
8146 }
8147
8148 #undef STBIR__FREE_AND_CLEAR
8149}
8150
8151static int stbir__get_max_split( int splits, int height )
8152{
8153 int i;
8154 int max = 0;
8155
8156 for( i = 0 ; i < splits ; i++ )
8157 {
8158 int each = height / ( splits - i );
8159 if ( each > max )
8160 max = each;
8161 height -= each;
8162 }
8163 return max;
8164}
8165
8166static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_n_coeffs_funcs[8] =
8167{
8168 0, stbir__horizontal_gather_1_channels_with_n_coeffs_funcs, stbir__horizontal_gather_2_channels_with_n_coeffs_funcs, stbir__horizontal_gather_3_channels_with_n_coeffs_funcs, stbir__horizontal_gather_4_channels_with_n_coeffs_funcs, 0,0, stbir__horizontal_gather_7_channels_with_n_coeffs_funcs
8169};
8170
8171static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_channels_funcs[8] =
8172{
8173 0, stbir__horizontal_gather_1_channels_funcs, stbir__horizontal_gather_2_channels_funcs, stbir__horizontal_gather_3_channels_funcs, stbir__horizontal_gather_4_channels_funcs, 0,0, stbir__horizontal_gather_7_channels_funcs
8174};
8175
8176// the polyphase period 1 kernels, [channels][coefficient count] (see the horizontal kernel section)
8177static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_period1_funcs[8] =
8178{
8179 0, stbir__horizontal_gather_1_channels_period1_funcs, stbir__horizontal_gather_2_channels_period1_funcs, stbir__horizontal_gather_3_channels_period1_funcs, stbir__horizontal_gather_4_channels_period1_funcs, 0,0, stbir__horizontal_gather_7_channels_period1_funcs
8180};
8181
8182// debug overrides (the tests set these; they are statics, so per translation unit): 0 = off
8183static int stbir__force_vertical_first = 0; // 1 = horizontal first, 2 = vertical first (dotimings, refcheck -vfirst)
8184static int stbir__chose_vertical_first = 0; // (read only) what the cost model picked for the last build
8185static int stbir__split_mode = 0; // 1 = row bands only, 2 = column groups only (refcheck_threads -row_split / -colsplit)
8186static int stbir__split_force_cols = 0; // force this many column groups, the bands are splits / this (refcheck_threads -grid)
8187static int stbir__column_slices = 0; // force this many column slices instead of the L2 fit (refcheck_time cfg N:)
8188static int stbir__4K_vertical_dest_shift = 0; // bytes added to the vertical gather destination's page offset (refcheck_time -vshift)
8189
8190// Vertical first or horizontal first? It matters most when the two axes scale very differently (2x wider and half the
8191// height: the vertical first order is around 3x faster), and 20-40% in ordinary cases. The decision is a kernel cost
8192// model: both orders move the same decoded pixels, so the question is only how much each kernel charges per byte it
8193// streams and the multiplies each direction does (vertical is more SIMDable than horizontal). We calculate the costs as:
8194//
8195// vertical first: (taps_v + 1) / s_h * rv + (1/s_h + 1) * rh (vertical over input-width rows, horizontal over output rows)
8196// horizontal first: (1/s_v) * (1/s_h + 1) * rh + (taps_v + 1) * rv (horizontal over every input row, vertical over output-width rows)
8197// (1/s = input/output per axis, with WRAP's margin pixels and scanlines added to the input)
8198//
8199// rh and rv are the scale factors for the horizontal and vertical costs. We then measure a bunch of horizontal
8200// first and vertical first resizes on a bunch of machines to train small tables to give us a relative speed of
8201// each direction.
8202
8203// horizontal tap count -> table column: 1, 2, 3, 4, 5-6, 7-8, 9-12, 13-17, 18+
8204static int stbir__horizontal_cost_bucket( int taps )
8205{
8206 if ( taps <= 4 ) return ( taps < 1 ) ? 0 : taps - 1;
8207 if ( taps <= 6 ) return 4;
8208 if ( taps <= 8 ) return 5;
8209 if ( taps <= 12 ) return 6;
8210 if ( taps <= 17 ) return 7;
8211 return 8;
8212}
8213
8214// [1,2,3,4,7 channels][bucket]: horizontal kernel cost per byte streamed, relative to the vertical kernel's
8215static float stbir__horizontal_cost[5][9] =
8216{
8217 { 12.472f, 6.718f, 6.656f, 8.008f, 5.957f, 6.068f, 6.474f, 7.646f, 10.184f },
8218 { 8.067f, 4.192f, 5.091f, 4.906f, 4.816f, 5.584f, 5.741f, 6.971f, 6.182f },
8219 { 5.495f, 3.718f, 3.206f, 7.861f, 4.816f, 5.584f, 5.741f, 5.584f, 6.068f },
8220 { 3.651f, 2.135f, 2.791f, 3.583f, 2.950f, 3.583f, 4.154f, 4.192f, 4.861f },
8221 { 2.434f, 2.001f, 2.521f, 3.583f, 2.817f, 3.236f, 3.550f, 3.822f, 4.271f },
8222};
8223
8224// the same when the horizontal is an integer downsample (the polyphase period 1 kernels)
8225static float stbir__horizontal_cost_period1[5][9] =
8226{
8227 { 12.406f, 6.718f, 6.534f, 5.186f, 5.114f, 5.138f, 6.656f, 5.431f, 10.374f },
8228 { 8.001f, 4.154f, 3.583f, 4.003f, 3.276f, 4.473f, 3.683f, 4.231f, 6.013f },
8229 { 5.024f, 3.033f, 3.583f, 3.683f, 3.268f, 4.432f, 3.650f, 4.231f, 5.283f },
8230 { 2.976f, 2.076f, 2.665f, 2.452f, 2.354f, 2.766f, 2.715f, 3.327f, 4.684f },
8231 { 2.184f, 1.928f, 2.342f, 1.983f, 2.233f, 3.005f, 2.715f, 3.421f, 3.858f },
8232};
8233
8234// a x b as a signed 128 bit value, for the exact cost comparison below
8235static void stbir__mul_64x64( stbir_int64 a, stbir_int64 b, stbir_int64 * hi, stbir_uint64 * lo )
8236{
8237 stbir_uint64 ua = (stbir_uint64) a, ub = (stbir_uint64) b;
8238 stbir_uint64 a0 = ua & 0xffffffffu, a1 = ua >> 32, b0 = ub & 0xffffffffu, b1 = ub >> 32;
8239 stbir_uint64 p00 = a0 * b0, p01 = a0 * b1, p10 = a1 * b0, p11 = a1 * b1;
8240 stbir_uint64 mid = ( p00 >> 32 ) + ( p01 & 0xffffffffu ) + ( p10 & 0xffffffffu );
8241 stbir_uint64 uhi = p11 + ( p01 >> 32 ) + ( p10 >> 32 ) + ( mid >> 32 );
8242 *lo = ( mid << 32 ) | ( p00 & 0xffffffffu );
8243 if ( a < 0 ) uhi -= ub; // the unsigned product of the two's complements, corrected to the signed one
8244 if ( b < 0 ) uhi -= ua;
8245 *hi = (stbir_int64) uhi;
8246}
8247
8248// a x b <= c x d, exactly
8249static int stbir__products_le( stbir_int64 a, stbir_int64 b, stbir_int64 c, stbir_int64 d )
8250{
8251 stbir_int64 lhi, rhi;
8252 stbir_uint64 llo, rlo;
8253 stbir__mul_64x64( a, b, &lhi, &llo );
8254 stbir__mul_64x64( c, d, &rhi, &rlo );
8255 return ( lhi < rhi ) || ( ( lhi == rhi ) && ( llo <= rlo ) );
8256}
8257
8258// WRAP is the one edge mode that changes the amount of work: the decoded rows carry the filter's margin pixels on both
8259// ends and the vertical runs over margin scanlines, so the input is effectively taps wider / taller.
8260static int stbir__should_do_vertical_first( int channel_index, int horizontal_filter_pixel_width, int horizontal_input_size, int horizontal_output_size, stbir_edge horizontal_edge, int horizontal_is_period1,
8261 int vertical_filter_pixel_width, int vertical_input_size, int vertical_output_size, stbir_edge vertical_edge )
8262{
8263 stbir_int64 horizontal_input, vertical_input, horizontal_output, vertical_output, vertical_taps, cost_ratio;
8264 int bucket, vertical_first;
8265
8266 // the horizontal kernel's cost relative to the vertical's, in 1/1000ths
8267 bucket = stbir__horizontal_cost_bucket( horizontal_filter_pixel_width );
8268 cost_ratio = (stbir_int64) ( ( horizontal_is_period1 ? stbir__horizontal_cost_period1 : stbir__horizontal_cost )[ channel_index ][ bucket ] * 1000.0f + 0.5f );
8269
8270 // the inputs with their wrap margins
8271 horizontal_input = (stbir_int64) horizontal_input_size + ( ( horizontal_edge == STBIR_EDGE_WRAP ) ? horizontal_filter_pixel_width : 0 );
8272 vertical_input = (stbir_int64) vertical_input_size + ( ( vertical_edge == STBIR_EDGE_WRAP ) ? vertical_filter_pixel_width : 0 );
8273 horizontal_output = horizontal_output_size;
8274 vertical_output = vertical_output_size;
8275 vertical_taps = (stbir_int64) vertical_filter_pixel_width + 1;
8276
8277 vertical_first = stbir__products_le( vertical_taps * vertical_output, ( horizontal_input - horizontal_output ) * 1000,
8278 cost_ratio * ( horizontal_input + horizontal_output ), vertical_input - vertical_output );
8279
8280 // the test hook: report the choice, and let the tests force it
8281 stbir__chose_vertical_first = vertical_first;
8282 if ( stbir__force_vertical_first )
8283 vertical_first = ( stbir__force_vertical_first == 2 ) ? 1 : 0;
8284
8285 return vertical_first;
8286}
8287
8288// layout lookups - must match stbir_internal_pixel_layout
8289static unsigned char stbir__pixel_channels[] = {
8290 1,2,3,3,4, // 1ch, 2ch, rgb, bgr, 4ch
8291 4,4,4,4,2,2, // RGBA,BGRA,ARGB,ABGR,RA,AR
8292 4,4,4,4,2,2, // RGBA_PM,BGRA_PM,ARGB_PM,ABGR_PM,RA_PM,AR_PM
8293};
8294
8295// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types
8296// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible
8297static stbir_internal_pixel_layout stbir__pixel_layout_convert_public_to_internal[] = {
8298 STBIRI_BGR, STBIRI_1CHANNEL, STBIRI_2CHANNEL, STBIRI_RGB, STBIRI_RGBA,
8299 STBIRI_4CHANNEL, STBIRI_BGRA, STBIRI_ARGB, STBIRI_ABGR, STBIRI_RA, STBIRI_AR,
8300 STBIRI_RGBA_PM, STBIRI_BGRA_PM, STBIRI_ARGB_PM, STBIRI_ABGR_PM, STBIRI_RA_PM, STBIRI_AR_PM,
8301};
8302
8303// We need a good guess at the L2 size for the column sizing (CACHE SIZE in the docs). We get this size, in order of preference as:
8304// - stbir_l2_cache_size_override, if you set it to something non-zero before resizing (static, starts at 0, bytes)
8305// - the OS, if you define one of these before including this header (we don't include the headers for you):
8306// STBIR_ASK_WINDOWS_FOR_L2_SIZE - needs <windows.h> and <malloc.h> (GetLogicalProcessorInformationEx into an _alloca buffer)
8307// STBIR_ASK_LINUX_FOR_L2_SIZE - needs <unistd.h> (sysconf _SC_LEVEL2_CACHE_SIZE)
8308// STBIR_ASK_MACOS_FOR_L2_SIZE - needs <sys/sysctl.h> (sysctlbyname hw.l2cachesize)
8309// - cpuid on x86/x64 (leaf 0x80000006, the L2 size in KB - Intel and AMD both fill it in)
8310// - 256 KB if none of that applies or answers
8311// An L2 of 1 MB or more is clamped to 512 KB to keep total resize internal memory use down.
8312
8313static int stbir_l2_cache_size_override = 0; // bytes, 0 = detect
8314
8315#if ( defined( _M_X64 ) || defined( _M_IX86 ) || defined( __x86_64__ ) || defined( __i386__ ) ) && !defined( _M_ARM64EC )
8316 #define STBIR__L2_FROM_CPUID
8317 #ifdef _MSC_VER
8318 #include <intrin.h>
8319 #else
8320 #include <cpuid.h>
8321 #endif
8322#endif
8323
8324// (asked on every build - cpuid is a few hundred cycles, and no cache means nothing shared between threads building at once)
8325static int stbir__l2_cache_size( void )
8326{
8327 int bytes = 0;
8328
8329 if ( stbir_l2_cache_size_override > 0 )
8330 return stbir_l2_cache_size_override;
8331
8332 #if defined( STBIR_ASK_WINDOWS_FOR_L2_SIZE )
8333 {
8334 // the level 2 data or unified cache of the first processor group that reports one. the list is a few hundred
8335 // bytes per cache level per core, so it goes on the stack (a 64 core machine's is ~30 KB; past 256 KB fall through)
8336 DWORD len = 0;
8337 GetLogicalProcessorInformationEx( RelationCache, 0, &len );
8338 if ( ( len ) && ( len <= 256 * 1024 ) )
8339 {
8340 char * buf = (char*) _alloca( len );
8341 if ( GetLogicalProcessorInformationEx( RelationCache, (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) buf, &len ) )
8342 {
8343 DWORD ofs = 0;
8344 while ( ofs < len )
8345 {
8346 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) ( buf + ofs );
8347 if ( ( info->Relationship == RelationCache ) && ( info->Cache.Level == 2 ) && ( ( info->Cache.Type == CacheData ) || ( info->Cache.Type == CacheUnified ) ) )
8348 {
8349 bytes = (int) info->Cache.CacheSize;
8350 break;
8351 }
8352 ofs += info->Size;
8353 }
8354 }
8355 }
8356 }
8357 #elif defined( STBIR_ASK_LINUX_FOR_L2_SIZE )
8358 {
8359 long v = sysconf( _SC_LEVEL2_CACHE_SIZE );
8360 if ( v > 0 )
8361 bytes = (int) v;
8362 }
8363 #elif defined( STBIR_ASK_MACOS_FOR_L2_SIZE )
8364 {
8365 stbir_uint64 v = 0;
8366 size_t vlen = sizeof( v );
8367 if ( ( sysctlbyname( "hw.l2cachesize", &v, &vlen, 0, 0 ) == 0 ) && ( v > 0 ) )
8368 bytes = (int) v;
8369 }
8370 #endif
8371
8372 #if defined( STBIR__L2_FROM_CPUID )
8373 if ( bytes <= 0 ) // no OS query, or it didn't answer
8374 {
8375 unsigned int regs[4] = { 0, 0, 0, 0 };
8376 #ifdef _MSC_VER
8377 __cpuid( (int*) regs, 0x80000000 );
8378 if ( regs[0] >= 0x80000006 )
8379 {
8380 __cpuid( (int*) regs, 0x80000006 );
8381 bytes = (int) ( regs[2] >> 16 ) * 1024; // ECX[31:16] = L2 size in KB
8382 }
8383 #else
8384 if ( __get_cpuid_max( 0x80000000, 0 ) >= 0x80000006 )
8385 {
8386 __get_cpuid( 0x80000006, ®s[0], ®s[1], ®s[2], ®s[3] );
8387 bytes = (int) ( regs[2] >> 16 ) * 1024;
8388 }
8389 #endif
8390 }
8391 #endif
8392
8393 if ( bytes <= 0 )
8394 bytes = 256 * 1024;
8395 else if ( bytes >= 1024 * 1024 )
8396 bytes = 512 * 1024;
8397
8398 return bytes;
8399}
8400
8401// The splits (what a caller threads over) are a grid of split_cols column groups x split_rows row bands. Any grid gives
8402// the same pixels and much the same memory (a split holds one slice's buffers either way), so stbir__choose_split_grid
8403// picks the fastest by estimate, for every count from the request down to one split:
8404// - a row band re-decodes the vertical filter's taps rows before its first output row, so short bands cost more.
8405// - column groups are free while they are the slices a single thread would run anyway (the L2 fit); each extra slice
8406// costs stbir_split_slice_row_cost columns of work per row. A group streams at least stbir_split_min_group_bytes
8407// of each input row; below stbir_split_group_full_bytes it pays stbir_split_group_cost_pct per (full / bytes - 1)
8408// per 8 threads, and narrow rows cost (threads / 8)^2 x (64 KB / input row bytes) / stbir_split_stream_cost_div.
8409// - a thread costs stbir_thread_start_cost of work to start and join, and the count is capped so that the work
8410// (output pixels x taps x channels / 4) is at least stbir_split_min_work x splits^2.
8411// - each count is scored by its largest split, and the last threads are dropped while they add less than
8412// stbir_split_min_thread_gain_pct of a thread's speed-up each over the best smaller count.
8413// So the build may return fewer splits than asked for (which was always allowed); stbir_split_extra_threads adds some back.
8414
8415// the split estimate's tuning (statics so an app can override them before resizing, like stbir_l2_cache_size_override;
8416// work units are output pixels x taps x channels / 4, roughly nanoseconds of resizing on a fast core)
8417static int stbir_split_min_group_bytes = 1024; // the least input row a column group streams (bytes)
8418static int stbir_split_group_full_bytes = 4096; // below this much input row a column group pays the narrow group cost:
8419static int stbir_split_group_cost_pct = 30; // this percent x ( full / bytes - 1 ) x ( threads / 8 ) of the work
8420static int stbir_split_stream_cost_div = 170; // narrow rows under many threads: ( threads / 8 )^2 x ( 64 KB / input row bytes ) / this, whatever the grid
8421static int stbir_split_slice_row_cost = 250; // input columns' worth of work per row for each slice beyond the L2 fit
8422static int stbir_thread_start_cost = 12000; // starting and joining one thread, in work units (a spinning pool ~1000, a thread per job 50000+)
8423static int stbir_split_min_thread_gain_pct = 30; // the last threads must each add this percent of a thread's worth of speed-up, or they are dropped
8424static int stbir_split_min_work = 20000; // the work must be at least this x splits^2
8425static int stbir_split_extra_threads = 0; // use this many more splits than the estimate settles on (never more than asked for)
8426#define STBIR__SPLIT_MAX_SPLITS 256 // the grid search's table
8427
8428
8429// slice boundaries are multiples of this many pixels (so the SIMD loops only run their remnants at the end of the row)
8430#define STBIR__SLICE_STEP 16
8431
8432// the most slices a row can have (each at least a step wide)
8433static int stbir__slice_max_count( int output_columns )
8434{
8435 int n = output_columns / STBIR__SLICE_STEP;
8436 return ( n < 1 ) ? 1 : n;
8437}
8438
8439
8440// The split grid for a request of *splits: cols x rows, and *splits becomes cols * rows (never more than asked for).
8441static void stbir__choose_split_grid( int * splits, int * out_cols, int * out_rows, int mode, int output_w, int output_h, int input_w, int input_pixel_bytes, int taps_v, float inv_scale_v, int l2_slices, int effective_channels )
8442{
8443 int max_splits = *splits;
8444 int requested_splits;
8445 int max_cols = stbir__slice_max_count( output_w ); // a column group needs at least one slice
8446 int best_cols = 1;
8447 int best_rows = 1;
8448
8449 if ( max_splits < 1 )
8450 max_splits = 1;
8451 if ( max_splits > STBIR__SPLIT_MAX_SPLITS )
8452 max_splits = STBIR__SPLIT_MAX_SPLITS;
8453 requested_splits = max_splits;
8454
8455 if ( stbir__split_force_cols > 0 )
8456 {
8457 // Debug: a forced grid.
8458 best_cols = ( stbir__split_force_cols < max_cols ) ? stbir__split_force_cols : max_cols;
8459 if ( best_cols > max_splits )
8460 best_cols = max_splits;
8461 best_rows = max_splits / best_cols;
8462 }
8463 else if ( mode == 1 )
8464 {
8465 // Row bands only.
8466 best_rows = ( max_splits < output_h ) ? max_splits : output_h;
8467 }
8468 else if ( mode == 2 )
8469 {
8470 // Column groups only.
8471 best_cols = ( max_splits < max_cols ) ? max_splits : max_cols;
8472 }
8473 else
8474 {
8475 // Everything in fixed point: times are in 1/65536ths of the single threaded work.
8476 stbir_uint64 work = (stbir_uint64)output_w * (stbir_uint64)output_h * (stbir_uint64)taps_v * (stbir_uint64)effective_channels / 4;
8477 stbir_uint64 band_warmup = (stbir_uint64)( (float)taps_v * 65536.0f / ( ( inv_scale_v > 1.0f ) ? inv_scale_v : 1.0f ) ); // what a band re-decodes before its first row, in output rows' worth of work
8478 stbir_uint64 row_bytes = (stbir_uint64)input_w * (stbir_uint64)input_pixel_bytes;
8479 stbir_uint64 estimate_of[ STBIR__SPLIT_MAX_SPLITS + 1 ];
8480 int cols_of[ STBIR__SPLIT_MAX_SPLITS + 1 ];
8481 int rows_of[ STBIR__SPLIT_MAX_SPLITS + 1 ];
8482 int count, cols;
8483
8484 if ( work < 1 )
8485 work = 1;
8486 if ( row_bytes < 1 )
8487 row_bytes = 1;
8488
8489 // The thread start/join cost grows with the count: the work must be at least min_work x splits^2.
8490 while ( ( max_splits > 1 ) && ( work < (stbir_uint64)stbir_split_min_work * (stbir_uint64)max_splits * (stbir_uint64)max_splits ) )
8491 --max_splits;
8492
8493 // The best grid for every count up to the request, by estimated time (the largest split's - it sets the pace).
8494 for (count = 1; count <= requested_splits; count++)
8495 {
8496 estimate_of[count] = ~(stbir_uint64)0;
8497 cols_of[count] = 1;
8498 rows_of[count] = 1;
8499
8500 for (cols = 1; cols <= count; cols++)
8501 {
8502 int rows, rows_per_band, extra_slices;
8503 stbir_uint64 group_bytes, narrow_cost, stream_cost, per_row_cost, estimate;
8504
8505 if ( count % cols )
8506 continue;
8507 if ( cols > max_cols )
8508 break;
8509
8510 // A column group streams its share of each input row, and never less than min_group_bytes of it.
8511 group_bytes = row_bytes / (stbir_uint64)cols;
8512 if ( group_bytes < 1 )
8513 group_bytes = 1;
8514 if ( ( cols > 1 ) && ( group_bytes < (stbir_uint64)stbir_split_min_group_bytes ) )
8515 break;
8516
8517 // Narrow groups cost group_cost_pct per ( full / bytes - 1 ) per 8 threads.
8518 narrow_cost = 0;
8519 if ( ( cols > 1 ) && ( group_bytes < (stbir_uint64)stbir_split_group_full_bytes ) )
8520 narrow_cost = ( ( (stbir_uint64)stbir_split_group_full_bytes * 65536 ) / group_bytes - 65536 ) * (stbir_uint64)stbir_split_group_cost_pct * (stbir_uint64)count / ( 100 * 8 );
8521
8522 // Narrow rows under many threads cost the same whatever the grid (every row is a fresh page, latency bound, and
8523 // the latency climbs with the thread count): the larger of this and the narrow group cost.
8524 stream_cost = ( (stbir_uint64)count * (stbir_uint64)count * 65536 * 65536 ) / ( (stbir_uint64)64 * (stbir_uint64)stbir_split_stream_cost_div * row_bytes );
8525 if ( stream_cost > narrow_cost )
8526 narrow_cost = stream_cost;
8527
8528 rows = count / cols;
8529 if ( rows > output_h )
8530 continue;
8531
8532 // The slowest split sets the time: the largest band (rows rounded up) doing the largest group's share of the
8533 // slices (the slice count rounded up to a multiple of the groups), against an even share of the work.
8534 rows_per_band = ( output_h + rows - 1 ) / rows;
8535 extra_slices = ( ( l2_slices + cols - 1 ) / cols ) * cols - l2_slices;
8536 per_row_cost = 65536 + band_warmup / (stbir_uint64)rows_per_band + ( (stbir_uint64)extra_slices * (stbir_uint64)stbir_split_slice_row_cost * 65536 ) / (stbir_uint64)input_w + narrow_cost;
8537 estimate = ( (stbir_uint64)rows_per_band * (stbir_uint64)rows ) * per_row_cost / ( (stbir_uint64)output_h * (stbir_uint64)count );
8538 estimate += ( (stbir_uint64)count * (stbir_uint64)stbir_thread_start_cost * 65536 ) / work; // plus the threads' own start/join cost
8539
8540 if ( estimate < estimate_of[count] )
8541 {
8542 estimate_of[count] = estimate;
8543 cols_of[count] = cols;
8544 rows_of[count] = rows;
8545 }
8546 }
8547 }
8548
8549 // From the request down, drop threads while they add less than min_thread_gain_pct of a thread's speed-up each
8550 // over the best smaller count (not the next one down: a prime count only has bad grids, which makes the count
8551 // above it look good).
8552 count = max_splits;
8553 while ( count > 1 )
8554 {
8555 stbir_uint64 speedup = ( estimate_of[1] * 256 ) / estimate_of[count];
8556 stbir_uint64 best_speedup = 0;
8557 int fewer, best_fewer = 1;
8558
8559 for (fewer = 1; fewer < count; fewer++)
8560 {
8561 stbir_uint64 speedup_fewer = ( estimate_of[1] * 256 ) / estimate_of[fewer];
8562 if ( speedup_fewer > best_speedup )
8563 {
8564 best_speedup = speedup_fewer;
8565 best_fewer = fewer;
8566 }
8567 }
8568
8569 if ( ( speedup > best_speedup ) && ( ( speedup - best_speedup ) * 100 >= (stbir_uint64)stbir_split_min_thread_gain_pct * 256 * (stbir_uint64)( count - best_fewer ) ) )
8570 break;
8571
8572 // The extra threads over the best smaller count weren't worth it: fall back to it and check again.
8573 count = best_fewer;
8574 }
8575
8576 // The app wants more than the estimate settled on.
8577 if ( stbir_split_extra_threads > 0 )
8578 {
8579 count += stbir_split_extra_threads;
8580 if ( count > requested_splits )
8581 count = requested_splits;
8582 // A count no grid fits (more bands than rows): the next one down.
8583 while ( ( count > 1 ) && ( estimate_of[count] == ~(stbir_uint64)0 ) )
8584 --count;
8585 }
8586
8587 best_cols = cols_of[count];
8588 best_rows = rows_of[count];
8589 }
8590
8591 if ( best_cols > max_cols )
8592 best_cols = max_cols;
8593 if ( best_rows > output_h )
8594 best_rows = output_h;
8595 if ( best_cols < 1 )
8596 best_cols = 1;
8597 if ( best_rows < 1 )
8598 best_rows = 1;
8599
8600 *out_cols = best_cols;
8601 *out_rows = best_rows;
8602 *splits = best_cols * best_rows;
8603}
8604
8605// where slice number slice of num_slices ends
8606static int stbir__slice_end( int output_columns, int num_slices, int slice )
8607{
8608 stbir_uint64 end;
8609
8610 if ( slice >= num_slices - 1 )
8611 return output_columns;
8612
8613 end = ( (stbir_uint64)( slice + 1 ) * (stbir_uint64)output_columns ) / (stbir_uint64)num_slices;
8614 return ( (int)end / STBIR__SLICE_STEP ) * STBIR__SLICE_STEP;
8615}
8616
8617// the widest slice there can be for a count (for sizing the per slice buffers): the average plus a step of rounding
8618static int stbir__slice_columns_for( int output_columns, int slices )
8619{
8620 int columns = ( output_columns + slices - 1 ) / slices + STBIR__SLICE_STEP;
8621 if ( columns >= output_columns )
8622 columns = output_columns;
8623 return columns;
8624}
8625
8626// the input pixels a slice decodes for that many columns (see the sizing in stbir__alloc_internal_mem_and_build_samplers)
8627static int stbir__slice_in_span_for( stbir__sampler const * horizontal, int slice_columns, int conservative_in_span )
8628{
8629 int span = (int)( slice_columns * horizontal->scale_info.inv_scale ) + 1 + horizontal->filter_pixel_width + 2 * horizontal->coefficient_width + 4;
8630 if ( ( slice_columns >= horizontal->scale_info.output_sub_size ) || ( span > conservative_in_span ) )
8631 span = conservative_in_span;
8632 return span;
8633}
8634
8635// the ring buffer for one slice: its entries are horizontal output rows (slice_columns wide), or decoded input rows
8636// (slice_in_span wide) when vertical first
8637static size_t stbir__ring_bytes_for( stbir__sampler const * horizontal, int slice_columns, int conservative_in_span, int effective_channels, int vertical_first, int ring_entries )
8638{
8639 int row_pixels = vertical_first ? stbir__slice_in_span_for( horizontal, slice_columns, conservative_in_span ) : slice_columns;
8640 return (size_t)ring_entries * ( (size_t)row_pixels * effective_channels * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING );
8641}
8642
8643// pick the slice count: the fewest slices whose ring buffer fits the L2 budget (allowing an eighth over - a little
8644// spill costs less than another slice's re-decode), or the most slices possible (16 columns each) if none fit
8645static int stbir__choose_column_slices( stbir__sampler const * horizontal, int conservative_in_span, int effective_channels, int vertical_first, int ring_entries )
8646{
8647 int output_columns = horizontal->scale_info.output_sub_size;
8648 size_t budget = (size_t)stbir__l2_cache_size();
8649 size_t allowed = budget + budget / 8;
8650 int max_slices = stbir__slice_max_count( output_columns );
8651 int slices;
8652
8653 for (slices = 1; slices < max_slices; slices++)
8654 {
8655 int slice_columns = stbir__slice_columns_for( output_columns, slices );
8656 if ( stbir__ring_bytes_for( horizontal, slice_columns, conservative_in_span, effective_channels, vertical_first, ring_entries ) <= allowed )
8657 return slices;
8658 }
8659 return max_slices;
8660}
8661
8662// splits the output columns into num_column_slices balanced slices on step boundaries (stbir__slice_end), and works
8663// out the input range each slice has to decode: the lowest n0 through the furthest pixel any of its kernels read
8664// (stbir__pack_coefficients arranged for those reads to stay within the conservative range).
8665static void stbir__build_column_slices( stbir__info * info, int slice_columns, int in_span_bound )
8666{
8667 stbir__sampler const * horizontal = &info->horizontal;
8668 int output_columns = horizontal->scale_info.output_sub_size;
8669 int widest = horizontal->coefficient_width; // the coefficients are packed to widest by now
8670 int first_column, end_column;
8671 int slice_index = 0;
8672
8673 STBIR__UNUSED( in_span_bound ); // both only checked in asserts
8674 STBIR__UNUSED( slice_columns );
8675
8676 for (first_column = 0; first_column < output_columns; first_column = end_column, slice_index++)
8677 {
8678 stbir__column_slice * slice = info->column_slices + slice_index;
8679 int in_first = 0x7fffffff;
8680 int in_last = -0x7fffffff;
8681 int x, unused;
8682
8683 end_column = stbir__slice_end( output_columns, info->num_column_slices, slice_index );
8684 STBIR_ASSERT( ( end_column > first_column ) && ( end_column <= output_columns ) && ( ( end_column - first_column ) <= slice_columns ) );
8685
8686 // the lowest first pixel through the furthest pixel any of the slice's kernels read
8687 for (x = first_column; x < end_column; x++)
8688 {
8689 stbir__contributors contributor;
8690 int last_read;
8691
8692 stbir__output_range( horizontal, x, &contributor.n0, &contributor.n1 );
8693 last_read = contributor.n0 + stbir__horizontal_read_extent( &contributor, widest ) - 1;
8694 if ( contributor.n0 < in_first )
8695 in_first = contributor.n0;
8696 if ( last_read > in_last )
8697 in_last = last_read;
8698 }
8699
8700 STBIR_ASSERT( in_first >= info->conservative_extents.n0 );
8701 STBIR_ASSERT( in_last <= info->conservative_extents.n1 );
8702 STBIR_ASSERT( ( in_last - in_first + 1 ) <= in_span_bound ); // the buffers were sized from in_span_bound in the first allocation pass
8703
8704 slice->x0 = first_column;
8705 slice->width = end_column - first_column;
8706 slice->in_n0 = in_first;
8707 slice->in_n1 = in_last;
8708 stbir__output_range( horizontal, first_column, &slice->first_n0, &unused );
8709 }
8710
8711 STBIR_ASSERT( slice_index == info->num_column_slices );
8712}
8713
8714// split s is row band ( s / split_cols ) of column group ( s % split_cols ).
8715static void stbir__setup_split_grid( stbir__info * info, int split_cols, int split_rows )
8716{
8717 int r, c, next = 0;
8718
8719 info->split_cols = split_cols;
8720 info->split_rows = split_rows;
8721 stbir__get_split_info( info->split_info, split_rows, info->vertical.scale_info.output_sub_size, info->vertical.filter_pixel_margin, info->vertical.scale_info.input_full_size, info->vertical.is_gather, &info->vertical );
8722 for( r = split_rows - 1 ; r >= 0 ; r-- )
8723 {
8724 stbir__per_split_info band = info->split_info[r];
8725 for( c = split_cols - 1 ; c >= 0 ; c-- )
8726 {
8727 stbir__per_split_info * s = info->split_info + r * split_cols + c;
8728 s->start_output_y = band.start_output_y;
8729 s->end_output_y = band.end_output_y;
8730 s->start_input_y = band.start_input_y;
8731 s->end_input_y = band.end_input_y;
8732 }
8733 }
8734 for( c = 0 ; c < split_cols ; c++ )
8735 {
8736 int count = ( info->num_column_slices - next ) / ( split_cols - c );
8737 for( r = 0 ; r < split_rows ; r++ )
8738 {
8739 info->split_info[ r * split_cols + c ].first_column_slice = next;
8740 info->split_info[ r * split_cols + c ].num_column_slices = count;
8741 }
8742 next += count;
8743 }
8744}
8745
8746// the per split buffers get a page of slack, to be repositioned within it at resize time (stbir__4K_alias_skew_buffers)
8747#define STBIR__4K_ALIAS_SKEW_SLACK 4096
8748
8749// What kind of alpha weighting the layouts call for: 0 = none, 1 = weight in, leave premultiplied, 2 = fancy weight
8750// in and unweight out, 3 = premultiplied in, unweight out, 4 = simple weight in and unweight out. Fancy weighting
8751// carries extra channels through the filters (*effective_channels).
8752static int stbir__alpha_weighting_type( stbir__sampler const * horizontal, stbir__sampler const * vertical, stbir_internal_pixel_layout input_pixel_layout, stbir_internal_pixel_layout output_pixel_layout, int fast_alpha, int * effective_channels )
8753{
8754 int input_has_alpha = ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR );
8755 int input_premultiplied = ( input_pixel_layout >= STBIRI_RGBA_PM ) && ( input_pixel_layout <= STBIRI_AR_PM );
8756 int output_has_alpha = ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR );
8757 int output_premultiplied = ( output_pixel_layout >= STBIRI_RGBA_PM ) && ( output_pixel_layout <= STBIRI_AR_PM );
8758
8759 int point_sampled = ( horizontal->filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( vertical->filter_enum == STBIR_FILTER_POINT_SAMPLE );
8760
8761 if ( input_has_alpha && output_has_alpha )
8762 {
8763 static int fancy_alpha_effective_cnts[6] = { 7, 7, 7, 7, 3, 3 };
8764 if ( point_sampled ) // nothing is blended, so weighting in and unweighting out would be the identity
8765 return 0;
8766 if ( fast_alpha )
8767 return 4;
8768 *effective_channels = fancy_alpha_effective_cnts[ input_pixel_layout - STBIRI_RGBA ];
8769 return 2;
8770 }
8771 // (the conversions between premultiplied and not still happen when point sampling)
8772 if ( input_premultiplied && output_has_alpha )
8773 return 3;
8774 if ( input_has_alpha && output_premultiplied )
8775 return 1;
8776 return 0;
8777}
8778
8779// The alpha weight and unweight functions for the weighting type (and the 3 channel color flip, which rides the same path).
8780static void stbir__set_alpha_functions( stbir__info * info, int alpha_weighting_type, stbir_internal_pixel_layout input_pixel_layout, stbir_internal_pixel_layout output_pixel_layout, float horizontal_scale )
8781{
8782 static stbir__alpha_weight_func * fancy_alpha_weights[6] = { stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_2ch, stbir__fancy_alpha_weight_2ch };
8783 static stbir__alpha_unweight_func * fancy_alpha_unweights[6] = { stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_2ch, stbir__fancy_alpha_unweight_2ch };
8784 static stbir__alpha_weight_func * simple_alpha_weights[6] = { stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_2ch, stbir__simple_alpha_weight_2ch };
8785 static stbir__alpha_unweight_func * simple_alpha_unweights[6] = { stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_2ch, stbir__simple_alpha_unweight_2ch };
8786
8787 info->alpha_weight = 0;
8788 info->alpha_unweight = 0;
8789
8790 if ( alpha_weighting_type == 2 )
8791 {
8792 // high quality alpha multiplying on the way in, dividing on the way out
8793 info->alpha_weight = fancy_alpha_weights[ input_pixel_layout - STBIRI_RGBA ];
8794 info->alpha_unweight = fancy_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ];
8795 }
8796 else if ( alpha_weighting_type == 4 )
8797 {
8798 // fast alpha multiplying on the way in, dividing on the way out
8799 info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ];
8800 info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ];
8801 }
8802 else if ( alpha_weighting_type == 1 )
8803 {
8804 // fast alpha on the way in, leave in premultiplied form on way out
8805 info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ];
8806 }
8807 else if ( alpha_weighting_type == 3 )
8808 {
8809 // incoming is premultiplied, fast alpha dividing on the way out - non-premultiplied output
8810 info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ];
8811 }
8812
8813 // handle 3-chan color flipping, using the alpha weight path
8814 if ( ( ( input_pixel_layout == STBIRI_RGB ) && ( output_pixel_layout == STBIRI_BGR ) ) ||
8815 ( ( input_pixel_layout == STBIRI_BGR ) && ( output_pixel_layout == STBIRI_RGB ) ) )
8816 {
8817 // do the flipping on the smaller of the two ends
8818 if ( horizontal_scale < 1.0f )
8819 info->alpha_unweight = stbir__simple_flip_3ch;
8820 else
8821 info->alpha_weight = stbir__simple_flip_3ch;
8822 }
8823}
8824
8825// Shared coefficients require identical geometry; inactive rational and exact-position fields may be uninitialized.
8826static int stbir__scale_info_matches( stbir__scale_info const * horizontal, stbir__scale_info const * vertical )
8827{
8828 if ( ( horizontal->input_full_size != vertical->input_full_size ) || ( horizontal->output_sub_size != vertical->output_sub_size ) ||
8829 ( horizontal->scale != vertical->scale ) || ( horizontal->inv_scale != vertical->inv_scale ) || // (the positions come from the reals)
8830 ( horizontal->pixel_shift != vertical->pixel_shift ) || ( horizontal->scale_is_rational != vertical->scale_is_rational ) ||
8831 ( horizontal->positions_exact != vertical->positions_exact ) )
8832 {
8833 return 0;
8834 }
8835
8836 if ( horizontal->scale_is_rational &&
8837 ( ( horizontal->scale_numerator != vertical->scale_numerator ) || ( horizontal->scale_denominator != vertical->scale_denominator ) ) )
8838 {
8839 return 0;
8840 }
8841
8842 if ( horizontal->positions_exact &&
8843 ( ( horizontal->shift_pixels != vertical->shift_pixels ) || ( horizontal->tap_scale_in != vertical->tap_scale_in ) ||
8844 ( horizontal->tap_scale_out != vertical->tap_scale_out ) ) )
8845 {
8846 return 0;
8847 }
8848
8849 return 1;
8850}
8851
8852// Are the two samplers identical? (happens a lot with mipmap generation) Then the vertical can share the horizontal's tables.
8853static int stbir__samplers_match( stbir__sampler const * horizontal, stbir__sampler const * vertical )
8854{
8855 return ( horizontal->filter_kernel_new == vertical->filter_kernel_new ) && ( horizontal->filter_kernel_old == vertical->filter_kernel_old ) && ( horizontal->filter_support_new == vertical->filter_support_new ) && ( horizontal->filter_radius == vertical->filter_radius ) && ( horizontal->filter_support_old == vertical->filter_support_old ) &&
8856 ( horizontal->edge == vertical->edge ) && ( horizontal->is_gather == vertical->is_gather ) &&
8857 stbir__scale_info_matches( &horizontal->scale_info, &vertical->scale_info );
8858}
8859
8860// The horizontal kernels for this channel count and coefficient width.
8861static void stbir__select_horizontal_kernels( stbir__info * info, stbir__sampler const * horizontal, int effective_channels )
8862{
8863 int widest = horizontal->extent_info.widest;
8864
8865 // start with defaulting to the n_coeffs functions (specialized on channels and remnant leftover)
8866 info->horizontal_gather_channels = stbir__horizontal_gather_n_coeffs_funcs[ effective_channels ][ widest & 3 ];
8867
8868 // but if the number of coeffs <= 17, use another set of special cases. This is by far the common case
8869 if ( widest <= 17 )
8870 info->horizontal_gather_channels = stbir__horizontal_gather_channels_funcs[ effective_channels ][ widest - 1 ];
8871
8872 // integer downsamples (polyphase period 1) use one coefficient set for the whole interior, so the kernel can keep the
8873 // coefficients in registers for the whole run - where there is one for this channel count and width (null otherwise)
8874 info->horizontal_gather_period1 = 0;
8875 if ( ( horizontal->shared_period == 1 ) && ( widest <= 17 ) )
8876 info->horizontal_gather_period1 = stbir__horizontal_gather_period1_funcs[ effective_channels ][ widest ];
8877}
8878
8879// The per split buffers are sized for one column slice (the slices run one after another and reuse them).
8880typedef struct stbir__slice_buffer_sizes
8881{
8882 int num_column_slices; // a multiple of the column groups, so they get equal shares
8883 int slice_columns; // the widest slice
8884 int slice_in_span; // the most input pixels a slice decodes
8885 size_t decode_buffer_size;
8886 size_t ring_buffer_length_bytes; // one ring entry
8887 size_t ring_buffer_size;
8888 size_t vertical_buffer_size;
8889} stbir__slice_buffer_sizes;
8890
8891static void stbir__size_slice_buffers( stbir__slice_buffer_sizes * sizes, stbir__sampler const * horizontal, int conservative_in_span, int l2_slices, int split_cols, int effective_channels, int vertical_first, int ring_buffer_num_entries )
8892{
8893 int output_columns = horizontal->scale_info.output_sub_size;
8894 int max_slices = stbir__slice_max_count( output_columns );
8895
8896 // the slice count made a multiple of the column groups (a slice is at least a step wide, so some groups may get none)
8897 sizes->num_column_slices = ( ( l2_slices + split_cols - 1 ) / split_cols ) * split_cols;
8898 if ( sizes->num_column_slices > max_slices )
8899 sizes->num_column_slices = max_slices;
8900 sizes->slice_columns = stbir__slice_columns_for( output_columns, sizes->num_column_slices );
8901
8902 // the input pixels a slice decodes: its columns' geometric span, plus the filter width, plus the room
8903 // stbir__pack_coefficients can move a contributor back by and the kernel can read past it (a coefficient
8904 // width each). the exact ranges come from the packed contributors in the second pass, which asserts
8905 // they fit in this. a single slice is exactly the conservative range.
8906 sizes->slice_in_span = stbir__slice_in_span_for( horizontal, sizes->slice_columns, conservative_in_span );
8907
8908 // sometimes read one float off in some of the unrolled loops (with a weight of zero coeff, so it doesn't have an effect)
8909 // we use a few extra floats instead of just 1, so that input callback buffer can overlap with the decode buffer without
8910 // the conversion routines overwriting the callback input data.
8911 sizes->decode_buffer_size = (size_t)sizes->slice_in_span * effective_channels * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING; // extra floats for input callback stagger
8912
8913#if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8)
8914 if ( effective_channels == 3 )
8915 sizes->decode_buffer_size += sizeof(float); // avx in 3 channel mode needs one float at the start of the buffer (only with separate allocations)
8916#endif
8917
8918 sizes->ring_buffer_length_bytes = (size_t)sizes->slice_columns * (size_t)effective_channels * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING; // extra floats for padding
8919
8920 // if we do vertical first, the ring buffer holds a whole decoded line
8921 if ( vertical_first )
8922 sizes->ring_buffer_length_bytes = ( sizes->decode_buffer_size + 15 ) & ~15;
8923
8924 // keep the ring buffer's entries off a page multiple apart: the vertical gather reads taps rows at the same column,
8925 // and at a 4K multiple stride they all fall in the same cache sets
8926 if ( ( sizes->ring_buffer_length_bytes & 4095 ) == 0 )
8927 sizes->ring_buffer_length_bytes += 64*3;
8928
8929 sizes->ring_buffer_size = (size_t)ring_buffer_num_entries * sizes->ring_buffer_length_bytes;
8930
8931 // The vertical buffer holds one horizontally resampled row: gathering, the output row (the vertical gather's result
8932 // horizontal first, the horizontal's result vertical first); scattering, the input row about to be scattered
8933 // (horizontal first) or the output row at eviction (vertical first). The accumulation itself is in the ring.
8934 sizes->vertical_buffer_size = (size_t)sizes->slice_columns * (size_t)effective_channels * sizeof(float) + sizeof(float); // extra float for padding
8935}
8936
8937static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sampler * horizontal, stbir__sampler * vertical, stbir__contributors * conservative, stbir_pixel_layout input_pixel_layout_public, stbir_pixel_layout output_pixel_layout_public, int splits, int new_x, int new_y, int fast_alpha, int input_pixel_bytes, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO )
8938{
8939 static char stbir_channel_count_index[8]={ 9,0,1,2, 3,9,9,4 };
8940
8941 stbir__info * info = 0;
8942 void * alloced = 0;
8943 size_t alloced_total = 0;
8944 int vertical_first;
8945 int alloc_ring_buffer_num_entries;
8946 int alpha_weighting_type;
8947 int conservative_split_output_size;
8948 int conservative_in_span, l2_slices, split_cols, split_rows;
8949 int copy_horizontal;
8950 stbir__slice_buffer_sizes sizes;
8951 stbir_internal_pixel_layout input_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ input_pixel_layout_public ];
8952 stbir_internal_pixel_layout output_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ output_pixel_layout_public ];
8953 int channels = stbir__pixel_channels[ input_pixel_layout ];
8954 int effective_channels = channels;
8955
8956 // channel in and out count must match currently
8957 if ( channels != stbir__pixel_channels[ output_pixel_layout ] )
8958 return 0;
8959
8960 alpha_weighting_type = stbir__alpha_weighting_type( horizontal, vertical, input_pixel_layout, output_pixel_layout, fast_alpha, &effective_channels );
8961
8962 // do the horizontal and vertical store their polyphase coefficient sets once? (sizes the tables, so before anything
8963 // is allocated). the vertical only when gathering (the scatter tables are per input scanline). the vertical kernels
8964 // read exactly n0..n1 of a set, so its epilogue needs no clip zone.
8965 stbir__plan_polyphase_sharing( horizontal, 1, user_data );
8966 if ( vertical->is_gather )
8967 stbir__plan_polyphase_sharing( vertical, 0, user_data );
8968
8969 vertical_first = stbir__should_do_vertical_first( (int)stbir_channel_count_index[ effective_channels ],
8970 horizontal->filter_pixel_width, horizontal->scale_info.input_full_size, horizontal->scale_info.output_sub_size, horizontal->edge, horizontal->shared_period == 1,
8971 vertical->filter_pixel_width, vertical->scale_info.input_full_size, vertical->scale_info.output_sub_size, vertical->edge );
8972
8973 // ring rows: gathering, an output's taps rows (plus one for float rounding); scattering, the output rows an input
8974 // row touches - 2 x the filter's support in output pixels, plus one, plus one for rounding
8975 if ( vertical->is_gather )
8976 alloc_ring_buffer_num_entries = vertical->filter_pixel_width + 1;
8977 else
8978 alloc_ring_buffer_num_entries = stbir__ceil_to_int( (stbir_real)2.0 * stbir__filter_radius_in_output_pixels( vertical->filter_radius, vertical->filter_support_new, vertical->filter_support_old, &vertical->scale_info, user_data ) ) + 2;
8979
8980 // how many vertical slices the single threaded resize would run (the setting, or the L2 fit), then the split grid -
8981 // which can lower the split count
8982 conservative_in_span = conservative->n1 - conservative->n0 + 1;
8983 l2_slices = stbir__column_slices;
8984 if ( l2_slices < 1 )
8985 l2_slices = stbir__choose_column_slices( horizontal, conservative_in_span, effective_channels, vertical_first, alloc_ring_buffer_num_entries );
8986 stbir__choose_split_grid( &splits, &split_cols, &split_rows, stbir__split_mode, horizontal->scale_info.output_sub_size, vertical->scale_info.output_sub_size, conservative_in_span, input_pixel_bytes, vertical->filter_pixel_width, (float) vertical->scale_info.inv_scale, l2_slices, effective_channels );
8987 conservative_split_output_size = stbir__get_max_split( split_rows, vertical->scale_info.output_sub_size );
8988
8989 // we never need more ring buffer entries than the scanlines we're outputting when in scatter mode
8990 if ( ( !vertical->is_gather ) && ( alloc_ring_buffer_num_entries > conservative_split_output_size ) )
8991 alloc_ring_buffer_num_entries = conservative_split_output_size;
8992
8993 stbir__size_slice_buffers( &sizes, horizontal, conservative_in_span, l2_slices, split_cols, effective_channels, vertical_first, alloc_ring_buffer_num_entries );
8994
8995 // are the two filters identical? then the vertical shares the horizontal's tables instead of generating its own
8996 copy_horizontal = stbir__samplers_match( horizontal, vertical );
8997
8998 // we make two passes through this loop, 1st to add everything up, 2nd to allocate and init
8999 for(;;)
9000 {
9001 int i;
9002 void * advance_mem = alloced;
9003
9004#ifdef STBIR__SEPARATE_ALLOCATIONS
9005 // a failed allocation frees whatever got allocated: the pointers the cleanup frees are cleared as their holders are
9006 // allocated, so every one is an allocation or null (if info itself failed, only the placeholder block exists)
9007 #define STBIR__NEXT_PTR( ptr, size, ntype ) if ( alloced ) { void * p = STBIR_MALLOC( size, user_data); if ( p == 0 ) { if ( info ) stbir__free_internal_mem( info ); else STBIR_FREE( alloced, user_data ); return 0; } (ptr) = (ntype*)p; }
9008#else
9009 #define STBIR__NEXT_PTR( ptr, size, ntype ) advance_mem = (void*) ( ( ((size_t)advance_mem) + 15 ) & ~15 ); if ( alloced ) ptr = (ntype*)advance_mem; advance_mem = (char*)(((size_t)advance_mem) + (size));
9010#endif
9011
9012 STBIR__NEXT_PTR( info, sizeof( stbir__info ), stbir__info );
9013 if ( info )
9014 {
9015 info->alloced_mem = alloced;
9016 info->user_data = user_data;
9017 info->splits = splits;
9018 info->alloc_ring_buffer_num_entries = (int)alloc_ring_buffer_num_entries;
9019 info->effective_channels = effective_channels;
9020 info->split_info = 0;
9021 info->column_slices = 0;
9022 info->horizontal.contributors = 0;
9023 info->horizontal.coefficients = 0;
9024 info->vertical.contributors = 0;
9025 info->vertical.coefficients = 0;
9026 }
9027
9028 STBIR__NEXT_PTR( info->split_info, sizeof( stbir__per_split_info ) * splits, stbir__per_split_info );
9029 #ifdef STBIR__SEPARATE_ALLOCATIONS
9030 if ( info )
9031 {
9032 for( i = 0 ; i < splits ; i++ )
9033 {
9034 info->split_info[i].ring_buffers = 0;
9035 info->split_info[i].decode_buffer_alloc = 0;
9036 info->split_info[i].vertical_buffer_alloc = 0;
9037 }
9038 }
9039 #endif
9040
9041 if ( info )
9042 {
9043 // initialize info fields
9044 info->alloced_total = alloced_total;
9045 info->channels = channels;
9046 info->offset_x = new_x;
9047 info->offset_y = new_y;
9048 info->ring_buffer_num_entries = 0;
9049 info->ring_buffer_length_bytes = sizes.ring_buffer_length_bytes;
9050 info->vertical_first = vertical_first;
9051
9052 info->input_pixel_layout_internal = input_pixel_layout;
9053 info->output_pixel_layout_internal = output_pixel_layout;
9054
9055 stbir__set_alpha_functions( info, alpha_weighting_type, input_pixel_layout, output_pixel_layout, (float) horizontal->scale_info.scale );
9056 }
9057
9058 // get all the per-split buffers (each with a page of slack, see stbir__4K_alias_skew_buffers)
9059 for( i = 0 ; i < splits ; i++ )
9060 {
9061 STBIR__NEXT_PTR( info->split_info[i].decode_buffer, sizes.decode_buffer_size + STBIR__4K_ALIAS_SKEW_SLACK, float );
9062
9063#ifdef STBIR__SEPARATE_ALLOCATIONS
9064
9065 #ifdef STBIR_SIMD8
9066 if ( ( info ) && ( effective_channels == 3 ) )
9067 ++info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer
9068 #endif
9069 if ( info )
9070 info->split_info[i].decode_buffer_alloc = info->split_info[i].decode_buffer;
9071
9072 STBIR__NEXT_PTR( info->split_info[i].ring_buffers, alloc_ring_buffer_num_entries * sizeof(float*), float* );
9073 {
9074 int j;
9075 if ( info )
9076 {
9077 for( j = 0 ; j < alloc_ring_buffer_num_entries ; j++ )
9078 info->split_info[i].ring_buffers[j] = 0;
9079 }
9080 for( j = 0 ; j < alloc_ring_buffer_num_entries ; j++ )
9081 {
9082 STBIR__NEXT_PTR( info->split_info[i].ring_buffers[j], sizes.ring_buffer_length_bytes, float );
9083 #ifdef STBIR_SIMD8
9084 if ( ( info ) && ( effective_channels == 3 ) )
9085 ++info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer
9086 #endif
9087 }
9088 }
9089#else
9090 STBIR__NEXT_PTR( info->split_info[i].ring_buffer, sizes.ring_buffer_size + STBIR__4K_ALIAS_SKEW_SLACK, float );
9091#endif
9092 STBIR__NEXT_PTR( info->split_info[i].vertical_buffer, sizes.vertical_buffer_size + STBIR__4K_ALIAS_SKEW_SLACK, float );
9093
9094 if ( info )
9095 {
9096 #ifndef STBIR__SEPARATE_ALLOCATIONS
9097 info->split_info[i].decode_buffer_alloc = info->split_info[i].decode_buffer;
9098 info->split_info[i].ring_buffer_alloc = info->split_info[i].ring_buffer;
9099 #endif
9100 info->split_info[i].vertical_buffer_alloc = info->split_info[i].vertical_buffer;
9101 }
9102 }
9103
9104 // the tables are allocated into info's samplers, and the samplers are built in the locals (copied into info below)
9105 STBIR__NEXT_PTR( info->horizontal.contributors, horizontal->contributors_size, stbir__contributors );
9106 STBIR__NEXT_PTR( info->horizontal.coefficients, horizontal->coefficients_size, float );
9107
9108 if ( !copy_horizontal ) // (sharing: the vertical reads the horizontal's tables, see below)
9109 {
9110 STBIR__NEXT_PTR( info->vertical.contributors, vertical->contributors_size, stbir__contributors );
9111 STBIR__NEXT_PTR( info->vertical.coefficients, vertical->coefficients_size, float );
9112 }
9113
9114 if ( info )
9115 {
9116 horizontal->contributors = info->horizontal.contributors;
9117 horizontal->coefficients = info->horizontal.coefficients;
9118 vertical->contributors = info->vertical.contributors;
9119 vertical->coefficients = info->vertical.coefficients;
9120 }
9121
9122 STBIR__NEXT_PTR( info->column_slices, sizeof( stbir__column_slice ) * sizes.num_column_slices, stbir__column_slice );
9123
9124 if ( info )
9125 {
9126 STBIR_PROFILE_BUILD_START( horizontal );
9127
9128 stbir__calculate_filters( horizontal, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO );
9129 stbir__select_horizontal_kernels( info, horizontal, effective_channels );
9130
9131 info->conservative_extents = *conservative;
9132
9133 // pack the horizontal coeffs into the column entries the kernels walk (the vertical's aren't packed: its kernels read exactly n0..n1)
9134 if ( horizontal->shared_period )
9135 horizontal->coefficient_width = stbir__pack_shared_coefficients( horizontal, info->conservative_extents.n0, info->conservative_extents.n1 );
9136 else
9137 horizontal->coefficient_width = stbir__pack_coefficients(horizontal->num_contributors, horizontal->contributors, horizontal->coefficients, horizontal->coefficient_width, horizontal->extent_info.widest, info->conservative_extents.n0, info->conservative_extents.n1 );
9138 horizontal->entry_floats = STBIR__COLUMN_ENTRY_FLOATS( horizontal->coefficient_width );
9139
9140 STBIR_MEMCPY( &info->horizontal, horizontal, sizeof( stbir__sampler ) );
9141
9142 // now that the contributors are final, work out exactly what each column slice decodes
9143 info->num_column_slices = sizes.num_column_slices;
9144 stbir__build_column_slices( info, sizes.slice_columns, sizes.slice_in_span );
9145
9146 STBIR_PROFILE_BUILD_END( horizontal );
9147
9148 STBIR_PROFILE_BUILD_START( vertical );
9149 if ( copy_horizontal )
9150 {
9151 // identical samplers: the vertical reads the horizontal's packed column entries (two ints, then the coefficients)
9152 STBIR_MEMCPY( vertical, horizontal, sizeof( stbir__sampler ) );
9153 vertical->coefficients += 2;
9154 vertical->coefficient_width = horizontal->entry_floats;
9155 }
9156 else
9157 stbir__calculate_filters( vertical, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO );
9158 STBIR_MEMCPY( &info->vertical, vertical, sizeof( stbir__sampler ) );
9159 STBIR_PROFILE_BUILD_END( vertical );
9160
9161 // the horizontal's contributors have been read for the last time (the slice table), so fill in the column entries'
9162 // advance and width (an advance that doesn't fit fails the build)
9163 if ( !stbir__fill_column_entries( &info->horizontal, effective_channels ) )
9164 {
9165 stbir__free_internal_mem( info );
9166 return 0;
9167 }
9168
9169 stbir__setup_split_grid( info, split_cols, split_rows );
9170
9171 // now we know precisely how many entries we need
9172 info->ring_buffer_num_entries = info->vertical.extent_info.widest;
9173
9174 // we never need more ring buffer entries than the scanlines we're outputting
9175 if ( ( !info->vertical.is_gather ) && ( info->ring_buffer_num_entries > conservative_split_output_size ) )
9176 info->ring_buffer_num_entries = conservative_split_output_size;
9177 STBIR_ASSERT( info->ring_buffer_num_entries <= info->alloc_ring_buffer_num_entries );
9178 }
9179 #undef STBIR__NEXT_PTR
9180
9181 // is this the first time through loop?
9182 if ( info == 0 )
9183 {
9184 alloced_total = ( 15 + (size_t)advance_mem );
9185 alloced = STBIR_MALLOC( alloced_total, user_data );
9186 if ( alloced == 0 )
9187 return 0;
9188 }
9189 else
9190 return info; // success
9191 }
9192}
9193
9194// 4K aliasing avoidance: each internal buffer has a page of slack and is placed half a page from the buffer
9195// that streams into it - input -> decode -> ring -> vertical buffer (horizontal first),
9196// input -> ring -> decode -> vertical buffer (vertical first).
9197static size_t stbir__4K_alias_place( void * alloc, size_t want )
9198{
9199 size_t have = ( (size_t)alloc ) & 4095;
9200 return ( ( want - have ) & 4095 ) & ~(size_t)15; // stays 16 byte aligned, within the slack
9201}
9202
9203static void stbir__4K_alias_skew_buffers( stbir__info const * info, stbir__per_split_info * split_info )
9204{
9205 size_t first = ( ( (size_t)info->input_data ) + 2048 ) & 4095; // the first internal buffer: half a page from the input row
9206 size_t second = ( first + 2048 ) & 4095; // and the next one, back at the input's offset
9207 size_t vertical_dest;
9208
9209 if ( info->vertical_first )
9210 {
9211 // decode: input -> ring. vertical: ring -> decode_buffer. horizontal: decode_buffer -> vertical_buffer. encode: vertical_buffer -> output
9212 #ifndef STBIR__SEPARATE_ALLOCATIONS
9213 split_info->ring_buffer = (float*) ( (char*)split_info->ring_buffer_alloc + stbir__4K_alias_place( split_info->ring_buffer_alloc, first ) );
9214 #endif
9215 vertical_dest = ( second + (size_t)stbir__4K_vertical_dest_shift ) & 4095;
9216 split_info->decode_buffer = (float*) ( (char*)split_info->decode_buffer_alloc + stbir__4K_alias_place( split_info->decode_buffer_alloc, vertical_dest ) );
9217 split_info->vertical_buffer = (float*) ( (char*)split_info->vertical_buffer_alloc + stbir__4K_alias_place( split_info->vertical_buffer_alloc, ( vertical_dest + 2048 ) & 4095 ) );
9218 }
9219 else
9220 {
9221 // decode: input -> decode_buffer. horizontal: decode_buffer -> ring. vertical: ring -> vertical_buffer. encode: vertical_buffer -> output
9222 split_info->decode_buffer = (float*) ( (char*)split_info->decode_buffer_alloc + stbir__4K_alias_place( split_info->decode_buffer_alloc, first ) );
9223 #ifndef STBIR__SEPARATE_ALLOCATIONS
9224 split_info->ring_buffer = (float*) ( (char*)split_info->ring_buffer_alloc + stbir__4K_alias_place( split_info->ring_buffer_alloc, second ) );
9225 #endif
9226 vertical_dest = ( first + (size_t)stbir__4K_vertical_dest_shift ) & 4095;
9227 split_info->vertical_buffer = (float*) ( (char*)split_info->vertical_buffer_alloc + stbir__4K_alias_place( split_info->vertical_buffer_alloc, vertical_dest ) );
9228 }
9229}
9230
9231static int stbir__perform_resize( stbir__info const * info, int split_start, int split_count )
9232{
9233 stbir__per_split_info * split_info = info->split_info + split_start;
9234 int b, i, can_merge;
9235
9236 for( i = 0 ; i < split_count ; i++ )
9237 stbir__4K_alias_skew_buffers( info, split_info + i );
9238
9239 STBIR_PROFILE_CLEAR_EXTRAS();
9240
9241 STBIR_PROFILE_FIRST_START( looping );
9242 // run the whole vertical loop once per column slice, so each pass only touches a cache sized slice of the buffers.
9243 // consecutive splits over the same slices are adjacent row bands and run as one band (one warm-up per slice) - except
9244 // when a scatter's ring was cut down to a band's rows: an input row can touch more output rows than that across
9245 // two bands, so those run one at a time
9246 can_merge = info->vertical.is_gather || ( info->ring_buffer_num_entries >= info->vertical.extent_info.widest );
9247 i = 0;
9248 while ( i < split_count )
9249 {
9250 int run = 1;
9251 while ( can_merge && ( i + run < split_count ) && ( split_info[i+run].first_column_slice == split_info[i].first_column_slice ) && ( split_info[i+run].num_column_slices == split_info[i].num_column_slices ) )
9252 ++run;
9253 STBIR_PROFILE_NEST_IN_FIRST( split_info + i );
9254 for( b = 0 ; b < split_info[i].num_column_slices ; b++ )
9255 {
9256 stbir__column_slice const * slice = info->column_slices + split_info[i].first_column_slice + b;
9257 if (info->vertical.is_gather)
9258 stbir__vertical_gather_loop( info, split_info + i, run, slice );
9259 else
9260 stbir__vertical_scatter_loop( info, split_info + i, run, slice );
9261 }
9262 i += run;
9263 }
9264 STBIR_PROFILE_END( looping );
9265
9266 return 1;
9267}
9268
9269static void stbir__update_info_from_resize( stbir__info * info, STBIR_RESIZE * resize )
9270{
9271 static stbir__decode_pixels_func * decode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]=
9272 {
9273 /* 1ch-4ch */ stbir__decode_uint8_srgb, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear,
9274 };
9275
9276 static stbir__decode_pixels_func * decode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]=
9277 {
9278 { /* RGBA */ stbir__decode_uint8_srgb4_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear },
9279 { /* BGRA */ stbir__decode_uint8_srgb4_linearalpha_BGRA, stbir__decode_uint8_srgb_BGRA, 0, stbir__decode_float_linear_BGRA, stbir__decode_half_float_linear_BGRA },
9280 { /* ARGB */ stbir__decode_uint8_srgb4_linearalpha_ARGB, stbir__decode_uint8_srgb_ARGB, 0, stbir__decode_float_linear_ARGB, stbir__decode_half_float_linear_ARGB },
9281 { /* ABGR */ stbir__decode_uint8_srgb4_linearalpha_ABGR, stbir__decode_uint8_srgb_ABGR, 0, stbir__decode_float_linear_ABGR, stbir__decode_half_float_linear_ABGR },
9282 { /* RA */ stbir__decode_uint8_srgb2_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear },
9283 { /* AR */ stbir__decode_uint8_srgb2_linearalpha_AR, stbir__decode_uint8_srgb_AR, 0, stbir__decode_float_linear_AR, stbir__decode_half_float_linear_AR },
9284 };
9285
9286 static stbir__decode_pixels_func * decode_simple_scaled_or_not[2][2]=
9287 {
9288 { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear },
9289 };
9290
9291 static stbir__decode_pixels_func * decode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]=
9292 {
9293 { /* RGBA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } },
9294 { /* BGRA */ { stbir__decode_uint8_linear_scaled_BGRA, stbir__decode_uint8_linear_BGRA }, { stbir__decode_uint16_linear_scaled_BGRA, stbir__decode_uint16_linear_BGRA } },
9295 { /* ARGB */ { stbir__decode_uint8_linear_scaled_ARGB, stbir__decode_uint8_linear_ARGB }, { stbir__decode_uint16_linear_scaled_ARGB, stbir__decode_uint16_linear_ARGB } },
9296 { /* ABGR */ { stbir__decode_uint8_linear_scaled_ABGR, stbir__decode_uint8_linear_ABGR }, { stbir__decode_uint16_linear_scaled_ABGR, stbir__decode_uint16_linear_ABGR } },
9297 { /* RA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } },
9298 { /* AR */ { stbir__decode_uint8_linear_scaled_AR, stbir__decode_uint8_linear_AR }, { stbir__decode_uint16_linear_scaled_AR, stbir__decode_uint16_linear_AR } }
9299 };
9300
9301 static stbir__encode_pixels_func * encode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]=
9302 {
9303 /* 1ch-4ch */ stbir__encode_uint8_srgb, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear,
9304 };
9305
9306 static stbir__encode_pixels_func * encode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]=
9307 {
9308 { /* RGBA */ stbir__encode_uint8_srgb4_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear },
9309 { /* BGRA */ stbir__encode_uint8_srgb4_linearalpha_BGRA, stbir__encode_uint8_srgb_BGRA, 0, stbir__encode_float_linear_BGRA, stbir__encode_half_float_linear_BGRA },
9310 { /* ARGB */ stbir__encode_uint8_srgb4_linearalpha_ARGB, stbir__encode_uint8_srgb_ARGB, 0, stbir__encode_float_linear_ARGB, stbir__encode_half_float_linear_ARGB },
9311 { /* ABGR */ stbir__encode_uint8_srgb4_linearalpha_ABGR, stbir__encode_uint8_srgb_ABGR, 0, stbir__encode_float_linear_ABGR, stbir__encode_half_float_linear_ABGR },
9312 { /* RA */ stbir__encode_uint8_srgb2_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear },
9313 { /* AR */ stbir__encode_uint8_srgb2_linearalpha_AR, stbir__encode_uint8_srgb_AR, 0, stbir__encode_float_linear_AR, stbir__encode_half_float_linear_AR }
9314 };
9315
9316 static stbir__encode_pixels_func * encode_simple_scaled_or_not[2][2]=
9317 {
9318 { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear },
9319 };
9320
9321 static stbir__encode_pixels_func * encode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]=
9322 {
9323 { /* RGBA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } },
9324 { /* BGRA */ { stbir__encode_uint8_linear_scaled_BGRA, stbir__encode_uint8_linear_BGRA }, { stbir__encode_uint16_linear_scaled_BGRA, stbir__encode_uint16_linear_BGRA } },
9325 { /* ARGB */ { stbir__encode_uint8_linear_scaled_ARGB, stbir__encode_uint8_linear_ARGB }, { stbir__encode_uint16_linear_scaled_ARGB, stbir__encode_uint16_linear_ARGB } },
9326 { /* ABGR */ { stbir__encode_uint8_linear_scaled_ABGR, stbir__encode_uint8_linear_ABGR }, { stbir__encode_uint16_linear_scaled_ABGR, stbir__encode_uint16_linear_ABGR } },
9327 { /* RA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } },
9328 { /* AR */ { stbir__encode_uint8_linear_scaled_AR, stbir__encode_uint8_linear_AR }, { stbir__encode_uint16_linear_scaled_AR, stbir__encode_uint16_linear_AR } }
9329 };
9330
9331 stbir__decode_pixels_func * decode_pixels = 0;
9332 stbir__encode_pixels_func * encode_pixels = 0;
9333 stbir_datatype input_type, output_type;
9334 int non_scaled;
9335
9336 input_type = resize->input_data_type;
9337 output_type = resize->output_data_type;
9338 info->input_data = resize->input_pixels;
9339 info->input_stride_bytes = resize->input_stride_in_bytes;
9340 info->output_stride_bytes = resize->output_stride_in_bytes;
9341
9342 // if we're completely point sampling, then we can turn off SRGB
9343 if ( ( info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( info->vertical.filter_enum == STBIR_FILTER_POINT_SAMPLE ) )
9344 {
9345 if ( ( ( input_type == STBIR_TYPE_UINT8_SRGB ) || ( input_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) &&
9346 ( ( output_type == STBIR_TYPE_UINT8_SRGB ) || ( output_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) )
9347 {
9348 input_type = STBIR_TYPE_UINT8;
9349 output_type = STBIR_TYPE_UINT8;
9350 }
9351 }
9352
9353 // recalc the output and input strides (zero means packed - the whole image's width, the sub rect is a region of it)
9354 if ( info->input_stride_bytes == 0 )
9355 info->input_stride_bytes = (stbir_int64)info->channels * info->horizontal.scale_info.input_full_size * stbir__type_size[input_type];
9356
9357 if ( info->output_stride_bytes == 0 )
9358 info->output_stride_bytes = (stbir_int64)info->channels * resize->output_w * stbir__type_size[output_type];
9359
9360 // calc offset
9361 info->output_data = ( (char*) resize->output_pixels ) + (ptrdiff_t)( info->offset_y * info->output_stride_bytes ) + ( (size_t) info->offset_x * (size_t) info->channels * (size_t) stbir__type_size[output_type] );
9362
9363 info->in_pixels_cb = resize->input_cb;
9364 info->user_data = resize->user_data;
9365 info->out_pixels_cb = resize->output_cb;
9366
9367 // check if we can run unscaled - 0-255.0/0-65535.0 instead of 0-1.0 (which is a tiny bit faster when doing linear 8->8 or 16->16)
9368 // don't short circuit when alpha weighting (get everything to 0-1.0 as usual)
9369 non_scaled = 0;
9370 if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) )
9371 if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) )
9372 non_scaled = 1;
9373
9374 // setup the input format converters
9375 if ( ( input_type == STBIR_TYPE_UINT8 ) || ( input_type == STBIR_TYPE_UINT16 ) )
9376 {
9377 if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL )
9378 decode_pixels = decode_simple_scaled_or_not[ input_type == STBIR_TYPE_UINT16 ][ non_scaled ];
9379 else
9380 decode_pixels = decode_alphas_scaled_or_not[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type == STBIR_TYPE_UINT16 ][ non_scaled ];
9381 }
9382 else
9383 {
9384 if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL )
9385 decode_pixels = decode_simple[ input_type - STBIR_TYPE_UINT8_SRGB ];
9386 else
9387 decode_pixels = decode_alphas[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type - STBIR_TYPE_UINT8_SRGB ];
9388 }
9389
9390 // setup the output format converters
9391 if ( ( output_type == STBIR_TYPE_UINT8 ) || ( output_type == STBIR_TYPE_UINT16 ) )
9392 {
9393 if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL )
9394 encode_pixels = encode_simple_scaled_or_not[ output_type == STBIR_TYPE_UINT16 ][ non_scaled ];
9395 else
9396 encode_pixels = encode_alphas_scaled_or_not[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type == STBIR_TYPE_UINT16 ][ non_scaled ];
9397 }
9398 else
9399 {
9400 if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL )
9401 encode_pixels = encode_simple[ output_type - STBIR_TYPE_UINT8_SRGB ];
9402 else
9403 encode_pixels = encode_alphas[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type - STBIR_TYPE_UINT8_SRGB ];
9404 }
9405
9406 info->input_type = input_type;
9407 info->output_type = output_type;
9408 info->decode_pixels = decode_pixels;
9409 info->encode_pixels = encode_pixels;
9410
9411 // the input callback's temp buffer sits inside the decode buffer, pushed forward so the decoder reads ahead of
9412 // its writes - except plain float, which is a no-op decode when the temp is exactly the decode target
9413 info->input_callback_offset_bytes = ( decode_pixels == stbir__decode_float_linear ) ? 0 : ( sizeof(float) * STBIR_INPUT_CALLBACK_PADDING );
9414}
9415
9416static void stbir__clip( int * outx, int * outsubw, int outw, stbir_real * u0, stbir_real * u1 )
9417{
9418 stbir_real per, adj;
9419 int over;
9420
9421 // do left/top edge
9422 if ( *outx < 0 )
9423 {
9424 per = ( (stbir_real)*outx ) / ( (stbir_real)*outsubw ); // is negative
9425 adj = per * ( *u1 - *u0 );
9426 *u0 -= adj; // increases u0
9427 *outsubw += *outx; // and drop the clipped off pixels from the output count
9428 *outx = 0;
9429 }
9430
9431 // do right/bot edge
9432 over = outw - ( *outx + *outsubw );
9433 if ( over < 0 )
9434 {
9435 per = ( (stbir_real)over ) / ( (stbir_real)*outsubw ); // is negative
9436 adj = per * ( *u1 - *u0 );
9437 *u1 += adj; // decrease u1
9438 *outsubw = outw - *outx;
9439 }
9440}
9441
9442static stbir_uint64 stbir__gcd( stbir_uint64 a, stbir_uint64 b )
9443{
9444 while ( b )
9445 {
9446 stbir_uint64 remainder = a % b;
9447 a = b;
9448 b = remainder;
9449 }
9450 return a;
9451}
9452
9453// The simplest fraction within tolerance of f (f>0): its continued fraction's first convergent that is, in 31 bits (or 0).
9454static int stbir__real_to_fraction( stbir_real f, stbir_real tolerance, stbir_uint32 * out_numer, stbir_uint32 * out_denom )
9455{
9456 stbir_real rem = f;
9457 stbir_uint64 numer = 1, denom = 0, numer_last = 0, denom_last = 1; // the convergents numer / denom
9458 int i;
9459
9460 if ( f <= (stbir_real) 0.0 )
9461 return 0;
9462 for( i = 0 ; i < (int) sizeof( stbir_real ) * 8 ; i++ ) // (a term is at least a bit of f)
9463 {
9464 stbir_uint64 term = (stbir_uint64) rem; // the floor (rem is positive)
9465 stbir_uint64 next;
9466 stbir_real frac, err;
9467
9468 if ( ( term > 0x7fffffff ) || ( ( term * numer + numer_last ) > 0x7fffffff ) || ( ( term * denom + denom_last ) > 0x7fffffff ) )
9469 return 0;
9470 next = term * numer + numer_last; numer_last = numer; numer = next;
9471 next = term * denom + denom_last; denom_last = denom; denom = next;
9472
9473 err = (stbir_real) numer / (stbir_real) denom - f;
9474 if ( err < (stbir_real) 0.0 )
9475 err = -err;
9476 if ( err <= tolerance )
9477 {
9478 *out_numer = (stbir_uint32) numer;
9479 *out_denom = (stbir_uint32) denom;
9480 return 1;
9481 }
9482
9483 frac = rem - (stbir_real) term;
9484 if ( frac <= (stbir_real) 0.0 )
9485 return 0; // (the fraction is exactly f, and still not within tolerance - can't happen for a tolerance >= 0)
9486 rem = (stbir_real) 1.0 / frac;
9487 }
9488 return 0;
9489}
9490
9491// try to find approximation that stays under the drift value: the far edge of the output moves by less than
9492// 1 / STBIR__POLYPHASE_DRIFT_DIV of an output pixel
9493#define STBIR__POLYPHASE_DRIFT_DIV 8192
9494static void stbir__approximate_polyphase( stbir_uint64 numer, stbir_uint64 denom, stbir_uint64 outputs, stbir_uint64 * out_numer, stbir_uint64 * out_denom, int * out_drift_in_pixels )
9495{
9496 stbir_uint64 top = numer, bot = denom;
9497 stbir_uint64 p = 1, q = 0, p_last = 0, q_last = 1;
9498
9499 while ( bot )
9500 {
9501 stbir_uint64 est = top / bot;
9502 stbir_uint64 remainder = top % bot;
9503 stbir_uint64 next, pd, qn, diff;
9504
9505 top = bot;
9506 bot = remainder;
9507
9508 // the next convergent p / q
9509 next = est * p + p_last;
9510 p_last = p;
9511 p = next;
9512
9513 next = est * q + q_last;
9514 q_last = q;
9515 q = next;
9516
9517 // no simpler fraction is close enough (the convergents only grow from here)
9518 if ( p >= outputs )
9519 return;
9520
9521 // its drift at the far edge, in output pixels: outputs x | p D - q N | / ( q N )
9522 pd = p * denom;
9523 qn = q * numer;
9524 diff = ( pd > qn ) ? pd - qn : qn - pd;
9525 if ( diff <= qn / ( outputs * STBIR__POLYPHASE_DRIFT_DIV ) )
9526 {
9527 // that drift at the far edge in input pixels (times denom / numer), rounded up - for the conservative range
9528 stbir_real drift_in = (stbir_real)outputs * (stbir_real)diff * (stbir_real)denom / ( (stbir_real)qn * (stbir_real)numer );
9529 *out_numer = p;
9530 *out_denom = q;
9531 *out_drift_in_pixels = (int)drift_in + 1;
9532 return;
9533 }
9534 }
9535}
9536
9537// The scale as a fraction: output_sub / ( input_full x ( s1 - s0 ) ), with s1 - s0 = ( n1 - n0 ) / d - exact if possible.
9538// Returns 0 when there is no fraction that fits in int.
9539static int stbir__scale_to_rational( int output_sub_range, int input_full_range, stbir_real input_s, int s_n0, int s_n1, int s_d, stbir_uint32 * out_numer, stbir_uint32 * out_denom, int * out_approximated_drift )
9540{
9541 stbir_uint64 numer, denom, g;
9542 stbir_real scale, err;
9543
9544 *out_numer = 0; // (not a fraction: the fields are read before the flag in places, so never leave them unset)
9545 *out_denom = 0;
9546 *out_approximated_drift = 0;
9547
9548 if ( ( s_d > 0 ) && ( s_n1 > s_n0 ) )
9549 {
9550 numer = (stbir_uint64)output_sub_range * (stbir_uint64)s_d;
9551 denom = (stbir_uint64)input_full_range * (stbir_uint64)( s_n1 - s_n0 );
9552 g = stbir__gcd( numer, denom );
9553 numer /= g; denom /= g;
9554 if ( ( numer <= 0x7fffffff ) && ( denom <= 0x7fffffff ) )
9555 {
9556 if ( numer >= (stbir_uint64)output_sub_range )
9557 stbir__approximate_polyphase( numer, denom, (stbir_uint64)output_sub_range, &numer, &denom, out_approximated_drift );
9558 *out_numer = (stbir_uint32) numer;
9559 *out_denom = (stbir_uint32) denom;
9560 return 1;
9561 }
9562 }
9563
9564 scale = (stbir_real)output_sub_range / ( (stbir_real)input_full_range * input_s );
9565 if ( !stbir__real_to_fraction( scale, scale / ( (stbir_real)output_sub_range * (stbir_real)STBIR__POLYPHASE_DRIFT_DIV ), out_numer, out_denom ) )
9566 return 0;
9567 err = (stbir_real)*out_numer / (stbir_real)*out_denom - scale;
9568 if ( err < (stbir_real) 0.0 )
9569 err = -err;
9570 if ( err > (stbir_real) 0.0 )
9571 *out_approximated_drift = (int)( (stbir_real)output_sub_range * err / ( scale * scale ) ) + 1; // the far edge's drift: output pixels x ( err / scale ), in input pixels ( / scale )
9572 return 1;
9573}
9574
9575// clip the input area to the image (and adjust the output area), the counterpart of stbir__clip: the output pixels whose
9576// centers land outside the image are dropped from the ends, so the ones left sample exactly where they did
9577static void stbir__clip_input( int * outx, int * outsubw, stbir_real * u0, stbir_real * u1 )
9578{
9579 stbir_real per_pixel = ( *u1 - *u0 ) / ( (stbir_real)*outsubw ); // input area per output pixel
9580 int drop;
9581
9582 // do left/top edge
9583 if ( *u0 < 0 )
9584 {
9585 drop = stbir__ceil_to_int( -*u0 / per_pixel - (stbir_real)0.5 ); // pixels centered before the image (one right on the edge stays)
9586 if ( drop > *outsubw )
9587 drop = *outsubw;
9588 if ( drop > 0 )
9589 {
9590 *u0 += (stbir_real)drop * per_pixel; // increases u0
9591 *outx += drop;
9592 *outsubw -= drop;
9593 }
9594 }
9595
9596 // do right/bot edge
9597 if ( *u1 > 1 )
9598 {
9599 drop = stbir__ceil_to_int( ( *u1 - 1 ) / per_pixel - (stbir_real)0.5 ); // pixels centered past the image
9600 if ( drop > *outsubw )
9601 drop = *outsubw;
9602 if ( drop > 0 )
9603 {
9604 *u1 -= (stbir_real)drop * per_pixel; // decreases u1
9605 *outsubw -= drop;
9606 }
9607 }
9608}
9609
9610static int stbir__calculate_region_transform( stbir__scale_info * scale_info, int output_full_range, int * output_offset, int output_sub_range, int input_full_range, stbir_real input_s0, stbir_real input_s1, int s_n0, int s_n1, int s_d )
9611{
9612 stbir_real output_range, input_range, output_s, input_s, input_pixels, ratio, unclipped_input_s;
9613 int unclipped_output_sub_range = output_sub_range;
9614 int unclipped_output_offset = *output_offset;
9615
9616 input_s = input_s1 - input_s0;
9617 unclipped_input_s = input_s;
9618
9619 // null area (a negative size is an empty one)
9620 if ( ( output_full_range <= 0 ) || ( input_full_range <= 0 ) ||
9621 ( output_sub_range <= 0 ) || ( input_s <= (stbir_real)0.0 ) )
9622 return 0;
9623
9624 // are either of the ranges completely out of bounds?
9625 if ( ( *output_offset >= output_full_range ) || ( ( *output_offset + output_sub_range ) <= 0 ) || ( input_s0 >= (stbir_real)1.0 ) || ( input_s1 <= (stbir_real)0.0 ) )
9626 return 0;
9627
9628 output_range = (stbir_real)output_full_range;
9629 input_range = (stbir_real)input_full_range;
9630
9631 output_s = ( (stbir_real)output_sub_range) / output_range;
9632
9633 // figure out the scaling to use
9634 ratio = output_s / input_s;
9635
9636 // save scale before clipping: the output pixels over the input pixels the region spans, and its inverse as the same
9637 // two numbers divided the other way - one rounding each, and since a division is monotonic the two agree about 1
9638 // (a scale that rounds to exactly 1 can't have an inverse over 1, which would put a ratio over 1 to the kernels)
9639 input_pixels = input_range * input_s;
9640 scale_info->scale = (stbir_real)output_sub_range / input_pixels;
9641 scale_info->inv_scale = input_pixels / (stbir_real)output_sub_range;
9642
9643 // clip output area to left/right output edges (and adjust input area)
9644 stbir__clip( output_offset, &output_sub_range, output_full_range, &input_s0, &input_s1 );
9645
9646 // clip input area to the image (and adjust output area)
9647 stbir__clip_input( output_offset, &output_sub_range, &input_s0, &input_s1 );
9648
9649 // recalc input area
9650 input_s = input_s1 - input_s0;
9651
9652 // after clipping do we have zero input area?
9653 if ( ( input_s <= (stbir_real)0.0 ) || ( output_sub_range <= 0 ) )
9654 return 0;
9655
9656 // calculate and store the starting source offsets in output pixel space
9657 scale_info->pixel_shift = input_s0 * ratio * output_range;
9658
9659 scale_info->scale_is_rational = stbir__scale_to_rational( unclipped_output_sub_range, input_full_range, unclipped_input_s, s_n0, s_n1, s_d, &scale_info->scale_numerator, &scale_info->scale_denominator, &scale_info->approximated_drift );
9660
9661 // an exact fraction gives the scale itself - correctly rounded, so an exact ratio's filter widths ( ceil( 2 support / scale ) )
9662 // don't pick up an extra tap from the region math landing an ulp under the integer, and a 1:1 is exactly 1 (the up / down
9663 // decision). an approximated fraction leaves the real scale: it is what the kernels see, and the decision has to agree with it
9664 if ( ( scale_info->scale_is_rational ) && ( !scale_info->approximated_drift ) )
9665 {
9666 scale_info->scale = (stbir_real) scale_info->scale_numerator / (stbir_real) scale_info->scale_denominator;
9667 scale_info->inv_scale = (stbir_real) scale_info->scale_denominator / (stbir_real) scale_info->scale_numerator;
9668 }
9669
9670 // exact tap positions when the shift is an integer ( n0 x output_sub / ( n1 - n0 ), plus the clip ) and the fraction wasn't approximated.
9671 // the numerators are ( 2o + 1 + 2 shift ) D and ( 2i + 1 ) N: with sizes up to 2^30 (the resizer's limit) and the
9672 // fraction's terms below 2^30 they stay below 2^62
9673 scale_info->positions_exact = 0;
9674 scale_info->shift_pixels = 0;
9675 if ( ( scale_info->scale_is_rational ) && ( !scale_info->approximated_drift ) && ( s_d > 0 ) && ( ( ( (stbir_int64)s_n0 * unclipped_output_sub_range ) % ( s_n1 - s_n0 ) ) == 0 ) &&
9676 ( scale_info->scale_numerator < ( 1 << 30 ) ) && ( scale_info->scale_denominator < ( 1 << 30 ) ) )
9677 {
9678 stbir_int64 shift = ( (stbir_int64)s_n0 * unclipped_output_sub_range ) / ( s_n1 - s_n0 ) + ( *output_offset - unclipped_output_offset );
9679 if ( ( shift > -( 1 << 30 ) ) && ( shift < ( 1 << 30 ) ) ) // the shift is in output pixels too (an output tile far past the image's own size)
9680 {
9681 scale_info->shift_pixels = (int) shift;
9682 scale_info->tap_scale_in = (stbir_real) 1.0 / ( (stbir_real) 2.0 * (stbir_real) scale_info->scale_numerator );
9683 scale_info->tap_scale_out = (stbir_real) 1.0 / ( (stbir_real) 2.0 * (stbir_real) scale_info->scale_denominator );
9684 scale_info->pixel_shift = (stbir_real) shift;
9685 scale_info->positions_exact = 1;
9686 }
9687 }
9688
9689 scale_info->input_full_size = input_full_range;
9690 scale_info->output_sub_size = output_sub_range;
9691
9692 return 1;
9693}
9694
9695
9696static void stbir__init_and_set_layout( STBIR_RESIZE * resize, stbir_pixel_layout pixel_layout, stbir_datatype data_type )
9697{
9698 resize->input_cb = 0;
9699 resize->output_cb = 0;
9700 resize->user_data = resize;
9701 resize->samplers = 0;
9702 resize->called_alloc = 0;
9703 resize->horizontal_filter = STBIR_FILTER_DEFAULT;
9704 resize->horizontal_filter_kernel = 0; resize->horizontal_filter_support = 0; resize->horizontal_filter_kernel_new = 0; resize->horizontal_filter_support_new = 0; resize->horizontal_filter_radius = 0;
9705 resize->vertical_filter = STBIR_FILTER_DEFAULT;
9706 resize->vertical_filter_kernel = 0; resize->vertical_filter_support = 0; resize->vertical_filter_kernel_new = 0; resize->vertical_filter_support_new = 0; resize->vertical_filter_radius = 0;
9707 resize->horizontal_edge = STBIR_EDGE_CLAMP;
9708 resize->vertical_edge = STBIR_EDGE_CLAMP;
9709 resize->input_s0 = 0; resize->input_t0 = 0; resize->input_s1 = 1; resize->input_t1 = 1;
9710 resize->input_s_n0 = 0; resize->input_s_n1 = 1; resize->input_s_d = 1;
9711 resize->input_t_n0 = 0; resize->input_t_n1 = 1; resize->input_t_d = 1;
9712 resize->output_subx = 0; resize->output_suby = 0; resize->output_subw = resize->output_w; resize->output_subh = resize->output_h;
9713 resize->input_data_type = data_type;
9714 resize->output_data_type = data_type;
9715 resize->input_pixel_layout_public = pixel_layout;
9716 resize->output_pixel_layout_public = pixel_layout;
9717 resize->needs_rebuild = 1;
9718}
9719
9720STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize,
9721 const void *input_pixels, int input_w, int input_h, stbir_int64 input_stride_in_bytes, // stride can be zero
9722 void *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes, // stride can be zero
9723 stbir_pixel_layout pixel_layout, stbir_datatype data_type )
9724{
9725 resize->input_pixels = input_pixels;
9726 resize->input_w = input_w;
9727 resize->input_h = input_h;
9728 resize->input_stride_in_bytes = input_stride_in_bytes;
9729 resize->output_pixels = output_pixels;
9730 resize->output_w = output_w;
9731 resize->output_h = output_h;
9732 resize->output_stride_in_bytes = output_stride_in_bytes;
9733 resize->fast_alpha = 0;
9734
9735 stbir__init_and_set_layout( resize, pixel_layout, data_type );
9736}
9737
9738// You can update parameters any time after resize_init
9739STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ) // by default, datatype from resize_init
9740{
9741 resize->input_data_type = input_type;
9742 resize->output_data_type = output_type;
9743 if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
9744 stbir__update_info_from_resize( resize->samplers, resize );
9745}
9746
9747STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ) // no callbacks by default
9748{
9749 resize->input_cb = input_cb;
9750 resize->output_cb = output_cb;
9751
9752 if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
9753 {
9754 resize->samplers->in_pixels_cb = input_cb;
9755 resize->samplers->out_pixels_cb = output_cb;
9756 }
9757}
9758
9759STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ) // pass back STBIR_RESIZE* by default
9760{
9761 resize->user_data = user_data;
9762 if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
9763 resize->samplers->user_data = user_data;
9764}
9765
9766STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, stbir_int64 input_stride_in_bytes, void * output_pixels, stbir_int64 output_stride_in_bytes )
9767{
9768 resize->input_pixels = input_pixels;
9769 resize->input_stride_in_bytes = input_stride_in_bytes;
9770 resize->output_pixels = output_pixels;
9771 resize->output_stride_in_bytes = output_stride_in_bytes;
9772 if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
9773 stbir__update_info_from_resize( resize->samplers, resize );
9774}
9775
9776
9777STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ) // CLAMP by default
9778{
9779 resize->horizontal_edge = horizontal_edge;
9780 resize->vertical_edge = vertical_edge;
9781 resize->needs_rebuild = 1;
9782 return 1;
9783}
9784
9785STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ) // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default
9786{
9787 resize->horizontal_filter = horizontal_filter;
9788 resize->vertical_filter = vertical_filter;
9789 resize->needs_rebuild = 1;
9790 return 1;
9791}
9792
9793STBIRDEF int stbir_set_filter_callbacks_new( STBIR_RESIZE * resize, stbir__kernel_callback_new * horizontal_filter, stbir__support_callback_new * horizontal_support, stbir__radius_callback_new * horizontal_radius, stbir__kernel_callback_new * vertical_filter, stbir__support_callback_new * vertical_support, stbir__radius_callback_new * vertical_radius )
9794{
9795 // a filter's extent is given one way: a support (filter pixels) or a radius (input pixels), not both
9796 if ( ( horizontal_support && horizontal_radius ) || ( vertical_support && vertical_radius ) )
9797 return 0;
9798 resize->horizontal_filter_kernel_new = horizontal_filter;
9799 resize->horizontal_filter_support_new = horizontal_support;
9800 resize->horizontal_filter_radius = horizontal_radius;
9801 resize->horizontal_filter_kernel = 0; // (the old form's, if it was set before)
9802 resize->horizontal_filter_support = 0;
9803 resize->vertical_filter_kernel_new = vertical_filter;
9804 resize->vertical_filter_support_new = vertical_support;
9805 resize->vertical_filter_radius = vertical_radius;
9806 resize->vertical_filter_kernel = 0;
9807 resize->vertical_filter_support = 0;
9808 resize->needs_rebuild = 1;
9809 return 1;
9810}
9811
9812// the old form: the float kernels and supports are kept apart and the sampler calls them through a conversion
9813STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support )
9814{
9815 resize->horizontal_filter_kernel = horizontal_filter;
9816 resize->horizontal_filter_support = horizontal_support;
9817 resize->horizontal_filter_kernel_new = 0;
9818 resize->horizontal_filter_support_new = 0;
9819 resize->horizontal_filter_radius = 0;
9820 resize->vertical_filter_kernel = vertical_filter;
9821 resize->vertical_filter_support = vertical_support;
9822 resize->vertical_filter_kernel_new = 0;
9823 resize->vertical_filter_support_new = 0;
9824 resize->vertical_filter_radius = 0;
9825 resize->needs_rebuild = 1;
9826 return 1;
9827}
9828
9829STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ) // sets new pixel layouts
9830{
9831 resize->input_pixel_layout_public = input_pixel_layout;
9832 resize->output_pixel_layout_public = output_pixel_layout;
9833 resize->needs_rebuild = 1;
9834 return 1;
9835}
9836
9837
9838STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ) // sets alpha speed
9839{
9840 resize->fast_alpha = non_pma_alpha_speed_over_quality;
9841 resize->needs_rebuild = 1;
9842 return 1;
9843}
9844
9845STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, stbir_real s0, stbir_real t0, stbir_real s1, stbir_real t1 ) // sets input region (full region by default)
9846{
9847 resize->input_s0 = s0;
9848 resize->input_t0 = t0;
9849 resize->input_s1 = s1;
9850 resize->input_t1 = t1;
9851 resize->input_s_d = 0; // not in pixels: the fractions are worked out from the reals at build time
9852 resize->input_t_d = 0;
9853 resize->needs_rebuild = 1;
9854
9855 // are we inbounds? (a zero or negative area, or a region wholly past the image, is empty)
9856 if ( ( s1 <= (stbir_real)0.0 ) || ( s1 <= s0 ) ||
9857 ( t1 <= (stbir_real)0.0 ) || ( t1 <= t0 ) ||
9858 ( s0 >= (stbir_real)1.0 ) ||
9859 ( t0 >= (stbir_real)1.0 ) )
9860 return 0;
9861
9862 return 1;
9863}
9864
9865STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets the output sub rect, in output pixels (the whole output by default)
9866{
9867 resize->output_subx = subx;
9868 resize->output_suby = suby;
9869 resize->output_subw = subw;
9870 resize->output_subh = subh;
9871 resize->needs_rebuild = 1;
9872
9873 // is there anything in it? (a negative size is an empty rect - the resize then has nothing to do)
9874 if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw <= 0 ) || ( subh <= 0 ) )
9875 return 0;
9876
9877 return 1;
9878}
9879
9880STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets both regions (full regions by default)
9881{
9882 stbir_real s0, t0, s1, t1;
9883
9884 s0 = ( (stbir_real)subx ) / ( (stbir_real)resize->output_w );
9885 t0 = ( (stbir_real)suby ) / ( (stbir_real)resize->output_h );
9886 s1 = ( (stbir_real)(subx+subw) ) / ( (stbir_real)resize->output_w );
9887 t1 = ( (stbir_real)(suby+subh) ) / ( (stbir_real)resize->output_h );
9888
9889 resize->input_s0 = s0;
9890 resize->input_t0 = t0;
9891 resize->input_s1 = s1;
9892 resize->input_t1 = t1;
9893 resize->input_s_n0 = subx;
9894 resize->input_s_n1 = subx + subw;
9895 resize->input_s_d = resize->output_w;
9896 resize->input_t_n0 = suby;
9897 resize->input_t_n1 = suby + subh;
9898 resize->input_t_d = resize->output_h;
9899 resize->output_subx = subx;
9900 resize->output_suby = suby;
9901 resize->output_subw = subw;
9902 resize->output_subh = subh;
9903 resize->needs_rebuild = 1;
9904
9905 // is there anything in it? (a negative size is an empty rect - the resize then has nothing to do)
9906 if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw <= 0 ) || ( subh <= 0 ) )
9907 return 0;
9908
9909 return 1;
9910}
9911
9912static int stbir__perform_build( STBIR_RESIZE * resize, int splits )
9913{
9914 stbir__contributors conservative = { 0, 0 };
9915 stbir__sampler horizontal, vertical;
9916 int new_output_subx, new_output_suby;
9917 stbir__info * out_info;
9918 #ifdef STBIR_PROFILE
9919 stbir__info profile_infod; // used to contain building profile info before everything is allocated
9920 stbir__info * profile_info = &profile_infod;
9921 #endif
9922
9923 // have we already built the samplers?
9924 if ( resize->samplers )
9925 return 0;
9926
9927 #define STBIR_RETURN_ERROR_AND_ASSERT( exp ) STBIR_ASSERT( !(exp) ); if (exp) return 0;
9928 STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->horizontal_filter >= STBIR_FILTER_OTHER)
9929 STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->vertical_filter >= STBIR_FILTER_OTHER)
9930 #undef STBIR_RETURN_ERROR_AND_ASSERT
9931
9932 if ( splits <= 0 )
9933 return 0;
9934
9935 STBIR_PROFILE_BUILD_FIRST_START( build );
9936
9937 new_output_subx = resize->output_subx;
9938 new_output_suby = resize->output_suby;
9939
9940 // do horizontal clip and scale calcs (an empty region - zero or negative sizes, nothing in bounds - fails the build)
9941 if ( !stbir__calculate_region_transform( &horizontal.scale_info, resize->output_w, &new_output_subx, resize->output_subw, resize->input_w, resize->input_s0, resize->input_s1, resize->input_s_n0, resize->input_s_n1, resize->input_s_d ) )
9942 return 0;
9943
9944 // do vertical clip and scale calcs
9945 if ( !stbir__calculate_region_transform( &vertical.scale_info, resize->output_h, &new_output_suby, resize->output_subh, resize->input_h, resize->input_t0, resize->input_t1, resize->input_t_n0, resize->input_t_n1, resize->input_t_d ) )
9946 return 0;
9947
9948 // if nothing to do, just return
9949 if ( ( horizontal.scale_info.output_sub_size == 0 ) || ( vertical.scale_info.output_sub_size == 0 ) )
9950 return 0;
9951
9952 stbir__set_sampler(&horizontal, resize->horizontal_filter, resize->horizontal_filter_kernel_new, resize->horizontal_filter_support_new, resize->horizontal_filter_radius, resize->horizontal_filter_kernel, resize->horizontal_filter_support, resize->horizontal_edge, &horizontal.scale_info, 1, resize->user_data );
9953 stbir__get_conservative_extents( &horizontal, &conservative, resize->user_data );
9954 stbir__set_sampler(&vertical, resize->vertical_filter, resize->vertical_filter_kernel_new, resize->vertical_filter_support_new, resize->vertical_filter_radius, resize->vertical_filter_kernel, resize->vertical_filter_support, resize->vertical_edge, &vertical.scale_info, 0, resize->user_data );
9955
9956 // the split count can come down in the build (stbir__choose_split_grid): the caller gets the count it can use
9957
9958 STBIR_PROFILE_BUILD_START( alloc );
9959 out_info = stbir__alloc_internal_mem_and_build_samplers( &horizontal, &vertical, &conservative, resize->input_pixel_layout_public, resize->output_pixel_layout_public, splits, new_output_subx, new_output_suby, resize->fast_alpha, stbir__pixel_channels[ stbir__pixel_layout_convert_public_to_internal[ resize->input_pixel_layout_public ] ] * stbir__type_size[ resize->input_data_type ], resize->user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO );
9960 STBIR_PROFILE_BUILD_END( alloc );
9961 STBIR_PROFILE_BUILD_END( build );
9962
9963 if ( out_info )
9964 {
9965 splits = out_info->splits;
9966 resize->splits = splits;
9967 resize->samplers = out_info;
9968 resize->needs_rebuild = 0;
9969 #ifdef STBIR_PROFILE
9970 STBIR_MEMCPY( &out_info->profile, &profile_infod.profile, sizeof( out_info->profile ) );
9971 #endif
9972
9973 // update anything that can be changed without recalcing samplers
9974 stbir__update_info_from_resize( out_info, resize );
9975
9976 return splits;
9977 }
9978
9979 return 0;
9980}
9981
9982STBIRDEF void stbir_free_samplers( STBIR_RESIZE * resize )
9983{
9984 if ( resize->samplers )
9985 {
9986 stbir__free_internal_mem( resize->samplers );
9987 resize->samplers = 0;
9988 resize->called_alloc = 0;
9989 }
9990}
9991
9992STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int splits )
9993{
9994 if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) )
9995 {
9996 if ( resize->samplers )
9997 stbir_free_samplers( resize );
9998
9999 resize->called_alloc = 1;
10000 return stbir__perform_build( resize, splits );
10001 }
10002
10003 STBIR_PROFILE_BUILD_CLEAR( resize->samplers );
10004
10005 return resize->splits; // already built: the count that build settled on (a new request isn't applied without a rebuild)
10006}
10007
10008STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize )
10009{
10010 return stbir_build_samplers_with_splits( resize, 1 );
10011}
10012
10013STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize )
10014{
10015 int result;
10016
10017 if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) )
10018 {
10019 int alloc_state = resize->called_alloc; // remember allocated state
10020
10021 if ( resize->samplers )
10022 {
10023 stbir__free_internal_mem( resize->samplers );
10024 resize->samplers = 0;
10025 }
10026
10027 if ( !stbir_build_samplers( resize ) )
10028 return 0; // (an empty resize fails the build too, so it comes out of here as a failure)
10029
10030 resize->called_alloc = alloc_state;
10031 }
10032 else
10033 {
10034 // didn't build anything - clear it
10035 STBIR_PROFILE_BUILD_CLEAR( resize->samplers );
10036 }
10037
10038 // do resize
10039 result = stbir__perform_resize( resize->samplers, 0, resize->splits );
10040
10041 // if we alloced, then free
10042 if ( !resize->called_alloc )
10043 {
10044 stbir_free_samplers( resize );
10045 resize->samplers = 0;
10046 }
10047
10048 return result;
10049}
10050
10051STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count )
10052{
10053 STBIR_ASSERT( resize->samplers );
10054
10055 // you **must** build samplers first when using split resize (and not change the resize after)
10056 if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) )
10057 return 0;
10058
10059 // if we're just doing the whole thing, call full
10060 if ( ( split_start == -1 ) || ( ( split_start == 0 ) && ( split_count == resize->splits ) ) )
10061 return stbir_resize_extended( resize );
10062
10063 if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) )
10064 return 0;
10065
10066 // do resize
10067 return stbir__perform_resize( resize->samplers, split_start, split_count );
10068}
10069
10070
10071static void * stbir_quick_resize_helper( const void *input_pixels , int input_w , int input_h, stbir_int64 input_stride_in_bytes,
10072 void *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes,
10073 stbir_pixel_layout pixel_layout, stbir_datatype data_type, stbir_edge edge, stbir_filter filter )
10074{
10075 STBIR_RESIZE resize;
10076 stbir_int64 scanline_output_in_bytes;
10077 stbir_int64 positive_output_stride_in_bytes;
10078 void * start_ptr;
10079 void * free_ptr;
10080
10081 scanline_output_in_bytes = (stbir_int64)output_w * stbir__type_size[ data_type ] * stbir__pixel_channels[ stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ];
10082 if ( scanline_output_in_bytes <= 0 )
10083 return 0;
10084
10085 // if zero stride, use scanline output
10086 if ( output_stride_in_bytes == 0 )
10087 output_stride_in_bytes = scanline_output_in_bytes;
10088
10089 // abs value for inverted images (negative pitches)
10090 positive_output_stride_in_bytes = output_stride_in_bytes;
10091 if ( positive_output_stride_in_bytes < 0 )
10092 positive_output_stride_in_bytes = -positive_output_stride_in_bytes;
10093
10094 // is the requested stride smaller than the scanline output? if so, just fail
10095 if ( positive_output_stride_in_bytes < scanline_output_in_bytes )
10096 return 0;
10097
10098 start_ptr = output_pixels;
10099 free_ptr = 0; // no free pointer, since they passed buffer to use
10100
10101 // did they pass a zero for the dest? if so, allocate the buffer
10102 if ( output_pixels == 0 )
10103 {
10104 size_t size;
10105 char * ptr;
10106
10107 size = (size_t)positive_output_stride_in_bytes * (size_t)output_h;
10108 if ( size == 0 )
10109 return 0;
10110
10111 ptr = (char*) STBIR_MALLOC( size, 0 );
10112 if ( ptr == 0 )
10113 return 0;
10114
10115 free_ptr = ptr;
10116
10117 // point at the last scanline, if they requested a flipped image
10118 if ( output_stride_in_bytes < 0 )
10119 start_ptr = ptr + ( (size_t)positive_output_stride_in_bytes * (size_t)( output_h - 1 ) );
10120 else
10121 start_ptr = ptr;
10122 }
10123
10124 // ok, now do the resize
10125 stbir_resize_init( &resize,
10126 input_pixels, input_w, input_h, input_stride_in_bytes,
10127 start_ptr, output_w, output_h, output_stride_in_bytes,
10128 pixel_layout, data_type );
10129
10130 resize.horizontal_edge = edge;
10131 resize.vertical_edge = edge;
10132 resize.horizontal_filter = filter;
10133 resize.vertical_filter = filter;
10134
10135 if ( !stbir_resize_extended( &resize ) )
10136 {
10137 if ( free_ptr )
10138 STBIR_FREE( free_ptr, 0 );
10139 return 0;
10140 }
10141
10142 return (free_ptr) ? free_ptr : start_ptr;
10143}
10144
10145
10146STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, stbir_int64 input_stride_in_bytes,
10147 unsigned char *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes,
10148 stbir_pixel_layout pixel_layout )
10149{
10150 return (unsigned char *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes,
10151 output_pixels, output_w, output_h, output_stride_in_bytes,
10152 pixel_layout, STBIR_TYPE_UINT8, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT );
10153}
10154
10155STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, stbir_int64 input_stride_in_bytes,
10156 unsigned char *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes,
10157 stbir_pixel_layout pixel_layout )
10158{
10159 return (unsigned char *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes,
10160 output_pixels, output_w, output_h, output_stride_in_bytes,
10161 pixel_layout, STBIR_TYPE_UINT8_SRGB, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT );
10162}
10163
10164
10165STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, stbir_int64 input_stride_in_bytes,
10166 float *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes,
10167 stbir_pixel_layout pixel_layout )
10168{
10169 return (float *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes,
10170 output_pixels, output_w, output_h, output_stride_in_bytes,
10171 pixel_layout, STBIR_TYPE_FLOAT, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT );
10172}
10173
10174
10175STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, stbir_int64 input_stride_in_bytes,
10176 void *output_pixels, int output_w, int output_h, stbir_int64 output_stride_in_bytes,
10177 stbir_pixel_layout pixel_layout, stbir_datatype data_type,
10178 stbir_edge edge, stbir_filter filter )
10179{
10180 return (void *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes,
10181 output_pixels, output_w, output_h, output_stride_in_bytes,
10182 pixel_layout, data_type, edge, filter );
10183}
10184
10185#ifdef STBIR_PROFILE
10186
10187STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize )
10188{
10189 static char const * bdescriptions[6] = { "Building", "Allocating", "Horizontal sampler", "Vertical sampler", "Coefficient cleanup", "Scatter coefficients" } ;
10190 stbir__info* samp = resize->samplers;
10191 int i;
10192
10193 for( i = 0 ; i < (int)STBIR__ARRAY_SIZE( bdescriptions ) ; i++)
10194 info->clocks[i] = samp->profile.array[i+1];
10195
10196 info->total_clocks = samp->profile.named.total;
10197 info->descriptions = bdescriptions;
10198 info->count = STBIR__ARRAY_SIZE( bdescriptions );
10199}
10200
10201STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize, int split_start, int split_count )
10202{
10203 static char const * descriptions[7] = { "Looping", "Vertical sampling", "Horizontal sampling", "Scanline input", "Scanline output", "Alpha weighting", "Alpha unweighting" };
10204 stbir__per_split_info * split_info;
10205 int s, i;
10206
10207 if ( split_start == -1 )
10208 {
10209 split_start = 0;
10210 split_count = resize->samplers->splits;
10211 }
10212
10213 if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) )
10214 {
10215 info->total_clocks = 0;
10216 info->descriptions = 0;
10217 info->count = 0;
10218 return;
10219 }
10220
10221 split_info = resize->samplers->split_info + split_start;
10222
10223 // sum up the profile from all the splits
10224 for( i = 0 ; i < (int)STBIR__ARRAY_SIZE( descriptions ) ; i++ )
10225 {
10226 stbir_uint64 sum = 0;
10227 for( s = 0 ; s < split_count ; s++ )
10228 sum += split_info[s].profile.array[i+1];
10229 info->clocks[i] = sum;
10230 }
10231
10232 info->total_clocks = 0;
10233 for( s = 0 ; s < split_count ; s++ )
10234 info->total_clocks += split_info[s].profile.named.total;
10235 info->descriptions = descriptions;
10236 info->count = STBIR__ARRAY_SIZE( descriptions );
10237}
10238
10239STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize )
10240{
10241 stbir_resize_split_profile_info( info, resize, -1, 0 );
10242}
10243
10244#endif // STBIR_PROFILE
10245
10246#undef STBIR_BGR
10247#undef STBIR_1CHANNEL
10248#undef STBIR_2CHANNEL
10249#undef STBIR_RGB
10250#undef STBIR_RGBA
10251#undef STBIR_4CHANNEL
10252#undef STBIR_BGRA
10253#undef STBIR_ARGB
10254#undef STBIR_ABGR
10255#undef STBIR_RA
10256#undef STBIR_AR
10257#undef STBIR_RGBA_PM
10258#undef STBIR_BGRA_PM
10259#undef STBIR_ARGB_PM
10260#undef STBIR_ABGR_PM
10261#undef STBIR_RA_PM
10262#undef STBIR_AR_PM
10263
10264#endif // STB_IMAGE_RESIZE_IMPLEMENTATION
10265
10266#else // STB_IMAGE_RESIZE_HORIZONTALS&STB_IMAGE_RESIZE_DO_VERTICALS
10267
10268// we reinclude the header file to define all the horizontal functions
10269// specializing each function for the number of coeffs is 20-40% faster *OVERALL*
10270
10271// by including the header file again this way, we can still debug the functions
10272
10273#define STBIR_strs_join2( start, mid, end ) start##mid##end
10274#define STBIR_strs_join1( start, mid, end ) STBIR_strs_join2( start, mid, end )
10275
10276#define STBIR_strs_join24( start, mid1, mid2, end ) start##mid1##mid2##end
10277#define STBIR_strs_join14( start, mid1, mid2, end ) STBIR_strs_join24( start, mid1, mid2, end )
10278
10279#ifdef STB_IMAGE_RESIZE_DO_CODERS
10280
10281#ifdef stbir__decode_suffix
10282#define STBIR__CODER_NAME( name ) STBIR_strs_join1( name, _, stbir__decode_suffix )
10283#else
10284#define STBIR__CODER_NAME( name ) name
10285#endif
10286
10287#ifdef stbir__decode_swizzle
10288#define stbir__decode_simdf8_flip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3),stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg)
10289#define stbir__decode_simdf4_flip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg)
10290#define stbir__encode_simdf8_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3),stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg)
10291#define stbir__encode_simdf4_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg)
10292#define stbir__encode_simdf16_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf16_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg) // (one lane's order, the shuffle repeats it)
10293// u8 and u16 inputs are swizzled as bytes before the expansion when there is a byte shuffle
10294#ifdef STBIR_SIMD_BYTE_SHUFFLE
10295#define STBIR__SWZ8( p ) ( ( (p)*4 + stbir__decode_order0 ) | ( ( (p)*4 + stbir__decode_order1 ) << 8 ) | ( ( (p)*4 + stbir__decode_order2 ) << 16 ) | ( ( (p)*4 + stbir__decode_order3 ) << 24 ) )
10296#define STBIR__SWZ16( p, a, b ) ( ( 2*((p)*4 + (a)) ) | ( ( 2*((p)*4 + (a)) + 1 ) << 8 ) | ( ( 2*((p)*4 + (b)) ) << 16 ) | ( ( 2*((p)*4 + (b)) + 1 ) << 24 ) )
10297static STBIR__SIMDI_CONST4( STBIR__CODER_NAME( stbir__decode_bytes_swizzle ), STBIR__SWZ8(0), STBIR__SWZ8(1), STBIR__SWZ8(2), STBIR__SWZ8(3) );
10298static STBIR__SIMDI_CONST4( STBIR__CODER_NAME( stbir__decode_words_swizzle ), STBIR__SWZ16(0,stbir__decode_order0,stbir__decode_order1), STBIR__SWZ16(0,stbir__decode_order2,stbir__decode_order3), STBIR__SWZ16(1,stbir__decode_order0,stbir__decode_order1), STBIR__SWZ16(1,stbir__decode_order2,stbir__decode_order3) );
10299#define stbir__decode_simdi_flip_u8( ireg ) stbir__simdi_shuffle_bytes( ireg, ireg, STBIR__CONSTI( STBIR__CODER_NAME( stbir__decode_bytes_swizzle ) ) )
10300#define stbir__decode_simdi_flip_u16( ireg ) stbir__simdi_shuffle_bytes( ireg, ireg, STBIR__CONSTI( STBIR__CODER_NAME( stbir__decode_words_swizzle ) ) )
10301#define stbir__decode_simdf8_flip_i( reg )
10302#define stbir__decode_simdf4_flip_i( reg )
10303#define stbir__decode_simdf16_flip_i( reg )
10304#ifdef STBIR_SIMD16
10305#define stbir__decode_simdi8_flip_u16( ireg ) stbir__simdi8_shuffle_bytes( ireg, ireg, STBIR__CONSTI( STBIR__CODER_NAME( stbir__decode_words_swizzle ) ) ) // (the 16-wide u16 decoder loads 32 bytes: the same table in both lanes)
10306#endif
10307#else
10308#define stbir__decode_simdi_flip_u8( ireg )
10309#define stbir__decode_simdi_flip_u16( ireg )
10310#define stbir__decode_simdi8_flip_u16( ireg )
10311#define stbir__decode_simdf8_flip_i( reg ) stbir__decode_simdf8_flip( reg )
10312#define stbir__decode_simdf4_flip_i( reg ) stbir__decode_simdf4_flip( reg )
10313#define stbir__decode_simdf16_flip_i( reg ) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf16_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg)
10314#endif
10315#else
10316#define stbir__decode_order0 0
10317#define stbir__decode_order1 1
10318#define stbir__decode_order2 2
10319#define stbir__decode_order3 3
10320#define stbir__encode_order0 0
10321#define stbir__encode_order1 1
10322#define stbir__encode_order2 2
10323#define stbir__encode_order3 3
10324#define stbir__decode_simdf8_flip(reg)
10325#define stbir__decode_simdf4_flip(reg)
10326#define stbir__decode_simdi_flip_u8( ireg )
10327#define stbir__decode_simdi_flip_u16( ireg )
10328#define stbir__decode_simdi8_flip_u16( ireg )
10329#define stbir__decode_simdf8_flip_i( reg )
10330#define stbir__decode_simdf4_flip_i( reg )
10331#define stbir__decode_simdf16_flip_i( reg )
10332#define stbir__encode_simdf8_unflip(reg)
10333#define stbir__encode_simdf4_unflip(reg)
10334#define stbir__encode_simdf16_unflip(reg)
10335#endif
10336
10337#if defined(STBIR_SIMD16)
10338#define stbir__encode_simdfX_unflip stbir__encode_simdf16_unflip
10339#elif defined(STBIR_SIMD8)
10340#define stbir__encode_simdfX_unflip stbir__encode_simdf8_unflip
10341#else
10342#define stbir__encode_simdfX_unflip stbir__encode_simdf4_unflip
10343#endif
10344
10345static float * STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * decodep, ptrdiff_t width_times_channels, void const * inputp )
10346{
10347 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
10348 float * decode_end = (float*) decode + width_times_channels;
10349 unsigned char const * input = (unsigned char const*)inputp;
10350
10351 #ifdef STBIR_SIMD
10352 unsigned char const * end_input_m16 = input + width_times_channels - 16;
10353 if ( width_times_channels >= 16 )
10354 {
10355 decode_end -= 16;
10356 STBIR_NO_UNROLL_LOOP_START_INF_FOR
10357 for(;;)
10358 {
10359 #if defined(STBIR_SIMD16)
10360 stbir__simdi i; stbir__simdi16 o;
10361 stbir__simdf16 of;
10362 STBIR_NO_UNROLL(decode);
10363 stbir__simdi_load( i, input );
10364 stbir__decode_simdi_flip_u8( i );
10365 stbir__simdi16_expand_u8_to_u32( o, i );
10366 stbir__simdi16_convert_i32_to_float( of, o );
10367 stbir__simdf16_mult( of, of, STBIR_max_uint8_as_float_inverted16 );
10368 stbir__decode_simdf16_flip_i( of );
10369 stbir__simdf16_store( decode, of );
10370 #elif defined(STBIR_SIMD8)
10371 stbir__simdi i; stbir__simdi8 o0,o1;
10372 stbir__simdf8 of0, of1;
10373 STBIR_NO_UNROLL(decode);
10374 stbir__simdi_load( i, input );
10375 stbir__decode_simdi_flip_u8( i );
10376 stbir__simdi8_expand_u8_to_u32( o0, o1, i );
10377 stbir__simdi8_convert_i32_to_float( of0, o0 );
10378 stbir__simdi8_convert_i32_to_float( of1, o1 );
10379 stbir__simdf8_mult( of0, of0, STBIR_max_uint8_as_float_inverted8);
10380 stbir__simdf8_mult( of1, of1, STBIR_max_uint8_as_float_inverted8);
10381 stbir__decode_simdf8_flip_i( of0 );
10382 stbir__decode_simdf8_flip_i( of1 );
10383 stbir__simdf8_store( decode + 0, of0 );
10384 stbir__simdf8_store( decode + 8, of1 );
10385 #else
10386 stbir__simdi i, o0, o1, o2, o3;
10387 stbir__simdf of0, of1, of2, of3;
10388 STBIR_NO_UNROLL(decode);
10389 stbir__simdi_load( i, input );
10390 stbir__decode_simdi_flip_u8( i );
10391 stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i);
10392 stbir__simdi_convert_i32_to_float( of0, o0 );
10393 stbir__simdi_convert_i32_to_float( of1, o1 );
10394 stbir__simdi_convert_i32_to_float( of2, o2 );
10395 stbir__simdi_convert_i32_to_float( of3, o3 );
10396 stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) );
10397 stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) );
10398 stbir__simdf_mult( of2, of2, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) );
10399 stbir__simdf_mult( of3, of3, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) );
10400 stbir__decode_simdf4_flip_i( of0 );
10401 stbir__decode_simdf4_flip_i( of1 );
10402 stbir__decode_simdf4_flip_i( of2 );
10403 stbir__decode_simdf4_flip_i( of3 );
10404 stbir__simdf_store( decode + 0, of0 );
10405 stbir__simdf_store( decode + 4, of1 );
10406 stbir__simdf_store( decode + 8, of2 );
10407 stbir__simdf_store( decode + 12, of3 );
10408 #endif
10409 decode += 16;
10410 input += 16;
10411 if ( decode <= decode_end )
10412 continue;
10413 if ( decode == ( decode_end + 16 ) )
10414 break;
10415 decode = decode_end; // backup and do last couple
10416 input = end_input_m16;
10417 }
10418 return decode_end + 16;
10419 }
10420 #endif
10421
10422 // try to do blocks of 4 when you can
10423 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
10424 decode += 4;
10425 STBIR_SIMD_NO_UNROLL_LOOP_START
10426 while( decode <= decode_end )
10427 {
10428 STBIR_SIMD_NO_UNROLL(decode);
10429 decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted;
10430 decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted;
10431 decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted;
10432 decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint8_as_float_inverted;
10433 decode += 4;
10434 input += 4;
10435 }
10436 decode -= 4;
10437 #endif
10438
10439 // do the remnants
10440 #if stbir__coder_min_num < 4
10441 STBIR_NO_UNROLL_LOOP_START
10442 while( decode < decode_end )
10443 {
10444 STBIR_NO_UNROLL(decode);
10445 decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted;
10446 #if stbir__coder_min_num >= 2
10447 decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted;
10448 #endif
10449 #if stbir__coder_min_num >= 3
10450 decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted;
10451 #endif
10452 decode += stbir__coder_min_num;
10453 input += stbir__coder_min_num;
10454 }
10455 #endif
10456
10457 return decode_end;
10458}
10459
10460static void STBIR__CODER_NAME( stbir__encode_uint8_linear_scaled )( void * outputp, ptrdiff_t width_times_channels, float const * encode )
10461{
10462 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp;
10463 unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels;
10464
10465 #ifdef STBIR_SIMD
10466 // double width blocks while they fit, then the four-at-a-time and scalar loops below finish the row. (the encode can
10467 // be in place - with an output callback it converts into the float buffer - so no backing up to redo the last block)
10468 STBIR_SIMD_NO_UNROLL_LOOP_START
10469 while ( ( end_output - output ) >= stbir__simdfX_float_count*2 )
10470 {
10471 stbir__simdfX e0, e1;
10472 STBIR_SIMD_NO_UNROLL(encode);
10473 stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode );
10474 stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode+stbir__simdfX_float_count );
10475 stbir__encode_simdfX_unflip( e0 );
10476 stbir__encode_simdfX_unflip( e1 );
10477 #if defined(STBIR_SIMD16)
10478 {
10479 stbir__simdi8 i;
10480 stbir__simdf16_pack_to_32bytes( i, e0, e1 );
10481 stbir__simdi8_store( output, i );
10482 }
10483 #elif defined(STBIR_SIMD8)
10484 {
10485 stbir__simdi i;
10486 stbir__simdf8_pack_to_16bytes( i, e0, e1 );
10487 stbir__simdi_store( output, i );
10488 }
10489 #else
10490 {
10491 stbir__simdi i;
10492 stbir__simdf_pack_to_8bytes( i, e0, e1 );
10493 stbir__simdi_store2( output, i );
10494 }
10495 #endif
10496 encode += stbir__simdfX_float_count*2;
10497 output += stbir__simdfX_float_count*2;
10498 }
10499
10500 // try to do blocks of 4 when you can
10501 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
10502 output += 4;
10503 STBIR_NO_UNROLL_LOOP_START
10504 while( output <= end_output )
10505 {
10506 stbir__simdf e0;
10507 stbir__simdi i0;
10508 STBIR_NO_UNROLL(encode);
10509 stbir__simdf_load( e0, encode );
10510 stbir__simdf_madd( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), e0 );
10511 stbir__encode_simdf4_unflip( e0 );
10512 stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4
10513 *(int*)(output-4) = stbir__simdi_to_int( i0 );
10514 output += 4;
10515 encode += 4;
10516 }
10517 output -= 4;
10518 #endif
10519
10520 // do the remnants
10521 #if stbir__coder_min_num < 4
10522 STBIR_NO_UNROLL_LOOP_START
10523 while( output < end_output )
10524 {
10525 // read the whole pixel before writing any of it - the encode can be in place, and a swizzle reads a channel after
10526 // the one it lands on has been overwritten
10527 stbir__simdf e0;
10528 #if stbir__coder_min_num >= 2
10529 stbir__simdf e1;
10530 #endif
10531 #if stbir__coder_min_num >= 3
10532 stbir__simdf e2;
10533 #endif
10534 STBIR_NO_UNROLL(encode);
10535 stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order0 );
10536 #if stbir__coder_min_num >= 2
10537 stbir__simdf_madd1_mem( e1, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order1 );
10538 #endif
10539 #if stbir__coder_min_num >= 3
10540 stbir__simdf_madd1_mem( e2, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order2 );
10541 #endif
10542 output[0] = stbir__simdf_convert_float_to_uint8( e0 );
10543 #if stbir__coder_min_num >= 2
10544 output[1] = stbir__simdf_convert_float_to_uint8( e1 );
10545 #endif
10546 #if stbir__coder_min_num >= 3
10547 output[2] = stbir__simdf_convert_float_to_uint8( e2 );
10548 #endif
10549 output += stbir__coder_min_num;
10550 encode += stbir__coder_min_num;
10551 }
10552 #endif
10553
10554 #else
10555
10556 // try to do blocks of 4 when you can
10557 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
10558 output += 4;
10559 while( output <= end_output )
10560 {
10561 float f0 = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f;
10562 float f1 = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f;
10563 float f2 = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f;
10564 float f3 = encode[stbir__encode_order3] * stbir__max_uint8_as_float + 0.5f;
10565 STBIR_CLAMP(f0, 0, 255);
10566 STBIR_CLAMP(f1, 0, 255);
10567 STBIR_CLAMP(f2, 0, 255);
10568 STBIR_CLAMP(f3, 0, 255);
10569 output[0-4] = (unsigned char)f0;
10570 output[1-4] = (unsigned char)f1;
10571 output[2-4] = (unsigned char)f2;
10572 output[3-4] = (unsigned char)f3;
10573 output += 4;
10574 encode += 4;
10575 }
10576 output -= 4;
10577 #endif
10578
10579 // do the remnants
10580 #if stbir__coder_min_num < 4
10581 STBIR_NO_UNROLL_LOOP_START
10582 while( output < end_output )
10583 {
10584 float f0 = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f;
10585 #if stbir__coder_min_num >= 2
10586 float f1 = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f;
10587 #endif
10588 #if stbir__coder_min_num >= 3
10589 float f2 = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f;
10590 #endif
10591 STBIR_NO_UNROLL(encode);
10592 STBIR_CLAMP(f0, 0, 255);
10593 output[0] = (unsigned char)f0;
10594 #if stbir__coder_min_num >= 2
10595 STBIR_CLAMP(f1, 0, 255);
10596 output[1] = (unsigned char)f1;
10597 #endif
10598 #if stbir__coder_min_num >= 3
10599 STBIR_CLAMP(f2, 0, 255);
10600 output[2] = (unsigned char)f2;
10601 #endif
10602 output += stbir__coder_min_num;
10603 encode += stbir__coder_min_num;
10604 }
10605 #endif
10606 #endif
10607}
10608
10609static float * STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, ptrdiff_t width_times_channels, void const * inputp )
10610{
10611 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
10612 float * decode_end = (float*) decode + width_times_channels;
10613 unsigned char const * input = (unsigned char const*)inputp;
10614
10615 #ifdef STBIR_SIMD
10616 unsigned char const * end_input_m16 = input + width_times_channels - 16;
10617 if ( width_times_channels >= 16 )
10618 {
10619 decode_end -= 16;
10620 STBIR_NO_UNROLL_LOOP_START_INF_FOR
10621 for(;;)
10622 {
10623 #if defined(STBIR_SIMD16)
10624 stbir__simdi i; stbir__simdi16 o;
10625 stbir__simdf16 of;
10626 STBIR_NO_UNROLL(decode);
10627 stbir__simdi_load( i, input );
10628 stbir__decode_simdi_flip_u8( i );
10629 stbir__simdi16_expand_u8_to_u32( o, i );
10630 stbir__simdi16_convert_i32_to_float( of, o );
10631 stbir__decode_simdf16_flip_i( of );
10632 stbir__simdf16_store( decode, of );
10633 #elif defined(STBIR_SIMD8)
10634 stbir__simdi i; stbir__simdi8 o0,o1;
10635 stbir__simdf8 of0, of1;
10636 STBIR_NO_UNROLL(decode);
10637 stbir__simdi_load( i, input );
10638 stbir__decode_simdi_flip_u8( i );
10639 stbir__simdi8_expand_u8_to_u32( o0, o1, i );
10640 stbir__simdi8_convert_i32_to_float( of0, o0 );
10641 stbir__simdi8_convert_i32_to_float( of1, o1 );
10642 stbir__decode_simdf8_flip_i( of0 );
10643 stbir__decode_simdf8_flip_i( of1 );
10644 stbir__simdf8_store( decode + 0, of0 );
10645 stbir__simdf8_store( decode + 8, of1 );
10646 #else
10647 stbir__simdi i, o0, o1, o2, o3;
10648 stbir__simdf of0, of1, of2, of3;
10649 STBIR_NO_UNROLL(decode);
10650 stbir__simdi_load( i, input );
10651 stbir__decode_simdi_flip_u8( i );
10652 stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i);
10653 stbir__simdi_convert_i32_to_float( of0, o0 );
10654 stbir__simdi_convert_i32_to_float( of1, o1 );
10655 stbir__simdi_convert_i32_to_float( of2, o2 );
10656 stbir__simdi_convert_i32_to_float( of3, o3 );
10657 stbir__decode_simdf4_flip_i( of0 );
10658 stbir__decode_simdf4_flip_i( of1 );
10659 stbir__decode_simdf4_flip_i( of2 );
10660 stbir__decode_simdf4_flip_i( of3 );
10661 stbir__simdf_store( decode + 0, of0 );
10662 stbir__simdf_store( decode + 4, of1 );
10663 stbir__simdf_store( decode + 8, of2 );
10664 stbir__simdf_store( decode + 12, of3 );
10665#endif
10666 decode += 16;
10667 input += 16;
10668 if ( decode <= decode_end )
10669 continue;
10670 if ( decode == ( decode_end + 16 ) )
10671 break;
10672 decode = decode_end; // backup and do last couple
10673 input = end_input_m16;
10674 }
10675 return decode_end + 16;
10676 }
10677 #endif
10678
10679 // try to do blocks of 4 when you can
10680 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
10681 decode += 4;
10682 STBIR_SIMD_NO_UNROLL_LOOP_START
10683 while( decode <= decode_end )
10684 {
10685 STBIR_SIMD_NO_UNROLL(decode);
10686 decode[0-4] = ((float)(input[stbir__decode_order0]));
10687 decode[1-4] = ((float)(input[stbir__decode_order1]));
10688 decode[2-4] = ((float)(input[stbir__decode_order2]));
10689 decode[3-4] = ((float)(input[stbir__decode_order3]));
10690 decode += 4;
10691 input += 4;
10692 }
10693 decode -= 4;
10694 #endif
10695
10696 // do the remnants
10697 #if stbir__coder_min_num < 4
10698 STBIR_NO_UNROLL_LOOP_START
10699 while( decode < decode_end )
10700 {
10701 STBIR_NO_UNROLL(decode);
10702 decode[0] = ((float)(input[stbir__decode_order0]));
10703 #if stbir__coder_min_num >= 2
10704 decode[1] = ((float)(input[stbir__decode_order1]));
10705 #endif
10706 #if stbir__coder_min_num >= 3
10707 decode[2] = ((float)(input[stbir__decode_order2]));
10708 #endif
10709 decode += stbir__coder_min_num;
10710 input += stbir__coder_min_num;
10711 }
10712 #endif
10713 return decode_end;
10714}
10715
10716static void STBIR__CODER_NAME( stbir__encode_uint8_linear )( void * outputp, ptrdiff_t width_times_channels, float const * encode )
10717{
10718 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp;
10719 unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels;
10720
10721 #ifdef STBIR_SIMD
10722 // double width blocks while they fit, then the four-at-a-time and scalar loops below finish the row. (the encode can
10723 // be in place - with an output callback it converts into the float buffer - so no backing up to redo the last block)
10724 STBIR_SIMD_NO_UNROLL_LOOP_START
10725 while ( ( end_output - output ) >= stbir__simdfX_float_count*2 )
10726 {
10727 stbir__simdfX e0, e1;
10728 STBIR_SIMD_NO_UNROLL(encode);
10729 stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode );
10730 stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count );
10731 stbir__encode_simdfX_unflip( e0 );
10732 stbir__encode_simdfX_unflip( e1 );
10733 #if defined(STBIR_SIMD16)
10734 {
10735 stbir__simdi8 i;
10736 stbir__simdf16_pack_to_32bytes( i, e0, e1 );
10737 stbir__simdi8_store( output, i );
10738 }
10739 #elif defined(STBIR_SIMD8)
10740 {
10741 stbir__simdi i;
10742 stbir__simdf8_pack_to_16bytes( i, e0, e1 );
10743 stbir__simdi_store( output, i );
10744 }
10745 #else
10746 {
10747 stbir__simdi i;
10748 stbir__simdf_pack_to_8bytes( i, e0, e1 );
10749 stbir__simdi_store2( output, i );
10750 }
10751 #endif
10752 encode += stbir__simdfX_float_count*2;
10753 output += stbir__simdfX_float_count*2;
10754 }
10755
10756 // try to do blocks of 4 when you can
10757 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
10758 output += 4;
10759 STBIR_NO_UNROLL_LOOP_START
10760 while( output <= end_output )
10761 {
10762 stbir__simdf e0;
10763 stbir__simdi i0;
10764 STBIR_NO_UNROLL(encode);
10765 stbir__simdf_load( e0, encode );
10766 stbir__simdf_add( e0, STBIR__CONSTF(STBIR_simd_point5), e0 );
10767 stbir__encode_simdf4_unflip( e0 );
10768 stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4
10769 *(int*)(output-4) = stbir__simdi_to_int( i0 );
10770 output += 4;
10771 encode += 4;
10772 }
10773 output -= 4;
10774 #endif
10775
10776 #else
10777
10778 // try to do blocks of 4 when you can
10779 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
10780 output += 4;
10781 while( output <= end_output )
10782 {
10783 float f0 = encode[stbir__encode_order0] + 0.5f;
10784 float f1 = encode[stbir__encode_order1] + 0.5f;
10785 float f2 = encode[stbir__encode_order2] + 0.5f;
10786 float f3 = encode[stbir__encode_order3] + 0.5f;
10787 STBIR_CLAMP(f0, 0, 255);
10788 STBIR_CLAMP(f1, 0, 255);
10789 STBIR_CLAMP(f2, 0, 255);
10790 STBIR_CLAMP(f3, 0, 255);
10791 output[0-4] = (unsigned char)f0;
10792 output[1-4] = (unsigned char)f1;
10793 output[2-4] = (unsigned char)f2;
10794 output[3-4] = (unsigned char)f3;
10795 output += 4;
10796 encode += 4;
10797 }
10798 output -= 4;
10799 #endif
10800
10801 #endif
10802
10803 // do the remnants
10804 #if stbir__coder_min_num < 4
10805 STBIR_NO_UNROLL_LOOP_START
10806 while( output < end_output )
10807 {
10808 float f0 = encode[stbir__encode_order0] + 0.5f;
10809 #if stbir__coder_min_num >= 2
10810 float f1 = encode[stbir__encode_order1] + 0.5f;
10811 #endif
10812 #if stbir__coder_min_num >= 3
10813 float f2 = encode[stbir__encode_order2] + 0.5f;
10814 #endif
10815 STBIR_NO_UNROLL(encode);
10816 STBIR_CLAMP(f0, 0, 255);
10817 output[0] = (unsigned char)f0;
10818 #if stbir__coder_min_num >= 2
10819 STBIR_CLAMP(f1, 0, 255);
10820 output[1] = (unsigned char)f1;
10821 #endif
10822 #if stbir__coder_min_num >= 3
10823 STBIR_CLAMP(f2, 0, 255);
10824 output[2] = (unsigned char)f2;
10825 #endif
10826 output += stbir__coder_min_num;
10827 encode += stbir__coder_min_num;
10828 }
10829 #endif
10830}
10831
10832static float * STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, ptrdiff_t width_times_channels, void const * inputp )
10833{
10834 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
10835 float * decode_end = (float*) decode + width_times_channels;
10836 unsigned char const * input = (unsigned char const *)inputp;
10837
10838 // try to do blocks of 4 when you can
10839 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
10840 decode += 4;
10841 while( decode <= decode_end )
10842 {
10843 decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ];
10844 decode[1-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ];
10845 decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ];
10846 decode[3-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order3 ] ];
10847 decode += 4;
10848 input += 4;
10849 }
10850 decode -= 4;
10851 #endif
10852
10853 // do the remnants
10854 #if stbir__coder_min_num < 4
10855 STBIR_NO_UNROLL_LOOP_START
10856 while( decode < decode_end )
10857 {
10858 STBIR_NO_UNROLL(decode);
10859 decode[0] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ];
10860 #if stbir__coder_min_num >= 2
10861 decode[1] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ];
10862 #endif
10863 #if stbir__coder_min_num >= 3
10864 decode[2] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ];
10865 #endif
10866 decode += stbir__coder_min_num;
10867 input += stbir__coder_min_num;
10868 }
10869 #endif
10870 return decode_end;
10871}
10872
10873#define stbir__min_max_shift20( i, f ) \
10874 do { \
10875 stbir__simdf_max( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_zero )) ); \
10876 stbir__simdf_min( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_one )) ); \
10877 stbir__simdi_32shr( i, stbir_simdi_castf( f ), 20 ); \
10878 } while(0)
10879
10880#define stbir__scale_and_convert( i, f ) \
10881 do { \
10882 stbir__simdf_madd( f, STBIR__CONSTF( STBIR_simd_point5 ), STBIR__CONSTF( STBIR_max_uint8_as_float ), f ); \
10883 stbir__simdf_max( f, f, stbir__simdf_zeroP() ); \
10884 stbir__simdf_min( f, f, STBIR__CONSTF( STBIR_max_uint8_as_float ) ); \
10885 stbir__simdf_convert_float_to_i32( i, f ); \
10886 } while(0)
10887
10888#define stbir__linear_to_srgb_finish( i, f ) \
10889{ \
10890 stbir__simdi temp; \
10891 stbir__simdi_32shr( temp, stbir_simdi_castf( f ), 12 ) ; \
10892 stbir__simdi_and( temp, temp, STBIR__CONSTI(STBIR_mantissa_mask) ); \
10893 stbir__simdi_or( temp, temp, STBIR__CONSTI(STBIR_topscale) ); \
10894 stbir__simdi_16madd( i, i, temp ); \
10895 stbir__simdi_32shr( i, i, 16 ); \
10896}
10897
10898// look up sRGB bytes for 4 float lanes per register (indexed by the float bits shifted right 20, see fp32_to_srgb8_tab4)
10899#define stbir__simdi_srgb_lookup_one( v, temp ) \
10900 do { \
10901 temp.m128i_i128 = v; \
10902 temp.m128i_u32[0] = STBIR__SRGB8_TAB4( temp.m128i_i32[0] ); \
10903 temp.m128i_u32[1] = STBIR__SRGB8_TAB4( temp.m128i_i32[1] ); \
10904 temp.m128i_u32[2] = STBIR__SRGB8_TAB4( temp.m128i_i32[2] ); \
10905 temp.m128i_u32[3] = STBIR__SRGB8_TAB4( temp.m128i_i32[3] ); \
10906 v = temp.m128i_i128; \
10907 } while(0)
10908
10909#define stbir__simdi_srgb_lookup2( v0,v1 ) \
10910 do { \
10911 stbir__simdi_u32 temp0,temp1; \
10912 stbir__simdi_srgb_lookup_one( v0, temp0 ); \
10913 stbir__simdi_srgb_lookup_one( v1, temp1 ); \
10914 } while(0)
10915
10916#define stbir__simdi_srgb_lookup3( v0,v1,v2 ) \
10917 do { \
10918 stbir__simdi_u32 temp0,temp1,temp2; \
10919 stbir__simdi_srgb_lookup_one( v0, temp0 ); \
10920 stbir__simdi_srgb_lookup_one( v1, temp1 ); \
10921 stbir__simdi_srgb_lookup_one( v2, temp2 ); \
10922 } while(0)
10923
10924#define stbir__simdi_srgb_lookup4( v0,v1,v2,v3 ) \
10925 do { \
10926 stbir__simdi_u32 temp0,temp1,temp2,temp3; \
10927 stbir__simdi_srgb_lookup_one( v0, temp0 ); \
10928 stbir__simdi_srgb_lookup_one( v1, temp1 ); \
10929 stbir__simdi_srgb_lookup_one( v2, temp2 ); \
10930 stbir__simdi_srgb_lookup_one( v3, temp3 ); \
10931 } while(0)
10932
10933static void STBIR__CODER_NAME( stbir__encode_uint8_srgb )( void * outputp, ptrdiff_t width_times_channels, float const * encode )
10934{
10935 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp;
10936 unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels;
10937
10938 #ifdef STBIR_SIMD
10939
10940 // double width blocks while they fit, then the four-at-a-time and scalar loops below finish the row. (the encode can
10941 // be in place - with an output callback it converts into the float buffer - so no backing up to redo the last block)
10942 STBIR_SIMD_NO_UNROLL_LOOP_START
10943 while ( ( end_output - output ) >= 16 )
10944 {
10945 stbir__simdf f0, f1, f2, f3;
10946 stbir__simdi i0, i1, i2, i3;
10947 STBIR_SIMD_NO_UNROLL(encode);
10948
10949 stbir__simdf_load4_transposed( f0, f1, f2, f3, encode );
10950
10951 stbir__min_max_shift20( i0, f0 );
10952 stbir__min_max_shift20( i1, f1 );
10953 stbir__min_max_shift20( i2, f2 );
10954 stbir__min_max_shift20( i3, f3 );
10955
10956 stbir__simdi_srgb_lookup4( i0, i1, i2, i3 );
10957
10958 stbir__linear_to_srgb_finish( i0, f0 );
10959 stbir__linear_to_srgb_finish( i1, f1 );
10960 stbir__linear_to_srgb_finish( i2, f2 );
10961 stbir__linear_to_srgb_finish( i3, f3 );
10962
10963 stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) );
10964
10965 encode += 16;
10966 output += 16;
10967 }
10968 #endif
10969
10970 // try to do blocks of 4 when you can
10971 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
10972 output += 4;
10973 STBIR_SIMD_NO_UNROLL_LOOP_START
10974 while ( output <= end_output )
10975 {
10976 float e0, e1, e2, e3;
10977 STBIR_SIMD_NO_UNROLL(encode);
10978
10979 e0 = encode[stbir__encode_order0];
10980 e1 = encode[stbir__encode_order1];
10981 e2 = encode[stbir__encode_order2];
10982 e3 = encode[stbir__encode_order3];
10983 output[0-4] = stbir__linear_to_srgb_uchar( e0 );
10984 output[1-4] = stbir__linear_to_srgb_uchar( e1 );
10985 output[2-4] = stbir__linear_to_srgb_uchar( e2 );
10986 output[3-4] = stbir__linear_to_srgb_uchar( e3 );
10987
10988 output += 4;
10989 encode += 4;
10990 }
10991 output -= 4;
10992 #endif
10993
10994 // do the remnants
10995 #if stbir__coder_min_num < 4
10996 STBIR_NO_UNROLL_LOOP_START
10997 while( output < end_output )
10998 {
10999 float e0;
11000 #if stbir__coder_min_num >= 2
11001 float e1;
11002 #endif
11003 #if stbir__coder_min_num >= 3
11004 float e2;
11005 #endif
11006 STBIR_NO_UNROLL(encode);
11007 e0 = encode[stbir__encode_order0];
11008 #if stbir__coder_min_num >= 2
11009 e1 = encode[stbir__encode_order1];
11010 #endif
11011 #if stbir__coder_min_num >= 3
11012 e2 = encode[stbir__encode_order2];
11013 #endif
11014 output[0] = stbir__linear_to_srgb_uchar( e0 );
11015 #if stbir__coder_min_num >= 2
11016 output[1] = stbir__linear_to_srgb_uchar( e1 );
11017 #endif
11018 #if stbir__coder_min_num >= 3
11019 output[2] = stbir__linear_to_srgb_uchar( e2 );
11020 #endif
11021 output += stbir__coder_min_num;
11022 encode += stbir__coder_min_num;
11023 }
11024 #endif
11025}
11026
11027#if ( stbir__coder_min_num == 4 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) )
11028
11029static float * STBIR__CODER_NAME(stbir__decode_uint8_srgb4_linearalpha)( float * decodep, ptrdiff_t width_times_channels, void const * inputp )
11030{
11031 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
11032 float * decode_end = (float*) decode + width_times_channels;
11033 unsigned char const * input = (unsigned char const *)inputp;
11034
11035 do {
11036 decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ];
11037 decode[1] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order1] ];
11038 decode[2] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order2] ];
11039 decode[3] = ( (float) input[stbir__decode_order3] ) * stbir__max_uint8_as_float_inverted;
11040 input += 4;
11041 decode += 4;
11042 } while( decode < decode_end );
11043 return decode_end;
11044}
11045
11046
11047static void STBIR__CODER_NAME( stbir__encode_uint8_srgb4_linearalpha )( void * outputp, ptrdiff_t width_times_channels, float const * encode )
11048{
11049 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp;
11050 unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels;
11051
11052 #ifdef STBIR_SIMD
11053
11054 // double width blocks while they fit, then the four-at-a-time and scalar loops below finish the row. (the encode can
11055 // be in place - with an output callback it converts into the float buffer - so no backing up to redo the last block)
11056 STBIR_SIMD_NO_UNROLL_LOOP_START
11057 while ( ( end_output - output ) >= 16 )
11058 {
11059 stbir__simdf f0, f1, f2, f3;
11060 stbir__simdi i0, i1, i2, i3;
11061
11062 STBIR_SIMD_NO_UNROLL(encode);
11063 stbir__simdf_load4_transposed( f0, f1, f2, f3, encode );
11064
11065 stbir__min_max_shift20( i0, f0 );
11066 stbir__min_max_shift20( i1, f1 );
11067 stbir__min_max_shift20( i2, f2 );
11068 stbir__scale_and_convert( i3, f3 );
11069
11070 stbir__simdi_srgb_lookup3( i0, i1, i2 );
11071
11072 stbir__linear_to_srgb_finish( i0, f0 );
11073 stbir__linear_to_srgb_finish( i1, f1 );
11074 stbir__linear_to_srgb_finish( i2, f2 );
11075
11076 stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) );
11077
11078 output += 16;
11079 encode += 16;
11080
11081 }
11082 #endif
11083
11084 // the SIMD loop above can have finished the row, so this has to test first
11085 STBIR_SIMD_NO_UNROLL_LOOP_START
11086 while ( output < end_output )
11087 {
11088 float f;
11089 STBIR_SIMD_NO_UNROLL(encode);
11090
11091 output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] );
11092 output[stbir__decode_order1] = stbir__linear_to_srgb_uchar( encode[1] );
11093 output[stbir__decode_order2] = stbir__linear_to_srgb_uchar( encode[2] );
11094
11095 f = encode[3] * stbir__max_uint8_as_float + 0.5f;
11096 STBIR_CLAMP(f, 0, 255);
11097 output[stbir__decode_order3] = (unsigned char) f;
11098
11099 output += 4;
11100 encode += 4;
11101 }
11102}
11103
11104#endif
11105
11106#if ( stbir__coder_min_num == 2 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) )
11107
11108static float * STBIR__CODER_NAME(stbir__decode_uint8_srgb2_linearalpha)( float * decodep, ptrdiff_t width_times_channels, void const * inputp )
11109{
11110 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
11111 float * decode_end = (float*) decode + width_times_channels;
11112 unsigned char const * input = (unsigned char const *)inputp;
11113
11114 decode += 4;
11115 while( decode <= decode_end )
11116 {
11117 decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ];
11118 decode[1-4] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted;
11119 decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0+2] ];
11120 decode[3-4] = ( (float) input[stbir__decode_order1+2] ) * stbir__max_uint8_as_float_inverted;
11121 input += 4;
11122 decode += 4;
11123 }
11124 decode -= 4;
11125 if( decode < decode_end )
11126 {
11127 decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ];
11128 decode[1] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted;
11129 }
11130 return decode_end;
11131}
11132
11133static void STBIR__CODER_NAME( stbir__encode_uint8_srgb2_linearalpha )( void * outputp, ptrdiff_t width_times_channels, float const * encode )
11134{
11135 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp;
11136 unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels;
11137
11138 #ifdef STBIR_SIMD
11139
11140 // double width blocks while they fit, then the four-at-a-time and scalar loops below finish the row. (the encode can
11141 // be in place - with an output callback it converts into the float buffer - so no backing up to redo the last block)
11142 STBIR_SIMD_NO_UNROLL_LOOP_START
11143 while ( ( end_output - output ) >= 16 )
11144 {
11145 stbir__simdf f0, f1, f2, f3;
11146 stbir__simdi i0, i1, i2, i3;
11147
11148 STBIR_SIMD_NO_UNROLL(encode);
11149 stbir__simdf_load4_transposed( f0, f1, f2, f3, encode );
11150
11151 stbir__min_max_shift20( i0, f0 );
11152 stbir__scale_and_convert( i1, f1 );
11153 stbir__min_max_shift20( i2, f2 );
11154 stbir__scale_and_convert( i3, f3 );
11155
11156 stbir__simdi_srgb_lookup2( i0, i2 );
11157
11158 stbir__linear_to_srgb_finish( i0, f0 );
11159 stbir__linear_to_srgb_finish( i2, f2 );
11160
11161 stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) );
11162
11163 output += 16;
11164 encode += 16;
11165 }
11166 #endif
11167
11168 // the SIMD loop above can have finished the row, so this has to test first
11169 STBIR_SIMD_NO_UNROLL_LOOP_START
11170 while ( output < end_output )
11171 {
11172 float f;
11173 STBIR_SIMD_NO_UNROLL(encode);
11174
11175 output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] );
11176
11177 f = encode[1] * stbir__max_uint8_as_float + 0.5f;
11178 STBIR_CLAMP(f, 0, 255);
11179 output[stbir__decode_order1] = (unsigned char) f;
11180
11181 output += 2;
11182 encode += 2;
11183 }
11184}
11185
11186#endif
11187
11188static float * STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decodep, ptrdiff_t width_times_channels, void const * inputp )
11189{
11190 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
11191 float * decode_end = (float*) decode + width_times_channels;
11192 unsigned short const * input = (unsigned short const *)inputp;
11193
11194 #ifdef STBIR_SIMD
11195 // a block is 16 words at 16 wide (one 32 byte load), 8 otherwise
11196 unsigned short const * end_input_mb = input + width_times_channels - STBIR__U16_DECODE_BLOCK;
11197 if ( width_times_channels >= STBIR__U16_DECODE_BLOCK )
11198 {
11199 decode_end -= STBIR__U16_DECODE_BLOCK;
11200 STBIR_NO_UNROLL_LOOP_START_INF_FOR
11201 for(;;)
11202 {
11203 #if defined(STBIR_SIMD16)
11204 stbir__simdi8 i; stbir__simdi16 o;
11205 stbir__simdf16 of;
11206 STBIR_NO_UNROLL(decode);
11207 stbir__simdi8_load( i, input );
11208 stbir__decode_simdi8_flip_u16( i );
11209 stbir__simdi16_expand_u16_to_u32( o, i );
11210 stbir__simdi16_convert_i32_to_float( of, o );
11211 stbir__simdf16_mult( of, of, STBIR_max_uint16_as_float_inverted16 );
11212 stbir__decode_simdf16_flip_i( of );
11213 stbir__simdf16_store( decode, of );
11214 #elif defined(STBIR_SIMD8)
11215 stbir__simdi i; stbir__simdi8 o;
11216 stbir__simdf8 of;
11217 STBIR_NO_UNROLL(decode);
11218 stbir__simdi_load( i, input );
11219 stbir__decode_simdi_flip_u16( i );
11220 stbir__simdi8_expand_u16_to_u32( o, i );
11221 stbir__simdi8_convert_i32_to_float( of, o );
11222 stbir__simdf8_mult( of, of, STBIR_max_uint16_as_float_inverted8);
11223 stbir__decode_simdf8_flip_i( of );
11224 stbir__simdf8_store( decode + 0, of );
11225 #else
11226 stbir__simdi i, o0, o1;
11227 stbir__simdf of0, of1;
11228 STBIR_NO_UNROLL(decode);
11229 stbir__simdi_load( i, input );
11230 stbir__decode_simdi_flip_u16( i );
11231 stbir__simdi_expand_u16_to_u32( o0,o1,i );
11232 stbir__simdi_convert_i32_to_float( of0, o0 );
11233 stbir__simdi_convert_i32_to_float( of1, o1 );
11234 stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted) );
11235 stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted));
11236 stbir__decode_simdf4_flip_i( of0 );
11237 stbir__decode_simdf4_flip_i( of1 );
11238 stbir__simdf_store( decode + 0, of0 );
11239 stbir__simdf_store( decode + 4, of1 );
11240 #endif
11241 decode += STBIR__U16_DECODE_BLOCK;
11242 input += STBIR__U16_DECODE_BLOCK;
11243 if ( decode <= decode_end )
11244 continue;
11245 if ( decode == ( decode_end + STBIR__U16_DECODE_BLOCK ) )
11246 break;
11247 decode = decode_end; // backup and do last couple
11248 input = end_input_mb;
11249 }
11250 return decode_end + STBIR__U16_DECODE_BLOCK;
11251 }
11252 #endif
11253
11254 // try to do blocks of 4 when you can
11255 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11256 decode += 4;
11257 STBIR_SIMD_NO_UNROLL_LOOP_START
11258 while( decode <= decode_end )
11259 {
11260 STBIR_SIMD_NO_UNROLL(decode);
11261 decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted;
11262 decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted;
11263 decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted;
11264 decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint16_as_float_inverted;
11265 decode += 4;
11266 input += 4;
11267 }
11268 decode -= 4;
11269 #endif
11270
11271 // do the remnants
11272 #if stbir__coder_min_num < 4
11273 STBIR_NO_UNROLL_LOOP_START
11274 while( decode < decode_end )
11275 {
11276 STBIR_NO_UNROLL(decode);
11277 decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted;
11278 #if stbir__coder_min_num >= 2
11279 decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted;
11280 #endif
11281 #if stbir__coder_min_num >= 3
11282 decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted;
11283 #endif
11284 decode += stbir__coder_min_num;
11285 input += stbir__coder_min_num;
11286 }
11287 #endif
11288 return decode_end;
11289}
11290
11291
11292static void STBIR__CODER_NAME(stbir__encode_uint16_linear_scaled)( void * outputp, ptrdiff_t width_times_channels, float const * encode )
11293{
11294 unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp;
11295 unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels;
11296
11297 #ifdef STBIR_SIMD
11298 {
11299 // double width blocks while they fit, then the four-at-a-time and scalar loops below finish the row. (the encode can
11300 // be in place - with an output callback it converts into the float buffer - so no backing up to redo the last block)
11301 STBIR_SIMD_NO_UNROLL_LOOP_START
11302 while ( ( end_output - output ) >= stbir__simdfX_float_count*2 )
11303 {
11304 stbir__simdfX e0, e1;
11305 stbir__simdiX i;
11306 STBIR_SIMD_NO_UNROLL(encode);
11307 stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode );
11308 stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode+stbir__simdfX_float_count );
11309 stbir__encode_simdfX_unflip( e0 );
11310 stbir__encode_simdfX_unflip( e1 );
11311 stbir__simdfX_pack_to_words( i, e0, e1 );
11312 stbir__simdiX_store( output, i );
11313 encode += stbir__simdfX_float_count*2;
11314 output += stbir__simdfX_float_count*2;
11315 }
11316 }
11317
11318 // try to do blocks of 4 when you can
11319 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11320 output += 4;
11321 STBIR_NO_UNROLL_LOOP_START
11322 while( output <= end_output )
11323 {
11324 stbir__simdf e;
11325 stbir__simdi i;
11326 STBIR_NO_UNROLL(encode);
11327 stbir__simdf_load( e, encode );
11328 stbir__simdf_madd( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), e );
11329 stbir__encode_simdf4_unflip( e );
11330 stbir__simdf_pack_to_8words( i, e, e ); // only use first 4
11331 stbir__simdi_store2( output-4, i );
11332 output += 4;
11333 encode += 4;
11334 }
11335 output -= 4;
11336 #endif
11337
11338 // do the remnants
11339 #if stbir__coder_min_num < 4
11340 STBIR_NO_UNROLL_LOOP_START
11341 while( output < end_output )
11342 {
11343 // read the whole pixel before writing any of it - the encode can be in place, and a swizzle reads a channel after
11344 // the one it lands on has been overwritten
11345 stbir__simdf e0;
11346 #if stbir__coder_min_num >= 2
11347 stbir__simdf e1;
11348 #endif
11349 #if stbir__coder_min_num >= 3
11350 stbir__simdf e2;
11351 #endif
11352 STBIR_NO_UNROLL(encode);
11353 stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order0 );
11354 #if stbir__coder_min_num >= 2
11355 stbir__simdf_madd1_mem( e1, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order1 );
11356 #endif
11357 #if stbir__coder_min_num >= 3
11358 stbir__simdf_madd1_mem( e2, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order2 );
11359 #endif
11360 output[0] = stbir__simdf_convert_float_to_short( e0 );
11361 #if stbir__coder_min_num >= 2
11362 output[1] = stbir__simdf_convert_float_to_short( e1 );
11363 #endif
11364 #if stbir__coder_min_num >= 3
11365 output[2] = stbir__simdf_convert_float_to_short( e2 );
11366 #endif
11367 output += stbir__coder_min_num;
11368 encode += stbir__coder_min_num;
11369 }
11370 #endif
11371
11372 #else
11373
11374 // try to do blocks of 4 when you can
11375 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11376 output += 4;
11377 STBIR_SIMD_NO_UNROLL_LOOP_START
11378 while( output <= end_output )
11379 {
11380 // read the whole pixel before writing any of it - the encode can be in place, and a swizzle reads a channel after
11381 // the one it lands on has been overwritten
11382 float f0 = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f;
11383 float f1 = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f;
11384 float f2 = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f;
11385 float f3 = encode[stbir__encode_order3] * stbir__max_uint16_as_float + 0.5f;
11386 STBIR_SIMD_NO_UNROLL(encode);
11387 STBIR_CLAMP(f0, 0, 65535); output[0-4] = (unsigned short)f0;
11388 STBIR_CLAMP(f1, 0, 65535); output[1-4] = (unsigned short)f1;
11389 STBIR_CLAMP(f2, 0, 65535); output[2-4] = (unsigned short)f2;
11390 STBIR_CLAMP(f3, 0, 65535); output[3-4] = (unsigned short)f3;
11391 output += 4;
11392 encode += 4;
11393 }
11394 output -= 4;
11395 #endif
11396
11397 // do the remnants
11398 #if stbir__coder_min_num < 4
11399 STBIR_NO_UNROLL_LOOP_START
11400 while( output < end_output )
11401 {
11402 float f0 = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f;
11403 #if stbir__coder_min_num >= 2
11404 float f1 = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f;
11405 #endif
11406 #if stbir__coder_min_num >= 3
11407 float f2 = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f;
11408 #endif
11409 STBIR_NO_UNROLL(encode);
11410 STBIR_CLAMP(f0, 0, 65535); output[0] = (unsigned short)f0;
11411 #if stbir__coder_min_num >= 2
11412 STBIR_CLAMP(f1, 0, 65535); output[1] = (unsigned short)f1;
11413 #endif
11414 #if stbir__coder_min_num >= 3
11415 STBIR_CLAMP(f2, 0, 65535); output[2] = (unsigned short)f2;
11416 #endif
11417 output += stbir__coder_min_num;
11418 encode += stbir__coder_min_num;
11419 }
11420 #endif
11421 #endif
11422}
11423
11424static float * STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, ptrdiff_t width_times_channels, void const * inputp )
11425{
11426 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
11427 float * decode_end = (float*) decode + width_times_channels;
11428 unsigned short const * input = (unsigned short const *)inputp;
11429
11430 #ifdef STBIR_SIMD
11431 unsigned short const * end_input_mb = input + width_times_channels - STBIR__U16_DECODE_BLOCK;
11432 if ( width_times_channels >= STBIR__U16_DECODE_BLOCK )
11433 {
11434 decode_end -= STBIR__U16_DECODE_BLOCK;
11435 STBIR_NO_UNROLL_LOOP_START_INF_FOR
11436 for(;;)
11437 {
11438 #if defined(STBIR_SIMD16)
11439 stbir__simdi8 i; stbir__simdi16 o;
11440 stbir__simdf16 of;
11441 STBIR_NO_UNROLL(decode);
11442 stbir__simdi8_load( i, input );
11443 stbir__decode_simdi8_flip_u16( i );
11444 stbir__simdi16_expand_u16_to_u32( o, i );
11445 stbir__simdi16_convert_i32_to_float( of, o );
11446 stbir__decode_simdf16_flip_i( of );
11447 stbir__simdf16_store( decode, of );
11448 #elif defined(STBIR_SIMD8)
11449 stbir__simdi i; stbir__simdi8 o;
11450 stbir__simdf8 of;
11451 STBIR_NO_UNROLL(decode);
11452 stbir__simdi_load( i, input );
11453 stbir__decode_simdi_flip_u16( i );
11454 stbir__simdi8_expand_u16_to_u32( o, i );
11455 stbir__simdi8_convert_i32_to_float( of, o );
11456 stbir__decode_simdf8_flip_i( of );
11457 stbir__simdf8_store( decode + 0, of );
11458 #else
11459 stbir__simdi i, o0, o1;
11460 stbir__simdf of0, of1;
11461 STBIR_NO_UNROLL(decode);
11462 stbir__simdi_load( i, input );
11463 stbir__decode_simdi_flip_u16( i );
11464 stbir__simdi_expand_u16_to_u32( o0, o1, i );
11465 stbir__simdi_convert_i32_to_float( of0, o0 );
11466 stbir__simdi_convert_i32_to_float( of1, o1 );
11467 stbir__decode_simdf4_flip_i( of0 );
11468 stbir__decode_simdf4_flip_i( of1 );
11469 stbir__simdf_store( decode + 0, of0 );
11470 stbir__simdf_store( decode + 4, of1 );
11471 #endif
11472 decode += STBIR__U16_DECODE_BLOCK;
11473 input += STBIR__U16_DECODE_BLOCK;
11474 if ( decode <= decode_end )
11475 continue;
11476 if ( decode == ( decode_end + STBIR__U16_DECODE_BLOCK ) )
11477 break;
11478 decode = decode_end; // backup and do last couple
11479 input = end_input_mb;
11480 }
11481 return decode_end + STBIR__U16_DECODE_BLOCK;
11482 }
11483 #endif
11484
11485 // try to do blocks of 4 when you can
11486 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11487 decode += 4;
11488 STBIR_SIMD_NO_UNROLL_LOOP_START
11489 while( decode <= decode_end )
11490 {
11491 STBIR_SIMD_NO_UNROLL(decode);
11492 decode[0-4] = ((float)(input[stbir__decode_order0]));
11493 decode[1-4] = ((float)(input[stbir__decode_order1]));
11494 decode[2-4] = ((float)(input[stbir__decode_order2]));
11495 decode[3-4] = ((float)(input[stbir__decode_order3]));
11496 decode += 4;
11497 input += 4;
11498 }
11499 decode -= 4;
11500 #endif
11501
11502 // do the remnants
11503 #if stbir__coder_min_num < 4
11504 STBIR_NO_UNROLL_LOOP_START
11505 while( decode < decode_end )
11506 {
11507 STBIR_NO_UNROLL(decode);
11508 decode[0] = ((float)(input[stbir__decode_order0]));
11509 #if stbir__coder_min_num >= 2
11510 decode[1] = ((float)(input[stbir__decode_order1]));
11511 #endif
11512 #if stbir__coder_min_num >= 3
11513 decode[2] = ((float)(input[stbir__decode_order2]));
11514 #endif
11515 decode += stbir__coder_min_num;
11516 input += stbir__coder_min_num;
11517 }
11518 #endif
11519 return decode_end;
11520}
11521
11522static void STBIR__CODER_NAME(stbir__encode_uint16_linear)( void * outputp, ptrdiff_t width_times_channels, float const * encode )
11523{
11524 unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp;
11525 unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels;
11526
11527 #ifdef STBIR_SIMD
11528 {
11529 // double width blocks while they fit, then the four-at-a-time and scalar loops below finish the row. (the encode can
11530 // be in place - with an output callback it converts into the float buffer - so no backing up to redo the last block)
11531 STBIR_SIMD_NO_UNROLL_LOOP_START
11532 while ( ( end_output - output ) >= stbir__simdfX_float_count*2 )
11533 {
11534 stbir__simdfX e0, e1;
11535 stbir__simdiX i;
11536 STBIR_SIMD_NO_UNROLL(encode);
11537 stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode );
11538 stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count );
11539 stbir__encode_simdfX_unflip( e0 );
11540 stbir__encode_simdfX_unflip( e1 );
11541 stbir__simdfX_pack_to_words( i, e0, e1 );
11542 stbir__simdiX_store( output, i );
11543 encode += stbir__simdfX_float_count*2;
11544 output += stbir__simdfX_float_count*2;
11545 }
11546 }
11547
11548 // try to do blocks of 4 when you can
11549 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11550 output += 4;
11551 STBIR_NO_UNROLL_LOOP_START
11552 while( output <= end_output )
11553 {
11554 stbir__simdf e;
11555 stbir__simdi i;
11556 STBIR_NO_UNROLL(encode);
11557 stbir__simdf_load( e, encode );
11558 stbir__simdf_add( e, STBIR__CONSTF(STBIR_simd_point5), e );
11559 stbir__encode_simdf4_unflip( e );
11560 stbir__simdf_pack_to_8words( i, e, e ); // only use first 4
11561 stbir__simdi_store2( output-4, i );
11562 output += 4;
11563 encode += 4;
11564 }
11565 output -= 4;
11566 #endif
11567
11568 #else
11569
11570 // try to do blocks of 4 when you can
11571 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11572 output += 4;
11573 STBIR_SIMD_NO_UNROLL_LOOP_START
11574 while( output <= end_output )
11575 {
11576 // read the whole pixel before writing any of it - the encode can be in place, and a swizzle reads a channel after
11577 // the one it lands on has been overwritten
11578 float f0 = encode[stbir__encode_order0] + 0.5f;
11579 float f1 = encode[stbir__encode_order1] + 0.5f;
11580 float f2 = encode[stbir__encode_order2] + 0.5f;
11581 float f3 = encode[stbir__encode_order3] + 0.5f;
11582 STBIR_SIMD_NO_UNROLL(encode);
11583 STBIR_CLAMP(f0, 0, 65535); output[0-4] = (unsigned short)f0;
11584 STBIR_CLAMP(f1, 0, 65535); output[1-4] = (unsigned short)f1;
11585 STBIR_CLAMP(f2, 0, 65535); output[2-4] = (unsigned short)f2;
11586 STBIR_CLAMP(f3, 0, 65535); output[3-4] = (unsigned short)f3;
11587 output += 4;
11588 encode += 4;
11589 }
11590 output -= 4;
11591 #endif
11592
11593 #endif
11594
11595 // do the remnants
11596 #if stbir__coder_min_num < 4
11597 STBIR_NO_UNROLL_LOOP_START
11598 while( output < end_output )
11599 {
11600 float f0 = encode[stbir__encode_order0] + 0.5f;
11601 #if stbir__coder_min_num >= 2
11602 float f1 = encode[stbir__encode_order1] + 0.5f;
11603 #endif
11604 #if stbir__coder_min_num >= 3
11605 float f2 = encode[stbir__encode_order2] + 0.5f;
11606 #endif
11607 STBIR_NO_UNROLL(encode);
11608 STBIR_CLAMP(f0, 0, 65535); output[0] = (unsigned short)f0;
11609 #if stbir__coder_min_num >= 2
11610 STBIR_CLAMP(f1, 0, 65535); output[1] = (unsigned short)f1;
11611 #endif
11612 #if stbir__coder_min_num >= 3
11613 STBIR_CLAMP(f2, 0, 65535); output[2] = (unsigned short)f2;
11614 #endif
11615 output += stbir__coder_min_num;
11616 encode += stbir__coder_min_num;
11617 }
11618 #endif
11619}
11620
11621static float * STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, ptrdiff_t width_times_channels, void const * inputp )
11622{
11623 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
11624 float * decode_end = (float*) decode + width_times_channels;
11625 stbir__FP16 const * input = (stbir__FP16 const *)inputp;
11626
11627 #ifdef STBIR_SIMD
11628 if ( width_times_channels >= 8 )
11629 {
11630 stbir__FP16 const * end_input_m8 = input + width_times_channels - 8;
11631 decode_end -= 8;
11632 STBIR_NO_UNROLL_LOOP_START_INF_FOR
11633 for(;;)
11634 {
11635 STBIR_NO_UNROLL(decode);
11636
11637 stbir__half_to_float_SIMD( decode, input );
11638 #ifdef stbir__decode_swizzle
11639 #ifdef STBIR_SIMD8
11640 {
11641 stbir__simdf8 of;
11642 stbir__simdf8_load( of, decode );
11643 stbir__decode_simdf8_flip( of );
11644 stbir__simdf8_store( decode, of );
11645 }
11646 #else
11647 {
11648 stbir__simdf of0,of1;
11649 stbir__simdf_load( of0, decode );
11650 stbir__simdf_load( of1, decode+4 );
11651 stbir__decode_simdf4_flip( of0 );
11652 stbir__decode_simdf4_flip( of1 );
11653 stbir__simdf_store( decode, of0 );
11654 stbir__simdf_store( decode+4, of1 );
11655 }
11656 #endif
11657 #endif
11658 decode += 8;
11659 input += 8;
11660 if ( decode <= decode_end )
11661 continue;
11662 if ( decode == ( decode_end + 8 ) )
11663 break;
11664 decode = decode_end; // backup and do last couple
11665 input = end_input_m8;
11666 }
11667 return decode_end + 8;
11668 }
11669 #endif
11670
11671 // try to do blocks of 4 when you can
11672 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11673 decode += 4;
11674 STBIR_SIMD_NO_UNROLL_LOOP_START
11675 while( decode <= decode_end )
11676 {
11677 STBIR_SIMD_NO_UNROLL(decode);
11678 decode[0-4] = stbir__half_to_float(input[stbir__decode_order0]);
11679 decode[1-4] = stbir__half_to_float(input[stbir__decode_order1]);
11680 decode[2-4] = stbir__half_to_float(input[stbir__decode_order2]);
11681 decode[3-4] = stbir__half_to_float(input[stbir__decode_order3]);
11682 decode += 4;
11683 input += 4;
11684 }
11685 decode -= 4;
11686 #endif
11687
11688 // do the remnants
11689 #if stbir__coder_min_num < 4
11690 STBIR_NO_UNROLL_LOOP_START
11691 while( decode < decode_end )
11692 {
11693 STBIR_NO_UNROLL(decode);
11694 decode[0] = stbir__half_to_float(input[stbir__decode_order0]);
11695 #if stbir__coder_min_num >= 2
11696 decode[1] = stbir__half_to_float(input[stbir__decode_order1]);
11697 #endif
11698 #if stbir__coder_min_num >= 3
11699 decode[2] = stbir__half_to_float(input[stbir__decode_order2]);
11700 #endif
11701 decode += stbir__coder_min_num;
11702 input += stbir__coder_min_num;
11703 }
11704 #endif
11705 return decode_end;
11706}
11707
11708static void STBIR__CODER_NAME( stbir__encode_half_float_linear )( void * outputp, ptrdiff_t width_times_channels, float const * encode )
11709{
11710 stbir__FP16 STBIR_SIMD_STREAMOUT_PTR( * ) output = (stbir__FP16*) outputp;
11711 stbir__FP16 * end_output = ( (stbir__FP16*) output ) + width_times_channels;
11712
11713 #ifdef STBIR_SIMD
11714 // double width blocks while they fit, then the four-at-a-time and scalar loops below finish the row. (the encode can
11715 // be in place - with an output callback it converts into the float buffer - so no backing up to redo the last block)
11716 STBIR_SIMD_NO_UNROLL_LOOP_START
11717 while ( ( end_output - output ) >= 8 )
11718 {
11719 STBIR_SIMD_NO_UNROLL(encode);
11720 #ifdef stbir__decode_swizzle
11721 #ifdef STBIR_SIMD8
11722 {
11723 stbir__simdf8 of;
11724 stbir__simdf8_load( of, encode );
11725 stbir__encode_simdf8_unflip( of );
11726 stbir__float_to_half_SIMD( output, (float*)&of );
11727 }
11728 #else
11729 {
11730 stbir__simdf of[2];
11731 stbir__simdf_load( of[0], encode );
11732 stbir__simdf_load( of[1], encode+4 );
11733 stbir__encode_simdf4_unflip( of[0] );
11734 stbir__encode_simdf4_unflip( of[1] );
11735 stbir__float_to_half_SIMD( output, (float*)of );
11736 }
11737 #endif
11738 #else
11739 stbir__float_to_half_SIMD( output, encode );
11740 #endif
11741 encode += 8;
11742 output += 8;
11743 }
11744 #endif
11745
11746 // try to do blocks of 4 when you can
11747 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11748 output += 4;
11749 STBIR_SIMD_NO_UNROLL_LOOP_START
11750 while( output <= end_output )
11751 {
11752 float e0, e1, e2, e3;
11753 STBIR_SIMD_NO_UNROLL(output);
11754 e0 = encode[stbir__encode_order0];
11755 e1 = encode[stbir__encode_order1];
11756 e2 = encode[stbir__encode_order2];
11757 e3 = encode[stbir__encode_order3];
11758 output[0-4] = stbir__float_to_half(e0);
11759 output[1-4] = stbir__float_to_half(e1);
11760 output[2-4] = stbir__float_to_half(e2);
11761 output[3-4] = stbir__float_to_half(e3);
11762 output += 4;
11763 encode += 4;
11764 }
11765 output -= 4;
11766 #endif
11767
11768 // do the remnants
11769 #if stbir__coder_min_num < 4
11770 STBIR_NO_UNROLL_LOOP_START
11771 while( output < end_output )
11772 {
11773 float e0;
11774 #if stbir__coder_min_num >= 2
11775 float e1;
11776 #endif
11777 #if stbir__coder_min_num >= 3
11778 float e2;
11779 #endif
11780 STBIR_NO_UNROLL(output);
11781 e0 = encode[stbir__encode_order0];
11782 #if stbir__coder_min_num >= 2
11783 e1 = encode[stbir__encode_order1];
11784 #endif
11785 #if stbir__coder_min_num >= 3
11786 e2 = encode[stbir__encode_order2];
11787 #endif
11788 output[0] = stbir__float_to_half(e0);
11789 #if stbir__coder_min_num >= 2
11790 output[1] = stbir__float_to_half(e1);
11791 #endif
11792 #if stbir__coder_min_num >= 3
11793 output[2] = stbir__float_to_half(e2);
11794 #endif
11795 output += stbir__coder_min_num;
11796 encode += stbir__coder_min_num;
11797 }
11798 #endif
11799}
11800
11801static float * STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, ptrdiff_t width_times_channels, void const * inputp )
11802{
11803 #ifdef stbir__decode_swizzle
11804 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
11805 float * decode_end = (float*) decode + width_times_channels;
11806 float const * input = (float const *)inputp;
11807
11808 #ifdef STBIR_SIMD
11809 if ( width_times_channels >= 16 )
11810 {
11811 float const * end_input_m16 = input + width_times_channels - 16;
11812 decode_end -= 16;
11813 STBIR_NO_UNROLL_LOOP_START_INF_FOR
11814 for(;;)
11815 {
11816 STBIR_NO_UNROLL(decode);
11817 #ifdef STBIR_SIMD8
11818 {
11819 stbir__simdf8 of0,of1;
11820 stbir__simdf8_load( of0, input );
11821 stbir__simdf8_load( of1, input+8 );
11822 stbir__decode_simdf8_flip( of0 );
11823 stbir__decode_simdf8_flip( of1 );
11824 stbir__simdf8_store( decode, of0 );
11825 stbir__simdf8_store( decode+8, of1 );
11826 }
11827 #else
11828 {
11829 stbir__simdf of0,of1,of2,of3;
11830 stbir__simdf_load( of0, input );
11831 stbir__simdf_load( of1, input+4 );
11832 stbir__simdf_load( of2, input+8 );
11833 stbir__simdf_load( of3, input+12 );
11834 stbir__decode_simdf4_flip( of0 );
11835 stbir__decode_simdf4_flip( of1 );
11836 stbir__decode_simdf4_flip( of2 );
11837 stbir__decode_simdf4_flip( of3 );
11838 stbir__simdf_store( decode, of0 );
11839 stbir__simdf_store( decode+4, of1 );
11840 stbir__simdf_store( decode+8, of2 );
11841 stbir__simdf_store( decode+12, of3 );
11842 }
11843 #endif
11844 decode += 16;
11845 input += 16;
11846 if ( decode <= decode_end )
11847 continue;
11848 if ( decode == ( decode_end + 16 ) )
11849 break;
11850 decode = decode_end; // backup and do last couple
11851 input = end_input_m16;
11852 }
11853 return decode_end + 16;
11854 }
11855 #endif
11856
11857 // try to do blocks of 4 when you can
11858 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11859 decode += 4;
11860 STBIR_SIMD_NO_UNROLL_LOOP_START
11861 while( decode <= decode_end )
11862 {
11863 STBIR_SIMD_NO_UNROLL(decode);
11864 decode[0-4] = input[stbir__decode_order0];
11865 decode[1-4] = input[stbir__decode_order1];
11866 decode[2-4] = input[stbir__decode_order2];
11867 decode[3-4] = input[stbir__decode_order3];
11868 decode += 4;
11869 input += 4;
11870 }
11871 decode -= 4;
11872 #endif
11873
11874 // do the remnants
11875 #if stbir__coder_min_num < 4
11876 STBIR_NO_UNROLL_LOOP_START
11877 while( decode < decode_end )
11878 {
11879 STBIR_NO_UNROLL(decode);
11880 decode[0] = input[stbir__decode_order0];
11881 #if stbir__coder_min_num >= 2
11882 decode[1] = input[stbir__decode_order1];
11883 #endif
11884 #if stbir__coder_min_num >= 3
11885 decode[2] = input[stbir__decode_order2];
11886 #endif
11887 decode += stbir__coder_min_num;
11888 input += stbir__coder_min_num;
11889 }
11890 #endif
11891 return decode_end;
11892
11893 #else
11894
11895 if ( (void*)decodep != inputp )
11896 STBIR_MEMCPY( decodep, inputp, width_times_channels * sizeof( float ) );
11897
11898 return decodep + width_times_channels;
11899
11900 #endif
11901}
11902
11903static void STBIR__CODER_NAME( stbir__encode_float_linear )( void * outputp, ptrdiff_t width_times_channels, float const * encode )
11904{
11905 #if !defined( STBIR_FLOAT_HIGH_CLAMP ) && !defined(STBIR_FLOAT_LOW_CLAMP) && !defined(stbir__decode_swizzle)
11906
11907 if ( (void*)outputp != (void*) encode )
11908 STBIR_MEMCPY( outputp, encode, width_times_channels * sizeof( float ) );
11909
11910 #else
11911
11912 float STBIR_SIMD_STREAMOUT_PTR( * ) output = (float*) outputp;
11913 float * end_output = ( (float*) output ) + width_times_channels;
11914
11915 #ifdef STBIR_FLOAT_HIGH_CLAMP
11916 #define stbir_scalar_hi_clamp( v ) if ( v > STBIR_FLOAT_HIGH_CLAMP ) v = STBIR_FLOAT_HIGH_CLAMP;
11917 #else
11918 #define stbir_scalar_hi_clamp( v )
11919 #endif
11920 #ifdef STBIR_FLOAT_LOW_CLAMP
11921 #define stbir_scalar_lo_clamp( v ) if ( v < STBIR_FLOAT_LOW_CLAMP ) v = STBIR_FLOAT_LOW_CLAMP;
11922 #else
11923 #define stbir_scalar_lo_clamp( v )
11924 #endif
11925
11926 #ifdef STBIR_SIMD
11927
11928 #ifdef STBIR_FLOAT_HIGH_CLAMP
11929 const stbir__simdfX high_clamp = stbir__simdf_frepX(STBIR_FLOAT_HIGH_CLAMP);
11930 #endif
11931 #ifdef STBIR_FLOAT_LOW_CLAMP
11932 const stbir__simdfX low_clamp = stbir__simdf_frepX(STBIR_FLOAT_LOW_CLAMP);
11933 #endif
11934
11935 // do as many double width blocks as fit and let the four-at-a-time and scalar loops below finish the row. (with an
11936 // output callback we encode in place, so we can't use the usual trick of backing up and redoing the last block)
11937 STBIR_SIMD_NO_UNROLL_LOOP_START
11938 while ( ( end_output - output ) >= ( stbir__simdfX_float_count * 2 ) )
11939 {
11940 stbir__simdfX e0, e1;
11941 STBIR_SIMD_NO_UNROLL(encode);
11942 stbir__simdfX_load( e0, encode );
11943 stbir__simdfX_load( e1, encode+stbir__simdfX_float_count );
11944#ifdef STBIR_FLOAT_HIGH_CLAMP
11945 stbir__simdfX_min( e0, e0, high_clamp );
11946 stbir__simdfX_min( e1, e1, high_clamp );
11947#endif
11948#ifdef STBIR_FLOAT_LOW_CLAMP
11949 stbir__simdfX_max( e0, e0, low_clamp );
11950 stbir__simdfX_max( e1, e1, low_clamp );
11951#endif
11952 stbir__encode_simdfX_unflip( e0 );
11953 stbir__encode_simdfX_unflip( e1 );
11954 stbir__simdfX_store( output, e0 );
11955 stbir__simdfX_store( output+stbir__simdfX_float_count, e1 );
11956 encode += stbir__simdfX_float_count * 2;
11957 output += stbir__simdfX_float_count * 2;
11958 }
11959
11960 // try to do blocks of 4 when you can
11961 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11962 output += 4;
11963 STBIR_NO_UNROLL_LOOP_START
11964 while( output <= end_output )
11965 {
11966 stbir__simdf e0;
11967 STBIR_NO_UNROLL(encode);
11968 stbir__simdf_load( e0, encode );
11969#ifdef STBIR_FLOAT_HIGH_CLAMP
11970 stbir__simdf_min( e0, e0, stbir__simdfX_cast_to_simdf4( high_clamp ) );
11971#endif
11972#ifdef STBIR_FLOAT_LOW_CLAMP
11973 stbir__simdf_max( e0, e0, stbir__simdfX_cast_to_simdf4( low_clamp ) );
11974#endif
11975 stbir__encode_simdf4_unflip( e0 );
11976 stbir__simdf_store( output-4, e0 );
11977 output += 4;
11978 encode += 4;
11979 }
11980 output -= 4;
11981 #endif
11982
11983 #else
11984
11985 // try to do blocks of 4 when you can
11986 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
11987 output += 4;
11988 STBIR_SIMD_NO_UNROLL_LOOP_START
11989 while( output <= end_output )
11990 {
11991 // read everything before writing anything - with an output callback we encode in place, so this is an in-place unflip
11992 float e0 = encode[ stbir__encode_order0 ];
11993 float e1 = encode[ stbir__encode_order1 ];
11994 float e2 = encode[ stbir__encode_order2 ];
11995 float e3 = encode[ stbir__encode_order3 ];
11996 STBIR_SIMD_NO_UNROLL(encode);
11997 stbir_scalar_hi_clamp( e0 );
11998 stbir_scalar_lo_clamp( e0 );
11999 stbir_scalar_hi_clamp( e1 );
12000 stbir_scalar_lo_clamp( e1 );
12001 stbir_scalar_hi_clamp( e2 );
12002 stbir_scalar_lo_clamp( e2 );
12003 stbir_scalar_hi_clamp( e3 );
12004 stbir_scalar_lo_clamp( e3 );
12005 output[0-4] = e0;
12006 output[1-4] = e1;
12007 output[2-4] = e2;
12008 output[3-4] = e3;
12009 output += 4;
12010 encode += 4;
12011 }
12012 output -= 4;
12013
12014 #endif
12015
12016 #endif
12017
12018 // do the remnants
12019 #if stbir__coder_min_num < 4
12020 STBIR_NO_UNROLL_LOOP_START
12021 while( output < end_output )
12022 {
12023 float e0 = encode[ stbir__encode_order0 ];
12024 #if stbir__coder_min_num >= 2
12025 float e1 = encode[ stbir__encode_order1 ];
12026 #endif
12027 #if stbir__coder_min_num >= 3
12028 float e2 = encode[ stbir__encode_order2 ];
12029 #endif
12030 STBIR_NO_UNROLL(encode);
12031 stbir_scalar_hi_clamp( e0 ); stbir_scalar_lo_clamp( e0 ); output[0] = e0;
12032 #if stbir__coder_min_num >= 2
12033 stbir_scalar_hi_clamp( e1 ); stbir_scalar_lo_clamp( e1 ); output[1] = e1;
12034 #endif
12035 #if stbir__coder_min_num >= 3
12036 stbir_scalar_hi_clamp( e2 ); stbir_scalar_lo_clamp( e2 ); output[2] = e2;
12037 #endif
12038 output += stbir__coder_min_num;
12039 encode += stbir__coder_min_num;
12040 }
12041 #endif
12042
12043 #endif
12044}
12045
12046#undef stbir__decode_suffix
12047#undef stbir__decode_simdf8_flip
12048#undef stbir__decode_simdf8_flip_i
12049#undef stbir__decode_simdf4_flip_i
12050#undef stbir__decode_simdi_flip_u8
12051#undef stbir__decode_simdi_flip_u16
12052#ifdef STBIR__SWZ8
12053#undef STBIR__SWZ8
12054#undef STBIR__SWZ16
12055#endif
12056#undef stbir__decode_simdf4_flip
12057#undef stbir__decode_order0
12058#undef stbir__decode_order1
12059#undef stbir__decode_order2
12060#undef stbir__decode_order3
12061#undef stbir__encode_order0
12062#undef stbir__encode_order1
12063#undef stbir__encode_order2
12064#undef stbir__encode_order3
12065#undef stbir__encode_simdf8_unflip
12066#undef stbir__encode_simdf16_unflip
12067#undef stbir__decode_simdf16_flip_i
12068#undef stbir__decode_simdi8_flip_u16
12069#undef stbir__encode_simdf4_unflip
12070#undef stbir__encode_simdfX_unflip
12071#undef STBIR__CODER_NAME
12072#undef stbir__coder_min_num
12073#undef stbir__decode_swizzle
12074#undef stbir_scalar_hi_clamp
12075#undef stbir_scalar_lo_clamp
12076#undef STB_IMAGE_RESIZE_DO_CODERS
12077
12078#elif defined( STB_IMAGE_RESIZE_DO_VERTICALS)
12079
12080#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12081#define STBIR_chans( start, end ) STBIR_strs_join14(start,STBIR__vertical_channels,end,_cont)
12082#else
12083#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__vertical_channels,end)
12084#endif
12085
12086#if STBIR__vertical_channels >= 1
12087#define stbIF0( code ) code
12088#else
12089#define stbIF0( code )
12090#endif
12091#if STBIR__vertical_channels >= 2
12092#define stbIF1( code ) code
12093#else
12094#define stbIF1( code )
12095#endif
12096#if STBIR__vertical_channels >= 3
12097#define stbIF2( code ) code
12098#else
12099#define stbIF2( code )
12100#endif
12101#if STBIR__vertical_channels >= 4
12102#define stbIF3( code ) code
12103#else
12104#define stbIF3( code )
12105#endif
12106#if STBIR__vertical_channels >= 5
12107#define stbIF4( code ) code
12108#else
12109#define stbIF4( code )
12110#endif
12111#if STBIR__vertical_channels >= 6
12112#define stbIF5( code ) code
12113#else
12114#define stbIF5( code )
12115#endif
12116#if STBIR__vertical_channels >= 7
12117#define stbIF6( code ) code
12118#else
12119#define stbIF6( code )
12120#endif
12121#if STBIR__vertical_channels >= 8
12122#define stbIF7( code ) code
12123#else
12124#define stbIF7( code )
12125#endif
12126
12127static void STBIR_chans( stbir__vertical_scatter_with_,_coeffs)( float ** outputs, float const * vertical_coefficients, float const * input, float const * input_end )
12128{
12129 stbIF0( float STBIR_SIMD_STREAMOUT_PTR( * ) output0 = outputs[0]; float c0s = vertical_coefficients[0]; )
12130 stbIF1( float STBIR_SIMD_STREAMOUT_PTR( * ) output1 = outputs[1]; float c1s = vertical_coefficients[1]; )
12131 stbIF2( float STBIR_SIMD_STREAMOUT_PTR( * ) output2 = outputs[2]; float c2s = vertical_coefficients[2]; )
12132 stbIF3( float STBIR_SIMD_STREAMOUT_PTR( * ) output3 = outputs[3]; float c3s = vertical_coefficients[3]; )
12133 stbIF4( float STBIR_SIMD_STREAMOUT_PTR( * ) output4 = outputs[4]; float c4s = vertical_coefficients[4]; )
12134 stbIF5( float STBIR_SIMD_STREAMOUT_PTR( * ) output5 = outputs[5]; float c5s = vertical_coefficients[5]; )
12135 stbIF6( float STBIR_SIMD_STREAMOUT_PTR( * ) output6 = outputs[6]; float c6s = vertical_coefficients[6]; )
12136 stbIF7( float STBIR_SIMD_STREAMOUT_PTR( * ) output7 = outputs[7]; float c7s = vertical_coefficients[7]; )
12137
12138 #ifdef STBIR_SIMD
12139 {
12140 stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); )
12141 stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); )
12142 stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); )
12143 stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); )
12144 stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); )
12145 stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); )
12146 stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); )
12147 stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); )
12148 STBIR_SIMD_NO_UNROLL_LOOP_START
12149 while ( ( (char*)input_end - (char*) input ) >= (16*stbir__simdfX_float_count) )
12150 {
12151 stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3;
12152 STBIR_SIMD_NO_UNROLL(output0);
12153
12154 stbir__simdfX_load( r0, input ); stbir__simdfX_load( r1, input+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input+(3*stbir__simdfX_float_count) );
12155
12156 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12157 stbIF0( stbir__simdfX_load( o0, output0 ); stbir__simdfX_load( o1, output0+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output0+(3*stbir__simdfX_float_count) );
12158 stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 );
12159 stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); )
12160 stbIF1( stbir__simdfX_load( o0, output1 ); stbir__simdfX_load( o1, output1+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output1+(3*stbir__simdfX_float_count) );
12161 stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 );
12162 stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); )
12163 stbIF2( stbir__simdfX_load( o0, output2 ); stbir__simdfX_load( o1, output2+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output2+(3*stbir__simdfX_float_count) );
12164 stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 );
12165 stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); )
12166 stbIF3( stbir__simdfX_load( o0, output3 ); stbir__simdfX_load( o1, output3+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output3+(3*stbir__simdfX_float_count) );
12167 stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 );
12168 stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); )
12169 stbIF4( stbir__simdfX_load( o0, output4 ); stbir__simdfX_load( o1, output4+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output4+(3*stbir__simdfX_float_count) );
12170 stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 );
12171 stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); )
12172 stbIF5( stbir__simdfX_load( o0, output5 ); stbir__simdfX_load( o1, output5+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output5+(2*stbir__simdfX_float_count)); stbir__simdfX_load( o3, output5+(3*stbir__simdfX_float_count) );
12173 stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 );
12174 stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); )
12175 stbIF6( stbir__simdfX_load( o0, output6 ); stbir__simdfX_load( o1, output6+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output6+(3*stbir__simdfX_float_count) );
12176 stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 );
12177 stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); )
12178 stbIF7( stbir__simdfX_load( o0, output7 ); stbir__simdfX_load( o1, output7+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output7+(3*stbir__simdfX_float_count) );
12179 stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 );
12180 stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); )
12181 #else
12182 stbIF0( stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 );
12183 stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); )
12184 stbIF1( stbir__simdfX_mult( o0, r0, c1 ); stbir__simdfX_mult( o1, r1, c1 ); stbir__simdfX_mult( o2, r2, c1 ); stbir__simdfX_mult( o3, r3, c1 );
12185 stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); )
12186 stbIF2( stbir__simdfX_mult( o0, r0, c2 ); stbir__simdfX_mult( o1, r1, c2 ); stbir__simdfX_mult( o2, r2, c2 ); stbir__simdfX_mult( o3, r3, c2 );
12187 stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); )
12188 stbIF3( stbir__simdfX_mult( o0, r0, c3 ); stbir__simdfX_mult( o1, r1, c3 ); stbir__simdfX_mult( o2, r2, c3 ); stbir__simdfX_mult( o3, r3, c3 );
12189 stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); )
12190 stbIF4( stbir__simdfX_mult( o0, r0, c4 ); stbir__simdfX_mult( o1, r1, c4 ); stbir__simdfX_mult( o2, r2, c4 ); stbir__simdfX_mult( o3, r3, c4 );
12191 stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); )
12192 stbIF5( stbir__simdfX_mult( o0, r0, c5 ); stbir__simdfX_mult( o1, r1, c5 ); stbir__simdfX_mult( o2, r2, c5 ); stbir__simdfX_mult( o3, r3, c5 );
12193 stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); )
12194 stbIF6( stbir__simdfX_mult( o0, r0, c6 ); stbir__simdfX_mult( o1, r1, c6 ); stbir__simdfX_mult( o2, r2, c6 ); stbir__simdfX_mult( o3, r3, c6 );
12195 stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); )
12196 stbIF7( stbir__simdfX_mult( o0, r0, c7 ); stbir__simdfX_mult( o1, r1, c7 ); stbir__simdfX_mult( o2, r2, c7 ); stbir__simdfX_mult( o3, r3, c7 );
12197 stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); )
12198 #endif
12199
12200 input += (4*stbir__simdfX_float_count);
12201 stbIF0( output0 += (4*stbir__simdfX_float_count); )
12202 stbIF1( output1 += (4*stbir__simdfX_float_count); )
12203 stbIF2( output2 += (4*stbir__simdfX_float_count); )
12204 stbIF3( output3 += (4*stbir__simdfX_float_count); )
12205 stbIF4( output4 += (4*stbir__simdfX_float_count); )
12206 stbIF5( output5 += (4*stbir__simdfX_float_count); )
12207 stbIF6( output6 += (4*stbir__simdfX_float_count); )
12208 stbIF7( output7 += (4*stbir__simdfX_float_count); )
12209 }
12210 STBIR_SIMD_NO_UNROLL_LOOP_START
12211 while ( ( (char*)input_end - (char*) input ) >= 16 )
12212 {
12213 stbir__simdf o0, r0;
12214 STBIR_SIMD_NO_UNROLL(output0);
12215
12216 stbir__simdf_load( r0, input );
12217
12218 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12219 stbIF0( stbir__simdf_load( o0, output0 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); )
12220 stbIF1( stbir__simdf_load( o0, output1 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); )
12221 stbIF2( stbir__simdf_load( o0, output2 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); )
12222 stbIF3( stbir__simdf_load( o0, output3 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); )
12223 stbIF4( stbir__simdf_load( o0, output4 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); )
12224 stbIF5( stbir__simdf_load( o0, output5 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); )
12225 stbIF6( stbir__simdf_load( o0, output6 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); )
12226 stbIF7( stbir__simdf_load( o0, output7 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); )
12227 #else
12228 stbIF0( stbir__simdf_mult( o0, r0, stbir__simdfX_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); )
12229 stbIF1( stbir__simdf_mult( o0, r0, stbir__simdfX_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); )
12230 stbIF2( stbir__simdf_mult( o0, r0, stbir__simdfX_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); )
12231 stbIF3( stbir__simdf_mult( o0, r0, stbir__simdfX_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); )
12232 stbIF4( stbir__simdf_mult( o0, r0, stbir__simdfX_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); )
12233 stbIF5( stbir__simdf_mult( o0, r0, stbir__simdfX_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); )
12234 stbIF6( stbir__simdf_mult( o0, r0, stbir__simdfX_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); )
12235 stbIF7( stbir__simdf_mult( o0, r0, stbir__simdfX_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); )
12236 #endif
12237
12238 input += 4;
12239 stbIF0( output0 += 4; )
12240 stbIF1( output1 += 4; )
12241 stbIF2( output2 += 4; )
12242 stbIF3( output3 += 4; )
12243 stbIF4( output4 += 4; )
12244 stbIF5( output5 += 4; )
12245 stbIF6( output6 += 4; )
12246 stbIF7( output7 += 4; )
12247 }
12248 }
12249 #else
12250 STBIR_NO_UNROLL_LOOP_START
12251 while ( ( (char*)input_end - (char*) input ) >= 16 )
12252 {
12253 float r0, r1, r2, r3;
12254 STBIR_NO_UNROLL(input);
12255
12256 r0 = input[0], r1 = input[1], r2 = input[2], r3 = input[3];
12257
12258 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12259 stbIF0( output0[0] += ( r0 * c0s ); output0[1] += ( r1 * c0s ); output0[2] += ( r2 * c0s ); output0[3] += ( r3 * c0s ); )
12260 stbIF1( output1[0] += ( r0 * c1s ); output1[1] += ( r1 * c1s ); output1[2] += ( r2 * c1s ); output1[3] += ( r3 * c1s ); )
12261 stbIF2( output2[0] += ( r0 * c2s ); output2[1] += ( r1 * c2s ); output2[2] += ( r2 * c2s ); output2[3] += ( r3 * c2s ); )
12262 stbIF3( output3[0] += ( r0 * c3s ); output3[1] += ( r1 * c3s ); output3[2] += ( r2 * c3s ); output3[3] += ( r3 * c3s ); )
12263 stbIF4( output4[0] += ( r0 * c4s ); output4[1] += ( r1 * c4s ); output4[2] += ( r2 * c4s ); output4[3] += ( r3 * c4s ); )
12264 stbIF5( output5[0] += ( r0 * c5s ); output5[1] += ( r1 * c5s ); output5[2] += ( r2 * c5s ); output5[3] += ( r3 * c5s ); )
12265 stbIF6( output6[0] += ( r0 * c6s ); output6[1] += ( r1 * c6s ); output6[2] += ( r2 * c6s ); output6[3] += ( r3 * c6s ); )
12266 stbIF7( output7[0] += ( r0 * c7s ); output7[1] += ( r1 * c7s ); output7[2] += ( r2 * c7s ); output7[3] += ( r3 * c7s ); )
12267 #else
12268 stbIF0( output0[0] = ( r0 * c0s ); output0[1] = ( r1 * c0s ); output0[2] = ( r2 * c0s ); output0[3] = ( r3 * c0s ); )
12269 stbIF1( output1[0] = ( r0 * c1s ); output1[1] = ( r1 * c1s ); output1[2] = ( r2 * c1s ); output1[3] = ( r3 * c1s ); )
12270 stbIF2( output2[0] = ( r0 * c2s ); output2[1] = ( r1 * c2s ); output2[2] = ( r2 * c2s ); output2[3] = ( r3 * c2s ); )
12271 stbIF3( output3[0] = ( r0 * c3s ); output3[1] = ( r1 * c3s ); output3[2] = ( r2 * c3s ); output3[3] = ( r3 * c3s ); )
12272 stbIF4( output4[0] = ( r0 * c4s ); output4[1] = ( r1 * c4s ); output4[2] = ( r2 * c4s ); output4[3] = ( r3 * c4s ); )
12273 stbIF5( output5[0] = ( r0 * c5s ); output5[1] = ( r1 * c5s ); output5[2] = ( r2 * c5s ); output5[3] = ( r3 * c5s ); )
12274 stbIF6( output6[0] = ( r0 * c6s ); output6[1] = ( r1 * c6s ); output6[2] = ( r2 * c6s ); output6[3] = ( r3 * c6s ); )
12275 stbIF7( output7[0] = ( r0 * c7s ); output7[1] = ( r1 * c7s ); output7[2] = ( r2 * c7s ); output7[3] = ( r3 * c7s ); )
12276 #endif
12277
12278 input += 4;
12279 stbIF0( output0 += 4; )
12280 stbIF1( output1 += 4; )
12281 stbIF2( output2 += 4; )
12282 stbIF3( output3 += 4; )
12283 stbIF4( output4 += 4; )
12284 stbIF5( output5 += 4; )
12285 stbIF6( output6 += 4; )
12286 stbIF7( output7 += 4; )
12287 }
12288 #endif
12289 STBIR_NO_UNROLL_LOOP_START
12290 while ( input < input_end )
12291 {
12292 float r = input[0];
12293 STBIR_NO_UNROLL(output0);
12294
12295 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12296 stbIF0( output0[0] += ( r * c0s ); )
12297 stbIF1( output1[0] += ( r * c1s ); )
12298 stbIF2( output2[0] += ( r * c2s ); )
12299 stbIF3( output3[0] += ( r * c3s ); )
12300 stbIF4( output4[0] += ( r * c4s ); )
12301 stbIF5( output5[0] += ( r * c5s ); )
12302 stbIF6( output6[0] += ( r * c6s ); )
12303 stbIF7( output7[0] += ( r * c7s ); )
12304 #else
12305 stbIF0( output0[0] = ( r * c0s ); )
12306 stbIF1( output1[0] = ( r * c1s ); )
12307 stbIF2( output2[0] = ( r * c2s ); )
12308 stbIF3( output3[0] = ( r * c3s ); )
12309 stbIF4( output4[0] = ( r * c4s ); )
12310 stbIF5( output5[0] = ( r * c5s ); )
12311 stbIF6( output6[0] = ( r * c6s ); )
12312 stbIF7( output7[0] = ( r * c7s ); )
12313 #endif
12314
12315 ++input;
12316 stbIF0( ++output0; )
12317 stbIF1( ++output1; )
12318 stbIF2( ++output2; )
12319 stbIF3( ++output3; )
12320 stbIF4( ++output4; )
12321 stbIF5( ++output5; )
12322 stbIF6( ++output6; )
12323 stbIF7( ++output7; )
12324 }
12325}
12326
12327static void STBIR_chans( stbir__vertical_gather_with_,_coeffs)( float * outputp, float const * vertical_coefficients, float const ** inputs, float const * input0_end )
12328{
12329 float STBIR_SIMD_STREAMOUT_PTR( * ) output = outputp;
12330
12331 stbIF0( float const * input0 = inputs[0]; float c0s = vertical_coefficients[0]; )
12332 stbIF1( float const * input1 = inputs[1]; float c1s = vertical_coefficients[1]; )
12333 stbIF2( float const * input2 = inputs[2]; float c2s = vertical_coefficients[2]; )
12334 stbIF3( float const * input3 = inputs[3]; float c3s = vertical_coefficients[3]; )
12335 stbIF4( float const * input4 = inputs[4]; float c4s = vertical_coefficients[4]; )
12336 stbIF5( float const * input5 = inputs[5]; float c5s = vertical_coefficients[5]; )
12337 stbIF6( float const * input6 = inputs[6]; float c6s = vertical_coefficients[6]; )
12338 stbIF7( float const * input7 = inputs[7]; float c7s = vertical_coefficients[7]; )
12339
12340#if ( STBIR__vertical_channels == 1 ) && !defined(STB_IMAGE_RESIZE_VERTICAL_CONTINUE)
12341 // one coefficient within a millionth of 1 (any channel count, the single coefficient instantiation): a copy
12342 if ( ( c0s >= (1.0f-0.000001f) ) && ( c0s <= (1.0f+0.000001f) ) )
12343 {
12344 STBIR_MEMCPY( output, input0, (char*)input0_end - (char*)input0 );
12345 return;
12346 }
12347#endif
12348
12349 #ifdef STBIR_SIMD
12350 {
12351 stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); )
12352 stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); )
12353 stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); )
12354 stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); )
12355 stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); )
12356 stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); )
12357 stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); )
12358 stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); )
12359
12360 STBIR_SIMD_NO_UNROLL_LOOP_START
12361 while ( ( (char*)input0_end - (char*) input0 ) >= (16*stbir__simdfX_float_count) )
12362 {
12363 stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3;
12364 STBIR_SIMD_NO_UNROLL(output);
12365
12366 // prefetch four loop iterations ahead (doesn't affect much for small resizes, but helps with big ones)
12367 stbIF0( stbir__prefetch( input0 + (16*stbir__simdfX_float_count) ); )
12368 stbIF1( stbir__prefetch( input1 + (16*stbir__simdfX_float_count) ); )
12369 stbIF2( stbir__prefetch( input2 + (16*stbir__simdfX_float_count) ); )
12370 stbIF3( stbir__prefetch( input3 + (16*stbir__simdfX_float_count) ); )
12371 stbIF4( stbir__prefetch( input4 + (16*stbir__simdfX_float_count) ); )
12372 stbIF5( stbir__prefetch( input5 + (16*stbir__simdfX_float_count) ); )
12373 stbIF6( stbir__prefetch( input6 + (16*stbir__simdfX_float_count) ); )
12374 stbIF7( stbir__prefetch( input7 + (16*stbir__simdfX_float_count) ); )
12375
12376 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12377 stbIF0( stbir__simdfX_load( o0, output ); stbir__simdfX_load( o1, output+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output+(3*stbir__simdfX_float_count) );
12378 stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) );
12379 stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 ); )
12380 #else
12381 stbIF0( stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) );
12382 stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); )
12383 #endif
12384
12385 stbIF1( stbir__simdfX_load( r0, input1 ); stbir__simdfX_load( r1, input1+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input1+(3*stbir__simdfX_float_count) );
12386 stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 ); )
12387 stbIF2( stbir__simdfX_load( r0, input2 ); stbir__simdfX_load( r1, input2+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input2+(3*stbir__simdfX_float_count) );
12388 stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 ); )
12389 stbIF3( stbir__simdfX_load( r0, input3 ); stbir__simdfX_load( r1, input3+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input3+(3*stbir__simdfX_float_count) );
12390 stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 ); )
12391 stbIF4( stbir__simdfX_load( r0, input4 ); stbir__simdfX_load( r1, input4+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input4+(3*stbir__simdfX_float_count) );
12392 stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 ); )
12393 stbIF5( stbir__simdfX_load( r0, input5 ); stbir__simdfX_load( r1, input5+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input5+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input5+(3*stbir__simdfX_float_count) );
12394 stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 ); )
12395 stbIF6( stbir__simdfX_load( r0, input6 ); stbir__simdfX_load( r1, input6+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input6+(3*stbir__simdfX_float_count) );
12396 stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 ); )
12397 stbIF7( stbir__simdfX_load( r0, input7 ); stbir__simdfX_load( r1, input7+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input7+(3*stbir__simdfX_float_count) );
12398 stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 ); )
12399
12400 stbir__simdfX_store( output, o0 ); stbir__simdfX_store( output+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output+(3*stbir__simdfX_float_count), o3 );
12401 output += (4*stbir__simdfX_float_count);
12402 stbIF0( input0 += (4*stbir__simdfX_float_count); )
12403 stbIF1( input1 += (4*stbir__simdfX_float_count); )
12404 stbIF2( input2 += (4*stbir__simdfX_float_count); )
12405 stbIF3( input3 += (4*stbir__simdfX_float_count); )
12406 stbIF4( input4 += (4*stbir__simdfX_float_count); )
12407 stbIF5( input5 += (4*stbir__simdfX_float_count); )
12408 stbIF6( input6 += (4*stbir__simdfX_float_count); )
12409 stbIF7( input7 += (4*stbir__simdfX_float_count); )
12410 }
12411
12412 STBIR_SIMD_NO_UNROLL_LOOP_START
12413 while ( ( (char*)input0_end - (char*) input0 ) >= 16 )
12414 {
12415 stbir__simdf o0, r0;
12416 STBIR_SIMD_NO_UNROLL(output);
12417
12418 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12419 stbIF0( stbir__simdf_load( o0, output ); stbir__simdf_load( r0, input0 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c0 ) ); )
12420 #else
12421 stbIF0( stbir__simdf_load( r0, input0 ); stbir__simdf_mult( o0, r0, stbir__simdfX_cast_to_simdf4( c0 ) ); )
12422 #endif
12423 stbIF1( stbir__simdf_load( r0, input1 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c1 ) ); )
12424 stbIF2( stbir__simdf_load( r0, input2 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c2 ) ); )
12425 stbIF3( stbir__simdf_load( r0, input3 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c3 ) ); )
12426 stbIF4( stbir__simdf_load( r0, input4 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c4 ) ); )
12427 stbIF5( stbir__simdf_load( r0, input5 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c5 ) ); )
12428 stbIF6( stbir__simdf_load( r0, input6 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c6 ) ); )
12429 stbIF7( stbir__simdf_load( r0, input7 ); stbir__simdf_madd( o0, o0, r0, stbir__simdfX_cast_to_simdf4( c7 ) ); )
12430
12431 stbir__simdf_store( output, o0 );
12432 output += 4;
12433 stbIF0( input0 += 4; )
12434 stbIF1( input1 += 4; )
12435 stbIF2( input2 += 4; )
12436 stbIF3( input3 += 4; )
12437 stbIF4( input4 += 4; )
12438 stbIF5( input5 += 4; )
12439 stbIF6( input6 += 4; )
12440 stbIF7( input7 += 4; )
12441 }
12442 }
12443 #else
12444 STBIR_NO_UNROLL_LOOP_START
12445 while ( ( (char*)input0_end - (char*) input0 ) >= 16 )
12446 {
12447 float o0, o1, o2, o3;
12448 STBIR_NO_UNROLL(output);
12449 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12450 stbIF0( o0 = output[0] + input0[0] * c0s; o1 = output[1] + input0[1] * c0s; o2 = output[2] + input0[2] * c0s; o3 = output[3] + input0[3] * c0s; )
12451 #else
12452 stbIF0( o0 = input0[0] * c0s; o1 = input0[1] * c0s; o2 = input0[2] * c0s; o3 = input0[3] * c0s; )
12453 #endif
12454 stbIF1( o0 += input1[0] * c1s; o1 += input1[1] * c1s; o2 += input1[2] * c1s; o3 += input1[3] * c1s; )
12455 stbIF2( o0 += input2[0] * c2s; o1 += input2[1] * c2s; o2 += input2[2] * c2s; o3 += input2[3] * c2s; )
12456 stbIF3( o0 += input3[0] * c3s; o1 += input3[1] * c3s; o2 += input3[2] * c3s; o3 += input3[3] * c3s; )
12457 stbIF4( o0 += input4[0] * c4s; o1 += input4[1] * c4s; o2 += input4[2] * c4s; o3 += input4[3] * c4s; )
12458 stbIF5( o0 += input5[0] * c5s; o1 += input5[1] * c5s; o2 += input5[2] * c5s; o3 += input5[3] * c5s; )
12459 stbIF6( o0 += input6[0] * c6s; o1 += input6[1] * c6s; o2 += input6[2] * c6s; o3 += input6[3] * c6s; )
12460 stbIF7( o0 += input7[0] * c7s; o1 += input7[1] * c7s; o2 += input7[2] * c7s; o3 += input7[3] * c7s; )
12461 output[0] = o0; output[1] = o1; output[2] = o2; output[3] = o3;
12462 output += 4;
12463 stbIF0( input0 += 4; )
12464 stbIF1( input1 += 4; )
12465 stbIF2( input2 += 4; )
12466 stbIF3( input3 += 4; )
12467 stbIF4( input4 += 4; )
12468 stbIF5( input5 += 4; )
12469 stbIF6( input6 += 4; )
12470 stbIF7( input7 += 4; )
12471 }
12472 #endif
12473 STBIR_NO_UNROLL_LOOP_START
12474 while ( input0 < input0_end )
12475 {
12476 float o0;
12477 STBIR_NO_UNROLL(output);
12478 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12479 stbIF0( o0 = output[0] + input0[0] * c0s; )
12480 #else
12481 stbIF0( o0 = input0[0] * c0s; )
12482 #endif
12483 stbIF1( o0 += input1[0] * c1s; )
12484 stbIF2( o0 += input2[0] * c2s; )
12485 stbIF3( o0 += input3[0] * c3s; )
12486 stbIF4( o0 += input4[0] * c4s; )
12487 stbIF5( o0 += input5[0] * c5s; )
12488 stbIF6( o0 += input6[0] * c6s; )
12489 stbIF7( o0 += input7[0] * c7s; )
12490 output[0] = o0;
12491 ++output;
12492 stbIF0( ++input0; )
12493 stbIF1( ++input1; )
12494 stbIF2( ++input2; )
12495 stbIF3( ++input3; )
12496 stbIF4( ++input4; )
12497 stbIF5( ++input5; )
12498 stbIF6( ++input6; )
12499 stbIF7( ++input7; )
12500 }
12501}
12502
12503#undef stbIF0
12504#undef stbIF1
12505#undef stbIF2
12506#undef stbIF3
12507#undef stbIF4
12508#undef stbIF5
12509#undef stbIF6
12510#undef stbIF7
12511#undef STB_IMAGE_RESIZE_DO_VERTICALS
12512#undef STBIR__vertical_channels
12513#undef STB_IMAGE_RESIZE_DO_HORIZONTALS
12514#undef STBIR_strs_join24
12515#undef STBIR_strs_join14
12516#undef STBIR_chans
12517#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12518#undef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
12519#endif
12520
12521#else // !STB_IMAGE_RESIZE_DO_VERTICALS
12522
12523#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__horizontal_channels,end)
12524
12525#ifndef stbir__2_coeff_only
12526#define stbir__2_coeff_only() \
12527 stbir__1_coeff_only(); \
12528 stbir__1_coeff_remnant(1);
12529#endif
12530
12531#ifndef stbir__2_coeff_remnant
12532#define stbir__2_coeff_remnant( ofs ) \
12533 stbir__1_coeff_remnant(ofs); \
12534 stbir__1_coeff_remnant((ofs)+1);
12535#endif
12536
12537#ifndef stbir__3_coeff_only
12538#define stbir__3_coeff_only() \
12539 stbir__2_coeff_only(); \
12540 stbir__1_coeff_remnant(2);
12541#endif
12542
12543#ifndef stbir__3_coeff_remnant
12544#define stbir__3_coeff_remnant( ofs ) \
12545 stbir__2_coeff_remnant(ofs); \
12546 stbir__1_coeff_remnant((ofs)+2);
12547#endif
12548
12549#ifndef stbir__3_coeff_setup
12550#define stbir__3_coeff_setup()
12551#endif
12552
12553#ifndef stbir__4_coeff_start
12554#define stbir__4_coeff_start() \
12555 stbir__2_coeff_only(); \
12556 stbir__2_coeff_remnant(2);
12557#endif
12558
12559#ifndef stbir__4_coeff_continue_from_4
12560#define stbir__4_coeff_continue_from_4( ofs ) \
12561 stbir__2_coeff_remnant(ofs); \
12562 stbir__2_coeff_remnant((ofs)+2);
12563#endif
12564
12565#ifndef stbir__store_output_tiny
12566#define stbir__store_output_tiny stbir__store_output
12567#endif
12568
12569static void STBIR_chans( stbir__horizontal_gather_,_channels_with_1_coeff)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12570{
12571 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12572 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12573 STBIR_SIMD_NO_UNROLL_LOOP_START
12574 do {
12575 float const * decode = decode_buffer;
12576 float const * hc = column->coefficients;
12577 stbir__1_coeff_only();
12578 stbir__store_output_tiny();
12579 } while ( output < output_end );
12580}
12581
12582static void STBIR_chans( stbir__horizontal_gather_,_channels_with_2_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12583{
12584 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12585 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12586 #ifdef stbir__x4_start
12587 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12588 while ( ( output_end - output ) >= 4 )
12589 {
12590 stbir__x4_setup();
12591 stbir__x4_2taps();
12592 stbir__x4_store();
12593 }
12594 if ( output < output_end )
12595 #endif
12596 STBIR_SIMD_NO_UNROLL_LOOP_START
12597 do {
12598 float const * decode = decode_buffer;
12599 float const * hc = column->coefficients;
12600 stbir__2_coeff_only();
12601 stbir__store_output_tiny();
12602 } while ( output < output_end );
12603}
12604
12605static void STBIR_chans( stbir__horizontal_gather_,_channels_with_3_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12606{
12607 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12608 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12609 #ifdef stbir__x4_start
12610 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12611 while ( ( output_end - output ) >= 4 )
12612 {
12613 stbir__x4_setup();
12614 stbir__x4_start();
12615 stbir__x4_finish3();
12616 stbir__x4_store();
12617 }
12618 if ( output < output_end )
12619 #endif
12620 STBIR_SIMD_NO_UNROLL_LOOP_START
12621 do {
12622 float const * decode = decode_buffer;
12623 float const * hc = column->coefficients;
12624 stbir__3_coeff_only();
12625 stbir__store_output_tiny();
12626 } while ( output < output_end );
12627}
12628
12629static void STBIR_chans( stbir__horizontal_gather_,_channels_with_4_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12630{
12631 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12632 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12633 #ifdef stbir__x4_start
12634 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12635 while ( ( output_end - output ) >= 4 )
12636 {
12637 stbir__x4_setup();
12638 stbir__x4_start();
12639 stbir__x4_finish();
12640 stbir__x4_store();
12641 }
12642 if ( output < output_end )
12643 #endif
12644 STBIR_SIMD_NO_UNROLL_LOOP_START
12645 do {
12646 float const * decode = decode_buffer;
12647 float const * hc = column->coefficients;
12648 stbir__4_coeff_start();
12649 stbir__store_output();
12650 } while ( output < output_end );
12651}
12652
12653static void STBIR_chans( stbir__horizontal_gather_,_channels_with_5_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12654{
12655 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12656 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12657 #ifdef stbir__x4_start
12658 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12659 while ( ( output_end - output ) >= 4 )
12660 {
12661 stbir__x4_setup();
12662 stbir__x4_start();
12663 stbir__x4_rem1(4);
12664 stbir__x4_finish();
12665 stbir__x4_store();
12666 }
12667 if ( output < output_end )
12668 #endif
12669 STBIR_SIMD_NO_UNROLL_LOOP_START
12670 do {
12671 float const * decode = decode_buffer;
12672 float const * hc = column->coefficients;
12673 stbir__4_coeff_start();
12674 stbir__1_coeff_remnant(4);
12675 stbir__store_output();
12676 } while ( output < output_end );
12677}
12678
12679static void STBIR_chans( stbir__horizontal_gather_,_channels_with_6_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12680{
12681 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12682 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12683 #ifdef stbir__x4_start
12684 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12685 while ( ( output_end - output ) >= 4 )
12686 {
12687 stbir__x4_setup();
12688 stbir__x4_start();
12689 stbir__x4_rem2(4);
12690 stbir__x4_finish();
12691 stbir__x4_store();
12692 }
12693 if ( output < output_end )
12694 #endif
12695 STBIR_SIMD_NO_UNROLL_LOOP_START
12696 do {
12697 float const * decode = decode_buffer;
12698 float const * hc = column->coefficients;
12699 stbir__4_coeff_start();
12700 stbir__2_coeff_remnant(4);
12701 stbir__store_output();
12702 } while ( output < output_end );
12703}
12704
12705static void STBIR_chans( stbir__horizontal_gather_,_channels_with_7_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12706{
12707 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12708 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12709 stbir__3_coeff_setup();
12710 #ifdef stbir__x4_start
12711 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12712 while ( ( output_end - output ) >= 4 )
12713 {
12714 stbir__x4_setup();
12715 stbir__x4_start();
12716 stbir__x4_rem3(4);
12717 stbir__x4_finish();
12718 stbir__x4_store();
12719 }
12720 if ( output < output_end )
12721 #endif
12722 STBIR_SIMD_NO_UNROLL_LOOP_START
12723 do {
12724 float const * decode = decode_buffer;
12725 float const * hc = column->coefficients;
12726
12727 stbir__4_coeff_start();
12728 stbir__3_coeff_remnant(4);
12729 stbir__store_output();
12730 } while ( output < output_end );
12731}
12732
12733static void STBIR_chans( stbir__horizontal_gather_,_channels_with_8_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12734{
12735 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12736 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12737 #ifdef stbir__x4_start
12738 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12739 while ( ( output_end - output ) >= 4 )
12740 {
12741 stbir__x4_setup();
12742 stbir__x4_start();
12743 stbir__x4_cont(4);
12744 stbir__x4_finish();
12745 stbir__x4_store();
12746 }
12747 if ( output < output_end )
12748 #endif
12749 STBIR_SIMD_NO_UNROLL_LOOP_START
12750 do {
12751 float const * decode = decode_buffer;
12752 float const * hc = column->coefficients;
12753 stbir__4_coeff_start();
12754 stbir__4_coeff_continue_from_4(4);
12755 stbir__store_output();
12756 } while ( output < output_end );
12757}
12758
12759static void STBIR_chans( stbir__horizontal_gather_,_channels_with_9_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12760{
12761 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12762 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12763 #ifdef stbir__x4_start
12764 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12765 while ( ( output_end - output ) >= 4 )
12766 {
12767 stbir__x4_setup();
12768 stbir__x4_start();
12769 stbir__x4_cont(4);
12770 stbir__x4_rem1(8);
12771 stbir__x4_finish();
12772 stbir__x4_store();
12773 }
12774 if ( output < output_end )
12775 #endif
12776 STBIR_SIMD_NO_UNROLL_LOOP_START
12777 do {
12778 float const * decode = decode_buffer;
12779 float const * hc = column->coefficients;
12780 stbir__4_coeff_start();
12781 stbir__4_coeff_continue_from_4(4);
12782 stbir__1_coeff_remnant(8);
12783 stbir__store_output();
12784 } while ( output < output_end );
12785}
12786
12787static void STBIR_chans( stbir__horizontal_gather_,_channels_with_10_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12788{
12789 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12790 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12791 #ifdef stbir__x4_start
12792 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12793 while ( ( output_end - output ) >= 4 )
12794 {
12795 stbir__x4_setup();
12796 stbir__x4_start();
12797 stbir__x4_cont(4);
12798 stbir__x4_rem2(8);
12799 stbir__x4_finish();
12800 stbir__x4_store();
12801 }
12802 if ( output < output_end )
12803 #endif
12804 STBIR_SIMD_NO_UNROLL_LOOP_START
12805 do {
12806 float const * decode = decode_buffer;
12807 float const * hc = column->coefficients;
12808 stbir__4_coeff_start();
12809 stbir__4_coeff_continue_from_4(4);
12810 stbir__2_coeff_remnant(8);
12811 stbir__store_output();
12812 } while ( output < output_end );
12813}
12814
12815static void STBIR_chans( stbir__horizontal_gather_,_channels_with_11_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12816{
12817 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12818 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12819 stbir__3_coeff_setup();
12820 #ifdef stbir__x4_start
12821 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12822 while ( ( output_end - output ) >= 4 )
12823 {
12824 stbir__x4_setup();
12825 stbir__x4_start();
12826 stbir__x4_cont(4);
12827 stbir__x4_rem3(8);
12828 stbir__x4_finish();
12829 stbir__x4_store();
12830 }
12831 if ( output < output_end )
12832 #endif
12833 STBIR_SIMD_NO_UNROLL_LOOP_START
12834 do {
12835 float const * decode = decode_buffer;
12836 float const * hc = column->coefficients;
12837 stbir__4_coeff_start();
12838 stbir__4_coeff_continue_from_4(4);
12839 stbir__3_coeff_remnant(8);
12840 stbir__store_output();
12841 } while ( output < output_end );
12842}
12843
12844static void STBIR_chans( stbir__horizontal_gather_,_channels_with_12_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12845{
12846 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12847 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12848 #ifdef stbir__x4_start
12849 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12850 while ( ( output_end - output ) >= 4 )
12851 {
12852 stbir__x4_setup();
12853 stbir__x4_start();
12854 stbir__x4_cont(4);
12855 stbir__x4_cont(8);
12856 stbir__x4_finish();
12857 stbir__x4_store();
12858 }
12859 if ( output < output_end )
12860 #endif
12861 STBIR_SIMD_NO_UNROLL_LOOP_START
12862 do {
12863 float const * decode = decode_buffer;
12864 float const * hc = column->coefficients;
12865 stbir__4_coeff_start();
12866 stbir__4_coeff_continue_from_4(4);
12867 stbir__4_coeff_continue_from_4(8);
12868 stbir__store_output();
12869 } while ( output < output_end );
12870}
12871
12872static void STBIR_chans( stbir__horizontal_gather_,_channels_with_13_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12873{
12874 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12875 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12876 #ifdef stbir__x4_start
12877 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12878 while ( ( output_end - output ) >= 4 )
12879 {
12880 stbir__x4_setup();
12881 stbir__x4_start();
12882 stbir__x4_cont(4);
12883 stbir__x4_cont(8);
12884 stbir__x4_rem1(12);
12885 stbir__x4_finish();
12886 stbir__x4_store();
12887 }
12888 if ( output < output_end )
12889 #endif
12890 STBIR_SIMD_NO_UNROLL_LOOP_START
12891 do {
12892 float const * decode = decode_buffer;
12893 float const * hc = column->coefficients;
12894 stbir__4_coeff_start();
12895 stbir__4_coeff_continue_from_4(4);
12896 stbir__4_coeff_continue_from_4(8);
12897 stbir__1_coeff_remnant(12);
12898 stbir__store_output();
12899 } while ( output < output_end );
12900}
12901
12902static void STBIR_chans( stbir__horizontal_gather_,_channels_with_14_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12903{
12904 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12905 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12906 #ifdef stbir__x4_start
12907 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12908 while ( ( output_end - output ) >= 4 )
12909 {
12910 stbir__x4_setup();
12911 stbir__x4_start();
12912 stbir__x4_cont(4);
12913 stbir__x4_cont(8);
12914 stbir__x4_rem2(12);
12915 stbir__x4_finish();
12916 stbir__x4_store();
12917 }
12918 if ( output < output_end )
12919 #endif
12920 STBIR_SIMD_NO_UNROLL_LOOP_START
12921 do {
12922 float const * decode = decode_buffer;
12923 float const * hc = column->coefficients;
12924 stbir__4_coeff_start();
12925 stbir__4_coeff_continue_from_4(4);
12926 stbir__4_coeff_continue_from_4(8);
12927 stbir__2_coeff_remnant(12);
12928 stbir__store_output();
12929 } while ( output < output_end );
12930}
12931
12932static void STBIR_chans( stbir__horizontal_gather_,_channels_with_15_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12933{
12934 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12935 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12936 stbir__3_coeff_setup();
12937 #ifdef stbir__x4_start
12938 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12939 while ( ( output_end - output ) >= 4 )
12940 {
12941 stbir__x4_setup();
12942 stbir__x4_start();
12943 stbir__x4_cont(4);
12944 stbir__x4_cont(8);
12945 stbir__x4_rem3(12);
12946 stbir__x4_finish();
12947 stbir__x4_store();
12948 }
12949 if ( output < output_end )
12950 #endif
12951 STBIR_SIMD_NO_UNROLL_LOOP_START
12952 do {
12953 float const * decode = decode_buffer;
12954 float const * hc = column->coefficients;
12955 stbir__4_coeff_start();
12956 stbir__4_coeff_continue_from_4(4);
12957 stbir__4_coeff_continue_from_4(8);
12958 stbir__3_coeff_remnant(12);
12959 stbir__store_output();
12960 } while ( output < output_end );
12961}
12962
12963static void STBIR_chans( stbir__horizontal_gather_,_channels_with_16_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12964{
12965 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12966 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12967 #ifdef stbir__x4_start
12968 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12969 while ( ( output_end - output ) >= 4 )
12970 {
12971 stbir__x4_setup();
12972 stbir__x4_start();
12973 stbir__x4_cont(4);
12974 stbir__x4_cont(8);
12975 stbir__x4_cont(12);
12976 stbir__x4_finish();
12977 stbir__x4_store();
12978 }
12979 if ( output < output_end )
12980 #endif
12981 STBIR_SIMD_NO_UNROLL_LOOP_START
12982 do {
12983 float const * decode = decode_buffer;
12984 float const * hc = column->coefficients;
12985 stbir__4_coeff_start();
12986 stbir__4_coeff_continue_from_4(4);
12987 stbir__4_coeff_continue_from_4(8);
12988 stbir__4_coeff_continue_from_4(12);
12989 stbir__store_output();
12990 } while ( output < output_end );
12991}
12992
12993static void STBIR_chans( stbir__horizontal_gather_,_channels_with_17_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
12994{
12995 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
12996 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
12997 #ifdef stbir__x4_start
12998 // four outputs at a time while they last (one channel only, see the stbir__x4 macros)
12999 while ( ( output_end - output ) >= 4 )
13000 {
13001 stbir__x4_setup();
13002 stbir__x4_start();
13003 stbir__x4_cont(4);
13004 stbir__x4_cont(8);
13005 stbir__x4_cont(12);
13006 stbir__x4_rem1(16);
13007 stbir__x4_finish();
13008 stbir__x4_store();
13009 }
13010 if ( output < output_end )
13011 #endif
13012 STBIR_SIMD_NO_UNROLL_LOOP_START
13013 do {
13014 float const * decode = decode_buffer;
13015 float const * hc = column->coefficients;
13016 stbir__4_coeff_start();
13017 stbir__4_coeff_continue_from_4(4);
13018 stbir__4_coeff_continue_from_4(8);
13019 stbir__4_coeff_continue_from_4(12);
13020 stbir__1_coeff_remnant(16);
13021 stbir__store_output();
13022 } while ( output < output_end );
13023}
13024
13025static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod0 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13026{
13027 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13028 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13029 STBIR_SIMD_NO_UNROLL_LOOP_START
13030 do {
13031 float const * decode = decode_buffer;
13032 int n = ( column->width - 4 + 3 ) >> 2;
13033 float const * hc = column->coefficients;
13034
13035 stbir__4_coeff_start();
13036 STBIR_SIMD_NO_UNROLL_LOOP_START
13037 do {
13038 hc += 4;
13039 decode += STBIR__horizontal_channels * 4;
13040 stbir__4_coeff_continue_from_4( 0 );
13041 --n;
13042 } while ( n > 0 );
13043 stbir__store_output();
13044 } while ( output < output_end );
13045}
13046
13047static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod1 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13048{
13049 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13050 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13051 STBIR_SIMD_NO_UNROLL_LOOP_START
13052 do {
13053 float const * decode = decode_buffer;
13054 int n = ( column->width - 5 + 3 ) >> 2;
13055 float const * hc = column->coefficients;
13056
13057 stbir__4_coeff_start();
13058 STBIR_SIMD_NO_UNROLL_LOOP_START
13059 do {
13060 hc += 4;
13061 decode += STBIR__horizontal_channels * 4;
13062 stbir__4_coeff_continue_from_4( 0 );
13063 --n;
13064 } while ( n > 0 );
13065 stbir__1_coeff_remnant( 4 );
13066 stbir__store_output();
13067 } while ( output < output_end );
13068}
13069
13070static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod2 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13071{
13072 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13073 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13074 STBIR_SIMD_NO_UNROLL_LOOP_START
13075 do {
13076 float const * decode = decode_buffer;
13077 int n = ( column->width - 6 + 3 ) >> 2;
13078 float const * hc = column->coefficients;
13079
13080 stbir__4_coeff_start();
13081 STBIR_SIMD_NO_UNROLL_LOOP_START
13082 do {
13083 hc += 4;
13084 decode += STBIR__horizontal_channels * 4;
13085 stbir__4_coeff_continue_from_4( 0 );
13086 --n;
13087 } while ( n > 0 );
13088 stbir__2_coeff_remnant( 4 );
13089
13090 stbir__store_output();
13091 } while ( output < output_end );
13092}
13093
13094static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod3 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13095{
13096 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13097 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13098 stbir__3_coeff_setup();
13099 STBIR_SIMD_NO_UNROLL_LOOP_START
13100 do {
13101 float const * decode = decode_buffer;
13102 int n = ( column->width - 7 + 3 ) >> 2;
13103 float const * hc = column->coefficients;
13104
13105 stbir__4_coeff_start();
13106 STBIR_SIMD_NO_UNROLL_LOOP_START
13107 do {
13108 hc += 4;
13109 decode += STBIR__horizontal_channels * 4;
13110 stbir__4_coeff_continue_from_4( 0 );
13111 --n;
13112 } while ( n > 0 );
13113 stbir__3_coeff_remnant( 4 );
13114
13115 stbir__store_output();
13116 } while ( output < output_end );
13117}
13118
13119// polyphase period 1 kernels (integer downsamples, see stbir__resample_horizontal_gather): every output uses the same
13120// coefficient set, so it is loaded into registers once per run and the pixel loop is just the decode loads and
13121// multiply-adds. the same steps as the kernels above, for the widths integer downsamples produce (4, 8: 2:1 with the
13122// support 1 and 2 filters; 5, 9: the same one tap wider; 11/12/13 and 16/17: 3:1 and 4:1 with the support 2 filters -
13123// 3:1 trims to 11 because its outermost taps land exactly on the filter's zero at +-2).
13124// only for the channel counts that define the step macros.
13125#ifdef stbir__period1_setup_4
13126static void STBIR_chans( stbir__horizontal_gather_,_channels_with_4_coeffs_period1)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13127{
13128 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13129 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13130 float const * hc = column->coefficients;
13131 int advance = column->advance;
13132 stbir__period1_setup_4()
13133 STBIR__UNUSED( entry_stride );
13134 #ifdef stbir__period1_x4_start
13135 // four outputs at a time while they last (one channel only)
13136 while ( ( output_end - output ) >= 4 )
13137 {
13138 stbir__period1_x4_setup();
13139 stbir__period1_x4_start();
13140 stbir__period1_x4_store();
13141 }
13142 if ( output < output_end )
13143 #endif
13144 STBIR_SIMD_NO_UNROLL_LOOP_START
13145 do {
13146 float const * decode = decode_buffer;
13147 stbir__period1_4_coeff_start();
13148 stbir__period1_store_output();
13149 } while ( output < output_end );
13150}
13151
13152static void STBIR_chans( stbir__horizontal_gather_,_channels_with_5_coeffs_period1)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13153{
13154 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13155 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13156 float const * hc = column->coefficients;
13157 int advance = column->advance;
13158 stbir__period1_setup_5()
13159 STBIR__UNUSED( entry_stride );
13160 #ifdef stbir__period1_x4_start
13161 // four outputs at a time while they last (one channel only)
13162 while ( ( output_end - output ) >= 4 )
13163 {
13164 stbir__period1_x4_setup();
13165 stbir__period1_x4_start();
13166 stbir__period1_x4_remnant(4);
13167 stbir__period1_x4_store();
13168 }
13169 if ( output < output_end )
13170 #endif
13171 STBIR_SIMD_NO_UNROLL_LOOP_START
13172 do {
13173 float const * decode = decode_buffer;
13174 stbir__period1_4_coeff_start();
13175 stbir__period1_1_coeff_remnant(4);
13176 stbir__period1_store_output();
13177 } while ( output < output_end );
13178}
13179
13180static void STBIR_chans( stbir__horizontal_gather_,_channels_with_8_coeffs_period1)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13181{
13182 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13183 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13184 float const * hc = column->coefficients;
13185 int advance = column->advance;
13186 stbir__period1_setup_8()
13187 STBIR__UNUSED( entry_stride );
13188 #ifdef stbir__period1_x4_start
13189 // four outputs at a time while they last (one channel only)
13190 while ( ( output_end - output ) >= 4 )
13191 {
13192 stbir__period1_x4_setup();
13193 stbir__period1_x4_start();
13194 stbir__period1_x4_continue();
13195 stbir__period1_x4_store();
13196 }
13197 if ( output < output_end )
13198 #endif
13199 STBIR_SIMD_NO_UNROLL_LOOP_START
13200 do {
13201 float const * decode = decode_buffer;
13202 stbir__period1_4_coeff_start();
13203 stbir__period1_4_coeff_continue();
13204 stbir__period1_store_output();
13205 } while ( output < output_end );
13206}
13207
13208static void STBIR_chans( stbir__horizontal_gather_,_channels_with_9_coeffs_period1)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13209{
13210 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13211 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13212 float const * hc = column->coefficients;
13213 int advance = column->advance;
13214 stbir__period1_setup_9()
13215 STBIR__UNUSED( entry_stride );
13216 #ifdef stbir__period1_x4_start
13217 // four outputs at a time while they last (one channel only)
13218 while ( ( output_end - output ) >= 4 )
13219 {
13220 stbir__period1_x4_setup();
13221 stbir__period1_x4_start();
13222 stbir__period1_x4_continue();
13223 stbir__period1_x4_remnant(8);
13224 stbir__period1_x4_store();
13225 }
13226 if ( output < output_end )
13227 #endif
13228 STBIR_SIMD_NO_UNROLL_LOOP_START
13229 do {
13230 float const * decode = decode_buffer;
13231 stbir__period1_4_coeff_start();
13232 stbir__period1_4_coeff_continue();
13233 stbir__period1_1_coeff_remnant(8);
13234 stbir__period1_store_output();
13235 } while ( output < output_end );
13236}
13237
13238static void STBIR_chans( stbir__horizontal_gather_,_channels_with_11_coeffs_period1)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13239{
13240 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13241 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13242 float const * hc = column->coefficients;
13243 int advance = column->advance;
13244 stbir__period1_setup_11()
13245 STBIR__UNUSED( entry_stride );
13246 #ifdef stbir__period1_x4_start
13247 // four outputs at a time while they last (one channel only)
13248 while ( ( output_end - output ) >= 4 )
13249 {
13250 stbir__period1_x4_setup();
13251 stbir__period1_x4_start();
13252 stbir__period1_x4_continue();
13253 stbir__period1_x4_remnant3(8);
13254 stbir__period1_x4_store();
13255 }
13256 if ( output < output_end )
13257 #endif
13258 STBIR_SIMD_NO_UNROLL_LOOP_START
13259 do {
13260 float const * decode = decode_buffer;
13261 stbir__period1_4_coeff_start();
13262 stbir__period1_4_coeff_continue();
13263 stbir__period1_3_coeff_remnant(8);
13264 stbir__period1_store_output();
13265 } while ( output < output_end );
13266}
13267
13268static void STBIR_chans( stbir__horizontal_gather_,_channels_with_12_coeffs_period1)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13269{
13270 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13271 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13272 float const * hc = column->coefficients;
13273 int advance = column->advance;
13274 stbir__period1_setup_12()
13275 STBIR__UNUSED( entry_stride );
13276 #ifdef stbir__period1_x4_start
13277 // four outputs at a time while they last (one channel only)
13278 while ( ( output_end - output ) >= 4 )
13279 {
13280 stbir__period1_x4_setup();
13281 stbir__period1_x4_start();
13282 stbir__period1_x4_continue();
13283 stbir__period1_x4_continue2();
13284 stbir__period1_x4_store();
13285 }
13286 if ( output < output_end )
13287 #endif
13288 STBIR_SIMD_NO_UNROLL_LOOP_START
13289 do {
13290 float const * decode = decode_buffer;
13291 stbir__period1_4_coeff_start();
13292 stbir__period1_4_coeff_continue();
13293 stbir__period1_4_coeff_continue2();
13294 stbir__period1_store_output();
13295 } while ( output < output_end );
13296}
13297
13298static void STBIR_chans( stbir__horizontal_gather_,_channels_with_13_coeffs_period1)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13299{
13300 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13301 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13302 float const * hc = column->coefficients;
13303 int advance = column->advance;
13304 stbir__period1_setup_13()
13305 STBIR__UNUSED( entry_stride );
13306 #ifdef stbir__period1_x4_start
13307 // four outputs at a time while they last (one channel only)
13308 while ( ( output_end - output ) >= 4 )
13309 {
13310 stbir__period1_x4_setup();
13311 stbir__period1_x4_start();
13312 stbir__period1_x4_continue();
13313 stbir__period1_x4_continue2();
13314 stbir__period1_x4_remnant(12);
13315 stbir__period1_x4_store();
13316 }
13317 if ( output < output_end )
13318 #endif
13319 STBIR_SIMD_NO_UNROLL_LOOP_START
13320 do {
13321 float const * decode = decode_buffer;
13322 stbir__period1_4_coeff_start();
13323 stbir__period1_4_coeff_continue();
13324 stbir__period1_4_coeff_continue2();
13325 stbir__period1_1_coeff_remnant(12);
13326 stbir__period1_store_output();
13327 } while ( output < output_end );
13328}
13329
13330static void STBIR_chans( stbir__horizontal_gather_,_channels_with_16_coeffs_period1)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13331{
13332 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13333 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13334 float const * hc = column->coefficients;
13335 int advance = column->advance;
13336 stbir__period1_setup_16()
13337 STBIR__UNUSED( entry_stride );
13338 #ifdef stbir__period1_x4_start
13339 // four outputs at a time while they last (one channel only)
13340 while ( ( output_end - output ) >= 4 )
13341 {
13342 stbir__period1_x4_setup();
13343 stbir__period1_x4_start();
13344 stbir__period1_x4_continue();
13345 stbir__period1_x4_continue2();
13346 stbir__period1_x4_continue3();
13347 stbir__period1_x4_store();
13348 }
13349 if ( output < output_end )
13350 #endif
13351 STBIR_SIMD_NO_UNROLL_LOOP_START
13352 do {
13353 float const * decode = decode_buffer;
13354 stbir__period1_4_coeff_start();
13355 stbir__period1_4_coeff_continue();
13356 stbir__period1_4_coeff_continue2();
13357 stbir__period1_4_coeff_continue3();
13358 stbir__period1_store_output();
13359 } while ( output < output_end );
13360}
13361
13362static void STBIR_chans( stbir__horizontal_gather_,_channels_with_17_coeffs_period1)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__column_entry const * column, int entry_stride )
13363{
13364 float const * output_end = output_buffer + (ptrdiff_t)output_sub_size * STBIR__horizontal_channels;
13365 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
13366 float const * hc = column->coefficients;
13367 int advance = column->advance;
13368 stbir__period1_setup_17()
13369 STBIR__UNUSED( entry_stride );
13370 #ifdef stbir__period1_x4_start
13371 // four outputs at a time while they last (one channel only)
13372 while ( ( output_end - output ) >= 4 )
13373 {
13374 stbir__period1_x4_setup();
13375 stbir__period1_x4_start();
13376 stbir__period1_x4_continue();
13377 stbir__period1_x4_continue2();
13378 stbir__period1_x4_continue3();
13379 stbir__period1_x4_remnant(16);
13380 stbir__period1_x4_store();
13381 }
13382 if ( output < output_end )
13383 #endif
13384 STBIR_SIMD_NO_UNROLL_LOOP_START
13385 do {
13386 float const * decode = decode_buffer;
13387 stbir__period1_4_coeff_start();
13388 stbir__period1_4_coeff_continue();
13389 stbir__period1_4_coeff_continue2();
13390 stbir__period1_4_coeff_continue3();
13391 stbir__period1_1_coeff_remnant(16);
13392 stbir__period1_store_output();
13393 } while ( output < output_end );
13394}
13395
13396// by coefficient count (null = use the normal kernel over the one entry)
13397static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_period1_funcs)[18]=
13398{
13399 0, 0, 0, 0,
13400 STBIR_chans(stbir__horizontal_gather_,_channels_with_4_coeffs_period1),
13401 STBIR_chans(stbir__horizontal_gather_,_channels_with_5_coeffs_period1),
13402 0, 0,
13403 STBIR_chans(stbir__horizontal_gather_,_channels_with_8_coeffs_period1),
13404 STBIR_chans(stbir__horizontal_gather_,_channels_with_9_coeffs_period1),
13405 0,
13406 STBIR_chans(stbir__horizontal_gather_,_channels_with_11_coeffs_period1),
13407 STBIR_chans(stbir__horizontal_gather_,_channels_with_12_coeffs_period1),
13408 STBIR_chans(stbir__horizontal_gather_,_channels_with_13_coeffs_period1),
13409 0, 0,
13410 STBIR_chans(stbir__horizontal_gather_,_channels_with_16_coeffs_period1),
13411 STBIR_chans(stbir__horizontal_gather_,_channels_with_17_coeffs_period1)
13412};
13413#else
13414static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_period1_funcs)[18]= { 0 };
13415#endif
13416
13417static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_funcs)[4]=
13418{
13419 STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod0),
13420 STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod1),
13421 STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod2),
13422 STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod3),
13423};
13424
13425static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_funcs)[17]=
13426{
13427 STBIR_chans(stbir__horizontal_gather_,_channels_with_1_coeff),
13428 STBIR_chans(stbir__horizontal_gather_,_channels_with_2_coeffs),
13429 STBIR_chans(stbir__horizontal_gather_,_channels_with_3_coeffs),
13430 STBIR_chans(stbir__horizontal_gather_,_channels_with_4_coeffs),
13431 STBIR_chans(stbir__horizontal_gather_,_channels_with_5_coeffs),
13432 STBIR_chans(stbir__horizontal_gather_,_channels_with_6_coeffs),
13433 STBIR_chans(stbir__horizontal_gather_,_channels_with_7_coeffs),
13434 STBIR_chans(stbir__horizontal_gather_,_channels_with_8_coeffs),
13435 STBIR_chans(stbir__horizontal_gather_,_channels_with_9_coeffs),
13436 STBIR_chans(stbir__horizontal_gather_,_channels_with_10_coeffs),
13437 STBIR_chans(stbir__horizontal_gather_,_channels_with_11_coeffs),
13438 STBIR_chans(stbir__horizontal_gather_,_channels_with_12_coeffs),
13439 STBIR_chans(stbir__horizontal_gather_,_channels_with_13_coeffs),
13440 STBIR_chans(stbir__horizontal_gather_,_channels_with_14_coeffs),
13441 STBIR_chans(stbir__horizontal_gather_,_channels_with_15_coeffs),
13442 STBIR_chans(stbir__horizontal_gather_,_channels_with_16_coeffs),
13443 STBIR_chans(stbir__horizontal_gather_,_channels_with_17_coeffs),
13444};
13445
13446#ifdef stbir__period1_setup_4
13447#undef stbir__period1_setup_4
13448#undef stbir__period1_setup_5
13449#undef stbir__period1_setup_8
13450#undef stbir__period1_setup_9
13451#undef stbir__period1_setup_11
13452#undef stbir__period1_setup_12
13453#undef stbir__period1_setup_13
13454#undef stbir__period1_setup_16
13455#undef stbir__period1_setup_17
13456#undef stbir__period1_4_coeff_start
13457#undef stbir__period1_4_coeff_continue
13458#undef stbir__period1_4_coeff_continue2
13459#undef stbir__period1_4_coeff_continue3
13460#undef stbir__period1_1_coeff_remnant
13461#undef stbir__period1_3_coeff_remnant
13462#undef stbir__period1_store_output
13463#endif
13464#ifdef stbir__period1_x4_start
13465#undef stbir__period1_x4_setup
13466#undef stbir__period1_x4_start
13467#undef stbir__period1_x4_next
13468#undef stbir__period1_x4_continue
13469#undef stbir__period1_x4_continue2
13470#undef stbir__period1_x4_continue3
13471#undef stbir__period1_x4_remnant
13472#undef stbir__period1_x4_remnant3
13473#undef stbir__period1_x4_store
13474#endif
13475#undef STBIR__horizontal_channels
13476#undef STB_IMAGE_RESIZE_DO_HORIZONTALS
13477#ifdef stbir__x4_start
13478#undef stbir__x4_setup
13479#undef stbir__x4_start
13480#undef stbir__x4_cont
13481#undef stbir__x4_rem1
13482#undef stbir__x4_rem2
13483#undef stbir__x4_rem3
13484#undef stbir__x4_2taps
13485#undef stbir__x4_finish
13486#undef stbir__x4_finish3
13487#undef stbir__x4_store
13488#endif
13489#undef stbir__1_coeff_only
13490#undef stbir__1_coeff_remnant
13491#undef stbir__2_coeff_only
13492#undef stbir__2_coeff_remnant
13493#undef stbir__3_coeff_only
13494#undef stbir__3_coeff_remnant
13495#undef stbir__3_coeff_setup
13496#undef stbir__4_coeff_start
13497#undef stbir__4_coeff_continue_from_4
13498#undef stbir__store_output
13499#undef stbir__store_output_tiny
13500#undef STBIR_chans
13501
13502#endif // HORIZONALS
13503
13504#undef STBIR_strs_join2
13505#undef STBIR_strs_join1
13506
13507#endif // STB_IMAGE_RESIZE_DO_HORIZONTALS/VERTICALS/CODERS
13508
13509/*
13510------------------------------------------------------------------------------
13511This software is available under 2 licenses -- choose whichever you prefer.
13512------------------------------------------------------------------------------
13513ALTERNATIVE A - MIT License
13514Copyright (c) 2017 Sean Barrett
13515Permission is hereby granted, free of charge, to any person obtaining a copy of
13516this software and associated documentation files (the "Software"), to deal in
13517the Software without restriction, including without limitation the rights to
13518use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
13519of the Software, and to permit persons to whom the Software is furnished to do
13520so, subject to the following conditions:
13521The above copyright notice and this permission notice shall be included in all
13522copies or substantial portions of the Software.
13523THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13524IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13525FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13526AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
13527LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13528OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13529SOFTWARE.
13530------------------------------------------------------------------------------
13531ALTERNATIVE B - Public Domain (www.unlicense.org)
13532This is free and unencumbered software released into the public domain.
13533Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
13534software, either in source code form or as a compiled binary, for any purpose,
13535commercial or non-commercial, and by any means.
13536In jurisdictions that recognize copyright laws, the author or authors of this
13537software dedicate any and all copyright interest in the software to the public
13538domain. We make this dedication for the benefit of the public at large and to
13539the detriment of our heirs and successors. We intend this dedication to be an
13540overt act of relinquishment in perpetuity of all present and future rights to
13541this software under copyright law.
13542THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13543IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13544FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13545AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
13546ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
13547WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13548------------------------------------------------------------------------------
13549*/