diff options
author | Campbell Barton <ideasman42@gmail.com> | 2010-05-25 01:52:18 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2010-05-25 01:52:18 +0400 |
commit | c61e25e6ac37296c13c0949b8363cc168125d750 (patch) | |
tree | 2bab770f7158ad937b6b7ccddea6480c8d594d90 /source/blender | |
parent | 3332b2b29e0b63a9768856c363dda3a4e52f4ce9 (diff) |
blend file thumbnailing
- uses same thumbnail system as image browser
- blend files show thumbnails in ubuntu/gnome (freedesktop spec)
- 128x128 images are embedded into the blend file header, a simple loader avoids reading the entire blend file to extract it when generating thumbnails in the file selector.
When the image browser reads a directory it loads images and creates thumbnails, blend files embedded images are treated just like loading an image.
- the thumbnail is created from the camera view in solid mode. (no camera == no thumbnal).
- readfile/writefile.c: had to use the 'TEST' code name to save thumbnails, anything else would segfault older blender versions on load. (its not used elsewhere).
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_utildefines.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/blender.c | 2 | ||||
-rw-r--r-- | source/blender/blenloader/BLO_writefile.h | 4 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 2 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 24 | ||||
-rw-r--r-- | source/blender/editors/space_file/filelist.c | 6 | ||||
-rw-r--r-- | source/blender/imbuf/IMB_thumbs.h | 7 | ||||
-rw-r--r-- | source/blender/imbuf/intern/thumbs.c | 25 | ||||
-rw-r--r-- | source/blender/imbuf/intern/thumbs_blend.c | 122 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_files.c | 63 |
10 files changed, 230 insertions, 27 deletions
diff --git a/source/blender/blenkernel/BKE_utildefines.h b/source/blender/blenkernel/BKE_utildefines.h index 73323c5a960..925b1d7171a 100644 --- a/source/blender/blenkernel/BKE_utildefines.h +++ b/source/blender/blenkernel/BKE_utildefines.h @@ -166,7 +166,7 @@ #define IMAG MAKE_ID('I','M','A','G') #define DNA1 MAKE_ID('D','N','A','1') -#define TEST MAKE_ID('T','E','S','T') +#define TEST MAKE_ID('T','E','S','T') /* used as preview between 'REND' and 'GLOB' */ #define REND MAKE_ID('R','E','N','D') #define USER MAKE_ID('U','S','E','R') diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 5d12675952c..046b8de2431 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -534,7 +534,7 @@ void BKE_write_undo(bContext *C, char *name) sprintf(numstr, "%d.blend", counter); BLI_make_file_string("/", tstr, btempdir, numstr); - success= BLO_write_file(CTX_data_main(C), tstr, G.fileflags, NULL); + success= BLO_write_file(CTX_data_main(C), tstr, G.fileflags, NULL, NULL); strcpy(curundo->str, tstr); } diff --git a/source/blender/blenloader/BLO_writefile.h b/source/blender/blenloader/BLO_writefile.h index a95e5867c7e..182c582cc0f 100644 --- a/source/blender/blenloader/BLO_writefile.h +++ b/source/blender/blenloader/BLO_writefile.h @@ -35,10 +35,12 @@ struct MemFile; struct Main; struct ReportList; -extern int BLO_write_file(struct Main *mainvar, char *dir, int write_flags, struct ReportList *reports); +extern int BLO_write_file(struct Main *mainvar, char *dir, int write_flags, struct ReportList *reports, int *thumb); extern int BLO_write_file_mem(struct Main *mainvar, struct MemFile *compare, struct MemFile *current, int write_flags, struct ReportList *reports); extern int BLO_write_runtime(struct Main *mainvar, char *file, char *exename, struct ReportList *reports); +#define BLEN_THUMB_SIZE 128 + #endif diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 381fc868a0d..8dbf2682bb6 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -10971,7 +10971,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filename) switch(bhead->code) { case DATA: case DNA1: - case TEST: + case TEST: /* used as preview since 2.5x */ case REND: bhead = blo_nextbhead(fd, bhead); break; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 5f2ee73e129..35fda1d8aa7 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -62,6 +62,7 @@ Any case: direct data is ALWAYS after the lib block - write library block - per LibBlock - write the ID of LibBlock +- write TEST (128x128, blend file preview, optional) - write FileGlobal (some global vars) - write SDNA - write USER if filename is ~/.B.blend @@ -2390,9 +2391,19 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar) writestruct(wd, GLOB, "FileGlobal", 1, &fg); } +/* preview image, first 2 values are width and height + * second are an RGBA image (unsigned char) + * note, this uses 'TEST' since new types will segfault on file load for older blender versions. + */ +static void write_thumb(WriteData *wd, int *img) +{ + if(img) + writedata(wd, TEST, (2 + img[0] * img[1]) * sizeof(int), img); +} + /* if MemFile * there's filesave to memory */ static int write_file_handle(Main *mainvar, int handle, MemFile *compare, MemFile *current, - int write_user_block, int write_flags) + int write_user_block, int write_flags, int *thumb) { BHead bhead; ListBase mainlist; @@ -2407,6 +2418,7 @@ static int write_file_handle(Main *mainvar, int handle, MemFile *compare, MemFil mywrite(wd, buf, 12); write_renderinfo(wd, mainvar); + write_thumb(wd, thumb); write_global(wd, write_flags, mainvar); /* no UI save in undo */ @@ -2458,7 +2470,7 @@ static int write_file_handle(Main *mainvar, int handle, MemFile *compare, MemFil } /* return: success (1) */ -int BLO_write_file(Main *mainvar, char *dir, int write_flags, ReportList *reports) +int BLO_write_file(Main *mainvar, char *dir, int write_flags, ReportList *reports, int *thumb) { char userfilename[FILE_MAXDIR+FILE_MAXFILE]; char tempname[FILE_MAXDIR+FILE_MAXFILE+1]; @@ -2497,7 +2509,7 @@ int BLO_write_file(Main *mainvar, char *dir, int write_flags, ReportList *report makeFilesRelative(dir, NULL); /* note, making relative to something OTHER then G.sce */ /* actual file writing */ - err= write_file_handle(mainvar, file, NULL,NULL, write_user_block, write_flags); + err= write_file_handle(mainvar, file, NULL,NULL, write_user_block, write_flags, thumb); close(file); /* rename/compress */ @@ -2550,7 +2562,7 @@ int BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int wr { int err; - err= write_file_handle(mainvar, 0, compare, current, 0, write_flags); + err= write_file_handle(mainvar, 0, compare, current, 0, write_flags, NULL); if(err==0) return 1; return 0; @@ -2646,7 +2658,7 @@ int BLO_write_runtime(Main *mainvar, char *file, char *exename, ReportList *repo outfd= open(gamename, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0777); if (outfd != -1) { - write_file_handle(mainvar, outfd, NULL,NULL, 0, G.fileflags); + write_file_handle(mainvar, outfd, NULL,NULL, 0, G.fileflags, NULL); if (write(outfd, " ", 1) != 1) { BKE_report(reports, RPT_ERROR, "Unable to write to output file."); @@ -2732,7 +2744,7 @@ int BLO_write_runtime(Main *mainvar, char *file, char *exename, ReportList *repo datastart= lseek(outfd, 0, SEEK_CUR); - write_file_handle(mainvar, outfd, NULL,NULL, 0, G.fileflags); + write_file_handle(mainvar, outfd, NULL,NULL, 0, G.fileflags, NULL); if (!handle_write_msb_int(outfd, datastart) || (write(outfd, "BRUNTIME", 8)!=8)) { BKE_report(reports, RPT_ERROR, "Unable to write to output file."); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index ebe42219f01..dd23c6b64b2 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1280,6 +1280,8 @@ static void thumbnails_startjob(void *tjv, short *stop, short *do_update) while ( (*stop==0) && (limg) ) { if ( limg->flags & IMAGEFILE ) { limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_IMAGE); + } else if ( limg->flags & BLENDERFILE ) { + limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_BLEND); } else if ( limg->flags & MOVIEFILE ) { limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_MOVIE); if (!limg->img) { @@ -1334,7 +1336,7 @@ void thumbnails_start(struct FileList* filelist, const struct bContext* C) tj->filelist = filelist; for (idx = 0; idx < filelist->numfiles;idx++) { if (!filelist->filelist[idx].image) { - if ( (filelist->filelist[idx].flags & IMAGEFILE) || (filelist->filelist[idx].flags & MOVIEFILE) ) { + if ( (filelist->filelist[idx].flags & (IMAGEFILE|MOVIEFILE|BLENDERFILE)) ) { FileImage* limg = MEM_callocN(sizeof(struct FileImage), "loadimage"); BLI_strncpy(limg->path, filelist->filelist[idx].path, FILE_MAX); limg->index= idx; @@ -1364,4 +1366,4 @@ void thumbnails_stop(struct FileList* filelist, const struct bContext* C) int thumbnails_running(struct FileList* filelist, const struct bContext* C) { return WM_jobs_test(CTX_wm_manager(C), filelist); -}
\ No newline at end of file +} diff --git a/source/blender/imbuf/IMB_thumbs.h b/source/blender/imbuf/IMB_thumbs.h index c7e39b9e0d7..ecb0ba8abd1 100644 --- a/source/blender/imbuf/IMB_thumbs.h +++ b/source/blender/imbuf/IMB_thumbs.h @@ -50,13 +50,14 @@ typedef enum ThumbSize { typedef enum ThumbSource { THB_SOURCE_IMAGE, - THB_SOURCE_MOVIE + THB_SOURCE_MOVIE, + THB_SOURCE_BLEND } ThumbSource; // IB_metadata /* create thumbnail for file and returns new imbuf for thumbnail */ -ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source); +ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source, ImBuf *ibuf); /* read thumbnail for file and returns new imbuf for thumbnail */ ImBuf* IMB_thumb_read(const char* path, ThumbSize size); @@ -70,6 +71,8 @@ ImBuf* IMB_thumb_manage(const char* path, ThumbSize size, ThumbSource source); /* create the necessary dirs to store the thumbnails */ void IMB_thumb_makedirs(); +/* special function for loading a thumbnail embedded into a blend file */ +ImBuf *IMB_loadblend_thumb(const char *path); #endif /* _IMB_THUMBS_H */ diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 45fbf49dbbd..8c75f5ab1ab 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -241,9 +241,8 @@ void IMB_thumb_makedirs() } /* create thumbnail for file and returns new imbuf for thumbnail */ -ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source) +ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source, ImBuf *img) { - ImBuf *img = 0; char uri[URI_MAX]; char desc[URI_MAX+22]; char tpath[FILE_MAX]; @@ -285,8 +284,18 @@ ImBuf* IMB_thumb_create(const char* path, ThumbSize size, ThumbSource source) img = IMB_allocImBuf(0,0,32, IB_rect | IB_metadata, 0); if (!img) return 0; } else { - if (THB_SOURCE_IMAGE == source) { - img = IMB_loadiffname(path, IB_rect | IB_metadata); + if (THB_SOURCE_IMAGE == source || THB_SOURCE_BLEND == source) { + + /* only load if we didnt give an image */ + if(img==NULL) { + if(THB_SOURCE_BLEND == source) { + img = IMB_loadblend_thumb(path); + } + else { + img = IMB_loadiffname(path, IB_rect | IB_metadata); + } + } + if (img != NULL) { stat(path, &info); sprintf(mtime, "%ld", info.st_mtime); @@ -425,10 +434,10 @@ ImBuf* IMB_thumb_manage(const char* path, ThumbSize size, ThumbSource source) IMB_thumb_delete(path, THB_NORMAL); IMB_thumb_delete(path, THB_LARGE); IMB_thumb_delete(path, THB_FAIL); - img = IMB_thumb_create(path, size, source); + img = IMB_thumb_create(path, size, source, NULL); if(!img){ /* thumb creation failed, write fail thumb */ - img = IMB_thumb_create(path, THB_FAIL, source); + img = IMB_thumb_create(path, THB_FAIL, source, NULL); if (img) { /* we don't need failed thumb anymore */ IMB_freeImBuf(img); @@ -438,10 +447,10 @@ ImBuf* IMB_thumb_manage(const char* path, ThumbSize size, ThumbSource source) } } } else { - img = IMB_thumb_create(path, size, source); + img = IMB_thumb_create(path, size, source, NULL); if(!img){ /* thumb creation failed, write fail thumb */ - img = IMB_thumb_create(path, THB_FAIL, source); + img = IMB_thumb_create(path, THB_FAIL, source, NULL); if (img) { /* we don't need failed thumb anymore */ IMB_freeImBuf(img); diff --git a/source/blender/imbuf/intern/thumbs_blend.c b/source/blender/imbuf/intern/thumbs_blend.c new file mode 100644 index 00000000000..f38975f1da1 --- /dev/null +++ b/source/blender/imbuf/intern/thumbs_blend.c @@ -0,0 +1,122 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Campbell Barton. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string.h> + +#include "zlib.h" + +#include "BKE_utildefines.h" +#include "BKE_global.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" +#include "IMB_thumbs.h" + +#include "MEM_guardedalloc.h" + +ImBuf *IMB_loadblend_thumb(const char *path) +{ + char buf[8]; + int code= 0; + char endian, pointer_size; + char endian_switch; + int len, im_len, x, y; + int *rect= NULL; + + gzFile gzfile; + + ImBuf *img; + + /* not necessarily a gzip */ + gzfile = gzopen(path, "rb"); + + if (NULL == gzfile ) { + return NULL; + } + + /* read the blend file header */ + if(gzread(gzfile, buf, 8) < 8) goto thumb_error; + if(strncmp(buf, "BLENDER", 7)) goto thumb_error; + + if(buf[7]=='-') pointer_size= 8; + else if(buf[7]=='_') pointer_size= 4; + else goto thumb_error; + + /* read the next 4 bytes, only need the first char, ignore the version */ + /* endian and vertsion (ignored) */ + if(gzread(gzfile, buf, 4) < 4) goto thumb_error; + + if(buf[0]=='V') endian= B_ENDIAN; /* big: PPC */ + else if(buf[0]=='v') endian= L_ENDIAN; /* little: x86 */ + else goto thumb_error; + + while(gzread(gzfile, &code, 4) == 4) { + endian_switch = ((ENDIAN_ORDER != endian)) ? 1 : 0; + + if(gzread(gzfile, buf, 4) < 4) goto thumb_error; + len = *( (int *)((void *)buf) ); + if(endian_switch) SWITCH_INT(len); + + /* finally read the rest of the bhead struct, pointer and 2 ints */ + if(gzread(gzfile, buf, pointer_size) < pointer_size) goto thumb_error; + if(gzread(gzfile, buf, 8) < 8) goto thumb_error; + /* we dont actually care whats in the bhead */ + + if (code==REND) { + gzseek(gzfile, len, SEEK_CUR); /* skip to the next */ + } + else { + break; + } + } + + /* using 'TEST' since new names segfault when loading in old blenders */ + if(code != TEST) goto thumb_error; + + /* finally malloc and read the data */ + rect= MEM_mallocN(len, "imb_loadblend_thumb"); + + if(gzread(gzfile, rect, len) < len) goto thumb_error; + + /* read ok! */ + gzclose(gzfile); + + x= rect[0]; y= rect[1]; + if(endian_switch) { SWITCH_INT(x); SWITCH_INT(y); } + + im_len = x * y * sizeof(int); + + img = IMB_allocImBuf(x, y, 32, IB_rect | IB_metadata, 0); + + memcpy(img->rect, rect + 2, im_len); + + MEM_freeN(rect); + + return img; + +thumb_error: + gzclose(gzfile); + if(rect) MEM_freeN(rect); + return NULL; +} diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index c981836a8ed..91ebb5d6fb4 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -77,10 +77,15 @@ #include "RNA_access.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" +#include "IMB_thumbs.h" + #include "ED_datafiles.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" +#include "ED_view3d.h" #include "ED_util.h" #include "GHOST_C-api.h" @@ -486,12 +491,53 @@ static void do_history(char *name, ReportList *reports) BKE_report(reports, RPT_ERROR, "Unable to make version backup"); } +/* writes a thumbnail for a blendfile */ +static void writeThumb(const char *path, Scene *scene, int **thumb_pt) +{ + /* will be scaled down, but gives some nice oversampling */ + ImBuf *ibuf; + int *thumb; + + *thumb_pt= NULL; + + if(G.background || scene->camera==NULL) + return; + + thumb = MEM_mallocN(((2 + (BLEN_THUMB_SIZE * BLEN_THUMB_SIZE))) * sizeof(int), "write_file thumb"); + + /* gets scaled to BLEN_THUMB_SIZE */ + ibuf= ED_view3d_draw_offscreen_imbuf_simple(scene, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, OB_SOLID); + + if(ibuf) { + + /* dirty oversampling */ + IMB_scaleImBuf(ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE); + + /* first write into thumb buffer */ + thumb[0] = BLEN_THUMB_SIZE; + thumb[1] = BLEN_THUMB_SIZE; + memcpy(thumb + 2, ibuf->rect, BLEN_THUMB_SIZE * BLEN_THUMB_SIZE * sizeof(int)); + + /* the image is scaled here */ + ibuf= IMB_thumb_create(path, THB_NORMAL, THB_SOURCE_BLEND, ibuf); + } + + if (ibuf) { + IMB_freeImBuf(ibuf); + } + + /* must be freed by caller */ + *thumb_pt= thumb; +} + int WM_write_file(bContext *C, char *target, int fileflags, ReportList *reports) { Library *li; int len; char di[FILE_MAX]; - + + int *thumb= NULL; + len = strlen(target); if (len == 0) { @@ -532,7 +578,10 @@ int WM_write_file(bContext *C, char *target, int fileflags, ReportList *reports) do_history(di, reports); - if (BLO_write_file(CTX_data_main(C), di, fileflags, reports)) { + /* blend file thumbnail */ + writeThumb(di, CTX_data_scene(C), &thumb); + + if (BLO_write_file(CTX_data_main(C), di, fileflags, reports, thumb)) { strcpy(G.sce, di); G.relbase_valid = 1; strcpy(G.main->name, di); /* is guaranteed current file */ @@ -546,7 +595,11 @@ int WM_write_file(bContext *C, char *target, int fileflags, ReportList *reports) else G.fileflags &= ~G_FILE_AUTOPLAY; writeBlog(); - } else { + + if(thumb) MEM_freeN(thumb); + } + else { + if(thumb) MEM_freeN(thumb); return -1; } @@ -571,7 +624,7 @@ int WM_write_homefile(bContext *C, wmOperator *op) /* force save as regular blend file */ fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_LOCK | G_FILE_SIGN); - BLO_write_file(CTX_data_main(C), tstr, fileflags, op->reports); + BLO_write_file(CTX_data_main(C), tstr, fileflags, op->reports, NULL); G.save_over= 0; @@ -640,7 +693,7 @@ void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) fileflags = G.fileflags & ~(G_FILE_COMPRESS|G_FILE_AUTOPLAY |G_FILE_LOCK|G_FILE_SIGN); /* no error reporting to console */ - BLO_write_file(CTX_data_main(C), filename, fileflags, NULL); + BLO_write_file(CTX_data_main(C), filename, fileflags, NULL, NULL); /* do timer after file write, just in case file write takes a long time */ wm->autosavetimer= WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime*60.0); |