|
Blender
V2.59
|
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