├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── downloads └── sprite_extractor.exe ├── example_images ├── ad.png ├── enemies_zelda.png ├── enemies_zelda.txt ├── enemies_zelda_packed.png ├── enemies_zelda_src.png ├── npcs │ ├── 2.png │ ├── 3.png │ ├── 5.png │ └── oldman │ │ ├── 1.png │ │ └── 4.png ├── npcs_zelda.png ├── npcs_zelda.txt ├── npcs_zelda_packed.png ├── npcs_zelda_packed.txt ├── player_zelda.png ├── player_zelda_packed.png ├── player_zelda_packed.txt └── player_zelda_src.png ├── main.c ├── stb_image.h ├── stb_image_write.h ├── stb_rect_pack.h └── tinyfiles.h /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | *.swp 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SpriteExtractor) 2 | 3 | set(SOURCES main.c) 4 | add_executable(sprite_extractor ${SOURCES}) 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Apaar Madan 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpriteExtractor 2 | When I'm prototyping games, I avoid spending time on art. I do this by grabbing temporary art assets from the internet. 3 | However, many of them have inconsistent spacing/padding and are a pain to work with in my engine. This tool fixes that. 4 | 5 | ## Downloads 6 | Pre-built binaries are located in the downloads folder. 7 | 8 | ## Features 9 | * Optionally pack sprites tightly and generate appropriate metadata 10 | * Label each frame with its index for easy visual lookup 11 | * Process entire directories of images (recursively) all at once 12 | * Absolutely no dependencies 13 | * Can process PNG, BMP, TGA, GIF, HDR, JPEG (baseline and progressive) via stb\_image.h 14 | * Always generates 4-component PNG images (Yes, this is a feature) 15 | 16 | ## Build 17 | You can use CMake to build this with whatever you want, which means you'll need CMake. 18 | 19 | If you're on windows and have Visual Studio 2017 installed: 20 | 21 | ``` 22 | mkdir bin 23 | cd bin 24 | cmake -G "Visual Studio 15 Win64" .. 25 | ``` 26 | will make a 64-bit project which you can open up in VS and build. 27 | 28 | If you're somewhere else and you want a makefile for a debug build, for example: 29 | 30 | ``` 31 | mkdir bin 32 | cd bin 33 | mkdir Debug 34 | cd Debug 35 | cmake -G "Makefile" ../.. -DCMAKE_BUILD_TYPE=Debug 36 | ``` 37 | 38 | will make a makefile in that directory to build a debug version of the app. 39 | 40 | ## Usage 41 | ``` 42 | sprite_extractor -h 43 | ``` 44 | Should give you all the usage info. 45 | Do note that it samples the top-left pixel of the image to determine the "background" color. 46 | 47 | ## Examples 48 | 49 | I turned this 50 | 51 | ![Alt text](example_images/enemies_zelda_src.png?raw=true "Zelda Enemies") 52 | 53 | into this 54 | 55 | ![Alt text](example_images/enemies_zelda.png?raw=true "Zelda Enemies Cleaned Up") 56 | 57 | by doing 58 | 59 | ``` 60 | sprite_extractor example_images/enemies_zelda_src.png example_images/enemies_zelda.png --frame-width 32 --frame-height 32 -e 5 --dest-width 512 --row-thresh 16 --min-width 4 --min-height 4 61 | ``` 62 | 63 | And this 64 | 65 | ![Alt text](example_images/player_zelda_src.png?raw=true "Zelda Player") 66 | 67 | into this 68 | 69 | ![Alt text](example_images/player_zelda_packed.png?raw=true "Zelda Player Cleaned Up") 70 | 71 | by doing 72 | 73 | ``` 74 | sprite_extractor example_images/player_zelda_src.png example_images/player_zelda_packed.png --pack 256 128 --frame-width 64 --frame-height 64 -e 2 --min-width 4 --min-height 4 --label --row-thresh 4 75 | ``` 76 | 77 | Notice how the "wave arcs" are placed into single frames despite them being separated from each other by a certain amount. 78 | This occurs because of the edge distance threshold. 79 | 80 | I was also able to take these images (see `example_images/npcs`) 81 | 82 | ![Alt text](example_images/npcs/oldman/1.png?raw=true "Zelda NPC") 83 | ![Alt text](example_images/npcs/2.png?raw=true "Zelda NPC") 84 | ![Alt text](example_images/npcs/3.png?raw=true "Zelda NPC") 85 | ![Alt text](example_images/npcs/oldman/4.png?raw=true "Zelda NPC") 86 | ![Alt text](example_images/npcs/5.png?raw=true "Zelda NPC") 87 | 88 | And make 89 | 90 | ![Alt text](example_images/npcs_zelda.png?raw=true "Zelda NPC") 91 | 92 | by doing 93 | 94 | ``` 95 | sprite_extractor --dir example_images/npcs example_images/npcs_zelda.png --dest-width 128 --frame-width 32 --frame-height 32 -e 0 --min-width 4 --min-height 4 96 | ``` 97 | 98 | Notice how some images contained multiple frames and others just one. This is fine, it still detects them as multiple frames even if they're all in the same file, just like it did for the single image examples. 99 | -------------------------------------------------------------------------------- /downloads/sprite_extractor.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/downloads/sprite_extractor.exe -------------------------------------------------------------------------------- /example_images/ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/ad.png -------------------------------------------------------------------------------- /example_images/enemies_zelda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/enemies_zelda.png -------------------------------------------------------------------------------- /example_images/enemies_zelda.txt: -------------------------------------------------------------------------------- 1 | 122 2 | 0 0 32 32 3 | 32 0 32 32 4 | 64 0 32 32 5 | 96 0 32 32 6 | 128 0 32 32 7 | 160 0 32 32 8 | 192 0 32 32 9 | 224 0 32 32 10 | 256 0 32 32 11 | 288 0 32 32 12 | 320 0 32 32 13 | 352 0 32 32 14 | 384 0 32 32 15 | 416 0 32 32 16 | 448 0 32 32 17 | 480 0 32 32 18 | 0 32 32 32 19 | 32 32 32 32 20 | 64 32 32 32 21 | 96 32 32 32 22 | 128 32 32 32 23 | 160 32 32 32 24 | 192 32 32 32 25 | 224 32 32 32 26 | 256 32 32 32 27 | 288 32 32 32 28 | 320 32 32 32 29 | 352 32 32 32 30 | 384 32 32 32 31 | 416 32 32 32 32 | 448 32 32 32 33 | 480 32 32 32 34 | 0 64 32 32 35 | 32 64 32 32 36 | 64 64 32 32 37 | 96 64 32 32 38 | 128 64 32 32 39 | 160 64 32 32 40 | 192 64 32 32 41 | 224 64 32 32 42 | 256 64 32 32 43 | 288 64 32 32 44 | 320 64 32 32 45 | 352 64 32 32 46 | 384 64 32 32 47 | 416 64 32 32 48 | 448 64 32 32 49 | 480 64 32 32 50 | 0 96 32 32 51 | 32 96 32 32 52 | 64 96 32 32 53 | 96 96 32 32 54 | 128 96 32 32 55 | 160 96 32 32 56 | 192 96 32 32 57 | 224 96 32 32 58 | 256 96 32 32 59 | 288 96 32 32 60 | 320 96 32 32 61 | 352 96 32 32 62 | 384 96 32 32 63 | 416 96 32 32 64 | 448 96 32 32 65 | 480 96 32 32 66 | 0 128 32 32 67 | 32 128 32 32 68 | 64 128 32 32 69 | 96 128 32 32 70 | 128 128 32 32 71 | 160 128 32 32 72 | 192 128 32 32 73 | 224 128 32 32 74 | 256 128 32 32 75 | 288 128 32 32 76 | 320 128 32 32 77 | 352 128 32 32 78 | 384 128 32 32 79 | 416 128 32 32 80 | 448 128 32 32 81 | 480 128 32 32 82 | 0 160 32 32 83 | 32 160 32 32 84 | 64 160 32 32 85 | 96 160 32 32 86 | 128 160 32 32 87 | 160 160 32 32 88 | 192 160 32 32 89 | 224 160 32 32 90 | 256 160 32 32 91 | 288 160 32 32 92 | 320 160 32 32 93 | 352 160 32 32 94 | 384 160 32 32 95 | 416 160 32 32 96 | 448 160 32 32 97 | 480 160 32 32 98 | 0 192 32 32 99 | 32 192 32 32 100 | 64 192 32 32 101 | 96 192 32 32 102 | 128 192 32 32 103 | 160 192 32 32 104 | 192 192 32 32 105 | 224 192 32 32 106 | 256 192 32 32 107 | 288 192 32 32 108 | 320 192 32 32 109 | 352 192 32 32 110 | 384 192 32 32 111 | 416 192 32 32 112 | 448 192 32 32 113 | 480 192 32 32 114 | 0 224 32 32 115 | 32 224 32 32 116 | 64 224 32 32 117 | 96 224 32 32 118 | 128 224 32 32 119 | 160 224 32 32 120 | 192 224 32 32 121 | 224 224 32 32 122 | 256 224 32 32 123 | 288 224 32 32 124 | -------------------------------------------------------------------------------- /example_images/enemies_zelda_packed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/enemies_zelda_packed.png -------------------------------------------------------------------------------- /example_images/enemies_zelda_src.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/enemies_zelda_src.png -------------------------------------------------------------------------------- /example_images/npcs/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/npcs/2.png -------------------------------------------------------------------------------- /example_images/npcs/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/npcs/3.png -------------------------------------------------------------------------------- /example_images/npcs/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/npcs/5.png -------------------------------------------------------------------------------- /example_images/npcs/oldman/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/npcs/oldman/1.png -------------------------------------------------------------------------------- /example_images/npcs/oldman/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/npcs/oldman/4.png -------------------------------------------------------------------------------- /example_images/npcs_zelda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/npcs_zelda.png -------------------------------------------------------------------------------- /example_images/npcs_zelda.txt: -------------------------------------------------------------------------------- 1 | 12 2 | 0 0 32 32 3 | 32 0 32 32 4 | 64 0 32 32 5 | 96 0 32 32 6 | 0 32 32 32 7 | 32 32 32 32 8 | 64 32 32 32 9 | 96 32 32 32 10 | 0 64 32 32 11 | 32 64 32 32 12 | 64 64 32 32 13 | 96 64 32 32 14 | -------------------------------------------------------------------------------- /example_images/npcs_zelda_packed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/npcs_zelda_packed.png -------------------------------------------------------------------------------- /example_images/npcs_zelda_packed.txt: -------------------------------------------------------------------------------- 1 | 12 2 | 0 16 14 16 3 | 56 16 8 16 4 | 0 0 16 16 5 | 32 0 14 16 6 | 16 0 16 16 7 | 14 16 14 16 8 | 28 16 14 16 9 | 42 16 14 16 10 | 28 32 8 16 11 | 0 32 14 16 12 | 46 0 14 16 13 | 14 32 14 16 14 | -------------------------------------------------------------------------------- /example_images/player_zelda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/player_zelda.png -------------------------------------------------------------------------------- /example_images/player_zelda_packed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/player_zelda_packed.png -------------------------------------------------------------------------------- /example_images/player_zelda_packed.txt: -------------------------------------------------------------------------------- 1 | 69 2 | 217 44 15 16 3 | 110 34 15 16 4 | 133 55 12 16 5 | 181 90 14 15 6 | 42 50 14 16 7 | 202 28 16 16 8 | 244 54 12 16 9 | 130 86 15 15 10 | 28 50 14 16 11 | 218 28 16 16 12 | 232 54 12 16 13 | 151 85 15 15 14 | 96 50 13 16 15 | 195 90 14 15 16 | 121 50 12 16 17 | 95 34 15 16 18 | 80 34 15 16 19 | 166 85 15 15 20 | 109 50 12 16 21 | 32 34 16 16 22 | 202 44 15 16 23 | 85 81 15 15 24 | 145 55 12 16 25 | 64 34 16 16 26 | 54 81 16 15 27 | 115 86 15 15 28 | 48 34 16 16 29 | 100 86 15 15 30 | 238 70 16 15 31 | 226 85 15 15 32 | 16 34 16 16 33 | 211 85 15 15 34 | 135 71 16 15 35 | 241 85 15 15 36 | 0 34 16 16 37 | 70 81 15 15 38 | 186 28 16 27 39 | 154 0 16 28 40 | 170 28 16 27 41 | 170 0 16 28 42 | 138 28 16 27 43 | 202 0 16 28 44 | 157 55 27 15 45 | 184 60 27 15 46 | 0 66 27 15 47 | 27 81 27 15 48 | 54 66 27 15 49 | 81 66 27 15 50 | 234 0 16 27 51 | 218 0 16 28 52 | 234 27 16 27 53 | 186 0 16 28 54 | 154 28 16 27 55 | 138 0 16 28 56 | 0 81 27 15 57 | 184 75 27 15 58 | 108 71 27 15 59 | 211 70 27 15 60 | 157 70 27 15 61 | 27 66 27 15 62 | 125 34 13 16 63 | 56 50 14 16 64 | 46 0 46 34 65 | 83 50 13 16 66 | 0 50 14 16 67 | 0 0 46 34 68 | 70 50 13 16 69 | 14 50 14 16 70 | 92 0 46 34 71 | -------------------------------------------------------------------------------- /example_images/player_zelda_src.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goodpaul6/SpriteExtractor/09607e91b87d31ac18c4bf075b9b4fddd25b7474/example_images/player_zelda_src.png -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_FRAMES 65536 7 | #define MAX_POINTS 100000 8 | 9 | #define STB_IMAGE_IMPLEMENTATION 10 | #include "stb_image.h" 11 | 12 | #define STB_IMAGE_WRITE_IMPLEMENTATION 13 | #include "stb_image_write.h" 14 | 15 | #define STB_RECT_PACK_IMPLEMENTATION 16 | #include "stb_rect_pack.h" 17 | 18 | #define TINYFILES_IMPLEMENTATION 19 | #include "tinyfiles.h" 20 | 21 | typedef struct 22 | { 23 | unsigned char r, g, b, a; 24 | } Pixel; 25 | 26 | typedef struct 27 | { 28 | unsigned char* src; 29 | int sw, sh; 30 | Pixel bg; 31 | 32 | int x, y; 33 | int w, h; 34 | } Rect; 35 | 36 | typedef struct 37 | { 38 | int x, y; 39 | int distFromEdge; 40 | } Point; 41 | 42 | typedef struct 43 | { 44 | bool isDir; 45 | const char* inputImage; 46 | const char* outputImage; 47 | int fw, fh; 48 | int maxDistFromEdge; 49 | int minW, minH; 50 | bool pot; 51 | int dw; 52 | int packW, packH; 53 | bool label; 54 | bool metadata; 55 | } Args; 56 | 57 | static Pixel NumFont[10][3 * 5]; 58 | 59 | static void GenerateNumFont(Pixel col) 60 | { 61 | static const unsigned char bits[10][3 * 5] = 62 | { 63 | { 64 | 1, 1, 1, 65 | 1, 0, 1, 66 | 1, 0, 1, 67 | 1, 0, 1, 68 | 1, 1, 1 69 | }, 70 | { 71 | 0, 1, 0, 72 | 0, 1, 0, 73 | 0, 1, 0, 74 | 0, 1, 0, 75 | 0, 1, 0, 76 | }, 77 | { 78 | 1, 1, 1, 79 | 0, 0, 1, 80 | 1, 1, 1, 81 | 1, 0, 0, 82 | 1, 1, 1 83 | }, 84 | { 85 | 1, 1, 1, 86 | 0, 0, 1, 87 | 1, 1, 1, 88 | 0, 0, 1, 89 | 1, 1, 1 90 | }, 91 | { 92 | 1, 0, 1, 93 | 1, 0, 1, 94 | 1, 1, 1, 95 | 0, 0, 1, 96 | 0, 0, 1 97 | }, 98 | { 99 | 1, 1, 1, 100 | 1, 0, 0, 101 | 1, 1, 1, 102 | 0, 0, 1, 103 | 1, 1, 1 104 | }, 105 | { 106 | 1, 1, 1, 107 | 1, 0, 0, 108 | 1, 1, 1, 109 | 1, 0, 1, 110 | 1, 1, 1 111 | }, 112 | { 113 | 1, 1, 1, 114 | 0, 0, 1, 115 | 0, 0, 1, 116 | 0, 0, 1, 117 | 0, 0, 1 118 | }, 119 | { 120 | 1, 1, 1, 121 | 1, 0, 1, 122 | 1, 1, 1, 123 | 1, 0, 1, 124 | 1, 1, 1 125 | }, 126 | { 127 | 1, 1, 1, 128 | 1, 0, 1, 129 | 1, 1, 1, 130 | 0, 0, 1, 131 | 1, 1, 1 132 | }, 133 | }; 134 | 135 | for (int i = 0; i < 10; ++i) { 136 | for (int y = 0; y < 5; ++y) { 137 | for (int x = 0; x < 3; ++x) { 138 | if (bits[i][x + y * 3]) { 139 | NumFont[i][x + y * 3] = col; 140 | } else { 141 | NumFont[i][x + y * 3] = (Pixel) { 0 }; 142 | } 143 | } 144 | } 145 | } 146 | } 147 | 148 | static int CompareFramesRowThresh; 149 | 150 | static void PrintUsage(const char* app) 151 | { 152 | fprintf(stderr, "Usage: %s (path/to/input/image or directory of images) path/to/output/image OPTIONS\n", app); 153 | fprintf(stderr, "Options:\n"); 154 | fprintf(stderr, "\t--dir\n\t\tThis must be specified if you supplied a directory of images instead of a single file.\n"); 155 | fprintf(stderr, "\t--frame-width DESIRED_FRAME_WIDTH\n\t\tDesired width of the frames.\n"); 156 | fprintf(stderr, "\t--frame-height DESIRED_FRAME_HEIGHT\n\t\tDesired height of the frames.\n"); 157 | fprintf(stderr, "\t-e EDGE_DISTANCE_THRESHOLD\n\t\tThe edge distance threshold is used to determine whether disconnected pixels still belongs to a frame.\n\t\tIf the distance from these pixels to the nearest edge is less than or equal to the\n\t\tthreshold, then they're incorporated.\n"); 158 | fprintf(stderr, "\t--min-width MIN_FRAME_WIDTH\n\t--min-height MIN_FRAME_HEIGHT\n\t\tAll frames smaller than these in both dimensions will be discarded.\n\t\tBy default these are frame width / 4 and frame height / 4.\n"); 159 | fprintf(stderr, "\t--pot\n\t\tThis is optional. It makes the app generate a power of two image.\n"); 160 | fprintf(stderr, "\t--dest-width DESIRED_OUTPUT_IMAGE_WIDTH\n\t\tThis is optional and mutually exclusive with the --pot option.\n\t\tThis is the width you want the output image to be.\n\t\tThe height will be determined from the number of frames detected.\n"); 161 | fprintf(stderr, "\t--row-thresh DESIRED_ROW_THRESHOLD\n\t\tThis is equal to half the frame height by default.\n\t\tIt is used to order the resulting frames. If two frames are within the threshold on the y axis\n\t\tthen they are ordered from left-to-right next to each other in the final image.\n"); 162 | fprintf(stderr, "\t--label\n\t\tPrints the rectangle indices into the top-left corner of the frames.\n"); 163 | fprintf(stderr, "\t--metadata\n\t\tIf specified, the rectangles are output to a text file in the format mentioned below.\n"); 164 | fprintf(stderr, "\t--pack PACKED_IMAGE_WIDTH PACKED_IMAGE_HEIGHT\n\t\tIf this is supplied, then the frames are tightly packed and metadata is generated for each frame.\n\t\tThe metadata is simply a text file with the number of frames followed by 4 integers\n\t\tfor each frame: x y w h\n"); 165 | } 166 | 167 | static bool ParseArgs(Args* args, int argc, char** argv) 168 | { 169 | if(argc < 2) { 170 | fprintf(stderr, "Not enough arguments.\n"); 171 | PrintUsage(argv[0]); 172 | return false; 173 | } 174 | 175 | memset(args, 0, sizeof(Args)); 176 | 177 | for(int i = 1; i < argc; ++i) { 178 | if(strcmp(argv[i], "-h") == 0) { 179 | PrintUsage(argv[0]); 180 | return false; 181 | } else if(strcmp(argv[i], "--dir") == 0) { 182 | args->isDir = true; 183 | } else if(strcmp(argv[i], "--frame-width") == 0) { 184 | args->fw = atoi(argv[i + 1]); 185 | i += 1; 186 | } else if(strcmp(argv[i], "--frame-height") == 0) { 187 | args->fh = atoi(argv[i + 1]); 188 | i += 1; 189 | } else if (strcmp(argv[i], "--min-width") == 0) { 190 | args->minW = atoi(argv[i + 1]); 191 | i += 1; 192 | } else if (strcmp(argv[i], "--min-height") == 0) { 193 | args->minH = atoi(argv[i + 1]); 194 | i += 1; 195 | } else if (strcmp(argv[i], "--dest-width") == 0) { 196 | args->dw = atoi(argv[i + 1]); 197 | i += 1; 198 | } else if (strcmp(argv[i], "--metadata") == 0) { 199 | args->metadata = true; 200 | } else if (strcmp(argv[i], "-e") == 0) { 201 | args->maxDistFromEdge = atoi(argv[i + 1]); 202 | i += 1; 203 | } else if(strcmp(argv[i], "--pack") == 0) { 204 | args->packW = atoi(argv[i + 1]); 205 | args->packH = atoi(argv[i + 2]); 206 | i += 2; 207 | } else if(strcmp(argv[i], "--pot") == 0) { 208 | args->pot = true; 209 | } else if (strcmp(argv[i], "--label") == 0) { 210 | args->label = true; 211 | } else if (strcmp(argv[i], "--row-thresh") == 0) { 212 | CompareFramesRowThresh = atoi(argv[i + 1]); 213 | i += 1; 214 | } else { 215 | if(!args->inputImage) args->inputImage = argv[i]; 216 | else if (!args->outputImage) args->outputImage = argv[i]; 217 | else { 218 | fprintf(stderr, "Not sure what '%s' is specified for.\n", argv[i]); 219 | return false; 220 | } 221 | } 222 | } 223 | 224 | if (!args->inputImage) { 225 | fprintf(stderr, "Please specify an input image.\n"); 226 | return false; 227 | } 228 | 229 | if(!args->outputImage) { 230 | fprintf(stderr, "Please specify an output image.\n"); 231 | return false; 232 | } 233 | 234 | if(args->packW > 0 && args->packH == 0) { 235 | fprintf(stderr, "Invalid pack size.\n"); 236 | return false; 237 | } 238 | 239 | if(args->packH > 0 && args->packW == 0) { 240 | fprintf(stderr, "Invalid pack size.\n"); 241 | return false; 242 | } 243 | 244 | if(args->fw == 0) { 245 | fprintf(stderr, "Specify a valid frame width.\n"); 246 | return false; 247 | } 248 | 249 | if(args->fh == 0) { 250 | fprintf(stderr, "Specify a valid frame height."); 251 | return false; 252 | } 253 | 254 | if(args->maxDistFromEdge < 0) { 255 | fprintf(stderr, "Please specify a non-negative edge distance."); 256 | return false; 257 | } 258 | 259 | if(args->packW > 0 && args->packH > 0 && args->pot) { 260 | fprintf(stderr, "Cannot specify pack width and height and also power of two.\n"); 261 | return false; 262 | } 263 | 264 | if(args->packW > 0 && args->packH > 0 && args->dw > 0) { 265 | fprintf(stderr, "Cannot specify pack width and height and also dest width.\n"); 266 | return false; 267 | } 268 | 269 | if(args->pot && args->dw > 0) { 270 | fprintf(stderr, "Cannot specify both power of two and destination width.\n"); 271 | return false; 272 | } 273 | 274 | if(args->packW == 0 && args->packH == 0 && !args->pot && args->dw <= 0) { 275 | fprintf(stderr, "Please specify either --pot or --dest-width.\n"); 276 | return false; 277 | } 278 | 279 | if (args->fw > 0 && (args->dw > 0 && (args->dw % args->fw) != 0)) { 280 | fprintf(stderr, "Dest width must be a multiple of frame width.\n"); 281 | return false; 282 | } 283 | 284 | if (args->minW == 0) { 285 | args->minW = args->fw / 4; 286 | } 287 | 288 | if (args->minH == 0) { 289 | args->minH = args->fh / 4; 290 | } 291 | 292 | if (args->label) { 293 | GenerateNumFont((Pixel) { 255, 255, 255, 255 }); 294 | } 295 | 296 | if(args->packW > 0 && args->packH > 0) { 297 | args->metadata = true; 298 | } 299 | 300 | if (CompareFramesRowThresh == 0) { 301 | CompareFramesRowThresh = args->fh / 2; 302 | } 303 | 304 | return true; 305 | } 306 | 307 | static bool PixelEqual(const Pixel* a, const Pixel* b) 308 | { 309 | return a->r == b->r && 310 | a->g == b->g && 311 | a->b == b->b && 312 | a->a == b->a; 313 | } 314 | 315 | static int CompareFrames(const void* va, const void* vb) 316 | { 317 | const Rect* a = va; 318 | const Rect* b = vb; 319 | 320 | if(abs(a->y - b->y) < CompareFramesRowThresh) { 321 | // They're in the same row, roughly 322 | return a->x - b->x; 323 | } 324 | 325 | return a->y - b->y; 326 | } 327 | 328 | static int NumFrames = 0; 329 | static Rect Frames[MAX_FRAMES]; 330 | 331 | static void ExtractFrames(const char* filename, const Args* args) 332 | { 333 | int w, h, n; 334 | unsigned char* src = stbi_load(filename, &w, &h, &n, 4); 335 | 336 | if(!src) { 337 | fprintf(stderr, "Failed to load image '%s': %s\n", filename, stbi_failure_reason()); 338 | if (args->isDir) { 339 | fprintf(stderr, "Skipping...\n"); 340 | } 341 | return; 342 | } 343 | 344 | int fw = args->fw; 345 | int fh = args->fh; 346 | int maxDistFromEdge = args->maxDistFromEdge; 347 | 348 | // Top left pixel is bg color 349 | const Pixel* bg = (Pixel*)src; 350 | 351 | printf("Processing image '%s'...\n", filename); 352 | printf("image size: %d, %d\n", w, h); 353 | printf("bg: %d %d %d %d\n", bg->r, bg->g, bg->b, bg->a); 354 | 355 | bool* checked = calloc(sizeof(bool), w * h); 356 | 357 | for(int y = 0; y < h; ++y) { 358 | for(int x = 0; x < w; ++x) { 359 | const Pixel* p = (Pixel*)(&src[x * 4 + y * (w * 4)]); 360 | 361 | if(PixelEqual(p, bg)) { 362 | continue; 363 | } 364 | 365 | if (checked[x + y * w]) continue; 366 | 367 | // Flood fill to find extents 368 | int minX = INT_MAX; 369 | int minY = INT_MAX; 370 | int maxX = INT_MIN; 371 | int maxY = INT_MIN; 372 | 373 | int numPoints = 0; 374 | static Point points[MAX_POINTS]; 375 | 376 | points[numPoints++] = (Point){ x, y, 0 }; 377 | 378 | while(numPoints > 0) { 379 | Point pt = points[numPoints - 1]; 380 | numPoints -= 1; 381 | 382 | if (pt.x < 0 || pt.y < 0 || pt.x >= w || pt.y >= h) continue; 383 | if (checked[pt.x + pt.y * w]) continue; 384 | 385 | p = (Pixel*)(&src[(pt.x * 4) + pt.y * (w * 4)]); 386 | 387 | bool isBg = PixelEqual(p, bg); 388 | if(isBg && pt.distFromEdge >= maxDistFromEdge) { 389 | continue; 390 | } 391 | 392 | int newDistFromEdge = 0; 393 | if (isBg) { 394 | newDistFromEdge = pt.distFromEdge + 1; 395 | } 396 | 397 | checked[pt.x + pt.y * w] = true; 398 | 399 | if(!isBg) { 400 | if(pt.x < minX) { 401 | minX = pt.x; 402 | } 403 | 404 | if(pt.y < minY) { 405 | minY = pt.y; 406 | } 407 | 408 | if(pt.x > maxX) { 409 | maxX = pt.x; 410 | } 411 | 412 | if(pt.y > maxY) { 413 | maxY = pt.y; 414 | } 415 | } 416 | 417 | assert(numPoints < MAX_POINTS); 418 | 419 | points[numPoints++] = (Point){pt.x - 1, pt.y, newDistFromEdge}; 420 | points[numPoints++] = (Point){pt.x, pt.y - 1, newDistFromEdge}; 421 | points[numPoints++] = (Point){pt.x + 1, pt.y, newDistFromEdge}; 422 | points[numPoints++] = (Point){pt.x, pt.y + 1, newDistFromEdge}; 423 | } 424 | 425 | Rect r = { src, w, h, *bg, minX, minY, maxX - minX + 1, maxY - minY + 1 }; 426 | 427 | if (r.w < args->minW && r.h < args->minH) { 428 | fprintf(stderr, "Found rect (%d,%d,%d,%d) but it's too small so I'm skipping it.\n", r.x, r.y, r.w, r.h); 429 | } else if(r.w > fw) { 430 | fprintf(stderr, "Found rect (%d,%d,%d,%d) but it's too large to fit in a single frame so I'm skipping it.\n", r.x, r.y, r.w, r.h); 431 | } else { 432 | assert(NumFrames < MAX_FRAMES); 433 | Frames[NumFrames++] = r; 434 | } 435 | } 436 | } 437 | 438 | free(checked); 439 | } 440 | 441 | static void TraverseImages(tfFILE* file, void* data) 442 | { 443 | ExtractFrames(file->path, data); 444 | } 445 | 446 | int main(int argc, char** argv) 447 | { 448 | Args args; 449 | 450 | if(!ParseArgs(&args, argc, argv)) { 451 | return 1; 452 | } 453 | 454 | if(args.isDir) { 455 | tfTraverse(args.inputImage, TraverseImages, &args); 456 | } else { 457 | ExtractFrames(args.inputImage, &args); 458 | } 459 | 460 | if(NumFrames == 0) { 461 | fprintf(stderr, "I found no frames. Are you sure you supplied the correct input?\n"); 462 | return 1; 463 | } 464 | 465 | qsort(Frames, NumFrames, sizeof(Rect), CompareFrames); 466 | 467 | int dw; 468 | int dh; 469 | 470 | static stbrp_rect rects[MAX_FRAMES]; 471 | 472 | if(args.packW == 0 && args.packH == 0) { 473 | if(args.pot) { 474 | int reqArea = NumFrames * args.fw * args.fh; 475 | 476 | dw = args.fw; 477 | dh = args.fh; 478 | 479 | while(dw * dh < reqArea) { 480 | dw *= 2; 481 | dh *= 2; 482 | } 483 | } else { 484 | dw = args.dw; 485 | dh = ((NumFrames * args.fw) / dw + 1) * args.fh; 486 | } 487 | } else { 488 | dw = args.packW; 489 | dh = args.packH; 490 | 491 | stbrp_context ctx; 492 | 493 | stbrp_node* nodes = malloc(sizeof(stbrp_node) * dw); 494 | 495 | stbrp_init_target(&ctx, dw, dh, nodes, dw); 496 | 497 | for(int i = 0; i < NumFrames; ++i) { 498 | rects[i].w = Frames[i].w; 499 | rects[i].h = Frames[i].h; 500 | } 501 | 502 | if(stbrp_pack_rects(&ctx, rects, NumFrames) != 1) { 503 | fprintf(stderr, "Failed to pack (some) rectangles. Try again with a different size or don't pack at all.\n"); 504 | return 1; 505 | } 506 | 507 | free(nodes); 508 | } 509 | if(args.metadata) { 510 | // Output rectangle metadata in top-left to bottom-right order 511 | char path[512]; 512 | 513 | if (strlen(args.outputImage) >= 512) { 514 | fprintf(stderr, "Failed; output image path is too long to generate metadata.\n"); 515 | return 1; 516 | } 517 | 518 | strcpy(path, args.outputImage); 519 | 520 | char* ext = strrchr(path, '.'); 521 | 522 | if (!ext) { 523 | fprintf(stderr, "Failed; missing extension on output image path.\n"); 524 | return 1; 525 | } 526 | 527 | ext[1] = 't'; 528 | ext[2] = 'x'; 529 | ext[3] = 't'; 530 | ext[4] = '\0'; 531 | 532 | FILE* file = fopen(path, "w"); 533 | 534 | if (!file) { 535 | fprintf(stderr, "Failed to open metadata file '%s' for writing.\n", path); 536 | return 1; 537 | } 538 | 539 | fprintf(file, "%d\n", NumFrames); 540 | 541 | for (int i = 0; i < NumFrames; ++i) { 542 | if(args.packW > 0 && args.packH > 0) { 543 | fprintf(file, "%d %d %d %d\n", rects[i].x, rects[i].y, rects[i].w, rects[i].h); 544 | } else { 545 | int columns = dw / args.fw; 546 | 547 | int dx = (i % columns) * args.fw; 548 | int dy = (i / columns) * args.fh; 549 | 550 | fprintf(file, "%d %d %d %d\n", dx, dy, args.fw, args.fh); 551 | } 552 | } 553 | 554 | fclose(file); 555 | printf("Successfully wrote metadata to '%s'.\n", path); 556 | } 557 | 558 | unsigned char* dest = calloc(4, dw * dh); 559 | 560 | for(int i = 0; i < NumFrames; ++i) { 561 | Rect r = Frames[i]; 562 | 563 | int dx, dy; 564 | 565 | int fw = args.fw; 566 | int fh = args.fh; 567 | 568 | if(args.packW == 0 && args.packH == 0) { 569 | int columns = dw / fw; 570 | 571 | dx = (i % columns) * fw + fw / 2 - r.w / 2; 572 | dy = (i / columns) * fh + fh / 2 - r.h / 2; 573 | } else { 574 | dx = rects[i].x; 575 | dy = rects[i].y; 576 | } 577 | 578 | for(int y = 0; y < r.h; ++y) { 579 | for(int x = 0; x < r.w; ++x) { 580 | Pixel sp = *(Pixel*)(&r.src[((x + r.x) * 4) + (y + r.y) * (r.sw * 4)]); 581 | 582 | if(PixelEqual(&sp, &r.bg)) continue; 583 | 584 | *(Pixel*)(&dest[(dx + x) * 4 + (y + dy) * (dw * 4)]) = sp; 585 | } 586 | } 587 | 588 | if (args.label) { 589 | char num[32]; 590 | sprintf(num, "%d", i); 591 | 592 | int len = strlen(num); 593 | 594 | for (int i = 0; i < len; ++i) { 595 | int digit = num[i] - '0'; 596 | 597 | for (int y = 0; y < 5; ++y) { 598 | for (int x = 0; x < 3; ++x) { 599 | Pixel sp = NumFont[digit][x + y * 3]; 600 | 601 | *(Pixel*)(&dest[(dx + x + i * 4) * 4 + (y + dy) * (dw * 4)]) = sp; 602 | } 603 | } 604 | } 605 | } 606 | } 607 | 608 | if(stbi_write_png(args.outputImage, dw, dh, 4, dest, dw * 4) == 0) { 609 | fprintf(stderr, "Failed to write file.\n"); 610 | } else { 611 | printf("Succeeded.\n"); 612 | } 613 | 614 | return 0; 615 | } 616 | -------------------------------------------------------------------------------- /stb_image_write.h: -------------------------------------------------------------------------------- 1 | /* stb_image_write - v1.09 - public domain - http://nothings.org/stb/stb_image_write.h 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 | If using a modern Microsoft Compiler, non-safe versions of CRT calls may cause 14 | compilation warnings or even errors. To avoid this, also before #including, 15 | 16 | #define STBI_MSC_SECURE_CRT 17 | 18 | ABOUT: 19 | 20 | This header file is a library for writing images to C stdio. It could be 21 | adapted to write to memory or a general streaming interface; let me know. 22 | 23 | The PNG output is not optimal; it is 20-50% larger than the file 24 | written by a decent optimizing implementation; though providing a custom 25 | zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. 26 | This library is designed for source code compactness and simplicity, 27 | not optimal image file size or run-time performance. 28 | 29 | BUILDING: 30 | 31 | You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. 32 | You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace 33 | malloc,realloc,free. 34 | You can #define STBIW_MEMMOVE() to replace memmove() 35 | You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function 36 | for PNG compression (instead of the builtin one), it must have the following signature: 37 | unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); 38 | The returned data will be freed with STBIW_FREE() (free() by default), 39 | so it must be heap allocated with STBIW_MALLOC() (malloc() by default), 40 | 41 | USAGE: 42 | 43 | There are five functions, one for each image file format: 44 | 45 | int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 46 | int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 47 | int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 48 | int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); 49 | int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 50 | 51 | void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically 52 | 53 | There are also five equivalent functions that use an arbitrary write function. You are 54 | expected to open/close your file-equivalent before and after calling these: 55 | 56 | 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); 57 | int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 58 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 59 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 60 | int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); 61 | 62 | where the callback is: 63 | void stbi_write_func(void *context, void *data, int size); 64 | 65 | You can configure it with these global variables: 66 | int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE 67 | int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression 68 | int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode 69 | 70 | 71 | You can define STBI_WRITE_NO_STDIO to disable the file variant of these 72 | functions, so the library will not use stdio.h at all. However, this will 73 | also disable HDR writing, because it requires stdio for formatted output. 74 | 75 | Each function returns 0 on failure and non-0 on success. 76 | 77 | The functions create an image file defined by the parameters. The image 78 | is a rectangle of pixels stored from left-to-right, top-to-bottom. 79 | Each pixel contains 'comp' channels of data stored interleaved with 8-bits 80 | per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is 81 | monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. 82 | The *data pointer points to the first byte of the top-left-most pixel. 83 | For PNG, "stride_in_bytes" is the distance in bytes from the first byte of 84 | a row of pixels to the first byte of the next row of pixels. 85 | 86 | PNG creates output files with the same number of components as the input. 87 | The BMP format expands Y to RGB in the file format and does not 88 | output alpha. 89 | 90 | PNG supports writing rectangles of data even when the bytes storing rows of 91 | data are not consecutive in memory (e.g. sub-rectangles of a larger image), 92 | by supplying the stride between the beginning of adjacent rows. The other 93 | formats do not. (Thus you cannot write a native-format BMP through the BMP 94 | writer, both because it is in BGR order and because it may have padding 95 | at the end of the line.) 96 | 97 | PNG allows you to set the deflate compression level by setting the global 98 | variable 'stbi_write_png_compression_level' (it defaults to 8). 99 | 100 | HDR expects linear float data. Since the format is always 32-bit rgb(e) 101 | data, alpha (if provided) is discarded, and for monochrome data it is 102 | replicated across all three channels. 103 | 104 | TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed 105 | data, set the global variable 'stbi_write_tga_with_rle' to 0. 106 | 107 | JPEG does ignore alpha channels in input data; quality is between 1 and 100. 108 | Higher quality looks better but results in a bigger image. 109 | JPEG baseline (no JPEG progressive). 110 | 111 | CREDITS: 112 | 113 | 114 | Sean Barrett - PNG/BMP/TGA 115 | Baldur Karlsson - HDR 116 | Jean-Sebastien Guay - TGA monochrome 117 | Tim Kelsey - misc enhancements 118 | Alan Hickman - TGA RLE 119 | Emmanuel Julien - initial file IO callback implementation 120 | Jon Olick - original jo_jpeg.cpp code 121 | Daniel Gibson - integrate JPEG, allow external zlib 122 | Aarni Koskela - allow choosing PNG filter 123 | 124 | bugfixes: 125 | github:Chribba 126 | Guillaume Chereau 127 | github:jry2 128 | github:romigrou 129 | Sergio Gonzalez 130 | Jonas Karlsson 131 | Filip Wasil 132 | Thatcher Ulrich 133 | github:poppolopoppo 134 | Patrick Boettcher 135 | github:xeekworx 136 | Cap Petschulat 137 | Simon Rodriguez 138 | Ivan Tikhonov 139 | github:ignotion 140 | Adam Schackart 141 | 142 | LICENSE 143 | 144 | See end of file for license information. 145 | 146 | */ 147 | 148 | #ifndef INCLUDE_STB_IMAGE_WRITE_H 149 | #define INCLUDE_STB_IMAGE_WRITE_H 150 | 151 | // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' 152 | #ifndef STBIWDEF 153 | #ifdef STB_IMAGE_WRITE_STATIC 154 | #define STBIWDEF static 155 | #else 156 | #ifdef __cplusplus 157 | #define STBIWDEF extern "C" 158 | #else 159 | #define STBIWDEF extern 160 | #endif 161 | #endif 162 | #endif 163 | 164 | #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations 165 | extern int stbi_write_tga_with_rle; 166 | extern int stbi_write_png_compression_level; 167 | extern int stbi_write_force_png_filter; 168 | #endif 169 | 170 | #ifndef STBI_WRITE_NO_STDIO 171 | STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 172 | STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 173 | STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 174 | STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 175 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); 176 | #endif 177 | 178 | typedef void stbi_write_func(void *context, void *data, int size); 179 | 180 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); 181 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 182 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 183 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 184 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); 185 | 186 | STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); 187 | 188 | #endif//INCLUDE_STB_IMAGE_WRITE_H 189 | 190 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION 191 | 192 | #ifdef _WIN32 193 | #ifndef _CRT_SECURE_NO_WARNINGS 194 | #define _CRT_SECURE_NO_WARNINGS 195 | #endif 196 | #ifndef _CRT_NONSTDC_NO_DEPRECATE 197 | #define _CRT_NONSTDC_NO_DEPRECATE 198 | #endif 199 | #endif 200 | 201 | #ifndef STBI_WRITE_NO_STDIO 202 | #include 203 | #endif // STBI_WRITE_NO_STDIO 204 | 205 | #include 206 | #include 207 | #include 208 | #include 209 | 210 | #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) 211 | // ok 212 | #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) 213 | // ok 214 | #else 215 | #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." 216 | #endif 217 | 218 | #ifndef STBIW_MALLOC 219 | #define STBIW_MALLOC(sz) malloc(sz) 220 | #define STBIW_REALLOC(p,newsz) realloc(p,newsz) 221 | #define STBIW_FREE(p) free(p) 222 | #endif 223 | 224 | #ifndef STBIW_REALLOC_SIZED 225 | #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) 226 | #endif 227 | 228 | 229 | #ifndef STBIW_MEMMOVE 230 | #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) 231 | #endif 232 | 233 | 234 | #ifndef STBIW_ASSERT 235 | #include 236 | #define STBIW_ASSERT(x) assert(x) 237 | #endif 238 | 239 | #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) 240 | 241 | #ifdef STB_IMAGE_WRITE_STATIC 242 | static int stbi__flip_vertically_on_write=0; 243 | static int stbi_write_png_compression_level = 8; 244 | static int stbi_write_tga_with_rle = 1; 245 | static int stbi_write_force_png_filter = -1; 246 | #else 247 | int stbi_write_png_compression_level = 8; 248 | int stbi__flip_vertically_on_write=0; 249 | int stbi_write_tga_with_rle = 1; 250 | int stbi_write_force_png_filter = -1; 251 | #endif 252 | 253 | STBIWDEF void stbi_flip_vertically_on_write(int flag) 254 | { 255 | stbi__flip_vertically_on_write = flag; 256 | } 257 | 258 | typedef struct 259 | { 260 | stbi_write_func *func; 261 | void *context; 262 | } stbi__write_context; 263 | 264 | // initialize a callback-based context 265 | static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) 266 | { 267 | s->func = c; 268 | s->context = context; 269 | } 270 | 271 | #ifndef STBI_WRITE_NO_STDIO 272 | 273 | static void stbi__stdio_write(void *context, void *data, int size) 274 | { 275 | fwrite(data,1,size,(FILE*) context); 276 | } 277 | 278 | static int stbi__start_write_file(stbi__write_context *s, const char *filename) 279 | { 280 | FILE *f; 281 | #ifdef STBI_MSC_SECURE_CRT 282 | if (fopen_s(&f, filename, "wb")) 283 | f = NULL; 284 | #else 285 | f = fopen(filename, "wb"); 286 | #endif 287 | stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); 288 | return f != NULL; 289 | } 290 | 291 | static void stbi__end_write_file(stbi__write_context *s) 292 | { 293 | fclose((FILE *)s->context); 294 | } 295 | 296 | #endif // !STBI_WRITE_NO_STDIO 297 | 298 | typedef unsigned int stbiw_uint32; 299 | typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; 300 | 301 | static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) 302 | { 303 | while (*fmt) { 304 | switch (*fmt++) { 305 | case ' ': break; 306 | case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); 307 | s->func(s->context,&x,1); 308 | break; } 309 | case '2': { int x = va_arg(v,int); 310 | unsigned char b[2]; 311 | b[0] = STBIW_UCHAR(x); 312 | b[1] = STBIW_UCHAR(x>>8); 313 | s->func(s->context,b,2); 314 | break; } 315 | case '4': { stbiw_uint32 x = va_arg(v,int); 316 | unsigned char b[4]; 317 | b[0]=STBIW_UCHAR(x); 318 | b[1]=STBIW_UCHAR(x>>8); 319 | b[2]=STBIW_UCHAR(x>>16); 320 | b[3]=STBIW_UCHAR(x>>24); 321 | s->func(s->context,b,4); 322 | break; } 323 | default: 324 | STBIW_ASSERT(0); 325 | return; 326 | } 327 | } 328 | } 329 | 330 | static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) 331 | { 332 | va_list v; 333 | va_start(v, fmt); 334 | stbiw__writefv(s, fmt, v); 335 | va_end(v); 336 | } 337 | 338 | static void stbiw__putc(stbi__write_context *s, unsigned char c) 339 | { 340 | s->func(s->context, &c, 1); 341 | } 342 | 343 | static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) 344 | { 345 | unsigned char arr[3]; 346 | arr[0] = a, arr[1] = b, arr[2] = c; 347 | s->func(s->context, arr, 3); 348 | } 349 | 350 | static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) 351 | { 352 | unsigned char bg[3] = { 255, 0, 255}, px[3]; 353 | int k; 354 | 355 | if (write_alpha < 0) 356 | s->func(s->context, &d[comp - 1], 1); 357 | 358 | switch (comp) { 359 | case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case 360 | case 1: 361 | if (expand_mono) 362 | stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp 363 | else 364 | s->func(s->context, d, 1); // monochrome TGA 365 | break; 366 | case 4: 367 | if (!write_alpha) { 368 | // composite against pink background 369 | for (k = 0; k < 3; ++k) 370 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; 371 | stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); 372 | break; 373 | } 374 | /* FALLTHROUGH */ 375 | case 3: 376 | stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); 377 | break; 378 | } 379 | if (write_alpha > 0) 380 | s->func(s->context, &d[comp - 1], 1); 381 | } 382 | 383 | static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) 384 | { 385 | stbiw_uint32 zero = 0; 386 | int i,j, j_end; 387 | 388 | if (y <= 0) 389 | return; 390 | 391 | if (stbi__flip_vertically_on_write) 392 | vdir *= -1; 393 | 394 | if (vdir < 0) 395 | j_end = -1, j = y-1; 396 | else 397 | j_end = y, j = 0; 398 | 399 | for (; j != j_end; j += vdir) { 400 | for (i=0; i < x; ++i) { 401 | unsigned char *d = (unsigned char *) data + (j*x+i)*comp; 402 | stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); 403 | } 404 | s->func(s->context, &zero, scanline_pad); 405 | } 406 | } 407 | 408 | static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) 409 | { 410 | if (y < 0 || x < 0) { 411 | return 0; 412 | } else { 413 | va_list v; 414 | va_start(v, fmt); 415 | stbiw__writefv(s, fmt, v); 416 | va_end(v); 417 | stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); 418 | return 1; 419 | } 420 | } 421 | 422 | static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) 423 | { 424 | int pad = (-x*3) & 3; 425 | return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, 426 | "11 4 22 4" "4 44 22 444444", 427 | 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 428 | 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header 429 | } 430 | 431 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 432 | { 433 | stbi__write_context s; 434 | stbi__start_write_callbacks(&s, func, context); 435 | return stbi_write_bmp_core(&s, x, y, comp, data); 436 | } 437 | 438 | #ifndef STBI_WRITE_NO_STDIO 439 | STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) 440 | { 441 | stbi__write_context s; 442 | if (stbi__start_write_file(&s,filename)) { 443 | int r = stbi_write_bmp_core(&s, x, y, comp, data); 444 | stbi__end_write_file(&s); 445 | return r; 446 | } else 447 | return 0; 448 | } 449 | #endif //!STBI_WRITE_NO_STDIO 450 | 451 | static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) 452 | { 453 | int has_alpha = (comp == 2 || comp == 4); 454 | int colorbytes = has_alpha ? comp-1 : comp; 455 | int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 456 | 457 | if (y < 0 || x < 0) 458 | return 0; 459 | 460 | if (!stbi_write_tga_with_rle) { 461 | return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, 462 | "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); 463 | } else { 464 | int i,j,k; 465 | int jend, jdir; 466 | 467 | stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); 468 | 469 | if (stbi__flip_vertically_on_write) { 470 | j = 0; 471 | jend = y; 472 | jdir = 1; 473 | } else { 474 | j = y-1; 475 | jend = -1; 476 | jdir = -1; 477 | } 478 | for (; j != jend; j += jdir) { 479 | unsigned char *row = (unsigned char *) data + j * x * comp; 480 | int len; 481 | 482 | for (i = 0; i < x; i += len) { 483 | unsigned char *begin = row + i * comp; 484 | int diff = 1; 485 | len = 1; 486 | 487 | if (i < x - 1) { 488 | ++len; 489 | diff = memcmp(begin, row + (i + 1) * comp, comp); 490 | if (diff) { 491 | const unsigned char *prev = begin; 492 | for (k = i + 2; k < x && len < 128; ++k) { 493 | if (memcmp(prev, row + k * comp, comp)) { 494 | prev += comp; 495 | ++len; 496 | } else { 497 | --len; 498 | break; 499 | } 500 | } 501 | } else { 502 | for (k = i + 2; k < x && len < 128; ++k) { 503 | if (!memcmp(begin, row + k * comp, comp)) { 504 | ++len; 505 | } else { 506 | break; 507 | } 508 | } 509 | } 510 | } 511 | 512 | if (diff) { 513 | unsigned char header = STBIW_UCHAR(len - 1); 514 | s->func(s->context, &header, 1); 515 | for (k = 0; k < len; ++k) { 516 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); 517 | } 518 | } else { 519 | unsigned char header = STBIW_UCHAR(len - 129); 520 | s->func(s->context, &header, 1); 521 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); 522 | } 523 | } 524 | } 525 | } 526 | return 1; 527 | } 528 | 529 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 530 | { 531 | stbi__write_context s; 532 | stbi__start_write_callbacks(&s, func, context); 533 | return stbi_write_tga_core(&s, x, y, comp, (void *) data); 534 | } 535 | 536 | #ifndef STBI_WRITE_NO_STDIO 537 | STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) 538 | { 539 | stbi__write_context s; 540 | if (stbi__start_write_file(&s,filename)) { 541 | int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); 542 | stbi__end_write_file(&s); 543 | return r; 544 | } else 545 | return 0; 546 | } 547 | #endif 548 | 549 | // ************************************************************************************************* 550 | // Radiance RGBE HDR writer 551 | // by Baldur Karlsson 552 | 553 | #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) 554 | 555 | void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) 556 | { 557 | int exponent; 558 | float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); 559 | 560 | if (maxcomp < 1e-32f) { 561 | rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 562 | } else { 563 | float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; 564 | 565 | rgbe[0] = (unsigned char)(linear[0] * normalize); 566 | rgbe[1] = (unsigned char)(linear[1] * normalize); 567 | rgbe[2] = (unsigned char)(linear[2] * normalize); 568 | rgbe[3] = (unsigned char)(exponent + 128); 569 | } 570 | } 571 | 572 | void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) 573 | { 574 | unsigned char lengthbyte = STBIW_UCHAR(length+128); 575 | STBIW_ASSERT(length+128 <= 255); 576 | s->func(s->context, &lengthbyte, 1); 577 | s->func(s->context, &databyte, 1); 578 | } 579 | 580 | void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) 581 | { 582 | unsigned char lengthbyte = STBIW_UCHAR(length); 583 | STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code 584 | s->func(s->context, &lengthbyte, 1); 585 | s->func(s->context, data, length); 586 | } 587 | 588 | void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) 589 | { 590 | unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; 591 | unsigned char rgbe[4]; 592 | float linear[3]; 593 | int x; 594 | 595 | scanlineheader[2] = (width&0xff00)>>8; 596 | scanlineheader[3] = (width&0x00ff); 597 | 598 | /* skip RLE for images too small or large */ 599 | if (width < 8 || width >= 32768) { 600 | for (x=0; x < width; x++) { 601 | switch (ncomp) { 602 | case 4: /* fallthrough */ 603 | case 3: linear[2] = scanline[x*ncomp + 2]; 604 | linear[1] = scanline[x*ncomp + 1]; 605 | linear[0] = scanline[x*ncomp + 0]; 606 | break; 607 | default: 608 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 609 | break; 610 | } 611 | stbiw__linear_to_rgbe(rgbe, linear); 612 | s->func(s->context, rgbe, 4); 613 | } 614 | } else { 615 | int c,r; 616 | /* encode into scratch buffer */ 617 | for (x=0; x < width; x++) { 618 | switch(ncomp) { 619 | case 4: /* fallthrough */ 620 | case 3: linear[2] = scanline[x*ncomp + 2]; 621 | linear[1] = scanline[x*ncomp + 1]; 622 | linear[0] = scanline[x*ncomp + 0]; 623 | break; 624 | default: 625 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 626 | break; 627 | } 628 | stbiw__linear_to_rgbe(rgbe, linear); 629 | scratch[x + width*0] = rgbe[0]; 630 | scratch[x + width*1] = rgbe[1]; 631 | scratch[x + width*2] = rgbe[2]; 632 | scratch[x + width*3] = rgbe[3]; 633 | } 634 | 635 | s->func(s->context, scanlineheader, 4); 636 | 637 | /* RLE each component separately */ 638 | for (c=0; c < 4; c++) { 639 | unsigned char *comp = &scratch[width*c]; 640 | 641 | x = 0; 642 | while (x < width) { 643 | // find first run 644 | r = x; 645 | while (r+2 < width) { 646 | if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) 647 | break; 648 | ++r; 649 | } 650 | if (r+2 >= width) 651 | r = width; 652 | // dump up to first run 653 | while (x < r) { 654 | int len = r-x; 655 | if (len > 128) len = 128; 656 | stbiw__write_dump_data(s, len, &comp[x]); 657 | x += len; 658 | } 659 | // if there's a run, output it 660 | if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd 661 | // find next byte after run 662 | while (r < width && comp[r] == comp[x]) 663 | ++r; 664 | // output run up to r 665 | while (x < r) { 666 | int len = r-x; 667 | if (len > 127) len = 127; 668 | stbiw__write_run_data(s, len, comp[x]); 669 | x += len; 670 | } 671 | } 672 | } 673 | } 674 | } 675 | } 676 | 677 | static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) 678 | { 679 | if (y <= 0 || x <= 0 || data == NULL) 680 | return 0; 681 | else { 682 | // Each component is stored separately. Allocate scratch space for full output scanline. 683 | unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); 684 | int i, len; 685 | char buffer[128]; 686 | char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; 687 | s->func(s->context, header, sizeof(header)-1); 688 | 689 | #ifdef STBI_MSC_SECURE_CRT 690 | len = sprintf_s(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 691 | #else 692 | len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 693 | #endif 694 | s->func(s->context, buffer, len); 695 | 696 | for(i=0; i < y; i++) 697 | stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)*x); 698 | STBIW_FREE(scratch); 699 | return 1; 700 | } 701 | } 702 | 703 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) 704 | { 705 | stbi__write_context s; 706 | stbi__start_write_callbacks(&s, func, context); 707 | return stbi_write_hdr_core(&s, x, y, comp, (float *) data); 708 | } 709 | 710 | #ifndef STBI_WRITE_NO_STDIO 711 | STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) 712 | { 713 | stbi__write_context s; 714 | if (stbi__start_write_file(&s,filename)) { 715 | int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); 716 | stbi__end_write_file(&s); 717 | return r; 718 | } else 719 | return 0; 720 | } 721 | #endif // STBI_WRITE_NO_STDIO 722 | 723 | 724 | ////////////////////////////////////////////////////////////////////////////// 725 | // 726 | // PNG writer 727 | // 728 | 729 | #ifndef STBIW_ZLIB_COMPRESS 730 | // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() 731 | #define stbiw__sbraw(a) ((int *) (a) - 2) 732 | #define stbiw__sbm(a) stbiw__sbraw(a)[0] 733 | #define stbiw__sbn(a) stbiw__sbraw(a)[1] 734 | 735 | #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) 736 | #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) 737 | #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) 738 | 739 | #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) 740 | #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) 741 | #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) 742 | 743 | static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) 744 | { 745 | int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; 746 | void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); 747 | STBIW_ASSERT(p); 748 | if (p) { 749 | if (!*arr) ((int *) p)[1] = 0; 750 | *arr = (void *) ((int *) p + 2); 751 | stbiw__sbm(*arr) = m; 752 | } 753 | return *arr; 754 | } 755 | 756 | static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) 757 | { 758 | while (*bitcount >= 8) { 759 | stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); 760 | *bitbuffer >>= 8; 761 | *bitcount -= 8; 762 | } 763 | return data; 764 | } 765 | 766 | static int stbiw__zlib_bitrev(int code, int codebits) 767 | { 768 | int res=0; 769 | while (codebits--) { 770 | res = (res << 1) | (code & 1); 771 | code >>= 1; 772 | } 773 | return res; 774 | } 775 | 776 | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) 777 | { 778 | int i; 779 | for (i=0; i < limit && i < 258; ++i) 780 | if (a[i] != b[i]) break; 781 | return i; 782 | } 783 | 784 | static unsigned int stbiw__zhash(unsigned char *data) 785 | { 786 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); 787 | hash ^= hash << 3; 788 | hash += hash >> 5; 789 | hash ^= hash << 4; 790 | hash += hash >> 17; 791 | hash ^= hash << 25; 792 | hash += hash >> 6; 793 | return hash; 794 | } 795 | 796 | #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) 797 | #define stbiw__zlib_add(code,codebits) \ 798 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) 799 | #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) 800 | // default huffman tables 801 | #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) 802 | #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) 803 | #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) 804 | #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) 805 | #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)) 806 | #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) 807 | 808 | #define stbiw__ZHASH 16384 809 | 810 | #endif // STBIW_ZLIB_COMPRESS 811 | 812 | unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) 813 | { 814 | #ifdef STBIW_ZLIB_COMPRESS 815 | // user provided a zlib compress implementation, use that 816 | return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); 817 | #else // use builtin 818 | static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; 819 | static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; 820 | static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; 821 | static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; 822 | unsigned int bitbuf=0; 823 | int i,j, bitcount=0; 824 | unsigned char *out = NULL; 825 | unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); 826 | if (hash_table == NULL) 827 | return NULL; 828 | if (quality < 5) quality = 5; 829 | 830 | stbiw__sbpush(out, 0x78); // DEFLATE 32K window 831 | stbiw__sbpush(out, 0x5e); // FLEVEL = 1 832 | stbiw__zlib_add(1,1); // BFINAL = 1 833 | stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman 834 | 835 | for (i=0; i < stbiw__ZHASH; ++i) 836 | hash_table[i] = NULL; 837 | 838 | i=0; 839 | while (i < data_len-3) { 840 | // hash next 3 bytes of data to be compressed 841 | int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; 842 | unsigned char *bestloc = 0; 843 | unsigned char **hlist = hash_table[h]; 844 | int n = stbiw__sbcount(hlist); 845 | for (j=0; j < n; ++j) { 846 | if (hlist[j]-data > i-32768) { // if entry lies within window 847 | int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); 848 | if (d >= best) best=d,bestloc=hlist[j]; 849 | } 850 | } 851 | // when hash table entry is too long, delete half the entries 852 | if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { 853 | STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); 854 | stbiw__sbn(hash_table[h]) = quality; 855 | } 856 | stbiw__sbpush(hash_table[h],data+i); 857 | 858 | if (bestloc) { 859 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal 860 | h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); 861 | hlist = hash_table[h]; 862 | n = stbiw__sbcount(hlist); 863 | for (j=0; j < n; ++j) { 864 | if (hlist[j]-data > i-32767) { 865 | int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); 866 | if (e > best) { // if next match is better, bail on current match 867 | bestloc = NULL; 868 | break; 869 | } 870 | } 871 | } 872 | } 873 | 874 | if (bestloc) { 875 | int d = (int) (data+i - bestloc); // distance back 876 | STBIW_ASSERT(d <= 32767 && best <= 258); 877 | for (j=0; best > lengthc[j+1]-1; ++j); 878 | stbiw__zlib_huff(j+257); 879 | if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); 880 | for (j=0; d > distc[j+1]-1; ++j); 881 | stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); 882 | if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); 883 | i += best; 884 | } else { 885 | stbiw__zlib_huffb(data[i]); 886 | ++i; 887 | } 888 | } 889 | // write out final bytes 890 | for (;i < data_len; ++i) 891 | stbiw__zlib_huffb(data[i]); 892 | stbiw__zlib_huff(256); // end of block 893 | // pad with 0 bits to byte boundary 894 | while (bitcount) 895 | stbiw__zlib_add(0,1); 896 | 897 | for (i=0; i < stbiw__ZHASH; ++i) 898 | (void) stbiw__sbfree(hash_table[i]); 899 | STBIW_FREE(hash_table); 900 | 901 | { 902 | // compute adler32 on input 903 | unsigned int s1=1, s2=0; 904 | int blocklen = (int) (data_len % 5552); 905 | j=0; 906 | while (j < data_len) { 907 | for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; 908 | s1 %= 65521, s2 %= 65521; 909 | j += blocklen; 910 | blocklen = 5552; 911 | } 912 | stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); 913 | stbiw__sbpush(out, STBIW_UCHAR(s2)); 914 | stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); 915 | stbiw__sbpush(out, STBIW_UCHAR(s1)); 916 | } 917 | *out_len = stbiw__sbn(out); 918 | // make returned pointer freeable 919 | STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); 920 | return (unsigned char *) stbiw__sbraw(out); 921 | #endif // STBIW_ZLIB_COMPRESS 922 | } 923 | 924 | static unsigned int stbiw__crc32(unsigned char *buffer, int len) 925 | { 926 | static unsigned int crc_table[256] = 927 | { 928 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 929 | 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 930 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 931 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 932 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 933 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 934 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 935 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 936 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 937 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 938 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 939 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 940 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 941 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 942 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 943 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 944 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 945 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 946 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 947 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 948 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 949 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 950 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 951 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 952 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 953 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 954 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 955 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 956 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 957 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 958 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 959 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 960 | }; 961 | 962 | unsigned int crc = ~0u; 963 | int i; 964 | for (i=0; i < len; ++i) 965 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; 966 | return ~crc; 967 | } 968 | 969 | #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) 970 | #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); 971 | #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) 972 | 973 | static void stbiw__wpcrc(unsigned char **data, int len) 974 | { 975 | unsigned int crc = stbiw__crc32(*data - len - 4, len+4); 976 | stbiw__wp32(*data, crc); 977 | } 978 | 979 | static unsigned char stbiw__paeth(int a, int b, int c) 980 | { 981 | int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); 982 | if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); 983 | if (pb <= pc) return STBIW_UCHAR(b); 984 | return STBIW_UCHAR(c); 985 | } 986 | 987 | // @OPTIMIZE: provide an option that always forces left-predict or paeth predict 988 | static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) 989 | { 990 | static int mapping[] = { 0,1,2,3,4 }; 991 | static int firstmap[] = { 0,1,0,5,6 }; 992 | int *mymap = (y != 0) ? mapping : firstmap; 993 | int i; 994 | int type = mymap[filter_type]; 995 | unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); 996 | int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; 997 | for (i = 0; i < n; ++i) { 998 | switch (type) { 999 | case 0: line_buffer[i] = z[i]; break; 1000 | case 1: line_buffer[i] = z[i]; break; 1001 | case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; 1002 | case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; 1003 | case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; 1004 | case 5: line_buffer[i] = z[i]; break; 1005 | case 6: line_buffer[i] = z[i]; break; 1006 | } 1007 | } 1008 | for (i=n; i < width*n; ++i) { 1009 | switch (type) { 1010 | case 0: line_buffer[i] = z[i]; break; 1011 | case 1: line_buffer[i] = z[i] - z[i-n]; break; 1012 | case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; 1013 | case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; 1014 | case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; 1015 | case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; 1016 | case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; 1017 | } 1018 | } 1019 | } 1020 | 1021 | unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) 1022 | { 1023 | int force_filter = stbi_write_force_png_filter; 1024 | int ctype[5] = { -1, 0, 4, 2, 6 }; 1025 | unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; 1026 | unsigned char *out,*o, *filt, *zlib; 1027 | signed char *line_buffer; 1028 | int j,zlen; 1029 | 1030 | if (stride_bytes == 0) 1031 | stride_bytes = x * n; 1032 | 1033 | if (force_filter >= 5) { 1034 | force_filter = -1; 1035 | } 1036 | 1037 | filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; 1038 | line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } 1039 | for (j=0; j < y; ++j) { 1040 | int filter_type; 1041 | if (force_filter > -1) { 1042 | filter_type = force_filter; 1043 | stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, line_buffer); 1044 | } else { // Estimate the best filter by running through all of them: 1045 | int best_filter = 0, best_filter_val = 0x7fffffff, est, i; 1046 | for (filter_type = 0; filter_type < 5; filter_type++) { 1047 | stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, line_buffer); 1048 | 1049 | // Estimate the entropy of the line using this filter; the less, the better. 1050 | est = 0; 1051 | for (i = 0; i < x*n; ++i) { 1052 | est += abs((signed char) line_buffer[i]); 1053 | } 1054 | if (est < best_filter_val) { 1055 | best_filter_val = est; 1056 | best_filter = filter_type; 1057 | } 1058 | } 1059 | if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it 1060 | stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, line_buffer); 1061 | filter_type = best_filter; 1062 | } 1063 | } 1064 | // when we get here, filter_type contains the filter type, and line_buffer contains the data 1065 | filt[j*(x*n+1)] = (unsigned char) filter_type; 1066 | STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); 1067 | } 1068 | STBIW_FREE(line_buffer); 1069 | zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); 1070 | STBIW_FREE(filt); 1071 | if (!zlib) return 0; 1072 | 1073 | // each tag requires 12 bytes of overhead 1074 | out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); 1075 | if (!out) return 0; 1076 | *out_len = 8 + 12+13 + 12+zlen + 12; 1077 | 1078 | o=out; 1079 | STBIW_MEMMOVE(o,sig,8); o+= 8; 1080 | stbiw__wp32(o, 13); // header length 1081 | stbiw__wptag(o, "IHDR"); 1082 | stbiw__wp32(o, x); 1083 | stbiw__wp32(o, y); 1084 | *o++ = 8; 1085 | *o++ = STBIW_UCHAR(ctype[n]); 1086 | *o++ = 0; 1087 | *o++ = 0; 1088 | *o++ = 0; 1089 | stbiw__wpcrc(&o,13); 1090 | 1091 | stbiw__wp32(o, zlen); 1092 | stbiw__wptag(o, "IDAT"); 1093 | STBIW_MEMMOVE(o, zlib, zlen); 1094 | o += zlen; 1095 | STBIW_FREE(zlib); 1096 | stbiw__wpcrc(&o, zlen); 1097 | 1098 | stbiw__wp32(o,0); 1099 | stbiw__wptag(o, "IEND"); 1100 | stbiw__wpcrc(&o,0); 1101 | 1102 | STBIW_ASSERT(o == out + *out_len); 1103 | 1104 | return out; 1105 | } 1106 | 1107 | #ifndef STBI_WRITE_NO_STDIO 1108 | STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) 1109 | { 1110 | FILE *f; 1111 | int len; 1112 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 1113 | if (png == NULL) return 0; 1114 | #ifdef STBI_MSC_SECURE_CRT 1115 | if (fopen_s(&f, filename, "wb")) 1116 | f = NULL; 1117 | #else 1118 | f = fopen(filename, "wb"); 1119 | #endif 1120 | if (!f) { STBIW_FREE(png); return 0; } 1121 | fwrite(png, 1, len, f); 1122 | fclose(f); 1123 | STBIW_FREE(png); 1124 | return 1; 1125 | } 1126 | #endif 1127 | 1128 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) 1129 | { 1130 | int len; 1131 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 1132 | if (png == NULL) return 0; 1133 | func(context, png, len); 1134 | STBIW_FREE(png); 1135 | return 1; 1136 | } 1137 | 1138 | 1139 | /* *************************************************************************** 1140 | * 1141 | * JPEG writer 1142 | * 1143 | * This is based on Jon Olick's jo_jpeg.cpp: 1144 | * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html 1145 | */ 1146 | 1147 | static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, 1148 | 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; 1149 | 1150 | static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { 1151 | int bitBuf = *bitBufP, bitCnt = *bitCntP; 1152 | bitCnt += bs[1]; 1153 | bitBuf |= bs[0] << (24 - bitCnt); 1154 | while(bitCnt >= 8) { 1155 | unsigned char c = (bitBuf >> 16) & 255; 1156 | stbiw__putc(s, c); 1157 | if(c == 255) { 1158 | stbiw__putc(s, 0); 1159 | } 1160 | bitBuf <<= 8; 1161 | bitCnt -= 8; 1162 | } 1163 | *bitBufP = bitBuf; 1164 | *bitCntP = bitCnt; 1165 | } 1166 | 1167 | static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { 1168 | float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; 1169 | float z1, z2, z3, z4, z5, z11, z13; 1170 | 1171 | float tmp0 = d0 + d7; 1172 | float tmp7 = d0 - d7; 1173 | float tmp1 = d1 + d6; 1174 | float tmp6 = d1 - d6; 1175 | float tmp2 = d2 + d5; 1176 | float tmp5 = d2 - d5; 1177 | float tmp3 = d3 + d4; 1178 | float tmp4 = d3 - d4; 1179 | 1180 | // Even part 1181 | float tmp10 = tmp0 + tmp3; // phase 2 1182 | float tmp13 = tmp0 - tmp3; 1183 | float tmp11 = tmp1 + tmp2; 1184 | float tmp12 = tmp1 - tmp2; 1185 | 1186 | d0 = tmp10 + tmp11; // phase 3 1187 | d4 = tmp10 - tmp11; 1188 | 1189 | z1 = (tmp12 + tmp13) * 0.707106781f; // c4 1190 | d2 = tmp13 + z1; // phase 5 1191 | d6 = tmp13 - z1; 1192 | 1193 | // Odd part 1194 | tmp10 = tmp4 + tmp5; // phase 2 1195 | tmp11 = tmp5 + tmp6; 1196 | tmp12 = tmp6 + tmp7; 1197 | 1198 | // The rotator is modified from fig 4-8 to avoid extra negations. 1199 | z5 = (tmp10 - tmp12) * 0.382683433f; // c6 1200 | z2 = tmp10 * 0.541196100f + z5; // c2-c6 1201 | z4 = tmp12 * 1.306562965f + z5; // c2+c6 1202 | z3 = tmp11 * 0.707106781f; // c4 1203 | 1204 | z11 = tmp7 + z3; // phase 5 1205 | z13 = tmp7 - z3; 1206 | 1207 | *d5p = z13 + z2; // phase 6 1208 | *d3p = z13 - z2; 1209 | *d1p = z11 + z4; 1210 | *d7p = z11 - z4; 1211 | 1212 | *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; 1213 | } 1214 | 1215 | static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { 1216 | int tmp1 = val < 0 ? -val : val; 1217 | val = val < 0 ? val-1 : val; 1218 | bits[1] = 1; 1219 | while(tmp1 >>= 1) { 1220 | ++bits[1]; 1221 | } 1222 | bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { 1260 | } 1261 | // end0pos = first element in reverse order !=0 1262 | if(end0pos == 0) { 1263 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); 1264 | return DU[0]; 1265 | } 1266 | for(i = 1; i <= end0pos; ++i) { 1267 | int startpos = i; 1268 | int nrzeroes; 1269 | unsigned short bits[2]; 1270 | for (; DU[i]==0 && i<=end0pos; ++i) { 1271 | } 1272 | nrzeroes = i-startpos; 1273 | if ( nrzeroes >= 16 ) { 1274 | int lng = nrzeroes>>4; 1275 | int nrmarker; 1276 | for (nrmarker=1; nrmarker <= lng; ++nrmarker) 1277 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); 1278 | nrzeroes &= 15; 1279 | } 1280 | stbiw__jpg_calcBits(DU[i], bits); 1281 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); 1282 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); 1283 | } 1284 | if(end0pos != 63) { 1285 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); 1286 | } 1287 | return DU[0]; 1288 | } 1289 | 1290 | static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { 1291 | // Constants that don't pollute global namespace 1292 | 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}; 1293 | static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; 1294 | 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}; 1295 | static const unsigned char std_ac_luminance_values[] = { 1296 | 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 1297 | 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 1298 | 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 1299 | 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 1300 | 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 1301 | 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 1302 | 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa 1303 | }; 1304 | 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}; 1305 | static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; 1306 | 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}; 1307 | static const unsigned char std_ac_chrominance_values[] = { 1308 | 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 1309 | 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 1310 | 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 1311 | 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 1312 | 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 1313 | 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 1314 | 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa 1315 | }; 1316 | // Huffman tables 1317 | static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; 1318 | static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; 1319 | static const unsigned short YAC_HT[256][2] = { 1320 | {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1321 | {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1322 | {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1323 | {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1324 | {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1325 | {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1326 | {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1327 | {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1328 | {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1329 | {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1330 | {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1331 | {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1332 | {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1333 | {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1334 | {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, 1335 | {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} 1336 | }; 1337 | static const unsigned short UVAC_HT[256][2] = { 1338 | {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1339 | {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1340 | {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1341 | {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1342 | {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1343 | {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1344 | {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1345 | {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1346 | {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1347 | {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1348 | {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1349 | {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1350 | {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1351 | {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1352 | {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, 1353 | {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} 1354 | }; 1355 | static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, 1356 | 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; 1357 | static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, 1358 | 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; 1359 | static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1360 | 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; 1361 | 1362 | int row, col, i, k; 1363 | float fdtbl_Y[64], fdtbl_UV[64]; 1364 | unsigned char YTable[64], UVTable[64]; 1365 | 1366 | if(!data || !width || !height || comp > 4 || comp < 1) { 1367 | return 0; 1368 | } 1369 | 1370 | quality = quality ? quality : 90; 1371 | quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; 1372 | quality = quality < 50 ? 5000 / quality : 200 - quality * 2; 1373 | 1374 | for(i = 0; i < 64; ++i) { 1375 | int uvti, yti = (YQT[i]*quality+50)/100; 1376 | YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); 1377 | uvti = (UVQT[i]*quality+50)/100; 1378 | UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); 1379 | } 1380 | 1381 | for(row = 0, k = 0; row < 8; ++row) { 1382 | for(col = 0; col < 8; ++col, ++k) { 1383 | fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); 1384 | fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); 1385 | } 1386 | } 1387 | 1388 | // Write Headers 1389 | { 1390 | static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; 1391 | static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; 1392 | const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), 1393 | 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; 1394 | s->func(s->context, (void*)head0, sizeof(head0)); 1395 | s->func(s->context, (void*)YTable, sizeof(YTable)); 1396 | stbiw__putc(s, 1); 1397 | s->func(s->context, UVTable, sizeof(UVTable)); 1398 | s->func(s->context, (void*)head1, sizeof(head1)); 1399 | s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); 1400 | s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); 1401 | stbiw__putc(s, 0x10); // HTYACinfo 1402 | s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); 1403 | s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); 1404 | stbiw__putc(s, 1); // HTUDCinfo 1405 | s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); 1406 | s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); 1407 | stbiw__putc(s, 0x11); // HTUACinfo 1408 | s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); 1409 | s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); 1410 | s->func(s->context, (void*)head2, sizeof(head2)); 1411 | } 1412 | 1413 | // Encode 8x8 macroblocks 1414 | { 1415 | static const unsigned short fillBits[] = {0x7F, 7}; 1416 | const unsigned char *imageData = (const unsigned char *)data; 1417 | int DCY=0, DCU=0, DCV=0; 1418 | int bitBuf=0, bitCnt=0; 1419 | // comp == 2 is grey+alpha (alpha is ignored) 1420 | int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; 1421 | int x, y, pos; 1422 | for(y = 0; y < height; y += 8) { 1423 | for(x = 0; x < width; x += 8) { 1424 | float YDU[64], UDU[64], VDU[64]; 1425 | for(row = y, pos = 0; row < y+8; ++row) { 1426 | for(col = x; col < x+8; ++col, ++pos) { 1427 | int p = (stbi__flip_vertically_on_write ? height-1-row : row)*width*comp + col*comp; 1428 | float r, g, b; 1429 | if(row >= height) { 1430 | p -= width*comp*(row+1 - height); 1431 | } 1432 | if(col >= width) { 1433 | p -= comp*(col+1 - width); 1434 | } 1435 | 1436 | r = imageData[p+0]; 1437 | g = imageData[p+ofsG]; 1438 | b = imageData[p+ofsB]; 1439 | YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; 1440 | UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; 1441 | VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; 1442 | } 1443 | } 1444 | 1445 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); 1446 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); 1447 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); 1448 | } 1449 | } 1450 | 1451 | // Do the bit alignment of the EOI marker 1452 | stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); 1453 | } 1454 | 1455 | // EOI 1456 | stbiw__putc(s, 0xFF); 1457 | stbiw__putc(s, 0xD9); 1458 | 1459 | return 1; 1460 | } 1461 | 1462 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) 1463 | { 1464 | stbi__write_context s; 1465 | stbi__start_write_callbacks(&s, func, context); 1466 | return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); 1467 | } 1468 | 1469 | 1470 | #ifndef STBI_WRITE_NO_STDIO 1471 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) 1472 | { 1473 | stbi__write_context s; 1474 | if (stbi__start_write_file(&s,filename)) { 1475 | int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); 1476 | stbi__end_write_file(&s); 1477 | return r; 1478 | } else 1479 | return 0; 1480 | } 1481 | #endif 1482 | 1483 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION 1484 | 1485 | /* Revision history 1486 | 1.09 (2018-02-11) 1487 | fix typo in zlib quality API, improve STB_I_W_STATIC in C++ 1488 | 1.08 (2018-01-29) 1489 | add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 1490 | 1.07 (2017-07-24) 1491 | doc fix 1492 | 1.06 (2017-07-23) 1493 | writing JPEG (using Jon Olick's code) 1494 | 1.05 ??? 1495 | 1.04 (2017-03-03) 1496 | monochrome BMP expansion 1497 | 1.03 ??? 1498 | 1.02 (2016-04-02) 1499 | avoid allocating large structures on the stack 1500 | 1.01 (2016-01-16) 1501 | STBIW_REALLOC_SIZED: support allocators with no realloc support 1502 | avoid race-condition in crc initialization 1503 | minor compile issues 1504 | 1.00 (2015-09-14) 1505 | installable file IO function 1506 | 0.99 (2015-09-13) 1507 | warning fixes; TGA rle support 1508 | 0.98 (2015-04-08) 1509 | added STBIW_MALLOC, STBIW_ASSERT etc 1510 | 0.97 (2015-01-18) 1511 | fixed HDR asserts, rewrote HDR rle logic 1512 | 0.96 (2015-01-17) 1513 | add HDR output 1514 | fix monochrome BMP 1515 | 0.95 (2014-08-17) 1516 | add monochrome TGA output 1517 | 0.94 (2014-05-31) 1518 | rename private functions to avoid conflicts with stb_image.h 1519 | 0.93 (2014-05-27) 1520 | warning fixes 1521 | 0.92 (2010-08-01) 1522 | casts to unsigned char to fix warnings 1523 | 0.91 (2010-07-17) 1524 | first public release 1525 | 0.90 first internal release 1526 | */ 1527 | 1528 | /* 1529 | ------------------------------------------------------------------------------ 1530 | This software is available under 2 licenses -- choose whichever you prefer. 1531 | ------------------------------------------------------------------------------ 1532 | ALTERNATIVE A - MIT License 1533 | Copyright (c) 2017 Sean Barrett 1534 | Permission is hereby granted, free of charge, to any person obtaining a copy of 1535 | this software and associated documentation files (the "Software"), to deal in 1536 | the Software without restriction, including without limitation the rights to 1537 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1538 | of the Software, and to permit persons to whom the Software is furnished to do 1539 | so, subject to the following conditions: 1540 | The above copyright notice and this permission notice shall be included in all 1541 | copies or substantial portions of the Software. 1542 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1543 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1544 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1545 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1546 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1547 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1548 | SOFTWARE. 1549 | ------------------------------------------------------------------------------ 1550 | ALTERNATIVE B - Public Domain (www.unlicense.org) 1551 | This is free and unencumbered software released into the public domain. 1552 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1553 | software, either in source code form or as a compiled binary, for any purpose, 1554 | commercial or non-commercial, and by any means. 1555 | In jurisdictions that recognize copyright laws, the author or authors of this 1556 | software dedicate any and all copyright interest in the software to the public 1557 | domain. We make this dedication for the benefit of the public at large and to 1558 | the detriment of our heirs and successors. We intend this dedication to be an 1559 | overt act of relinquishment in perpetuity of all present and future rights to 1560 | this software under copyright law. 1561 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1562 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1563 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1564 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1565 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1566 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1567 | ------------------------------------------------------------------------------ 1568 | */ 1569 | -------------------------------------------------------------------------------- /stb_rect_pack.h: -------------------------------------------------------------------------------- 1 | // stb_rect_pack.h - v0.11 - public domain - rectangle packing 2 | // Sean Barrett 2014 3 | // 4 | // Useful for e.g. packing rectangular textures into an atlas. 5 | // Does not do rotation. 6 | // 7 | // Not necessarily the awesomest packing method, but better than 8 | // the totally naive one in stb_truetype (which is primarily what 9 | // this is meant to replace). 10 | // 11 | // Has only had a few tests run, may have issues. 12 | // 13 | // More docs to come. 14 | // 15 | // No memory allocations; uses qsort() and assert() from stdlib. 16 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 17 | // 18 | // This library currently uses the Skyline Bottom-Left algorithm. 19 | // 20 | // Please note: better rectangle packers are welcome! Please 21 | // implement them to the same API, but with a different init 22 | // function. 23 | // 24 | // Credits 25 | // 26 | // Library 27 | // Sean Barrett 28 | // Minor features 29 | // Martins Mozeiko 30 | // github:IntellectualKitty 31 | // 32 | // Bugfixes / warning fixes 33 | // Jeremy Jaussaud 34 | // 35 | // Version history: 36 | // 37 | // 0.11 (2017-03-03) return packing success/fail result 38 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 39 | // 0.09 (2016-08-27) fix compiler warnings 40 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 41 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 42 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 43 | // 0.05: added STBRP_ASSERT to allow replacing assert 44 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 45 | // 0.01: initial release 46 | // 47 | // LICENSE 48 | // 49 | // See end of file for license information. 50 | 51 | ////////////////////////////////////////////////////////////////////////////// 52 | // 53 | // INCLUDE SECTION 54 | // 55 | 56 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 57 | #define STB_INCLUDE_STB_RECT_PACK_H 58 | 59 | #define STB_RECT_PACK_VERSION 1 60 | 61 | #ifdef STBRP_STATIC 62 | #define STBRP_DEF static 63 | #else 64 | #define STBRP_DEF extern 65 | #endif 66 | 67 | #ifdef __cplusplus 68 | extern "C" { 69 | #endif 70 | 71 | typedef struct stbrp_context stbrp_context; 72 | typedef struct stbrp_node stbrp_node; 73 | typedef struct stbrp_rect stbrp_rect; 74 | 75 | #ifdef STBRP_LARGE_RECTS 76 | typedef int stbrp_coord; 77 | #else 78 | typedef unsigned short stbrp_coord; 79 | #endif 80 | 81 | STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 82 | // Assign packed locations to rectangles. The rectangles are of type 83 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 84 | // are 'num_rects' many of them. 85 | // 86 | // Rectangles which are successfully packed have the 'was_packed' flag 87 | // set to a non-zero value and 'x' and 'y' store the minimum location 88 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 89 | // if you imagine y increasing downwards). Rectangles which do not fit 90 | // have the 'was_packed' flag set to 0. 91 | // 92 | // You should not try to access the 'rects' array from another thread 93 | // while this function is running, as the function temporarily reorders 94 | // the array while it executes. 95 | // 96 | // To pack into another rectangle, you need to call stbrp_init_target 97 | // again. To continue packing into the same rectangle, you can call 98 | // this function again. Calling this multiple times with multiple rect 99 | // arrays will probably produce worse packing results than calling it 100 | // a single time with the full rectangle array, but the option is 101 | // available. 102 | // 103 | // The function returns 1 if all of the rectangles were successfully 104 | // packed and 0 otherwise. 105 | 106 | struct stbrp_rect 107 | { 108 | // reserved for your use: 109 | int id; 110 | 111 | // input: 112 | stbrp_coord w, h; 113 | 114 | // output: 115 | stbrp_coord x, y; 116 | int was_packed; // non-zero if valid packing 117 | 118 | }; // 16 bytes, nominally 119 | 120 | 121 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 122 | // Initialize a rectangle packer to: 123 | // pack a rectangle that is 'width' by 'height' in dimensions 124 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 125 | // 126 | // You must call this function every time you start packing into a new target. 127 | // 128 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 129 | // the following stbrp_pack_rects() call (or calls), but can be freed after 130 | // the call (or calls) finish. 131 | // 132 | // Note: to guarantee best results, either: 133 | // 1. make sure 'num_nodes' >= 'width' 134 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 135 | // 136 | // If you don't do either of the above things, widths will be quantized to multiples 137 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 138 | // 139 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 140 | // may run out of temporary storage and be unable to pack some rectangles. 141 | 142 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 143 | // Optionally call this function after init but before doing any packing to 144 | // change the handling of the out-of-temp-memory scenario, described above. 145 | // If you call init again, this will be reset to the default (false). 146 | 147 | 148 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 149 | // Optionally select which packing heuristic the library should use. Different 150 | // heuristics will produce better/worse results for different data sets. 151 | // If you call init again, this will be reset to the default. 152 | 153 | enum 154 | { 155 | STBRP_HEURISTIC_Skyline_default=0, 156 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 157 | STBRP_HEURISTIC_Skyline_BF_sortHeight 158 | }; 159 | 160 | 161 | ////////////////////////////////////////////////////////////////////////////// 162 | // 163 | // the details of the following structures don't matter to you, but they must 164 | // be visible so you can handle the memory allocations for them 165 | 166 | struct stbrp_node 167 | { 168 | stbrp_coord x,y; 169 | stbrp_node *next; 170 | }; 171 | 172 | struct stbrp_context 173 | { 174 | int width; 175 | int height; 176 | int align; 177 | int init_mode; 178 | int heuristic; 179 | int num_nodes; 180 | stbrp_node *active_head; 181 | stbrp_node *free_head; 182 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 183 | }; 184 | 185 | #ifdef __cplusplus 186 | } 187 | #endif 188 | 189 | #endif 190 | 191 | ////////////////////////////////////////////////////////////////////////////// 192 | // 193 | // IMPLEMENTATION SECTION 194 | // 195 | 196 | #ifdef STB_RECT_PACK_IMPLEMENTATION 197 | #ifndef STBRP_SORT 198 | #include 199 | #define STBRP_SORT qsort 200 | #endif 201 | 202 | #ifndef STBRP_ASSERT 203 | #include 204 | #define STBRP_ASSERT assert 205 | #endif 206 | 207 | #ifdef _MSC_VER 208 | #define STBRP__NOTUSED(v) (void)(v) 209 | #else 210 | #define STBRP__NOTUSED(v) (void)sizeof(v) 211 | #endif 212 | 213 | enum 214 | { 215 | STBRP__INIT_skyline = 1 216 | }; 217 | 218 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 219 | { 220 | switch (context->init_mode) { 221 | case STBRP__INIT_skyline: 222 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 223 | context->heuristic = heuristic; 224 | break; 225 | default: 226 | STBRP_ASSERT(0); 227 | } 228 | } 229 | 230 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 231 | { 232 | if (allow_out_of_mem) 233 | // if it's ok to run out of memory, then don't bother aligning them; 234 | // this gives better packing, but may fail due to OOM (even though 235 | // the rectangles easily fit). @TODO a smarter approach would be to only 236 | // quantize once we've hit OOM, then we could get rid of this parameter. 237 | context->align = 1; 238 | else { 239 | // if it's not ok to run out of memory, then quantize the widths 240 | // so that num_nodes is always enough nodes. 241 | // 242 | // I.e. num_nodes * align >= width 243 | // align >= width / num_nodes 244 | // align = ceil(width/num_nodes) 245 | 246 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 247 | } 248 | } 249 | 250 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 251 | { 252 | int i; 253 | #ifndef STBRP_LARGE_RECTS 254 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 255 | #endif 256 | 257 | for (i=0; i < num_nodes-1; ++i) 258 | nodes[i].next = &nodes[i+1]; 259 | nodes[i].next = NULL; 260 | context->init_mode = STBRP__INIT_skyline; 261 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 262 | context->free_head = &nodes[0]; 263 | context->active_head = &context->extra[0]; 264 | context->width = width; 265 | context->height = height; 266 | context->num_nodes = num_nodes; 267 | stbrp_setup_allow_out_of_mem(context, 0); 268 | 269 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 270 | context->extra[0].x = 0; 271 | context->extra[0].y = 0; 272 | context->extra[0].next = &context->extra[1]; 273 | context->extra[1].x = (stbrp_coord) width; 274 | #ifdef STBRP_LARGE_RECTS 275 | context->extra[1].y = (1<<30); 276 | #else 277 | context->extra[1].y = 65535; 278 | #endif 279 | context->extra[1].next = NULL; 280 | } 281 | 282 | // find minimum y position if it starts at x1 283 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 284 | { 285 | stbrp_node *node = first; 286 | int x1 = x0 + width; 287 | int min_y, visited_width, waste_area; 288 | 289 | STBRP__NOTUSED(c); 290 | 291 | STBRP_ASSERT(first->x <= x0); 292 | 293 | #if 0 294 | // skip in case we're past the node 295 | while (node->next->x <= x0) 296 | ++node; 297 | #else 298 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 299 | #endif 300 | 301 | STBRP_ASSERT(node->x <= x0); 302 | 303 | min_y = 0; 304 | waste_area = 0; 305 | visited_width = 0; 306 | while (node->x < x1) { 307 | if (node->y > min_y) { 308 | // raise min_y higher. 309 | // we've accounted for all waste up to min_y, 310 | // but we'll now add more waste for everything we've visted 311 | waste_area += visited_width * (node->y - min_y); 312 | min_y = node->y; 313 | // the first time through, visited_width might be reduced 314 | if (node->x < x0) 315 | visited_width += node->next->x - x0; 316 | else 317 | visited_width += node->next->x - node->x; 318 | } else { 319 | // add waste area 320 | int under_width = node->next->x - node->x; 321 | if (under_width + visited_width > width) 322 | under_width = width - visited_width; 323 | waste_area += under_width * (min_y - node->y); 324 | visited_width += under_width; 325 | } 326 | node = node->next; 327 | } 328 | 329 | *pwaste = waste_area; 330 | return min_y; 331 | } 332 | 333 | typedef struct 334 | { 335 | int x,y; 336 | stbrp_node **prev_link; 337 | } stbrp__findresult; 338 | 339 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 340 | { 341 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 342 | stbrp__findresult fr; 343 | stbrp_node **prev, *node, *tail, **best = NULL; 344 | 345 | // align to multiple of c->align 346 | width = (width + c->align - 1); 347 | width -= width % c->align; 348 | STBRP_ASSERT(width % c->align == 0); 349 | 350 | node = c->active_head; 351 | prev = &c->active_head; 352 | while (node->x + width <= c->width) { 353 | int y,waste; 354 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 355 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 356 | // bottom left 357 | if (y < best_y) { 358 | best_y = y; 359 | best = prev; 360 | } 361 | } else { 362 | // best-fit 363 | if (y + height <= c->height) { 364 | // can only use it if it first vertically 365 | if (y < best_y || (y == best_y && waste < best_waste)) { 366 | best_y = y; 367 | best_waste = waste; 368 | best = prev; 369 | } 370 | } 371 | } 372 | prev = &node->next; 373 | node = node->next; 374 | } 375 | 376 | best_x = (best == NULL) ? 0 : (*best)->x; 377 | 378 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 379 | // 380 | // e.g, if fitting 381 | // 382 | // ____________________ 383 | // |____________________| 384 | // 385 | // into 386 | // 387 | // | | 388 | // | ____________| 389 | // |____________| 390 | // 391 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 392 | // 393 | // This makes BF take about 2x the time 394 | 395 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 396 | tail = c->active_head; 397 | node = c->active_head; 398 | prev = &c->active_head; 399 | // find first node that's admissible 400 | while (tail->x < width) 401 | tail = tail->next; 402 | while (tail) { 403 | int xpos = tail->x - width; 404 | int y,waste; 405 | STBRP_ASSERT(xpos >= 0); 406 | // find the left position that matches this 407 | while (node->next->x <= xpos) { 408 | prev = &node->next; 409 | node = node->next; 410 | } 411 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 412 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 413 | if (y + height < c->height) { 414 | if (y <= best_y) { 415 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 416 | best_x = xpos; 417 | STBRP_ASSERT(y <= best_y); 418 | best_y = y; 419 | best_waste = waste; 420 | best = prev; 421 | } 422 | } 423 | } 424 | tail = tail->next; 425 | } 426 | } 427 | 428 | fr.prev_link = best; 429 | fr.x = best_x; 430 | fr.y = best_y; 431 | return fr; 432 | } 433 | 434 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 435 | { 436 | // find best position according to heuristic 437 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 438 | stbrp_node *node, *cur; 439 | 440 | // bail if: 441 | // 1. it failed 442 | // 2. the best node doesn't fit (we don't always check this) 443 | // 3. we're out of memory 444 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 445 | res.prev_link = NULL; 446 | return res; 447 | } 448 | 449 | // on success, create new node 450 | node = context->free_head; 451 | node->x = (stbrp_coord) res.x; 452 | node->y = (stbrp_coord) (res.y + height); 453 | 454 | context->free_head = node->next; 455 | 456 | // insert the new node into the right starting point, and 457 | // let 'cur' point to the remaining nodes needing to be 458 | // stiched back in 459 | 460 | cur = *res.prev_link; 461 | if (cur->x < res.x) { 462 | // preserve the existing one, so start testing with the next one 463 | stbrp_node *next = cur->next; 464 | cur->next = node; 465 | cur = next; 466 | } else { 467 | *res.prev_link = node; 468 | } 469 | 470 | // from here, traverse cur and free the nodes, until we get to one 471 | // that shouldn't be freed 472 | while (cur->next && cur->next->x <= res.x + width) { 473 | stbrp_node *next = cur->next; 474 | // move the current node to the free list 475 | cur->next = context->free_head; 476 | context->free_head = cur; 477 | cur = next; 478 | } 479 | 480 | // stitch the list back in 481 | node->next = cur; 482 | 483 | if (cur->x < res.x + width) 484 | cur->x = (stbrp_coord) (res.x + width); 485 | 486 | #ifdef _DEBUG 487 | cur = context->active_head; 488 | while (cur->x < context->width) { 489 | STBRP_ASSERT(cur->x < cur->next->x); 490 | cur = cur->next; 491 | } 492 | STBRP_ASSERT(cur->next == NULL); 493 | 494 | { 495 | stbrp_node *L1 = NULL, *L2 = NULL; 496 | int count=0; 497 | cur = context->active_head; 498 | while (cur) { 499 | L1 = cur; 500 | cur = cur->next; 501 | ++count; 502 | } 503 | cur = context->free_head; 504 | while (cur) { 505 | L2 = cur; 506 | cur = cur->next; 507 | ++count; 508 | } 509 | STBRP_ASSERT(count == context->num_nodes+2); 510 | } 511 | #endif 512 | 513 | return res; 514 | } 515 | 516 | static int rect_height_compare(const void *a, const void *b) 517 | { 518 | const stbrp_rect *p = (const stbrp_rect *) a; 519 | const stbrp_rect *q = (const stbrp_rect *) b; 520 | if (p->h > q->h) 521 | return -1; 522 | if (p->h < q->h) 523 | return 1; 524 | return (p->w > q->w) ? -1 : (p->w < q->w); 525 | } 526 | 527 | static int rect_original_order(const void *a, const void *b) 528 | { 529 | const stbrp_rect *p = (const stbrp_rect *) a; 530 | const stbrp_rect *q = (const stbrp_rect *) b; 531 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 532 | } 533 | 534 | #ifdef STBRP_LARGE_RECTS 535 | #define STBRP__MAXVAL 0xffffffff 536 | #else 537 | #define STBRP__MAXVAL 0xffff 538 | #endif 539 | 540 | STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 541 | { 542 | int i, all_rects_packed = 1; 543 | 544 | // we use the 'was_packed' field internally to allow sorting/unsorting 545 | for (i=0; i < num_rects; ++i) { 546 | rects[i].was_packed = i; 547 | #ifndef STBRP_LARGE_RECTS 548 | STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); 549 | #endif 550 | } 551 | 552 | // sort according to heuristic 553 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 554 | 555 | for (i=0; i < num_rects; ++i) { 556 | if (rects[i].w == 0 || rects[i].h == 0) { 557 | rects[i].x = rects[i].y = 0; // empty rect needs no space 558 | } else { 559 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 560 | if (fr.prev_link) { 561 | rects[i].x = (stbrp_coord) fr.x; 562 | rects[i].y = (stbrp_coord) fr.y; 563 | } else { 564 | rects[i].x = rects[i].y = STBRP__MAXVAL; 565 | } 566 | } 567 | } 568 | 569 | // unsort 570 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 571 | 572 | // set was_packed flags and all_rects_packed status 573 | for (i=0; i < num_rects; ++i) { 574 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 575 | if (!rects[i].was_packed) 576 | all_rects_packed = 0; 577 | } 578 | 579 | // return the all_rects_packed status 580 | return all_rects_packed; 581 | } 582 | #endif 583 | 584 | /* 585 | ------------------------------------------------------------------------------ 586 | This software is available under 2 licenses -- choose whichever you prefer. 587 | ------------------------------------------------------------------------------ 588 | ALTERNATIVE A - MIT License 589 | Copyright (c) 2017 Sean Barrett 590 | Permission is hereby granted, free of charge, to any person obtaining a copy of 591 | this software and associated documentation files (the "Software"), to deal in 592 | the Software without restriction, including without limitation the rights to 593 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 594 | of the Software, and to permit persons to whom the Software is furnished to do 595 | so, subject to the following conditions: 596 | The above copyright notice and this permission notice shall be included in all 597 | copies or substantial portions of the Software. 598 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 599 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 600 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 601 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 602 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 603 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 604 | SOFTWARE. 605 | ------------------------------------------------------------------------------ 606 | ALTERNATIVE B - Public Domain (www.unlicense.org) 607 | This is free and unencumbered software released into the public domain. 608 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 609 | software, either in source code form or as a compiled binary, for any purpose, 610 | commercial or non-commercial, and by any means. 611 | In jurisdictions that recognize copyright laws, the author or authors of this 612 | software dedicate any and all copyright interest in the software to the public 613 | domain. We make this dedication for the benefit of the public at large and to 614 | the detriment of our heirs and successors. We intend this dedication to be an 615 | overt act of relinquishment in perpetuity of all present and future rights to 616 | this software under copyright law. 617 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 618 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 619 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 620 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 621 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 622 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 623 | ------------------------------------------------------------------------------ 624 | */ 625 | -------------------------------------------------------------------------------- /tinyfiles.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | tinyfiles.h - v1.0 7 | 8 | To create implementation (the function definitions) 9 | #define TINYFILES_IMPLEMENTATION 10 | in *one* C/CPP file (translation unit) that includes this file 11 | 12 | Summary: 13 | Utility header for traversing directories to apply a function on each found file. 14 | Recursively finds sub-directories. Can also be used to iterate over files in a 15 | folder manually. All operations done in a cross-platform manner (thx posix!). 16 | 17 | This header does no dynamic memory allocation, and performs internally safe string 18 | copies as necessary. Strings for paths, file names and file extensions are all 19 | capped, and intended to use primarily the C run-time stack memory. Feel free to 20 | modify the defines in this file to adjust string size limitations. 21 | 22 | Read the header for specifics on each function. 23 | 24 | Here's an example to print all files in a folder: 25 | tfDIR dir; 26 | tfDirOpen( &dir, "a" ); 27 | 28 | while ( dir.has_next ) 29 | { 30 | tfFILE file; 31 | tfReadFile( &dir, &file ); 32 | printf( "%s\n", file.name ); 33 | tfDirNext( &dir ); 34 | } 35 | 36 | tfDirClose( &dir ); 37 | */ 38 | 39 | #if !defined( TINYFILES_H ) 40 | 41 | #define TF_WINDOWS 1 42 | #define TF_MAC 2 43 | #define TF_UNIX 3 44 | 45 | #if defined( _WIN32 ) 46 | #define TF_PLATFORM TF_WINDOWS 47 | #if !defined( _CRT_SECURE_NO_WARNINGS ) 48 | #define _CRT_SECURE_NO_WARNINGS 49 | #endif 50 | #elif defined( __APPLE__ ) 51 | #define TF_PLATFORM TF_MAC 52 | #else 53 | #define TF_PLATFORM TF_UNIX 54 | #endif 55 | 56 | #include // strerror, strncpy 57 | 58 | // change to 0 to compile out any debug checks 59 | #define TF_DEBUG_CHECKS 1 60 | 61 | #if TF_DEBUG_CHECKS 62 | 63 | #include // printf 64 | #include // assert 65 | #include 66 | #define TF_ASSERT assert 67 | 68 | #else 69 | 70 | #define TF_ASSERT( ... ) 71 | 72 | #endif // TF_DEBUG_CHECKS 73 | 74 | #define TF_MAX_PATH 1024 75 | #define TF_MAX_FILENAME 256 76 | #define TF_MAX_EXT 32 77 | 78 | struct tfFILE; 79 | struct tfDIR; 80 | struct tfFILETIME; 81 | typedef struct tfFILE tfFILE; 82 | typedef struct tfDIR tfDIR; 83 | typedef struct tfFILETIME tfFILETIME; 84 | typedef void (*tfCallback)( tfFILE* file, void* udata ); 85 | 86 | // Stores the file extension in tfFILE::ext, and returns a pointer to 87 | // tfFILE::ext 88 | const char* tfGetExt( tfFILE* file ); 89 | 90 | // Applies a function (cb) to all files in a directory. Will recursively visit 91 | // all subdirectories. Useful for asset management, file searching, indexing, etc. 92 | void tfTraverse( const char* path, tfCallback cb, void* udata ); 93 | 94 | // Fills out a tfFILE struct with file information. Does not actually open the 95 | // file contents, and instead performs more lightweight OS-specific calls. 96 | int tfReadFile( tfDIR* dir, tfFILE* file ); 97 | 98 | // Once a tfDIR is opened, this function can be used to grab another file 99 | // from the operating system. 100 | void tfDirNext( tfDIR* dir ); 101 | 102 | // Performs lightweight OS-specific call to close internal handle. 103 | void tfDirClose( tfDIR* dir ); 104 | 105 | // Performs lightweight OS-specific call to open a file handle on a directory. 106 | int tfDirOpen( tfDIR* dir, const char* path ); 107 | 108 | // Compares file last write times. -1 if file at path_a was modified earlier than path_b. 109 | // 0 if they are equal. 1 if file at path_b was modified earlier than path_a. 110 | int tfCompareFileTimesByPath( const char* path_a, const char* path_b ); 111 | 112 | // Retrieves time file was last modified, returns 0 upon failure 113 | int tfGetFileTime( const char* path, tfFILETIME* time ); 114 | 115 | // Compares file last write times. -1 if time_a was modified earlier than path_b. 116 | // 0 if they are equal. 1 if time_b was modified earlier than path_a. 117 | int tfCompareFileTimes( tfFILETIME* time_a, tfFILETIME* time_b ); 118 | 119 | // Returns 1 of file exists, otherwise returns 0. 120 | int tfFileExists( const char* path ); 121 | 122 | // Returns 1 if the file's extension matches the string in ext 123 | // Returns 0 otherwise 124 | int tfMatchExt( tfFILE* file, const char* ext ); 125 | 126 | // Prints detected errors to stdout 127 | void tfDoUnitTests(); 128 | 129 | #if TF_PLATFORM == TF_WINDOWS 130 | 131 | #if !defined _CRT_SECURE_NO_WARNINGS 132 | #define _CRT_SECURE_NO_WARNINGS 133 | #endif 134 | #include 135 | 136 | struct tfFILE 137 | { 138 | char path[ TF_MAX_PATH ]; 139 | char name[ TF_MAX_FILENAME ]; 140 | char ext[ TF_MAX_EXT ]; 141 | int is_dir; 142 | int is_reg; 143 | size_t size; 144 | }; 145 | 146 | struct tfDIR 147 | { 148 | char path[ TF_MAX_PATH ]; 149 | int has_next; 150 | HANDLE handle; 151 | WIN32_FIND_DATAA fdata; 152 | }; 153 | 154 | struct tfFILETIME 155 | { 156 | FILETIME time; 157 | }; 158 | 159 | #elif TF_PLATFORM == TF_MAC || TF_PLATFORM == TF_UNIX 160 | 161 | #include 162 | #include 163 | #include 164 | #include 165 | 166 | struct tfFILE 167 | { 168 | char path[ TF_MAX_PATH ]; 169 | char name[ TF_MAX_FILENAME ]; 170 | char ext[ TF_MAX_EXT ]; 171 | int is_dir; 172 | int is_reg; 173 | int size; 174 | struct stat info; 175 | }; 176 | 177 | struct tfDIR 178 | { 179 | char path[ TF_MAX_PATH ]; 180 | int has_next; 181 | DIR* dir; 182 | struct dirent* entry; 183 | }; 184 | 185 | struct tfFILETIME 186 | { 187 | time_t time; 188 | }; 189 | 190 | #endif 191 | 192 | #define TINYFILES_H 193 | #endif 194 | 195 | #ifdef TINYFILES_IMPLEMENTATION 196 | #undef TINYFILES_IMPLEMENTATION 197 | 198 | #define tfSafeStrCpy( dst, src, n, max ) tfSafeStrCopy_internal( dst, src, n, max, __FILE__, __LINE__ ) 199 | static int tfSafeStrCopy_internal( char* dst, const char* src, int n, int max, const char* file, int line ) 200 | { 201 | int c; 202 | const char* original = src; 203 | 204 | do 205 | { 206 | if ( n >= max ) 207 | { 208 | if ( !TF_DEBUG_CHECKS ) break; 209 | printf( "ERROR: String \"%s\" too long to copy on line %d in file %s (max length of %d).\n" 210 | , original 211 | , line 212 | , file 213 | , max ); 214 | TF_ASSERT( 0 ); 215 | } 216 | 217 | c = *src++; 218 | dst[ n ] = c; 219 | ++n; 220 | } while ( c ); 221 | 222 | return n; 223 | } 224 | 225 | const char* tfGetExt( tfFILE* file ) 226 | { 227 | char* period = file->name; 228 | char c; 229 | while ( (c = *period++) ) if ( c == '.' ) break; 230 | if ( c ) tfSafeStrCpy( file->ext, period, 0, TF_MAX_EXT ); 231 | else file->ext[ 0 ] = 0; 232 | return file->ext; 233 | } 234 | 235 | void tfTraverse( const char* path, tfCallback cb, void* udata ) 236 | { 237 | tfDIR dir; 238 | tfDirOpen( &dir, path ); 239 | 240 | while ( dir.has_next ) 241 | { 242 | tfFILE file; 243 | tfReadFile( &dir, &file ); 244 | 245 | if ( file.is_dir && file.name[ 0 ] != '.' ) 246 | { 247 | char path2[ TF_MAX_PATH ]; 248 | int n = tfSafeStrCpy( path2, path, 0, TF_MAX_PATH ); 249 | n = tfSafeStrCpy( path2, "/", n - 1, TF_MAX_PATH ); 250 | tfSafeStrCpy( path2, file.name, n -1, TF_MAX_PATH ); 251 | tfTraverse( path2, cb, udata ); 252 | } 253 | 254 | if ( file.is_reg ) cb( &file, udata ); 255 | tfDirNext( &dir ); 256 | } 257 | 258 | tfDirClose( &dir ); 259 | } 260 | 261 | int tfMatchExt( tfFILE* file, const char* ext ) 262 | { 263 | if (*ext == '.') ++ext; 264 | return !strcmp( file->ext, ext ); 265 | } 266 | 267 | #if TF_PLATFORM == TF_WINDOWS 268 | 269 | int tfReadFile( tfDIR* dir, tfFILE* file ) 270 | { 271 | TF_ASSERT( dir->handle != INVALID_HANDLE_VALUE ); 272 | 273 | int n = 0; 274 | char* fpath = file->path; 275 | char* dpath = dir->path; 276 | 277 | n = tfSafeStrCpy( fpath, dpath, 0, TF_MAX_PATH ); 278 | n = tfSafeStrCpy( fpath, "/", n - 1, TF_MAX_PATH ); 279 | 280 | char* dname = dir->fdata.cFileName; 281 | char* fname = file->name; 282 | 283 | tfSafeStrCpy( fname, dname, 0, TF_MAX_FILENAME ); 284 | tfSafeStrCpy( fpath, fname, n - 1, TF_MAX_PATH ); 285 | 286 | size_t max_dword = MAXDWORD; 287 | file->size = ((size_t)dir->fdata.nFileSizeHigh * (max_dword + 1)) + (size_t)dir->fdata.nFileSizeLow; 288 | tfGetExt( file ); 289 | 290 | file->is_dir = !!(dir->fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); 291 | file->is_reg = !!(dir->fdata.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || 292 | !(dir->fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); 293 | 294 | return 1; 295 | } 296 | 297 | void tfDirNext( tfDIR* dir ) 298 | { 299 | TF_ASSERT( dir->has_next ); 300 | 301 | if ( !FindNextFileA( dir->handle, &dir->fdata ) ) 302 | { 303 | dir->has_next = 0; 304 | DWORD err = GetLastError( ); 305 | TF_ASSERT( err == ERROR_SUCCESS || err == ERROR_NO_MORE_FILES ); 306 | } 307 | } 308 | 309 | void tfDirClose( tfDIR* dir ) 310 | { 311 | dir->path[ 0 ] = 0; 312 | dir->has_next = 0; 313 | if ( dir->handle != INVALID_HANDLE_VALUE ) FindClose( dir->handle ); 314 | } 315 | 316 | int tfDirOpen( tfDIR* dir, const char* path ) 317 | { 318 | int n = tfSafeStrCpy( dir->path, path, 0, TF_MAX_PATH ); 319 | n = tfSafeStrCpy( dir->path, "\\*", n - 1, TF_MAX_PATH ); 320 | dir->handle = FindFirstFileA( dir->path, &dir->fdata ); 321 | dir->path[ n - 3 ] = 0; 322 | 323 | if ( dir->handle == INVALID_HANDLE_VALUE ) 324 | { 325 | printf( "ERROR: Failed to open directory (%s): %s.\n", path, strerror( errno ) ); 326 | tfDirClose( dir ); 327 | TF_ASSERT( 0 ); 328 | return 0; 329 | } 330 | 331 | dir->has_next = 1; 332 | 333 | return 1; 334 | } 335 | 336 | int tfCompareFileTimesByPath( const char* path_a, const char* path_b ) 337 | { 338 | FILETIME time_a = { 0 }; 339 | FILETIME time_b = { 0 }; 340 | WIN32_FILE_ATTRIBUTE_DATA data; 341 | 342 | if ( GetFileAttributesExA( path_a, GetFileExInfoStandard, &data ) ) time_a = data.ftLastWriteTime; 343 | if ( GetFileAttributesExA( path_b, GetFileExInfoStandard, &data ) ) time_b = data.ftLastWriteTime; 344 | return CompareFileTime( &time_a, &time_b ); 345 | } 346 | 347 | int tfGetFileTime( const char* path, tfFILETIME* time ) 348 | { 349 | FILETIME initialized_to_zero = { 0 }; 350 | time->time = initialized_to_zero; 351 | WIN32_FILE_ATTRIBUTE_DATA data; 352 | if ( GetFileAttributesExA( path, GetFileExInfoStandard, &data ) ) 353 | { 354 | time->time = data.ftLastWriteTime; 355 | return 1; 356 | } 357 | return 0; 358 | } 359 | 360 | int tfCompareFileTimes( tfFILETIME* time_a, tfFILETIME* time_b ) 361 | { 362 | return CompareFileTime( &time_a->time, &time_b->time ); 363 | } 364 | 365 | int tfFileExists( const char* path ) 366 | { 367 | WIN32_FILE_ATTRIBUTE_DATA unused; 368 | return GetFileAttributesExA( path, GetFileExInfoStandard, &unused ); 369 | } 370 | 371 | #elif TF_PLATFORM == TF_MAC || TF_PLATFORM == TF_UNIX 372 | 373 | int tfReadFile( tfDIR* dir, tfFILE* file ) 374 | { 375 | TF_ASSERT( dir->entry ); 376 | 377 | int n = 0; 378 | char* fpath = file->path; 379 | char* dpath = dir->path; 380 | 381 | n = tfSafeStrCpy( fpath, dpath, 0, TF_MAX_PATH ); 382 | n = tfSafeStrCpy( fpath, "/", n - 1, TF_MAX_PATH ); 383 | 384 | char* dname = dir->entry->d_name; 385 | char* fname = file->name; 386 | 387 | tfSafeStrCpy( fname, dname, 0, TF_MAX_FILENAME ); 388 | tfSafeStrCpy( fpath, fname, n - 1, TF_MAX_PATH ); 389 | 390 | if ( stat( file->path, &file->info ) ) 391 | return 0; 392 | 393 | file->size = file->info.st_size; 394 | tfGetExt( file ); 395 | 396 | file->is_dir = S_ISDIR( file->info.st_mode ); 397 | file->is_reg = S_ISREG( file->info.st_mode ); 398 | 399 | return 1; 400 | } 401 | 402 | void tfDirNext( tfDIR* dir ) 403 | { 404 | TF_ASSERT( dir->has_next ); 405 | dir->entry = readdir( dir->dir ); 406 | dir->has_next = dir->entry ? 1 : 0; 407 | } 408 | 409 | void tfDirClose( tfDIR* dir ) 410 | { 411 | dir->path[ 0 ] = 0; 412 | if ( dir->dir ) closedir( dir->dir ); 413 | dir->dir = 0; 414 | dir->has_next = 0; 415 | dir->entry = 0; 416 | } 417 | 418 | int tfDirOpen( tfDIR* dir, const char* path ) 419 | { 420 | tfSafeStrCpy( dir->path, path, 0, TF_MAX_PATH ); 421 | dir->dir = opendir( path ); 422 | 423 | if ( !dir->dir ) 424 | { 425 | printf( "ERROR: Failed to open directory (%s): %s.\n", path, strerror( errno ) ); 426 | tfDirClose( dir ); 427 | TF_ASSERT( 0 ); 428 | return 0; 429 | } 430 | 431 | dir->has_next = 1; 432 | dir->entry = readdir( dir->dir ); 433 | if ( !dir->dir ) dir->has_next = 0; 434 | 435 | return 1; 436 | } 437 | 438 | // Warning : untested code! (let me know if it breaks) 439 | int tfCompareFileTimesByPath( const char* path_a, const char* path_b ) 440 | { 441 | time_t time_a; 442 | time_t time_b; 443 | struct stat info; 444 | if ( stat( path_a, &info ) ) return 0; 445 | time_a = info.st_mtime; 446 | if ( stat( path_b, &info ) ) return 0; 447 | time_b = info.st_mtime; 448 | return (int)difftime( time_a, time_b ); 449 | } 450 | 451 | // Warning : untested code! (let me know if it breaks) 452 | int tfGetFileTime( const char* path, tfFILETIME* time ) 453 | { 454 | struct stat info; 455 | if ( stat( path, &info ) ) return 0; 456 | time->time = info.st_mtime; 457 | return 1; 458 | } 459 | 460 | // Warning : untested code! (let me know if it breaks) 461 | int tfCompareFileTimes( tfFILETIME* time_a, tfFILETIME* time_b ) 462 | { 463 | return (int)difftime( time_a->time, time_b->time ); 464 | } 465 | 466 | // Warning : untested code! (let me know if it breaks) 467 | int tfFileExists( const char* path ) 468 | { 469 | return access( path, F_OK ) != -1; 470 | } 471 | 472 | #endif // TF_PLATFORM 473 | 474 | #endif 475 | 476 | /* 477 | ------------------------------------------------------------------------------ 478 | This software is available under 2 licenses - you may choose the one you like. 479 | ------------------------------------------------------------------------------ 480 | ALTERNATIVE A - zlib license 481 | Copyright (c) 2017 Randy Gaul http://www.randygaul.net 482 | This software is provided 'as-is', without any express or implied warranty. 483 | In no event will the authors be held liable for any damages arising from 484 | the use of this software. 485 | Permission is granted to anyone to use this software for any purpose, 486 | including commercial applications, and to alter it and redistribute it 487 | freely, subject to the following restrictions: 488 | 1. The origin of this software must not be misrepresented; you must not 489 | claim that you wrote the original software. If you use this software 490 | in a product, an acknowledgment in the product documentation would be 491 | appreciated but is not required. 492 | 2. Altered source versions must be plainly marked as such, and must not 493 | be misrepresented as being the original software. 494 | 3. This notice may not be removed or altered from any source distribution. 495 | ------------------------------------------------------------------------------ 496 | ALTERNATIVE B - Public Domain (www.unlicense.org) 497 | This is free and unencumbered software released into the public domain. 498 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 499 | software, either in source code form or as a compiled binary, for any purpose, 500 | commercial or non-commercial, and by any means. 501 | In jurisdictions that recognize copyright laws, the author or authors of this 502 | software dedicate any and all copyright interest in the software to the public 503 | domain. We make this dedication for the benefit of the public at large and to 504 | the detriment of our heirs and successors. We intend this dedication to be an 505 | overt act of relinquishment in perpetuity of all present and future rights to 506 | this software under copyright law. 507 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 508 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 509 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 510 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 511 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 512 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 513 | ------------------------------------------------------------------------------ 514 | */ 515 | --------------------------------------------------------------------------------