Blender V2.61 - r43446

thumbs.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2007 Blender Foundation
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): Andrea Weikert.
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00033 #include <stdio.h>
00034 
00035 #include "MEM_guardedalloc.h"
00036 
00037 #include "BLI_blenlib.h"
00038 #include "BLI_md5.h"
00039 
00040 #include "BKE_utildefines.h"
00041 
00042 #include "IMB_imbuf_types.h"
00043 #include "IMB_imbuf.h"
00044 #include "IMB_thumbs.h"
00045 #include "IMB_metadata.h"
00046 
00047 #include <ctype.h>
00048 #include <stdlib.h>
00049 #include <string.h>
00050 #include <time.h>
00051 #include <sys/types.h>
00052 #include <sys/stat.h>
00053 #include <stdio.h>
00054 
00055 #ifdef WIN32
00056 #include <windows.h> /* need to include windows.h so _WIN32_IE is defined  */
00057 #ifndef _WIN32_IE
00058 #define _WIN32_IE 0x0400 /* minimal requirements for SHGetSpecialFolderPath on MINGW MSVC has this defined already */
00059 #endif
00060 #include <shlobj.h> /* for SHGetSpecialFolderPath, has to be done before BLI_winstuff because 'near' is disabled through BLI_windstuff */
00061 #include <process.h> /* getpid */
00062 #include <direct.h> /* chdir */
00063 #include "BLI_winstuff.h"
00064 #else
00065 #include <unistd.h>
00066 #endif
00067 
00068 #define URI_MAX FILE_MAX*3 + 8
00069 
00070 static int get_thumb_dir( char* dir , ThumbSize size)
00071 {
00072 #ifdef WIN32
00073     /* yes, applications shouldn't store data there, but so does GIMP :)*/
00074     SHGetSpecialFolderPath(0, dir, CSIDL_PROFILE, 0);
00075 #else
00076     const char* home = getenv("HOME");
00077     if (!home) return 0;
00078     BLI_strncpy(dir, home, FILE_MAX);
00079 #endif
00080     switch(size) {
00081         case THB_NORMAL:
00082             strcat(dir, "/.thumbnails/normal/");
00083             break;
00084         case THB_LARGE:
00085             strcat(dir, "/.thumbnails/large/");
00086             break;
00087         case THB_FAIL:
00088             strcat(dir, "/.thumbnails/fail/blender/");
00089             break;
00090         default:
00091             return 0; /* unknown size */
00092     }
00093     return 1;
00094 }
00095 
00101 typedef enum {
00102   UNSAFE_ALL        = 0x1,  /* Escape all unsafe characters   */
00103   UNSAFE_ALLOW_PLUS = 0x2,  /* Allows '+'  */
00104   UNSAFE_PATH       = 0x8,  /* Allows '/', '&', '=', ':', '@', '+', '$' and ',' */
00105   UNSAFE_HOST       = 0x10, /* Allows '/' and ':' and '@' */
00106   UNSAFE_SLASHES    = 0x20  /* Allows all characters except for '/' and '%' */
00107 } UnsafeCharacterSet;
00108 
00109 static const unsigned char acceptable[96] = {
00110     /* A table of the ASCII chars from space (32) to DEL (127) */
00111     /*      !    "    #    $    %    &    '    (    )    *    +    ,    -    .    / */
00112     0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
00113     /* 0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ? */
00114     0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
00115     /* @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O */
00116     0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
00117     /* P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _ */
00118     0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
00119     /* `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o */
00120     0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
00121     /* p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~  DEL */
00122     0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
00123 };
00124 
00125 static const char hex[17] = "0123456789abcdef";
00126 
00127 /* Note: This escape function works on file: URIs, but if you want to
00128  * escape something else, please read RFC-2396 */
00129 static void escape_uri_string (const char *string, char* escaped_string, int len,UnsafeCharacterSet mask)
00130 {
00131 #define ACCEPTABLE(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
00132 
00133     const char *p;
00134     char *q;
00135     int c;
00136     UnsafeCharacterSet use_mask;
00137     use_mask = mask;
00138 
00139     for (q = escaped_string, p = string; (*p != '\0') && len; p++) {
00140         c = (unsigned char) *p;
00141         len--;
00142 
00143         if (!ACCEPTABLE (c)) {
00144             *q++ = '%'; /* means hex coming */
00145             *q++ = hex[c >> 4];
00146             *q++ = hex[c & 15];
00147         } else {
00148             *q++ = *p;
00149         }
00150     }
00151   
00152     *q = '\0';
00153 }
00154 
00155 static void to_hex_char(char* hexbytes, const unsigned char* bytes, int len)
00156 {
00157     const unsigned char *p;
00158     char *q;
00159 
00160     for (q = hexbytes, p = bytes; len; p++) {
00161         const unsigned char c = (unsigned char) *p;
00162         len--;
00163         *q++ = hex[c >> 4];
00164         *q++ = hex[c & 15];
00165     }
00166 }
00167 
00170 static int uri_from_filename( const char *path, char *uri )
00171 {
00172     char orig_uri[URI_MAX]; 
00173     const char* dirstart = path;
00174     
00175 #ifdef WIN32
00176     {
00177         char vol[3];
00178 
00179         BLI_strncpy(orig_uri, "file:///", FILE_MAX);
00180         if (strlen(path) < 2 && path[1] != ':') {
00181             /* not a correct absolute path */
00182             return 0;
00183         }
00184         /* on windows, using always uppercase drive/volume letter in uri */
00185         vol[0] = (unsigned char)toupper(path[0]);
00186         vol[1] = ':';
00187         vol[2] = '\0';
00188         strcat(orig_uri, vol);
00189         dirstart += 2;
00190     }
00191 #else
00192     BLI_strncpy(orig_uri, "file://", FILE_MAX);
00193 #endif
00194     strcat(orig_uri, dirstart);
00195     BLI_char_switch(orig_uri, '\\', '/');
00196     
00197 #ifdef WITH_ICONV
00198     {
00199         char uri_utf8[FILE_MAX*3+8];
00200         escape_uri_string(orig_uri, uri_utf8, FILE_MAX*3+8, UNSAFE_PATH);
00201         BLI_string_to_utf8(uri_utf8, uri, NULL);
00202     }
00203 #else 
00204     escape_uri_string(orig_uri, uri, FILE_MAX*3+8, UNSAFE_PATH);
00205 #endif
00206     return 1;
00207 }
00208 
00209 static void thumbname_from_uri(const char* uri, char* thumb, const int thumb_len)
00210 {
00211     char hexdigest[33];
00212     unsigned char digest[16];
00213 
00214     md5_buffer( uri, strlen(uri), digest);
00215     hexdigest[0] = '\0';
00216     to_hex_char(hexdigest, digest, 16);
00217     hexdigest[32] = '\0';
00218     BLI_snprintf(thumb, thumb_len, "%s.png", hexdigest);
00219 }
00220 
00221 static int thumbpath_from_uri(const char* uri, char* path, const int path_len, ThumbSize size)
00222 {
00223     char tmppath[FILE_MAX];
00224     int rv = 0;
00225 
00226     if (get_thumb_dir(tmppath, size)) {
00227         char thumb[40];
00228         thumbname_from_uri(uri, thumb, sizeof(thumb));
00229         BLI_snprintf(path, path_len, "%s%s", tmppath, thumb);
00230         rv = 1;
00231     }
00232     return rv;
00233 }
00234 
00235 void IMB_thumb_makedirs(void)
00236 {
00237     char tpath[FILE_MAX];
00238     if (get_thumb_dir(tpath, THB_NORMAL)) {
00239         BLI_dir_create_recursive(tpath);
00240     }
00241     if (get_thumb_dir(tpath, THB_FAIL)) {
00242         BLI_dir_create_recursive(tpath);
00243     }
00244 }
00245 
00246 /* create thumbnail for file and returns new imbuf for thumbnail */
00247 ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source, ImBuf *img)
00248 {
00249     char uri[URI_MAX]= "";
00250     char desc[URI_MAX+22];
00251     char tpath[FILE_MAX];
00252     char tdir[FILE_MAX];
00253     char temp[FILE_MAX];
00254     char mtime[40]= "0"; /* incase we can't stat the file */
00255     char cwidth[40]= "0"; /* incase images have no data */
00256     char cheight[40]= "0";
00257     char thumb[40];
00258     short tsize = 128;
00259     short ex, ey;
00260     float scaledx, scaledy; 
00261     struct stat info;
00262 
00263     switch(size) {
00264         case THB_NORMAL:
00265             tsize = 128;
00266             break;
00267         case THB_LARGE:
00268             tsize = 256;
00269             break;
00270         case THB_FAIL:
00271             tsize = 1;
00272             break;
00273         default:
00274             return NULL; /* unknown size */
00275     }
00276 
00277     /* exception, skip images over 100mb */
00278     if(source == THB_SOURCE_IMAGE) {
00279         const size_t size= BLI_file_size(path);
00280         if(size != -1 && size > THUMB_SIZE_MAX) {
00281             // printf("file too big: %d, skipping %s\n", (int)size, path);
00282             return NULL;
00283         }
00284     }
00285 
00286     uri_from_filename(path, uri);
00287     thumbname_from_uri(uri, thumb, sizeof(thumb));
00288     if (get_thumb_dir(tdir, size)) {
00289         BLI_snprintf(tpath, FILE_MAX, "%s%s", tdir, thumb);
00290         thumb[8] = '\0'; /* shorten for tempname, not needed anymore */
00291         BLI_snprintf(temp, FILE_MAX, "%sblender_%d_%s.png", tdir, abs(getpid()), thumb);
00292         if (BLI_path_ncmp(path, tdir, sizeof(tdir)) == 0) {
00293             return NULL;
00294         }
00295         if (size == THB_FAIL) {
00296             img = IMB_allocImBuf(1,1,32, IB_rect | IB_metadata);
00297             if (!img) return NULL;
00298         } else {
00299             if (THB_SOURCE_IMAGE == source || THB_SOURCE_BLEND == source) {
00300                 
00301                 /* only load if we didnt give an image */
00302                 if(img==NULL) {
00303                     if(THB_SOURCE_BLEND == source) {
00304                         img = IMB_loadblend_thumb(path);
00305                     }
00306                     else {
00307                         img = IMB_loadiffname(path, IB_rect | IB_metadata);
00308                     }
00309                 }
00310 
00311                 if (img != NULL) {
00312                     stat(path, &info);
00313                     BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime);
00314                     BLI_snprintf(cwidth, sizeof(cwidth), "%d", img->x);
00315                     BLI_snprintf(cheight, sizeof(cheight), "%d", img->y);
00316                 }
00317             } else if (THB_SOURCE_MOVIE == source) {
00318                 struct anim * anim = NULL;
00319                 anim = IMB_open_anim(path, IB_rect | IB_metadata, 0);
00320                 if (anim != NULL) {
00321                     img = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
00322                     if (img == NULL) {
00323                         printf("not an anim; %s\n", path);
00324                     } else {
00325                         IMB_freeImBuf(img);
00326                         img = IMB_anim_previewframe(anim);                      
00327                     }
00328                     IMB_free_anim(anim);
00329                 }
00330                 stat(path, &info);
00331                 BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime);
00332             }
00333             if (!img) return NULL;
00334 
00335             if (img->x > img->y) {
00336                 scaledx = (float)tsize;
00337                 scaledy =  ( (float)img->y/(float)img->x )*tsize;
00338             }
00339             else {
00340                 scaledy = (float)tsize;
00341                 scaledx =  ( (float)img->x/(float)img->y )*tsize;
00342             }
00343             ex = (short)scaledx;
00344             ey = (short)scaledy;
00345             
00346             /* save some time by only scaling byte buf */
00347             if(img->rect_float) {
00348                 if(img->rect == NULL) {
00349                     IMB_rect_from_float(img);
00350                 }
00351 
00352                 imb_freerectfloatImBuf(img);
00353             }
00354 
00355             IMB_scaleImBuf(img, ex, ey);
00356         }
00357         BLI_snprintf(desc, sizeof(desc), "Thumbnail for %s", uri);
00358         IMB_metadata_change_field(img, "Description", desc);
00359         IMB_metadata_change_field(img, "Software", "Blender");
00360         IMB_metadata_change_field(img, "Thumb::URI", uri);
00361         IMB_metadata_change_field(img, "Thumb::MTime", mtime);
00362         if (THB_SOURCE_IMAGE == source) {
00363             IMB_metadata_change_field(img, "Thumb::Image::Width", cwidth);
00364             IMB_metadata_change_field(img, "Thumb::Image::Height", cheight);
00365         }
00366         img->ftype = PNG;
00367         img->planes = 32;
00368         if (IMB_saveiff(img, temp, IB_rect | IB_metadata)) {
00369 #ifndef WIN32
00370             chmod(temp, S_IRUSR | S_IWUSR);
00371 #endif  
00372             BLI_rename(temp, tpath);
00373         }
00374 
00375         return img;
00376     }
00377     return img;
00378 }
00379 
00380 /* read thumbnail for file and returns new imbuf for thumbnail */
00381 ImBuf* IMB_thumb_read(const char* path, ThumbSize size)
00382 {
00383     char thumb[FILE_MAX];
00384     char uri[FILE_MAX*3+8];
00385     ImBuf *img = NULL;
00386 
00387     if (!uri_from_filename(path,uri)) {
00388         return NULL;
00389     }
00390     if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) {      
00391         img = IMB_loadiffname(thumb, IB_rect | IB_metadata);
00392     }
00393 
00394     return img;
00395 }
00396 
00397 /* delete all thumbs for the file */
00398 void IMB_thumb_delete(const char* path, ThumbSize size)
00399 {
00400     char thumb[FILE_MAX];
00401     char uri[FILE_MAX*3+8];
00402 
00403     if (!uri_from_filename(path ,uri)) {
00404         return;
00405     }
00406     if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) {
00407         if (BLI_path_ncmp(path, thumb, sizeof(thumb)) == 0) {
00408             return;
00409         }
00410         if (BLI_exists(thumb)) {
00411             BLI_delete(thumb, 0, 0);
00412         }
00413     }
00414 }
00415 
00416 
00417 /* create the thumb if necessary and manage failed and old thumbs */
00418 ImBuf* IMB_thumb_manage(const char* path, ThumbSize size, ThumbSource source)
00419 {
00420     char thumb[FILE_MAX];
00421     char uri[FILE_MAX*3+8];
00422     struct stat st;
00423     ImBuf* img = NULL;
00424     
00425     if (stat(path, &st)) {
00426         return NULL;
00427     }   
00428     if (!uri_from_filename(path,uri)) {
00429         return NULL;
00430     }
00431     if (thumbpath_from_uri(uri, thumb, sizeof(thumb), THB_FAIL)) {
00432         /* failure thumb exists, don't try recreating */
00433         if (BLI_exists(thumb)) {
00434             return NULL;
00435         }
00436     }
00437 
00438     if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) {
00439         if (BLI_path_ncmp(path, thumb, sizeof(thumb)) == 0) {
00440             img = IMB_loadiffname(path, IB_rect);
00441         } else {
00442             img = IMB_loadiffname(thumb, IB_rect | IB_metadata);
00443             if (img) {
00444                 char mtime[40];
00445                 if (!IMB_metadata_get_field(img, "Thumb::MTime", mtime, 40)) {
00446                     /* illegal thumb, forget it! */
00447                     IMB_freeImBuf(img);
00448                     img = NULL;
00449                 } else {
00450                     time_t t = atol(mtime);
00451                     if (st.st_mtime != t) {
00452                         /* recreate all thumbs */
00453                         IMB_freeImBuf(img);
00454                         img = NULL;
00455                         IMB_thumb_delete(path, THB_NORMAL);
00456                         IMB_thumb_delete(path, THB_LARGE);
00457                         IMB_thumb_delete(path, THB_FAIL);
00458                         img = IMB_thumb_create(path, size, source, NULL);
00459                         if(!img){
00460                             /* thumb creation failed, write fail thumb */
00461                             img = IMB_thumb_create(path, THB_FAIL, source, NULL);
00462                             if (img) {
00463                                 /* we don't need failed thumb anymore */
00464                                 IMB_freeImBuf(img);
00465                                 img = NULL;
00466                             }
00467                         }
00468                     }
00469                 }
00470             } else {
00471                 img = IMB_thumb_create(path, size, source, NULL);
00472                 if(!img){
00473                     /* thumb creation failed, write fail thumb */
00474                     img = IMB_thumb_create(path, THB_FAIL, source, NULL);
00475                     if (img) {
00476                         /* we don't need failed thumb anymore */
00477                         IMB_freeImBuf(img);
00478                         img = NULL;
00479                     }
00480                 }
00481             }
00482         }
00483     }
00484 
00485     return img;
00486 }
00487 
00488