master
stb_image_resize2.h
1/* stb_image_resize2 - v2.18 - 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, 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. For AVX and AVX2, we auto-select it by detecting the /arch:AVX
69 or /arch:AVX2 switches. You can also always manually turn SSE2, AVX or AVX2
70 support on by defining STBIR_SSE2, STBIR_AVX or STBIR_AVX2.
71
72 On Linux, SSE2 and Neon is on by default for 64-bit x64 or ARM64. For 32-bit,
73 we select x86 SIMD mode by whether you have -msse2, -mavx or -mavx2 enabled
74 on the command line. For 32-bit ARM, you must pass -mfpu=neon-vfpv4 for both
75 clang and GCC, but GCC also requires an additional -mfp16-format=ieee to
76 automatically enable NEON.
77
78 On x86 platforms, you can also define STBIR_FP16C to turn on FP16C instructions
79 for converting back and forth to half-floats. This is autoselected when we
80 are using AVX2. Clang and GCC also require the -mf16c switch. ARM always uses
81 the built-in half float hardware NEON instructions.
82
83 You can also tell us to use multiply-add instructions with STBIR_USE_FMA.
84 Because x86 doesn't always have fma, we turn it off by default to maintain
85 determinism across all platforms. If you don't care about non-FMA determinism
86 and are willing to restrict yourself to more recent x86 CPUs (around the AVX
87 timeframe), then fma will give you around a 15% speedup.
88
89 You can force off SIMD in all cases by defining STBIR_NO_SIMD. You can turn
90 off AVX or AVX2 specifically with STBIR_NO_AVX or STBIR_NO_AVX2. AVX is 10%
91 to 40% faster, and AVX2 is generally another 12%.
92
93 ALPHA CHANNEL
94 Most of the resizing functions provide the ability to control how the alpha
95 channel of an image is processed.
96
97 When alpha represents transparency, it is important that when combining
98 colors with filtering, the pixels should not be treated equally; they
99 should use a weighted average based on their alpha values. For example,
100 if a pixel is 1% opaque bright green and another pixel is 99% opaque
101 black and you average them, the average will be 50% opaque, but the
102 unweighted average and will be a middling green color, while the weighted
103 average will be nearly black. This means the unweighted version introduced
104 green energy that didn't exist in the source image.
105
106 (If you want to know why this makes sense, you can work out the math for
107 the following: consider what happens if you alpha composite a source image
108 over a fixed color and then average the output, vs. if you average the
109 source image pixels and then composite that over the same fixed color.
110 Only the weighted average produces the same result as the ground truth
111 composite-then-average result.)
112
113 Therefore, it is in general best to "alpha weight" the pixels when applying
114 filters to them. This essentially means multiplying the colors by the alpha
115 values before combining them, and then dividing by the alpha value at the
116 end.
117
118 The computer graphics industry introduced a technique called "premultiplied
119 alpha" or "associated alpha" in which image colors are stored in image files
120 already multiplied by their alpha. This saves some math when compositing,
121 and also avoids the need to divide by the alpha at the end (which is quite
122 inefficient). However, while premultiplied alpha is common in the movie CGI
123 industry, it is not commonplace in other industries like videogames, and most
124 consumer file formats are generally expected to contain not-premultiplied
125 colors. For example, Photoshop saves PNG files "unpremultiplied", and web
126 browsers like Chrome and Firefox expect PNG images to be unpremultiplied.
127
128 Note that there are three possibilities that might describe your image
129 and resize expectation:
130
131 1. images are not premultiplied, alpha weighting is desired
132 2. images are not premultiplied, alpha weighting is not desired
133 3. images are premultiplied
134
135 Both case #2 and case #3 require the exact same math: no alpha weighting
136 should be applied or removed. Only case 1 requires extra math operations;
137 the other two cases can be handled identically.
138
139 stb_image_resize expects case #1 by default, applying alpha weighting to
140 images, expecting the input images to be unpremultiplied. This is what the
141 COLOR+ALPHA buffer types tell the resizer to do.
142
143 When you use the pixel layouts STBIR_RGBA, STBIR_BGRA, STBIR_ARGB,
144 STBIR_ABGR, STBIR_RA, or STBIR_AR you are telling us that the pixels are
145 non-premultiplied. In these cases, the resizer will alpha weight the colors
146 (effectively creating the premultiplied image), do the filtering, and then
147 convert back to non-premult on exit.
148
149 When you use the pixel layouts STBIR_RGBA_PM, STBIR_BGRA_PM, STBIR_ARGB_PM,
150 STBIR_ABGR_PM, STBIR_RA_PM or STBIR_AR_PM, you are telling that the pixels
151 ARE premultiplied. In this case, the resizer doesn't have to do the
152 premultipling - it can filter directly on the input. This about twice as
153 fast as the non-premultiplied case, so it's the right option if your data is
154 already setup correctly.
155
156 When you use the pixel layout STBIR_4CHANNEL or STBIR_2CHANNEL, you are
157 telling us that there is no channel that represents transparency; it may be
158 RGB and some unrelated fourth channel that has been stored in the alpha
159 channel, but it is actually not alpha. No special processing will be
160 performed.
161
162 The difference between the generic 4 or 2 channel layouts, and the
163 specialized _PM versions is with the _PM versions you are telling us that
164 the data *is* alpha, just don't premultiply it. That's important when
165 using SRGB pixel formats, we need to know where the alpha is, because
166 it is converted linearly (rather than with the SRGB converters).
167
168 Because alpha weighting produces the same effect as premultiplying, you
169 even have the option with non-premultiplied inputs to let the resizer
170 produce a premultiplied output. Because the intially computed alpha-weighted
171 output image is effectively premultiplied, this is actually more performant
172 than the normal path which un-premultiplies the output image as a final step.
173
174 Finally, when converting both in and out of non-premulitplied space (for
175 example, when using STBIR_RGBA), we go to somewhat heroic measures to
176 ensure that areas with zero alpha value pixels get something reasonable
177 in the RGB values. If you don't care about the RGB values of zero alpha
178 pixels, you can call the stbir_set_non_pm_alpha_speed_over_quality()
179 function - this runs a premultiplied resize about 25% faster. That said,
180 when you really care about speed, using premultiplied pixels for both in
181 and out (STBIR_RGBA_PM, etc) much faster than both of these premultiplied
182 options.
183
184 PIXEL LAYOUT CONVERSION
185 The resizer can convert from some pixel layouts to others. When using the
186 stbir_set_pixel_layouts(), you can, for example, specify STBIR_RGBA
187 on input, and STBIR_ARGB on output, and it will re-organize the channels
188 during the resize. Currently, you can only convert between two pixel
189 layouts with the same number of channels.
190
191 DETERMINISM
192 We commit to being deterministic (from x64 to ARM to scalar to SIMD, etc).
193 This requires compiling with fast-math off (using at least /fp:precise).
194 Also, you must turn off fp-contracting (which turns mult+adds into fmas)!
195 We attempt to do this with pragmas, but with Clang, you usually want to add
196 -ffp-contract=off to the command line as well.
197
198 For 32-bit x86, you must use SSE and SSE2 codegen for determinism. That is,
199 if the scalar x87 unit gets used at all, we immediately lose determinism.
200 On Microsoft Visual Studio 2008 and earlier, from what we can tell there is
201 no way to be deterministic in 32-bit x86 (some x87 always leaks in, even
202 with fp:strict). On 32-bit x86 GCC, determinism requires both -msse2 and
203 -fpmath=sse.
204
205 Note that we will not be deterministic with float data containing NaNs -
206 the NaNs will propagate differently on different SIMD and platforms.
207
208 If you turn on STBIR_USE_FMA, then we will be deterministic with other
209 fma targets, but we will differ from non-fma targets (this is unavoidable,
210 because a fma isn't simply an add with a mult - it also introduces a
211 rounding difference compared to non-fma instruction sequences.
212
213 FLOAT PIXEL FORMAT RANGE
214 Any range of values can be used for the non-alpha float data that you pass
215 in (0 to 1, -1 to 1, whatever). However, if you are inputting float values
216 but *outputting* bytes or shorts, you must use a range of 0 to 1 so that we
217 scale back properly. The alpha channel must also be 0 to 1 for any format
218 that does premultiplication prior to resizing.
219
220 Note also that with float output, using filters with negative lobes, the
221 output filtered values might go slightly out of range. You can define
222 STBIR_FLOAT_LOW_CLAMP and/or STBIR_FLOAT_HIGH_CLAMP to specify the range
223 to clamp to on output, if that's important.
224
225 MAX/MIN SCALE FACTORS
226 The input pixel resolutions are in integers, and we do the internal pointer
227 resolution in size_t sized integers. However, the scale ratio from input
228 resolution to output resolution is calculated in float form. This means
229 the effective possible scale ratio is limited to 24 bits (or 16 million
230 to 1). As you get close to the size of the float resolution (again, 16
231 million pixels wide or high), you might start seeing float inaccuracy
232 issues in general in the pipeline. If you have to do extreme resizes,
233 you can usually do this is multiple stages (using float intermediate
234 buffers).
235
236 FLIPPED IMAGES
237 Stride is just the delta from one scanline to the next. This means you can
238 use a negative stride to handle inverted images (point to the final
239 scanline and use a negative stride). You can invert the input or output,
240 using negative strides.
241
242 DEFAULT FILTERS
243 For functions which don't provide explicit control over what filters to
244 use, you can change the compile-time defaults with:
245
246 #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something
247 #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something
248
249 See stbir_filter in the header-file section for the list of filters.
250
251 NEW FILTERS
252 A number of 1D filter kernels are supplied. For a list of supported
253 filters, see the stbir_filter enum. You can install your own filters by
254 using the stbir_set_filter_callbacks function.
255
256 PROGRESS
257 For interactive use with slow resize operations, you can use the
258 scanline callbacks in the extended API. It would have to be a *very* large
259 image resample to need progress though - we're very fast.
260
261 CEIL and FLOOR
262 In scalar mode, the only functions we use from math.h are ceilf and floorf,
263 but if you have your own versions, you can define the STBIR_CEILF(v) and
264 STBIR_FLOORF(v) macros and we'll use them instead. In SIMD, we just use
265 our own versions.
266
267 ASSERT
268 Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
269
270 PORTING FROM VERSION 1
271 The API has changed. You can continue to use the old version of stb_image_resize.h,
272 which is available in the "deprecated/" directory.
273
274 If you're using the old simple-to-use API, porting is straightforward.
275 (For more advanced APIs, read the documentation.)
276
277 stbir_resize_uint8():
278 - call `stbir_resize_uint8_linear`, cast channel count to `stbir_pixel_layout`
279
280 stbir_resize_float():
281 - call `stbir_resize_float_linear`, cast channel count to `stbir_pixel_layout`
282
283 stbir_resize_uint8_srgb():
284 - function name is unchanged
285 - cast channel count to `stbir_pixel_layout`
286 - above is sufficient unless your image has alpha and it's not RGBA/BGRA
287 - in that case, follow the below instructions for stbir_resize_uint8_srgb_edgemode
288
289 stbir_resize_uint8_srgb_edgemode()
290 - switch to the "medium complexity" API
291 - stbir_resize(), very similar API but a few more parameters:
292 - pixel_layout: cast channel count to `stbir_pixel_layout`
293 - data_type: STBIR_TYPE_UINT8_SRGB
294 - edge: unchanged (STBIR_EDGE_WRAP, etc.)
295 - filter: STBIR_FILTER_DEFAULT
296 - which channel is alpha is specified in stbir_pixel_layout, see enum for details
297
298 FUTURE TODOS
299 * For polyphase integral filters, we just memcpy the coeffs to dupe
300 them, but we should indirect and use the same coeff memory.
301 * Add pixel layout conversions for sensible different channel counts
302 (maybe, 1->3/4, 3->4, 4->1, 3->1).
303 * For SIMD encode and decode scanline routines, do any pre-aligning
304 for bad input/output buffer alignments and pitch?
305 * For very wide scanlines, we should we do vertical strips to stay within
306 L2 cache. Maybe do chunks of 1K pixels at a time. There would be
307 some pixel reconversion, but probably dwarfed by things falling out
308 of cache. Probably also something possible with alternating between
309 scattering and gathering at high resize scales?
310 * Should we have a multiple MIPs at the same time function (could keep
311 more memory in cache during multiple resizes)?
312 * Rewrite the coefficient generator to do many at once.
313 * AVX-512 vertical kernels - worried about downclocking here.
314 * Convert the reincludes to macros when we know they aren't changing.
315 * Experiment with pivoting the horizontal and always using the
316 vertical filters (which are faster, but perhaps not enough to overcome
317 the pivot cost and the extra memory touches). Need to buffer the whole
318 image so have to balance memory use.
319 * Most of our code is internally function pointers, should we compile
320 all the SIMD stuff always and dynamically dispatch?
321
322 CONTRIBUTORS
323 Jeff Roberts: 2.0 implementation, optimizations, SIMD
324 Martins Mozeiko: NEON simd, WASM simd, clang and GCC whisperer
325 Fabian Giesen: half float and srgb converters
326 Sean Barrett: API design, optimizations
327 Jorge L Rodriguez: Original 1.0 implementation
328 Aras Pranckevicius: bugfixes
329 Nathan Reed: warning fixes for 1.0
330
331 REVISIONS
332 2.18 (2026-03-25) fixed coefficient calculation when skipping a coefficient off
333 the left side of the window, added non-aligned access safe
334 memcpy mode for scalar path, fixed various typos, and fixed
335 define error in the float clamp output mode.
336 2.17 (2025-10-25) silly format bug in easy-to-use APIs.
337 2.16 (2025-10-21) fixed the easy-to-use APIs to allow inverted bitmaps (negative
338 strides), fix vertical filter kernel callback, fix threaded
339 gather buffer priming (and assert).
340 (thanks adipose, TainZerL, and Harrison Green)
341 2.15 (2025-07-17) fixed an assert in debug mode when using floats with input
342 callbacks, work around GCC warning when adding to null ptr
343 (thanks Johannes Spohr and Pyry Kovanen).
344 2.14 (2025-05-09) fixed a bug using downsampling gather horizontal first, and
345 scatter with vertical first.
346 2.13 (2025-02-27) fixed a bug when using input callbacks, turned off simd for
347 tiny-c, fixed some variables that should have been static,
348 fixes a bug when calculating temp memory with resizes that
349 exceed 2GB of temp memory (very large resizes).
350 2.12 (2024-10-18) fix incorrect use of user_data with STBIR_FREE
351 2.11 (2024-09-08) fix harmless asan warnings in 2-channel and 3-channel mode
352 with AVX-2, fix some weird scaling edge conditions with
353 point sample mode.
354 2.10 (2024-07-27) fix the defines GCC and mingw for loop unroll control,
355 fix MSVC 32-bit arm half float routines.
356 2.09 (2024-06-19) fix the defines for 32-bit ARM GCC builds (was selecting
357 hardware half floats).
358 2.08 (2024-06-10) fix for RGB->BGR three channel flips and add SIMD (thanks
359 to Ryan Salsbury), fix for sub-rect resizes, use the
360 pragmas to control unrolling when they are available.
361 2.07 (2024-05-24) fix for slow final split during threaded conversions of very
362 wide scanlines when downsampling (caused by extra input
363 converting), fix for wide scanline resamples with many
364 splits (int overflow), fix GCC warning.
365 2.06 (2024-02-10) fix for identical width/height 3x or more down-scaling
366 undersampling a single row on rare resize ratios (about 1%).
367 2.05 (2024-02-07) fix for 2 pixel to 1 pixel resizes with wrap (thanks Aras),
368 fix for output callback (thanks Julien Koenen).
369 2.04 (2023-11-17) fix for rare AVX bug, shadowed symbol (thanks Nikola Smiljanic).
370 2.03 (2023-11-01) ASAN and TSAN warnings fixed, minor tweaks.
371 2.00 (2023-10-10) mostly new source: new api, optimizations, simd, vertical-first, etc
372 2x-5x faster without simd, 4x-12x faster with simd,
373 in some cases, 20x to 40x faster esp resizing large to very small.
374 0.96 (2019-03-04) fixed warnings
375 0.95 (2017-07-23) fixed warnings
376 0.94 (2017-03-18) fixed warnings
377 0.93 (2017-03-03) fixed bug with certain combinations of heights
378 0.92 (2017-01-02) fix integer overflow on large (>2GB) images
379 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions
380 0.90 (2014-09-17) first released version
381
382 LICENSE
383 See end of file for license information.
384*/
385
386#if !defined(STB_IMAGE_RESIZE_DO_HORIZONTALS) && !defined(STB_IMAGE_RESIZE_DO_VERTICALS) && !defined(STB_IMAGE_RESIZE_DO_CODERS) // for internal re-includes
387
388#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE2_H
389#define STBIR_INCLUDE_STB_IMAGE_RESIZE2_H
390
391#include <stddef.h>
392#ifdef _MSC_VER
393typedef unsigned char stbir_uint8;
394typedef unsigned short stbir_uint16;
395typedef unsigned int stbir_uint32;
396typedef unsigned __int64 stbir_uint64;
397#else
398#include <stdint.h>
399typedef uint8_t stbir_uint8;
400typedef uint16_t stbir_uint16;
401typedef uint32_t stbir_uint32;
402typedef uint64_t stbir_uint64;
403#endif
404
405#ifndef STBIRDEF
406#ifdef STB_IMAGE_RESIZE_STATIC
407#define STBIRDEF static
408#else
409#ifdef __cplusplus
410#define STBIRDEF extern "C"
411#else
412#define STBIRDEF extern
413#endif
414#endif
415#endif
416
417//////////////////////////////////////////////////////////////////////////////
418//// start "header file" ///////////////////////////////////////////////////
419//
420// Easy-to-use API:
421//
422// * stride is the offset between successive rows of image data
423// in memory, in bytes. specify 0 for packed continuously in memory
424// * colorspace is linear or sRGB as specified by function name
425// * Uses the default filters
426// * Uses edge mode clamped
427// * returned result is 1 for success or 0 in case of an error.
428
429
430// stbir_pixel_layout specifies:
431// number of channels
432// order of channels
433// whether color is premultiplied by alpha
434// for back compatibility, you can cast the old channel count to an stbir_pixel_layout
435typedef enum
436{
437 STBIR_1CHANNEL = 1,
438 STBIR_2CHANNEL = 2,
439 STBIR_RGB = 3, // 3-chan, with order specified (for channel flipping)
440 STBIR_BGR = 0, // 3-chan, with order specified (for channel flipping)
441 STBIR_4CHANNEL = 5,
442
443 STBIR_RGBA = 4, // alpha formats, where alpha is NOT premultiplied into color channels
444 STBIR_BGRA = 6,
445 STBIR_ARGB = 7,
446 STBIR_ABGR = 8,
447 STBIR_RA = 9,
448 STBIR_AR = 10,
449
450 STBIR_RGBA_PM = 11, // alpha formats, where alpha is premultiplied into color channels
451 STBIR_BGRA_PM = 12,
452 STBIR_ARGB_PM = 13,
453 STBIR_ABGR_PM = 14,
454 STBIR_RA_PM = 15,
455 STBIR_AR_PM = 16,
456
457 STBIR_RGBA_NO_AW = 11, // alpha formats, where NO alpha weighting is applied at all!
458 STBIR_BGRA_NO_AW = 12, // these are just synonyms for the _PM flags (which also do
459 STBIR_ARGB_NO_AW = 13, // no alpha weighting). These names just make it more clear
460 STBIR_ABGR_NO_AW = 14, // for some folks).
461 STBIR_RA_NO_AW = 15,
462 STBIR_AR_NO_AW = 16,
463
464} stbir_pixel_layout;
465
466//===============================================================
467// Simple-complexity API
468//
469// If output_pixels is NULL (0), then we will allocate the buffer and return it to you.
470//--------------------------------
471
472STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes,
473 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
474 stbir_pixel_layout pixel_type );
475
476STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes,
477 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
478 stbir_pixel_layout pixel_type );
479
480STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes,
481 float *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
482 stbir_pixel_layout pixel_type );
483//===============================================================
484
485//===============================================================
486// Medium-complexity API
487//
488// This extends the easy-to-use API as follows:
489//
490// * Can specify the datatype - U8, U8_SRGB, U16, FLOAT, HALF_FLOAT
491// * Edge wrap can selected explicitly
492// * Filter can be selected explicitly
493//--------------------------------
494
495typedef enum
496{
497 STBIR_EDGE_CLAMP = 0,
498 STBIR_EDGE_REFLECT = 1,
499 STBIR_EDGE_WRAP = 2, // this edge mode is slower and uses more memory
500 STBIR_EDGE_ZERO = 3,
501} stbir_edge;
502
503typedef enum
504{
505 STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses
506 STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios
507 STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering
508 STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque
509 STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline
510 STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3
511 STBIR_FILTER_POINT_SAMPLE = 6, // Simple point sampling
512 STBIR_FILTER_OTHER = 7, // User callback specified
513} stbir_filter;
514
515typedef enum
516{
517 STBIR_TYPE_UINT8 = 0,
518 STBIR_TYPE_UINT8_SRGB = 1,
519 STBIR_TYPE_UINT8_SRGB_ALPHA = 2, // alpha channel, when present, should also be SRGB (this is very unusual)
520 STBIR_TYPE_UINT16 = 3,
521 STBIR_TYPE_FLOAT = 4,
522 STBIR_TYPE_HALF_FLOAT = 5
523} stbir_datatype;
524
525// medium api
526STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes,
527 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
528 stbir_pixel_layout pixel_layout, stbir_datatype data_type,
529 stbir_edge edge, stbir_filter filter );
530//===============================================================
531
532
533
534//===============================================================
535// Extended-complexity API
536//
537// This API exposes all resize functionality.
538//
539// * Separate filter types for each axis
540// * Separate edge modes for each axis
541// * Separate input and output data types
542// * Can specify regions with subpixel correctness
543// * Can specify alpha flags
544// * Can specify a memory callback
545// * Can specify a callback data type for pixel input and output
546// * Can be threaded for a single resize
547// * Can be used to resize many frames without recalculating the sampler info
548//
549// Use this API as follows:
550// 1) Call the stbir_resize_init function on a local STBIR_RESIZE structure
551// 2) Call any of the stbir_set functions
552// 3) Optionally call stbir_build_samplers() if you are going to resample multiple times
553// with the same input and output dimensions (like resizing video frames)
554// 4) Resample by calling stbir_resize_extended().
555// 5) Call stbir_free_samplers() if you called stbir_build_samplers()
556//--------------------------------
557
558
559// Types:
560
561// INPUT CALLBACK: this callback is used for input scanlines
562typedef void const * stbir_input_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context );
563
564// OUTPUT CALLBACK: this callback is used for output scanlines
565typedef void stbir_output_callback( void const * output_ptr, int num_pixels, int y, void * context );
566
567// callbacks for user installed filters
568typedef float stbir__kernel_callback( float x, float scale, void * user_data ); // centered at zero
569typedef float stbir__support_callback( float scale, void * user_data );
570
571// internal structure with precomputed scaling
572typedef struct stbir__info stbir__info;
573
574typedef struct STBIR_RESIZE // use the stbir_resize_init and stbir_override functions to set these values for future compatibility
575{
576 void * user_data;
577 void const * input_pixels;
578 int input_w, input_h;
579 double input_s0, input_t0, input_s1, input_t1;
580 stbir_input_callback * input_cb;
581 void * output_pixels;
582 int output_w, output_h;
583 int output_subx, output_suby, output_subw, output_subh;
584 stbir_output_callback * output_cb;
585 int input_stride_in_bytes;
586 int output_stride_in_bytes;
587 int splits;
588 int fast_alpha;
589 int needs_rebuild;
590 int called_alloc;
591 stbir_pixel_layout input_pixel_layout_public;
592 stbir_pixel_layout output_pixel_layout_public;
593 stbir_datatype input_data_type;
594 stbir_datatype output_data_type;
595 stbir_filter horizontal_filter, vertical_filter;
596 stbir_edge horizontal_edge, vertical_edge;
597 stbir__kernel_callback * horizontal_filter_kernel; stbir__support_callback * horizontal_filter_support;
598 stbir__kernel_callback * vertical_filter_kernel; stbir__support_callback * vertical_filter_support;
599 stbir__info * samplers;
600} STBIR_RESIZE;
601
602// extended complexity api
603
604
605// First off, you must ALWAYS call stbir_resize_init on your resize structure before any of the other calls!
606STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize,
607 const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero
608 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero
609 stbir_pixel_layout pixel_layout, stbir_datatype data_type );
610
611//===============================================================
612// You can update these parameters any time after resize_init and there is no cost
613//--------------------------------
614
615STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type );
616STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ); // no callbacks by default
617STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ); // pass back STBIR_RESIZE* by default
618STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes );
619
620//===============================================================
621
622
623//===============================================================
624// If you call any of these functions, you will trigger a sampler rebuild!
625//--------------------------------
626
627STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ); // sets new buffer layouts
628STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ); // CLAMP by default
629
630STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ); // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default
631STBIRDEF 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 );
632
633STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets both sub-regions (full regions by default)
634STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ); // sets input sub-region (full region by default)
635STBIRDEF 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)
636
637// when inputting AND outputting non-premultiplied alpha pixels, we use a slower but higher quality technique
638// that fills the zero alpha pixel's RGB values with something plausible. If you don't care about areas of
639// zero alpha, you can call this function to get about a 25% speed improvement for STBIR_RGBA to STBIR_RGBA
640// types of resizes.
641STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality );
642//===============================================================
643
644
645//===============================================================
646// You can call build_samplers to prebuild all the internal data we need to resample.
647// Then, if you call resize_extended many times with the same resize, you only pay the
648// cost once.
649// If you do call build_samplers, you MUST call free_samplers eventually.
650//--------------------------------
651
652// This builds the samplers and does one allocation
653STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize );
654
655// You MUST call this, if you call stbir_build_samplers or stbir_build_samplers_with_splits
656STBIRDEF void stbir_free_samplers( STBIR_RESIZE * resize );
657//===============================================================
658
659
660// And this is the main function to perform the resize synchronously on one thread.
661STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize );
662
663
664//===============================================================
665// Use these functions for multithreading.
666// 1) You call stbir_build_samplers_with_splits first on the main thread
667// 2) Then stbir_resize_with_split on each thread
668// 3) stbir_free_samplers when done on the main thread
669//--------------------------------
670
671// This will build samplers for threading.
672// You can pass in the number of threads you'd like to use (try_splits).
673// It returns the number of splits (threads) that you can call it with.
674/// It might be less if the image resize can't be split up that many ways.
675
676STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int try_splits );
677
678// This function does a split of the resizing (you call this fuction for each
679// split, on multiple threads). A split is a piece of the output resize pixel space.
680
681// Note that you MUST call stbir_build_samplers_with_splits before stbir_resize_extended_split!
682
683// Usually, you will always call stbir_resize_split with split_start as the thread_index
684// and "1" for the split_count.
685// But, if you have a weird situation where you MIGHT want 8 threads, but sometimes
686// only 4 threads, you can use 0,2,4,6 for the split_start's and use "2" for the
687// split_count each time to turn in into a 4 thread resize. (This is unusual).
688
689STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count );
690//===============================================================
691
692
693//===============================================================
694// Pixel Callbacks info:
695//--------------------------------
696
697// The input callback is super flexible - it calls you with the input address
698// (based on the stride and base pointer), it gives you an optional_output
699// pointer that you can fill, or you can just return your own pointer into
700// your own data.
701//
702// You can also do conversion from non-supported data types if necessary - in
703// this case, you ignore the input_ptr and just use the x and y parameters to
704// calculate your own input_ptr based on the size of each non-supported pixel.
705// (Something like the third example below.)
706//
707// You can also install just an input or just an output callback by setting the
708// callback that you don't want to zero.
709//
710// First example, progress: (getting a callback that you can monitor the progress):
711// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context )
712// {
713// percentage_done = y / input_height;
714// return input_ptr; // use buffer from call
715// }
716//
717// Next example, copying: (copy from some other buffer or stream):
718// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context )
719// {
720// CopyOrStreamData( optional_output, other_data_src, num_pixels * pixel_width_in_bytes );
721// return optional_output; // return the optional buffer that we filled
722// }
723//
724// Third example, input another buffer without copying: (zero-copy from other buffer):
725// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context )
726// {
727// void * pixels = ( (char*) other_image_base ) + ( y * other_image_stride ) + ( x * other_pixel_width_in_bytes );
728// return pixels; // return pointer to your data without copying
729// }
730//
731//
732// The output callback is considerably simpler - it just calls you so that you can dump
733// out each scanline. You could even directly copy out to disk if you have a simple format
734// like TGA or BMP. You can also convert to other output types here if you want.
735//
736// Simple example:
737// void const * my_output( void * output_ptr, int num_pixels, int y, void * context )
738// {
739// percentage_done = y / output_height;
740// fwrite( output_ptr, pixel_width_in_bytes, num_pixels, output_file );
741// }
742//===============================================================
743
744
745
746
747//===============================================================
748// optional built-in profiling API
749//--------------------------------
750
751#ifdef STBIR_PROFILE
752
753typedef struct STBIR_PROFILE_INFO
754{
755 stbir_uint64 total_clocks;
756
757 // how many clocks spent (of total_clocks) in the various resize routines, along with a string description
758 // there are "resize_count" number of zones
759 stbir_uint64 clocks[ 8 ];
760 char const ** descriptions;
761
762 // count of clocks and descriptions
763 stbir_uint32 count;
764} STBIR_PROFILE_INFO;
765
766// use after calling stbir_resize_extended (or stbir_build_samplers or stbir_build_samplers_with_splits)
767STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize );
768
769// use after calling stbir_resize_extended
770STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize );
771
772// use after calling stbir_resize_extended_split
773STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize, int split_start, int split_num );
774
775//===============================================================
776
777#endif
778
779
780//// end header file /////////////////////////////////////////////////////
781#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE2_H
782
783#if defined(STB_IMAGE_RESIZE_IMPLEMENTATION) || defined(STB_IMAGE_RESIZE2_IMPLEMENTATION)
784
785#ifndef STBIR_ASSERT
786#include <assert.h>
787#define STBIR_ASSERT(x) assert(x)
788#endif
789
790#ifndef STBIR_MALLOC
791#include <stdlib.h>
792#define STBIR_MALLOC(size,user_data) ((void)(user_data), malloc(size))
793#define STBIR_FREE(ptr,user_data) ((void)(user_data), free(ptr))
794// (we used the comma operator to evaluate user_data, to avoid "unused parameter" warnings)
795#endif
796
797#ifdef _MSC_VER
798
799#define stbir__inline __forceinline
800
801#else
802
803#define stbir__inline __inline__
804
805// Clang address sanitizer
806#if defined(__has_feature)
807 #if __has_feature(address_sanitizer) || __has_feature(memory_sanitizer)
808 #ifndef STBIR__SEPARATE_ALLOCATIONS
809 #define STBIR__SEPARATE_ALLOCATIONS
810 #endif
811 #endif
812#endif
813
814#endif
815
816// GCC and MSVC
817#if defined(__SANITIZE_ADDRESS__)
818 #ifndef STBIR__SEPARATE_ALLOCATIONS
819 #define STBIR__SEPARATE_ALLOCATIONS
820 #endif
821#endif
822
823// Always turn off automatic FMA use - use STBIR_USE_FMA if you want.
824// Otherwise, this is a determinism disaster.
825#ifndef STBIR_DONT_CHANGE_FP_CONTRACT // override in case you don't want this behavior
826#if defined(_MSC_VER) && !defined(__clang__)
827#if _MSC_VER > 1200
828#pragma fp_contract(off)
829#endif
830#elif defined(__GNUC__) && !defined(__clang__)
831#pragma GCC optimize("fp-contract=off")
832#else
833#pragma STDC FP_CONTRACT OFF
834#endif
835#endif
836
837#ifdef _MSC_VER
838#define STBIR__UNUSED(v) (void)(v)
839#else
840#define STBIR__UNUSED(v) (void)sizeof(v)
841#endif
842
843#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0]))
844
845
846#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE
847#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
848#endif
849
850#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE
851#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL
852#endif
853
854
855#ifndef STBIR__HEADER_FILENAME
856#define STBIR__HEADER_FILENAME "stb_image_resize2.h"
857#endif
858
859// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types
860// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible
861typedef enum
862{
863 STBIRI_1CHANNEL = 0,
864 STBIRI_2CHANNEL = 1,
865 STBIRI_RGB = 2,
866 STBIRI_BGR = 3,
867 STBIRI_4CHANNEL = 4,
868
869 STBIRI_RGBA = 5,
870 STBIRI_BGRA = 6,
871 STBIRI_ARGB = 7,
872 STBIRI_ABGR = 8,
873 STBIRI_RA = 9,
874 STBIRI_AR = 10,
875
876 STBIRI_RGBA_PM = 11,
877 STBIRI_BGRA_PM = 12,
878 STBIRI_ARGB_PM = 13,
879 STBIRI_ABGR_PM = 14,
880 STBIRI_RA_PM = 15,
881 STBIRI_AR_PM = 16,
882} stbir_internal_pixel_layout;
883
884// define the public pixel layouts to not compile inside the implementation (to avoid accidental use)
885#define STBIR_BGR bad_dont_use_in_implementation
886#define STBIR_1CHANNEL STBIR_BGR
887#define STBIR_2CHANNEL STBIR_BGR
888#define STBIR_RGB STBIR_BGR
889#define STBIR_RGBA STBIR_BGR
890#define STBIR_4CHANNEL STBIR_BGR
891#define STBIR_BGRA STBIR_BGR
892#define STBIR_ARGB STBIR_BGR
893#define STBIR_ABGR STBIR_BGR
894#define STBIR_RA STBIR_BGR
895#define STBIR_AR STBIR_BGR
896#define STBIR_RGBA_PM STBIR_BGR
897#define STBIR_BGRA_PM STBIR_BGR
898#define STBIR_ARGB_PM STBIR_BGR
899#define STBIR_ABGR_PM STBIR_BGR
900#define STBIR_RA_PM STBIR_BGR
901#define STBIR_AR_PM STBIR_BGR
902
903// must match stbir_datatype
904static unsigned char stbir__type_size[] = {
905 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
906};
907
908// When gathering, the contributors are which source pixels contribute.
909// When scattering, the contributors are which destination pixels are contributed to.
910typedef struct
911{
912 int n0; // First contributing pixel
913 int n1; // Last contributing pixel
914} stbir__contributors;
915
916typedef struct
917{
918 int lowest; // First sample index for whole filter
919 int highest; // Last sample index for whole filter
920 int widest; // widest single set of samples for an output
921} stbir__filter_extent_info;
922
923typedef struct
924{
925 int n0; // First pixel of decode buffer to write to
926 int n1; // Last pixel of decode that will be written to
927 int pixel_offset_for_input; // Pixel offset into input_scanline
928} stbir__span;
929
930typedef struct stbir__scale_info
931{
932 int input_full_size;
933 int output_sub_size;
934 float scale;
935 float inv_scale;
936 float pixel_shift; // starting shift in output pixel space (in pixels)
937 int scale_is_rational;
938 stbir_uint32 scale_numerator, scale_denominator;
939} stbir__scale_info;
940
941typedef struct
942{
943 stbir__contributors * contributors;
944 float* coefficients;
945 stbir__contributors * gather_prescatter_contributors;
946 float * gather_prescatter_coefficients;
947 stbir__scale_info scale_info;
948 float support;
949 stbir_filter filter_enum;
950 stbir__kernel_callback * filter_kernel;
951 stbir__support_callback * filter_support;
952 stbir_edge edge;
953 int coefficient_width;
954 int filter_pixel_width;
955 int filter_pixel_margin;
956 int num_contributors;
957 int contributors_size;
958 int coefficients_size;
959 stbir__filter_extent_info extent_info;
960 int is_gather; // 0 = scatter, 1 = gather with scale >= 1, 2 = gather with scale < 1
961 int gather_prescatter_num_contributors;
962 int gather_prescatter_coefficient_width;
963 int gather_prescatter_contributors_size;
964 int gather_prescatter_coefficients_size;
965} stbir__sampler;
966
967typedef struct
968{
969 stbir__contributors conservative;
970 int edge_sizes[2]; // this can be less than filter_pixel_margin, if the filter and scaling falls off
971 stbir__span spans[2]; // can be two spans, if doing input subrect with clamp mode WRAP
972} stbir__extents;
973
974typedef struct
975{
976#ifdef STBIR_PROFILE
977 union
978 {
979 struct { stbir_uint64 total, looping, vertical, horizontal, decode, encode, alpha, unalpha; } named;
980 stbir_uint64 array[8];
981 } profile;
982 stbir_uint64 * current_zone_excluded_ptr;
983#endif
984 float* decode_buffer;
985
986 int ring_buffer_first_scanline;
987 int ring_buffer_last_scanline;
988 int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer
989 int start_output_y, end_output_y;
990 int start_input_y, end_input_y; // used in scatter only
991
992 #ifdef STBIR__SEPARATE_ALLOCATIONS
993 float** ring_buffers; // one pointer for each ring buffer
994 #else
995 float* ring_buffer; // one big buffer that we index into
996 #endif
997
998 float* vertical_buffer;
999
1000 char no_cache_straddle[64];
1001} stbir__per_split_info;
1002
1003typedef float * stbir__decode_pixels_func( float * decode, int width_times_channels, void const * input );
1004typedef void stbir__alpha_weight_func( float * decode_buffer, int width_times_channels );
1005typedef void stbir__horizontal_gather_channels_func( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer,
1006 stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width );
1007typedef void stbir__alpha_unweight_func(float * encode_buffer, int width_times_channels );
1008typedef void stbir__encode_pixels_func( void * output, int width_times_channels, float const * encode );
1009
1010struct stbir__info
1011{
1012#ifdef STBIR_PROFILE
1013 union
1014 {
1015 struct { stbir_uint64 total, build, alloc, horizontal, vertical, cleanup, pivot; } named;
1016 stbir_uint64 array[7];
1017 } profile;
1018 stbir_uint64 * current_zone_excluded_ptr;
1019#endif
1020 stbir__sampler horizontal;
1021 stbir__sampler vertical;
1022
1023 void const * input_data;
1024 void * output_data;
1025
1026 int input_stride_bytes;
1027 int output_stride_bytes;
1028 int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter)
1029 int ring_buffer_num_entries; // Total number of entries in the ring buffer.
1030
1031 stbir_datatype input_type;
1032 stbir_datatype output_type;
1033
1034 stbir_input_callback * in_pixels_cb;
1035 void * user_data;
1036 stbir_output_callback * out_pixels_cb;
1037
1038 stbir__extents scanline_extents;
1039
1040 void * alloced_mem;
1041 stbir__per_split_info * split_info; // by default 1, but there will be N of these allocated based on the thread init you did
1042
1043 stbir__decode_pixels_func * decode_pixels;
1044 stbir__alpha_weight_func * alpha_weight;
1045 stbir__horizontal_gather_channels_func * horizontal_gather_channels;
1046 stbir__alpha_unweight_func * alpha_unweight;
1047 stbir__encode_pixels_func * encode_pixels;
1048
1049 int alloc_ring_buffer_num_entries; // Number of entries in the ring buffer that will be allocated
1050 int splits; // count of splits
1051
1052 stbir_internal_pixel_layout input_pixel_layout_internal;
1053 stbir_internal_pixel_layout output_pixel_layout_internal;
1054
1055 int input_color_and_type;
1056 int offset_x, offset_y; // offset within output_data
1057 int vertical_first;
1058 int channels;
1059 int effective_channels; // same as channels, except on RGBA/ARGB (7), or XA/AX (3)
1060 size_t alloced_total;
1061};
1062
1063
1064#define stbir__max_uint8_as_float 255.0f
1065#define stbir__max_uint16_as_float 65535.0f
1066#define stbir__max_uint8_as_float_inverted 3.9215689e-03f // (1.0f/255.0f)
1067#define stbir__max_uint16_as_float_inverted 1.5259022e-05f // (1.0f/65535.0f)
1068#define stbir__small_float ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20))
1069
1070// min/max friendly
1071#define STBIR_CLAMP(x, xmin, xmax) for(;;) { \
1072 if ( (x) < (xmin) ) (x) = (xmin); \
1073 if ( (x) > (xmax) ) (x) = (xmax); \
1074 break; \
1075}
1076
1077static stbir__inline int stbir__min(int a, int b)
1078{
1079 return a < b ? a : b;
1080}
1081
1082static stbir__inline int stbir__max(int a, int b)
1083{
1084 return a > b ? a : b;
1085}
1086
1087static float stbir__srgb_uchar_to_linear_float[256] = {
1088 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f,
1089 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f,
1090 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f,
1091 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f,
1092 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f,
1093 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f,
1094 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f,
1095 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f,
1096 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f,
1097 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f,
1098 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f,
1099 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f,
1100 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f,
1101 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f,
1102 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f,
1103 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f,
1104 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f,
1105 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f,
1106 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f,
1107 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f,
1108 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f,
1109 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f,
1110 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f,
1111 0.982251f, 0.991102f, 1.0f
1112};
1113
1114typedef union
1115{
1116 unsigned int u;
1117 float f;
1118} stbir__FP32;
1119
1120// From https://gist.github.com/rygorous/2203834
1121
1122static const stbir_uint32 fp32_to_srgb8_tab4[104] = {
1123 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
1124 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
1125 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
1126 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
1127 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
1128 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
1129 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
1130 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
1131 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
1132 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
1133 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
1134 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
1135 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
1136};
1137
1138static stbir__inline stbir_uint8 stbir__linear_to_srgb_uchar(float in)
1139{
1140 static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps
1141 static const stbir__FP32 minval = { (127-13) << 23 };
1142 stbir_uint32 tab,bias,scale,t;
1143 stbir__FP32 f;
1144
1145 // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively.
1146 // The tests are carefully written so that NaNs map to 0, same as in the reference
1147 // implementation.
1148 if (!(in > minval.f)) // written this way to catch NaNs
1149 return 0;
1150 if (in > almostone.f)
1151 return 255;
1152
1153 // Do the table lookup and unpack bias, scale
1154 f.f = in;
1155 tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20];
1156 bias = (tab >> 16) << 9;
1157 scale = tab & 0xffff;
1158
1159 // Grab next-highest mantissa bits and perform linear interpolation
1160 t = (f.u >> 12) & 0xff;
1161 return (unsigned char) ((bias + scale*t) >> 16);
1162}
1163
1164#ifndef STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT
1165#define STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT 32 // when downsampling and <= 32 scanlines of buffering, use gather. gather used down to 1/8th scaling for 25% win.
1166#endif
1167
1168#ifndef STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS
1169#define STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS 4 // when threading, what is the minimum number of scanlines for a split?
1170#endif
1171
1172#define STBIR_INPUT_CALLBACK_PADDING 3
1173
1174#ifdef _M_IX86_FP
1175#if ( _M_IX86_FP >= 1 )
1176#ifndef STBIR_SSE
1177#define STBIR_SSE
1178#endif
1179#endif
1180#endif
1181
1182#ifdef __TINYC__
1183 // tiny c has no intrinsics yet - this can become a version check if they add them
1184 #define STBIR_NO_SIMD
1185#endif
1186
1187#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(_M_AMD64) || defined(__SSE2__) || defined(STBIR_SSE) || defined(STBIR_SSE2)
1188 #ifndef STBIR_SSE2
1189 #define STBIR_SSE2
1190 #endif
1191 #if defined(__AVX__) || defined(STBIR_AVX2)
1192 #ifndef STBIR_AVX
1193 #ifndef STBIR_NO_AVX
1194 #define STBIR_AVX
1195 #endif
1196 #endif
1197 #endif
1198 #if defined(__AVX2__) || defined(STBIR_AVX2)
1199 #ifndef STBIR_NO_AVX2
1200 #ifndef STBIR_AVX2
1201 #define STBIR_AVX2
1202 #endif
1203 #if defined( _MSC_VER ) && !defined(__clang__)
1204 #ifndef STBIR_FP16C // FP16C instructions are on all AVX2 cpus, so we can autoselect it here on microsoft - clang needs -mf16c
1205 #define STBIR_FP16C
1206 #endif
1207 #endif
1208 #endif
1209 #endif
1210 #ifdef __F16C__
1211 #ifndef STBIR_FP16C // turn on FP16C instructions if the define is set (for clang and gcc)
1212 #define STBIR_FP16C
1213 #endif
1214 #endif
1215#endif
1216
1217#if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || ((__ARM_NEON_FP & 4) != 0) || defined(__ARM_NEON__)
1218#ifndef STBIR_NEON
1219#define STBIR_NEON
1220#endif
1221#endif
1222
1223#if defined(_M_ARM) || defined(__arm__)
1224#ifdef STBIR_USE_FMA
1225#undef STBIR_USE_FMA // no FMA for 32-bit arm on MSVC
1226#endif
1227#endif
1228
1229#if defined(__wasm__) && defined(__wasm_simd128__)
1230#ifndef STBIR_WASM
1231#define STBIR_WASM
1232#endif
1233#endif
1234
1235// restrict pointers for the output pointers, other loop and unroll control
1236#if defined( _MSC_VER ) && !defined(__clang__)
1237 #define STBIR_STREAMOUT_PTR( star ) star __restrict
1238 #define STBIR_NO_UNROLL( ptr ) __assume(ptr) // this oddly keeps msvc from unrolling a loop
1239 #if _MSC_VER >= 1900
1240 #define STBIR_NO_UNROLL_LOOP_START __pragma(loop( no_vector ))
1241 #else
1242 #define STBIR_NO_UNROLL_LOOP_START
1243 #endif
1244#elif defined( __clang__ )
1245 #define STBIR_STREAMOUT_PTR( star ) star __restrict__
1246 #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr))
1247 #if ( __clang_major__ >= 4 ) || ( ( __clang_major__ >= 3 ) && ( __clang_minor__ >= 5 ) )
1248 #define STBIR_NO_UNROLL_LOOP_START _Pragma("clang loop unroll(disable)") _Pragma("clang loop vectorize(disable)")
1249 #else
1250 #define STBIR_NO_UNROLL_LOOP_START
1251 #endif
1252#elif defined( __GNUC__ )
1253 #define STBIR_STREAMOUT_PTR( star ) star __restrict__
1254 #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr))
1255 #if __GNUC__ >= 14
1256 #define STBIR_NO_UNROLL_LOOP_START _Pragma("GCC unroll 0") _Pragma("GCC novector")
1257 #else
1258 #define STBIR_NO_UNROLL_LOOP_START
1259 #endif
1260 #define STBIR_NO_UNROLL_LOOP_START_INF_FOR
1261#else
1262 #define STBIR_STREAMOUT_PTR( star ) star
1263 #define STBIR_NO_UNROLL( ptr )
1264 #define STBIR_NO_UNROLL_LOOP_START
1265#endif
1266
1267#ifndef STBIR_NO_UNROLL_LOOP_START_INF_FOR
1268#define STBIR_NO_UNROLL_LOOP_START_INF_FOR STBIR_NO_UNROLL_LOOP_START
1269#endif
1270
1271#ifdef STBIR_NO_SIMD // force simd off for whatever reason
1272
1273// force simd off overrides everything else, so clear it all
1274
1275#ifdef STBIR_SSE2
1276#undef STBIR_SSE2
1277#endif
1278
1279#ifdef STBIR_AVX
1280#undef STBIR_AVX
1281#endif
1282
1283#ifdef STBIR_NEON
1284#undef STBIR_NEON
1285#endif
1286
1287#ifdef STBIR_AVX2
1288#undef STBIR_AVX2
1289#endif
1290
1291#ifdef STBIR_FP16C
1292#undef STBIR_FP16C
1293#endif
1294
1295#ifdef STBIR_WASM
1296#undef STBIR_WASM
1297#endif
1298
1299#ifdef STBIR_SIMD
1300#undef STBIR_SIMD
1301#endif
1302
1303#else // STBIR_SIMD
1304
1305#ifdef STBIR_SSE2
1306 #include <emmintrin.h>
1307
1308 #define stbir__simdf __m128
1309 #define stbir__simdi __m128i
1310
1311 #define stbir_simdi_castf( reg ) _mm_castps_si128(reg)
1312 #define stbir_simdf_casti( reg ) _mm_castsi128_ps(reg)
1313
1314 #define stbir__simdf_load( reg, ptr ) (reg) = _mm_loadu_ps( (float const*)(ptr) )
1315 #define stbir__simdi_load( reg, ptr ) (reg) = _mm_loadu_si128 ( (stbir__simdi const*)(ptr) )
1316 #define stbir__simdf_load1( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf)
1317 #define stbir__simdi_load1( out, ptr ) (out) = _mm_castps_si128( _mm_load_ss( (float const*)(ptr) ))
1318 #define stbir__simdf_load1z( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values must be zero
1319 #define stbir__simdf_frep4( fvar ) _mm_set_ps1( fvar )
1320 #define stbir__simdf_load1frep4( out, fvar ) (out) = _mm_set_ps1( fvar )
1321 #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)
1322 #define stbir__simdf_load2z( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values must be zero
1323 #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = _mm_castpd_ps(_mm_loadh_pd( _mm_castps_pd(reg), (double*)(ptr) ))
1324
1325 #define stbir__simdf_zeroP() _mm_setzero_ps()
1326 #define stbir__simdf_zero( reg ) (reg) = _mm_setzero_ps()
1327
1328 #define stbir__simdf_store( ptr, reg ) _mm_storeu_ps( (float*)(ptr), reg )
1329 #define stbir__simdf_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), reg )
1330 #define stbir__simdf_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), _mm_castps_si128(reg) )
1331 #define stbir__simdf_store2h( ptr, reg ) _mm_storeh_pd( (double*)(ptr), _mm_castps_pd(reg) )
1332
1333 #define stbir__simdi_store( ptr, reg ) _mm_storeu_si128( (__m128i*)(ptr), reg )
1334 #define stbir__simdi_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), _mm_castsi128_ps(reg) )
1335 #define stbir__simdi_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), (reg) )
1336
1337 #define stbir__prefetch( ptr ) _mm_prefetch((char*)(ptr), _MM_HINT_T0 )
1338
1339 #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \
1340 { \
1341 stbir__simdi zero = _mm_setzero_si128(); \
1342 out2 = _mm_unpacklo_epi8( ireg, zero ); \
1343 out3 = _mm_unpackhi_epi8( ireg, zero ); \
1344 out0 = _mm_unpacklo_epi16( out2, zero ); \
1345 out1 = _mm_unpackhi_epi16( out2, zero ); \
1346 out2 = _mm_unpacklo_epi16( out3, zero ); \
1347 out3 = _mm_unpackhi_epi16( out3, zero ); \
1348 }
1349
1350#define stbir__simdi_expand_u8_to_1u32(out,ireg) \
1351 { \
1352 stbir__simdi zero = _mm_setzero_si128(); \
1353 out = _mm_unpacklo_epi8( ireg, zero ); \
1354 out = _mm_unpacklo_epi16( out, zero ); \
1355 }
1356
1357 #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \
1358 { \
1359 stbir__simdi zero = _mm_setzero_si128(); \
1360 out0 = _mm_unpacklo_epi16( ireg, zero ); \
1361 out1 = _mm_unpackhi_epi16( ireg, zero ); \
1362 }
1363
1364 #define stbir__simdf_convert_float_to_i32( i, f ) (i) = _mm_cvttps_epi32(f)
1365 #define stbir__simdf_convert_float_to_int( f ) _mm_cvtt_ss2si(f)
1366 #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()))))
1367 #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()))))
1368
1369 #define stbir__simdi_to_int( i ) _mm_cvtsi128_si32(i)
1370 #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = _mm_cvtepi32_ps( ireg )
1371 #define stbir__simdf_add( out, reg0, reg1 ) (out) = _mm_add_ps( reg0, reg1 )
1372 #define stbir__simdf_mult( out, reg0, reg1 ) (out) = _mm_mul_ps( reg0, reg1 )
1373 #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = _mm_mul_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) )
1374 #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = _mm_mul_ss( reg, _mm_load_ss( (float const*)(ptr) ) )
1375 #define stbir__simdf_add_mem( out, reg, ptr ) (out) = _mm_add_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) )
1376 #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = _mm_add_ss( reg, _mm_load_ss( (float const*)(ptr) ) )
1377
1378 #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd
1379 #include <immintrin.h>
1380 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_fmadd_ps( mul1, mul2, add )
1381 #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_fmadd_ss( mul1, mul2, add )
1382 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ps( mul, _mm_loadu_ps( (float const*)(ptr) ), add )
1383 #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ss( mul, _mm_load_ss( (float const*)(ptr) ), add )
1384 #else
1385 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_add_ps( add, _mm_mul_ps( mul1, mul2 ) )
1386 #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_add_ss( add, _mm_mul_ss( mul1, mul2 ) )
1387 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_add_ps( add, _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ) )
1388 #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_add_ss( add, _mm_mul_ss( mul, _mm_load_ss( (float const*)(ptr) ) ) )
1389 #endif
1390
1391 #define stbir__simdf_add1( out, reg0, reg1 ) (out) = _mm_add_ss( reg0, reg1 )
1392 #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = _mm_mul_ss( reg0, reg1 )
1393
1394 #define stbir__simdf_and( out, reg0, reg1 ) (out) = _mm_and_ps( reg0, reg1 )
1395 #define stbir__simdf_or( out, reg0, reg1 ) (out) = _mm_or_ps( reg0, reg1 )
1396
1397 #define stbir__simdf_min( out, reg0, reg1 ) (out) = _mm_min_ps( reg0, reg1 )
1398 #define stbir__simdf_max( out, reg0, reg1 ) (out) = _mm_max_ps( reg0, reg1 )
1399 #define stbir__simdf_min1( out, reg0, reg1 ) (out) = _mm_min_ss( reg0, reg1 )
1400 #define stbir__simdf_max1( out, reg0, reg1 ) (out) = _mm_max_ss( reg0, reg1 )
1401
1402 #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) ) )
1403 #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) ) )
1404
1405 static const stbir__simdf STBIR_zeroones = { 0.0f,1.0f,0.0f,1.0f };
1406 static const stbir__simdf STBIR_onezeros = { 1.0f,0.0f,1.0f,0.0f };
1407 #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) ) )
1408 #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) ) )
1409 #define stbir__simdf_a1a1( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_srli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_zeroones )
1410 #define stbir__simdf_1a1a( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_slli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_onezeros )
1411
1412 #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) ) )
1413
1414 #define stbir__simdi_and( out, reg0, reg1 ) (out) = _mm_and_si128( reg0, reg1 )
1415 #define stbir__simdi_or( out, reg0, reg1 ) (out) = _mm_or_si128( reg0, reg1 )
1416 #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = _mm_madd_epi16( reg0, reg1 )
1417
1418 #define stbir__simdf_pack_to_8bytes(out,aa,bb) \
1419 { \
1420 stbir__simdf af,bf; \
1421 stbir__simdi a,b; \
1422 af = _mm_min_ps( aa, STBIR_max_uint8_as_float ); \
1423 bf = _mm_min_ps( bb, STBIR_max_uint8_as_float ); \
1424 af = _mm_max_ps( af, _mm_setzero_ps() ); \
1425 bf = _mm_max_ps( bf, _mm_setzero_ps() ); \
1426 a = _mm_cvttps_epi32( af ); \
1427 b = _mm_cvttps_epi32( bf ); \
1428 a = _mm_packs_epi32( a, b ); \
1429 out = _mm_packus_epi16( a, a ); \
1430 }
1431
1432 #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \
1433 stbir__simdf_load( o0, (ptr) ); \
1434 stbir__simdf_load( o1, (ptr)+4 ); \
1435 stbir__simdf_load( o2, (ptr)+8 ); \
1436 stbir__simdf_load( o3, (ptr)+12 ); \
1437 { \
1438 __m128 tmp0, tmp1, tmp2, tmp3; \
1439 tmp0 = _mm_unpacklo_ps(o0, o1); \
1440 tmp2 = _mm_unpacklo_ps(o2, o3); \
1441 tmp1 = _mm_unpackhi_ps(o0, o1); \
1442 tmp3 = _mm_unpackhi_ps(o2, o3); \
1443 o0 = _mm_movelh_ps(tmp0, tmp2); \
1444 o1 = _mm_movehl_ps(tmp2, tmp0); \
1445 o2 = _mm_movelh_ps(tmp1, tmp3); \
1446 o3 = _mm_movehl_ps(tmp3, tmp1); \
1447 }
1448
1449 #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \
1450 r0 = _mm_packs_epi32( r0, r1 ); \
1451 r2 = _mm_packs_epi32( r2, r3 ); \
1452 r1 = _mm_unpacklo_epi16( r0, r2 ); \
1453 r3 = _mm_unpackhi_epi16( r0, r2 ); \
1454 r0 = _mm_unpacklo_epi16( r1, r3 ); \
1455 r2 = _mm_unpackhi_epi16( r1, r3 ); \
1456 r0 = _mm_packus_epi16( r0, r2 ); \
1457 stbir__simdi_store( ptr, r0 ); \
1458
1459 #define stbir__simdi_32shr( out, reg, imm ) out = _mm_srli_epi32( reg, imm )
1460
1461 #if defined(_MSC_VER) && !defined(__clang__)
1462 // msvc inits with 8 bytes
1463 #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)
1464 #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 )
1465 #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 )
1466 #else
1467 // everything else inits with long long's
1468 #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)))
1469 #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)))
1470 #endif
1471
1472 #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x }
1473 #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { STBIR__CONST_4_32i(x) }
1474 #define STBIR__CONSTF(var) (var)
1475 #define STBIR__CONSTI(var) (var)
1476
1477 #if defined(STBIR_AVX) || defined(__SSE4_1__)
1478 #include <smmintrin.h>
1479 #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())))
1480 #else
1481 static STBIR__SIMDI_CONST(stbir__s32_32768, 32768);
1482 static STBIR__SIMDI_CONST(stbir__s16_32768, ((32768<<16)|32768));
1483
1484 #define stbir__simdf_pack_to_8words(out,reg0,reg1) \
1485 { \
1486 stbir__simdi tmp0,tmp1; \
1487 tmp0 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \
1488 tmp1 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \
1489 tmp0 = _mm_sub_epi32( tmp0, stbir__s32_32768 ); \
1490 tmp1 = _mm_sub_epi32( tmp1, stbir__s32_32768 ); \
1491 out = _mm_packs_epi32( tmp0, tmp1 ); \
1492 out = _mm_sub_epi16( out, stbir__s16_32768 ); \
1493 }
1494
1495 #endif
1496
1497 #define STBIR_SIMD
1498
1499 // if we detect AVX, set the simd8 defines
1500 #ifdef STBIR_AVX
1501 #include <immintrin.h>
1502 #define STBIR_SIMD8
1503 #define stbir__simdf8 __m256
1504 #define stbir__simdi8 __m256i
1505 #define stbir__simdf8_load( out, ptr ) (out) = _mm256_loadu_ps( (float const *)(ptr) )
1506 #define stbir__simdi8_load( out, ptr ) (out) = _mm256_loadu_si256( (__m256i const *)(ptr) )
1507 #define stbir__simdf8_mult( out, a, b ) (out) = _mm256_mul_ps( (a), (b) )
1508 #define stbir__simdf8_store( ptr, out ) _mm256_storeu_ps( (float*)(ptr), out )
1509 #define stbir__simdi8_store( ptr, reg ) _mm256_storeu_si256( (__m256i*)(ptr), reg )
1510 #define stbir__simdf8_frep8( fval ) _mm256_set1_ps( fval )
1511
1512 #define stbir__simdf8_min( out, reg0, reg1 ) (out) = _mm256_min_ps( reg0, reg1 )
1513 #define stbir__simdf8_max( out, reg0, reg1 ) (out) = _mm256_max_ps( reg0, reg1 )
1514
1515 #define stbir__simdf8_add4halves( out, bot4, top8 ) (out) = _mm_add_ps( bot4, _mm256_extractf128_ps( top8, 1 ) )
1516 #define stbir__simdf8_mult_mem( out, reg, ptr ) (out) = _mm256_mul_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) )
1517 #define stbir__simdf8_add_mem( out, reg, ptr ) (out) = _mm256_add_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) )
1518 #define stbir__simdf8_add( out, a, b ) (out) = _mm256_add_ps( a, b )
1519 #define stbir__simdf8_load1b( out, ptr ) (out) = _mm256_broadcast_ss( ptr )
1520 #define stbir__simdf_load1rep4( out, ptr ) (out) = _mm_broadcast_ss( ptr ) // avx load instruction
1521
1522 #define stbir__simdi8_convert_i32_to_float(out, ireg) (out) = _mm256_cvtepi32_ps( ireg )
1523 #define stbir__simdf8_convert_float_to_i32( i, f ) (i) = _mm256_cvttps_epi32(f)
1524
1525 #define stbir__simdf8_bot4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (0<<0)+(2<<4) )
1526 #define stbir__simdf8_top4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (1<<0)+(3<<4) )
1527
1528 #define stbir__simdf8_gettop4( reg ) _mm256_extractf128_ps(reg,1)
1529
1530 #ifdef STBIR_AVX2
1531
1532 #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \
1533 { \
1534 stbir__simdi8 a, zero =_mm256_setzero_si256();\
1535 a = _mm256_permute4x64_epi64( _mm256_unpacklo_epi8( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), zero ),(0<<0)+(2<<2)+(1<<4)+(3<<6)); \
1536 out0 = _mm256_unpacklo_epi16( a, zero ); \
1537 out1 = _mm256_unpackhi_epi16( a, zero ); \
1538 }
1539
1540 #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \
1541 { \
1542 stbir__simdi8 t; \
1543 stbir__simdf8 af,bf; \
1544 stbir__simdi8 a,b; \
1545 af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \
1546 bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \
1547 af = _mm256_max_ps( af, _mm256_setzero_ps() ); \
1548 bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \
1549 a = _mm256_cvttps_epi32( af ); \
1550 b = _mm256_cvttps_epi32( bf ); \
1551 t = _mm256_permute4x64_epi64( _mm256_packs_epi32( a, b ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \
1552 out = _mm256_castsi256_si128( _mm256_permute4x64_epi64( _mm256_packus_epi16( t, t ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ) ); \
1553 }
1554
1555 #define stbir__simdi8_expand_u16_to_u32(out,ireg) out = _mm256_unpacklo_epi16( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), _mm256_setzero_si256() );
1556
1557 #define stbir__simdf8_pack_to_16words(out,aa,bb) \
1558 { \
1559 stbir__simdf8 af,bf; \
1560 stbir__simdi8 a,b; \
1561 af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \
1562 bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \
1563 af = _mm256_max_ps( af, _mm256_setzero_ps() ); \
1564 bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \
1565 a = _mm256_cvttps_epi32( af ); \
1566 b = _mm256_cvttps_epi32( bf ); \
1567 (out) = _mm256_permute4x64_epi64( _mm256_packus_epi32(a, b), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \
1568 }
1569
1570 #else
1571
1572 #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \
1573 { \
1574 stbir__simdi a,zero = _mm_setzero_si128(); \
1575 a = _mm_unpacklo_epi8( ireg, zero ); \
1576 out0 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \
1577 a = _mm_unpackhi_epi8( ireg, zero ); \
1578 out1 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \
1579 }
1580
1581 #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \
1582 { \
1583 stbir__simdi t; \
1584 stbir__simdf8 af,bf; \
1585 stbir__simdi8 a,b; \
1586 af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \
1587 bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \
1588 af = _mm256_max_ps( af, _mm256_setzero_ps() ); \
1589 bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \
1590 a = _mm256_cvttps_epi32( af ); \
1591 b = _mm256_cvttps_epi32( bf ); \
1592 out = _mm_packs_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \
1593 out = _mm_packus_epi16( out, out ); \
1594 t = _mm_packs_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \
1595 t = _mm_packus_epi16( t, t ); \
1596 out = _mm_castps_si128( _mm_shuffle_ps( _mm_castsi128_ps(out), _mm_castsi128_ps(t), (0<<0)+(1<<2)+(0<<4)+(1<<6) ) ); \
1597 }
1598
1599 #define stbir__simdi8_expand_u16_to_u32(out,ireg) \
1600 { \
1601 stbir__simdi a,b,zero = _mm_setzero_si128(); \
1602 a = _mm_unpacklo_epi16( ireg, zero ); \
1603 b = _mm_unpackhi_epi16( ireg, zero ); \
1604 out = _mm256_insertf128_si256( _mm256_castsi128_si256( a ), b, 1 ); \
1605 }
1606
1607 #define stbir__simdf8_pack_to_16words(out,aa,bb) \
1608 { \
1609 stbir__simdi t0,t1; \
1610 stbir__simdf8 af,bf; \
1611 stbir__simdi8 a,b; \
1612 af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \
1613 bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \
1614 af = _mm256_max_ps( af, _mm256_setzero_ps() ); \
1615 bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \
1616 a = _mm256_cvttps_epi32( af ); \
1617 b = _mm256_cvttps_epi32( bf ); \
1618 t0 = _mm_packus_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \
1619 t1 = _mm_packus_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \
1620 out = _mm256_setr_m128i( t0, t1 ); \
1621 }
1622
1623 #endif
1624
1625 static __m256i stbir_00001111 = { STBIR__CONST_4d_32i( 0, 0, 0, 0 ), STBIR__CONST_4d_32i( 1, 1, 1, 1 ) };
1626 #define stbir__simdf8_0123to00001111( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00001111 )
1627
1628 static __m256i stbir_22223333 = { STBIR__CONST_4d_32i( 2, 2, 2, 2 ), STBIR__CONST_4d_32i( 3, 3, 3, 3 ) };
1629 #define stbir__simdf8_0123to22223333( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_22223333 )
1630
1631 #define stbir__simdf8_0123to2222( out, in ) (out) = stbir__simdf_swiz(_mm256_castps256_ps128(in), 2,2,2,2 )
1632
1633 #define stbir__simdf8_load4b( out, ptr ) (out) = _mm256_broadcast_ps( (__m128 const *)(ptr) )
1634
1635 static __m256i stbir_00112233 = { STBIR__CONST_4d_32i( 0, 0, 1, 1 ), STBIR__CONST_4d_32i( 2, 2, 3, 3 ) };
1636 #define stbir__simdf8_0123to00112233( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00112233 )
1637 #define stbir__simdf8_add4( out, a8, b ) (out) = _mm256_add_ps( a8, _mm256_castps128_ps256( b ) )
1638
1639 static __m256i stbir_load6 = { STBIR__CONST_4_32i( 0x80000000 ), STBIR__CONST_4d_32i( 0x80000000, 0x80000000, 0, 0 ) };
1640 #define stbir__simdf8_load6z( out, ptr ) (out) = _mm256_maskload_ps( ptr, stbir_load6 )
1641
1642 #define stbir__simdf8_0123to00000000( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(0<<4)+(0<<6) )
1643 #define stbir__simdf8_0123to11111111( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(1<<4)+(1<<6) )
1644 #define stbir__simdf8_0123to22222222( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(2<<2)+(2<<4)+(2<<6) )
1645 #define stbir__simdf8_0123to33333333( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(3<<2)+(3<<4)+(3<<6) )
1646 #define stbir__simdf8_0123to21032103( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(1<<2)+(0<<4)+(3<<6) )
1647 #define stbir__simdf8_0123to32103210( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(2<<2)+(1<<4)+(0<<6) )
1648 #define stbir__simdf8_0123to12301230( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(2<<2)+(3<<4)+(0<<6) )
1649 #define stbir__simdf8_0123to10321032( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(0<<2)+(3<<4)+(2<<6) )
1650 #define stbir__simdf8_0123to30123012( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(0<<2)+(1<<4)+(2<<6) )
1651
1652 #define stbir__simdf8_0123to11331133( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(3<<4)+(3<<6) )
1653 #define stbir__simdf8_0123to00220022( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(2<<4)+(2<<6) )
1654
1655 #define stbir__simdf8_aaa1( out, alp, ones ) (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) )
1656 #define stbir__simdf8_1aaa( out, alp, ones ) (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) )
1657 #define stbir__simdf8_a1a1( out, alp, ones) (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) )
1658 #define stbir__simdf8_1a1a( out, alp, ones) (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) )
1659
1660 #define stbir__simdf8_zero( reg ) (reg) = _mm256_setzero_ps()
1661
1662 #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd
1663 #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_fmadd_ps( mul1, mul2, add )
1664 #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_fmadd_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ), add )
1665 #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 )
1666 #else
1667 #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul1, mul2 ) )
1668 #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ) ) )
1669 #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() ) )
1670 #endif
1671 #define stbir__if_simdf8_cast_to_simdf4( val ) _mm256_castps256_ps128( val )
1672
1673 #endif
1674
1675 #ifdef STBIR_FLOORF
1676 #undef STBIR_FLOORF
1677 #endif
1678 #define STBIR_FLOORF stbir_simd_floorf
1679 static stbir__inline float stbir_simd_floorf(float x) // martins floorf
1680 {
1681 #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41)
1682 __m128 t = _mm_set_ss(x);
1683 return _mm_cvtss_f32( _mm_floor_ss(t, t) );
1684 #else
1685 __m128 f = _mm_set_ss(x);
1686 __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f));
1687 __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(f, t), _mm_set_ss(-1.0f)));
1688 return _mm_cvtss_f32(r);
1689 #endif
1690 }
1691
1692 #ifdef STBIR_CEILF
1693 #undef STBIR_CEILF
1694 #endif
1695 #define STBIR_CEILF stbir_simd_ceilf
1696 static stbir__inline float stbir_simd_ceilf(float x) // martins ceilf
1697 {
1698 #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41)
1699 __m128 t = _mm_set_ss(x);
1700 return _mm_cvtss_f32( _mm_ceil_ss(t, t) );
1701 #else
1702 __m128 f = _mm_set_ss(x);
1703 __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f));
1704 __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(t, f), _mm_set_ss(1.0f)));
1705 return _mm_cvtss_f32(r);
1706 #endif
1707 }
1708
1709#elif defined(STBIR_NEON)
1710
1711 #include <arm_neon.h>
1712
1713 #define stbir__simdf float32x4_t
1714 #define stbir__simdi uint32x4_t
1715
1716 #define stbir_simdi_castf( reg ) vreinterpretq_u32_f32(reg)
1717 #define stbir_simdf_casti( reg ) vreinterpretq_f32_u32(reg)
1718
1719 #define stbir__simdf_load( reg, ptr ) (reg) = vld1q_f32( (float const*)(ptr) )
1720 #define stbir__simdi_load( reg, ptr ) (reg) = vld1q_u32( (uint32_t const*)(ptr) )
1721 #define stbir__simdf_load1( out, ptr ) (out) = vld1q_dup_f32( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf)
1722 #define stbir__simdi_load1( out, ptr ) (out) = vld1q_dup_u32( (uint32_t const*)(ptr) )
1723 #define stbir__simdf_load1z( out, ptr ) (out) = vld1q_lane_f32( (float const*)(ptr), vdupq_n_f32(0), 0 ) // top values must be zero
1724 #define stbir__simdf_frep4( fvar ) vdupq_n_f32( fvar )
1725 #define stbir__simdf_load1frep4( out, fvar ) (out) = vdupq_n_f32( fvar )
1726 #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)
1727 #define stbir__simdf_load2z( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values must be zero
1728 #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = vcombine_f32( vget_low_f32(reg), vld1_f32( (float const*)(ptr) ) )
1729
1730 #define stbir__simdf_zeroP() vdupq_n_f32(0)
1731 #define stbir__simdf_zero( reg ) (reg) = vdupq_n_f32(0)
1732
1733 #define stbir__simdf_store( ptr, reg ) vst1q_f32( (float*)(ptr), reg )
1734 #define stbir__simdf_store1( ptr, reg ) vst1q_lane_f32( (float*)(ptr), reg, 0)
1735 #define stbir__simdf_store2( ptr, reg ) vst1_f32( (float*)(ptr), vget_low_f32(reg) )
1736 #define stbir__simdf_store2h( ptr, reg ) vst1_f32( (float*)(ptr), vget_high_f32(reg) )
1737
1738 #define stbir__simdi_store( ptr, reg ) vst1q_u32( (uint32_t*)(ptr), reg )
1739 #define stbir__simdi_store1( ptr, reg ) vst1q_lane_u32( (uint32_t*)(ptr), reg, 0 )
1740 #define stbir__simdi_store2( ptr, reg ) vst1_u32( (uint32_t*)(ptr), vget_low_u32(reg) )
1741
1742 #define stbir__prefetch( ptr )
1743
1744 #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \
1745 { \
1746 uint16x8_t l = vmovl_u8( vget_low_u8 ( vreinterpretq_u8_u32(ireg) ) ); \
1747 uint16x8_t h = vmovl_u8( vget_high_u8( vreinterpretq_u8_u32(ireg) ) ); \
1748 out0 = vmovl_u16( vget_low_u16 ( l ) ); \
1749 out1 = vmovl_u16( vget_high_u16( l ) ); \
1750 out2 = vmovl_u16( vget_low_u16 ( h ) ); \
1751 out3 = vmovl_u16( vget_high_u16( h ) ); \
1752 }
1753
1754 #define stbir__simdi_expand_u8_to_1u32(out,ireg) \
1755 { \
1756 uint16x8_t tmp = vmovl_u8( vget_low_u8( vreinterpretq_u8_u32(ireg) ) ); \
1757 out = vmovl_u16( vget_low_u16( tmp ) ); \
1758 }
1759
1760 #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \
1761 { \
1762 uint16x8_t tmp = vreinterpretq_u16_u32(ireg); \
1763 out0 = vmovl_u16( vget_low_u16 ( tmp ) ); \
1764 out1 = vmovl_u16( vget_high_u16( tmp ) ); \
1765 }
1766
1767 #define stbir__simdf_convert_float_to_i32( i, f ) (i) = vreinterpretq_u32_s32( vcvtq_s32_f32(f) )
1768 #define stbir__simdf_convert_float_to_int( f ) vgetq_lane_s32(vcvtq_s32_f32(f), 0)
1769 #define stbir__simdi_to_int( i ) (int)vgetq_lane_u32(i, 0)
1770 #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))
1771 #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))
1772 #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = vcvtq_f32_s32( vreinterpretq_s32_u32(ireg) )
1773 #define stbir__simdf_add( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 )
1774 #define stbir__simdf_mult( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 )
1775 #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_f32( (float const*)(ptr) ) )
1776 #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) )
1777 #define stbir__simdf_add_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_f32( (float const*)(ptr) ) )
1778 #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) )
1779
1780 #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd (and also x64 no madd to arm madd)
1781 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 )
1782 #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 )
1783 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_f32( (float const*)(ptr) ) )
1784 #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_dup_f32( (float const*)(ptr) ) )
1785 #else
1786 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) )
1787 #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) )
1788 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_f32( (float const*)(ptr) ) ) )
1789 #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_dup_f32( (float const*)(ptr) ) ) )
1790 #endif
1791
1792 #define stbir__simdf_add1( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 )
1793 #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 )
1794
1795 #define stbir__simdf_and( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) )
1796 #define stbir__simdf_or( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) )
1797
1798 #define stbir__simdf_min( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 )
1799 #define stbir__simdf_max( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 )
1800 #define stbir__simdf_min1( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 )
1801 #define stbir__simdf_max1( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 )
1802
1803 #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 3 )
1804 #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 2 )
1805
1806 #define stbir__simdf_a1a1( out, alp, ones ) (out) = vzipq_f32(vuzpq_f32(alp, alp).val[1], ones).val[0]
1807 #define stbir__simdf_1a1a( out, alp, ones ) (out) = vzipq_f32(ones, vuzpq_f32(alp, alp).val[0]).val[0]
1808
1809 #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ )
1810
1811 #define stbir__simdf_aaa1( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3, ones, 3)
1812 #define stbir__simdf_1aaa( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0, ones, 0)
1813
1814 #if defined( _MSC_VER ) && !defined(__clang__)
1815 #define stbir_make16(a,b,c,d) vcombine_u8( \
1816 vcreate_u8( (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \
1817 ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56)), \
1818 vcreate_u8( (4*c+0) | ((4*c+1)<<8) | ((4*c+2)<<16) | ((4*c+3)<<24) | \
1819 ((stbir_uint64)(4*d+0)<<32) | ((stbir_uint64)(4*d+1)<<40) | ((stbir_uint64)(4*d+2)<<48) | ((stbir_uint64)(4*d+3)<<56) ) )
1820
1821 static stbir__inline uint8x16x2_t stbir_make16x2(float32x4_t rega,float32x4_t regb)
1822 {
1823 uint8x16x2_t r = { vreinterpretq_u8_f32(rega), vreinterpretq_u8_f32(regb) };
1824 return r;
1825 }
1826 #else
1827 #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}
1828 #define stbir_make16x2(a,b) (uint8x16x2_t){{vreinterpretq_u8_f32(a),vreinterpretq_u8_f32(b)}}
1829 #endif
1830
1831 #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vqtbl1q_u8( vreinterpretq_u8_f32(reg), stbir_make16(one, two, three, four) ) )
1832 #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) ) )
1833
1834 #define stbir__simdi_16madd( out, reg0, reg1 ) \
1835 { \
1836 int16x8_t r0 = vreinterpretq_s16_u32(reg0); \
1837 int16x8_t r1 = vreinterpretq_s16_u32(reg1); \
1838 int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \
1839 int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \
1840 (out) = vreinterpretq_u32_s32( vpaddq_s32(tmp0, tmp1) ); \
1841 }
1842
1843 #else
1844
1845 #define stbir__simdf_aaa1( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3)
1846 #define stbir__simdf_1aaa( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0)
1847
1848 #if defined( _MSC_VER ) && !defined(__clang__)
1849 static stbir__inline uint8x8x2_t stbir_make8x2(float32x4_t reg)
1850 {
1851 uint8x8x2_t r = { { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } };
1852 return r;
1853 }
1854 #define stbir_make8(a,b) vcreate_u8( \
1855 (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \
1856 ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56) )
1857 #else
1858 #define stbir_make8x2(reg) (uint8x8x2_t){ { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } }
1859 #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}
1860 #endif
1861
1862 #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vcombine_u8( \
1863 vtbl2_u8( stbir_make8x2( reg ), stbir_make8( one, two ) ), \
1864 vtbl2_u8( stbir_make8x2( reg ), stbir_make8( three, four ) ) ) )
1865
1866 #define stbir__simdi_16madd( out, reg0, reg1 ) \
1867 { \
1868 int16x8_t r0 = vreinterpretq_s16_u32(reg0); \
1869 int16x8_t r1 = vreinterpretq_s16_u32(reg1); \
1870 int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \
1871 int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \
1872 int32x2_t out0 = vpadd_s32( vget_low_s32(tmp0), vget_high_s32(tmp0) ); \
1873 int32x2_t out1 = vpadd_s32( vget_low_s32(tmp1), vget_high_s32(tmp1) ); \
1874 (out) = vreinterpretq_u32_s32( vcombine_s32(out0, out1) ); \
1875 }
1876
1877 #endif
1878
1879 #define stbir__simdi_and( out, reg0, reg1 ) (out) = vandq_u32( reg0, reg1 )
1880 #define stbir__simdi_or( out, reg0, reg1 ) (out) = vorrq_u32( reg0, reg1 )
1881
1882 #define stbir__simdf_pack_to_8bytes(out,aa,bb) \
1883 { \
1884 float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \
1885 float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \
1886 int16x4_t ai = vqmovn_s32( vcvtq_s32_f32( af ) ); \
1887 int16x4_t bi = vqmovn_s32( vcvtq_s32_f32( bf ) ); \
1888 uint8x8_t out8 = vqmovun_s16( vcombine_s16(ai, bi) ); \
1889 out = vreinterpretq_u32_u8( vcombine_u8(out8, out8) ); \
1890 }
1891
1892 #define stbir__simdf_pack_to_8words(out,aa,bb) \
1893 { \
1894 float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \
1895 float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \
1896 int32x4_t ai = vcvtq_s32_f32( af ); \
1897 int32x4_t bi = vcvtq_s32_f32( bf ); \
1898 out = vreinterpretq_u32_u16( vcombine_u16(vqmovun_s32(ai), vqmovun_s32(bi)) ); \
1899 }
1900
1901 #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \
1902 { \
1903 int16x4x2_t tmp0 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r0)), vqmovn_s32(vreinterpretq_s32_u32(r2)) ); \
1904 int16x4x2_t tmp1 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r1)), vqmovn_s32(vreinterpretq_s32_u32(r3)) ); \
1905 uint8x8x2_t out = \
1906 { { \
1907 vqmovun_s16( vcombine_s16(tmp0.val[0], tmp0.val[1]) ), \
1908 vqmovun_s16( vcombine_s16(tmp1.val[0], tmp1.val[1]) ), \
1909 } }; \
1910 vst2_u8(ptr, out); \
1911 }
1912
1913 #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \
1914 { \
1915 float32x4x4_t tmp = vld4q_f32(ptr); \
1916 o0 = tmp.val[0]; \
1917 o1 = tmp.val[1]; \
1918 o2 = tmp.val[2]; \
1919 o3 = tmp.val[3]; \
1920 }
1921
1922 #define stbir__simdi_32shr( out, reg, imm ) out = vshrq_n_u32( reg, imm )
1923
1924 #if defined( _MSC_VER ) && !defined(__clang__)
1925 #define STBIR__SIMDF_CONST(var, x) __declspec(align(8)) float var[] = { x, x, x, x }
1926 #define STBIR__SIMDI_CONST(var, x) __declspec(align(8)) uint32_t var[] = { x, x, x, x }
1927 #define STBIR__CONSTF(var) (*(const float32x4_t*)var)
1928 #define STBIR__CONSTI(var) (*(const uint32x4_t*)var)
1929 #else
1930 #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x }
1931 #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x }
1932 #define STBIR__CONSTF(var) (var)
1933 #define STBIR__CONSTI(var) (var)
1934 #endif
1935
1936 #ifdef STBIR_FLOORF
1937 #undef STBIR_FLOORF
1938 #endif
1939 #define STBIR_FLOORF stbir_simd_floorf
1940 static stbir__inline float stbir_simd_floorf(float x)
1941 {
1942 #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ )
1943 return vget_lane_f32( vrndm_f32( vdup_n_f32(x) ), 0);
1944 #else
1945 float32x2_t f = vdup_n_f32(x);
1946 float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f));
1947 uint32x2_t a = vclt_f32(f, t);
1948 uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(-1.0f));
1949 float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b)));
1950 return vget_lane_f32(r, 0);
1951 #endif
1952 }
1953
1954 #ifdef STBIR_CEILF
1955 #undef STBIR_CEILF
1956 #endif
1957 #define STBIR_CEILF stbir_simd_ceilf
1958 static stbir__inline float stbir_simd_ceilf(float x)
1959 {
1960 #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ )
1961 return vget_lane_f32( vrndp_f32( vdup_n_f32(x) ), 0);
1962 #else
1963 float32x2_t f = vdup_n_f32(x);
1964 float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f));
1965 uint32x2_t a = vclt_f32(t, f);
1966 uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(1.0f));
1967 float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b)));
1968 return vget_lane_f32(r, 0);
1969 #endif
1970 }
1971
1972 #define STBIR_SIMD
1973
1974#elif defined(STBIR_WASM)
1975
1976 #include <wasm_simd128.h>
1977
1978 #define stbir__simdf v128_t
1979 #define stbir__simdi v128_t
1980
1981 #define stbir_simdi_castf( reg ) (reg)
1982 #define stbir_simdf_casti( reg ) (reg)
1983
1984 #define stbir__simdf_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) )
1985 #define stbir__simdi_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) )
1986 #define stbir__simdf_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf)
1987 #define stbir__simdi_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) )
1988 #define stbir__simdf_load1z( out, ptr ) (out) = wasm_v128_load32_zero( (void const*)(ptr) ) // top values must be zero
1989 #define stbir__simdf_frep4( fvar ) wasm_f32x4_splat( fvar )
1990 #define stbir__simdf_load1frep4( out, fvar ) (out) = wasm_f32x4_splat( fvar )
1991 #define stbir__simdf_load2( out, ptr ) (out) = wasm_v128_load64_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf)
1992 #define stbir__simdf_load2z( out, ptr ) (out) = wasm_v128_load64_zero( (void const*)(ptr) ) // top values must be zero
1993 #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = wasm_v128_load64_lane( (void const*)(ptr), reg, 1 )
1994
1995 #define stbir__simdf_zeroP() wasm_f32x4_const_splat(0)
1996 #define stbir__simdf_zero( reg ) (reg) = wasm_f32x4_const_splat(0)
1997
1998 #define stbir__simdf_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg )
1999 #define stbir__simdf_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 )
2000 #define stbir__simdf_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 )
2001 #define stbir__simdf_store2h( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 1 )
2002
2003 #define stbir__simdi_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg )
2004 #define stbir__simdi_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 )
2005 #define stbir__simdi_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 )
2006
2007 #define stbir__prefetch( ptr )
2008
2009 #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \
2010 { \
2011 v128_t l = wasm_u16x8_extend_low_u8x16 ( ireg ); \
2012 v128_t h = wasm_u16x8_extend_high_u8x16( ireg ); \
2013 out0 = wasm_u32x4_extend_low_u16x8 ( l ); \
2014 out1 = wasm_u32x4_extend_high_u16x8( l ); \
2015 out2 = wasm_u32x4_extend_low_u16x8 ( h ); \
2016 out3 = wasm_u32x4_extend_high_u16x8( h ); \
2017 }
2018
2019 #define stbir__simdi_expand_u8_to_1u32(out,ireg) \
2020 { \
2021 v128_t tmp = wasm_u16x8_extend_low_u8x16(ireg); \
2022 out = wasm_u32x4_extend_low_u16x8(tmp); \
2023 }
2024
2025 #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \
2026 { \
2027 out0 = wasm_u32x4_extend_low_u16x8 ( ireg ); \
2028 out1 = wasm_u32x4_extend_high_u16x8( ireg ); \
2029 }
2030
2031 #define stbir__simdf_convert_float_to_i32( i, f ) (i) = wasm_i32x4_trunc_sat_f32x4(f)
2032 #define stbir__simdf_convert_float_to_int( f ) wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(f), 0)
2033 #define stbir__simdi_to_int( i ) wasm_i32x4_extract_lane(i, 0)
2034 #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))
2035 #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))
2036 #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = wasm_f32x4_convert_i32x4(ireg)
2037 #define stbir__simdf_add( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 )
2038 #define stbir__simdf_mult( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 )
2039 #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load( (void const*)(ptr) ) )
2040 #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load32_splat( (void const*)(ptr) ) )
2041 #define stbir__simdf_add_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load( (void const*)(ptr) ) )
2042 #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load32_splat( (void const*)(ptr) ) )
2043
2044 #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) )
2045 #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) )
2046 #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load( (void const*)(ptr) ) ) )
2047 #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) ) ) )
2048
2049 #define stbir__simdf_add1( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 )
2050 #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 )
2051
2052 #define stbir__simdf_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 )
2053 #define stbir__simdf_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 )
2054
2055 #define stbir__simdf_min( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 )
2056 #define stbir__simdf_max( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 )
2057 #define stbir__simdf_min1( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 )
2058 #define stbir__simdf_max1( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 )
2059
2060 #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 3, 4, 5, -1 )
2061 #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 2, 3, 4, -1 )
2062
2063 #define stbir__simdf_aaa1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 3, 3, 3, 4)
2064 #define stbir__simdf_1aaa(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 0, 0)
2065 #define stbir__simdf_a1a1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 1, 4, 3, 4)
2066 #define stbir__simdf_1a1a(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 4, 2)
2067
2068 #define stbir__simdf_swiz( reg, one, two, three, four ) wasm_i32x4_shuffle(reg, reg, one, two, three, four)
2069
2070 #define stbir__simdi_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 )
2071 #define stbir__simdi_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 )
2072 #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = wasm_i32x4_dot_i16x8( reg0, reg1 )
2073
2074 #define stbir__simdf_pack_to_8bytes(out,aa,bb) \
2075 { \
2076 v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \
2077 v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \
2078 v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \
2079 v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \
2080 v128_t out16 = wasm_i16x8_narrow_i32x4( ai, bi ); \
2081 out = wasm_u8x16_narrow_i16x8( out16, out16 ); \
2082 }
2083
2084 #define stbir__simdf_pack_to_8words(out,aa,bb) \
2085 { \
2086 v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \
2087 v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \
2088 v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \
2089 v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \
2090 out = wasm_u16x8_narrow_i32x4( ai, bi ); \
2091 }
2092
2093 #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \
2094 { \
2095 v128_t tmp0 = wasm_i16x8_narrow_i32x4(r0, r1); \
2096 v128_t tmp1 = wasm_i16x8_narrow_i32x4(r2, r3); \
2097 v128_t tmp = wasm_u8x16_narrow_i16x8(tmp0, tmp1); \
2098 tmp = wasm_i8x16_shuffle(tmp, tmp, 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); \
2099 wasm_v128_store( (void*)(ptr), tmp); \
2100 }
2101
2102 #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \
2103 { \
2104 v128_t t0 = wasm_v128_load( ptr ); \
2105 v128_t t1 = wasm_v128_load( ptr+4 ); \
2106 v128_t t2 = wasm_v128_load( ptr+8 ); \
2107 v128_t t3 = wasm_v128_load( ptr+12 ); \
2108 v128_t s0 = wasm_i32x4_shuffle(t0, t1, 0, 4, 2, 6); \
2109 v128_t s1 = wasm_i32x4_shuffle(t0, t1, 1, 5, 3, 7); \
2110 v128_t s2 = wasm_i32x4_shuffle(t2, t3, 0, 4, 2, 6); \
2111 v128_t s3 = wasm_i32x4_shuffle(t2, t3, 1, 5, 3, 7); \
2112 o0 = wasm_i32x4_shuffle(s0, s2, 0, 1, 4, 5); \
2113 o1 = wasm_i32x4_shuffle(s1, s3, 0, 1, 4, 5); \
2114 o2 = wasm_i32x4_shuffle(s0, s2, 2, 3, 6, 7); \
2115 o3 = wasm_i32x4_shuffle(s1, s3, 2, 3, 6, 7); \
2116 }
2117
2118 #define stbir__simdi_32shr( out, reg, imm ) out = wasm_u32x4_shr( reg, imm )
2119
2120 typedef float stbir__f32x4 __attribute__((__vector_size__(16), __aligned__(16)));
2121 #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = (v128_t)(stbir__f32x4){ x, x, x, x }
2122 #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x }
2123 #define STBIR__CONSTF(var) (var)
2124 #define STBIR__CONSTI(var) (var)
2125
2126 #ifdef STBIR_FLOORF
2127 #undef STBIR_FLOORF
2128 #endif
2129 #define STBIR_FLOORF stbir_simd_floorf
2130 static stbir__inline float stbir_simd_floorf(float x)
2131 {
2132 return wasm_f32x4_extract_lane( wasm_f32x4_floor( wasm_f32x4_splat(x) ), 0);
2133 }
2134
2135 #ifdef STBIR_CEILF
2136 #undef STBIR_CEILF
2137 #endif
2138 #define STBIR_CEILF stbir_simd_ceilf
2139 static stbir__inline float stbir_simd_ceilf(float x)
2140 {
2141 return wasm_f32x4_extract_lane( wasm_f32x4_ceil( wasm_f32x4_splat(x) ), 0);
2142 }
2143
2144 #define STBIR_SIMD
2145
2146#endif // SSE2/NEON/WASM
2147
2148#endif // NO SIMD
2149
2150#ifdef STBIR_SIMD8
2151 #define stbir__simdfX stbir__simdf8
2152 #define stbir__simdiX stbir__simdi8
2153 #define stbir__simdfX_load stbir__simdf8_load
2154 #define stbir__simdiX_load stbir__simdi8_load
2155 #define stbir__simdfX_mult stbir__simdf8_mult
2156 #define stbir__simdfX_add_mem stbir__simdf8_add_mem
2157 #define stbir__simdfX_madd_mem stbir__simdf8_madd_mem
2158 #define stbir__simdfX_store stbir__simdf8_store
2159 #define stbir__simdiX_store stbir__simdi8_store
2160 #define stbir__simdf_frepX stbir__simdf8_frep8
2161 #define stbir__simdfX_madd stbir__simdf8_madd
2162 #define stbir__simdfX_min stbir__simdf8_min
2163 #define stbir__simdfX_max stbir__simdf8_max
2164 #define stbir__simdfX_aaa1 stbir__simdf8_aaa1
2165 #define stbir__simdfX_1aaa stbir__simdf8_1aaa
2166 #define stbir__simdfX_a1a1 stbir__simdf8_a1a1
2167 #define stbir__simdfX_1a1a stbir__simdf8_1a1a
2168 #define stbir__simdfX_convert_float_to_i32 stbir__simdf8_convert_float_to_i32
2169 #define stbir__simdfX_pack_to_words stbir__simdf8_pack_to_16words
2170 #define stbir__simdfX_zero stbir__simdf8_zero
2171 #define STBIR_onesX STBIR_ones8
2172 #define STBIR_max_uint8_as_floatX STBIR_max_uint8_as_float8
2173 #define STBIR_max_uint16_as_floatX STBIR_max_uint16_as_float8
2174 #define STBIR_simd_point5X STBIR_simd_point58
2175 #define stbir__simdfX_float_count 8
2176 #define stbir__simdfX_0123to1230 stbir__simdf8_0123to12301230
2177 #define stbir__simdfX_0123to2103 stbir__simdf8_0123to21032103
2178 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 };
2179 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 };
2180 static const stbir__simdf8 STBIR_ones8 = { 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 };
2181 static const stbir__simdf8 STBIR_simd_point58 = { 0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5 };
2182 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 };
2183 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 };
2184#else
2185 #define stbir__simdfX stbir__simdf
2186 #define stbir__simdiX stbir__simdi
2187 #define stbir__simdfX_load stbir__simdf_load
2188 #define stbir__simdiX_load stbir__simdi_load
2189 #define stbir__simdfX_mult stbir__simdf_mult
2190 #define stbir__simdfX_add_mem stbir__simdf_add_mem
2191 #define stbir__simdfX_madd_mem stbir__simdf_madd_mem
2192 #define stbir__simdfX_store stbir__simdf_store
2193 #define stbir__simdiX_store stbir__simdi_store
2194 #define stbir__simdf_frepX stbir__simdf_frep4
2195 #define stbir__simdfX_madd stbir__simdf_madd
2196 #define stbir__simdfX_min stbir__simdf_min
2197 #define stbir__simdfX_max stbir__simdf_max
2198 #define stbir__simdfX_aaa1 stbir__simdf_aaa1
2199 #define stbir__simdfX_1aaa stbir__simdf_1aaa
2200 #define stbir__simdfX_a1a1 stbir__simdf_a1a1
2201 #define stbir__simdfX_1a1a stbir__simdf_1a1a
2202 #define stbir__simdfX_convert_float_to_i32 stbir__simdf_convert_float_to_i32
2203 #define stbir__simdfX_pack_to_words stbir__simdf_pack_to_8words
2204 #define stbir__simdfX_zero stbir__simdf_zero
2205 #define STBIR_onesX STBIR__CONSTF(STBIR_ones)
2206 #define STBIR_simd_point5X STBIR__CONSTF(STBIR_simd_point5)
2207 #define STBIR_max_uint8_as_floatX STBIR__CONSTF(STBIR_max_uint8_as_float)
2208 #define STBIR_max_uint16_as_floatX STBIR__CONSTF(STBIR_max_uint16_as_float)
2209 #define stbir__simdfX_float_count 4
2210 #define stbir__if_simdf8_cast_to_simdf4( val ) ( val )
2211 #define stbir__simdfX_0123to1230 stbir__simdf_0123to1230
2212 #define stbir__simdfX_0123to2103 stbir__simdf_0123to2103
2213#endif
2214
2215
2216#if defined(STBIR_NEON) && !defined(_M_ARM) && !defined(__arm__)
2217
2218 #if defined( _MSC_VER ) && !defined(__clang__)
2219 typedef __int16 stbir__FP16;
2220 #else
2221 typedef float16_t stbir__FP16;
2222 #endif
2223
2224#else // no NEON, or 32-bit ARM for MSVC
2225
2226 typedef union stbir__FP16
2227 {
2228 unsigned short u;
2229 } stbir__FP16;
2230
2231#endif
2232
2233#if (!defined(STBIR_NEON) && !defined(STBIR_FP16C)) || (defined(STBIR_NEON) && defined(_M_ARM)) || (defined(STBIR_NEON) && defined(__arm__))
2234
2235 // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668
2236
2237 static stbir__inline float stbir__half_to_float( stbir__FP16 h )
2238 {
2239 static const stbir__FP32 magic = { (254 - 15) << 23 };
2240 static const stbir__FP32 was_infnan = { (127 + 16) << 23 };
2241 stbir__FP32 o;
2242
2243 o.u = (h.u & 0x7fff) << 13; // exponent/mantissa bits
2244 o.f *= magic.f; // exponent adjust
2245 if (o.f >= was_infnan.f) // make sure Inf/NaN survive
2246 o.u |= 255 << 23;
2247 o.u |= (h.u & 0x8000) << 16; // sign bit
2248 return o.f;
2249 }
2250
2251 static stbir__inline stbir__FP16 stbir__float_to_half(float val)
2252 {
2253 stbir__FP32 f32infty = { 255 << 23 };
2254 stbir__FP32 f16max = { (127 + 16) << 23 };
2255 stbir__FP32 denorm_magic = { ((127 - 15) + (23 - 10) + 1) << 23 };
2256 unsigned int sign_mask = 0x80000000u;
2257 stbir__FP16 o = { 0 };
2258 stbir__FP32 f;
2259 unsigned int sign;
2260
2261 f.f = val;
2262 sign = f.u & sign_mask;
2263 f.u ^= sign;
2264
2265 if (f.u >= f16max.u) // result is Inf or NaN (all exponent bits set)
2266 o.u = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf
2267 else // (De)normalized number or zero
2268 {
2269 if (f.u < (113 << 23)) // resulting FP16 is subnormal or zero
2270 {
2271 // use a magic value to align our 10 mantissa bits at the bottom of
2272 // the float. as long as FP addition is round-to-nearest-even this
2273 // just works.
2274 f.f += denorm_magic.f;
2275 // and one integer subtract of the bias later, we have our final float!
2276 o.u = (unsigned short) ( f.u - denorm_magic.u );
2277 }
2278 else
2279 {
2280 unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd
2281 // update exponent, rounding bias part 1
2282 f.u = f.u + ((15u - 127) << 23) + 0xfff;
2283 // rounding bias part 2
2284 f.u += mant_odd;
2285 // take the bits!
2286 o.u = (unsigned short) ( f.u >> 13 );
2287 }
2288 }
2289
2290 o.u |= sign >> 16;
2291 return o;
2292 }
2293
2294#endif
2295
2296
2297#if defined(STBIR_FP16C)
2298
2299 #include <immintrin.h>
2300
2301 static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input)
2302 {
2303 _mm256_storeu_ps( (float*)output, _mm256_cvtph_ps( _mm_loadu_si128( (__m128i const* )input ) ) );
2304 }
2305
2306 static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input)
2307 {
2308 _mm_storeu_si128( (__m128i*)output, _mm256_cvtps_ph( _mm256_loadu_ps( input ), 0 ) );
2309 }
2310
2311 static stbir__inline float stbir__half_to_float( stbir__FP16 h )
2312 {
2313 return _mm_cvtss_f32( _mm_cvtph_ps( _mm_cvtsi32_si128( (int)h.u ) ) );
2314 }
2315
2316 static stbir__inline stbir__FP16 stbir__float_to_half( float f )
2317 {
2318 stbir__FP16 h;
2319 h.u = (unsigned short) _mm_cvtsi128_si32( _mm_cvtps_ph( _mm_set_ss( f ), 0 ) );
2320 return h;
2321 }
2322
2323#elif defined(STBIR_SSE2)
2324
2325 // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668
2326 stbir__inline static void stbir__half_to_float_SIMD(float * output, void const * input)
2327 {
2328 static const STBIR__SIMDI_CONST(mask_nosign, 0x7fff);
2329 static const STBIR__SIMDI_CONST(smallest_normal, 0x0400);
2330 static const STBIR__SIMDI_CONST(infinity, 0x7c00);
2331 static const STBIR__SIMDI_CONST(expadjust_normal, (127 - 15) << 23);
2332 static const STBIR__SIMDI_CONST(magic_denorm, 113 << 23);
2333
2334 __m128i i = _mm_loadu_si128 ( (__m128i const*)(input) );
2335 __m128i h = _mm_unpacklo_epi16 ( i, _mm_setzero_si128() );
2336 __m128i mnosign = STBIR__CONSTI(mask_nosign);
2337 __m128i eadjust = STBIR__CONSTI(expadjust_normal);
2338 __m128i smallest = STBIR__CONSTI(smallest_normal);
2339 __m128i infty = STBIR__CONSTI(infinity);
2340 __m128i expmant = _mm_and_si128(mnosign, h);
2341 __m128i justsign = _mm_xor_si128(h, expmant);
2342 __m128i b_notinfnan = _mm_cmpgt_epi32(infty, expmant);
2343 __m128i b_isdenorm = _mm_cmpgt_epi32(smallest, expmant);
2344 __m128i shifted = _mm_slli_epi32(expmant, 13);
2345 __m128i adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust);
2346 __m128i adjusted = _mm_add_epi32(eadjust, shifted);
2347 __m128i den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm));
2348 __m128i adjusted2 = _mm_add_epi32(adjusted, adj_infnan);
2349 __m128 den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm);
2350 __m128 adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm));
2351 __m128 adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2));
2352 __m128 adjusted5 = _mm_or_ps(adjusted3, adjusted4);
2353 __m128i sign = _mm_slli_epi32(justsign, 16);
2354 __m128 final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign));
2355 stbir__simdf_store( output + 0, final );
2356
2357 h = _mm_unpackhi_epi16 ( i, _mm_setzero_si128() );
2358 expmant = _mm_and_si128(mnosign, h);
2359 justsign = _mm_xor_si128(h, expmant);
2360 b_notinfnan = _mm_cmpgt_epi32(infty, expmant);
2361 b_isdenorm = _mm_cmpgt_epi32(smallest, expmant);
2362 shifted = _mm_slli_epi32(expmant, 13);
2363 adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust);
2364 adjusted = _mm_add_epi32(eadjust, shifted);
2365 den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm));
2366 adjusted2 = _mm_add_epi32(adjusted, adj_infnan);
2367 den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm);
2368 adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm));
2369 adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2));
2370 adjusted5 = _mm_or_ps(adjusted3, adjusted4);
2371 sign = _mm_slli_epi32(justsign, 16);
2372 final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign));
2373 stbir__simdf_store( output + 4, final );
2374
2375 // ~38 SSE2 ops for 8 values
2376 }
2377
2378 // Fabian's round-to-nearest-even float to half
2379 // ~48 SSE2 ops for 8 output
2380 stbir__inline static void stbir__float_to_half_SIMD(void * output, float const * input)
2381 {
2382 static const STBIR__SIMDI_CONST(mask_sign, 0x80000000u);
2383 static const STBIR__SIMDI_CONST(c_f16max, (127 + 16) << 23); // all FP32 values >=this round to +inf
2384 static const STBIR__SIMDI_CONST(c_nanbit, 0x200);
2385 static const STBIR__SIMDI_CONST(c_infty_as_fp16, 0x7c00);
2386 static const STBIR__SIMDI_CONST(c_min_normal, (127 - 14) << 23); // smallest FP32 that yields a normalized FP16
2387 static const STBIR__SIMDI_CONST(c_subnorm_magic, ((127 - 15) + (23 - 10) + 1) << 23);
2388 static const STBIR__SIMDI_CONST(c_normal_bias, 0xfff - ((127 - 15) << 23)); // adjust exponent and add mantissa rounding
2389
2390 __m128 f = _mm_loadu_ps(input);
2391 __m128 msign = _mm_castsi128_ps(STBIR__CONSTI(mask_sign));
2392 __m128 justsign = _mm_and_ps(msign, f);
2393 __m128 absf = _mm_xor_ps(f, justsign);
2394 __m128i absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit)
2395 __m128i f16max = STBIR__CONSTI(c_f16max);
2396 __m128 b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN?
2397 __m128i b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special?
2398 __m128i nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), STBIR__CONSTI(c_nanbit));
2399 __m128i inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials
2400
2401 __m128i min_normal = STBIR__CONSTI(c_min_normal);
2402 __m128i b_issub = _mm_cmpgt_epi32(min_normal, absf_int);
2403
2404 // "result is subnormal" path
2405 __m128 subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa
2406 __m128i subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias
2407
2408 // "result is normal" path
2409 __m128i mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign
2410 __m128i mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0
2411
2412 __m128i round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias));
2413 __m128i round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE)
2414 __m128i normal = _mm_srli_epi32(round2, 13); // rounded result
2415
2416 // combine the two non-specials
2417 __m128i nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal));
2418
2419 // merge in specials as well
2420 __m128i joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan));
2421
2422 __m128i sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16);
2423 __m128i final2, final= _mm_or_si128(joined, sign_shift);
2424
2425 f = _mm_loadu_ps(input+4);
2426 justsign = _mm_and_ps(msign, f);
2427 absf = _mm_xor_ps(f, justsign);
2428 absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit)
2429 b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN?
2430 b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special?
2431 nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), c_nanbit);
2432 inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials
2433
2434 b_issub = _mm_cmpgt_epi32(min_normal, absf_int);
2435
2436 // "result is subnormal" path
2437 subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa
2438 subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias
2439
2440 // "result is normal" path
2441 mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign
2442 mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0
2443
2444 round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias));
2445 round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE)
2446 normal = _mm_srli_epi32(round2, 13); // rounded result
2447
2448 // combine the two non-specials
2449 nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal));
2450
2451 // merge in specials as well
2452 joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan));
2453
2454 sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16);
2455 final2 = _mm_or_si128(joined, sign_shift);
2456 final = _mm_packs_epi32(final, final2);
2457 stbir__simdi_store( output,final );
2458 }
2459
2460#elif defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) // 64-bit ARM on MSVC (not clang)
2461
2462 static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input)
2463 {
2464 float16x4_t in0 = vld1_f16(input + 0);
2465 float16x4_t in1 = vld1_f16(input + 4);
2466 vst1q_f32(output + 0, vcvt_f32_f16(in0));
2467 vst1q_f32(output + 4, vcvt_f32_f16(in1));
2468 }
2469
2470 static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input)
2471 {
2472 float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0));
2473 float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4));
2474 vst1_f16(output+0, out0);
2475 vst1_f16(output+4, out1);
2476 }
2477
2478 static stbir__inline float stbir__half_to_float( stbir__FP16 h )
2479 {
2480 return vgetq_lane_f32(vcvt_f32_f16(vld1_dup_f16(&h)), 0);
2481 }
2482
2483 static stbir__inline stbir__FP16 stbir__float_to_half( float f )
2484 {
2485 return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0).n16_u16[0];
2486 }
2487
2488#elif defined(STBIR_NEON) && ( defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) ) // 64-bit ARM
2489
2490 static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input)
2491 {
2492 float16x8_t in = vld1q_f16(input);
2493 vst1q_f32(output + 0, vcvt_f32_f16(vget_low_f16(in)));
2494 vst1q_f32(output + 4, vcvt_f32_f16(vget_high_f16(in)));
2495 }
2496
2497 static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input)
2498 {
2499 float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0));
2500 float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4));
2501 vst1q_f16(output, vcombine_f16(out0, out1));
2502 }
2503
2504 static stbir__inline float stbir__half_to_float( stbir__FP16 h )
2505 {
2506 return vgetq_lane_f32(vcvt_f32_f16(vdup_n_f16(h)), 0);
2507 }
2508
2509 static stbir__inline stbir__FP16 stbir__float_to_half( float f )
2510 {
2511 return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0);
2512 }
2513
2514#elif defined(STBIR_WASM) || (defined(STBIR_NEON) && (defined(_MSC_VER) || defined(_M_ARM) || defined(__arm__))) // WASM or 32-bit ARM on MSVC/clang
2515
2516 static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input)
2517 {
2518 for (int i=0; i<8; i++)
2519 {
2520 output[i] = stbir__half_to_float(input[i]);
2521 }
2522 }
2523 static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input)
2524 {
2525 for (int i=0; i<8; i++)
2526 {
2527 output[i] = stbir__float_to_half(input[i]);
2528 }
2529 }
2530
2531#endif
2532
2533
2534#ifdef STBIR_SIMD
2535
2536#define stbir__simdf_0123to3333( out, reg ) (out) = stbir__simdf_swiz( reg, 3,3,3,3 )
2537#define stbir__simdf_0123to2222( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,2,2 )
2538#define stbir__simdf_0123to1111( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,1,1 )
2539#define stbir__simdf_0123to0000( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,0 )
2540#define stbir__simdf_0123to0003( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,3 )
2541#define stbir__simdf_0123to0001( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,1 )
2542#define stbir__simdf_0123to1122( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,2,2 )
2543#define stbir__simdf_0123to2333( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,3,3 )
2544#define stbir__simdf_0123to0023( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,3 )
2545#define stbir__simdf_0123to1230( out, reg ) (out) = stbir__simdf_swiz( reg, 1,2,3,0 )
2546#define stbir__simdf_0123to2103( out, reg ) (out) = stbir__simdf_swiz( reg, 2,1,0,3 )
2547#define stbir__simdf_0123to3210( out, reg ) (out) = stbir__simdf_swiz( reg, 3,2,1,0 )
2548#define stbir__simdf_0123to2301( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,0,1 )
2549#define stbir__simdf_0123to3012( out, reg ) (out) = stbir__simdf_swiz( reg, 3,0,1,2 )
2550#define stbir__simdf_0123to0011( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,1,1 )
2551#define stbir__simdf_0123to1100( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,0,0 )
2552#define stbir__simdf_0123to2233( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,3,3 )
2553#define stbir__simdf_0123to1133( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,3,3 )
2554#define stbir__simdf_0123to0022( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,2 )
2555#define stbir__simdf_0123to1032( out, reg ) (out) = stbir__simdf_swiz( reg, 1,0,3,2 )
2556
2557typedef union stbir__simdi_u32
2558{
2559 stbir_uint32 m128i_u32[4];
2560 int m128i_i32[4];
2561 stbir__simdi m128i_i128;
2562} stbir__simdi_u32;
2563
2564static const int STBIR_mask[9] = { 0,0,0,-1,-1,-1,0,0,0 };
2565
2566static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float, stbir__max_uint8_as_float);
2567static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float, stbir__max_uint16_as_float);
2568static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float_inverted, stbir__max_uint8_as_float_inverted);
2569static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float_inverted, stbir__max_uint16_as_float_inverted);
2570
2571static const STBIR__SIMDF_CONST(STBIR_simd_point5, 0.5f);
2572static const STBIR__SIMDF_CONST(STBIR_ones, 1.0f);
2573static const STBIR__SIMDI_CONST(STBIR_almost_zero, (127 - 13) << 23);
2574static const STBIR__SIMDI_CONST(STBIR_almost_one, 0x3f7fffff);
2575static const STBIR__SIMDI_CONST(STBIR_mantissa_mask, 0xff);
2576static const STBIR__SIMDI_CONST(STBIR_topscale, 0x02000000);
2577
2578// Basically, in simd mode, we unroll the proper amount, and we don't want
2579// the non-simd remnant loops to be unroll because they only run a few times
2580// Adding this switch saves about 5K on clang which is Captain Unroll the 3rd.
2581#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star )
2582#define STBIR_SIMD_NO_UNROLL(ptr) STBIR_NO_UNROLL(ptr)
2583#define STBIR_SIMD_NO_UNROLL_LOOP_START STBIR_NO_UNROLL_LOOP_START
2584#define STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR STBIR_NO_UNROLL_LOOP_START_INF_FOR
2585
2586#ifdef STBIR_MEMCPY
2587#undef STBIR_MEMCPY
2588#endif
2589#define STBIR_MEMCPY stbir_simd_memcpy
2590
2591// override normal use of memcpy with much simpler copy (faster and smaller with our sized copies)
2592static void stbir_simd_memcpy( void * dest, void const * src, size_t bytes )
2593{
2594 char STBIR_SIMD_STREAMOUT_PTR (*) d = (char*) dest;
2595 char STBIR_SIMD_STREAMOUT_PTR( * ) d_end = ((char*) dest) + bytes;
2596 ptrdiff_t ofs_to_src = (char*)src - (char*)dest;
2597
2598 // check overlaps
2599 STBIR_ASSERT( ( ( d >= ( (char*)src) + bytes ) ) || ( ( d + bytes ) <= (char*)src ) );
2600
2601 if ( bytes < (16*stbir__simdfX_float_count) )
2602 {
2603 if ( bytes < 16 )
2604 {
2605 if ( bytes )
2606 {
2607 STBIR_SIMD_NO_UNROLL_LOOP_START
2608 do
2609 {
2610 STBIR_SIMD_NO_UNROLL(d);
2611 d[ 0 ] = d[ ofs_to_src ];
2612 ++d;
2613 } while ( d < d_end );
2614 }
2615 }
2616 else
2617 {
2618 stbir__simdf x;
2619 // do one unaligned to get us aligned for the stream out below
2620 stbir__simdf_load( x, ( d + ofs_to_src ) );
2621 stbir__simdf_store( d, x );
2622 d = (char*)( ( ( (size_t)d ) + 16 ) & ~15 );
2623
2624 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
2625 for(;;)
2626 {
2627 STBIR_SIMD_NO_UNROLL(d);
2628
2629 if ( d > ( d_end - 16 ) )
2630 {
2631 if ( d == d_end )
2632 return;
2633 d = d_end - 16;
2634 }
2635
2636 stbir__simdf_load( x, ( d + ofs_to_src ) );
2637 stbir__simdf_store( d, x );
2638 d += 16;
2639 }
2640 }
2641 }
2642 else
2643 {
2644 stbir__simdfX x0,x1,x2,x3;
2645
2646 // do one unaligned to get us aligned for the stream out below
2647 stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count );
2648 stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count );
2649 stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count );
2650 stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count );
2651 stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 );
2652 stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 );
2653 stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 );
2654 stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 );
2655 d = (char*)( ( ( (size_t)d ) + (16*stbir__simdfX_float_count) ) & ~((16*stbir__simdfX_float_count)-1) );
2656
2657 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
2658 for(;;)
2659 {
2660 STBIR_SIMD_NO_UNROLL(d);
2661
2662 if ( d > ( d_end - (16*stbir__simdfX_float_count) ) )
2663 {
2664 if ( d == d_end )
2665 return;
2666 d = d_end - (16*stbir__simdfX_float_count);
2667 }
2668
2669 stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count );
2670 stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count );
2671 stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count );
2672 stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count );
2673 stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 );
2674 stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 );
2675 stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 );
2676 stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 );
2677 d += (16*stbir__simdfX_float_count);
2678 }
2679 }
2680}
2681
2682// memcpy that is specically intentionally overlapping (src is smaller then dest, so can be
2683// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to
2684// the diff between dest and src)
2685static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes )
2686{
2687 char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src;
2688 char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes;
2689 ptrdiff_t ofs_to_dest = (char*)dest - (char*)src;
2690
2691 if ( ofs_to_dest >= 16 ) // is the overlap more than 16 away?
2692 {
2693 char STBIR_SIMD_STREAMOUT_PTR( * ) s_end16 = ((char*) src) + (bytes&~15);
2694 STBIR_SIMD_NO_UNROLL_LOOP_START
2695 do
2696 {
2697 stbir__simdf x;
2698 STBIR_SIMD_NO_UNROLL(sd);
2699 stbir__simdf_load( x, sd );
2700 stbir__simdf_store( ( sd + ofs_to_dest ), x );
2701 sd += 16;
2702 } while ( sd < s_end16 );
2703
2704 if ( sd == s_end )
2705 return;
2706 }
2707
2708 do
2709 {
2710 STBIR_SIMD_NO_UNROLL(sd);
2711 *(int*)( sd + ofs_to_dest ) = *(int*) sd;
2712 sd += 4;
2713 } while ( sd < s_end );
2714}
2715
2716#else // no SSE2
2717
2718// when in scalar mode, we let unrolling happen, so this macro just does the __restrict
2719#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star )
2720#define STBIR_SIMD_NO_UNROLL(ptr)
2721#define STBIR_SIMD_NO_UNROLL_LOOP_START
2722#define STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
2723
2724#endif // SSE2
2725
2726
2727#ifdef STBIR_PROFILE
2728
2729#ifndef STBIR_PROFILE_FUNC
2730
2731#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_ )
2732
2733#ifdef _MSC_VER
2734
2735 STBIRDEF stbir_uint64 __rdtsc();
2736 #define STBIR_PROFILE_FUNC() __rdtsc()
2737
2738#else // non msvc
2739
2740 static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC()
2741 {
2742 stbir_uint32 lo, hi;
2743 asm volatile ("rdtsc" : "=a" (lo), "=d" (hi) );
2744 return ( ( (stbir_uint64) hi ) << 32 ) | ( (stbir_uint64) lo );
2745 }
2746
2747#endif // msvc
2748
2749#elif defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || defined(__ARM_NEON__)
2750
2751#if defined( _MSC_VER ) && !defined(__clang__)
2752
2753 #define STBIR_PROFILE_FUNC() _ReadStatusReg(ARM64_CNTVCT)
2754
2755#else
2756
2757 static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC()
2758 {
2759 stbir_uint64 tsc;
2760 asm volatile("mrs %0, cntvct_el0" : "=r" (tsc));
2761 return tsc;
2762 }
2763
2764#endif
2765
2766#else // x64, arm
2767
2768#error Unknown platform for profiling.
2769
2770#endif // x64, arm
2771
2772#endif // STBIR_PROFILE_FUNC
2773
2774#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO ,stbir__per_split_info * split_info
2775#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO ,split_info
2776
2777#define STBIR_ONLY_PROFILE_BUILD_GET_INFO ,stbir__info * profile_info
2778#define STBIR_ONLY_PROFILE_BUILD_SET_INFO ,profile_info
2779
2780// super light-weight micro profiler
2781#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;
2782#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; }
2783#define STBIR_PROFILE_FIRST_START_ll( info, wh ) { int i; info->current_zone_excluded_ptr = &info->profile.named.total; for(i=0;i<STBIR__ARRAY_SIZE(info->profile.array);i++) info->profile.array[i]=0; } STBIR_PROFILE_START_ll( info, wh );
2784#define STBIR_PROFILE_CLEAR_EXTRAS_ll( info, num ) { int extra; for(extra=1;extra<(num);extra++) { int i; for(i=0;i<STBIR__ARRAY_SIZE((info)->profile.array);i++) (info)[extra].profile.array[i]=0; } }
2785
2786// for thread data
2787#define STBIR_PROFILE_START( wh ) STBIR_PROFILE_START_ll( split_info, wh )
2788#define STBIR_PROFILE_END( wh ) STBIR_PROFILE_END_ll( split_info, wh )
2789#define STBIR_PROFILE_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( split_info, wh )
2790#define STBIR_PROFILE_CLEAR_EXTRAS() STBIR_PROFILE_CLEAR_EXTRAS_ll( split_info, split_count )
2791
2792// for build data
2793#define STBIR_PROFILE_BUILD_START( wh ) STBIR_PROFILE_START_ll( profile_info, wh )
2794#define STBIR_PROFILE_BUILD_END( wh ) STBIR_PROFILE_END_ll( profile_info, wh )
2795#define STBIR_PROFILE_BUILD_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( profile_info, wh )
2796#define STBIR_PROFILE_BUILD_CLEAR( info ) { int i; for(i=0;i<STBIR__ARRAY_SIZE(info->profile.array);i++) info->profile.array[i]=0; }
2797
2798#else // no profile
2799
2800#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO
2801#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO
2802
2803#define STBIR_ONLY_PROFILE_BUILD_GET_INFO
2804#define STBIR_ONLY_PROFILE_BUILD_SET_INFO
2805
2806#define STBIR_PROFILE_START( wh )
2807#define STBIR_PROFILE_END( wh )
2808#define STBIR_PROFILE_FIRST_START( wh )
2809#define STBIR_PROFILE_CLEAR_EXTRAS( )
2810
2811#define STBIR_PROFILE_BUILD_START( wh )
2812#define STBIR_PROFILE_BUILD_END( wh )
2813#define STBIR_PROFILE_BUILD_FIRST_START( wh )
2814#define STBIR_PROFILE_BUILD_CLEAR( info )
2815
2816#endif // stbir_profile
2817
2818#ifndef STBIR_CEILF
2819#include <math.h>
2820#if _MSC_VER <= 1200 // support VC6 for Sean
2821#define STBIR_CEILF(x) ((float)ceil((float)(x)))
2822#define STBIR_FLOORF(x) ((float)floor((float)(x)))
2823#else
2824#define STBIR_CEILF(x) ceilf(x)
2825#define STBIR_FLOORF(x) floorf(x)
2826#endif
2827#endif
2828
2829#ifndef STBIR_MEMCPY
2830// For memcpy
2831#include <string.h>
2832#define STBIR_MEMCPY( dest, src, len ) memcpy( dest, src, len )
2833#endif
2834
2835#ifndef STBIR_SIMD
2836
2837// memcpy that is specifically intentionally overlapping (src is smaller then dest, so can be
2838// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to
2839// the diff between dest and src)
2840static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes )
2841{
2842 char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src;
2843 char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes;
2844 ptrdiff_t ofs_to_dest = (char*)dest - (char*)src;
2845
2846 if ( ofs_to_dest >= 8 ) // is the overlap more than 8 away
2847 {
2848 char STBIR_SIMD_STREAMOUT_PTR( * ) s_end8 = ((char*) src) + (bytes&~7);
2849
2850 if ( ( ( ((ptrdiff_t)dest)|((ptrdiff_t)src) ) & 7 ) == 0 ) // is it 8byte aligned?
2851 {
2852 STBIR_NO_UNROLL_LOOP_START
2853 do
2854 {
2855 STBIR_NO_UNROLL(sd);
2856 *(stbir_uint64*)( sd + ofs_to_dest ) = *(stbir_uint64*) sd;
2857 sd += 8;
2858 } while ( sd < s_end8 );
2859 }
2860 else
2861 {
2862 STBIR_NO_UNROLL_LOOP_START
2863 do
2864 {
2865 int a,b;
2866 STBIR_NO_UNROLL(sd);
2867 a = ((int*)sd)[0];
2868 b = ((int*)sd)[1];
2869 ((int*)( sd + ofs_to_dest ))[0] = a;
2870 ((int*)( sd + ofs_to_dest ))[1] = b;
2871 sd += 8;
2872 } while ( sd < s_end8 );
2873 }
2874
2875 if ( sd == s_end )
2876 return;
2877 }
2878
2879 STBIR_NO_UNROLL_LOOP_START
2880 do
2881 {
2882 STBIR_NO_UNROLL(sd);
2883 *(int*)( sd + ofs_to_dest ) = *(int*) sd;
2884 sd += 4;
2885 } while ( sd < s_end );
2886}
2887
2888#endif
2889
2890static float stbir__filter_trapezoid(float x, float scale, void * user_data)
2891{
2892 float halfscale = scale / 2;
2893 float t = 0.5f + halfscale;
2894 STBIR_ASSERT(scale <= 1);
2895 STBIR__UNUSED(user_data);
2896
2897 if ( x < 0.0f ) x = -x;
2898
2899 if (x >= t)
2900 return 0.0f;
2901 else
2902 {
2903 float r = 0.5f - halfscale;
2904 if (x <= r)
2905 return 1.0f;
2906 else
2907 return (t - x) / scale;
2908 }
2909}
2910
2911static float stbir__support_trapezoid(float scale, void * user_data)
2912{
2913 STBIR__UNUSED(user_data);
2914 return 0.5f + scale / 2.0f;
2915}
2916
2917static float stbir__filter_triangle(float x, float s, void * user_data)
2918{
2919 STBIR__UNUSED(s);
2920 STBIR__UNUSED(user_data);
2921
2922 if ( x < 0.0f ) x = -x;
2923
2924 if (x <= 1.0f)
2925 return 1.0f - x;
2926 else
2927 return 0.0f;
2928}
2929
2930static float stbir__filter_point(float x, float s, void * user_data)
2931{
2932 STBIR__UNUSED(x);
2933 STBIR__UNUSED(s);
2934 STBIR__UNUSED(user_data);
2935
2936 return 1.0f;
2937}
2938
2939static float stbir__filter_cubic(float x, float s, void * user_data)
2940{
2941 STBIR__UNUSED(s);
2942 STBIR__UNUSED(user_data);
2943
2944 if ( x < 0.0f ) x = -x;
2945
2946 if (x < 1.0f)
2947 return (4.0f + x*x*(3.0f*x - 6.0f))/6.0f;
2948 else if (x < 2.0f)
2949 return (8.0f + x*(-12.0f + x*(6.0f - x)))/6.0f;
2950
2951 return (0.0f);
2952}
2953
2954static float stbir__filter_catmullrom(float x, float s, void * user_data)
2955{
2956 STBIR__UNUSED(s);
2957 STBIR__UNUSED(user_data);
2958
2959 if ( x < 0.0f ) x = -x;
2960
2961 if (x < 1.0f)
2962 return 1.0f - x*x*(2.5f - 1.5f*x);
2963 else if (x < 2.0f)
2964 return 2.0f - x*(4.0f + x*(0.5f*x - 2.5f));
2965
2966 return (0.0f);
2967}
2968
2969static float stbir__filter_mitchell(float x, float s, void * user_data)
2970{
2971 STBIR__UNUSED(s);
2972 STBIR__UNUSED(user_data);
2973
2974 if ( x < 0.0f ) x = -x;
2975
2976 if (x < 1.0f)
2977 return (16.0f + x*x*(21.0f * x - 36.0f))/18.0f;
2978 else if (x < 2.0f)
2979 return (32.0f + x*(-60.0f + x*(36.0f - 7.0f*x)))/18.0f;
2980
2981 return (0.0f);
2982}
2983
2984static float stbir__support_zeropoint5(float s, void * user_data)
2985{
2986 STBIR__UNUSED(s);
2987 STBIR__UNUSED(user_data);
2988 return 0.5f;
2989}
2990
2991static float stbir__support_one(float s, void * user_data)
2992{
2993 STBIR__UNUSED(s);
2994 STBIR__UNUSED(user_data);
2995 return 1;
2996}
2997
2998static float stbir__support_two(float s, void * user_data)
2999{
3000 STBIR__UNUSED(s);
3001 STBIR__UNUSED(user_data);
3002 return 2;
3003}
3004
3005// This is the maximum number of input samples that can affect an output sample
3006// with the given filter from the output pixel's perspective
3007static int stbir__get_filter_pixel_width(stbir__support_callback * support, float scale, void * user_data)
3008{
3009 STBIR_ASSERT(support != 0);
3010
3011 if ( scale >= ( 1.0f-stbir__small_float ) ) // upscale
3012 return (int)STBIR_CEILF(support(1.0f/scale,user_data) * 2.0f);
3013 else
3014 return (int)STBIR_CEILF(support(scale,user_data) * 2.0f / scale);
3015}
3016
3017// this is how many coefficents per run of the filter (which is different
3018// from the filter_pixel_width depending on if we are scattering or gathering)
3019static int stbir__get_coefficient_width(stbir__sampler * samp, int is_gather, void * user_data)
3020{
3021 float scale = samp->scale_info.scale;
3022 stbir__support_callback * support = samp->filter_support;
3023
3024 switch( is_gather )
3025 {
3026 case 1:
3027 return (int)STBIR_CEILF(support(1.0f / scale, user_data) * 2.0f);
3028 case 2:
3029 return (int)STBIR_CEILF(support(scale, user_data) * 2.0f / scale);
3030 case 0:
3031 return (int)STBIR_CEILF(support(scale, user_data) * 2.0f);
3032 default:
3033 STBIR_ASSERT( (is_gather >= 0 ) && (is_gather <= 2 ) );
3034 return 0;
3035 }
3036}
3037
3038static int stbir__get_contributors(stbir__sampler * samp, int is_gather)
3039{
3040 if (is_gather)
3041 return samp->scale_info.output_sub_size;
3042 else
3043 return (samp->scale_info.input_full_size + samp->filter_pixel_margin * 2);
3044}
3045
3046static int stbir__edge_zero_full( int n, int max )
3047{
3048 STBIR__UNUSED(n);
3049 STBIR__UNUSED(max);
3050 return 0; // NOTREACHED
3051}
3052
3053static int stbir__edge_clamp_full( int n, int max )
3054{
3055 if (n < 0)
3056 return 0;
3057
3058 if (n >= max)
3059 return max - 1;
3060
3061 return n; // NOTREACHED
3062}
3063
3064static int stbir__edge_reflect_full( int n, int max )
3065{
3066 if (n < 0)
3067 {
3068 if (n > -max)
3069 return -n;
3070 else
3071 return max - 1;
3072 }
3073
3074 if (n >= max)
3075 {
3076 int max2 = max * 2;
3077 if (n >= max2)
3078 return 0;
3079 else
3080 return max2 - n - 1;
3081 }
3082
3083 return n; // NOTREACHED
3084}
3085
3086static int stbir__edge_wrap_full( int n, int max )
3087{
3088 if (n >= 0)
3089 return (n % max);
3090 else
3091 {
3092 int m = (-n) % max;
3093
3094 if (m != 0)
3095 m = max - m;
3096
3097 return (m);
3098 }
3099}
3100
3101typedef int stbir__edge_wrap_func( int n, int max );
3102static stbir__edge_wrap_func * stbir__edge_wrap_slow[] =
3103{
3104 stbir__edge_clamp_full, // STBIR_EDGE_CLAMP
3105 stbir__edge_reflect_full, // STBIR_EDGE_REFLECT
3106 stbir__edge_wrap_full, // STBIR_EDGE_WRAP
3107 stbir__edge_zero_full, // STBIR_EDGE_ZERO
3108};
3109
3110stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max)
3111{
3112 // avoid per-pixel switch
3113 if (n >= 0 && n < max)
3114 return n;
3115 return stbir__edge_wrap_slow[edge]( n, max );
3116}
3117
3118#define STBIR__MERGE_RUNS_PIXEL_THRESHOLD 16
3119
3120// get information on the extents of a sampler
3121static void stbir__get_extents( stbir__sampler * samp, stbir__extents * scanline_extents )
3122{
3123 int j, stop;
3124 int left_margin, right_margin;
3125 int min_n = 0x7fffffff, max_n = -0x7fffffff;
3126 int min_left = 0x7fffffff, max_left = -0x7fffffff;
3127 int min_right = 0x7fffffff, max_right = -0x7fffffff;
3128 stbir_edge edge = samp->edge;
3129 stbir__contributors* contributors = samp->contributors;
3130 int output_sub_size = samp->scale_info.output_sub_size;
3131 int input_full_size = samp->scale_info.input_full_size;
3132 int filter_pixel_margin = samp->filter_pixel_margin;
3133
3134 STBIR_ASSERT( samp->is_gather );
3135
3136 stop = output_sub_size;
3137 for (j = 0; j < stop; j++ )
3138 {
3139 STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 );
3140 if ( contributors[j].n0 < min_n )
3141 {
3142 min_n = contributors[j].n0;
3143 stop = j + filter_pixel_margin; // if we find a new min, only scan another filter width
3144 if ( stop > output_sub_size ) stop = output_sub_size;
3145 }
3146 }
3147
3148 stop = 0;
3149 for (j = output_sub_size - 1; j >= stop; j-- )
3150 {
3151 STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 );
3152 if ( contributors[j].n1 > max_n )
3153 {
3154 max_n = contributors[j].n1;
3155 stop = j - filter_pixel_margin; // if we find a new max, only scan another filter width
3156 if (stop<0) stop = 0;
3157 }
3158 }
3159
3160 STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n );
3161 STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n );
3162
3163 // now calculate how much into the margins we really read
3164 left_margin = 0;
3165 if ( min_n < 0 )
3166 {
3167 left_margin = -min_n;
3168 min_n = 0;
3169 }
3170
3171 right_margin = 0;
3172 if ( max_n >= input_full_size )
3173 {
3174 right_margin = max_n - input_full_size + 1;
3175 max_n = input_full_size - 1;
3176 }
3177
3178 // index 1 is margin pixel extents (how many pixels we hang over the edge)
3179 scanline_extents->edge_sizes[0] = left_margin;
3180 scanline_extents->edge_sizes[1] = right_margin;
3181
3182 // index 2 is pixels read from the input
3183 scanline_extents->spans[0].n0 = min_n;
3184 scanline_extents->spans[0].n1 = max_n;
3185 scanline_extents->spans[0].pixel_offset_for_input = min_n;
3186
3187 // default to no other input range
3188 scanline_extents->spans[1].n0 = 0;
3189 scanline_extents->spans[1].n1 = -1;
3190 scanline_extents->spans[1].pixel_offset_for_input = 0;
3191
3192 // don't have to do edge calc for zero clamp
3193 if ( edge == STBIR_EDGE_ZERO )
3194 return;
3195
3196 // convert margin pixels to the pixels within the input (min and max)
3197 for( j = -left_margin ; j < 0 ; j++ )
3198 {
3199 int p = stbir__edge_wrap( edge, j, input_full_size );
3200 if ( p < min_left )
3201 min_left = p;
3202 if ( p > max_left )
3203 max_left = p;
3204 }
3205
3206 for( j = input_full_size ; j < (input_full_size + right_margin) ; j++ )
3207 {
3208 int p = stbir__edge_wrap( edge, j, input_full_size );
3209 if ( p < min_right )
3210 min_right = p;
3211 if ( p > max_right )
3212 max_right = p;
3213 }
3214
3215 // merge the left margin pixel region if it connects within 4 pixels of main pixel region
3216 if ( min_left != 0x7fffffff )
3217 {
3218 if ( ( ( min_left <= min_n ) && ( ( max_left + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) ||
3219 ( ( min_n <= min_left ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_left ) ) )
3220 {
3221 scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_left );
3222 scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_left );
3223 scanline_extents->spans[0].pixel_offset_for_input = min_n;
3224 left_margin = 0;
3225 }
3226 }
3227
3228 // merge the right margin pixel region if it connects within 4 pixels of main pixel region
3229 if ( min_right != 0x7fffffff )
3230 {
3231 if ( ( ( min_right <= min_n ) && ( ( max_right + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) ||
3232 ( ( min_n <= min_right ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_right ) ) )
3233 {
3234 scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_right );
3235 scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_right );
3236 scanline_extents->spans[0].pixel_offset_for_input = min_n;
3237 right_margin = 0;
3238 }
3239 }
3240
3241 STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n );
3242 STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n );
3243
3244 // you get two ranges when you have the WRAP edge mode and you are doing just the a piece of the resize
3245 // so you need to get a second run of pixels from the opposite side of the scanline (which you
3246 // wouldn't need except for WRAP)
3247
3248
3249 // if we can't merge the min_left range, add it as a second range
3250 if ( ( left_margin ) && ( min_left != 0x7fffffff ) )
3251 {
3252 stbir__span * newspan = scanline_extents->spans + 1;
3253 STBIR_ASSERT( right_margin == 0 );
3254 if ( min_left < scanline_extents->spans[0].n0 )
3255 {
3256 scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0;
3257 scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0;
3258 scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1;
3259 --newspan;
3260 }
3261 newspan->pixel_offset_for_input = min_left;
3262 newspan->n0 = -left_margin;
3263 newspan->n1 = ( max_left - min_left ) - left_margin;
3264 scanline_extents->edge_sizes[0] = 0; // don't need to copy the left margin, since we are directly decoding into the margin
3265 }
3266 // if we can't merge the min_right range, add it as a second range
3267 else
3268 if ( ( right_margin ) && ( min_right != 0x7fffffff ) )
3269 {
3270 stbir__span * newspan = scanline_extents->spans + 1;
3271 if ( min_right < scanline_extents->spans[0].n0 )
3272 {
3273 scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0;
3274 scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0;
3275 scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1;
3276 --newspan;
3277 }
3278 newspan->pixel_offset_for_input = min_right;
3279 newspan->n0 = scanline_extents->spans[1].n1 + 1;
3280 newspan->n1 = scanline_extents->spans[1].n1 + 1 + ( max_right - min_right );
3281 scanline_extents->edge_sizes[1] = 0; // don't need to copy the right margin, since we are directly decoding into the margin
3282 }
3283
3284 // sort the spans into write output order
3285 if ( ( scanline_extents->spans[1].n1 > scanline_extents->spans[1].n0 ) && ( scanline_extents->spans[0].n0 > scanline_extents->spans[1].n0 ) )
3286 {
3287 stbir__span tspan = scanline_extents->spans[0];
3288 scanline_extents->spans[0] = scanline_extents->spans[1];
3289 scanline_extents->spans[1] = tspan;
3290 }
3291}
3292
3293static void stbir__calculate_in_pixel_range( int * first_pixel, int * last_pixel, float out_pixel_center, float out_filter_radius, float inv_scale, float out_shift, int input_size, stbir_edge edge )
3294{
3295 int first, last;
3296 float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius;
3297 float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius;
3298
3299 float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) * inv_scale;
3300 float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) * inv_scale;
3301
3302 first = (int)(STBIR_FLOORF(in_pixel_influence_lowerbound + 0.5f));
3303 last = (int)(STBIR_FLOORF(in_pixel_influence_upperbound - 0.5f));
3304 if ( last < first ) last = first; // point sample mode can span a value *right* at 0.5, and cause these to cross
3305
3306 if ( edge == STBIR_EDGE_WRAP )
3307 {
3308 if ( first < -input_size )
3309 first = -input_size;
3310 if ( last >= (input_size*2))
3311 last = (input_size*2) - 1;
3312 }
3313
3314 *first_pixel = first;
3315 *last_pixel = last;
3316}
3317
3318static void stbir__calculate_coefficients_for_gather_upsample( float out_filter_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float* coefficient_group, int coefficient_width, stbir_edge edge, void * user_data )
3319{
3320 int n, end;
3321 float inv_scale = scale_info->inv_scale;
3322 float out_shift = scale_info->pixel_shift;
3323 int input_size = scale_info->input_full_size;
3324 int numerator = scale_info->scale_numerator;
3325 int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) );
3326
3327 // Looping through out pixels
3328 end = num_contributors; if ( polyphase ) end = numerator;
3329 for (n = 0; n < end; n++)
3330 {
3331 int i;
3332 int last_non_zero;
3333 float out_pixel_center = (float)n + 0.5f;
3334 float in_center_of_out = (out_pixel_center + out_shift) * inv_scale;
3335
3336 int in_first_pixel, in_last_pixel;
3337
3338 stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, out_pixel_center, out_filter_radius, inv_scale, out_shift, input_size, edge );
3339
3340 // make sure we never generate a range larger than our precalculated coeff width
3341 // this only happens in point sample mode, but it's a good safe thing to do anyway
3342 if ( ( in_last_pixel - in_first_pixel + 1 ) > coefficient_width )
3343 in_last_pixel = in_first_pixel + coefficient_width - 1;
3344
3345 last_non_zero = -1;
3346 for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
3347 {
3348 float in_pixel_center = (float)(i + in_first_pixel) + 0.5f;
3349 float coeff = kernel(in_center_of_out - in_pixel_center, inv_scale, user_data);
3350
3351 // kill denormals
3352 if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) )
3353 {
3354 if ( i == 0 ) // if we're at the front, just eat zero contributors
3355 {
3356 STBIR_ASSERT ( ( in_last_pixel - in_first_pixel ) != 0 ); // there should be at least one contrib
3357 ++in_first_pixel;
3358 i--;
3359 continue;
3360 }
3361 coeff = 0; // make sure is fully zero (should keep denormals away)
3362 }
3363 else
3364 last_non_zero = i;
3365
3366 coefficient_group[i] = coeff;
3367 }
3368
3369 in_last_pixel = last_non_zero+in_first_pixel; // kills trailing zeros
3370 contributors->n0 = in_first_pixel;
3371 contributors->n1 = in_last_pixel;
3372
3373 STBIR_ASSERT(contributors->n1 >= contributors->n0);
3374
3375 ++contributors;
3376 coefficient_group += coefficient_width;
3377 }
3378}
3379
3380static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs, int new_pixel, float new_coeff, int max_width )
3381{
3382 if ( contribs->n1 < contribs->n0 ) // this first clause should never happen, but handle in case
3383 {
3384 contribs->n0 = contribs->n1 = new_pixel;
3385 coeffs[0] = new_coeff;
3386 }
3387 else if ( new_pixel <= contribs->n1 ) // before the end
3388 {
3389 if ( new_pixel < contribs->n0 ) // before the front?
3390 {
3391 if ( ( contribs->n1 - new_pixel + 1 ) <= max_width )
3392 {
3393 int j, o = contribs->n0 - new_pixel;
3394 for ( j = contribs->n1 - contribs->n0 ; j >= 0 ; j-- )
3395 coeffs[ j + o ] = coeffs[ j ];
3396 for ( j = 1 ; j < o ; j++ )
3397 coeffs[ j ] = 0;
3398 coeffs[ 0 ] = new_coeff;
3399 contribs->n0 = new_pixel;
3400 }
3401 }
3402 else
3403 {
3404 // add new weight to existing coeff if already there
3405 coeffs[ new_pixel - contribs->n0 ] += new_coeff;
3406 }
3407 }
3408 else
3409 {
3410 if ( ( new_pixel - contribs->n0 + 1 ) <= max_width )
3411 {
3412 int j, e = new_pixel - contribs->n0;
3413 for( j = ( contribs->n1 - contribs->n0 ) + 1 ; j < e ; j++ ) // clear in-betweens coeffs if there are any
3414 coeffs[j] = 0;
3415
3416 coeffs[ e ] = new_coeff;
3417 contribs->n1 = new_pixel;
3418 }
3419 }
3420}
3421
3422static void stbir__calculate_out_pixel_range( int * first_pixel, int * last_pixel, float in_pixel_center, float in_pixels_radius, float scale, float out_shift, int out_size )
3423{
3424 float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius;
3425 float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius;
3426 float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale - out_shift;
3427 float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale - out_shift;
3428 int out_first_pixel = (int)(STBIR_FLOORF(out_pixel_influence_lowerbound + 0.5f));
3429 int out_last_pixel = (int)(STBIR_FLOORF(out_pixel_influence_upperbound - 0.5f));
3430
3431 if ( out_first_pixel < 0 )
3432 out_first_pixel = 0;
3433 if ( out_last_pixel >= out_size )
3434 out_last_pixel = out_size - 1;
3435 *first_pixel = out_first_pixel;
3436 *last_pixel = out_last_pixel;
3437}
3438
3439static void stbir__calculate_coefficients_for_gather_downsample( int start, int end, float in_pixels_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int coefficient_width, int num_contributors, stbir__contributors * contributors, float * coefficient_group, void * user_data )
3440{
3441 int in_pixel;
3442 int i;
3443 int first_out_inited = -1;
3444 float scale = scale_info->scale;
3445 float out_shift = scale_info->pixel_shift;
3446 int out_size = scale_info->output_sub_size;
3447 int numerator = scale_info->scale_numerator;
3448 int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < out_size ) );
3449
3450 STBIR__UNUSED(num_contributors);
3451
3452 // Loop through the input pixels
3453 for (in_pixel = start; in_pixel < end; in_pixel++)
3454 {
3455 float in_pixel_center = (float)in_pixel + 0.5f;
3456 float out_center_of_in = in_pixel_center * scale - out_shift;
3457 int out_first_pixel, out_last_pixel;
3458
3459 stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, in_pixel_center, in_pixels_radius, scale, out_shift, out_size );
3460
3461 if ( out_first_pixel > out_last_pixel )
3462 continue;
3463
3464 // clamp or exit if we are using polyphase filtering, and the limit is up
3465 if ( polyphase )
3466 {
3467 // when polyphase, you only have to do coeffs up to the numerator count
3468 if ( out_first_pixel == numerator )
3469 break;
3470
3471 // don't do any extra work, clamp last pixel at numerator too
3472 if ( out_last_pixel >= numerator )
3473 out_last_pixel = numerator - 1;
3474 }
3475
3476 for (i = 0; i <= out_last_pixel - out_first_pixel; i++)
3477 {
3478 float out_pixel_center = (float)(i + out_first_pixel) + 0.5f;
3479 float x = out_pixel_center - out_center_of_in;
3480 float coeff = kernel(x, scale, user_data) * scale;
3481
3482 // kill the coeff if it's too small (avoid denormals)
3483 if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) )
3484 coeff = 0.0f;
3485
3486 {
3487 int out = i + out_first_pixel;
3488 float * coeffs = coefficient_group + out * coefficient_width;
3489 stbir__contributors * contribs = contributors + out;
3490
3491 // is this the first time this output pixel has been seen? Init it.
3492 if ( out > first_out_inited )
3493 {
3494 STBIR_ASSERT( out == ( first_out_inited + 1 ) ); // ensure we have only advanced one at time
3495 first_out_inited = out;
3496 contribs->n0 = in_pixel;
3497 contribs->n1 = in_pixel;
3498 coeffs[0] = coeff;
3499 }
3500 else
3501 {
3502 // insert on end (always in order)
3503 if ( coeffs[0] == 0.0f ) // if the first coefficent is zero, then zap it for this coeffs
3504 {
3505 STBIR_ASSERT( ( in_pixel - contribs->n0 ) == 1 ); // ensure that when we zap, we're at the 2nd pos
3506 contribs->n0 = in_pixel;
3507 }
3508 contribs->n1 = in_pixel;
3509 STBIR_ASSERT( ( in_pixel - contribs->n0 ) < coefficient_width );
3510 coeffs[in_pixel - contribs->n0] = coeff;
3511 }
3512 }
3513 }
3514 }
3515}
3516
3517#ifdef STBIR_RENORMALIZE_IN_FLOAT
3518#define STBIR_RENORM_TYPE float
3519#else
3520#define STBIR_RENORM_TYPE double
3521#endif
3522
3523static 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 * coefficient_group, int coefficient_width )
3524{
3525 int input_size = scale_info->input_full_size;
3526 int input_last_n1 = input_size - 1;
3527 int n, end;
3528 int lowest = 0x7fffffff;
3529 int highest = -0x7fffffff;
3530 int widest = -1;
3531 int numerator = scale_info->scale_numerator;
3532 int denominator = scale_info->scale_denominator;
3533 int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) );
3534 float * coeffs;
3535 stbir__contributors * contribs;
3536
3537 // weight all the coeffs for each sample
3538 coeffs = coefficient_group;
3539 contribs = contributors;
3540 end = num_contributors; if ( polyphase ) end = numerator;
3541 for (n = 0; n < end; n++)
3542 {
3543 int i;
3544 STBIR_RENORM_TYPE filter_scale, total_filter = 0;
3545 int e;
3546
3547 // add all contribs
3548 e = contribs->n1 - contribs->n0;
3549 for( i = 0 ; i <= e ; i++ )
3550 {
3551 total_filter += (STBIR_RENORM_TYPE) coeffs[i];
3552 STBIR_ASSERT( ( coeffs[i] >= -2.0f ) && ( coeffs[i] <= 2.0f ) ); // check for wonky weights
3553 }
3554
3555 // rescale
3556 if ( ( total_filter < stbir__small_float ) && ( total_filter > -stbir__small_float ) )
3557 {
3558 // all coeffs are extremely small, just zero it
3559 contribs->n1 = contribs->n0;
3560 coeffs[0] = 0.0f;
3561 }
3562 else
3563 {
3564 // if the total isn't 1.0, rescale everything
3565 if ( ( total_filter < (1.0f-stbir__small_float) ) || ( total_filter > (1.0f+stbir__small_float) ) )
3566 {
3567 filter_scale = ((STBIR_RENORM_TYPE)1.0) / total_filter;
3568
3569 // scale them all
3570 for (i = 0; i <= e; i++)
3571 coeffs[i] = (float) ( coeffs[i] * filter_scale );
3572 }
3573 }
3574 ++contribs;
3575 coeffs += coefficient_width;
3576 }
3577
3578 // if we have a rational for the scale, we can exploit the polyphaseness to not calculate
3579 // most of the coefficients, so we copy them here
3580 if ( polyphase )
3581 {
3582 stbir__contributors * prev_contribs = contributors;
3583 stbir__contributors * cur_contribs = contributors + numerator;
3584
3585 for( n = numerator ; n < num_contributors ; n++ )
3586 {
3587 cur_contribs->n0 = prev_contribs->n0 + denominator;
3588 cur_contribs->n1 = prev_contribs->n1 + denominator;
3589 ++cur_contribs;
3590 ++prev_contribs;
3591 }
3592 stbir_overlapping_memcpy( coefficient_group + numerator * coefficient_width, coefficient_group, ( num_contributors - numerator ) * coefficient_width * sizeof( coeffs[ 0 ] ) );
3593 }
3594
3595 coeffs = coefficient_group;
3596 contribs = contributors;
3597
3598 for (n = 0; n < num_contributors; n++)
3599 {
3600 int i;
3601
3602 // in zero edge mode, just remove out of bounds contribs completely (since their weights are accounted for now)
3603 if ( edge == STBIR_EDGE_ZERO )
3604 {
3605 // shrink the right side if necessary
3606 if ( contribs->n1 > input_last_n1 )
3607 contribs->n1 = input_last_n1;
3608
3609 // shrink the left side
3610 if ( contribs->n0 < 0 )
3611 {
3612 int j, left, skips = 0;
3613
3614 skips = -contribs->n0;
3615 contribs->n0 = 0;
3616
3617 // now move down the weights
3618 left = contribs->n1 - contribs->n0 + 1;
3619 if ( left > 0 )
3620 {
3621 for( j = 0 ; j < left ; j++ )
3622 coeffs[ j ] = coeffs[ j + skips ];
3623 }
3624 }
3625 }
3626 else if ( ( edge == STBIR_EDGE_CLAMP ) || ( edge == STBIR_EDGE_REFLECT ) )
3627 {
3628 // for clamp and reflect, calculate the true inbounds position (based on edge type) and just add that to the existing weight
3629
3630 // right hand side first
3631 if ( contribs->n1 > input_last_n1 )
3632 {
3633 int start = contribs->n0;
3634 int endi = contribs->n1;
3635 contribs->n1 = input_last_n1;
3636 for( i = input_size; i <= endi; i++ )
3637 stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), coeffs[i-start], coefficient_width );
3638 }
3639
3640 // now check left hand edge
3641 if ( contribs->n0 < 0 )
3642 {
3643 int save_n0;
3644 float save_n0_coeff;
3645 float * c = coeffs - ( contribs->n0 + 1 );
3646
3647 // reinsert the coeffs with it reflected or clamped (insert accumulates, if the coeffs exist)
3648 for( i = -1 ; i > contribs->n0 ; i-- )
3649 stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), *c--, coefficient_width );
3650 save_n0 = contribs->n0;
3651 save_n0_coeff = c[0]; // save it, since we didn't do the final one (i==n0), because there might be too many coeffs to hold (before we resize)!
3652
3653 // now slide all the coeffs down (since we have accumulated them in the positive contribs) and reset the first contrib
3654 contribs->n0 = 0;
3655 for(i = 0 ; i <= contribs->n1 ; i++ )
3656 coeffs[i] = coeffs[i-save_n0];
3657
3658 // now that we have shrunk down the contribs, we insert the first one safely
3659 stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( save_n0, input_size ), save_n0_coeff, coefficient_width );
3660 }
3661 }
3662
3663 if ( contribs->n0 <= contribs->n1 )
3664 {
3665 int diff = contribs->n1 - contribs->n0 + 1;
3666 while ( diff && ( coeffs[ diff-1 ] == 0.0f ) )
3667 --diff;
3668
3669 contribs->n1 = contribs->n0 + diff - 1;
3670
3671 if ( contribs->n0 <= contribs->n1 )
3672 {
3673 if ( contribs->n0 < lowest )
3674 lowest = contribs->n0;
3675 if ( contribs->n1 > highest )
3676 highest = contribs->n1;
3677 if ( diff > widest )
3678 widest = diff;
3679 }
3680
3681 // re-zero out unused coefficients (if any)
3682 for( i = diff ; i < coefficient_width ; i++ )
3683 coeffs[i] = 0.0f;
3684 }
3685
3686 ++contribs;
3687 coeffs += coefficient_width;
3688 }
3689 filter_info->lowest = lowest;
3690 filter_info->highest = highest;
3691 filter_info->widest = widest;
3692}
3693
3694#undef STBIR_RENORM_TYPE
3695
3696static int stbir__pack_coefficients( int num_contributors, stbir__contributors* contributors, float * coefficents, int coefficient_width, int widest, int row0, int row1 )
3697{
3698 #define STBIR_MOVE_1( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint32*)(dest))[0] = ((stbir_uint32*)(src))[0]; }
3699 #define STBIR_MOVE_2( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; }
3700 #ifdef STBIR_SIMD
3701 #define STBIR_MOVE_4( dest, src ) { stbir__simdf t; STBIR_NO_UNROLL(dest); stbir__simdf_load( t, src ); stbir__simdf_store( dest, t ); }
3702 #else
3703 #define STBIR_MOVE_4( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; ((stbir_uint64*)(dest))[1] = ((stbir_uint64*)(src))[1]; }
3704 #endif
3705
3706 int row_end = row1 + 1;
3707 STBIR__UNUSED( row0 ); // only used in an assert
3708
3709 if ( coefficient_width != widest )
3710 {
3711 float * pc = coefficents;
3712 float * coeffs = coefficents;
3713 float * pc_end = coefficents + num_contributors * widest;
3714 switch( widest )
3715 {
3716 case 1:
3717 STBIR_NO_UNROLL_LOOP_START
3718 do {
3719 STBIR_MOVE_1( pc, coeffs );
3720 ++pc;
3721 coeffs += coefficient_width;
3722 } while ( pc < pc_end );
3723 break;
3724 case 2:
3725 STBIR_NO_UNROLL_LOOP_START
3726 do {
3727 STBIR_MOVE_2( pc, coeffs );
3728 pc += 2;
3729 coeffs += coefficient_width;
3730 } while ( pc < pc_end );
3731 break;
3732 case 3:
3733 STBIR_NO_UNROLL_LOOP_START
3734 do {
3735 STBIR_MOVE_2( pc, coeffs );
3736 STBIR_MOVE_1( pc+2, coeffs+2 );
3737 pc += 3;
3738 coeffs += coefficient_width;
3739 } while ( pc < pc_end );
3740 break;
3741 case 4:
3742 STBIR_NO_UNROLL_LOOP_START
3743 do {
3744 STBIR_MOVE_4( pc, coeffs );
3745 pc += 4;
3746 coeffs += coefficient_width;
3747 } while ( pc < pc_end );
3748 break;
3749 case 5:
3750 STBIR_NO_UNROLL_LOOP_START
3751 do {
3752 STBIR_MOVE_4( pc, coeffs );
3753 STBIR_MOVE_1( pc+4, coeffs+4 );
3754 pc += 5;
3755 coeffs += coefficient_width;
3756 } while ( pc < pc_end );
3757 break;
3758 case 6:
3759 STBIR_NO_UNROLL_LOOP_START
3760 do {
3761 STBIR_MOVE_4( pc, coeffs );
3762 STBIR_MOVE_2( pc+4, coeffs+4 );
3763 pc += 6;
3764 coeffs += coefficient_width;
3765 } while ( pc < pc_end );
3766 break;
3767 case 7:
3768 STBIR_NO_UNROLL_LOOP_START
3769 do {
3770 STBIR_MOVE_4( pc, coeffs );
3771 STBIR_MOVE_2( pc+4, coeffs+4 );
3772 STBIR_MOVE_1( pc+6, coeffs+6 );
3773 pc += 7;
3774 coeffs += coefficient_width;
3775 } while ( pc < pc_end );
3776 break;
3777 case 8:
3778 STBIR_NO_UNROLL_LOOP_START
3779 do {
3780 STBIR_MOVE_4( pc, coeffs );
3781 STBIR_MOVE_4( pc+4, coeffs+4 );
3782 pc += 8;
3783 coeffs += coefficient_width;
3784 } while ( pc < pc_end );
3785 break;
3786 case 9:
3787 STBIR_NO_UNROLL_LOOP_START
3788 do {
3789 STBIR_MOVE_4( pc, coeffs );
3790 STBIR_MOVE_4( pc+4, coeffs+4 );
3791 STBIR_MOVE_1( pc+8, coeffs+8 );
3792 pc += 9;
3793 coeffs += coefficient_width;
3794 } while ( pc < pc_end );
3795 break;
3796 case 10:
3797 STBIR_NO_UNROLL_LOOP_START
3798 do {
3799 STBIR_MOVE_4( pc, coeffs );
3800 STBIR_MOVE_4( pc+4, coeffs+4 );
3801 STBIR_MOVE_2( pc+8, coeffs+8 );
3802 pc += 10;
3803 coeffs += coefficient_width;
3804 } while ( pc < pc_end );
3805 break;
3806 case 11:
3807 STBIR_NO_UNROLL_LOOP_START
3808 do {
3809 STBIR_MOVE_4( pc, coeffs );
3810 STBIR_MOVE_4( pc+4, coeffs+4 );
3811 STBIR_MOVE_2( pc+8, coeffs+8 );
3812 STBIR_MOVE_1( pc+10, coeffs+10 );
3813 pc += 11;
3814 coeffs += coefficient_width;
3815 } while ( pc < pc_end );
3816 break;
3817 case 12:
3818 STBIR_NO_UNROLL_LOOP_START
3819 do {
3820 STBIR_MOVE_4( pc, coeffs );
3821 STBIR_MOVE_4( pc+4, coeffs+4 );
3822 STBIR_MOVE_4( pc+8, coeffs+8 );
3823 pc += 12;
3824 coeffs += coefficient_width;
3825 } while ( pc < pc_end );
3826 break;
3827 default:
3828 STBIR_NO_UNROLL_LOOP_START
3829 do {
3830 float * copy_end = pc + widest - 4;
3831 float * c = coeffs;
3832 do {
3833 STBIR_NO_UNROLL( pc );
3834 STBIR_MOVE_4( pc, c );
3835 pc += 4;
3836 c += 4;
3837 } while ( pc <= copy_end );
3838 copy_end += 4;
3839 STBIR_NO_UNROLL_LOOP_START
3840 while ( pc < copy_end )
3841 {
3842 STBIR_MOVE_1( pc, c );
3843 ++pc; ++c;
3844 }
3845 coeffs += coefficient_width;
3846 } while ( pc < pc_end );
3847 break;
3848 }
3849 }
3850
3851 // some horizontal routines read one float off the end (which is then masked off), so put in a sentinel so we don't read an snan or denormal
3852 coefficents[ widest * num_contributors ] = 8888.0f;
3853
3854 // the minimum we might read for unrolled filters widths is 12. So, we need to
3855 // make sure we never read outside the decode buffer, by possibly moving
3856 // the sample area back into the scanline, and putting zeros weights first.
3857 // we start on the right edge and check until we're well past the possible
3858 // clip area (2*widest).
3859 {
3860 stbir__contributors * contribs = contributors + num_contributors - 1;
3861 float * coeffs = coefficents + widest * ( num_contributors - 1 );
3862
3863 // go until no chance of clipping (this is usually less than 8 lops)
3864 while ( ( contribs >= contributors ) && ( ( contribs->n0 + widest*2 ) >= row_end ) )
3865 {
3866 // might we clip??
3867 if ( ( contribs->n0 + widest ) > row_end )
3868 {
3869 int stop_range = widest;
3870
3871 // if range is larger than 12, it will be handled by generic loops that can terminate on the exact length
3872 // of this contrib n1, instead of a fixed widest amount - so calculate this
3873 if ( widest > 12 )
3874 {
3875 int mod;
3876
3877 // how far will be read in the n_coeff loop (which depends on the widest count mod4);
3878 mod = widest & 3;
3879 stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod;
3880
3881 // the n_coeff loops do a minimum amount of coeffs, so factor that in!
3882 if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod;
3883 }
3884
3885 // now see if we still clip with the refined range
3886 if ( ( contribs->n0 + stop_range ) > row_end )
3887 {
3888 int new_n0 = row_end - stop_range;
3889 int num = contribs->n1 - contribs->n0 + 1;
3890 int backup = contribs->n0 - new_n0;
3891 float * from_co = coeffs + num - 1;
3892 float * to_co = from_co + backup;
3893
3894 STBIR_ASSERT( ( new_n0 >= row0 ) && ( new_n0 < contribs->n0 ) );
3895
3896 // move the coeffs over
3897 while( num )
3898 {
3899 *to_co-- = *from_co--;
3900 --num;
3901 }
3902 // zero new positions
3903 while ( to_co >= coeffs )
3904 *to_co-- = 0;
3905 // set new start point
3906 contribs->n0 = new_n0;
3907 if ( widest > 12 )
3908 {
3909 int mod;
3910
3911 // how far will be read in the n_coeff loop (which depends on the widest count mod4);
3912 mod = widest & 3;
3913 stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod;
3914
3915 // the n_coeff loops do a minimum amount of coeffs, so factor that in!
3916 if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod;
3917 }
3918 }
3919 }
3920 --contribs;
3921 coeffs -= widest;
3922 }
3923 }
3924
3925 return widest;
3926 #undef STBIR_MOVE_1
3927 #undef STBIR_MOVE_2
3928 #undef STBIR_MOVE_4
3929}
3930
3931static void stbir__calculate_filters( stbir__sampler * samp, stbir__sampler * other_axis_for_pivot, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO )
3932{
3933 int n;
3934 float scale = samp->scale_info.scale;
3935 stbir__kernel_callback * kernel = samp->filter_kernel;
3936 stbir__support_callback * support = samp->filter_support;
3937 float inv_scale = samp->scale_info.inv_scale;
3938 int input_full_size = samp->scale_info.input_full_size;
3939 int gather_num_contributors = samp->num_contributors;
3940 stbir__contributors* gather_contributors = samp->contributors;
3941 float * gather_coeffs = samp->coefficients;
3942 int gather_coefficient_width = samp->coefficient_width;
3943
3944 switch ( samp->is_gather )
3945 {
3946 case 1: // gather upsample
3947 {
3948 float out_pixels_radius = support(inv_scale,user_data) * scale;
3949
3950 stbir__calculate_coefficients_for_gather_upsample( out_pixels_radius, kernel, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width, samp->edge, user_data );
3951
3952 STBIR_PROFILE_BUILD_START( cleanup );
3953 stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width );
3954 STBIR_PROFILE_BUILD_END( cleanup );
3955 }
3956 break;
3957
3958 case 0: // scatter downsample (only on vertical)
3959 case 2: // gather downsample
3960 {
3961 float in_pixels_radius = support(scale,user_data) * inv_scale;
3962 int filter_pixel_margin = samp->filter_pixel_margin;
3963 int input_end = input_full_size + filter_pixel_margin;
3964
3965 // if this is a scatter, we do a downsample gather to get the coeffs, and then pivot after
3966 if ( !samp->is_gather )
3967 {
3968 // check if we are using the same gather downsample on the horizontal as this vertical,
3969 // if so, then we don't have to generate them, we can just pivot from the horizontal.
3970 if ( other_axis_for_pivot )
3971 {
3972 gather_contributors = other_axis_for_pivot->contributors;
3973 gather_coeffs = other_axis_for_pivot->coefficients;
3974 gather_coefficient_width = other_axis_for_pivot->coefficient_width;
3975 gather_num_contributors = other_axis_for_pivot->num_contributors;
3976 samp->extent_info.lowest = other_axis_for_pivot->extent_info.lowest;
3977 samp->extent_info.highest = other_axis_for_pivot->extent_info.highest;
3978 samp->extent_info.widest = other_axis_for_pivot->extent_info.widest;
3979 goto jump_right_to_pivot;
3980 }
3981
3982 gather_contributors = samp->gather_prescatter_contributors;
3983 gather_coeffs = samp->gather_prescatter_coefficients;
3984 gather_coefficient_width = samp->gather_prescatter_coefficient_width;
3985 gather_num_contributors = samp->gather_prescatter_num_contributors;
3986 }
3987
3988 stbir__calculate_coefficients_for_gather_downsample( -filter_pixel_margin, input_end, in_pixels_radius, kernel, &samp->scale_info, gather_coefficient_width, gather_num_contributors, gather_contributors, gather_coeffs, user_data );
3989
3990 STBIR_PROFILE_BUILD_START( cleanup );
3991 stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width );
3992 STBIR_PROFILE_BUILD_END( cleanup );
3993
3994 if ( !samp->is_gather )
3995 {
3996 // if this is a scatter (vertical only), then we need to pivot the coeffs
3997 stbir__contributors * scatter_contributors;
3998 int highest_set;
3999
4000 jump_right_to_pivot:
4001
4002 STBIR_PROFILE_BUILD_START( pivot );
4003
4004 highest_set = (-filter_pixel_margin) - 1;
4005 for (n = 0; n < gather_num_contributors; n++)
4006 {
4007 int k;
4008 int gn0 = gather_contributors->n0, gn1 = gather_contributors->n1;
4009 int scatter_coefficient_width = samp->coefficient_width;
4010 float * scatter_coeffs = samp->coefficients + ( gn0 + filter_pixel_margin ) * scatter_coefficient_width;
4011 float * g_coeffs = gather_coeffs;
4012 scatter_contributors = samp->contributors + ( gn0 + filter_pixel_margin );
4013
4014 for (k = gn0 ; k <= gn1 ; k++ )
4015 {
4016 float gc = *g_coeffs++;
4017
4018 // skip zero and denormals - must skip zeros to avoid adding coeffs beyond scatter_coefficient_width
4019 // (which happens when pivoting from horizontal, which might have dummy zeros)
4020 if ( ( ( gc >= stbir__small_float ) || ( gc <= -stbir__small_float ) ) )
4021 {
4022 if ( ( k > highest_set ) || ( scatter_contributors->n0 > scatter_contributors->n1 ) )
4023 {
4024 {
4025 // if we are skipping over several contributors, we need to clear the skipped ones
4026 stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1);
4027 while ( clear_contributors < scatter_contributors )
4028 {
4029 clear_contributors->n0 = 0;
4030 clear_contributors->n1 = -1;
4031 ++clear_contributors;
4032 }
4033 }
4034 scatter_contributors->n0 = n;
4035 scatter_contributors->n1 = n;
4036 scatter_coeffs[0] = gc;
4037 highest_set = k;
4038 }
4039 else
4040 {
4041 stbir__insert_coeff( scatter_contributors, scatter_coeffs, n, gc, scatter_coefficient_width );
4042 }
4043 STBIR_ASSERT( ( scatter_contributors->n1 - scatter_contributors->n0 + 1 ) <= scatter_coefficient_width );
4044 }
4045 ++scatter_contributors;
4046 scatter_coeffs += scatter_coefficient_width;
4047 }
4048
4049 ++gather_contributors;
4050 gather_coeffs += gather_coefficient_width;
4051 }
4052
4053 // now clear any unset contribs
4054 {
4055 stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1);
4056 stbir__contributors * end_contributors = samp->contributors + samp->num_contributors;
4057 while ( clear_contributors < end_contributors )
4058 {
4059 clear_contributors->n0 = 0;
4060 clear_contributors->n1 = -1;
4061 ++clear_contributors;
4062 }
4063 }
4064
4065 STBIR_PROFILE_BUILD_END( pivot );
4066 }
4067 }
4068 break;
4069 }
4070}
4071
4072
4073//========================================================================================================
4074// scanline decoders and encoders
4075
4076#define stbir__coder_min_num 1
4077#define STB_IMAGE_RESIZE_DO_CODERS
4078#include STBIR__HEADER_FILENAME
4079
4080#define stbir__decode_suffix BGRA
4081#define stbir__decode_swizzle
4082#define stbir__decode_order0 2
4083#define stbir__decode_order1 1
4084#define stbir__decode_order2 0
4085#define stbir__decode_order3 3
4086#define stbir__encode_order0 2
4087#define stbir__encode_order1 1
4088#define stbir__encode_order2 0
4089#define stbir__encode_order3 3
4090#define stbir__coder_min_num 4
4091#define STB_IMAGE_RESIZE_DO_CODERS
4092#include STBIR__HEADER_FILENAME
4093
4094#define stbir__decode_suffix ARGB
4095#define stbir__decode_swizzle
4096#define stbir__decode_order0 1
4097#define stbir__decode_order1 2
4098#define stbir__decode_order2 3
4099#define stbir__decode_order3 0
4100#define stbir__encode_order0 3
4101#define stbir__encode_order1 0
4102#define stbir__encode_order2 1
4103#define stbir__encode_order3 2
4104#define stbir__coder_min_num 4
4105#define STB_IMAGE_RESIZE_DO_CODERS
4106#include STBIR__HEADER_FILENAME
4107
4108#define stbir__decode_suffix ABGR
4109#define stbir__decode_swizzle
4110#define stbir__decode_order0 3
4111#define stbir__decode_order1 2
4112#define stbir__decode_order2 1
4113#define stbir__decode_order3 0
4114#define stbir__encode_order0 3
4115#define stbir__encode_order1 2
4116#define stbir__encode_order2 1
4117#define stbir__encode_order3 0
4118#define stbir__coder_min_num 4
4119#define STB_IMAGE_RESIZE_DO_CODERS
4120#include STBIR__HEADER_FILENAME
4121
4122#define stbir__decode_suffix AR
4123#define stbir__decode_swizzle
4124#define stbir__decode_order0 1
4125#define stbir__decode_order1 0
4126#define stbir__decode_order2 3
4127#define stbir__decode_order3 2
4128#define stbir__encode_order0 1
4129#define stbir__encode_order1 0
4130#define stbir__encode_order2 3
4131#define stbir__encode_order3 2
4132#define stbir__coder_min_num 2
4133#define STB_IMAGE_RESIZE_DO_CODERS
4134#include STBIR__HEADER_FILENAME
4135
4136
4137// fancy alpha means we expand to keep both premultipied and non-premultiplied color channels
4138static void stbir__fancy_alpha_weight_4ch( float * out_buffer, int width_times_channels )
4139{
4140 float STBIR_STREAMOUT_PTR(*) out = out_buffer;
4141 float const * end_decode = out_buffer + ( width_times_channels / 4 ) * 7; // decode buffer aligned to end of out_buffer
4142 float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels;
4143
4144 // fancy alpha is stored internally as R G B A Rpm Gpm Bpm
4145
4146 #ifdef STBIR_SIMD
4147
4148 #ifdef STBIR_SIMD8
4149 decode += 16;
4150 STBIR_NO_UNROLL_LOOP_START
4151 while ( decode <= end_decode )
4152 {
4153 stbir__simdf8 d0,d1,a0,a1,p0,p1;
4154 STBIR_NO_UNROLL(decode);
4155 stbir__simdf8_load( d0, decode-16 );
4156 stbir__simdf8_load( d1, decode-16+8 );
4157 stbir__simdf8_0123to33333333( a0, d0 );
4158 stbir__simdf8_0123to33333333( a1, d1 );
4159 stbir__simdf8_mult( p0, a0, d0 );
4160 stbir__simdf8_mult( p1, a1, d1 );
4161 stbir__simdf8_bot4s( a0, d0, p0 );
4162 stbir__simdf8_bot4s( a1, d1, p1 );
4163 stbir__simdf8_top4s( d0, d0, p0 );
4164 stbir__simdf8_top4s( d1, d1, p1 );
4165 stbir__simdf8_store ( out, a0 );
4166 stbir__simdf8_store ( out+7, d0 );
4167 stbir__simdf8_store ( out+14, a1 );
4168 stbir__simdf8_store ( out+21, d1 );
4169 decode += 16;
4170 out += 28;
4171 }
4172 decode -= 16;
4173 #else
4174 decode += 8;
4175 STBIR_NO_UNROLL_LOOP_START
4176 while ( decode <= end_decode )
4177 {
4178 stbir__simdf d0,a0,d1,a1,p0,p1;
4179 STBIR_NO_UNROLL(decode);
4180 stbir__simdf_load( d0, decode-8 );
4181 stbir__simdf_load( d1, decode-8+4 );
4182 stbir__simdf_0123to3333( a0, d0 );
4183 stbir__simdf_0123to3333( a1, d1 );
4184 stbir__simdf_mult( p0, a0, d0 );
4185 stbir__simdf_mult( p1, a1, d1 );
4186 stbir__simdf_store ( out, d0 );
4187 stbir__simdf_store ( out+4, p0 );
4188 stbir__simdf_store ( out+7, d1 );
4189 stbir__simdf_store ( out+7+4, p1 );
4190 decode += 8;
4191 out += 14;
4192 }
4193 decode -= 8;
4194 #endif
4195
4196 // might be one last odd pixel
4197 #ifdef STBIR_SIMD8
4198 STBIR_NO_UNROLL_LOOP_START
4199 while ( decode < end_decode )
4200 #else
4201 if ( decode < end_decode )
4202 #endif
4203 {
4204 stbir__simdf d,a,p;
4205 STBIR_NO_UNROLL(decode);
4206 stbir__simdf_load( d, decode );
4207 stbir__simdf_0123to3333( a, d );
4208 stbir__simdf_mult( p, a, d );
4209 stbir__simdf_store ( out, d );
4210 stbir__simdf_store ( out+4, p );
4211 decode += 4;
4212 out += 7;
4213 }
4214
4215 #else
4216
4217 while( decode < end_decode )
4218 {
4219 float r = decode[0], g = decode[1], b = decode[2], alpha = decode[3];
4220 out[0] = r;
4221 out[1] = g;
4222 out[2] = b;
4223 out[3] = alpha;
4224 out[4] = r * alpha;
4225 out[5] = g * alpha;
4226 out[6] = b * alpha;
4227 out += 7;
4228 decode += 4;
4229 }
4230
4231 #endif
4232}
4233
4234static void stbir__fancy_alpha_weight_2ch( float * out_buffer, int width_times_channels )
4235{
4236 float STBIR_STREAMOUT_PTR(*) out = out_buffer;
4237 float const * end_decode = out_buffer + ( width_times_channels / 2 ) * 3;
4238 float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels;
4239
4240 // for fancy alpha, turns into: [X A Xpm][X A Xpm],etc
4241
4242 #ifdef STBIR_SIMD
4243
4244 decode += 8;
4245 if ( decode <= end_decode )
4246 {
4247 STBIR_NO_UNROLL_LOOP_START
4248 do {
4249 #ifdef STBIR_SIMD8
4250 stbir__simdf8 d0,a0,p0;
4251 STBIR_NO_UNROLL(decode);
4252 stbir__simdf8_load( d0, decode-8 );
4253 stbir__simdf8_0123to11331133( p0, d0 );
4254 stbir__simdf8_0123to00220022( a0, d0 );
4255 stbir__simdf8_mult( p0, p0, a0 );
4256
4257 stbir__simdf_store2( out, stbir__if_simdf8_cast_to_simdf4( d0 ) );
4258 stbir__simdf_store( out+2, stbir__if_simdf8_cast_to_simdf4( p0 ) );
4259 stbir__simdf_store2h( out+3, stbir__if_simdf8_cast_to_simdf4( d0 ) );
4260
4261 stbir__simdf_store2( out+6, stbir__simdf8_gettop4( d0 ) );
4262 stbir__simdf_store( out+8, stbir__simdf8_gettop4( p0 ) );
4263 stbir__simdf_store2h( out+9, stbir__simdf8_gettop4( d0 ) );
4264 #else
4265 stbir__simdf d0,a0,d1,a1,p0,p1;
4266 STBIR_NO_UNROLL(decode);
4267 stbir__simdf_load( d0, decode-8 );
4268 stbir__simdf_load( d1, decode-8+4 );
4269 stbir__simdf_0123to1133( p0, d0 );
4270 stbir__simdf_0123to1133( p1, d1 );
4271 stbir__simdf_0123to0022( a0, d0 );
4272 stbir__simdf_0123to0022( a1, d1 );
4273 stbir__simdf_mult( p0, p0, a0 );
4274 stbir__simdf_mult( p1, p1, a1 );
4275
4276 stbir__simdf_store2( out, d0 );
4277 stbir__simdf_store( out+2, p0 );
4278 stbir__simdf_store2h( out+3, d0 );
4279
4280 stbir__simdf_store2( out+6, d1 );
4281 stbir__simdf_store( out+8, p1 );
4282 stbir__simdf_store2h( out+9, d1 );
4283 #endif
4284 decode += 8;
4285 out += 12;
4286 } while ( decode <= end_decode );
4287 }
4288 decode -= 8;
4289 #endif
4290
4291 STBIR_SIMD_NO_UNROLL_LOOP_START
4292 while( decode < end_decode )
4293 {
4294 float x = decode[0], y = decode[1];
4295 STBIR_SIMD_NO_UNROLL(decode);
4296 out[0] = x;
4297 out[1] = y;
4298 out[2] = x * y;
4299 out += 3;
4300 decode += 2;
4301 }
4302}
4303
4304static void stbir__fancy_alpha_unweight_4ch( float * encode_buffer, int width_times_channels )
4305{
4306 float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer;
4307 float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer;
4308 float const * end_output = encode_buffer + width_times_channels;
4309
4310 // fancy RGBA is stored internally as R G B A Rpm Gpm Bpm
4311
4312 STBIR_SIMD_NO_UNROLL_LOOP_START
4313 do {
4314 float alpha = input[3];
4315#ifdef STBIR_SIMD
4316 stbir__simdf i,ia;
4317 STBIR_SIMD_NO_UNROLL(encode);
4318 if ( alpha < stbir__small_float )
4319 {
4320 stbir__simdf_load( i, input );
4321 stbir__simdf_store( encode, i );
4322 }
4323 else
4324 {
4325 stbir__simdf_load1frep4( ia, 1.0f / alpha );
4326 stbir__simdf_load( i, input+4 );
4327 stbir__simdf_mult( i, i, ia );
4328 stbir__simdf_store( encode, i );
4329 encode[3] = alpha;
4330 }
4331#else
4332 if ( alpha < stbir__small_float )
4333 {
4334 encode[0] = input[0];
4335 encode[1] = input[1];
4336 encode[2] = input[2];
4337 }
4338 else
4339 {
4340 float ialpha = 1.0f / alpha;
4341 encode[0] = input[4] * ialpha;
4342 encode[1] = input[5] * ialpha;
4343 encode[2] = input[6] * ialpha;
4344 }
4345 encode[3] = alpha;
4346#endif
4347
4348 input += 7;
4349 encode += 4;
4350 } while ( encode < end_output );
4351}
4352
4353// format: [X A Xpm][X A Xpm] etc
4354static void stbir__fancy_alpha_unweight_2ch( float * encode_buffer, int width_times_channels )
4355{
4356 float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer;
4357 float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer;
4358 float const * end_output = encode_buffer + width_times_channels;
4359
4360 do {
4361 float alpha = input[1];
4362 encode[0] = input[0];
4363 if ( alpha >= stbir__small_float )
4364 encode[0] = input[2] / alpha;
4365 encode[1] = alpha;
4366
4367 input += 3;
4368 encode += 2;
4369 } while ( encode < end_output );
4370}
4371
4372static void stbir__simple_alpha_weight_4ch( float * decode_buffer, int width_times_channels )
4373{
4374 float STBIR_STREAMOUT_PTR(*) decode = decode_buffer;
4375 float const * end_decode = decode_buffer + width_times_channels;
4376
4377 #ifdef STBIR_SIMD
4378 {
4379 decode += 2 * stbir__simdfX_float_count;
4380 STBIR_NO_UNROLL_LOOP_START
4381 while ( decode <= end_decode )
4382 {
4383 stbir__simdfX d0,a0,d1,a1;
4384 STBIR_NO_UNROLL(decode);
4385 stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count );
4386 stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count );
4387 stbir__simdfX_aaa1( a0, d0, STBIR_onesX );
4388 stbir__simdfX_aaa1( a1, d1, STBIR_onesX );
4389 stbir__simdfX_mult( d0, d0, a0 );
4390 stbir__simdfX_mult( d1, d1, a1 );
4391 stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 );
4392 stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 );
4393 decode += 2 * stbir__simdfX_float_count;
4394 }
4395 decode -= 2 * stbir__simdfX_float_count;
4396
4397 // few last pixels remnants
4398 #ifdef STBIR_SIMD8
4399 STBIR_NO_UNROLL_LOOP_START
4400 while ( decode < end_decode )
4401 #else
4402 if ( decode < end_decode )
4403 #endif
4404 {
4405 stbir__simdf d,a;
4406 stbir__simdf_load( d, decode );
4407 stbir__simdf_aaa1( a, d, STBIR__CONSTF(STBIR_ones) );
4408 stbir__simdf_mult( d, d, a );
4409 stbir__simdf_store ( decode, d );
4410 decode += 4;
4411 }
4412 }
4413
4414 #else
4415
4416 while( decode < end_decode )
4417 {
4418 float alpha = decode[3];
4419 decode[0] *= alpha;
4420 decode[1] *= alpha;
4421 decode[2] *= alpha;
4422 decode += 4;
4423 }
4424
4425 #endif
4426}
4427
4428static void stbir__simple_alpha_weight_2ch( float * decode_buffer, int width_times_channels )
4429{
4430 float STBIR_STREAMOUT_PTR(*) decode = decode_buffer;
4431 float const * end_decode = decode_buffer + width_times_channels;
4432
4433 #ifdef STBIR_SIMD
4434 decode += 2 * stbir__simdfX_float_count;
4435 STBIR_NO_UNROLL_LOOP_START
4436 while ( decode <= end_decode )
4437 {
4438 stbir__simdfX d0,a0,d1,a1;
4439 STBIR_NO_UNROLL(decode);
4440 stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count );
4441 stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count );
4442 stbir__simdfX_a1a1( a0, d0, STBIR_onesX );
4443 stbir__simdfX_a1a1( a1, d1, STBIR_onesX );
4444 stbir__simdfX_mult( d0, d0, a0 );
4445 stbir__simdfX_mult( d1, d1, a1 );
4446 stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 );
4447 stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 );
4448 decode += 2 * stbir__simdfX_float_count;
4449 }
4450 decode -= 2 * stbir__simdfX_float_count;
4451 #endif
4452
4453 STBIR_SIMD_NO_UNROLL_LOOP_START
4454 while( decode < end_decode )
4455 {
4456 float alpha = decode[1];
4457 STBIR_SIMD_NO_UNROLL(decode);
4458 decode[0] *= alpha;
4459 decode += 2;
4460 }
4461}
4462
4463static void stbir__simple_alpha_unweight_4ch( float * encode_buffer, int width_times_channels )
4464{
4465 float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer;
4466 float const * end_output = encode_buffer + width_times_channels;
4467
4468 STBIR_SIMD_NO_UNROLL_LOOP_START
4469 do {
4470 float alpha = encode[3];
4471
4472#ifdef STBIR_SIMD
4473 stbir__simdf i,ia;
4474 STBIR_SIMD_NO_UNROLL(encode);
4475 if ( alpha >= stbir__small_float )
4476 {
4477 stbir__simdf_load1frep4( ia, 1.0f / alpha );
4478 stbir__simdf_load( i, encode );
4479 stbir__simdf_mult( i, i, ia );
4480 stbir__simdf_store( encode, i );
4481 encode[3] = alpha;
4482 }
4483#else
4484 if ( alpha >= stbir__small_float )
4485 {
4486 float ialpha = 1.0f / alpha;
4487 encode[0] *= ialpha;
4488 encode[1] *= ialpha;
4489 encode[2] *= ialpha;
4490 }
4491#endif
4492 encode += 4;
4493 } while ( encode < end_output );
4494}
4495
4496static void stbir__simple_alpha_unweight_2ch( float * encode_buffer, int width_times_channels )
4497{
4498 float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer;
4499 float const * end_output = encode_buffer + width_times_channels;
4500
4501 do {
4502 float alpha = encode[1];
4503 if ( alpha >= stbir__small_float )
4504 encode[0] /= alpha;
4505 encode += 2;
4506 } while ( encode < end_output );
4507}
4508
4509
4510// only used in RGB->BGR or BGR->RGB
4511static void stbir__simple_flip_3ch( float * decode_buffer, int width_times_channels )
4512{
4513 float STBIR_STREAMOUT_PTR(*) decode = decode_buffer;
4514 float const * end_decode = decode_buffer + width_times_channels;
4515
4516#ifdef STBIR_SIMD
4517 #ifdef stbir__simdf_swiz2 // do we have two argument swizzles?
4518 end_decode -= 12;
4519 STBIR_NO_UNROLL_LOOP_START
4520 while( decode <= end_decode )
4521 {
4522 // on arm64 8 instructions, no overlapping stores
4523 stbir__simdf a,b,c,na,nb;
4524 STBIR_SIMD_NO_UNROLL(decode);
4525 stbir__simdf_load( a, decode );
4526 stbir__simdf_load( b, decode+4 );
4527 stbir__simdf_load( c, decode+8 );
4528
4529 na = stbir__simdf_swiz2( a, b, 2, 1, 0, 5 );
4530 b = stbir__simdf_swiz2( a, b, 4, 3, 6, 7 );
4531 nb = stbir__simdf_swiz2( b, c, 0, 1, 4, 3 );
4532 c = stbir__simdf_swiz2( b, c, 2, 7, 6, 5 );
4533
4534 stbir__simdf_store( decode, na );
4535 stbir__simdf_store( decode+4, nb );
4536 stbir__simdf_store( decode+8, c );
4537 decode += 12;
4538 }
4539 end_decode += 12;
4540 #else
4541 end_decode -= 24;
4542 STBIR_NO_UNROLL_LOOP_START
4543 while( decode <= end_decode )
4544 {
4545 // 26 instructions on x64
4546 stbir__simdf a,b,c,d,e,f,g;
4547 float i21, i23;
4548 STBIR_SIMD_NO_UNROLL(decode);
4549 stbir__simdf_load( a, decode );
4550 stbir__simdf_load( b, decode+3 );
4551 stbir__simdf_load( c, decode+6 );
4552 stbir__simdf_load( d, decode+9 );
4553 stbir__simdf_load( e, decode+12 );
4554 stbir__simdf_load( f, decode+15 );
4555 stbir__simdf_load( g, decode+18 );
4556
4557 a = stbir__simdf_swiz( a, 2, 1, 0, 3 );
4558 b = stbir__simdf_swiz( b, 2, 1, 0, 3 );
4559 c = stbir__simdf_swiz( c, 2, 1, 0, 3 );
4560 d = stbir__simdf_swiz( d, 2, 1, 0, 3 );
4561 e = stbir__simdf_swiz( e, 2, 1, 0, 3 );
4562 f = stbir__simdf_swiz( f, 2, 1, 0, 3 );
4563 g = stbir__simdf_swiz( g, 2, 1, 0, 3 );
4564
4565 // stores overlap, need to be in order,
4566 stbir__simdf_store( decode, a );
4567 i21 = decode[21];
4568 stbir__simdf_store( decode+3, b );
4569 i23 = decode[23];
4570 stbir__simdf_store( decode+6, c );
4571 stbir__simdf_store( decode+9, d );
4572 stbir__simdf_store( decode+12, e );
4573 stbir__simdf_store( decode+15, f );
4574 stbir__simdf_store( decode+18, g );
4575 decode[21] = i23;
4576 decode[23] = i21;
4577 decode += 24;
4578 }
4579 end_decode += 24;
4580 #endif
4581#else
4582 end_decode -= 12;
4583 STBIR_NO_UNROLL_LOOP_START
4584 while( decode <= end_decode )
4585 {
4586 // 16 instructions
4587 float t0,t1,t2,t3;
4588 STBIR_NO_UNROLL(decode);
4589 t0 = decode[0]; t1 = decode[3]; t2 = decode[6]; t3 = decode[9];
4590 decode[0] = decode[2]; decode[3] = decode[5]; decode[6] = decode[8]; decode[9] = decode[11];
4591 decode[2] = t0; decode[5] = t1; decode[8] = t2; decode[11] = t3;
4592 decode += 12;
4593 }
4594 end_decode += 12;
4595#endif
4596
4597 STBIR_NO_UNROLL_LOOP_START
4598 while( decode < end_decode )
4599 {
4600 float t = decode[0];
4601 STBIR_NO_UNROLL(decode);
4602 decode[0] = decode[2];
4603 decode[2] = t;
4604 decode += 3;
4605 }
4606}
4607
4608
4609
4610static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float * output_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO )
4611{
4612 int channels = stbir_info->channels;
4613 int effective_channels = stbir_info->effective_channels;
4614 int input_sample_in_bytes = stbir__type_size[stbir_info->input_type] * channels;
4615 stbir_edge edge_horizontal = stbir_info->horizontal.edge;
4616 stbir_edge edge_vertical = stbir_info->vertical.edge;
4617 int row = stbir__edge_wrap(edge_vertical, n, stbir_info->vertical.scale_info.input_full_size);
4618 const void* input_plane_data = ( (char *) stbir_info->input_data ) + (size_t)row * (size_t) stbir_info->input_stride_bytes;
4619 stbir__span const * spans = stbir_info->scanline_extents.spans;
4620 float * full_decode_buffer = output_buffer - stbir_info->scanline_extents.conservative.n0 * effective_channels;
4621 float * last_decoded = 0;
4622
4623 // if we are on edge_zero, and we get in here with an out of bounds n, then the calculate filters has failed
4624 STBIR_ASSERT( !(edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->vertical.scale_info.input_full_size)) );
4625
4626 do
4627 {
4628 float * decode_buffer;
4629 void const * input_data;
4630 float * end_decode;
4631 int width_times_channels;
4632 int width;
4633
4634 if ( spans->n1 < spans->n0 )
4635 break;
4636
4637 width = spans->n1 + 1 - spans->n0;
4638 decode_buffer = full_decode_buffer + spans->n0 * effective_channels;
4639 end_decode = full_decode_buffer + ( spans->n1 + 1 ) * effective_channels;
4640 width_times_channels = width * channels;
4641
4642 // read directly out of input plane by default
4643 input_data = ( (char*)input_plane_data ) + spans->pixel_offset_for_input * input_sample_in_bytes;
4644
4645 // if we have an input callback, call it to get the input data
4646 if ( stbir_info->in_pixels_cb )
4647 {
4648 // 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
4649 input_data = stbir_info->in_pixels_cb( ( (char*) end_decode ) - ( width * input_sample_in_bytes ) + ( ( stbir_info->input_type != STBIR_TYPE_FLOAT ) ? ( sizeof(float)*STBIR_INPUT_CALLBACK_PADDING ) : 0 ), input_plane_data, width, spans->pixel_offset_for_input, row, stbir_info->user_data );
4650 }
4651
4652 STBIR_PROFILE_START( decode );
4653 // 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)
4654 last_decoded = stbir_info->decode_pixels( (float*)end_decode - width_times_channels, width_times_channels, input_data );
4655 STBIR_PROFILE_END( decode );
4656
4657 if (stbir_info->alpha_weight)
4658 {
4659 STBIR_PROFILE_START( alpha );
4660 stbir_info->alpha_weight( decode_buffer, width_times_channels );
4661 STBIR_PROFILE_END( alpha );
4662 }
4663
4664 ++spans;
4665 } while ( spans <= ( &stbir_info->scanline_extents.spans[1] ) );
4666
4667 // handle the edge_wrap filter (all other types are handled back out at the calculate_filter stage)
4668 // basically the idea here is that if we have the whole scanline in memory, we don't redecode the
4669 // wrapped edge pixels, and instead just memcpy them from the scanline into the edge positions
4670 if ( ( edge_horizontal == STBIR_EDGE_WRAP ) && ( stbir_info->scanline_extents.edge_sizes[0] | stbir_info->scanline_extents.edge_sizes[1] ) )
4671 {
4672 // this code only runs if we're in edge_wrap, and we're doing the entire scanline
4673 int e, start_x[2];
4674 int input_full_size = stbir_info->horizontal.scale_info.input_full_size;
4675
4676 start_x[0] = -stbir_info->scanline_extents.edge_sizes[0]; // left edge start x
4677 start_x[1] = input_full_size; // right edge
4678
4679 for( e = 0; e < 2 ; e++ )
4680 {
4681 // do each margin
4682 int margin = stbir_info->scanline_extents.edge_sizes[e];
4683 if ( margin )
4684 {
4685 int x = start_x[e];
4686 float * marg = full_decode_buffer + x * effective_channels;
4687 float const * src = full_decode_buffer + stbir__edge_wrap(edge_horizontal, x, input_full_size) * effective_channels;
4688 STBIR_MEMCPY( marg, src, margin * effective_channels * sizeof(float) );
4689 if ( e == 1 ) last_decoded = marg + margin * effective_channels;
4690 }
4691 }
4692 }
4693
4694 // some of the horizontal gathers read one float off the edge (which is masked out), but we force a zero here to make sure no NaNs leak in
4695 // (we can't pre-zero it, because the input callback can use that area as padding)
4696 last_decoded[0] = 0.0f;
4697
4698 // we clear this extra float, because the final output pixel filter kernel might have used one less coeff than the max filter width
4699 // when this happens, we do read that pixel from the input, so it too could be Nan, so just zero an extra one.
4700 // this fits because each scanline is padded by three floats (STBIR_INPUT_CALLBACK_PADDING)
4701 last_decoded[1] = 0.0f;
4702}
4703
4704
4705//=================
4706// Do 1 channel horizontal routines
4707
4708#ifdef STBIR_SIMD
4709
4710#define stbir__1_coeff_only() \
4711 stbir__simdf tot,c; \
4712 STBIR_SIMD_NO_UNROLL(decode); \
4713 stbir__simdf_load1( c, hc ); \
4714 stbir__simdf_mult1_mem( tot, c, decode );
4715
4716#define stbir__2_coeff_only() \
4717 stbir__simdf tot,c,d; \
4718 STBIR_SIMD_NO_UNROLL(decode); \
4719 stbir__simdf_load2z( c, hc ); \
4720 stbir__simdf_load2( d, decode ); \
4721 stbir__simdf_mult( tot, c, d ); \
4722 stbir__simdf_0123to1230( c, tot ); \
4723 stbir__simdf_add1( tot, tot, c );
4724
4725#define stbir__3_coeff_only() \
4726 stbir__simdf tot,c,t; \
4727 STBIR_SIMD_NO_UNROLL(decode); \
4728 stbir__simdf_load( c, hc ); \
4729 stbir__simdf_mult_mem( tot, c, decode ); \
4730 stbir__simdf_0123to1230( c, tot ); \
4731 stbir__simdf_0123to2301( t, tot ); \
4732 stbir__simdf_add1( tot, tot, c ); \
4733 stbir__simdf_add1( tot, tot, t );
4734
4735#define stbir__store_output_tiny() \
4736 stbir__simdf_store1( output, tot ); \
4737 horizontal_coefficients += coefficient_width; \
4738 ++horizontal_contributors; \
4739 output += 1;
4740
4741#define stbir__4_coeff_start() \
4742 stbir__simdf tot,c; \
4743 STBIR_SIMD_NO_UNROLL(decode); \
4744 stbir__simdf_load( c, hc ); \
4745 stbir__simdf_mult_mem( tot, c, decode ); \
4746
4747#define stbir__4_coeff_continue_from_4( ofs ) \
4748 STBIR_SIMD_NO_UNROLL(decode); \
4749 stbir__simdf_load( c, hc + (ofs) ); \
4750 stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) );
4751
4752#define stbir__1_coeff_remnant( ofs ) \
4753 { stbir__simdf d; \
4754 stbir__simdf_load1z( c, hc + (ofs) ); \
4755 stbir__simdf_load1( d, decode + (ofs) ); \
4756 stbir__simdf_madd( tot, tot, d, c ); }
4757
4758#define stbir__2_coeff_remnant( ofs ) \
4759 { stbir__simdf d; \
4760 stbir__simdf_load2z( c, hc+(ofs) ); \
4761 stbir__simdf_load2( d, decode+(ofs) ); \
4762 stbir__simdf_madd( tot, tot, d, c ); }
4763
4764#define stbir__3_coeff_setup() \
4765 stbir__simdf mask; \
4766 stbir__simdf_load( mask, STBIR_mask + 3 );
4767
4768#define stbir__3_coeff_remnant( ofs ) \
4769 stbir__simdf_load( c, hc+(ofs) ); \
4770 stbir__simdf_and( c, c, mask ); \
4771 stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) );
4772
4773#define stbir__store_output() \
4774 stbir__simdf_0123to2301( c, tot ); \
4775 stbir__simdf_add( tot, tot, c ); \
4776 stbir__simdf_0123to1230( c, tot ); \
4777 stbir__simdf_add1( tot, tot, c ); \
4778 stbir__simdf_store1( output, tot ); \
4779 horizontal_coefficients += coefficient_width; \
4780 ++horizontal_contributors; \
4781 output += 1;
4782
4783#else
4784
4785#define stbir__1_coeff_only() \
4786 float tot; \
4787 tot = decode[0]*hc[0];
4788
4789#define stbir__2_coeff_only() \
4790 float tot; \
4791 tot = decode[0] * hc[0]; \
4792 tot += decode[1] * hc[1];
4793
4794#define stbir__3_coeff_only() \
4795 float tot; \
4796 tot = decode[0] * hc[0]; \
4797 tot += decode[1] * hc[1]; \
4798 tot += decode[2] * hc[2];
4799
4800#define stbir__store_output_tiny() \
4801 output[0] = tot; \
4802 horizontal_coefficients += coefficient_width; \
4803 ++horizontal_contributors; \
4804 output += 1;
4805
4806#define stbir__4_coeff_start() \
4807 float tot0,tot1,tot2,tot3; \
4808 tot0 = decode[0] * hc[0]; \
4809 tot1 = decode[1] * hc[1]; \
4810 tot2 = decode[2] * hc[2]; \
4811 tot3 = decode[3] * hc[3];
4812
4813#define stbir__4_coeff_continue_from_4( ofs ) \
4814 tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \
4815 tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \
4816 tot2 += decode[2+(ofs)] * hc[2+(ofs)]; \
4817 tot3 += decode[3+(ofs)] * hc[3+(ofs)];
4818
4819#define stbir__1_coeff_remnant( ofs ) \
4820 tot0 += decode[0+(ofs)] * hc[0+(ofs)];
4821
4822#define stbir__2_coeff_remnant( ofs ) \
4823 tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \
4824 tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \
4825
4826#define stbir__3_coeff_remnant( ofs ) \
4827 tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \
4828 tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \
4829 tot2 += decode[2+(ofs)] * hc[2+(ofs)];
4830
4831#define stbir__store_output() \
4832 output[0] = (tot0+tot2)+(tot1+tot3); \
4833 horizontal_coefficients += coefficient_width; \
4834 ++horizontal_contributors; \
4835 output += 1;
4836
4837#endif
4838
4839#define STBIR__horizontal_channels 1
4840#define STB_IMAGE_RESIZE_DO_HORIZONTALS
4841#include STBIR__HEADER_FILENAME
4842
4843
4844//=================
4845// Do 2 channel horizontal routines
4846
4847#ifdef STBIR_SIMD
4848
4849#define stbir__1_coeff_only() \
4850 stbir__simdf tot,c,d; \
4851 STBIR_SIMD_NO_UNROLL(decode); \
4852 stbir__simdf_load1z( c, hc ); \
4853 stbir__simdf_0123to0011( c, c ); \
4854 stbir__simdf_load2( d, decode ); \
4855 stbir__simdf_mult( tot, d, c );
4856
4857#define stbir__2_coeff_only() \
4858 stbir__simdf tot,c; \
4859 STBIR_SIMD_NO_UNROLL(decode); \
4860 stbir__simdf_load2( c, hc ); \
4861 stbir__simdf_0123to0011( c, c ); \
4862 stbir__simdf_mult_mem( tot, c, decode );
4863
4864#define stbir__3_coeff_only() \
4865 stbir__simdf tot,c,cs,d; \
4866 STBIR_SIMD_NO_UNROLL(decode); \
4867 stbir__simdf_load( cs, hc ); \
4868 stbir__simdf_0123to0011( c, cs ); \
4869 stbir__simdf_mult_mem( tot, c, decode ); \
4870 stbir__simdf_0123to2222( c, cs ); \
4871 stbir__simdf_load2z( d, decode+4 ); \
4872 stbir__simdf_madd( tot, tot, d, c );
4873
4874#define stbir__store_output_tiny() \
4875 stbir__simdf_0123to2301( c, tot ); \
4876 stbir__simdf_add( tot, tot, c ); \
4877 stbir__simdf_store2( output, tot ); \
4878 horizontal_coefficients += coefficient_width; \
4879 ++horizontal_contributors; \
4880 output += 2;
4881
4882#ifdef STBIR_SIMD8
4883
4884#define stbir__4_coeff_start() \
4885 stbir__simdf8 tot0,c,cs; \
4886 STBIR_SIMD_NO_UNROLL(decode); \
4887 stbir__simdf8_load4b( cs, hc ); \
4888 stbir__simdf8_0123to00112233( c, cs ); \
4889 stbir__simdf8_mult_mem( tot0, c, decode );
4890
4891#define stbir__4_coeff_continue_from_4( ofs ) \
4892 STBIR_SIMD_NO_UNROLL(decode); \
4893 stbir__simdf8_load4b( cs, hc + (ofs) ); \
4894 stbir__simdf8_0123to00112233( c, cs ); \
4895 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*2 );
4896
4897#define stbir__1_coeff_remnant( ofs ) \
4898 { stbir__simdf t,d; \
4899 stbir__simdf_load1z( t, hc + (ofs) ); \
4900 stbir__simdf_load2( d, decode + (ofs) * 2 ); \
4901 stbir__simdf_0123to0011( t, t ); \
4902 stbir__simdf_mult( t, t, d ); \
4903 stbir__simdf8_add4( tot0, tot0, t ); }
4904
4905#define stbir__2_coeff_remnant( ofs ) \
4906 { stbir__simdf t; \
4907 stbir__simdf_load2( t, hc + (ofs) ); \
4908 stbir__simdf_0123to0011( t, t ); \
4909 stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \
4910 stbir__simdf8_add4( tot0, tot0, t ); }
4911
4912#define stbir__3_coeff_remnant( ofs ) \
4913 { stbir__simdf8 d; \
4914 stbir__simdf8_load4b( cs, hc + (ofs) ); \
4915 stbir__simdf8_0123to00112233( c, cs ); \
4916 stbir__simdf8_load6z( d, decode+(ofs)*2 ); \
4917 stbir__simdf8_madd( tot0, tot0, c, d ); }
4918
4919#define stbir__store_output() \
4920 { stbir__simdf t,d; \
4921 stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \
4922 stbir__simdf_0123to2301( d, t ); \
4923 stbir__simdf_add( t, t, d ); \
4924 stbir__simdf_store2( output, t ); \
4925 horizontal_coefficients += coefficient_width; \
4926 ++horizontal_contributors; \
4927 output += 2; }
4928
4929#else
4930
4931#define stbir__4_coeff_start() \
4932 stbir__simdf tot0,tot1,c,cs; \
4933 STBIR_SIMD_NO_UNROLL(decode); \
4934 stbir__simdf_load( cs, hc ); \
4935 stbir__simdf_0123to0011( c, cs ); \
4936 stbir__simdf_mult_mem( tot0, c, decode ); \
4937 stbir__simdf_0123to2233( c, cs ); \
4938 stbir__simdf_mult_mem( tot1, c, decode+4 );
4939
4940#define stbir__4_coeff_continue_from_4( ofs ) \
4941 STBIR_SIMD_NO_UNROLL(decode); \
4942 stbir__simdf_load( cs, hc + (ofs) ); \
4943 stbir__simdf_0123to0011( c, cs ); \
4944 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \
4945 stbir__simdf_0123to2233( c, cs ); \
4946 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*2+4 );
4947
4948#define stbir__1_coeff_remnant( ofs ) \
4949 { stbir__simdf d; \
4950 stbir__simdf_load1z( cs, hc + (ofs) ); \
4951 stbir__simdf_0123to0011( c, cs ); \
4952 stbir__simdf_load2( d, decode + (ofs) * 2 ); \
4953 stbir__simdf_madd( tot0, tot0, d, c ); }
4954
4955#define stbir__2_coeff_remnant( ofs ) \
4956 stbir__simdf_load2( cs, hc + (ofs) ); \
4957 stbir__simdf_0123to0011( c, cs ); \
4958 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 );
4959
4960#define stbir__3_coeff_remnant( ofs ) \
4961 { stbir__simdf d; \
4962 stbir__simdf_load( cs, hc + (ofs) ); \
4963 stbir__simdf_0123to0011( c, cs ); \
4964 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \
4965 stbir__simdf_0123to2222( c, cs ); \
4966 stbir__simdf_load2z( d, decode + (ofs) * 2 + 4 ); \
4967 stbir__simdf_madd( tot1, tot1, d, c ); }
4968
4969#define stbir__store_output() \
4970 stbir__simdf_add( tot0, tot0, tot1 ); \
4971 stbir__simdf_0123to2301( c, tot0 ); \
4972 stbir__simdf_add( tot0, tot0, c ); \
4973 stbir__simdf_store2( output, tot0 ); \
4974 horizontal_coefficients += coefficient_width; \
4975 ++horizontal_contributors; \
4976 output += 2;
4977
4978#endif
4979
4980#else
4981
4982#define stbir__1_coeff_only() \
4983 float tota,totb,c; \
4984 c = hc[0]; \
4985 tota = decode[0]*c; \
4986 totb = decode[1]*c;
4987
4988#define stbir__2_coeff_only() \
4989 float tota,totb,c; \
4990 c = hc[0]; \
4991 tota = decode[0]*c; \
4992 totb = decode[1]*c; \
4993 c = hc[1]; \
4994 tota += decode[2]*c; \
4995 totb += decode[3]*c;
4996
4997// this weird order of add matches the simd
4998#define stbir__3_coeff_only() \
4999 float tota,totb,c; \
5000 c = hc[0]; \
5001 tota = decode[0]*c; \
5002 totb = decode[1]*c; \
5003 c = hc[2]; \
5004 tota += decode[4]*c; \
5005 totb += decode[5]*c; \
5006 c = hc[1]; \
5007 tota += decode[2]*c; \
5008 totb += decode[3]*c;
5009
5010#define stbir__store_output_tiny() \
5011 output[0] = tota; \
5012 output[1] = totb; \
5013 horizontal_coefficients += coefficient_width; \
5014 ++horizontal_contributors; \
5015 output += 2;
5016
5017#define stbir__4_coeff_start() \
5018 float tota0,tota1,tota2,tota3,totb0,totb1,totb2,totb3,c; \
5019 c = hc[0]; \
5020 tota0 = decode[0]*c; \
5021 totb0 = decode[1]*c; \
5022 c = hc[1]; \
5023 tota1 = decode[2]*c; \
5024 totb1 = decode[3]*c; \
5025 c = hc[2]; \
5026 tota2 = decode[4]*c; \
5027 totb2 = decode[5]*c; \
5028 c = hc[3]; \
5029 tota3 = decode[6]*c; \
5030 totb3 = decode[7]*c;
5031
5032#define stbir__4_coeff_continue_from_4( ofs ) \
5033 c = hc[0+(ofs)]; \
5034 tota0 += decode[0+(ofs)*2]*c; \
5035 totb0 += decode[1+(ofs)*2]*c; \
5036 c = hc[1+(ofs)]; \
5037 tota1 += decode[2+(ofs)*2]*c; \
5038 totb1 += decode[3+(ofs)*2]*c; \
5039 c = hc[2+(ofs)]; \
5040 tota2 += decode[4+(ofs)*2]*c; \
5041 totb2 += decode[5+(ofs)*2]*c; \
5042 c = hc[3+(ofs)]; \
5043 tota3 += decode[6+(ofs)*2]*c; \
5044 totb3 += decode[7+(ofs)*2]*c;
5045
5046#define stbir__1_coeff_remnant( ofs ) \
5047 c = hc[0+(ofs)]; \
5048 tota0 += decode[0+(ofs)*2] * c; \
5049 totb0 += decode[1+(ofs)*2] * c;
5050
5051#define stbir__2_coeff_remnant( ofs ) \
5052 c = hc[0+(ofs)]; \
5053 tota0 += decode[0+(ofs)*2] * c; \
5054 totb0 += decode[1+(ofs)*2] * c; \
5055 c = hc[1+(ofs)]; \
5056 tota1 += decode[2+(ofs)*2] * c; \
5057 totb1 += decode[3+(ofs)*2] * c;
5058
5059#define stbir__3_coeff_remnant( ofs ) \
5060 c = hc[0+(ofs)]; \
5061 tota0 += decode[0+(ofs)*2] * c; \
5062 totb0 += decode[1+(ofs)*2] * c; \
5063 c = hc[1+(ofs)]; \
5064 tota1 += decode[2+(ofs)*2] * c; \
5065 totb1 += decode[3+(ofs)*2] * c; \
5066 c = hc[2+(ofs)]; \
5067 tota2 += decode[4+(ofs)*2] * c; \
5068 totb2 += decode[5+(ofs)*2] * c;
5069
5070#define stbir__store_output() \
5071 output[0] = (tota0+tota2)+(tota1+tota3); \
5072 output[1] = (totb0+totb2)+(totb1+totb3); \
5073 horizontal_coefficients += coefficient_width; \
5074 ++horizontal_contributors; \
5075 output += 2;
5076
5077#endif
5078
5079#define STBIR__horizontal_channels 2
5080#define STB_IMAGE_RESIZE_DO_HORIZONTALS
5081#include STBIR__HEADER_FILENAME
5082
5083
5084//=================
5085// Do 3 channel horizontal routines
5086
5087#ifdef STBIR_SIMD
5088
5089#define stbir__1_coeff_only() \
5090 stbir__simdf tot,c,d; \
5091 STBIR_SIMD_NO_UNROLL(decode); \
5092 stbir__simdf_load1z( c, hc ); \
5093 stbir__simdf_0123to0001( c, c ); \
5094 stbir__simdf_load( d, decode ); \
5095 stbir__simdf_mult( tot, d, c );
5096
5097#define stbir__2_coeff_only() \
5098 stbir__simdf tot,c,cs,d; \
5099 STBIR_SIMD_NO_UNROLL(decode); \
5100 stbir__simdf_load2( cs, hc ); \
5101 stbir__simdf_0123to0000( c, cs ); \
5102 stbir__simdf_load( d, decode ); \
5103 stbir__simdf_mult( tot, d, c ); \
5104 stbir__simdf_0123to1111( c, cs ); \
5105 stbir__simdf_load( d, decode+3 ); \
5106 stbir__simdf_madd( tot, tot, d, c );
5107
5108#define stbir__3_coeff_only() \
5109 stbir__simdf tot,c,d,cs; \
5110 STBIR_SIMD_NO_UNROLL(decode); \
5111 stbir__simdf_load( cs, hc ); \
5112 stbir__simdf_0123to0000( c, cs ); \
5113 stbir__simdf_load( d, decode ); \
5114 stbir__simdf_mult( tot, d, c ); \
5115 stbir__simdf_0123to1111( c, cs ); \
5116 stbir__simdf_load( d, decode+3 ); \
5117 stbir__simdf_madd( tot, tot, d, c ); \
5118 stbir__simdf_0123to2222( c, cs ); \
5119 stbir__simdf_load( d, decode+6 ); \
5120 stbir__simdf_madd( tot, tot, d, c );
5121
5122#define stbir__store_output_tiny() \
5123 stbir__simdf_store2( output, tot ); \
5124 stbir__simdf_0123to2301( tot, tot ); \
5125 stbir__simdf_store1( output+2, tot ); \
5126 horizontal_coefficients += coefficient_width; \
5127 ++horizontal_contributors; \
5128 output += 3;
5129
5130#ifdef STBIR_SIMD8
5131
5132// we're loading from the XXXYYY decode by -1 to get the XXXYYY into different halves of the AVX reg fyi
5133#define stbir__4_coeff_start() \
5134 stbir__simdf8 tot0,tot1,c,cs; stbir__simdf t; \
5135 STBIR_SIMD_NO_UNROLL(decode); \
5136 stbir__simdf8_load4b( cs, hc ); \
5137 stbir__simdf8_0123to00001111( c, cs ); \
5138 stbir__simdf8_mult_mem( tot0, c, decode - 1 ); \
5139 stbir__simdf8_0123to22223333( c, cs ); \
5140 stbir__simdf8_mult_mem( tot1, c, decode+6 - 1 );
5141
5142#define stbir__4_coeff_continue_from_4( ofs ) \
5143 STBIR_SIMD_NO_UNROLL(decode); \
5144 stbir__simdf8_load4b( cs, hc + (ofs) ); \
5145 stbir__simdf8_0123to00001111( c, cs ); \
5146 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \
5147 stbir__simdf8_0123to22223333( c, cs ); \
5148 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*3 + 6 - 1 );
5149
5150#define stbir__1_coeff_remnant( ofs ) \
5151 STBIR_SIMD_NO_UNROLL(decode); \
5152 stbir__simdf_load1rep4( t, hc + (ofs) ); \
5153 stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*3 - 1 );
5154
5155#define stbir__2_coeff_remnant( ofs ) \
5156 STBIR_SIMD_NO_UNROLL(decode); \
5157 stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \
5158 stbir__simdf8_0123to22223333( c, cs ); \
5159 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 );
5160
5161 #define stbir__3_coeff_remnant( ofs ) \
5162 STBIR_SIMD_NO_UNROLL(decode); \
5163 stbir__simdf8_load4b( cs, hc + (ofs) ); \
5164 stbir__simdf8_0123to00001111( c, cs ); \
5165 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \
5166 stbir__simdf8_0123to2222( t, cs ); \
5167 stbir__simdf8_madd_mem4( tot1, tot1, t, decode+(ofs)*3 + 6 - 1 );
5168
5169#define stbir__store_output() \
5170 stbir__simdf8_add( tot0, tot0, tot1 ); \
5171 stbir__simdf_0123to1230( t, stbir__if_simdf8_cast_to_simdf4( tot0 ) ); \
5172 stbir__simdf8_add4halves( t, t, tot0 ); \
5173 horizontal_coefficients += coefficient_width; \
5174 ++horizontal_contributors; \
5175 output += 3; \
5176 if ( output < output_end ) \
5177 { \
5178 stbir__simdf_store( output-3, t ); \
5179 continue; \
5180 } \
5181 { stbir__simdf tt; stbir__simdf_0123to2301( tt, t ); \
5182 stbir__simdf_store2( output-3, t ); \
5183 stbir__simdf_store1( output+2-3, tt ); } \
5184 break;
5185
5186
5187#else
5188
5189#define stbir__4_coeff_start() \
5190 stbir__simdf tot0,tot1,tot2,c,cs; \
5191 STBIR_SIMD_NO_UNROLL(decode); \
5192 stbir__simdf_load( cs, hc ); \
5193 stbir__simdf_0123to0001( c, cs ); \
5194 stbir__simdf_mult_mem( tot0, c, decode ); \
5195 stbir__simdf_0123to1122( c, cs ); \
5196 stbir__simdf_mult_mem( tot1, c, decode+4 ); \
5197 stbir__simdf_0123to2333( c, cs ); \
5198 stbir__simdf_mult_mem( tot2, c, decode+8 );
5199
5200#define stbir__4_coeff_continue_from_4( ofs ) \
5201 STBIR_SIMD_NO_UNROLL(decode); \
5202 stbir__simdf_load( cs, hc + (ofs) ); \
5203 stbir__simdf_0123to0001( c, cs ); \
5204 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \
5205 stbir__simdf_0123to1122( c, cs ); \
5206 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \
5207 stbir__simdf_0123to2333( c, cs ); \
5208 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*3+8 );
5209
5210#define stbir__1_coeff_remnant( ofs ) \
5211 STBIR_SIMD_NO_UNROLL(decode); \
5212 stbir__simdf_load1z( c, hc + (ofs) ); \
5213 stbir__simdf_0123to0001( c, c ); \
5214 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 );
5215
5216#define stbir__2_coeff_remnant( ofs ) \
5217 { stbir__simdf d; \
5218 STBIR_SIMD_NO_UNROLL(decode); \
5219 stbir__simdf_load2z( cs, hc + (ofs) ); \
5220 stbir__simdf_0123to0001( c, cs ); \
5221 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \
5222 stbir__simdf_0123to1122( c, cs ); \
5223 stbir__simdf_load2z( d, decode+(ofs)*3+4 ); \
5224 stbir__simdf_madd( tot1, tot1, c, d ); }
5225
5226#define stbir__3_coeff_remnant( ofs ) \
5227 { stbir__simdf d; \
5228 STBIR_SIMD_NO_UNROLL(decode); \
5229 stbir__simdf_load( cs, hc + (ofs) ); \
5230 stbir__simdf_0123to0001( c, cs ); \
5231 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \
5232 stbir__simdf_0123to1122( c, cs ); \
5233 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \
5234 stbir__simdf_0123to2222( c, cs ); \
5235 stbir__simdf_load1z( d, decode+(ofs)*3+8 ); \
5236 stbir__simdf_madd( tot2, tot2, c, d ); }
5237
5238#define stbir__store_output() \
5239 stbir__simdf_0123ABCDto3ABx( c, tot0, tot1 ); \
5240 stbir__simdf_0123ABCDto23Ax( cs, tot1, tot2 ); \
5241 stbir__simdf_0123to1230( tot2, tot2 ); \
5242 stbir__simdf_add( tot0, tot0, cs ); \
5243 stbir__simdf_add( c, c, tot2 ); \
5244 stbir__simdf_add( tot0, tot0, c ); \
5245 horizontal_coefficients += coefficient_width; \
5246 ++horizontal_contributors; \
5247 output += 3; \
5248 if ( output < output_end ) \
5249 { \
5250 stbir__simdf_store( output-3, tot0 ); \
5251 continue; \
5252 } \
5253 stbir__simdf_0123to2301( tot1, tot0 ); \
5254 stbir__simdf_store2( output-3, tot0 ); \
5255 stbir__simdf_store1( output+2-3, tot1 ); \
5256 break;
5257
5258#endif
5259
5260#else
5261
5262#define stbir__1_coeff_only() \
5263 float tot0, tot1, tot2, c; \
5264 c = hc[0]; \
5265 tot0 = decode[0]*c; \
5266 tot1 = decode[1]*c; \
5267 tot2 = decode[2]*c;
5268
5269#define stbir__2_coeff_only() \
5270 float tot0, tot1, tot2, c; \
5271 c = hc[0]; \
5272 tot0 = decode[0]*c; \
5273 tot1 = decode[1]*c; \
5274 tot2 = decode[2]*c; \
5275 c = hc[1]; \
5276 tot0 += decode[3]*c; \
5277 tot1 += decode[4]*c; \
5278 tot2 += decode[5]*c;
5279
5280#define stbir__3_coeff_only() \
5281 float tot0, tot1, tot2, c; \
5282 c = hc[0]; \
5283 tot0 = decode[0]*c; \
5284 tot1 = decode[1]*c; \
5285 tot2 = decode[2]*c; \
5286 c = hc[1]; \
5287 tot0 += decode[3]*c; \
5288 tot1 += decode[4]*c; \
5289 tot2 += decode[5]*c; \
5290 c = hc[2]; \
5291 tot0 += decode[6]*c; \
5292 tot1 += decode[7]*c; \
5293 tot2 += decode[8]*c;
5294
5295#define stbir__store_output_tiny() \
5296 output[0] = tot0; \
5297 output[1] = tot1; \
5298 output[2] = tot2; \
5299 horizontal_coefficients += coefficient_width; \
5300 ++horizontal_contributors; \
5301 output += 3;
5302
5303#define stbir__4_coeff_start() \
5304 float tota0,tota1,tota2,totb0,totb1,totb2,totc0,totc1,totc2,totd0,totd1,totd2,c; \
5305 c = hc[0]; \
5306 tota0 = decode[0]*c; \
5307 tota1 = decode[1]*c; \
5308 tota2 = decode[2]*c; \
5309 c = hc[1]; \
5310 totb0 = decode[3]*c; \
5311 totb1 = decode[4]*c; \
5312 totb2 = decode[5]*c; \
5313 c = hc[2]; \
5314 totc0 = decode[6]*c; \
5315 totc1 = decode[7]*c; \
5316 totc2 = decode[8]*c; \
5317 c = hc[3]; \
5318 totd0 = decode[9]*c; \
5319 totd1 = decode[10]*c; \
5320 totd2 = decode[11]*c;
5321
5322#define stbir__4_coeff_continue_from_4( ofs ) \
5323 c = hc[0+(ofs)]; \
5324 tota0 += decode[0+(ofs)*3]*c; \
5325 tota1 += decode[1+(ofs)*3]*c; \
5326 tota2 += decode[2+(ofs)*3]*c; \
5327 c = hc[1+(ofs)]; \
5328 totb0 += decode[3+(ofs)*3]*c; \
5329 totb1 += decode[4+(ofs)*3]*c; \
5330 totb2 += decode[5+(ofs)*3]*c; \
5331 c = hc[2+(ofs)]; \
5332 totc0 += decode[6+(ofs)*3]*c; \
5333 totc1 += decode[7+(ofs)*3]*c; \
5334 totc2 += decode[8+(ofs)*3]*c; \
5335 c = hc[3+(ofs)]; \
5336 totd0 += decode[9+(ofs)*3]*c; \
5337 totd1 += decode[10+(ofs)*3]*c; \
5338 totd2 += decode[11+(ofs)*3]*c;
5339
5340#define stbir__1_coeff_remnant( ofs ) \
5341 c = hc[0+(ofs)]; \
5342 tota0 += decode[0+(ofs)*3]*c; \
5343 tota1 += decode[1+(ofs)*3]*c; \
5344 tota2 += decode[2+(ofs)*3]*c;
5345
5346#define stbir__2_coeff_remnant( ofs ) \
5347 c = hc[0+(ofs)]; \
5348 tota0 += decode[0+(ofs)*3]*c; \
5349 tota1 += decode[1+(ofs)*3]*c; \
5350 tota2 += decode[2+(ofs)*3]*c; \
5351 c = hc[1+(ofs)]; \
5352 totb0 += decode[3+(ofs)*3]*c; \
5353 totb1 += decode[4+(ofs)*3]*c; \
5354 totb2 += decode[5+(ofs)*3]*c; \
5355
5356#define stbir__3_coeff_remnant( ofs ) \
5357 c = hc[0+(ofs)]; \
5358 tota0 += decode[0+(ofs)*3]*c; \
5359 tota1 += decode[1+(ofs)*3]*c; \
5360 tota2 += decode[2+(ofs)*3]*c; \
5361 c = hc[1+(ofs)]; \
5362 totb0 += decode[3+(ofs)*3]*c; \
5363 totb1 += decode[4+(ofs)*3]*c; \
5364 totb2 += decode[5+(ofs)*3]*c; \
5365 c = hc[2+(ofs)]; \
5366 totc0 += decode[6+(ofs)*3]*c; \
5367 totc1 += decode[7+(ofs)*3]*c; \
5368 totc2 += decode[8+(ofs)*3]*c;
5369
5370#define stbir__store_output() \
5371 output[0] = (tota0+totc0)+(totb0+totd0); \
5372 output[1] = (tota1+totc1)+(totb1+totd1); \
5373 output[2] = (tota2+totc2)+(totb2+totd2); \
5374 horizontal_coefficients += coefficient_width; \
5375 ++horizontal_contributors; \
5376 output += 3;
5377
5378#endif
5379
5380#define STBIR__horizontal_channels 3
5381#define STB_IMAGE_RESIZE_DO_HORIZONTALS
5382#include STBIR__HEADER_FILENAME
5383
5384//=================
5385// Do 4 channel horizontal routines
5386
5387#ifdef STBIR_SIMD
5388
5389#define stbir__1_coeff_only() \
5390 stbir__simdf tot,c; \
5391 STBIR_SIMD_NO_UNROLL(decode); \
5392 stbir__simdf_load1( c, hc ); \
5393 stbir__simdf_0123to0000( c, c ); \
5394 stbir__simdf_mult_mem( tot, c, decode );
5395
5396#define stbir__2_coeff_only() \
5397 stbir__simdf tot,c,cs; \
5398 STBIR_SIMD_NO_UNROLL(decode); \
5399 stbir__simdf_load2( cs, hc ); \
5400 stbir__simdf_0123to0000( c, cs ); \
5401 stbir__simdf_mult_mem( tot, c, decode ); \
5402 stbir__simdf_0123to1111( c, cs ); \
5403 stbir__simdf_madd_mem( tot, tot, c, decode+4 );
5404
5405#define stbir__3_coeff_only() \
5406 stbir__simdf tot,c,cs; \
5407 STBIR_SIMD_NO_UNROLL(decode); \
5408 stbir__simdf_load( cs, hc ); \
5409 stbir__simdf_0123to0000( c, cs ); \
5410 stbir__simdf_mult_mem( tot, c, decode ); \
5411 stbir__simdf_0123to1111( c, cs ); \
5412 stbir__simdf_madd_mem( tot, tot, c, decode+4 ); \
5413 stbir__simdf_0123to2222( c, cs ); \
5414 stbir__simdf_madd_mem( tot, tot, c, decode+8 );
5415
5416#define stbir__store_output_tiny() \
5417 stbir__simdf_store( output, tot ); \
5418 horizontal_coefficients += coefficient_width; \
5419 ++horizontal_contributors; \
5420 output += 4;
5421
5422#ifdef STBIR_SIMD8
5423
5424#define stbir__4_coeff_start() \
5425 stbir__simdf8 tot0,c,cs; stbir__simdf t; \
5426 STBIR_SIMD_NO_UNROLL(decode); \
5427 stbir__simdf8_load4b( cs, hc ); \
5428 stbir__simdf8_0123to00001111( c, cs ); \
5429 stbir__simdf8_mult_mem( tot0, c, decode ); \
5430 stbir__simdf8_0123to22223333( c, cs ); \
5431 stbir__simdf8_madd_mem( tot0, tot0, c, decode+8 );
5432
5433#define stbir__4_coeff_continue_from_4( ofs ) \
5434 STBIR_SIMD_NO_UNROLL(decode); \
5435 stbir__simdf8_load4b( cs, hc + (ofs) ); \
5436 stbir__simdf8_0123to00001111( c, cs ); \
5437 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
5438 stbir__simdf8_0123to22223333( c, cs ); \
5439 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 );
5440
5441#define stbir__1_coeff_remnant( ofs ) \
5442 STBIR_SIMD_NO_UNROLL(decode); \
5443 stbir__simdf_load1rep4( t, hc + (ofs) ); \
5444 stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4 );
5445
5446#define stbir__2_coeff_remnant( ofs ) \
5447 STBIR_SIMD_NO_UNROLL(decode); \
5448 stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \
5449 stbir__simdf8_0123to22223333( c, cs ); \
5450 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 );
5451
5452 #define stbir__3_coeff_remnant( ofs ) \
5453 STBIR_SIMD_NO_UNROLL(decode); \
5454 stbir__simdf8_load4b( cs, hc + (ofs) ); \
5455 stbir__simdf8_0123to00001111( c, cs ); \
5456 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
5457 stbir__simdf8_0123to2222( t, cs ); \
5458 stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4+8 );
5459
5460#define stbir__store_output() \
5461 stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \
5462 stbir__simdf_store( output, t ); \
5463 horizontal_coefficients += coefficient_width; \
5464 ++horizontal_contributors; \
5465 output += 4;
5466
5467#else
5468
5469#define stbir__4_coeff_start() \
5470 stbir__simdf tot0,tot1,c,cs; \
5471 STBIR_SIMD_NO_UNROLL(decode); \
5472 stbir__simdf_load( cs, hc ); \
5473 stbir__simdf_0123to0000( c, cs ); \
5474 stbir__simdf_mult_mem( tot0, c, decode ); \
5475 stbir__simdf_0123to1111( c, cs ); \
5476 stbir__simdf_mult_mem( tot1, c, decode+4 ); \
5477 stbir__simdf_0123to2222( c, cs ); \
5478 stbir__simdf_madd_mem( tot0, tot0, c, decode+8 ); \
5479 stbir__simdf_0123to3333( c, cs ); \
5480 stbir__simdf_madd_mem( tot1, tot1, c, decode+12 );
5481
5482#define stbir__4_coeff_continue_from_4( ofs ) \
5483 STBIR_SIMD_NO_UNROLL(decode); \
5484 stbir__simdf_load( cs, hc + (ofs) ); \
5485 stbir__simdf_0123to0000( c, cs ); \
5486 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
5487 stbir__simdf_0123to1111( c, cs ); \
5488 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \
5489 stbir__simdf_0123to2222( c, cs ); \
5490 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); \
5491 stbir__simdf_0123to3333( c, cs ); \
5492 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+12 );
5493
5494#define stbir__1_coeff_remnant( ofs ) \
5495 STBIR_SIMD_NO_UNROLL(decode); \
5496 stbir__simdf_load1( c, hc + (ofs) ); \
5497 stbir__simdf_0123to0000( c, c ); \
5498 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 );
5499
5500#define stbir__2_coeff_remnant( ofs ) \
5501 STBIR_SIMD_NO_UNROLL(decode); \
5502 stbir__simdf_load2( cs, hc + (ofs) ); \
5503 stbir__simdf_0123to0000( c, cs ); \
5504 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
5505 stbir__simdf_0123to1111( c, cs ); \
5506 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 );
5507
5508#define stbir__3_coeff_remnant( ofs ) \
5509 STBIR_SIMD_NO_UNROLL(decode); \
5510 stbir__simdf_load( cs, hc + (ofs) ); \
5511 stbir__simdf_0123to0000( c, cs ); \
5512 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \
5513 stbir__simdf_0123to1111( c, cs ); \
5514 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \
5515 stbir__simdf_0123to2222( c, cs ); \
5516 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 );
5517
5518#define stbir__store_output() \
5519 stbir__simdf_add( tot0, tot0, tot1 ); \
5520 stbir__simdf_store( output, tot0 ); \
5521 horizontal_coefficients += coefficient_width; \
5522 ++horizontal_contributors; \
5523 output += 4;
5524
5525#endif
5526
5527#else
5528
5529#define stbir__1_coeff_only() \
5530 float p0,p1,p2,p3,c; \
5531 STBIR_SIMD_NO_UNROLL(decode); \
5532 c = hc[0]; \
5533 p0 = decode[0] * c; \
5534 p1 = decode[1] * c; \
5535 p2 = decode[2] * c; \
5536 p3 = decode[3] * c;
5537
5538#define stbir__2_coeff_only() \
5539 float p0,p1,p2,p3,c; \
5540 STBIR_SIMD_NO_UNROLL(decode); \
5541 c = hc[0]; \
5542 p0 = decode[0] * c; \
5543 p1 = decode[1] * c; \
5544 p2 = decode[2] * c; \
5545 p3 = decode[3] * c; \
5546 c = hc[1]; \
5547 p0 += decode[4] * c; \
5548 p1 += decode[5] * c; \
5549 p2 += decode[6] * c; \
5550 p3 += decode[7] * c;
5551
5552#define stbir__3_coeff_only() \
5553 float p0,p1,p2,p3,c; \
5554 STBIR_SIMD_NO_UNROLL(decode); \
5555 c = hc[0]; \
5556 p0 = decode[0] * c; \
5557 p1 = decode[1] * c; \
5558 p2 = decode[2] * c; \
5559 p3 = decode[3] * c; \
5560 c = hc[1]; \
5561 p0 += decode[4] * c; \
5562 p1 += decode[5] * c; \
5563 p2 += decode[6] * c; \
5564 p3 += decode[7] * c; \
5565 c = hc[2]; \
5566 p0 += decode[8] * c; \
5567 p1 += decode[9] * c; \
5568 p2 += decode[10] * c; \
5569 p3 += decode[11] * c;
5570
5571#define stbir__store_output_tiny() \
5572 output[0] = p0; \
5573 output[1] = p1; \
5574 output[2] = p2; \
5575 output[3] = p3; \
5576 horizontal_coefficients += coefficient_width; \
5577 ++horizontal_contributors; \
5578 output += 4;
5579
5580#define stbir__4_coeff_start() \
5581 float x0,x1,x2,x3,y0,y1,y2,y3,c; \
5582 STBIR_SIMD_NO_UNROLL(decode); \
5583 c = hc[0]; \
5584 x0 = decode[0] * c; \
5585 x1 = decode[1] * c; \
5586 x2 = decode[2] * c; \
5587 x3 = decode[3] * c; \
5588 c = hc[1]; \
5589 y0 = decode[4] * c; \
5590 y1 = decode[5] * c; \
5591 y2 = decode[6] * c; \
5592 y3 = decode[7] * c; \
5593 c = hc[2]; \
5594 x0 += decode[8] * c; \
5595 x1 += decode[9] * c; \
5596 x2 += decode[10] * c; \
5597 x3 += decode[11] * c; \
5598 c = hc[3]; \
5599 y0 += decode[12] * c; \
5600 y1 += decode[13] * c; \
5601 y2 += decode[14] * c; \
5602 y3 += decode[15] * c;
5603
5604#define stbir__4_coeff_continue_from_4( ofs ) \
5605 STBIR_SIMD_NO_UNROLL(decode); \
5606 c = hc[0+(ofs)]; \
5607 x0 += decode[0+(ofs)*4] * c; \
5608 x1 += decode[1+(ofs)*4] * c; \
5609 x2 += decode[2+(ofs)*4] * c; \
5610 x3 += decode[3+(ofs)*4] * c; \
5611 c = hc[1+(ofs)]; \
5612 y0 += decode[4+(ofs)*4] * c; \
5613 y1 += decode[5+(ofs)*4] * c; \
5614 y2 += decode[6+(ofs)*4] * c; \
5615 y3 += decode[7+(ofs)*4] * c; \
5616 c = hc[2+(ofs)]; \
5617 x0 += decode[8+(ofs)*4] * c; \
5618 x1 += decode[9+(ofs)*4] * c; \
5619 x2 += decode[10+(ofs)*4] * c; \
5620 x3 += decode[11+(ofs)*4] * c; \
5621 c = hc[3+(ofs)]; \
5622 y0 += decode[12+(ofs)*4] * c; \
5623 y1 += decode[13+(ofs)*4] * c; \
5624 y2 += decode[14+(ofs)*4] * c; \
5625 y3 += decode[15+(ofs)*4] * c;
5626
5627#define stbir__1_coeff_remnant( ofs ) \
5628 STBIR_SIMD_NO_UNROLL(decode); \
5629 c = hc[0+(ofs)]; \
5630 x0 += decode[0+(ofs)*4] * c; \
5631 x1 += decode[1+(ofs)*4] * c; \
5632 x2 += decode[2+(ofs)*4] * c; \
5633 x3 += decode[3+(ofs)*4] * c;
5634
5635#define stbir__2_coeff_remnant( ofs ) \
5636 STBIR_SIMD_NO_UNROLL(decode); \
5637 c = hc[0+(ofs)]; \
5638 x0 += decode[0+(ofs)*4] * c; \
5639 x1 += decode[1+(ofs)*4] * c; \
5640 x2 += decode[2+(ofs)*4] * c; \
5641 x3 += decode[3+(ofs)*4] * c; \
5642 c = hc[1+(ofs)]; \
5643 y0 += decode[4+(ofs)*4] * c; \
5644 y1 += decode[5+(ofs)*4] * c; \
5645 y2 += decode[6+(ofs)*4] * c; \
5646 y3 += decode[7+(ofs)*4] * c;
5647
5648#define stbir__3_coeff_remnant( ofs ) \
5649 STBIR_SIMD_NO_UNROLL(decode); \
5650 c = hc[0+(ofs)]; \
5651 x0 += decode[0+(ofs)*4] * c; \
5652 x1 += decode[1+(ofs)*4] * c; \
5653 x2 += decode[2+(ofs)*4] * c; \
5654 x3 += decode[3+(ofs)*4] * c; \
5655 c = hc[1+(ofs)]; \
5656 y0 += decode[4+(ofs)*4] * c; \
5657 y1 += decode[5+(ofs)*4] * c; \
5658 y2 += decode[6+(ofs)*4] * c; \
5659 y3 += decode[7+(ofs)*4] * c; \
5660 c = hc[2+(ofs)]; \
5661 x0 += decode[8+(ofs)*4] * c; \
5662 x1 += decode[9+(ofs)*4] * c; \
5663 x2 += decode[10+(ofs)*4] * c; \
5664 x3 += decode[11+(ofs)*4] * c;
5665
5666#define stbir__store_output() \
5667 output[0] = x0 + y0; \
5668 output[1] = x1 + y1; \
5669 output[2] = x2 + y2; \
5670 output[3] = x3 + y3; \
5671 horizontal_coefficients += coefficient_width; \
5672 ++horizontal_contributors; \
5673 output += 4;
5674
5675#endif
5676
5677#define STBIR__horizontal_channels 4
5678#define STB_IMAGE_RESIZE_DO_HORIZONTALS
5679#include STBIR__HEADER_FILENAME
5680
5681
5682
5683//=================
5684// Do 7 channel horizontal routines
5685
5686#ifdef STBIR_SIMD
5687
5688#define stbir__1_coeff_only() \
5689 stbir__simdf tot0,tot1,c; \
5690 STBIR_SIMD_NO_UNROLL(decode); \
5691 stbir__simdf_load1( c, hc ); \
5692 stbir__simdf_0123to0000( c, c ); \
5693 stbir__simdf_mult_mem( tot0, c, decode ); \
5694 stbir__simdf_mult_mem( tot1, c, decode+3 );
5695
5696#define stbir__2_coeff_only() \
5697 stbir__simdf tot0,tot1,c,cs; \
5698 STBIR_SIMD_NO_UNROLL(decode); \
5699 stbir__simdf_load2( cs, hc ); \
5700 stbir__simdf_0123to0000( c, cs ); \
5701 stbir__simdf_mult_mem( tot0, c, decode ); \
5702 stbir__simdf_mult_mem( tot1, c, decode+3 ); \
5703 stbir__simdf_0123to1111( c, cs ); \
5704 stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \
5705 stbir__simdf_madd_mem( tot1, tot1, c,decode+10 );
5706
5707#define stbir__3_coeff_only() \
5708 stbir__simdf tot0,tot1,c,cs; \
5709 STBIR_SIMD_NO_UNROLL(decode); \
5710 stbir__simdf_load( cs, hc ); \
5711 stbir__simdf_0123to0000( c, cs ); \
5712 stbir__simdf_mult_mem( tot0, c, decode ); \
5713 stbir__simdf_mult_mem( tot1, c, decode+3 ); \
5714 stbir__simdf_0123to1111( c, cs ); \
5715 stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \
5716 stbir__simdf_madd_mem( tot1, tot1, c, decode+10 ); \
5717 stbir__simdf_0123to2222( c, cs ); \
5718 stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \
5719 stbir__simdf_madd_mem( tot1, tot1, c, decode+17 );
5720
5721#define stbir__store_output_tiny() \
5722 stbir__simdf_store( output+3, tot1 ); \
5723 stbir__simdf_store( output, tot0 ); \
5724 horizontal_coefficients += coefficient_width; \
5725 ++horizontal_contributors; \
5726 output += 7;
5727
5728#ifdef STBIR_SIMD8
5729
5730#define stbir__4_coeff_start() \
5731 stbir__simdf8 tot0,tot1,c,cs; \
5732 STBIR_SIMD_NO_UNROLL(decode); \
5733 stbir__simdf8_load4b( cs, hc ); \
5734 stbir__simdf8_0123to00000000( c, cs ); \
5735 stbir__simdf8_mult_mem( tot0, c, decode ); \
5736 stbir__simdf8_0123to11111111( c, cs ); \
5737 stbir__simdf8_mult_mem( tot1, c, decode+7 ); \
5738 stbir__simdf8_0123to22222222( c, cs ); \
5739 stbir__simdf8_madd_mem( tot0, tot0, c, decode+14 ); \
5740 stbir__simdf8_0123to33333333( c, cs ); \
5741 stbir__simdf8_madd_mem( tot1, tot1, c, decode+21 );
5742
5743#define stbir__4_coeff_continue_from_4( ofs ) \
5744 STBIR_SIMD_NO_UNROLL(decode); \
5745 stbir__simdf8_load4b( cs, hc + (ofs) ); \
5746 stbir__simdf8_0123to00000000( c, cs ); \
5747 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
5748 stbir__simdf8_0123to11111111( c, cs ); \
5749 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \
5750 stbir__simdf8_0123to22222222( c, cs ); \
5751 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \
5752 stbir__simdf8_0123to33333333( c, cs ); \
5753 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+21 );
5754
5755#define stbir__1_coeff_remnant( ofs ) \
5756 STBIR_SIMD_NO_UNROLL(decode); \
5757 stbir__simdf8_load1b( c, hc + (ofs) ); \
5758 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 );
5759
5760#define stbir__2_coeff_remnant( ofs ) \
5761 STBIR_SIMD_NO_UNROLL(decode); \
5762 stbir__simdf8_load1b( c, hc + (ofs) ); \
5763 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
5764 stbir__simdf8_load1b( c, hc + (ofs)+1 ); \
5765 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 );
5766
5767#define stbir__3_coeff_remnant( ofs ) \
5768 STBIR_SIMD_NO_UNROLL(decode); \
5769 stbir__simdf8_load4b( cs, hc + (ofs) ); \
5770 stbir__simdf8_0123to00000000( c, cs ); \
5771 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
5772 stbir__simdf8_0123to11111111( c, cs ); \
5773 stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \
5774 stbir__simdf8_0123to22222222( c, cs ); \
5775 stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 );
5776
5777#define stbir__store_output() \
5778 stbir__simdf8_add( tot0, tot0, tot1 ); \
5779 horizontal_coefficients += coefficient_width; \
5780 ++horizontal_contributors; \
5781 output += 7; \
5782 if ( output < output_end ) \
5783 { \
5784 stbir__simdf8_store( output-7, tot0 ); \
5785 continue; \
5786 } \
5787 stbir__simdf_store( output-7+3, stbir__simdf_swiz(stbir__simdf8_gettop4(tot0),0,0,1,2) ); \
5788 stbir__simdf_store( output-7, stbir__if_simdf8_cast_to_simdf4(tot0) ); \
5789 break;
5790
5791#else
5792
5793#define stbir__4_coeff_start() \
5794 stbir__simdf tot0,tot1,tot2,tot3,c,cs; \
5795 STBIR_SIMD_NO_UNROLL(decode); \
5796 stbir__simdf_load( cs, hc ); \
5797 stbir__simdf_0123to0000( c, cs ); \
5798 stbir__simdf_mult_mem( tot0, c, decode ); \
5799 stbir__simdf_mult_mem( tot1, c, decode+3 ); \
5800 stbir__simdf_0123to1111( c, cs ); \
5801 stbir__simdf_mult_mem( tot2, c, decode+7 ); \
5802 stbir__simdf_mult_mem( tot3, c, decode+10 ); \
5803 stbir__simdf_0123to2222( c, cs ); \
5804 stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \
5805 stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); \
5806 stbir__simdf_0123to3333( c, cs ); \
5807 stbir__simdf_madd_mem( tot2, tot2, c, decode+21 ); \
5808 stbir__simdf_madd_mem( tot3, tot3, c, decode+24 );
5809
5810#define stbir__4_coeff_continue_from_4( ofs ) \
5811 STBIR_SIMD_NO_UNROLL(decode); \
5812 stbir__simdf_load( cs, hc + (ofs) ); \
5813 stbir__simdf_0123to0000( c, cs ); \
5814 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
5815 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \
5816 stbir__simdf_0123to1111( c, cs ); \
5817 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \
5818 stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \
5819 stbir__simdf_0123to2222( c, cs ); \
5820 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \
5821 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); \
5822 stbir__simdf_0123to3333( c, cs ); \
5823 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+21 ); \
5824 stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+24 );
5825
5826#define stbir__1_coeff_remnant( ofs ) \
5827 STBIR_SIMD_NO_UNROLL(decode); \
5828 stbir__simdf_load1( c, hc + (ofs) ); \
5829 stbir__simdf_0123to0000( c, c ); \
5830 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
5831 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \
5832
5833#define stbir__2_coeff_remnant( ofs ) \
5834 STBIR_SIMD_NO_UNROLL(decode); \
5835 stbir__simdf_load2( cs, hc + (ofs) ); \
5836 stbir__simdf_0123to0000( c, cs ); \
5837 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
5838 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \
5839 stbir__simdf_0123to1111( c, cs ); \
5840 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \
5841 stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 );
5842
5843#define stbir__3_coeff_remnant( ofs ) \
5844 STBIR_SIMD_NO_UNROLL(decode); \
5845 stbir__simdf_load( cs, hc + (ofs) ); \
5846 stbir__simdf_0123to0000( c, cs ); \
5847 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \
5848 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \
5849 stbir__simdf_0123to1111( c, cs ); \
5850 stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \
5851 stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \
5852 stbir__simdf_0123to2222( c, cs ); \
5853 stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \
5854 stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 );
5855
5856#define stbir__store_output() \
5857 stbir__simdf_add( tot0, tot0, tot2 ); \
5858 stbir__simdf_add( tot1, tot1, tot3 ); \
5859 stbir__simdf_store( output+3, tot1 ); \
5860 stbir__simdf_store( output, tot0 ); \
5861 horizontal_coefficients += coefficient_width; \
5862 ++horizontal_contributors; \
5863 output += 7;
5864
5865#endif
5866
5867#else
5868
5869#define stbir__1_coeff_only() \
5870 float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \
5871 c = hc[0]; \
5872 tot0 = decode[0]*c; \
5873 tot1 = decode[1]*c; \
5874 tot2 = decode[2]*c; \
5875 tot3 = decode[3]*c; \
5876 tot4 = decode[4]*c; \
5877 tot5 = decode[5]*c; \
5878 tot6 = decode[6]*c;
5879
5880#define stbir__2_coeff_only() \
5881 float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \
5882 c = hc[0]; \
5883 tot0 = decode[0]*c; \
5884 tot1 = decode[1]*c; \
5885 tot2 = decode[2]*c; \
5886 tot3 = decode[3]*c; \
5887 tot4 = decode[4]*c; \
5888 tot5 = decode[5]*c; \
5889 tot6 = decode[6]*c; \
5890 c = hc[1]; \
5891 tot0 += decode[7]*c; \
5892 tot1 += decode[8]*c; \
5893 tot2 += decode[9]*c; \
5894 tot3 += decode[10]*c; \
5895 tot4 += decode[11]*c; \
5896 tot5 += decode[12]*c; \
5897 tot6 += decode[13]*c; \
5898
5899#define stbir__3_coeff_only() \
5900 float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \
5901 c = hc[0]; \
5902 tot0 = decode[0]*c; \
5903 tot1 = decode[1]*c; \
5904 tot2 = decode[2]*c; \
5905 tot3 = decode[3]*c; \
5906 tot4 = decode[4]*c; \
5907 tot5 = decode[5]*c; \
5908 tot6 = decode[6]*c; \
5909 c = hc[1]; \
5910 tot0 += decode[7]*c; \
5911 tot1 += decode[8]*c; \
5912 tot2 += decode[9]*c; \
5913 tot3 += decode[10]*c; \
5914 tot4 += decode[11]*c; \
5915 tot5 += decode[12]*c; \
5916 tot6 += decode[13]*c; \
5917 c = hc[2]; \
5918 tot0 += decode[14]*c; \
5919 tot1 += decode[15]*c; \
5920 tot2 += decode[16]*c; \
5921 tot3 += decode[17]*c; \
5922 tot4 += decode[18]*c; \
5923 tot5 += decode[19]*c; \
5924 tot6 += decode[20]*c; \
5925
5926#define stbir__store_output_tiny() \
5927 output[0] = tot0; \
5928 output[1] = tot1; \
5929 output[2] = tot2; \
5930 output[3] = tot3; \
5931 output[4] = tot4; \
5932 output[5] = tot5; \
5933 output[6] = tot6; \
5934 horizontal_coefficients += coefficient_width; \
5935 ++horizontal_contributors; \
5936 output += 7;
5937
5938#define stbir__4_coeff_start() \
5939 float x0,x1,x2,x3,x4,x5,x6,y0,y1,y2,y3,y4,y5,y6,c; \
5940 STBIR_SIMD_NO_UNROLL(decode); \
5941 c = hc[0]; \
5942 x0 = decode[0] * c; \
5943 x1 = decode[1] * c; \
5944 x2 = decode[2] * c; \
5945 x3 = decode[3] * c; \
5946 x4 = decode[4] * c; \
5947 x5 = decode[5] * c; \
5948 x6 = decode[6] * c; \
5949 c = hc[1]; \
5950 y0 = decode[7] * c; \
5951 y1 = decode[8] * c; \
5952 y2 = decode[9] * c; \
5953 y3 = decode[10] * c; \
5954 y4 = decode[11] * c; \
5955 y5 = decode[12] * c; \
5956 y6 = decode[13] * c; \
5957 c = hc[2]; \
5958 x0 += decode[14] * c; \
5959 x1 += decode[15] * c; \
5960 x2 += decode[16] * c; \
5961 x3 += decode[17] * c; \
5962 x4 += decode[18] * c; \
5963 x5 += decode[19] * c; \
5964 x6 += decode[20] * c; \
5965 c = hc[3]; \
5966 y0 += decode[21] * c; \
5967 y1 += decode[22] * c; \
5968 y2 += decode[23] * c; \
5969 y3 += decode[24] * c; \
5970 y4 += decode[25] * c; \
5971 y5 += decode[26] * c; \
5972 y6 += decode[27] * c;
5973
5974#define stbir__4_coeff_continue_from_4( ofs ) \
5975 STBIR_SIMD_NO_UNROLL(decode); \
5976 c = hc[0+(ofs)]; \
5977 x0 += decode[0+(ofs)*7] * c; \
5978 x1 += decode[1+(ofs)*7] * c; \
5979 x2 += decode[2+(ofs)*7] * c; \
5980 x3 += decode[3+(ofs)*7] * c; \
5981 x4 += decode[4+(ofs)*7] * c; \
5982 x5 += decode[5+(ofs)*7] * c; \
5983 x6 += decode[6+(ofs)*7] * c; \
5984 c = hc[1+(ofs)]; \
5985 y0 += decode[7+(ofs)*7] * c; \
5986 y1 += decode[8+(ofs)*7] * c; \
5987 y2 += decode[9+(ofs)*7] * c; \
5988 y3 += decode[10+(ofs)*7] * c; \
5989 y4 += decode[11+(ofs)*7] * c; \
5990 y5 += decode[12+(ofs)*7] * c; \
5991 y6 += decode[13+(ofs)*7] * c; \
5992 c = hc[2+(ofs)]; \
5993 x0 += decode[14+(ofs)*7] * c; \
5994 x1 += decode[15+(ofs)*7] * c; \
5995 x2 += decode[16+(ofs)*7] * c; \
5996 x3 += decode[17+(ofs)*7] * c; \
5997 x4 += decode[18+(ofs)*7] * c; \
5998 x5 += decode[19+(ofs)*7] * c; \
5999 x6 += decode[20+(ofs)*7] * c; \
6000 c = hc[3+(ofs)]; \
6001 y0 += decode[21+(ofs)*7] * c; \
6002 y1 += decode[22+(ofs)*7] * c; \
6003 y2 += decode[23+(ofs)*7] * c; \
6004 y3 += decode[24+(ofs)*7] * c; \
6005 y4 += decode[25+(ofs)*7] * c; \
6006 y5 += decode[26+(ofs)*7] * c; \
6007 y6 += decode[27+(ofs)*7] * c;
6008
6009#define stbir__1_coeff_remnant( ofs ) \
6010 STBIR_SIMD_NO_UNROLL(decode); \
6011 c = hc[0+(ofs)]; \
6012 x0 += decode[0+(ofs)*7] * c; \
6013 x1 += decode[1+(ofs)*7] * c; \
6014 x2 += decode[2+(ofs)*7] * c; \
6015 x3 += decode[3+(ofs)*7] * c; \
6016 x4 += decode[4+(ofs)*7] * c; \
6017 x5 += decode[5+(ofs)*7] * c; \
6018 x6 += decode[6+(ofs)*7] * c; \
6019
6020#define stbir__2_coeff_remnant( ofs ) \
6021 STBIR_SIMD_NO_UNROLL(decode); \
6022 c = hc[0+(ofs)]; \
6023 x0 += decode[0+(ofs)*7] * c; \
6024 x1 += decode[1+(ofs)*7] * c; \
6025 x2 += decode[2+(ofs)*7] * c; \
6026 x3 += decode[3+(ofs)*7] * c; \
6027 x4 += decode[4+(ofs)*7] * c; \
6028 x5 += decode[5+(ofs)*7] * c; \
6029 x6 += decode[6+(ofs)*7] * c; \
6030 c = hc[1+(ofs)]; \
6031 y0 += decode[7+(ofs)*7] * c; \
6032 y1 += decode[8+(ofs)*7] * c; \
6033 y2 += decode[9+(ofs)*7] * c; \
6034 y3 += decode[10+(ofs)*7] * c; \
6035 y4 += decode[11+(ofs)*7] * c; \
6036 y5 += decode[12+(ofs)*7] * c; \
6037 y6 += decode[13+(ofs)*7] * c; \
6038
6039#define stbir__3_coeff_remnant( ofs ) \
6040 STBIR_SIMD_NO_UNROLL(decode); \
6041 c = hc[0+(ofs)]; \
6042 x0 += decode[0+(ofs)*7] * c; \
6043 x1 += decode[1+(ofs)*7] * c; \
6044 x2 += decode[2+(ofs)*7] * c; \
6045 x3 += decode[3+(ofs)*7] * c; \
6046 x4 += decode[4+(ofs)*7] * c; \
6047 x5 += decode[5+(ofs)*7] * c; \
6048 x6 += decode[6+(ofs)*7] * c; \
6049 c = hc[1+(ofs)]; \
6050 y0 += decode[7+(ofs)*7] * c; \
6051 y1 += decode[8+(ofs)*7] * c; \
6052 y2 += decode[9+(ofs)*7] * c; \
6053 y3 += decode[10+(ofs)*7] * c; \
6054 y4 += decode[11+(ofs)*7] * c; \
6055 y5 += decode[12+(ofs)*7] * c; \
6056 y6 += decode[13+(ofs)*7] * c; \
6057 c = hc[2+(ofs)]; \
6058 x0 += decode[14+(ofs)*7] * c; \
6059 x1 += decode[15+(ofs)*7] * c; \
6060 x2 += decode[16+(ofs)*7] * c; \
6061 x3 += decode[17+(ofs)*7] * c; \
6062 x4 += decode[18+(ofs)*7] * c; \
6063 x5 += decode[19+(ofs)*7] * c; \
6064 x6 += decode[20+(ofs)*7] * c; \
6065
6066#define stbir__store_output() \
6067 output[0] = x0 + y0; \
6068 output[1] = x1 + y1; \
6069 output[2] = x2 + y2; \
6070 output[3] = x3 + y3; \
6071 output[4] = x4 + y4; \
6072 output[5] = x5 + y5; \
6073 output[6] = x6 + y6; \
6074 horizontal_coefficients += coefficient_width; \
6075 ++horizontal_contributors; \
6076 output += 7;
6077
6078#endif
6079
6080#define STBIR__horizontal_channels 7
6081#define STB_IMAGE_RESIZE_DO_HORIZONTALS
6082#include STBIR__HEADER_FILENAME
6083
6084
6085// include all of the vertical resamplers (both scatter and gather versions)
6086
6087#define STBIR__vertical_channels 1
6088#define STB_IMAGE_RESIZE_DO_VERTICALS
6089#include STBIR__HEADER_FILENAME
6090
6091#define STBIR__vertical_channels 1
6092#define STB_IMAGE_RESIZE_DO_VERTICALS
6093#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
6094#include STBIR__HEADER_FILENAME
6095
6096#define STBIR__vertical_channels 2
6097#define STB_IMAGE_RESIZE_DO_VERTICALS
6098#include STBIR__HEADER_FILENAME
6099
6100#define STBIR__vertical_channels 2
6101#define STB_IMAGE_RESIZE_DO_VERTICALS
6102#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
6103#include STBIR__HEADER_FILENAME
6104
6105#define STBIR__vertical_channels 3
6106#define STB_IMAGE_RESIZE_DO_VERTICALS
6107#include STBIR__HEADER_FILENAME
6108
6109#define STBIR__vertical_channels 3
6110#define STB_IMAGE_RESIZE_DO_VERTICALS
6111#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
6112#include STBIR__HEADER_FILENAME
6113
6114#define STBIR__vertical_channels 4
6115#define STB_IMAGE_RESIZE_DO_VERTICALS
6116#include STBIR__HEADER_FILENAME
6117
6118#define STBIR__vertical_channels 4
6119#define STB_IMAGE_RESIZE_DO_VERTICALS
6120#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
6121#include STBIR__HEADER_FILENAME
6122
6123#define STBIR__vertical_channels 5
6124#define STB_IMAGE_RESIZE_DO_VERTICALS
6125#include STBIR__HEADER_FILENAME
6126
6127#define STBIR__vertical_channels 5
6128#define STB_IMAGE_RESIZE_DO_VERTICALS
6129#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
6130#include STBIR__HEADER_FILENAME
6131
6132#define STBIR__vertical_channels 6
6133#define STB_IMAGE_RESIZE_DO_VERTICALS
6134#include STBIR__HEADER_FILENAME
6135
6136#define STBIR__vertical_channels 6
6137#define STB_IMAGE_RESIZE_DO_VERTICALS
6138#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
6139#include STBIR__HEADER_FILENAME
6140
6141#define STBIR__vertical_channels 7
6142#define STB_IMAGE_RESIZE_DO_VERTICALS
6143#include STBIR__HEADER_FILENAME
6144
6145#define STBIR__vertical_channels 7
6146#define STB_IMAGE_RESIZE_DO_VERTICALS
6147#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
6148#include STBIR__HEADER_FILENAME
6149
6150#define STBIR__vertical_channels 8
6151#define STB_IMAGE_RESIZE_DO_VERTICALS
6152#include STBIR__HEADER_FILENAME
6153
6154#define STBIR__vertical_channels 8
6155#define STB_IMAGE_RESIZE_DO_VERTICALS
6156#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE
6157#include STBIR__HEADER_FILENAME
6158
6159typedef void STBIR_VERTICAL_GATHERFUNC( float * output, float const * coeffs, float const ** inputs, float const * input0_end );
6160
6161static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers[ 8 ] =
6162{
6163 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
6164};
6165
6166static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers_continues[ 8 ] =
6167{
6168 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
6169};
6170
6171typedef void STBIR_VERTICAL_SCATTERFUNC( float ** outputs, float const * coeffs, float const * input, float const * input_end );
6172
6173static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_sets[ 8 ] =
6174{
6175 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
6176};
6177
6178static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_blends[ 8 ] =
6179{
6180 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
6181};
6182
6183
6184static void stbir__encode_scanline( stbir__info const * stbir_info, void *output_buffer_data, float * encode_buffer, int row STBIR_ONLY_PROFILE_GET_SPLIT_INFO )
6185{
6186 int num_pixels = stbir_info->horizontal.scale_info.output_sub_size;
6187 int channels = stbir_info->channels;
6188 int width_times_channels = num_pixels * channels;
6189 void * output_buffer;
6190
6191 // un-alpha weight if we need to
6192 if ( stbir_info->alpha_unweight )
6193 {
6194 STBIR_PROFILE_START( unalpha );
6195 stbir_info->alpha_unweight( encode_buffer, width_times_channels );
6196 STBIR_PROFILE_END( unalpha );
6197 }
6198
6199 // write directly into output by default
6200 output_buffer = output_buffer_data;
6201
6202 // if we have an output callback, we first convert the decode buffer in place (and then hand that to the callback)
6203 if ( stbir_info->out_pixels_cb )
6204 output_buffer = encode_buffer;
6205
6206 STBIR_PROFILE_START( encode );
6207 // convert into the output buffer
6208 stbir_info->encode_pixels( output_buffer, width_times_channels, encode_buffer );
6209 STBIR_PROFILE_END( encode );
6210
6211 // if we have an output callback, call it to send the data
6212 if ( stbir_info->out_pixels_cb )
6213 stbir_info->out_pixels_cb( output_buffer, num_pixels, row, stbir_info->user_data );
6214}
6215
6216
6217// Get the ring buffer pointer for an index
6218static float* stbir__get_ring_buffer_entry(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int index )
6219{
6220 STBIR_ASSERT( index < stbir_info->ring_buffer_num_entries );
6221
6222 #ifdef STBIR__SEPARATE_ALLOCATIONS
6223 return split_info->ring_buffers[ index ];
6224 #else
6225 return (float*) ( ( (char*) split_info->ring_buffer ) + ( index * stbir_info->ring_buffer_length_bytes ) );
6226 #endif
6227}
6228
6229// Get the specified scan line from the ring buffer
6230static float* stbir__get_ring_buffer_scanline(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int get_scanline)
6231{
6232 int ring_buffer_index = (split_info->ring_buffer_begin_index + (get_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries;
6233 return stbir__get_ring_buffer_entry( stbir_info, split_info, ring_buffer_index );
6234}
6235
6236static void stbir__resample_horizontal_gather(stbir__info const * stbir_info, float* output_buffer, float const * input_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO )
6237{
6238 float const * decode_buffer = input_buffer - ( stbir_info->scanline_extents.conservative.n0 * stbir_info->effective_channels );
6239
6240 STBIR_PROFILE_START( horizontal );
6241 if ( ( stbir_info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( stbir_info->horizontal.scale_info.scale == 1.0f ) )
6242 STBIR_MEMCPY( output_buffer, input_buffer, stbir_info->horizontal.scale_info.output_sub_size * sizeof( float ) * stbir_info->effective_channels );
6243 else
6244 stbir_info->horizontal_gather_channels( output_buffer, stbir_info->horizontal.scale_info.output_sub_size, decode_buffer, stbir_info->horizontal.contributors, stbir_info->horizontal.coefficients, stbir_info->horizontal.coefficient_width );
6245 STBIR_PROFILE_END( horizontal );
6246}
6247
6248static 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 )
6249{
6250 float* encode_buffer = split_info->vertical_buffer;
6251 float* decode_buffer = split_info->decode_buffer;
6252 int vertical_first = stbir_info->vertical_first;
6253 int width = (vertical_first) ? ( stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1 ) : stbir_info->horizontal.scale_info.output_sub_size;
6254 int width_times_channels = stbir_info->effective_channels * width;
6255
6256 STBIR_ASSERT( stbir_info->vertical.is_gather );
6257
6258 // loop over the contributing scanlines and scale into the buffer
6259 STBIR_PROFILE_START( vertical );
6260 {
6261 int k = 0, total = contrib_n1 - contrib_n0 + 1;
6262 STBIR_ASSERT( total > 0 );
6263 do {
6264 float const * inputs[8];
6265 int i, cnt = total; if ( cnt > 8 ) cnt = 8;
6266 for( i = 0 ; i < cnt ; i++ )
6267 inputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+contrib_n0 );
6268
6269 // call the N scanlines at a time function (up to 8 scanlines of blending at once)
6270 ((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 );
6271 k += cnt;
6272 total -= cnt;
6273 } while ( total );
6274 }
6275 STBIR_PROFILE_END( vertical );
6276
6277 if ( vertical_first )
6278 {
6279 // Now resample the gathered vertical data in the horizontal axis into the encode buffer
6280 decode_buffer[ width_times_channels ] = 0.0f; // clear two over for horizontals with a remnant of 3
6281 decode_buffer[ width_times_channels+1 ] = 0.0f;
6282 stbir__resample_horizontal_gather(stbir_info, encode_buffer, decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6283 }
6284
6285 stbir__encode_scanline( stbir_info, ( (char *) stbir_info->output_data ) + ((size_t)n * (size_t)stbir_info->output_stride_bytes),
6286 encode_buffer, n STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6287}
6288
6289static void stbir__decode_and_resample_for_vertical_gather_loop(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n)
6290{
6291 int ring_buffer_index;
6292 float* ring_buffer;
6293
6294 // Decode the nth scanline from the source image into the decode buffer.
6295 stbir__decode_scanline( stbir_info, n, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6296
6297 // update new end scanline
6298 split_info->ring_buffer_last_scanline = n;
6299
6300 // get ring buffer
6301 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;
6302 ring_buffer = stbir__get_ring_buffer_entry(stbir_info, split_info, ring_buffer_index);
6303
6304 // Now resample it into the ring buffer.
6305 stbir__resample_horizontal_gather( stbir_info, ring_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6306
6307 // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling.
6308}
6309
6310static void stbir__vertical_gather_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count )
6311{
6312 int y, start_output_y, end_output_y;
6313 stbir__contributors* vertical_contributors = stbir_info->vertical.contributors;
6314 float const * vertical_coefficients = stbir_info->vertical.coefficients;
6315
6316 STBIR_ASSERT( stbir_info->vertical.is_gather );
6317
6318 start_output_y = split_info->start_output_y;
6319 end_output_y = split_info[split_count-1].end_output_y;
6320
6321 vertical_contributors += start_output_y;
6322 vertical_coefficients += start_output_y * stbir_info->vertical.coefficient_width;
6323
6324 // initialize the ring buffer for gathering
6325 split_info->ring_buffer_begin_index = 0;
6326 split_info->ring_buffer_first_scanline = vertical_contributors->n0;
6327 split_info->ring_buffer_last_scanline = split_info->ring_buffer_first_scanline - 1; // means "empty"
6328
6329 for (y = start_output_y; y < end_output_y; y++)
6330 {
6331 int in_first_scanline, in_last_scanline;
6332
6333 in_first_scanline = vertical_contributors->n0;
6334 in_last_scanline = vertical_contributors->n1;
6335
6336 // make sure the indexing hasn't broken
6337 STBIR_ASSERT( in_first_scanline >= split_info->ring_buffer_first_scanline );
6338
6339 // Load in new scanlines
6340 while (in_last_scanline > split_info->ring_buffer_last_scanline)
6341 {
6342 STBIR_ASSERT( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) <= stbir_info->ring_buffer_num_entries );
6343
6344 // make sure there was room in the ring buffer when we add new scanlines
6345 if ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries )
6346 {
6347 split_info->ring_buffer_first_scanline++;
6348 split_info->ring_buffer_begin_index++;
6349 }
6350
6351 if ( stbir_info->vertical_first )
6352 {
6353 float * ring_buffer = stbir__get_ring_buffer_scanline( stbir_info, split_info, ++split_info->ring_buffer_last_scanline );
6354 // Decode the nth scanline from the source image into the decode buffer.
6355 stbir__decode_scanline( stbir_info, split_info->ring_buffer_last_scanline, ring_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6356 }
6357 else
6358 {
6359 stbir__decode_and_resample_for_vertical_gather_loop(stbir_info, split_info, split_info->ring_buffer_last_scanline + 1);
6360 }
6361 }
6362
6363 // Now all buffers should be ready to write a row of vertical sampling, so do it.
6364 stbir__resample_vertical_gather(stbir_info, split_info, y, in_first_scanline, in_last_scanline, vertical_coefficients );
6365
6366 ++vertical_contributors;
6367 vertical_coefficients += stbir_info->vertical.coefficient_width;
6368 }
6369}
6370
6371#define STBIR__FLOAT_EMPTY_MARKER 3.0e+38F
6372#define STBIR__FLOAT_BUFFER_IS_EMPTY(ptr) ((ptr)[0]==STBIR__FLOAT_EMPTY_MARKER)
6373
6374static void stbir__encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info)
6375{
6376 // evict a scanline out into the output buffer
6377 float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index );
6378
6379 // dump the scanline out
6380 stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (size_t)split_info->ring_buffer_first_scanline * (size_t)stbir_info->output_stride_bytes ), ring_buffer_entry, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6381
6382 // mark it as empty
6383 ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER;
6384
6385 // advance the first scanline
6386 split_info->ring_buffer_first_scanline++;
6387 if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries )
6388 split_info->ring_buffer_begin_index = 0;
6389}
6390
6391static void stbir__horizontal_resample_and_encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info)
6392{
6393 // evict a scanline out into the output buffer
6394
6395 float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index );
6396
6397 // Now resample it into the buffer.
6398 stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, ring_buffer_entry STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6399
6400 // dump the scanline out
6401 stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (size_t)split_info->ring_buffer_first_scanline * (size_t)stbir_info->output_stride_bytes ), split_info->vertical_buffer, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6402
6403 // mark it as empty
6404 ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER;
6405
6406 // advance the first scanline
6407 split_info->ring_buffer_first_scanline++;
6408 if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries )
6409 split_info->ring_buffer_begin_index = 0;
6410}
6411
6412static 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 )
6413{
6414 STBIR_ASSERT( !stbir_info->vertical.is_gather );
6415
6416 STBIR_PROFILE_START( vertical );
6417 {
6418 int k = 0, total = n1 - n0 + 1;
6419 STBIR_ASSERT( total > 0 );
6420 do {
6421 float * outputs[8];
6422 int i, n = total; if ( n > 8 ) n = 8;
6423 for( i = 0 ; i < n ; i++ )
6424 {
6425 outputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+n0 );
6426 if ( ( i ) && ( STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[i] ) != STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ) ) ) // make sure runs are of the same type
6427 {
6428 n = i;
6429 break;
6430 }
6431 }
6432 // call the scatter to N scanlines at a time function (up to 8 scanlines of scattering at once)
6433 ((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 );
6434 k += n;
6435 total -= n;
6436 } while ( total );
6437 }
6438
6439 STBIR_PROFILE_END( vertical );
6440}
6441
6442typedef void stbir__handle_scanline_for_scatter_func(stbir__info const * stbir_info, stbir__per_split_info* split_info);
6443
6444static void stbir__vertical_scatter_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count )
6445{
6446 int y, start_output_y, end_output_y, start_input_y, end_input_y;
6447 stbir__contributors* vertical_contributors = stbir_info->vertical.contributors;
6448 float const * vertical_coefficients = stbir_info->vertical.coefficients;
6449 stbir__handle_scanline_for_scatter_func * handle_scanline_for_scatter;
6450 void * scanline_scatter_buffer;
6451 void * scanline_scatter_buffer_end;
6452 int on_first_input_y, last_input_y;
6453 int width = (stbir_info->vertical_first) ? ( stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1 ) : stbir_info->horizontal.scale_info.output_sub_size;
6454 int width_times_channels = stbir_info->effective_channels * width;
6455
6456 STBIR_ASSERT( !stbir_info->vertical.is_gather );
6457
6458 start_output_y = split_info->start_output_y;
6459 end_output_y = split_info[split_count-1].end_output_y; // may do multiple split counts
6460
6461 start_input_y = split_info->start_input_y;
6462 end_input_y = split_info[split_count-1].end_input_y;
6463
6464 // adjust for starting offset start_input_y
6465 y = start_input_y + stbir_info->vertical.filter_pixel_margin;
6466 vertical_contributors += y ;
6467 vertical_coefficients += stbir_info->vertical.coefficient_width * y;
6468
6469 if ( stbir_info->vertical_first )
6470 {
6471 handle_scanline_for_scatter = stbir__horizontal_resample_and_encode_first_scanline_from_scatter;
6472 scanline_scatter_buffer = split_info->decode_buffer;
6473 scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * (stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1);
6474 }
6475 else
6476 {
6477 handle_scanline_for_scatter = stbir__encode_first_scanline_from_scatter;
6478 scanline_scatter_buffer = split_info->vertical_buffer;
6479 scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * stbir_info->horizontal.scale_info.output_sub_size;
6480 }
6481
6482 // initialize the ring buffer for scattering
6483 split_info->ring_buffer_first_scanline = start_output_y;
6484 split_info->ring_buffer_last_scanline = -1;
6485 split_info->ring_buffer_begin_index = -1;
6486
6487 // mark all the buffers as empty to start
6488 for( y = 0 ; y < stbir_info->ring_buffer_num_entries ; y++ )
6489 {
6490 float * decode_buffer = stbir__get_ring_buffer_entry( stbir_info, split_info, y );
6491 decode_buffer[ width_times_channels ] = 0.0f; // clear two over for horizontals with a remnant of 3
6492 decode_buffer[ width_times_channels+1 ] = 0.0f;
6493 decode_buffer[0] = STBIR__FLOAT_EMPTY_MARKER; // only used on scatter
6494 }
6495
6496 // do the loop in input space
6497 on_first_input_y = 1; last_input_y = start_input_y;
6498 for (y = start_input_y ; y < end_input_y; y++)
6499 {
6500 int out_first_scanline, out_last_scanline;
6501
6502 out_first_scanline = vertical_contributors->n0;
6503 out_last_scanline = vertical_contributors->n1;
6504
6505 STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries);
6506
6507 if ( ( out_last_scanline >= out_first_scanline ) && ( ( ( out_first_scanline >= start_output_y ) && ( out_first_scanline < end_output_y ) ) || ( ( out_last_scanline >= start_output_y ) && ( out_last_scanline < end_output_y ) ) ) )
6508 {
6509 float const * vc = vertical_coefficients;
6510
6511 // keep track of the range actually seen for the next resize
6512 last_input_y = y;
6513 if ( ( on_first_input_y ) && ( y > start_input_y ) )
6514 split_info->start_input_y = y;
6515 on_first_input_y = 0;
6516
6517 // clip the region
6518 if ( out_first_scanline < start_output_y )
6519 {
6520 vc += start_output_y - out_first_scanline;
6521 out_first_scanline = start_output_y;
6522 }
6523
6524 if ( out_last_scanline >= end_output_y )
6525 out_last_scanline = end_output_y - 1;
6526
6527 // if very first scanline, init the index
6528 if (split_info->ring_buffer_begin_index < 0)
6529 split_info->ring_buffer_begin_index = out_first_scanline - start_output_y;
6530
6531 STBIR_ASSERT( split_info->ring_buffer_begin_index <= out_first_scanline );
6532
6533 // Decode the nth scanline from the source image into the decode buffer.
6534 stbir__decode_scanline( stbir_info, y, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6535
6536 // When horizontal first, we resample horizontally into the vertical buffer before we scatter it out
6537 if ( !stbir_info->vertical_first )
6538 stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO );
6539
6540 // Now it's sitting in the buffer ready to be distributed into the ring buffers.
6541
6542 // evict from the ringbuffer, if we need are full
6543 if ( ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) &&
6544 ( out_last_scanline > split_info->ring_buffer_last_scanline ) )
6545 handle_scanline_for_scatter( stbir_info, split_info );
6546
6547 // Now the horizontal buffer is ready to write to all ring buffer rows, so do it.
6548 stbir__resample_vertical_scatter(stbir_info, split_info, out_first_scanline, out_last_scanline, vc, (float*)scanline_scatter_buffer, (float*)scanline_scatter_buffer_end );
6549
6550 // update the end of the buffer
6551 if ( out_last_scanline > split_info->ring_buffer_last_scanline )
6552 split_info->ring_buffer_last_scanline = out_last_scanline;
6553 }
6554 ++vertical_contributors;
6555 vertical_coefficients += stbir_info->vertical.coefficient_width;
6556 }
6557
6558 // now evict the scanlines that are left over in the ring buffer
6559 while ( split_info->ring_buffer_first_scanline < end_output_y )
6560 handle_scanline_for_scatter(stbir_info, split_info);
6561
6562 // update the end_input_y if we do multiple resizes with the same data
6563 ++last_input_y;
6564 for( y = 0 ; y < split_count; y++ )
6565 if ( split_info[y].end_input_y > last_input_y )
6566 split_info[y].end_input_y = last_input_y;
6567}
6568
6569
6570static stbir__kernel_callback * stbir__builtin_kernels[] = { 0, stbir__filter_trapezoid, stbir__filter_triangle, stbir__filter_cubic, stbir__filter_catmullrom, stbir__filter_mitchell, stbir__filter_point };
6571static stbir__support_callback * stbir__builtin_supports[] = { 0, stbir__support_trapezoid, stbir__support_one, stbir__support_two, stbir__support_two, stbir__support_two, stbir__support_zeropoint5 };
6572
6573static void stbir__set_sampler(stbir__sampler * samp, stbir_filter filter, stbir__kernel_callback * kernel, stbir__support_callback * support, stbir_edge edge, stbir__scale_info * scale_info, int always_gather, void * user_data )
6574{
6575 // set filter
6576 if (filter == 0)
6577 {
6578 filter = STBIR_DEFAULT_FILTER_DOWNSAMPLE; // default to downsample
6579 if (scale_info->scale >= ( 1.0f - stbir__small_float ) )
6580 {
6581 if ( (scale_info->scale <= ( 1.0f + stbir__small_float ) ) && ( STBIR_CEILF(scale_info->pixel_shift) == scale_info->pixel_shift ) )
6582 filter = STBIR_FILTER_POINT_SAMPLE;
6583 else
6584 filter = STBIR_DEFAULT_FILTER_UPSAMPLE;
6585 }
6586 }
6587 samp->filter_enum = filter;
6588
6589 STBIR_ASSERT(samp->filter_enum != 0);
6590 STBIR_ASSERT((unsigned)samp->filter_enum < STBIR_FILTER_OTHER);
6591 samp->filter_kernel = stbir__builtin_kernels[ filter ];
6592 samp->filter_support = stbir__builtin_supports[ filter ];
6593
6594 if ( kernel && support )
6595 {
6596 samp->filter_kernel = kernel;
6597 samp->filter_support = support;
6598 samp->filter_enum = STBIR_FILTER_OTHER;
6599 }
6600
6601 samp->edge = edge;
6602 samp->filter_pixel_width = stbir__get_filter_pixel_width (samp->filter_support, scale_info->scale, user_data );
6603 // Gather is always better, but in extreme downsamples, you have to most or all of the data in memory
6604 // For horizontal, we always have all the pixels, so we always use gather here (always_gather==1).
6605 // For vertical, we use gather if scaling up (which means we will have samp->filter_pixel_width
6606 // scanlines in memory at once).
6607 samp->is_gather = 0;
6608 if ( scale_info->scale >= ( 1.0f - stbir__small_float ) )
6609 samp->is_gather = 1;
6610 else if ( ( always_gather ) || ( samp->filter_pixel_width <= STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT ) )
6611 samp->is_gather = 2;
6612
6613 // pre calculate stuff based on the above
6614 samp->coefficient_width = stbir__get_coefficient_width(samp, samp->is_gather, user_data);
6615
6616 // filter_pixel_width is the conservative size in pixels of input that affect an output pixel.
6617 // In rare cases (only with 2 pix to 1 pix with the default filters), it's possible that the
6618 // filter will extend before or after the scanline beyond just one extra entire copy of the
6619 // scanline (we would hit the edge twice). We don't let you do that, so we clamp the total
6620 // width to 3x the total of input pixel (once for the scanline, once for the left side
6621 // overhang, and once for the right side). We only do this for edge mode, since the other
6622 // modes can just re-edge clamp back in again.
6623 if ( edge == STBIR_EDGE_WRAP )
6624 if ( samp->filter_pixel_width > ( scale_info->input_full_size * 3 ) )
6625 samp->filter_pixel_width = scale_info->input_full_size * 3;
6626
6627 // This is how much to expand buffers to account for filters seeking outside
6628 // the image boundaries.
6629 samp->filter_pixel_margin = samp->filter_pixel_width / 2;
6630
6631 // filter_pixel_margin is the amount that this filter can overhang on just one side of either
6632 // end of the scanline (left or the right). Since we only allow you to overhang 1 scanline's
6633 // worth of pixels, we clamp this one side of overhang to the input scanline size. Again,
6634 // this clamping only happens in rare cases with the default filters (2 pix to 1 pix).
6635 if ( edge == STBIR_EDGE_WRAP )
6636 if ( samp->filter_pixel_margin > scale_info->input_full_size )
6637 samp->filter_pixel_margin = scale_info->input_full_size;
6638
6639 samp->num_contributors = stbir__get_contributors(samp, samp->is_gather);
6640
6641 samp->contributors_size = samp->num_contributors * sizeof(stbir__contributors);
6642 samp->coefficients_size = samp->num_contributors * samp->coefficient_width * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING; // extra sizeof(float) is padding
6643
6644 samp->gather_prescatter_contributors = 0;
6645 samp->gather_prescatter_coefficients = 0;
6646 if ( samp->is_gather == 0 )
6647 {
6648 samp->gather_prescatter_coefficient_width = samp->filter_pixel_width;
6649 samp->gather_prescatter_num_contributors = stbir__get_contributors(samp, 2);
6650 samp->gather_prescatter_contributors_size = samp->gather_prescatter_num_contributors * sizeof(stbir__contributors);
6651 samp->gather_prescatter_coefficients_size = samp->gather_prescatter_num_contributors * samp->gather_prescatter_coefficient_width * sizeof(float);
6652 }
6653}
6654
6655static void stbir__get_conservative_extents( stbir__sampler * samp, stbir__contributors * range, void * user_data )
6656{
6657 float scale = samp->scale_info.scale;
6658 float out_shift = samp->scale_info.pixel_shift;
6659 stbir__support_callback * support = samp->filter_support;
6660 int input_full_size = samp->scale_info.input_full_size;
6661 stbir_edge edge = samp->edge;
6662 float inv_scale = samp->scale_info.inv_scale;
6663
6664 STBIR_ASSERT( samp->is_gather != 0 );
6665
6666 if ( samp->is_gather == 1 )
6667 {
6668 int in_first_pixel, in_last_pixel;
6669 float out_filter_radius = support(inv_scale, user_data) * scale;
6670
6671 stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0.5, out_filter_radius, inv_scale, out_shift, input_full_size, edge );
6672 range->n0 = in_first_pixel;
6673 stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, ( (float)(samp->scale_info.output_sub_size-1) ) + 0.5f, out_filter_radius, inv_scale, out_shift, input_full_size, edge );
6674 range->n1 = in_last_pixel;
6675 }
6676 else if ( samp->is_gather == 2 ) // downsample gather, refine
6677 {
6678 float in_pixels_radius = support(scale, user_data) * inv_scale;
6679 int filter_pixel_margin = samp->filter_pixel_margin;
6680 int output_sub_size = samp->scale_info.output_sub_size;
6681 int input_end;
6682 int n;
6683 int in_first_pixel, in_last_pixel;
6684
6685 // get a conservative area of the input range
6686 stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0, 0, inv_scale, out_shift, input_full_size, edge );
6687 range->n0 = in_first_pixel;
6688 stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, (float)output_sub_size, 0, inv_scale, out_shift, input_full_size, edge );
6689 range->n1 = in_last_pixel;
6690
6691 // now go through the margin to the start of area to find bottom
6692 n = range->n0 + 1;
6693 input_end = -filter_pixel_margin;
6694 while( n >= input_end )
6695 {
6696 int out_first_pixel, out_last_pixel;
6697 stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size );
6698 if ( out_first_pixel > out_last_pixel )
6699 break;
6700
6701 if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) )
6702 range->n0 = n;
6703 --n;
6704 }
6705
6706 // now go through the end of the area through the margin to find top
6707 n = range->n1 - 1;
6708 input_end = n + 1 + filter_pixel_margin;
6709 while( n <= input_end )
6710 {
6711 int out_first_pixel, out_last_pixel;
6712 stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size );
6713 if ( out_first_pixel > out_last_pixel )
6714 break;
6715 if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) )
6716 range->n1 = n;
6717 ++n;
6718 }
6719 }
6720
6721 if ( samp->edge == STBIR_EDGE_WRAP )
6722 {
6723 // if we are wrapping, and we are very close to the image size (so the edges might merge), just use the scanline up to the edge
6724 if ( ( range->n0 > 0 ) && ( range->n1 >= input_full_size ) )
6725 {
6726 int marg = range->n1 - input_full_size + 1;
6727 if ( ( marg + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= range->n0 )
6728 range->n0 = 0;
6729 }
6730 if ( ( range->n0 < 0 ) && ( range->n1 < (input_full_size-1) ) )
6731 {
6732 int marg = -range->n0;
6733 if ( ( input_full_size - marg - STBIR__MERGE_RUNS_PIXEL_THRESHOLD - 1 ) <= range->n1 )
6734 range->n1 = input_full_size - 1;
6735 }
6736 }
6737 else
6738 {
6739 // for non-edge-wrap modes, we never read over the edge, so clamp
6740 if ( range->n0 < 0 )
6741 range->n0 = 0;
6742 if ( range->n1 >= input_full_size )
6743 range->n1 = input_full_size - 1;
6744 }
6745}
6746
6747static 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__contributors * contribs )
6748{
6749 int i, cur;
6750 int left = output_height;
6751
6752 cur = 0;
6753 for( i = 0 ; i < splits ; i++ )
6754 {
6755 int each;
6756
6757 split_info[i].start_output_y = cur;
6758 each = left / ( splits - i );
6759 split_info[i].end_output_y = cur + each;
6760
6761 // ok, when we are gathering, we need to make sure we are starting on a y offset that doesn't have
6762 // a "special" set of coefficients. Basically, with exactly the right filter at exactly the right
6763 // resize at exactly the right phase, some of the coefficents can be zero. When they are zero, we
6764 // don't process them at all. But this leads to a tricky thing with the thread splits, where we
6765 // might have a set of two coeffs like this for example: (4,4) and (3,6). The 4,4 means there was
6766 // just one single coeff because things worked out perfectly (normally, they all have 4 coeffs
6767 // like the range 3,6. The problem is that if we start right on the (4,4) on a brand new thread,
6768 // then when we get to (3,6), we don't have the "3" sample in memory (because we didn't load
6769 // it on the initial (4,4) range because it didn't have a 3 (we only add new samples that are
6770 // larger than our existing samples - it's just how the eviction works). So, our solution here
6771 // is pretty simple, if we start right on a range that has samples that start earlier, then we
6772 // simply bump up our previous thread split range to include it, and then start this threads
6773 // range with the smaller sample. It just moves one scanline from one thread split to another,
6774 // so that we end with the unusual one, instead of start with it. To do this, we check 2-4
6775 // sample at each thread split start and then occassionally move them.
6776
6777 if ( ( is_gather ) && ( i ) )
6778 {
6779 stbir__contributors * small_contribs;
6780 int j, smallest, stop, start_n0;
6781 stbir__contributors * split_contribs = contribs + cur;
6782
6783 // scan for a max of 3x the filter width or until the next thread split
6784 stop = vertical_pixel_margin * 3;
6785 if ( each < stop )
6786 stop = each;
6787
6788 // loops a few times before early out
6789 smallest = 0;
6790 small_contribs = split_contribs;
6791 start_n0 = small_contribs->n0;
6792 for( j = 1 ; j <= stop ; j++ )
6793 {
6794 ++split_contribs;
6795 if ( split_contribs->n0 > start_n0 )
6796 break;
6797 if ( split_contribs->n0 < small_contribs->n0 )
6798 {
6799 small_contribs = split_contribs;
6800 smallest = j;
6801 }
6802 }
6803
6804 split_info[i-1].end_output_y += smallest;
6805 split_info[i].start_output_y += smallest;
6806 }
6807
6808 cur += each;
6809 left -= each;
6810
6811 // scatter range (updated to minimum as you run it)
6812 split_info[i].start_input_y = -vertical_pixel_margin;
6813 split_info[i].end_input_y = input_full_height + vertical_pixel_margin;
6814 }
6815}
6816
6817static void stbir__free_internal_mem( stbir__info *info )
6818{
6819 #define STBIR__FREE_AND_CLEAR( ptr ) { if ( ptr ) { void * p = (ptr); (ptr) = 0; STBIR_FREE( p, info->user_data); } }
6820
6821 if ( info )
6822 {
6823 #ifndef STBIR__SEPARATE_ALLOCATIONS
6824 STBIR__FREE_AND_CLEAR( info->alloced_mem );
6825 #else
6826 int i,j;
6827
6828 if ( ( info->vertical.gather_prescatter_contributors ) && ( (void*)info->vertical.gather_prescatter_contributors != (void*)info->split_info[0].decode_buffer ) )
6829 {
6830 STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_coefficients );
6831 STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_contributors );
6832 }
6833 for( i = 0 ; i < info->splits ; i++ )
6834 {
6835 for( j = 0 ; j < info->alloc_ring_buffer_num_entries ; j++ )
6836 {
6837 #ifdef STBIR_SIMD8
6838 if ( info->effective_channels == 3 )
6839 --info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer
6840 #endif
6841 STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers[j] );
6842 }
6843
6844 #ifdef STBIR_SIMD8
6845 if ( info->effective_channels == 3 )
6846 --info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer
6847 #endif
6848 STBIR__FREE_AND_CLEAR( info->split_info[i].decode_buffer );
6849 STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers );
6850 STBIR__FREE_AND_CLEAR( info->split_info[i].vertical_buffer );
6851 }
6852 STBIR__FREE_AND_CLEAR( info->split_info );
6853 if ( info->vertical.coefficients != info->horizontal.coefficients )
6854 {
6855 STBIR__FREE_AND_CLEAR( info->vertical.coefficients );
6856 STBIR__FREE_AND_CLEAR( info->vertical.contributors );
6857 }
6858 STBIR__FREE_AND_CLEAR( info->horizontal.coefficients );
6859 STBIR__FREE_AND_CLEAR( info->horizontal.contributors );
6860 STBIR__FREE_AND_CLEAR( info->alloced_mem );
6861 STBIR_FREE( info, info->user_data );
6862 #endif
6863 }
6864
6865 #undef STBIR__FREE_AND_CLEAR
6866}
6867
6868static int stbir__get_max_split( int splits, int height )
6869{
6870 int i;
6871 int max = 0;
6872
6873 for( i = 0 ; i < splits ; i++ )
6874 {
6875 int each = height / ( splits - i );
6876 if ( each > max )
6877 max = each;
6878 height -= each;
6879 }
6880 return max;
6881}
6882
6883static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_n_coeffs_funcs[8] =
6884{
6885 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
6886};
6887
6888static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_channels_funcs[8] =
6889{
6890 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
6891};
6892
6893// there are six resize classifications: 0 == vertical scatter, 1 == vertical gather < 1x scale, 2 == vertical gather 1x-2x scale, 4 == vertical gather < 3x scale, 4 == vertical gather > 3x scale, 5 == <=4 pixel height, 6 == <=4 pixel wide column
6894#define STBIR_RESIZE_CLASSIFICATIONS 8
6895
6896static float stbir__compute_weights[5][STBIR_RESIZE_CLASSIFICATIONS][4]= // 5 = 0=1chan, 1=2chan, 2=3chan, 3=4chan, 4=7chan
6897{
6898 {
6899 { 1.00000f, 1.00000f, 0.31250f, 1.00000f },
6900 { 0.56250f, 0.59375f, 0.00000f, 0.96875f },
6901 { 1.00000f, 0.06250f, 0.00000f, 1.00000f },
6902 { 0.00000f, 0.09375f, 1.00000f, 1.00000f },
6903 { 1.00000f, 1.00000f, 0.31250f, 1.00000f },
6904 { 0.03125f, 0.12500f, 1.00000f, 1.00000f },
6905 { 1.00000f, 1.00000f, 0.06250f, 1.00000f },
6906 { 0.00000f, 1.00000f, 0.00000f, 0.03125f },
6907 }, {
6908 { 0.00000f, 0.84375f, 0.00000f, 0.03125f },
6909 { 0.09375f, 0.93750f, 0.00000f, 0.78125f },
6910 { 0.87500f, 0.21875f, 0.00000f, 0.96875f },
6911 { 0.09375f, 0.09375f, 1.00000f, 1.00000f },
6912 { 0.00000f, 0.84375f, 0.00000f, 0.03125f },
6913 { 0.03125f, 0.12500f, 1.00000f, 1.00000f },
6914 { 1.00000f, 1.00000f, 0.06250f, 1.00000f },
6915 { 0.00000f, 1.00000f, 0.00000f, 0.53125f },
6916 }, {
6917 { 0.00000f, 0.53125f, 0.00000f, 0.03125f },
6918 { 0.06250f, 0.96875f, 0.00000f, 0.53125f },
6919 { 0.87500f, 0.18750f, 0.00000f, 0.93750f },
6920 { 0.00000f, 0.09375f, 1.00000f, 1.00000f },
6921 { 0.00000f, 0.53125f, 0.00000f, 0.03125f },
6922 { 0.03125f, 0.12500f, 1.00000f, 1.00000f },
6923 { 1.00000f, 1.00000f, 0.06250f, 1.00000f },
6924 { 0.00000f, 1.00000f, 0.00000f, 0.56250f },
6925 }, {
6926 { 0.00000f, 0.50000f, 0.00000f, 0.71875f },
6927 { 0.06250f, 0.84375f, 0.00000f, 0.87500f },
6928 { 1.00000f, 0.50000f, 0.50000f, 0.96875f },
6929 { 1.00000f, 0.09375f, 0.31250f, 0.50000f },
6930 { 0.00000f, 0.50000f, 0.00000f, 0.71875f },
6931 { 1.00000f, 0.03125f, 0.03125f, 0.53125f },
6932 { 1.00000f, 1.00000f, 0.06250f, 1.00000f },
6933 { 0.00000f, 1.00000f, 0.03125f, 0.18750f },
6934 }, {
6935 { 0.00000f, 0.59375f, 0.00000f, 0.96875f },
6936 { 0.06250f, 0.81250f, 0.06250f, 0.59375f },
6937 { 0.75000f, 0.43750f, 0.12500f, 0.96875f },
6938 { 0.87500f, 0.06250f, 0.18750f, 0.43750f },
6939 { 0.00000f, 0.59375f, 0.00000f, 0.96875f },
6940 { 0.15625f, 0.12500f, 1.00000f, 1.00000f },
6941 { 1.00000f, 1.00000f, 0.06250f, 1.00000f },
6942 { 0.00000f, 1.00000f, 0.03125f, 0.34375f },
6943 }
6944};
6945
6946// structure that allow us to query and override info for training the costs
6947typedef struct STBIR__V_FIRST_INFO
6948{
6949 double v_cost, h_cost;
6950 int control_v_first; // 0 = no control, 1 = force hori, 2 = force vert
6951 int v_first;
6952 int v_resize_classification;
6953 int is_gather;
6954} STBIR__V_FIRST_INFO;
6955
6956#ifdef STBIR__V_FIRST_INFO_BUFFER
6957static STBIR__V_FIRST_INFO STBIR__V_FIRST_INFO_BUFFER = {0};
6958#define STBIR__V_FIRST_INFO_POINTER &STBIR__V_FIRST_INFO_BUFFER
6959#else
6960#define STBIR__V_FIRST_INFO_POINTER 0
6961#endif
6962
6963// Figure out whether to scale along the horizontal or vertical first.
6964// This only *super* important when you are scaling by a massively
6965// different amount in the vertical vs the horizontal (for example, if
6966// you are scaling by 2x in the width, and 0.5x in the height, then you
6967// want to do the vertical scale first, because it's around 3x faster
6968// in that order.
6969//
6970// In more normal circumstances, this makes a 20-40% differences, so
6971// it's good to get right, but not critical. The normal way that you
6972// decide which direction goes first is just figuring out which
6973// direction does more multiplies. But with modern CPUs with their
6974// fancy caches and SIMD and high IPC abilities, so there's just a lot
6975// more that goes into it.
6976//
6977// My handwavy sort of solution is to have an app that does a whole
6978// bunch of timing for both vertical and horizontal first modes,
6979// and then another app that can read lots of these timing files
6980// and try to search for the best weights to use. Dotimings.c
6981// is the app that does a bunch of timings, and vf_train.c is the
6982// app that solves for the best weights (and shows how well it
6983// does currently).
6984
6985static int stbir__should_do_vertical_first( float weights_table[STBIR_RESIZE_CLASSIFICATIONS][4], int horizontal_filter_pixel_width, float horizontal_scale, int horizontal_output_size, int vertical_filter_pixel_width, float vertical_scale, int vertical_output_size, int is_gather, STBIR__V_FIRST_INFO * info )
6986{
6987 double v_cost, h_cost;
6988 float * weights;
6989 int vertical_first;
6990 int v_classification;
6991
6992 // categorize the resize into buckets
6993 if ( ( vertical_output_size <= 4 ) || ( horizontal_output_size <= 4 ) )
6994 v_classification = ( vertical_output_size < horizontal_output_size ) ? 6 : 7;
6995 else if ( ( !is_gather ) && ( ( vertical_output_size <= 16 ) || ( horizontal_output_size <= 16 ) ) )
6996 v_classification = 4;
6997 else if ( vertical_scale <= 1.0f )
6998 v_classification = ( is_gather ) ? 1 : 0;
6999 else if ( vertical_scale <= 2.0f)
7000 v_classification = 2;
7001 else if ( vertical_scale <= 3.0f)
7002 v_classification = 3;
7003 else
7004 v_classification = 5; // everything bigger than 3x
7005
7006 // use the right weights
7007 weights = weights_table[ v_classification ];
7008
7009 // this is the costs when you don't take into account modern CPUs with high ipc and simd and caches - wish we had a better estimate
7010 h_cost = (float)horizontal_filter_pixel_width * weights[0] + horizontal_scale * (float)vertical_filter_pixel_width * weights[1];
7011 v_cost = (float)vertical_filter_pixel_width * weights[2] + vertical_scale * (float)horizontal_filter_pixel_width * weights[3];
7012
7013 // use computation estimate to decide vertical first or not
7014 vertical_first = ( v_cost <= h_cost ) ? 1 : 0;
7015
7016 // save these, if requested
7017 if ( info )
7018 {
7019 info->h_cost = h_cost;
7020 info->v_cost = v_cost;
7021 info->v_resize_classification = v_classification;
7022 info->v_first = vertical_first;
7023 info->is_gather = is_gather;
7024 }
7025
7026 // and this allows us to override everything for testing (see dotiming.c)
7027 if ( ( info ) && ( info->control_v_first ) )
7028 vertical_first = ( info->control_v_first == 2 ) ? 1 : 0;
7029
7030 return vertical_first;
7031}
7032
7033// layout lookups - must match stbir_internal_pixel_layout
7034static unsigned char stbir__pixel_channels[] = {
7035 1,2,3,3,4, // 1ch, 2ch, rgb, bgr, 4ch
7036 4,4,4,4,2,2, // RGBA,BGRA,ARGB,ABGR,RA,AR
7037 4,4,4,4,2,2, // RGBA_PM,BGRA_PM,ARGB_PM,ABGR_PM,RA_PM,AR_PM
7038};
7039
7040// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types
7041// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible
7042static stbir_internal_pixel_layout stbir__pixel_layout_convert_public_to_internal[] = {
7043 STBIRI_BGR, STBIRI_1CHANNEL, STBIRI_2CHANNEL, STBIRI_RGB, STBIRI_RGBA,
7044 STBIRI_4CHANNEL, STBIRI_BGRA, STBIRI_ARGB, STBIRI_ABGR, STBIRI_RA, STBIRI_AR,
7045 STBIRI_RGBA_PM, STBIRI_BGRA_PM, STBIRI_ARGB_PM, STBIRI_ABGR_PM, STBIRI_RA_PM, STBIRI_AR_PM,
7046};
7047
7048static 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, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO )
7049{
7050 static char stbir_channel_count_index[8]={ 9,0,1,2, 3,9,9,4 };
7051
7052 stbir__info * info = 0;
7053 void * alloced = 0;
7054 size_t alloced_total = 0;
7055 int vertical_first;
7056 size_t decode_buffer_size, ring_buffer_length_bytes, ring_buffer_size, vertical_buffer_size;
7057 int alloc_ring_buffer_num_entries;
7058
7059 int alpha_weighting_type = 0; // 0=none, 1=simple, 2=fancy
7060 int conservative_split_output_size = stbir__get_max_split( splits, vertical->scale_info.output_sub_size );
7061 stbir_internal_pixel_layout input_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ input_pixel_layout_public ];
7062 stbir_internal_pixel_layout output_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ output_pixel_layout_public ];
7063 int channels = stbir__pixel_channels[ input_pixel_layout ];
7064 int effective_channels = channels;
7065
7066 // first figure out what type of alpha weighting to use (if any)
7067 if ( ( horizontal->filter_enum != STBIR_FILTER_POINT_SAMPLE ) || ( vertical->filter_enum != STBIR_FILTER_POINT_SAMPLE ) ) // no alpha weighting on point sampling
7068 {
7069 if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) )
7070 {
7071 if ( fast_alpha )
7072 {
7073 alpha_weighting_type = 4;
7074 }
7075 else
7076 {
7077 static int fancy_alpha_effective_cnts[6] = { 7, 7, 7, 7, 3, 3 };
7078 alpha_weighting_type = 2;
7079 effective_channels = fancy_alpha_effective_cnts[ input_pixel_layout - STBIRI_RGBA ];
7080 }
7081 }
7082 else if ( ( input_pixel_layout >= STBIRI_RGBA_PM ) && ( input_pixel_layout <= STBIRI_AR_PM ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) )
7083 {
7084 // input premult, output non-premult
7085 alpha_weighting_type = 3;
7086 }
7087 else if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA_PM ) && ( output_pixel_layout <= STBIRI_AR_PM ) )
7088 {
7089 // input non-premult, output premult
7090 alpha_weighting_type = 1;
7091 }
7092 }
7093
7094 // channel in and out count must match currently
7095 if ( channels != stbir__pixel_channels[ output_pixel_layout ] )
7096 return 0;
7097
7098 // get vertical first
7099 vertical_first = stbir__should_do_vertical_first( stbir__compute_weights[ (int)stbir_channel_count_index[ effective_channels ] ], horizontal->filter_pixel_width, horizontal->scale_info.scale, horizontal->scale_info.output_sub_size, vertical->filter_pixel_width, vertical->scale_info.scale, vertical->scale_info.output_sub_size, vertical->is_gather, STBIR__V_FIRST_INFO_POINTER );
7100
7101 // sometimes read one float off in some of the unrolled loops (with a weight of zero coeff, so it doesn't have an effect)
7102 // we use a few extra floats instead of just 1, so that input callback buffer can overlap with the decode buffer without
7103 // the conversion routines overwriting the callback input data.
7104 decode_buffer_size = ( conservative->n1 - conservative->n0 + 1 ) * effective_channels * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING; // extra floats for input callback stagger
7105
7106#if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8)
7107 if ( effective_channels == 3 )
7108 decode_buffer_size += sizeof(float); // avx in 3 channel mode needs one float at the start of the buffer (only with separate allocations)
7109#endif
7110
7111 ring_buffer_length_bytes = (size_t)horizontal->scale_info.output_sub_size * (size_t)effective_channels * sizeof(float) + sizeof(float)*STBIR_INPUT_CALLBACK_PADDING; // extra floats for padding
7112
7113 // if we do vertical first, the ring buffer holds a whole decoded line
7114 if ( vertical_first )
7115 ring_buffer_length_bytes = ( decode_buffer_size + 15 ) & ~15;
7116
7117 if ( ( ring_buffer_length_bytes & 4095 ) == 0 ) ring_buffer_length_bytes += 64*3; // avoid 4k alias
7118
7119 // One extra entry because floating point precision problems sometimes cause an extra to be necessary.
7120 alloc_ring_buffer_num_entries = vertical->filter_pixel_width + 1;
7121
7122 // we never need more ring buffer entries than the scanlines we're outputting when in scatter mode
7123 if ( ( !vertical->is_gather ) && ( alloc_ring_buffer_num_entries > conservative_split_output_size ) )
7124 alloc_ring_buffer_num_entries = conservative_split_output_size;
7125
7126 ring_buffer_size = (size_t)alloc_ring_buffer_num_entries * (size_t)ring_buffer_length_bytes;
7127
7128 // The vertical buffer is used differently, depending on whether we are scattering
7129 // the vertical scanlines, or gathering them.
7130 // If scattering, it's used at the temp buffer to accumulate each output.
7131 // If gathering, it's just the output buffer.
7132 vertical_buffer_size = (size_t)horizontal->scale_info.output_sub_size * (size_t)effective_channels * sizeof(float) + sizeof(float); // extra float for padding
7133
7134 // we make two passes through this loop, 1st to add everything up, 2nd to allocate and init
7135 for(;;)
7136 {
7137 int i;
7138 void * advance_mem = alloced;
7139 int copy_horizontal = 0;
7140 stbir__sampler * possibly_use_horizontal_for_pivot = 0;
7141
7142#ifdef STBIR__SEPARATE_ALLOCATIONS
7143 #define STBIR__NEXT_PTR( ptr, size, ntype ) if ( alloced ) { void * p = STBIR_MALLOC( size, user_data); if ( p == 0 ) { stbir__free_internal_mem( info ); return 0; } (ptr) = (ntype*)p; }
7144#else
7145 #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));
7146#endif
7147
7148 STBIR__NEXT_PTR( info, sizeof( stbir__info ), stbir__info );
7149
7150 STBIR__NEXT_PTR( info->split_info, sizeof( stbir__per_split_info ) * splits, stbir__per_split_info );
7151
7152 if ( info )
7153 {
7154 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 };
7155 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 };
7156 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 };
7157 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 };
7158
7159 // initialize info fields
7160 info->alloced_mem = alloced;
7161 info->alloced_total = alloced_total;
7162
7163 info->channels = channels;
7164 info->effective_channels = effective_channels;
7165
7166 info->offset_x = new_x;
7167 info->offset_y = new_y;
7168 info->alloc_ring_buffer_num_entries = (int)alloc_ring_buffer_num_entries;
7169 info->ring_buffer_num_entries = 0;
7170 info->ring_buffer_length_bytes = (int)ring_buffer_length_bytes;
7171 info->splits = splits;
7172 info->vertical_first = vertical_first;
7173
7174 info->input_pixel_layout_internal = input_pixel_layout;
7175 info->output_pixel_layout_internal = output_pixel_layout;
7176
7177 // setup alpha weight functions
7178 info->alpha_weight = 0;
7179 info->alpha_unweight = 0;
7180
7181 // handle alpha weighting functions and overrides
7182 if ( alpha_weighting_type == 2 )
7183 {
7184 // high quality alpha multiplying on the way in, dividing on the way out
7185 info->alpha_weight = fancy_alpha_weights[ input_pixel_layout - STBIRI_RGBA ];
7186 info->alpha_unweight = fancy_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ];
7187 }
7188 else if ( alpha_weighting_type == 4 )
7189 {
7190 // fast alpha multiplying on the way in, dividing on the way out
7191 info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ];
7192 info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ];
7193 }
7194 else if ( alpha_weighting_type == 1 )
7195 {
7196 // fast alpha on the way in, leave in premultiplied form on way out
7197 info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ];
7198 }
7199 else if ( alpha_weighting_type == 3 )
7200 {
7201 // incoming is premultiplied, fast alpha dividing on the way out - non-premultiplied output
7202 info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ];
7203 }
7204
7205 // handle 3-chan color flipping, using the alpha weight path
7206 if ( ( ( input_pixel_layout == STBIRI_RGB ) && ( output_pixel_layout == STBIRI_BGR ) ) ||
7207 ( ( input_pixel_layout == STBIRI_BGR ) && ( output_pixel_layout == STBIRI_RGB ) ) )
7208 {
7209 // do the flipping on the smaller of the two ends
7210 if ( horizontal->scale_info.scale < 1.0f )
7211 info->alpha_unweight = stbir__simple_flip_3ch;
7212 else
7213 info->alpha_weight = stbir__simple_flip_3ch;
7214 }
7215
7216 }
7217
7218 // get all the per-split buffers
7219 for( i = 0 ; i < splits ; i++ )
7220 {
7221 STBIR__NEXT_PTR( info->split_info[i].decode_buffer, decode_buffer_size, float );
7222
7223#ifdef STBIR__SEPARATE_ALLOCATIONS
7224
7225 #ifdef STBIR_SIMD8
7226 if ( ( info ) && ( effective_channels == 3 ) )
7227 ++info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer
7228 #endif
7229
7230 STBIR__NEXT_PTR( info->split_info[i].ring_buffers, alloc_ring_buffer_num_entries * sizeof(float*), float* );
7231 {
7232 int j;
7233 for( j = 0 ; j < alloc_ring_buffer_num_entries ; j++ )
7234 {
7235 STBIR__NEXT_PTR( info->split_info[i].ring_buffers[j], ring_buffer_length_bytes, float );
7236 #ifdef STBIR_SIMD8
7237 if ( ( info ) && ( effective_channels == 3 ) )
7238 ++info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer
7239 #endif
7240 }
7241 }
7242#else
7243 STBIR__NEXT_PTR( info->split_info[i].ring_buffer, ring_buffer_size, float );
7244#endif
7245 STBIR__NEXT_PTR( info->split_info[i].vertical_buffer, vertical_buffer_size, float );
7246 }
7247
7248 // alloc memory for to-be-pivoted coeffs (if necessary)
7249 if ( vertical->is_gather == 0 )
7250 {
7251 size_t both;
7252 size_t temp_mem_amt;
7253
7254 // when in vertical scatter mode, we first build the coefficients in gather mode, and then pivot after,
7255 // that means we need two buffers, so we try to use the decode buffer and ring buffer for this. if that
7256 // is too small, we just allocate extra memory to use as this temp.
7257
7258 both = (size_t)vertical->gather_prescatter_contributors_size + (size_t)vertical->gather_prescatter_coefficients_size;
7259
7260#ifdef STBIR__SEPARATE_ALLOCATIONS
7261 temp_mem_amt = decode_buffer_size;
7262
7263 #ifdef STBIR_SIMD8
7264 if ( effective_channels == 3 )
7265 --temp_mem_amt; // avx in 3 channel mode needs one float at the start of the buffer
7266 #endif
7267#else
7268 temp_mem_amt = (size_t)( decode_buffer_size + ring_buffer_size + vertical_buffer_size ) * (size_t)splits;
7269#endif
7270 if ( temp_mem_amt >= both )
7271 {
7272 if ( info )
7273 {
7274 vertical->gather_prescatter_contributors = (stbir__contributors*)info->split_info[0].decode_buffer;
7275 vertical->gather_prescatter_coefficients = (float*) ( ( (char*)info->split_info[0].decode_buffer ) + vertical->gather_prescatter_contributors_size );
7276 }
7277 }
7278 else
7279 {
7280 // ring+decode memory is too small, so allocate temp memory
7281 STBIR__NEXT_PTR( vertical->gather_prescatter_contributors, vertical->gather_prescatter_contributors_size, stbir__contributors );
7282 STBIR__NEXT_PTR( vertical->gather_prescatter_coefficients, vertical->gather_prescatter_coefficients_size, float );
7283 }
7284 }
7285
7286 STBIR__NEXT_PTR( horizontal->contributors, horizontal->contributors_size, stbir__contributors );
7287 STBIR__NEXT_PTR( horizontal->coefficients, horizontal->coefficients_size, float );
7288
7289 // are the two filters identical?? (happens a lot with mipmap generation)
7290 if ( ( horizontal->filter_kernel == vertical->filter_kernel ) && ( horizontal->filter_support == vertical->filter_support ) && ( horizontal->edge == vertical->edge ) && ( horizontal->scale_info.output_sub_size == vertical->scale_info.output_sub_size ) )
7291 {
7292 float diff_scale = horizontal->scale_info.scale - vertical->scale_info.scale;
7293 float diff_shift = horizontal->scale_info.pixel_shift - vertical->scale_info.pixel_shift;
7294 if ( diff_scale < 0.0f ) diff_scale = -diff_scale;
7295 if ( diff_shift < 0.0f ) diff_shift = -diff_shift;
7296 if ( ( diff_scale <= stbir__small_float ) && ( diff_shift <= stbir__small_float ) )
7297 {
7298 if ( horizontal->is_gather == vertical->is_gather )
7299 {
7300 copy_horizontal = 1;
7301 goto no_vert_alloc;
7302 }
7303 // everything matches, but vertical is scatter, horizontal is gather, use horizontal coeffs for vertical pivot coeffs
7304 possibly_use_horizontal_for_pivot = horizontal;
7305 }
7306 }
7307
7308 STBIR__NEXT_PTR( vertical->contributors, vertical->contributors_size, stbir__contributors );
7309 STBIR__NEXT_PTR( vertical->coefficients, vertical->coefficients_size, float );
7310
7311 no_vert_alloc:
7312
7313 if ( info )
7314 {
7315 STBIR_PROFILE_BUILD_START( horizontal );
7316
7317 stbir__calculate_filters( horizontal, 0, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO );
7318
7319 // setup the horizontal gather functions
7320 // start with defaulting to the n_coeffs functions (specialized on channels and remnant leftover)
7321 info->horizontal_gather_channels = stbir__horizontal_gather_n_coeffs_funcs[ effective_channels ][ horizontal->extent_info.widest & 3 ];
7322 // but if the number of coeffs <= 12, use another set of special cases. <=12 coeffs is any enlarging resize, or shrinking resize down to about 1/3 size
7323 if ( horizontal->extent_info.widest <= 12 )
7324 info->horizontal_gather_channels = stbir__horizontal_gather_channels_funcs[ effective_channels ][ horizontal->extent_info.widest - 1 ];
7325
7326 info->scanline_extents.conservative.n0 = conservative->n0;
7327 info->scanline_extents.conservative.n1 = conservative->n1;
7328
7329 // get exact extents
7330 stbir__get_extents( horizontal, &info->scanline_extents );
7331
7332 // pack the horizontal coeffs
7333 horizontal->coefficient_width = stbir__pack_coefficients(horizontal->num_contributors, horizontal->contributors, horizontal->coefficients, horizontal->coefficient_width, horizontal->extent_info.widest, info->scanline_extents.conservative.n0, info->scanline_extents.conservative.n1 );
7334
7335 STBIR_MEMCPY( &info->horizontal, horizontal, sizeof( stbir__sampler ) );
7336
7337 STBIR_PROFILE_BUILD_END( horizontal );
7338
7339 if ( copy_horizontal )
7340 {
7341 STBIR_MEMCPY( &info->vertical, horizontal, sizeof( stbir__sampler ) );
7342 }
7343 else
7344 {
7345 STBIR_PROFILE_BUILD_START( vertical );
7346
7347 stbir__calculate_filters( vertical, possibly_use_horizontal_for_pivot, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO );
7348 STBIR_MEMCPY( &info->vertical, vertical, sizeof( stbir__sampler ) );
7349
7350 STBIR_PROFILE_BUILD_END( vertical );
7351 }
7352
7353 // setup the vertical split ranges
7354 stbir__get_split_info( info->split_info, info->splits, 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.contributors );
7355
7356 // now we know precisely how many entries we need
7357 info->ring_buffer_num_entries = info->vertical.extent_info.widest;
7358
7359 // we never need more ring buffer entries than the scanlines we're outputting
7360 if ( ( !info->vertical.is_gather ) && ( info->ring_buffer_num_entries > conservative_split_output_size ) )
7361 info->ring_buffer_num_entries = conservative_split_output_size;
7362 STBIR_ASSERT( info->ring_buffer_num_entries <= info->alloc_ring_buffer_num_entries );
7363 }
7364 #undef STBIR__NEXT_PTR
7365
7366
7367 // is this the first time through loop?
7368 if ( info == 0 )
7369 {
7370 alloced_total = ( 15 + (size_t)advance_mem );
7371 alloced = STBIR_MALLOC( alloced_total, user_data );
7372 if ( alloced == 0 )
7373 return 0;
7374 }
7375 else
7376 return info; // success
7377 }
7378}
7379
7380static int stbir__perform_resize( stbir__info const * info, int split_start, int split_count )
7381{
7382 stbir__per_split_info * split_info = info->split_info + split_start;
7383
7384 STBIR_PROFILE_CLEAR_EXTRAS();
7385
7386 STBIR_PROFILE_FIRST_START( looping );
7387 if (info->vertical.is_gather)
7388 stbir__vertical_gather_loop( info, split_info, split_count );
7389 else
7390 stbir__vertical_scatter_loop( info, split_info, split_count );
7391 STBIR_PROFILE_END( looping );
7392
7393 return 1;
7394}
7395
7396static void stbir__update_info_from_resize( stbir__info * info, STBIR_RESIZE * resize )
7397{
7398 static stbir__decode_pixels_func * decode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]=
7399 {
7400 /* 1ch-4ch */ stbir__decode_uint8_srgb, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear,
7401 };
7402
7403 static stbir__decode_pixels_func * decode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]=
7404 {
7405 { /* RGBA */ stbir__decode_uint8_srgb4_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear },
7406 { /* BGRA */ stbir__decode_uint8_srgb4_linearalpha_BGRA, stbir__decode_uint8_srgb_BGRA, 0, stbir__decode_float_linear_BGRA, stbir__decode_half_float_linear_BGRA },
7407 { /* ARGB */ stbir__decode_uint8_srgb4_linearalpha_ARGB, stbir__decode_uint8_srgb_ARGB, 0, stbir__decode_float_linear_ARGB, stbir__decode_half_float_linear_ARGB },
7408 { /* ABGR */ stbir__decode_uint8_srgb4_linearalpha_ABGR, stbir__decode_uint8_srgb_ABGR, 0, stbir__decode_float_linear_ABGR, stbir__decode_half_float_linear_ABGR },
7409 { /* RA */ stbir__decode_uint8_srgb2_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear },
7410 { /* AR */ stbir__decode_uint8_srgb2_linearalpha_AR, stbir__decode_uint8_srgb_AR, 0, stbir__decode_float_linear_AR, stbir__decode_half_float_linear_AR },
7411 };
7412
7413 static stbir__decode_pixels_func * decode_simple_scaled_or_not[2][2]=
7414 {
7415 { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear },
7416 };
7417
7418 static stbir__decode_pixels_func * decode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]=
7419 {
7420 { /* RGBA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } },
7421 { /* BGRA */ { stbir__decode_uint8_linear_scaled_BGRA, stbir__decode_uint8_linear_BGRA }, { stbir__decode_uint16_linear_scaled_BGRA, stbir__decode_uint16_linear_BGRA } },
7422 { /* ARGB */ { stbir__decode_uint8_linear_scaled_ARGB, stbir__decode_uint8_linear_ARGB }, { stbir__decode_uint16_linear_scaled_ARGB, stbir__decode_uint16_linear_ARGB } },
7423 { /* ABGR */ { stbir__decode_uint8_linear_scaled_ABGR, stbir__decode_uint8_linear_ABGR }, { stbir__decode_uint16_linear_scaled_ABGR, stbir__decode_uint16_linear_ABGR } },
7424 { /* RA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } },
7425 { /* AR */ { stbir__decode_uint8_linear_scaled_AR, stbir__decode_uint8_linear_AR }, { stbir__decode_uint16_linear_scaled_AR, stbir__decode_uint16_linear_AR } }
7426 };
7427
7428 static stbir__encode_pixels_func * encode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]=
7429 {
7430 /* 1ch-4ch */ stbir__encode_uint8_srgb, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear,
7431 };
7432
7433 static stbir__encode_pixels_func * encode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]=
7434 {
7435 { /* RGBA */ stbir__encode_uint8_srgb4_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear },
7436 { /* BGRA */ stbir__encode_uint8_srgb4_linearalpha_BGRA, stbir__encode_uint8_srgb_BGRA, 0, stbir__encode_float_linear_BGRA, stbir__encode_half_float_linear_BGRA },
7437 { /* ARGB */ stbir__encode_uint8_srgb4_linearalpha_ARGB, stbir__encode_uint8_srgb_ARGB, 0, stbir__encode_float_linear_ARGB, stbir__encode_half_float_linear_ARGB },
7438 { /* ABGR */ stbir__encode_uint8_srgb4_linearalpha_ABGR, stbir__encode_uint8_srgb_ABGR, 0, stbir__encode_float_linear_ABGR, stbir__encode_half_float_linear_ABGR },
7439 { /* RA */ stbir__encode_uint8_srgb2_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear },
7440 { /* AR */ stbir__encode_uint8_srgb2_linearalpha_AR, stbir__encode_uint8_srgb_AR, 0, stbir__encode_float_linear_AR, stbir__encode_half_float_linear_AR }
7441 };
7442
7443 static stbir__encode_pixels_func * encode_simple_scaled_or_not[2][2]=
7444 {
7445 { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear },
7446 };
7447
7448 static stbir__encode_pixels_func * encode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]=
7449 {
7450 { /* RGBA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } },
7451 { /* BGRA */ { stbir__encode_uint8_linear_scaled_BGRA, stbir__encode_uint8_linear_BGRA }, { stbir__encode_uint16_linear_scaled_BGRA, stbir__encode_uint16_linear_BGRA } },
7452 { /* ARGB */ { stbir__encode_uint8_linear_scaled_ARGB, stbir__encode_uint8_linear_ARGB }, { stbir__encode_uint16_linear_scaled_ARGB, stbir__encode_uint16_linear_ARGB } },
7453 { /* ABGR */ { stbir__encode_uint8_linear_scaled_ABGR, stbir__encode_uint8_linear_ABGR }, { stbir__encode_uint16_linear_scaled_ABGR, stbir__encode_uint16_linear_ABGR } },
7454 { /* RA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } },
7455 { /* AR */ { stbir__encode_uint8_linear_scaled_AR, stbir__encode_uint8_linear_AR }, { stbir__encode_uint16_linear_scaled_AR, stbir__encode_uint16_linear_AR } }
7456 };
7457
7458 stbir__decode_pixels_func * decode_pixels = 0;
7459 stbir__encode_pixels_func * encode_pixels = 0;
7460 stbir_datatype input_type, output_type;
7461
7462 input_type = resize->input_data_type;
7463 output_type = resize->output_data_type;
7464 info->input_data = resize->input_pixels;
7465 info->input_stride_bytes = resize->input_stride_in_bytes;
7466 info->output_stride_bytes = resize->output_stride_in_bytes;
7467
7468 // if we're completely point sampling, then we can turn off SRGB
7469 if ( ( info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( info->vertical.filter_enum == STBIR_FILTER_POINT_SAMPLE ) )
7470 {
7471 if ( ( ( input_type == STBIR_TYPE_UINT8_SRGB ) || ( input_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) &&
7472 ( ( output_type == STBIR_TYPE_UINT8_SRGB ) || ( output_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) )
7473 {
7474 input_type = STBIR_TYPE_UINT8;
7475 output_type = STBIR_TYPE_UINT8;
7476 }
7477 }
7478
7479 // recalc the output and input strides
7480 if ( info->input_stride_bytes == 0 )
7481 info->input_stride_bytes = info->channels * info->horizontal.scale_info.input_full_size * stbir__type_size[input_type];
7482
7483 if ( info->output_stride_bytes == 0 )
7484 info->output_stride_bytes = info->channels * info->horizontal.scale_info.output_sub_size * stbir__type_size[output_type];
7485
7486 // calc offset
7487 info->output_data = ( (char*) resize->output_pixels ) + ( (size_t) info->offset_y * (size_t) resize->output_stride_in_bytes ) + ( info->offset_x * info->channels * stbir__type_size[output_type] );
7488
7489 info->in_pixels_cb = resize->input_cb;
7490 info->user_data = resize->user_data;
7491 info->out_pixels_cb = resize->output_cb;
7492
7493 // setup the input format converters
7494 if ( ( input_type == STBIR_TYPE_UINT8 ) || ( input_type == STBIR_TYPE_UINT16 ) )
7495 {
7496 int non_scaled = 0;
7497
7498 // 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)
7499 if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual)
7500 if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) )
7501 non_scaled = 1;
7502
7503 if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL )
7504 decode_pixels = decode_simple_scaled_or_not[ input_type == STBIR_TYPE_UINT16 ][ non_scaled ];
7505 else
7506 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 ];
7507 }
7508 else
7509 {
7510 if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL )
7511 decode_pixels = decode_simple[ input_type - STBIR_TYPE_UINT8_SRGB ];
7512 else
7513 decode_pixels = decode_alphas[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type - STBIR_TYPE_UINT8_SRGB ];
7514 }
7515
7516 // setup the output format converters
7517 if ( ( output_type == STBIR_TYPE_UINT8 ) || ( output_type == STBIR_TYPE_UINT16 ) )
7518 {
7519 int non_scaled = 0;
7520
7521 // 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)
7522 if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual)
7523 if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) )
7524 non_scaled = 1;
7525
7526 if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL )
7527 encode_pixels = encode_simple_scaled_or_not[ output_type == STBIR_TYPE_UINT16 ][ non_scaled ];
7528 else
7529 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 ];
7530 }
7531 else
7532 {
7533 if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL )
7534 encode_pixels = encode_simple[ output_type - STBIR_TYPE_UINT8_SRGB ];
7535 else
7536 encode_pixels = encode_alphas[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type - STBIR_TYPE_UINT8_SRGB ];
7537 }
7538
7539 info->input_type = input_type;
7540 info->output_type = output_type;
7541 info->decode_pixels = decode_pixels;
7542 info->encode_pixels = encode_pixels;
7543}
7544
7545static void stbir__clip( int * outx, int * outsubw, int outw, double * u0, double * u1 )
7546{
7547 double per, adj;
7548 int over;
7549
7550 // do left/top edge
7551 if ( *outx < 0 )
7552 {
7553 per = ( (double)*outx ) / ( (double)*outsubw ); // is negative
7554 adj = per * ( *u1 - *u0 );
7555 *u0 -= adj; // increases u0
7556 *outx = 0;
7557 }
7558
7559 // do right/bot edge
7560 over = outw - ( *outx + *outsubw );
7561 if ( over < 0 )
7562 {
7563 per = ( (double)over ) / ( (double)*outsubw ); // is negative
7564 adj = per * ( *u1 - *u0 );
7565 *u1 += adj; // decrease u1
7566 *outsubw = outw - *outx;
7567 }
7568}
7569
7570// converts a double to a rational that has less than one float bit of error (returns 0 if unable to do so)
7571static int stbir__double_to_rational(double f, stbir_uint32 limit, stbir_uint32 *numer, stbir_uint32 *denom, int limit_denom ) // limit_denom (1) or limit numer (0)
7572{
7573 double err;
7574 stbir_uint64 top, bot;
7575 stbir_uint64 numer_last = 0;
7576 stbir_uint64 denom_last = 1;
7577 stbir_uint64 numer_estimate = 1;
7578 stbir_uint64 denom_estimate = 0;
7579
7580 // scale to past float error range
7581 top = (stbir_uint64)( f * (double)(1 << 25) );
7582 bot = 1 << 25;
7583
7584 // keep refining, but usually stops in a few loops - usually 5 for bad cases
7585 for(;;)
7586 {
7587 stbir_uint64 est, temp;
7588
7589 // hit limit, break out and do best full range estimate
7590 if ( ( ( limit_denom ) ? denom_estimate : numer_estimate ) >= limit )
7591 break;
7592
7593 // is the current error less than 1 bit of a float? if so, we're done
7594 if ( denom_estimate )
7595 {
7596 err = ( (double)numer_estimate / (double)denom_estimate ) - f;
7597 if ( err < 0.0 ) err = -err;
7598 if ( err < ( 1.0 / (double)(1<<24) ) )
7599 {
7600 // yup, found it
7601 *numer = (stbir_uint32) numer_estimate;
7602 *denom = (stbir_uint32) denom_estimate;
7603 return 1;
7604 }
7605 }
7606
7607 // no more refinement bits left? break out and do full range estimate
7608 if ( bot == 0 )
7609 break;
7610
7611 // gcd the estimate bits
7612 est = top / bot;
7613 temp = top % bot;
7614 top = bot;
7615 bot = temp;
7616
7617 // move remainders
7618 temp = est * denom_estimate + denom_last;
7619 denom_last = denom_estimate;
7620 denom_estimate = temp;
7621
7622 // move remainders
7623 temp = est * numer_estimate + numer_last;
7624 numer_last = numer_estimate;
7625 numer_estimate = temp;
7626 }
7627
7628 // we didn't find anything good enough for float, use a full range estimate
7629 if ( limit_denom )
7630 {
7631 numer_estimate= (stbir_uint64)( f * (double)limit + 0.5 );
7632 denom_estimate = limit;
7633 }
7634 else
7635 {
7636 numer_estimate = limit;
7637 denom_estimate = (stbir_uint64)( ( (double)limit / f ) + 0.5 );
7638 }
7639
7640 *numer = (stbir_uint32) numer_estimate;
7641 *denom = (stbir_uint32) denom_estimate;
7642
7643 err = ( denom_estimate ) ? ( ( (double)(stbir_uint32)numer_estimate / (double)(stbir_uint32)denom_estimate ) - f ) : 1.0;
7644 if ( err < 0.0 ) err = -err;
7645 return ( err < ( 1.0 / (double)(1<<24) ) ) ? 1 : 0;
7646}
7647
7648static 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, double input_s0, double input_s1 )
7649{
7650 double output_range, input_range, output_s, input_s, ratio, scale;
7651
7652 input_s = input_s1 - input_s0;
7653
7654 // null area
7655 if ( ( output_full_range == 0 ) || ( input_full_range == 0 ) ||
7656 ( output_sub_range == 0 ) || ( input_s <= stbir__small_float ) )
7657 return 0;
7658
7659 // are either of the ranges completely out of bounds?
7660 if ( ( *output_offset >= output_full_range ) || ( ( *output_offset + output_sub_range ) <= 0 ) || ( input_s0 >= (1.0f-stbir__small_float) ) || ( input_s1 <= stbir__small_float ) )
7661 return 0;
7662
7663 output_range = (double)output_full_range;
7664 input_range = (double)input_full_range;
7665
7666 output_s = ( (double)output_sub_range) / output_range;
7667
7668 // figure out the scaling to use
7669 ratio = output_s / input_s;
7670
7671 // save scale before clipping
7672 scale = ( output_range / input_range ) * ratio;
7673 scale_info->scale = (float)scale;
7674 scale_info->inv_scale = (float)( 1.0 / scale );
7675
7676 // clip output area to left/right output edges (and adjust input area)
7677 stbir__clip( output_offset, &output_sub_range, output_full_range, &input_s0, &input_s1 );
7678
7679 // recalc input area
7680 input_s = input_s1 - input_s0;
7681
7682 // after clipping do we have zero input area?
7683 if ( input_s <= stbir__small_float )
7684 return 0;
7685
7686 // calculate and store the starting source offsets in output pixel space
7687 scale_info->pixel_shift = (float) ( input_s0 * ratio * output_range );
7688
7689 scale_info->scale_is_rational = stbir__double_to_rational( scale, ( scale <= 1.0 ) ? output_full_range : input_full_range, &scale_info->scale_numerator, &scale_info->scale_denominator, ( scale >= 1.0 ) );
7690
7691 scale_info->input_full_size = input_full_range;
7692 scale_info->output_sub_size = output_sub_range;
7693
7694 return 1;
7695}
7696
7697
7698static void stbir__init_and_set_layout( STBIR_RESIZE * resize, stbir_pixel_layout pixel_layout, stbir_datatype data_type )
7699{
7700 resize->input_cb = 0;
7701 resize->output_cb = 0;
7702 resize->user_data = resize;
7703 resize->samplers = 0;
7704 resize->called_alloc = 0;
7705 resize->horizontal_filter = STBIR_FILTER_DEFAULT;
7706 resize->horizontal_filter_kernel = 0; resize->horizontal_filter_support = 0;
7707 resize->vertical_filter = STBIR_FILTER_DEFAULT;
7708 resize->vertical_filter_kernel = 0; resize->vertical_filter_support = 0;
7709 resize->horizontal_edge = STBIR_EDGE_CLAMP;
7710 resize->vertical_edge = STBIR_EDGE_CLAMP;
7711 resize->input_s0 = 0; resize->input_t0 = 0; resize->input_s1 = 1; resize->input_t1 = 1;
7712 resize->output_subx = 0; resize->output_suby = 0; resize->output_subw = resize->output_w; resize->output_subh = resize->output_h;
7713 resize->input_data_type = data_type;
7714 resize->output_data_type = data_type;
7715 resize->input_pixel_layout_public = pixel_layout;
7716 resize->output_pixel_layout_public = pixel_layout;
7717 resize->needs_rebuild = 1;
7718}
7719
7720STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize,
7721 const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero
7722 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero
7723 stbir_pixel_layout pixel_layout, stbir_datatype data_type )
7724{
7725 resize->input_pixels = input_pixels;
7726 resize->input_w = input_w;
7727 resize->input_h = input_h;
7728 resize->input_stride_in_bytes = input_stride_in_bytes;
7729 resize->output_pixels = output_pixels;
7730 resize->output_w = output_w;
7731 resize->output_h = output_h;
7732 resize->output_stride_in_bytes = output_stride_in_bytes;
7733 resize->fast_alpha = 0;
7734
7735 stbir__init_and_set_layout( resize, pixel_layout, data_type );
7736}
7737
7738// You can update parameters any time after resize_init
7739STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ) // by default, datatype from resize_init
7740{
7741 resize->input_data_type = input_type;
7742 resize->output_data_type = output_type;
7743 if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
7744 stbir__update_info_from_resize( resize->samplers, resize );
7745}
7746
7747STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ) // no callbacks by default
7748{
7749 resize->input_cb = input_cb;
7750 resize->output_cb = output_cb;
7751
7752 if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
7753 {
7754 resize->samplers->in_pixels_cb = input_cb;
7755 resize->samplers->out_pixels_cb = output_cb;
7756 }
7757}
7758
7759STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ) // pass back STBIR_RESIZE* by default
7760{
7761 resize->user_data = user_data;
7762 if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
7763 resize->samplers->user_data = user_data;
7764}
7765
7766STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes )
7767{
7768 resize->input_pixels = input_pixels;
7769 resize->input_stride_in_bytes = input_stride_in_bytes;
7770 resize->output_pixels = output_pixels;
7771 resize->output_stride_in_bytes = output_stride_in_bytes;
7772 if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
7773 stbir__update_info_from_resize( resize->samplers, resize );
7774}
7775
7776
7777STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ) // CLAMP by default
7778{
7779 resize->horizontal_edge = horizontal_edge;
7780 resize->vertical_edge = vertical_edge;
7781 resize->needs_rebuild = 1;
7782 return 1;
7783}
7784
7785STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ) // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default
7786{
7787 resize->horizontal_filter = horizontal_filter;
7788 resize->vertical_filter = vertical_filter;
7789 resize->needs_rebuild = 1;
7790 return 1;
7791}
7792
7793STBIRDEF 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 )
7794{
7795 resize->horizontal_filter_kernel = horizontal_filter; resize->horizontal_filter_support = horizontal_support;
7796 resize->vertical_filter_kernel = vertical_filter; resize->vertical_filter_support = vertical_support;
7797 resize->needs_rebuild = 1;
7798 return 1;
7799}
7800
7801STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ) // sets new pixel layouts
7802{
7803 resize->input_pixel_layout_public = input_pixel_layout;
7804 resize->output_pixel_layout_public = output_pixel_layout;
7805 resize->needs_rebuild = 1;
7806 return 1;
7807}
7808
7809
7810STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ) // sets alpha speed
7811{
7812 resize->fast_alpha = non_pma_alpha_speed_over_quality;
7813 resize->needs_rebuild = 1;
7814 return 1;
7815}
7816
7817STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ) // sets input region (full region by default)
7818{
7819 resize->input_s0 = s0;
7820 resize->input_t0 = t0;
7821 resize->input_s1 = s1;
7822 resize->input_t1 = t1;
7823 resize->needs_rebuild = 1;
7824
7825 // are we inbounds?
7826 if ( ( s1 < stbir__small_float ) || ( (s1-s0) < stbir__small_float ) ||
7827 ( t1 < stbir__small_float ) || ( (t1-t0) < stbir__small_float ) ||
7828 ( s0 > (1.0f-stbir__small_float) ) ||
7829 ( t0 > (1.0f-stbir__small_float) ) )
7830 return 0;
7831
7832 return 1;
7833}
7834
7835STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets input region (full region by default)
7836{
7837 resize->output_subx = subx;
7838 resize->output_suby = suby;
7839 resize->output_subw = subw;
7840 resize->output_subh = subh;
7841 resize->needs_rebuild = 1;
7842
7843 // are we inbounds?
7844 if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) )
7845 return 0;
7846
7847 return 1;
7848}
7849
7850STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets both regions (full regions by default)
7851{
7852 double s0, t0, s1, t1;
7853
7854 s0 = ( (double)subx ) / ( (double)resize->output_w );
7855 t0 = ( (double)suby ) / ( (double)resize->output_h );
7856 s1 = ( (double)(subx+subw) ) / ( (double)resize->output_w );
7857 t1 = ( (double)(suby+subh) ) / ( (double)resize->output_h );
7858
7859 resize->input_s0 = s0;
7860 resize->input_t0 = t0;
7861 resize->input_s1 = s1;
7862 resize->input_t1 = t1;
7863 resize->output_subx = subx;
7864 resize->output_suby = suby;
7865 resize->output_subw = subw;
7866 resize->output_subh = subh;
7867 resize->needs_rebuild = 1;
7868
7869 // are we inbounds?
7870 if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) )
7871 return 0;
7872
7873 return 1;
7874}
7875
7876static int stbir__perform_build( STBIR_RESIZE * resize, int splits )
7877{
7878 stbir__contributors conservative = { 0, 0 };
7879 stbir__sampler horizontal, vertical;
7880 int new_output_subx, new_output_suby;
7881 stbir__info * out_info;
7882 #ifdef STBIR_PROFILE
7883 stbir__info profile_infod; // used to contain building profile info before everything is allocated
7884 stbir__info * profile_info = &profile_infod;
7885 #endif
7886
7887 // have we already built the samplers?
7888 if ( resize->samplers )
7889 return 0;
7890
7891 #define STBIR_RETURN_ERROR_AND_ASSERT( exp ) STBIR_ASSERT( !(exp) ); if (exp) return 0;
7892 STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->horizontal_filter >= STBIR_FILTER_OTHER)
7893 STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->vertical_filter >= STBIR_FILTER_OTHER)
7894 #undef STBIR_RETURN_ERROR_AND_ASSERT
7895
7896 if ( splits <= 0 )
7897 return 0;
7898
7899 STBIR_PROFILE_BUILD_FIRST_START( build );
7900
7901 new_output_subx = resize->output_subx;
7902 new_output_suby = resize->output_suby;
7903
7904 // do horizontal clip and scale calcs
7905 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 ) )
7906 return 0;
7907
7908 // do vertical clip and scale calcs
7909 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 ) )
7910 return 0;
7911
7912 // if nothing to do, just return
7913 if ( ( horizontal.scale_info.output_sub_size == 0 ) || ( vertical.scale_info.output_sub_size == 0 ) )
7914 return 0;
7915
7916 stbir__set_sampler(&horizontal, resize->horizontal_filter, resize->horizontal_filter_kernel, resize->horizontal_filter_support, resize->horizontal_edge, &horizontal.scale_info, 1, resize->user_data );
7917 stbir__get_conservative_extents( &horizontal, &conservative, resize->user_data );
7918 stbir__set_sampler(&vertical, resize->vertical_filter, resize->vertical_filter_kernel, resize->vertical_filter_support, resize->vertical_edge, &vertical.scale_info, 0, resize->user_data );
7919
7920 if ( ( vertical.scale_info.output_sub_size / splits ) < STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS ) // each split should be a minimum of 4 scanlines (handwavey choice)
7921 {
7922 splits = vertical.scale_info.output_sub_size / STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS;
7923 if ( splits == 0 ) splits = 1;
7924 }
7925
7926 STBIR_PROFILE_BUILD_START( alloc );
7927 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, resize->user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO );
7928 STBIR_PROFILE_BUILD_END( alloc );
7929 STBIR_PROFILE_BUILD_END( build );
7930
7931 if ( out_info )
7932 {
7933 resize->splits = splits;
7934 resize->samplers = out_info;
7935 resize->needs_rebuild = 0;
7936 #ifdef STBIR_PROFILE
7937 STBIR_MEMCPY( &out_info->profile, &profile_infod.profile, sizeof( out_info->profile ) );
7938 #endif
7939
7940 // update anything that can be changed without recalcing samplers
7941 stbir__update_info_from_resize( out_info, resize );
7942
7943 return splits;
7944 }
7945
7946 return 0;
7947}
7948
7949STBIRDEF void stbir_free_samplers( STBIR_RESIZE * resize )
7950{
7951 if ( resize->samplers )
7952 {
7953 stbir__free_internal_mem( resize->samplers );
7954 resize->samplers = 0;
7955 resize->called_alloc = 0;
7956 }
7957}
7958
7959STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int splits )
7960{
7961 if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) )
7962 {
7963 if ( resize->samplers )
7964 stbir_free_samplers( resize );
7965
7966 resize->called_alloc = 1;
7967 return stbir__perform_build( resize, splits );
7968 }
7969
7970 STBIR_PROFILE_BUILD_CLEAR( resize->samplers );
7971
7972 return 1;
7973}
7974
7975STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize )
7976{
7977 return stbir_build_samplers_with_splits( resize, 1 );
7978}
7979
7980STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize )
7981{
7982 int result;
7983
7984 if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) )
7985 {
7986 int alloc_state = resize->called_alloc; // remember allocated state
7987
7988 if ( resize->samplers )
7989 {
7990 stbir__free_internal_mem( resize->samplers );
7991 resize->samplers = 0;
7992 }
7993
7994 if ( !stbir_build_samplers( resize ) )
7995 return 0;
7996
7997 resize->called_alloc = alloc_state;
7998
7999 // if build_samplers succeeded (above), but there are no samplers set, then
8000 // the area to stretch into was zero pixels, so don't do anything and return
8001 // success
8002 if ( resize->samplers == 0 )
8003 return 1;
8004 }
8005 else
8006 {
8007 // didn't build anything - clear it
8008 STBIR_PROFILE_BUILD_CLEAR( resize->samplers );
8009 }
8010
8011 // do resize
8012 result = stbir__perform_resize( resize->samplers, 0, resize->splits );
8013
8014 // if we alloced, then free
8015 if ( !resize->called_alloc )
8016 {
8017 stbir_free_samplers( resize );
8018 resize->samplers = 0;
8019 }
8020
8021 return result;
8022}
8023
8024STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count )
8025{
8026 STBIR_ASSERT( resize->samplers );
8027
8028 // if we're just doing the whole thing, call full
8029 if ( ( split_start == -1 ) || ( ( split_start == 0 ) && ( split_count == resize->splits ) ) )
8030 return stbir_resize_extended( resize );
8031
8032 // you **must** build samplers first when using split resize
8033 if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) )
8034 return 0;
8035
8036 if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) )
8037 return 0;
8038
8039 // do resize
8040 return stbir__perform_resize( resize->samplers, split_start, split_count );
8041}
8042
8043
8044static void * stbir_quick_resize_helper( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes,
8045 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
8046 stbir_pixel_layout pixel_layout, stbir_datatype data_type, stbir_edge edge, stbir_filter filter )
8047{
8048 STBIR_RESIZE resize;
8049 int scanline_output_in_bytes;
8050 int positive_output_stride_in_bytes;
8051 void * start_ptr;
8052 void * free_ptr;
8053
8054 scanline_output_in_bytes = output_w * stbir__type_size[ data_type ] * stbir__pixel_channels[ stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ];
8055 if ( scanline_output_in_bytes == 0 )
8056 return 0;
8057
8058 // if zero stride, use scanline output
8059 if ( output_stride_in_bytes == 0 )
8060 output_stride_in_bytes = scanline_output_in_bytes;
8061
8062 // abs value for inverted images (negative pitches)
8063 positive_output_stride_in_bytes = output_stride_in_bytes;
8064 if ( positive_output_stride_in_bytes < 0 )
8065 positive_output_stride_in_bytes = -positive_output_stride_in_bytes;
8066
8067 // is the requested stride smaller than the scanline output? if so, just fail
8068 if ( positive_output_stride_in_bytes < scanline_output_in_bytes )
8069 return 0;
8070
8071 start_ptr = output_pixels;
8072 free_ptr = 0; // no free pointer, since they passed buffer to use
8073
8074 // did they pass a zero for the dest? if so, allocate the buffer
8075 if ( output_pixels == 0 )
8076 {
8077 size_t size;
8078 char * ptr;
8079
8080 size = (size_t)positive_output_stride_in_bytes * (size_t)output_h;
8081 if ( size == 0 )
8082 return 0;
8083
8084 ptr = (char*) STBIR_MALLOC( size, 0 );
8085 if ( ptr == 0 )
8086 return 0;
8087
8088 free_ptr = ptr;
8089
8090 // point at the last scanline, if they requested a flipped image
8091 if ( output_stride_in_bytes < 0 )
8092 start_ptr = ptr + ( (size_t)positive_output_stride_in_bytes * (size_t)( output_h - 1 ) );
8093 else
8094 start_ptr = ptr;
8095 }
8096
8097 // ok, now do the resize
8098 stbir_resize_init( &resize,
8099 input_pixels, input_w, input_h, input_stride_in_bytes,
8100 start_ptr, output_w, output_h, output_stride_in_bytes,
8101 pixel_layout, data_type );
8102
8103 resize.horizontal_edge = edge;
8104 resize.vertical_edge = edge;
8105 resize.horizontal_filter = filter;
8106 resize.vertical_filter = filter;
8107
8108 if ( !stbir_resize_extended( &resize ) )
8109 {
8110 if ( free_ptr )
8111 STBIR_FREE( free_ptr, 0 );
8112 return 0;
8113 }
8114
8115 return (free_ptr) ? free_ptr : start_ptr;
8116}
8117
8118
8119
8120STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes,
8121 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
8122 stbir_pixel_layout pixel_layout )
8123{
8124 return (unsigned char *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes,
8125 output_pixels, output_w, output_h, output_stride_in_bytes,
8126 pixel_layout, STBIR_TYPE_UINT8, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT );
8127}
8128
8129STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes,
8130 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
8131 stbir_pixel_layout pixel_layout )
8132{
8133 return (unsigned char *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes,
8134 output_pixels, output_w, output_h, output_stride_in_bytes,
8135 pixel_layout, STBIR_TYPE_UINT8_SRGB, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT );
8136}
8137
8138
8139STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes,
8140 float *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
8141 stbir_pixel_layout pixel_layout )
8142{
8143 return (float *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes,
8144 output_pixels, output_w, output_h, output_stride_in_bytes,
8145 pixel_layout, STBIR_TYPE_FLOAT, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT );
8146}
8147
8148
8149STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes,
8150 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
8151 stbir_pixel_layout pixel_layout, stbir_datatype data_type,
8152 stbir_edge edge, stbir_filter filter )
8153{
8154 return (void *) stbir_quick_resize_helper( input_pixels , input_w , input_h, input_stride_in_bytes,
8155 output_pixels, output_w, output_h, output_stride_in_bytes,
8156 pixel_layout, data_type, edge, filter );
8157}
8158
8159#ifdef STBIR_PROFILE
8160
8161STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize )
8162{
8163 static char const * bdescriptions[6] = { "Building", "Allocating", "Horizontal sampler", "Vertical sampler", "Coefficient cleanup", "Coefficient pivot" } ;
8164 stbir__info* samp = resize->samplers;
8165 int i;
8166
8167 typedef int testa[ (STBIR__ARRAY_SIZE( bdescriptions ) == (STBIR__ARRAY_SIZE( samp->profile.array )-1) )?1:-1];
8168 typedef int testb[ (sizeof( samp->profile.array ) == (sizeof(samp->profile.named)) )?1:-1];
8169 typedef int testc[ (sizeof( info->clocks ) >= (sizeof(samp->profile.named)) )?1:-1];
8170
8171 for( i = 0 ; i < STBIR__ARRAY_SIZE( bdescriptions ) ; i++)
8172 info->clocks[i] = samp->profile.array[i+1];
8173
8174 info->total_clocks = samp->profile.named.total;
8175 info->descriptions = bdescriptions;
8176 info->count = STBIR__ARRAY_SIZE( bdescriptions );
8177}
8178
8179STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize, int split_start, int split_count )
8180{
8181 static char const * descriptions[7] = { "Looping", "Vertical sampling", "Horizontal sampling", "Scanline input", "Scanline output", "Alpha weighting", "Alpha unweighting" };
8182 stbir__per_split_info * split_info;
8183 int s, i;
8184
8185 typedef int testa[ (STBIR__ARRAY_SIZE( descriptions ) == (STBIR__ARRAY_SIZE( split_info->profile.array )-1) )?1:-1];
8186 typedef int testb[ (sizeof( split_info->profile.array ) == (sizeof(split_info->profile.named)) )?1:-1];
8187 typedef int testc[ (sizeof( info->clocks ) >= (sizeof(split_info->profile.named)) )?1:-1];
8188
8189 if ( split_start == -1 )
8190 {
8191 split_start = 0;
8192 split_count = resize->samplers->splits;
8193 }
8194
8195 if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) )
8196 {
8197 info->total_clocks = 0;
8198 info->descriptions = 0;
8199 info->count = 0;
8200 return;
8201 }
8202
8203 split_info = resize->samplers->split_info + split_start;
8204
8205 // sum up the profile from all the splits
8206 for( i = 0 ; i < STBIR__ARRAY_SIZE( descriptions ) ; i++ )
8207 {
8208 stbir_uint64 sum = 0;
8209 for( s = 0 ; s < split_count ; s++ )
8210 sum += split_info[s].profile.array[i+1];
8211 info->clocks[i] = sum;
8212 }
8213
8214 info->total_clocks = split_info->profile.named.total;
8215 info->descriptions = descriptions;
8216 info->count = STBIR__ARRAY_SIZE( descriptions );
8217}
8218
8219STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize )
8220{
8221 stbir_resize_split_profile_info( info, resize, -1, 0 );
8222}
8223
8224#endif // STBIR_PROFILE
8225
8226#undef STBIR_BGR
8227#undef STBIR_1CHANNEL
8228#undef STBIR_2CHANNEL
8229#undef STBIR_RGB
8230#undef STBIR_RGBA
8231#undef STBIR_4CHANNEL
8232#undef STBIR_BGRA
8233#undef STBIR_ARGB
8234#undef STBIR_ABGR
8235#undef STBIR_RA
8236#undef STBIR_AR
8237#undef STBIR_RGBA_PM
8238#undef STBIR_BGRA_PM
8239#undef STBIR_ARGB_PM
8240#undef STBIR_ABGR_PM
8241#undef STBIR_RA_PM
8242#undef STBIR_AR_PM
8243
8244#endif // STB_IMAGE_RESIZE_IMPLEMENTATION
8245
8246#else // STB_IMAGE_RESIZE_HORIZONTALS&STB_IMAGE_RESIZE_DO_VERTICALS
8247
8248// we reinclude the header file to define all the horizontal functions
8249// specializing each function for the number of coeffs is 20-40% faster *OVERALL*
8250
8251// by including the header file again this way, we can still debug the functions
8252
8253#define STBIR_strs_join2( start, mid, end ) start##mid##end
8254#define STBIR_strs_join1( start, mid, end ) STBIR_strs_join2( start, mid, end )
8255
8256#define STBIR_strs_join24( start, mid1, mid2, end ) start##mid1##mid2##end
8257#define STBIR_strs_join14( start, mid1, mid2, end ) STBIR_strs_join24( start, mid1, mid2, end )
8258
8259#ifdef STB_IMAGE_RESIZE_DO_CODERS
8260
8261#ifdef stbir__decode_suffix
8262#define STBIR__CODER_NAME( name ) STBIR_strs_join1( name, _, stbir__decode_suffix )
8263#else
8264#define STBIR__CODER_NAME( name ) name
8265#endif
8266
8267#ifdef stbir__decode_swizzle
8268#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)
8269#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)
8270#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)
8271#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)
8272#else
8273#define stbir__decode_order0 0
8274#define stbir__decode_order1 1
8275#define stbir__decode_order2 2
8276#define stbir__decode_order3 3
8277#define stbir__encode_order0 0
8278#define stbir__encode_order1 1
8279#define stbir__encode_order2 2
8280#define stbir__encode_order3 3
8281#define stbir__decode_simdf8_flip(reg)
8282#define stbir__decode_simdf4_flip(reg)
8283#define stbir__encode_simdf8_unflip(reg)
8284#define stbir__encode_simdf4_unflip(reg)
8285#endif
8286
8287#ifdef STBIR_SIMD8
8288#define stbir__encode_simdfX_unflip stbir__encode_simdf8_unflip
8289#else
8290#define stbir__encode_simdfX_unflip stbir__encode_simdf4_unflip
8291#endif
8292
8293static float * STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * decodep, int width_times_channels, void const * inputp )
8294{
8295 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
8296 float * decode_end = (float*) decode + width_times_channels;
8297 unsigned char const * input = (unsigned char const*)inputp;
8298
8299 #ifdef STBIR_SIMD
8300 unsigned char const * end_input_m16 = input + width_times_channels - 16;
8301 if ( width_times_channels >= 16 )
8302 {
8303 decode_end -= 16;
8304 STBIR_NO_UNROLL_LOOP_START_INF_FOR
8305 for(;;)
8306 {
8307 #ifdef STBIR_SIMD8
8308 stbir__simdi i; stbir__simdi8 o0,o1;
8309 stbir__simdf8 of0, of1;
8310 STBIR_NO_UNROLL(decode);
8311 stbir__simdi_load( i, input );
8312 stbir__simdi8_expand_u8_to_u32( o0, o1, i );
8313 stbir__simdi8_convert_i32_to_float( of0, o0 );
8314 stbir__simdi8_convert_i32_to_float( of1, o1 );
8315 stbir__simdf8_mult( of0, of0, STBIR_max_uint8_as_float_inverted8);
8316 stbir__simdf8_mult( of1, of1, STBIR_max_uint8_as_float_inverted8);
8317 stbir__decode_simdf8_flip( of0 );
8318 stbir__decode_simdf8_flip( of1 );
8319 stbir__simdf8_store( decode + 0, of0 );
8320 stbir__simdf8_store( decode + 8, of1 );
8321 #else
8322 stbir__simdi i, o0, o1, o2, o3;
8323 stbir__simdf of0, of1, of2, of3;
8324 STBIR_NO_UNROLL(decode);
8325 stbir__simdi_load( i, input );
8326 stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i);
8327 stbir__simdi_convert_i32_to_float( of0, o0 );
8328 stbir__simdi_convert_i32_to_float( of1, o1 );
8329 stbir__simdi_convert_i32_to_float( of2, o2 );
8330 stbir__simdi_convert_i32_to_float( of3, o3 );
8331 stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) );
8332 stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) );
8333 stbir__simdf_mult( of2, of2, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) );
8334 stbir__simdf_mult( of3, of3, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) );
8335 stbir__decode_simdf4_flip( of0 );
8336 stbir__decode_simdf4_flip( of1 );
8337 stbir__decode_simdf4_flip( of2 );
8338 stbir__decode_simdf4_flip( of3 );
8339 stbir__simdf_store( decode + 0, of0 );
8340 stbir__simdf_store( decode + 4, of1 );
8341 stbir__simdf_store( decode + 8, of2 );
8342 stbir__simdf_store( decode + 12, of3 );
8343 #endif
8344 decode += 16;
8345 input += 16;
8346 if ( decode <= decode_end )
8347 continue;
8348 if ( decode == ( decode_end + 16 ) )
8349 break;
8350 decode = decode_end; // backup and do last couple
8351 input = end_input_m16;
8352 }
8353 return decode_end + 16;
8354 }
8355 #endif
8356
8357 // try to do blocks of 4 when you can
8358 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
8359 decode += 4;
8360 STBIR_SIMD_NO_UNROLL_LOOP_START
8361 while( decode <= decode_end )
8362 {
8363 STBIR_SIMD_NO_UNROLL(decode);
8364 decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted;
8365 decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted;
8366 decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted;
8367 decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint8_as_float_inverted;
8368 decode += 4;
8369 input += 4;
8370 }
8371 decode -= 4;
8372 #endif
8373
8374 // do the remnants
8375 #if stbir__coder_min_num < 4
8376 STBIR_NO_UNROLL_LOOP_START
8377 while( decode < decode_end )
8378 {
8379 STBIR_NO_UNROLL(decode);
8380 decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted;
8381 #if stbir__coder_min_num >= 2
8382 decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted;
8383 #endif
8384 #if stbir__coder_min_num >= 3
8385 decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted;
8386 #endif
8387 decode += stbir__coder_min_num;
8388 input += stbir__coder_min_num;
8389 }
8390 #endif
8391
8392 return decode_end;
8393}
8394
8395static void STBIR__CODER_NAME( stbir__encode_uint8_linear_scaled )( void * outputp, int width_times_channels, float const * encode )
8396{
8397 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp;
8398 unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels;
8399
8400 #ifdef STBIR_SIMD
8401 if ( width_times_channels >= stbir__simdfX_float_count*2 )
8402 {
8403 float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2;
8404 end_output -= stbir__simdfX_float_count*2;
8405 STBIR_NO_UNROLL_LOOP_START_INF_FOR
8406 for(;;)
8407 {
8408 stbir__simdfX e0, e1;
8409 stbir__simdi i;
8410 STBIR_SIMD_NO_UNROLL(encode);
8411 stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode );
8412 stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode+stbir__simdfX_float_count );
8413 stbir__encode_simdfX_unflip( e0 );
8414 stbir__encode_simdfX_unflip( e1 );
8415 #ifdef STBIR_SIMD8
8416 stbir__simdf8_pack_to_16bytes( i, e0, e1 );
8417 stbir__simdi_store( output, i );
8418 #else
8419 stbir__simdf_pack_to_8bytes( i, e0, e1 );
8420 stbir__simdi_store2( output, i );
8421 #endif
8422 encode += stbir__simdfX_float_count*2;
8423 output += stbir__simdfX_float_count*2;
8424 if ( output <= end_output )
8425 continue;
8426 if ( output == ( end_output + stbir__simdfX_float_count*2 ) )
8427 break;
8428 output = end_output; // backup and do last couple
8429 encode = end_encode_m8;
8430 }
8431 return;
8432 }
8433
8434 // try to do blocks of 4 when you can
8435 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
8436 output += 4;
8437 STBIR_NO_UNROLL_LOOP_START
8438 while( output <= end_output )
8439 {
8440 stbir__simdf e0;
8441 stbir__simdi i0;
8442 STBIR_NO_UNROLL(encode);
8443 stbir__simdf_load( e0, encode );
8444 stbir__simdf_madd( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), e0 );
8445 stbir__encode_simdf4_unflip( e0 );
8446 stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4
8447 *(int*)(output-4) = stbir__simdi_to_int( i0 );
8448 output += 4;
8449 encode += 4;
8450 }
8451 output -= 4;
8452 #endif
8453
8454 // do the remnants
8455 #if stbir__coder_min_num < 4
8456 STBIR_NO_UNROLL_LOOP_START
8457 while( output < end_output )
8458 {
8459 stbir__simdf e0;
8460 STBIR_NO_UNROLL(encode);
8461 stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_uint8( e0 );
8462 #if stbir__coder_min_num >= 2
8463 stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_uint8( e0 );
8464 #endif
8465 #if stbir__coder_min_num >= 3
8466 stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_uint8( e0 );
8467 #endif
8468 output += stbir__coder_min_num;
8469 encode += stbir__coder_min_num;
8470 }
8471 #endif
8472
8473 #else
8474
8475 // try to do blocks of 4 when you can
8476 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
8477 output += 4;
8478 while( output <= end_output )
8479 {
8480 float f;
8481 f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f;
8482 f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f;
8483 f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f;
8484 f = encode[stbir__encode_order3] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f;
8485 output += 4;
8486 encode += 4;
8487 }
8488 output -= 4;
8489 #endif
8490
8491 // do the remnants
8492 #if stbir__coder_min_num < 4
8493 STBIR_NO_UNROLL_LOOP_START
8494 while( output < end_output )
8495 {
8496 float f;
8497 STBIR_NO_UNROLL(encode);
8498 f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f;
8499 #if stbir__coder_min_num >= 2
8500 f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f;
8501 #endif
8502 #if stbir__coder_min_num >= 3
8503 f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f;
8504 #endif
8505 output += stbir__coder_min_num;
8506 encode += stbir__coder_min_num;
8507 }
8508 #endif
8509 #endif
8510}
8511
8512static float * STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, int width_times_channels, void const * inputp )
8513{
8514 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
8515 float * decode_end = (float*) decode + width_times_channels;
8516 unsigned char const * input = (unsigned char const*)inputp;
8517
8518 #ifdef STBIR_SIMD
8519 unsigned char const * end_input_m16 = input + width_times_channels - 16;
8520 if ( width_times_channels >= 16 )
8521 {
8522 decode_end -= 16;
8523 STBIR_NO_UNROLL_LOOP_START_INF_FOR
8524 for(;;)
8525 {
8526 #ifdef STBIR_SIMD8
8527 stbir__simdi i; stbir__simdi8 o0,o1;
8528 stbir__simdf8 of0, of1;
8529 STBIR_NO_UNROLL(decode);
8530 stbir__simdi_load( i, input );
8531 stbir__simdi8_expand_u8_to_u32( o0, o1, i );
8532 stbir__simdi8_convert_i32_to_float( of0, o0 );
8533 stbir__simdi8_convert_i32_to_float( of1, o1 );
8534 stbir__decode_simdf8_flip( of0 );
8535 stbir__decode_simdf8_flip( of1 );
8536 stbir__simdf8_store( decode + 0, of0 );
8537 stbir__simdf8_store( decode + 8, of1 );
8538 #else
8539 stbir__simdi i, o0, o1, o2, o3;
8540 stbir__simdf of0, of1, of2, of3;
8541 STBIR_NO_UNROLL(decode);
8542 stbir__simdi_load( i, input );
8543 stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i);
8544 stbir__simdi_convert_i32_to_float( of0, o0 );
8545 stbir__simdi_convert_i32_to_float( of1, o1 );
8546 stbir__simdi_convert_i32_to_float( of2, o2 );
8547 stbir__simdi_convert_i32_to_float( of3, o3 );
8548 stbir__decode_simdf4_flip( of0 );
8549 stbir__decode_simdf4_flip( of1 );
8550 stbir__decode_simdf4_flip( of2 );
8551 stbir__decode_simdf4_flip( of3 );
8552 stbir__simdf_store( decode + 0, of0 );
8553 stbir__simdf_store( decode + 4, of1 );
8554 stbir__simdf_store( decode + 8, of2 );
8555 stbir__simdf_store( decode + 12, of3 );
8556#endif
8557 decode += 16;
8558 input += 16;
8559 if ( decode <= decode_end )
8560 continue;
8561 if ( decode == ( decode_end + 16 ) )
8562 break;
8563 decode = decode_end; // backup and do last couple
8564 input = end_input_m16;
8565 }
8566 return decode_end + 16;
8567 }
8568 #endif
8569
8570 // try to do blocks of 4 when you can
8571 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
8572 decode += 4;
8573 STBIR_SIMD_NO_UNROLL_LOOP_START
8574 while( decode <= decode_end )
8575 {
8576 STBIR_SIMD_NO_UNROLL(decode);
8577 decode[0-4] = ((float)(input[stbir__decode_order0]));
8578 decode[1-4] = ((float)(input[stbir__decode_order1]));
8579 decode[2-4] = ((float)(input[stbir__decode_order2]));
8580 decode[3-4] = ((float)(input[stbir__decode_order3]));
8581 decode += 4;
8582 input += 4;
8583 }
8584 decode -= 4;
8585 #endif
8586
8587 // do the remnants
8588 #if stbir__coder_min_num < 4
8589 STBIR_NO_UNROLL_LOOP_START
8590 while( decode < decode_end )
8591 {
8592 STBIR_NO_UNROLL(decode);
8593 decode[0] = ((float)(input[stbir__decode_order0]));
8594 #if stbir__coder_min_num >= 2
8595 decode[1] = ((float)(input[stbir__decode_order1]));
8596 #endif
8597 #if stbir__coder_min_num >= 3
8598 decode[2] = ((float)(input[stbir__decode_order2]));
8599 #endif
8600 decode += stbir__coder_min_num;
8601 input += stbir__coder_min_num;
8602 }
8603 #endif
8604 return decode_end;
8605}
8606
8607static void STBIR__CODER_NAME( stbir__encode_uint8_linear )( void * outputp, int width_times_channels, float const * encode )
8608{
8609 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp;
8610 unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels;
8611
8612 #ifdef STBIR_SIMD
8613 if ( width_times_channels >= stbir__simdfX_float_count*2 )
8614 {
8615 float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2;
8616 end_output -= stbir__simdfX_float_count*2;
8617 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
8618 for(;;)
8619 {
8620 stbir__simdfX e0, e1;
8621 stbir__simdi i;
8622 STBIR_SIMD_NO_UNROLL(encode);
8623 stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode );
8624 stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count );
8625 stbir__encode_simdfX_unflip( e0 );
8626 stbir__encode_simdfX_unflip( e1 );
8627 #ifdef STBIR_SIMD8
8628 stbir__simdf8_pack_to_16bytes( i, e0, e1 );
8629 stbir__simdi_store( output, i );
8630 #else
8631 stbir__simdf_pack_to_8bytes( i, e0, e1 );
8632 stbir__simdi_store2( output, i );
8633 #endif
8634 encode += stbir__simdfX_float_count*2;
8635 output += stbir__simdfX_float_count*2;
8636 if ( output <= end_output )
8637 continue;
8638 if ( output == ( end_output + stbir__simdfX_float_count*2 ) )
8639 break;
8640 output = end_output; // backup and do last couple
8641 encode = end_encode_m8;
8642 }
8643 return;
8644 }
8645
8646 // try to do blocks of 4 when you can
8647 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
8648 output += 4;
8649 STBIR_NO_UNROLL_LOOP_START
8650 while( output <= end_output )
8651 {
8652 stbir__simdf e0;
8653 stbir__simdi i0;
8654 STBIR_NO_UNROLL(encode);
8655 stbir__simdf_load( e0, encode );
8656 stbir__simdf_add( e0, STBIR__CONSTF(STBIR_simd_point5), e0 );
8657 stbir__encode_simdf4_unflip( e0 );
8658 stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4
8659 *(int*)(output-4) = stbir__simdi_to_int( i0 );
8660 output += 4;
8661 encode += 4;
8662 }
8663 output -= 4;
8664 #endif
8665
8666 #else
8667
8668 // try to do blocks of 4 when you can
8669 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
8670 output += 4;
8671 while( output <= end_output )
8672 {
8673 float f;
8674 f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f;
8675 f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f;
8676 f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f;
8677 f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f;
8678 output += 4;
8679 encode += 4;
8680 }
8681 output -= 4;
8682 #endif
8683
8684 #endif
8685
8686 // do the remnants
8687 #if stbir__coder_min_num < 4
8688 STBIR_NO_UNROLL_LOOP_START
8689 while( output < end_output )
8690 {
8691 float f;
8692 STBIR_NO_UNROLL(encode);
8693 f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f;
8694 #if stbir__coder_min_num >= 2
8695 f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f;
8696 #endif
8697 #if stbir__coder_min_num >= 3
8698 f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f;
8699 #endif
8700 output += stbir__coder_min_num;
8701 encode += stbir__coder_min_num;
8702 }
8703 #endif
8704}
8705
8706static float * STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, int width_times_channels, void const * inputp )
8707{
8708 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
8709 float * decode_end = (float*) decode + width_times_channels;
8710 unsigned char const * input = (unsigned char const *)inputp;
8711
8712 // try to do blocks of 4 when you can
8713 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
8714 decode += 4;
8715 while( decode <= decode_end )
8716 {
8717 decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ];
8718 decode[1-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ];
8719 decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ];
8720 decode[3-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order3 ] ];
8721 decode += 4;
8722 input += 4;
8723 }
8724 decode -= 4;
8725 #endif
8726
8727 // do the remnants
8728 #if stbir__coder_min_num < 4
8729 STBIR_NO_UNROLL_LOOP_START
8730 while( decode < decode_end )
8731 {
8732 STBIR_NO_UNROLL(decode);
8733 decode[0] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ];
8734 #if stbir__coder_min_num >= 2
8735 decode[1] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ];
8736 #endif
8737 #if stbir__coder_min_num >= 3
8738 decode[2] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ];
8739 #endif
8740 decode += stbir__coder_min_num;
8741 input += stbir__coder_min_num;
8742 }
8743 #endif
8744 return decode_end;
8745}
8746
8747#define stbir__min_max_shift20( i, f ) \
8748 stbir__simdf_max( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_zero )) ); \
8749 stbir__simdf_min( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_one )) ); \
8750 stbir__simdi_32shr( i, stbir_simdi_castf( f ), 20 );
8751
8752#define stbir__scale_and_convert( i, f ) \
8753 stbir__simdf_madd( f, STBIR__CONSTF( STBIR_simd_point5 ), STBIR__CONSTF( STBIR_max_uint8_as_float ), f ); \
8754 stbir__simdf_max( f, f, stbir__simdf_zeroP() ); \
8755 stbir__simdf_min( f, f, STBIR__CONSTF( STBIR_max_uint8_as_float ) ); \
8756 stbir__simdf_convert_float_to_i32( i, f );
8757
8758#define stbir__linear_to_srgb_finish( i, f ) \
8759{ \
8760 stbir__simdi temp; \
8761 stbir__simdi_32shr( temp, stbir_simdi_castf( f ), 12 ) ; \
8762 stbir__simdi_and( temp, temp, STBIR__CONSTI(STBIR_mantissa_mask) ); \
8763 stbir__simdi_or( temp, temp, STBIR__CONSTI(STBIR_topscale) ); \
8764 stbir__simdi_16madd( i, i, temp ); \
8765 stbir__simdi_32shr( i, i, 16 ); \
8766}
8767
8768#define stbir__simdi_table_lookup2( v0,v1, table ) \
8769{ \
8770 stbir__simdi_u32 temp0,temp1; \
8771 temp0.m128i_i128 = v0; \
8772 temp1.m128i_i128 = v1; \
8773 temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \
8774 temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \
8775 v0 = temp0.m128i_i128; \
8776 v1 = temp1.m128i_i128; \
8777}
8778
8779#define stbir__simdi_table_lookup3( v0,v1,v2, table ) \
8780{ \
8781 stbir__simdi_u32 temp0,temp1,temp2; \
8782 temp0.m128i_i128 = v0; \
8783 temp1.m128i_i128 = v1; \
8784 temp2.m128i_i128 = v2; \
8785 temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \
8786 temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \
8787 temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \
8788 v0 = temp0.m128i_i128; \
8789 v1 = temp1.m128i_i128; \
8790 v2 = temp2.m128i_i128; \
8791}
8792
8793#define stbir__simdi_table_lookup4( v0,v1,v2,v3, table ) \
8794{ \
8795 stbir__simdi_u32 temp0,temp1,temp2,temp3; \
8796 temp0.m128i_i128 = v0; \
8797 temp1.m128i_i128 = v1; \
8798 temp2.m128i_i128 = v2; \
8799 temp3.m128i_i128 = v3; \
8800 temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \
8801 temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \
8802 temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \
8803 temp3.m128i_u32[0] = table[temp3.m128i_i32[0]]; temp3.m128i_u32[1] = table[temp3.m128i_i32[1]]; temp3.m128i_u32[2] = table[temp3.m128i_i32[2]]; temp3.m128i_u32[3] = table[temp3.m128i_i32[3]]; \
8804 v0 = temp0.m128i_i128; \
8805 v1 = temp1.m128i_i128; \
8806 v2 = temp2.m128i_i128; \
8807 v3 = temp3.m128i_i128; \
8808}
8809
8810static void STBIR__CODER_NAME( stbir__encode_uint8_srgb )( void * outputp, int width_times_channels, float const * encode )
8811{
8812 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp;
8813 unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels;
8814
8815 #ifdef STBIR_SIMD
8816
8817 if ( width_times_channels >= 16 )
8818 {
8819 float const * end_encode_m16 = encode + width_times_channels - 16;
8820 end_output -= 16;
8821 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
8822 for(;;)
8823 {
8824 stbir__simdf f0, f1, f2, f3;
8825 stbir__simdi i0, i1, i2, i3;
8826 STBIR_SIMD_NO_UNROLL(encode);
8827
8828 stbir__simdf_load4_transposed( f0, f1, f2, f3, encode );
8829
8830 stbir__min_max_shift20( i0, f0 );
8831 stbir__min_max_shift20( i1, f1 );
8832 stbir__min_max_shift20( i2, f2 );
8833 stbir__min_max_shift20( i3, f3 );
8834
8835 stbir__simdi_table_lookup4( i0, i1, i2, i3, ( fp32_to_srgb8_tab4 - (127-13)*8 ) );
8836
8837 stbir__linear_to_srgb_finish( i0, f0 );
8838 stbir__linear_to_srgb_finish( i1, f1 );
8839 stbir__linear_to_srgb_finish( i2, f2 );
8840 stbir__linear_to_srgb_finish( i3, f3 );
8841
8842 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) );
8843
8844 encode += 16;
8845 output += 16;
8846 if ( output <= end_output )
8847 continue;
8848 if ( output == ( end_output + 16 ) )
8849 break;
8850 output = end_output; // backup and do last couple
8851 encode = end_encode_m16;
8852 }
8853 return;
8854 }
8855 #endif
8856
8857 // try to do blocks of 4 when you can
8858 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
8859 output += 4;
8860 STBIR_SIMD_NO_UNROLL_LOOP_START
8861 while ( output <= end_output )
8862 {
8863 STBIR_SIMD_NO_UNROLL(encode);
8864
8865 output[0-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] );
8866 output[1-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] );
8867 output[2-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] );
8868 output[3-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order3] );
8869
8870 output += 4;
8871 encode += 4;
8872 }
8873 output -= 4;
8874 #endif
8875
8876 // do the remnants
8877 #if stbir__coder_min_num < 4
8878 STBIR_NO_UNROLL_LOOP_START
8879 while( output < end_output )
8880 {
8881 STBIR_NO_UNROLL(encode);
8882 output[0] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] );
8883 #if stbir__coder_min_num >= 2
8884 output[1] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] );
8885 #endif
8886 #if stbir__coder_min_num >= 3
8887 output[2] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] );
8888 #endif
8889 output += stbir__coder_min_num;
8890 encode += stbir__coder_min_num;
8891 }
8892 #endif
8893}
8894
8895#if ( stbir__coder_min_num == 4 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) )
8896
8897static float * STBIR__CODER_NAME(stbir__decode_uint8_srgb4_linearalpha)( float * decodep, int width_times_channels, void const * inputp )
8898{
8899 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
8900 float * decode_end = (float*) decode + width_times_channels;
8901 unsigned char const * input = (unsigned char const *)inputp;
8902
8903 do {
8904 decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ];
8905 decode[1] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order1] ];
8906 decode[2] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order2] ];
8907 decode[3] = ( (float) input[stbir__decode_order3] ) * stbir__max_uint8_as_float_inverted;
8908 input += 4;
8909 decode += 4;
8910 } while( decode < decode_end );
8911 return decode_end;
8912}
8913
8914
8915static void STBIR__CODER_NAME( stbir__encode_uint8_srgb4_linearalpha )( void * outputp, int width_times_channels, float const * encode )
8916{
8917 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp;
8918 unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels;
8919
8920 #ifdef STBIR_SIMD
8921
8922 if ( width_times_channels >= 16 )
8923 {
8924 float const * end_encode_m16 = encode + width_times_channels - 16;
8925 end_output -= 16;
8926 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
8927 for(;;)
8928 {
8929 stbir__simdf f0, f1, f2, f3;
8930 stbir__simdi i0, i1, i2, i3;
8931
8932 STBIR_SIMD_NO_UNROLL(encode);
8933 stbir__simdf_load4_transposed( f0, f1, f2, f3, encode );
8934
8935 stbir__min_max_shift20( i0, f0 );
8936 stbir__min_max_shift20( i1, f1 );
8937 stbir__min_max_shift20( i2, f2 );
8938 stbir__scale_and_convert( i3, f3 );
8939
8940 stbir__simdi_table_lookup3( i0, i1, i2, ( fp32_to_srgb8_tab4 - (127-13)*8 ) );
8941
8942 stbir__linear_to_srgb_finish( i0, f0 );
8943 stbir__linear_to_srgb_finish( i1, f1 );
8944 stbir__linear_to_srgb_finish( i2, f2 );
8945
8946 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) );
8947
8948 output += 16;
8949 encode += 16;
8950
8951 if ( output <= end_output )
8952 continue;
8953 if ( output == ( end_output + 16 ) )
8954 break;
8955 output = end_output; // backup and do last couple
8956 encode = end_encode_m16;
8957 }
8958 return;
8959 }
8960 #endif
8961
8962 STBIR_SIMD_NO_UNROLL_LOOP_START
8963 do {
8964 float f;
8965 STBIR_SIMD_NO_UNROLL(encode);
8966
8967 output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] );
8968 output[stbir__decode_order1] = stbir__linear_to_srgb_uchar( encode[1] );
8969 output[stbir__decode_order2] = stbir__linear_to_srgb_uchar( encode[2] );
8970
8971 f = encode[3] * stbir__max_uint8_as_float + 0.5f;
8972 STBIR_CLAMP(f, 0, 255);
8973 output[stbir__decode_order3] = (unsigned char) f;
8974
8975 output += 4;
8976 encode += 4;
8977 } while( output < end_output );
8978}
8979
8980#endif
8981
8982#if ( stbir__coder_min_num == 2 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) )
8983
8984static float * STBIR__CODER_NAME(stbir__decode_uint8_srgb2_linearalpha)( float * decodep, int width_times_channels, void const * inputp )
8985{
8986 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
8987 float * decode_end = (float*) decode + width_times_channels;
8988 unsigned char const * input = (unsigned char const *)inputp;
8989
8990 decode += 4;
8991 while( decode <= decode_end )
8992 {
8993 decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ];
8994 decode[1-4] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted;
8995 decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0+2] ];
8996 decode[3-4] = ( (float) input[stbir__decode_order1+2] ) * stbir__max_uint8_as_float_inverted;
8997 input += 4;
8998 decode += 4;
8999 }
9000 decode -= 4;
9001 if( decode < decode_end )
9002 {
9003 decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ];
9004 decode[1] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted;
9005 }
9006 return decode_end;
9007}
9008
9009static void STBIR__CODER_NAME( stbir__encode_uint8_srgb2_linearalpha )( void * outputp, int width_times_channels, float const * encode )
9010{
9011 unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp;
9012 unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels;
9013
9014 #ifdef STBIR_SIMD
9015
9016 if ( width_times_channels >= 16 )
9017 {
9018 float const * end_encode_m16 = encode + width_times_channels - 16;
9019 end_output -= 16;
9020 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
9021 for(;;)
9022 {
9023 stbir__simdf f0, f1, f2, f3;
9024 stbir__simdi i0, i1, i2, i3;
9025
9026 STBIR_SIMD_NO_UNROLL(encode);
9027 stbir__simdf_load4_transposed( f0, f1, f2, f3, encode );
9028
9029 stbir__min_max_shift20( i0, f0 );
9030 stbir__scale_and_convert( i1, f1 );
9031 stbir__min_max_shift20( i2, f2 );
9032 stbir__scale_and_convert( i3, f3 );
9033
9034 stbir__simdi_table_lookup2( i0, i2, ( fp32_to_srgb8_tab4 - (127-13)*8 ) );
9035
9036 stbir__linear_to_srgb_finish( i0, f0 );
9037 stbir__linear_to_srgb_finish( i2, f2 );
9038
9039 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) );
9040
9041 output += 16;
9042 encode += 16;
9043 if ( output <= end_output )
9044 continue;
9045 if ( output == ( end_output + 16 ) )
9046 break;
9047 output = end_output; // backup and do last couple
9048 encode = end_encode_m16;
9049 }
9050 return;
9051 }
9052 #endif
9053
9054 STBIR_SIMD_NO_UNROLL_LOOP_START
9055 do {
9056 float f;
9057 STBIR_SIMD_NO_UNROLL(encode);
9058
9059 output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] );
9060
9061 f = encode[1] * stbir__max_uint8_as_float + 0.5f;
9062 STBIR_CLAMP(f, 0, 255);
9063 output[stbir__decode_order1] = (unsigned char) f;
9064
9065 output += 2;
9066 encode += 2;
9067 } while( output < end_output );
9068}
9069
9070#endif
9071
9072static float * STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decodep, int width_times_channels, void const * inputp )
9073{
9074 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
9075 float * decode_end = (float*) decode + width_times_channels;
9076 unsigned short const * input = (unsigned short const *)inputp;
9077
9078 #ifdef STBIR_SIMD
9079 unsigned short const * end_input_m8 = input + width_times_channels - 8;
9080 if ( width_times_channels >= 8 )
9081 {
9082 decode_end -= 8;
9083 STBIR_NO_UNROLL_LOOP_START_INF_FOR
9084 for(;;)
9085 {
9086 #ifdef STBIR_SIMD8
9087 stbir__simdi i; stbir__simdi8 o;
9088 stbir__simdf8 of;
9089 STBIR_NO_UNROLL(decode);
9090 stbir__simdi_load( i, input );
9091 stbir__simdi8_expand_u16_to_u32( o, i );
9092 stbir__simdi8_convert_i32_to_float( of, o );
9093 stbir__simdf8_mult( of, of, STBIR_max_uint16_as_float_inverted8);
9094 stbir__decode_simdf8_flip( of );
9095 stbir__simdf8_store( decode + 0, of );
9096 #else
9097 stbir__simdi i, o0, o1;
9098 stbir__simdf of0, of1;
9099 STBIR_NO_UNROLL(decode);
9100 stbir__simdi_load( i, input );
9101 stbir__simdi_expand_u16_to_u32( o0,o1,i );
9102 stbir__simdi_convert_i32_to_float( of0, o0 );
9103 stbir__simdi_convert_i32_to_float( of1, o1 );
9104 stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted) );
9105 stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted));
9106 stbir__decode_simdf4_flip( of0 );
9107 stbir__decode_simdf4_flip( of1 );
9108 stbir__simdf_store( decode + 0, of0 );
9109 stbir__simdf_store( decode + 4, of1 );
9110 #endif
9111 decode += 8;
9112 input += 8;
9113 if ( decode <= decode_end )
9114 continue;
9115 if ( decode == ( decode_end + 8 ) )
9116 break;
9117 decode = decode_end; // backup and do last couple
9118 input = end_input_m8;
9119 }
9120 return decode_end + 8;
9121 }
9122 #endif
9123
9124 // try to do blocks of 4 when you can
9125 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9126 decode += 4;
9127 STBIR_SIMD_NO_UNROLL_LOOP_START
9128 while( decode <= decode_end )
9129 {
9130 STBIR_SIMD_NO_UNROLL(decode);
9131 decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted;
9132 decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted;
9133 decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted;
9134 decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint16_as_float_inverted;
9135 decode += 4;
9136 input += 4;
9137 }
9138 decode -= 4;
9139 #endif
9140
9141 // do the remnants
9142 #if stbir__coder_min_num < 4
9143 STBIR_NO_UNROLL_LOOP_START
9144 while( decode < decode_end )
9145 {
9146 STBIR_NO_UNROLL(decode);
9147 decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted;
9148 #if stbir__coder_min_num >= 2
9149 decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted;
9150 #endif
9151 #if stbir__coder_min_num >= 3
9152 decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted;
9153 #endif
9154 decode += stbir__coder_min_num;
9155 input += stbir__coder_min_num;
9156 }
9157 #endif
9158 return decode_end;
9159}
9160
9161
9162static void STBIR__CODER_NAME(stbir__encode_uint16_linear_scaled)( void * outputp, int width_times_channels, float const * encode )
9163{
9164 unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp;
9165 unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels;
9166
9167 #ifdef STBIR_SIMD
9168 {
9169 if ( width_times_channels >= stbir__simdfX_float_count*2 )
9170 {
9171 float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2;
9172 end_output -= stbir__simdfX_float_count*2;
9173 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
9174 for(;;)
9175 {
9176 stbir__simdfX e0, e1;
9177 stbir__simdiX i;
9178 STBIR_SIMD_NO_UNROLL(encode);
9179 stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode );
9180 stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode+stbir__simdfX_float_count );
9181 stbir__encode_simdfX_unflip( e0 );
9182 stbir__encode_simdfX_unflip( e1 );
9183 stbir__simdfX_pack_to_words( i, e0, e1 );
9184 stbir__simdiX_store( output, i );
9185 encode += stbir__simdfX_float_count*2;
9186 output += stbir__simdfX_float_count*2;
9187 if ( output <= end_output )
9188 continue;
9189 if ( output == ( end_output + stbir__simdfX_float_count*2 ) )
9190 break;
9191 output = end_output; // backup and do last couple
9192 encode = end_encode_m8;
9193 }
9194 return;
9195 }
9196 }
9197
9198 // try to do blocks of 4 when you can
9199 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9200 output += 4;
9201 STBIR_NO_UNROLL_LOOP_START
9202 while( output <= end_output )
9203 {
9204 stbir__simdf e;
9205 stbir__simdi i;
9206 STBIR_NO_UNROLL(encode);
9207 stbir__simdf_load( e, encode );
9208 stbir__simdf_madd( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), e );
9209 stbir__encode_simdf4_unflip( e );
9210 stbir__simdf_pack_to_8words( i, e, e ); // only use first 4
9211 stbir__simdi_store2( output-4, i );
9212 output += 4;
9213 encode += 4;
9214 }
9215 output -= 4;
9216 #endif
9217
9218 // do the remnants
9219 #if stbir__coder_min_num < 4
9220 STBIR_NO_UNROLL_LOOP_START
9221 while( output < end_output )
9222 {
9223 stbir__simdf e;
9224 STBIR_NO_UNROLL(encode);
9225 stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_short( e );
9226 #if stbir__coder_min_num >= 2
9227 stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_short( e );
9228 #endif
9229 #if stbir__coder_min_num >= 3
9230 stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_short( e );
9231 #endif
9232 output += stbir__coder_min_num;
9233 encode += stbir__coder_min_num;
9234 }
9235 #endif
9236
9237 #else
9238
9239 // try to do blocks of 4 when you can
9240 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9241 output += 4;
9242 STBIR_SIMD_NO_UNROLL_LOOP_START
9243 while( output <= end_output )
9244 {
9245 float f;
9246 STBIR_SIMD_NO_UNROLL(encode);
9247 f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f;
9248 f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f;
9249 f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f;
9250 f = encode[stbir__encode_order3] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f;
9251 output += 4;
9252 encode += 4;
9253 }
9254 output -= 4;
9255 #endif
9256
9257 // do the remnants
9258 #if stbir__coder_min_num < 4
9259 STBIR_NO_UNROLL_LOOP_START
9260 while( output < end_output )
9261 {
9262 float f;
9263 STBIR_NO_UNROLL(encode);
9264 f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f;
9265 #if stbir__coder_min_num >= 2
9266 f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f;
9267 #endif
9268 #if stbir__coder_min_num >= 3
9269 f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f;
9270 #endif
9271 output += stbir__coder_min_num;
9272 encode += stbir__coder_min_num;
9273 }
9274 #endif
9275 #endif
9276}
9277
9278static float * STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, int width_times_channels, void const * inputp )
9279{
9280 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
9281 float * decode_end = (float*) decode + width_times_channels;
9282 unsigned short const * input = (unsigned short const *)inputp;
9283
9284 #ifdef STBIR_SIMD
9285 unsigned short const * end_input_m8 = input + width_times_channels - 8;
9286 if ( width_times_channels >= 8 )
9287 {
9288 decode_end -= 8;
9289 STBIR_NO_UNROLL_LOOP_START_INF_FOR
9290 for(;;)
9291 {
9292 #ifdef STBIR_SIMD8
9293 stbir__simdi i; stbir__simdi8 o;
9294 stbir__simdf8 of;
9295 STBIR_NO_UNROLL(decode);
9296 stbir__simdi_load( i, input );
9297 stbir__simdi8_expand_u16_to_u32( o, i );
9298 stbir__simdi8_convert_i32_to_float( of, o );
9299 stbir__decode_simdf8_flip( of );
9300 stbir__simdf8_store( decode + 0, of );
9301 #else
9302 stbir__simdi i, o0, o1;
9303 stbir__simdf of0, of1;
9304 STBIR_NO_UNROLL(decode);
9305 stbir__simdi_load( i, input );
9306 stbir__simdi_expand_u16_to_u32( o0, o1, i );
9307 stbir__simdi_convert_i32_to_float( of0, o0 );
9308 stbir__simdi_convert_i32_to_float( of1, o1 );
9309 stbir__decode_simdf4_flip( of0 );
9310 stbir__decode_simdf4_flip( of1 );
9311 stbir__simdf_store( decode + 0, of0 );
9312 stbir__simdf_store( decode + 4, of1 );
9313 #endif
9314 decode += 8;
9315 input += 8;
9316 if ( decode <= decode_end )
9317 continue;
9318 if ( decode == ( decode_end + 8 ) )
9319 break;
9320 decode = decode_end; // backup and do last couple
9321 input = end_input_m8;
9322 }
9323 return decode_end + 8;
9324 }
9325 #endif
9326
9327 // try to do blocks of 4 when you can
9328 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9329 decode += 4;
9330 STBIR_SIMD_NO_UNROLL_LOOP_START
9331 while( decode <= decode_end )
9332 {
9333 STBIR_SIMD_NO_UNROLL(decode);
9334 decode[0-4] = ((float)(input[stbir__decode_order0]));
9335 decode[1-4] = ((float)(input[stbir__decode_order1]));
9336 decode[2-4] = ((float)(input[stbir__decode_order2]));
9337 decode[3-4] = ((float)(input[stbir__decode_order3]));
9338 decode += 4;
9339 input += 4;
9340 }
9341 decode -= 4;
9342 #endif
9343
9344 // do the remnants
9345 #if stbir__coder_min_num < 4
9346 STBIR_NO_UNROLL_LOOP_START
9347 while( decode < decode_end )
9348 {
9349 STBIR_NO_UNROLL(decode);
9350 decode[0] = ((float)(input[stbir__decode_order0]));
9351 #if stbir__coder_min_num >= 2
9352 decode[1] = ((float)(input[stbir__decode_order1]));
9353 #endif
9354 #if stbir__coder_min_num >= 3
9355 decode[2] = ((float)(input[stbir__decode_order2]));
9356 #endif
9357 decode += stbir__coder_min_num;
9358 input += stbir__coder_min_num;
9359 }
9360 #endif
9361 return decode_end;
9362}
9363
9364static void STBIR__CODER_NAME(stbir__encode_uint16_linear)( void * outputp, int width_times_channels, float const * encode )
9365{
9366 unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp;
9367 unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels;
9368
9369 #ifdef STBIR_SIMD
9370 {
9371 if ( width_times_channels >= stbir__simdfX_float_count*2 )
9372 {
9373 float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2;
9374 end_output -= stbir__simdfX_float_count*2;
9375 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
9376 for(;;)
9377 {
9378 stbir__simdfX e0, e1;
9379 stbir__simdiX i;
9380 STBIR_SIMD_NO_UNROLL(encode);
9381 stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode );
9382 stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count );
9383 stbir__encode_simdfX_unflip( e0 );
9384 stbir__encode_simdfX_unflip( e1 );
9385 stbir__simdfX_pack_to_words( i, e0, e1 );
9386 stbir__simdiX_store( output, i );
9387 encode += stbir__simdfX_float_count*2;
9388 output += stbir__simdfX_float_count*2;
9389 if ( output <= end_output )
9390 continue;
9391 if ( output == ( end_output + stbir__simdfX_float_count*2 ) )
9392 break;
9393 output = end_output; // backup and do last couple
9394 encode = end_encode_m8;
9395 }
9396 return;
9397 }
9398 }
9399
9400 // try to do blocks of 4 when you can
9401 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9402 output += 4;
9403 STBIR_NO_UNROLL_LOOP_START
9404 while( output <= end_output )
9405 {
9406 stbir__simdf e;
9407 stbir__simdi i;
9408 STBIR_NO_UNROLL(encode);
9409 stbir__simdf_load( e, encode );
9410 stbir__simdf_add( e, STBIR__CONSTF(STBIR_simd_point5), e );
9411 stbir__encode_simdf4_unflip( e );
9412 stbir__simdf_pack_to_8words( i, e, e ); // only use first 4
9413 stbir__simdi_store2( output-4, i );
9414 output += 4;
9415 encode += 4;
9416 }
9417 output -= 4;
9418 #endif
9419
9420 #else
9421
9422 // try to do blocks of 4 when you can
9423 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9424 output += 4;
9425 STBIR_SIMD_NO_UNROLL_LOOP_START
9426 while( output <= end_output )
9427 {
9428 float f;
9429 STBIR_SIMD_NO_UNROLL(encode);
9430 f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f;
9431 f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f;
9432 f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f;
9433 f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f;
9434 output += 4;
9435 encode += 4;
9436 }
9437 output -= 4;
9438 #endif
9439
9440 #endif
9441
9442 // do the remnants
9443 #if stbir__coder_min_num < 4
9444 STBIR_NO_UNROLL_LOOP_START
9445 while( output < end_output )
9446 {
9447 float f;
9448 STBIR_NO_UNROLL(encode);
9449 f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f;
9450 #if stbir__coder_min_num >= 2
9451 f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f;
9452 #endif
9453 #if stbir__coder_min_num >= 3
9454 f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f;
9455 #endif
9456 output += stbir__coder_min_num;
9457 encode += stbir__coder_min_num;
9458 }
9459 #endif
9460}
9461
9462static float * STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, int width_times_channels, void const * inputp )
9463{
9464 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
9465 float * decode_end = (float*) decode + width_times_channels;
9466 stbir__FP16 const * input = (stbir__FP16 const *)inputp;
9467
9468 #ifdef STBIR_SIMD
9469 if ( width_times_channels >= 8 )
9470 {
9471 stbir__FP16 const * end_input_m8 = input + width_times_channels - 8;
9472 decode_end -= 8;
9473 STBIR_NO_UNROLL_LOOP_START_INF_FOR
9474 for(;;)
9475 {
9476 STBIR_NO_UNROLL(decode);
9477
9478 stbir__half_to_float_SIMD( decode, input );
9479 #ifdef stbir__decode_swizzle
9480 #ifdef STBIR_SIMD8
9481 {
9482 stbir__simdf8 of;
9483 stbir__simdf8_load( of, decode );
9484 stbir__decode_simdf8_flip( of );
9485 stbir__simdf8_store( decode, of );
9486 }
9487 #else
9488 {
9489 stbir__simdf of0,of1;
9490 stbir__simdf_load( of0, decode );
9491 stbir__simdf_load( of1, decode+4 );
9492 stbir__decode_simdf4_flip( of0 );
9493 stbir__decode_simdf4_flip( of1 );
9494 stbir__simdf_store( decode, of0 );
9495 stbir__simdf_store( decode+4, of1 );
9496 }
9497 #endif
9498 #endif
9499 decode += 8;
9500 input += 8;
9501 if ( decode <= decode_end )
9502 continue;
9503 if ( decode == ( decode_end + 8 ) )
9504 break;
9505 decode = decode_end; // backup and do last couple
9506 input = end_input_m8;
9507 }
9508 return decode_end + 8;
9509 }
9510 #endif
9511
9512 // try to do blocks of 4 when you can
9513 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9514 decode += 4;
9515 STBIR_SIMD_NO_UNROLL_LOOP_START
9516 while( decode <= decode_end )
9517 {
9518 STBIR_SIMD_NO_UNROLL(decode);
9519 decode[0-4] = stbir__half_to_float(input[stbir__decode_order0]);
9520 decode[1-4] = stbir__half_to_float(input[stbir__decode_order1]);
9521 decode[2-4] = stbir__half_to_float(input[stbir__decode_order2]);
9522 decode[3-4] = stbir__half_to_float(input[stbir__decode_order3]);
9523 decode += 4;
9524 input += 4;
9525 }
9526 decode -= 4;
9527 #endif
9528
9529 // do the remnants
9530 #if stbir__coder_min_num < 4
9531 STBIR_NO_UNROLL_LOOP_START
9532 while( decode < decode_end )
9533 {
9534 STBIR_NO_UNROLL(decode);
9535 decode[0] = stbir__half_to_float(input[stbir__decode_order0]);
9536 #if stbir__coder_min_num >= 2
9537 decode[1] = stbir__half_to_float(input[stbir__decode_order1]);
9538 #endif
9539 #if stbir__coder_min_num >= 3
9540 decode[2] = stbir__half_to_float(input[stbir__decode_order2]);
9541 #endif
9542 decode += stbir__coder_min_num;
9543 input += stbir__coder_min_num;
9544 }
9545 #endif
9546 return decode_end;
9547}
9548
9549static void STBIR__CODER_NAME( stbir__encode_half_float_linear )( void * outputp, int width_times_channels, float const * encode )
9550{
9551 stbir__FP16 STBIR_SIMD_STREAMOUT_PTR( * ) output = (stbir__FP16*) outputp;
9552 stbir__FP16 * end_output = ( (stbir__FP16*) output ) + width_times_channels;
9553
9554 #ifdef STBIR_SIMD
9555 if ( width_times_channels >= 8 )
9556 {
9557 float const * end_encode_m8 = encode + width_times_channels - 8;
9558 end_output -= 8;
9559 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
9560 for(;;)
9561 {
9562 STBIR_SIMD_NO_UNROLL(encode);
9563 #ifdef stbir__decode_swizzle
9564 #ifdef STBIR_SIMD8
9565 {
9566 stbir__simdf8 of;
9567 stbir__simdf8_load( of, encode );
9568 stbir__encode_simdf8_unflip( of );
9569 stbir__float_to_half_SIMD( output, (float*)&of );
9570 }
9571 #else
9572 {
9573 stbir__simdf of[2];
9574 stbir__simdf_load( of[0], encode );
9575 stbir__simdf_load( of[1], encode+4 );
9576 stbir__encode_simdf4_unflip( of[0] );
9577 stbir__encode_simdf4_unflip( of[1] );
9578 stbir__float_to_half_SIMD( output, (float*)of );
9579 }
9580 #endif
9581 #else
9582 stbir__float_to_half_SIMD( output, encode );
9583 #endif
9584 encode += 8;
9585 output += 8;
9586 if ( output <= end_output )
9587 continue;
9588 if ( output == ( end_output + 8 ) )
9589 break;
9590 output = end_output; // backup and do last couple
9591 encode = end_encode_m8;
9592 }
9593 return;
9594 }
9595 #endif
9596
9597 // try to do blocks of 4 when you can
9598 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9599 output += 4;
9600 STBIR_SIMD_NO_UNROLL_LOOP_START
9601 while( output <= end_output )
9602 {
9603 STBIR_SIMD_NO_UNROLL(output);
9604 output[0-4] = stbir__float_to_half(encode[stbir__encode_order0]);
9605 output[1-4] = stbir__float_to_half(encode[stbir__encode_order1]);
9606 output[2-4] = stbir__float_to_half(encode[stbir__encode_order2]);
9607 output[3-4] = stbir__float_to_half(encode[stbir__encode_order3]);
9608 output += 4;
9609 encode += 4;
9610 }
9611 output -= 4;
9612 #endif
9613
9614 // do the remnants
9615 #if stbir__coder_min_num < 4
9616 STBIR_NO_UNROLL_LOOP_START
9617 while( output < end_output )
9618 {
9619 STBIR_NO_UNROLL(output);
9620 output[0] = stbir__float_to_half(encode[stbir__encode_order0]);
9621 #if stbir__coder_min_num >= 2
9622 output[1] = stbir__float_to_half(encode[stbir__encode_order1]);
9623 #endif
9624 #if stbir__coder_min_num >= 3
9625 output[2] = stbir__float_to_half(encode[stbir__encode_order2]);
9626 #endif
9627 output += stbir__coder_min_num;
9628 encode += stbir__coder_min_num;
9629 }
9630 #endif
9631}
9632
9633static float * STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, int width_times_channels, void const * inputp )
9634{
9635 #ifdef stbir__decode_swizzle
9636 float STBIR_STREAMOUT_PTR( * ) decode = decodep;
9637 float * decode_end = (float*) decode + width_times_channels;
9638 float const * input = (float const *)inputp;
9639
9640 #ifdef STBIR_SIMD
9641 if ( width_times_channels >= 16 )
9642 {
9643 float const * end_input_m16 = input + width_times_channels - 16;
9644 decode_end -= 16;
9645 STBIR_NO_UNROLL_LOOP_START_INF_FOR
9646 for(;;)
9647 {
9648 STBIR_NO_UNROLL(decode);
9649 #ifdef stbir__decode_swizzle
9650 #ifdef STBIR_SIMD8
9651 {
9652 stbir__simdf8 of0,of1;
9653 stbir__simdf8_load( of0, input );
9654 stbir__simdf8_load( of1, input+8 );
9655 stbir__decode_simdf8_flip( of0 );
9656 stbir__decode_simdf8_flip( of1 );
9657 stbir__simdf8_store( decode, of0 );
9658 stbir__simdf8_store( decode+8, of1 );
9659 }
9660 #else
9661 {
9662 stbir__simdf of0,of1,of2,of3;
9663 stbir__simdf_load( of0, input );
9664 stbir__simdf_load( of1, input+4 );
9665 stbir__simdf_load( of2, input+8 );
9666 stbir__simdf_load( of3, input+12 );
9667 stbir__decode_simdf4_flip( of0 );
9668 stbir__decode_simdf4_flip( of1 );
9669 stbir__decode_simdf4_flip( of2 );
9670 stbir__decode_simdf4_flip( of3 );
9671 stbir__simdf_store( decode, of0 );
9672 stbir__simdf_store( decode+4, of1 );
9673 stbir__simdf_store( decode+8, of2 );
9674 stbir__simdf_store( decode+12, of3 );
9675 }
9676 #endif
9677 #endif
9678 decode += 16;
9679 input += 16;
9680 if ( decode <= decode_end )
9681 continue;
9682 if ( decode == ( decode_end + 16 ) )
9683 break;
9684 decode = decode_end; // backup and do last couple
9685 input = end_input_m16;
9686 }
9687 return decode_end + 16;
9688 }
9689 #endif
9690
9691 // try to do blocks of 4 when you can
9692 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9693 decode += 4;
9694 STBIR_SIMD_NO_UNROLL_LOOP_START
9695 while( decode <= decode_end )
9696 {
9697 STBIR_SIMD_NO_UNROLL(decode);
9698 decode[0-4] = input[stbir__decode_order0];
9699 decode[1-4] = input[stbir__decode_order1];
9700 decode[2-4] = input[stbir__decode_order2];
9701 decode[3-4] = input[stbir__decode_order3];
9702 decode += 4;
9703 input += 4;
9704 }
9705 decode -= 4;
9706 #endif
9707
9708 // do the remnants
9709 #if stbir__coder_min_num < 4
9710 STBIR_NO_UNROLL_LOOP_START
9711 while( decode < decode_end )
9712 {
9713 STBIR_NO_UNROLL(decode);
9714 decode[0] = input[stbir__decode_order0];
9715 #if stbir__coder_min_num >= 2
9716 decode[1] = input[stbir__decode_order1];
9717 #endif
9718 #if stbir__coder_min_num >= 3
9719 decode[2] = input[stbir__decode_order2];
9720 #endif
9721 decode += stbir__coder_min_num;
9722 input += stbir__coder_min_num;
9723 }
9724 #endif
9725 return decode_end;
9726
9727 #else
9728
9729 if ( (void*)decodep != inputp )
9730 STBIR_MEMCPY( decodep, inputp, width_times_channels * sizeof( float ) );
9731
9732 return decodep + width_times_channels;
9733
9734 #endif
9735}
9736
9737static void STBIR__CODER_NAME( stbir__encode_float_linear )( void * outputp, int width_times_channels, float const * encode )
9738{
9739 #if !defined( STBIR_FLOAT_HIGH_CLAMP ) && !defined(STBIR_FLOAT_LOW_CLAMP) && !defined(stbir__decode_swizzle)
9740
9741 if ( (void*)outputp != (void*) encode )
9742 STBIR_MEMCPY( outputp, encode, width_times_channels * sizeof( float ) );
9743
9744 #else
9745
9746 float STBIR_SIMD_STREAMOUT_PTR( * ) output = (float*) outputp;
9747 float * end_output = ( (float*) output ) + width_times_channels;
9748
9749 #ifdef STBIR_FLOAT_HIGH_CLAMP
9750 #define stbir_scalar_hi_clamp( v ) if ( v > STBIR_FLOAT_HIGH_CLAMP ) v = STBIR_FLOAT_HIGH_CLAMP;
9751 #else
9752 #define stbir_scalar_hi_clamp( v )
9753 #endif
9754 #ifdef STBIR_FLOAT_LOW_CLAMP
9755 #define stbir_scalar_lo_clamp( v ) if ( v < STBIR_FLOAT_LOW_CLAMP ) v = STBIR_FLOAT_LOW_CLAMP;
9756 #else
9757 #define stbir_scalar_lo_clamp( v )
9758 #endif
9759
9760 #ifdef STBIR_SIMD
9761
9762 #ifdef STBIR_FLOAT_HIGH_CLAMP
9763 const stbir__simdfX high_clamp = stbir__simdf_frepX(STBIR_FLOAT_HIGH_CLAMP);
9764 #endif
9765 #ifdef STBIR_FLOAT_LOW_CLAMP
9766 const stbir__simdfX low_clamp = stbir__simdf_frepX(STBIR_FLOAT_LOW_CLAMP);
9767 #endif
9768
9769 if ( width_times_channels >= ( stbir__simdfX_float_count * 2 ) )
9770 {
9771 float const * end_encode_m8 = encode + width_times_channels - ( stbir__simdfX_float_count * 2 );
9772 end_output -= ( stbir__simdfX_float_count * 2 );
9773 STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
9774 for(;;)
9775 {
9776 stbir__simdfX e0, e1;
9777 STBIR_SIMD_NO_UNROLL(encode);
9778 stbir__simdfX_load( e0, encode );
9779 stbir__simdfX_load( e1, encode+stbir__simdfX_float_count );
9780#ifdef STBIR_FLOAT_HIGH_CLAMP
9781 stbir__simdfX_min( e0, e0, high_clamp );
9782 stbir__simdfX_min( e1, e1, high_clamp );
9783#endif
9784#ifdef STBIR_FLOAT_LOW_CLAMP
9785 stbir__simdfX_max( e0, e0, low_clamp );
9786 stbir__simdfX_max( e1, e1, low_clamp );
9787#endif
9788 stbir__encode_simdfX_unflip( e0 );
9789 stbir__encode_simdfX_unflip( e1 );
9790 stbir__simdfX_store( output, e0 );
9791 stbir__simdfX_store( output+stbir__simdfX_float_count, e1 );
9792 encode += stbir__simdfX_float_count * 2;
9793 output += stbir__simdfX_float_count * 2;
9794 if ( output <= end_output )
9795 continue;
9796 if ( output == ( end_output + ( stbir__simdfX_float_count * 2 ) ) )
9797 break;
9798 output = end_output; // backup and do last couple
9799 encode = end_encode_m8;
9800 }
9801 return;
9802 }
9803
9804 // try to do blocks of 4 when you can
9805 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9806 output += 4;
9807 STBIR_NO_UNROLL_LOOP_START
9808 while( output <= end_output )
9809 {
9810 stbir__simdf e0;
9811 STBIR_NO_UNROLL(encode);
9812 stbir__simdf_load( e0, encode );
9813#ifdef STBIR_FLOAT_HIGH_CLAMP
9814 stbir__simdf_min( e0, e0, high_clamp );
9815#endif
9816#ifdef STBIR_FLOAT_LOW_CLAMP
9817 stbir__simdf_max( e0, e0, low_clamp );
9818#endif
9819 stbir__encode_simdf4_unflip( e0 );
9820 stbir__simdf_store( output-4, e0 );
9821 output += 4;
9822 encode += 4;
9823 }
9824 output -= 4;
9825 #endif
9826
9827 #else
9828
9829 // try to do blocks of 4 when you can
9830 #if stbir__coder_min_num != 3 // doesn't divide cleanly by four
9831 output += 4;
9832 STBIR_SIMD_NO_UNROLL_LOOP_START
9833 while( output <= end_output )
9834 {
9835 float e;
9836 STBIR_SIMD_NO_UNROLL(encode);
9837 e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0-4] = e;
9838 e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1-4] = e;
9839 e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2-4] = e;
9840 e = encode[ stbir__encode_order3 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[3-4] = e;
9841 output += 4;
9842 encode += 4;
9843 }
9844 output -= 4;
9845
9846 #endif
9847
9848 #endif
9849
9850 // do the remnants
9851 #if stbir__coder_min_num < 4
9852 STBIR_NO_UNROLL_LOOP_START
9853 while( output < end_output )
9854 {
9855 float e;
9856 STBIR_NO_UNROLL(encode);
9857 e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0] = e;
9858 #if stbir__coder_min_num >= 2
9859 e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1] = e;
9860 #endif
9861 #if stbir__coder_min_num >= 3
9862 e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2] = e;
9863 #endif
9864 output += stbir__coder_min_num;
9865 encode += stbir__coder_min_num;
9866 }
9867 #endif
9868
9869 #endif
9870}
9871
9872#undef stbir__decode_suffix
9873#undef stbir__decode_simdf8_flip
9874#undef stbir__decode_simdf4_flip
9875#undef stbir__decode_order0
9876#undef stbir__decode_order1
9877#undef stbir__decode_order2
9878#undef stbir__decode_order3
9879#undef stbir__encode_order0
9880#undef stbir__encode_order1
9881#undef stbir__encode_order2
9882#undef stbir__encode_order3
9883#undef stbir__encode_simdf8_unflip
9884#undef stbir__encode_simdf4_unflip
9885#undef stbir__encode_simdfX_unflip
9886#undef STBIR__CODER_NAME
9887#undef stbir__coder_min_num
9888#undef stbir__decode_swizzle
9889#undef stbir_scalar_hi_clamp
9890#undef stbir_scalar_lo_clamp
9891#undef STB_IMAGE_RESIZE_DO_CODERS
9892
9893#elif defined( STB_IMAGE_RESIZE_DO_VERTICALS)
9894
9895#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
9896#define STBIR_chans( start, end ) STBIR_strs_join14(start,STBIR__vertical_channels,end,_cont)
9897#else
9898#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__vertical_channels,end)
9899#endif
9900
9901#if STBIR__vertical_channels >= 1
9902#define stbIF0( code ) code
9903#else
9904#define stbIF0( code )
9905#endif
9906#if STBIR__vertical_channels >= 2
9907#define stbIF1( code ) code
9908#else
9909#define stbIF1( code )
9910#endif
9911#if STBIR__vertical_channels >= 3
9912#define stbIF2( code ) code
9913#else
9914#define stbIF2( code )
9915#endif
9916#if STBIR__vertical_channels >= 4
9917#define stbIF3( code ) code
9918#else
9919#define stbIF3( code )
9920#endif
9921#if STBIR__vertical_channels >= 5
9922#define stbIF4( code ) code
9923#else
9924#define stbIF4( code )
9925#endif
9926#if STBIR__vertical_channels >= 6
9927#define stbIF5( code ) code
9928#else
9929#define stbIF5( code )
9930#endif
9931#if STBIR__vertical_channels >= 7
9932#define stbIF6( code ) code
9933#else
9934#define stbIF6( code )
9935#endif
9936#if STBIR__vertical_channels >= 8
9937#define stbIF7( code ) code
9938#else
9939#define stbIF7( code )
9940#endif
9941
9942static void STBIR_chans( stbir__vertical_scatter_with_,_coeffs)( float ** outputs, float const * vertical_coefficients, float const * input, float const * input_end )
9943{
9944 stbIF0( float STBIR_SIMD_STREAMOUT_PTR( * ) output0 = outputs[0]; float c0s = vertical_coefficients[0]; )
9945 stbIF1( float STBIR_SIMD_STREAMOUT_PTR( * ) output1 = outputs[1]; float c1s = vertical_coefficients[1]; )
9946 stbIF2( float STBIR_SIMD_STREAMOUT_PTR( * ) output2 = outputs[2]; float c2s = vertical_coefficients[2]; )
9947 stbIF3( float STBIR_SIMD_STREAMOUT_PTR( * ) output3 = outputs[3]; float c3s = vertical_coefficients[3]; )
9948 stbIF4( float STBIR_SIMD_STREAMOUT_PTR( * ) output4 = outputs[4]; float c4s = vertical_coefficients[4]; )
9949 stbIF5( float STBIR_SIMD_STREAMOUT_PTR( * ) output5 = outputs[5]; float c5s = vertical_coefficients[5]; )
9950 stbIF6( float STBIR_SIMD_STREAMOUT_PTR( * ) output6 = outputs[6]; float c6s = vertical_coefficients[6]; )
9951 stbIF7( float STBIR_SIMD_STREAMOUT_PTR( * ) output7 = outputs[7]; float c7s = vertical_coefficients[7]; )
9952
9953 #ifdef STBIR_SIMD
9954 {
9955 stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); )
9956 stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); )
9957 stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); )
9958 stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); )
9959 stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); )
9960 stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); )
9961 stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); )
9962 stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); )
9963 STBIR_SIMD_NO_UNROLL_LOOP_START
9964 while ( ( (char*)input_end - (char*) input ) >= (16*stbir__simdfX_float_count) )
9965 {
9966 stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3;
9967 STBIR_SIMD_NO_UNROLL(output0);
9968
9969 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) );
9970
9971 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
9972 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) );
9973 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 );
9974 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 ); )
9975 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) );
9976 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 );
9977 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 ); )
9978 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) );
9979 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 );
9980 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 ); )
9981 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) );
9982 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 );
9983 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 ); )
9984 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) );
9985 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 );
9986 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 ); )
9987 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) );
9988 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 );
9989 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 ); )
9990 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) );
9991 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 );
9992 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 ); )
9993 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) );
9994 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 );
9995 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 ); )
9996 #else
9997 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 );
9998 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 ); )
9999 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 );
10000 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 ); )
10001 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 );
10002 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 ); )
10003 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 );
10004 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 ); )
10005 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 );
10006 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 ); )
10007 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 );
10008 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 ); )
10009 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 );
10010 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 ); )
10011 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 );
10012 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 ); )
10013 #endif
10014
10015 input += (4*stbir__simdfX_float_count);
10016 stbIF0( output0 += (4*stbir__simdfX_float_count); ) stbIF1( output1 += (4*stbir__simdfX_float_count); ) stbIF2( output2 += (4*stbir__simdfX_float_count); ) stbIF3( output3 += (4*stbir__simdfX_float_count); ) stbIF4( output4 += (4*stbir__simdfX_float_count); ) stbIF5( output5 += (4*stbir__simdfX_float_count); ) stbIF6( output6 += (4*stbir__simdfX_float_count); ) stbIF7( output7 += (4*stbir__simdfX_float_count); )
10017 }
10018 STBIR_SIMD_NO_UNROLL_LOOP_START
10019 while ( ( (char*)input_end - (char*) input ) >= 16 )
10020 {
10021 stbir__simdf o0, r0;
10022 STBIR_SIMD_NO_UNROLL(output0);
10023
10024 stbir__simdf_load( r0, input );
10025
10026 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
10027 stbIF0( stbir__simdf_load( o0, output0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); )
10028 stbIF1( stbir__simdf_load( o0, output1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); )
10029 stbIF2( stbir__simdf_load( o0, output2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); )
10030 stbIF3( stbir__simdf_load( o0, output3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); )
10031 stbIF4( stbir__simdf_load( o0, output4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); )
10032 stbIF5( stbir__simdf_load( o0, output5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); )
10033 stbIF6( stbir__simdf_load( o0, output6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); )
10034 stbIF7( stbir__simdf_load( o0, output7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); )
10035 #else
10036 stbIF0( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); )
10037 stbIF1( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); )
10038 stbIF2( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); )
10039 stbIF3( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); )
10040 stbIF4( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); )
10041 stbIF5( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); )
10042 stbIF6( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); )
10043 stbIF7( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); )
10044 #endif
10045
10046 input += 4;
10047 stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; )
10048 }
10049 }
10050 #else
10051 STBIR_NO_UNROLL_LOOP_START
10052 while ( ( (char*)input_end - (char*) input ) >= 16 )
10053 {
10054 float r0, r1, r2, r3;
10055 STBIR_NO_UNROLL(input);
10056
10057 r0 = input[0], r1 = input[1], r2 = input[2], r3 = input[3];
10058
10059 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
10060 stbIF0( output0[0] += ( r0 * c0s ); output0[1] += ( r1 * c0s ); output0[2] += ( r2 * c0s ); output0[3] += ( r3 * c0s ); )
10061 stbIF1( output1[0] += ( r0 * c1s ); output1[1] += ( r1 * c1s ); output1[2] += ( r2 * c1s ); output1[3] += ( r3 * c1s ); )
10062 stbIF2( output2[0] += ( r0 * c2s ); output2[1] += ( r1 * c2s ); output2[2] += ( r2 * c2s ); output2[3] += ( r3 * c2s ); )
10063 stbIF3( output3[0] += ( r0 * c3s ); output3[1] += ( r1 * c3s ); output3[2] += ( r2 * c3s ); output3[3] += ( r3 * c3s ); )
10064 stbIF4( output4[0] += ( r0 * c4s ); output4[1] += ( r1 * c4s ); output4[2] += ( r2 * c4s ); output4[3] += ( r3 * c4s ); )
10065 stbIF5( output5[0] += ( r0 * c5s ); output5[1] += ( r1 * c5s ); output5[2] += ( r2 * c5s ); output5[3] += ( r3 * c5s ); )
10066 stbIF6( output6[0] += ( r0 * c6s ); output6[1] += ( r1 * c6s ); output6[2] += ( r2 * c6s ); output6[3] += ( r3 * c6s ); )
10067 stbIF7( output7[0] += ( r0 * c7s ); output7[1] += ( r1 * c7s ); output7[2] += ( r2 * c7s ); output7[3] += ( r3 * c7s ); )
10068 #else
10069 stbIF0( output0[0] = ( r0 * c0s ); output0[1] = ( r1 * c0s ); output0[2] = ( r2 * c0s ); output0[3] = ( r3 * c0s ); )
10070 stbIF1( output1[0] = ( r0 * c1s ); output1[1] = ( r1 * c1s ); output1[2] = ( r2 * c1s ); output1[3] = ( r3 * c1s ); )
10071 stbIF2( output2[0] = ( r0 * c2s ); output2[1] = ( r1 * c2s ); output2[2] = ( r2 * c2s ); output2[3] = ( r3 * c2s ); )
10072 stbIF3( output3[0] = ( r0 * c3s ); output3[1] = ( r1 * c3s ); output3[2] = ( r2 * c3s ); output3[3] = ( r3 * c3s ); )
10073 stbIF4( output4[0] = ( r0 * c4s ); output4[1] = ( r1 * c4s ); output4[2] = ( r2 * c4s ); output4[3] = ( r3 * c4s ); )
10074 stbIF5( output5[0] = ( r0 * c5s ); output5[1] = ( r1 * c5s ); output5[2] = ( r2 * c5s ); output5[3] = ( r3 * c5s ); )
10075 stbIF6( output6[0] = ( r0 * c6s ); output6[1] = ( r1 * c6s ); output6[2] = ( r2 * c6s ); output6[3] = ( r3 * c6s ); )
10076 stbIF7( output7[0] = ( r0 * c7s ); output7[1] = ( r1 * c7s ); output7[2] = ( r2 * c7s ); output7[3] = ( r3 * c7s ); )
10077 #endif
10078
10079 input += 4;
10080 stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; )
10081 }
10082 #endif
10083 STBIR_NO_UNROLL_LOOP_START
10084 while ( input < input_end )
10085 {
10086 float r = input[0];
10087 STBIR_NO_UNROLL(output0);
10088
10089 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
10090 stbIF0( output0[0] += ( r * c0s ); )
10091 stbIF1( output1[0] += ( r * c1s ); )
10092 stbIF2( output2[0] += ( r * c2s ); )
10093 stbIF3( output3[0] += ( r * c3s ); )
10094 stbIF4( output4[0] += ( r * c4s ); )
10095 stbIF5( output5[0] += ( r * c5s ); )
10096 stbIF6( output6[0] += ( r * c6s ); )
10097 stbIF7( output7[0] += ( r * c7s ); )
10098 #else
10099 stbIF0( output0[0] = ( r * c0s ); )
10100 stbIF1( output1[0] = ( r * c1s ); )
10101 stbIF2( output2[0] = ( r * c2s ); )
10102 stbIF3( output3[0] = ( r * c3s ); )
10103 stbIF4( output4[0] = ( r * c4s ); )
10104 stbIF5( output5[0] = ( r * c5s ); )
10105 stbIF6( output6[0] = ( r * c6s ); )
10106 stbIF7( output7[0] = ( r * c7s ); )
10107 #endif
10108
10109 ++input;
10110 stbIF0( ++output0; ) stbIF1( ++output1; ) stbIF2( ++output2; ) stbIF3( ++output3; ) stbIF4( ++output4; ) stbIF5( ++output5; ) stbIF6( ++output6; ) stbIF7( ++output7; )
10111 }
10112}
10113
10114static void STBIR_chans( stbir__vertical_gather_with_,_coeffs)( float * outputp, float const * vertical_coefficients, float const ** inputs, float const * input0_end )
10115{
10116 float STBIR_SIMD_STREAMOUT_PTR( * ) output = outputp;
10117
10118 stbIF0( float const * input0 = inputs[0]; float c0s = vertical_coefficients[0]; )
10119 stbIF1( float const * input1 = inputs[1]; float c1s = vertical_coefficients[1]; )
10120 stbIF2( float const * input2 = inputs[2]; float c2s = vertical_coefficients[2]; )
10121 stbIF3( float const * input3 = inputs[3]; float c3s = vertical_coefficients[3]; )
10122 stbIF4( float const * input4 = inputs[4]; float c4s = vertical_coefficients[4]; )
10123 stbIF5( float const * input5 = inputs[5]; float c5s = vertical_coefficients[5]; )
10124 stbIF6( float const * input6 = inputs[6]; float c6s = vertical_coefficients[6]; )
10125 stbIF7( float const * input7 = inputs[7]; float c7s = vertical_coefficients[7]; )
10126
10127#if ( STBIR__vertical_channels == 1 ) && !defined(STB_IMAGE_RESIZE_VERTICAL_CONTINUE)
10128 // check single channel one weight
10129 if ( ( c0s >= (1.0f-0.000001f) ) && ( c0s <= (1.0f+0.000001f) ) )
10130 {
10131 STBIR_MEMCPY( output, input0, (char*)input0_end - (char*)input0 );
10132 return;
10133 }
10134#endif
10135
10136 #ifdef STBIR_SIMD
10137 {
10138 stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); )
10139 stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); )
10140 stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); )
10141 stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); )
10142 stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); )
10143 stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); )
10144 stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); )
10145 stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); )
10146
10147 STBIR_SIMD_NO_UNROLL_LOOP_START
10148 while ( ( (char*)input0_end - (char*) input0 ) >= (16*stbir__simdfX_float_count) )
10149 {
10150 stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3;
10151 STBIR_SIMD_NO_UNROLL(output);
10152
10153 // prefetch four loop iterations ahead (doesn't affect much for small resizes, but helps with big ones)
10154 stbIF0( stbir__prefetch( input0 + (16*stbir__simdfX_float_count) ); )
10155 stbIF1( stbir__prefetch( input1 + (16*stbir__simdfX_float_count) ); )
10156 stbIF2( stbir__prefetch( input2 + (16*stbir__simdfX_float_count) ); )
10157 stbIF3( stbir__prefetch( input3 + (16*stbir__simdfX_float_count) ); )
10158 stbIF4( stbir__prefetch( input4 + (16*stbir__simdfX_float_count) ); )
10159 stbIF5( stbir__prefetch( input5 + (16*stbir__simdfX_float_count) ); )
10160 stbIF6( stbir__prefetch( input6 + (16*stbir__simdfX_float_count) ); )
10161 stbIF7( stbir__prefetch( input7 + (16*stbir__simdfX_float_count) ); )
10162
10163 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
10164 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) );
10165 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) );
10166 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 ); )
10167 #else
10168 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) );
10169 stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); )
10170 #endif
10171
10172 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) );
10173 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 ); )
10174 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) );
10175 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 ); )
10176 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) );
10177 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 ); )
10178 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) );
10179 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 ); )
10180 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) );
10181 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 ); )
10182 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) );
10183 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 ); )
10184 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) );
10185 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 ); )
10186
10187 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 );
10188 output += (4*stbir__simdfX_float_count);
10189 stbIF0( input0 += (4*stbir__simdfX_float_count); ) stbIF1( input1 += (4*stbir__simdfX_float_count); ) stbIF2( input2 += (4*stbir__simdfX_float_count); ) stbIF3( input3 += (4*stbir__simdfX_float_count); ) stbIF4( input4 += (4*stbir__simdfX_float_count); ) stbIF5( input5 += (4*stbir__simdfX_float_count); ) stbIF6( input6 += (4*stbir__simdfX_float_count); ) stbIF7( input7 += (4*stbir__simdfX_float_count); )
10190 }
10191
10192 STBIR_SIMD_NO_UNROLL_LOOP_START
10193 while ( ( (char*)input0_end - (char*) input0 ) >= 16 )
10194 {
10195 stbir__simdf o0, r0;
10196 STBIR_SIMD_NO_UNROLL(output);
10197
10198 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
10199 stbIF0( stbir__simdf_load( o0, output ); stbir__simdf_load( r0, input0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); )
10200 #else
10201 stbIF0( stbir__simdf_load( r0, input0 ); stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); )
10202 #endif
10203 stbIF1( stbir__simdf_load( r0, input1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); )
10204 stbIF2( stbir__simdf_load( r0, input2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); )
10205 stbIF3( stbir__simdf_load( r0, input3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); )
10206 stbIF4( stbir__simdf_load( r0, input4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); )
10207 stbIF5( stbir__simdf_load( r0, input5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); )
10208 stbIF6( stbir__simdf_load( r0, input6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); )
10209 stbIF7( stbir__simdf_load( r0, input7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); )
10210
10211 stbir__simdf_store( output, o0 );
10212 output += 4;
10213 stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; )
10214 }
10215 }
10216 #else
10217 STBIR_NO_UNROLL_LOOP_START
10218 while ( ( (char*)input0_end - (char*) input0 ) >= 16 )
10219 {
10220 float o0, o1, o2, o3;
10221 STBIR_NO_UNROLL(output);
10222 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
10223 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; )
10224 #else
10225 stbIF0( o0 = input0[0] * c0s; o1 = input0[1] * c0s; o2 = input0[2] * c0s; o3 = input0[3] * c0s; )
10226 #endif
10227 stbIF1( o0 += input1[0] * c1s; o1 += input1[1] * c1s; o2 += input1[2] * c1s; o3 += input1[3] * c1s; )
10228 stbIF2( o0 += input2[0] * c2s; o1 += input2[1] * c2s; o2 += input2[2] * c2s; o3 += input2[3] * c2s; )
10229 stbIF3( o0 += input3[0] * c3s; o1 += input3[1] * c3s; o2 += input3[2] * c3s; o3 += input3[3] * c3s; )
10230 stbIF4( o0 += input4[0] * c4s; o1 += input4[1] * c4s; o2 += input4[2] * c4s; o3 += input4[3] * c4s; )
10231 stbIF5( o0 += input5[0] * c5s; o1 += input5[1] * c5s; o2 += input5[2] * c5s; o3 += input5[3] * c5s; )
10232 stbIF6( o0 += input6[0] * c6s; o1 += input6[1] * c6s; o2 += input6[2] * c6s; o3 += input6[3] * c6s; )
10233 stbIF7( o0 += input7[0] * c7s; o1 += input7[1] * c7s; o2 += input7[2] * c7s; o3 += input7[3] * c7s; )
10234 output[0] = o0; output[1] = o1; output[2] = o2; output[3] = o3;
10235 output += 4;
10236 stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; )
10237 }
10238 #endif
10239 STBIR_NO_UNROLL_LOOP_START
10240 while ( input0 < input0_end )
10241 {
10242 float o0;
10243 STBIR_NO_UNROLL(output);
10244 #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
10245 stbIF0( o0 = output[0] + input0[0] * c0s; )
10246 #else
10247 stbIF0( o0 = input0[0] * c0s; )
10248 #endif
10249 stbIF1( o0 += input1[0] * c1s; )
10250 stbIF2( o0 += input2[0] * c2s; )
10251 stbIF3( o0 += input3[0] * c3s; )
10252 stbIF4( o0 += input4[0] * c4s; )
10253 stbIF5( o0 += input5[0] * c5s; )
10254 stbIF6( o0 += input6[0] * c6s; )
10255 stbIF7( o0 += input7[0] * c7s; )
10256 output[0] = o0;
10257 ++output;
10258 stbIF0( ++input0; ) stbIF1( ++input1; ) stbIF2( ++input2; ) stbIF3( ++input3; ) stbIF4( ++input4; ) stbIF5( ++input5; ) stbIF6( ++input6; ) stbIF7( ++input7; )
10259 }
10260}
10261
10262#undef stbIF0
10263#undef stbIF1
10264#undef stbIF2
10265#undef stbIF3
10266#undef stbIF4
10267#undef stbIF5
10268#undef stbIF6
10269#undef stbIF7
10270#undef STB_IMAGE_RESIZE_DO_VERTICALS
10271#undef STBIR__vertical_channels
10272#undef STB_IMAGE_RESIZE_DO_HORIZONTALS
10273#undef STBIR_strs_join24
10274#undef STBIR_strs_join14
10275#undef STBIR_chans
10276#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
10277#undef STB_IMAGE_RESIZE_VERTICAL_CONTINUE
10278#endif
10279
10280#else // !STB_IMAGE_RESIZE_DO_VERTICALS
10281
10282#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__horizontal_channels,end)
10283
10284#ifndef stbir__2_coeff_only
10285#define stbir__2_coeff_only() \
10286 stbir__1_coeff_only(); \
10287 stbir__1_coeff_remnant(1);
10288#endif
10289
10290#ifndef stbir__2_coeff_remnant
10291#define stbir__2_coeff_remnant( ofs ) \
10292 stbir__1_coeff_remnant(ofs); \
10293 stbir__1_coeff_remnant((ofs)+1);
10294#endif
10295
10296#ifndef stbir__3_coeff_only
10297#define stbir__3_coeff_only() \
10298 stbir__2_coeff_only(); \
10299 stbir__1_coeff_remnant(2);
10300#endif
10301
10302#ifndef stbir__3_coeff_remnant
10303#define stbir__3_coeff_remnant( ofs ) \
10304 stbir__2_coeff_remnant(ofs); \
10305 stbir__1_coeff_remnant((ofs)+2);
10306#endif
10307
10308#ifndef stbir__3_coeff_setup
10309#define stbir__3_coeff_setup()
10310#endif
10311
10312#ifndef stbir__4_coeff_start
10313#define stbir__4_coeff_start() \
10314 stbir__2_coeff_only(); \
10315 stbir__2_coeff_remnant(2);
10316#endif
10317
10318#ifndef stbir__4_coeff_continue_from_4
10319#define stbir__4_coeff_continue_from_4( ofs ) \
10320 stbir__2_coeff_remnant(ofs); \
10321 stbir__2_coeff_remnant((ofs)+2);
10322#endif
10323
10324#ifndef stbir__store_output_tiny
10325#define stbir__store_output_tiny stbir__store_output
10326#endif
10327
10328static void STBIR_chans( stbir__horizontal_gather_,_channels_with_1_coeff)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10329{
10330 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10331 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10332 STBIR_SIMD_NO_UNROLL_LOOP_START
10333 do {
10334 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10335 float const * hc = horizontal_coefficients;
10336 stbir__1_coeff_only();
10337 stbir__store_output_tiny();
10338 } while ( output < output_end );
10339}
10340
10341static void STBIR_chans( stbir__horizontal_gather_,_channels_with_2_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10342{
10343 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10344 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10345 STBIR_SIMD_NO_UNROLL_LOOP_START
10346 do {
10347 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10348 float const * hc = horizontal_coefficients;
10349 stbir__2_coeff_only();
10350 stbir__store_output_tiny();
10351 } while ( output < output_end );
10352}
10353
10354static void STBIR_chans( stbir__horizontal_gather_,_channels_with_3_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10355{
10356 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10357 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10358 STBIR_SIMD_NO_UNROLL_LOOP_START
10359 do {
10360 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10361 float const * hc = horizontal_coefficients;
10362 stbir__3_coeff_only();
10363 stbir__store_output_tiny();
10364 } while ( output < output_end );
10365}
10366
10367static void STBIR_chans( stbir__horizontal_gather_,_channels_with_4_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10368{
10369 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10370 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10371 STBIR_SIMD_NO_UNROLL_LOOP_START
10372 do {
10373 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10374 float const * hc = horizontal_coefficients;
10375 stbir__4_coeff_start();
10376 stbir__store_output();
10377 } while ( output < output_end );
10378}
10379
10380static void STBIR_chans( stbir__horizontal_gather_,_channels_with_5_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10381{
10382 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10383 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10384 STBIR_SIMD_NO_UNROLL_LOOP_START
10385 do {
10386 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10387 float const * hc = horizontal_coefficients;
10388 stbir__4_coeff_start();
10389 stbir__1_coeff_remnant(4);
10390 stbir__store_output();
10391 } while ( output < output_end );
10392}
10393
10394static void STBIR_chans( stbir__horizontal_gather_,_channels_with_6_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10395{
10396 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10397 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10398 STBIR_SIMD_NO_UNROLL_LOOP_START
10399 do {
10400 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10401 float const * hc = horizontal_coefficients;
10402 stbir__4_coeff_start();
10403 stbir__2_coeff_remnant(4);
10404 stbir__store_output();
10405 } while ( output < output_end );
10406}
10407
10408static void STBIR_chans( stbir__horizontal_gather_,_channels_with_7_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10409{
10410 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10411 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10412 stbir__3_coeff_setup();
10413 STBIR_SIMD_NO_UNROLL_LOOP_START
10414 do {
10415 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10416 float const * hc = horizontal_coefficients;
10417
10418 stbir__4_coeff_start();
10419 stbir__3_coeff_remnant(4);
10420 stbir__store_output();
10421 } while ( output < output_end );
10422}
10423
10424static void STBIR_chans( stbir__horizontal_gather_,_channels_with_8_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10425{
10426 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10427 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10428 STBIR_SIMD_NO_UNROLL_LOOP_START
10429 do {
10430 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10431 float const * hc = horizontal_coefficients;
10432 stbir__4_coeff_start();
10433 stbir__4_coeff_continue_from_4(4);
10434 stbir__store_output();
10435 } while ( output < output_end );
10436}
10437
10438static void STBIR_chans( stbir__horizontal_gather_,_channels_with_9_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10439{
10440 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10441 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10442 STBIR_SIMD_NO_UNROLL_LOOP_START
10443 do {
10444 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10445 float const * hc = horizontal_coefficients;
10446 stbir__4_coeff_start();
10447 stbir__4_coeff_continue_from_4(4);
10448 stbir__1_coeff_remnant(8);
10449 stbir__store_output();
10450 } while ( output < output_end );
10451}
10452
10453static void STBIR_chans( stbir__horizontal_gather_,_channels_with_10_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10454{
10455 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10456 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10457 STBIR_SIMD_NO_UNROLL_LOOP_START
10458 do {
10459 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10460 float const * hc = horizontal_coefficients;
10461 stbir__4_coeff_start();
10462 stbir__4_coeff_continue_from_4(4);
10463 stbir__2_coeff_remnant(8);
10464 stbir__store_output();
10465 } while ( output < output_end );
10466}
10467
10468static void STBIR_chans( stbir__horizontal_gather_,_channels_with_11_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10469{
10470 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10471 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10472 stbir__3_coeff_setup();
10473 STBIR_SIMD_NO_UNROLL_LOOP_START
10474 do {
10475 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10476 float const * hc = horizontal_coefficients;
10477 stbir__4_coeff_start();
10478 stbir__4_coeff_continue_from_4(4);
10479 stbir__3_coeff_remnant(8);
10480 stbir__store_output();
10481 } while ( output < output_end );
10482}
10483
10484static void STBIR_chans( stbir__horizontal_gather_,_channels_with_12_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10485{
10486 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10487 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10488 STBIR_SIMD_NO_UNROLL_LOOP_START
10489 do {
10490 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10491 float const * hc = horizontal_coefficients;
10492 stbir__4_coeff_start();
10493 stbir__4_coeff_continue_from_4(4);
10494 stbir__4_coeff_continue_from_4(8);
10495 stbir__store_output();
10496 } while ( output < output_end );
10497}
10498
10499static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod0 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10500{
10501 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10502 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10503 STBIR_SIMD_NO_UNROLL_LOOP_START
10504 do {
10505 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10506 int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 4 + 3 ) >> 2;
10507 float const * hc = horizontal_coefficients;
10508
10509 stbir__4_coeff_start();
10510 STBIR_SIMD_NO_UNROLL_LOOP_START
10511 do {
10512 hc += 4;
10513 decode += STBIR__horizontal_channels * 4;
10514 stbir__4_coeff_continue_from_4( 0 );
10515 --n;
10516 } while ( n > 0 );
10517 stbir__store_output();
10518 } while ( output < output_end );
10519}
10520
10521static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod1 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10522{
10523 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10524 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10525 STBIR_SIMD_NO_UNROLL_LOOP_START
10526 do {
10527 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10528 int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 5 + 3 ) >> 2;
10529 float const * hc = horizontal_coefficients;
10530
10531 stbir__4_coeff_start();
10532 STBIR_SIMD_NO_UNROLL_LOOP_START
10533 do {
10534 hc += 4;
10535 decode += STBIR__horizontal_channels * 4;
10536 stbir__4_coeff_continue_from_4( 0 );
10537 --n;
10538 } while ( n > 0 );
10539 stbir__1_coeff_remnant( 4 );
10540 stbir__store_output();
10541 } while ( output < output_end );
10542}
10543
10544static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod2 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10545{
10546 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10547 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10548 STBIR_SIMD_NO_UNROLL_LOOP_START
10549 do {
10550 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10551 int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 6 + 3 ) >> 2;
10552 float const * hc = horizontal_coefficients;
10553
10554 stbir__4_coeff_start();
10555 STBIR_SIMD_NO_UNROLL_LOOP_START
10556 do {
10557 hc += 4;
10558 decode += STBIR__horizontal_channels * 4;
10559 stbir__4_coeff_continue_from_4( 0 );
10560 --n;
10561 } while ( n > 0 );
10562 stbir__2_coeff_remnant( 4 );
10563
10564 stbir__store_output();
10565 } while ( output < output_end );
10566}
10567
10568static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod3 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width )
10569{
10570 float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels;
10571 float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer;
10572 stbir__3_coeff_setup();
10573 STBIR_SIMD_NO_UNROLL_LOOP_START
10574 do {
10575 float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels;
10576 int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 7 + 3 ) >> 2;
10577 float const * hc = horizontal_coefficients;
10578
10579 stbir__4_coeff_start();
10580 STBIR_SIMD_NO_UNROLL_LOOP_START
10581 do {
10582 hc += 4;
10583 decode += STBIR__horizontal_channels * 4;
10584 stbir__4_coeff_continue_from_4( 0 );
10585 --n;
10586 } while ( n > 0 );
10587 stbir__3_coeff_remnant( 4 );
10588
10589 stbir__store_output();
10590 } while ( output < output_end );
10591}
10592
10593static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_funcs)[4]=
10594{
10595 STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod0),
10596 STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod1),
10597 STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod2),
10598 STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod3),
10599};
10600
10601static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_funcs)[12]=
10602{
10603 STBIR_chans(stbir__horizontal_gather_,_channels_with_1_coeff),
10604 STBIR_chans(stbir__horizontal_gather_,_channels_with_2_coeffs),
10605 STBIR_chans(stbir__horizontal_gather_,_channels_with_3_coeffs),
10606 STBIR_chans(stbir__horizontal_gather_,_channels_with_4_coeffs),
10607 STBIR_chans(stbir__horizontal_gather_,_channels_with_5_coeffs),
10608 STBIR_chans(stbir__horizontal_gather_,_channels_with_6_coeffs),
10609 STBIR_chans(stbir__horizontal_gather_,_channels_with_7_coeffs),
10610 STBIR_chans(stbir__horizontal_gather_,_channels_with_8_coeffs),
10611 STBIR_chans(stbir__horizontal_gather_,_channels_with_9_coeffs),
10612 STBIR_chans(stbir__horizontal_gather_,_channels_with_10_coeffs),
10613 STBIR_chans(stbir__horizontal_gather_,_channels_with_11_coeffs),
10614 STBIR_chans(stbir__horizontal_gather_,_channels_with_12_coeffs),
10615};
10616
10617#undef STBIR__horizontal_channels
10618#undef STB_IMAGE_RESIZE_DO_HORIZONTALS
10619#undef stbir__1_coeff_only
10620#undef stbir__1_coeff_remnant
10621#undef stbir__2_coeff_only
10622#undef stbir__2_coeff_remnant
10623#undef stbir__3_coeff_only
10624#undef stbir__3_coeff_remnant
10625#undef stbir__3_coeff_setup
10626#undef stbir__4_coeff_start
10627#undef stbir__4_coeff_continue_from_4
10628#undef stbir__store_output
10629#undef stbir__store_output_tiny
10630#undef STBIR_chans
10631
10632#endif // HORIZONALS
10633
10634#undef STBIR_strs_join2
10635#undef STBIR_strs_join1
10636
10637#endif // STB_IMAGE_RESIZE_DO_HORIZONTALS/VERTICALS/CODERS
10638
10639/*
10640------------------------------------------------------------------------------
10641This software is available under 2 licenses -- choose whichever you prefer.
10642------------------------------------------------------------------------------
10643ALTERNATIVE A - MIT License
10644Copyright (c) 2017 Sean Barrett
10645Permission is hereby granted, free of charge, to any person obtaining a copy of
10646this software and associated documentation files (the "Software"), to deal in
10647the Software without restriction, including without limitation the rights to
10648use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10649of the Software, and to permit persons to whom the Software is furnished to do
10650so, subject to the following conditions:
10651The above copyright notice and this permission notice shall be included in all
10652copies or substantial portions of the Software.
10653THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10654IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10655FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10656AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
10657LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10658OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
10659SOFTWARE.
10660------------------------------------------------------------------------------
10661ALTERNATIVE B - Public Domain (www.unlicense.org)
10662This is free and unencumbered software released into the public domain.
10663Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
10664software, either in source code form or as a compiled binary, for any purpose,
10665commercial or non-commercial, and by any means.
10666In jurisdictions that recognize copyright laws, the author or authors of this
10667software dedicate any and all copyright interest in the software to the public
10668domain. We make this dedication for the benefit of the public at large and to
10669the detriment of our heirs and successors. We intend this dedication to be an
10670overt act of relinquishment in perpetuity of all present and future rights to
10671this software under copyright law.
10672THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10673IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10674FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10675AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
10676ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10677WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10678------------------------------------------------------------------------------
10679*/