├── .gitignore ├── LICENSE ├── README.md ├── stb_image.nimble ├── stb_image ├── components.nim ├── read.c ├── read.nim ├── stb_image.h ├── stb_image_write.h ├── write.c └── write.nim ├── testdata ├── image1.png ├── image2.bmp ├── image3.pgm ├── image4.jpeg ├── save1.bmp ├── save2.png ├── save3.tga ├── save4.tga ├── save5.jpeg └── save6.jpeg └── tests.nim /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw* 2 | 3 | # Binaries and Nim stuff 4 | nimcache/ 5 | nakefile 6 | tests 7 | tests.exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nim stb_image Wrapper 2 | ===================== 3 | 4 | This is a Nim wrapper of the stb_image single header file libraries 5 | (`stb_image.h` and `stb_image_write.h`). 6 | 7 | Official repo page can be found here: 8 | https://gitlab.com/define-private-public/stb_image-Nim 9 | 10 | All other sites should be considered mirrors, though if you log an issue on 11 | GitHub, I'll probably address it. 12 | 13 | The used header files are included in this repo (they end in the name 14 | `_header.nim`) but you may provide your own. 15 | The versions that are used are: 16 | 17 | - `stb_image.h`: v2.23 18 | - `stb_image_write.h`: v1.07 19 | 20 | They can be found here: https://github.com/nothings/stb 21 | 22 | If something is out of date or incorrect. Please notify me on the GitLab issue 23 | tracker so I can fix it. Though if something like a header file is out of date, 24 | you should be able to simply exchange it out (unless something was changed in 25 | the API). 26 | 27 | 28 | zlib client 29 | ----------- 30 | For PNG support, stb_image requires a functioning zlib compressor/decompressor, 31 | and one is built in. I'd like to thank RSDuck for exposing the functions with 32 | some Nim friend wrappers. Se you want to see how to use it, check `tests.nim` 33 | for some samples. 34 | 35 | 36 | Callback Functions & stbi_image_resize 37 | ---------------------------------------------------- 38 | 39 | Only the callback funtions for `stbi_image_write` are implemented. I'd like to 40 | thank Eduardo Bart (@edubart) for help on this. As for `stbi_image_resize`, I 41 | felt that the scope of this wrapper should be focused on image loading and 42 | saving, not processing. 43 | 44 | Though if someone would like these included in the wrapper I am open to pull 45 | requests. 46 | 47 | 48 | License 49 | ------- 50 | 51 | In the nature of Sean Barret (author of stb) putting his libraries in public 52 | domain, so is this code. Specifically it falls under the "Unlicense." Please 53 | check the file `LICENSE` for details. 54 | 55 | 56 | How To Use 57 | ---------- 58 | 59 | I've tried to write this to be as close as possible to the orignal C API, so if 60 | you're already familiar with it, there should be no hassle. If you don't all of 61 | the functions are documented in `stb_image/read.nim` and `stb_image/write.nim`. 62 | 63 | Import what you want to use. I recommend adding the `as` semantic: 64 | 65 | ```nim 66 | import stb_image/read as stbi 67 | import stb_image/write as stbiw 68 | ``` 69 | 70 | An original C call would look like this: 71 | 72 | ```c 73 | #include "stb_image.h" 74 | 75 | // Get the image data 76 | int width, height, channels; 77 | unsigned char *data = stbi_load("kevin_bacon.jpeg", &width, &height, &channels, STBI_default); 78 | 79 | // Do what you want... 80 | 81 | // Cleanup 82 | stbi_image_free(data); 83 | ``` 84 | 85 | But becomes this: 86 | 87 | ```nim 88 | import stb_image/read as stbi 89 | 90 | var 91 | width, height, channels: int 92 | data: seq[uint8] 93 | 94 | data = stbi.load("kevin_bacon.png", width, height, channels, stbi.Default) 95 | # No need to do any GC yourself, as Nim takes care of that for you! 96 | ``` 97 | 98 | Functions that had names `like_this` have been turned `intoThis`. That `stbi_` 99 | portion has also been dropped. 100 | 101 | If you want to write pixels, it's like what you see here, but in reverse: 102 | 103 | ```nim 104 | import stb_image/write as stbiw 105 | 106 | # Stuff some pixels 107 | var data: seq[uint8] = @[] 108 | data.add(0x00) 109 | data.add(0x80) 110 | data.add(0xFF) 111 | 112 | # save it (as monochrome) 113 | stbiw.writeBMP("three.bmp", 3, 1, stbiw.Y, data) 114 | ``` 115 | 116 | Some of the functions (or variables) that effect the library globally are still 117 | there (e.g. `setFlipVerticallyOnLoad()` in `stb_image`), but some other has been 118 | moved to functions calls as to not act in a global manor (e.g. the `useRLE` 119 | parameter for `writeTGA()` in `stb_image/write`). 120 | 121 | I also recommend reading through the documentation at the top of the original 122 | header files too, as they give you a bit more of a grasp of how things work and 123 | the limitations of `stb_image`. 124 | 125 | All of the functions that can do image reading (which are, well, only found in 126 | `stb_image/read.nim`) will throw an `STBIException` if there was an error 127 | retriving the image data. The `.message` field of the exception will contain 128 | some slight information into what went wrong. 129 | 130 | 131 | Future Plans 132 | ------------ 133 | 134 | - Return a data structure that descrives an image instead of a sequence of 135 | bytes/shorts/floats from the functions. This may be a much more natural way 136 | for the programmer to get an image and make a little more sense than 137 | returning results in both the "return," statement and "var," parameters. 138 | Such a system might have a data structure like this: 139 | 140 | ```nim 141 | type 142 | # `T` should only be uint8, uint16, or float32 143 | STBImage[T]* = ref object of RootObj 144 | width*: int 145 | height*: int 146 | channels*: int # One of the values from `stb_image/components.nim` 147 | pixelData*: seq[T] 148 | ``` 149 | 150 | And the Nim-Friendly functions would change from this: 151 | 152 | ```nim 153 | # Data 154 | var 155 | width, height, channels: int, 156 | pixels: seq[uint8] 157 | 158 | # Load the image 159 | pixels = stbi.load("kevin_bacon.jpeg", width, height, channels, stbi.Default) 160 | ``` 161 | 162 | Over to this: 163 | 164 | ```nim 165 | var image = stbi.load("kevin_bacon.jpeg", stbi.Default) 166 | ``` 167 | 168 | It may also solve an issue with the pixel data being copied (unecessarly) 169 | with the current wrappers. See this thread in the Nim Forum for details: 170 | http://forum.nim-lang.org/t/2665 171 | 172 | The only thing I don't like about this is that it would break the familiarity 173 | with the original C API. I don't want to maintain multiple functions that 174 | have the same functionality so I would be removing those orignal bindings. 175 | 176 | Trying to figure out how to make the `STBImage` type play nice with the 177 | write functions might be a little more work too (e.g. validating there is 178 | enough data and correct parameters). 179 | 180 | I'd like to get some comments on this before moving forward with it. 181 | 182 | - I really would like add unit tests for the functions listed in the `Untested 183 | Functions` section to verify they work, but I'm in need of some very simple 184 | test images. 185 | 186 | - Add wrappers/bindings for the `stb_image_resize.h` library. It's part of the 187 | STB toolkit (and falls under it's "image," section), but it wasn't related to 188 | image IO so I decided to leave it out. It also looked like quite a bit of 189 | work to add in. If someone wants to submit a pull request, I'll review it. 190 | 191 | 192 | Unbound Functions 193 | ----------------- 194 | 195 | Right now `stbi_load_gif_from_memory` isn't bound, but will be in the future. 196 | See: https://gitlab.com/define-private-public/stb_image-Nim/issues/6 for details 197 | on why this hasn't been added in yet. 198 | 199 | 200 | Untested Functions 201 | ------------------ 202 | 203 | All functions should have tests for them, but I wasn't able to find some test 204 | images (namely for the HDR) functions. So they do have bindings for 205 | completeness, but they haven't been proven to work. If you do happent to have 206 | some very simple test images I could use, please provide them. Here is the list 207 | of functions that are untested. 208 | 209 | From `stb_image.h`: 210 | - `load16()` 211 | - `loadFromFile16()` 212 | - `load16FromMemory()` 213 | - `loadF()` 214 | - `loadFFromMemory()` 215 | - `loadFFromFile()` 216 | - `HDRToLDRGamma()` 217 | - `HDRToLDRScale()` 218 | - `LDRToHDRGamma()` 219 | - `LDRToHDRScale()` 220 | - `isHDRFromMemory()` 221 | - `isHDR()` 222 | - `isHDRFromFile()` 223 | - `setUnpremultiplyOnLoad()` 224 | - `stbiConvertIPhoneRGBToPNG()` 225 | 226 | 227 | From `stb_image_write.h`: 228 | - `writeHDR()` 229 | 230 | 231 | Thanks 232 | ------ 233 | - Eduardo Bart (@edubart), for help with a segfault issue and write callback 234 | functions. 235 | - King Eca (@EasiestSoft), for help with the native call types 236 | 237 | -------------------------------------------------------------------------------- /stb_image.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | version = "2.5" 3 | author = "Benjamin N. Summerton " 4 | description = "A wrapper for stb_image (including stb_image_write & zlib client)." 5 | license = "Unlicense (Public Domain)" 6 | 7 | # deps 8 | requires "nim >= 0.15.0" 9 | 10 | skipFiles = @["tests.nim"] 11 | skipDirs = @["testdata"] 12 | -------------------------------------------------------------------------------- /stb_image/components.nim: -------------------------------------------------------------------------------- 1 | # File: stbi_image/components.nim 2 | # Author: Benjamin N. Summerton (define-private-public) 3 | # License: Unlicense (Public Domain) 4 | # Description: These are Nim friendly variables for the component parameters. 5 | 6 | 7 | # Components 8 | const 9 | # Used by req_comp 10 | Default* = 0 # (for stb_image) 11 | 12 | # Monochrome 13 | Grey* = 1 # (for stb_image) 14 | Y* = 1 # (for stb_image_write) 15 | 16 | # Monochrome w/ Alpha 17 | GreyAlpha* = 2 # (for stb_image) 18 | YA* = 2 # (for stb_image_write) 19 | 20 | # Red, Green, Blue (and alpha) 21 | RGB* = 3 # (used by all) 22 | RGBA* = 4 # (used by all) 23 | 24 | -------------------------------------------------------------------------------- /stb_image/read.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include "stb_image.h" 3 | -------------------------------------------------------------------------------- /stb_image/read.nim: -------------------------------------------------------------------------------- 1 | # File: stb_image/read.nim 2 | # Author: Benjamin N. Summerton (define-private-public) 3 | # License: Unlicense (Public Domain) 4 | # Description: A nim wrapper for stb_image.h. The reading ops 5 | 6 | 7 | import components 8 | export components.Default 9 | export components.Grey 10 | export components.GreyAlpha 11 | export components.RGB 12 | export components.RGBA 13 | 14 | when defined(windows) and defined(vcc): 15 | {.pragma: stbcall, stdcall.} 16 | else: 17 | {.pragma: stbcall, cdecl.} 18 | 19 | # Include the header 20 | {.compile: "stb_image/read.c".} 21 | 22 | # Need to link the math library 23 | when defined(Posix) and not defined(haiku): 24 | {.passl: "-lm".} 25 | 26 | 27 | # Custom exception, only for image reading errors 28 | type 29 | STBIException* = object of Exception 30 | 31 | 32 | # NOTE: this function is here for completness, but it's not exposed in the 33 | # nim-friendly API, since seq[byte] are GC'd 34 | proc stbi_image_free(retval_from_stbi_load: pointer) 35 | {.importc: "stbi_image_free", stbcall.} 36 | 37 | proc stbi_failure_reason(): cstring 38 | {.importc: "stbi_failure_reason", stbcall.} 39 | 40 | 41 | ## Get an error message for why a read might have failed. This is not a 42 | ## threadsafe function. 43 | proc failureReason*(): string = 44 | return $stbi_failure_reason() 45 | 46 | 47 | 48 | # ================== 49 | # 8 bits per channel 50 | # ================== 51 | 52 | proc stbi_load( 53 | filename: cstring; 54 | x, y, channels_in_file: var cint; 55 | desired_channels: cint 56 | ): ptr cuchar 57 | {.importc: "stbi_load", stbcall.} 58 | 59 | proc stbi_load_from_memory( 60 | buffer: ptr cuchar; 61 | len: cint; 62 | x, y, channels_in_file: var cint; 63 | desired_channels: cint 64 | ): ptr cuchar 65 | {.importc: "stbi_load_from_memory", stbcall.} 66 | 67 | # Right now I'm not planning on using the callback functions, but if someone 68 | # requests it (or provides a pull request), I'll consider adding them in. 69 | #stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); 70 | 71 | proc stbi_load_from_file( 72 | f: File; 73 | x, y, channels_in_file: var cint; 74 | desired_channels: cint 75 | ): ptr cuchar 76 | {.importc: "stbi_load_from_file", stbcall.} 77 | 78 | 79 | ## This takes in a filename and will return a sequence (of unsigned bytes) that 80 | ## is the pixel data. `x`, `y` are the dimensions of the image, and 81 | ## `channels_in_file` is the format (e.g. "RGBA," "GreyAlpha," etc.). 82 | ## `desired_channels` will attempt to change it to with format you would like 83 | ## though it's not guarenteed. Set it to `0` if you don't care (a.k.a 84 | ## "Default"). 85 | proc load*(filename: string; x, y, channels_in_file: var int; desired_channels: int): seq[byte] = 86 | var 87 | width: cint 88 | height: cint 89 | components: cint 90 | 91 | # Read 92 | let data = stbi_load(filename.cstring, width, height, components, desired_channels.cint) 93 | 94 | # Check for a bad read 95 | if data == nil: 96 | raise newException(STBIException, failureReason()) 97 | 98 | # Set the returns 99 | x = width.int 100 | y = height.int 101 | channels_in_file = components.int 102 | 103 | let actual_channels = if desired_channels > 0: desired_channels else: components.int 104 | 105 | # Copy pixel data 106 | var pixelData: seq[byte] 107 | newSeq(pixelData, x * y * actual_channels) 108 | copyMem(pixelData[0].addr, data, pixelData.len) 109 | 110 | # Free loaded image data 111 | stbi_image_free(data) 112 | 113 | return pixelData 114 | 115 | 116 | ## This takes in a sequences of bytes (of an image file) 117 | ## and will return a sequence (of unsigned bytes) that 118 | ## is the pixel data. `x`, `y` are the dimensions of the image, and 119 | ## `channels_in_file` is the format (e.g. "RGBA," "GreyAlpha," etc.). 120 | ## `desired_channels` will attempt to change it to with format you would like 121 | ## though it's not guarenteed. Set it to `0` if you don't care (a.k.a 122 | ## "Default"). 123 | proc loadFromMemory*(buffer: seq[byte]; x, y, channels_in_file: var int; desired_channels: int): seq[byte] = 124 | var 125 | # Cast the buffer to another data type 126 | castedBuffer = cast[ptr cuchar](buffer[0].unsafeAddr) 127 | 128 | # Return values 129 | width: cint 130 | height: cint 131 | components: cint 132 | 133 | # Read 134 | let data = stbi_load_from_memory(castedBuffer, buffer.len.cint, width, height, components, desired_channels.cint) 135 | 136 | # Check for a bad read 137 | if data == nil: 138 | raise newException(STBIException, failureReason()) 139 | 140 | # Set the returns 141 | x = width.int 142 | y = height.int 143 | channels_in_file = components.int 144 | 145 | let actual_channels = if desired_channels > 0: desired_channels else: components.int 146 | 147 | # Copy pixel data 148 | var pixelData: seq[byte] 149 | newSeq(pixelData, x * y * actual_channels) 150 | copyMem(pixelData[0].addr, data, pixelData.len) 151 | 152 | # Free loaded image data 153 | stbi_image_free(data) 154 | 155 | return pixelData 156 | 157 | 158 | ## This takes in a File and will return a sequence (of unsigned bytes) that 159 | ## is the pixel data. `x`, `y` are the dimensions of the image, and 160 | ## `channels_in_file` is the format (e.g. "RGBA," "GreyAlpha," etc.). 161 | ## `desired_channels` will attempt to change it to with format you would like 162 | ## though it's not guarenteed. Set it to `0` if you don't care (a.k.a 163 | ## "Default"). 164 | ## 165 | ## This will also close the file handle too. 166 | proc loadFromFile*(f: File, x, y, channels_in_file: var int, desired_channels: int): seq[byte] = 167 | var 168 | width: cint 169 | height: cint 170 | components: cint 171 | 172 | # Read 173 | let data = stbi_load_from_file(f, width, height, components, desired_channels.cint) 174 | 175 | # Check for a bad read 176 | if data == nil: 177 | raise newException(STBIException, failureReason()) 178 | 179 | # Set the returns 180 | x = width.int 181 | y = height.int 182 | channels_in_file = components.int 183 | 184 | let actual_channels = if desired_channels > 0: desired_channels else: components.int 185 | 186 | # Copy pixel data 187 | var pixelData: seq[byte] 188 | newSeq(pixelData, x * y * actual_channels) 189 | copyMem(pixelData[0].addr, data, pixelData.len) 190 | 191 | # Free loaded image data 192 | stbi_image_free(data) 193 | 194 | return pixelData 195 | 196 | 197 | 198 | # =================== 199 | # 16 bits per channel 200 | # =================== 201 | 202 | proc stbi_load_16( 203 | filename: cstring; 204 | x, y, channels_in_file: var cint, 205 | desired_channels: cint 206 | ): ptr cushort 207 | {.importc: "stbi_load_16", stbcall.} 208 | 209 | proc stbi_load_from_file_16( 210 | f: File; 211 | x, y, channels_in_file: var cint; 212 | desired_channels: cint 213 | ): ptr cushort 214 | {.importc: "stbi_load_from_file_16", stbcall.} 215 | 216 | proc stbi_load_16_from_memory( 217 | buffer: ptr cuchar; 218 | len: cint; 219 | x, y, channels_in_file: var cint; 220 | desired_channels: cint 221 | ): ptr cushort 222 | {.importc: "stbi_load_16_from_memory", stbcall.} 223 | 224 | 225 | ## This takes in a filename and will return a sequence (of unsigned shorts) that 226 | ## is the pixel data. `x`, `y` are the dimensions of the image, and 227 | ## `channels_in_file` is the format (e.g. "RGBA," "GreyAlpha," etc.). 228 | ## `desired_channels` will attempt to change it to with format you would like 229 | ## though it's not guarenteed. Set it to `0` if you don't care (a.k.a 230 | ## "Default"). 231 | ## 232 | ## This is used for files where the channels for the pixel data are encoded as 233 | ## 16 bit integers (e.g. some Photoshop files). 234 | proc load16*(filename: string; x, y, channels_in_file: var int; desired_channels: int): seq[uint16] = 235 | var 236 | width: cint 237 | height: cint 238 | components: cint 239 | 240 | # Read 241 | let data = stbi_load_16(filename.cstring, width, height, components, desired_channels.cint) 242 | 243 | # Check for a bad read 244 | if data == nil: 245 | raise newException(STBIException, failureReason()) 246 | 247 | # Set the returns 248 | x = width.int 249 | y = height.int 250 | channels_in_file = components.int 251 | 252 | let actual_channels = if desired_channels > 0: desired_channels else: components.int 253 | 254 | # Copy pixel data 255 | var pixelData: seq[uint16] 256 | newSeq(pixelData, x * y * actual_channels) 257 | copyMem(pixelData[0].addr, data, pixelData.len) 258 | 259 | # Free loaded image data 260 | stbi_image_free(data) 261 | 262 | return pixelData 263 | 264 | 265 | ## This takes in a File and will return a sequence (of unsigned shorts) that 266 | ## is the pixel data. `x`, `y` are the dimensions of the image, and 267 | ## `channels_in_file` is the format (e.g. "RGBA," "GreyAlpha," etc.). 268 | ## `desired_channels` will attempt to change it to with format you would like 269 | ## though it's not guarenteed. Set it to `0` if you don't care (a.k.a 270 | ## "Default"). 271 | ## 272 | ## This will also close the file handle too. 273 | ## 274 | ## This is used for files where the channels for the pixel data are encoded as 275 | ## 16 bit integers (e.g. some Photoshop files). 276 | proc loadFromFile16*(f: File; x, y, channels_in_file: var int; desired_channels: int): seq[uint16] = 277 | var 278 | width: cint 279 | height: cint 280 | components: cint 281 | 282 | # Read 283 | let data = stbi_load_from_file(f, width, height, components, desired_channels.cint) 284 | 285 | # Check for a bad read 286 | if data == nil: 287 | raise newException(STBIException, failureReason()) 288 | 289 | # Set the returns 290 | x = width.int 291 | y = height.int 292 | channels_in_file = components.int 293 | 294 | let actual_channels = if desired_channels > 0: desired_channels else: components.int 295 | 296 | # Copy pixel data 297 | var pixelData: seq[uint16] 298 | newSeq(pixelData, x * y * actual_channels) 299 | copyMem(pixelData[0].addr, data, pixelData.len) 300 | 301 | # Free loaded image data 302 | stbi_image_free(data) 303 | 304 | return pixelData 305 | 306 | 307 | ## This takes in a sequences of unsigned short (of an image file) 308 | ## is the pixel data. `x`, `y` are the dimensions of the image, and 309 | ## `channels_in_file` is the format (e.g. "RGBA," "GreyAlpha," etc.). 310 | ## `desired_channels` will attempt to change it to with format you would like 311 | ## though it's not guarenteed. Set it to `0` if you don't care (a.k.a 312 | ## "Default"). 313 | ## 314 | ## This is used for files where the channels for the pixel data are encoded as 315 | ## 16 bit integers (e.g. some Photoshop files). 316 | proc load16FromMemory*(buffer: seq[byte]; x, y, channels_in_file: var int; desired_channels: int): seq[uint16] = 317 | var 318 | # Cast the buffer to another data type 319 | castedBuffer = cast[ptr cuchar](buffer[0].unsafeAddr) 320 | 321 | width: cint 322 | height: cint 323 | components: cint 324 | 325 | # Read 326 | let data = stbi_load_16_from_memory(castedBuffer, buffer.len.cint, width, height, components, desired_channels.cint) 327 | 328 | # Check for a bad read 329 | if data == nil: 330 | raise newException(STBIException, failureReason()) 331 | 332 | # Set the returns 333 | x = width.int 334 | y = height.int 335 | channels_in_file = components.int 336 | 337 | let actual_channels = if desired_channels > 0: desired_channels else: components.int 338 | 339 | # Copy pixel data 340 | var pixelData: seq[uint16] 341 | newSeq(pixelData, x * y * actual_channels) 342 | copyMem(pixelData[0].addr, data, pixelData.len) 343 | 344 | # Free loaded image data 345 | stbi_image_free(data) 346 | 347 | return pixelData 348 | 349 | 350 | 351 | # ======================= 352 | # Float channel interface 353 | # ======================= 354 | 355 | proc stbi_loadf( 356 | filename: cstring; 357 | x, y, channels_in_file: var cint; 358 | desired_channels: cint 359 | ): ptr cfloat 360 | {.importc: "stbi_loadf", stbcall.} 361 | 362 | proc stbi_loadf_from_memory( 363 | buffer: ptr cuchar; 364 | len: cint; 365 | x, y, channels_in_file: var cint; 366 | desired_channels: cint 367 | ): ptr cfloat 368 | {.importc: "stbi_loadf_from_memory", stbcall.} 369 | 370 | # The callback functions are going to be skipped (see the README.md) 371 | #float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); 372 | 373 | proc stbi_loadf_from_file( 374 | f: File; 375 | x, y, channels_in_file: var cint; 376 | desired_channels: cint 377 | ): ptr cfloat 378 | {.importc: "stbi_loadf_from_file", stbcall.} 379 | 380 | 381 | ## This takes in a filename and will return a sequence (of 32 bit floats) that 382 | ## is the pixel data. `x`, `y` are the dimensions of the image, and 383 | ## `channels_in_file` is the format (e.g. "RGBA," "GreyAlpha," etc.). 384 | ## `desired_channels` will attempt to change it to with format you would like 385 | ## though it's not guarenteed. Set it to `0` if you don't care (a.k.a 386 | ## "Default"). 387 | proc loadF*(filename: string; x, y, channels_in_file: var int; desired_channels: int): seq[float32] = 388 | var 389 | width: cint 390 | height: cint 391 | components: cint 392 | 393 | # Read 394 | let data = stbi_loadf(filename.cstring, width, height, components, desired_channels.cint) 395 | 396 | # Check for a bad read 397 | if data == nil: 398 | raise newException(STBIException, failureReason()) 399 | 400 | # Set the returns 401 | x = width.int 402 | y = height.int 403 | channels_in_file = components.int 404 | 405 | let actual_channels = if desired_channels > 0: desired_channels else: components.int 406 | 407 | # Copy pixel data 408 | var pixelData: seq[float32] 409 | newSeq(pixelData, x * y * actual_channels) 410 | copyMem(pixelData[0].addr, data, pixelData.len) 411 | 412 | # Free loaded image data 413 | stbi_image_free(data) 414 | 415 | return pixelData 416 | 417 | 418 | ## This takes in a sequences of bytes (of an image file) 419 | ## and will return a sequence (of 32 bit floats) that 420 | ## is the pixel data. `x`, `y` are the dimensions of the image, and 421 | ## `channels_in_file` is the format (e.g. "RGBA," "GreyAlpha," etc.). 422 | ## `desired_channels` will attempt to change it to with format you would like 423 | ## though it's not guarenteed. Set it to `0` if you don't care (a.k.a 424 | ## "Default"). 425 | proc loadFFromMemory*(buffer: seq[byte]; x, y, channels_in_file: var int; desired_channels: int): seq[float32] = 426 | var 427 | # Cast the buffer to another data type 428 | castedBuffer = cast[ptr cuchar](buffer[0].unsafeAddr) 429 | 430 | # Return values 431 | width: cint 432 | height: cint 433 | components: cint 434 | 435 | # Read 436 | let data = stbi_loadf_from_memory(castedBuffer, buffer.len.cint, width, height, components, desired_channels.cint) 437 | 438 | # Check for a bad read 439 | if data == nil: 440 | raise newException(STBIException, failureReason()) 441 | 442 | # Set the returns 443 | x = width.int 444 | y = height.int 445 | channels_in_file = components.int 446 | 447 | let actual_channels = if desired_channels > 0: desired_channels else: components.int 448 | 449 | # Copy pixel data 450 | var pixelData: seq[float32] 451 | newSeq(pixelData, x * y * actual_channels) 452 | copyMem(pixelData[0].addr, data, pixelData.len) 453 | 454 | # Free loaded image data 455 | stbi_image_free(data) 456 | 457 | return pixelData 458 | 459 | 460 | ## This takes in a File and will return a sequence (of 32 bit floats) that 461 | ## is the pixel data. `x`, `y` are the dimensions of the image, and 462 | ## `channels_in_file` is the format (e.g. "RGBA," "GreyAlpha," etc.). 463 | ## `desired_channels` will attempt to change it to with format you would like 464 | ## though it's not guarenteed. Set it to `0` if you don't care (a.k.a 465 | ## "Default"). 466 | ## 467 | ## This will also close the file handle too. 468 | proc loadFFromFile*(f: File, x, y, channels_in_file: var int, desired_channels: int): seq[float32] = 469 | var 470 | width: cint 471 | height: cint 472 | components: cint 473 | 474 | # Read 475 | let data = stbi_loadf_from_file(f, width, height, components, desired_channels.cint) 476 | 477 | # Check for a bad read 478 | if data == nil: 479 | raise newException(STBIException, failureReason()) 480 | 481 | # Set the returns 482 | x = width.int 483 | y = height.int 484 | channels_in_file = components.int 485 | 486 | let actual_channels = if desired_channels > 0: desired_channels else: components.int 487 | 488 | # Copy pixel data 489 | var pixelData: seq[float32] 490 | newSeq(pixelData, x * y * actual_channels) 491 | copyMem(pixelData[0].addr, data, pixelData.len) 492 | 493 | # Free loaded image data 494 | stbi_image_free(data) 495 | 496 | return pixelData 497 | 498 | 499 | 500 | # ============= 501 | # HDR functions 502 | # ============= 503 | 504 | proc stbi_hdr_to_ldr_gamma(gamma: cfloat) 505 | {.importc: "stbi_hdr_to_ldr_gamma", stbcall.} 506 | 507 | proc stbi_hdr_to_ldr_scale(scale: cfloat) 508 | {.importc: "stbi_hdr_to_ldr_scale", stbcall.} 509 | 510 | proc stbi_ldr_to_hdr_gamma(gamma: cfloat) 511 | {.importc: "stbi_ldr_to_hdr_gamma", stbcall.} 512 | 513 | proc stbi_ldr_to_hdr_scale(scale: cfloat) 514 | {.importc: "stbi_ldr_to_hdr_scale", stbcall.} 515 | 516 | # The callback functions are going to be skipped (see the README.md) 517 | #int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); 518 | 519 | proc stbi_is_hdr_from_memory(buffer: ptr cuchar; len: cint): cint 520 | {.importc: "stbi_is_hdr_from_memory", stbcall.} 521 | 522 | proc stbi_is_hdr(filename: cstring): cint 523 | {.importc: "stbi_is_hdr", stbcall.} 524 | 525 | proc stbi_is_hdr_from_file(f: File): cint 526 | {.importc: "stbi_is_hdr_from_file", stbcall.} 527 | 528 | 529 | ## Please see the "HDR image support" section in the `stb_image.h` header file 530 | proc HDRToLDRGamma*(gamma: float) = 531 | stbi_hdr_to_ldr_gamma(gamma.cfloat) 532 | 533 | 534 | ## Please see the "HDR image support" section in the `stb_image.h` header file 535 | proc HDRToLDRScale*(scale: float) = 536 | stbi_hdr_to_ldr_scale(scale.cfloat) 537 | 538 | 539 | ## Please see the "HDR image support" section in the `stb_image.h` header file 540 | proc LDRToHDRGamma*(gamma: float) = 541 | stbi_ldr_to_hdr_gamma(gamma.cfloat) 542 | 543 | 544 | ## Please see the "HDR image support" section in the `stb_image.h` header file 545 | proc LDRToHDRScale*(scale: float) = 546 | stbi_ldr_to_hdr_scale(scale.cfloat) 547 | 548 | 549 | ## Checks to see if an image is an HDR image, from memory (as a string of bytes) 550 | proc isHDRFromMemory*(buffer: seq[byte]): bool = 551 | var castedBuffer = cast[ptr cuchar](buffer[0].unsafeAddr) 552 | return (stbi_is_hdr_from_memory(castedBuffer, buffer.len.cint) == 1) 553 | 554 | 555 | ## Checks to see if an image, with the given filename, is an HDR image. 556 | proc isHDR*(filename: string): bool = 557 | return (stbi_is_hdr(filename.cstring) == 1) 558 | 559 | 560 | ## Checks to see if an image is an HDR image, from a File pointer. 561 | proc isHDRFromFile*(f: File): bool = 562 | return (stbi_is_hdr_from_file(f) == 1) 563 | 564 | 565 | 566 | # ============== 567 | # Info Functions 568 | # ============== 569 | 570 | proc stbi_info_from_memory( 571 | buffer: ptr cuchar; 572 | len: cint; 573 | x, y, comp: var cint 574 | ): cint 575 | {.importc: "stbi_info_from_memory", stbcall.} 576 | 577 | # NOTE: I am skipping callback functions unless there is a demand for them 578 | #int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); 579 | 580 | proc stbi_info( 581 | filename: cstring; 582 | x, y, comp: var cint 583 | ): cint 584 | {.importc: "stbi_info", stbcall.} 585 | 586 | proc stbi_info_from_file( 587 | f: File; 588 | x, y, comp: var cint 589 | ): cint 590 | {.importc: "stbi_info_from_file", stbcall.} 591 | 592 | 593 | ## Querys a buffer to see if that data is a loadable image and get it's 594 | ## dimensions. Returns true if stb_image can load this image, false otherwise. 595 | proc infoFromMemory*(buffer: seq[byte]; x, y, comp: var int): bool = 596 | var 597 | # Cast the buffer to another data type 598 | castedBuffer = cast[ptr cuchar](buffer[0].unsafeAddr) 599 | width: cint 600 | height: cint 601 | channels: cint 602 | r = stbi_info_from_memory(castedBuffer, buffer.len.cint, width, height, channels) 603 | 604 | # Set the data and return 605 | x = width.int 606 | y = height.int 607 | comp = channels.int 608 | return (r == 1) 609 | 610 | 611 | ## Querys a filename to see if that file is a loadable image and get it's 612 | ## dimensions. Returns true if stb_image can load this image, false otherwise. 613 | proc info*(filename: string; x, y, comp: var int): bool = 614 | var 615 | width: cint 616 | height: cint 617 | channels: cint 618 | r = stbi_info(filename.cstring, width, height, channels) 619 | 620 | # Set the data & return 621 | x = width.int 622 | y = height.int 623 | comp = channels.int 624 | return (r == 1) 625 | 626 | 627 | ## Querys a File pointer to see if that file is a loadable image and get it's 628 | ## dimensions. Returns true if stb_image can load this image, false otherwise. 629 | ## 630 | ## This will also close the file handle. 631 | proc infoFromFile*(f: File; x, y, comp: var int): bool = 632 | var 633 | width: cint 634 | height: cint 635 | channels: cint 636 | r = stbi_info_from_file(f, width, height, channels) 637 | 638 | # Set the data & return 639 | x = width.int 640 | y = height.int 641 | comp = channels.int 642 | return (r == 1) 643 | 644 | 645 | 646 | # =============== 647 | # Extra Functions 648 | # =============== 649 | 650 | proc stbi_set_unpremultiply_on_load(flag_true_if_should_unpremultiply: cint) 651 | {.importc: "stbi_set_unpremultiply_on_load", stbcall.} 652 | 653 | proc stbi_convert_iphone_png_to_rgb(flag_true_if_should_convert: cint) 654 | {.importc: "stbi_convert_iphone_png_to_rgb", stbcall.} 655 | 656 | proc stbi_set_flip_vertically_on_load(flag_true_if_should_flip: cint) 657 | {.importc: "stbi_set_flip_vertically_on_load", stbcall.} 658 | 659 | 660 | ## From the header file: "For image formats that explicitly notate that they 661 | ## have premultiplied alpha, we just return the colors as stored in the file. 662 | ## set this flag to force unpremultiplication. results are undefined if the 663 | ## unpremultiply overflow. This function acts globally, so if you use it once I 664 | ## recommend calling it again right after loading what you want. 665 | proc setUnpremultiplyOnLoad*(unpremultiply: bool) = 666 | stbi_set_unpremultiply_on_load(if unpremultiply: 1 else: 0) 667 | 668 | 669 | ## From the header file: "indicate whether we should process iphone images back 670 | ## to canonical format." This function acts globally, so if you use it once I 671 | ## recommend calling it again right after loading what you want. 672 | proc convertIPhonePNGToRGB*(convert: bool) = 673 | stbi_convert_iphone_png_to_rgb(if convert: 1 else: 0) 674 | 675 | 676 | ## From the header file: "flip the image vertically, so the first pixels in the 677 | ## output array is the bottom left". This function acts globally, so if you use 678 | ## it once, I recommend calling it again right after loading what you want. 679 | proc setFlipVerticallyOnLoad*(flip: bool) = 680 | stbi_set_flip_vertically_on_load(if flip: 1 else: 0) 681 | 682 | 683 | 684 | # ===================== 685 | # ZLIB Client Functions 686 | # ===================== 687 | 688 | # C Wrapper procedures. Only these three are needed, all other only provide other default values 689 | proc stbi_zlib_decode_malloc_guesssize_headerflag(buffer: ptr cuchar, len: cint, initial_size: cint, 690 | outlen: ptr cint, parse_header: cint): ptr cuchar {.importc: "stbi_zlib_decode_malloc_guesssize_headerflag", stbcall.} 691 | 692 | proc stbi_zlib_decode_buffer(obuffer: ptr cuchar, olen: cint, ibuffer: ptr cuchar, ilen: cint): cint 693 | {.importc: "stbi_zlib_decode_buffer", stbcall.} 694 | 695 | proc stbi_zlib_decode_noheader_buffer(obuffer: ptr cuchar, olen: cint, ibuffer: ptr cuchar, ilen: cint): cint 696 | {.importc: "stbi_zlib_decode_noheader_buffer", stbcall.} 697 | 698 | ## Uncompresses ``buffer`` and returns the decompressed data. Too parse a raw inflate stream 699 | ## switch parseheader to ``false``. 700 | ## It allocates a new buffer, which size is determined by ``initial_size``. If the buffer isn't sufficient 701 | ## it may be reallocated. 702 | ## For faster decompression, especially if you know the output size, use ``zlibDecodeBuffer``. 703 | proc zlibDecodeMalloc*(buffer: openArray[byte], initial_size = 16384, parseheader = true): seq[byte] = 704 | var length = cint 0 705 | 706 | let data = stbi_zlib_decode_malloc_guesssize_headerflag(cast[ptr cuchar](unsafeAddr buffer[0]), 707 | cint buffer.len, cint initial_size, addr length, cint parseheader) 708 | 709 | # some error has occured 710 | if data.isNil: 711 | raise newException(STBIException, failureReason()) 712 | 713 | result = newSeq[byte](int length) 714 | copyMem(addr result[0], data, length) 715 | 716 | stbi_image_free(data) 717 | 718 | ## Uncompresses the data from ``input`` and puts the uncompressed data into ``output``. It doesn't allocate 719 | ## nor resize any buffers. 720 | ## The amount of data written to ``output`` is returned. 721 | ## Switch ``parseheader`` to ``false`` to parse a raw deflate stream. 722 | proc zlibDecodeBuffer*(input: openArray[byte], output: var openArray[byte], parseheader = true): Natural = 723 | let 724 | inputPtr = cast[ptr cuchar](output[0].addr) 725 | outputPtr = cast[ptr cuchar](input[0].unsafeAddr) 726 | bytesRead = (if not parseheader: 727 | stbi_zlib_decode_noheader_buffer(inputPtr, cint output.len, outputPtr, cint input.len) else: 728 | stbi_zlib_decode_buffer(inputPtr, cint output.len, outputPtr, cint input.len)) 729 | # some error has occured 730 | if bytesRead == -1: 731 | raise newException(STBIException, failureReason()) 732 | 733 | Natural(bytesRead) 734 | -------------------------------------------------------------------------------- /stb_image/stb_image_write.h: -------------------------------------------------------------------------------- 1 | /* stb_image_write - v1.07 - 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 | ABOUT: 14 | 15 | This header file is a library for writing images to C stdio. It could be 16 | adapted to write to memory or a general streaming interface; let me know. 17 | 18 | The PNG output is not optimal; it is 20-50% larger than the file 19 | written by a decent optimizing implementation. This library is designed 20 | for source code compactness and simplicity, not optimal image file size 21 | or run-time performance. 22 | 23 | BUILDING: 24 | 25 | You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. 26 | You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace 27 | malloc,realloc,free. 28 | You can define STBIW_MEMMOVE() to replace memmove() 29 | 30 | USAGE: 31 | 32 | There are four functions, one for each image file format: 33 | 34 | int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 35 | int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 36 | int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 37 | int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 38 | int stbi_write_jpg(char const *filename, int w, int h, int comp, const float *data); 39 | 40 | There are also four equivalent functions that use an arbitrary write function. You are 41 | expected to open/close your file-equivalent before and after calling these: 42 | 43 | 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); 44 | int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 45 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 46 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 47 | int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); 48 | 49 | where the callback is: 50 | void stbi_write_func(void *context, void *data, int size); 51 | 52 | You can define STBI_WRITE_NO_STDIO to disable the file variant of these 53 | functions, so the library will not use stdio.h at all. However, this will 54 | also disable HDR writing, because it requires stdio for formatted output. 55 | 56 | Each function returns 0 on failure and non-0 on success. 57 | 58 | The functions create an image file defined by the parameters. The image 59 | is a rectangle of pixels stored from left-to-right, top-to-bottom. 60 | Each pixel contains 'comp' channels of data stored interleaved with 8-bits 61 | per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is 62 | monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. 63 | The *data pointer points to the first byte of the top-left-most pixel. 64 | For PNG, "stride_in_bytes" is the distance in bytes from the first byte of 65 | a row of pixels to the first byte of the next row of pixels. 66 | 67 | PNG creates output files with the same number of components as the input. 68 | The BMP format expands Y to RGB in the file format and does not 69 | output alpha. 70 | 71 | PNG supports writing rectangles of data even when the bytes storing rows of 72 | data are not consecutive in memory (e.g. sub-rectangles of a larger image), 73 | by supplying the stride between the beginning of adjacent rows. The other 74 | formats do not. (Thus you cannot write a native-format BMP through the BMP 75 | writer, both because it is in BGR order and because it may have padding 76 | at the end of the line.) 77 | 78 | HDR expects linear float data. Since the format is always 32-bit rgb(e) 79 | data, alpha (if provided) is discarded, and for monochrome data it is 80 | replicated across all three channels. 81 | 82 | TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed 83 | data, set the global variable 'stbi_write_tga_with_rle' to 0. 84 | 85 | JPEG does ignore alpha channels in input data; quality is between 1 and 100. 86 | Higher quality looks better but results in a bigger image. 87 | JPEG baseline (no JPEG progressive). 88 | 89 | CREDITS: 90 | 91 | PNG/BMP/TGA 92 | Sean Barrett 93 | HDR 94 | Baldur Karlsson 95 | TGA monochrome: 96 | Jean-Sebastien Guay 97 | misc enhancements: 98 | Tim Kelsey 99 | TGA RLE 100 | Alan Hickman 101 | initial file IO callback implementation 102 | Emmanuel Julien 103 | JPEG 104 | Jon Olick (original jo_jpeg.cpp code) 105 | Daniel Gibson 106 | bugfixes: 107 | github:Chribba 108 | Guillaume Chereau 109 | github:jry2 110 | github:romigrou 111 | Sergio Gonzalez 112 | Jonas Karlsson 113 | Filip Wasil 114 | Thatcher Ulrich 115 | github:poppolopoppo 116 | Patrick Boettcher 117 | 118 | LICENSE 119 | 120 | See end of file for license information. 121 | 122 | */ 123 | 124 | #ifndef INCLUDE_STB_IMAGE_WRITE_H 125 | #define INCLUDE_STB_IMAGE_WRITE_H 126 | 127 | #ifdef __cplusplus 128 | extern "C" { 129 | #endif 130 | 131 | #ifdef STB_IMAGE_WRITE_STATIC 132 | #define STBIWDEF static 133 | #else 134 | #define STBIWDEF extern 135 | extern int stbi_write_tga_with_rle; 136 | #endif 137 | 138 | #ifndef STBI_WRITE_NO_STDIO 139 | STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 140 | STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 141 | STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 142 | STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 143 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); 144 | #endif 145 | 146 | typedef void stbi_write_func(void *context, void *data, int size); 147 | 148 | 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); 149 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 150 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 151 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 152 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); 153 | 154 | #ifdef __cplusplus 155 | } 156 | #endif 157 | 158 | #endif//INCLUDE_STB_IMAGE_WRITE_H 159 | 160 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION 161 | 162 | #ifdef _WIN32 163 | #ifndef _CRT_SECURE_NO_WARNINGS 164 | #define _CRT_SECURE_NO_WARNINGS 165 | #endif 166 | #ifndef _CRT_NONSTDC_NO_DEPRECATE 167 | #define _CRT_NONSTDC_NO_DEPRECATE 168 | #endif 169 | #endif 170 | 171 | #ifndef STBI_WRITE_NO_STDIO 172 | #include 173 | #endif // STBI_WRITE_NO_STDIO 174 | 175 | #include 176 | #include 177 | #include 178 | #include 179 | 180 | #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) 181 | // ok 182 | #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) 183 | // ok 184 | #else 185 | #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." 186 | #endif 187 | 188 | #ifndef STBIW_MALLOC 189 | #define STBIW_MALLOC(sz) malloc(sz) 190 | #define STBIW_REALLOC(p,newsz) realloc(p,newsz) 191 | #define STBIW_FREE(p) free(p) 192 | #endif 193 | 194 | #ifndef STBIW_REALLOC_SIZED 195 | #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) 196 | #endif 197 | 198 | 199 | #ifndef STBIW_MEMMOVE 200 | #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) 201 | #endif 202 | 203 | 204 | #ifndef STBIW_ASSERT 205 | #include 206 | #define STBIW_ASSERT(x) assert(x) 207 | #endif 208 | 209 | #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) 210 | 211 | typedef struct 212 | { 213 | stbi_write_func *func; 214 | void *context; 215 | } stbi__write_context; 216 | 217 | // initialize a callback-based context 218 | static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) 219 | { 220 | s->func = c; 221 | s->context = context; 222 | } 223 | 224 | #ifndef STBI_WRITE_NO_STDIO 225 | 226 | static void stbi__stdio_write(void *context, void *data, int size) 227 | { 228 | fwrite(data,1,size,(FILE*) context); 229 | } 230 | 231 | static int stbi__start_write_file(stbi__write_context *s, const char *filename) 232 | { 233 | FILE *f = fopen(filename, "wb"); 234 | stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); 235 | return f != NULL; 236 | } 237 | 238 | static void stbi__end_write_file(stbi__write_context *s) 239 | { 240 | fclose((FILE *)s->context); 241 | } 242 | 243 | #endif // !STBI_WRITE_NO_STDIO 244 | 245 | typedef unsigned int stbiw_uint32; 246 | typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; 247 | 248 | #ifdef STB_IMAGE_WRITE_STATIC 249 | static int stbi_write_tga_with_rle = 1; 250 | #else 251 | int stbi_write_tga_with_rle = 1; 252 | #endif 253 | 254 | static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) 255 | { 256 | while (*fmt) { 257 | switch (*fmt++) { 258 | case ' ': break; 259 | case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); 260 | s->func(s->context,&x,1); 261 | break; } 262 | case '2': { int x = va_arg(v,int); 263 | unsigned char b[2]; 264 | b[0] = STBIW_UCHAR(x); 265 | b[1] = STBIW_UCHAR(x>>8); 266 | s->func(s->context,b,2); 267 | break; } 268 | case '4': { stbiw_uint32 x = va_arg(v,int); 269 | unsigned char b[4]; 270 | b[0]=STBIW_UCHAR(x); 271 | b[1]=STBIW_UCHAR(x>>8); 272 | b[2]=STBIW_UCHAR(x>>16); 273 | b[3]=STBIW_UCHAR(x>>24); 274 | s->func(s->context,b,4); 275 | break; } 276 | default: 277 | STBIW_ASSERT(0); 278 | return; 279 | } 280 | } 281 | } 282 | 283 | static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) 284 | { 285 | va_list v; 286 | va_start(v, fmt); 287 | stbiw__writefv(s, fmt, v); 288 | va_end(v); 289 | } 290 | 291 | static void stbiw__putc(stbi__write_context *s, unsigned char c) 292 | { 293 | s->func(s->context, &c, 1); 294 | } 295 | 296 | static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) 297 | { 298 | unsigned char arr[3]; 299 | arr[0] = a, arr[1] = b, arr[2] = c; 300 | s->func(s->context, arr, 3); 301 | } 302 | 303 | static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) 304 | { 305 | unsigned char bg[3] = { 255, 0, 255}, px[3]; 306 | int k; 307 | 308 | if (write_alpha < 0) 309 | s->func(s->context, &d[comp - 1], 1); 310 | 311 | switch (comp) { 312 | case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case 313 | case 1: 314 | if (expand_mono) 315 | stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp 316 | else 317 | s->func(s->context, d, 1); // monochrome TGA 318 | break; 319 | case 4: 320 | if (!write_alpha) { 321 | // composite against pink background 322 | for (k = 0; k < 3; ++k) 323 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; 324 | stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); 325 | break; 326 | } 327 | /* FALLTHROUGH */ 328 | case 3: 329 | stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); 330 | break; 331 | } 332 | if (write_alpha > 0) 333 | s->func(s->context, &d[comp - 1], 1); 334 | } 335 | 336 | 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) 337 | { 338 | stbiw_uint32 zero = 0; 339 | int i,j, j_end; 340 | 341 | if (y <= 0) 342 | return; 343 | 344 | if (vdir < 0) 345 | j_end = -1, j = y-1; 346 | else 347 | j_end = y, j = 0; 348 | 349 | for (; j != j_end; j += vdir) { 350 | for (i=0; i < x; ++i) { 351 | unsigned char *d = (unsigned char *) data + (j*x+i)*comp; 352 | stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); 353 | } 354 | s->func(s->context, &zero, scanline_pad); 355 | } 356 | } 357 | 358 | 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, ...) 359 | { 360 | if (y < 0 || x < 0) { 361 | return 0; 362 | } else { 363 | va_list v; 364 | va_start(v, fmt); 365 | stbiw__writefv(s, fmt, v); 366 | va_end(v); 367 | stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); 368 | return 1; 369 | } 370 | } 371 | 372 | static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) 373 | { 374 | int pad = (-x*3) & 3; 375 | return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, 376 | "11 4 22 4" "4 44 22 444444", 377 | 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 378 | 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header 379 | } 380 | 381 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 382 | { 383 | stbi__write_context s; 384 | stbi__start_write_callbacks(&s, func, context); 385 | return stbi_write_bmp_core(&s, x, y, comp, data); 386 | } 387 | 388 | #ifndef STBI_WRITE_NO_STDIO 389 | STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) 390 | { 391 | stbi__write_context s; 392 | if (stbi__start_write_file(&s,filename)) { 393 | int r = stbi_write_bmp_core(&s, x, y, comp, data); 394 | stbi__end_write_file(&s); 395 | return r; 396 | } else 397 | return 0; 398 | } 399 | #endif //!STBI_WRITE_NO_STDIO 400 | 401 | static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) 402 | { 403 | int has_alpha = (comp == 2 || comp == 4); 404 | int colorbytes = has_alpha ? comp-1 : comp; 405 | int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 406 | 407 | if (y < 0 || x < 0) 408 | return 0; 409 | 410 | if (!stbi_write_tga_with_rle) { 411 | return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, 412 | "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); 413 | } else { 414 | int i,j,k; 415 | 416 | 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); 417 | 418 | for (j = y - 1; j >= 0; --j) { 419 | unsigned char *row = (unsigned char *) data + j * x * comp; 420 | int len; 421 | 422 | for (i = 0; i < x; i += len) { 423 | unsigned char *begin = row + i * comp; 424 | int diff = 1; 425 | len = 1; 426 | 427 | if (i < x - 1) { 428 | ++len; 429 | diff = memcmp(begin, row + (i + 1) * comp, comp); 430 | if (diff) { 431 | const unsigned char *prev = begin; 432 | for (k = i + 2; k < x && len < 128; ++k) { 433 | if (memcmp(prev, row + k * comp, comp)) { 434 | prev += comp; 435 | ++len; 436 | } else { 437 | --len; 438 | break; 439 | } 440 | } 441 | } else { 442 | for (k = i + 2; k < x && len < 128; ++k) { 443 | if (!memcmp(begin, row + k * comp, comp)) { 444 | ++len; 445 | } else { 446 | break; 447 | } 448 | } 449 | } 450 | } 451 | 452 | if (diff) { 453 | unsigned char header = STBIW_UCHAR(len - 1); 454 | s->func(s->context, &header, 1); 455 | for (k = 0; k < len; ++k) { 456 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); 457 | } 458 | } else { 459 | unsigned char header = STBIW_UCHAR(len - 129); 460 | s->func(s->context, &header, 1); 461 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); 462 | } 463 | } 464 | } 465 | } 466 | return 1; 467 | } 468 | 469 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 470 | { 471 | stbi__write_context s; 472 | stbi__start_write_callbacks(&s, func, context); 473 | return stbi_write_tga_core(&s, x, y, comp, (void *) data); 474 | } 475 | 476 | #ifndef STBI_WRITE_NO_STDIO 477 | STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) 478 | { 479 | stbi__write_context s; 480 | if (stbi__start_write_file(&s,filename)) { 481 | int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); 482 | stbi__end_write_file(&s); 483 | return r; 484 | } else 485 | return 0; 486 | } 487 | #endif 488 | 489 | // ************************************************************************************************* 490 | // Radiance RGBE HDR writer 491 | // by Baldur Karlsson 492 | 493 | #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) 494 | 495 | void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) 496 | { 497 | int exponent; 498 | float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); 499 | 500 | if (maxcomp < 1e-32f) { 501 | rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 502 | } else { 503 | float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; 504 | 505 | rgbe[0] = (unsigned char)(linear[0] * normalize); 506 | rgbe[1] = (unsigned char)(linear[1] * normalize); 507 | rgbe[2] = (unsigned char)(linear[2] * normalize); 508 | rgbe[3] = (unsigned char)(exponent + 128); 509 | } 510 | } 511 | 512 | void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) 513 | { 514 | unsigned char lengthbyte = STBIW_UCHAR(length+128); 515 | STBIW_ASSERT(length+128 <= 255); 516 | s->func(s->context, &lengthbyte, 1); 517 | s->func(s->context, &databyte, 1); 518 | } 519 | 520 | void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) 521 | { 522 | unsigned char lengthbyte = STBIW_UCHAR(length); 523 | STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code 524 | s->func(s->context, &lengthbyte, 1); 525 | s->func(s->context, data, length); 526 | } 527 | 528 | void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) 529 | { 530 | unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; 531 | unsigned char rgbe[4]; 532 | float linear[3]; 533 | int x; 534 | 535 | scanlineheader[2] = (width&0xff00)>>8; 536 | scanlineheader[3] = (width&0x00ff); 537 | 538 | /* skip RLE for images too small or large */ 539 | if (width < 8 || width >= 32768) { 540 | for (x=0; x < width; x++) { 541 | switch (ncomp) { 542 | case 4: /* fallthrough */ 543 | case 3: linear[2] = scanline[x*ncomp + 2]; 544 | linear[1] = scanline[x*ncomp + 1]; 545 | linear[0] = scanline[x*ncomp + 0]; 546 | break; 547 | default: 548 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 549 | break; 550 | } 551 | stbiw__linear_to_rgbe(rgbe, linear); 552 | s->func(s->context, rgbe, 4); 553 | } 554 | } else { 555 | int c,r; 556 | /* encode into scratch buffer */ 557 | for (x=0; x < width; x++) { 558 | switch(ncomp) { 559 | case 4: /* fallthrough */ 560 | case 3: linear[2] = scanline[x*ncomp + 2]; 561 | linear[1] = scanline[x*ncomp + 1]; 562 | linear[0] = scanline[x*ncomp + 0]; 563 | break; 564 | default: 565 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 566 | break; 567 | } 568 | stbiw__linear_to_rgbe(rgbe, linear); 569 | scratch[x + width*0] = rgbe[0]; 570 | scratch[x + width*1] = rgbe[1]; 571 | scratch[x + width*2] = rgbe[2]; 572 | scratch[x + width*3] = rgbe[3]; 573 | } 574 | 575 | s->func(s->context, scanlineheader, 4); 576 | 577 | /* RLE each component separately */ 578 | for (c=0; c < 4; c++) { 579 | unsigned char *comp = &scratch[width*c]; 580 | 581 | x = 0; 582 | while (x < width) { 583 | // find first run 584 | r = x; 585 | while (r+2 < width) { 586 | if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) 587 | break; 588 | ++r; 589 | } 590 | if (r+2 >= width) 591 | r = width; 592 | // dump up to first run 593 | while (x < r) { 594 | int len = r-x; 595 | if (len > 128) len = 128; 596 | stbiw__write_dump_data(s, len, &comp[x]); 597 | x += len; 598 | } 599 | // if there's a run, output it 600 | if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd 601 | // find next byte after run 602 | while (r < width && comp[r] == comp[x]) 603 | ++r; 604 | // output run up to r 605 | while (x < r) { 606 | int len = r-x; 607 | if (len > 127) len = 127; 608 | stbiw__write_run_data(s, len, comp[x]); 609 | x += len; 610 | } 611 | } 612 | } 613 | } 614 | } 615 | } 616 | 617 | static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) 618 | { 619 | if (y <= 0 || x <= 0 || data == NULL) 620 | return 0; 621 | else { 622 | // Each component is stored separately. Allocate scratch space for full output scanline. 623 | unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); 624 | int i, len; 625 | char buffer[128]; 626 | char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; 627 | s->func(s->context, header, sizeof(header)-1); 628 | 629 | len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 630 | s->func(s->context, buffer, len); 631 | 632 | for(i=0; i < y; i++) 633 | stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); 634 | STBIW_FREE(scratch); 635 | return 1; 636 | } 637 | } 638 | 639 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) 640 | { 641 | stbi__write_context s; 642 | stbi__start_write_callbacks(&s, func, context); 643 | return stbi_write_hdr_core(&s, x, y, comp, (float *) data); 644 | } 645 | 646 | #ifndef STBI_WRITE_NO_STDIO 647 | STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) 648 | { 649 | stbi__write_context s; 650 | if (stbi__start_write_file(&s,filename)) { 651 | int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); 652 | stbi__end_write_file(&s); 653 | return r; 654 | } else 655 | return 0; 656 | } 657 | #endif // STBI_WRITE_NO_STDIO 658 | 659 | 660 | ////////////////////////////////////////////////////////////////////////////// 661 | // 662 | // PNG writer 663 | // 664 | 665 | // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() 666 | #define stbiw__sbraw(a) ((int *) (a) - 2) 667 | #define stbiw__sbm(a) stbiw__sbraw(a)[0] 668 | #define stbiw__sbn(a) stbiw__sbraw(a)[1] 669 | 670 | #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) 671 | #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) 672 | #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) 673 | 674 | #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) 675 | #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) 676 | #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) 677 | 678 | static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) 679 | { 680 | int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; 681 | void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); 682 | STBIW_ASSERT(p); 683 | if (p) { 684 | if (!*arr) ((int *) p)[1] = 0; 685 | *arr = (void *) ((int *) p + 2); 686 | stbiw__sbm(*arr) = m; 687 | } 688 | return *arr; 689 | } 690 | 691 | static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) 692 | { 693 | while (*bitcount >= 8) { 694 | stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); 695 | *bitbuffer >>= 8; 696 | *bitcount -= 8; 697 | } 698 | return data; 699 | } 700 | 701 | static int stbiw__zlib_bitrev(int code, int codebits) 702 | { 703 | int res=0; 704 | while (codebits--) { 705 | res = (res << 1) | (code & 1); 706 | code >>= 1; 707 | } 708 | return res; 709 | } 710 | 711 | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) 712 | { 713 | int i; 714 | for (i=0; i < limit && i < 258; ++i) 715 | if (a[i] != b[i]) break; 716 | return i; 717 | } 718 | 719 | static unsigned int stbiw__zhash(unsigned char *data) 720 | { 721 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); 722 | hash ^= hash << 3; 723 | hash += hash >> 5; 724 | hash ^= hash << 4; 725 | hash += hash >> 17; 726 | hash ^= hash << 25; 727 | hash += hash >> 6; 728 | return hash; 729 | } 730 | 731 | #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) 732 | #define stbiw__zlib_add(code,codebits) \ 733 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) 734 | #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) 735 | // default huffman tables 736 | #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) 737 | #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) 738 | #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) 739 | #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) 740 | #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)) 741 | #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) 742 | 743 | #define stbiw__ZHASH 16384 744 | 745 | unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) 746 | { 747 | 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 }; 748 | 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 }; 749 | 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 }; 750 | 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 }; 751 | unsigned int bitbuf=0; 752 | int i,j, bitcount=0; 753 | unsigned char *out = NULL; 754 | unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); 755 | if (quality < 5) quality = 5; 756 | 757 | stbiw__sbpush(out, 0x78); // DEFLATE 32K window 758 | stbiw__sbpush(out, 0x5e); // FLEVEL = 1 759 | stbiw__zlib_add(1,1); // BFINAL = 1 760 | stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman 761 | 762 | for (i=0; i < stbiw__ZHASH; ++i) 763 | hash_table[i] = NULL; 764 | 765 | i=0; 766 | while (i < data_len-3) { 767 | // hash next 3 bytes of data to be compressed 768 | int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; 769 | unsigned char *bestloc = 0; 770 | unsigned char **hlist = hash_table[h]; 771 | int n = stbiw__sbcount(hlist); 772 | for (j=0; j < n; ++j) { 773 | if (hlist[j]-data > i-32768) { // if entry lies within window 774 | int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); 775 | if (d >= best) best=d,bestloc=hlist[j]; 776 | } 777 | } 778 | // when hash table entry is too long, delete half the entries 779 | if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { 780 | STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); 781 | stbiw__sbn(hash_table[h]) = quality; 782 | } 783 | stbiw__sbpush(hash_table[h],data+i); 784 | 785 | if (bestloc) { 786 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal 787 | h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); 788 | hlist = hash_table[h]; 789 | n = stbiw__sbcount(hlist); 790 | for (j=0; j < n; ++j) { 791 | if (hlist[j]-data > i-32767) { 792 | int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); 793 | if (e > best) { // if next match is better, bail on current match 794 | bestloc = NULL; 795 | break; 796 | } 797 | } 798 | } 799 | } 800 | 801 | if (bestloc) { 802 | int d = (int) (data+i - bestloc); // distance back 803 | STBIW_ASSERT(d <= 32767 && best <= 258); 804 | for (j=0; best > lengthc[j+1]-1; ++j); 805 | stbiw__zlib_huff(j+257); 806 | if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); 807 | for (j=0; d > distc[j+1]-1; ++j); 808 | stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); 809 | if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); 810 | i += best; 811 | } else { 812 | stbiw__zlib_huffb(data[i]); 813 | ++i; 814 | } 815 | } 816 | // write out final bytes 817 | for (;i < data_len; ++i) 818 | stbiw__zlib_huffb(data[i]); 819 | stbiw__zlib_huff(256); // end of block 820 | // pad with 0 bits to byte boundary 821 | while (bitcount) 822 | stbiw__zlib_add(0,1); 823 | 824 | for (i=0; i < stbiw__ZHASH; ++i) 825 | (void) stbiw__sbfree(hash_table[i]); 826 | STBIW_FREE(hash_table); 827 | 828 | { 829 | // compute adler32 on input 830 | unsigned int s1=1, s2=0; 831 | int blocklen = (int) (data_len % 5552); 832 | j=0; 833 | while (j < data_len) { 834 | for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; 835 | s1 %= 65521, s2 %= 65521; 836 | j += blocklen; 837 | blocklen = 5552; 838 | } 839 | stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); 840 | stbiw__sbpush(out, STBIW_UCHAR(s2)); 841 | stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); 842 | stbiw__sbpush(out, STBIW_UCHAR(s1)); 843 | } 844 | *out_len = stbiw__sbn(out); 845 | // make returned pointer freeable 846 | STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); 847 | return (unsigned char *) stbiw__sbraw(out); 848 | } 849 | 850 | static unsigned int stbiw__crc32(unsigned char *buffer, int len) 851 | { 852 | static unsigned int crc_table[256] = 853 | { 854 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 855 | 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 856 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 857 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 858 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 859 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 860 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 861 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 862 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 863 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 864 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 865 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 866 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 867 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 868 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 869 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 870 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 871 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 872 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 873 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 874 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 875 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 876 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 877 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 878 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 879 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 880 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 881 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 882 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 883 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 884 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 885 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 886 | }; 887 | 888 | unsigned int crc = ~0u; 889 | int i; 890 | for (i=0; i < len; ++i) 891 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; 892 | return ~crc; 893 | } 894 | 895 | #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) 896 | #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); 897 | #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) 898 | 899 | static void stbiw__wpcrc(unsigned char **data, int len) 900 | { 901 | unsigned int crc = stbiw__crc32(*data - len - 4, len+4); 902 | stbiw__wp32(*data, crc); 903 | } 904 | 905 | static unsigned char stbiw__paeth(int a, int b, int c) 906 | { 907 | int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); 908 | if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); 909 | if (pb <= pc) return STBIW_UCHAR(b); 910 | return STBIW_UCHAR(c); 911 | } 912 | 913 | // @OPTIMIZE: provide an option that always forces left-predict or paeth predict 914 | unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) 915 | { 916 | int ctype[5] = { -1, 0, 4, 2, 6 }; 917 | unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; 918 | unsigned char *out,*o, *filt, *zlib; 919 | signed char *line_buffer; 920 | int i,j,k,p,zlen; 921 | 922 | if (stride_bytes == 0) 923 | stride_bytes = x * n; 924 | 925 | filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; 926 | line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } 927 | for (j=0; j < y; ++j) { 928 | static int mapping[] = { 0,1,2,3,4 }; 929 | static int firstmap[] = { 0,1,0,5,6 }; 930 | int *mymap = (j != 0) ? mapping : firstmap; 931 | int best = 0, bestval = 0x7fffffff; 932 | for (p=0; p < 2; ++p) { 933 | for (k= p?best:0; k < 5; ++k) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass 934 | int type = mymap[k],est=0; 935 | unsigned char *z = pixels + stride_bytes*j; 936 | for (i=0; i < n; ++i) 937 | switch (type) { 938 | case 0: line_buffer[i] = z[i]; break; 939 | case 1: line_buffer[i] = z[i]; break; 940 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 941 | case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; 942 | case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; 943 | case 5: line_buffer[i] = z[i]; break; 944 | case 6: line_buffer[i] = z[i]; break; 945 | } 946 | for (i=n; i < x*n; ++i) { 947 | switch (type) { 948 | case 0: line_buffer[i] = z[i]; break; 949 | case 1: line_buffer[i] = z[i] - z[i-n]; break; 950 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 951 | case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; 952 | case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; 953 | case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; 954 | case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; 955 | } 956 | } 957 | if (p) break; 958 | for (i=0; i < x*n; ++i) 959 | est += abs((signed char) line_buffer[i]); 960 | if (est < bestval) { bestval = est; best = k; } 961 | } 962 | } 963 | // when we get here, best contains the filter type, and line_buffer contains the data 964 | filt[j*(x*n+1)] = (unsigned char) best; 965 | STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); 966 | } 967 | STBIW_FREE(line_buffer); 968 | zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory 969 | STBIW_FREE(filt); 970 | if (!zlib) return 0; 971 | 972 | // each tag requires 12 bytes of overhead 973 | out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); 974 | if (!out) return 0; 975 | *out_len = 8 + 12+13 + 12+zlen + 12; 976 | 977 | o=out; 978 | STBIW_MEMMOVE(o,sig,8); o+= 8; 979 | stbiw__wp32(o, 13); // header length 980 | stbiw__wptag(o, "IHDR"); 981 | stbiw__wp32(o, x); 982 | stbiw__wp32(o, y); 983 | *o++ = 8; 984 | *o++ = STBIW_UCHAR(ctype[n]); 985 | *o++ = 0; 986 | *o++ = 0; 987 | *o++ = 0; 988 | stbiw__wpcrc(&o,13); 989 | 990 | stbiw__wp32(o, zlen); 991 | stbiw__wptag(o, "IDAT"); 992 | STBIW_MEMMOVE(o, zlib, zlen); 993 | o += zlen; 994 | STBIW_FREE(zlib); 995 | stbiw__wpcrc(&o, zlen); 996 | 997 | stbiw__wp32(o,0); 998 | stbiw__wptag(o, "IEND"); 999 | stbiw__wpcrc(&o,0); 1000 | 1001 | STBIW_ASSERT(o == out + *out_len); 1002 | 1003 | return out; 1004 | } 1005 | 1006 | #ifndef STBI_WRITE_NO_STDIO 1007 | STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) 1008 | { 1009 | FILE *f; 1010 | int len; 1011 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 1012 | if (png == NULL) return 0; 1013 | f = fopen(filename, "wb"); 1014 | if (!f) { STBIW_FREE(png); return 0; } 1015 | fwrite(png, 1, len, f); 1016 | fclose(f); 1017 | STBIW_FREE(png); 1018 | return 1; 1019 | } 1020 | #endif 1021 | 1022 | 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) 1023 | { 1024 | int len; 1025 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 1026 | if (png == NULL) return 0; 1027 | func(context, png, len); 1028 | STBIW_FREE(png); 1029 | return 1; 1030 | } 1031 | 1032 | 1033 | /* *************************************************************************** 1034 | * 1035 | * JPEG writer 1036 | * 1037 | * This is based on Jon Olick's jo_jpeg.cpp: 1038 | * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html 1039 | */ 1040 | 1041 | 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, 1042 | 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 }; 1043 | 1044 | static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { 1045 | int bitBuf = *bitBufP, bitCnt = *bitCntP; 1046 | bitCnt += bs[1]; 1047 | bitBuf |= bs[0] << (24 - bitCnt); 1048 | while(bitCnt >= 8) { 1049 | unsigned char c = (bitBuf >> 16) & 255; 1050 | stbiw__putc(s, c); 1051 | if(c == 255) { 1052 | stbiw__putc(s, 0); 1053 | } 1054 | bitBuf <<= 8; 1055 | bitCnt -= 8; 1056 | } 1057 | *bitBufP = bitBuf; 1058 | *bitCntP = bitCnt; 1059 | } 1060 | 1061 | static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { 1062 | float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; 1063 | float z1, z2, z3, z4, z5, z11, z13; 1064 | 1065 | float tmp0 = d0 + d7; 1066 | float tmp7 = d0 - d7; 1067 | float tmp1 = d1 + d6; 1068 | float tmp6 = d1 - d6; 1069 | float tmp2 = d2 + d5; 1070 | float tmp5 = d2 - d5; 1071 | float tmp3 = d3 + d4; 1072 | float tmp4 = d3 - d4; 1073 | 1074 | // Even part 1075 | float tmp10 = tmp0 + tmp3; // phase 2 1076 | float tmp13 = tmp0 - tmp3; 1077 | float tmp11 = tmp1 + tmp2; 1078 | float tmp12 = tmp1 - tmp2; 1079 | 1080 | d0 = tmp10 + tmp11; // phase 3 1081 | d4 = tmp10 - tmp11; 1082 | 1083 | z1 = (tmp12 + tmp13) * 0.707106781f; // c4 1084 | d2 = tmp13 + z1; // phase 5 1085 | d6 = tmp13 - z1; 1086 | 1087 | // Odd part 1088 | tmp10 = tmp4 + tmp5; // phase 2 1089 | tmp11 = tmp5 + tmp6; 1090 | tmp12 = tmp6 + tmp7; 1091 | 1092 | // The rotator is modified from fig 4-8 to avoid extra negations. 1093 | z5 = (tmp10 - tmp12) * 0.382683433f; // c6 1094 | z2 = tmp10 * 0.541196100f + z5; // c2-c6 1095 | z4 = tmp12 * 1.306562965f + z5; // c2+c6 1096 | z3 = tmp11 * 0.707106781f; // c4 1097 | 1098 | z11 = tmp7 + z3; // phase 5 1099 | z13 = tmp7 - z3; 1100 | 1101 | *d5p = z13 + z2; // phase 6 1102 | *d3p = z13 - z2; 1103 | *d1p = z11 + z4; 1104 | *d7p = z11 - z4; 1105 | 1106 | *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; 1107 | } 1108 | 1109 | static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { 1110 | int tmp1 = val < 0 ? -val : val; 1111 | val = val < 0 ? val-1 : val; 1112 | bits[1] = 1; 1113 | while(tmp1 >>= 1) { 1114 | ++bits[1]; 1115 | } 1116 | bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { 1154 | } 1155 | // end0pos = first element in reverse order !=0 1156 | if(end0pos == 0) { 1157 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); 1158 | return DU[0]; 1159 | } 1160 | for(i = 1; i <= end0pos; ++i) { 1161 | int startpos = i; 1162 | int nrzeroes; 1163 | unsigned short bits[2]; 1164 | for (; DU[i]==0 && i<=end0pos; ++i) { 1165 | } 1166 | nrzeroes = i-startpos; 1167 | if ( nrzeroes >= 16 ) { 1168 | int lng = nrzeroes>>4; 1169 | int nrmarker; 1170 | for (nrmarker=1; nrmarker <= lng; ++nrmarker) 1171 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); 1172 | nrzeroes &= 15; 1173 | } 1174 | stbiw__jpg_calcBits(DU[i], bits); 1175 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); 1176 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); 1177 | } 1178 | if(end0pos != 63) { 1179 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); 1180 | } 1181 | return DU[0]; 1182 | } 1183 | 1184 | static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { 1185 | // Constants that don't pollute global namespace 1186 | 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}; 1187 | static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; 1188 | 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}; 1189 | static const unsigned char std_ac_luminance_values[] = { 1190 | 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 1191 | 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 1192 | 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 1193 | 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 1194 | 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 1195 | 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 1196 | 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa 1197 | }; 1198 | 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}; 1199 | static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; 1200 | 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}; 1201 | static const unsigned char std_ac_chrominance_values[] = { 1202 | 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 1203 | 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 1204 | 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 1205 | 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 1206 | 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 1207 | 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 1208 | 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa 1209 | }; 1210 | // Huffman tables 1211 | 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}}; 1212 | 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}}; 1213 | static const unsigned short YAC_HT[256][2] = { 1214 | {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}, 1215 | {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}, 1216 | {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}, 1217 | {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}, 1218 | {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}, 1219 | {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}, 1220 | {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}, 1221 | {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}, 1222 | {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}, 1223 | {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}, 1224 | {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}, 1225 | {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}, 1226 | {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}, 1227 | {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}, 1228 | {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}, 1229 | {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} 1230 | }; 1231 | static const unsigned short UVAC_HT[256][2] = { 1232 | {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}, 1233 | {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}, 1234 | {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}, 1235 | {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}, 1236 | {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}, 1237 | {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}, 1238 | {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}, 1239 | {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}, 1240 | {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}, 1241 | {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}, 1242 | {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}, 1243 | {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}, 1244 | {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}, 1245 | {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}, 1246 | {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}, 1247 | {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} 1248 | }; 1249 | 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, 1250 | 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}; 1251 | 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, 1252 | 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}; 1253 | static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1254 | 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; 1255 | 1256 | int row, col, i, k; 1257 | float fdtbl_Y[64], fdtbl_UV[64]; 1258 | unsigned char YTable[64], UVTable[64]; 1259 | 1260 | if(!data || !width || !height || comp > 4 || comp < 1) { 1261 | return 0; 1262 | } 1263 | 1264 | quality = quality ? quality : 90; 1265 | quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; 1266 | quality = quality < 50 ? 5000 / quality : 200 - quality * 2; 1267 | 1268 | for(i = 0; i < 64; ++i) { 1269 | int uvti, yti = (YQT[i]*quality+50)/100; 1270 | YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); 1271 | uvti = (UVQT[i]*quality+50)/100; 1272 | UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); 1273 | } 1274 | 1275 | for(row = 0, k = 0; row < 8; ++row) { 1276 | for(col = 0; col < 8; ++col, ++k) { 1277 | fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); 1278 | fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); 1279 | } 1280 | } 1281 | 1282 | // Write Headers 1283 | { 1284 | 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 }; 1285 | static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; 1286 | const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), 1287 | 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; 1288 | s->func(s->context, (void*)head0, sizeof(head0)); 1289 | s->func(s->context, (void*)YTable, sizeof(YTable)); 1290 | stbiw__putc(s, 1); 1291 | s->func(s->context, UVTable, sizeof(UVTable)); 1292 | s->func(s->context, (void*)head1, sizeof(head1)); 1293 | s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); 1294 | s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); 1295 | stbiw__putc(s, 0x10); // HTYACinfo 1296 | s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); 1297 | s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); 1298 | stbiw__putc(s, 1); // HTUDCinfo 1299 | s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); 1300 | s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); 1301 | stbiw__putc(s, 0x11); // HTUACinfo 1302 | s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); 1303 | s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); 1304 | s->func(s->context, (void*)head2, sizeof(head2)); 1305 | } 1306 | 1307 | // Encode 8x8 macroblocks 1308 | { 1309 | static const unsigned short fillBits[] = {0x7F, 7}; 1310 | const unsigned char *imageData = (const unsigned char *)data; 1311 | int DCY=0, DCU=0, DCV=0; 1312 | int bitBuf=0, bitCnt=0; 1313 | // comp == 2 is grey+alpha (alpha is ignored) 1314 | int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; 1315 | int x, y, pos; 1316 | for(y = 0; y < height; y += 8) { 1317 | for(x = 0; x < width; x += 8) { 1318 | float YDU[64], UDU[64], VDU[64]; 1319 | for(row = y, pos = 0; row < y+8; ++row) { 1320 | for(col = x; col < x+8; ++col, ++pos) { 1321 | int p = row*width*comp + col*comp; 1322 | float r, g, b; 1323 | if(row >= height) { 1324 | p -= width*comp*(row+1 - height); 1325 | } 1326 | if(col >= width) { 1327 | p -= comp*(col+1 - width); 1328 | } 1329 | 1330 | r = imageData[p+0]; 1331 | g = imageData[p+ofsG]; 1332 | b = imageData[p+ofsB]; 1333 | YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; 1334 | UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; 1335 | VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; 1336 | } 1337 | } 1338 | 1339 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); 1340 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); 1341 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); 1342 | } 1343 | } 1344 | 1345 | // Do the bit alignment of the EOI marker 1346 | stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); 1347 | } 1348 | 1349 | // EOI 1350 | stbiw__putc(s, 0xFF); 1351 | stbiw__putc(s, 0xD9); 1352 | 1353 | return 1; 1354 | } 1355 | 1356 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) 1357 | { 1358 | stbi__write_context s; 1359 | stbi__start_write_callbacks(&s, func, context); 1360 | return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); 1361 | } 1362 | 1363 | 1364 | #ifndef STBI_WRITE_NO_STDIO 1365 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) 1366 | { 1367 | stbi__write_context s; 1368 | if (stbi__start_write_file(&s,filename)) { 1369 | int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); 1370 | stbi__end_write_file(&s); 1371 | return r; 1372 | } else 1373 | return 0; 1374 | } 1375 | #endif 1376 | 1377 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION 1378 | 1379 | /* Revision history 1380 | 1.07 (2017-07-24) 1381 | doc fix 1382 | 1.06 (2017-07-23) 1383 | writing JPEG (using Jon Olick's code) 1384 | 1.05 ??? 1385 | 1.04 (2017-03-03) 1386 | monochrome BMP expansion 1387 | 1.03 ??? 1388 | 1.02 (2016-04-02) 1389 | avoid allocating large structures on the stack 1390 | 1.01 (2016-01-16) 1391 | STBIW_REALLOC_SIZED: support allocators with no realloc support 1392 | avoid race-condition in crc initialization 1393 | minor compile issues 1394 | 1.00 (2015-09-14) 1395 | installable file IO function 1396 | 0.99 (2015-09-13) 1397 | warning fixes; TGA rle support 1398 | 0.98 (2015-04-08) 1399 | added STBIW_MALLOC, STBIW_ASSERT etc 1400 | 0.97 (2015-01-18) 1401 | fixed HDR asserts, rewrote HDR rle logic 1402 | 0.96 (2015-01-17) 1403 | add HDR output 1404 | fix monochrome BMP 1405 | 0.95 (2014-08-17) 1406 | add monochrome TGA output 1407 | 0.94 (2014-05-31) 1408 | rename private functions to avoid conflicts with stb_image.h 1409 | 0.93 (2014-05-27) 1410 | warning fixes 1411 | 0.92 (2010-08-01) 1412 | casts to unsigned char to fix warnings 1413 | 0.91 (2010-07-17) 1414 | first public release 1415 | 0.90 first internal release 1416 | */ 1417 | 1418 | /* 1419 | ------------------------------------------------------------------------------ 1420 | This software is available under 2 licenses -- choose whichever you prefer. 1421 | ------------------------------------------------------------------------------ 1422 | ALTERNATIVE A - MIT License 1423 | Copyright (c) 2017 Sean Barrett 1424 | Permission is hereby granted, free of charge, to any person obtaining a copy of 1425 | this software and associated documentation files (the "Software"), to deal in 1426 | the Software without restriction, including without limitation the rights to 1427 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1428 | of the Software, and to permit persons to whom the Software is furnished to do 1429 | so, subject to the following conditions: 1430 | The above copyright notice and this permission notice shall be included in all 1431 | copies or substantial portions of the Software. 1432 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1433 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1434 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1435 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1436 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1437 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1438 | SOFTWARE. 1439 | ------------------------------------------------------------------------------ 1440 | ALTERNATIVE B - Public Domain (www.unlicense.org) 1441 | This is free and unencumbered software released into the public domain. 1442 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1443 | software, either in source code form or as a compiled binary, for any purpose, 1444 | commercial or non-commercial, and by any means. 1445 | In jurisdictions that recognize copyright laws, the author or authors of this 1446 | software dedicate any and all copyright interest in the software to the public 1447 | domain. We make this dedication for the benefit of the public at large and to 1448 | the detriment of our heirs and successors. We intend this dedication to be an 1449 | overt act of relinquishment in perpetuity of all present and future rights to 1450 | this software under copyright law. 1451 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1452 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1453 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1454 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1455 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1456 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1457 | ------------------------------------------------------------------------------ 1458 | */ 1459 | -------------------------------------------------------------------------------- /stb_image/write.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_WRITE_IMPLEMENTATION 2 | #include "stb_image_write.h" 3 | -------------------------------------------------------------------------------- /stb_image/write.nim: -------------------------------------------------------------------------------- 1 | # File: stb_image/write.nim 2 | # Author: Benjamin N. Summerton (define-private-public) 3 | # License: Unlicense (Public Domain) 4 | # Description: A nim wrapper for stb_image_write.h. 5 | 6 | 7 | import streams 8 | import sequtils 9 | 10 | import components 11 | export components.Y 12 | export components.YA 13 | export components.RGB 14 | export components.RGBA 15 | 16 | when defined(windows) and defined(vcc): 17 | {.pragma: stbcall, stdcall.} 18 | else: 19 | {.pragma: stbcall, cdecl.} 20 | 21 | # Include the header 22 | {.compile: "stb_image/write.c".} 23 | 24 | when defined(Posix) and not defined(haiku): 25 | {.passl: "-lm".} 26 | 27 | 28 | # Used for set if the TGA function should use run length encoding 29 | var stbi_write_tga_with_rle {.importc: "stbi_write_tga_with_rle".}: cint 30 | 31 | 32 | # Internal functions 33 | proc stbi_write_png( 34 | filename: cstring; 35 | w, h, comp: cint; 36 | data: pointer, 37 | stride_in_bytes: int 38 | ): cint 39 | {.importc: "stbi_write_png", stbcall.} 40 | 41 | proc stbi_write_bmp( 42 | filename: cstring; 43 | w, h, comp: cint; 44 | data: pointer 45 | ): cint 46 | {.importc: "stbi_write_bmp", stbcall.} 47 | 48 | proc stbi_write_tga( 49 | filename: cstring; 50 | w, h, comp: cint; 51 | data: pointer 52 | ): cint 53 | {.importc: "stbi_write_tga", stbcall.} 54 | 55 | proc stbi_write_hdr( 56 | filename: cstring; 57 | w, h, comp: cint; 58 | data: ptr cfloat 59 | ): cint 60 | {.importc: "stbi_write_hdr", stbcall.} 61 | 62 | proc stbi_write_jpg( 63 | filename: cstring; 64 | w, h, comp: cint; 65 | data: pointer; 66 | quality: cint; 67 | ): cint 68 | {.importc: "stbi_write_jpg", stbcall.} 69 | 70 | 71 | ## This proc will let you write out data to a PNG file. `w` and `h` are the 72 | ## size of the image you want. `comp` is how many components make up a single 73 | ## pixel (.e.g "RGBA," "RGB", "YA"). The entries in `data` should match be the 74 | ## same as `w * h * comp`. This returns a true upon success. 75 | ## 76 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 77 | ## 78 | ## By default the stride is set to zero. 79 | proc writePNG*(filename: string; w, h, comp: int; data: openarray[byte]; stride_in_bytes: int = 0): bool {.discardable.} = 80 | return (stbi_write_png(filename.cstring, w.cint, h.cint, comp.cint, data[0].unsafeAddr, stride_in_bytes) == 1) 81 | 82 | 83 | ## This proc will let you write out data to a BMP file. `w` and `h` are the 84 | ## size of the image you want. `comp` is how many components make up a single 85 | ## pixel (.e.g "RGB", "YA"). The entries in `data` should match be the 86 | ## same as `w * h * comp`. This returns a true upon success. 87 | ## 88 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 89 | proc writeBMP*(filename: string; w, h, comp: int; data: openarray[byte]): bool {.discardable.} = 90 | return (stbi_write_bmp(filename.cstring, w.cint, h.cint, comp.cint, data[0].unsafeAddr) == 1) 91 | 92 | 93 | ## This proc will let you write out data to a TGA file. `w` and `h` are the 94 | ## size of the image you want. `comp` is how many components make up a single 95 | ## pixel (.e.g "RGBA," "RGBA", "YA"). The entries in `data` should match be the 96 | ## same as `w * h * comp`. This returns a true upon success. 97 | ## 98 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 99 | ## 100 | ## By default this function will save the TGA with run-length encoding, but this 101 | ## can be turned off by setting `useRLE` to `false`. 102 | proc writeTGA*(filename: string; w, h, comp: int; data: openarray[byte]; useRLE: bool = true): bool {.discardable.} = 103 | # Set RLE option 104 | stbi_write_tga_with_rle = if useRLE: 1 else: 0 105 | return (stbi_write_tga(filename.cstring, w.cint, h.cint, comp.cint, data[0].unsafeAddr) == 1) 106 | 107 | 108 | ## This proc will let you write out data to a BMP file. `w` and `h` are the 109 | ## size of the image you want. `comp` is how many components make up a single 110 | ## pixel (.e.g "RGB", "YA"). The entries in `data` should match be the 111 | ## same as `w * h * comp`. This returns a true upon success. 112 | ## 113 | ## From the header file: 114 | ## ---- 115 | ## HDR expects linear float data. Since the format is always 32-bit rgb(e) 116 | ## data, alpha (if provided) is discarded, and for monochrome data it is 117 | ## replicated across all three channels. 118 | ## -- 119 | ## 120 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 121 | proc writeHDR*(filename: string; w, h, comp: int; data: openarray[float32]): bool {.discardable.} = 122 | return (stbi_write_hdr(filename.cstring, w.cint, h.cint, comp.cint, data[0].unsafeAddr) == 1) 123 | 124 | 125 | ## This proc will let you write out data to a JPEG file. `w` and `h` are the 126 | ## size of the image you want. `comp` is how many components make up a single 127 | ## pixel (.e.g "RGB", "YA"). `quality` is how well the quality of the saved 128 | ## JPEG image should be. It should be a value between [1, 100]. 1 for the 129 | ## lowest quality and 100 for the highest. Higher quality will result in larger 130 | ## file sizes. The entries in `data` should match be the same as `w * h * comp` 131 | ## . This returns a true upon success. 132 | ## 133 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 134 | proc writeJPG*(filename: string; w, h, comp: int; data: openarray[byte]; quality: int): bool {.discardable.} = 135 | return (stbi_write_jpg(filename.cstring, w.cint, h.cint, comp.cint, data[0].unsafeAddr, quality.cint) == 1) 136 | 137 | 138 | 139 | # Callback prototype for the `*_func` writing functions 140 | type writeCallback* = proc (context, data: pointer, size: cint) {.cdecl.} 141 | 142 | 143 | proc stbi_write_png_to_func( 144 | fn: writeCallback, 145 | context: pointer, 146 | w, h, comp: cint, 147 | data: pointer, 148 | stride_in_bytes: cint 149 | ): cint 150 | {.importc: "stbi_write_png_to_func", stbcall.} 151 | 152 | proc stbi_write_bmp_to_func( 153 | fn: writeCallback, 154 | context: pointer, 155 | w, h, comp: cint, 156 | data: pointer 157 | ): cint 158 | {.importc: "stbi_write_bmp_to_func", stbcall.} 159 | 160 | proc stbi_write_tga_to_func( 161 | fn: writeCallback, 162 | context: pointer, 163 | w, h, comp: cint, 164 | data: pointer 165 | ): cint 166 | {.importc: "stbi_write_tga_to_func", stbcall.} 167 | 168 | proc stbi_write_hdr_to_func( 169 | fn: writeCallback, 170 | context: pointer, 171 | w, h, comp: cint, 172 | data: pointer 173 | ): cint 174 | {.importc: "stbi_write_hdr_to_func", stbcall.} 175 | 176 | proc stbi_write_jpg_to_func( 177 | fn: writeCallback, 178 | context: pointer, 179 | w, h, comp: cint, 180 | data: pointer, 181 | quality: cint 182 | ): cint 183 | {.importc: "stbi_write_jpg_to_func", stbcall.} 184 | 185 | 186 | proc streamWriteData(context, data: pointer, size: cint) {.cdecl.} = 187 | if size > 0: 188 | let stream = cast[ptr StringStream](context) 189 | stream[].writeData(data, size) 190 | 191 | 192 | ## This proc will let you write out PNG data to memory. `w` and `h` are the 193 | ## size of the image you want. `comp` is how many components make up a single 194 | ## pixel (.e.g "RGBA," "RGB", "YA"). The entries in `data` should match be the 195 | ## same as `w * h * comp`. This raises an IOError exception on failure. 196 | ## Returns a binary sequence contaning the PNG data. 197 | ## 198 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 199 | ## 200 | ## By default the stride is set to zero. 201 | proc writePNG*(w, h, comp: int; data: openarray[byte]; stride_in_bytes: int = 0): seq[byte] = 202 | var buffer = newStringStream() 203 | 204 | if stbi_write_png_to_func(streamWriteData, buffer.addr, w.cint, h.cint, comp.cint, data[0].unsafeAddr, stride_in_bytes.cint) != 1: 205 | raise newException(IOError, "Failed to write PNG to memory") 206 | 207 | return cast[seq[byte]](buffer.data) 208 | 209 | 210 | ## This proc will let you write out BMP data to memory. `w` and `h` are the 211 | ## size of the image you want. `comp` is how many components make up a single 212 | ## pixel (.e.g "RGB", "YA"). The entries in `data` should match be the 213 | ## same as `w * h * comp`. This raises an IOError exception on failure. 214 | ## 215 | ## Returns a binary sequence contaning the BMP data. 216 | ## 217 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 218 | proc writeBMP*(w, h, comp: int; data: openarray[byte]): seq[byte] = 219 | var buffer = newStringStream() 220 | 221 | if stbi_write_bmp_to_func(streamWriteData, buffer.addr, w.cint, h.cint, comp.cint, data[0].unsafeAddr) != 1: 222 | raise newException(IOError, "Failed to write BMP to memory") 223 | 224 | return cast[seq[byte]](buffer.data) 225 | 226 | ## This proc will let you write out TGA data to memory. `w` and `h` are the 227 | ## size of the image you want. `comp` is how many components make up a single 228 | ## pixel (.e.g "RGBA," "RGBA", "YA"). The entries in `data` should match be the 229 | ## same as `w * h * comp`. This raises an IOError exception on failure. 230 | ## 231 | ## Returns a binary sequence contaning the TGA data. 232 | ## 233 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 234 | ## 235 | ## By default this function will save the TGA with run-length encoding, but this 236 | ## can be turned off by setting `useRLE` to `false`. 237 | proc writeTGA*(w, h, comp: int; data: openarray[byte]; useRLE: bool = true): seq[byte] = 238 | # Set RLE option 239 | stbi_write_tga_with_rle = if useRLE: 1 else: 0 240 | var buffer = newStringStream() 241 | 242 | if stbi_write_tga_to_func(streamWriteData, buffer.addr, w.cint, h.cint, comp.cint, data[0].unsafeAddr) != 1: 243 | raise newException(IOError, "Failed to write TGA to memory") 244 | 245 | return cast[seq[byte]](buffer.data) 246 | 247 | 248 | ## This proc will let you write out HDR data to memory. `w` and `h` are the 249 | ## size of the image you want. `comp` is how many components make up a single 250 | ## pixel (.e.g "RGB", "YA"). The entries in `data` should match be the 251 | ## same as `w * h * comp`. This raises an IOError exception on failure. 252 | ## 253 | ## Returns a binary sequence contaning the HDR data. 254 | ## 255 | ## From the header file: 256 | ## ---- 257 | ## HDR expects linear float data. Since the format is always 32-bit rgb(e) 258 | ## data, alpha (if provided) is discarded, and for monochrome data it is 259 | ## replicated across all three channels. 260 | ## -- 261 | ## 262 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 263 | proc writeHDR*(w, h, comp: int; data: openarray[byte]): seq[byte] = 264 | var buffer = newStringStream() 265 | 266 | if stbi_write_hdr_to_func(streamWriteData, buffer.addr, w.cint, h.cint, comp.cint, data[0].unsafeAddr) != 1: 267 | raise newException(IOError, "Failed to write HDR to memory") 268 | 269 | return cast[seq[byte]](buffer.data) 270 | 271 | 272 | ## This proc will let you write out JPG data to memory. `w` and `h` are the 273 | ## size of the image you want. `comp` is how many components make up a single 274 | ## pixel (.e.g "RGB", "YA"). `quality` is how well the quality of the saved 275 | ## JPEG image should be. It should be a value between [1, 100]. 1 for the 276 | ## lowest quality and 100 for the highest. Higher quality will result in larger 277 | ## file sizes. The entries in `data` should match be the same as `w * h * comp` 278 | ## . This raises an IOError exception on failure. 279 | ## 280 | ## Returns a binary sequence contaning the JPG data. 281 | ## 282 | ## Please see the documentation in the `stbi_image_write.h` file for more info. 283 | proc writeJPG*(w, h, comp: int; data: openarray[byte]; quality: int): seq[byte] = 284 | var buffer = newStringStream() 285 | 286 | if stbi_write_jpg_to_func(streamWriteData, buffer.addr, w.cint, h.cint, comp.cint, data[0].unsafeAddr, quality.cint) != 1: 287 | raise newException(IOError, "Failed to write JPG to memory") 288 | 289 | return cast[seq[byte]](buffer.data) 290 | 291 | -------------------------------------------------------------------------------- /testdata/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/image1.png -------------------------------------------------------------------------------- /testdata/image2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/image2.bmp -------------------------------------------------------------------------------- /testdata/image3.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/image3.pgm -------------------------------------------------------------------------------- /testdata/image4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/image4.jpeg -------------------------------------------------------------------------------- /testdata/save1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/save1.bmp -------------------------------------------------------------------------------- /testdata/save2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/save2.png -------------------------------------------------------------------------------- /testdata/save3.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/save3.tga -------------------------------------------------------------------------------- /testdata/save4.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/save4.tga -------------------------------------------------------------------------------- /testdata/save5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/save5.jpeg -------------------------------------------------------------------------------- /testdata/save6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/stb_image-Nim/ba5f45286bfa9bed93d8d6b941949cd6218ec888/testdata/save6.jpeg -------------------------------------------------------------------------------- /tests.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | import stb_image/read as stbi 4 | import stb_image/write as stbiw 5 | import sequtils 6 | 7 | const 8 | testImage1 = "testdata/image1.png" 9 | testImage2 = "testdata/image2.bmp" 10 | testImage3: seq[byte] = @[ 11 | 0x50'u8, 0x35'u8, 0x0a'u8, 0x23'u8, 0x20'u8, 0x43'u8, 0x52'u8, 0x45'u8, 0x41'u8, 0x54'u8, 0x4f'u8, 0x52'u8, 0x3a'u8, 0x20'u8, 0x47'u8, 0x49'u8, 12 | 0x4d'u8, 0x50'u8, 0x20'u8, 0x50'u8, 0x4e'u8, 0x4d'u8, 0x20'u8, 0x46'u8, 0x69'u8, 0x6c'u8, 0x74'u8, 0x65'u8, 0x72'u8, 0x20'u8, 0x56'u8, 0x65'u8, 13 | 0x72'u8, 0x73'u8, 0x69'u8, 0x6f'u8, 0x6e'u8, 0x20'u8, 0x31'u8, 0x2e'u8, 0x31'u8, 0x0a'u8, 0x31'u8, 0x20'u8, 0x32'u8, 0x0a'u8, 0x32'u8, 0x35'u8, 14 | 0x35'u8, 0x0a'u8, 0x54'u8, 0xa8'u8 15 | ] 16 | testImage4 = "testdata/image4.jpeg" 17 | 18 | testSave1 = "testdata/save1.bmp" 19 | testSave2 = "testdata/save2.png" 20 | testSave3 = "testdata/save3.tga" 21 | testSave4 = "testdata/save4.tga" 22 | testSave5 = "testdata/save5.jpeg" 23 | testSave6 = "testdata/save6.jpeg" 24 | 25 | 26 | # This is a little handy proc so I don't have to type so much 27 | proc addRGB(pixelData: var seq[byte]; r, g, b: byte) = 28 | pixelData.add(r) 29 | pixelData.add(g) 30 | pixelData.add(b) 31 | 32 | 33 | # Another handy proc for less typing 34 | proc addRGBA(pixelData: var seq[byte]; r, g, b, a: byte) = 35 | pixelData.add(r) 36 | pixelData.add(g) 37 | pixelData.add(b) 38 | pixelData.add(a) 39 | 40 | 41 | # Yet another handy proc for even less typing 42 | proc addYA(pixelData: var seq[byte]; mono, alpha: byte) = 43 | pixelData.add(mono) 44 | pixelData.add(alpha) 45 | 46 | 47 | 48 | 49 | suite "Unit Tests for stb_image wrapper": 50 | test "stbi.STBIException": 51 | # Test an STBIException being thrown 52 | # Data 53 | var 54 | width: int 55 | height: int 56 | channels: int 57 | pixels: seq[byte] 58 | 59 | # Load a non existant image 60 | try: 61 | pixels = stbi.load("testdata/kevin_bacon.jpeg", width, height, channels, stbi.Default) 62 | # This should never 63 | check(false) 64 | except STBIException: 65 | # This should hit 66 | check(true) 67 | except: 68 | # We should only get an STBIException, so check(false) here too 69 | check(false) 70 | 71 | 72 | test "stbi.load": 73 | # Data 74 | var 75 | width: int 76 | height: int 77 | channels: int 78 | pixels: seq[byte] 79 | 80 | # Load the image 81 | pixels = stbi.load(testImage1, width, height, channels, stbi.Default) 82 | 83 | check(width == 2) 84 | check(height == 2) 85 | check(channels == stbi.RGBA) 86 | check(pixels.len == (width * height * stbi.RGBA)) 87 | 88 | # Test the pixel data 89 | # 1 == 25% white 90 | check(pixels[0 + 0] == 0xFF) # R 91 | check(pixels[0 + 1] == 0xFF) # G 92 | check(pixels[0 + 2] == 0xFF) # B 93 | check(pixels[0 + 3] == 0x3F) # A 94 | 95 | # 2 == 50% yellow 96 | check(pixels[4 + 0] == 0xFF) # R 97 | check(pixels[4 + 1] == 0xFF) # G 98 | check(pixels[4 + 2] == 0x00) # B 99 | check(pixels[4 + 3] == 0x7F) # A 100 | 101 | # 1 == 75% magenta 102 | check(pixels[8 + 0] == 0xFF) # R 103 | check(pixels[8 + 1] == 0x00) # G 104 | check(pixels[8 + 2] == 0xFF) # B 105 | check(pixels[8 + 3] == 0xBF) # A 106 | 107 | # 1 == 100% cyan 108 | check(pixels[12 + 0] == 0x00) # R 109 | check(pixels[12 + 1] == 0xFF) # G 110 | check(pixels[12 + 2] == 0xFF) # B 111 | check(pixels[12 + 3] == 0xFF) # A 112 | 113 | test "stbi.load [desired_channels > actual_channels]": 114 | # Data 115 | var 116 | width: int 117 | height: int 118 | channels: int 119 | pixels: seq[byte] 120 | 121 | pixels = stbi.load(testImage4, width, height, channels, stbi.RGBA) 122 | check(width == 2) 123 | check(height == 2) 124 | check(channels == stbi.RGB) 125 | check(pixels.len == (width * height * stbi.RGBA)) 126 | 127 | # Test the pixel data 128 | check(pixels[0 + 0] == 0x11) # R 129 | check(pixels[0 + 1] == 0x11) # G 130 | check(pixels[0 + 2] == 0x11) # B 131 | check(pixels[0 + 3] == 0xFF) # A 132 | 133 | check(pixels[4 + 0] == 0x11) # R 134 | check(pixels[4 + 1] == 0x11) # G 135 | check(pixels[4 + 2] == 0x11) # B 136 | check(pixels[4 + 3] == 0xFF) # A 137 | 138 | check(pixels[8 + 0] == 0x11) # R 139 | check(pixels[8 + 1] == 0x11) # G 140 | check(pixels[8 + 2] == 0x11) # B 141 | check(pixels[8 + 3] == 0xFF) # A 142 | 143 | check(pixels[12 + 0] == 0x11) # R 144 | check(pixels[12 + 1] == 0x11) # G 145 | check(pixels[12 + 2] == 0x11) # B 146 | check(pixels[12 + 3] == 0xFF) # A 147 | 148 | test "stbi.loadFromFile": 149 | # Data 150 | var 151 | fileObj: File 152 | width: int 153 | height: int 154 | channels: int 155 | pixels: seq[byte] 156 | 157 | # Open the file object 158 | check(open(fileObj, testImage2)) 159 | 160 | # Load the image 161 | pixels = stbi.loadFromFile(fileObj, width, height, channels, stbi.Default) 162 | 163 | check(width == 2) 164 | check(height == 1) 165 | check(channels == stbi.RGB) 166 | check(pixels.len == (width * height * stbi.RGB)) 167 | 168 | # Test the pixel data 169 | # 1 == white 170 | check(pixels[0 + 0] == 0xFF) 171 | check(pixels[0 + 1] == 0xFF) 172 | check(pixels[0 + 2] == 0xFF) 173 | 174 | # 2 == Black 175 | check(pixels[3 + 0] == 0x00) 176 | check(pixels[3 + 1] == 0x00) 177 | check(pixels[3 + 2] == 0x00) 178 | 179 | 180 | test "stbi.loadFromMemory": 181 | # Data 182 | var 183 | width: int 184 | height: int 185 | channels: int 186 | pixels: seq[byte] 187 | 188 | # Load the image 189 | pixels = stbi.loadFromMemory(testImage3, width, height, channels, stbi.Grey) 190 | 191 | check(width == 1) 192 | check(height == 2) 193 | check(channels == stbi.Grey) 194 | check(pixels.len == (width * height * stbi.Grey)) 195 | 196 | # Test the pixel data 197 | # 1 == darker grey 198 | check(pixels[0] == 84) 199 | 200 | # 2 == lighter grey 201 | check(pixels[1] == 168) 202 | 203 | 204 | # NOTE: not able to find a suitable 16 bit image to test against 205 | # test "stbi.load16": 206 | # check(false) 207 | # 208 | # 209 | # NOTE: not able to find a suitable 16 bit image to test against 210 | # test "stbi.loadFromFile16": 211 | # check(false) 212 | # 213 | # 214 | # NOTE: not able to find a suitable floating point image to test against 215 | # test "stbi.loadF": 216 | # check(false) 217 | # 218 | # 219 | # NOTE: not able to find a suitable floating point image to test against 220 | # test "stbi.loadFFromMemory": 221 | # check(false) 222 | # 223 | # 224 | # NOTE: not able to find a suitable floating point image to test against 225 | # test "stbi.loadFFromFile": 226 | # check(false) 227 | # 228 | # 229 | # NOTE: not able to find a suitable HDR image to test against 230 | # test "stbi.HDRToLDRGamma": 231 | # check(false) 232 | # 233 | # 234 | # NOTE: not able to find a suitable HDR image to test against 235 | # test "stbi.HDRToLDRScale": 236 | # check(false) 237 | # 238 | # 239 | # NOTE: not able to find a suitable HDR image to test against 240 | # test "stbi.LDRToHDRGamma": 241 | # check(false) 242 | # 243 | # 244 | # NOTE: not able to find a suitable HDR image to test against 245 | # test "stbi.LDRToHDRScale": 246 | # check(false) 247 | # 248 | # 249 | # NOTE: not able to find a suitable HDR image to test against 250 | # test "stbi.isHDRFromMemory": 251 | # check(false) 252 | # 253 | # 254 | # NOTE: not able to find a suitable HDR image to test against 255 | # test "stbi.isHDR": 256 | # check(false) 257 | # 258 | # 259 | # NOTE: not able to find a suitable HDR image to test against 260 | # test "stbi.isHDRFromFile": 261 | # check(false) 262 | 263 | 264 | 265 | suite "Unit tests for stbi_image_write wrapper": 266 | test "stbiw.writeBMP": 267 | # data 268 | var 269 | width = 2 270 | height = 2 271 | channels = stbi.RGB 272 | pixels: seq[byte] = @[] 273 | filename = "save1.bmp" 274 | 275 | # Set the pixel data 276 | pixels.addRGB(0xFF, 0x00, 0x00) # Red 277 | pixels.addRGB(0x00, 0xFF, 0x00) # Green 278 | pixels.addRGB(0x00, 0x00, 0xFF) # Blue 279 | pixels.addRGB(0x00, 0x00, 0x00) # Black 280 | 281 | # Non-zero is returned on success 282 | check(stbiw.writeBMP(filename, width, height, channels, pixels)) 283 | 284 | # Verify the image with the one in "testdata/" 285 | var 286 | testPixels = cast[seq[byte]](readFile(testSave1)) 287 | ourPixels = cast[seq[byte]](readFile(filename)) 288 | 289 | # check for equivilancy 290 | check(testPixels == ourPixels) 291 | 292 | # remove the image 293 | removeFile(filename) 294 | 295 | 296 | test "stbiw.writeBMP [memory]": 297 | # data 298 | var 299 | width = 2 300 | height = 2 301 | channels = stbi.RGB 302 | pixels: seq[byte] = @[] 303 | testPixels = cast[seq[byte]](readFile(testSave1)) 304 | 305 | # Set the pixel data 306 | pixels.addRGB(0xFF, 0x00, 0x00) # Red 307 | pixels.addRGB(0x00, 0xFF, 0x00) # Green 308 | pixels.addRGB(0x00, 0x00, 0xFF) # Blue 309 | pixels.addRGB(0x00, 0x00, 0x00) # Black 310 | 311 | # save and load from memory 312 | let memoryPixels = stbiw.writeBMP(width, height, channels, pixels) 313 | 314 | # check for equivilancy 315 | check(testPixels == memoryPixels) 316 | 317 | 318 | test "stbiw.writePNG": 319 | # data 320 | var 321 | width = 3 322 | height = 2 323 | channels = stbiw.YA 324 | pixels: seq[byte] = @[] 325 | filename = "save2.png" 326 | 327 | # Set the pixel data 328 | pixels.addYA(0xFF, 0x33) # White 329 | pixels.addYA(0xFF, 0x66) 330 | pixels.addYA(0xFF, 0x99) 331 | 332 | pixels.addYA(0x00, 0x99) # Black 333 | pixels.addYA(0x00, 0x66) 334 | pixels.addYA(0x00, 0x33) 335 | 336 | # Non-zero is returned on success 337 | check(stbiw.writePNG(filename, width, height, channels, pixels)) 338 | 339 | # Verify image is the same in testdata/ 340 | var 341 | testPixels = cast[seq[byte]](readFile(testSave2)) 342 | ourPixels = cast[seq[byte]](readFile(filename)) 343 | 344 | # Check for sameness 345 | check(testPixels == ourPixels) 346 | 347 | # Remove the image 348 | removeFile(filename) 349 | 350 | 351 | test "stbiw.writePNG [memory]": 352 | # data 353 | var 354 | width = 3 355 | height = 2 356 | channels = stbiw.YA 357 | pixels: seq[byte] = @[] 358 | testPixels = cast[seq[byte]](readFile(testSave2)) 359 | 360 | # Set the pixel data 361 | pixels.addYA(0xFF, 0x33) # White 362 | pixels.addYA(0xFF, 0x66) 363 | pixels.addYA(0xFF, 0x99) 364 | 365 | pixels.addYA(0x00, 0x99) # Black 366 | pixels.addYA(0x00, 0x66) 367 | pixels.addYA(0x00, 0x33) 368 | 369 | # save and load from memory 370 | let memoryPixels = stbiw.writePNG(width, height, channels, pixels) 371 | 372 | # check for equivilancy 373 | check(testPixels == memoryPixels) 374 | 375 | 376 | test "stbiw.writeTGA [RLE]": 377 | var 378 | width = 5 379 | height = 2 380 | channels = stbi.RGBA 381 | pixels: seq[byte] = @[] 382 | filename = "save3.tga" 383 | 384 | # Set the pixel data 385 | for i in countup(1, 5): 386 | pixels.addRGBA(0x00, 0x00, 0x00, 0x80) 387 | for i in countup(1, 5): 388 | pixels.addRGBA(0xFF, 0xFF, 0xFF, 0x80) 389 | 390 | # Non-zero is returned on success 391 | # Writing with RLE by default 392 | check(stbiw.writeTGA(filename, width, height, channels, pixels)) 393 | 394 | # Verify image is the same in testdata/ 395 | var 396 | testPixels = cast[seq[byte]](readFile(testSave3)) 397 | ourPixels = cast[seq[byte]](readFile(filename)) 398 | 399 | # Check for sameness 400 | check(testPixels == ourPixels) 401 | 402 | # Remove the image 403 | removeFile(filename) 404 | 405 | 406 | test "stbiw.writeTGA [memory, RLE]": 407 | var 408 | width = 5 409 | height = 2 410 | channels = stbi.RGBA 411 | pixels: seq[byte] = @[] 412 | testPixels = cast[seq[byte]](readFile(testSave3)) 413 | 414 | # Set the pixel data 415 | for i in countup(1, 5): 416 | pixels.addRGBA(0x00, 0x00, 0x00, 0x80) 417 | for i in countup(1, 5): 418 | pixels.addRGBA(0xFF, 0xFF, 0xFF, 0x80) 419 | 420 | # save and load from memory 421 | let memoryPixels = stbiw.writeTGA(width, height, channels, pixels) 422 | 423 | # check for equivilancy 424 | check(testPixels == memoryPixels) 425 | 426 | 427 | test "stbiw.writeTGA [non-RLE]": 428 | # Data 429 | var 430 | width = 2 431 | height = 3 432 | channels = stbi.RGBA 433 | pixels: seq[byte] = @[] 434 | filename = "save4.tga" 435 | 436 | # Set the pixel data 437 | pixels.addRGBA(0xFF, 0x66, 0x00, 0x80) 438 | pixels.addRGBA(0xFF, 0x99, 0x00, 0xFF) 439 | 440 | pixels.addRGBA(0x66, 0x00, 0xFF, 0x80) 441 | pixels.addRGBA(0x99, 0x00, 0xFF, 0xFF) 442 | 443 | pixels.addRGBA(0x00, 0x66, 0xFF, 0x80) 444 | pixels.addRGBA(0x00, 0x99, 0xFF, 0xFF) 445 | 446 | # Non-zero is returned on success 447 | check(stbiw.writeTGA(filename, width, height, channels, pixels, false)) 448 | 449 | # Verify image is the same in testdata/ 450 | var 451 | testPixels = cast[seq[byte]](readFile(testSave4)) 452 | ourPixels = cast[seq[byte]](readFile(filename)) 453 | 454 | # Check for sameness 455 | check(testPixels == ourPixels) 456 | 457 | # Remove the image 458 | removeFile(filename) 459 | 460 | 461 | test "stbiw.writeTGA [memory, non-RLE]": 462 | # Data 463 | var 464 | width = 2 465 | height = 3 466 | channels = stbi.RGBA 467 | pixels: seq[byte] = @[] 468 | testPixels = cast[seq[byte]](readFile(testSave4)) 469 | 470 | # Set the pixel data 471 | pixels.addRGBA(0xFF, 0x66, 0x00, 0x80) 472 | pixels.addRGBA(0xFF, 0x99, 0x00, 0xFF) 473 | 474 | pixels.addRGBA(0x66, 0x00, 0xFF, 0x80) 475 | pixels.addRGBA(0x99, 0x00, 0xFF, 0xFF) 476 | 477 | pixels.addRGBA(0x00, 0x66, 0xFF, 0x80) 478 | pixels.addRGBA(0x00, 0x99, 0xFF, 0xFF) 479 | 480 | # save and load from memory 481 | let memoryPixels = stbiw.writeTGA(width, height, channels, pixels, false) 482 | 483 | # check for equivilancy 484 | check(testPixels == memoryPixels) 485 | 486 | 487 | test "stbiw.writeJPG [quality=10]": 488 | # data 489 | var 490 | width = 2 491 | height = 2 492 | channels = stbi.RGB 493 | pixels: seq[byte] = @[] 494 | filename = "save5.jpeg" 495 | 496 | # Set the pixel data 497 | pixels.addRGB(0xFF, 0x00, 0x00) # Red 498 | pixels.addRGB(0x00, 0xFF, 0x00) # Green 499 | pixels.addRGB(0x00, 0x00, 0xFF) # Blue 500 | pixels.addRGB(0x00, 0x00, 0x00) # Black 501 | 502 | # Non-zero is returned on success 503 | check(stbiw.writeJPG(filename, width, height, channels, pixels, 10)) 504 | 505 | # Verify the image with the one in "testdata/" 506 | var 507 | testPixels = cast[seq[byte]](readFile(testSave5)) 508 | ourPixels = cast[seq[byte]](readFile(filename)) 509 | 510 | # check for equivilancy 511 | check(testPixels == ourPixels) 512 | 513 | # remove the image 514 | removeFile(filename) 515 | 516 | 517 | test "stbiw.writeJPG [memory, quality=10]": 518 | # data 519 | var 520 | width = 2 521 | height = 2 522 | channels = stbi.RGB 523 | pixels: seq[byte] = @[] 524 | testPixels = cast[seq[byte]](readFile(testSave5)) 525 | 526 | # Set the pixel data 527 | pixels.addRGB(0xFF, 0x00, 0x00) # Red 528 | pixels.addRGB(0x00, 0xFF, 0x00) # Green 529 | pixels.addRGB(0x00, 0x00, 0xFF) # Blue 530 | pixels.addRGB(0x00, 0x00, 0x00) # Black 531 | 532 | # save and load from memory 533 | let memoryPixels = stbiw.writeJPG(width, height, channels, pixels, 10) 534 | 535 | # check for equivilancy 536 | check(testPixels == memoryPixels) 537 | 538 | 539 | test "stbiw.writeJPG [quality=100]": 540 | # data 541 | var 542 | width = 2 543 | height = 2 544 | channels = stbi.RGB 545 | pixels: seq[byte] = @[] 546 | filename = "save6.jpeg" 547 | 548 | # Set the pixel data 549 | pixels.addRGB(0xFF, 0x00, 0x00) # Red 550 | pixels.addRGB(0x00, 0xFF, 0x00) # Green 551 | pixels.addRGB(0x00, 0x00, 0xFF) # Blue 552 | pixels.addRGB(0x00, 0x00, 0x00) # Black 553 | 554 | # Non-zero is returned on success 555 | check(stbiw.writeJPG(filename, width, height, channels, pixels, 100)) 556 | 557 | # Verify the image with the one in "testdata/" 558 | var 559 | testPixels = cast[seq[byte]](readFile(testSave6)) 560 | ourPixels = cast[seq[byte]](readFile(filename)) 561 | 562 | # check for equivilancy 563 | check(testPixels == ourPixels) 564 | 565 | # remove the image 566 | removeFile(filename) 567 | 568 | 569 | test "stbiw.writeJPG [memory, quality=100]": 570 | # data 571 | var 572 | width = 2 573 | height = 2 574 | channels = stbi.RGB 575 | pixels: seq[byte] = @[] 576 | testPixels = cast[seq[byte]](readFile(testSave6)) 577 | 578 | # Set the pixel data 579 | pixels.addRGB(0xFF, 0x00, 0x00) # Red 580 | pixels.addRGB(0x00, 0xFF, 0x00) # Green 581 | pixels.addRGB(0x00, 0x00, 0xFF) # Blue 582 | pixels.addRGB(0x00, 0x00, 0x00) # Black 583 | 584 | # save and load from memory 585 | let memoryPixels = stbiw.writeJPG(width, height, channels, pixels, 100) 586 | 587 | # check for equivilancy 588 | check(testPixels == memoryPixels) 589 | 590 | 591 | # Note, I haven't been able to find a simple HDR image to test against, so 592 | # that is why there isn't a test for it here. 593 | # test "stbiw.writeHDR": 594 | # check(false) 595 | 596 | 597 | 598 | suite "info functions": 599 | test "stbi.infoFromMemory": 600 | # Data 601 | var 602 | width: int 603 | height: int 604 | channels: int 605 | 606 | check(stbi.infoFromMemory(testImage3, width, height, channels)) 607 | check(width == 1) 608 | check(height == 2) 609 | check(channels == stbi.Grey) 610 | 611 | 612 | test "stbi.info": 613 | # Data 614 | var 615 | width: int 616 | height: int 617 | channels: int 618 | 619 | check(stbi.info(testImage1, width, height, channels)) 620 | check(width == 2) 621 | check(height == 2) 622 | check(channels == stbi.RGBA) 623 | 624 | 625 | test "stbi.infoFromFile": 626 | # Data 627 | var 628 | width: int 629 | height: int 630 | channels: int 631 | fileObj: File 632 | 633 | # Open the file object 634 | check(open(fileObj, testImage2)) 635 | 636 | check(stbi.infoFromFile(fileObj, width, height, channels)) 637 | check(width == 2) 638 | check(height == 1) 639 | check(channels == stbi.RGB) 640 | 641 | 642 | 643 | suite "extra functions (from stb_image.h)": 644 | # # NOTE: I have no idea how to test this function, but I would like to 645 | # test "stbi.setUnpremultiplyOnLoad": 646 | # check(false) 647 | 648 | # # NOTE: I have no idea how to test this function, but I would like to 649 | # test "stbi.convertIPhonePNGToRGB": 650 | # check(false) 651 | 652 | 653 | test "stbi.setFlipVerticallyOnLoad": 654 | # Flip upside down 655 | stbi.setFlipVerticallyOnLoad(true) 656 | 657 | # NOTE: This is all coped from the "stbi.load" test at the top 658 | 659 | # Data 660 | var 661 | width: int 662 | height: int 663 | channels: int 664 | pixels: seq[byte] 665 | 666 | # Load the image 667 | pixels = stbi.load(testImage1, width, height, channels, stbi.Default) 668 | 669 | check(width == 2) 670 | check(height == 2) 671 | check(channels == stbi.RGBA) 672 | check(pixels.len == (width * height * stbi.RGBA)) 673 | 674 | # Test the pixel data 675 | 676 | # 1 == 75% magenta 677 | check(pixels[0 + 0] == 0xFF) # R 678 | check(pixels[0 + 1] == 0x00) # G 679 | check(pixels[0 + 2] == 0xFF) # B 680 | check(pixels[0 + 3] == 0xBF) # A 681 | 682 | # 1 == 100% cyan 683 | check(pixels[4 + 0] == 0x00) # R 684 | check(pixels[4 + 1] == 0xFF) # G 685 | check(pixels[4 + 2] == 0xFF) # B 686 | check(pixels[4 + 3] == 0xFF) # A 687 | 688 | # 1 == 25% white 689 | check(pixels[8 + 0] == 0xFF) # R 690 | check(pixels[8 + 1] == 0xFF) # G 691 | check(pixels[8 + 2] == 0xFF) # B 692 | check(pixels[8 + 3] == 0x3F) # A 693 | 694 | # 2 == 50% yellow 695 | check(pixels[12 + 0] == 0xFF) # R 696 | check(pixels[12 + 1] == 0xFF) # G 697 | check(pixels[12 + 2] == 0x00) # B 698 | check(pixels[12 + 3] == 0x7F) # A 699 | 700 | # Reset 701 | stbi.setFlipVerticallyOnLoad(false) 702 | 703 | 704 | suite "zlib functions": 705 | const 706 | # testStringUncompressed compressed with deflate and zlib header 707 | testStringCompressed = [0x78'u8, 0x1, 0xc5, 0x50, 0x3b, 0x6e, 0xc4, 0x40, 0x8, 0xbd, 0xca, 0x3b, 0x80, 0xe5, 0x3b, 0xa4, 0xdf, 0x2e, 0x27, 0xc0, 0x33, 0xc8, 0x22, 0x6b, 0x6, 0x67, 0x0, 0x4b, 0x7b, 0xfb, 0xb0, 0x1b, 0x29, 0x5d, 0xd2, 0xa6, 0xa0, 0xe1, 0x7d, 0xf5, 0x6e, 0x36, 0x59, 0x21, 0xa7, 0xa7, 0xa2, 0xdb, 0x61, 0x13, 0x2e, 0x1, 0x52, 0x8e, 0x5, 0xcd, 0x86, 0x73, 0x70, 0x64, 0x3d, 0xa9, 0x17, 0xa7, 0xc9, 0xd8, 0xc1, 0x87, 0xc4, 0x5c, 0xe0, 0xdc, 0xd1, 0x85, 0x14, 0xc3, 0x46, 0xea, 0x3, 0x2c, 0x53, 0xad, 0x23, 0x58, 0xcf, 0x32, 0x91, 0x71, 0x49, 0xcf, 0x11, 0xc8, 0xc0, 0x41, 0x5b, 0x85, 0x80, 0xe3, 0x3b, 0x80, 0xa1, 0xb4, 0xf, 0x2, 0x1d, 0xf2, 0x99, 708 | 0x8f, 0x32, 0xe0, 0x49, 0x15, 0xf6, 0xe3, 0x77, 0xd9, 0x91, 0x67, 0x24, 0xad, 0x78, 0xb, 0x5c, 0x3c, 0xd, 0x6c, 0xfe, 0x54, 0x53, 0x6b, 0xe9, 0x4f, 0x7e, 0xe0, 0x23, 0x3d, 0xc, 0x3d, 0xeb, 0x5e, 0x96, 0x2f, 0x9c, 0x9, 0x93, 0xb7, 0xd4, 0x15, 0xef, 0x55, 0x1a, 0xad, 0x7a, 0x12, 0xee, 0xe4, 0x1d, 0x7b, 0x6e, 0x3c, 0xf7, 0xc9, 0x63, 0xa9, 0xb6, 0x95, 0x44, 0x8, 0xba, 0x8b, 0x52, 0xe1, 0x4e, 0xa3, 0x45, 0x96, 0xde, 0x3, 0xb7, 0x5f, 0xb7, 0x58, 0xff, 0xc0, 0xfe, 0x7f, 0xa7, 0x2f, 0xda, 0x2, 0xa5, 0xa7,] 709 | # testStringUncompressed compressed with deflate but without zlib header 710 | testStringCompressedNoHeader = [0xc5'u8, 0x50, 0x3b, 0x6e, 0xc4, 0x40, 0x8, 0xbd, 0xca, 0x3b, 0x80, 0xe5, 0x3b, 0xa4, 0xdf, 0x2e, 0x27, 0xc0, 0x33, 0xc8, 0x22, 0x6b, 0x6, 0x67, 0x0, 0x4b, 0x7b, 0xfb, 0xb0, 0x1b, 0x29, 0x5d, 0xd2, 0xa6, 0xa0, 0xe1, 0x7d, 0xf5, 0x6e, 0x36, 0x59, 0x21, 0xa7, 0xa7, 0xa2, 0xdb, 0x61, 0x13, 0x2e, 0x1, 0x52, 0x8e, 0x5, 0xcd, 0x86, 0x73, 0x70, 0x64, 0x3d, 0xa9, 0x17, 0xa7, 0xc9, 0xd8, 0xc1, 0x87, 0xc4, 0x5c, 0xe0, 0xdc, 0xd1, 0x85, 0x14, 0xc3, 0x46, 0xea, 0x3, 0x2c, 0x53, 0xad, 0x23, 0x58, 0xcf, 0x32, 0x91, 0x71, 0x49, 711 | 0xcf, 0x11, 0xc8, 0xc0, 0x41, 0x5b, 0x85, 0x80, 0xe3, 0x3b, 0x80, 0xa1, 0xb4, 0xf, 0x2, 0x1d, 0xf2, 0x99, 0x8f, 0x32, 0xe0, 0x49, 0x15, 0xf6, 0xe3, 0x77, 0xd9, 0x91, 0x67, 0x24, 0xad, 0x78, 0xb, 0x5c, 0x3c, 0xd, 0x6c, 0xfe, 0x54, 0x53, 0x6b, 0xe9, 0x4f, 0x7e, 0xe0, 0x23, 0x3d, 0xc, 0x3d, 0xeb, 0x5e, 0x96, 0x2f, 0x9c, 0x9, 0x93, 0xb7, 0xd4, 0x15, 0xef, 0x55, 0x1a, 0xad, 0x7a, 0x12, 0xee, 0xe4, 0x1d, 0x7b, 0x6e, 0x3c, 0xf7, 0xc9, 0x63, 0xa9, 0xb6, 0x95, 0x44, 0x8, 0xba, 0x8b, 0x52, 0xe1, 0x4e, 0xa3, 0x45, 0x96, 0xde, 0x3, 0xb7, 0x5f, 0xb7, 0x58, 0xff, 0xc0, 0xfe, 0x7f, 0xa7, 0x2f] 712 | testStringUncompressed = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. " 713 | 714 | test "stbi.zlibDecodeMalloc": 715 | require cast[string](stbi.zlibDecodeMalloc(testStringCompressed)) == testStringUncompressed 716 | 717 | test "stbi.zlibDecodeBuffer": 718 | var output = newSeq[byte](testStringUncompressed.len) 719 | require stbi.zlibDecodeBuffer(testStringCompressed, output) == testStringUncompressed.len 720 | require cast[string](output) == testStringUncompressed 721 | 722 | test "stbi.zlibDecodeBuffer [out of memory]": 723 | var output = newSeq[byte](testStringUncompressed.len div 2) 724 | expect STBIException: 725 | discard stbi.zlibDecodeBuffer(testStringCompressed, output) 726 | 727 | test "stbi.zlibDecodeMalloc [raw stream, expecting error]": 728 | expect STBIException: 729 | discard stbi.zlibDecodeMalloc(testStringCompressedNoHeader) 730 | 731 | test "stbi.zlibDecodeBuffer [raw stream]": 732 | var output = newSeq[byte](testStringUncompressed.len) 733 | require zlibDecodeBuffer(testStringCompressedNoHeader, output, false) == testStringUncompressed.len 734 | 735 | --------------------------------------------------------------------------------