├── .gitattributes
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── examples
├── ascii-aperture.png
├── ascii-c.png
├── ascii-monroe.png
├── ascii-pepsi.png
└── ascii-pink_floyd.png
├── images
├── aperture.jpg
├── c.png
├── img.png
├── linux.jpg
├── liverpool.png
├── mona_lisa.jpg
├── monroe.jpg
├── pepsi.png
├── pikachu.jpg
├── pink_floyd.jpg
└── wave.jpg
├── img2ascii.png
├── src
├── args.h
├── ascii_art.h
├── main.c
└── utils.h
└── stb_image
├── stb_image.h
├── stb_image_resize.h
└── stb_image_write.h
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.h linguist-language=C
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/
2 | tests/
3 | *.exe
4 | *.txt
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Josef Veselý
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SRC = src/main.c
2 | BIN = img2ascii
3 | CC = gcc
4 | CFLAGS = -Wall -Wextra
5 | LIBS = -lm
6 |
7 | all:
8 | $(CC) $(CFLAGS) -o $(BIN) $(SRC) $(LIBS)
9 |
10 | clean:
11 | $(RM) $(BIN) $(BIN).exe
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ##

4 | *A command-line tool for converting images to ASCII art*
5 |
6 | [](https://github.com/JosefVesely/img2ascii)
7 | [](https://github.com/JosefVesely/img2ascii/blob/main/LICENSE)
8 | [](https://github.com/JosefVesely/img2ascii/stargazers)
9 |
10 |
11 |
12 | ## 🛠️ Installation
13 |
14 | ```sh
15 | # Clone the repository
16 | git clone https://github.com/JosefVesely/img2ascii
17 |
18 | # Navigate to the project directory
19 | cd img2ascii
20 |
21 | # Compile the program
22 | make
23 | ```
24 |
25 | ## 🚀 Usage
26 |
27 | Run using the following syntax:
28 |
29 | ```md
30 | img2ascii [options] -i [-o ]
31 | ```
32 |
33 | ### Options:
34 |
35 | | Short | Long | Parameter | Description |
36 | |-------|-----------|-----------|-----------------------------------------------|
37 | | -i | --input | *File* | Path of the input image file ***(required)*** |
38 | | -o | --output | *File* | Path of the output file |
39 | | -w | --width | *Number* | Width of the output image |
40 | | -c | --chars | *String* | Characters to be used for the ASCII image |
41 | | -p | --print | | Print the output to the console |
42 | | -r | --reverse | | Reverse the string of characters |
43 |
44 | ## 🧩 Examples
45 |
46 | | Input | Output |
47 | |--------------------------------------------------|--------------------------------------------------|
48 | |
|
|
49 | | `img2ascii -i images/c.png -w 40 -p` | |
50 | |
|
|
51 | | `img2ascii -i images/monroe.jpg -w 35 -p -r` | |
52 | |
|
|
53 | | `img2ascii -i images/pink_floyd.jpg -w 80 -p -r` | |
54 | |
|
|
55 | | `img2ascii -i images/pepsi.png -w 35 -p -r` | |
56 | |
|
|
57 | | `img2ascii -i images/aperture.jpg -w 50 -p` | |
58 |
59 | ## 🧪 Compatibility
60 |
61 | The supported image file formats are **PNG**, **JPEG** and **BMP**
62 |
63 | This program has been tested and works on the following operating systems:
64 |
65 | - **Windows 11**
66 | - **Linux Mint 22**
67 |
68 | ## 🤝 Contributing
69 |
70 | Contributions are welcome! Feel free to open an issue or submit a pull request
71 |
72 | ## 📜 License
73 |
74 | This project is licensed under the [MIT License](https://github.com/JosefVesely/img2ascii/blob/main/LICENSE)
--------------------------------------------------------------------------------
/examples/ascii-aperture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/examples/ascii-aperture.png
--------------------------------------------------------------------------------
/examples/ascii-c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/examples/ascii-c.png
--------------------------------------------------------------------------------
/examples/ascii-monroe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/examples/ascii-monroe.png
--------------------------------------------------------------------------------
/examples/ascii-pepsi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/examples/ascii-pepsi.png
--------------------------------------------------------------------------------
/examples/ascii-pink_floyd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/examples/ascii-pink_floyd.png
--------------------------------------------------------------------------------
/images/aperture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/aperture.jpg
--------------------------------------------------------------------------------
/images/c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/c.png
--------------------------------------------------------------------------------
/images/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/img.png
--------------------------------------------------------------------------------
/images/linux.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/linux.jpg
--------------------------------------------------------------------------------
/images/liverpool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/liverpool.png
--------------------------------------------------------------------------------
/images/mona_lisa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/mona_lisa.jpg
--------------------------------------------------------------------------------
/images/monroe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/monroe.jpg
--------------------------------------------------------------------------------
/images/pepsi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/pepsi.png
--------------------------------------------------------------------------------
/images/pikachu.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/pikachu.jpg
--------------------------------------------------------------------------------
/images/pink_floyd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/pink_floyd.jpg
--------------------------------------------------------------------------------
/images/wave.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/images/wave.jpg
--------------------------------------------------------------------------------
/img2ascii.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JosefVesely/img2ascii/ec838d7472bc270f8a74dc54b2bb116181e3eec3/img2ascii.png
--------------------------------------------------------------------------------
/src/args.h:
--------------------------------------------------------------------------------
1 | #ifndef ARGS_H
2 | #define ARGS_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "utils.h"
9 |
10 | enum {
11 | GRAYSCALE_FLAG = 1 << 0,
12 | REVERSE_FLAG = 1 << 1,
13 | PRINT_FLAG = 1 << 2,
14 | DEBUG_FLAG = 1 << 3
15 | };
16 |
17 | void process_arguments(
18 | int argc,
19 | char **argv,
20 | char **input_filepath,
21 | char **output_filepath,
22 | char **characters,
23 | int *desired_width,
24 | uint8_t *flags,
25 | bool *resize_image
26 | ) {
27 | // Exit if no command line arguments are given
28 |
29 | if (argc == 1) {
30 | printf("No input file\n");
31 | show_usage();
32 | exit(EXIT_FAILURE);
33 | }
34 |
35 | struct option long_options[] =
36 | {
37 | { "help", no_argument, NULL, 'h' },
38 | { "input", required_argument, NULL, 'i' },
39 | { "output", required_argument, NULL, 'o' },
40 | { "width", required_argument, NULL, 'w' },
41 | { "chars", required_argument, NULL, 'c' },
42 | { "grayscale", no_argument, NULL, 'g' },
43 | { "print", no_argument, NULL, 'p' },
44 | { "reverse", no_argument, NULL, 'r' },
45 | { "debug", no_argument, NULL, 'd' },
46 | { 0, 0, 0, 0 }
47 | };
48 |
49 | int option;
50 | const char *short_options = "hi:o:w:c:gprd";
51 |
52 | while ((option = getopt_long(argc, argv, short_options, long_options, NULL)) != EOF)
53 | {
54 | switch (option)
55 | {
56 | case 'h':
57 | show_usage();
58 | exit(EXIT_FAILURE);
59 |
60 | case 'i':
61 | *input_filepath = optarg;
62 | break;
63 |
64 | case 'o':
65 | *output_filepath = optarg;
66 | break;
67 |
68 | case 'w':
69 | *desired_width = atoi(optarg);
70 | *resize_image = true;
71 | break;
72 |
73 | case 'c':
74 | if (strlen(optarg) != 0) {
75 | *characters = (char *)realloc(*characters, strlen(optarg) + 1);
76 | strcpy(*characters, optarg);
77 | }
78 | break;
79 |
80 | case 'g':
81 | *flags |= GRAYSCALE_FLAG;
82 | break;
83 |
84 | case 'p':
85 | *flags |= PRINT_FLAG;
86 | break;
87 |
88 | case 'r':
89 | *flags |= REVERSE_FLAG;
90 | break;
91 |
92 | case 'd':
93 | *flags |= DEBUG_FLAG;
94 | break;
95 |
96 | case '?':
97 | printf("\nHint: Use the \e[1m--help\e[0m option to get help about the usage \n\n");
98 | exit(EXIT_FAILURE);
99 | }
100 | }
101 |
102 | if (*input_filepath == NULL) {
103 | printf("No input file\n");
104 | show_usage();
105 | exit(EXIT_FAILURE);
106 | }
107 |
108 | if (*output_filepath == NULL) {
109 | *flags |= PRINT_FLAG;
110 | }
111 | }
112 |
113 | #endif // ARGS_H
--------------------------------------------------------------------------------
/src/ascii_art.h:
--------------------------------------------------------------------------------
1 | #ifndef ASCII_ART_H
2 | #define ASCII_ART_H
3 |
4 | #define STB_IMAGE_IMPLEMENTATION
5 | #define STB_IMAGE_WRITE_IMPLEMENTATION
6 | #define STB_IMAGE_RESIZE_IMPLEMENTATION
7 | #include "../stb_image/stb_image.h"
8 | #include "../stb_image/stb_image_write.h"
9 | #include "../stb_image/stb_image_resize.h"
10 |
11 | #include
12 | #include
13 | #include "utils.h"
14 |
15 |
16 | uint8_t *load_image(const char *input_filepath, int *desired_width, int *desired_height, bool *resize_image)
17 | {
18 | // Load the image
19 |
20 | const int channels = STBI_rgb;
21 | int width, height;
22 |
23 | uint8_t *image = stbi_load(input_filepath, &width, &height, NULL, channels);
24 |
25 | if (image == NULL) {
26 | fprintf(stderr, "Could not load image \n");
27 | exit(EXIT_FAILURE);
28 | }
29 |
30 | // Resize the image
31 |
32 | if (*resize_image) {
33 | // Check if the chosen width is valid
34 | if (*desired_width <= 0) {
35 | fprintf(stderr, "Argument 'width' must be greater than 0 \n");
36 | exit(EXIT_FAILURE);
37 | }
38 | else if (*desired_width > width) {
39 | fprintf(stderr, "Argument 'width' can not be greater than the original image width (%ipx) \n", width);
40 | exit(EXIT_FAILURE);
41 | }
42 |
43 | *desired_height = height / (width / (float)*desired_width);
44 |
45 | stbir_resize_uint8(
46 | image, width, height, width * channels,
47 | image, *desired_width, *desired_height, *desired_width * channels, channels
48 | );
49 | }
50 | else {
51 | *desired_width = width;
52 | *desired_height = height;
53 | }
54 | return image;
55 | }
56 |
57 | void get_rgb(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *image, int i)
58 | {
59 | const int channels = 3;
60 | uint8_t *pixel_offset = image + i * channels;
61 |
62 | *r = pixel_offset[0];
63 | *g = pixel_offset[1];
64 | *b = pixel_offset[2];
65 | }
66 |
67 | uint8_t get_intensity(uint8_t *image, int i)
68 | {
69 | const int channels = 3;
70 | uint8_t *pixel_offset = image + i * channels;
71 | uint8_t r = pixel_offset[0];
72 | uint8_t g = pixel_offset[1];
73 | uint8_t b = pixel_offset[2];
74 |
75 | return (uint8_t)round(0.299 * r + 0.587 * g + 0.114 * b);
76 | }
77 |
78 | char *get_output_grayscale(uint8_t *image, int desired_width, int desired_height, char *characters, uint8_t flags)
79 | {
80 | if (flags & REVERSE_FLAG) {
81 | reverse_string(characters);
82 | }
83 |
84 | int output_size = desired_height * desired_width + desired_height + 1;
85 | char *output = (char *)malloc(output_size * sizeof(char));
86 |
87 | int characters_count = strlen(characters);
88 |
89 | int ptr = 0; // Index of the output string
90 |
91 | for (int i = 0; i < desired_height * desired_width; i++) {
92 | int intensity = get_intensity(image, i);
93 |
94 | int char_index = intensity / (255 / (float)(characters_count - 1));
95 |
96 | output[ptr] = characters[char_index];
97 |
98 | if ((i + 1) % desired_width == 0) {
99 | output[++ptr] = '\n';
100 | }
101 | ptr++;
102 | }
103 | output[ptr] = '\0';
104 |
105 | return output;
106 | }
107 |
108 | char *get_output_rgb(uint8_t *image, int width, int height, char *characters, uint8_t flags)
109 | {
110 | if (flags & REVERSE_FLAG) {
111 | reverse_string(characters);
112 | }
113 |
114 | int length = (height * width + height) * 21 + 1; // Length calculation
115 | char *output = (char *)malloc(length * sizeof(char));
116 |
117 | int characters_count = strlen(characters);
118 | int ptr = 0; // Index of the output string
119 |
120 | uint8_t r_prev, g_prev, b_prev;
121 |
122 | for (int i = 0; i < height * width; i++) {
123 | int intensity = get_intensity(image, i);
124 | int char_index = intensity / (255 / (float)(characters_count - 1));
125 |
126 | uint8_t r, g, b;
127 | get_rgb(&r, &g, &b, image, i);
128 |
129 | if (!(r == r_prev && g == g_prev && b == b_prev)) {
130 | ptr += snprintf(output + ptr, length - ptr, "\e[38;2;%i;%i;%im", r, g, b); // Append the ANSI code
131 | }
132 | r_prev = r;
133 | g_prev = g;
134 | b_prev = b;
135 |
136 | output[ptr++] = characters[char_index];
137 |
138 | if ((i + 1) % width == 0) {
139 | output[ptr++] = '\n';
140 | }
141 | }
142 | snprintf(output + ptr, length - ptr, "\e[0m"); // Append the ANSI reset code
143 |
144 | return output;
145 | }
146 |
147 | void write_output(
148 | uint8_t *image,
149 | char *input_filepath,
150 | char *output_filepath,
151 | char *characters,
152 | int width,
153 | int height,
154 | uint8_t flags
155 | ) {
156 | char *output = NULL;
157 |
158 | if (flags & GRAYSCALE_FLAG) {
159 | output = get_output_grayscale(image, width, height, characters, flags);
160 | } else {
161 | output = get_output_rgb(image, width, height, characters, flags);
162 | }
163 |
164 | if (flags & DEBUG_FLAG) {
165 | printf(
166 | "Input: %s \n"
167 | "Output: %s \n"
168 | "Resolution: %ix%i \n"
169 | "Characters (%i): \"%s\" \n",
170 | input_filepath,
171 | output_filepath != NULL ? output_filepath : "stdout",
172 | width, height,
173 | (int)strlen(characters), characters
174 | );
175 | }
176 |
177 | if (flags & PRINT_FLAG) {
178 | printf("%s", output);
179 | }
180 |
181 | if (output_filepath != NULL) {
182 | FILE *file_ptr = NULL;
183 |
184 | errno = 0;
185 | file_ptr = fopen(output_filepath, "w");
186 |
187 | if (file_ptr == NULL) {
188 | fprintf(stderr, "Could not create an output file: %s \n", strerror(errno));
189 | exit(EXIT_FAILURE);
190 | }
191 | fprintf(file_ptr, "%s", output);
192 | }
193 |
194 | // Free memory
195 | stbi_image_free(image);
196 | free(output);
197 | free(output_filepath);
198 | free(characters);
199 | }
200 |
201 | #endif // ASCII_ART_H
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "args.h"
8 | #include "utils.h"
9 | #include "ascii_art.h"
10 |
11 |
12 | int main(int argc, char **argv)
13 | {
14 | // Parse arguments from the command line
15 |
16 | char *input_filepath = NULL;
17 | char *output_filepath = NULL;
18 | char *characters = strdup("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ");
19 | int desired_width = 0;
20 | int desired_height = 0;
21 | uint8_t flags = 0;
22 | bool resize_image = false;
23 |
24 | process_arguments(
25 | argc,
26 | argv,
27 | &input_filepath,
28 | &output_filepath,
29 | &characters,
30 | &desired_width,
31 | &flags,
32 | &resize_image
33 | );
34 |
35 | // Load the image
36 |
37 | uint8_t *image = load_image(input_filepath, &desired_width, &desired_height, &resize_image);
38 |
39 | // Create an output file
40 |
41 | write_output(
42 | image,
43 | input_filepath,
44 | output_filepath,
45 | characters,
46 | desired_width,
47 | desired_height,
48 | flags
49 | );
50 |
51 | return EXIT_SUCCESS;
52 | }
--------------------------------------------------------------------------------
/src/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef UTILS_H
2 | #define UTILS_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 |
9 | void reverse_string(char *str)
10 | {
11 | int len = strlen(str);
12 |
13 | for (int i = 0; i < len / 2; i++) {
14 | char temp = str[i];
15 | str[i] = str[len - i - 1];
16 | str[len - i - 1] = temp;
17 | }
18 | }
19 |
20 | void show_usage(void)
21 | {
22 | printf(
23 | "\nUsage: \e[1mimg2ascii [options] -i [-o ]\e[0m \n\n"
24 |
25 | "A command-line tool for converting images to ASCII art \n\n"
26 |
27 | "Options: \n"
28 | " -i, --input Path of the input image file (required) \n"
29 | " -o, --output Path of the output file \n"
30 | " -w, --width Width of the output \n"
31 | " -c, --chars Characters to be used for the ASCII image \n"
32 | " -p, --print Print the output to the console \n"
33 | " -r, --reverse Reverse the string of characters \n"
34 | " -d, --debug Print some useful information \n\n"
35 | );
36 | }
37 |
38 | #endif // UTILS_H
--------------------------------------------------------------------------------
/stb_image/stb_image_resize.h:
--------------------------------------------------------------------------------
1 | /* stb_image_resize - v0.97 - public domain image resizing
2 | by Jorge L Rodriguez (@VinoBS) - 2014
3 | http://github.com/nothings/stb
4 |
5 | Written with emphasis on usability, portability, and efficiency. (No
6 | SIMD or threads, so it be easily outperformed by libs that use those.)
7 | Only scaling and translation is supported, no rotations or shears.
8 | Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation.
9 |
10 | COMPILING & LINKING
11 | In one C/C++ file that #includes this file, do this:
12 | #define STB_IMAGE_RESIZE_IMPLEMENTATION
13 | before the #include. That will create the implementation in that file.
14 |
15 | QUICKSTART
16 | stbir_resize_uint8( input_pixels , in_w , in_h , 0,
17 | output_pixels, out_w, out_h, 0, num_channels)
18 | stbir_resize_float(...)
19 | stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0,
20 | output_pixels, out_w, out_h, 0,
21 | num_channels , alpha_chan , 0)
22 | stbir_resize_uint8_srgb_edgemode(
23 | input_pixels , in_w , in_h , 0,
24 | output_pixels, out_w, out_h, 0,
25 | num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP)
26 | // WRAP/REFLECT/ZERO
27 |
28 | FULL API
29 | See the "header file" section of the source for API documentation.
30 |
31 | ADDITIONAL DOCUMENTATION
32 |
33 | SRGB & FLOATING POINT REPRESENTATION
34 | The sRGB functions presume IEEE floating point. If you do not have
35 | IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use
36 | a slower implementation.
37 |
38 | MEMORY ALLOCATION
39 | The resize functions here perform a single memory allocation using
40 | malloc. To control the memory allocation, before the #include that
41 | triggers the implementation, do:
42 |
43 | #define STBIR_MALLOC(size,context) ...
44 | #define STBIR_FREE(ptr,context) ...
45 |
46 | Each resize function makes exactly one call to malloc/free, so to use
47 | temp memory, store the temp memory in the context and return that.
48 |
49 | ASSERT
50 | Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
51 |
52 | OPTIMIZATION
53 | Define STBIR_SATURATE_INT to compute clamp values in-range using
54 | integer operations instead of float operations. This may be faster
55 | on some platforms.
56 |
57 | DEFAULT FILTERS
58 | For functions which don't provide explicit control over what filters
59 | to use, you can change the compile-time defaults with
60 |
61 | #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something
62 | #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something
63 |
64 | See stbir_filter in the header-file section for the list of filters.
65 |
66 | NEW FILTERS
67 | A number of 1D filter kernels are used. For a list of
68 | supported filters see the stbir_filter enum. To add a new filter,
69 | write a filter function and add it to stbir__filter_info_table.
70 |
71 | PROGRESS
72 | For interactive use with slow resize operations, you can install
73 | a progress-report callback:
74 |
75 | #define STBIR_PROGRESS_REPORT(val) some_func(val)
76 |
77 | The parameter val is a float which goes from 0 to 1 as progress is made.
78 |
79 | For example:
80 |
81 | static void my_progress_report(float progress);
82 | #define STBIR_PROGRESS_REPORT(val) my_progress_report(val)
83 |
84 | #define STB_IMAGE_RESIZE_IMPLEMENTATION
85 | #include "stb_image_resize.h"
86 |
87 | static void my_progress_report(float progress)
88 | {
89 | printf("Progress: %f%%\n", progress*100);
90 | }
91 |
92 | MAX CHANNELS
93 | If your image has more than 64 channels, define STBIR_MAX_CHANNELS
94 | to the max you'll have.
95 |
96 | ALPHA CHANNEL
97 | Most of the resizing functions provide the ability to control how
98 | the alpha channel of an image is processed. The important things
99 | to know about this:
100 |
101 | 1. The best mathematically-behaved version of alpha to use is
102 | called "premultiplied alpha", in which the other color channels
103 | have had the alpha value multiplied in. If you use premultiplied
104 | alpha, linear filtering (such as image resampling done by this
105 | library, or performed in texture units on GPUs) does the "right
106 | thing". While premultiplied alpha is standard in the movie CGI
107 | industry, it is still uncommon in the videogame/real-time world.
108 |
109 | If you linearly filter non-premultiplied alpha, strange effects
110 | occur. (For example, the 50/50 average of 99% transparent bright green
111 | and 1% transparent black produces 50% transparent dark green when
112 | non-premultiplied, whereas premultiplied it produces 50%
113 | transparent near-black. The former introduces green energy
114 | that doesn't exist in the source image.)
115 |
116 | 2. Artists should not edit premultiplied-alpha images; artists
117 | want non-premultiplied alpha images. Thus, art tools generally output
118 | non-premultiplied alpha images.
119 |
120 | 3. You will get best results in most cases by converting images
121 | to premultiplied alpha before processing them mathematically.
122 |
123 | 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the
124 | resizer does not do anything special for the alpha channel;
125 | it is resampled identically to other channels. This produces
126 | the correct results for premultiplied-alpha images, but produces
127 | less-than-ideal results for non-premultiplied-alpha images.
128 |
129 | 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED,
130 | then the resizer weights the contribution of input pixels
131 | based on their alpha values, or, equivalently, it multiplies
132 | the alpha value into the color channels, resamples, then divides
133 | by the resultant alpha value. Input pixels which have alpha=0 do
134 | not contribute at all to output pixels unless _all_ of the input
135 | pixels affecting that output pixel have alpha=0, in which case
136 | the result for that pixel is the same as it would be without
137 | STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for
138 | input images in integer formats. For input images in float format,
139 | input pixels with alpha=0 have no effect, and output pixels
140 | which have alpha=0 will be 0 in all channels. (For float images,
141 | you can manually achieve the same result by adding a tiny epsilon
142 | value to the alpha channel of every image, and then subtracting
143 | or clamping it at the end.)
144 |
145 | 6. You can suppress the behavior described in #5 and make
146 | all-0-alpha pixels have 0 in all channels by #defining
147 | STBIR_NO_ALPHA_EPSILON.
148 |
149 | 7. You can separately control whether the alpha channel is
150 | interpreted as linear or affected by the colorspace. By default
151 | it is linear; you almost never want to apply the colorspace.
152 | (For example, graphics hardware does not apply sRGB conversion
153 | to the alpha channel.)
154 |
155 | CONTRIBUTORS
156 | Jorge L Rodriguez: Implementation
157 | Sean Barrett: API design, optimizations
158 | Aras Pranckevicius: bugfix
159 | Nathan Reed: warning fixes
160 |
161 | REVISIONS
162 | 0.97 (2020-02-02) fixed warning
163 | 0.96 (2019-03-04) fixed warnings
164 | 0.95 (2017-07-23) fixed warnings
165 | 0.94 (2017-03-18) fixed warnings
166 | 0.93 (2017-03-03) fixed bug with certain combinations of heights
167 | 0.92 (2017-01-02) fix integer overflow on large (>2GB) images
168 | 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions
169 | 0.90 (2014-09-17) first released version
170 |
171 | LICENSE
172 | See end of file for license information.
173 |
174 | TODO
175 | Don't decode all of the image data when only processing a partial tile
176 | Don't use full-width decode buffers when only processing a partial tile
177 | When processing wide images, break processing into tiles so data fits in L1 cache
178 | Installable filters?
179 | Resize that respects alpha test coverage
180 | (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage:
181 | https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp )
182 | */
183 |
184 | #ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H
185 | #define STBIR_INCLUDE_STB_IMAGE_RESIZE_H
186 |
187 | #ifdef _MSC_VER
188 | typedef unsigned char stbir_uint8;
189 | typedef unsigned short stbir_uint16;
190 | typedef unsigned int stbir_uint32;
191 | #else
192 | #include
193 | typedef uint8_t stbir_uint8;
194 | typedef uint16_t stbir_uint16;
195 | typedef uint32_t stbir_uint32;
196 | #endif
197 |
198 | #ifndef STBIRDEF
199 | #ifdef STB_IMAGE_RESIZE_STATIC
200 | #define STBIRDEF static
201 | #else
202 | #ifdef __cplusplus
203 | #define STBIRDEF extern "C"
204 | #else
205 | #define STBIRDEF extern
206 | #endif
207 | #endif
208 | #endif
209 |
210 | //////////////////////////////////////////////////////////////////////////////
211 | //
212 | // Easy-to-use API:
213 | //
214 | // * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4)
215 | // * input_w is input image width (x-axis), input_h is input image height (y-axis)
216 | // * stride is the offset between successive rows of image data in memory, in bytes. you can
217 | // specify 0 to mean packed continuously in memory
218 | // * alpha channel is treated identically to other channels.
219 | // * colorspace is linear or sRGB as specified by function name
220 | // * returned result is 1 for success or 0 in case of an error.
221 | // #define STBIR_ASSERT() to trigger an assert on parameter validation errors.
222 | // * Memory required grows approximately linearly with input and output size, but with
223 | // discontinuities at input_w == output_w and input_h == output_h.
224 | // * These functions use a "default" resampling filter defined at compile time. To change the filter,
225 | // you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE
226 | // and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API.
227 |
228 | STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
229 | unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
230 | int num_channels);
231 |
232 | STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
233 | float *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
234 | int num_channels);
235 |
236 |
237 | // The following functions interpret image data as gamma-corrected sRGB.
238 | // Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel,
239 | // or otherwise provide the index of the alpha channel. Flags value
240 | // of 0 will probably do the right thing if you're not sure what
241 | // the flags mean.
242 |
243 | #define STBIR_ALPHA_CHANNEL_NONE -1
244 |
245 | // Set this flag if your texture has premultiplied alpha. Otherwise, stbir will
246 | // use alpha-weighted resampling (effectively premultiplying, resampling,
247 | // then unpremultiplying).
248 | #define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0)
249 | // The specified alpha channel should be handled as gamma-corrected value even
250 | // when doing sRGB operations.
251 | #define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1)
252 |
253 | STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
254 | unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
255 | int num_channels, int alpha_channel, int flags);
256 |
257 |
258 | typedef enum
259 | {
260 | STBIR_EDGE_CLAMP = 1,
261 | STBIR_EDGE_REFLECT = 2,
262 | STBIR_EDGE_WRAP = 3,
263 | STBIR_EDGE_ZERO = 4,
264 | } stbir_edge;
265 |
266 | // This function adds the ability to specify how requests to sample off the edge of the image are handled.
267 | STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
268 | unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
269 | int num_channels, int alpha_channel, int flags,
270 | stbir_edge edge_wrap_mode);
271 |
272 | //////////////////////////////////////////////////////////////////////////////
273 | //
274 | // Medium-complexity API
275 | //
276 | // This extends the easy-to-use API as follows:
277 | //
278 | // * Alpha-channel can be processed separately
279 | // * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE
280 | // * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT)
281 | // * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)
282 | // * Filter can be selected explicitly
283 | // * uint16 image type
284 | // * sRGB colorspace available for all types
285 | // * context parameter for passing to STBIR_MALLOC
286 |
287 | typedef enum
288 | {
289 | STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses
290 | STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios
291 | STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering
292 | STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque
293 | STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline
294 | STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3
295 | } stbir_filter;
296 |
297 | typedef enum
298 | {
299 | STBIR_COLORSPACE_LINEAR,
300 | STBIR_COLORSPACE_SRGB,
301 |
302 | STBIR_MAX_COLORSPACES,
303 | } stbir_colorspace;
304 |
305 | // The following functions are all identical except for the type of the image data
306 |
307 | STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
308 | unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
309 | int num_channels, int alpha_channel, int flags,
310 | stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
311 | void *alloc_context);
312 |
313 | STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
314 | stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
315 | int num_channels, int alpha_channel, int flags,
316 | stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
317 | void *alloc_context);
318 |
319 | STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
320 | float *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
321 | int num_channels, int alpha_channel, int flags,
322 | stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
323 | void *alloc_context);
324 |
325 |
326 |
327 | //////////////////////////////////////////////////////////////////////////////
328 | //
329 | // Full-complexity API
330 | //
331 | // This extends the medium API as follows:
332 | //
333 | // * uint32 image type
334 | // * not typesafe
335 | // * separate filter types for each axis
336 | // * separate edge modes for each axis
337 | // * can specify scale explicitly for subpixel correctness
338 | // * can specify image source tile using texture coordinates
339 |
340 | typedef enum
341 | {
342 | STBIR_TYPE_UINT8 ,
343 | STBIR_TYPE_UINT16,
344 | STBIR_TYPE_UINT32,
345 | STBIR_TYPE_FLOAT ,
346 |
347 | STBIR_MAX_TYPES
348 | } stbir_datatype;
349 |
350 | STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
351 | void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
352 | stbir_datatype datatype,
353 | int num_channels, int alpha_channel, int flags,
354 | stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
355 | stbir_filter filter_horizontal, stbir_filter filter_vertical,
356 | stbir_colorspace space, void *alloc_context);
357 |
358 | STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
359 | void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
360 | stbir_datatype datatype,
361 | int num_channels, int alpha_channel, int flags,
362 | stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
363 | stbir_filter filter_horizontal, stbir_filter filter_vertical,
364 | stbir_colorspace space, void *alloc_context,
365 | float x_scale, float y_scale,
366 | float x_offset, float y_offset);
367 |
368 | STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
369 | void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
370 | stbir_datatype datatype,
371 | int num_channels, int alpha_channel, int flags,
372 | stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
373 | stbir_filter filter_horizontal, stbir_filter filter_vertical,
374 | stbir_colorspace space, void *alloc_context,
375 | float s0, float t0, float s1, float t1);
376 | // (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.
377 |
378 | //
379 | //
380 | //// end header file /////////////////////////////////////////////////////
381 | #endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H
382 |
383 |
384 |
385 |
386 |
387 | #ifdef STB_IMAGE_RESIZE_IMPLEMENTATION
388 |
389 | #ifndef STBIR_ASSERT
390 | #include
391 | #define STBIR_ASSERT(x) assert(x)
392 | #endif
393 |
394 | // For memset
395 | #include
396 |
397 | #include
398 |
399 | #ifndef STBIR_MALLOC
400 | #include
401 | // use comma operator to evaluate c, to avoid "unused parameter" warnings
402 | #define STBIR_MALLOC(size,c) ((void)(c), malloc(size))
403 | #define STBIR_FREE(ptr,c) ((void)(c), free(ptr))
404 | #endif
405 |
406 | #ifndef _MSC_VER
407 | #ifdef __cplusplus
408 | #define stbir__inline inline
409 | #else
410 | #define stbir__inline
411 | #endif
412 | #else
413 | #define stbir__inline __forceinline
414 | #endif
415 |
416 |
417 | // should produce compiler error if size is wrong
418 | typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1];
419 |
420 | #ifdef _MSC_VER
421 | #define STBIR__NOTUSED(v) (void)(v)
422 | #else
423 | #define STBIR__NOTUSED(v) (void)sizeof(v)
424 | #endif
425 |
426 | #define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0]))
427 |
428 | #ifndef STBIR_DEFAULT_FILTER_UPSAMPLE
429 | #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
430 | #endif
431 |
432 | #ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE
433 | #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL
434 | #endif
435 |
436 | #ifndef STBIR_PROGRESS_REPORT
437 | #define STBIR_PROGRESS_REPORT(float_0_to_1)
438 | #endif
439 |
440 | #ifndef STBIR_MAX_CHANNELS
441 | #define STBIR_MAX_CHANNELS 64
442 | #endif
443 |
444 | #if STBIR_MAX_CHANNELS > 65536
445 | #error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536."
446 | // because we store the indices in 16-bit variables
447 | #endif
448 |
449 | // This value is added to alpha just before premultiplication to avoid
450 | // zeroing out color values. It is equivalent to 2^-80. If you don't want
451 | // that behavior (it may interfere if you have floating point images with
452 | // very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to
453 | // disable it.
454 | #ifndef STBIR_ALPHA_EPSILON
455 | #define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20))
456 | #endif
457 |
458 |
459 |
460 | #ifdef _MSC_VER
461 | #define STBIR__UNUSED_PARAM(v) (void)(v)
462 | #else
463 | #define STBIR__UNUSED_PARAM(v) (void)sizeof(v)
464 | #endif
465 |
466 | // must match stbir_datatype
467 | static unsigned char stbir__type_size[] = {
468 | 1, // STBIR_TYPE_UINT8
469 | 2, // STBIR_TYPE_UINT16
470 | 4, // STBIR_TYPE_UINT32
471 | 4, // STBIR_TYPE_FLOAT
472 | };
473 |
474 | // Kernel function centered at 0
475 | typedef float (stbir__kernel_fn)(float x, float scale);
476 | typedef float (stbir__support_fn)(float scale);
477 |
478 | typedef struct
479 | {
480 | stbir__kernel_fn* kernel;
481 | stbir__support_fn* support;
482 | } stbir__filter_info;
483 |
484 | // When upsampling, the contributors are which source pixels contribute.
485 | // When downsampling, the contributors are which destination pixels are contributed to.
486 | typedef struct
487 | {
488 | int n0; // First contributing pixel
489 | int n1; // Last contributing pixel
490 | } stbir__contributors;
491 |
492 | typedef struct
493 | {
494 | const void* input_data;
495 | int input_w;
496 | int input_h;
497 | int input_stride_bytes;
498 |
499 | void* output_data;
500 | int output_w;
501 | int output_h;
502 | int output_stride_bytes;
503 |
504 | float s0, t0, s1, t1;
505 |
506 | float horizontal_shift; // Units: output pixels
507 | float vertical_shift; // Units: output pixels
508 | float horizontal_scale;
509 | float vertical_scale;
510 |
511 | int channels;
512 | int alpha_channel;
513 | stbir_uint32 flags;
514 | stbir_datatype type;
515 | stbir_filter horizontal_filter;
516 | stbir_filter vertical_filter;
517 | stbir_edge edge_horizontal;
518 | stbir_edge edge_vertical;
519 | stbir_colorspace colorspace;
520 |
521 | stbir__contributors* horizontal_contributors;
522 | float* horizontal_coefficients;
523 |
524 | stbir__contributors* vertical_contributors;
525 | float* vertical_coefficients;
526 |
527 | int decode_buffer_pixels;
528 | float* decode_buffer;
529 |
530 | float* horizontal_buffer;
531 |
532 | // cache these because ceil/floor are inexplicably showing up in profile
533 | int horizontal_coefficient_width;
534 | int vertical_coefficient_width;
535 | int horizontal_filter_pixel_width;
536 | int vertical_filter_pixel_width;
537 | int horizontal_filter_pixel_margin;
538 | int vertical_filter_pixel_margin;
539 | int horizontal_num_contributors;
540 | int vertical_num_contributors;
541 |
542 | 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)
543 | int ring_buffer_num_entries; // Total number of entries in the ring buffer.
544 | int ring_buffer_first_scanline;
545 | int ring_buffer_last_scanline;
546 | int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer
547 | float* ring_buffer;
548 |
549 | float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds.
550 |
551 | int horizontal_contributors_size;
552 | int horizontal_coefficients_size;
553 | int vertical_contributors_size;
554 | int vertical_coefficients_size;
555 | int decode_buffer_size;
556 | int horizontal_buffer_size;
557 | int ring_buffer_size;
558 | int encode_buffer_size;
559 | } stbir__info;
560 |
561 |
562 | static const float stbir__max_uint8_as_float = 255.0f;
563 | static const float stbir__max_uint16_as_float = 65535.0f;
564 | static const double stbir__max_uint32_as_float = 4294967295.0;
565 |
566 |
567 | static stbir__inline int stbir__min(int a, int b)
568 | {
569 | return a < b ? a : b;
570 | }
571 |
572 | static stbir__inline float stbir__saturate(float x)
573 | {
574 | if (x < 0)
575 | return 0;
576 |
577 | if (x > 1)
578 | return 1;
579 |
580 | return x;
581 | }
582 |
583 | #ifdef STBIR_SATURATE_INT
584 | static stbir__inline stbir_uint8 stbir__saturate8(int x)
585 | {
586 | if ((unsigned int) x <= 255)
587 | return x;
588 |
589 | if (x < 0)
590 | return 0;
591 |
592 | return 255;
593 | }
594 |
595 | static stbir__inline stbir_uint16 stbir__saturate16(int x)
596 | {
597 | if ((unsigned int) x <= 65535)
598 | return x;
599 |
600 | if (x < 0)
601 | return 0;
602 |
603 | return 65535;
604 | }
605 | #endif
606 |
607 | static float stbir__srgb_uchar_to_linear_float[256] = {
608 | 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f,
609 | 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f,
610 | 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f,
611 | 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f,
612 | 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f,
613 | 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f,
614 | 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f,
615 | 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f,
616 | 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f,
617 | 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f,
618 | 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f,
619 | 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f,
620 | 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f,
621 | 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f,
622 | 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f,
623 | 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f,
624 | 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f,
625 | 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f,
626 | 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f,
627 | 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f,
628 | 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f,
629 | 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f,
630 | 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f,
631 | 0.982251f, 0.991102f, 1.0f
632 | };
633 |
634 | static float stbir__srgb_to_linear(float f)
635 | {
636 | if (f <= 0.04045f)
637 | return f / 12.92f;
638 | else
639 | return (float)pow((f + 0.055f) / 1.055f, 2.4f);
640 | }
641 |
642 | static float stbir__linear_to_srgb(float f)
643 | {
644 | if (f <= 0.0031308f)
645 | return f * 12.92f;
646 | else
647 | return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f;
648 | }
649 |
650 | #ifndef STBIR_NON_IEEE_FLOAT
651 | // From https://gist.github.com/rygorous/2203834
652 |
653 | typedef union
654 | {
655 | stbir_uint32 u;
656 | float f;
657 | } stbir__FP32;
658 |
659 | static const stbir_uint32 fp32_to_srgb8_tab4[104] = {
660 | 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
661 | 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
662 | 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
663 | 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
664 | 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
665 | 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
666 | 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
667 | 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
668 | 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
669 | 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
670 | 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
671 | 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
672 | 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
673 | };
674 |
675 | static stbir_uint8 stbir__linear_to_srgb_uchar(float in)
676 | {
677 | static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps
678 | static const stbir__FP32 minval = { (127-13) << 23 };
679 | stbir_uint32 tab,bias,scale,t;
680 | stbir__FP32 f;
681 |
682 | // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively.
683 | // The tests are carefully written so that NaNs map to 0, same as in the reference
684 | // implementation.
685 | if (!(in > minval.f)) // written this way to catch NaNs
686 | in = minval.f;
687 | if (in > almostone.f)
688 | in = almostone.f;
689 |
690 | // Do the table lookup and unpack bias, scale
691 | f.f = in;
692 | tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20];
693 | bias = (tab >> 16) << 9;
694 | scale = tab & 0xffff;
695 |
696 | // Grab next-highest mantissa bits and perform linear interpolation
697 | t = (f.u >> 12) & 0xff;
698 | return (unsigned char) ((bias + scale*t) >> 16);
699 | }
700 |
701 | #else
702 | // sRGB transition values, scaled by 1<<28
703 | static int stbir__srgb_offset_to_linear_scaled[256] =
704 | {
705 | 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603,
706 | 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926,
707 | 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148,
708 | 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856,
709 | 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731,
710 | 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369,
711 | 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021,
712 | 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073,
713 | 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389,
714 | 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552,
715 | 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066,
716 | 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490,
717 | 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568,
718 | 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316,
719 | 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096,
720 | 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700,
721 | 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376,
722 | 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912,
723 | 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648,
724 | 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512,
725 | 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072,
726 | 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544,
727 | 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832,
728 | 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528,
729 | 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968,
730 | 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184,
731 | 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992,
732 | 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968,
733 | 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480,
734 | 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656,
735 | 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464,
736 | 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664,
737 | };
738 |
739 | static stbir_uint8 stbir__linear_to_srgb_uchar(float f)
740 | {
741 | int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp
742 | int v = 0;
743 | int i;
744 |
745 | // Refine the guess with a short binary search.
746 | i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
747 | i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
748 | i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
749 | i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
750 | i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
751 | i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
752 | i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
753 | i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
754 |
755 | return (stbir_uint8) v;
756 | }
757 | #endif
758 |
759 | static float stbir__filter_trapezoid(float x, float scale)
760 | {
761 | float halfscale = scale / 2;
762 | float t = 0.5f + halfscale;
763 | STBIR_ASSERT(scale <= 1);
764 |
765 | x = (float)fabs(x);
766 |
767 | if (x >= t)
768 | return 0;
769 | else
770 | {
771 | float r = 0.5f - halfscale;
772 | if (x <= r)
773 | return 1;
774 | else
775 | return (t - x) / scale;
776 | }
777 | }
778 |
779 | static float stbir__support_trapezoid(float scale)
780 | {
781 | STBIR_ASSERT(scale <= 1);
782 | return 0.5f + scale / 2;
783 | }
784 |
785 | static float stbir__filter_triangle(float x, float s)
786 | {
787 | STBIR__UNUSED_PARAM(s);
788 |
789 | x = (float)fabs(x);
790 |
791 | if (x <= 1.0f)
792 | return 1 - x;
793 | else
794 | return 0;
795 | }
796 |
797 | static float stbir__filter_cubic(float x, float s)
798 | {
799 | STBIR__UNUSED_PARAM(s);
800 |
801 | x = (float)fabs(x);
802 |
803 | if (x < 1.0f)
804 | return (4 + x*x*(3*x - 6))/6;
805 | else if (x < 2.0f)
806 | return (8 + x*(-12 + x*(6 - x)))/6;
807 |
808 | return (0.0f);
809 | }
810 |
811 | static float stbir__filter_catmullrom(float x, float s)
812 | {
813 | STBIR__UNUSED_PARAM(s);
814 |
815 | x = (float)fabs(x);
816 |
817 | if (x < 1.0f)
818 | return 1 - x*x*(2.5f - 1.5f*x);
819 | else if (x < 2.0f)
820 | return 2 - x*(4 + x*(0.5f*x - 2.5f));
821 |
822 | return (0.0f);
823 | }
824 |
825 | static float stbir__filter_mitchell(float x, float s)
826 | {
827 | STBIR__UNUSED_PARAM(s);
828 |
829 | x = (float)fabs(x);
830 |
831 | if (x < 1.0f)
832 | return (16 + x*x*(21 * x - 36))/18;
833 | else if (x < 2.0f)
834 | return (32 + x*(-60 + x*(36 - 7*x)))/18;
835 |
836 | return (0.0f);
837 | }
838 |
839 | static float stbir__support_zero(float s)
840 | {
841 | STBIR__UNUSED_PARAM(s);
842 | return 0;
843 | }
844 |
845 | static float stbir__support_one(float s)
846 | {
847 | STBIR__UNUSED_PARAM(s);
848 | return 1;
849 | }
850 |
851 | static float stbir__support_two(float s)
852 | {
853 | STBIR__UNUSED_PARAM(s);
854 | return 2;
855 | }
856 |
857 | static stbir__filter_info stbir__filter_info_table[] = {
858 | { NULL, stbir__support_zero },
859 | { stbir__filter_trapezoid, stbir__support_trapezoid },
860 | { stbir__filter_triangle, stbir__support_one },
861 | { stbir__filter_cubic, stbir__support_two },
862 | { stbir__filter_catmullrom, stbir__support_two },
863 | { stbir__filter_mitchell, stbir__support_two },
864 | };
865 |
866 | stbir__inline static int stbir__use_upsampling(float ratio)
867 | {
868 | return ratio > 1;
869 | }
870 |
871 | stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info)
872 | {
873 | return stbir__use_upsampling(stbir_info->horizontal_scale);
874 | }
875 |
876 | stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info)
877 | {
878 | return stbir__use_upsampling(stbir_info->vertical_scale);
879 | }
880 |
881 | // This is the maximum number of input samples that can affect an output sample
882 | // with the given filter
883 | static int stbir__get_filter_pixel_width(stbir_filter filter, float scale)
884 | {
885 | STBIR_ASSERT(filter != 0);
886 | STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
887 |
888 | if (stbir__use_upsampling(scale))
889 | return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2);
890 | else
891 | return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale);
892 | }
893 |
894 | // This is how much to expand buffers to account for filters seeking outside
895 | // the image boundaries.
896 | static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale)
897 | {
898 | return stbir__get_filter_pixel_width(filter, scale) / 2;
899 | }
900 |
901 | static int stbir__get_coefficient_width(stbir_filter filter, float scale)
902 | {
903 | if (stbir__use_upsampling(scale))
904 | return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2);
905 | else
906 | return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2);
907 | }
908 |
909 | static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size)
910 | {
911 | if (stbir__use_upsampling(scale))
912 | return output_size;
913 | else
914 | return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2);
915 | }
916 |
917 | static int stbir__get_total_horizontal_coefficients(stbir__info* info)
918 | {
919 | return info->horizontal_num_contributors
920 | * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale);
921 | }
922 |
923 | static int stbir__get_total_vertical_coefficients(stbir__info* info)
924 | {
925 | return info->vertical_num_contributors
926 | * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale);
927 | }
928 |
929 | static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n)
930 | {
931 | return &contributors[n];
932 | }
933 |
934 | // For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample,
935 | // if you change it here change it there too.
936 | static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c)
937 | {
938 | int width = stbir__get_coefficient_width(filter, scale);
939 | return &coefficients[width*n + c];
940 | }
941 |
942 | static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max)
943 | {
944 | switch (edge)
945 | {
946 | case STBIR_EDGE_ZERO:
947 | return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later
948 |
949 | case STBIR_EDGE_CLAMP:
950 | if (n < 0)
951 | return 0;
952 |
953 | if (n >= max)
954 | return max - 1;
955 |
956 | return n; // NOTREACHED
957 |
958 | case STBIR_EDGE_REFLECT:
959 | {
960 | if (n < 0)
961 | {
962 | if (n < max)
963 | return -n;
964 | else
965 | return max - 1;
966 | }
967 |
968 | if (n >= max)
969 | {
970 | int max2 = max * 2;
971 | if (n >= max2)
972 | return 0;
973 | else
974 | return max2 - n - 1;
975 | }
976 |
977 | return n; // NOTREACHED
978 | }
979 |
980 | case STBIR_EDGE_WRAP:
981 | if (n >= 0)
982 | return (n % max);
983 | else
984 | {
985 | int m = (-n) % max;
986 |
987 | if (m != 0)
988 | m = max - m;
989 |
990 | return (m);
991 | }
992 | // NOTREACHED
993 |
994 | default:
995 | STBIR_ASSERT(!"Unimplemented edge type");
996 | return 0;
997 | }
998 | }
999 |
1000 | stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max)
1001 | {
1002 | // avoid per-pixel switch
1003 | if (n >= 0 && n < max)
1004 | return n;
1005 | return stbir__edge_wrap_slow(edge, n, max);
1006 | }
1007 |
1008 | // What input pixels contribute to this output pixel?
1009 | static 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)
1010 | {
1011 | float out_pixel_center = (float)n + 0.5f;
1012 | float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius;
1013 | float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius;
1014 |
1015 | float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio;
1016 | float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio;
1017 |
1018 | *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio;
1019 | *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5));
1020 | *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5));
1021 | }
1022 |
1023 | // What output pixels does this input pixel contribute to?
1024 | static 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)
1025 | {
1026 | float in_pixel_center = (float)n + 0.5f;
1027 | float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius;
1028 | float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius;
1029 |
1030 | float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift;
1031 | float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift;
1032 |
1033 | *out_center_of_in = in_pixel_center * scale_ratio - out_shift;
1034 | *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5));
1035 | *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5));
1036 | }
1037 |
1038 | static 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)
1039 | {
1040 | int i;
1041 | float total_filter = 0;
1042 | float filter_scale;
1043 |
1044 | 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.
1045 |
1046 | contributor->n0 = in_first_pixel;
1047 | contributor->n1 = in_last_pixel;
1048 |
1049 | STBIR_ASSERT(contributor->n1 >= contributor->n0);
1050 |
1051 | for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
1052 | {
1053 | float in_pixel_center = (float)(i + in_first_pixel) + 0.5f;
1054 | coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale);
1055 |
1056 | // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.)
1057 | if (i == 0 && !coefficient_group[i])
1058 | {
1059 | contributor->n0 = ++in_first_pixel;
1060 | i--;
1061 | continue;
1062 | }
1063 |
1064 | total_filter += coefficient_group[i];
1065 | }
1066 |
1067 | // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be.
1068 | // It would be true in exact math but is at best approximately true in floating-point math,
1069 | // and it would not make sense to try and put actual bounds on this here because it depends
1070 | // on the image aspect ratio which can get pretty extreme.
1071 | //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0);
1072 |
1073 | STBIR_ASSERT(total_filter > 0.9);
1074 | STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off.
1075 |
1076 | // Make sure the sum of all coefficients is 1.
1077 | filter_scale = 1 / total_filter;
1078 |
1079 | for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
1080 | coefficient_group[i] *= filter_scale;
1081 |
1082 | for (i = in_last_pixel - in_first_pixel; i >= 0; i--)
1083 | {
1084 | if (coefficient_group[i])
1085 | break;
1086 |
1087 | // This line has no weight. We can skip it.
1088 | contributor->n1 = contributor->n0 + i - 1;
1089 | }
1090 | }
1091 |
1092 | static 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)
1093 | {
1094 | int i;
1095 |
1096 | 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.
1097 |
1098 | contributor->n0 = out_first_pixel;
1099 | contributor->n1 = out_last_pixel;
1100 |
1101 | STBIR_ASSERT(contributor->n1 >= contributor->n0);
1102 |
1103 | for (i = 0; i <= out_last_pixel - out_first_pixel; i++)
1104 | {
1105 | float out_pixel_center = (float)(i + out_first_pixel) + 0.5f;
1106 | float x = out_pixel_center - out_center_of_in;
1107 | coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio;
1108 | }
1109 |
1110 | // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be.
1111 | // It would be true in exact math but is at best approximately true in floating-point math,
1112 | // and it would not make sense to try and put actual bounds on this here because it depends
1113 | // on the image aspect ratio which can get pretty extreme.
1114 | //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0);
1115 |
1116 | for (i = out_last_pixel - out_first_pixel; i >= 0; i--)
1117 | {
1118 | if (coefficient_group[i])
1119 | break;
1120 |
1121 | // This line has no weight. We can skip it.
1122 | contributor->n1 = contributor->n0 + i - 1;
1123 | }
1124 | }
1125 |
1126 | static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size)
1127 | {
1128 | int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size);
1129 | int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio);
1130 | int i, j;
1131 | int skip;
1132 |
1133 | for (i = 0; i < output_size; i++)
1134 | {
1135 | float scale;
1136 | float total = 0;
1137 |
1138 | for (j = 0; j < num_contributors; j++)
1139 | {
1140 | if (i >= contributors[j].n0 && i <= contributors[j].n1)
1141 | {
1142 | float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0);
1143 | total += coefficient;
1144 | }
1145 | else if (i < contributors[j].n0)
1146 | break;
1147 | }
1148 |
1149 | STBIR_ASSERT(total > 0.9f);
1150 | STBIR_ASSERT(total < 1.1f);
1151 |
1152 | scale = 1 / total;
1153 |
1154 | for (j = 0; j < num_contributors; j++)
1155 | {
1156 | if (i >= contributors[j].n0 && i <= contributors[j].n1)
1157 | *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale;
1158 | else if (i < contributors[j].n0)
1159 | break;
1160 | }
1161 | }
1162 |
1163 | // Optimize: Skip zero coefficients and contributions outside of image bounds.
1164 | // Do this after normalizing because normalization depends on the n0/n1 values.
1165 | for (j = 0; j < num_contributors; j++)
1166 | {
1167 | int range, max, width;
1168 |
1169 | skip = 0;
1170 | while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0)
1171 | skip++;
1172 |
1173 | contributors[j].n0 += skip;
1174 |
1175 | while (contributors[j].n0 < 0)
1176 | {
1177 | contributors[j].n0++;
1178 | skip++;
1179 | }
1180 |
1181 | range = contributors[j].n1 - contributors[j].n0 + 1;
1182 | max = stbir__min(num_coefficients, range);
1183 |
1184 | width = stbir__get_coefficient_width(filter, scale_ratio);
1185 | for (i = 0; i < max; i++)
1186 | {
1187 | if (i + skip >= width)
1188 | break;
1189 |
1190 | *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip);
1191 | }
1192 |
1193 | continue;
1194 | }
1195 |
1196 | // Using min to avoid writing into invalid pixels.
1197 | for (i = 0; i < num_contributors; i++)
1198 | contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1);
1199 | }
1200 |
1201 | // Each scan line uses the same kernel values so we should calculate the kernel
1202 | // values once and then we can use them for every scan line.
1203 | static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size)
1204 | {
1205 | int n;
1206 | int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size);
1207 |
1208 | if (stbir__use_upsampling(scale_ratio))
1209 | {
1210 | float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio;
1211 |
1212 | // Looping through out pixels
1213 | for (n = 0; n < total_contributors; n++)
1214 | {
1215 | float in_center_of_out; // Center of the current out pixel in the in pixel space
1216 | int in_first_pixel, in_last_pixel;
1217 |
1218 | stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out);
1219 |
1220 | 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));
1221 | }
1222 | }
1223 | else
1224 | {
1225 | float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio;
1226 |
1227 | // Looping through in pixels
1228 | for (n = 0; n < total_contributors; n++)
1229 | {
1230 | float out_center_of_in; // Center of the current out pixel in the in pixel space
1231 | int out_first_pixel, out_last_pixel;
1232 | int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio);
1233 |
1234 | stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in);
1235 |
1236 | 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));
1237 | }
1238 |
1239 | stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size);
1240 | }
1241 | }
1242 |
1243 | static float* stbir__get_decode_buffer(stbir__info* stbir_info)
1244 | {
1245 | // The 0 index of the decode buffer starts after the margin. This makes
1246 | // it okay to use negative indexes on the decode buffer.
1247 | return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels];
1248 | }
1249 |
1250 | #define STBIR__DECODE(type, colorspace) ((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace))
1251 |
1252 | static void stbir__decode_scanline(stbir__info* stbir_info, int n)
1253 | {
1254 | int c;
1255 | int channels = stbir_info->channels;
1256 | int alpha_channel = stbir_info->alpha_channel;
1257 | int type = stbir_info->type;
1258 | int colorspace = stbir_info->colorspace;
1259 | int input_w = stbir_info->input_w;
1260 | size_t input_stride_bytes = stbir_info->input_stride_bytes;
1261 | float* decode_buffer = stbir__get_decode_buffer(stbir_info);
1262 | stbir_edge edge_horizontal = stbir_info->edge_horizontal;
1263 | stbir_edge edge_vertical = stbir_info->edge_vertical;
1264 | size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes;
1265 | const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset;
1266 | int max_x = input_w + stbir_info->horizontal_filter_pixel_margin;
1267 | int decode = STBIR__DECODE(type, colorspace);
1268 |
1269 | int x = -stbir_info->horizontal_filter_pixel_margin;
1270 |
1271 | // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input,
1272 | // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO
1273 | if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h))
1274 | {
1275 | for (; x < max_x; x++)
1276 | for (c = 0; c < channels; c++)
1277 | decode_buffer[x*channels + c] = 0;
1278 | return;
1279 | }
1280 |
1281 | switch (decode)
1282 | {
1283 | case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR):
1284 | for (; x < max_x; x++)
1285 | {
1286 | int decode_pixel_index = x * channels;
1287 | int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1288 | for (c = 0; c < channels; c++)
1289 | decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float;
1290 | }
1291 | break;
1292 |
1293 | case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB):
1294 | for (; x < max_x; x++)
1295 | {
1296 | int decode_pixel_index = x * channels;
1297 | int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1298 | for (c = 0; c < channels; c++)
1299 | decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]];
1300 |
1301 | if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1302 | decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float;
1303 | }
1304 | break;
1305 |
1306 | case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR):
1307 | for (; x < max_x; x++)
1308 | {
1309 | int decode_pixel_index = x * channels;
1310 | int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1311 | for (c = 0; c < channels; c++)
1312 | decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float;
1313 | }
1314 | break;
1315 |
1316 | case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB):
1317 | for (; x < max_x; x++)
1318 | {
1319 | int decode_pixel_index = x * channels;
1320 | int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1321 | for (c = 0; c < channels; c++)
1322 | 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);
1323 |
1324 | if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1325 | decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float;
1326 | }
1327 | break;
1328 |
1329 | case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR):
1330 | for (; x < max_x; x++)
1331 | {
1332 | int decode_pixel_index = x * channels;
1333 | int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1334 | for (c = 0; c < channels; c++)
1335 | decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float);
1336 | }
1337 | break;
1338 |
1339 | case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB):
1340 | for (; x < max_x; x++)
1341 | {
1342 | int decode_pixel_index = x * channels;
1343 | int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1344 | for (c = 0; c < channels; c++)
1345 | 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));
1346 |
1347 | if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1348 | decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float);
1349 | }
1350 | break;
1351 |
1352 | case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR):
1353 | for (; x < max_x; x++)
1354 | {
1355 | int decode_pixel_index = x * channels;
1356 | int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1357 | for (c = 0; c < channels; c++)
1358 | decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c];
1359 | }
1360 | break;
1361 |
1362 | case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB):
1363 | for (; x < max_x; x++)
1364 | {
1365 | int decode_pixel_index = x * channels;
1366 | int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
1367 | for (c = 0; c < channels; c++)
1368 | decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]);
1369 |
1370 | if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1371 | decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel];
1372 | }
1373 |
1374 | break;
1375 |
1376 | default:
1377 | STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
1378 | break;
1379 | }
1380 |
1381 | if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED))
1382 | {
1383 | for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++)
1384 | {
1385 | int decode_pixel_index = x * channels;
1386 |
1387 | // If the alpha value is 0 it will clobber the color values. Make sure it's not.
1388 | float alpha = decode_buffer[decode_pixel_index + alpha_channel];
1389 | #ifndef STBIR_NO_ALPHA_EPSILON
1390 | if (stbir_info->type != STBIR_TYPE_FLOAT) {
1391 | alpha += STBIR_ALPHA_EPSILON;
1392 | decode_buffer[decode_pixel_index + alpha_channel] = alpha;
1393 | }
1394 | #endif
1395 | for (c = 0; c < channels; c++)
1396 | {
1397 | if (c == alpha_channel)
1398 | continue;
1399 |
1400 | decode_buffer[decode_pixel_index + c] *= alpha;
1401 | }
1402 | }
1403 | }
1404 |
1405 | if (edge_horizontal == STBIR_EDGE_ZERO)
1406 | {
1407 | for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++)
1408 | {
1409 | for (c = 0; c < channels; c++)
1410 | decode_buffer[x*channels + c] = 0;
1411 | }
1412 | for (x = input_w; x < max_x; x++)
1413 | {
1414 | for (c = 0; c < channels; c++)
1415 | decode_buffer[x*channels + c] = 0;
1416 | }
1417 | }
1418 | }
1419 |
1420 | static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length)
1421 | {
1422 | return &ring_buffer[index * ring_buffer_length];
1423 | }
1424 |
1425 | static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n)
1426 | {
1427 | int ring_buffer_index;
1428 | float* ring_buffer;
1429 |
1430 | stbir_info->ring_buffer_last_scanline = n;
1431 |
1432 | if (stbir_info->ring_buffer_begin_index < 0)
1433 | {
1434 | ring_buffer_index = stbir_info->ring_buffer_begin_index = 0;
1435 | stbir_info->ring_buffer_first_scanline = n;
1436 | }
1437 | else
1438 | {
1439 | 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;
1440 | STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index);
1441 | }
1442 |
1443 | ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float));
1444 | memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes);
1445 |
1446 | return ring_buffer;
1447 | }
1448 |
1449 |
1450 | static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer)
1451 | {
1452 | int x, k;
1453 | int output_w = stbir_info->output_w;
1454 | int channels = stbir_info->channels;
1455 | float* decode_buffer = stbir__get_decode_buffer(stbir_info);
1456 | stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors;
1457 | float* horizontal_coefficients = stbir_info->horizontal_coefficients;
1458 | int coefficient_width = stbir_info->horizontal_coefficient_width;
1459 |
1460 | for (x = 0; x < output_w; x++)
1461 | {
1462 | int n0 = horizontal_contributors[x].n0;
1463 | int n1 = horizontal_contributors[x].n1;
1464 |
1465 | int out_pixel_index = x * channels;
1466 | int coefficient_group = coefficient_width * x;
1467 | int coefficient_counter = 0;
1468 |
1469 | STBIR_ASSERT(n1 >= n0);
1470 | STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin);
1471 | STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin);
1472 | STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
1473 | STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
1474 |
1475 | switch (channels) {
1476 | case 1:
1477 | for (k = n0; k <= n1; k++)
1478 | {
1479 | int in_pixel_index = k * 1;
1480 | float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1481 | STBIR_ASSERT(coefficient != 0);
1482 | output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1483 | }
1484 | break;
1485 | case 2:
1486 | for (k = n0; k <= n1; k++)
1487 | {
1488 | int in_pixel_index = k * 2;
1489 | float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1490 | STBIR_ASSERT(coefficient != 0);
1491 | output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1492 | output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1493 | }
1494 | break;
1495 | case 3:
1496 | for (k = n0; k <= n1; k++)
1497 | {
1498 | int in_pixel_index = k * 3;
1499 | float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1500 | STBIR_ASSERT(coefficient != 0);
1501 | output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1502 | output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1503 | output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
1504 | }
1505 | break;
1506 | case 4:
1507 | for (k = n0; k <= n1; k++)
1508 | {
1509 | int in_pixel_index = k * 4;
1510 | float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1511 | STBIR_ASSERT(coefficient != 0);
1512 | output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1513 | output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1514 | output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
1515 | output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient;
1516 | }
1517 | break;
1518 | default:
1519 | for (k = n0; k <= n1; k++)
1520 | {
1521 | int in_pixel_index = k * channels;
1522 | float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
1523 | int c;
1524 | STBIR_ASSERT(coefficient != 0);
1525 | for (c = 0; c < channels; c++)
1526 | output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
1527 | }
1528 | break;
1529 | }
1530 | }
1531 | }
1532 |
1533 | static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer)
1534 | {
1535 | int x, k;
1536 | int input_w = stbir_info->input_w;
1537 | int channels = stbir_info->channels;
1538 | float* decode_buffer = stbir__get_decode_buffer(stbir_info);
1539 | stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors;
1540 | float* horizontal_coefficients = stbir_info->horizontal_coefficients;
1541 | int coefficient_width = stbir_info->horizontal_coefficient_width;
1542 | int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin;
1543 | int max_x = input_w + filter_pixel_margin * 2;
1544 |
1545 | STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info));
1546 |
1547 | switch (channels) {
1548 | case 1:
1549 | for (x = 0; x < max_x; x++)
1550 | {
1551 | int n0 = horizontal_contributors[x].n0;
1552 | int n1 = horizontal_contributors[x].n1;
1553 |
1554 | int in_x = x - filter_pixel_margin;
1555 | int in_pixel_index = in_x * 1;
1556 | int max_n = n1;
1557 | int coefficient_group = coefficient_width * x;
1558 |
1559 | for (k = n0; k <= max_n; k++)
1560 | {
1561 | int out_pixel_index = k * 1;
1562 | float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1563 | output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1564 | }
1565 | }
1566 | break;
1567 |
1568 | case 2:
1569 | for (x = 0; x < max_x; x++)
1570 | {
1571 | int n0 = horizontal_contributors[x].n0;
1572 | int n1 = horizontal_contributors[x].n1;
1573 |
1574 | int in_x = x - filter_pixel_margin;
1575 | int in_pixel_index = in_x * 2;
1576 | int max_n = n1;
1577 | int coefficient_group = coefficient_width * x;
1578 |
1579 | for (k = n0; k <= max_n; k++)
1580 | {
1581 | int out_pixel_index = k * 2;
1582 | float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1583 | output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1584 | output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1585 | }
1586 | }
1587 | break;
1588 |
1589 | case 3:
1590 | for (x = 0; x < max_x; x++)
1591 | {
1592 | int n0 = horizontal_contributors[x].n0;
1593 | int n1 = horizontal_contributors[x].n1;
1594 |
1595 | int in_x = x - filter_pixel_margin;
1596 | int in_pixel_index = in_x * 3;
1597 | int max_n = n1;
1598 | int coefficient_group = coefficient_width * x;
1599 |
1600 | for (k = n0; k <= max_n; k++)
1601 | {
1602 | int out_pixel_index = k * 3;
1603 | float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1604 | output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1605 | output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1606 | output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
1607 | }
1608 | }
1609 | break;
1610 |
1611 | case 4:
1612 | for (x = 0; x < max_x; x++)
1613 | {
1614 | int n0 = horizontal_contributors[x].n0;
1615 | int n1 = horizontal_contributors[x].n1;
1616 |
1617 | int in_x = x - filter_pixel_margin;
1618 | int in_pixel_index = in_x * 4;
1619 | int max_n = n1;
1620 | int coefficient_group = coefficient_width * x;
1621 |
1622 | for (k = n0; k <= max_n; k++)
1623 | {
1624 | int out_pixel_index = k * 4;
1625 | float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1626 | output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
1627 | output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
1628 | output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
1629 | output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient;
1630 | }
1631 | }
1632 | break;
1633 |
1634 | default:
1635 | for (x = 0; x < max_x; x++)
1636 | {
1637 | int n0 = horizontal_contributors[x].n0;
1638 | int n1 = horizontal_contributors[x].n1;
1639 |
1640 | int in_x = x - filter_pixel_margin;
1641 | int in_pixel_index = in_x * channels;
1642 | int max_n = n1;
1643 | int coefficient_group = coefficient_width * x;
1644 |
1645 | for (k = n0; k <= max_n; k++)
1646 | {
1647 | int c;
1648 | int out_pixel_index = k * channels;
1649 | float coefficient = horizontal_coefficients[coefficient_group + k - n0];
1650 | for (c = 0; c < channels; c++)
1651 | output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
1652 | }
1653 | }
1654 | break;
1655 | }
1656 | }
1657 |
1658 | static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n)
1659 | {
1660 | // Decode the nth scanline from the source image into the decode buffer.
1661 | stbir__decode_scanline(stbir_info, n);
1662 |
1663 | // Now resample it into the ring buffer.
1664 | if (stbir__use_width_upsampling(stbir_info))
1665 | stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n));
1666 | else
1667 | stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n));
1668 |
1669 | // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling.
1670 | }
1671 |
1672 | static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n)
1673 | {
1674 | // Decode the nth scanline from the source image into the decode buffer.
1675 | stbir__decode_scanline(stbir_info, n);
1676 |
1677 | memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float));
1678 |
1679 | // Now resample it into the horizontal buffer.
1680 | if (stbir__use_width_upsampling(stbir_info))
1681 | stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer);
1682 | else
1683 | stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer);
1684 |
1685 | // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers.
1686 | }
1687 |
1688 | // Get the specified scan line from the ring buffer.
1689 | static 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)
1690 | {
1691 | int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries;
1692 | return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length);
1693 | }
1694 |
1695 |
1696 | static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode)
1697 | {
1698 | int x;
1699 | int n;
1700 | int num_nonalpha;
1701 | stbir_uint16 nonalpha[STBIR_MAX_CHANNELS];
1702 |
1703 | if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
1704 | {
1705 | for (x=0; x < num_pixels; ++x)
1706 | {
1707 | int pixel_index = x*channels;
1708 |
1709 | float alpha = encode_buffer[pixel_index + alpha_channel];
1710 | float reciprocal_alpha = alpha ? 1.0f / alpha : 0;
1711 |
1712 | // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb
1713 | for (n = 0; n < channels; n++)
1714 | if (n != alpha_channel)
1715 | encode_buffer[pixel_index + n] *= reciprocal_alpha;
1716 |
1717 | // We added in a small epsilon to prevent the color channel from being deleted with zero alpha.
1718 | // Because we only add it for integer types, it will automatically be discarded on integer
1719 | // conversion, so we don't need to subtract it back out (which would be problematic for
1720 | // numeric precision reasons).
1721 | }
1722 | }
1723 |
1724 | // build a table of all channels that need colorspace correction, so
1725 | // we don't perform colorspace correction on channels that don't need it.
1726 | for (x = 0, num_nonalpha = 0; x < channels; ++x)
1727 | {
1728 | if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
1729 | {
1730 | nonalpha[num_nonalpha++] = (stbir_uint16)x;
1731 | }
1732 | }
1733 |
1734 | #define STBIR__ROUND_INT(f) ((int) ((f)+0.5))
1735 | #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5))
1736 |
1737 | #ifdef STBIR__SATURATE_INT
1738 | #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float ))
1739 | #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float))
1740 | #else
1741 | #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float )
1742 | #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float)
1743 | #endif
1744 |
1745 | switch (decode)
1746 | {
1747 | case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR):
1748 | for (x=0; x < num_pixels; ++x)
1749 | {
1750 | int pixel_index = x*channels;
1751 |
1752 | for (n = 0; n < channels; n++)
1753 | {
1754 | int index = pixel_index + n;
1755 | ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]);
1756 | }
1757 | }
1758 | break;
1759 |
1760 | case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB):
1761 | for (x=0; x < num_pixels; ++x)
1762 | {
1763 | int pixel_index = x*channels;
1764 |
1765 | for (n = 0; n < num_nonalpha; n++)
1766 | {
1767 | int index = pixel_index + nonalpha[n];
1768 | ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]);
1769 | }
1770 |
1771 | if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
1772 | ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]);
1773 | }
1774 | break;
1775 |
1776 | case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR):
1777 | for (x=0; x < num_pixels; ++x)
1778 | {
1779 | int pixel_index = x*channels;
1780 |
1781 | for (n = 0; n < channels; n++)
1782 | {
1783 | int index = pixel_index + n;
1784 | ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]);
1785 | }
1786 | }
1787 | break;
1788 |
1789 | case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB):
1790 | for (x=0; x < num_pixels; ++x)
1791 | {
1792 | int pixel_index = x*channels;
1793 |
1794 | for (n = 0; n < num_nonalpha; n++)
1795 | {
1796 | int index = pixel_index + nonalpha[n];
1797 | ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float);
1798 | }
1799 |
1800 | if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1801 | ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]);
1802 | }
1803 |
1804 | break;
1805 |
1806 | case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR):
1807 | for (x=0; x < num_pixels; ++x)
1808 | {
1809 | int pixel_index = x*channels;
1810 |
1811 | for (n = 0; n < channels; n++)
1812 | {
1813 | int index = pixel_index + n;
1814 | ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float);
1815 | }
1816 | }
1817 | break;
1818 |
1819 | case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB):
1820 | for (x=0; x < num_pixels; ++x)
1821 | {
1822 | int pixel_index = x*channels;
1823 |
1824 | for (n = 0; n < num_nonalpha; n++)
1825 | {
1826 | int index = pixel_index + nonalpha[n];
1827 | ((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);
1828 | }
1829 |
1830 | if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1831 | ((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);
1832 | }
1833 | break;
1834 |
1835 | case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR):
1836 | for (x=0; x < num_pixels; ++x)
1837 | {
1838 | int pixel_index = x*channels;
1839 |
1840 | for (n = 0; n < channels; n++)
1841 | {
1842 | int index = pixel_index + n;
1843 | ((float*)output_buffer)[index] = encode_buffer[index];
1844 | }
1845 | }
1846 | break;
1847 |
1848 | case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB):
1849 | for (x=0; x < num_pixels; ++x)
1850 | {
1851 | int pixel_index = x*channels;
1852 |
1853 | for (n = 0; n < num_nonalpha; n++)
1854 | {
1855 | int index = pixel_index + nonalpha[n];
1856 | ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]);
1857 | }
1858 |
1859 | if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
1860 | ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel];
1861 | }
1862 | break;
1863 |
1864 | default:
1865 | STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
1866 | break;
1867 | }
1868 | }
1869 |
1870 | static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n)
1871 | {
1872 | int x, k;
1873 | int output_w = stbir_info->output_w;
1874 | stbir__contributors* vertical_contributors = stbir_info->vertical_contributors;
1875 | float* vertical_coefficients = stbir_info->vertical_coefficients;
1876 | int channels = stbir_info->channels;
1877 | int alpha_channel = stbir_info->alpha_channel;
1878 | int type = stbir_info->type;
1879 | int colorspace = stbir_info->colorspace;
1880 | int ring_buffer_entries = stbir_info->ring_buffer_num_entries;
1881 | void* output_data = stbir_info->output_data;
1882 | float* encode_buffer = stbir_info->encode_buffer;
1883 | int decode = STBIR__DECODE(type, colorspace);
1884 | int coefficient_width = stbir_info->vertical_coefficient_width;
1885 | int coefficient_counter;
1886 | int contributor = n;
1887 |
1888 | float* ring_buffer = stbir_info->ring_buffer;
1889 | int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index;
1890 | int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline;
1891 | int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
1892 |
1893 | int n0,n1, output_row_start;
1894 | int coefficient_group = coefficient_width * contributor;
1895 |
1896 | n0 = vertical_contributors[contributor].n0;
1897 | n1 = vertical_contributors[contributor].n1;
1898 |
1899 | output_row_start = n * stbir_info->output_stride_bytes;
1900 |
1901 | STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
1902 |
1903 | memset(encode_buffer, 0, output_w * sizeof(float) * channels);
1904 |
1905 | // I tried reblocking this for better cache usage of encode_buffer
1906 | // (using x_outer, k, x_inner), but it lost speed. -- stb
1907 |
1908 | coefficient_counter = 0;
1909 | switch (channels) {
1910 | case 1:
1911 | for (k = n0; k <= n1; k++)
1912 | {
1913 | int coefficient_index = coefficient_counter++;
1914 | 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);
1915 | float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1916 | for (x = 0; x < output_w; ++x)
1917 | {
1918 | int in_pixel_index = x * 1;
1919 | encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
1920 | }
1921 | }
1922 | break;
1923 | case 2:
1924 | for (k = n0; k <= n1; k++)
1925 | {
1926 | int coefficient_index = coefficient_counter++;
1927 | 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);
1928 | float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1929 | for (x = 0; x < output_w; ++x)
1930 | {
1931 | int in_pixel_index = x * 2;
1932 | encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
1933 | encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient;
1934 | }
1935 | }
1936 | break;
1937 | case 3:
1938 | for (k = n0; k <= n1; k++)
1939 | {
1940 | int coefficient_index = coefficient_counter++;
1941 | 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);
1942 | float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1943 | for (x = 0; x < output_w; ++x)
1944 | {
1945 | int in_pixel_index = x * 3;
1946 | encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
1947 | encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient;
1948 | encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient;
1949 | }
1950 | }
1951 | break;
1952 | case 4:
1953 | for (k = n0; k <= n1; k++)
1954 | {
1955 | int coefficient_index = coefficient_counter++;
1956 | 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);
1957 | float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1958 | for (x = 0; x < output_w; ++x)
1959 | {
1960 | int in_pixel_index = x * 4;
1961 | encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient;
1962 | encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient;
1963 | encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient;
1964 | encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient;
1965 | }
1966 | }
1967 | break;
1968 | default:
1969 | for (k = n0; k <= n1; k++)
1970 | {
1971 | int coefficient_index = coefficient_counter++;
1972 | 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);
1973 | float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
1974 | for (x = 0; x < output_w; ++x)
1975 | {
1976 | int in_pixel_index = x * channels;
1977 | int c;
1978 | for (c = 0; c < channels; c++)
1979 | encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient;
1980 | }
1981 | }
1982 | break;
1983 | }
1984 | stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode);
1985 | }
1986 |
1987 | static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n)
1988 | {
1989 | int x, k;
1990 | int output_w = stbir_info->output_w;
1991 | stbir__contributors* vertical_contributors = stbir_info->vertical_contributors;
1992 | float* vertical_coefficients = stbir_info->vertical_coefficients;
1993 | int channels = stbir_info->channels;
1994 | int ring_buffer_entries = stbir_info->ring_buffer_num_entries;
1995 | float* horizontal_buffer = stbir_info->horizontal_buffer;
1996 | int coefficient_width = stbir_info->vertical_coefficient_width;
1997 | int contributor = n + stbir_info->vertical_filter_pixel_margin;
1998 |
1999 | float* ring_buffer = stbir_info->ring_buffer;
2000 | int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index;
2001 | int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline;
2002 | int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
2003 | int n0,n1;
2004 |
2005 | n0 = vertical_contributors[contributor].n0;
2006 | n1 = vertical_contributors[contributor].n1;
2007 |
2008 | STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
2009 |
2010 | for (k = n0; k <= n1; k++)
2011 | {
2012 | int coefficient_index = k - n0;
2013 | int coefficient_group = coefficient_width * contributor;
2014 | float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
2015 |
2016 | 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);
2017 |
2018 | switch (channels) {
2019 | case 1:
2020 | for (x = 0; x < output_w; x++)
2021 | {
2022 | int in_pixel_index = x * 1;
2023 | ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
2024 | }
2025 | break;
2026 | case 2:
2027 | for (x = 0; x < output_w; x++)
2028 | {
2029 | int in_pixel_index = x * 2;
2030 | ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
2031 | ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient;
2032 | }
2033 | break;
2034 | case 3:
2035 | for (x = 0; x < output_w; x++)
2036 | {
2037 | int in_pixel_index = x * 3;
2038 | ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
2039 | ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient;
2040 | ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient;
2041 | }
2042 | break;
2043 | case 4:
2044 | for (x = 0; x < output_w; x++)
2045 | {
2046 | int in_pixel_index = x * 4;
2047 | ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient;
2048 | ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient;
2049 | ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient;
2050 | ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient;
2051 | }
2052 | break;
2053 | default:
2054 | for (x = 0; x < output_w; x++)
2055 | {
2056 | int in_pixel_index = x * channels;
2057 |
2058 | int c;
2059 | for (c = 0; c < channels; c++)
2060 | ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient;
2061 | }
2062 | break;
2063 | }
2064 | }
2065 | }
2066 |
2067 | static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
2068 | {
2069 | int y;
2070 | float scale_ratio = stbir_info->vertical_scale;
2071 | float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio;
2072 |
2073 | STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
2074 |
2075 | for (y = 0; y < stbir_info->output_h; y++)
2076 | {
2077 | float in_center_of_out = 0; // Center of the current out scanline in the in scanline space
2078 | int in_first_scanline = 0, in_last_scanline = 0;
2079 |
2080 | 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);
2081 |
2082 | STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries);
2083 |
2084 | if (stbir_info->ring_buffer_begin_index >= 0)
2085 | {
2086 | // Get rid of whatever we don't need anymore.
2087 | while (in_first_scanline > stbir_info->ring_buffer_first_scanline)
2088 | {
2089 | if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline)
2090 | {
2091 | // We just popped the last scanline off the ring buffer.
2092 | // Reset it to the empty state.
2093 | stbir_info->ring_buffer_begin_index = -1;
2094 | stbir_info->ring_buffer_first_scanline = 0;
2095 | stbir_info->ring_buffer_last_scanline = 0;
2096 | break;
2097 | }
2098 | else
2099 | {
2100 | stbir_info->ring_buffer_first_scanline++;
2101 | stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries;
2102 | }
2103 | }
2104 | }
2105 |
2106 | // Load in new ones.
2107 | if (stbir_info->ring_buffer_begin_index < 0)
2108 | stbir__decode_and_resample_upsample(stbir_info, in_first_scanline);
2109 |
2110 | while (in_last_scanline > stbir_info->ring_buffer_last_scanline)
2111 | stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1);
2112 |
2113 | // Now all buffers should be ready to write a row of vertical sampling.
2114 | stbir__resample_vertical_upsample(stbir_info, y);
2115 |
2116 | STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h);
2117 | }
2118 | }
2119 |
2120 | static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline)
2121 | {
2122 | int output_stride_bytes = stbir_info->output_stride_bytes;
2123 | int channels = stbir_info->channels;
2124 | int alpha_channel = stbir_info->alpha_channel;
2125 | int type = stbir_info->type;
2126 | int colorspace = stbir_info->colorspace;
2127 | int output_w = stbir_info->output_w;
2128 | void* output_data = stbir_info->output_data;
2129 | int decode = STBIR__DECODE(type, colorspace);
2130 |
2131 | float* ring_buffer = stbir_info->ring_buffer;
2132 | int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
2133 |
2134 | if (stbir_info->ring_buffer_begin_index >= 0)
2135 | {
2136 | // Get rid of whatever we don't need anymore.
2137 | while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline)
2138 | {
2139 | if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h)
2140 | {
2141 | int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes;
2142 | float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length);
2143 | stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode);
2144 | STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h);
2145 | }
2146 |
2147 | if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline)
2148 | {
2149 | // We just popped the last scanline off the ring buffer.
2150 | // Reset it to the empty state.
2151 | stbir_info->ring_buffer_begin_index = -1;
2152 | stbir_info->ring_buffer_first_scanline = 0;
2153 | stbir_info->ring_buffer_last_scanline = 0;
2154 | break;
2155 | }
2156 | else
2157 | {
2158 | stbir_info->ring_buffer_first_scanline++;
2159 | stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries;
2160 | }
2161 | }
2162 | }
2163 | }
2164 |
2165 | static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
2166 | {
2167 | int y;
2168 | float scale_ratio = stbir_info->vertical_scale;
2169 | int output_h = stbir_info->output_h;
2170 | float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio;
2171 | int pixel_margin = stbir_info->vertical_filter_pixel_margin;
2172 | int max_y = stbir_info->input_h + pixel_margin;
2173 |
2174 | STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
2175 |
2176 | for (y = -pixel_margin; y < max_y; y++)
2177 | {
2178 | float out_center_of_in; // Center of the current out scanline in the in scanline space
2179 | int out_first_scanline, out_last_scanline;
2180 |
2181 | 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);
2182 |
2183 | STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries);
2184 |
2185 | if (out_last_scanline < 0 || out_first_scanline >= output_h)
2186 | continue;
2187 |
2188 | stbir__empty_ring_buffer(stbir_info, out_first_scanline);
2189 |
2190 | stbir__decode_and_resample_downsample(stbir_info, y);
2191 |
2192 | // Load in new ones.
2193 | if (stbir_info->ring_buffer_begin_index < 0)
2194 | stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline);
2195 |
2196 | while (out_last_scanline > stbir_info->ring_buffer_last_scanline)
2197 | stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1);
2198 |
2199 | // Now the horizontal buffer is ready to write to all ring buffer rows.
2200 | stbir__resample_vertical_downsample(stbir_info, y);
2201 | }
2202 |
2203 | stbir__empty_ring_buffer(stbir_info, stbir_info->output_h);
2204 | }
2205 |
2206 | static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels)
2207 | {
2208 | info->input_w = input_w;
2209 | info->input_h = input_h;
2210 | info->output_w = output_w;
2211 | info->output_h = output_h;
2212 | info->channels = channels;
2213 | }
2214 |
2215 | static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform)
2216 | {
2217 | info->s0 = s0;
2218 | info->t0 = t0;
2219 | info->s1 = s1;
2220 | info->t1 = t1;
2221 |
2222 | if (transform)
2223 | {
2224 | info->horizontal_scale = transform[0];
2225 | info->vertical_scale = transform[1];
2226 | info->horizontal_shift = transform[2];
2227 | info->vertical_shift = transform[3];
2228 | }
2229 | else
2230 | {
2231 | info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0);
2232 | info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0);
2233 |
2234 | info->horizontal_shift = s0 * info->output_w / (s1 - s0);
2235 | info->vertical_shift = t0 * info->output_h / (t1 - t0);
2236 | }
2237 | }
2238 |
2239 | static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter)
2240 | {
2241 | if (h_filter == 0)
2242 | h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE;
2243 | if (v_filter == 0)
2244 | v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE;
2245 | info->horizontal_filter = h_filter;
2246 | info->vertical_filter = v_filter;
2247 | }
2248 |
2249 | static stbir_uint32 stbir__calculate_memory(stbir__info *info)
2250 | {
2251 | int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale);
2252 | int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale);
2253 |
2254 | info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w);
2255 | info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h);
2256 |
2257 | // One extra entry because floating point precision problems sometimes cause an extra to be necessary.
2258 | info->ring_buffer_num_entries = filter_height + 1;
2259 |
2260 | info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors);
2261 | info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float);
2262 | info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors);
2263 | info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float);
2264 | info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float);
2265 | info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float);
2266 | info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float);
2267 | info->encode_buffer_size = info->output_w * info->channels * sizeof(float);
2268 |
2269 | STBIR_ASSERT(info->horizontal_filter != 0);
2270 | STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late
2271 | STBIR_ASSERT(info->vertical_filter != 0);
2272 | STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late
2273 |
2274 | if (stbir__use_height_upsampling(info))
2275 | // The horizontal buffer is for when we're downsampling the height and we
2276 | // can't output the result of sampling the decode buffer directly into the
2277 | // ring buffers.
2278 | info->horizontal_buffer_size = 0;
2279 | else
2280 | // The encode buffer is to retain precision in the height upsampling method
2281 | // and isn't used when height downsampling.
2282 | info->encode_buffer_size = 0;
2283 |
2284 | return info->horizontal_contributors_size + info->horizontal_coefficients_size
2285 | + info->vertical_contributors_size + info->vertical_coefficients_size
2286 | + info->decode_buffer_size + info->horizontal_buffer_size
2287 | + info->ring_buffer_size + info->encode_buffer_size;
2288 | }
2289 |
2290 | static int stbir__resize_allocated(stbir__info *info,
2291 | const void* input_data, int input_stride_in_bytes,
2292 | void* output_data, int output_stride_in_bytes,
2293 | int alpha_channel, stbir_uint32 flags, stbir_datatype type,
2294 | stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace,
2295 | void* tempmem, size_t tempmem_size_in_bytes)
2296 | {
2297 | size_t memory_required = stbir__calculate_memory(info);
2298 |
2299 | int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type];
2300 | int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type];
2301 |
2302 | #ifdef STBIR_DEBUG_OVERWRITE_TEST
2303 | #define OVERWRITE_ARRAY_SIZE 8
2304 | unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE];
2305 | unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE];
2306 | unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE];
2307 | unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE];
2308 |
2309 | size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type];
2310 | memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE);
2311 | memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE);
2312 | memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE);
2313 | memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE);
2314 | #endif
2315 |
2316 | STBIR_ASSERT(info->channels >= 0);
2317 | STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS);
2318 |
2319 | if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS)
2320 | return 0;
2321 |
2322 | STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
2323 | STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
2324 |
2325 | if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table))
2326 | return 0;
2327 | if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table))
2328 | return 0;
2329 |
2330 | if (alpha_channel < 0)
2331 | flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED;
2332 |
2333 | if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) {
2334 | STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels);
2335 | }
2336 |
2337 | if (alpha_channel >= info->channels)
2338 | return 0;
2339 |
2340 | STBIR_ASSERT(tempmem);
2341 |
2342 | if (!tempmem)
2343 | return 0;
2344 |
2345 | STBIR_ASSERT(tempmem_size_in_bytes >= memory_required);
2346 |
2347 | if (tempmem_size_in_bytes < memory_required)
2348 | return 0;
2349 |
2350 | memset(tempmem, 0, tempmem_size_in_bytes);
2351 |
2352 | info->input_data = input_data;
2353 | info->input_stride_bytes = width_stride_input;
2354 |
2355 | info->output_data = output_data;
2356 | info->output_stride_bytes = width_stride_output;
2357 |
2358 | info->alpha_channel = alpha_channel;
2359 | info->flags = flags;
2360 | info->type = type;
2361 | info->edge_horizontal = edge_horizontal;
2362 | info->edge_vertical = edge_vertical;
2363 | info->colorspace = colorspace;
2364 |
2365 | info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale);
2366 | info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale );
2367 | info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale);
2368 | info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale );
2369 | info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale);
2370 | info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale );
2371 |
2372 | info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float);
2373 | info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2;
2374 |
2375 | #define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size)
2376 |
2377 | info->horizontal_contributors = (stbir__contributors *) tempmem;
2378 | info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float);
2379 | info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors);
2380 | info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float);
2381 | info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float);
2382 |
2383 | if (stbir__use_height_upsampling(info))
2384 | {
2385 | info->horizontal_buffer = NULL;
2386 | info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float);
2387 | info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float);
2388 |
2389 | STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
2390 | }
2391 | else
2392 | {
2393 | info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float);
2394 | info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float);
2395 | info->encode_buffer = NULL;
2396 |
2397 | STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
2398 | }
2399 |
2400 | #undef STBIR__NEXT_MEMPTR
2401 |
2402 | // This signals that the ring buffer is empty
2403 | info->ring_buffer_begin_index = -1;
2404 |
2405 | stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w);
2406 | stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h);
2407 |
2408 | STBIR_PROGRESS_REPORT(0);
2409 |
2410 | if (stbir__use_height_upsampling(info))
2411 | stbir__buffer_loop_upsample(info);
2412 | else
2413 | stbir__buffer_loop_downsample(info);
2414 |
2415 | STBIR_PROGRESS_REPORT(1);
2416 |
2417 | #ifdef STBIR_DEBUG_OVERWRITE_TEST
2418 | STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
2419 | STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0);
2420 | STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
2421 | STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0);
2422 | #endif
2423 |
2424 | return 1;
2425 | }
2426 |
2427 |
2428 | static int stbir__resize_arbitrary(
2429 | void *alloc_context,
2430 | const void* input_data, int input_w, int input_h, int input_stride_in_bytes,
2431 | void* output_data, int output_w, int output_h, int output_stride_in_bytes,
2432 | float s0, float t0, float s1, float t1, float *transform,
2433 | int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type,
2434 | stbir_filter h_filter, stbir_filter v_filter,
2435 | stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace)
2436 | {
2437 | stbir__info info;
2438 | int result;
2439 | size_t memory_required;
2440 | void* extra_memory;
2441 |
2442 | stbir__setup(&info, input_w, input_h, output_w, output_h, channels);
2443 | stbir__calculate_transform(&info, s0,t0,s1,t1,transform);
2444 | stbir__choose_filter(&info, h_filter, v_filter);
2445 | memory_required = stbir__calculate_memory(&info);
2446 | extra_memory = STBIR_MALLOC(memory_required, alloc_context);
2447 |
2448 | if (!extra_memory)
2449 | return 0;
2450 |
2451 | result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes,
2452 | output_data, output_stride_in_bytes,
2453 | alpha_channel, flags, type,
2454 | edge_horizontal, edge_vertical,
2455 | colorspace, extra_memory, memory_required);
2456 |
2457 | STBIR_FREE(extra_memory, alloc_context);
2458 |
2459 | return result;
2460 | }
2461 |
2462 | STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2463 | unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2464 | int num_channels)
2465 | {
2466 | return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
2467 | output_pixels, output_w, output_h, output_stride_in_bytes,
2468 | 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
2469 | STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR);
2470 | }
2471 |
2472 | STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2473 | float *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2474 | int num_channels)
2475 | {
2476 | return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
2477 | output_pixels, output_w, output_h, output_stride_in_bytes,
2478 | 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
2479 | STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR);
2480 | }
2481 |
2482 | STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2483 | unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2484 | int num_channels, int alpha_channel, int flags)
2485 | {
2486 | return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
2487 | output_pixels, output_w, output_h, output_stride_in_bytes,
2488 | 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
2489 | STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB);
2490 | }
2491 |
2492 | STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2493 | unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2494 | int num_channels, int alpha_channel, int flags,
2495 | stbir_edge edge_wrap_mode)
2496 | {
2497 | return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes,
2498 | output_pixels, output_w, output_h, output_stride_in_bytes,
2499 | 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
2500 | edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB);
2501 | }
2502 |
2503 | STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2504 | unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2505 | int num_channels, int alpha_channel, int flags,
2506 | stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
2507 | void *alloc_context)
2508 | {
2509 | return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2510 | output_pixels, output_w, output_h, output_stride_in_bytes,
2511 | 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter,
2512 | edge_wrap_mode, edge_wrap_mode, space);
2513 | }
2514 |
2515 | STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2516 | stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
2517 | int num_channels, int alpha_channel, int flags,
2518 | stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
2519 | void *alloc_context)
2520 | {
2521 | return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2522 | output_pixels, output_w, output_h, output_stride_in_bytes,
2523 | 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter,
2524 | edge_wrap_mode, edge_wrap_mode, space);
2525 | }
2526 |
2527 |
2528 | STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2529 | float *output_pixels , int output_w, int output_h, int output_stride_in_bytes,
2530 | int num_channels, int alpha_channel, int flags,
2531 | stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space,
2532 | void *alloc_context)
2533 | {
2534 | return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2535 | output_pixels, output_w, output_h, output_stride_in_bytes,
2536 | 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter,
2537 | edge_wrap_mode, edge_wrap_mode, space);
2538 | }
2539 |
2540 |
2541 | STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2542 | void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2543 | stbir_datatype datatype,
2544 | int num_channels, int alpha_channel, int flags,
2545 | stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
2546 | stbir_filter filter_horizontal, stbir_filter filter_vertical,
2547 | stbir_colorspace space, void *alloc_context)
2548 | {
2549 | return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2550 | output_pixels, output_w, output_h, output_stride_in_bytes,
2551 | 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical,
2552 | edge_mode_horizontal, edge_mode_vertical, space);
2553 | }
2554 |
2555 |
2556 | STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2557 | void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2558 | stbir_datatype datatype,
2559 | int num_channels, int alpha_channel, int flags,
2560 | stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
2561 | stbir_filter filter_horizontal, stbir_filter filter_vertical,
2562 | stbir_colorspace space, void *alloc_context,
2563 | float x_scale, float y_scale,
2564 | float x_offset, float y_offset)
2565 | {
2566 | float transform[4];
2567 | transform[0] = x_scale;
2568 | transform[1] = y_scale;
2569 | transform[2] = x_offset;
2570 | transform[3] = y_offset;
2571 | return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2572 | output_pixels, output_w, output_h, output_stride_in_bytes,
2573 | 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical,
2574 | edge_mode_horizontal, edge_mode_vertical, space);
2575 | }
2576 |
2577 | STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes,
2578 | void *output_pixels, int output_w, int output_h, int output_stride_in_bytes,
2579 | stbir_datatype datatype,
2580 | int num_channels, int alpha_channel, int flags,
2581 | stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical,
2582 | stbir_filter filter_horizontal, stbir_filter filter_vertical,
2583 | stbir_colorspace space, void *alloc_context,
2584 | float s0, float t0, float s1, float t1)
2585 | {
2586 | return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes,
2587 | output_pixels, output_w, output_h, output_stride_in_bytes,
2588 | s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical,
2589 | edge_mode_horizontal, edge_mode_vertical, space);
2590 | }
2591 |
2592 | #endif // STB_IMAGE_RESIZE_IMPLEMENTATION
2593 |
2594 | /*
2595 | ------------------------------------------------------------------------------
2596 | This software is available under 2 licenses -- choose whichever you prefer.
2597 | ------------------------------------------------------------------------------
2598 | ALTERNATIVE A - MIT License
2599 | Copyright (c) 2017 Sean Barrett
2600 | Permission is hereby granted, free of charge, to any person obtaining a copy of
2601 | this software and associated documentation files (the "Software"), to deal in
2602 | the Software without restriction, including without limitation the rights to
2603 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
2604 | of the Software, and to permit persons to whom the Software is furnished to do
2605 | so, subject to the following conditions:
2606 | The above copyright notice and this permission notice shall be included in all
2607 | copies or substantial portions of the Software.
2608 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2609 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2610 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2611 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2612 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2613 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2614 | SOFTWARE.
2615 | ------------------------------------------------------------------------------
2616 | ALTERNATIVE B - Public Domain (www.unlicense.org)
2617 | This is free and unencumbered software released into the public domain.
2618 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
2619 | software, either in source code form or as a compiled binary, for any purpose,
2620 | commercial or non-commercial, and by any means.
2621 | In jurisdictions that recognize copyright laws, the author or authors of this
2622 | software dedicate any and all copyright interest in the software to the public
2623 | domain. We make this dedication for the benefit of the public at large and to
2624 | the detriment of our heirs and successors. We intend this dedication to be an
2625 | overt act of relinquishment in perpetuity of all present and future rights to
2626 | this software under copyright law.
2627 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2628 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2629 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2630 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2631 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2632 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2633 | ------------------------------------------------------------------------------
2634 | */
2635 |
--------------------------------------------------------------------------------
/stb_image/stb_image_write.h:
--------------------------------------------------------------------------------
1 | /* stb_image_write - v1.16 - public domain - http://nothings.org/stb
2 | writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015
3 | no warranty implied; use at your own risk
4 |
5 | Before #including,
6 |
7 | #define STB_IMAGE_WRITE_IMPLEMENTATION
8 |
9 | in the file that you want to have the implementation.
10 |
11 | Will probably not work correctly with strict-aliasing optimizations.
12 |
13 | ABOUT:
14 |
15 | This header file is a library for writing images to C stdio or a callback.
16 |
17 | The PNG output is not optimal; it is 20-50% larger than the file
18 | written by a decent optimizing implementation; though providing a custom
19 | zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that.
20 | This library is designed for source code compactness and simplicity,
21 | not optimal image file size or run-time performance.
22 |
23 | BUILDING:
24 |
25 | You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.
26 | You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace
27 | malloc,realloc,free.
28 | You can #define STBIW_MEMMOVE() to replace memmove()
29 | You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function
30 | for PNG compression (instead of the builtin one), it must have the following signature:
31 | unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality);
32 | The returned data will be freed with STBIW_FREE() (free() by default),
33 | so it must be heap allocated with STBIW_MALLOC() (malloc() by default),
34 |
35 | UNICODE:
36 |
37 | If compiling for Windows and you wish to use Unicode filenames, compile
38 | with
39 | #define STBIW_WINDOWS_UTF8
40 | and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert
41 | Windows wchar_t filenames to utf8.
42 |
43 | USAGE:
44 |
45 | There are five functions, one for each image file format:
46 |
47 | int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
48 | int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
49 | int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
50 | int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality);
51 | int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
52 |
53 | void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically
54 |
55 | There are also five equivalent functions that use an arbitrary write function. You are
56 | expected to open/close your file-equivalent before and after calling these:
57 |
58 | int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
59 | int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
60 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
61 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
62 | int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);
63 |
64 | where the callback is:
65 | void stbi_write_func(void *context, void *data, int size);
66 |
67 | You can configure it with these global variables:
68 | int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE
69 | int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression
70 | int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode
71 |
72 |
73 | You can define STBI_WRITE_NO_STDIO to disable the file variant of these
74 | functions, so the library will not use stdio.h at all. However, this will
75 | also disable HDR writing, because it requires stdio for formatted output.
76 |
77 | Each function returns 0 on failure and non-0 on success.
78 |
79 | The functions create an image file defined by the parameters. The image
80 | is a rectangle of pixels stored from left-to-right, top-to-bottom.
81 | Each pixel contains 'comp' channels of data stored interleaved with 8-bits
82 | per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
83 | monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
84 | The *data pointer points to the first byte of the top-left-most pixel.
85 | For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
86 | a row of pixels to the first byte of the next row of pixels.
87 |
88 | PNG creates output files with the same number of components as the input.
89 | The BMP format expands Y to RGB in the file format and does not
90 | output alpha.
91 |
92 | PNG supports writing rectangles of data even when the bytes storing rows of
93 | data are not consecutive in memory (e.g. sub-rectangles of a larger image),
94 | by supplying the stride between the beginning of adjacent rows. The other
95 | formats do not. (Thus you cannot write a native-format BMP through the BMP
96 | writer, both because it is in BGR order and because it may have padding
97 | at the end of the line.)
98 |
99 | PNG allows you to set the deflate compression level by setting the global
100 | variable 'stbi_write_png_compression_level' (it defaults to 8).
101 |
102 | HDR expects linear float data. Since the format is always 32-bit rgb(e)
103 | data, alpha (if provided) is discarded, and for monochrome data it is
104 | replicated across all three channels.
105 |
106 | TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed
107 | data, set the global variable 'stbi_write_tga_with_rle' to 0.
108 |
109 | JPEG does ignore alpha channels in input data; quality is between 1 and 100.
110 | Higher quality looks better but results in a bigger image.
111 | JPEG baseline (no JPEG progressive).
112 |
113 | CREDITS:
114 |
115 |
116 | Sean Barrett - PNG/BMP/TGA
117 | Baldur Karlsson - HDR
118 | Jean-Sebastien Guay - TGA monochrome
119 | Tim Kelsey - misc enhancements
120 | Alan Hickman - TGA RLE
121 | Emmanuel Julien - initial file IO callback implementation
122 | Jon Olick - original jo_jpeg.cpp code
123 | Daniel Gibson - integrate JPEG, allow external zlib
124 | Aarni Koskela - allow choosing PNG filter
125 |
126 | bugfixes:
127 | github:Chribba
128 | Guillaume Chereau
129 | github:jry2
130 | github:romigrou
131 | Sergio Gonzalez
132 | Jonas Karlsson
133 | Filip Wasil
134 | Thatcher Ulrich
135 | github:poppolopoppo
136 | Patrick Boettcher
137 | github:xeekworx
138 | Cap Petschulat
139 | Simon Rodriguez
140 | Ivan Tikhonov
141 | github:ignotion
142 | Adam Schackart
143 | Andrew Kensler
144 |
145 | LICENSE
146 |
147 | See end of file for license information.
148 |
149 | */
150 |
151 | #ifndef INCLUDE_STB_IMAGE_WRITE_H
152 | #define INCLUDE_STB_IMAGE_WRITE_H
153 |
154 | #include
155 |
156 | // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline'
157 | #ifndef STBIWDEF
158 | #ifdef STB_IMAGE_WRITE_STATIC
159 | #define STBIWDEF static
160 | #else
161 | #ifdef __cplusplus
162 | #define STBIWDEF extern "C"
163 | #else
164 | #define STBIWDEF extern
165 | #endif
166 | #endif
167 | #endif
168 |
169 | #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations
170 | STBIWDEF int stbi_write_tga_with_rle;
171 | STBIWDEF int stbi_write_png_compression_level;
172 | STBIWDEF int stbi_write_force_png_filter;
173 | #endif
174 |
175 | #ifndef STBI_WRITE_NO_STDIO
176 | STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
177 | STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
178 | STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
179 | STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
180 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality);
181 |
182 | #ifdef STBIW_WINDOWS_UTF8
183 | STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
184 | #endif
185 | #endif
186 |
187 | typedef void stbi_write_func(void *context, void *data, int size);
188 |
189 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
190 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
191 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
192 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
193 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);
194 |
195 | STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);
196 |
197 | #endif//INCLUDE_STB_IMAGE_WRITE_H
198 |
199 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION
200 |
201 | #ifdef _WIN32
202 | #ifndef _CRT_SECURE_NO_WARNINGS
203 | #define _CRT_SECURE_NO_WARNINGS
204 | #endif
205 | #ifndef _CRT_NONSTDC_NO_DEPRECATE
206 | #define _CRT_NONSTDC_NO_DEPRECATE
207 | #endif
208 | #endif
209 |
210 | #ifndef STBI_WRITE_NO_STDIO
211 | #include
212 | #endif // STBI_WRITE_NO_STDIO
213 |
214 | #include
215 | #include
216 | #include
217 | #include
218 |
219 | #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))
220 | // ok
221 | #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)
222 | // ok
223 | #else
224 | #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)."
225 | #endif
226 |
227 | #ifndef STBIW_MALLOC
228 | #define STBIW_MALLOC(sz) malloc(sz)
229 | #define STBIW_REALLOC(p,newsz) realloc(p,newsz)
230 | #define STBIW_FREE(p) free(p)
231 | #endif
232 |
233 | #ifndef STBIW_REALLOC_SIZED
234 | #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)
235 | #endif
236 |
237 |
238 | #ifndef STBIW_MEMMOVE
239 | #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)
240 | #endif
241 |
242 |
243 | #ifndef STBIW_ASSERT
244 | #include
245 | #define STBIW_ASSERT(x) assert(x)
246 | #endif
247 |
248 | #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)
249 |
250 | #ifdef STB_IMAGE_WRITE_STATIC
251 | static int stbi_write_png_compression_level = 8;
252 | static int stbi_write_tga_with_rle = 1;
253 | static int stbi_write_force_png_filter = -1;
254 | #else
255 | int stbi_write_png_compression_level = 8;
256 | int stbi_write_tga_with_rle = 1;
257 | int stbi_write_force_png_filter = -1;
258 | #endif
259 |
260 | static int stbi__flip_vertically_on_write = 0;
261 |
262 | STBIWDEF void stbi_flip_vertically_on_write(int flag)
263 | {
264 | stbi__flip_vertically_on_write = flag;
265 | }
266 |
267 | typedef struct
268 | {
269 | stbi_write_func *func;
270 | void *context;
271 | unsigned char buffer[64];
272 | int buf_used;
273 | } stbi__write_context;
274 |
275 | // initialize a callback-based context
276 | static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context)
277 | {
278 | s->func = c;
279 | s->context = context;
280 | }
281 |
282 | #ifndef STBI_WRITE_NO_STDIO
283 |
284 | static void stbi__stdio_write(void *context, void *data, int size)
285 | {
286 | fwrite(data,1,size,(FILE*) context);
287 | }
288 |
289 | #if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8)
290 | #ifdef __cplusplus
291 | #define STBIW_EXTERN extern "C"
292 | #else
293 | #define STBIW_EXTERN extern
294 | #endif
295 | STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
296 | STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
297 |
298 | STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
299 | {
300 | return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
301 | }
302 | #endif
303 |
304 | static FILE *stbiw__fopen(char const *filename, char const *mode)
305 | {
306 | FILE *f;
307 | #if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8)
308 | wchar_t wMode[64];
309 | wchar_t wFilename[1024];
310 | if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
311 | return 0;
312 |
313 | if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
314 | return 0;
315 |
316 | #if defined(_MSC_VER) && _MSC_VER >= 1400
317 | if (0 != _wfopen_s(&f, wFilename, wMode))
318 | f = 0;
319 | #else
320 | f = _wfopen(wFilename, wMode);
321 | #endif
322 |
323 | #elif defined(_MSC_VER) && _MSC_VER >= 1400
324 | if (0 != fopen_s(&f, filename, mode))
325 | f=0;
326 | #else
327 | f = fopen(filename, mode);
328 | #endif
329 | return f;
330 | }
331 |
332 | static int stbi__start_write_file(stbi__write_context *s, const char *filename)
333 | {
334 | FILE *f = stbiw__fopen(filename, "wb");
335 | stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);
336 | return f != NULL;
337 | }
338 |
339 | static void stbi__end_write_file(stbi__write_context *s)
340 | {
341 | fclose((FILE *)s->context);
342 | }
343 |
344 | #endif // !STBI_WRITE_NO_STDIO
345 |
346 | typedef unsigned int stbiw_uint32;
347 | typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
348 |
349 | static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)
350 | {
351 | while (*fmt) {
352 | switch (*fmt++) {
353 | case ' ': break;
354 | case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));
355 | s->func(s->context,&x,1);
356 | break; }
357 | case '2': { int x = va_arg(v,int);
358 | unsigned char b[2];
359 | b[0] = STBIW_UCHAR(x);
360 | b[1] = STBIW_UCHAR(x>>8);
361 | s->func(s->context,b,2);
362 | break; }
363 | case '4': { stbiw_uint32 x = va_arg(v,int);
364 | unsigned char b[4];
365 | b[0]=STBIW_UCHAR(x);
366 | b[1]=STBIW_UCHAR(x>>8);
367 | b[2]=STBIW_UCHAR(x>>16);
368 | b[3]=STBIW_UCHAR(x>>24);
369 | s->func(s->context,b,4);
370 | break; }
371 | default:
372 | STBIW_ASSERT(0);
373 | return;
374 | }
375 | }
376 | }
377 |
378 | static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
379 | {
380 | va_list v;
381 | va_start(v, fmt);
382 | stbiw__writefv(s, fmt, v);
383 | va_end(v);
384 | }
385 |
386 | static void stbiw__write_flush(stbi__write_context *s)
387 | {
388 | if (s->buf_used) {
389 | s->func(s->context, &s->buffer, s->buf_used);
390 | s->buf_used = 0;
391 | }
392 | }
393 |
394 | static void stbiw__putc(stbi__write_context *s, unsigned char c)
395 | {
396 | s->func(s->context, &c, 1);
397 | }
398 |
399 | static void stbiw__write1(stbi__write_context *s, unsigned char a)
400 | {
401 | if ((size_t)s->buf_used + 1 > sizeof(s->buffer))
402 | stbiw__write_flush(s);
403 | s->buffer[s->buf_used++] = a;
404 | }
405 |
406 | static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)
407 | {
408 | int n;
409 | if ((size_t)s->buf_used + 3 > sizeof(s->buffer))
410 | stbiw__write_flush(s);
411 | n = s->buf_used;
412 | s->buf_used = n+3;
413 | s->buffer[n+0] = a;
414 | s->buffer[n+1] = b;
415 | s->buffer[n+2] = c;
416 | }
417 |
418 | static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)
419 | {
420 | unsigned char bg[3] = { 255, 0, 255}, px[3];
421 | int k;
422 |
423 | if (write_alpha < 0)
424 | stbiw__write1(s, d[comp - 1]);
425 |
426 | switch (comp) {
427 | case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case
428 | case 1:
429 | if (expand_mono)
430 | stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
431 | else
432 | stbiw__write1(s, d[0]); // monochrome TGA
433 | break;
434 | case 4:
435 | if (!write_alpha) {
436 | // composite against pink background
437 | for (k = 0; k < 3; ++k)
438 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
439 | stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
440 | break;
441 | }
442 | /* FALLTHROUGH */
443 | case 3:
444 | stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
445 | break;
446 | }
447 | if (write_alpha > 0)
448 | stbiw__write1(s, d[comp - 1]);
449 | }
450 |
451 | static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)
452 | {
453 | stbiw_uint32 zero = 0;
454 | int i,j, j_end;
455 |
456 | if (y <= 0)
457 | return;
458 |
459 | if (stbi__flip_vertically_on_write)
460 | vdir *= -1;
461 |
462 | if (vdir < 0) {
463 | j_end = -1; j = y-1;
464 | } else {
465 | j_end = y; j = 0;
466 | }
467 |
468 | for (; j != j_end; j += vdir) {
469 | for (i=0; i < x; ++i) {
470 | unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
471 | stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
472 | }
473 | stbiw__write_flush(s);
474 | s->func(s->context, &zero, scanline_pad);
475 | }
476 | }
477 |
478 | static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)
479 | {
480 | if (y < 0 || x < 0) {
481 | return 0;
482 | } else {
483 | va_list v;
484 | va_start(v, fmt);
485 | stbiw__writefv(s, fmt, v);
486 | va_end(v);
487 | stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);
488 | return 1;
489 | }
490 | }
491 |
492 | static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)
493 | {
494 | if (comp != 4) {
495 | // write RGB bitmap
496 | int pad = (-x*3) & 3;
497 | return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,
498 | "11 4 22 4" "4 44 22 444444",
499 | 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
500 | 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
501 | } else {
502 | // RGBA bitmaps need a v4 header
503 | // use BI_BITFIELDS mode with 32bpp and alpha mask
504 | // (straight BI_RGB with alpha mask doesn't work in most readers)
505 | return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0,
506 | "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444",
507 | 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header
508 | 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header
509 | }
510 | }
511 |
512 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
513 | {
514 | stbi__write_context s = { 0 };
515 | stbi__start_write_callbacks(&s, func, context);
516 | return stbi_write_bmp_core(&s, x, y, comp, data);
517 | }
518 |
519 | #ifndef STBI_WRITE_NO_STDIO
520 | STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
521 | {
522 | stbi__write_context s = { 0 };
523 | if (stbi__start_write_file(&s,filename)) {
524 | int r = stbi_write_bmp_core(&s, x, y, comp, data);
525 | stbi__end_write_file(&s);
526 | return r;
527 | } else
528 | return 0;
529 | }
530 | #endif //!STBI_WRITE_NO_STDIO
531 |
532 | static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data)
533 | {
534 | int has_alpha = (comp == 2 || comp == 4);
535 | int colorbytes = has_alpha ? comp-1 : comp;
536 | int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
537 |
538 | if (y < 0 || x < 0)
539 | return 0;
540 |
541 | if (!stbi_write_tga_with_rle) {
542 | return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,
543 | "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
544 | } else {
545 | int i,j,k;
546 | int jend, jdir;
547 |
548 | stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);
549 |
550 | if (stbi__flip_vertically_on_write) {
551 | j = 0;
552 | jend = y;
553 | jdir = 1;
554 | } else {
555 | j = y-1;
556 | jend = -1;
557 | jdir = -1;
558 | }
559 | for (; j != jend; j += jdir) {
560 | unsigned char *row = (unsigned char *) data + j * x * comp;
561 | int len;
562 |
563 | for (i = 0; i < x; i += len) {
564 | unsigned char *begin = row + i * comp;
565 | int diff = 1;
566 | len = 1;
567 |
568 | if (i < x - 1) {
569 | ++len;
570 | diff = memcmp(begin, row + (i + 1) * comp, comp);
571 | if (diff) {
572 | const unsigned char *prev = begin;
573 | for (k = i + 2; k < x && len < 128; ++k) {
574 | if (memcmp(prev, row + k * comp, comp)) {
575 | prev += comp;
576 | ++len;
577 | } else {
578 | --len;
579 | break;
580 | }
581 | }
582 | } else {
583 | for (k = i + 2; k < x && len < 128; ++k) {
584 | if (!memcmp(begin, row + k * comp, comp)) {
585 | ++len;
586 | } else {
587 | break;
588 | }
589 | }
590 | }
591 | }
592 |
593 | if (diff) {
594 | unsigned char header = STBIW_UCHAR(len - 1);
595 | stbiw__write1(s, header);
596 | for (k = 0; k < len; ++k) {
597 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
598 | }
599 | } else {
600 | unsigned char header = STBIW_UCHAR(len - 129);
601 | stbiw__write1(s, header);
602 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
603 | }
604 | }
605 | }
606 | stbiw__write_flush(s);
607 | }
608 | return 1;
609 | }
610 |
611 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
612 | {
613 | stbi__write_context s = { 0 };
614 | stbi__start_write_callbacks(&s, func, context);
615 | return stbi_write_tga_core(&s, x, y, comp, (void *) data);
616 | }
617 |
618 | #ifndef STBI_WRITE_NO_STDIO
619 | STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
620 | {
621 | stbi__write_context s = { 0 };
622 | if (stbi__start_write_file(&s,filename)) {
623 | int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);
624 | stbi__end_write_file(&s);
625 | return r;
626 | } else
627 | return 0;
628 | }
629 | #endif
630 |
631 | // *************************************************************************************************
632 | // Radiance RGBE HDR writer
633 | // by Baldur Karlsson
634 |
635 | #define stbiw__max(a, b) ((a) > (b) ? (a) : (b))
636 |
637 | #ifndef STBI_WRITE_NO_STDIO
638 |
639 | static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
640 | {
641 | int exponent;
642 | float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));
643 |
644 | if (maxcomp < 1e-32f) {
645 | rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
646 | } else {
647 | float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;
648 |
649 | rgbe[0] = (unsigned char)(linear[0] * normalize);
650 | rgbe[1] = (unsigned char)(linear[1] * normalize);
651 | rgbe[2] = (unsigned char)(linear[2] * normalize);
652 | rgbe[3] = (unsigned char)(exponent + 128);
653 | }
654 | }
655 |
656 | static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)
657 | {
658 | unsigned char lengthbyte = STBIW_UCHAR(length+128);
659 | STBIW_ASSERT(length+128 <= 255);
660 | s->func(s->context, &lengthbyte, 1);
661 | s->func(s->context, &databyte, 1);
662 | }
663 |
664 | static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)
665 | {
666 | unsigned char lengthbyte = STBIW_UCHAR(length);
667 | STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
668 | s->func(s->context, &lengthbyte, 1);
669 | s->func(s->context, data, length);
670 | }
671 |
672 | static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)
673 | {
674 | unsigned char scanlineheader[4] = { 2, 2, 0, 0 };
675 | unsigned char rgbe[4];
676 | float linear[3];
677 | int x;
678 |
679 | scanlineheader[2] = (width&0xff00)>>8;
680 | scanlineheader[3] = (width&0x00ff);
681 |
682 | /* skip RLE for images too small or large */
683 | if (width < 8 || width >= 32768) {
684 | for (x=0; x < width; x++) {
685 | switch (ncomp) {
686 | case 4: /* fallthrough */
687 | case 3: linear[2] = scanline[x*ncomp + 2];
688 | linear[1] = scanline[x*ncomp + 1];
689 | linear[0] = scanline[x*ncomp + 0];
690 | break;
691 | default:
692 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
693 | break;
694 | }
695 | stbiw__linear_to_rgbe(rgbe, linear);
696 | s->func(s->context, rgbe, 4);
697 | }
698 | } else {
699 | int c,r;
700 | /* encode into scratch buffer */
701 | for (x=0; x < width; x++) {
702 | switch(ncomp) {
703 | case 4: /* fallthrough */
704 | case 3: linear[2] = scanline[x*ncomp + 2];
705 | linear[1] = scanline[x*ncomp + 1];
706 | linear[0] = scanline[x*ncomp + 0];
707 | break;
708 | default:
709 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
710 | break;
711 | }
712 | stbiw__linear_to_rgbe(rgbe, linear);
713 | scratch[x + width*0] = rgbe[0];
714 | scratch[x + width*1] = rgbe[1];
715 | scratch[x + width*2] = rgbe[2];
716 | scratch[x + width*3] = rgbe[3];
717 | }
718 |
719 | s->func(s->context, scanlineheader, 4);
720 |
721 | /* RLE each component separately */
722 | for (c=0; c < 4; c++) {
723 | unsigned char *comp = &scratch[width*c];
724 |
725 | x = 0;
726 | while (x < width) {
727 | // find first run
728 | r = x;
729 | while (r+2 < width) {
730 | if (comp[r] == comp[r+1] && comp[r] == comp[r+2])
731 | break;
732 | ++r;
733 | }
734 | if (r+2 >= width)
735 | r = width;
736 | // dump up to first run
737 | while (x < r) {
738 | int len = r-x;
739 | if (len > 128) len = 128;
740 | stbiw__write_dump_data(s, len, &comp[x]);
741 | x += len;
742 | }
743 | // if there's a run, output it
744 | if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd
745 | // find next byte after run
746 | while (r < width && comp[r] == comp[x])
747 | ++r;
748 | // output run up to r
749 | while (x < r) {
750 | int len = r-x;
751 | if (len > 127) len = 127;
752 | stbiw__write_run_data(s, len, comp[x]);
753 | x += len;
754 | }
755 | }
756 | }
757 | }
758 | }
759 | }
760 |
761 | static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)
762 | {
763 | if (y <= 0 || x <= 0 || data == NULL)
764 | return 0;
765 | else {
766 | // Each component is stored separately. Allocate scratch space for full output scanline.
767 | unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
768 | int i, len;
769 | char buffer[128];
770 | char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
771 | s->func(s->context, header, sizeof(header)-1);
772 |
773 | #ifdef __STDC_LIB_EXT1__
774 | len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
775 | #else
776 | len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
777 | #endif
778 | s->func(s->context, buffer, len);
779 |
780 | for(i=0; i < y; i++)
781 | stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i));
782 | STBIW_FREE(scratch);
783 | return 1;
784 | }
785 | }
786 |
787 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)
788 | {
789 | stbi__write_context s = { 0 };
790 | stbi__start_write_callbacks(&s, func, context);
791 | return stbi_write_hdr_core(&s, x, y, comp, (float *) data);
792 | }
793 |
794 | STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
795 | {
796 | stbi__write_context s = { 0 };
797 | if (stbi__start_write_file(&s,filename)) {
798 | int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);
799 | stbi__end_write_file(&s);
800 | return r;
801 | } else
802 | return 0;
803 | }
804 | #endif // STBI_WRITE_NO_STDIO
805 |
806 |
807 | //////////////////////////////////////////////////////////////////////////////
808 | //
809 | // PNG writer
810 | //
811 |
812 | #ifndef STBIW_ZLIB_COMPRESS
813 | // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
814 | #define stbiw__sbraw(a) ((int *) (void *) (a) - 2)
815 | #define stbiw__sbm(a) stbiw__sbraw(a)[0]
816 | #define stbiw__sbn(a) stbiw__sbraw(a)[1]
817 |
818 | #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a))
819 | #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0)
820 | #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a)))
821 |
822 | #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v))
823 | #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0)
824 | #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0)
825 |
826 | static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
827 | {
828 | int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;
829 | void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);
830 | STBIW_ASSERT(p);
831 | if (p) {
832 | if (!*arr) ((int *) p)[1] = 0;
833 | *arr = (void *) ((int *) p + 2);
834 | stbiw__sbm(*arr) = m;
835 | }
836 | return *arr;
837 | }
838 |
839 | static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
840 | {
841 | while (*bitcount >= 8) {
842 | stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));
843 | *bitbuffer >>= 8;
844 | *bitcount -= 8;
845 | }
846 | return data;
847 | }
848 |
849 | static int stbiw__zlib_bitrev(int code, int codebits)
850 | {
851 | int res=0;
852 | while (codebits--) {
853 | res = (res << 1) | (code & 1);
854 | code >>= 1;
855 | }
856 | return res;
857 | }
858 |
859 | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit)
860 | {
861 | int i;
862 | for (i=0; i < limit && i < 258; ++i)
863 | if (a[i] != b[i]) break;
864 | return i;
865 | }
866 |
867 | static unsigned int stbiw__zhash(unsigned char *data)
868 | {
869 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
870 | hash ^= hash << 3;
871 | hash += hash >> 5;
872 | hash ^= hash << 4;
873 | hash += hash >> 17;
874 | hash ^= hash << 25;
875 | hash += hash >> 6;
876 | return hash;
877 | }
878 |
879 | #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))
880 | #define stbiw__zlib_add(code,codebits) \
881 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())
882 | #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c)
883 | // default huffman tables
884 | #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8)
885 | #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9)
886 | #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7)
887 | #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8)
888 | #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n))
889 | #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n))
890 |
891 | #define stbiw__ZHASH 16384
892 |
893 | #endif // STBIW_ZLIB_COMPRESS
894 |
895 | STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
896 | {
897 | #ifdef STBIW_ZLIB_COMPRESS
898 | // user provided a zlib compress implementation, use that
899 | return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality);
900 | #else // use builtin
901 | static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };
902 | static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
903 | static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };
904 | static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
905 | unsigned int bitbuf=0;
906 | int i,j, bitcount=0;
907 | unsigned char *out = NULL;
908 | unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**));
909 | if (hash_table == NULL)
910 | return NULL;
911 | if (quality < 5) quality = 5;
912 |
913 | stbiw__sbpush(out, 0x78); // DEFLATE 32K window
914 | stbiw__sbpush(out, 0x5e); // FLEVEL = 1
915 | stbiw__zlib_add(1,1); // BFINAL = 1
916 | stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman
917 |
918 | for (i=0; i < stbiw__ZHASH; ++i)
919 | hash_table[i] = NULL;
920 |
921 | i=0;
922 | while (i < data_len-3) {
923 | // hash next 3 bytes of data to be compressed
924 | int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;
925 | unsigned char *bestloc = 0;
926 | unsigned char **hlist = hash_table[h];
927 | int n = stbiw__sbcount(hlist);
928 | for (j=0; j < n; ++j) {
929 | if (hlist[j]-data > i-32768) { // if entry lies within window
930 | int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);
931 | if (d >= best) { best=d; bestloc=hlist[j]; }
932 | }
933 | }
934 | // when hash table entry is too long, delete half the entries
935 | if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {
936 | STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
937 | stbiw__sbn(hash_table[h]) = quality;
938 | }
939 | stbiw__sbpush(hash_table[h],data+i);
940 |
941 | if (bestloc) {
942 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
943 | h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1);
944 | hlist = hash_table[h];
945 | n = stbiw__sbcount(hlist);
946 | for (j=0; j < n; ++j) {
947 | if (hlist[j]-data > i-32767) {
948 | int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1);
949 | if (e > best) { // if next match is better, bail on current match
950 | bestloc = NULL;
951 | break;
952 | }
953 | }
954 | }
955 | }
956 |
957 | if (bestloc) {
958 | int d = (int) (data+i - bestloc); // distance back
959 | STBIW_ASSERT(d <= 32767 && best <= 258);
960 | for (j=0; best > lengthc[j+1]-1; ++j);
961 | stbiw__zlib_huff(j+257);
962 | if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);
963 | for (j=0; d > distc[j+1]-1; ++j);
964 | stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5);
965 | if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]);
966 | i += best;
967 | } else {
968 | stbiw__zlib_huffb(data[i]);
969 | ++i;
970 | }
971 | }
972 | // write out final bytes
973 | for (;i < data_len; ++i)
974 | stbiw__zlib_huffb(data[i]);
975 | stbiw__zlib_huff(256); // end of block
976 | // pad with 0 bits to byte boundary
977 | while (bitcount)
978 | stbiw__zlib_add(0,1);
979 |
980 | for (i=0; i < stbiw__ZHASH; ++i)
981 | (void) stbiw__sbfree(hash_table[i]);
982 | STBIW_FREE(hash_table);
983 |
984 | // store uncompressed instead if compression was worse
985 | if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) {
986 | stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1
987 | for (j = 0; j < data_len;) {
988 | int blocklen = data_len - j;
989 | if (blocklen > 32767) blocklen = 32767;
990 | stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression
991 | stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN
992 | stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8));
993 | stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN
994 | stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8));
995 | memcpy(out+stbiw__sbn(out), data+j, blocklen);
996 | stbiw__sbn(out) += blocklen;
997 | j += blocklen;
998 | }
999 | }
1000 |
1001 | {
1002 | // compute adler32 on input
1003 | unsigned int s1=1, s2=0;
1004 | int blocklen = (int) (data_len % 5552);
1005 | j=0;
1006 | while (j < data_len) {
1007 | for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; }
1008 | s1 %= 65521; s2 %= 65521;
1009 | j += blocklen;
1010 | blocklen = 5552;
1011 | }
1012 | stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));
1013 | stbiw__sbpush(out, STBIW_UCHAR(s2));
1014 | stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));
1015 | stbiw__sbpush(out, STBIW_UCHAR(s1));
1016 | }
1017 | *out_len = stbiw__sbn(out);
1018 | // make returned pointer freeable
1019 | STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);
1020 | return (unsigned char *) stbiw__sbraw(out);
1021 | #endif // STBIW_ZLIB_COMPRESS
1022 | }
1023 |
1024 | static unsigned int stbiw__crc32(unsigned char *buffer, int len)
1025 | {
1026 | #ifdef STBIW_CRC32
1027 | return STBIW_CRC32(buffer, len);
1028 | #else
1029 | static unsigned int crc_table[256] =
1030 | {
1031 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
1032 | 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
1033 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
1034 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
1035 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
1036 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
1037 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
1038 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
1039 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
1040 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
1041 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
1042 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
1043 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
1044 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
1045 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
1046 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
1047 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
1048 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
1049 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
1050 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
1051 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
1052 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
1053 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
1054 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
1055 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
1056 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
1057 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
1058 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
1059 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
1060 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
1061 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
1062 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
1063 | };
1064 |
1065 | unsigned int crc = ~0u;
1066 | int i;
1067 | for (i=0; i < len; ++i)
1068 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
1069 | return ~crc;
1070 | #endif
1071 | }
1072 |
1073 | #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4)
1074 | #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
1075 | #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])
1076 |
1077 | static void stbiw__wpcrc(unsigned char **data, int len)
1078 | {
1079 | unsigned int crc = stbiw__crc32(*data - len - 4, len+4);
1080 | stbiw__wp32(*data, crc);
1081 | }
1082 |
1083 | static unsigned char stbiw__paeth(int a, int b, int c)
1084 | {
1085 | int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
1086 | if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);
1087 | if (pb <= pc) return STBIW_UCHAR(b);
1088 | return STBIW_UCHAR(c);
1089 | }
1090 |
1091 | // @OPTIMIZE: provide an option that always forces left-predict or paeth predict
1092 | static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer)
1093 | {
1094 | static int mapping[] = { 0,1,2,3,4 };
1095 | static int firstmap[] = { 0,1,0,5,6 };
1096 | int *mymap = (y != 0) ? mapping : firstmap;
1097 | int i;
1098 | int type = mymap[filter_type];
1099 | unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y);
1100 | int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;
1101 |
1102 | if (type==0) {
1103 | memcpy(line_buffer, z, width*n);
1104 | return;
1105 | }
1106 |
1107 | // first loop isn't optimized since it's just one pixel
1108 | for (i = 0; i < n; ++i) {
1109 | switch (type) {
1110 | case 1: line_buffer[i] = z[i]; break;
1111 | case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;
1112 | case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break;
1113 | case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break;
1114 | case 5: line_buffer[i] = z[i]; break;
1115 | case 6: line_buffer[i] = z[i]; break;
1116 | }
1117 | }
1118 | switch (type) {
1119 | case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break;
1120 | case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break;
1121 | case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break;
1122 | case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break;
1123 | case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break;
1124 | case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;
1125 | }
1126 | }
1127 |
1128 | STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
1129 | {
1130 | int force_filter = stbi_write_force_png_filter;
1131 | int ctype[5] = { -1, 0, 4, 2, 6 };
1132 | unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
1133 | unsigned char *out,*o, *filt, *zlib;
1134 | signed char *line_buffer;
1135 | int j,zlen;
1136 |
1137 | if (stride_bytes == 0)
1138 | stride_bytes = x * n;
1139 |
1140 | if (force_filter >= 5) {
1141 | force_filter = -1;
1142 | }
1143 |
1144 | filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;
1145 | line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }
1146 | for (j=0; j < y; ++j) {
1147 | int filter_type;
1148 | if (force_filter > -1) {
1149 | filter_type = force_filter;
1150 | stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer);
1151 | } else { // Estimate the best filter by running through all of them:
1152 | int best_filter = 0, best_filter_val = 0x7fffffff, est, i;
1153 | for (filter_type = 0; filter_type < 5; filter_type++) {
1154 | stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer);
1155 |
1156 | // Estimate the entropy of the line using this filter; the less, the better.
1157 | est = 0;
1158 | for (i = 0; i < x*n; ++i) {
1159 | est += abs((signed char) line_buffer[i]);
1160 | }
1161 | if (est < best_filter_val) {
1162 | best_filter_val = est;
1163 | best_filter = filter_type;
1164 | }
1165 | }
1166 | if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it
1167 | stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer);
1168 | filter_type = best_filter;
1169 | }
1170 | }
1171 | // when we get here, filter_type contains the filter type, and line_buffer contains the data
1172 | filt[j*(x*n+1)] = (unsigned char) filter_type;
1173 | STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);
1174 | }
1175 | STBIW_FREE(line_buffer);
1176 | zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level);
1177 | STBIW_FREE(filt);
1178 | if (!zlib) return 0;
1179 |
1180 | // each tag requires 12 bytes of overhead
1181 | out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);
1182 | if (!out) return 0;
1183 | *out_len = 8 + 12+13 + 12+zlen + 12;
1184 |
1185 | o=out;
1186 | STBIW_MEMMOVE(o,sig,8); o+= 8;
1187 | stbiw__wp32(o, 13); // header length
1188 | stbiw__wptag(o, "IHDR");
1189 | stbiw__wp32(o, x);
1190 | stbiw__wp32(o, y);
1191 | *o++ = 8;
1192 | *o++ = STBIW_UCHAR(ctype[n]);
1193 | *o++ = 0;
1194 | *o++ = 0;
1195 | *o++ = 0;
1196 | stbiw__wpcrc(&o,13);
1197 |
1198 | stbiw__wp32(o, zlen);
1199 | stbiw__wptag(o, "IDAT");
1200 | STBIW_MEMMOVE(o, zlib, zlen);
1201 | o += zlen;
1202 | STBIW_FREE(zlib);
1203 | stbiw__wpcrc(&o, zlen);
1204 |
1205 | stbiw__wp32(o,0);
1206 | stbiw__wptag(o, "IEND");
1207 | stbiw__wpcrc(&o,0);
1208 |
1209 | STBIW_ASSERT(o == out + *out_len);
1210 |
1211 | return out;
1212 | }
1213 |
1214 | #ifndef STBI_WRITE_NO_STDIO
1215 | STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
1216 | {
1217 | FILE *f;
1218 | int len;
1219 | unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);
1220 | if (png == NULL) return 0;
1221 |
1222 | f = stbiw__fopen(filename, "wb");
1223 | if (!f) { STBIW_FREE(png); return 0; }
1224 | fwrite(png, 1, len, f);
1225 | fclose(f);
1226 | STBIW_FREE(png);
1227 | return 1;
1228 | }
1229 | #endif
1230 |
1231 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)
1232 | {
1233 | int len;
1234 | unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);
1235 | if (png == NULL) return 0;
1236 | func(context, png, len);
1237 | STBIW_FREE(png);
1238 | return 1;
1239 | }
1240 |
1241 |
1242 | /* ***************************************************************************
1243 | *
1244 | * JPEG writer
1245 | *
1246 | * This is based on Jon Olick's jo_jpeg.cpp:
1247 | * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html
1248 | */
1249 |
1250 | static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,
1251 | 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
1252 |
1253 | static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) {
1254 | int bitBuf = *bitBufP, bitCnt = *bitCntP;
1255 | bitCnt += bs[1];
1256 | bitBuf |= bs[0] << (24 - bitCnt);
1257 | while(bitCnt >= 8) {
1258 | unsigned char c = (bitBuf >> 16) & 255;
1259 | stbiw__putc(s, c);
1260 | if(c == 255) {
1261 | stbiw__putc(s, 0);
1262 | }
1263 | bitBuf <<= 8;
1264 | bitCnt -= 8;
1265 | }
1266 | *bitBufP = bitBuf;
1267 | *bitCntP = bitCnt;
1268 | }
1269 |
1270 | static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) {
1271 | float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p;
1272 | float z1, z2, z3, z4, z5, z11, z13;
1273 |
1274 | float tmp0 = d0 + d7;
1275 | float tmp7 = d0 - d7;
1276 | float tmp1 = d1 + d6;
1277 | float tmp6 = d1 - d6;
1278 | float tmp2 = d2 + d5;
1279 | float tmp5 = d2 - d5;
1280 | float tmp3 = d3 + d4;
1281 | float tmp4 = d3 - d4;
1282 |
1283 | // Even part
1284 | float tmp10 = tmp0 + tmp3; // phase 2
1285 | float tmp13 = tmp0 - tmp3;
1286 | float tmp11 = tmp1 + tmp2;
1287 | float tmp12 = tmp1 - tmp2;
1288 |
1289 | d0 = tmp10 + tmp11; // phase 3
1290 | d4 = tmp10 - tmp11;
1291 |
1292 | z1 = (tmp12 + tmp13) * 0.707106781f; // c4
1293 | d2 = tmp13 + z1; // phase 5
1294 | d6 = tmp13 - z1;
1295 |
1296 | // Odd part
1297 | tmp10 = tmp4 + tmp5; // phase 2
1298 | tmp11 = tmp5 + tmp6;
1299 | tmp12 = tmp6 + tmp7;
1300 |
1301 | // The rotator is modified from fig 4-8 to avoid extra negations.
1302 | z5 = (tmp10 - tmp12) * 0.382683433f; // c6
1303 | z2 = tmp10 * 0.541196100f + z5; // c2-c6
1304 | z4 = tmp12 * 1.306562965f + z5; // c2+c6
1305 | z3 = tmp11 * 0.707106781f; // c4
1306 |
1307 | z11 = tmp7 + z3; // phase 5
1308 | z13 = tmp7 - z3;
1309 |
1310 | *d5p = z13 + z2; // phase 6
1311 | *d3p = z13 - z2;
1312 | *d1p = z11 + z4;
1313 | *d7p = z11 - z4;
1314 |
1315 | *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6;
1316 | }
1317 |
1318 | static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {
1319 | int tmp1 = val < 0 ? -val : val;
1320 | val = val < 0 ? val-1 : val;
1321 | bits[1] = 1;
1322 | while(tmp1 >>= 1) {
1323 | ++bits[1];
1324 | }
1325 | bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) {
1368 | }
1369 | // end0pos = first element in reverse order !=0
1370 | if(end0pos == 0) {
1371 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
1372 | return DU[0];
1373 | }
1374 | for(i = 1; i <= end0pos; ++i) {
1375 | int startpos = i;
1376 | int nrzeroes;
1377 | unsigned short bits[2];
1378 | for (; DU[i]==0 && i<=end0pos; ++i) {
1379 | }
1380 | nrzeroes = i-startpos;
1381 | if ( nrzeroes >= 16 ) {
1382 | int lng = nrzeroes>>4;
1383 | int nrmarker;
1384 | for (nrmarker=1; nrmarker <= lng; ++nrmarker)
1385 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);
1386 | nrzeroes &= 15;
1387 | }
1388 | stbiw__jpg_calcBits(DU[i], bits);
1389 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]);
1390 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
1391 | }
1392 | if(end0pos != 63) {
1393 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
1394 | }
1395 | return DU[0];
1396 | }
1397 |
1398 | static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) {
1399 | // Constants that don't pollute global namespace
1400 | static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0};
1401 | static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
1402 | static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d};
1403 | static const unsigned char std_ac_luminance_values[] = {
1404 | 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
1405 | 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
1406 | 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
1407 | 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
1408 | 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
1409 | 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
1410 | 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
1411 | };
1412 | static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0};
1413 | static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
1414 | static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77};
1415 | static const unsigned char std_ac_chrominance_values[] = {
1416 | 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
1417 | 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
1418 | 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
1419 | 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
1420 | 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
1421 | 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
1422 | 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
1423 | };
1424 | // Huffman tables
1425 | static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}};
1426 | static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}};
1427 | static const unsigned short YAC_HT[256][2] = {
1428 | {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1429 | {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1430 | {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1431 | {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1432 | {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1433 | {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1434 | {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1435 | {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1436 | {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1437 | {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1438 | {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1439 | {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1440 | {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1441 | {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1442 | {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0},
1443 | {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
1444 | };
1445 | static const unsigned short UVAC_HT[256][2] = {
1446 | {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1447 | {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1448 | {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1449 | {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1450 | {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1451 | {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1452 | {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1453 | {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1454 | {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1455 | {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1456 | {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1457 | {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1458 | {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1459 | {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1460 | {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0},
1461 | {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
1462 | };
1463 | static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,
1464 | 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99};
1465 | static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,
1466 | 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99};
1467 | static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
1468 | 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };
1469 |
1470 | int row, col, i, k, subsample;
1471 | float fdtbl_Y[64], fdtbl_UV[64];
1472 | unsigned char YTable[64], UVTable[64];
1473 |
1474 | if(!data || !width || !height || comp > 4 || comp < 1) {
1475 | return 0;
1476 | }
1477 |
1478 | quality = quality ? quality : 90;
1479 | subsample = quality <= 90 ? 1 : 0;
1480 | quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;
1481 | quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
1482 |
1483 | for(i = 0; i < 64; ++i) {
1484 | int uvti, yti = (YQT[i]*quality+50)/100;
1485 | YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti);
1486 | uvti = (UVQT[i]*quality+50)/100;
1487 | UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);
1488 | }
1489 |
1490 | for(row = 0, k = 0; row < 8; ++row) {
1491 | for(col = 0; col < 8; ++col, ++k) {
1492 | fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
1493 | fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
1494 | }
1495 | }
1496 |
1497 | // Write Headers
1498 | {
1499 | static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 };
1500 | static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };
1501 | const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width),
1502 | 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };
1503 | s->func(s->context, (void*)head0, sizeof(head0));
1504 | s->func(s->context, (void*)YTable, sizeof(YTable));
1505 | stbiw__putc(s, 1);
1506 | s->func(s->context, UVTable, sizeof(UVTable));
1507 | s->func(s->context, (void*)head1, sizeof(head1));
1508 | s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1);
1509 | s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));
1510 | stbiw__putc(s, 0x10); // HTYACinfo
1511 | s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1);
1512 | s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));
1513 | stbiw__putc(s, 1); // HTUDCinfo
1514 | s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1);
1515 | s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));
1516 | stbiw__putc(s, 0x11); // HTUACinfo
1517 | s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1);
1518 | s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));
1519 | s->func(s->context, (void*)head2, sizeof(head2));
1520 | }
1521 |
1522 | // Encode 8x8 macroblocks
1523 | {
1524 | static const unsigned short fillBits[] = {0x7F, 7};
1525 | int DCY=0, DCU=0, DCV=0;
1526 | int bitBuf=0, bitCnt=0;
1527 | // comp == 2 is grey+alpha (alpha is ignored)
1528 | int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
1529 | const unsigned char *dataR = (const unsigned char *)data;
1530 | const unsigned char *dataG = dataR + ofsG;
1531 | const unsigned char *dataB = dataR + ofsB;
1532 | int x, y, pos;
1533 | if(subsample) {
1534 | for(y = 0; y < height; y += 16) {
1535 | for(x = 0; x < width; x += 16) {
1536 | float Y[256], U[256], V[256];
1537 | for(row = y, pos = 0; row < y+16; ++row) {
1538 | // row >= height => use last input row
1539 | int clamped_row = (row < height) ? row : height - 1;
1540 | int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
1541 | for(col = x; col < x+16; ++col, ++pos) {
1542 | // if col >= width => use pixel from last input column
1543 | int p = base_p + ((col < width) ? col : (width-1))*comp;
1544 | float r = dataR[p], g = dataG[p], b = dataB[p];
1545 | Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
1546 | U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
1547 | V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
1548 | }
1549 | }
1550 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1551 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1552 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1553 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1554 |
1555 | // subsample U,V
1556 | {
1557 | float subU[64], subV[64];
1558 | int yy, xx;
1559 | for(yy = 0, pos = 0; yy < 8; ++yy) {
1560 | for(xx = 0; xx < 8; ++xx, ++pos) {
1561 | int j = yy*32+xx*2;
1562 | subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f;
1563 | subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f;
1564 | }
1565 | }
1566 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
1567 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
1568 | }
1569 | }
1570 | }
1571 | } else {
1572 | for(y = 0; y < height; y += 8) {
1573 | for(x = 0; x < width; x += 8) {
1574 | float Y[64], U[64], V[64];
1575 | for(row = y, pos = 0; row < y+8; ++row) {
1576 | // row >= height => use last input row
1577 | int clamped_row = (row < height) ? row : height - 1;
1578 | int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
1579 | for(col = x; col < x+8; ++col, ++pos) {
1580 | // if col >= width => use pixel from last input column
1581 | int p = base_p + ((col < width) ? col : (width-1))*comp;
1582 | float r = dataR[p], g = dataG[p], b = dataB[p];
1583 | Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
1584 | U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
1585 | V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
1586 | }
1587 | }
1588 |
1589 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1590 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
1591 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
1592 | }
1593 | }
1594 | }
1595 |
1596 | // Do the bit alignment of the EOI marker
1597 | stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);
1598 | }
1599 |
1600 | // EOI
1601 | stbiw__putc(s, 0xFF);
1602 | stbiw__putc(s, 0xD9);
1603 |
1604 | return 1;
1605 | }
1606 |
1607 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality)
1608 | {
1609 | stbi__write_context s = { 0 };
1610 | stbi__start_write_callbacks(&s, func, context);
1611 | return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality);
1612 | }
1613 |
1614 |
1615 | #ifndef STBI_WRITE_NO_STDIO
1616 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)
1617 | {
1618 | stbi__write_context s = { 0 };
1619 | if (stbi__start_write_file(&s,filename)) {
1620 | int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);
1621 | stbi__end_write_file(&s);
1622 | return r;
1623 | } else
1624 | return 0;
1625 | }
1626 | #endif
1627 |
1628 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION
1629 |
1630 | /* Revision history
1631 | 1.16 (2021-07-11)
1632 | make Deflate code emit uncompressed blocks when it would otherwise expand
1633 | support writing BMPs with alpha channel
1634 | 1.15 (2020-07-13) unknown
1635 | 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels
1636 | 1.13
1637 | 1.12
1638 | 1.11 (2019-08-11)
1639 |
1640 | 1.10 (2019-02-07)
1641 | support utf8 filenames in Windows; fix warnings and platform ifdefs
1642 | 1.09 (2018-02-11)
1643 | fix typo in zlib quality API, improve STB_I_W_STATIC in C++
1644 | 1.08 (2018-01-29)
1645 | add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter
1646 | 1.07 (2017-07-24)
1647 | doc fix
1648 | 1.06 (2017-07-23)
1649 | writing JPEG (using Jon Olick's code)
1650 | 1.05 ???
1651 | 1.04 (2017-03-03)
1652 | monochrome BMP expansion
1653 | 1.03 ???
1654 | 1.02 (2016-04-02)
1655 | avoid allocating large structures on the stack
1656 | 1.01 (2016-01-16)
1657 | STBIW_REALLOC_SIZED: support allocators with no realloc support
1658 | avoid race-condition in crc initialization
1659 | minor compile issues
1660 | 1.00 (2015-09-14)
1661 | installable file IO function
1662 | 0.99 (2015-09-13)
1663 | warning fixes; TGA rle support
1664 | 0.98 (2015-04-08)
1665 | added STBIW_MALLOC, STBIW_ASSERT etc
1666 | 0.97 (2015-01-18)
1667 | fixed HDR asserts, rewrote HDR rle logic
1668 | 0.96 (2015-01-17)
1669 | add HDR output
1670 | fix monochrome BMP
1671 | 0.95 (2014-08-17)
1672 | add monochrome TGA output
1673 | 0.94 (2014-05-31)
1674 | rename private functions to avoid conflicts with stb_image.h
1675 | 0.93 (2014-05-27)
1676 | warning fixes
1677 | 0.92 (2010-08-01)
1678 | casts to unsigned char to fix warnings
1679 | 0.91 (2010-07-17)
1680 | first public release
1681 | 0.90 first internal release
1682 | */
1683 |
1684 | /*
1685 | ------------------------------------------------------------------------------
1686 | This software is available under 2 licenses -- choose whichever you prefer.
1687 | ------------------------------------------------------------------------------
1688 | ALTERNATIVE A - MIT License
1689 | Copyright (c) 2017 Sean Barrett
1690 | Permission is hereby granted, free of charge, to any person obtaining a copy of
1691 | this software and associated documentation files (the "Software"), to deal in
1692 | the Software without restriction, including without limitation the rights to
1693 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1694 | of the Software, and to permit persons to whom the Software is furnished to do
1695 | so, subject to the following conditions:
1696 | The above copyright notice and this permission notice shall be included in all
1697 | copies or substantial portions of the Software.
1698 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1699 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1700 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1701 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1702 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1703 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1704 | SOFTWARE.
1705 | ------------------------------------------------------------------------------
1706 | ALTERNATIVE B - Public Domain (www.unlicense.org)
1707 | This is free and unencumbered software released into the public domain.
1708 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
1709 | software, either in source code form or as a compiled binary, for any purpose,
1710 | commercial or non-commercial, and by any means.
1711 | In jurisdictions that recognize copyright laws, the author or authors of this
1712 | software dedicate any and all copyright interest in the software to the public
1713 | domain. We make this dedication for the benefit of the public at large and to
1714 | the detriment of our heirs and successors. We intend this dedication to be an
1715 | overt act of relinquishment in perpetuity of all present and future rights to
1716 | this software under copyright law.
1717 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1718 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1719 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1720 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1721 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1722 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1723 | ------------------------------------------------------------------------------
1724 | */
1725 |
--------------------------------------------------------------------------------