├── .gitattributes
├── .editorconfig
├── third
├── stb_image.cpp
├── stb_image_write.cpp
├── CMakeLists.txt
└── stb_image_write.h
├── .gitignore
├── CMakeLists.txt
├── framepacker.vcxproj.filters
├── framepacker.sln
├── LICENSE
├── framepacker.vcxproj.user
├── README.md
├── framepacker.vcxproj
├── framepacker.cpp
└── framepacker.hpp
/.gitattributes:
--------------------------------------------------------------------------------
1 | third/* linguist-vendored
2 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | indent_size = 2
6 |
--------------------------------------------------------------------------------
/third/stb_image.cpp:
--------------------------------------------------------------------------------
1 | #define STB_IMAGE_IMPLEMENTATION 1
2 | #include "stb_image.h"
3 |
--------------------------------------------------------------------------------
/third/stb_image_write.cpp:
--------------------------------------------------------------------------------
1 | #define STB_IMAGE_WRITE_IMPLEMENTATION
2 |
3 | #include "stb_image_write.h"
4 |
--------------------------------------------------------------------------------
/third/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8.2)
2 |
3 | add_library (framepacker_third_party
4 | stb_image.h
5 | stb_image.cpp
6 | stb_image_write.h
7 | stb_image_write.cpp)
8 |
9 | target_include_directories(framepacker_third_party PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.vs
2 | /output
3 | /temp
4 |
5 | /third/CMakeFiles
6 | /third/Makefile
7 | /third/cmake_install.cmake
8 | /CMakeFiles
9 | /Makefile
10 | /cmake_install.cmake
11 | /CMakeCache.txt
12 | /framepacker
13 |
14 | *.VC.db
15 | *.exe
16 | *.dll
17 | *.lib
18 | *.obj
19 | *.dbg
20 |
21 | *.a
22 | *.o
23 | *.so
24 |
25 | build
26 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8.2)
2 |
3 | project(framepacker_prj CXX)
4 |
5 | add_subdirectory(third)
6 |
7 | add_library(framepacker_prj INTERFACE)
8 |
9 | target_include_directories(framepacker_prj INTERFACE "${PROJECT_SOURCE_DIR}")
10 |
11 | add_executable(framepacker framepacker.cpp)
12 |
13 | target_link_libraries(framepacker PRIVATE framepacker_prj framepacker_third_party)
14 |
15 | if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
16 | target_compile_options(framepacker PRIVATE -Wall -Wextra -Werror -Wfatal-errors -std=c++11)
17 | endif((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
18 |
--------------------------------------------------------------------------------
/framepacker.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | third
7 |
8 |
9 | third
10 |
11 |
12 |
13 |
14 | {ad280806-94bd-4448-9069-70eafe4ee3e2}
15 |
16 |
17 |
18 |
19 | third
20 |
21 |
22 | third
23 |
24 |
25 |
--------------------------------------------------------------------------------
/framepacker.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "framepacker", "framepacker.vcxproj", "{44E27B10-3638-4E00-A372-285EFAFE1D9A}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Win32 = Debug|Win32
9 | Release|Win32 = Release|Win32
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {44E27B10-3638-4E00-A372-285EFAFE1D9A}.Debug|Win32.ActiveCfg = Debug|Win32
13 | {44E27B10-3638-4E00-A372-285EFAFE1D9A}.Debug|Win32.Build.0 = Debug|Win32
14 | {44E27B10-3638-4E00-A372-285EFAFE1D9A}.Release|Win32.ActiveCfg = Release|Win32
15 | {44E27B10-3638-4E00-A372-285EFAFE1D9A}.Release|Win32.Build.0 = Release|Win32
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (C) 2015, 2020 Wang Renxin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/framepacker.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | test/pet_icon_17.png test/pet_icon_01.png test/pet_icon_02.png test/pet_icon_03.png test/pet_icon_04.png test/pet_icon_05.png test/pet_icon_06.png test/pet_icon_07.png test/pet_icon_08.png test/pet_icon_09.png test/pet_icon_10.png test/pet_icon_11.png test/pet_icon_12.png test/pet_icon_13.png test/pet_icon_14.png test/pet_icon_15.png test/pet_icon_16.png test/pet_icon_17.png test/pet_icon_18.png test/pet_icon_19.png test/pet_icon_20.png test/pet_icon_01.png test/pet_icon_01.png test/pet_icon_01.png test/pet_icon_01.png test/pet_icon_01.png -o output -w -s 128x256 -t
5 | WindowsLocalDebugger
6 |
7 |
8 |
9 |
10 | WindowsLocalDebugger
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # framepacker
2 |
3 | **framepacker** is a freeware which implements a texture bin packing algorithm. It's similar to texture tools like the TexturePacker. You are free to use, copy, modify or distribute it under the MIT license.
4 |
5 | It implements a bin packer algorithm refer to the [Binary Tree Bin Packing Algorithm](http://codeincomplete.com/posts/2011/5/7/bin_packing/).
6 |
7 | ## Compile
8 |
9 | A C++ 11 compiler is required to compile the source code. The `framepacker::packer` template class in `framepacker.hpp` is the algorithm implementation. And it offers a specialization in `framepacker.cpp` using stb image.
10 |
11 | ## Usage
12 |
13 | It works as a command line tool:
14 |
15 | * framepacker FILE_LIST [-o output_file] [OPTIONS] - Packs some images
16 | * FILE_LIST
17 | * := file * : Eg. file1.png file2.png
18 | * OPTIONS
19 | * := -p N : Padding, default to 1
20 | * := -s MxN : Expected texture size, eg. 256x512, may enlarge result
21 | * := -t : Disable rotation
22 | * := -w : Disable forcing texture to POT (Power of 2)
23 | * := -m : Disable alpha trim
24 |
25 | Eg. "**framepacker** foo.png bar.png -o out", it generates a packed `out.png` image and another `out.json` meta data which includes packing information to look up slices from the image.
26 |
27 | ## Performance
28 |
29 | **framepacker** is fast, it packs 200 textures in less than 0.15s on common desktop machines at 2015.
30 |
--------------------------------------------------------------------------------
/framepacker.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 |
14 | {44E27B10-3638-4E00-A372-285EFAFE1D9A}
15 | Win32Proj
16 | framepacker
17 | 8.1
18 |
19 |
20 |
21 | Application
22 | true
23 | v140
24 | Unicode
25 |
26 |
27 | Application
28 | false
29 | v140
30 | true
31 | Unicode
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | true
45 | $(SolutionDir)output/debug/
46 | $(SolutionDir)temp/debug/
47 |
48 |
49 | false
50 | $(SolutionDir)output/release/
51 | $(SolutionDir)temp/release/
52 |
53 |
54 |
55 |
56 |
57 | Level3
58 | Disabled
59 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
60 | true
61 | MultiThreadedDebugDLL
62 |
63 |
64 | $(ProjectDir)third;$(ProjectDir)
65 |
66 |
67 | Console
68 | true
69 | %(AdditionalDependencies)
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | Level3
87 |
88 |
89 | MaxSpeed
90 | true
91 | true
92 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
93 | true
94 | MultiThreadedDLL
95 | $(ProjectDir)third;$(ProjectDir)
96 |
97 |
98 | Console
99 | true
100 | true
101 | true
102 | %(AdditionalDependencies)
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/framepacker.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | ** This source file is part of framepacker
3 | **
4 | ** For the latest info, see https://github.com/paladin-t/framepacker/
5 | **
6 | ** Copyright (C) 2015, 2020 Wang Renxin
7 | ** Copyright (C) 2020 Taylor Holberton
8 | **
9 | ** Permission is hereby granted, free of charge, to any person obtaining a copy of
10 | ** this software and associated documentation files (the "Software"), to deal in
11 | ** the Software without restriction, including without limitation the rights to
12 | ** use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
13 | ** the Software, and to permit persons to whom the Software is furnished to do so,
14 | ** subject to the following conditions:
15 | **
16 | ** The above copyright notice and this permission notice shall be included in all
17 | ** copies or substantial portions of the Software.
18 | **
19 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
21 | ** FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
22 | ** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
23 | ** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 | ** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 | */
26 |
27 | #include
28 |
29 | #include
30 | #include
31 |
32 | #include
33 | #include
34 |
35 | // From 'third/' directory.
36 | #include
37 | #include
38 |
39 | namespace fp = framepacker;
40 |
41 | namespace {
42 |
43 | using size_type = std::size_t;
44 |
45 | using color_type = unsigned char;
46 |
47 | struct rgba final {
48 | color_type data[4];
49 |
50 | inline static constexpr rgba from_buffer(const color_type* rgba_ptr) noexcept {
51 | return rgba {
52 | rgba_ptr[0],
53 | rgba_ptr[1],
54 | rgba_ptr[2],
55 | rgba_ptr[3]
56 | };
57 | }
58 |
59 | inline color_type& operator [] (size_type i) noexcept {
60 | return data[i];
61 | }
62 |
63 | inline const color_type& operator [] (size_type i) const noexcept {
64 | return data[i];
65 | }
66 |
67 | inline constexpr bool is_transparent() const noexcept {
68 | return data[3] == 0;
69 | }
70 | };
71 |
72 | class image final {
73 | public:
74 | ~image() {
75 | std::free(color_buffer);
76 | color_buffer = nullptr;
77 | w = 0;
78 | h = 0;
79 | }
80 |
81 | inline const char* get_path(void) const noexcept {
82 | return path.c_str();
83 | }
84 |
85 | size_type width(void) const noexcept {
86 | return w;
87 | }
88 | size_type height(void) const noexcept {
89 | return h;
90 | }
91 |
92 | inline constexpr const color_type* data(size_type x = 0, size_type y = 0) const noexcept {
93 | return &color_buffer[((y * w) + x) * 4];
94 | }
95 |
96 | inline color_type* data(size_type x = 0, size_type y = 0) noexcept {
97 | return &color_buffer[((y * w) + x) * 4];
98 | }
99 |
100 | inline constexpr rgba pixel(size_type x, size_type y) const noexcept {
101 | return rgba::from_buffer(&color_buffer[((y * w) + x) * 4]);
102 | }
103 |
104 | inline void pixel(size_type x, size_type y, const rgba &c) noexcept {
105 | auto* dst = data(x, y);
106 | dst[0] = c[0];
107 | dst[1] = c[1];
108 | dst[2] = c[2];
109 | dst[3] = c[3];
110 | }
111 |
112 | void copy_from(
113 | const image &src,
114 | size_type src_x,
115 | size_type src_y,
116 | size_type width,
117 | size_type height,
118 | size_type dst_x,
119 | size_type dst_y
120 | ) {
121 | // TODO : Worth checking if 'src' is ever equal to 'this'.
122 | // If not, then we don't need the temporary buffer.
123 | color_type* tmp = (color_type*)std::malloc(width * height * 4);
124 | if (!tmp) {
125 | return;
126 | }
127 |
128 | // Copy from source onto temporary buffer.
129 | for (size_type y = 0; y < height; y++) {
130 | const color_type* src_ptr = src.data(src_x, src_y + y);
131 |
132 | color_type* dst_ptr = &tmp[y * width * 4];
133 |
134 | std::memcpy(dst_ptr, src_ptr, width * 4);
135 | }
136 |
137 | // Copy from temporary buffer to destination.
138 | for (size_type y = 0; y < height; y++) {
139 | const color_type* src_ptr = &tmp[y * width * 4];
140 |
141 | color_type* dst_ptr = data(dst_x, dst_y + y);
142 |
143 | std::memcpy(dst_ptr, src_ptr, width * 4);
144 | }
145 |
146 | std::free(tmp);
147 | }
148 |
149 | inline constexpr bool is_transparent(size_type x, size_type y) const noexcept {
150 | return pixel(x, y).is_transparent();
151 | }
152 |
153 | bool resize(size_type _w, size_type _h) noexcept {
154 | auto* tmp = (color_type*)std::realloc(color_buffer, _w * _h * 4);
155 | if (!tmp) {
156 | return false;
157 | }
158 |
159 | color_buffer = tmp;
160 | w = _w;
161 | h = _h;
162 |
163 | return true;
164 | }
165 |
166 | bool load_file(const char* p) noexcept {
167 | int tmp_w = 0;
168 | int tmp_h = 0;
169 | int tmp_n = 0;
170 | color_type* tmp = stbi_load(p, &tmp_w, &tmp_h, &tmp_n, 4);
171 | if (!tmp) {
172 | return false;
173 | }
174 |
175 | std::free(color_buffer);
176 |
177 | color_buffer = tmp;
178 | w = size_type(tmp_w);
179 | h = size_type(tmp_h);
180 |
181 | path = p;
182 |
183 | return true;
184 | }
185 |
186 | bool save_png(const char* p) noexcept {
187 | return stbi_write_png(p, w, h, 4, color_buffer, w * 4) != 0;
188 | }
189 |
190 | private:
191 | std::string path;
192 | color_type* color_buffer = nullptr;
193 | size_type w = 0;
194 | size_type h = 0;
195 | };
196 |
197 | using packer_type = fp::packer;
198 |
199 | #define _BIN_FILE_NAME "framepacker"
200 |
201 | #define _CHECK_ARG(__c, __i, __e) \
202 | do { \
203 | if(__c <= __i + 1) { \
204 | printf(__e); \
205 | return; \
206 | } \
207 | } while(0)
208 |
209 | void show_tip(void) {
210 | printf("[Usage]\n");
211 | printf(" %s FILE_LIST [-o output_file] [OPTIONS] - Packs some images\n", _BIN_FILE_NAME);
212 | printf("\n");
213 | printf(" FILE_LIST := file * : Eg. file1.png file2.png\n");
214 | printf(" OPTIONS := -p N : Padding, default to 1\n");
215 | printf(" := -s MxN : Expected texture size, eg. 256x512, may enlarge result\n");
216 | printf(" := -t : Disable rotation\n");
217 | printf(" := -w : Disable forcing texture to POT (Power of 2)\n");
218 | printf(" := -m : Disable alpha trim\n");
219 | }
220 |
221 | void process_parameters(int argc, char* argv[]) {
222 | int i = 1;
223 | std::string m = "\0";
224 | int padding = 1;
225 | int w = 0, h = 0;
226 | bool allow_rotate = true;
227 | bool power_of_2 = true;
228 | bool alpha_trim = true;
229 |
230 | std::list inputs;
231 | std::list > options;
232 |
233 | // Parse arguments.
234 | while (i < argc) {
235 | if (!std::memcmp(argv[i], "-", 1)) {
236 | if (!std::memcmp(argv[i] + 1, "o", 1)) {
237 | m = 'o';
238 | _CHECK_ARG(argc, i, "-o: File name expected.\n");
239 | options.push_back(std::make_pair(m, argv[++i]));
240 | ++i;
241 | } else if (!memcmp(argv[i] + 1, "p", 1)) {
242 | _CHECK_ARG(argc, i, "-p: Padding size expected.\n");
243 | padding = std::max(std::atoi(argv[++i]), 0);
244 | } else if (!memcmp(argv[i] + 1, "s", 1)) {
245 | _CHECK_ARG(argc, i, "-s: Size expected.\n");
246 | std::string buf = argv[++i];
247 | size_t sep = buf.find('x');
248 | if (sep != std::string::npos) {
249 | std::string wstr = buf.substr(0, sep);
250 | std::string hstr = buf.substr(sep + 1);
251 | w = std::atoi(wstr.c_str());
252 | h = std::atoi(hstr.c_str());
253 | }
254 | ++i;
255 | } else if (!memcmp(argv[i] + 1, "t", 1)) {
256 | allow_rotate = false;
257 | ++i;
258 | } else if (!memcmp(argv[i] + 1, "w", 1)) {
259 | power_of_2 = false;
260 | ++i;
261 | } else if (!memcmp(argv[i] + 1, "m", 1)) {
262 | alpha_trim = false;
263 | ++i;
264 | } else {
265 | printf("Unknown argument: %s.\n", argv[i++]);
266 | }
267 | } else {
268 | inputs.push_back(argv[i++]);
269 | }
270 | }
271 |
272 | // Tell output path.
273 | std::string outpath = "output.png";
274 | for (auto it = options.begin(); it != options.end(); ++it) {
275 | if (it->first == "o")
276 | outpath = it->second;
277 | }
278 |
279 | packer_type packer;
280 | packer.padding = padding;
281 | packer.output_texture_size = fp::vec2(w, h);
282 | packer.allow_rotate = allow_rotate;
283 | packer.power_of_2 = power_of_2;
284 | packer.alpha_trim = alpha_trim;
285 | packer.comparer = packer_type::compare_area;
286 |
287 | for (auto it = inputs.begin(); it != inputs.end(); ++it) {
288 | image* img = new image;
289 |
290 | if (!img->load_file(it->c_str())) {
291 | // TODO : handle this
292 | }
293 |
294 | packer_type::texture_type texture(img);
295 |
296 | packer.add(it->c_str(), texture);
297 | }
298 |
299 | // Pack it.
300 | packer_type::texture_type result(new image);
301 | packer_type::texture_coll_type packed;
302 | packer_type::texture_coll_type failed;
303 | packer.pack(result, packed, failed);
304 |
305 | // Prompt.
306 | {
307 | for (auto it = packed.begin(); it != packed.end(); ++it) {
308 | packer_type::block_type &blk = it->second;
309 | printf(" %s [%d, %d] - [%d, %d]\n", it->first.c_str(), blk.fit->min_x(), blk.fit->min_y(), blk.fit->min_x() + blk.fit->width(), blk.fit->min_y() + blk.fit->height());
310 | }
311 | }
312 | if (failed.size()) {
313 | for (auto it = failed.begin(); it != failed.end(); ++it) {
314 | printf(" %s\n", it->first.c_str());
315 | }
316 | }
317 |
318 | // Serialize.
319 | {
320 | std::string out_tex = outpath + ".png";
321 | result->save_png(out_tex.c_str());
322 | printf("Texture written: %s.\n", out_tex.c_str());
323 | }
324 | {
325 | std::string out_meta = outpath + ".json";
326 | std::ofstream fs(out_meta);
327 | packer.write_meta(fs, packed, result, outpath.c_str());
328 | fs.close();
329 | printf("Meta data written: %s.\n", out_meta.c_str());
330 | }
331 | }
332 |
333 | } // namespace
334 |
335 | int main(int argc, char* argv[]) {
336 | if (argc == 1)
337 | show_tip();
338 | else if (argc >= 2)
339 | process_parameters(argc, argv);
340 |
341 | return 0;
342 | }
343 |
--------------------------------------------------------------------------------
/framepacker.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | ** This source file is part of framepacker
3 | **
4 | ** For the latest info, see https://github.com/paladin-t/framepacker/
5 | **
6 | ** Copyright (C) 2015 Wang Renxin
7 | **
8 | ** Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | ** this software and associated documentation files (the "Software"), to deal in
10 | ** the Software without restriction, including without limitation the rights to
11 | ** use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | ** the Software, and to permit persons to whom the Software is furnished to do so,
13 | ** subject to the following conditions:
14 | **
15 | ** The above copyright notice and this permission notice shall be included in all
16 | ** copies or substantial portions of the Software.
17 | **
18 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
20 | ** FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
21 | ** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22 | ** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 | ** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 | */
25 |
26 | #ifndef FRAMEPACKER_FRAMEPACKER_HPP
27 | #define FRAMEPACKER_FRAMEPACKER_HPP
28 |
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | namespace framepacker {
38 |
39 | struct match_path_separator {
40 | bool operator () (char ch) const {
41 | return ch == '\\' || ch == '/';
42 | }
43 | };
44 |
45 | std::string basename(const std::string &pathname) {
46 | return std::string(std::find_if(pathname.rbegin(), pathname.rend(), match_path_separator()).base(), pathname.end());
47 | }
48 |
49 | struct vec2 {
50 | vec2() : x(0), y(0) {
51 | }
52 | vec2(int _x, int _y) : x(_x), y(_y) {
53 | }
54 |
55 | vec2 operator + (const vec2 &o) const {
56 | return vec2(x + o.x, y + o.y);
57 | }
58 | vec2 operator - (const vec2 &o) const {
59 | return vec2(x - o.x, y - o.y);
60 | }
61 |
62 | bool is_zero(void) const {
63 | return x == 0 && y == 0;
64 | }
65 |
66 | int x;
67 | int y;
68 | };
69 |
70 | struct rect {
71 | rect() {
72 | }
73 | rect(int l, int t, int r, int b) {
74 | min = vec2(l, t);
75 | max = vec2(r, b);
76 | }
77 |
78 | int min_x(void) const {
79 | return min.x;
80 | }
81 | int min_y(void) const {
82 | return min.y;
83 | }
84 | int max_x(void) const {
85 | return max.x;
86 | }
87 | int max_y(void) const {
88 | return max.y;
89 | }
90 |
91 | void min_x(int v) {
92 | min.x = v;
93 | if (v > max.x)
94 | max.x = v;
95 | }
96 | void min_y(int v) {
97 | min.y = v;
98 | if (v > max.y)
99 | max.y = v;
100 | }
101 | void max_x(int v) {
102 | max.x = v;
103 | if (v < min.x)
104 | min.x = v;
105 | }
106 | void max_y(int v) {
107 | max.y = v;
108 | if (v < min.y)
109 | min.y = v;
110 | }
111 |
112 | int width(void) const {
113 | return max.x - min.x;
114 | }
115 | int height(void) const {
116 | return max.y - min.y;
117 | }
118 |
119 | void width(unsigned w) {
120 | max_x(min_x() + w);
121 | }
122 | void height(unsigned h) {
123 | max_y(min_y() + h);
124 | }
125 |
126 | int area(void) const {
127 | return width() * height();
128 | }
129 |
130 | vec2 left_top(void) const {
131 | return min;
132 | }
133 | vec2 left_bottom(void) const {
134 | return vec2(min.x, max.y);
135 | }
136 | vec2 right_top(void) const {
137 | return vec2(max.x, min.y);
138 | }
139 | vec2 right_bottom(void) const {
140 | return max;
141 | }
142 |
143 | vec2 size(void) const {
144 | return vec2(max.x - min.x + 1, max.y - min.y + 1);
145 | }
146 |
147 | rect add(int p) const {
148 | return rect(min.x + p, min.y + p, max.x + p, max.y + p);
149 | }
150 |
151 | vec2 min;
152 | vec2 max;
153 | };
154 |
155 | std::ostream &operator << (std::ostream &s, const vec2 &r) {
156 | s << "{" << r.x << "," << r.y << "}";
157 |
158 | return s;
159 | }
160 |
161 | std::ostream &operator << (std::ostream &s, const rect &r) {
162 | s << "{" << r.min << "," << r.size() << "}";
163 |
164 | return s;
165 | }
166 |
167 | struct node : public rect {
168 | typedef std::shared_ptr ptr_type;
169 |
170 | node() : used(false), down(nullptr), right(nullptr) {
171 | }
172 | node(int x, int y, int w, int h) : used(false), down(nullptr), right(nullptr) {
173 | min_x(x);
174 | min_y(y);
175 | width(w);
176 | height(h);
177 | }
178 | node(const node &o) : rect(o) {
179 | used = o.used;
180 | down = o.down;
181 | right = o.right;
182 | }
183 | ~node() {
184 | clear();
185 | }
186 |
187 | void clear(void) {
188 | if (down) {
189 | down->clear();
190 | down = nullptr;
191 | }
192 | if (right) {
193 | right->clear();
194 | right = nullptr;
195 | }
196 | }
197 |
198 | bool used;
199 | node::ptr_type down;
200 | node::ptr_type right;
201 | };
202 |
203 | template
204 | struct block {
205 | typedef T texture_type;
206 |
207 | block() : fit(nullptr), rotated(false) {
208 | }
209 | block(const texture_type &i, const char* p, bool alpha_trim) : fit(nullptr), rotated(false) {
210 | texture = i;
211 | calc_valid(alpha_trim);
212 | path = p;
213 | name = basename(p);
214 | }
215 | block(const block &o) {
216 | fit = o.fit;
217 | texture = o.texture;
218 | valid = o.valid;
219 | path = o.path;
220 | name = o.name;
221 | rotated = o.rotated;
222 | }
223 | ~block() {
224 | }
225 |
226 | block &operator = (const block &o) {
227 | fit = o.fit;
228 | texture = o.texture;
229 | valid = o.valid;
230 | path = o.path;
231 | name = o.name;
232 |
233 | return *this;
234 | }
235 |
236 | rect valid_area(void) const {
237 | if (!rotated)
238 | return valid;
239 |
240 | rect ret;
241 | ret.min = valid.min;
242 | ret.max = vec2(valid.max_y(), valid.max_x());
243 |
244 | return ret;
245 | }
246 |
247 | void calc_valid(bool alpha_trim) {
248 | if (alpha_trim) {
249 | int minx = std::numeric_limits::max();
250 | int miny = std::numeric_limits::max();
251 | int maxx = std::numeric_limits::min();
252 | int maxy = std::numeric_limits::min();
253 |
254 | bool found = false;
255 | for (int j = 0; j < int(texture->height()); j++) {
256 | for (int i = 0; i < int(texture->width()); i++) {
257 | if (!texture->is_transparent(i, j)) {
258 | miny = j;
259 | found = true;
260 |
261 | break;
262 | }
263 | }
264 | if (found)
265 | break;
266 | }
267 | found = false;
268 | for (int j = texture->height() - 1; j >= 0; j--) {
269 | for (int i = 0; i < int(texture->width()); i++) {
270 | if (!texture->is_transparent(i, j)) {
271 | maxy = j;
272 | found = true;
273 |
274 | break;
275 | }
276 | }
277 | if (found)
278 | break;
279 | }
280 | found = false;
281 | for (int i = 0; i < int(texture->width()); i++) {
282 | for (int j = miny; j <= maxy; j++) {
283 | if (!texture->is_transparent(i, j)) {
284 | minx = i;
285 | found = true;
286 |
287 | break;
288 | }
289 | }
290 | if (found)
291 | break;
292 | }
293 | found = false;
294 | for (int i = texture->width() - 1; i >= 0; i--) {
295 | for (int j = miny; j <= maxy; j++) {
296 | if (!texture->is_transparent(i, j)) {
297 | maxx = i;
298 | found = true;
299 |
300 | break;
301 | }
302 | }
303 | if (found)
304 | break;
305 | }
306 |
307 | valid.min_x(minx);
308 | valid.min_y(miny);
309 | valid.max_x(maxx);
310 | valid.max_y(maxy);
311 | } else {
312 | valid.min_x(0);
313 | valid.min_y(0);
314 | valid.max_x(texture->width() - 1);
315 | valid.max_y(texture->height() - 1);
316 | }
317 | }
318 |
319 | void clear(void) {
320 | if (fit)
321 | fit->clear();
322 | rotated = false;
323 | }
324 |
325 | void write_meta(std::ostream &s, int &indent, int padding) const {
326 | indent++;
327 | rect frame;
328 | vec2 loc = fit->min;
329 | frame.min = loc;
330 | frame.max = loc + valid.size() - vec2(1, 1);
331 | s << get_indent(indent) << "frame : " << "\"" << frame.add(padding) << "\"" << "," << std::endl;
332 | s << get_indent(indent) << "offset : " << "\"" << vec2() << "\"" << "," << std::endl;
333 | s << get_indent(indent) << "rotated : " << "false" << "," << std::endl;
334 | s << get_indent(indent) << "sourceColorRect : " << "\"" << valid << "\"" << "," << std::endl;
335 | s << get_indent(indent) << "sourceSize : " << "\"" << vec2(texture->width(), texture->height()) << "\"" << std::endl;
336 | indent--;
337 | }
338 |
339 | std::string get_indent(int indent) const {
340 | std::string ret;
341 | for (int i = 0; i < indent; i++)
342 | ret += "\t";
343 |
344 | return ret;
345 | }
346 |
347 | node::ptr_type fit;
348 | texture_type texture;
349 | rect valid;
350 | std::string path;
351 | std::string name;
352 | bool rotated;
353 | };
354 |
355 | template
356 | class packer {
357 | public:
358 | typedef I image_type;
359 | typedef std::shared_ptr texture_type;
360 | typedef block block_type;
361 | typedef std::string name_type;
362 | typedef std::pair texture_item_type;
363 | typedef std::list texture_coll_type;
364 | typedef bool(*texture_compare_type)(const texture_item_type &, const texture_item_type &);
365 |
366 | static bool compare_smart(const texture_item_type &l, const texture_item_type &r) {
367 | return l.second.valid.width() < r.second.valid.width();
368 | }
369 | static bool compare_rand(const texture_item_type &, const texture_item_type &) {
370 | return std::rand() % 100 < 50;
371 | }
372 | static bool compare_width(const texture_item_type &l, const texture_item_type &r) {
373 | return l.second.valid.width() < r.second.valid.width();
374 | }
375 | static bool compare_height(const texture_item_type &l, const texture_item_type &r) {
376 | return l.second.valid.height() < r.second.valid.height();
377 | }
378 | static bool compare_area(const texture_item_type &l, const texture_item_type &r) {
379 | return l.second.valid.area() < r.second.valid.area();
380 | }
381 |
382 | public:
383 | packer() : comparer(compare_smart), padding(1), allow_rotate(true), power_of_2(true), alpha_trim(true) {
384 | total_area = 0;
385 | indent = 0;
386 | }
387 | ~packer() {
388 | tidy();
389 | }
390 |
391 | const block_type* get(const name_type &name) const {
392 | for (auto it = images.begin(); it != images.end(); ++it) {
393 | const texture_item_type &i = *it;
394 | const block_type &blk = i.second;
395 | if (i.first == name)
396 | return &blk;
397 | }
398 |
399 | return nullptr;
400 | }
401 | bool add(const name_type &name, const texture_type &img) {
402 | if (get(name))
403 | return false;
404 |
405 | images.push_back(texture_item_type(name, block_type(img, name.c_str(), alpha_trim)));
406 | total_area += images.back().second.valid.area();
407 |
408 | return true;
409 | }
410 | bool remove(const name_type &name) {
411 | if (!get(name))
412 | return false;
413 |
414 | for (auto it = images.begin(); it != images.end(); ++it) {
415 | texture_item_type &i = *it;
416 | block_type &blk = i.second;
417 | if (i.first == name) {
418 | total_area -= blk.valid.area();
419 |
420 | images.erase(it);
421 |
422 | break;
423 | }
424 | }
425 |
426 | return true;
427 | }
428 | void clear(void) {
429 | tidy();
430 |
431 | images.clear();
432 |
433 | total_area = 0;
434 | }
435 |
436 | void tidy(void) {
437 | for (auto it = images.begin(); it != images.end(); ++it) {
438 | texture_item_type &i = *it;
439 | block_type &blk = i.second;
440 | blk.clear();
441 | }
442 | if (root)
443 | root->clear();
444 | }
445 |
446 | void write_meta(std::ostream &s, const texture_coll_type &p, const texture_type &t, const char* n) {
447 | s << "{" << std::endl;
448 | {
449 | indent++;
450 | s << get_indent() << "frames : {" << std::endl;
451 | {
452 | indent++;
453 | unsigned int i = 0;
454 | for (auto it = p.begin(); it != p.end(); ++it) {
455 | const block_type &blk = it->second;
456 | s << get_indent() << blk.name << " : {" << std::endl;
457 | blk.write_meta(s, indent, padding);
458 | if (i == p.size() - 1)
459 | s << get_indent() << "}" << std::endl;
460 | else
461 | s << get_indent() << "}," << std::endl;
462 | i++;
463 | }
464 | indent--;
465 | }
466 | s << get_indent() << "}," << std::endl;
467 | indent--;
468 | }
469 | {
470 | indent++;
471 | s << get_indent() << "metadata : {" << std::endl;
472 | {
473 | indent++;
474 | s << get_indent() << "textureName : " << n << "," << std::endl;
475 | s << get_indent() << "size : " << "\"" << vec2(t->width(), t->height()) << "\"" << std::endl;
476 | indent--;
477 | }
478 | s << get_indent() << "}" << std::endl;
479 | indent--;
480 | }
481 | s << "}" << std::endl;
482 | }
483 |
484 | void pack(texture_type &result, texture_coll_type &packed, texture_coll_type &failed) {
485 | // Step 1. Tidy.
486 | tidy();
487 |
488 | // Step 2. Sort.
489 | images.sort(comparer);
490 |
491 | // Step 3. Fit.
492 | if (output_texture_size.is_zero()) {
493 | if (sqrt_area) {
494 | int s = (int)(std::sqrt(total_area) + 0.5f);
495 | root = node::ptr_type(new node(0, 0, s, s));
496 | } else {
497 | int w = images.size() ? (images.begin()->second.valid.width() + (padding * 2)) : 0;
498 | int h = images.size() ? (images.begin()->second.valid.height() + (padding * 2)) : 0;
499 | root = node::ptr_type(new node(0, 0, w, h));
500 | }
501 | } else {
502 | root = node::ptr_type(new node(0, 0, output_texture_size.x, output_texture_size.y));
503 | }
504 | node::ptr_type nd = nullptr;
505 | for (auto it = images.begin(); it != images.end(); ++it) {
506 | texture_item_type &i = *it;
507 | block_type &blk = i.second;
508 | if ((nd = find(root, blk.valid.width() + (padding * 2), blk.valid.height() + (padding * 2))))
509 | blk.fit = split(nd, blk.valid.width() + (padding * 2), blk.valid.height() + (padding * 2));
510 | else
511 | blk.fit = grow(blk.valid.width() + (padding * 2), blk.valid.height() + (padding * 2));
512 |
513 | if (!blk.fit && allow_rotate) {
514 | blk.rotated = true;
515 | if ((nd = find(root, blk.valid_area().width() + (padding * 2), blk.valid_area().height() + (padding * 2))))
516 | blk.fit = split(nd, blk.valid_area().width() + (padding * 2), blk.valid_area().height() + (padding * 2));
517 | else
518 | blk.fit = grow(blk.valid_area().width() + (padding * 2), blk.valid_area().height() + (padding * 2));
519 | }
520 |
521 | if (blk.fit)
522 | packed.push_back(texture_item_type(i.first, block_type(blk)));
523 | else
524 | failed.push_back(texture_item_type(i.first, block_type(blk)));
525 | }
526 |
527 | // Step 4. Draw.
528 | vec2 os(root->width(), root->height());
529 | if (power_of_2) {
530 | os.x = (int)std::pow(2, std::ceil(std::log(os.x) / std::log(2)));
531 | os.y = (int)std::pow(2, std::ceil(std::log(os.y) / std::log(2)));
532 | }
533 | result->resize(os.x, os.y);
534 | for (auto it = packed.begin(); it != packed.end(); ++it) {
535 | texture_item_type &i = *it;
536 | block_type &blk = i.second;
537 | if (blk.rotated) {
538 | for (int j = 0; j < blk.valid.height(); j++) {
539 | for (int i = 0; i < blk.valid.width(); i++) {
540 | int x = i + blk.fit->min_x() + padding;
541 | int y = j + blk.fit->min_y() + padding;
542 | result->pixel(y, x, blk.texture->pixel(i + blk.valid.min_x(), j + blk.valid.min_y()));
543 | }
544 | }
545 | } else {
546 | if (per_pixel) {
547 | for (int j = 0; j < blk.valid.height(); j++) {
548 | for (int i = 0; i < blk.valid.width(); i++) {
549 | int x = i + blk.fit->min_x() + padding;
550 | int y = j + blk.fit->min_y() + padding;
551 | result->pixel(x, y, blk.texture->pixel(i + blk.valid.min_x(), j + blk.valid.min_y()));
552 | }
553 | }
554 | } else {
555 | result->copy_from(
556 | *blk.texture.get(),
557 | blk.valid.min_x(), blk.texture->height() - blk.valid.min_y() - blk.valid.height(),
558 | blk.valid.width(), blk.valid.height(),
559 | blk.fit->min_x() + padding, blk.fit->min_y() + padding
560 | );
561 | }
562 | }
563 | }
564 | }
565 |
566 | private:
567 | node::ptr_type find(node::ptr_type root, int w, int h) {
568 | if (root->used) {
569 | node::ptr_type r = find(root->right, w, h);
570 | if (!r) r = find(root->down, w, h);
571 |
572 | return r;
573 | } else if (w <= root->width() && h <= root->height()) {
574 | return root;
575 | } else {
576 | return nullptr;
577 | }
578 | }
579 |
580 | node::ptr_type split(node::ptr_type nd, int w, int h) {
581 | nd->used = true;
582 | nd->down = node::ptr_type(new node(nd->min_x(), nd->min_y() + h, nd->width(), nd->height() - h));
583 | nd->right = node::ptr_type(new node(nd->min_x() + w, nd->min_y(), nd->width() - w, h));
584 |
585 | return nd;
586 | }
587 |
588 | node::ptr_type grow(int w, int h) {
589 | bool can_grow_down = w <= root->width();
590 | bool can_grow_right = h <= root->height();
591 |
592 | bool should_grow_right = can_grow_right && (root->height() >= (root->width() + w));
593 | bool should_grow_down = can_grow_down && (root->width() >= (root->height() + h));
594 |
595 | if (should_grow_right)
596 | return grow_right(w, h);
597 | else if (should_grow_down)
598 | return grow_down(w, h);
599 | else if (can_grow_right)
600 | return grow_right(w, h);
601 | else if (can_grow_down)
602 | return grow_down(w, h);
603 | else
604 | return nullptr;
605 | }
606 |
607 | node::ptr_type grow_right(int w, int h) {
608 | node* r = new node(0, 0, root->width() + w, root->height());
609 | r->used = true;
610 | r->down = root;
611 | r->right = node::ptr_type(new node(root->width(), 0, w, root->height()));
612 | root = node::ptr_type(r);
613 |
614 | node::ptr_type node = nullptr;
615 | if ((node = find(root, w, h)))
616 | return split(node, w, h);
617 | else
618 | return nullptr;
619 | }
620 | node::ptr_type grow_down(int w, int h) {
621 | node* r = new node(0, 0, root->width(), root->height() + h);
622 | r->used = true;
623 | r->down = node::ptr_type(new node(0, root->height(), root->width(), h));
624 | r->right = root;
625 | root = node::ptr_type(r);
626 |
627 | node::ptr_type node = nullptr;
628 | if ((node = find(root, w, h)))
629 | return split(node, w, h);
630 | else
631 | return nullptr;
632 | }
633 |
634 | std::string get_indent(void) const {
635 | std::string ret;
636 | for (int i = 0; i < indent; i++)
637 | ret += "\t";
638 |
639 | return ret;
640 | }
641 |
642 | public:
643 | texture_compare_type comparer;
644 |
645 | int padding;
646 | bool allow_rotate;
647 | bool power_of_2;
648 | bool alpha_trim;
649 | vec2 output_texture_size;
650 |
651 | private:
652 | texture_coll_type images;
653 | node::ptr_type root;
654 | int total_area;
655 |
656 | int indent;
657 | };
658 |
659 | }
660 |
661 | #endif // FRAMEPACKER_FRAMEPACKER_HPP
662 |
--------------------------------------------------------------------------------
/third/stb_image_write.h:
--------------------------------------------------------------------------------
1 | /* stb_image_write - v1.14 - public domain - http://nothings.org/stb
2 | writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015
3 | no warranty implied; use at your own risk
4 |
5 | Before #including,
6 |
7 | #define STB_IMAGE_WRITE_IMPLEMENTATION
8 |
9 | in the file that you want to have the implementation.
10 |
11 | Will probably not work correctly with strict-aliasing optimizations.
12 |
13 | ABOUT:
14 |
15 | This header file is a library for writing images to C stdio or a callback.
16 |
17 | The PNG output is not optimal; it is 20-50% larger than the file
18 | written by a decent optimizing implementation; though providing a custom
19 | zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that.
20 | This library is designed for source code compactness and simplicity,
21 | not optimal image file size or run-time performance.
22 |
23 | BUILDING:
24 |
25 | You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.
26 | You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace
27 | malloc,realloc,free.
28 | You can #define STBIW_MEMMOVE() to replace memmove()
29 | You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function
30 | for PNG compression (instead of the builtin one), it must have the following signature:
31 | unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality);
32 | The returned data will be freed with STBIW_FREE() (free() by default),
33 | so it must be heap allocated with STBIW_MALLOC() (malloc() by default),
34 |
35 | UNICODE:
36 |
37 | If compiling for Windows and you wish to use Unicode filenames, compile
38 | with
39 | #define STBIW_WINDOWS_UTF8
40 | and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert
41 | Windows wchar_t filenames to utf8.
42 |
43 | USAGE:
44 |
45 | There are five functions, one for each image file format:
46 |
47 | int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
48 | int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
49 | int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
50 | int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality);
51 | int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
52 |
53 | void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically
54 |
55 | There are also five equivalent functions that use an arbitrary write function. You are
56 | expected to open/close your file-equivalent before and after calling these:
57 |
58 | int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
59 | int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
60 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
61 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
62 | int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);
63 |
64 | where the callback is:
65 | void stbi_write_func(void *context, void *data, int size);
66 |
67 | You can configure it with these global variables:
68 | int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE
69 | int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression
70 | int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode
71 |
72 |
73 | You can define STBI_WRITE_NO_STDIO to disable the file variant of these
74 | functions, so the library will not use stdio.h at all. However, this will
75 | also disable HDR writing, because it requires stdio for formatted output.
76 |
77 | Each function returns 0 on failure and non-0 on success.
78 |
79 | The functions create an image file defined by the parameters. The image
80 | is a rectangle of pixels stored from left-to-right, top-to-bottom.
81 | Each pixel contains 'comp' channels of data stored interleaved with 8-bits
82 | per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
83 | monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
84 | The *data pointer points to the first byte of the top-left-most pixel.
85 | For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
86 | a row of pixels to the first byte of the next row of pixels.
87 |
88 | PNG creates output files with the same number of components as the input.
89 | The BMP format expands Y to RGB in the file format and does not
90 | output alpha.
91 |
92 | PNG supports writing rectangles of data even when the bytes storing rows of
93 | data are not consecutive in memory (e.g. sub-rectangles of a larger image),
94 | by supplying the stride between the beginning of adjacent rows. The other
95 | formats do not. (Thus you cannot write a native-format BMP through the BMP
96 | writer, both because it is in BGR order and because it may have padding
97 | at the end of the line.)
98 |
99 | PNG allows you to set the deflate compression level by setting the global
100 | variable 'stbi_write_png_compression_level' (it defaults to 8).
101 |
102 | HDR expects linear float data. Since the format is always 32-bit rgb(e)
103 | data, alpha (if provided) is discarded, and for monochrome data it is
104 | replicated across all three channels.
105 |
106 | TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed
107 | data, set the global variable 'stbi_write_tga_with_rle' to 0.
108 |
109 | JPEG does ignore alpha channels in input data; quality is between 1 and 100.
110 | Higher quality looks better but results in a bigger image.
111 | JPEG baseline (no JPEG progressive).
112 |
113 | CREDITS:
114 |
115 |
116 | Sean Barrett - PNG/BMP/TGA
117 | Baldur Karlsson - HDR
118 | Jean-Sebastien Guay - TGA monochrome
119 | Tim Kelsey - misc enhancements
120 | Alan Hickman - TGA RLE
121 | Emmanuel Julien - initial file IO callback implementation
122 | Jon Olick - original jo_jpeg.cpp code
123 | Daniel Gibson - integrate JPEG, allow external zlib
124 | Aarni Koskela - allow choosing PNG filter
125 |
126 | bugfixes:
127 | github:Chribba
128 | Guillaume Chereau
129 | github:jry2
130 | github:romigrou
131 | Sergio Gonzalez
132 | Jonas Karlsson
133 | Filip Wasil
134 | Thatcher Ulrich
135 | github:poppolopoppo
136 | Patrick Boettcher
137 | github:xeekworx
138 | Cap Petschulat
139 | Simon Rodriguez
140 | Ivan Tikhonov
141 | github:ignotion
142 | Adam Schackart
143 |
144 | LICENSE
145 |
146 | See end of file for license information.
147 |
148 | */
149 |
150 | #ifndef INCLUDE_STB_IMAGE_WRITE_H
151 | #define INCLUDE_STB_IMAGE_WRITE_H
152 |
153 | #include
154 |
155 | // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline'
156 | #ifndef STBIWDEF
157 | #ifdef STB_IMAGE_WRITE_STATIC
158 | #define STBIWDEF static
159 | #else
160 | #ifdef __cplusplus
161 | #define STBIWDEF extern "C"
162 | #else
163 | #define STBIWDEF extern
164 | #endif
165 | #endif
166 | #endif
167 |
168 | #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations
169 | extern int stbi_write_tga_with_rle;
170 | extern int stbi_write_png_compression_level;
171 | extern int stbi_write_force_png_filter;
172 | #endif
173 |
174 | #ifndef STBI_WRITE_NO_STDIO
175 | STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
176 | STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
177 | STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
178 | STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
179 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality);
180 |
181 | #ifdef STBI_WINDOWS_UTF8
182 | STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
183 | #endif
184 | #endif
185 |
186 | typedef void stbi_write_func(void *context, void *data, int size);
187 |
188 | 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);
189 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
190 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
191 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
192 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);
193 |
194 | STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);
195 |
196 | #endif//INCLUDE_STB_IMAGE_WRITE_H
197 |
198 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION
199 |
200 | #ifdef _WIN32
201 | #ifndef _CRT_SECURE_NO_WARNINGS
202 | #define _CRT_SECURE_NO_WARNINGS
203 | #endif
204 | #ifndef _CRT_NONSTDC_NO_DEPRECATE
205 | #define _CRT_NONSTDC_NO_DEPRECATE
206 | #endif
207 | #endif
208 |
209 | #ifndef STBI_WRITE_NO_STDIO
210 | #include
211 | #endif // STBI_WRITE_NO_STDIO
212 |
213 | #include
214 | #include
215 | #include
216 | #include
217 |
218 | #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))
219 | // ok
220 | #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)
221 | // ok
222 | #else
223 | #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)."
224 | #endif
225 |
226 | #ifndef STBIW_MALLOC
227 | #define STBIW_MALLOC(sz) malloc(sz)
228 | #define STBIW_REALLOC(p,newsz) realloc(p,newsz)
229 | #define STBIW_FREE(p) free(p)
230 | #endif
231 |
232 | #ifndef STBIW_REALLOC_SIZED
233 | #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)
234 | #endif
235 |
236 |
237 | #ifndef STBIW_MEMMOVE
238 | #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)
239 | #endif
240 |
241 |
242 | #ifndef STBIW_ASSERT
243 | #include
244 | #define STBIW_ASSERT(x) assert(x)
245 | #endif
246 |
247 | #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)
248 |
249 | #ifdef STB_IMAGE_WRITE_STATIC
250 | static int stbi_write_png_compression_level = 8;
251 | static int stbi_write_tga_with_rle = 1;
252 | static int stbi_write_force_png_filter = -1;
253 | #else
254 | int stbi_write_png_compression_level = 8;
255 | int stbi_write_tga_with_rle = 1;
256 | int stbi_write_force_png_filter = -1;
257 | #endif
258 |
259 | static int stbi__flip_vertically_on_write = 0;
260 |
261 | STBIWDEF void stbi_flip_vertically_on_write(int flag)
262 | {
263 | stbi__flip_vertically_on_write = flag;
264 | }
265 |
266 | typedef struct
267 | {
268 | stbi_write_func *func;
269 | void *context;
270 | } stbi__write_context;
271 |
272 | // initialize a callback-based context
273 | static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context)
274 | {
275 | s->func = c;
276 | s->context = context;
277 | }
278 |
279 | #ifndef STBI_WRITE_NO_STDIO
280 |
281 | static void stbi__stdio_write(void *context, void *data, int size)
282 | {
283 | fwrite(data,1,size,(FILE*) context);
284 | }
285 |
286 | #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
287 | #ifdef __cplusplus
288 | #define STBIW_EXTERN extern "C"
289 | #else
290 | #define STBIW_EXTERN extern
291 | #endif
292 | STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
293 | STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
294 |
295 | STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
296 | {
297 | return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
298 | }
299 | #endif
300 |
301 | static FILE *stbiw__fopen(char const *filename, char const *mode)
302 | {
303 | FILE *f;
304 | #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
305 | wchar_t wMode[64];
306 | wchar_t wFilename[1024];
307 | if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
308 | return 0;
309 |
310 | if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
311 | return 0;
312 |
313 | #if _MSC_VER >= 1400
314 | if (0 != _wfopen_s(&f, wFilename, wMode))
315 | f = 0;
316 | #else
317 | f = _wfopen(wFilename, wMode);
318 | #endif
319 |
320 | #elif defined(_MSC_VER) && _MSC_VER >= 1400
321 | if (0 != fopen_s(&f, filename, mode))
322 | f=0;
323 | #else
324 | f = fopen(filename, mode);
325 | #endif
326 | return f;
327 | }
328 |
329 | static int stbi__start_write_file(stbi__write_context *s, const char *filename)
330 | {
331 | FILE *f = stbiw__fopen(filename, "wb");
332 | stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);
333 | return f != NULL;
334 | }
335 |
336 | static void stbi__end_write_file(stbi__write_context *s)
337 | {
338 | fclose((FILE *)s->context);
339 | }
340 |
341 | #endif // !STBI_WRITE_NO_STDIO
342 |
343 | typedef unsigned int stbiw_uint32;
344 | typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
345 |
346 | static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)
347 | {
348 | while (*fmt) {
349 | switch (*fmt++) {
350 | case ' ': break;
351 | case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));
352 | s->func(s->context,&x,1);
353 | break; }
354 | case '2': { int x = va_arg(v,int);
355 | unsigned char b[2];
356 | b[0] = STBIW_UCHAR(x);
357 | b[1] = STBIW_UCHAR(x>>8);
358 | s->func(s->context,b,2);
359 | break; }
360 | case '4': { stbiw_uint32 x = va_arg(v,int);
361 | unsigned char b[4];
362 | b[0]=STBIW_UCHAR(x);
363 | b[1]=STBIW_UCHAR(x>>8);
364 | b[2]=STBIW_UCHAR(x>>16);
365 | b[3]=STBIW_UCHAR(x>>24);
366 | s->func(s->context,b,4);
367 | break; }
368 | default:
369 | STBIW_ASSERT(0);
370 | return;
371 | }
372 | }
373 | }
374 |
375 | static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
376 | {
377 | va_list v;
378 | va_start(v, fmt);
379 | stbiw__writefv(s, fmt, v);
380 | va_end(v);
381 | }
382 |
383 | static void stbiw__putc(stbi__write_context *s, unsigned char c)
384 | {
385 | s->func(s->context, &c, 1);
386 | }
387 |
388 | static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)
389 | {
390 | unsigned char arr[3];
391 | arr[0] = a; arr[1] = b; arr[2] = c;
392 | s->func(s->context, arr, 3);
393 | }
394 |
395 | static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)
396 | {
397 | unsigned char bg[3] = { 255, 0, 255}, px[3];
398 | int k;
399 |
400 | if (write_alpha < 0)
401 | s->func(s->context, &d[comp - 1], 1);
402 |
403 | switch (comp) {
404 | case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case
405 | case 1:
406 | if (expand_mono)
407 | stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
408 | else
409 | s->func(s->context, d, 1); // monochrome TGA
410 | break;
411 | case 4:
412 | if (!write_alpha) {
413 | // composite against pink background
414 | for (k = 0; k < 3; ++k)
415 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
416 | stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
417 | break;
418 | }
419 | /* FALLTHROUGH */
420 | case 3:
421 | stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
422 | break;
423 | }
424 | if (write_alpha > 0)
425 | s->func(s->context, &d[comp - 1], 1);
426 | }
427 |
428 | 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)
429 | {
430 | stbiw_uint32 zero = 0;
431 | int i,j, j_end;
432 |
433 | if (y <= 0)
434 | return;
435 |
436 | if (stbi__flip_vertically_on_write)
437 | vdir *= -1;
438 |
439 | if (vdir < 0) {
440 | j_end = -1; j = y-1;
441 | } else {
442 | j_end = y; j = 0;
443 | }
444 |
445 | for (; j != j_end; j += vdir) {
446 | for (i=0; i < x; ++i) {
447 | unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
448 | stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
449 | }
450 | s->func(s->context, &zero, scanline_pad);
451 | }
452 | }
453 |
454 | 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, ...)
455 | {
456 | if (y < 0 || x < 0) {
457 | return 0;
458 | } else {
459 | va_list v;
460 | va_start(v, fmt);
461 | stbiw__writefv(s, fmt, v);
462 | va_end(v);
463 | stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);
464 | return 1;
465 | }
466 | }
467 |
468 | static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)
469 | {
470 | int pad = (-x*3) & 3;
471 | return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,
472 | "11 4 22 4" "4 44 22 444444",
473 | 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
474 | 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
475 | }
476 |
477 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
478 | {
479 | stbi__write_context s;
480 | stbi__start_write_callbacks(&s, func, context);
481 | return stbi_write_bmp_core(&s, x, y, comp, data);
482 | }
483 |
484 | #ifndef STBI_WRITE_NO_STDIO
485 | STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
486 | {
487 | stbi__write_context s;
488 | if (stbi__start_write_file(&s,filename)) {
489 | int r = stbi_write_bmp_core(&s, x, y, comp, data);
490 | stbi__end_write_file(&s);
491 | return r;
492 | } else
493 | return 0;
494 | }
495 | #endif //!STBI_WRITE_NO_STDIO
496 |
497 | static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data)
498 | {
499 | int has_alpha = (comp == 2 || comp == 4);
500 | int colorbytes = has_alpha ? comp-1 : comp;
501 | int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
502 |
503 | if (y < 0 || x < 0)
504 | return 0;
505 |
506 | if (!stbi_write_tga_with_rle) {
507 | return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,
508 | "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
509 | } else {
510 | int i,j,k;
511 | int jend, jdir;
512 |
513 | 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);
514 |
515 | if (stbi__flip_vertically_on_write) {
516 | j = 0;
517 | jend = y;
518 | jdir = 1;
519 | } else {
520 | j = y-1;
521 | jend = -1;
522 | jdir = -1;
523 | }
524 | for (; j != jend; j += jdir) {
525 | unsigned char *row = (unsigned char *) data + j * x * comp;
526 | int len;
527 |
528 | for (i = 0; i < x; i += len) {
529 | unsigned char *begin = row + i * comp;
530 | int diff = 1;
531 | len = 1;
532 |
533 | if (i < x - 1) {
534 | ++len;
535 | diff = memcmp(begin, row + (i + 1) * comp, comp);
536 | if (diff) {
537 | const unsigned char *prev = begin;
538 | for (k = i + 2; k < x && len < 128; ++k) {
539 | if (memcmp(prev, row + k * comp, comp)) {
540 | prev += comp;
541 | ++len;
542 | } else {
543 | --len;
544 | break;
545 | }
546 | }
547 | } else {
548 | for (k = i + 2; k < x && len < 128; ++k) {
549 | if (!memcmp(begin, row + k * comp, comp)) {
550 | ++len;
551 | } else {
552 | break;
553 | }
554 | }
555 | }
556 | }
557 |
558 | if (diff) {
559 | unsigned char header = STBIW_UCHAR(len - 1);
560 | s->func(s->context, &header, 1);
561 | for (k = 0; k < len; ++k) {
562 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
563 | }
564 | } else {
565 | unsigned char header = STBIW_UCHAR(len - 129);
566 | s->func(s->context, &header, 1);
567 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
568 | }
569 | }
570 | }
571 | }
572 | return 1;
573 | }
574 |
575 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
576 | {
577 | stbi__write_context s;
578 | stbi__start_write_callbacks(&s, func, context);
579 | return stbi_write_tga_core(&s, x, y, comp, (void *) data);
580 | }
581 |
582 | #ifndef STBI_WRITE_NO_STDIO
583 | STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
584 | {
585 | stbi__write_context s;
586 | if (stbi__start_write_file(&s,filename)) {
587 | int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);
588 | stbi__end_write_file(&s);
589 | return r;
590 | } else
591 | return 0;
592 | }
593 | #endif
594 |
595 | // *************************************************************************************************
596 | // Radiance RGBE HDR writer
597 | // by Baldur Karlsson
598 |
599 | #define stbiw__max(a, b) ((a) > (b) ? (a) : (b))
600 |
601 | static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
602 | {
603 | int exponent;
604 | float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));
605 |
606 | if (maxcomp < 1e-32f) {
607 | rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
608 | } else {
609 | float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;
610 |
611 | rgbe[0] = (unsigned char)(linear[0] * normalize);
612 | rgbe[1] = (unsigned char)(linear[1] * normalize);
613 | rgbe[2] = (unsigned char)(linear[2] * normalize);
614 | rgbe[3] = (unsigned char)(exponent + 128);
615 | }
616 | }
617 |
618 | static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)
619 | {
620 | unsigned char lengthbyte = STBIW_UCHAR(length+128);
621 | STBIW_ASSERT(length+128 <= 255);
622 | s->func(s->context, &lengthbyte, 1);
623 | s->func(s->context, &databyte, 1);
624 | }
625 |
626 | static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)
627 | {
628 | unsigned char lengthbyte = STBIW_UCHAR(length);
629 | STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
630 | s->func(s->context, &lengthbyte, 1);
631 | s->func(s->context, data, length);
632 | }
633 |
634 | static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)
635 | {
636 | unsigned char scanlineheader[4] = { 2, 2, 0, 0 };
637 | unsigned char rgbe[4];
638 | float linear[3];
639 | int x;
640 |
641 | scanlineheader[2] = (width&0xff00)>>8;
642 | scanlineheader[3] = (width&0x00ff);
643 |
644 | /* skip RLE for images too small or large */
645 | if (width < 8 || width >= 32768) {
646 | for (x=0; x < width; x++) {
647 | switch (ncomp) {
648 | case 4: /* fallthrough */
649 | case 3: linear[2] = scanline[x*ncomp + 2];
650 | linear[1] = scanline[x*ncomp + 1];
651 | linear[0] = scanline[x*ncomp + 0];
652 | break;
653 | default:
654 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
655 | break;
656 | }
657 | stbiw__linear_to_rgbe(rgbe, linear);
658 | s->func(s->context, rgbe, 4);
659 | }
660 | } else {
661 | int c,r;
662 | /* encode into scratch buffer */
663 | for (x=0; x < width; x++) {
664 | switch(ncomp) {
665 | case 4: /* fallthrough */
666 | case 3: linear[2] = scanline[x*ncomp + 2];
667 | linear[1] = scanline[x*ncomp + 1];
668 | linear[0] = scanline[x*ncomp + 0];
669 | break;
670 | default:
671 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
672 | break;
673 | }
674 | stbiw__linear_to_rgbe(rgbe, linear);
675 | scratch[x + width*0] = rgbe[0];
676 | scratch[x + width*1] = rgbe[1];
677 | scratch[x + width*2] = rgbe[2];
678 | scratch[x + width*3] = rgbe[3];
679 | }
680 |
681 | s->func(s->context, scanlineheader, 4);
682 |
683 | /* RLE each component separately */
684 | for (c=0; c < 4; c++) {
685 | unsigned char *comp = &scratch[width*c];
686 |
687 | x = 0;
688 | while (x < width) {
689 | // find first run
690 | r = x;
691 | while (r+2 < width) {
692 | if (comp[r] == comp[r+1] && comp[r] == comp[r+2])
693 | break;
694 | ++r;
695 | }
696 | if (r+2 >= width)
697 | r = width;
698 | // dump up to first run
699 | while (x < r) {
700 | int len = r-x;
701 | if (len > 128) len = 128;
702 | stbiw__write_dump_data(s, len, &comp[x]);
703 | x += len;
704 | }
705 | // if there's a run, output it
706 | if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd
707 | // find next byte after run
708 | while (r < width && comp[r] == comp[x])
709 | ++r;
710 | // output run up to r
711 | while (x < r) {
712 | int len = r-x;
713 | if (len > 127) len = 127;
714 | stbiw__write_run_data(s, len, comp[x]);
715 | x += len;
716 | }
717 | }
718 | }
719 | }
720 | }
721 | }
722 |
723 | static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)
724 | {
725 | if (y <= 0 || x <= 0 || data == NULL)
726 | return 0;
727 | else {
728 | // Each component is stored separately. Allocate scratch space for full output scanline.
729 | unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
730 | int i, len;
731 | char buffer[128];
732 | char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
733 | s->func(s->context, header, sizeof(header)-1);
734 |
735 | #ifdef __STDC_WANT_SECURE_LIB__
736 | len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
737 | #else
738 | len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
739 | #endif
740 | s->func(s->context, buffer, len);
741 |
742 | for(i=0; i < y; i++)
743 | stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i));
744 | STBIW_FREE(scratch);
745 | return 1;
746 | }
747 | }
748 |
749 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)
750 | {
751 | stbi__write_context s;
752 | stbi__start_write_callbacks(&s, func, context);
753 | return stbi_write_hdr_core(&s, x, y, comp, (float *) data);
754 | }
755 |
756 | #ifndef STBI_WRITE_NO_STDIO
757 | STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
758 | {
759 | stbi__write_context s;
760 | if (stbi__start_write_file(&s,filename)) {
761 | int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);
762 | stbi__end_write_file(&s);
763 | return r;
764 | } else
765 | return 0;
766 | }
767 | #endif // STBI_WRITE_NO_STDIO
768 |
769 |
770 | //////////////////////////////////////////////////////////////////////////////
771 | //
772 | // PNG writer
773 | //
774 |
775 | #ifndef STBIW_ZLIB_COMPRESS
776 | // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
777 | #define stbiw__sbraw(a) ((int *) (void *) (a) - 2)
778 | #define stbiw__sbm(a) stbiw__sbraw(a)[0]
779 | #define stbiw__sbn(a) stbiw__sbraw(a)[1]
780 |
781 | #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a))
782 | #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0)
783 | #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a)))
784 |
785 | #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v))
786 | #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0)
787 | #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0)
788 |
789 | static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
790 | {
791 | int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;
792 | void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);
793 | STBIW_ASSERT(p);
794 | if (p) {
795 | if (!*arr) ((int *) p)[1] = 0;
796 | *arr = (void *) ((int *) p + 2);
797 | stbiw__sbm(*arr) = m;
798 | }
799 | return *arr;
800 | }
801 |
802 | static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
803 | {
804 | while (*bitcount >= 8) {
805 | stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));
806 | *bitbuffer >>= 8;
807 | *bitcount -= 8;
808 | }
809 | return data;
810 | }
811 |
812 | static int stbiw__zlib_bitrev(int code, int codebits)
813 | {
814 | int res=0;
815 | while (codebits--) {
816 | res = (res << 1) | (code & 1);
817 | code >>= 1;
818 | }
819 | return res;
820 | }
821 |
822 | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit)
823 | {
824 | int i;
825 | for (i=0; i < limit && i < 258; ++i)
826 | if (a[i] != b[i]) break;
827 | return i;
828 | }
829 |
830 | static unsigned int stbiw__zhash(unsigned char *data)
831 | {
832 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
833 | hash ^= hash << 3;
834 | hash += hash >> 5;
835 | hash ^= hash << 4;
836 | hash += hash >> 17;
837 | hash ^= hash << 25;
838 | hash += hash >> 6;
839 | return hash;
840 | }
841 |
842 | #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))
843 | #define stbiw__zlib_add(code,codebits) \
844 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())
845 | #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c)
846 | // default huffman tables
847 | #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8)
848 | #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9)
849 | #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7)
850 | #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8)
851 | #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))
852 | #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n))
853 |
854 | #define stbiw__ZHASH 16384
855 |
856 | #endif // STBIW_ZLIB_COMPRESS
857 |
858 | STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
859 | {
860 | #ifdef STBIW_ZLIB_COMPRESS
861 | // user provided a zlib compress implementation, use that
862 | return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality);
863 | #else // use builtin
864 | 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 };
865 | 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 };
866 | 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 };
867 | 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 };
868 | unsigned int bitbuf=0;
869 | int i,j, bitcount=0;
870 | unsigned char *out = NULL;
871 | unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**));
872 | if (hash_table == NULL)
873 | return NULL;
874 | if (quality < 5) quality = 5;
875 |
876 | stbiw__sbpush(out, 0x78); // DEFLATE 32K window
877 | stbiw__sbpush(out, 0x5e); // FLEVEL = 1
878 | stbiw__zlib_add(1,1); // BFINAL = 1
879 | stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman
880 |
881 | for (i=0; i < stbiw__ZHASH; ++i)
882 | hash_table[i] = NULL;
883 |
884 | i=0;
885 | while (i < data_len-3) {
886 | // hash next 3 bytes of data to be compressed
887 | int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;
888 | unsigned char *bestloc = 0;
889 | unsigned char **hlist = hash_table[h];
890 | int n = stbiw__sbcount(hlist);
891 | for (j=0; j < n; ++j) {
892 | if (hlist[j]-data > i-32768) { // if entry lies within window
893 | int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);
894 | if (d >= best) { best=d; bestloc=hlist[j]; }
895 | }
896 | }
897 | // when hash table entry is too long, delete half the entries
898 | if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {
899 | STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
900 | stbiw__sbn(hash_table[h]) = quality;
901 | }
902 | stbiw__sbpush(hash_table[h],data+i);
903 |
904 | if (bestloc) {
905 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
906 | h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1);
907 | hlist = hash_table[h];
908 | n = stbiw__sbcount(hlist);
909 | for (j=0; j < n; ++j) {
910 | if (hlist[j]-data > i-32767) {
911 | int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1);
912 | if (e > best) { // if next match is better, bail on current match
913 | bestloc = NULL;
914 | break;
915 | }
916 | }
917 | }
918 | }
919 |
920 | if (bestloc) {
921 | int d = (int) (data+i - bestloc); // distance back
922 | STBIW_ASSERT(d <= 32767 && best <= 258);
923 | for (j=0; best > lengthc[j+1]-1; ++j);
924 | stbiw__zlib_huff(j+257);
925 | if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);
926 | for (j=0; d > distc[j+1]-1; ++j);
927 | stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5);
928 | if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]);
929 | i += best;
930 | } else {
931 | stbiw__zlib_huffb(data[i]);
932 | ++i;
933 | }
934 | }
935 | // write out final bytes
936 | for (;i < data_len; ++i)
937 | stbiw__zlib_huffb(data[i]);
938 | stbiw__zlib_huff(256); // end of block
939 | // pad with 0 bits to byte boundary
940 | while (bitcount)
941 | stbiw__zlib_add(0,1);
942 |
943 | for (i=0; i < stbiw__ZHASH; ++i)
944 | (void) stbiw__sbfree(hash_table[i]);
945 | STBIW_FREE(hash_table);
946 |
947 | {
948 | // compute adler32 on input
949 | unsigned int s1=1, s2=0;
950 | int blocklen = (int) (data_len % 5552);
951 | j=0;
952 | while (j < data_len) {
953 | for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; }
954 | s1 %= 65521; s2 %= 65521;
955 | j += blocklen;
956 | blocklen = 5552;
957 | }
958 | stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));
959 | stbiw__sbpush(out, STBIW_UCHAR(s2));
960 | stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));
961 | stbiw__sbpush(out, STBIW_UCHAR(s1));
962 | }
963 | *out_len = stbiw__sbn(out);
964 | // make returned pointer freeable
965 | STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);
966 | return (unsigned char *) stbiw__sbraw(out);
967 | #endif // STBIW_ZLIB_COMPRESS
968 | }
969 |
970 | static unsigned int stbiw__crc32(unsigned char *buffer, int len)
971 | {
972 | #ifdef STBIW_CRC32
973 | return STBIW_CRC32(buffer, len);
974 | #else
975 | static unsigned int crc_table[256] =
976 | {
977 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
978 | 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
979 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
980 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
981 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
982 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
983 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
984 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
985 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
986 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
987 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
988 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
989 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
990 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
991 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
992 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
993 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
994 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
995 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
996 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
997 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
998 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
999 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
1000 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
1001 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
1002 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
1003 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
1004 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
1005 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
1006 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
1007 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
1008 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
1009 | };
1010 |
1011 | unsigned int crc = ~0u;
1012 | int i;
1013 | for (i=0; i < len; ++i)
1014 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
1015 | return ~crc;
1016 | #endif
1017 | }
1018 |
1019 | #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)
1020 | #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
1021 | #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])
1022 |
1023 | static void stbiw__wpcrc(unsigned char **data, int len)
1024 | {
1025 | unsigned int crc = stbiw__crc32(*data - len - 4, len+4);
1026 | stbiw__wp32(*data, crc);
1027 | }
1028 |
1029 | static unsigned char stbiw__paeth(int a, int b, int c)
1030 | {
1031 | int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
1032 | if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);
1033 | if (pb <= pc) return STBIW_UCHAR(b);
1034 | return STBIW_UCHAR(c);
1035 | }
1036 |
1037 | // @OPTIMIZE: provide an option that always forces left-predict or paeth predict
1038 | static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer)
1039 | {
1040 | static int mapping[] = { 0,1,2,3,4 };
1041 | static int firstmap[] = { 0,1,0,5,6 };
1042 | int *mymap = (y != 0) ? mapping : firstmap;
1043 | int i;
1044 | int type = mymap[filter_type];
1045 | unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y);
1046 | int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;
1047 |
1048 | if (type==0) {
1049 | memcpy(line_buffer, z, width*n);
1050 | return;
1051 | }
1052 |
1053 | // first loop isn't optimized since it's just one pixel
1054 | for (i = 0; i < n; ++i) {
1055 | switch (type) {
1056 | case 1: line_buffer[i] = z[i]; break;
1057 | case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;
1058 | case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break;
1059 | case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break;
1060 | case 5: line_buffer[i] = z[i]; break;
1061 | case 6: line_buffer[i] = z[i]; break;
1062 | }
1063 | }
1064 | switch (type) {
1065 | case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break;
1066 | case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break;
1067 | case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break;
1068 | case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break;
1069 | case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break;
1070 | case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;
1071 | }
1072 | }
1073 |
1074 | STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
1075 | {
1076 | int force_filter = stbi_write_force_png_filter;
1077 | int ctype[5] = { -1, 0, 4, 2, 6 };
1078 | unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
1079 | unsigned char *out,*o, *filt, *zlib;
1080 | signed char *line_buffer;
1081 | int j,zlen;
1082 |
1083 | if (stride_bytes == 0)
1084 | stride_bytes = x * n;
1085 |
1086 | if (force_filter >= 5) {
1087 | force_filter = -1;
1088 | }
1089 |
1090 | filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;
1091 | line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }
1092 | for (j=0; j < y; ++j) {
1093 | int filter_type;
1094 | if (force_filter > -1) {
1095 | filter_type = force_filter;
1096 | stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer);
1097 | } else { // Estimate the best filter by running through all of them:
1098 | int best_filter = 0, best_filter_val = 0x7fffffff, est, i;
1099 | for (filter_type = 0; filter_type < 5; filter_type++) {
1100 | stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer);
1101 |
1102 | // Estimate the entropy of the line using this filter; the less, the better.
1103 | est = 0;
1104 | for (i = 0; i < x*n; ++i) {
1105 | est += abs((signed char) line_buffer[i]);
1106 | }
1107 | if (est < best_filter_val) {
1108 | best_filter_val = est;
1109 | best_filter = filter_type;
1110 | }
1111 | }
1112 | if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it
1113 | stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer);
1114 | filter_type = best_filter;
1115 | }
1116 | }
1117 | // when we get here, filter_type contains the filter type, and line_buffer contains the data
1118 | filt[j*(x*n+1)] = (unsigned char) filter_type;
1119 | STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);
1120 | }
1121 | STBIW_FREE(line_buffer);
1122 | zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level);
1123 | STBIW_FREE(filt);
1124 | if (!zlib) return 0;
1125 |
1126 | // each tag requires 12 bytes of overhead
1127 | out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);
1128 | if (!out) return 0;
1129 | *out_len = 8 + 12+13 + 12+zlen + 12;
1130 |
1131 | o=out;
1132 | STBIW_MEMMOVE(o,sig,8); o+= 8;
1133 | stbiw__wp32(o, 13); // header length
1134 | stbiw__wptag(o, "IHDR");
1135 | stbiw__wp32(o, x);
1136 | stbiw__wp32(o, y);
1137 | *o++ = 8;
1138 | *o++ = STBIW_UCHAR(ctype[n]);
1139 | *o++ = 0;
1140 | *o++ = 0;
1141 | *o++ = 0;
1142 | stbiw__wpcrc(&o,13);
1143 |
1144 | stbiw__wp32(o, zlen);
1145 | stbiw__wptag(o, "IDAT");
1146 | STBIW_MEMMOVE(o, zlib, zlen);
1147 | o += zlen;
1148 | STBIW_FREE(zlib);
1149 | stbiw__wpcrc(&o, zlen);
1150 |
1151 | stbiw__wp32(o,0);
1152 | stbiw__wptag(o, "IEND");
1153 | stbiw__wpcrc(&o,0);
1154 |
1155 | STBIW_ASSERT(o == out + *out_len);
1156 |
1157 | return out;
1158 | }
1159 |
1160 | #ifndef STBI_WRITE_NO_STDIO
1161 | STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
1162 | {
1163 | FILE *f;
1164 | int len;
1165 | unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);
1166 | if (png == NULL) return 0;
1167 |
1168 | f = stbiw__fopen(filename, "wb");
1169 | if (!f) { STBIW_FREE(png); return 0; }
1170 | fwrite(png, 1, len, f);
1171 | fclose(f);
1172 | STBIW_FREE(png);
1173 | return 1;
1174 | }
1175 | #endif
1176 |
1177 | 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)
1178 | {
1179 | int len;
1180 | unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);
1181 | if (png == NULL) return 0;
1182 | func(context, png, len);
1183 | STBIW_FREE(png);
1184 | return 1;
1185 | }
1186 |
1187 |
1188 | /* ***************************************************************************
1189 | *
1190 | * JPEG writer
1191 | *
1192 | * This is based on Jon Olick's jo_jpeg.cpp:
1193 | * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html
1194 | */
1195 |
1196 | 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,
1197 | 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 };
1198 |
1199 | static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) {
1200 | int bitBuf = *bitBufP, bitCnt = *bitCntP;
1201 | bitCnt += bs[1];
1202 | bitBuf |= bs[0] << (24 - bitCnt);
1203 | while(bitCnt >= 8) {
1204 | unsigned char c = (bitBuf >> 16) & 255;
1205 | stbiw__putc(s, c);
1206 | if(c == 255) {
1207 | stbiw__putc(s, 0);
1208 | }
1209 | bitBuf <<= 8;
1210 | bitCnt -= 8;
1211 | }
1212 | *bitBufP = bitBuf;
1213 | *bitCntP = bitCnt;
1214 | }
1215 |
1216 | static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) {
1217 | float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p;
1218 | float z1, z2, z3, z4, z5, z11, z13;
1219 |
1220 | float tmp0 = d0 + d7;
1221 | float tmp7 = d0 - d7;
1222 | float tmp1 = d1 + d6;
1223 | float tmp6 = d1 - d6;
1224 | float tmp2 = d2 + d5;
1225 | float tmp5 = d2 - d5;
1226 | float tmp3 = d3 + d4;
1227 | float tmp4 = d3 - d4;
1228 |
1229 | // Even part
1230 | float tmp10 = tmp0 + tmp3; // phase 2
1231 | float tmp13 = tmp0 - tmp3;
1232 | float tmp11 = tmp1 + tmp2;
1233 | float tmp12 = tmp1 - tmp2;
1234 |
1235 | d0 = tmp10 + tmp11; // phase 3
1236 | d4 = tmp10 - tmp11;
1237 |
1238 | z1 = (tmp12 + tmp13) * 0.707106781f; // c4
1239 | d2 = tmp13 + z1; // phase 5
1240 | d6 = tmp13 - z1;
1241 |
1242 | // Odd part
1243 | tmp10 = tmp4 + tmp5; // phase 2
1244 | tmp11 = tmp5 + tmp6;
1245 | tmp12 = tmp6 + tmp7;
1246 |
1247 | // The rotator is modified from fig 4-8 to avoid extra negations.
1248 | z5 = (tmp10 - tmp12) * 0.382683433f; // c6
1249 | z2 = tmp10 * 0.541196100f + z5; // c2-c6
1250 | z4 = tmp12 * 1.306562965f + z5; // c2+c6
1251 | z3 = tmp11 * 0.707106781f; // c4
1252 |
1253 | z11 = tmp7 + z3; // phase 5
1254 | z13 = tmp7 - z3;
1255 |
1256 | *d5p = z13 + z2; // phase 6
1257 | *d3p = z13 - z2;
1258 | *d1p = z11 + z4;
1259 | *d7p = z11 - z4;
1260 |
1261 | *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6;
1262 | }
1263 |
1264 | static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {
1265 | int tmp1 = val < 0 ? -val : val;
1266 | val = val < 0 ? val-1 : val;
1267 | bits[1] = 1;
1268 | while(tmp1 >>= 1) {
1269 | ++bits[1];
1270 | }
1271 | bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) {
1314 | }
1315 | // end0pos = first element in reverse order !=0
1316 | if(end0pos == 0) {
1317 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
1318 | return DU[0];
1319 | }
1320 | for(i = 1; i <= end0pos; ++i) {
1321 | int startpos = i;
1322 | int nrzeroes;
1323 | unsigned short bits[2];
1324 | for (; DU[i]==0 && i<=end0pos; ++i) {
1325 | }
1326 | nrzeroes = i-startpos;
1327 | if ( nrzeroes >= 16 ) {
1328 | int lng = nrzeroes>>4;
1329 | int nrmarker;
1330 | for (nrmarker=1; nrmarker <= lng; ++nrmarker)
1331 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);
1332 | nrzeroes &= 15;
1333 | }
1334 | stbiw__jpg_calcBits(DU[i], bits);
1335 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]);
1336 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
1337 | }
1338 | if(end0pos != 63) {
1339 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
1340 | }
1341 | return DU[0];
1342 | }
1343 |
1344 | static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) {
1345 | // Constants that don't pollute global namespace
1346 | 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};
1347 | static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
1348 | 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};
1349 | static const unsigned char std_ac_luminance_values[] = {
1350 | 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
1351 | 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
1352 | 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
1353 | 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
1354 | 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
1355 | 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
1356 | 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
1357 | };
1358 | 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};
1359 | static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
1360 | 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};
1361 | static const unsigned char std_ac_chrominance_values[] = {
1362 | 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
1363 | 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
1364 | 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
1365 | 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
1366 | 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
1367 | 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
1368 | 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
1369 | };
1370 | // Huffman tables
1371 | 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}};
1372 | 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}};
1373 | static const unsigned short YAC_HT[256][2] = {
1374 | {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},
1375 | {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},
1376 | {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},
1377 | {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},
1378 | {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},
1379 | {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},
1380 | {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},
1381 | {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},
1382 | {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},
1383 | {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},
1384 | {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},
1385 | {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},
1386 | {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},
1387 | {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},
1388 | {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},
1389 | {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}
1390 | };
1391 | static const unsigned short UVAC_HT[256][2] = {
1392 | {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},
1393 | {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},
1394 | {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},
1395 | {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},
1396 | {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},
1397 | {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},
1398 | {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},
1399 | {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},
1400 | {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},
1401 | {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},
1402 | {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},
1403 | {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},
1404 | {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},
1405 | {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},
1406 | {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},
1407 | {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}
1408 | };
1409 | 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,
1410 | 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};
1411 | 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,
1412 | 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};
1413 | static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
1414 | 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };
1415 |
1416 | int row, col, i, k, subsample;
1417 | float fdtbl_Y[64], fdtbl_UV[64];
1418 | unsigned char YTable[64], UVTable[64];
1419 |
1420 | if(!data || !width || !height || comp > 4 || comp < 1) {
1421 | return 0;
1422 | }
1423 |
1424 | quality = quality ? quality : 90;
1425 | subsample = quality <= 90 ? 1 : 0;
1426 | quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;
1427 | quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
1428 |
1429 | for(i = 0; i < 64; ++i) {
1430 | int uvti, yti = (YQT[i]*quality+50)/100;
1431 | YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti);
1432 | uvti = (UVQT[i]*quality+50)/100;
1433 | UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);
1434 | }
1435 |
1436 | for(row = 0, k = 0; row < 8; ++row) {
1437 | for(col = 0; col < 8; ++col, ++k) {
1438 | fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
1439 | fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
1440 | }
1441 | }
1442 |
1443 | // Write Headers
1444 | {
1445 | 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 };
1446 | static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };
1447 | const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width),
1448 | 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };
1449 | s->func(s->context, (void*)head0, sizeof(head0));
1450 | s->func(s->context, (void*)YTable, sizeof(YTable));
1451 | stbiw__putc(s, 1);
1452 | s->func(s->context, UVTable, sizeof(UVTable));
1453 | s->func(s->context, (void*)head1, sizeof(head1));
1454 | s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1);
1455 | s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));
1456 | stbiw__putc(s, 0x10); // HTYACinfo
1457 | s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1);
1458 | s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));
1459 | stbiw__putc(s, 1); // HTUDCinfo
1460 | s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1);
1461 | s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));
1462 | stbiw__putc(s, 0x11); // HTUACinfo
1463 | s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1);
1464 | s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));
1465 | s->func(s->context, (void*)head2, sizeof(head2));
1466 | }
1467 |
1468 | // Encode 8x8 macroblocks
1469 | {
1470 | static const unsigned short fillBits[] = {0x7F, 7};
1471 | int DCY=0, DCU=0, DCV=0;
1472 | int bitBuf=0, bitCnt=0;
1473 | // comp == 2 is grey+alpha (alpha is ignored)
1474 | int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
1475 | const unsigned char *dataR = (const unsigned char *)data;
1476 | const unsigned char *dataG = dataR + ofsG;
1477 | const unsigned char *dataB = dataR + ofsB;
1478 | int x, y, pos;
1479 | if(subsample) {
1480 | for(y = 0; y < height; y += 16) {
1481 | for(x = 0; x < width; x += 16) {
1482 | float Y[256], U[256], V[256];
1483 | for(row = y, pos = 0; row < y+16; ++row) {
1484 | // row >= height => use last input row
1485 | int clamped_row = (row < height) ? row : height - 1;
1486 | int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
1487 | for(col = x; col < x+16; ++col, ++pos) {
1488 | // if col >= width => use pixel from last input column
1489 | int p = base_p + ((col < width) ? col : (width-1))*comp;
1490 | float r = dataR[p], g = dataG[p], b = dataB[p];
1491 | Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
1492 | U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
1493 | V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
1494 | }
1495 | }
1496 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1497 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1498 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1499 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1500 |
1501 | // subsample U,V
1502 | {
1503 | float subU[64], subV[64];
1504 | int yy, xx;
1505 | for(yy = 0, pos = 0; yy < 8; ++yy) {
1506 | for(xx = 0; xx < 8; ++xx, ++pos) {
1507 | int j = yy*32+xx*2;
1508 | subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f;
1509 | subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f;
1510 | }
1511 | }
1512 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
1513 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
1514 | }
1515 | }
1516 | }
1517 | } else {
1518 | for(y = 0; y < height; y += 8) {
1519 | for(x = 0; x < width; x += 8) {
1520 | float Y[64], U[64], V[64];
1521 | for(row = y, pos = 0; row < y+8; ++row) {
1522 | // row >= height => use last input row
1523 | int clamped_row = (row < height) ? row : height - 1;
1524 | int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
1525 | for(col = x; col < x+8; ++col, ++pos) {
1526 | // if col >= width => use pixel from last input column
1527 | int p = base_p + ((col < width) ? col : (width-1))*comp;
1528 | float r = dataR[p], g = dataG[p], b = dataB[p];
1529 | Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
1530 | U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
1531 | V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
1532 | }
1533 | }
1534 |
1535 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1536 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
1537 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
1538 | }
1539 | }
1540 | }
1541 |
1542 | // Do the bit alignment of the EOI marker
1543 | stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);
1544 | }
1545 |
1546 | // EOI
1547 | stbiw__putc(s, 0xFF);
1548 | stbiw__putc(s, 0xD9);
1549 |
1550 | return 1;
1551 | }
1552 |
1553 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality)
1554 | {
1555 | stbi__write_context s;
1556 | stbi__start_write_callbacks(&s, func, context);
1557 | return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality);
1558 | }
1559 |
1560 |
1561 | #ifndef STBI_WRITE_NO_STDIO
1562 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)
1563 | {
1564 | stbi__write_context s;
1565 | if (stbi__start_write_file(&s,filename)) {
1566 | int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);
1567 | stbi__end_write_file(&s);
1568 | return r;
1569 | } else
1570 | return 0;
1571 | }
1572 | #endif
1573 |
1574 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION
1575 |
1576 | /* Revision history
1577 | 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels
1578 | 1.13
1579 | 1.12
1580 | 1.11 (2019-08-11)
1581 |
1582 | 1.10 (2019-02-07)
1583 | support utf8 filenames in Windows; fix warnings and platform ifdefs
1584 | 1.09 (2018-02-11)
1585 | fix typo in zlib quality API, improve STB_I_W_STATIC in C++
1586 | 1.08 (2018-01-29)
1587 | add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter
1588 | 1.07 (2017-07-24)
1589 | doc fix
1590 | 1.06 (2017-07-23)
1591 | writing JPEG (using Jon Olick's code)
1592 | 1.05 ???
1593 | 1.04 (2017-03-03)
1594 | monochrome BMP expansion
1595 | 1.03 ???
1596 | 1.02 (2016-04-02)
1597 | avoid allocating large structures on the stack
1598 | 1.01 (2016-01-16)
1599 | STBIW_REALLOC_SIZED: support allocators with no realloc support
1600 | avoid race-condition in crc initialization
1601 | minor compile issues
1602 | 1.00 (2015-09-14)
1603 | installable file IO function
1604 | 0.99 (2015-09-13)
1605 | warning fixes; TGA rle support
1606 | 0.98 (2015-04-08)
1607 | added STBIW_MALLOC, STBIW_ASSERT etc
1608 | 0.97 (2015-01-18)
1609 | fixed HDR asserts, rewrote HDR rle logic
1610 | 0.96 (2015-01-17)
1611 | add HDR output
1612 | fix monochrome BMP
1613 | 0.95 (2014-08-17)
1614 | add monochrome TGA output
1615 | 0.94 (2014-05-31)
1616 | rename private functions to avoid conflicts with stb_image.h
1617 | 0.93 (2014-05-27)
1618 | warning fixes
1619 | 0.92 (2010-08-01)
1620 | casts to unsigned char to fix warnings
1621 | 0.91 (2010-07-17)
1622 | first public release
1623 | 0.90 first internal release
1624 | */
1625 |
1626 | /*
1627 | ------------------------------------------------------------------------------
1628 | This software is available under 2 licenses -- choose whichever you prefer.
1629 | ------------------------------------------------------------------------------
1630 | ALTERNATIVE A - MIT License
1631 | Copyright (c) 2017 Sean Barrett
1632 | Permission is hereby granted, free of charge, to any person obtaining a copy of
1633 | this software and associated documentation files (the "Software"), to deal in
1634 | the Software without restriction, including without limitation the rights to
1635 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1636 | of the Software, and to permit persons to whom the Software is furnished to do
1637 | so, subject to the following conditions:
1638 | The above copyright notice and this permission notice shall be included in all
1639 | copies or substantial portions of the Software.
1640 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1641 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1642 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1643 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1644 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1645 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1646 | SOFTWARE.
1647 | ------------------------------------------------------------------------------
1648 | ALTERNATIVE B - Public Domain (www.unlicense.org)
1649 | This is free and unencumbered software released into the public domain.
1650 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
1651 | software, either in source code form or as a compiled binary, for any purpose,
1652 | commercial or non-commercial, and by any means.
1653 | In jurisdictions that recognize copyright laws, the author or authors of this
1654 | software dedicate any and all copyright interest in the software to the public
1655 | domain. We make this dedication for the benefit of the public at large and to
1656 | the detriment of our heirs and successors. We intend this dedication to be an
1657 | overt act of relinquishment in perpetuity of all present and future rights to
1658 | this software under copyright law.
1659 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1660 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1661 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1662 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1663 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1664 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1665 | ------------------------------------------------------------------------------
1666 | */
1667 |
--------------------------------------------------------------------------------