doxygen: add newline after \file
[blender.git] / source / blender / datatoc / datatoc_icon.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup datatoc
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <assert.h>
25 #include <errno.h>
26
27 /* for bool */
28 #include "../blenlib/BLI_sys_types.h"
29
30 /* for DIR */
31 #if !defined(WIN32) || defined(FREEWINDOWS)
32 #  include <dirent.h>
33 #endif
34
35 #include "png.h"
36
37
38 /* for Win32 DIR functions */
39 #ifdef WIN32
40 #  include "../blenlib/BLI_winstuff.h"
41 #endif
42
43 #ifdef WIN32
44 #  define SEP '\\'
45 #else
46 #  define SEP '/'
47 #endif
48
49 /* -------------------------------------------------------------------- */
50 /* Utility functions */
51
52 static int path_ensure_slash(char *string)
53 {
54         int len = strlen(string);
55         if (len == 0 || string[len - 1] != SEP) {
56                 string[len] = SEP;
57                 string[len + 1] = '\0';
58                 return len + 1;
59         }
60         return len;
61 }
62
63 static bool path_test_extension(const char *str, const char *ext)
64 {
65         const size_t a = strlen(str);
66         const size_t b = strlen(ext);
67         return !(a == 0 || b == 0 || b >= a) && (strcmp(ext, str + a - b) == 0);
68 }
69
70 static void endian_switch_uint32(unsigned int *val)
71 {
72         unsigned int tval = *val;
73         *val = ((tval >> 24))             |
74                ((tval << 8) & 0x00ff0000) |
75                ((tval >> 8) & 0x0000ff00) |
76                ((tval << 24));
77 }
78
79 /* -------------------------------------------------------------------- */
80 /* Write a PNG from RGBA pixels */
81
82 static bool write_png(const char *name, const unsigned int *pixels,
83                      const int width, const int height)
84 {
85         png_structp png_ptr;
86         png_infop info_ptr;
87         png_bytepp row_pointers = NULL;
88
89         FILE *fp;
90
91         const int bytesperpixel = 4;
92         const int compression = 9;
93         int i;
94
95         fp = fopen(name, "wb");
96         if (fp == NULL) {
97                 printf("%s: Cannot open file for writing '%s'\n", __func__, name);
98                 return false;
99         }
100
101         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
102                                           NULL, NULL, NULL);
103         if (png_ptr == NULL) {
104                 printf("%s: Cannot png_create_write_struct for file: '%s'\n", __func__, name);
105                 fclose(fp);
106                 return false;
107         }
108
109         info_ptr = png_create_info_struct(png_ptr);
110         if (info_ptr == NULL) {
111                 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
112                 printf("%s: Cannot png_create_info_struct for file: '%s'\n", __func__, name);
113                 fclose(fp);
114                 return false;
115         }
116
117         if (setjmp(png_jmpbuf(png_ptr))) {
118                 png_destroy_write_struct(&png_ptr, &info_ptr);
119                 printf("%s: Cannot setjmp for file: '%s'\n", __func__, name);
120                 fclose(fp);
121                 return false;
122         }
123
124         /* write the file */
125         png_init_io(png_ptr, fp);
126
127         png_set_compression_level(png_ptr, compression);
128
129         /* png image settings */
130         png_set_IHDR(png_ptr,
131                      info_ptr,
132                      width,
133                      height,
134                      8,
135                      PNG_COLOR_TYPE_RGBA,
136                      PNG_INTERLACE_NONE,
137                      PNG_COMPRESSION_TYPE_DEFAULT,
138                      PNG_FILTER_TYPE_DEFAULT);
139
140         /* write the file header information */
141         png_write_info(png_ptr, info_ptr);
142
143 #ifdef __LITTLE_ENDIAN__
144         png_set_swap(png_ptr);
145 #endif
146
147         /* allocate memory for an array of row-pointers */
148         row_pointers = (png_bytepp) malloc(height * sizeof(png_bytep));
149         if (row_pointers == NULL) {
150                 printf("%s: Cannot allocate row-pointers array for file '%s'\n", __func__, name);
151                 png_destroy_write_struct(&png_ptr, &info_ptr);
152                 if (fp) {
153                         fclose(fp);
154                 }
155                 return false;
156         }
157
158         /* set the individual row-pointers to point at the correct offsets */
159         for (i = 0; i < height; i++) {
160                 row_pointers[height - 1 - i] = (png_bytep)
161                                                (((const unsigned char *)pixels) +
162                                                 (i * width) * bytesperpixel * sizeof(unsigned char));
163         }
164
165         /* write out the entire image data in one call */
166         png_write_image(png_ptr, row_pointers);
167
168         /* write the additional chunks to the PNG file (not really needed) */
169         png_write_end(png_ptr, info_ptr);
170
171         /* clean up */
172         free(row_pointers);
173         png_destroy_write_struct(&png_ptr, &info_ptr);
174
175         fflush(fp);
176         fclose(fp);
177
178         return true;
179 }
180
181
182 /* -------------------------------------------------------------------- */
183 /* Merge icon-data from files */
184
185 struct IconHead {
186         unsigned int icon_w, icon_h;
187         unsigned int orig_x, orig_y;
188         unsigned int canvas_w, canvas_h;
189 };
190
191 static bool icon_decode_head(FILE *f_src,
192                             struct IconHead *r_head)
193 {
194         if (fread(r_head, 1, sizeof(*r_head), f_src) == sizeof(*r_head)) {
195 #ifndef __LITTLE_ENDIAN__
196                 endian_switch_uint32(&r_head->icon_w);
197                 endian_switch_uint32(&r_head->icon_h);
198                 endian_switch_uint32(&r_head->orig_x);
199                 endian_switch_uint32(&r_head->orig_y);
200                 endian_switch_uint32(&r_head->canvas_w);
201                 endian_switch_uint32(&r_head->canvas_h);
202 #endif
203                 return true;
204         }
205
206         /* quiet warning */
207         (void)endian_switch_uint32;
208
209         return false;
210 }
211
212 static bool icon_decode(FILE *f_src,
213                         struct IconHead *r_head, unsigned int **r_pixels)
214 {
215         unsigned int *pixels;
216         unsigned int pixels_size;
217
218         if (!icon_decode_head(f_src, r_head)) {
219                 printf("%s: failed to read header\n", __func__);
220                 return false;
221         }
222
223         pixels_size = sizeof(char[4]) * r_head->icon_w * r_head->icon_h;
224         pixels = malloc(pixels_size);
225         if (pixels == NULL) {
226                 printf("%s: failed to allocate pixels\n", __func__);
227                 return false;
228         }
229
230         if (fread(pixels, 1, pixels_size, f_src) != pixels_size) {
231                 printf("%s: failed to read pixels\n", __func__);
232                 free(pixels);
233                 return false;
234         }
235
236         *r_pixels = pixels;
237         return true;
238 }
239
240 static bool icon_read(const char *file_src,
241                       struct IconHead *r_head, unsigned int **r_pixels)
242 {
243         FILE *f_src;
244         bool success;
245
246         f_src = fopen(file_src, "rb");
247         if (f_src == NULL) {
248                 printf("%s: failed to open '%s'\n", __func__, file_src);
249                 return false;
250         }
251
252         success = icon_decode(f_src, r_head, r_pixels);
253
254         fclose(f_src);
255         return success;
256 }
257
258 static bool icon_merge(const char *file_src,
259                        unsigned int **r_pixels_canvas,
260                        unsigned int *r_canvas_w, unsigned int *r_canvas_h)
261 {
262         struct IconHead head;
263         unsigned int *pixels;
264
265         unsigned int x, y;
266
267         /* canvas */
268         unsigned int *pixels_canvas;
269         unsigned int canvas_w, canvas_h;
270
271         if (!icon_read(file_src, &head, &pixels)) {
272                 return false;
273         }
274
275         if (*r_canvas_w == 0) {
276                 /* init once */
277                 *r_canvas_w = head.canvas_w;
278                 *r_canvas_h = head.canvas_h;
279                 *r_pixels_canvas = calloc(1, (head.canvas_w * head.canvas_h) * sizeof(unsigned char[4]));
280         }
281
282         canvas_w = *r_canvas_w;
283         canvas_h = *r_canvas_h;
284         pixels_canvas = *r_pixels_canvas;
285
286         assert(head.canvas_w == canvas_w);
287         assert(head.canvas_h == canvas_h);
288
289         for (x = 0; x < head.icon_w; x++) {
290                 for (y = 0; y < head.icon_h; y++) {
291                         unsigned int pixel;
292                         unsigned int dst_x, dst_y;
293                         unsigned int pixel_xy_dst;
294
295
296                         /* get pixel */
297                         pixel = pixels[(y * head.icon_w) + x];
298
299                         /* set pixel */
300                         dst_x = head.orig_x + x;
301                         dst_y = head.orig_y + y;
302                         pixel_xy_dst = (dst_y * canvas_w) + dst_x;
303                         assert(pixel_xy_dst < (canvas_w * canvas_h));
304                         pixels_canvas[pixel_xy_dst] = pixel;
305                 }
306         }
307
308         free(pixels);
309
310         /* only for bounds check */
311         (void)canvas_h;
312
313         return true;
314 }
315
316 static bool icondir_to_png(const char *path_src, const char *file_dst)
317 {
318         /* Takes a path full of 'dat' files and writes out */
319         DIR *dir;
320         const struct dirent *fname;
321         char filepath[1024];
322         char *filename;
323         int path_str_len;
324         int found = 0, fail = 0;
325
326         unsigned int *pixels_canvas = NULL;
327         unsigned int canvas_w = 0, canvas_h = 0;
328
329         errno = 0;
330         dir = opendir(path_src);
331         if (dir == NULL) {
332                 printf("%s: failed to dir '%s', (%s)\n", __func__, path_src, errno ? strerror(errno) : "unknown");
333                 return false;
334         }
335
336         strcpy(filepath, path_src);
337         path_str_len = path_ensure_slash(filepath);
338         filename = &filepath[path_str_len];
339
340
341         while ((fname = readdir(dir)) != NULL) {
342                 if (path_test_extension(fname->d_name, ".dat")) {
343
344                         strcpy(filename, fname->d_name);
345
346                         if (icon_merge(filepath, &pixels_canvas, &canvas_w, &canvas_h)) {
347                                 found++;
348                         }
349                         else {
350                                 fail++;
351                         }
352                 }
353         }
354
355         closedir(dir);
356
357         if (found == 0) {
358                 printf("%s: dir '%s' has no icons\n", __func__, path_src);
359         }
360
361         if (fail != 0) {
362                 printf("%s: dir '%s' failed %d icons\n", __func__, path_src, fail);
363         }
364
365         /* write pixels  */
366         write_png(file_dst, pixels_canvas, canvas_w, canvas_h);
367
368         free(pixels_canvas);
369
370         return true;
371 }
372
373
374 /* -------------------------------------------------------------------- */
375 /* Main and parse args */
376
377 int main(int argc, char **argv)
378 {
379         const char *path_src;
380         const char *file_dst;
381
382
383         if (argc < 3) {
384                 printf("Usage: datatoc_icon <dir_icons> <data_icon_to.png>\n");
385                 exit(1);
386         }
387
388         path_src = argv[1];
389         file_dst = argv[2];
390
391         return (icondir_to_png(path_src, file_dst) == true) ? 0 : 1;
392 }