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