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