├── .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 |
--------------------------------------------------------------------------------