blob: cece8ef9fb6e83db4eb4600d7efe756e9af2a73c [file] [log] [blame]
Jim Flynn6217c3d2022-06-14 10:58:23 +01001//
2// Copyright (c) 2017 Sean Barrett
3// SPDX-License-Identifier: MIT
4//
5
Sadik Armagan93e2e402019-05-02 09:31:38 +01006/* stb_image_resize - v0.95 - public domain image resizing
7 by Jorge L Rodriguez (@VinoBS) - 2014
8 http://github.com/nothings/stb
9
10 Written with emphasis on usability, portability, and efficiency. (No
11 SIMD or threads, so it be easily outperformed by libs that use those.)
12 Only scaling and translation is supported, no rotations or shears.
13 Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation.
14
15 COMPILING & LINKING
16 In one C/C++ file that #includes this file, do this:
17 #define STB_IMAGE_RESIZE_IMPLEMENTATION
18 before the #include. That will create the implementation in that file.
19
20 QUICKSTART
21 stbir_resize_uint8( input_pixels , in_w , in_h , 0,
22 output_pixels, out_w, out_h, 0, num_channels)
23 stbir_resize_float(...)
24 stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0,
25 output_pixels, out_w, out_h, 0,
26 num_channels , alpha_chan , 0)
27 stbir_resize_uint8_srgb_edgemode(
Jim Flynn6217c3d2022-06-14 10:58:23 +010028 input_pixels , in_w , in_h , 0,
29 output_pixels, out_w, out_h, 0,
Sadik Armagan93e2e402019-05-02 09:31:38 +010030 num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP)
31 // WRAP/REFLECT/ZERO
32
33 FULL API
34 See the "header file" section of the source for API documentation.
35
36 ADDITIONAL DOCUMENTATION
37
38 SRGB & FLOATING POINT REPRESENTATION
39 The sRGB functions presume IEEE floating point. If you do not have
40 IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use
41 a slower implementation.
42
43 MEMORY ALLOCATION
44 The resize functions here perform a single memory allocation using
45 malloc. To control the memory allocation, before the #include that
46 triggers the implementation, do:
47
48 #define STBIR_MALLOC(size,context) ...
49 #define STBIR_FREE(ptr,context) ...
50
51 Each resize function makes exactly one call to malloc/free, so to use
52 temp memory, store the temp memory in the context and return that.
53
54 ASSERT
55 Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
56
57 OPTIMIZATION
58 Define STBIR_SATURATE_INT to compute clamp values in-range using
59 integer operations instead of float operations. This may be faster
60 on some platforms.
61
62 DEFAULT FILTERS
63 For functions which don't provide explicit control over what filters
64 to use, you can change the compile-time defaults with
65
66 #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something
67 #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something
68
69 See stbir_filter in the header-file section for the list of filters.
70
71 NEW FILTERS
72 A number of 1D filter kernels are used. For a list of
73 supported filters see the stbir_filter enum. To add a new filter,
74 write a filter function and add it to stbir__filter_info_table.
75
76 PROGRESS
77 For interactive use with slow resize operations, you can install
78 a progress-report callback:
79
80 #define STBIR_PROGRESS_REPORT(val) some_func(val)
81
82 The parameter val is a float which goes from 0 to 1 as progress is made.
83
84 For example:
85
86 static void my_progress_report(float progress);
87 #define STBIR_PROGRESS_REPORT(val) my_progress_report(val)
88
89 #define STB_IMAGE_RESIZE_IMPLEMENTATION
90 #include "stb_image_resize.h"
91
92 static void my_progress_report(float progress)
93 {
94 printf("Progress: %f%%\n", progress*100);
95 }
96
97 MAX CHANNELS
98 If your image has more than 64 channels, define STBIR_MAX_CHANNELS
99 to the max you'll have.
100
101 ALPHA CHANNEL
102 Most of the resizing functions provide the ability to control how
103 the alpha channel of an image is processed. The important things
104 to know about this:
105
106 1. The best mathematically-behaved version of alpha to use is
107 called "premultiplied alpha", in which the other color channels
108 have had the alpha value multiplied in. If you use premultiplied
109 alpha, linear filtering (such as image resampling done by this
110 library, or performed in texture units on GPUs) does the "right
111 thing". While premultiplied alpha is standard in the movie CGI
112 industry, it is still uncommon in the videogame/real-time world.
113
114 If you linearly filter non-premultiplied alpha, strange effects
115 occur. (For example, the 50/50 average of 99% transparent bright green
116 and 1% transparent black produces 50% transparent dark green when
117 non-premultiplied, whereas premultiplied it produces 50%
118 transparent near-black. The former introduces green energy
119 that doesn't exist in the source image.)
120
121 2. Artists should not edit premultiplied-alpha images; artists
122 want non-premultiplied alpha images. Thus, art tools generally output
123 non-premultiplied alpha images.
124
125 3. You will get best results in most cases by converting images
126 to premultiplied alpha before processing them mathematically.
127
128 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the
129 resizer does not do anything special for the alpha channel;
130 it is resampled identically to other channels. This produces
131 the correct results for premultiplied-alpha images, but produces
132 less-than-ideal results for non-premultiplied-alpha images.
133
134 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED,
135 then the resizer weights the contribution of input pixels
136 based on their alpha values, or, equivalently, it multiplies
137 the alpha value into the color channels, resamples, then divides
138 by the resultant alpha value. Input pixels which have alpha=0 do
139 not contribute at all to output pixels unless _all_ of the input
140 pixels affecting that output pixel have alpha=0, in which case
141 the result for that pixel is the same as it would be without
142 STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for
143 input images in integer formats. For input images in float format,
144 input pixels with alpha=0 have no effect, and output pixels
145 which have alpha=0 will be 0 in all channels. (For float images,
146 you can manually achieve the same result by adding a tiny epsilon
147 value to the alpha channel of every image, and then subtracting
148 or clamping it at the end.)
149
150 6. You can suppress the behavior described in #5 and make
151 all-0-alpha pixels have 0 in all channels by #defining
152 STBIR_NO_ALPHA_EPSILON.
153
154 7. You can separately control whether the alpha channel is
155 interpreted as linear or affected by the colorspace. By default
156 it is linear; you almost never want to apply the colorspace.
157 (For example, graphics hardware does not apply sRGB conversion
158 to the alpha channel.)
159
160 CONTRIBUTORS
161 Jorge L Rodriguez: Implementation
162 Sean Barrett: API design, optimizations
163 Aras Pranckevicius: bugfix
164 Nathan Reed: warning fixes
165
166 REVISIONS
167 0.95 (2017-07-23) fixed warnings
168 0.94 (2017-03-18) fixed warnings
169 0.93 (2017-03-03) fixed bug with certain combinations of heights
170 0.92 (2017-01-02) fix integer overflow on large (>2GB) images
171 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions
172 0.90 (2014-09-17) first released version
173
174 LICENSE
175 See end of file for license information.
176
177 TODO
178 Don't decode all of the image data when only processing a partial tile
179 Don't use full-width decode buffers when only processing a partial tile
180 When processing wide images, break processing into tiles so data fits in L1 cache
181 Installable filters?
182 Resize that respects alpha test coverage
183 (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage:
184 https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp )
185*/
186
187#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H
188#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H
189
190#ifdef _MSC_VER
191typedef unsigned char stbir_uint8;
192typedef unsigned short stbir_uint16;
193typedef unsigned int stbir_uint32;
194#else
195#include <stdint.h>
196typedef uint8_t stbir_uint8;
197typedef uint16_t stbir_uint16;
198typedef uint32_t stbir_uint32;
199#endif
200
201#ifdef STB_IMAGE_RESIZE_STATIC
202#define STBIRDEF static
203#else
204#ifdef __cplusplus
205#define STBIRDEF extern "C"
206#else
207#define STBIRDEF extern
208#endif
209#endif
210
211
212//////////////////////////////////////////////////////////////////////////////
213//
214// Easy-to-use API:
215//
216// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4)
217// * input_w is input image width (x-axis), input_h is input image height (y-axis)
218// * stride is the offset between successive rows of image data in memory, in bytes. you can
219// specify 0 to mean packed continuously in memory
220// * alpha channel is treated identically to other channels.
221// * colorspace is linear or sRGB as specified by function name
222// * returned result is 1 for success or 0 in case of an error.
223// #define STBIR_ASSERT() to trigger an assert on parameter validation errors.
224// * Memory required grows approximately linearly with input and output size, but with
225// discontinuities at input_w == output_w and input_h == output_h.
226// * These functions use a "default" resampling filter defined at compile time. To change the filter,
227// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE
228// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API.
229
230STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
231 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
232 int num_channels);
233
234STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
235 float *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
236 int num_channels);
237
238
Jim Flynn6217c3d2022-06-14 10:58:23 +0100239// The following functions interpret image data as gamma-corrected sRGB.
Sadik Armagan93e2e402019-05-02 09:31:38 +0100240// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel,
241// or otherwise provide the index of the alpha channel. Flags value
242// of 0 will probably do the right thing if you're not sure what
243// the flags mean.
244
245#define STBIR_ALPHA_CHANNEL_NONE -1
246
247// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will
248// use alpha-weighted resampling (effectively premultiplying, resampling,
249// then unpremultiplying).
250#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0)
251// The specified alpha channel should be handled as gamma-corrected value even
252// when doing sRGB operations.
253#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1)
254
255STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
256 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
257 int num_channels, int alpha_channel, int flags);
258
259
260typedef enum
261{
262 STBIR_EDGE_CLAMP = 1,
263 STBIR_EDGE_REFLECT = 2,
264 STBIR_EDGE_WRAP = 3,
265 STBIR_EDGE_ZERO = 4,
266} stbir_edge;
267
268// This function adds the ability to specify how requests to sample off the edge of the image are handled.
269STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
270 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
271 int num_channels, int alpha_channel, int flags,
272 stbir_edge edge_wrap_mode);
273
274//////////////////////////////////////////////////////////////////////////////
275//
276// Medium-complexity API
277//
278// This extends the easy-to-use API as follows:
279//
280// * Alpha-channel can be processed separately
281// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE
282// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT)
283// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)
284// * Filter can be selected explicitly
285// * uint16 image type
286// * sRGB colorspace available for all types
287// * context parameter for passing to STBIR_MALLOC
288
289typedef enum
290{
291 STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses
292 STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios
293 STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering
294 STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque
295 STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline
296 STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3
297} stbir_filter;
298
299typedef enum
300{
301 STBIR_COLORSPACE_LINEAR,
302 STBIR_COLORSPACE_SRGB,
303
304 STBIR_MAX_COLORSPACES,
305} stbir_colorspace;
306
307// The following functions are all identical except for the type of the image data
308
309STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
310 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
311 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +0100312 stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
Sadik Armagan93e2e402019-05-02 09:31:38 +0100313 void *alloc_context);
314
315STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
316 stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
317 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +0100318 stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
Sadik Armagan93e2e402019-05-02 09:31:38 +0100319 void *alloc_context);
320
321STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
322 float *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
323 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +0100324 stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
Sadik Armagan93e2e402019-05-02 09:31:38 +0100325 void *alloc_context);
326
327
328
329//////////////////////////////////////////////////////////////////////////////
330//
331// Full-complexity API
332//
333// This extends the medium API as follows:
334//
335// * uint32 image type
336// * not typesafe
337// * separate filter types for each axis
338// * separate edge modes for each axis
339// * can specify scale explicitly for subpixel correctness
340// * can specify image source tile using texture coordinates
341
342typedef enum
343{
344 STBIR_TYPE_UINT8 ,
345 STBIR_TYPE_UINT16,
346 STBIR_TYPE_UINT32,
347 STBIR_TYPE_FLOAT ,
348
349 STBIR_MAX_TYPES
350} stbir_datatype;
351
352STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
353 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
354 stbir_datatype datatype,
355 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +0100356 stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
Sadik Armagan93e2e402019-05-02 09:31:38 +0100357 stbir_filter filter_horizontal, stbir_filter filter_vertical,
358 stbir_colorspace space, void *alloc_context);
359
360STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
361 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
362 stbir_datatype datatype,
363 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +0100364 stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
Sadik Armagan93e2e402019-05-02 09:31:38 +0100365 stbir_filter filter_horizontal, stbir_filter filter_vertical,
366 stbir_colorspace space, void *alloc_context,
367 float x_scale, float y_scale,
368 float x_offset, float y_offset);
369
370STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
371 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
372 stbir_datatype datatype,
373 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +0100374 stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
Sadik Armagan93e2e402019-05-02 09:31:38 +0100375 stbir_filter filter_horizontal, stbir_filter filter_vertical,
376 stbir_colorspace space, void *alloc_context,
377 float s0, float t0, float s1, float t1);
378// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use.
379
380//
381//
382//// end header file /////////////////////////////////////////////////////
383#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H
384
385
386
387
388
389#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION
390
391#ifndef STBIR_ASSERT
392#include <assert.h>
393#define STBIR_ASSERT(x) assert(x)
394#endif
395
396// For memset
397#include <string.h>
398
399#include <math.h>
400
401#ifndef STBIR_MALLOC
402#include <stdlib.h>
403// use comma operator to evaluate c, to avoid "unused parameter" warnings
404#define STBIR_MALLOC(size,c) ((void)(c), malloc(size))
405#define STBIR_FREE(ptr,c) ((void)(c), free(ptr))
406#endif
407
408#ifndef _MSC_VER
409#ifdef __cplusplus
410#define stbir__inline inline
411#else
412#define stbir__inline
413#endif
414#else
415#define stbir__inline __forceinline
416#endif
417
418
419// should produce compiler error if size is wrong
420typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1];
421
422#ifdef _MSC_VER
423#define STBIR__NOTUSED(v) (void)(v)
424#else
425#define STBIR__NOTUSED(v) (void)sizeof(v)
426#endif
427
428#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0]))
429
430#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE
431#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
432#endif
433
434#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE
435#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL
436#endif
437
438#ifndef STBIR_PROGRESS_REPORT
439#define STBIR_PROGRESS_REPORT(float_0_to_1)
440#endif
441
442#ifndef STBIR_MAX_CHANNELS
443#define STBIR_MAX_CHANNELS 64
444#endif
445
446#if STBIR_MAX_CHANNELS > 65536
447#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536."
448// because we store the indices in 16-bit variables
449#endif
450
451// This value is added to alpha just before premultiplication to avoid
452// zeroing out color values. It is equivalent to 2^-80. If you don't want
453// that behavior (it may interfere if you have floating point images with
454// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to
455// disable it.
456#ifndef STBIR_ALPHA_EPSILON
457#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20))
458#endif
459
460
461
462#ifdef _MSC_VER
463#define STBIR__UNUSED_PARAM(v) (void)(v)
464#else
465#define STBIR__UNUSED_PARAM(v) (void)sizeof(v)
466#endif
467
468// must match stbir_datatype
469static unsigned char stbir__type_size[] = {
470 1, // STBIR_TYPE_UINT8
471 2, // STBIR_TYPE_UINT16
472 4, // STBIR_TYPE_UINT32
473 4, // STBIR_TYPE_FLOAT
474};
475
476// Kernel function centered at 0
477typedef float (stbir__kernel_fn)(float x, float scale);
478typedef float (stbir__support_fn)(float scale);
479
480typedef struct
481{
482 stbir__kernel_fn* kernel;
483 stbir__support_fn* support;
484} stbir__filter_info;
485
486// When upsampling, the contributors are which source pixels contribute.
487// When downsampling, the contributors are which destination pixels are contributed to.
488typedef struct
489{
490 int n0; // First contributing pixel
491 int n1; // Last contributing pixel
492} stbir__contributors;
493
494typedef struct
495{
496 const void* input_data;
497 int input_w;
498 int input_h;
499 int input_stride_bytes;
500
501 void* output_data;
502 int output_w;
503 int output_h;
504 int output_stride_bytes;
505
506 float s0, t0, s1, t1;
507
508 float horizontal_shift; // Units: output pixels
509 float vertical_shift; // Units: output pixels
510 float horizontal_scale;
511 float vertical_scale;
512
513 int channels;
514 int alpha_channel;
515 stbir_uint32 flags;
516 stbir_datatype type;
517 stbir_filter horizontal_filter;
518 stbir_filter vertical_filter;
519 stbir_edge edge_horizontal;
520 stbir_edge edge_vertical;
521 stbir_colorspace colorspace;
522
523 stbir__contributors* horizontal_contributors;
524 float* horizontal_coefficients;
525
526 stbir__contributors* vertical_contributors;
527 float* vertical_coefficients;
528
529 int decode_buffer_pixels;
530 float* decode_buffer;
531
532 float* horizontal_buffer;
533
534 // cache these because ceil/floor are inexplicably showing up in profile
535 int horizontal_coefficient_width;
536 int vertical_coefficient_width;
537 int horizontal_filter_pixel_width;
538 int vertical_filter_pixel_width;
539 int horizontal_filter_pixel_margin;
540 int vertical_filter_pixel_margin;
541 int horizontal_num_contributors;
542 int vertical_num_contributors;
543
544 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)
545 int ring_buffer_num_entries; // Total number of entries in the ring buffer.
546 int ring_buffer_first_scanline;
547 int ring_buffer_last_scanline;
548 int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer
549 float* ring_buffer;
550
551 float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds.
552
553 int horizontal_contributors_size;
554 int horizontal_coefficients_size;
555 int vertical_contributors_size;
556 int vertical_coefficients_size;
557 int decode_buffer_size;
558 int horizontal_buffer_size;
559 int ring_buffer_size;
560 int encode_buffer_size;
561} stbir__info;
562
563
564static const float stbir__max_uint8_as_float = 255.0f;
565static const float stbir__max_uint16_as_float = 65535.0f;
566static const double stbir__max_uint32_as_float = 4294967295.0;
567
568
569static stbir__inline int stbir__min(int a, int b)
570{
571 return a < b ? a : b;
572}
573
574static stbir__inline float stbir__saturate(float x)
575{
576 if (x < 0)
577 return 0;
578
579 if (x > 1)
580 return 1;
581
582 return x;
583}
584
585#ifdef STBIR_SATURATE_INT
586static stbir__inline stbir_uint8 stbir__saturate8(int x)
587{
588 if ((unsigned int) x <= 255)
589 return x;
590
591 if (x < 0)
592 return 0;
593
594 return 255;
595}
596
597static stbir__inline stbir_uint16 stbir__saturate16(int x)
598{
599 if ((unsigned int) x <= 65535)
600 return x;
601
602 if (x < 0)
603 return 0;
604
605 return 65535;
606}
607#endif
608
609static float stbir__srgb_uchar_to_linear_float[256] = {
610 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f,
611 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f,
612 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f,
613 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f,
614 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f,
615 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f,
616 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f,
617 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f,
618 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f,
619 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f,
620 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f,
621 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f,
622 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f,
623 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f,
624 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f,
625 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f,
626 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f,
627 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f,
628 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f,
629 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f,
630 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f,
631 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f,
632 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f,
633 0.982251f, 0.991102f, 1.0f
634};
635
636static float stbir__srgb_to_linear(float f)
637{
638 if (f <= 0.04045f)
639 return f / 12.92f;
640 else
641 return (float)pow((f + 0.055f) / 1.055f, 2.4f);
642}
643
644static float stbir__linear_to_srgb(float f)
645{
646 if (f <= 0.0031308f)
647 return f * 12.92f;
648 else
649 return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f;
650}
651
652#ifndef STBIR_NON_IEEE_FLOAT
653// From https://gist.github.com/rygorous/2203834
654
655typedef union
656{
657 stbir_uint32 u;
658 float f;
659} stbir__FP32;
660
661static const stbir_uint32 fp32_to_srgb8_tab4[104] = {
662 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
663 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
664 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
665 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
666 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
667 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
668 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
669 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
670 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
671 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
672 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
673 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
674 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
675};
Jim Flynn6217c3d2022-06-14 10:58:23 +0100676
Sadik Armagan93e2e402019-05-02 09:31:38 +0100677static stbir_uint8 stbir__linear_to_srgb_uchar(float in)
678{
679 static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps
680 static const stbir__FP32 minval = { (127-13) << 23 };
681 stbir_uint32 tab,bias,scale,t;
682 stbir__FP32 f;
Jim Flynn6217c3d2022-06-14 10:58:23 +0100683
Sadik Armagan93e2e402019-05-02 09:31:38 +0100684 // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively.
685 // The tests are carefully written so that NaNs map to 0, same as in the reference
686 // implementation.
687 if (!(in > minval.f)) // written this way to catch NaNs
688 in = minval.f;
689 if (in > almostone.f)
690 in = almostone.f;
Jim Flynn6217c3d2022-06-14 10:58:23 +0100691
Sadik Armagan93e2e402019-05-02 09:31:38 +0100692 // Do the table lookup and unpack bias, scale
693 f.f = in;
694 tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20];
695 bias = (tab >> 16) << 9;
696 scale = tab & 0xffff;
Jim Flynn6217c3d2022-06-14 10:58:23 +0100697
Sadik Armagan93e2e402019-05-02 09:31:38 +0100698 // Grab next-highest mantissa bits and perform linear interpolation
699 t = (f.u >> 12) & 0xff;
700 return (unsigned char) ((bias + scale*t) >> 16);
701}
702
703#else
704// sRGB transition values, scaled by 1<<28
705static int stbir__srgb_offset_to_linear_scaled[256] =
706{
707 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603,
708 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926,
709 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148,
710 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856,
711 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731,
712 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369,
713 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021,
714 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073,
715 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389,
716 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552,
717 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066,
718 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490,
719 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568,
720 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316,
721 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096,
722 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700,
723 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376,
724 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912,
725 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648,
726 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512,
727 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072,
728 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544,
729 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832,
730 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528,
731 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968,
732 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184,
733 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992,
734 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968,
735 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480,
736 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656,
737 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464,
738 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664,
739};
740
741static stbir_uint8 stbir__linear_to_srgb_uchar(float f)
742{
743 int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp
744 int v = 0;
745 int i;
746
747 // Refine the guess with a short binary search.
748 i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
749 i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
750 i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
751 i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
752 i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
753 i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
754 i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
755 i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
756
757 return (stbir_uint8) v;
758}
759#endif
760
761static float stbir__filter_trapezoid(float x, float scale)
762{
763 float halfscale = scale / 2;
764 float t = 0.5f + halfscale;
765 STBIR_ASSERT(scale <= 1);
766
767 x = (float)fabs(x);
768
769 if (x >= t)
770 return 0;
771 else
772 {
773 float r = 0.5f - halfscale;
774 if (x <= r)
775 return 1;
776 else
777 return (t - x) / scale;
778 }
779}
780
781static float stbir__support_trapezoid(float scale)
782{
783 STBIR_ASSERT(scale <= 1);
784 return 0.5f + scale / 2;
785}
786
787static float stbir__filter_triangle(float x, float s)
788{
789 STBIR__UNUSED_PARAM(s);
790
791 x = (float)fabs(x);
792
793 if (x <= 1.0f)
794 return 1 - x;
795 else
796 return 0;
797}
798
799static float stbir__filter_cubic(float x, float s)
800{
801 STBIR__UNUSED_PARAM(s);
802
803 x = (float)fabs(x);
804
805 if (x < 1.0f)
806 return (4 + x*x*(3*x - 6))/6;
807 else if (x < 2.0f)
808 return (8 + x*(-12 + x*(6 - x)))/6;
809
810 return (0.0f);
811}
812
813static float stbir__filter_catmullrom(float x, float s)
814{
815 STBIR__UNUSED_PARAM(s);
816
817 x = (float)fabs(x);
818
819 if (x < 1.0f)
820 return 1 - x*x*(2.5f - 1.5f*x);
821 else if (x < 2.0f)
822 return 2 - x*(4 + x*(0.5f*x - 2.5f));
823
824 return (0.0f);
825}
826
827static float stbir__filter_mitchell(float x, float s)
828{
829 STBIR__UNUSED_PARAM(s);
830
831 x = (float)fabs(x);
832
833 if (x < 1.0f)
834 return (16 + x*x*(21 * x - 36))/18;
835 else if (x < 2.0f)
836 return (32 + x*(-60 + x*(36 - 7*x)))/18;
837
838 return (0.0f);
839}
840
841static float stbir__support_zero(float s)
842{
843 STBIR__UNUSED_PARAM(s);
844 return 0;
845}
846
847static float stbir__support_one(float s)
848{
849 STBIR__UNUSED_PARAM(s);
850 return 1;
851}
852
853static float stbir__support_two(float s)
854{
855 STBIR__UNUSED_PARAM(s);
856 return 2;
857}
858
859static stbir__filter_info stbir__filter_info_table[] = {
860 { NULL, stbir__support_zero },
861 { stbir__filter_trapezoid, stbir__support_trapezoid },
862 { stbir__filter_triangle, stbir__support_one },
863 { stbir__filter_cubic, stbir__support_two },
864 { stbir__filter_catmullrom, stbir__support_two },
865 { stbir__filter_mitchell, stbir__support_two },
866};
867
868stbir__inline static int stbir__use_upsampling(float ratio)
869{
870 return ratio > 1;
871}
872
873stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info)
874{
875 return stbir__use_upsampling(stbir_info->horizontal_scale);
876}
877
878stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info)
879{
880 return stbir__use_upsampling(stbir_info->vertical_scale);
881}
882
883// This is the maximum number of input samples that can affect an output sample
884// with the given filter
885static int stbir__get_filter_pixel_width(stbir_filter filter, float scale)
886{
887 STBIR_ASSERT(filter != 0);
888 STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
889
890 if (stbir__use_upsampling(scale))
891 return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2);
892 else
893 return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale);
894}
895
896// This is how much to expand buffers to account for filters seeking outside
897// the image boundaries.
898static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale)
899{
900 return stbir__get_filter_pixel_width(filter, scale) / 2;
901}
902
903static int stbir__get_coefficient_width(stbir_filter filter, float scale)
904{
905 if (stbir__use_upsampling(scale))
906 return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2);
907 else
908 return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2);
909}
910
911static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size)
912{
913 if (stbir__use_upsampling(scale))
914 return output_size;
915 else
916 return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2);
917}
918
919static int stbir__get_total_horizontal_coefficients(stbir__info* info)
920{
921 return info->horizontal_num_contributors
922 * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale);
923}
924
925static int stbir__get_total_vertical_coefficients(stbir__info* info)
926{
927 return info->vertical_num_contributors
928 * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale);
929}
930
931static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n)
932{
933 return &contributors[n];
934}
935
936// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample,
937// if you change it here change it there too.
938static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c)
939{
940 int width = stbir__get_coefficient_width(filter, scale);
941 return &coefficients[width*n + c];
942}
943
944static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max)
945{
946 switch (edge)
947 {
948 case STBIR_EDGE_ZERO:
949 return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later
950
951 case STBIR_EDGE_CLAMP:
952 if (n < 0)
953 return 0;
954
955 if (n >= max)
956 return max - 1;
957
958 return n; // NOTREACHED
959
960 case STBIR_EDGE_REFLECT:
961 {
962 if (n < 0)
963 {
964 if (n < max)
965 return -n;
966 else
967 return max - 1;
968 }
969
970 if (n >= max)
971 {
972 int max2 = max * 2;
973 if (n >= max2)
974 return 0;
975 else
976 return max2 - n - 1;
977 }
978
979 return n; // NOTREACHED
980 }
981
982 case STBIR_EDGE_WRAP:
983 if (n >= 0)
984 return (n % max);
985 else
986 {
987 int m = (-n) % max;
988
989 if (m != 0)
990 m = max - m;
991
992 return (m);
993 }
994 // NOTREACHED
995
996 default:
997 STBIR_ASSERT(!"Unimplemented edge type");
998 return 0;
999 }
1000}
1001
1002stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max)
1003{
1004 // avoid per-pixel switch
1005 if (n >= 0 && n < max)
1006 return n;
1007 return stbir__edge_wrap_slow(edge, n, max);
1008}
1009
1010// What input pixels contribute to this output pixel?
1011static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out)
1012{
1013 float out_pixel_center = (float)n + 0.5f;
1014 float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius;
1015 float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius;
1016
1017 float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio;
1018 float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio;
1019
1020 *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio;
1021 *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5));
1022 *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5));
1023}
1024
1025// What output pixels does this input pixel contribute to?
1026static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in)
1027{
1028 float in_pixel_center = (float)n + 0.5f;
1029 float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius;
1030 float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius;
1031
1032 float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift;
1033 float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift;
1034
1035 *out_center_of_in = in_pixel_center * scale_ratio - out_shift;
1036 *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5));
1037 *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5));
1038}
1039
1040static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group)
1041{
1042 int i;
1043 float total_filter = 0;
1044 float filter_scale;
1045
1046 STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
1047
1048 contributor->n0 = in_first_pixel;
1049 contributor->n1 = in_last_pixel;
1050
1051 STBIR_ASSERT(contributor->n1 >= contributor->n0);
1052
1053 for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
1054 {
1055 float in_pixel_center = (float)(i + in_first_pixel) + 0.5f;
1056 coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale);
1057
1058 // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.)
1059 if (i == 0 && !coefficient_group[i])
1060 {
1061 contributor->n0 = ++in_first_pixel;
1062 i--;
1063 continue;
1064 }
1065
1066 total_filter += coefficient_group[i];
1067 }
1068
1069 STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0);
1070
1071 STBIR_ASSERT(total_filter > 0.9);
1072 STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off.
1073
1074 // Make sure the sum of all coefficients is 1.
1075 filter_scale = 1 / total_filter;
1076
1077 for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
1078 coefficient_group[i] *= filter_scale;
1079
1080 for (i = in_last_pixel - in_first_pixel; i >= 0; i--)
1081 {
1082 if (coefficient_group[i])
1083 break;
1084
1085 // This line has no weight. We can skip it.
1086 contributor->n1 = contributor->n0 + i - 1;
1087 }
1088}
1089
1090static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group)
1091{
1092 int i;
1093
1094 STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
1095
1096 contributor->n0 = out_first_pixel;
1097 contributor->n1 = out_last_pixel;
1098
1099 STBIR_ASSERT(contributor->n1 >= contributor->n0);
1100
1101 for (i = 0; i <= out_last_pixel - out_first_pixel; i++)
1102 {
1103 float out_pixel_center = (float)(i + out_first_pixel) + 0.5f;
1104 float x = out_pixel_center - out_center_of_in;
1105 coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio;
1106 }
1107
1108 STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0);
1109
1110 for (i = out_last_pixel - out_first_pixel; i >= 0; i--)
1111 {
1112 if (coefficient_group[i])
1113 break;
1114
1115 // This line has no weight. We can skip it.
1116 contributor->n1 = contributor->n0 + i - 1;
1117 }
1118}
1119
1120static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size)
1121{
1122 int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size);
1123 int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio);
1124 int i, j;
1125 int skip;
1126
1127 for (i = 0; i < output_size; i++)
1128 {
1129 float scale;
1130 float total = 0;
1131
1132 for (j = 0; j < num_contributors; j++)
1133 {
1134 if (i >= contributors[j].n0 && i <= contributors[j].n1)
1135 {
1136 float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0);
1137 total += coefficient;
1138 }
1139 else if (i < contributors[j].n0)
1140 break;
1141 }
1142
1143 STBIR_ASSERT(total > 0.9f);
1144 STBIR_ASSERT(total < 1.1f);
1145
1146 scale = 1 / total;
1147
1148 for (j = 0; j < num_contributors; j++)
1149 {
1150 if (i >= contributors[j].n0 && i <= contributors[j].n1)
1151 *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale;
1152 else if (i < contributors[j].n0)
1153 break;
1154 }
1155 }
1156
1157 // Optimize: Skip zero coefficients and contributions outside of image bounds.
1158 // Do this after normalizing because normalization depends on the n0/n1 values.
1159 for (j = 0; j < num_contributors; j++)
1160 {
1161 int range, max, width;
1162
1163 skip = 0;
1164 while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0)
1165 skip++;
1166
1167 contributors[j].n0 += skip;
1168
1169 while (contributors[j].n0 < 0)
1170 {
1171 contributors[j].n0++;
1172 skip++;
1173 }
1174
1175 range = contributors[j].n1 - contributors[j].n0 + 1;
1176 max = stbir__min(num_coefficients, range);
1177
1178 width = stbir__get_coefficient_width(filter, scale_ratio);
1179 for (i = 0; i < max; i++)
1180 {
1181 if (i + skip >= width)
1182 break;
1183
1184 *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip);
1185 }
1186
1187 continue;
1188 }
1189
1190 // Using min to avoid writing into invalid pixels.
1191 for (i = 0; i < num_contributors; i++)
1192 contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1);
1193}
1194
1195// Each scan line uses the same kernel values so we should calculate the kernel
1196// values once and then we can use them for every scan line.
1197static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size)
1198{
1199 int n;
1200 int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size);
1201
1202 if (stbir__use_upsampling(scale_ratio))
1203 {
1204 float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio;
1205
1206 // Looping through out pixels
1207 for (n = 0; n < total_contributors; n++)
1208 {
1209 float in_center_of_out; // Center of the current out pixel in the in pixel space
1210 int in_first_pixel, in_last_pixel;
1211
1212 stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out);
1213
1214 stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
1215 }
1216 }
1217 else
1218 {
1219 float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio;
1220
1221 // Looping through in pixels
1222 for (n = 0; n < total_contributors; n++)
1223 {
1224 float out_center_of_in; // Center of the current out pixel in the in pixel space
1225 int out_first_pixel, out_last_pixel;
1226 int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio);
1227
1228 stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in);
1229
1230 stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
1231 }
1232
1233 stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size);
1234 }
1235}
1236
1237static float* stbir__get_decode_buffer(stbir__info* stbir_info)
1238{
1239 // The 0 index of the decode buffer starts after the margin. This makes
1240 // it okay to use negative indexes on the decode buffer.
1241 return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels];
1242}
1243
1244#define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace))
1245
1246static void stbir__decode_scanline(stbir__info* stbir_info, int n)
1247{
1248 int c;
1249 int channels = stbir_info->channels;
1250 int alpha_channel = stbir_info->alpha_channel;
1251 int type = stbir_info->type;
1252 int colorspace = stbir_info->colorspace;
1253 int input_w = stbir_info->input_w;
1254 size_t input_stride_bytes = stbir_info->input_stride_bytes;
1255 float* decode_buffer = stbir__get_decode_buffer(stbir_info);
1256 stbir_edge edge_horizontal = stbir_info->edge_horizontal;
1257 stbir_edge edge_vertical = stbir_info->edge_vertical;
1258 size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes;
1259 const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset;
1260 int max_x = input_w + stbir_info->horizontal_filter_pixel_margin;
1261 int decode = STBIR__DECODE(type, colorspace);
1262
1263 int x = -stbir_info->horizontal_filter_pixel_margin;
1264
1265 // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input,
1266 // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO
1267 if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h))
1268 {
1269 for (; x < max_x; x++)
1270 for (c = 0; c < channels; c++)
1271 decode_buffer[x*channels + c] = 0;
1272 return;
1273 }
1274
1275 switch (decode)
1276 {
1277 case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR):
1278 for (; x < max_x; x++)
1279 {
1280 int decode_pixel_index = x * channels;
1281 int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1282 for (c = 0; c < channels; c++)
1283 decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float;
1284 }
1285 break;
1286
1287 case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB):
1288 for (; x < max_x; x++)
1289 {
1290 int decode_pixel_index = x * channels;
1291 int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1292 for (c = 0; c < channels; c++)
1293 decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]];
1294
1295 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1296 decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float;
1297 }
1298 break;
1299
1300 case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR):
1301 for (; x < max_x; x++)
1302 {
1303 int decode_pixel_index = x * channels;
1304 int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1305 for (c = 0; c < channels; c++)
1306 decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float;
1307 }
1308 break;
1309
1310 case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB):
1311 for (; x < max_x; x++)
1312 {
1313 int decode_pixel_index = x * channels;
1314 int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1315 for (c = 0; c < channels; c++)
1316 decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float);
1317
1318 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1319 decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float;
1320 }
1321 break;
1322
1323 case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR):
1324 for (; x < max_x; x++)
1325 {
1326 int decode_pixel_index = x * channels;
1327 int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1328 for (c = 0; c < channels; c++)
1329 decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float);
1330 }
1331 break;
1332
1333 case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB):
1334 for (; x < max_x; x++)
1335 {
1336 int decode_pixel_index = x * channels;
1337 int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1338 for (c = 0; c < channels; c++)
1339 decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float));
1340
1341 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1342 decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float);
1343 }
1344 break;
1345
1346 case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR):
1347 for (; x < max_x; x++)
1348 {
1349 int decode_pixel_index = x * channels;
1350 int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1351 for (c = 0; c < channels; c++)
1352 decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c];
1353 }
1354 break;
1355
1356 case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB):
1357 for (; x < max_x; x++)
1358 {
1359 int decode_pixel_index = x * channels;
1360 int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1361 for (c = 0; c < channels; c++)
1362 decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]);
1363
1364 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1365 decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel];
1366 }
1367
1368 break;
1369
1370 default:
1371 STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
1372 break;
1373 }
1374
1375 if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED))
1376 {
1377 for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++)
1378 {
1379 int decode_pixel_index = x * channels;
1380
1381 // If the alpha value is 0 it will clobber the color values. Make sure it's not.
1382 float alpha = decode_buffer[decode_pixel_index + alpha_channel];
1383#ifndef STBIR_NO_ALPHA_EPSILON
1384 if (stbir_info->type != STBIR_TYPE_FLOAT) {
1385 alpha += STBIR_ALPHA_EPSILON;
1386 decode_buffer[decode_pixel_index + alpha_channel] = alpha;
1387 }
1388#endif
1389 for (c = 0; c < channels; c++)
1390 {
1391 if (c == alpha_channel)
1392 continue;
1393
1394 decode_buffer[decode_pixel_index + c] *= alpha;
1395 }
1396 }
1397 }
1398
1399 if (edge_horizontal == STBIR_EDGE_ZERO)
1400 {
1401 for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++)
1402 {
1403 for (c = 0; c < channels; c++)
1404 decode_buffer[x*channels + c] = 0;
1405 }
1406 for (x = input_w; x < max_x; x++)
1407 {
1408 for (c = 0; c < channels; c++)
1409 decode_buffer[x*channels + c] = 0;
1410 }
1411 }
1412}
1413
1414static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length)
1415{
1416 return &ring_buffer[index * ring_buffer_length];
1417}
1418
1419static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n)
1420{
1421 int ring_buffer_index;
1422 float* ring_buffer;
1423
1424 stbir_info->ring_buffer_last_scanline = n;
1425
1426 if (stbir_info->ring_buffer_begin_index < 0)
1427 {
1428 ring_buffer_index = stbir_info->ring_buffer_begin_index = 0;
1429 stbir_info->ring_buffer_first_scanline = n;
1430 }
1431 else
1432 {
1433 ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries;
1434 STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index);
1435 }
1436
1437 ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float));
1438 memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes);
1439
1440 return ring_buffer;
1441}
1442
1443
1444static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer)
1445{
1446 int x, k;
1447 int output_w = stbir_info->output_w;
1448 int channels = stbir_info->channels;
1449 float* decode_buffer = stbir__get_decode_buffer(stbir_info);
1450 stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors;
1451 float* horizontal_coefficients = stbir_info->horizontal_coefficients;
1452 int coefficient_width = stbir_info->horizontal_coefficient_width;
1453
1454 for (x = 0; x < output_w; x++)
1455 {
1456 int n0 = horizontal_contributors[x].n0;
1457 int n1 = horizontal_contributors[x].n1;
1458
1459 int out_pixel_index = x * channels;
1460 int coefficient_group = coefficient_width * x;
1461 int coefficient_counter = 0;
1462
1463 STBIR_ASSERT(n1 >= n0);
1464 STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin);
1465 STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin);
1466 STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
1467 STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
1468
1469 switch (channels) {
1470 case 1:
1471 for (k = n0; k <= n1; k++)
1472 {
1473 int in_pixel_index = k * 1;
1474 float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1475 STBIR_ASSERT(coefficient != 0);
1476 output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1477 }
1478 break;
1479 case 2:
1480 for (k = n0; k <= n1; k++)
1481 {
1482 int in_pixel_index = k * 2;
1483 float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1484 STBIR_ASSERT(coefficient != 0);
1485 output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1486 output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1487 }
1488 break;
1489 case 3:
1490 for (k = n0; k <= n1; k++)
1491 {
1492 int in_pixel_index = k * 3;
1493 float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1494 STBIR_ASSERT(coefficient != 0);
1495 output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1496 output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1497 output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
1498 }
1499 break;
1500 case 4:
1501 for (k = n0; k <= n1; k++)
1502 {
1503 int in_pixel_index = k * 4;
1504 float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1505 STBIR_ASSERT(coefficient != 0);
1506 output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1507 output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1508 output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
1509 output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient;
1510 }
1511 break;
1512 default:
1513 for (k = n0; k <= n1; k++)
1514 {
1515 int in_pixel_index = k * channels;
1516 float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1517 int c;
1518 STBIR_ASSERT(coefficient != 0);
1519 for (c = 0; c < channels; c++)
1520 output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
1521 }
1522 break;
1523 }
1524 }
1525}
1526
1527static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer)
1528{
1529 int x, k;
1530 int input_w = stbir_info->input_w;
1531 int channels = stbir_info->channels;
1532 float* decode_buffer = stbir__get_decode_buffer(stbir_info);
1533 stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors;
1534 float* horizontal_coefficients = stbir_info->horizontal_coefficients;
1535 int coefficient_width = stbir_info->horizontal_coefficient_width;
1536 int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin;
1537 int max_x = input_w + filter_pixel_margin * 2;
1538
1539 STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info));
1540
1541 switch (channels) {
1542 case 1:
1543 for (x = 0; x < max_x; x++)
1544 {
1545 int n0 = horizontal_contributors[x].n0;
1546 int n1 = horizontal_contributors[x].n1;
1547
1548 int in_x = x - filter_pixel_margin;
1549 int in_pixel_index = in_x * 1;
1550 int max_n = n1;
1551 int coefficient_group = coefficient_width * x;
1552
1553 for (k = n0; k <= max_n; k++)
1554 {
1555 int out_pixel_index = k * 1;
1556 float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1557 STBIR_ASSERT(coefficient != 0);
1558 output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1559 }
1560 }
1561 break;
1562
1563 case 2:
1564 for (x = 0; x < max_x; x++)
1565 {
1566 int n0 = horizontal_contributors[x].n0;
1567 int n1 = horizontal_contributors[x].n1;
1568
1569 int in_x = x - filter_pixel_margin;
1570 int in_pixel_index = in_x * 2;
1571 int max_n = n1;
1572 int coefficient_group = coefficient_width * x;
1573
1574 for (k = n0; k <= max_n; k++)
1575 {
1576 int out_pixel_index = k * 2;
1577 float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1578 STBIR_ASSERT(coefficient != 0);
1579 output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1580 output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1581 }
1582 }
1583 break;
1584
1585 case 3:
1586 for (x = 0; x < max_x; x++)
1587 {
1588 int n0 = horizontal_contributors[x].n0;
1589 int n1 = horizontal_contributors[x].n1;
1590
1591 int in_x = x - filter_pixel_margin;
1592 int in_pixel_index = in_x * 3;
1593 int max_n = n1;
1594 int coefficient_group = coefficient_width * x;
1595
1596 for (k = n0; k <= max_n; k++)
1597 {
1598 int out_pixel_index = k * 3;
1599 float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1600 STBIR_ASSERT(coefficient != 0);
1601 output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1602 output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1603 output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
1604 }
1605 }
1606 break;
1607
1608 case 4:
1609 for (x = 0; x < max_x; x++)
1610 {
1611 int n0 = horizontal_contributors[x].n0;
1612 int n1 = horizontal_contributors[x].n1;
1613
1614 int in_x = x - filter_pixel_margin;
1615 int in_pixel_index = in_x * 4;
1616 int max_n = n1;
1617 int coefficient_group = coefficient_width * x;
1618
1619 for (k = n0; k <= max_n; k++)
1620 {
1621 int out_pixel_index = k * 4;
1622 float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1623 STBIR_ASSERT(coefficient != 0);
1624 output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1625 output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1626 output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
1627 output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient;
1628 }
1629 }
1630 break;
1631
1632 default:
1633 for (x = 0; x < max_x; x++)
1634 {
1635 int n0 = horizontal_contributors[x].n0;
1636 int n1 = horizontal_contributors[x].n1;
1637
1638 int in_x = x - filter_pixel_margin;
1639 int in_pixel_index = in_x * channels;
1640 int max_n = n1;
1641 int coefficient_group = coefficient_width * x;
1642
1643 for (k = n0; k <= max_n; k++)
1644 {
1645 int c;
1646 int out_pixel_index = k * channels;
1647 float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1648 STBIR_ASSERT(coefficient != 0);
1649 for (c = 0; c < channels; c++)
1650 output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
1651 }
1652 }
1653 break;
1654 }
1655}
1656
1657static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n)
1658{
1659 // Decode the nth scanline from the source image into the decode buffer.
1660 stbir__decode_scanline(stbir_info, n);
1661
1662 // Now resample it into the ring buffer.
1663 if (stbir__use_width_upsampling(stbir_info))
1664 stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n));
1665 else
1666 stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n));
1667
1668 // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling.
1669}
1670
1671static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n)
1672{
1673 // Decode the nth scanline from the source image into the decode buffer.
1674 stbir__decode_scanline(stbir_info, n);
1675
1676 memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float));
1677
1678 // Now resample it into the horizontal buffer.
1679 if (stbir__use_width_upsampling(stbir_info))
1680 stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer);
1681 else
1682 stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer);
1683
1684 // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers.
1685}
1686
1687// Get the specified scan line from the ring buffer.
1688static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length)
1689{
1690 int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries;
1691 return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length);
1692}
1693
1694
1695static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode)
1696{
1697 int x;
1698 int n;
1699 int num_nonalpha;
1700 stbir_uint16 nonalpha[STBIR_MAX_CHANNELS];
1701
1702 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
1703 {
1704 for (x=0; x < num_pixels; ++x)
1705 {
1706 int pixel_index = x*channels;
1707
1708 float alpha = encode_buffer[pixel_index + alpha_channel];
1709 float reciprocal_alpha = alpha ? 1.0f / alpha : 0;
1710
1711 // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb
1712 for (n = 0; n < channels; n++)
1713 if (n != alpha_channel)
1714 encode_buffer[pixel_index + n] *= reciprocal_alpha;
1715
1716 // We added in a small epsilon to prevent the color channel from being deleted with zero alpha.
1717 // Because we only add it for integer types, it will automatically be discarded on integer
1718 // conversion, so we don't need to subtract it back out (which would be problematic for
1719 // numeric precision reasons).
1720 }
1721 }
1722
1723 // build a table of all channels that need colorspace correction, so
1724 // we don't perform colorspace correction on channels that don't need it.
1725 for (x = 0, num_nonalpha = 0; x < channels; ++x)
1726 {
1727 if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
1728 {
1729 nonalpha[num_nonalpha++] = (stbir_uint16)x;
1730 }
1731 }
1732
1733 #define STBIR__ROUND_INT(f) ((int) ((f)+0.5))
1734 #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5))
1735
1736 #ifdef STBIR__SATURATE_INT
1737 #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float ))
1738 #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float))
1739 #else
1740 #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float )
1741 #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float)
1742 #endif
1743
1744 switch (decode)
1745 {
1746 case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR):
1747 for (x=0; x < num_pixels; ++x)
1748 {
1749 int pixel_index = x*channels;
1750
1751 for (n = 0; n < channels; n++)
1752 {
1753 int index = pixel_index + n;
1754 ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]);
1755 }
1756 }
1757 break;
1758
1759 case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB):
1760 for (x=0; x < num_pixels; ++x)
1761 {
1762 int pixel_index = x*channels;
1763
1764 for (n = 0; n < num_nonalpha; n++)
1765 {
1766 int index = pixel_index + nonalpha[n];
1767 ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]);
1768 }
1769
1770 if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
1771 ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]);
1772 }
1773 break;
1774
1775 case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR):
1776 for (x=0; x < num_pixels; ++x)
1777 {
1778 int pixel_index = x*channels;
1779
1780 for (n = 0; n < channels; n++)
1781 {
1782 int index = pixel_index + n;
1783 ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]);
1784 }
1785 }
1786 break;
1787
1788 case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB):
1789 for (x=0; x < num_pixels; ++x)
1790 {
1791 int pixel_index = x*channels;
1792
1793 for (n = 0; n < num_nonalpha; n++)
1794 {
1795 int index = pixel_index + nonalpha[n];
1796 ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float);
1797 }
1798
1799 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1800 ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]);
1801 }
1802
1803 break;
1804
1805 case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR):
1806 for (x=0; x < num_pixels; ++x)
1807 {
1808 int pixel_index = x*channels;
1809
1810 for (n = 0; n < channels; n++)
1811 {
1812 int index = pixel_index + n;
1813 ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float);
1814 }
1815 }
1816 break;
1817
1818 case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB):
1819 for (x=0; x < num_pixels; ++x)
1820 {
1821 int pixel_index = x*channels;
1822
1823 for (n = 0; n < num_nonalpha; n++)
1824 {
1825 int index = pixel_index + nonalpha[n];
1826 ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float);
1827 }
1828
1829 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1830 ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float);
1831 }
1832 break;
1833
1834 case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR):
1835 for (x=0; x < num_pixels; ++x)
1836 {
1837 int pixel_index = x*channels;
1838
1839 for (n = 0; n < channels; n++)
1840 {
1841 int index = pixel_index + n;
1842 ((float*)output_buffer)[index] = encode_buffer[index];
1843 }
1844 }
1845 break;
1846
1847 case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB):
1848 for (x=0; x < num_pixels; ++x)
1849 {
1850 int pixel_index = x*channels;
1851
1852 for (n = 0; n < num_nonalpha; n++)
1853 {
1854 int index = pixel_index + nonalpha[n];
1855 ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]);
1856 }
1857
1858 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1859 ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel];
1860 }
1861 break;
1862
1863 default:
1864 STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
1865 break;
1866 }
1867}
1868
1869static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n)
1870{
1871 int x, k;
1872 int output_w = stbir_info->output_w;
1873 stbir__contributors* vertical_contributors = stbir_info->vertical_contributors;
1874 float* vertical_coefficients = stbir_info->vertical_coefficients;
1875 int channels = stbir_info->channels;
1876 int alpha_channel = stbir_info->alpha_channel;
1877 int type = stbir_info->type;
1878 int colorspace = stbir_info->colorspace;
1879 int ring_buffer_entries = stbir_info->ring_buffer_num_entries;
1880 void* output_data = stbir_info->output_data;
1881 float* encode_buffer = stbir_info->encode_buffer;
1882 int decode = STBIR__DECODE(type, colorspace);
1883 int coefficient_width = stbir_info->vertical_coefficient_width;
1884 int coefficient_counter;
1885 int contributor = n;
1886
1887 float* ring_buffer = stbir_info->ring_buffer;
1888 int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index;
1889 int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline;
1890 int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
1891
1892 int n0,n1, output_row_start;
1893 int coefficient_group = coefficient_width * contributor;
1894
1895 n0 = vertical_contributors[contributor].n0;
1896 n1 = vertical_contributors[contributor].n1;
1897
1898 output_row_start = n * stbir_info->output_stride_bytes;
1899
1900 STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
1901
1902 memset(encode_buffer, 0, output_w * sizeof(float) * channels);
1903
1904 // I tried reblocking this for better cache usage of encode_buffer
1905 // (using x_outer, k, x_inner), but it lost speed. -- stb
1906
1907 coefficient_counter = 0;
1908 switch (channels) {
1909 case 1:
1910 for (k = n0; k <= n1; k++)
1911 {
1912 int coefficient_index = coefficient_counter++;
1913 float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
1914 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1915 for (x = 0; x < output_w; ++x)
1916 {
1917 int in_pixel_index = x * 1;
1918 encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
1919 }
1920 }
1921 break;
1922 case 2:
1923 for (k = n0; k <= n1; k++)
1924 {
1925 int coefficient_index = coefficient_counter++;
1926 float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
1927 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1928 for (x = 0; x < output_w; ++x)
1929 {
1930 int in_pixel_index = x * 2;
1931 encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
1932 encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient;
1933 }
1934 }
1935 break;
1936 case 3:
1937 for (k = n0; k <= n1; k++)
1938 {
1939 int coefficient_index = coefficient_counter++;
1940 float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
1941 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1942 for (x = 0; x < output_w; ++x)
1943 {
1944 int in_pixel_index = x * 3;
1945 encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
1946 encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient;
1947 encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient;
1948 }
1949 }
1950 break;
1951 case 4:
1952 for (k = n0; k <= n1; k++)
1953 {
1954 int coefficient_index = coefficient_counter++;
1955 float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
1956 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1957 for (x = 0; x < output_w; ++x)
1958 {
1959 int in_pixel_index = x * 4;
1960 encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
1961 encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient;
1962 encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient;
1963 encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient;
1964 }
1965 }
1966 break;
1967 default:
1968 for (k = n0; k <= n1; k++)
1969 {
1970 int coefficient_index = coefficient_counter++;
1971 float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
1972 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1973 for (x = 0; x < output_w; ++x)
1974 {
1975 int in_pixel_index = x * channels;
1976 int c;
1977 for (c = 0; c < channels; c++)
1978 encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient;
1979 }
1980 }
1981 break;
1982 }
1983 stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode);
1984}
1985
1986static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n)
1987{
1988 int x, k;
1989 int output_w = stbir_info->output_w;
1990 stbir__contributors* vertical_contributors = stbir_info->vertical_contributors;
1991 float* vertical_coefficients = stbir_info->vertical_coefficients;
1992 int channels = stbir_info->channels;
1993 int ring_buffer_entries = stbir_info->ring_buffer_num_entries;
1994 float* horizontal_buffer = stbir_info->horizontal_buffer;
1995 int coefficient_width = stbir_info->vertical_coefficient_width;
1996 int contributor = n + stbir_info->vertical_filter_pixel_margin;
1997
1998 float* ring_buffer = stbir_info->ring_buffer;
1999 int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index;
2000 int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline;
2001 int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
2002 int n0,n1;
2003
2004 n0 = vertical_contributors[contributor].n0;
2005 n1 = vertical_contributors[contributor].n1;
2006
2007 STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
2008
2009 for (k = n0; k <= n1; k++)
2010 {
2011 int coefficient_index = k - n0;
2012 int coefficient_group = coefficient_width * contributor;
2013 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
2014
2015 float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
2016
2017 switch (channels) {
2018 case 1:
2019 for (x = 0; x < output_w; x++)
2020 {
2021 int in_pixel_index = x * 1;
2022 ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
2023 }
2024 break;
2025 case 2:
2026 for (x = 0; x < output_w; x++)
2027 {
2028 int in_pixel_index = x * 2;
2029 ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
2030 ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient;
2031 }
2032 break;
2033 case 3:
2034 for (x = 0; x < output_w; x++)
2035 {
2036 int in_pixel_index = x * 3;
2037 ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
2038 ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient;
2039 ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient;
2040 }
2041 break;
2042 case 4:
2043 for (x = 0; x < output_w; x++)
2044 {
2045 int in_pixel_index = x * 4;
2046 ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
2047 ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient;
2048 ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient;
2049 ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient;
2050 }
2051 break;
2052 default:
2053 for (x = 0; x < output_w; x++)
2054 {
2055 int in_pixel_index = x * channels;
2056
2057 int c;
2058 for (c = 0; c < channels; c++)
2059 ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient;
2060 }
2061 break;
2062 }
2063 }
2064}
2065
2066static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
2067{
2068 int y;
2069 float scale_ratio = stbir_info->vertical_scale;
2070 float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio;
2071
2072 STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
2073
2074 for (y = 0; y < stbir_info->output_h; y++)
2075 {
2076 float in_center_of_out = 0; // Center of the current out scanline in the in scanline space
2077 int in_first_scanline = 0, in_last_scanline = 0;
2078
2079 stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out);
2080
2081 STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries);
2082
2083 if (stbir_info->ring_buffer_begin_index >= 0)
2084 {
2085 // Get rid of whatever we don't need anymore.
2086 while (in_first_scanline > stbir_info->ring_buffer_first_scanline)
2087 {
2088 if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline)
2089 {
2090 // We just popped the last scanline off the ring buffer.
2091 // Reset it to the empty state.
2092 stbir_info->ring_buffer_begin_index = -1;
2093 stbir_info->ring_buffer_first_scanline = 0;
2094 stbir_info->ring_buffer_last_scanline = 0;
2095 break;
2096 }
2097 else
2098 {
2099 stbir_info->ring_buffer_first_scanline++;
2100 stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries;
2101 }
2102 }
2103 }
2104
2105 // Load in new ones.
2106 if (stbir_info->ring_buffer_begin_index < 0)
2107 stbir__decode_and_resample_upsample(stbir_info, in_first_scanline);
2108
2109 while (in_last_scanline > stbir_info->ring_buffer_last_scanline)
2110 stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1);
2111
2112 // Now all buffers should be ready to write a row of vertical sampling.
2113 stbir__resample_vertical_upsample(stbir_info, y);
2114
2115 STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h);
2116 }
2117}
2118
2119static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline)
2120{
2121 int output_stride_bytes = stbir_info->output_stride_bytes;
2122 int channels = stbir_info->channels;
2123 int alpha_channel = stbir_info->alpha_channel;
2124 int type = stbir_info->type;
2125 int colorspace = stbir_info->colorspace;
2126 int output_w = stbir_info->output_w;
2127 void* output_data = stbir_info->output_data;
2128 int decode = STBIR__DECODE(type, colorspace);
2129
2130 float* ring_buffer = stbir_info->ring_buffer;
2131 int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
2132
2133 if (stbir_info->ring_buffer_begin_index >= 0)
2134 {
2135 // Get rid of whatever we don't need anymore.
2136 while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline)
2137 {
2138 if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h)
2139 {
2140 int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes;
2141 float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length);
2142 stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode);
2143 STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h);
2144 }
2145
2146 if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline)
2147 {
2148 // We just popped the last scanline off the ring buffer.
2149 // Reset it to the empty state.
2150 stbir_info->ring_buffer_begin_index = -1;
2151 stbir_info->ring_buffer_first_scanline = 0;
2152 stbir_info->ring_buffer_last_scanline = 0;
2153 break;
2154 }
2155 else
2156 {
2157 stbir_info->ring_buffer_first_scanline++;
2158 stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries;
2159 }
2160 }
2161 }
2162}
2163
2164static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
2165{
2166 int y;
2167 float scale_ratio = stbir_info->vertical_scale;
2168 int output_h = stbir_info->output_h;
2169 float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio;
2170 int pixel_margin = stbir_info->vertical_filter_pixel_margin;
2171 int max_y = stbir_info->input_h + pixel_margin;
2172
2173 STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
2174
2175 for (y = -pixel_margin; y < max_y; y++)
2176 {
2177 float out_center_of_in; // Center of the current out scanline in the in scanline space
2178 int out_first_scanline, out_last_scanline;
2179
2180 stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in);
2181
2182 STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries);
2183
2184 if (out_last_scanline < 0 || out_first_scanline >= output_h)
2185 continue;
2186
2187 stbir__empty_ring_buffer(stbir_info, out_first_scanline);
2188
2189 stbir__decode_and_resample_downsample(stbir_info, y);
2190
2191 // Load in new ones.
2192 if (stbir_info->ring_buffer_begin_index < 0)
2193 stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline);
2194
2195 while (out_last_scanline > stbir_info->ring_buffer_last_scanline)
2196 stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1);
2197
2198 // Now the horizontal buffer is ready to write to all ring buffer rows.
2199 stbir__resample_vertical_downsample(stbir_info, y);
2200 }
2201
2202 stbir__empty_ring_buffer(stbir_info, stbir_info->output_h);
2203}
2204
2205static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels)
2206{
2207 info->input_w = input_w;
2208 info->input_h = input_h;
2209 info->output_w = output_w;
2210 info->output_h = output_h;
2211 info->channels = channels;
2212}
2213
2214static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform)
2215{
2216 info->s0 = s0;
2217 info->t0 = t0;
2218 info->s1 = s1;
2219 info->t1 = t1;
2220
2221 if (transform)
2222 {
2223 info->horizontal_scale = transform[0];
2224 info->vertical_scale = transform[1];
2225 info->horizontal_shift = transform[2];
2226 info->vertical_shift = transform[3];
2227 }
2228 else
2229 {
2230 info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0);
2231 info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0);
2232
2233 info->horizontal_shift = s0 * info->output_w / (s1 - s0);
2234 info->vertical_shift = t0 * info->output_h / (t1 - t0);
2235 }
2236}
2237
2238static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter)
2239{
2240 if (h_filter == 0)
2241 h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE;
2242 if (v_filter == 0)
2243 v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE;
2244 info->horizontal_filter = h_filter;
2245 info->vertical_filter = v_filter;
2246}
2247
2248static stbir_uint32 stbir__calculate_memory(stbir__info *info)
2249{
2250 int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale);
2251 int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale);
2252
2253 info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w);
2254 info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h);
2255
2256 // One extra entry because floating point precision problems sometimes cause an extra to be necessary.
2257 info->ring_buffer_num_entries = filter_height + 1;
2258
2259 info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors);
2260 info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float);
2261 info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors);
2262 info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float);
2263 info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float);
2264 info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float);
2265 info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float);
2266 info->encode_buffer_size = info->output_w * info->channels * sizeof(float);
2267
2268 STBIR_ASSERT(info->horizontal_filter != 0);
2269 STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late
2270 STBIR_ASSERT(info->vertical_filter != 0);
2271 STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late
2272
2273 if (stbir__use_height_upsampling(info))
2274 // The horizontal buffer is for when we're downsampling the height and we
2275 // can't output the result of sampling the decode buffer directly into the
2276 // ring buffers.
2277 info->horizontal_buffer_size = 0;
2278 else
2279 // The encode buffer is to retain precision in the height upsampling method
2280 // and isn't used when height downsampling.
2281 info->encode_buffer_size = 0;
2282
2283 return info->horizontal_contributors_size + info->horizontal_coefficients_size
2284 + info->vertical_contributors_size + info->vertical_coefficients_size
2285 + info->decode_buffer_size + info->horizontal_buffer_size
2286 + info->ring_buffer_size + info->encode_buffer_size;
2287}
2288
2289static int stbir__resize_allocated(stbir__info *info,
2290 const void* input_data, int input_stride_in_bytes,
2291 void* output_data, int output_stride_in_bytes,
2292 int alpha_channel, stbir_uint32 flags, stbir_datatype type,
2293 stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace,
2294 void* tempmem, size_t tempmem_size_in_bytes)
2295{
2296 size_t memory_required = stbir__calculate_memory(info);
2297
2298 int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type];
2299 int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type];
2300
2301#ifdef STBIR_DEBUG_OVERWRITE_TEST
2302#define OVERWRITE_ARRAY_SIZE 8
2303 unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE];
2304 unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE];
2305 unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE];
2306 unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE];
2307
2308 size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type];
2309 memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE);
2310 memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE);
2311 memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE);
2312 memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE);
2313#endif
2314
2315 STBIR_ASSERT(info->channels >= 0);
2316 STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS);
2317
2318 if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS)
2319 return 0;
2320
2321 STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
2322 STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
2323
2324 if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table))
2325 return 0;
2326 if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table))
2327 return 0;
2328
2329 if (alpha_channel < 0)
2330 flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED;
2331
2332 if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
2333 STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels);
2334
2335 if (alpha_channel >= info->channels)
2336 return 0;
2337
2338 STBIR_ASSERT(tempmem);
2339
2340 if (!tempmem)
2341 return 0;
2342
2343 STBIR_ASSERT(tempmem_size_in_bytes >= memory_required);
2344
2345 if (tempmem_size_in_bytes < memory_required)
2346 return 0;
2347
2348 memset(tempmem, 0, tempmem_size_in_bytes);
2349
2350 info->input_data = input_data;
2351 info->input_stride_bytes = width_stride_input;
2352
2353 info->output_data = output_data;
2354 info->output_stride_bytes = width_stride_output;
2355
2356 info->alpha_channel = alpha_channel;
2357 info->flags = flags;
2358 info->type = type;
2359 info->edge_horizontal = edge_horizontal;
2360 info->edge_vertical = edge_vertical;
2361 info->colorspace = colorspace;
2362
2363 info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale);
2364 info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale );
2365 info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale);
2366 info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale );
2367 info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale);
2368 info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale );
2369
2370 info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float);
2371 info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2;
2372
2373#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size)
2374
2375 info->horizontal_contributors = (stbir__contributors *) tempmem;
2376 info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float);
2377 info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors);
2378 info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float);
2379 info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float);
2380
2381 if (stbir__use_height_upsampling(info))
2382 {
2383 info->horizontal_buffer = NULL;
2384 info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float);
2385 info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float);
2386
2387 STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
2388 }
2389 else
2390 {
2391 info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float);
2392 info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float);
2393 info->encode_buffer = NULL;
2394
2395 STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
2396 }
2397
2398#undef STBIR__NEXT_MEMPTR
2399
2400 // This signals that the ring buffer is empty
2401 info->ring_buffer_begin_index = -1;
2402
2403 stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w);
2404 stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h);
2405
2406 STBIR_PROGRESS_REPORT(0);
2407
2408 if (stbir__use_height_upsampling(info))
2409 stbir__buffer_loop_upsample(info);
2410 else
2411 stbir__buffer_loop_downsample(info);
2412
2413 STBIR_PROGRESS_REPORT(1);
2414
2415#ifdef STBIR_DEBUG_OVERWRITE_TEST
2416 STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
2417 STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0);
2418 STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
2419 STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0);
2420#endif
2421
2422 return 1;
2423}
2424
2425
2426static int stbir__resize_arbitrary(
2427 void *alloc_context,
2428 const void* input_data, int input_w, int input_h, int input_stride_in_bytes,
2429 void* output_data, int output_w, int output_h, int output_stride_in_bytes,
2430 float s0, float t0, float s1, float t1, float *transform,
2431 int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type,
2432 stbir_filter h_filter, stbir_filter v_filter,
2433 stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace)
2434{
2435 stbir__info info;
2436 int result;
2437 size_t memory_required;
2438 void* extra_memory;
2439
2440 stbir__setup(&info, input_w, input_h, output_w, output_h, channels);
2441 stbir__calculate_transform(&info, s0,t0,s1,t1,transform);
2442 stbir__choose_filter(&info, h_filter, v_filter);
2443 memory_required = stbir__calculate_memory(&info);
2444 extra_memory = STBIR_MALLOC(memory_required, alloc_context);
2445
2446 if (!extra_memory)
2447 return 0;
2448
2449 result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes,
Jim Flynn6217c3d2022-06-14 10:58:23 +01002450 output_data, output_stride_in_bytes,
Sadik Armagan93e2e402019-05-02 09:31:38 +01002451 alpha_channel, flags, type,
2452 edge_horizontal, edge_vertical,
2453 colorspace, extra_memory, memory_required);
2454
2455 STBIR_FREE(extra_memory, alloc_context);
2456
2457 return result;
2458}
2459
2460STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2461 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2462 int num_channels)
2463{
2464 return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
2465 output_pixels, output_w, output_h, output_stride_in_bytes,
2466 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
2467 STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR);
2468}
2469
2470STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2471 float *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2472 int num_channels)
2473{
2474 return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
2475 output_pixels, output_w, output_h, output_stride_in_bytes,
2476 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
2477 STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR);
2478}
2479
2480STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2481 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2482 int num_channels, int alpha_channel, int flags)
2483{
2484 return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
2485 output_pixels, output_w, output_h, output_stride_in_bytes,
2486 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
2487 STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB);
2488}
2489
2490STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2491 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2492 int num_channels, int alpha_channel, int flags,
2493 stbir_edge edge_wrap_mode)
2494{
2495 return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
2496 output_pixels, output_w, output_h, output_stride_in_bytes,
2497 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
2498 edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB);
2499}
2500
2501STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2502 unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2503 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +01002504 stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
Sadik Armagan93e2e402019-05-02 09:31:38 +01002505 void *alloc_context)
2506{
2507 return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2508 output_pixels, output_w, output_h, output_stride_in_bytes,
2509 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter,
2510 edge_wrap_mode, edge_wrap_mode, space);
2511}
2512
2513STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2514 stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
2515 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +01002516 stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
Sadik Armagan93e2e402019-05-02 09:31:38 +01002517 void *alloc_context)
2518{
2519 return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2520 output_pixels, output_w, output_h, output_stride_in_bytes,
2521 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter,
2522 edge_wrap_mode, edge_wrap_mode, space);
2523}
2524
2525
2526STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2527 float *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
2528 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +01002529 stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
Sadik Armagan93e2e402019-05-02 09:31:38 +01002530 void *alloc_context)
2531{
2532 return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2533 output_pixels, output_w, output_h, output_stride_in_bytes,
2534 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter,
2535 edge_wrap_mode, edge_wrap_mode, space);
2536}
2537
2538
2539STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2540 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2541 stbir_datatype datatype,
2542 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +01002543 stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
Sadik Armagan93e2e402019-05-02 09:31:38 +01002544 stbir_filter filter_horizontal, stbir_filter filter_vertical,
2545 stbir_colorspace space, void *alloc_context)
2546{
2547 return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2548 output_pixels, output_w, output_h, output_stride_in_bytes,
2549 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical,
2550 edge_mode_horizontal, edge_mode_vertical, space);
2551}
2552
2553
2554STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2555 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2556 stbir_datatype datatype,
2557 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +01002558 stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
Sadik Armagan93e2e402019-05-02 09:31:38 +01002559 stbir_filter filter_horizontal, stbir_filter filter_vertical,
2560 stbir_colorspace space, void *alloc_context,
2561 float x_scale, float y_scale,
2562 float x_offset, float y_offset)
2563{
2564 float transform[4];
2565 transform[0] = x_scale;
2566 transform[1] = y_scale;
2567 transform[2] = x_offset;
2568 transform[3] = y_offset;
2569 return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2570 output_pixels, output_w, output_h, output_stride_in_bytes,
2571 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical,
2572 edge_mode_horizontal, edge_mode_vertical, space);
2573}
2574
2575STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2576 void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2577 stbir_datatype datatype,
2578 int num_channels, int alpha_channel, int flags,
Jim Flynn6217c3d2022-06-14 10:58:23 +01002579 stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
Sadik Armagan93e2e402019-05-02 09:31:38 +01002580 stbir_filter filter_horizontal, stbir_filter filter_vertical,
2581 stbir_colorspace space, void *alloc_context,
2582 float s0, float t0, float s1, float t1)
2583{
2584 return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2585 output_pixels, output_w, output_h, output_stride_in_bytes,
2586 s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical,
2587 edge_mode_horizontal, edge_mode_vertical, space);
2588}
2589
2590#endif // STB_IMAGE_RESIZE_IMPLEMENTATION
2591
2592/*
2593------------------------------------------------------------------------------
2594MIT License
2595Copyright (c) 2017 Sean Barrett
Jim Flynn6217c3d2022-06-14 10:58:23 +01002596Permission is hereby granted, free of charge, to any person obtaining a copy of
2597this software and associated documentation files (the "Software"), to deal in
2598the Software without restriction, including without limitation the rights to
2599use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
2600of the Software, and to permit persons to whom the Software is furnished to do
Sadik Armagan93e2e402019-05-02 09:31:38 +01002601so, subject to the following conditions:
Jim Flynn6217c3d2022-06-14 10:58:23 +01002602The above copyright notice and this permission notice shall be included in all
Sadik Armagan93e2e402019-05-02 09:31:38 +01002603copies or substantial portions of the Software.
Jim Flynn6217c3d2022-06-14 10:58:23 +01002604THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2605IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2606FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2607AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2608LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2609OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
Sadik Armagan93e2e402019-05-02 09:31:38 +01002610SOFTWARE.
2611------------------------------------------------------------------------------
2612*/