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