├── .github └── FUNDING.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── skin_smoothing.c ├── stb_image.h └── stb_image_write.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://www.paypal.com/paypalme/cpuimage/ 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.4) 2 | project(skin_smoothing) 3 | SET(CMAKE_BUILD_TYPE "Release") 4 | add_executable(skin_smoothing skin_smoothing.c) 5 | target_link_libraries(skin_smoothing -lm) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Zhihan Gao 2 | 3 | MIT License 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Skin Smoothing 2 | an efficient algorithm for skin smoothing implementation in c 3 | 4 | ## The algorithm steps are as follows 5 | 1. detect skin color, adapt radius according to skin color ratio 6 | 7 | 2. perform edge detection to obtain a edge map && smoothing level for apply skin denoise 8 | 9 | 3. re-detect skin color based on the denoise results, filtered non-skin areas 10 | 11 | ## Donating 12 | If you found this project useful, consider buying me a coffee 13 | 14 | Buy Me A Coffee -------------------------------------------------------------------------------- /skin_smoothing.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | 3 | #include "stb_image.h" 4 | 5 | #define STB_IMAGE_WRITE_IMPLEMENTATION 6 | 7 | #include "stb_image_write.h" 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef min 13 | #define min(a, b) (((a) < (b)) ? (a) : (b)) 14 | #endif 15 | 16 | #ifndef max 17 | #define max(a, b) (((a) > (b)) ? (a) : (b)) 18 | #endif 19 | 20 | #ifndef ClampToByte 21 | #define ClampToByte(v) (((unsigned)(int)(v)) <(255) ? (v) : (v < 0) ? (0) : (255)) 22 | #endif 23 | 24 | #include 25 | 26 | #if defined(__APPLE__) 27 | # include 28 | #elif defined(_WIN32) 29 | # define WIN32_LEAN_AND_MEAN 30 | 31 | # include 32 | 33 | #else // __linux 34 | 35 | # include 36 | 37 | # ifndef CLOCK_MONOTONIC //_RAW 38 | # define CLOCK_MONOTONIC CLOCK_REALTIME 39 | # endif 40 | #endif 41 | 42 | static 43 | uint64_t nanotimer() { 44 | static int ever = 0; 45 | #if defined(__APPLE__) 46 | static mach_timebase_info_data_t frequency; 47 | if (!ever) { 48 | if (mach_timebase_info(&frequency) != KERN_SUCCESS) { 49 | return 0; 50 | } 51 | ever = 1; 52 | } 53 | return (mach_absolute_time() * frequency.numer / frequency.denom); 54 | #elif defined(_WIN32) 55 | static LARGE_INTEGER frequency; 56 | if (!ever) { 57 | QueryPerformanceFrequency(&frequency); 58 | ever = 1; 59 | } 60 | LARGE_INTEGER t; 61 | QueryPerformanceCounter(&t); 62 | return (t.QuadPart * (uint64_t) 1e9) / frequency.QuadPart; 63 | #else // __linux 64 | struct timespec t; 65 | if (!ever) { 66 | if (clock_gettime(CLOCK_MONOTONIC, &t) != 0) { 67 | return 0; 68 | } 69 | ever = 1; 70 | } 71 | clock_gettime(CLOCK_MONOTONIC, &t); 72 | return (t.tv_sec * (uint64_t) 1e9) + t.tv_nsec; 73 | #endif 74 | } 75 | 76 | 77 | static double now() { 78 | static uint64_t epoch = 0; 79 | if (!epoch) { 80 | epoch = nanotimer(); 81 | } 82 | return (nanotimer() - epoch) / 1e9; 83 | }; 84 | 85 | double calcElapsed(double start, double end) { 86 | double took = -start; 87 | return took + end; 88 | } 89 | 90 | unsigned char *loadImage(const char *filename, int *width, int *height, int *channels) { 91 | return stbi_load(filename, width, height, channels, 0); 92 | } 93 | 94 | void saveImage(const char *filename, int width, int height, int channels, unsigned char *Output) { 95 | if (!stbi_write_jpg(filename, width, height, channels, Output, 100)) { 96 | fprintf(stderr, "save file fail.\n"); 97 | return; 98 | } 99 | } 100 | 101 | void splitpath(const char *path, char *drv, char *dir, char *name, char *ext) { 102 | const char *end; 103 | const char *p; 104 | const char *s; 105 | if (path[0] && path[1] == ':') { 106 | if (drv) { 107 | *drv++ = *path++; 108 | *drv++ = *path++; 109 | *drv = '\0'; 110 | } 111 | } else if (drv) 112 | *drv = '\0'; 113 | for (end = path; *end && *end != ':';) 114 | end++; 115 | for (p = end; p > path && *--p != '\\' && *p != '/';) 116 | if (*p == '.') { 117 | end = p; 118 | break; 119 | } 120 | if (ext) 121 | for (s = end; (*ext = *s++);) 122 | ext++; 123 | for (p = end; p > path;) 124 | if (*--p == '\\' || *p == '/') { 125 | p++; 126 | break; 127 | } 128 | if (name) { 129 | for (s = p; s < end;) 130 | *name++ = *s++; 131 | *name = '\0'; 132 | } 133 | if (dir) { 134 | for (s = path; s < p;) 135 | *dir++ = *s++; 136 | *dir = '\0'; 137 | } 138 | } 139 | 140 | 141 | unsigned int skinDetection(unsigned char *rgb_src, int width, int height, int channels) { 142 | int stride = width * channels; 143 | int lastCol = width * channels - channels; 144 | int lastRow = height * stride - stride; 145 | unsigned int sum = 0; 146 | for (int y = 0; y < height; y++) { 147 | int cur_row = stride * y; 148 | int next_row = min(cur_row + stride, lastRow); 149 | unsigned char *next_scanLine = rgb_src + next_row; 150 | unsigned char *cur_scanLine = rgb_src + cur_row; 151 | for (int x = 0; x < width; x++) { 152 | int cur_col = x * channels; 153 | int next_col = min(cur_col + channels, lastCol); 154 | unsigned char *c00 = cur_scanLine + cur_col; 155 | unsigned char *c10 = cur_scanLine + next_col; 156 | unsigned char *c01 = next_scanLine + cur_col; 157 | unsigned char *c11 = next_scanLine + next_col; 158 | int r_avg = ((c00[0] + c10[0] + c01[0] + c11[0])) >> 2; 159 | int g_avg = ((c00[1] + c10[1] + c01[1] + c11[1])) >> 2; 160 | int b_avg = ((c00[2] + c10[2] + c01[2] + c11[2])) >> 2; 161 | if (r_avg >= 60 && g_avg >= 40 && b_avg >= 20 && r_avg >= b_avg && (r_avg - g_avg) >= 10 && 162 | max(max(r_avg, g_avg), b_avg) - min(min(r_avg, g_avg), b_avg) >= 10) { 163 | sum++; 164 | } 165 | } 166 | } 167 | return sum; 168 | } 169 | 170 | void skinFilter(unsigned char *input, unsigned char *output, int width, int height, int channels) { 171 | int stride = width * channels; 172 | int lastCol = width * channels - channels; 173 | int lastRow = height * stride - stride; 174 | for (int y = 0; y < height; y++) { 175 | int cur_row = stride * y; 176 | int next_row = min(cur_row + stride, lastRow); 177 | unsigned char *next_scanOutLine = output + next_row; 178 | unsigned char *cur_scanOutLine = output + cur_row; 179 | unsigned char *scanOutLine = output + y * stride; 180 | unsigned char *scanInLine = input + y * stride; 181 | for (int x = 0; x < width; x++) { 182 | int cur_col = x * channels; 183 | int next_col = min(cur_col + channels, lastCol); 184 | unsigned char *c00 = cur_scanOutLine + cur_col; 185 | unsigned char *c10 = cur_scanOutLine + next_col; 186 | unsigned char *c01 = next_scanOutLine + cur_col; 187 | unsigned char *c11 = next_scanOutLine + next_col; 188 | int r_avg = ((c00[0] + c10[0] + c01[0] + c11[0])) >> 2; 189 | int g_avg = ((c00[1] + c10[1] + c01[1] + c11[1])) >> 2; 190 | int b_avg = ((c00[2] + c10[2] + c01[2] + c11[2])) >> 2; 191 | int is_skin = !(r_avg >= 60 && g_avg >= 40 && b_avg >= 20 && r_avg >= b_avg && (r_avg - g_avg) >= 10 && 192 | max(max(r_avg, g_avg), b_avg) - min(min(r_avg, g_avg), b_avg) >= 10); 193 | if (is_skin) 194 | for (int c = 0; c < channels; ++c) 195 | scanOutLine[c] = scanInLine[c]; 196 | scanOutLine += channels; 197 | scanInLine += channels; 198 | } 199 | } 200 | } 201 | 202 | void getOffsetPos(int *offsetPos, int length, int left, int right, int step) { 203 | if (offsetPos == NULL) return; 204 | if ((length < 0) || (left < 0) || (right < 0)) 205 | return; 206 | for (int x = -left; x < length + right; x++) { 207 | int pos = x; 208 | int length2 = length + length; 209 | if (pos < 0) { 210 | do { 211 | pos += length2; 212 | } while (pos < 0); 213 | } else if (pos >= length2) { 214 | do { 215 | pos -= length2; 216 | } while (pos >= length2); 217 | } 218 | if (pos >= length) 219 | pos = length2 - 1 - pos; 220 | offsetPos[x + left] = pos * step; 221 | } 222 | } 223 | 224 | int Abs(int v) { 225 | return (v ^ (v >> 31)) - (v >> 31); 226 | } 227 | 228 | void skinDenoise(unsigned char *input, unsigned char *output, int width, int height, int channels, int radius, 229 | int smoothingLevel) { 230 | if ((input == NULL) || (output == NULL)) return; 231 | if ((width <= 0) || (height <= 0)) return; 232 | if ((radius <= 0) || (smoothingLevel <= 0)) return; 233 | if ((channels != 1) && (channels != 3)) return; 234 | int windowSize = (2 * radius + 1) * (2 * radius + 1); 235 | int *colPower = (int *) malloc(width * channels * sizeof(int)); 236 | int *colValue = (int *) malloc(width * channels * sizeof(int)); 237 | int *rowPos = (int *) malloc((width + radius + radius) * channels * sizeof(int)); 238 | int *colPos = (int *) malloc((height + radius + radius) * channels * sizeof(int)); 239 | if ((colPower == NULL) || (colValue == NULL) || (rowPos == NULL) || (colPos == NULL)) { 240 | if (colPower) free(colPower); 241 | if (colValue) free(colValue); 242 | if (rowPos) free(rowPos); 243 | if (colPos) free(colPos); 244 | return; 245 | } 246 | int stride = width * channels; 247 | int smoothLut[256] = {0}; 248 | float ii = 0.f; 249 | for (int i = 0; i <= 255; i++, ii -= 1.) { 250 | smoothLut[i] = (int) ((expf(ii * (1.0f / (smoothingLevel * 255.0f))) + (smoothingLevel * (i + 1)) + 1) * 0.5f); 251 | smoothLut[i] = max(smoothLut[i], 1); 252 | } 253 | getOffsetPos(rowPos, width, radius, radius, channels); 254 | getOffsetPos(colPos, height, radius, radius, stride); 255 | int *rowOffset = rowPos + radius; 256 | int *colOffSet = colPos + radius; 257 | for (int y = 0; y < height; y++) { 258 | unsigned char *scanInLine = input + y * stride; 259 | unsigned char *scanOutLine = output + y * stride; 260 | if (y == 0) { 261 | for (int x = 0; x < stride; x += channels) { 262 | int colSum[3] = {0}; 263 | int colSumPow[3] = {0}; 264 | for (int z = -radius; z <= radius; z++) { 265 | unsigned char *sample = input + colOffSet[z] + x; 266 | for (int c = 0; c < channels; ++c) { 267 | colSum[c] += sample[c]; 268 | colSumPow[c] += sample[c] * sample[c]; 269 | } 270 | } 271 | for (int c = 0; c < channels; ++c) { 272 | colValue[x + c] = colSum[c]; 273 | colPower[x + c] = colSumPow[c]; 274 | } 275 | } 276 | } else { 277 | unsigned char *lastCol = input + colOffSet[y - radius - 1]; 278 | unsigned char *nextCol = input + colOffSet[y + radius]; 279 | for (int x = 0; x < stride; x += channels) { 280 | for (int c = 0; c < channels; ++c) { 281 | colValue[x + c] -= lastCol[x + c] - nextCol[x + c]; 282 | colPower[x + c] -= lastCol[x + c] * lastCol[x + c] - nextCol[x + c] * nextCol[x + c]; 283 | } 284 | } 285 | } 286 | int prevSum[3] = {0}; 287 | int prevPowerSum[3] = {0}; 288 | for (int z = -radius; z <= radius; z++) { 289 | int index = rowOffset[z]; 290 | for (int c = 0; c < channels; ++c) { 291 | prevSum[c] += colValue[index + c]; 292 | prevPowerSum[c] += colPower[index + c]; 293 | } 294 | } 295 | for (int c = 0; c < channels; ++c) { 296 | const int mean = prevSum[c] / windowSize; 297 | const int diff = mean - scanInLine[c]; 298 | const int edge = ClampToByte(diff); 299 | const int masked_edge = (edge * scanInLine[c] + (256 - edge) * mean) >> 8; 300 | const int var = Abs(prevPowerSum[c] - mean * prevSum[c]) / windowSize; 301 | const int out = masked_edge - diff * var / (var + smoothLut[scanInLine[c]]); 302 | scanOutLine[c] = ClampToByte(out); 303 | } 304 | scanInLine += channels; 305 | scanOutLine += channels; 306 | for (int x = 1; x < width; x++) { 307 | int lastRow = rowOffset[x - radius - 1]; 308 | int nextRow = rowOffset[x + radius]; 309 | for (int c = 0; c < channels; ++c) { 310 | prevSum[c] = prevSum[c] - colValue[lastRow + c] + colValue[nextRow + c]; 311 | prevPowerSum[c] = prevPowerSum[c] - colPower[lastRow + c] + colPower[nextRow + c]; 312 | const int mean = prevSum[c] / windowSize; 313 | const int diff = mean - scanInLine[c]; 314 | const int edge = ClampToByte(diff); 315 | const int masked_edge = (edge * scanInLine[c] + (256 - edge) * mean) >> 8; 316 | const int var = Abs(prevPowerSum[c] - mean * prevSum[c]) / windowSize; 317 | const int out = masked_edge - diff * var / (var + smoothLut[scanInLine[c]]); 318 | scanOutLine[c] = ClampToByte(out); 319 | } 320 | scanInLine += channels; 321 | scanOutLine += channels; 322 | } 323 | } 324 | if (colPower) free(colPower); 325 | if (colValue) free(colValue); 326 | if (rowPos) free(rowPos); 327 | if (colPos) free(colPos); 328 | } 329 | 330 | 331 | void skinSmoothing(unsigned char *input, unsigned char *output, int width, int height, int channels, 332 | int smoothingLevel, int apply_skin_filter) { 333 | if (input == NULL || output == NULL || width == 0 || height == 0 || channels == 1) 334 | return; 335 | //1.detect skin color, adapt radius according to skin color ratio 336 | unsigned int skinSum = skinDetection(input, width, height, channels); 337 | float skin_rate = skinSum / (float) (width * height) * 100; 338 | int radius = min(width, height) / skin_rate + 1; 339 | //2.perform edge detection to obtain a edge map && smoothing level for apply skin denoise 340 | skinDenoise(input, output, width, height, channels, radius, smoothingLevel); 341 | //3.re-detect skin color based on the denoise results, filtered non-skin areas 342 | if (apply_skin_filter) 343 | skinFilter(input, output, width, height, channels); 344 | } 345 | 346 | 347 | int main(int argc, char **argv) { 348 | printf("Image Processing \n "); 349 | printf("blog:http://cpuimage.cnblogs.com/ \n "); 350 | printf("Skin Smoothing\n "); 351 | if (argc < 2) { 352 | printf("usage: \n "); 353 | printf("%s filename \n ", argv[0]); 354 | printf("%s image.jpg \n ", argv[0]); 355 | getchar(); 356 | return 0; 357 | } 358 | char *in_file = argv[1]; 359 | char drive[3]; 360 | char dir[256]; 361 | char fname[256]; 362 | char ext[256]; 363 | char out_file[1024]; 364 | splitpath(in_file, drive, dir, fname, ext); 365 | sprintf(out_file, "%s%s%s_out.jpg", drive, dir, fname); 366 | int width = 0; 367 | int height = 0; 368 | int channels = 0; 369 | unsigned char *input = NULL; 370 | input = loadImage(in_file, &width, &height, &channels); 371 | if (input) { 372 | unsigned char *output = (unsigned char *) calloc(width * channels * height * sizeof(unsigned char), 1); 373 | if (output) { 374 | int smoothingLevel = 10; 375 | int apply_skin_filter = 0; 376 | double startTime = now(); 377 | skinSmoothing(input, output, width, height, channels, smoothingLevel, apply_skin_filter); 378 | double elapsed = calcElapsed(startTime, now()); 379 | printf("elapsed time: %d ms.\n ", (int) (elapsed * 1000)); 380 | saveImage(out_file, width, height, channels, output); 381 | free(output); 382 | } 383 | free(input); 384 | } else { 385 | printf("load file: %s fail!\n", in_file); 386 | } 387 | printf("press any key to exit. \n"); 388 | getchar(); 389 | return 0; 390 | } 391 | -------------------------------------------------------------------------------- /stb_image_write.h: -------------------------------------------------------------------------------- 1 | /* stb_image_write - v1.14 - 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 | 144 | LICENSE 145 | 146 | See end of file for license information. 147 | 148 | */ 149 | 150 | #ifndef INCLUDE_STB_IMAGE_WRITE_H 151 | #define INCLUDE_STB_IMAGE_WRITE_H 152 | 153 | #include 154 | 155 | // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' 156 | #ifndef STBIWDEF 157 | #ifdef STB_IMAGE_WRITE_STATIC 158 | #define STBIWDEF static 159 | #else 160 | #ifdef __cplusplus 161 | #define STBIWDEF extern "C" 162 | #else 163 | #define STBIWDEF extern 164 | #endif 165 | #endif 166 | #endif 167 | 168 | #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations 169 | extern int stbi_write_tga_with_rle; 170 | extern int stbi_write_png_compression_level; 171 | extern int stbi_write_force_png_filter; 172 | #endif 173 | 174 | #ifndef STBI_WRITE_NO_STDIO 175 | 176 | STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 177 | 178 | STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 179 | 180 | STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 181 | 182 | STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 183 | 184 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); 185 | 186 | #ifdef STBI_WINDOWS_UTF8 187 | STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); 188 | #endif 189 | #endif 190 | 191 | typedef void stbi_write_func(void *context, void *data, int size); 192 | 193 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, 194 | int stride_in_bytes); 195 | 196 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 197 | 198 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 199 | 200 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 201 | 202 | STBIWDEF int 203 | stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); 204 | 205 | STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); 206 | 207 | #endif//INCLUDE_STB_IMAGE_WRITE_H 208 | 209 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION 210 | 211 | #ifdef _WIN32 212 | #ifndef _CRT_SECURE_NO_WARNINGS 213 | #define _CRT_SECURE_NO_WARNINGS 214 | #endif 215 | #ifndef _CRT_NONSTDC_NO_DEPRECATE 216 | #define _CRT_NONSTDC_NO_DEPRECATE 217 | #endif 218 | #endif 219 | 220 | #ifndef STBI_WRITE_NO_STDIO 221 | 222 | #include 223 | 224 | #endif // STBI_WRITE_NO_STDIO 225 | 226 | #include 227 | #include 228 | #include 229 | #include 230 | 231 | #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) 232 | // ok 233 | #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) 234 | // ok 235 | #else 236 | #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." 237 | #endif 238 | 239 | #ifndef STBIW_MALLOC 240 | #define STBIW_MALLOC(sz) malloc(sz) 241 | #define STBIW_REALLOC(p, newsz) realloc(p,newsz) 242 | #define STBIW_FREE(p) free(p) 243 | #endif 244 | 245 | #ifndef STBIW_REALLOC_SIZED 246 | #define STBIW_REALLOC_SIZED(p, oldsz, newsz) STBIW_REALLOC(p,newsz) 247 | #endif 248 | 249 | 250 | #ifndef STBIW_MEMMOVE 251 | #define STBIW_MEMMOVE(a, b, sz) memmove(a,b,sz) 252 | #endif 253 | 254 | 255 | #ifndef STBIW_ASSERT 256 | 257 | #include 258 | 259 | #define STBIW_ASSERT(x) assert(x) 260 | #endif 261 | 262 | #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) 263 | 264 | #ifdef STB_IMAGE_WRITE_STATIC 265 | static int stbi_write_png_compression_level = 8; 266 | static int stbi_write_tga_with_rle = 1; 267 | static int stbi_write_force_png_filter = -1; 268 | #else 269 | int stbi_write_png_compression_level = 8; 270 | int stbi_write_tga_with_rle = 1; 271 | int stbi_write_force_png_filter = -1; 272 | #endif 273 | 274 | static int stbi__flip_vertically_on_write = 0; 275 | 276 | STBIWDEF void stbi_flip_vertically_on_write(int flag) { 277 | stbi__flip_vertically_on_write = flag; 278 | } 279 | 280 | typedef struct { 281 | stbi_write_func *func; 282 | void *context; 283 | } stbi__write_context; 284 | 285 | // initialize a callback-based context 286 | static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) { 287 | s->func = c; 288 | s->context = context; 289 | } 290 | 291 | #ifndef STBI_WRITE_NO_STDIO 292 | 293 | static void stbi__stdio_write(void *context, void *data, int size) { 294 | fwrite(data, 1, size, (FILE *) context); 295 | } 296 | 297 | #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) 298 | #ifdef __cplusplus 299 | #define STBIW_EXTERN extern "C" 300 | #else 301 | #define STBIW_EXTERN extern 302 | #endif 303 | STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); 304 | 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); 305 | 306 | STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) 307 | { 308 | return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); 309 | } 310 | #endif 311 | 312 | static FILE *stbiw__fopen(char const *filename, char const *mode) { 313 | FILE *f; 314 | #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) 315 | wchar_t wMode[64]; 316 | wchar_t wFilename[1024]; 317 | if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) 318 | return 0; 319 | 320 | if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) 321 | return 0; 322 | 323 | #if _MSC_VER >= 1400 324 | if (0 != _wfopen_s(&f, wFilename, wMode)) 325 | f = 0; 326 | #else 327 | f = _wfopen(wFilename, wMode); 328 | #endif 329 | 330 | #elif defined(_MSC_VER) && _MSC_VER >= 1400 331 | if (0 != fopen_s(&f, filename, mode)) 332 | f=0; 333 | #else 334 | f = fopen(filename, mode); 335 | #endif 336 | return f; 337 | } 338 | 339 | static int stbi__start_write_file(stbi__write_context *s, const char *filename) { 340 | FILE *f = stbiw__fopen(filename, "wb"); 341 | stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); 342 | return f != NULL; 343 | } 344 | 345 | static void stbi__end_write_file(stbi__write_context *s) { 346 | fclose((FILE *) s->context); 347 | } 348 | 349 | #endif // !STBI_WRITE_NO_STDIO 350 | 351 | typedef unsigned int stbiw_uint32; 352 | typedef int stb_image_write_test[sizeof(stbiw_uint32) == 4 ? 1 : -1]; 353 | 354 | static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { 355 | while (*fmt) { 356 | switch (*fmt++) { 357 | case ' ': 358 | break; 359 | case '1': { 360 | unsigned char x = STBIW_UCHAR(va_arg(v, int)); 361 | s->func(s->context, &x, 1); 362 | break; 363 | } 364 | case '2': { 365 | int x = va_arg(v, int); 366 | unsigned char b[2]; 367 | b[0] = STBIW_UCHAR(x); 368 | b[1] = STBIW_UCHAR(x >> 8); 369 | s->func(s->context, b, 2); 370 | break; 371 | } 372 | case '4': { 373 | stbiw_uint32 x = va_arg(v, int); 374 | unsigned char b[4]; 375 | b[0] = STBIW_UCHAR(x); 376 | b[1] = STBIW_UCHAR(x >> 8); 377 | b[2] = STBIW_UCHAR(x >> 16); 378 | b[3] = STBIW_UCHAR(x >> 24); 379 | s->func(s->context, b, 4); 380 | break; 381 | } 382 | default: 383 | STBIW_ASSERT(0); 384 | return; 385 | } 386 | } 387 | } 388 | 389 | static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { 390 | va_list v; 391 | va_start(v, fmt); 392 | stbiw__writefv(s, fmt, v); 393 | va_end(v); 394 | } 395 | 396 | static void stbiw__putc(stbi__write_context *s, unsigned char c) { 397 | s->func(s->context, &c, 1); 398 | } 399 | 400 | static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { 401 | unsigned char arr[3]; 402 | arr[0] = a; 403 | arr[1] = b; 404 | arr[2] = c; 405 | s->func(s->context, arr, 3); 406 | } 407 | 408 | static void 409 | stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) { 410 | unsigned char bg[3] = {255, 0, 255}, px[3]; 411 | int k; 412 | 413 | if (write_alpha < 0) 414 | s->func(s->context, &d[comp - 1], 1); 415 | 416 | switch (comp) { 417 | case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case 418 | case 1: 419 | if (expand_mono) 420 | stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp 421 | else 422 | s->func(s->context, d, 1); // monochrome TGA 423 | break; 424 | case 4: 425 | if (!write_alpha) { 426 | // composite against pink background 427 | for (k = 0; k < 3; ++k) 428 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; 429 | stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); 430 | break; 431 | } 432 | /* FALLTHROUGH */ 433 | case 3: 434 | stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); 435 | break; 436 | } 437 | if (write_alpha > 0) 438 | s->func(s->context, &d[comp - 1], 1); 439 | } 440 | 441 | static void 442 | stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, 443 | int scanline_pad, int expand_mono) { 444 | stbiw_uint32 zero = 0; 445 | int i, j, j_end; 446 | 447 | if (y <= 0) 448 | return; 449 | 450 | if (stbi__flip_vertically_on_write) 451 | vdir *= -1; 452 | 453 | if (vdir < 0) { 454 | j_end = -1; 455 | j = y - 1; 456 | } else { 457 | j_end = y; 458 | j = 0; 459 | } 460 | 461 | for (; j != j_end; j += vdir) { 462 | for (i = 0; i < x; ++i) { 463 | unsigned char *d = (unsigned char *) data + (j * x + i) * comp; 464 | stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); 465 | } 466 | s->func(s->context, &zero, scanline_pad); 467 | } 468 | } 469 | 470 | static int 471 | stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, 472 | int alpha, int pad, const char *fmt, ...) { 473 | if (y < 0 || x < 0) { 474 | return 0; 475 | } else { 476 | va_list v; 477 | va_start(v, fmt); 478 | stbiw__writefv(s, fmt, v); 479 | va_end(v); 480 | stbiw__write_pixels(s, rgb_dir, vdir, x, y, comp, data, alpha, pad, expand_mono); 481 | return 1; 482 | } 483 | } 484 | 485 | static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { 486 | int pad = (-x * 3) & 3; 487 | return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *) data, 0, pad, 488 | "11 4 22 4" "4 44 22 444444", 489 | 'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, 14 + 40, // file header 490 | 40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header 491 | } 492 | 493 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { 494 | stbi__write_context s; 495 | stbi__start_write_callbacks(&s, func, context); 496 | return stbi_write_bmp_core(&s, x, y, comp, data); 497 | } 498 | 499 | #ifndef STBI_WRITE_NO_STDIO 500 | 501 | STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { 502 | stbi__write_context s; 503 | if (stbi__start_write_file(&s, filename)) { 504 | int r = stbi_write_bmp_core(&s, x, y, comp, data); 505 | stbi__end_write_file(&s); 506 | return r; 507 | } else 508 | return 0; 509 | } 510 | 511 | #endif //!STBI_WRITE_NO_STDIO 512 | 513 | static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) { 514 | int has_alpha = (comp == 2 || comp == 4); 515 | int colorbytes = has_alpha ? comp - 1 : comp; 516 | int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 517 | 518 | if (y < 0 || x < 0) 519 | return 0; 520 | 521 | if (!stbi_write_tga_with_rle) { 522 | return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, 523 | "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, 524 | has_alpha * 8); 525 | } else { 526 | int i, j, k; 527 | int jend, jdir; 528 | 529 | stbiw__writef(s, "111 221 2222 11", 0, 0, format + 8, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, 530 | has_alpha * 8); 531 | 532 | if (stbi__flip_vertically_on_write) { 533 | j = 0; 534 | jend = y; 535 | jdir = 1; 536 | } else { 537 | j = y - 1; 538 | jend = -1; 539 | jdir = -1; 540 | } 541 | for (; j != jend; j += jdir) { 542 | unsigned char *row = (unsigned char *) data + j * x * comp; 543 | int len; 544 | 545 | for (i = 0; i < x; i += len) { 546 | unsigned char *begin = row + i * comp; 547 | int diff = 1; 548 | len = 1; 549 | 550 | if (i < x - 1) { 551 | ++len; 552 | diff = memcmp(begin, row + (i + 1) * comp, comp); 553 | if (diff) { 554 | const unsigned char *prev = begin; 555 | for (k = i + 2; k < x && len < 128; ++k) { 556 | if (memcmp(prev, row + k * comp, comp)) { 557 | prev += comp; 558 | ++len; 559 | } else { 560 | --len; 561 | break; 562 | } 563 | } 564 | } else { 565 | for (k = i + 2; k < x && len < 128; ++k) { 566 | if (!memcmp(begin, row + k * comp, comp)) { 567 | ++len; 568 | } else { 569 | break; 570 | } 571 | } 572 | } 573 | } 574 | 575 | if (diff) { 576 | unsigned char header = STBIW_UCHAR(len - 1); 577 | s->func(s->context, &header, 1); 578 | for (k = 0; k < len; ++k) { 579 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); 580 | } 581 | } else { 582 | unsigned char header = STBIW_UCHAR(len - 129); 583 | s->func(s->context, &header, 1); 584 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); 585 | } 586 | } 587 | } 588 | } 589 | return 1; 590 | } 591 | 592 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { 593 | stbi__write_context s; 594 | stbi__start_write_callbacks(&s, func, context); 595 | return stbi_write_tga_core(&s, x, y, comp, (void *) data); 596 | } 597 | 598 | #ifndef STBI_WRITE_NO_STDIO 599 | 600 | STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { 601 | stbi__write_context s; 602 | if (stbi__start_write_file(&s, filename)) { 603 | int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); 604 | stbi__end_write_file(&s); 605 | return r; 606 | } else 607 | return 0; 608 | } 609 | 610 | #endif 611 | 612 | // ************************************************************************************************* 613 | // Radiance RGBE HDR writer 614 | // by Baldur Karlsson 615 | 616 | #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) 617 | 618 | static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { 619 | int exponent; 620 | float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); 621 | 622 | if (maxcomp < 1e-32f) { 623 | rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 624 | } else { 625 | float normalize = (float) frexp(maxcomp, &exponent) * 256.0f / maxcomp; 626 | 627 | rgbe[0] = (unsigned char) (linear[0] * normalize); 628 | rgbe[1] = (unsigned char) (linear[1] * normalize); 629 | rgbe[2] = (unsigned char) (linear[2] * normalize); 630 | rgbe[3] = (unsigned char) (exponent + 128); 631 | } 632 | } 633 | 634 | static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { 635 | unsigned char lengthbyte = STBIW_UCHAR(length + 128); 636 | STBIW_ASSERT(length + 128 <= 255); 637 | s->func(s->context, &lengthbyte, 1); 638 | s->func(s->context, &databyte, 1); 639 | } 640 | 641 | static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { 642 | unsigned char lengthbyte = STBIW_UCHAR(length); 643 | STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code 644 | s->func(s->context, &lengthbyte, 1); 645 | s->func(s->context, data, length); 646 | } 647 | 648 | static void 649 | stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { 650 | unsigned char scanlineheader[4] = {2, 2, 0, 0}; 651 | unsigned char rgbe[4]; 652 | float linear[3]; 653 | int x; 654 | 655 | scanlineheader[2] = (width & 0xff00) >> 8; 656 | scanlineheader[3] = (width & 0x00ff); 657 | 658 | /* skip RLE for images too small or large */ 659 | if (width < 8 || width >= 32768) { 660 | for (x = 0; x < width; x++) { 661 | switch (ncomp) { 662 | case 4: /* fallthrough */ 663 | case 3: 664 | linear[2] = scanline[x * ncomp + 2]; 665 | linear[1] = scanline[x * ncomp + 1]; 666 | linear[0] = scanline[x * ncomp + 0]; 667 | break; 668 | default: 669 | linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0]; 670 | break; 671 | } 672 | stbiw__linear_to_rgbe(rgbe, linear); 673 | s->func(s->context, rgbe, 4); 674 | } 675 | } else { 676 | int c, r; 677 | /* encode into scratch buffer */ 678 | for (x = 0; x < width; x++) { 679 | switch (ncomp) { 680 | case 4: /* fallthrough */ 681 | case 3: 682 | linear[2] = scanline[x * ncomp + 2]; 683 | linear[1] = scanline[x * ncomp + 1]; 684 | linear[0] = scanline[x * ncomp + 0]; 685 | break; 686 | default: 687 | linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0]; 688 | break; 689 | } 690 | stbiw__linear_to_rgbe(rgbe, linear); 691 | scratch[x + width * 0] = rgbe[0]; 692 | scratch[x + width * 1] = rgbe[1]; 693 | scratch[x + width * 2] = rgbe[2]; 694 | scratch[x + width * 3] = rgbe[3]; 695 | } 696 | 697 | s->func(s->context, scanlineheader, 4); 698 | 699 | /* RLE each component separately */ 700 | for (c = 0; c < 4; c++) { 701 | unsigned char *comp = &scratch[width * c]; 702 | 703 | x = 0; 704 | while (x < width) { 705 | // find first run 706 | r = x; 707 | while (r + 2 < width) { 708 | if (comp[r] == comp[r + 1] && comp[r] == comp[r + 2]) 709 | break; 710 | ++r; 711 | } 712 | if (r + 2 >= width) 713 | r = width; 714 | // dump up to first run 715 | while (x < r) { 716 | int len = r - x; 717 | if (len > 128) len = 128; 718 | stbiw__write_dump_data(s, len, &comp[x]); 719 | x += len; 720 | } 721 | // if there's a run, output it 722 | if (r + 2 < width) { // same test as what we break out of in search loop, so only true if we break'd 723 | // find next byte after run 724 | while (r < width && comp[r] == comp[x]) 725 | ++r; 726 | // output run up to r 727 | while (x < r) { 728 | int len = r - x; 729 | if (len > 127) len = 127; 730 | stbiw__write_run_data(s, len, comp[x]); 731 | x += len; 732 | } 733 | } 734 | } 735 | } 736 | } 737 | } 738 | 739 | static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) { 740 | if (y <= 0 || x <= 0 || data == NULL) 741 | return 0; 742 | else { 743 | // Each component is stored separately. Allocate scratch space for full output scanline. 744 | unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x * 4); 745 | int i, len; 746 | char buffer[128]; 747 | char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; 748 | s->func(s->context, header, sizeof(header) - 1); 749 | 750 | #ifdef __STDC_WANT_SECURE_LIB__ 751 | len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 752 | #else 753 | len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 754 | #endif 755 | s->func(s->context, buffer, len); 756 | 757 | for (i = 0; i < y; i++) 758 | stbiw__write_hdr_scanline(s, x, comp, scratch, 759 | data + comp * x * (stbi__flip_vertically_on_write ? y - 1 - i : i)); 760 | STBIW_FREE(scratch); 761 | return 1; 762 | } 763 | } 764 | 765 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { 766 | stbi__write_context s; 767 | stbi__start_write_callbacks(&s, func, context); 768 | return stbi_write_hdr_core(&s, x, y, comp, (float *) data); 769 | } 770 | 771 | #ifndef STBI_WRITE_NO_STDIO 772 | 773 | STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { 774 | stbi__write_context s; 775 | if (stbi__start_write_file(&s, filename)) { 776 | int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); 777 | stbi__end_write_file(&s); 778 | return r; 779 | } else 780 | return 0; 781 | } 782 | 783 | #endif // STBI_WRITE_NO_STDIO 784 | 785 | 786 | ////////////////////////////////////////////////////////////////////////////// 787 | // 788 | // PNG writer 789 | // 790 | 791 | #ifndef STBIW_ZLIB_COMPRESS 792 | // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() 793 | #define stbiw__sbraw(a) ((int *) (void *) (a) - 2) 794 | #define stbiw__sbm(a) stbiw__sbraw(a)[0] 795 | #define stbiw__sbn(a) stbiw__sbraw(a)[1] 796 | 797 | #define stbiw__sbneedgrow(a, n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) 798 | #define stbiw__sbmaybegrow(a, n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) 799 | #define stbiw__sbgrow(a, n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) 800 | 801 | #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) 802 | #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) 803 | #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) 804 | 805 | static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { 806 | int m = *arr ? 2 * stbiw__sbm(*arr) + increment : increment + 1; 807 | void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, 808 | *arr ? (stbiw__sbm(*arr) * itemsize + sizeof(int) * 2) : 0, 809 | itemsize * m + sizeof(int) * 2); 810 | STBIW_ASSERT(p); 811 | if (p) { 812 | if (!*arr) ((int *) p)[1] = 0; 813 | *arr = (void *) ((int *) p + 2); 814 | stbiw__sbm(*arr) = m; 815 | } 816 | return *arr; 817 | } 818 | 819 | static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { 820 | while (*bitcount >= 8) { 821 | stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); 822 | *bitbuffer >>= 8; 823 | *bitcount -= 8; 824 | } 825 | return data; 826 | } 827 | 828 | static int stbiw__zlib_bitrev(int code, int codebits) { 829 | int res = 0; 830 | while (codebits--) { 831 | res = (res << 1) | (code & 1); 832 | code >>= 1; 833 | } 834 | return res; 835 | } 836 | 837 | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) { 838 | int i; 839 | for (i = 0; i < limit && i < 258; ++i) 840 | if (a[i] != b[i]) break; 841 | return i; 842 | } 843 | 844 | static unsigned int stbiw__zhash(unsigned char *data) { 845 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); 846 | hash ^= hash << 3; 847 | hash += hash >> 5; 848 | hash ^= hash << 4; 849 | hash += hash >> 17; 850 | hash ^= hash << 25; 851 | hash += hash >> 6; 852 | return hash; 853 | } 854 | 855 | #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) 856 | #define stbiw__zlib_add(code, codebits) \ 857 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) 858 | #define stbiw__zlib_huffa(b, c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) 859 | // default huffman tables 860 | #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) 861 | #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) 862 | #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) 863 | #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) 864 | #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)) 865 | #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) 866 | 867 | #define stbiw__ZHASH 16384 868 | 869 | #endif // STBIW_ZLIB_COMPRESS 870 | 871 | STBIWDEF unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { 872 | #ifdef STBIW_ZLIB_COMPRESS 873 | // user provided a zlib compress implementation, use that 874 | return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); 875 | #else // use builtin 876 | 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, 877 | 99, 115, 131, 163, 195, 227, 258, 259}; 878 | 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, 879 | 5, 0}; 880 | static unsigned short distc[] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 881 | 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 32768}; 882 | 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, 883 | 12, 12, 13, 13}; 884 | unsigned int bitbuf = 0; 885 | int i, j, bitcount = 0; 886 | unsigned char *out = NULL; 887 | unsigned char ***hash_table = (unsigned char ***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char **)); 888 | if (hash_table == NULL) 889 | return NULL; 890 | if (quality < 5) quality = 5; 891 | 892 | stbiw__sbpush(out, 0x78); // DEFLATE 32K window 893 | stbiw__sbpush(out, 0x5e); // FLEVEL = 1 894 | stbiw__zlib_add(1, 1); // BFINAL = 1 895 | stbiw__zlib_add(1, 2); // BTYPE = 1 -- fixed huffman 896 | 897 | for (i = 0; i < stbiw__ZHASH; ++i) 898 | hash_table[i] = NULL; 899 | 900 | i = 0; 901 | while (i < data_len - 3) { 902 | // hash next 3 bytes of data to be compressed 903 | int h = stbiw__zhash(data + i) & (stbiw__ZHASH - 1), best = 3; 904 | unsigned char *bestloc = 0; 905 | unsigned char **hlist = hash_table[h]; 906 | int n = stbiw__sbcount(hlist); 907 | for (j = 0; j < n; ++j) { 908 | if (hlist[j] - data > i - 32768) { // if entry lies within window 909 | int d = stbiw__zlib_countm(hlist[j], data + i, data_len - i); 910 | if (d >= best) { 911 | best = d; 912 | bestloc = hlist[j]; 913 | } 914 | } 915 | } 916 | // when hash table entry is too long, delete half the entries 917 | if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2 * quality) { 918 | STBIW_MEMMOVE(hash_table[h], hash_table[h] + quality, sizeof(hash_table[h][0]) * quality); 919 | stbiw__sbn(hash_table[h]) = quality; 920 | } 921 | stbiw__sbpush(hash_table[h], data + i); 922 | 923 | if (bestloc) { 924 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal 925 | h = stbiw__zhash(data + i + 1) & (stbiw__ZHASH - 1); 926 | hlist = hash_table[h]; 927 | n = stbiw__sbcount(hlist); 928 | for (j = 0; j < n; ++j) { 929 | if (hlist[j] - data > i - 32767) { 930 | int e = stbiw__zlib_countm(hlist[j], data + i + 1, data_len - i - 1); 931 | if (e > best) { // if next match is better, bail on current match 932 | bestloc = NULL; 933 | break; 934 | } 935 | } 936 | } 937 | } 938 | 939 | if (bestloc) { 940 | int d = (int) (data + i - bestloc); // distance back 941 | STBIW_ASSERT(d <= 32767 && best <= 258); 942 | for (j = 0; best > lengthc[j + 1] - 1; ++j); 943 | stbiw__zlib_huff(j + 257); 944 | if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); 945 | for (j = 0; d > distc[j + 1] - 1; ++j); 946 | stbiw__zlib_add(stbiw__zlib_bitrev(j, 5), 5); 947 | if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); 948 | i += best; 949 | } else { 950 | stbiw__zlib_huffb(data[i]); 951 | ++i; 952 | } 953 | } 954 | // write out final bytes 955 | for (; i < data_len; ++i) 956 | stbiw__zlib_huffb(data[i]); 957 | stbiw__zlib_huff(256); // end of block 958 | // pad with 0 bits to byte boundary 959 | while (bitcount) 960 | stbiw__zlib_add(0, 1); 961 | 962 | for (i = 0; i < stbiw__ZHASH; ++i) 963 | (void) stbiw__sbfree(hash_table[i]); 964 | STBIW_FREE(hash_table); 965 | 966 | { 967 | // compute adler32 on input 968 | unsigned int s1 = 1, s2 = 0; 969 | int blocklen = (int) (data_len % 5552); 970 | j = 0; 971 | while (j < data_len) { 972 | for (i = 0; i < blocklen; ++i) { 973 | s1 += data[j + i]; 974 | s2 += s1; 975 | } 976 | s1 %= 65521; 977 | s2 %= 65521; 978 | j += blocklen; 979 | blocklen = 5552; 980 | } 981 | stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); 982 | stbiw__sbpush(out, STBIW_UCHAR(s2)); 983 | stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); 984 | stbiw__sbpush(out, STBIW_UCHAR(s1)); 985 | } 986 | *out_len = stbiw__sbn(out); 987 | // make returned pointer freeable 988 | STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); 989 | return (unsigned char *) stbiw__sbraw(out); 990 | #endif // STBIW_ZLIB_COMPRESS 991 | } 992 | 993 | static unsigned int stbiw__crc32(unsigned char *buffer, int len) { 994 | #ifdef STBIW_CRC32 995 | return STBIW_CRC32(buffer, len); 996 | #else 997 | static unsigned int crc_table[256] = 998 | { 999 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 1000 | 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 1001 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 1002 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 1003 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 1004 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 1005 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 1006 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 1007 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 1008 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 1009 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 1010 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 1011 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 1012 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 1013 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 1014 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 1015 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 1016 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 1017 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 1018 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 1019 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 1020 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 1021 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 1022 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 1023 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 1024 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 1025 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 1026 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 1027 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 1028 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 1029 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 1030 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 1031 | }; 1032 | 1033 | unsigned int crc = ~0u; 1034 | int i; 1035 | for (i = 0; i < len; ++i) 1036 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; 1037 | return ~crc; 1038 | #endif 1039 | } 1040 | 1041 | #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) 1042 | #define stbiw__wp32(data, v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); 1043 | #define stbiw__wptag(data, s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) 1044 | 1045 | static void stbiw__wpcrc(unsigned char **data, int len) { 1046 | unsigned int crc = stbiw__crc32(*data - len - 4, len + 4); 1047 | stbiw__wp32(*data, crc); 1048 | } 1049 | 1050 | static unsigned char stbiw__paeth(int a, int b, int c) { 1051 | int p = a + b - c, pa = abs(p - a), pb = abs(p - b), pc = abs(p - c); 1052 | if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); 1053 | if (pb <= pc) return STBIW_UCHAR(b); 1054 | return STBIW_UCHAR(c); 1055 | } 1056 | 1057 | // @OPTIMIZE: provide an option that always forces left-predict or paeth predict 1058 | static void 1059 | stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, 1060 | signed char *line_buffer) { 1061 | static int mapping[] = {0, 1, 2, 3, 4}; 1062 | static int firstmap[] = {0, 1, 0, 5, 6}; 1063 | int *mymap = (y != 0) ? mapping : firstmap; 1064 | int i; 1065 | int type = mymap[filter_type]; 1066 | unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height - 1 - y : y); 1067 | int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; 1068 | 1069 | if (type == 0) { 1070 | memcpy(line_buffer, z, width * n); 1071 | return; 1072 | } 1073 | 1074 | // first loop isn't optimized since it's just one pixel 1075 | for (i = 0; i < n; ++i) { 1076 | switch (type) { 1077 | case 1: 1078 | line_buffer[i] = z[i]; 1079 | break; 1080 | case 2: 1081 | line_buffer[i] = z[i] - z[i - signed_stride]; 1082 | break; 1083 | case 3: 1084 | line_buffer[i] = z[i] - (z[i - signed_stride] >> 1); 1085 | break; 1086 | case 4: 1087 | line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0, z[i - signed_stride], 0)); 1088 | break; 1089 | case 5: 1090 | line_buffer[i] = z[i]; 1091 | break; 1092 | case 6: 1093 | line_buffer[i] = z[i]; 1094 | break; 1095 | } 1096 | } 1097 | switch (type) { 1098 | case 1: 1099 | for (i = n; i < width * n; ++i) line_buffer[i] = z[i] - z[i - n]; 1100 | break; 1101 | case 2: 1102 | for (i = n; i < width * n; ++i) line_buffer[i] = z[i] - z[i - signed_stride]; 1103 | break; 1104 | case 3: 1105 | for (i = n; i < width * n; ++i) line_buffer[i] = z[i] - ((z[i - n] + z[i - signed_stride]) >> 1); 1106 | break; 1107 | case 4: 1108 | for (i = n; i < width * n; ++i) 1109 | line_buffer[i] = z[i] - stbiw__paeth(z[i - n], z[i - signed_stride], z[i - signed_stride - n]); 1110 | break; 1111 | case 5: 1112 | for (i = n; i < width * n; ++i) line_buffer[i] = z[i] - (z[i - n] >> 1); 1113 | break; 1114 | case 6: 1115 | for (i = n; i < width * n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i - n], 0, 0); 1116 | break; 1117 | } 1118 | } 1119 | 1120 | STBIWDEF unsigned char * 1121 | stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { 1122 | int force_filter = stbi_write_force_png_filter; 1123 | int ctype[5] = {-1, 0, 4, 2, 6}; 1124 | unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; 1125 | unsigned char *out, *o, *filt, *zlib; 1126 | signed char *line_buffer; 1127 | int j, zlen; 1128 | 1129 | if (stride_bytes == 0) 1130 | stride_bytes = x * n; 1131 | 1132 | if (force_filter >= 5) { 1133 | force_filter = -1; 1134 | } 1135 | 1136 | filt = (unsigned char *) STBIW_MALLOC((x * n + 1) * y); 1137 | if (!filt) return 0; 1138 | line_buffer = (signed char *) STBIW_MALLOC(x * n); 1139 | if (!line_buffer) { 1140 | STBIW_FREE(filt); 1141 | return 0; 1142 | } 1143 | for (j = 0; j < y; ++j) { 1144 | int filter_type; 1145 | if (force_filter > -1) { 1146 | filter_type = force_filter; 1147 | stbiw__encode_png_line((unsigned char *) (pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); 1148 | } else { // Estimate the best filter by running through all of them: 1149 | int best_filter = 0, best_filter_val = 0x7fffffff, est, i; 1150 | for (filter_type = 0; filter_type < 5; filter_type++) { 1151 | stbiw__encode_png_line((unsigned char *) (pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); 1152 | 1153 | // Estimate the entropy of the line using this filter; the less, the better. 1154 | est = 0; 1155 | for (i = 0; i < x * n; ++i) { 1156 | est += abs((signed char) line_buffer[i]); 1157 | } 1158 | if (est < best_filter_val) { 1159 | best_filter_val = est; 1160 | best_filter = filter_type; 1161 | } 1162 | } 1163 | if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it 1164 | stbiw__encode_png_line((unsigned char *) (pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); 1165 | filter_type = best_filter; 1166 | } 1167 | } 1168 | // when we get here, filter_type contains the filter type, and line_buffer contains the data 1169 | filt[j * (x * n + 1)] = (unsigned char) filter_type; 1170 | STBIW_MEMMOVE(filt + j * (x * n + 1) + 1, line_buffer, x * n); 1171 | } 1172 | STBIW_FREE(line_buffer); 1173 | zlib = stbi_zlib_compress(filt, y * (x * n + 1), &zlen, stbi_write_png_compression_level); 1174 | STBIW_FREE(filt); 1175 | if (!zlib) return 0; 1176 | 1177 | // each tag requires 12 bytes of overhead 1178 | out = (unsigned char *) STBIW_MALLOC(8 + 12 + 13 + 12 + zlen + 12); 1179 | if (!out) return 0; 1180 | *out_len = 8 + 12 + 13 + 12 + zlen + 12; 1181 | 1182 | o = out; 1183 | STBIW_MEMMOVE(o, sig, 8); 1184 | o += 8; 1185 | stbiw__wp32(o, 13); // header length 1186 | stbiw__wptag(o, "IHDR"); 1187 | stbiw__wp32(o, x); 1188 | stbiw__wp32(o, y); 1189 | *o++ = 8; 1190 | *o++ = STBIW_UCHAR(ctype[n]); 1191 | *o++ = 0; 1192 | *o++ = 0; 1193 | *o++ = 0; 1194 | stbiw__wpcrc(&o, 13); 1195 | 1196 | stbiw__wp32(o, zlen); 1197 | stbiw__wptag(o, "IDAT"); 1198 | STBIW_MEMMOVE(o, zlib, zlen); 1199 | o += zlen; 1200 | STBIW_FREE(zlib); 1201 | stbiw__wpcrc(&o, zlen); 1202 | 1203 | stbiw__wp32(o, 0); 1204 | stbiw__wptag(o, "IEND"); 1205 | stbiw__wpcrc(&o, 0); 1206 | 1207 | STBIW_ASSERT(o == out + *out_len); 1208 | 1209 | return out; 1210 | } 1211 | 1212 | #ifndef STBI_WRITE_NO_STDIO 1213 | 1214 | STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { 1215 | FILE *f; 1216 | int len; 1217 | unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); 1218 | if (png == NULL) return 0; 1219 | 1220 | f = stbiw__fopen(filename, "wb"); 1221 | if (!f) { 1222 | STBIW_FREE(png); 1223 | return 0; 1224 | } 1225 | fwrite(png, 1, len, f); 1226 | fclose(f); 1227 | STBIW_FREE(png); 1228 | return 1; 1229 | } 1230 | 1231 | #endif 1232 | 1233 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, 1234 | int stride_bytes) { 1235 | int len; 1236 | unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); 1237 | if (png == NULL) return 0; 1238 | func(context, png, len); 1239 | STBIW_FREE(png); 1240 | return 1; 1241 | } 1242 | 1243 | 1244 | /* *************************************************************************** 1245 | * 1246 | * JPEG writer 1247 | * 1248 | * This is based on Jon Olick's jo_jpeg.cpp: 1249 | * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html 1250 | */ 1251 | 1252 | 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, 1253 | 25, 30, 41, 43, 9, 11, 18, 1254 | 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 1255 | 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 1256 | 58, 62, 63}; 1257 | 1258 | static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { 1259 | int bitBuf = *bitBufP, bitCnt = *bitCntP; 1260 | bitCnt += bs[1]; 1261 | bitBuf |= bs[0] << (24 - bitCnt); 1262 | while (bitCnt >= 8) { 1263 | unsigned char c = (bitBuf >> 16) & 255; 1264 | stbiw__putc(s, c); 1265 | if (c == 255) { 1266 | stbiw__putc(s, 0); 1267 | } 1268 | bitBuf <<= 8; 1269 | bitCnt -= 8; 1270 | } 1271 | *bitBufP = bitBuf; 1272 | *bitCntP = bitCnt; 1273 | } 1274 | 1275 | static void 1276 | stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { 1277 | float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; 1278 | float z1, z2, z3, z4, z5, z11, z13; 1279 | 1280 | float tmp0 = d0 + d7; 1281 | float tmp7 = d0 - d7; 1282 | float tmp1 = d1 + d6; 1283 | float tmp6 = d1 - d6; 1284 | float tmp2 = d2 + d5; 1285 | float tmp5 = d2 - d5; 1286 | float tmp3 = d3 + d4; 1287 | float tmp4 = d3 - d4; 1288 | 1289 | // Even part 1290 | float tmp10 = tmp0 + tmp3; // phase 2 1291 | float tmp13 = tmp0 - tmp3; 1292 | float tmp11 = tmp1 + tmp2; 1293 | float tmp12 = tmp1 - tmp2; 1294 | 1295 | d0 = tmp10 + tmp11; // phase 3 1296 | d4 = tmp10 - tmp11; 1297 | 1298 | z1 = (tmp12 + tmp13) * 0.707106781f; // c4 1299 | d2 = tmp13 + z1; // phase 5 1300 | d6 = tmp13 - z1; 1301 | 1302 | // Odd part 1303 | tmp10 = tmp4 + tmp5; // phase 2 1304 | tmp11 = tmp5 + tmp6; 1305 | tmp12 = tmp6 + tmp7; 1306 | 1307 | // The rotator is modified from fig 4-8 to avoid extra negations. 1308 | z5 = (tmp10 - tmp12) * 0.382683433f; // c6 1309 | z2 = tmp10 * 0.541196100f + z5; // c2-c6 1310 | z4 = tmp12 * 1.306562965f + z5; // c2+c6 1311 | z3 = tmp11 * 0.707106781f; // c4 1312 | 1313 | z11 = tmp7 + z3; // phase 5 1314 | z13 = tmp7 - z3; 1315 | 1316 | *d5p = z13 + z2; // phase 6 1317 | *d3p = z13 - z2; 1318 | *d1p = z11 + z4; 1319 | *d7p = z11 - z4; 1320 | 1321 | *d0p = d0; 1322 | *d2p = d2; 1323 | *d4p = d4; 1324 | *d6p = d6; 1325 | } 1326 | 1327 | static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { 1328 | int tmp1 = val < 0 ? -val : val; 1329 | val = val < 0 ? val - 1 : val; 1330 | bits[1] = 1; 1331 | while (tmp1 >>= 1) { 1332 | ++bits[1]; 1333 | } 1334 | bits[0] = val & ((1 << bits[1]) - 1); 1335 | } 1336 | 1337 | static int 1338 | stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, int du_stride, float *fdtbl, int DC, 1339 | const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) { 1340 | const unsigned short EOB[2] = {HTAC[0x00][0], HTAC[0x00][1]}; 1341 | const unsigned short M16zeroes[2] = {HTAC[0xF0][0], HTAC[0xF0][1]}; 1342 | int dataOff, i, j, n, diff, end0pos, x, y; 1343 | int DU[64]; 1344 | 1345 | // DCT rows 1346 | for (dataOff = 0, n = du_stride * 8; dataOff < n; dataOff += du_stride) { 1347 | stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff + 1], &CDU[dataOff + 2], &CDU[dataOff + 3], &CDU[dataOff + 4], 1348 | &CDU[dataOff + 5], &CDU[dataOff + 6], &CDU[dataOff + 7]); 1349 | } 1350 | // DCT columns 1351 | for (dataOff = 0; dataOff < 8; ++dataOff) { 1352 | stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff + du_stride], &CDU[dataOff + du_stride * 2], 1353 | &CDU[dataOff + du_stride * 3], &CDU[dataOff + du_stride * 4], 1354 | &CDU[dataOff + du_stride * 5], &CDU[dataOff + du_stride * 6], &CDU[dataOff + du_stride * 7]); 1355 | } 1356 | // Quantize/descale/zigzag the coefficients 1357 | for (y = 0, j = 0; y < 8; ++y) { 1358 | for (x = 0; x < 8; ++x, ++j) { 1359 | float v; 1360 | i = y * du_stride + x; 1361 | v = CDU[i] * fdtbl[j]; 1362 | // DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f)); 1363 | // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway? 1364 | DU[stbiw__jpg_ZigZag[j]] = (int) (v < 0 ? v - 0.5f : v + 0.5f); 1365 | } 1366 | } 1367 | 1368 | // Encode DC 1369 | diff = DU[0] - DC; 1370 | if (diff == 0) { 1371 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]); 1372 | } else { 1373 | unsigned short bits[2]; 1374 | stbiw__jpg_calcBits(diff, bits); 1375 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]); 1376 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); 1377 | } 1378 | // Encode ACs 1379 | end0pos = 63; 1380 | for (; (end0pos > 0) && (DU[end0pos] == 0); --end0pos) { 1381 | } 1382 | // end0pos = first element in reverse order !=0 1383 | if (end0pos == 0) { 1384 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); 1385 | return DU[0]; 1386 | } 1387 | for (i = 1; i <= end0pos; ++i) { 1388 | int startpos = i; 1389 | int nrzeroes; 1390 | unsigned short bits[2]; 1391 | for (; DU[i] == 0 && i <= end0pos; ++i) { 1392 | } 1393 | nrzeroes = i - startpos; 1394 | if (nrzeroes >= 16) { 1395 | int lng = nrzeroes >> 4; 1396 | int nrmarker; 1397 | for (nrmarker = 1; nrmarker <= lng; ++nrmarker) 1398 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); 1399 | nrzeroes &= 15; 1400 | } 1401 | stbiw__jpg_calcBits(DU[i], bits); 1402 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes << 4) + bits[1]]); 1403 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); 1404 | } 1405 | if (end0pos != 63) { 1406 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); 1407 | } 1408 | return DU[0]; 1409 | } 1410 | 1411 | static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void *data, int quality) { 1412 | // Constants that don't pollute global namespace 1413 | 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}; 1414 | static const unsigned char std_dc_luminance_values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; 1415 | 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}; 1416 | static const unsigned char std_ac_luminance_values[] = { 1417 | 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 1418 | 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 1419 | 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 1420 | 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 1421 | 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 1422 | 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 1423 | 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 1424 | 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 1425 | 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 1426 | 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 1427 | 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 1428 | 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 1429 | 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa 1430 | }; 1431 | 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}; 1432 | static const unsigned char std_dc_chrominance_values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; 1433 | 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}; 1434 | static const unsigned char std_ac_chrominance_values[] = { 1435 | 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 1436 | 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 1437 | 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 1438 | 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 1439 | 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 1440 | 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 1441 | 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 1442 | 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 1443 | 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 1444 | 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 1445 | 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 1446 | 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 1447 | 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa 1448 | }; 1449 | // Huffman tables 1450 | static const unsigned short YDC_HT[256][2] = {{0, 2}, 1451 | {2, 3}, 1452 | {3, 3}, 1453 | {4, 3}, 1454 | {5, 3}, 1455 | {6, 3}, 1456 | {14, 4}, 1457 | {30, 5}, 1458 | {62, 6}, 1459 | {126, 7}, 1460 | {254, 8}, 1461 | {510, 9}}; 1462 | static const unsigned short UVDC_HT[256][2] = {{0, 2}, 1463 | {1, 2}, 1464 | {2, 2}, 1465 | {6, 3}, 1466 | {14, 4}, 1467 | {30, 5}, 1468 | {62, 6}, 1469 | {126, 7}, 1470 | {254, 8}, 1471 | {510, 9}, 1472 | {1022, 10}, 1473 | {2046, 11}}; 1474 | static const unsigned short YAC_HT[256][2] = { 1475 | {10, 4}, 1476 | {0, 2}, 1477 | {1, 2}, 1478 | {4, 3}, 1479 | {11, 4}, 1480 | {26, 5}, 1481 | {120, 7}, 1482 | {248, 8}, 1483 | {1014, 10}, 1484 | {65410, 16}, 1485 | {65411, 16}, 1486 | {0, 0}, 1487 | {0, 0}, 1488 | {0, 0}, 1489 | {0, 0}, 1490 | {0, 0}, 1491 | {0, 0}, 1492 | {12, 4}, 1493 | {27, 5}, 1494 | {121, 7}, 1495 | {502, 9}, 1496 | {2038, 11}, 1497 | {65412, 16}, 1498 | {65413, 16}, 1499 | {65414, 16}, 1500 | {65415, 16}, 1501 | {65416, 16}, 1502 | {0, 0}, 1503 | {0, 0}, 1504 | {0, 0}, 1505 | {0, 0}, 1506 | {0, 0}, 1507 | {0, 0}, 1508 | {28, 5}, 1509 | {249, 8}, 1510 | {1015, 10}, 1511 | {4084, 12}, 1512 | {65417, 16}, 1513 | {65418, 16}, 1514 | {65419, 16}, 1515 | {65420, 16}, 1516 | {65421, 16}, 1517 | {65422, 16}, 1518 | {0, 0}, 1519 | {0, 0}, 1520 | {0, 0}, 1521 | {0, 0}, 1522 | {0, 0}, 1523 | {0, 0}, 1524 | {58, 6}, 1525 | {503, 9}, 1526 | {4085, 12}, 1527 | {65423, 16}, 1528 | {65424, 16}, 1529 | {65425, 16}, 1530 | {65426, 16}, 1531 | {65427, 16}, 1532 | {65428, 16}, 1533 | {65429, 16}, 1534 | {0, 0}, 1535 | {0, 0}, 1536 | {0, 0}, 1537 | {0, 0}, 1538 | {0, 0}, 1539 | {0, 0}, 1540 | {59, 6}, 1541 | {1016, 10}, 1542 | {65430, 16}, 1543 | {65431, 16}, 1544 | {65432, 16}, 1545 | {65433, 16}, 1546 | {65434, 16}, 1547 | {65435, 16}, 1548 | {65436, 16}, 1549 | {65437, 16}, 1550 | {0, 0}, 1551 | {0, 0}, 1552 | {0, 0}, 1553 | {0, 0}, 1554 | {0, 0}, 1555 | {0, 0}, 1556 | {122, 7}, 1557 | {2039, 11}, 1558 | {65438, 16}, 1559 | {65439, 16}, 1560 | {65440, 16}, 1561 | {65441, 16}, 1562 | {65442, 16}, 1563 | {65443, 16}, 1564 | {65444, 16}, 1565 | {65445, 16}, 1566 | {0, 0}, 1567 | {0, 0}, 1568 | {0, 0}, 1569 | {0, 0}, 1570 | {0, 0}, 1571 | {0, 0}, 1572 | {123, 7}, 1573 | {4086, 12}, 1574 | {65446, 16}, 1575 | {65447, 16}, 1576 | {65448, 16}, 1577 | {65449, 16}, 1578 | {65450, 16}, 1579 | {65451, 16}, 1580 | {65452, 16}, 1581 | {65453, 16}, 1582 | {0, 0}, 1583 | {0, 0}, 1584 | {0, 0}, 1585 | {0, 0}, 1586 | {0, 0}, 1587 | {0, 0}, 1588 | {250, 8}, 1589 | {4087, 12}, 1590 | {65454, 16}, 1591 | {65455, 16}, 1592 | {65456, 16}, 1593 | {65457, 16}, 1594 | {65458, 16}, 1595 | {65459, 16}, 1596 | {65460, 16}, 1597 | {65461, 16}, 1598 | {0, 0}, 1599 | {0, 0}, 1600 | {0, 0}, 1601 | {0, 0}, 1602 | {0, 0}, 1603 | {0, 0}, 1604 | {504, 9}, 1605 | {32704, 15}, 1606 | {65462, 16}, 1607 | {65463, 16}, 1608 | {65464, 16}, 1609 | {65465, 16}, 1610 | {65466, 16}, 1611 | {65467, 16}, 1612 | {65468, 16}, 1613 | {65469, 16}, 1614 | {0, 0}, 1615 | {0, 0}, 1616 | {0, 0}, 1617 | {0, 0}, 1618 | {0, 0}, 1619 | {0, 0}, 1620 | {505, 9}, 1621 | {65470, 16}, 1622 | {65471, 16}, 1623 | {65472, 16}, 1624 | {65473, 16}, 1625 | {65474, 16}, 1626 | {65475, 16}, 1627 | {65476, 16}, 1628 | {65477, 16}, 1629 | {65478, 16}, 1630 | {0, 0}, 1631 | {0, 0}, 1632 | {0, 0}, 1633 | {0, 0}, 1634 | {0, 0}, 1635 | {0, 0}, 1636 | {506, 9}, 1637 | {65479, 16}, 1638 | {65480, 16}, 1639 | {65481, 16}, 1640 | {65482, 16}, 1641 | {65483, 16}, 1642 | {65484, 16}, 1643 | {65485, 16}, 1644 | {65486, 16}, 1645 | {65487, 16}, 1646 | {0, 0}, 1647 | {0, 0}, 1648 | {0, 0}, 1649 | {0, 0}, 1650 | {0, 0}, 1651 | {0, 0}, 1652 | {1017, 10}, 1653 | {65488, 16}, 1654 | {65489, 16}, 1655 | {65490, 16}, 1656 | {65491, 16}, 1657 | {65492, 16}, 1658 | {65493, 16}, 1659 | {65494, 16}, 1660 | {65495, 16}, 1661 | {65496, 16}, 1662 | {0, 0}, 1663 | {0, 0}, 1664 | {0, 0}, 1665 | {0, 0}, 1666 | {0, 0}, 1667 | {0, 0}, 1668 | {1018, 10}, 1669 | {65497, 16}, 1670 | {65498, 16}, 1671 | {65499, 16}, 1672 | {65500, 16}, 1673 | {65501, 16}, 1674 | {65502, 16}, 1675 | {65503, 16}, 1676 | {65504, 16}, 1677 | {65505, 16}, 1678 | {0, 0}, 1679 | {0, 0}, 1680 | {0, 0}, 1681 | {0, 0}, 1682 | {0, 0}, 1683 | {0, 0}, 1684 | {2040, 11}, 1685 | {65506, 16}, 1686 | {65507, 16}, 1687 | {65508, 16}, 1688 | {65509, 16}, 1689 | {65510, 16}, 1690 | {65511, 16}, 1691 | {65512, 16}, 1692 | {65513, 16}, 1693 | {65514, 16}, 1694 | {0, 0}, 1695 | {0, 0}, 1696 | {0, 0}, 1697 | {0, 0}, 1698 | {0, 0}, 1699 | {0, 0}, 1700 | {65515, 16}, 1701 | {65516, 16}, 1702 | {65517, 16}, 1703 | {65518, 16}, 1704 | {65519, 16}, 1705 | {65520, 16}, 1706 | {65521, 16}, 1707 | {65522, 16}, 1708 | {65523, 16}, 1709 | {65524, 16}, 1710 | {0, 0}, 1711 | {0, 0}, 1712 | {0, 0}, 1713 | {0, 0}, 1714 | {0, 0}, 1715 | {2041, 11}, 1716 | {65525, 16}, 1717 | {65526, 16}, 1718 | {65527, 16}, 1719 | {65528, 16}, 1720 | {65529, 16}, 1721 | {65530, 16}, 1722 | {65531, 16}, 1723 | {65532, 16}, 1724 | {65533, 16}, 1725 | {65534, 16}, 1726 | {0, 0}, 1727 | {0, 0}, 1728 | {0, 0}, 1729 | {0, 0}, 1730 | {0, 0} 1731 | }; 1732 | static const unsigned short UVAC_HT[256][2] = { 1733 | {0, 2}, 1734 | {1, 2}, 1735 | {4, 3}, 1736 | {10, 4}, 1737 | {24, 5}, 1738 | {25, 5}, 1739 | {56, 6}, 1740 | {120, 7}, 1741 | {500, 9}, 1742 | {1014, 10}, 1743 | {4084, 12}, 1744 | {0, 0}, 1745 | {0, 0}, 1746 | {0, 0}, 1747 | {0, 0}, 1748 | {0, 0}, 1749 | {0, 0}, 1750 | {11, 4}, 1751 | {57, 6}, 1752 | {246, 8}, 1753 | {501, 9}, 1754 | {2038, 11}, 1755 | {4085, 12}, 1756 | {65416, 16}, 1757 | {65417, 16}, 1758 | {65418, 16}, 1759 | {65419, 16}, 1760 | {0, 0}, 1761 | {0, 0}, 1762 | {0, 0}, 1763 | {0, 0}, 1764 | {0, 0}, 1765 | {0, 0}, 1766 | {26, 5}, 1767 | {247, 8}, 1768 | {1015, 10}, 1769 | {4086, 12}, 1770 | {32706, 15}, 1771 | {65420, 16}, 1772 | {65421, 16}, 1773 | {65422, 16}, 1774 | {65423, 16}, 1775 | {65424, 16}, 1776 | {0, 0}, 1777 | {0, 0}, 1778 | {0, 0}, 1779 | {0, 0}, 1780 | {0, 0}, 1781 | {0, 0}, 1782 | {27, 5}, 1783 | {248, 8}, 1784 | {1016, 10}, 1785 | {4087, 12}, 1786 | {65425, 16}, 1787 | {65426, 16}, 1788 | {65427, 16}, 1789 | {65428, 16}, 1790 | {65429, 16}, 1791 | {65430, 16}, 1792 | {0, 0}, 1793 | {0, 0}, 1794 | {0, 0}, 1795 | {0, 0}, 1796 | {0, 0}, 1797 | {0, 0}, 1798 | {58, 6}, 1799 | {502, 9}, 1800 | {65431, 16}, 1801 | {65432, 16}, 1802 | {65433, 16}, 1803 | {65434, 16}, 1804 | {65435, 16}, 1805 | {65436, 16}, 1806 | {65437, 16}, 1807 | {65438, 16}, 1808 | {0, 0}, 1809 | {0, 0}, 1810 | {0, 0}, 1811 | {0, 0}, 1812 | {0, 0}, 1813 | {0, 0}, 1814 | {59, 6}, 1815 | {1017, 10}, 1816 | {65439, 16}, 1817 | {65440, 16}, 1818 | {65441, 16}, 1819 | {65442, 16}, 1820 | {65443, 16}, 1821 | {65444, 16}, 1822 | {65445, 16}, 1823 | {65446, 16}, 1824 | {0, 0}, 1825 | {0, 0}, 1826 | {0, 0}, 1827 | {0, 0}, 1828 | {0, 0}, 1829 | {0, 0}, 1830 | {121, 7}, 1831 | {2039, 11}, 1832 | {65447, 16}, 1833 | {65448, 16}, 1834 | {65449, 16}, 1835 | {65450, 16}, 1836 | {65451, 16}, 1837 | {65452, 16}, 1838 | {65453, 16}, 1839 | {65454, 16}, 1840 | {0, 0}, 1841 | {0, 0}, 1842 | {0, 0}, 1843 | {0, 0}, 1844 | {0, 0}, 1845 | {0, 0}, 1846 | {122, 7}, 1847 | {2040, 11}, 1848 | {65455, 16}, 1849 | {65456, 16}, 1850 | {65457, 16}, 1851 | {65458, 16}, 1852 | {65459, 16}, 1853 | {65460, 16}, 1854 | {65461, 16}, 1855 | {65462, 16}, 1856 | {0, 0}, 1857 | {0, 0}, 1858 | {0, 0}, 1859 | {0, 0}, 1860 | {0, 0}, 1861 | {0, 0}, 1862 | {249, 8}, 1863 | {65463, 16}, 1864 | {65464, 16}, 1865 | {65465, 16}, 1866 | {65466, 16}, 1867 | {65467, 16}, 1868 | {65468, 16}, 1869 | {65469, 16}, 1870 | {65470, 16}, 1871 | {65471, 16}, 1872 | {0, 0}, 1873 | {0, 0}, 1874 | {0, 0}, 1875 | {0, 0}, 1876 | {0, 0}, 1877 | {0, 0}, 1878 | {503, 9}, 1879 | {65472, 16}, 1880 | {65473, 16}, 1881 | {65474, 16}, 1882 | {65475, 16}, 1883 | {65476, 16}, 1884 | {65477, 16}, 1885 | {65478, 16}, 1886 | {65479, 16}, 1887 | {65480, 16}, 1888 | {0, 0}, 1889 | {0, 0}, 1890 | {0, 0}, 1891 | {0, 0}, 1892 | {0, 0}, 1893 | {0, 0}, 1894 | {504, 9}, 1895 | {65481, 16}, 1896 | {65482, 16}, 1897 | {65483, 16}, 1898 | {65484, 16}, 1899 | {65485, 16}, 1900 | {65486, 16}, 1901 | {65487, 16}, 1902 | {65488, 16}, 1903 | {65489, 16}, 1904 | {0, 0}, 1905 | {0, 0}, 1906 | {0, 0}, 1907 | {0, 0}, 1908 | {0, 0}, 1909 | {0, 0}, 1910 | {505, 9}, 1911 | {65490, 16}, 1912 | {65491, 16}, 1913 | {65492, 16}, 1914 | {65493, 16}, 1915 | {65494, 16}, 1916 | {65495, 16}, 1917 | {65496, 16}, 1918 | {65497, 16}, 1919 | {65498, 16}, 1920 | {0, 0}, 1921 | {0, 0}, 1922 | {0, 0}, 1923 | {0, 0}, 1924 | {0, 0}, 1925 | {0, 0}, 1926 | {506, 9}, 1927 | {65499, 16}, 1928 | {65500, 16}, 1929 | {65501, 16}, 1930 | {65502, 16}, 1931 | {65503, 16}, 1932 | {65504, 16}, 1933 | {65505, 16}, 1934 | {65506, 16}, 1935 | {65507, 16}, 1936 | {0, 0}, 1937 | {0, 0}, 1938 | {0, 0}, 1939 | {0, 0}, 1940 | {0, 0}, 1941 | {0, 0}, 1942 | {2041, 11}, 1943 | {65508, 16}, 1944 | {65509, 16}, 1945 | {65510, 16}, 1946 | {65511, 16}, 1947 | {65512, 16}, 1948 | {65513, 16}, 1949 | {65514, 16}, 1950 | {65515, 16}, 1951 | {65516, 16}, 1952 | {0, 0}, 1953 | {0, 0}, 1954 | {0, 0}, 1955 | {0, 0}, 1956 | {0, 0}, 1957 | {0, 0}, 1958 | {16352, 14}, 1959 | {65517, 16}, 1960 | {65518, 16}, 1961 | {65519, 16}, 1962 | {65520, 16}, 1963 | {65521, 16}, 1964 | {65522, 16}, 1965 | {65523, 16}, 1966 | {65524, 16}, 1967 | {65525, 16}, 1968 | {0, 0}, 1969 | {0, 0}, 1970 | {0, 0}, 1971 | {0, 0}, 1972 | {0, 0}, 1973 | {1018, 10}, 1974 | {32707, 15}, 1975 | {65526, 16}, 1976 | {65527, 16}, 1977 | {65528, 16}, 1978 | {65529, 16}, 1979 | {65530, 16}, 1980 | {65531, 16}, 1981 | {65532, 16}, 1982 | {65533, 16}, 1983 | {65534, 16}, 1984 | {0, 0}, 1985 | {0, 0}, 1986 | {0, 0}, 1987 | {0, 0}, 1988 | {0, 0} 1989 | }; 1990 | 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, 1991 | 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 1992 | 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 1993 | 101, 72, 92, 95, 98, 112, 100, 103, 99}; 1994 | 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, 1995 | 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 1996 | 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 1997 | 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}; 1998 | static const float aasf[] = {1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1999 | 1.175875602f * 2.828427125f, 2000 | 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 2001 | 0.275899379f * 2.828427125f}; 2002 | 2003 | int row, col, i, k, subsample; 2004 | float fdtbl_Y[64], fdtbl_UV[64]; 2005 | unsigned char YTable[64], UVTable[64]; 2006 | 2007 | if (!data || !width || !height || comp > 4 || comp < 1) { 2008 | return 0; 2009 | } 2010 | 2011 | quality = quality ? quality : 90; 2012 | subsample = quality <= 90 ? 1 : 0; 2013 | quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; 2014 | quality = quality < 50 ? 5000 / quality : 200 - quality * 2; 2015 | 2016 | for (i = 0; i < 64; ++i) { 2017 | int uvti, yti = (YQT[i] * quality + 50) / 100; 2018 | YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); 2019 | uvti = (UVQT[i] * quality + 50) / 100; 2020 | UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); 2021 | } 2022 | 2023 | for (row = 0, k = 0; row < 8; ++row) { 2024 | for (col = 0; col < 8; ++col, ++k) { 2025 | fdtbl_Y[k] = 1 / (YTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); 2026 | fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); 2027 | } 2028 | } 2029 | 2030 | // Write Headers 2031 | { 2032 | static const unsigned char head0[] = {0xFF, 0xD8, 0xFF, 0xE0, 0, 0x10, 'J', 'F', 'I', 'F', 0, 1, 1, 0, 0, 1, 0, 2033 | 1, 0, 0, 0xFF, 0xDB, 0, 0x84, 0}; 2034 | static const unsigned char head2[] = {0xFF, 0xDA, 0, 0xC, 3, 1, 0, 2, 0x11, 3, 0x11, 0, 0x3F, 0}; 2035 | const unsigned char head1[] = {0xFF, 0xC0, 0, 0x11, 8, (unsigned char) (height >> 8), STBIW_UCHAR(height), 2036 | (unsigned char) (width >> 8), STBIW_UCHAR(width), 2037 | 3, 1, (unsigned char) (subsample ? 0x22 : 0x11), 0, 2, 0x11, 1, 3, 0x11, 1, 0xFF, 2038 | 0xC4, 0x01, 0xA2, 0}; 2039 | s->func(s->context, (void *) head0, sizeof(head0)); 2040 | s->func(s->context, (void *) YTable, sizeof(YTable)); 2041 | stbiw__putc(s, 1); 2042 | s->func(s->context, UVTable, sizeof(UVTable)); 2043 | s->func(s->context, (void *) head1, sizeof(head1)); 2044 | s->func(s->context, (void *) (std_dc_luminance_nrcodes + 1), sizeof(std_dc_luminance_nrcodes) - 1); 2045 | s->func(s->context, (void *) std_dc_luminance_values, sizeof(std_dc_luminance_values)); 2046 | stbiw__putc(s, 0x10); // HTYACinfo 2047 | s->func(s->context, (void *) (std_ac_luminance_nrcodes + 1), sizeof(std_ac_luminance_nrcodes) - 1); 2048 | s->func(s->context, (void *) std_ac_luminance_values, sizeof(std_ac_luminance_values)); 2049 | stbiw__putc(s, 1); // HTUDCinfo 2050 | s->func(s->context, (void *) (std_dc_chrominance_nrcodes + 1), sizeof(std_dc_chrominance_nrcodes) - 1); 2051 | s->func(s->context, (void *) std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); 2052 | stbiw__putc(s, 0x11); // HTUACinfo 2053 | s->func(s->context, (void *) (std_ac_chrominance_nrcodes + 1), sizeof(std_ac_chrominance_nrcodes) - 1); 2054 | s->func(s->context, (void *) std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); 2055 | s->func(s->context, (void *) head2, sizeof(head2)); 2056 | } 2057 | 2058 | // Encode 8x8 macroblocks 2059 | { 2060 | static const unsigned short fillBits[] = {0x7F, 7}; 2061 | int DCY = 0, DCU = 0, DCV = 0; 2062 | int bitBuf = 0, bitCnt = 0; 2063 | // comp == 2 is grey+alpha (alpha is ignored) 2064 | int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; 2065 | const unsigned char *dataR = (const unsigned char *) data; 2066 | const unsigned char *dataG = dataR + ofsG; 2067 | const unsigned char *dataB = dataR + ofsB; 2068 | int x, y, pos; 2069 | if (subsample) { 2070 | for (y = 0; y < height; y += 16) { 2071 | for (x = 0; x < width; x += 16) { 2072 | float Y[256], U[256], V[256]; 2073 | for (row = y, pos = 0; row < y + 16; ++row) { 2074 | // row >= height => use last input row 2075 | int clamped_row = (row < height) ? row : height - 1; 2076 | int base_p = 2077 | (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) : clamped_row) * width * 2078 | comp; 2079 | for (col = x; col < x + 16; ++col, ++pos) { 2080 | // if col >= width => use pixel from last input column 2081 | int p = base_p + ((col < width) ? col : (width - 1)) * comp; 2082 | float r = dataR[p], g = dataG[p], b = dataB[p]; 2083 | Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; 2084 | U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; 2085 | V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; 2086 | } 2087 | } 2088 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); 2089 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); 2090 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); 2091 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); 2092 | 2093 | // subsample U,V 2094 | { 2095 | float subU[64], subV[64]; 2096 | int yy, xx; 2097 | for (yy = 0, pos = 0; yy < 8; ++yy) { 2098 | for (xx = 0; xx < 8; ++xx, ++pos) { 2099 | int j = yy * 32 + xx * 2; 2100 | subU[pos] = (U[j + 0] + U[j + 1] + U[j + 16] + U[j + 17]) * 0.25f; 2101 | subV[pos] = (V[j + 0] + V[j + 1] + V[j + 16] + V[j + 17]) * 0.25f; 2102 | } 2103 | } 2104 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); 2105 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); 2106 | } 2107 | } 2108 | } 2109 | } else { 2110 | for (y = 0; y < height; y += 8) { 2111 | for (x = 0; x < width; x += 8) { 2112 | float Y[64], U[64], V[64]; 2113 | for (row = y, pos = 0; row < y + 8; ++row) { 2114 | // row >= height => use last input row 2115 | int clamped_row = (row < height) ? row : height - 1; 2116 | int base_p = 2117 | (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) : clamped_row) * width * 2118 | comp; 2119 | for (col = x; col < x + 8; ++col, ++pos) { 2120 | // if col >= width => use pixel from last input column 2121 | int p = base_p + ((col < width) ? col : (width - 1)) * comp; 2122 | float r = dataR[p], g = dataG[p], b = dataB[p]; 2123 | Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; 2124 | U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; 2125 | V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; 2126 | } 2127 | } 2128 | 2129 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); 2130 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); 2131 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); 2132 | } 2133 | } 2134 | } 2135 | 2136 | // Do the bit alignment of the EOI marker 2137 | stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); 2138 | } 2139 | 2140 | // EOI 2141 | stbiw__putc(s, 0xFF); 2142 | stbiw__putc(s, 0xD9); 2143 | 2144 | return 1; 2145 | } 2146 | 2147 | STBIWDEF int 2148 | stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) { 2149 | stbi__write_context s; 2150 | stbi__start_write_callbacks(&s, func, context); 2151 | return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); 2152 | } 2153 | 2154 | 2155 | #ifndef STBI_WRITE_NO_STDIO 2156 | 2157 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { 2158 | stbi__write_context s; 2159 | if (stbi__start_write_file(&s, filename)) { 2160 | int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); 2161 | stbi__end_write_file(&s); 2162 | return r; 2163 | } else 2164 | return 0; 2165 | } 2166 | 2167 | #endif 2168 | 2169 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION 2170 | 2171 | /* Revision history 2172 | 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels 2173 | 1.13 2174 | 1.12 2175 | 1.11 (2019-08-11) 2176 | 2177 | 1.10 (2019-02-07) 2178 | support utf8 filenames in Windows; fix warnings and platform ifdefs 2179 | 1.09 (2018-02-11) 2180 | fix typo in zlib quality API, improve STB_I_W_STATIC in C++ 2181 | 1.08 (2018-01-29) 2182 | add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 2183 | 1.07 (2017-07-24) 2184 | doc fix 2185 | 1.06 (2017-07-23) 2186 | writing JPEG (using Jon Olick's code) 2187 | 1.05 ??? 2188 | 1.04 (2017-03-03) 2189 | monochrome BMP expansion 2190 | 1.03 ??? 2191 | 1.02 (2016-04-02) 2192 | avoid allocating large structures on the stack 2193 | 1.01 (2016-01-16) 2194 | STBIW_REALLOC_SIZED: support allocators with no realloc support 2195 | avoid race-condition in crc initialization 2196 | minor compile issues 2197 | 1.00 (2015-09-14) 2198 | installable file IO function 2199 | 0.99 (2015-09-13) 2200 | warning fixes; TGA rle support 2201 | 0.98 (2015-04-08) 2202 | added STBIW_MALLOC, STBIW_ASSERT etc 2203 | 0.97 (2015-01-18) 2204 | fixed HDR asserts, rewrote HDR rle logic 2205 | 0.96 (2015-01-17) 2206 | add HDR output 2207 | fix monochrome BMP 2208 | 0.95 (2014-08-17) 2209 | add monochrome TGA output 2210 | 0.94 (2014-05-31) 2211 | rename private functions to avoid conflicts with stb_image.h 2212 | 0.93 (2014-05-27) 2213 | warning fixes 2214 | 0.92 (2010-08-01) 2215 | casts to unsigned char to fix warnings 2216 | 0.91 (2010-07-17) 2217 | first public release 2218 | 0.90 first internal release 2219 | */ 2220 | 2221 | /* 2222 | ------------------------------------------------------------------------------ 2223 | This software is available under 2 licenses -- choose whichever you prefer. 2224 | ------------------------------------------------------------------------------ 2225 | ALTERNATIVE A - MIT License 2226 | Copyright (c) 2017 Sean Barrett 2227 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2228 | this software and associated documentation files (the "Software"), to deal in 2229 | the Software without restriction, including without limitation the rights to 2230 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 2231 | of the Software, and to permit persons to whom the Software is furnished to do 2232 | so, subject to the following conditions: 2233 | The above copyright notice and this permission notice shall be included in all 2234 | copies or substantial portions of the Software. 2235 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2236 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2237 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 2238 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2239 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2240 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 2241 | SOFTWARE. 2242 | ------------------------------------------------------------------------------ 2243 | ALTERNATIVE B - Public Domain (www.unlicense.org) 2244 | This is free and unencumbered software released into the public domain. 2245 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 2246 | software, either in source code form or as a compiled binary, for any purpose, 2247 | commercial or non-commercial, and by any means. 2248 | In jurisdictions that recognize copyright laws, the author or authors of this 2249 | software dedicate any and all copyright interest in the software to the public 2250 | domain. We make this dedication for the benefit of the public at large and to 2251 | the detriment of our heirs and successors. We intend this dedication to be an 2252 | overt act of relinquishment in perpetuity of all present and future rights to 2253 | this software under copyright law. 2254 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2255 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2256 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 2257 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2258 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 2259 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2260 | ------------------------------------------------------------------------------ 2261 | */ --------------------------------------------------------------------------------