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