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