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