Color Management, Stage 2: Switch color pipeline to use OpenColorIO
[blender.git] / source / blender / imbuf / intern / thumbs.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  * The Original Code is Copyright (C) 2007 Blender Foundation
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Andrea Weikert.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/imbuf/intern/thumbs.c
29  *  \ingroup imbuf
30  */
31
32
33 #include <stdio.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_string.h"
38 #include "BLI_path_util.h"
39 #include "BLI_fileops.h"
40 #include "BLI_md5.h"
41
42 #include "IMB_imbuf_types.h"
43 #include "IMB_imbuf.h"
44 #include "IMB_thumbs.h"
45 #include "IMB_metadata.h"
46
47 #include <ctype.h>
48 #include <string.h>
49 #include <time.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <stdio.h>
53
54 #ifdef WIN32
55 #  include <windows.h> /* need to include windows.h so _WIN32_IE is defined  */
56 #  ifndef _WIN32_IE
57 #    define _WIN32_IE 0x0400 /* minimal requirements for SHGetSpecialFolderPath on MINGW MSVC has this defined already */
58 #  endif
59 #  include <shlobj.h>  /* for SHGetSpecialFolderPath, has to be done before BLI_winstuff
60                         * because 'near' is disabled through BLI_windstuff */
61 #  include <process.h> /* getpid */
62 #  include <direct.h> /* chdir */
63 #  include "BLI_winstuff.h"
64 #  include "utfconv.h"
65 #else
66 #  include <unistd.h>
67 #endif
68
69 #define URI_MAX FILE_MAX * 3 + 8
70
71 static int get_thumb_dir(char *dir, ThumbSize size)
72 {
73 #ifdef WIN32
74         wchar_t dir_16[MAX_PATH];
75         /* yes, applications shouldn't store data there, but so does GIMP :)*/
76         SHGetSpecialFolderPathW(0, dir_16, CSIDL_PROFILE, 0);
77         conv_utf_16_to_8(dir_16, dir, FILE_MAX);
78
79
80 #else
81         const char *home = getenv("HOME");
82         if (!home) return 0;
83         BLI_strncpy(dir, home, FILE_MAX);
84 #endif
85         switch (size) {
86                 case THB_NORMAL:
87                         strcat(dir, "/.thumbnails/normal/");
88                         break;
89                 case THB_LARGE:
90                         strcat(dir, "/.thumbnails/large/");
91                         break;
92                 case THB_FAIL:
93                         strcat(dir, "/.thumbnails/fail/blender/");
94                         break;
95                 default:
96                         return 0; /* unknown size */
97         }
98         return 1;
99 }
100
101 /** ----- begin of adapted code from glib ---
102  * The following code is adapted from function g_escape_uri_string from the gnome glib
103  * Source: http://svn.gnome.org/viewcvs/glib/trunk/glib/gconvert.c?view=markup
104  * released under the Gnu General Public License.
105  */
106 typedef enum {
107         UNSAFE_ALL        = 0x1, /* Escape all unsafe characters   */
108         UNSAFE_ALLOW_PLUS = 0x2, /* Allows '+'  */
109         UNSAFE_PATH       = 0x8, /* Allows '/', '&', '=', ':', '@', '+', '$' and ',' */
110         UNSAFE_HOST       = 0x10, /* Allows '/' and ':' and '@' */
111         UNSAFE_SLASHES    = 0x20 /* Allows all characters except for '/' and '%' */
112 } UnsafeCharacterSet;
113
114 static const unsigned char acceptable[96] = {
115         /* A table of the ASCII chars from space (32) to DEL (127) */
116         /*      !    "    #    $    %    &    '    (    )    *    +    ,    -    .    / */
117         0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
118         /* 0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ? */
119         0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
120         /* @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O */
121         0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
122         /* P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _ */
123         0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
124         /* `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o */
125         0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
126         /* p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~  DEL */
127         0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
128 };
129
130 static const char hex[17] = "0123456789abcdef";
131
132 /* Note: This escape function works on file: URIs, but if you want to
133  * escape something else, please read RFC-2396 */
134 static void escape_uri_string(const char *string, char *escaped_string, int len, UnsafeCharacterSet mask)
135 {
136 #define ACCEPTABLE(a) ((a) >= 32 && (a) < 128 && (acceptable[(a) - 32] & use_mask))
137
138         const char *p;
139         char *q;
140         int c;
141         UnsafeCharacterSet use_mask;
142         use_mask = mask;
143
144         for (q = escaped_string, p = string; (*p != '\0') && len; p++) {
145                 c = (unsigned char) *p;
146                 len--;
147
148                 if (!ACCEPTABLE(c)) {
149                         *q++ = '%'; /* means hex coming */
150                         *q++ = hex[c >> 4];
151                         *q++ = hex[c & 15];
152                 }
153                 else {
154                         *q++ = *p;
155                 }
156         }
157   
158         *q = '\0';
159 }
160
161 static void to_hex_char(char *hexbytes, const unsigned char *bytes, int len)
162 {
163         const unsigned char *p;
164         char *q;
165
166         for (q = hexbytes, p = bytes; len; p++) {
167                 const unsigned char c = (unsigned char) *p;
168                 len--;
169                 *q++ = hex[c >> 4];
170                 *q++ = hex[c & 15];
171         }
172 }
173
174 /** ----- end of adapted code from glib --- */
175
176 static int uri_from_filename(const char *path, char *uri)
177 {
178         char orig_uri[URI_MAX]; 
179         const char *dirstart = path;
180         
181 #ifdef WIN32
182         {
183                 char vol[3];
184
185                 BLI_strncpy(orig_uri, "file:///", FILE_MAX);
186                 if (strlen(path) < 2 && path[1] != ':') {
187                         /* not a correct absolute path */
188                         return 0;
189                 }
190                 /* on windows, using always uppercase drive/volume letter in uri */
191                 vol[0] = (unsigned char)toupper(path[0]);
192                 vol[1] = ':';
193                 vol[2] = '\0';
194                 strcat(orig_uri, vol);
195                 dirstart += 2;
196         }
197 #else
198         BLI_strncpy(orig_uri, "file://", FILE_MAX);
199 #endif
200         strcat(orig_uri, dirstart);
201         BLI_char_switch(orig_uri, '\\', '/');
202         
203 #ifdef WITH_ICONV
204         {
205                 char uri_utf8[FILE_MAX * 3 + 8];
206                 escape_uri_string(orig_uri, uri_utf8, FILE_MAX * 3 + 8, UNSAFE_PATH);
207                 BLI_string_to_utf8(uri_utf8, uri, NULL);
208         }
209 #else 
210         escape_uri_string(orig_uri, uri, FILE_MAX * 3 + 8, UNSAFE_PATH);
211 #endif
212         return 1;
213 }
214
215 static void thumbname_from_uri(const char *uri, char *thumb, const int thumb_len)
216 {
217         char hexdigest[33];
218         unsigned char digest[16];
219
220         md5_buffer(uri, strlen(uri), digest);
221         hexdigest[0] = '\0';
222         to_hex_char(hexdigest, digest, 16);
223         hexdigest[32] = '\0';
224         BLI_snprintf(thumb, thumb_len, "%s.png", hexdigest);
225
226         // printf("%s: '%s' --> '%s'\n", __func__, uri, thumb);
227 }
228
229 static int thumbpath_from_uri(const char *uri, char *path, const int path_len, ThumbSize size)
230 {
231         char tmppath[FILE_MAX];
232         int rv = 0;
233
234         if (get_thumb_dir(tmppath, size)) {
235                 char thumb[40];
236                 thumbname_from_uri(uri, thumb, sizeof(thumb));
237                 BLI_snprintf(path, path_len, "%s%s", tmppath, thumb);
238                 rv = 1;
239         }
240         return rv;
241 }
242
243 void IMB_thumb_makedirs(void)
244 {
245         char tpath[FILE_MAX];
246         if (get_thumb_dir(tpath, THB_NORMAL)) {
247                 BLI_dir_create_recursive(tpath);
248         }
249         if (get_thumb_dir(tpath, THB_FAIL)) {
250                 BLI_dir_create_recursive(tpath);
251         }
252 }
253
254 /* create thumbnail for file and returns new imbuf for thumbnail */
255 ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, ImBuf *img)
256 {
257         char uri[URI_MAX] = "";
258         char desc[URI_MAX + 22];
259         char tpath[FILE_MAX];
260         char tdir[FILE_MAX];
261         char temp[FILE_MAX];
262         char mtime[40] = "0"; /* in case we can't stat the file */
263         char cwidth[40] = "0"; /* in case images have no data */
264         char cheight[40] = "0";
265         char thumb[40];
266         short tsize = 128;
267         short ex, ey;
268         float scaledx, scaledy; 
269         struct stat info;
270
271         switch (size) {
272                 case THB_NORMAL:
273                         tsize = 128;
274                         break;
275                 case THB_LARGE:
276                         tsize = 256;
277                         break;
278                 case THB_FAIL:
279                         tsize = 1;
280                         break;
281                 default:
282                         return NULL; /* unknown size */
283         }
284
285         /* exception, skip images over 100mb */
286         if (source == THB_SOURCE_IMAGE) {
287                 const size_t size = BLI_file_size(path);
288                 if (size != -1 && size > THUMB_SIZE_MAX) {
289                         // printf("file too big: %d, skipping %s\n", (int)size, path);
290                         return NULL;
291                 }
292         }
293
294         uri_from_filename(path, uri);
295         thumbname_from_uri(uri, thumb, sizeof(thumb));
296         if (get_thumb_dir(tdir, size)) {
297                 BLI_snprintf(tpath, FILE_MAX, "%s%s", tdir, thumb);
298                 thumb[8] = '\0'; /* shorten for tempname, not needed anymore */
299                 BLI_snprintf(temp, FILE_MAX, "%sblender_%d_%s.png", tdir, abs(getpid()), thumb);
300                 if (BLI_path_ncmp(path, tdir, sizeof(tdir)) == 0) {
301                         return NULL;
302                 }
303                 if (size == THB_FAIL) {
304                         img = IMB_allocImBuf(1, 1, 32, IB_rect | IB_metadata);
305                         if (!img) return NULL;
306                 }
307                 else {
308                         if (THB_SOURCE_IMAGE == source || THB_SOURCE_BLEND == source) {
309                                 
310                                 /* only load if we didnt give an image */
311                                 if (img == NULL) {
312                                         if (THB_SOURCE_BLEND == source) {
313                                                 img = IMB_loadblend_thumb(path);
314                                         }
315                                         else {
316                                                 img = IMB_loadiffname(path, IB_rect | IB_metadata, NULL);
317                                         }
318                                 }
319
320                                 if (img != NULL) {
321                                         stat(path, &info);
322                                         BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime);
323                                         BLI_snprintf(cwidth, sizeof(cwidth), "%d", img->x);
324                                         BLI_snprintf(cheight, sizeof(cheight), "%d", img->y);
325                                 }
326                         }
327                         else if (THB_SOURCE_MOVIE == source) {
328                                 struct anim *anim = NULL;
329                                 anim = IMB_open_anim(path, IB_rect | IB_metadata, 0, NULL);
330                                 if (anim != NULL) {
331                                         img = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
332                                         if (img == NULL) {
333                                                 printf("not an anim; %s\n", path);
334                                         }
335                                         else {
336                                                 IMB_freeImBuf(img);
337                                                 img = IMB_anim_previewframe(anim);                                              
338                                         }
339                                         IMB_free_anim(anim);
340                                 }
341                                 stat(path, &info);
342                                 BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime);
343                         }
344                         if (!img) return NULL;
345
346                         if (img->x > img->y) {
347                                 scaledx = (float)tsize;
348                                 scaledy =  ( (float)img->y / (float)img->x) * tsize;
349                         }
350                         else {
351                                 scaledy = (float)tsize;
352                                 scaledx =  ( (float)img->x / (float)img->y) * tsize;
353                         }
354                         ex = (short)scaledx;
355                         ey = (short)scaledy;
356                         
357                         /* save some time by only scaling byte buf */
358                         if (img->rect_float) {
359                                 if (img->rect == NULL) {
360                                         IMB_rect_from_float(img);
361                                 }
362
363                                 imb_freerectfloatImBuf(img);
364                         }
365
366                         IMB_scaleImBuf(img, ex, ey);
367                 }
368                 BLI_snprintf(desc, sizeof(desc), "Thumbnail for %s", uri);
369                 IMB_metadata_change_field(img, "Description", desc);
370                 IMB_metadata_change_field(img, "Software", "Blender");
371                 IMB_metadata_change_field(img, "Thumb::URI", uri);
372                 IMB_metadata_change_field(img, "Thumb::MTime", mtime);
373                 if (THB_SOURCE_IMAGE == source) {
374                         IMB_metadata_change_field(img, "Thumb::Image::Width", cwidth);
375                         IMB_metadata_change_field(img, "Thumb::Image::Height", cheight);
376                 }
377                 img->ftype = PNG;
378                 img->planes = 32;
379
380                 if (IMB_saveiff(img, temp, IB_rect | IB_metadata)) {
381 #ifndef WIN32
382                         chmod(temp, S_IRUSR | S_IWUSR);
383 #endif
384                         // printf("%s saving thumb: '%s'\n", __func__, tpath);
385
386                         BLI_rename(temp, tpath);
387                 }
388
389                 return img;
390         }
391         return img;
392 }
393
394 /* read thumbnail for file and returns new imbuf for thumbnail */
395 ImBuf *IMB_thumb_read(const char *path, ThumbSize size)
396 {
397         char thumb[FILE_MAX];
398         char uri[FILE_MAX * 3 + 8];
399         ImBuf *img = NULL;
400
401         if (!uri_from_filename(path, uri)) {
402                 return NULL;
403         }
404         if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) {              
405                 img = IMB_loadiffname(thumb, IB_rect | IB_metadata, NULL);
406         }
407
408         return img;
409 }
410
411 /* delete all thumbs for the file */
412 void IMB_thumb_delete(const char *path, ThumbSize size)
413 {
414         char thumb[FILE_MAX];
415         char uri[FILE_MAX * 3 + 8];
416
417         if (!uri_from_filename(path, uri)) {
418                 return;
419         }
420         if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) {
421                 if (BLI_path_ncmp(path, thumb, sizeof(thumb)) == 0) {
422                         return;
423                 }
424                 if (BLI_exists(thumb)) {
425                         BLI_delete(thumb, 0, 0);
426                 }
427         }
428 }
429
430
431 /* create the thumb if necessary and manage failed and old thumbs */
432 ImBuf *IMB_thumb_manage(const char *path, ThumbSize size, ThumbSource source)
433 {
434         char thumb[FILE_MAX];
435         char uri[FILE_MAX * 3 + 8];
436         struct stat st;
437         ImBuf *img = NULL;
438         
439         if (stat(path, &st)) {
440                 return NULL;
441         }       
442         if (!uri_from_filename(path, uri)) {
443                 return NULL;
444         }
445         if (thumbpath_from_uri(uri, thumb, sizeof(thumb), THB_FAIL)) {
446                 /* failure thumb exists, don't try recreating */
447                 if (BLI_exists(thumb)) {
448                         /* clear out of date fail case */
449                         if (BLI_file_older(thumb, path)) {
450                                 BLI_delete(thumb, 0, 0);
451                         }
452                         else {
453                                 return NULL;
454                         }
455                 }
456         }
457
458         if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) {
459                 if (BLI_path_ncmp(path, thumb, sizeof(thumb)) == 0) {
460                         img = IMB_loadiffname(path, IB_rect, NULL);
461                 }
462                 else {
463                         img = IMB_loadiffname(thumb, IB_rect | IB_metadata, NULL);
464                         if (img) {
465                                 char mtime[40];
466                                 if (!IMB_metadata_get_field(img, "Thumb::MTime", mtime, 40)) {
467                                         /* illegal thumb, forget it! */
468                                         IMB_freeImBuf(img);
469                                         img = NULL;
470                                 }
471                                 else {
472                                         time_t t = atol(mtime);
473                                         if (st.st_mtime != t) {
474                                                 /* recreate all thumbs */
475                                                 IMB_freeImBuf(img);
476                                                 img = NULL;
477                                                 IMB_thumb_delete(path, THB_NORMAL);
478                                                 IMB_thumb_delete(path, THB_LARGE);
479                                                 IMB_thumb_delete(path, THB_FAIL);
480                                                 img = IMB_thumb_create(path, size, source, NULL);
481                                                 if (!img) {
482                                                         /* thumb creation failed, write fail thumb */
483                                                         img = IMB_thumb_create(path, THB_FAIL, source, NULL);
484                                                         if (img) {
485                                                                 /* we don't need failed thumb anymore */
486                                                                 IMB_freeImBuf(img);
487                                                                 img = NULL;
488                                                         }
489                                                 }
490                                         }
491                                 }
492                         }
493                         else {
494                                 img = IMB_thumb_create(path, size, source, NULL);
495                                 if (!img) {
496                                         /* thumb creation failed, write fail thumb */
497                                         img = IMB_thumb_create(path, THB_FAIL, source, NULL);
498                                         if (img) {
499                                                 /* we don't need failed thumb anymore */
500                                                 IMB_freeImBuf(img);
501                                                 img = NULL;
502                                         }
503                                 }
504                         }
505                 }
506         }
507
508         return img;
509 }