Changeset View
Changeset View
Standalone View
Standalone View
source/blender/imbuf/intern/thumbs.c
| Show All 26 Lines | |||||
| /** \file blender/imbuf/intern/thumbs.c | /** \file blender/imbuf/intern/thumbs.c | ||||
| * \ingroup imbuf | * \ingroup imbuf | ||||
| */ | */ | ||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include "MEM_guardedalloc.h" | |||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_string.h" | #include "BLI_string.h" | ||||
| #include "BLI_path_util.h" | #include "BLI_path_util.h" | ||||
| #include "BLI_fileops.h" | #include "BLI_fileops.h" | ||||
| #include "BLI_ghash.h" | |||||
| #include "BLI_hash_md5.h" | #include "BLI_hash_md5.h" | ||||
| #include "BLI_system.h" | #include "BLI_system.h" | ||||
| #include "BLI_threads.h" | |||||
| #include BLI_SYSTEM_PID_H | #include BLI_SYSTEM_PID_H | ||||
| #include "BLO_readfile.h" /* XXX Hope this is not badlevel! */ | |||||
| #include "IMB_imbuf_types.h" | #include "IMB_imbuf_types.h" | ||||
| #include "IMB_imbuf.h" | #include "IMB_imbuf.h" | ||||
| #include "IMB_thumbs.h" | #include "IMB_thumbs.h" | ||||
| #include "IMB_metadata.h" | #include "IMB_metadata.h" | ||||
| #include <ctype.h> | #include <ctype.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <time.h> | #include <time.h> | ||||
| ▲ Show 20 Lines • Show All 254 Lines • ▼ Show 20 Lines | #endif | ||||
| if (get_thumb_dir(tpath, THB_FAIL)) { | if (get_thumb_dir(tpath, THB_FAIL)) { | ||||
| BLI_dir_create_recursive(tpath); | BLI_dir_create_recursive(tpath); | ||||
| } | } | ||||
| } | } | ||||
| /* create thumbnail for file and returns new imbuf for thumbnail */ | /* create thumbnail for file and returns new imbuf for thumbnail */ | ||||
| static ImBuf *thumb_create_ex( | static ImBuf *thumb_create_ex( | ||||
| const char *file_path, const char *uri, const char *thumb, const bool use_hash, const char *hash, | const char *file_path, const char *uri, const char *thumb, const bool use_hash, const char *hash, | ||||
| const char *blen_group, const char *blen_id, | |||||
| ThumbSize size, ThumbSource source, ImBuf *img) | ThumbSize size, ThumbSource source, ImBuf *img) | ||||
| { | { | ||||
| char desc[URI_MAX + 22]; | char desc[URI_MAX + 22]; | ||||
| char tpath[FILE_MAX]; | char tpath[FILE_MAX]; | ||||
| char tdir[FILE_MAX]; | char tdir[FILE_MAX]; | ||||
| char temp[FILE_MAX]; | char temp[FILE_MAX]; | ||||
| char mtime[40] = "0"; /* in case we can't stat the file */ | char mtime[40] = "0"; /* in case we can't stat the file */ | ||||
| char cwidth[40] = "0"; /* in case images have no data */ | char cwidth[40] = "0"; /* in case images have no data */ | ||||
| ▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | else { | ||||
| if (ELEM(source, THB_SOURCE_IMAGE, THB_SOURCE_BLEND, THB_SOURCE_FONT)) { | if (ELEM(source, THB_SOURCE_IMAGE, THB_SOURCE_BLEND, THB_SOURCE_FONT)) { | ||||
| /* only load if we didnt give an image */ | /* only load if we didnt give an image */ | ||||
| if (img == NULL) { | if (img == NULL) { | ||||
| switch (source) { | switch (source) { | ||||
| case THB_SOURCE_IMAGE: | case THB_SOURCE_IMAGE: | ||||
| img = IMB_loadiffname(file_path, IB_rect | IB_metadata, NULL); | img = IMB_loadiffname(file_path, IB_rect | IB_metadata, NULL); | ||||
| break; | break; | ||||
| case THB_SOURCE_BLEND: | case THB_SOURCE_BLEND: | ||||
| img = IMB_thumb_load_blend(file_path); | img = IMB_thumb_load_blend(file_path, blen_group, blen_id); | ||||
| break; | break; | ||||
| case THB_SOURCE_FONT: | case THB_SOURCE_FONT: | ||||
| img = IMB_thumb_load_font(file_path, tsize, tsize); | img = IMB_thumb_load_font(file_path, tsize, tsize); | ||||
| break; | break; | ||||
| default: | default: | ||||
| BLI_assert(0); /* This should never happen */ | BLI_assert(0); /* This should never happen */ | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | #endif | ||||
| BLI_rename(temp, tpath); | BLI_rename(temp, tpath); | ||||
| } | } | ||||
| } | } | ||||
| return img; | return img; | ||||
| } | } | ||||
| static ImBuf *thumb_create_or_fail( | static ImBuf *thumb_create_or_fail( | ||||
| const char *file_path, const char *uri, const char *thumb, const bool use_hash, const char *hash, | const char *file_path, const char *uri, const char *thumb, const bool use_hash, const char *hash, | ||||
| ThumbSize size, ThumbSource source) | const char *blen_group, const char *blen_id, ThumbSize size, ThumbSource source) | ||||
| { | { | ||||
| ImBuf *img = thumb_create_ex(file_path, uri, thumb, use_hash, hash, size, source, NULL); | ImBuf *img = thumb_create_ex(file_path, uri, thumb, use_hash, hash, blen_group, blen_id, size, source, NULL); | ||||
| if (!img) { | if (!img) { | ||||
| /* thumb creation failed, write fail thumb */ | /* thumb creation failed, write fail thumb */ | ||||
| img = thumb_create_ex(file_path, uri, thumb, use_hash, hash, THB_FAIL, source, NULL); | img = thumb_create_ex(file_path, uri, thumb, use_hash, hash, blen_group, blen_id, THB_FAIL, source, NULL); | ||||
| if (img) { | if (img) { | ||||
| /* we don't need failed thumb anymore */ | /* we don't need failed thumb anymore */ | ||||
| IMB_freeImBuf(img); | IMB_freeImBuf(img); | ||||
| img = NULL; | img = NULL; | ||||
| } | } | ||||
| } | } | ||||
| return img; | return img; | ||||
| } | } | ||||
| ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, ImBuf *img) | ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, ImBuf *img) | ||||
| { | { | ||||
| char uri[URI_MAX] = ""; | char uri[URI_MAX] = ""; | ||||
| char thumb_name[40]; | char thumb_name[40]; | ||||
| uri_from_filename(path, uri); | uri_from_filename(path, uri); | ||||
| thumbname_from_uri(uri, thumb_name, sizeof(thumb_name)); | thumbname_from_uri(uri, thumb_name, sizeof(thumb_name)); | ||||
| return thumb_create_ex(path, uri, thumb_name, false, THUMB_DEFAULT_HASH, size, source, img); | return thumb_create_ex(path, uri, thumb_name, false, THUMB_DEFAULT_HASH, NULL, NULL, size, source, img); | ||||
| } | } | ||||
| /* read thumbnail for file and returns new imbuf for thumbnail */ | /* read thumbnail for file and returns new imbuf for thumbnail */ | ||||
| ImBuf *IMB_thumb_read(const char *path, ThumbSize size) | ImBuf *IMB_thumb_read(const char *path, ThumbSize size) | ||||
| { | { | ||||
| char thumb[FILE_MAX]; | char thumb[FILE_MAX]; | ||||
| char uri[URI_MAX]; | char uri[URI_MAX]; | ||||
| ImBuf *img = NULL; | ImBuf *img = NULL; | ||||
| Show All 29 Lines | |||||
| /* create the thumb if necessary and manage failed and old thumbs */ | /* create the thumb if necessary and manage failed and old thumbs */ | ||||
| ImBuf *IMB_thumb_manage(const char *org_path, ThumbSize size, ThumbSource source) | ImBuf *IMB_thumb_manage(const char *org_path, ThumbSize size, ThumbSource source) | ||||
| { | { | ||||
| char thumb_path[FILE_MAX]; | char thumb_path[FILE_MAX]; | ||||
| char thumb_name[40]; | char thumb_name[40]; | ||||
| char uri[URI_MAX]; | char uri[URI_MAX]; | ||||
| char path_buff[FILE_MAX]; | |||||
| const char *file_path; | const char *file_path; | ||||
| const char *path; | const char *path; | ||||
| BLI_stat_t st; | BLI_stat_t st; | ||||
| ImBuf *img = NULL; | ImBuf *img = NULL; | ||||
| char *blen_group = NULL, *blen_id = NULL; | |||||
| path = file_path = org_path; | path = file_path = org_path; | ||||
| if (source == THB_SOURCE_BLEND) { | |||||
| if (BLO_library_path_explode(path, path_buff, &blen_group, &blen_id)) { | |||||
| if (blen_group) { | |||||
| if (!blen_id) { | |||||
| /* No preview for blen groups */ | |||||
| return NULL; | |||||
| } | |||||
| file_path = path_buff; /* path needs to be a valid file! */ | |||||
| } | |||||
| } | |||||
| } | |||||
| if (BLI_stat(file_path, &st) == -1) { | if (BLI_stat(file_path, &st) == -1) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| if (!uri_from_filename(path, uri)) { | if (!uri_from_filename(path, uri)) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| if (thumbpath_from_uri(uri, thumb_path, sizeof(thumb_path), THB_FAIL)) { | if (thumbpath_from_uri(uri, thumb_path, sizeof(thumb_path), THB_FAIL)) { | ||||
| /* failure thumb exists, don't try recreating */ | /* failure thumb exists, don't try recreating */ | ||||
| if (BLI_exists(thumb_path)) { | if (BLI_exists(thumb_path)) { | ||||
| /* clear out of date fail case */ | /* clear out of date fail case (note for blen IDs we use blender file itself here) */ | ||||
| if (BLI_file_older(thumb_path, file_path)) { | if (BLI_file_older(thumb_path, file_path)) { | ||||
| BLI_delete(thumb_path, false, false); | BLI_delete(thumb_path, false, false); | ||||
| } | } | ||||
| else { | else { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| Show All 32 Lines | else { | ||||
| if (regenerate) { | if (regenerate) { | ||||
| /* recreate all thumbs */ | /* recreate all thumbs */ | ||||
| IMB_freeImBuf(img); | IMB_freeImBuf(img); | ||||
| img = NULL; | img = NULL; | ||||
| IMB_thumb_delete(path, THB_NORMAL); | IMB_thumb_delete(path, THB_NORMAL); | ||||
| IMB_thumb_delete(path, THB_LARGE); | IMB_thumb_delete(path, THB_LARGE); | ||||
| IMB_thumb_delete(path, THB_FAIL); | IMB_thumb_delete(path, THB_FAIL); | ||||
| img = thumb_create_or_fail(file_path, uri, thumb_name, use_hash, thumb_hash, size, source); | img = thumb_create_or_fail( | ||||
| file_path, uri, thumb_name, use_hash, thumb_hash, blen_group, blen_id, size, source); | |||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| char thumb_hash[33]; | char thumb_hash[33]; | ||||
| const bool use_hash = thumbhash_from_path(file_path, source, thumb_hash); | const bool use_hash = thumbhash_from_path(file_path, source, thumb_hash); | ||||
| img = thumb_create_or_fail(file_path, uri, thumb_name, use_hash, thumb_hash, size, source); | img = thumb_create_or_fail( | ||||
| file_path, uri, thumb_name, use_hash, thumb_hash, blen_group, blen_id, size, source); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return img; | return img; | ||||
| } | } | ||||
| /* ***** Threading ***** */ | |||||
| /* Thumbnail handling is not really threadsafe in itself. | |||||
| * However, as long as we do not operate on the same file, we shall have no collision. | |||||
| * So idea is to 'lock' a given source file path. | |||||
| */ | |||||
| static struct IMBThumbLocks { | |||||
| GSet *locked_paths; | |||||
| int lock_counter; | |||||
| ThreadCondition cond; | |||||
| } thumb_locks = {0}; | |||||
| void IMB_thumb_locks_acquire(void) { | |||||
| BLI_lock_thread(LOCK_IMAGE); | |||||
| if (thumb_locks.lock_counter == 0) { | |||||
| BLI_assert(thumb_locks.locked_paths == NULL); | |||||
| thumb_locks.locked_paths = BLI_gset_str_new(__func__); | |||||
| BLI_condition_init(&thumb_locks.cond); | |||||
| } | |||||
| thumb_locks.lock_counter++; | |||||
| BLI_assert(thumb_locks.locked_paths != NULL); | |||||
| BLI_assert(thumb_locks.lock_counter > 0); | |||||
| BLI_unlock_thread(LOCK_IMAGE); | |||||
| } | |||||
| void IMB_thumb_locks_release(void) { | |||||
| BLI_lock_thread(LOCK_IMAGE); | |||||
| BLI_assert((thumb_locks.locked_paths != NULL) && (thumb_locks.lock_counter > 0)); | |||||
| thumb_locks.lock_counter--; | |||||
| if (thumb_locks.lock_counter == 0) { | |||||
| BLI_gset_free(thumb_locks.locked_paths, MEM_freeN); | |||||
| thumb_locks.locked_paths = NULL; | |||||
| BLI_condition_end(&thumb_locks.cond); | |||||
| } | |||||
| BLI_unlock_thread(LOCK_IMAGE); | |||||
| } | |||||
| void IMB_thumb_lock_path(const char *path) { | |||||
| void *key = BLI_strdup(path); | |||||
| BLI_lock_thread(LOCK_IMAGE); | |||||
| BLI_assert((thumb_locks.locked_paths != NULL) && (thumb_locks.lock_counter > 0)); | |||||
| if (thumb_locks.locked_paths) { | |||||
| while (!BLI_gset_add(thumb_locks.locked_paths, key)) { | |||||
| BLI_condition_wait_global_mutex(&thumb_locks.cond, LOCK_IMAGE); | |||||
| } | |||||
| // printf("%s: locked %s\n", __func__, path); | |||||
| } | |||||
| BLI_unlock_thread(LOCK_IMAGE); | |||||
| } | |||||
| void IMB_thumb_unlock_path(const char *path) { | |||||
| const void *key = path; | |||||
| BLI_lock_thread(LOCK_IMAGE); | |||||
| BLI_assert((thumb_locks.locked_paths != NULL) && (thumb_locks.lock_counter > 0)); | |||||
| if (thumb_locks.locked_paths) { | |||||
| if (!BLI_gset_remove(thumb_locks.locked_paths, key, MEM_freeN)) { | |||||
| BLI_assert(0); | |||||
| } | |||||
| BLI_condition_notify_all(&thumb_locks.cond); | |||||
| // printf("%s: UN-locked %s\n", __func__, path); | |||||
| } | |||||
| BLI_unlock_thread(LOCK_IMAGE); | |||||
| } | |||||