[gimp-gap] Performance relevant changes for storyboard when rendering images.
- From: Wolfgang Hofer <wolfgangh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp-gap] Performance relevant changes for storyboard when rendering images.
- Date: Wed, 26 Sep 2012 15:42:42 +0000 (UTC)
commit 5e5492f38a69883285664ff51c441ef3ea537f40
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date: Wed Sep 26 17:37:50 2012 +0200
Performance relevant changes for storyboard when rendering images.
ChangeLog | 36 ++
gap/gap_frame_fetcher.c | 660 +++++++++++++++++++++++++++++-----
gap/gap_frame_fetcher.h | 43 ++-
gap/gap_image.c | 23 +-
gap/gap_story_render_processor.c | 741 ++++++++++++++++++++++++++++++++++++--
5 files changed, 1381 insertions(+), 122 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 75b6853..651960f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,39 @@
+2012-09-26 Wolfgang Hofer <hof gimp org>
+
+- Performance relevant changes for storyboard when rendering
+ large images.
+ o) gap_frame_fetcher supports caching of prescaled images.
+ o) storyboard render processor look ahead when processing
+ frame types GAP_FRN_IMAGE or GAP_FRN_ANIMIMAGE
+ and calculate the max prescale size.
+
+ look ahead as far as the same imagename is used.
+
+ procedure: p_stb_render_image_or_animimage
+
+ - "gap-image-delete-workarond"
+
+ The workaround disables undo and scales the image down to miniumum size
+ before calling the gimp_image_delete procedure.
+ this way memory resources for big layers will be freed up immediate.
+
+ Note that this workaround implematation is already present in previous
+ GIMP-GAP versions but now can be turned off via gimprc configuration.
+
+ When using GIMP-GAP with gimp-2.6.x it is recommanded to enable this workaround
+ (otherwise encoding videos or stroryboard editing may quickly run out of memory
+ because lots of internally rendering is done by creating temporary images
+ and deletes them immediate after processing).
+
+ This workaround may not be required when GIMP-GAP is used with newer gimp
+ versions. (not yet tested)
+
+ * gap/gap_image.c
+ * gap/gap_frame_fetcher.c [.h]
+ * gap/gap_story_render_processor.c
+
+
+
2012-08-28 Wolfgang Hofer <hof gimp org>
- Speed up for the BlendFill filter.
diff --git a/gap/gap_frame_fetcher.c b/gap/gap_frame_fetcher.c
index c6d4814..bbdfdfa 100644
--- a/gap/gap_frame_fetcher.c
+++ b/gap/gap_frame_fetcher.c
@@ -102,13 +102,29 @@
/* the lists of cached images and duplicates are implemented via GIMP image parasites,
* where images are simply loaded by GIMP without adding a display and marked with a non persistent parasite.
- * the GAP_IMAGE_CACHE_PARASITE holds the modification timestamp (mtime), gint32 ffetch_user_id and full filename (inclusive terminating 0)
+ * the GAP_IMAGE_CACHE_PARASITE
+ * holds the modification timestamp (mtime), gint32 ffetch_user_id
+ * gint32 type (GAP_IMAGE_CACHE_TYPE_ORIGINAL_SIZE or GAP_IMAGE_CACHE_TYPE_PRESCALE_ENABLED)
+ * gint32 orig_width, gint32 orig_height (the unscaled original size of the image)
+ * and full filename (inclusive terminating 0)
* the GAP_IMAGE_DUP_CACHE_PARASITE holds the gint32 ffetch_user_id
*/
#define GAP_IMAGE_CACHE_PARASITE "GAP-IMAGE-CACHE-PARASITE"
#define GAP_IMAGE_DUP_CACHE_PARASITE "GAP-IMAGE-DUP-CACHE-PARASITE"
+#define GAP_IMAGE_CACHE_TYPE_ORIGINAL_SIZE 0
+#define GAP_IMAGE_CACHE_TYPE_PRESCALE_ENABLED 1
+
+typedef struct GapImageCacheParasitePointers {
+ gint32 *mtime_ptr;
+ gint32 *ffetch_id_ptr;
+ gint32 *type_ptr;
+ gint32 *orig_width_ptr;
+ gint32 *orig_height_ptr;
+ gchar *filename_ptr;
+} GapImageCacheParasitePointers;
+
typedef struct GapFFetchResourceUserElem
{
@@ -116,6 +132,15 @@ typedef struct GapFFetchResourceUserElem
void *next;
} GapFFetchResourceUserElem;
+#define MAX_OLD_IMAGE_IDS 10
+
+typedef struct GapImageChacheInfo /* nickname: cinf */
+{
+ gint32 old_cached_image_ids[MAX_OLD_IMAGE_IDS];
+ gint32 number_of_old_cached_images;
+ gint32 number_of_cached_images;
+} GapImageChacheInfo;
+
/* -------- types for the video handle cache ------- */
@@ -153,6 +178,20 @@ static GapFFetchResourceUserElem *global_rsource_users = NULL;
* FRAME FETCHER procedures *
*************************************************************
*/
+static void p_init_GapImageCacheParasitePointers(GapImageCacheParasitePointers *paraPtr
+ , guchar *parasite_data
+ );
+static gint32 p_find_cache_image(const char* filename, gint32 ffetch_user_id
+ , gint32 cachedImageType
+ , GapImageChacheInfo *cinf
+ , gint32 *originalWidthPtr, gint32 *originalHeightPtr
+ );
+static gint32 p_load_image_and_add_to_cache(const char* filename, gint32 ffetch_user_id
+ , gboolean addToCache, gint32 cachedImageType
+ , GapImageChacheInfo *cinf
+ , gint32 *originalWidthPtr, gint32 *originalHeightPtr
+ );
+
static gint32 p_load_cache_image(const char* filename, gint32 ffetch_user_id, gboolean addToCache);
static void p_drop_image_cache(void);
#ifdef GAP_ENABLE_VIDEOAPI_SUPPORT
@@ -169,6 +208,25 @@ static gint32 p_get_ffetch_max_img_cache_elements();
static gint32 p_get_ffetch_max_gvc_cache_elements();
static gint32 ffetch_gva_frames_to_keep_cached();
+/* ----------------------------------------------------
+ * p_init_GapImageCacheParasitePointers
+ * ----------------------------------------------------
+ * init pointers in the GapImageCacheParasitePointers to access the
+ * parasite data.
+ * Note that the filename_ptr refers to the last entry that has variable size
+ * and is a \0 terminated string.
+ */
+static void
+p_init_GapImageCacheParasitePointers(GapImageCacheParasitePointers *paraPtr, guchar *parasite_data)
+{
+ paraPtr->mtime_ptr = (gint32 *)parasite_data;
+ paraPtr->ffetch_id_ptr = (gint32 *)¶site_data[sizeof(gint32)];
+ paraPtr->type_ptr = (gint32 *)¶site_data[2 * sizeof(gint32)];
+ paraPtr->orig_width_ptr = (gint32 *)¶site_data[3 * sizeof(gint32)];
+ paraPtr->orig_height_ptr = (gint32 *)¶site_data[4 * sizeof(gint32)];
+ paraPtr->filename_ptr = (gchar *) ¶site_data[5 * sizeof(gint32)];
+
+} /* end p_init_GapImageCacheParasitePointers */
/* ----------------------------------------------------
* p_get_ffetch_max_img_cache_elements
@@ -272,36 +330,54 @@ p_get_ffetch_gva_frames_to_keep_cached()
return (value);
} /* end p_get_ffetch_gva_frames_to_keep_cached */
+
/* ----------------------------------------------------
- * p_load_cache_image
+ * p_find_cache_image
* ----------------------------------------------------
- * load an image from cache or from file (in case image is not already cached)
- * in case the flag addToCache is TRUE the image will be automatically added
- * to the cache after read from file operation.
+ * find an image with filename and matching cachedImageType
+ * GAP_IMAGE_CACHE_TYPE_ORIGINAL_SIZE or
+ * GAP_IMAGE_CACHE_TYPE_PRESCALE_ENABLED
+ * in the cache.
+ * returns the imageId of the cached image or -1 if not found in the cache.
+ *
+ * OUT parameters
+ * first_cached_image_id .. id of the 1st image in the cache.
+ * number_of_cached_images .. total number of cached images (only valid when image was NOT FOUND!)
+ * originalWidth[Height]Ptr .. the unscaled original size of the image.
*/
static gint32
-p_load_cache_image(const char* filename, gint32 ffetch_user_id, gboolean addToCache)
+p_find_cache_image(const char* filename, gint32 ffetch_user_id
+ , gint32 cachedImageType
+ , GapImageChacheInfo *cinf
+ , gint32 *originalWidthPtr, gint32 *originalHeightPtr
+ )
{
gint32 l_image_id;
- char *l_filename;
gint32 *images;
gint nimages;
gint l_idi;
- gint l_number_of_cached_images;
- gint32 l_first_cached_image_id;
GimpParasite *l_parasite;
-
+ cinf->old_cached_image_ids[0] = -1;
+ cinf->number_of_old_cached_images = 0;
+ cinf->number_of_cached_images = 0;
+ *originalWidthPtr = 0;
+ *originalHeightPtr = 0;
+ if(gap_debug)
+ {
+ printf("p_find_cache_image START cachedImageType:%d %s\n"
+ ,(int)cachedImageType
+ ,filename
+ );
+ }
if(filename == NULL)
{
- printf("p_load_cache_image: ** ERROR cant load filename == NULL! pid:%d\n", (int)gap_base_getpid());
+ printf("p_find_cache_image: ** ERROR cant load filename == NULL! pid:%d\n", (int)gap_base_getpid());
return -1;
}
l_image_id = -1;
- l_first_cached_image_id = -1;
- l_number_of_cached_images = 0;
images = gimp_image_list(&nimages);
for(l_idi=0; l_idi < nimages; l_idi++)
{
@@ -309,29 +385,32 @@ p_load_cache_image(const char* filename, gint32 ffetch_user_id, gboolean addToCa
if(l_parasite)
{
- gint32 *mtime_ptr;
- gint32 *ffetch_id_ptr;
- gchar *filename_ptr;
+ GapImageCacheParasitePointers para;
+ GapImageCacheParasitePointers *paraPtr;
- mtime_ptr = (gint32 *) l_parasite->data;
- ffetch_id_ptr = (gint32 *)&l_parasite->data[sizeof(gint32)];
- filename_ptr = (gchar *)&l_parasite->data[sizeof(gint32) + sizeof(gint32)];
+ paraPtr = ¶
+ p_init_GapImageCacheParasitePointers(paraPtr, l_parasite->data);
+
- l_number_of_cached_images++;
- if (l_first_cached_image_id < 0)
+ cinf->number_of_cached_images++;
+ if (cinf->number_of_old_cached_images < MAX_OLD_IMAGE_IDS)
{
- l_first_cached_image_id = images[l_idi];
+ cinf->old_cached_image_ids[cinf->number_of_old_cached_images] = images[l_idi];
+ cinf->number_of_old_cached_images++;
}
- if(strcmp(filename, filename_ptr) == 0)
+ if ((*(paraPtr->type_ptr) == cachedImageType)
+ && (strcmp(filename, paraPtr->filename_ptr) == 0))
{
gint32 mtimefile;
mtimefile = gap_file_get_mtime(filename);
- if(mtimefile == *mtime_ptr)
+ if(mtimefile == *(paraPtr->mtime_ptr))
{
/* image found in cache */
l_image_id = images[l_idi];
+ *originalWidthPtr = *(paraPtr->orig_width_ptr);
+ *originalHeightPtr = *(paraPtr->orig_height_ptr);
}
else
{
@@ -342,10 +421,10 @@ p_load_cache_image(const char* filename, gint32 ffetch_user_id, gboolean addToCa
{
printf("FrameFetcher: DELETE because mtime changed : (image_id:%d) ffetchId:%d name:%s mtimefile:%d mtimecache:%d pid:%d\n"
, (int)images[l_idi]
- , (int)*ffetch_id_ptr
+ , (int)*(paraPtr->ffetch_id_ptr)
, gimp_image_get_filename(images[l_idi])
, (int)mtimefile
- , (int)*mtime_ptr
+ , (int)*(paraPtr->mtime_ptr)
, (int)gap_base_getpid()
);
}
@@ -361,16 +440,59 @@ p_load_cache_image(const char* filename, gint32 ffetch_user_id, gboolean addToCa
g_free(images);
}
+
if (l_image_id >= 0)
{
if(gap_debug)
{
- printf("FrameFetcher: p_load_cache_image CACHE-HIT :%s (image_id:%d) pid:%d\n"
+ printf("FrameFetcher: p_find_cache_image CACHE-HIT :%s (image_id:%d) pid:%d\n"
, filename, (int)l_image_id, (int)gap_base_getpid());
}
return(l_image_id);
}
+ if(gap_debug)
+ {
+ printf("p_find_cache_image END Not found in cache %s\n"
+ ,filename
+ );
+ }
+
+ return (-1); /* indicates image not found in cache */
+
+} /* end p_find_cache_image */
+
+
+
+
+/* ----------------------------------------------------
+ * p_load_image_and_add_to_cache
+ * ----------------------------------------------------
+ * load the image from file (depends on enableLoad flag)
+ *
+ * in case the flag addToCache is TRUE the loaded image will be automatically added
+ * to the cache after read from file operation.
+ * This will also delete the the first cached imge in case the number_of_cached_images
+ * exceded the configured limit (gimprc paramter "gap_ffetch_max_img_cache_elements")
+ */
+static gint32
+p_load_image_and_add_to_cache(const char* filename, gint32 ffetch_user_id
+ , gboolean addToCache, gint32 cachedImageType
+ , GapImageChacheInfo *cinf
+ , gint32 *originalWidthPtr, gint32 *originalHeightPtr
+ )
+{
+ gint32 l_image_id;
+ char *l_filename;
+
+ GimpParasite *l_parasite;
+
+ if(filename == NULL)
+ {
+ printf("p_load_image_and_add_to_cache: ** ERROR cant load filename == NULL! pid:%d\n", (int)gap_base_getpid());
+ return -1;
+ }
+
l_filename = g_strdup(filename);
l_image_id = gap_lib_load_image(l_filename);
if(gap_debug)
@@ -379,64 +501,146 @@ p_load_cache_image(const char* filename, gint32 ffetch_user_id, gboolean addToCa
, l_filename, (int)l_image_id, (int)gap_base_getpid());
}
- if((l_image_id >= 0) && (addToCache == TRUE))
+ if(l_image_id >= 0)
{
- guchar *parasite_data;
- gint32 parasite_size;
- gint32 *parasite_mtime_ptr;
- gint32 *parasite_ffetch_id_ptr;
- gchar *parasite_filename_ptr;
- gint32 len_filename0; /* filename length including the terminating 0 */
-
- if (l_number_of_cached_images > p_get_ffetch_max_img_cache_elements())
+ *originalWidthPtr = gimp_image_width(l_image_id);
+ *originalHeightPtr = gimp_image_height(l_image_id);
+
+ if(addToCache == TRUE)
{
- /* the image cache already has more elements than desired,
- * drop the 1st cached image
- */
+ guchar *parasite_data;
+ gint32 parasite_size;
+ gint32 len_filename0; /* filename length including the terminating 0 */
+ GapImageCacheParasitePointers para;
+ GapImageCacheParasitePointers *paraPtr;
+ gint ii;
+
if(gap_debug)
{
- printf("FrameFetcher: DELETE because cache is full: (image_id:%d) name:%s number_of_cached_images:%d pid:%d\n"
- , (int)l_first_cached_image_id
- , gimp_image_get_filename(l_first_cached_image_id)
- , (int)l_number_of_cached_images
- , (int)gap_base_getpid()
- );
+ printf("(L2) number_of_cached_images:%d first_cached_image_id:%d\n"
+ , (int)cinf->number_of_cached_images
+ , (int)cinf->old_cached_image_ids[0]
+ );
+ }
+
+ ii = 0;
+ while ((cinf->number_of_cached_images >= p_get_ffetch_max_img_cache_elements())
+ && (cinf->old_cached_image_ids[ii] >= 0))
+ {
+ /* the image cache already has more elements than desired,
+ * drop the 1st cached image
+ */
+ if(gap_debug)
+ {
+ printf("FrameFetcher: DELETE because cache is full: (image_id:%d) name:%s number_of_cached_images:%d pid:%d\n"
+ , (int)cinf->old_cached_image_ids[ii]
+ , gimp_image_get_filename(cinf->old_cached_image_ids[ii])
+ , (int)cinf->number_of_cached_images
+ , (int)gap_base_getpid()
+ );
+ }
+ gap_image_delete_immediate(cinf->old_cached_image_ids[ii]);
+ cinf->number_of_cached_images--;
+ ii++;
+ if((ii >= MAX_OLD_IMAGE_IDS)
+ || (ii >= cinf->number_of_old_cached_images))
+ {
+ break;
+ }
}
- gap_image_delete_immediate(l_first_cached_image_id);
- }
-
- /* build parasite data including mtime and full filename with terminating 0 byte */
- len_filename0 = strlen(filename) + 1;
- parasite_size = sizeof(gint32) + sizeof(gint32) + len_filename0;
- parasite_data = g_malloc0(parasite_size);
- parasite_mtime_ptr = (gint32 *)parasite_data;
- parasite_ffetch_id_ptr = (gint32 *)¶site_data[sizeof(gint32)];
- parasite_filename_ptr = (gchar *)¶site_data[sizeof(gint32) + sizeof(gint32)];
- *parasite_mtime_ptr = gap_file_get_mtime(filename);
- *parasite_ffetch_id_ptr = ffetch_user_id;
- memcpy(parasite_filename_ptr, filename, len_filename0);
+ /* build parasite data including mtime and full filename with terminating 0 byte */
+ len_filename0 = strlen(filename) + 1;
+ parasite_size = 5 * sizeof(gint32) + len_filename0;
+ parasite_data = g_malloc0(parasite_size);
+
+ paraPtr = ¶
+ p_init_GapImageCacheParasitePointers(paraPtr, parasite_data);
+
+ *(paraPtr->mtime_ptr) = gap_file_get_mtime(filename);
+ *(paraPtr->ffetch_id_ptr) = ffetch_user_id;
+ *(paraPtr->type_ptr) = cachedImageType;
+ *(paraPtr->orig_width_ptr) = gimp_image_width(l_image_id);
+ *(paraPtr->orig_height_ptr) = gimp_image_height(l_image_id);
+ memcpy(paraPtr->filename_ptr, filename, len_filename0);
+
+ /* attach a parasite to mark the image as part of the gap image cache */
+ l_parasite = gimp_parasite_new(GAP_IMAGE_CACHE_PARASITE
+ ,0 /* GIMP_PARASITE_PERSISTENT 0 for non persistent */
+ ,parasite_size
+ ,parasite_data
+ );
+
+ if(l_parasite)
+ {
+ gimp_image_parasite_attach(l_image_id, l_parasite);
+ gimp_parasite_free(l_parasite);
+ }
+ g_free(parasite_data);
- /* attach a parasite to mark the image as part of the gap image cache */
- l_parasite = gimp_parasite_new(GAP_IMAGE_CACHE_PARASITE
- ,0 /* GIMP_PARASITE_PERSISTENT 0 for non persistent */
- ,parasite_size
- ,parasite_data
- );
-
- if(l_parasite)
- {
- gimp_image_parasite_attach(l_image_id, l_parasite);
- gimp_parasite_free(l_parasite);
}
- g_free(parasite_data);
-
+
}
+
g_free(l_filename);
return(l_image_id);
-} /* end p_load_cache_image */
+} /* end p_load_image_and_add_to_cache */
+
+
+
+/* ----------------------------------------------------
+ * p_load_cache_image
+ * ----------------------------------------------------
+ * load an image from cache or from file (in case image is not already cached)
+ * in case the flag addToCache is TRUE the image will be automatically added
+ * to the cache after read from file operation.
+ * Note: this procedure handles only images at original size (GAP_IMAGE_CACHE_TYPE_ORIGINAL_SIZE)
+ * a prescaled cached variant of the image will be ignored (e.g NOT be found by this procedure)
+ */
+static gint32
+p_load_cache_image(const char* filename, gint32 ffetch_user_id, gboolean addToCache)
+{
+ gint32 retImgageId;
+ gint32 originalWidth;
+ gint32 originalHeight;
+ GapImageChacheInfo gapImageChacheInfo;
+ if(gap_debug)
+ {
+ printf("p_load_cache_image START %s\n"
+ ,filename
+ );
+ }
+ retImgageId = p_find_cache_image(filename, ffetch_user_id
+ , GAP_IMAGE_CACHE_TYPE_ORIGINAL_SIZE /* cachedImageType */
+ , &gapImageChacheInfo
+ , &originalWidth, &originalHeight
+ );
+ if (retImgageId < 0)
+ {
+ retImgageId = p_load_image_and_add_to_cache(filename, ffetch_user_id
+ , addToCache
+ , GAP_IMAGE_CACHE_TYPE_ORIGINAL_SIZE /* cachedImageType */
+ , &gapImageChacheInfo
+ , &originalWidth
+ , &originalHeight
+ );
+ }
+
+ if(gap_debug)
+ {
+ printf("p_load_cache_image END id:%d %s\n"
+ ,(int)retImgageId
+ , filename
+ );
+ }
+
+ return (retImgageId);
+
+} /* end p_load_cache_image */
+
+
/* ----------------------------------------------------
* p_drop_image_cache
@@ -760,6 +964,7 @@ gap_frame_fetch_delete_list_of_duplicated_images(gint32 ffetch_user_id)
* gap_frame_fetch_orig_image
* ----------------------------
* returns image_id of the original cached image.
+ * RESTRICTION: the Caller must NOT not modify that image and shall not open a display for it!
*/
gint32
gap_frame_fetch_orig_image(gint32 ffetch_user_id
@@ -771,6 +976,256 @@ gap_frame_fetch_orig_image(gint32 ffetch_user_id
} /* end gap_frame_fetch_orig_image */
+/* -------------------------------
+ * gap_frame_fetch_image_scale
+ * -------------------------------
+ */
+void
+gap_frame_fetch_image_scale(gint32 imageId, gint32 prescaleWidth, gint32 prescaleHeight)
+{
+ if(gap_debug)
+ {
+ char *l_filename;
+ l_filename = gimp_image_get_filename(imageId);
+
+ printf("gap_frame_fetch_image_SCALE id:%d (%dx%d) to newSize ==> (%dx%d) %s\n"
+ ,(int)imageId
+ ,(int)gimp_image_width(imageId)
+ ,(int)gimp_image_height(imageId)
+ ,(int)prescaleWidth
+ ,(int)prescaleHeight
+ ,l_filename
+ );
+
+ if(l_filename != NULL)
+ {
+ g_free(l_filename);
+ }
+ }
+ gimp_image_undo_disable(imageId);
+ gimp_image_scale(imageId, prescaleWidth, prescaleHeight);
+
+} /* end gap_frame_fetch_image_scale */
+
+/* -------------------------------
+ * gap_frame_fetch_image_duplicate
+ * -------------------------------
+ */
+gint32
+gap_frame_fetch_image_duplicate(gint32 imageId)
+{
+ gint32 dupImageId;
+ if(gap_debug)
+ {
+ char *l_filename;
+ l_filename = gimp_image_get_filename(imageId);
+
+ printf("gap_frame_fetch_image_DUPLICATE id:%d (%dx%d) %s\n"
+ ,(int)imageId
+ ,(int)gimp_image_width(imageId)
+ ,(int)gimp_image_height(imageId)
+ ,l_filename
+ );
+
+ if(l_filename != NULL)
+ {
+ g_free(l_filename);
+ }
+ }
+
+ dupImageId = gimp_image_duplicate(imageId);
+ return (dupImageId);
+}
+
+
+/* -------------------------------
+ * p_duplicate_image_for_prescale
+ * -------------------------------
+ * duplicate the specified image
+ * and mark the duplicate as "prescaleEnabled"
+ */
+static gint32
+p_duplicate_image_for_prescale(gint32 origsizeImageId)
+{
+ gint32 retImageId;
+ GimpParasite *l_parasite;
+
+ retImageId = gap_frame_fetch_image_duplicate(origsizeImageId);
+
+ /* change the type information in the image parasite in the copy */
+ l_parasite = gimp_image_parasite_find(retImageId, GAP_IMAGE_CACHE_PARASITE);
+ if (l_parasite)
+ {
+ GapImageCacheParasitePointers para;
+ GapImageCacheParasitePointers *paraPtr;
+
+ paraPtr = ¶
+ p_init_GapImageCacheParasitePointers(paraPtr, l_parasite->data);
+
+ gap_frame_fetch_remove_parasite(retImageId);
+
+ *(paraPtr->type_ptr) = GAP_IMAGE_CACHE_TYPE_PRESCALE_ENABLED;
+ gimp_image_parasite_attach(retImageId, l_parasite);
+
+ gimp_parasite_free(l_parasite);
+ }
+ return (retImageId);
+
+} /* end p_duplicate_image_for_prescale */
+
+
+/* -------------------------------
+ * gap_frame_fetch_prescaled_image
+ * -------------------------------
+ * returns image_id of the prescaled cached image.
+ * RESTRICTION: the Caller must NOT not modify that image and shall not open a display for it!
+ * Note that the returned image can be either the prescaled copy or the cached image at
+ * original size (in case prescale Size not yet known or exactly matching original size.
+ */
+gint32
+gap_frame_fetch_prescaled_image(gint32 ffetch_user_id
+ ,const char *filename /* full filename of the image */
+ ,gboolean addToCache /* enable caching on prescaled image */
+ ,gint32 prescaleWidth /* use 0 in case prescale size is not yet known */
+ ,gint32 prescaleHeight /* use 0 in case prescale size is not yet known */
+ ,gint32 *originalWidthPtr /* OUT: width of the unscaled original image file */
+ ,gint32 *originalHeightPtr /* OUT: width of the unscaled original image file */
+ )
+{
+ GapImageChacheInfo gapImageChacheInfo;
+ gint32 retImageId = -1;
+ gint32 prescaleImageId = -1;
+ gint32 origsizeImageId = -1;
+
+ if(gap_debug)
+ {
+ printf("gap_frame_fetch_prescaled_image START %s\n"
+ ,filename
+ );
+ }
+ /* search in cache for images marked as "prescaleEnabled" */
+ prescaleImageId = p_find_cache_image(filename, ffetch_user_id
+ , GAP_IMAGE_CACHE_TYPE_PRESCALE_ENABLED /* cachedImageType */
+ , &gapImageChacheInfo
+ , originalWidthPtr
+ , originalHeightPtr
+ );
+
+ if (prescaleImageId >= 0)
+ {
+ retImageId = prescaleImageId;
+ gint32 pWidth = gimp_image_width(prescaleImageId);
+ gint32 pHeight = gimp_image_height(prescaleImageId);
+
+ /* check width for re-upscale attempt */
+ if ((prescaleWidth > pWidth)
+ && (*originalWidthPtr > pWidth))
+ {
+ retImageId = -1;
+ }
+
+ /* check height for re-upscale attempt */
+ if ((prescaleHeight > pHeight)
+ && (*originalHeightPtr > pHeight))
+ {
+ retImageId = -1;
+ }
+
+ if (retImageId >= 0)
+ {
+ if ((pWidth != prescaleWidth)
+ || (pHeight != prescaleHeight))
+ {
+ if ((prescaleWidth > 0)
+ && (prescaleHeight >0))
+ {
+ gap_frame_fetch_image_scale(retImageId, prescaleWidth, prescaleHeight);
+ }
+ }
+ return(retImageId);
+ }
+
+
+ if(gap_debug)
+ {
+ printf("gap_frame_fetch_prescaled_image DELETE img: %d from cache %s\n"
+ ,(int)prescaleImageId
+ ,filename
+ );
+ }
+ /* remove prescaleImageId from cache.
+ * The current requested prescale size is greater than the available
+ * prescaled image in the cache AND the original is larger than the cached variante.
+ * in this case remove the prescaled image from the cache
+ * (to force re-caching of a larger version and avoid quality loss
+ * that could be caused by upscaling of an already downscaled image)
+ */
+ gap_image_delete_immediate(prescaleImageId);
+ }
+
+ /* search in cache for non prescaled image at original size (not marked as "prescaleEnabled" */
+ origsizeImageId = p_find_cache_image(filename, ffetch_user_id
+ , GAP_IMAGE_CACHE_TYPE_ORIGINAL_SIZE /* cachedImageType */
+ , &gapImageChacheInfo
+ , originalWidthPtr
+ , originalHeightPtr
+ );
+ if (origsizeImageId >= 0)
+ {
+ if ((gimp_image_width(origsizeImageId) == prescaleWidth)
+ && (gimp_image_height(origsizeImageId) == prescaleHeight))
+ {
+ return (origsizeImageId);
+ }
+ retImageId = p_duplicate_image_for_prescale(origsizeImageId);
+ }
+
+ if (retImageId < 0)
+ {
+ origsizeImageId = p_load_image_and_add_to_cache(filename, ffetch_user_id
+ , addToCache
+ , GAP_IMAGE_CACHE_TYPE_ORIGINAL_SIZE /* cachedImageType */
+ , &gapImageChacheInfo
+ , originalWidthPtr
+ , originalHeightPtr
+ );
+ if (origsizeImageId >= 0)
+ {
+ if ((prescaleWidth <= 0)
+ || (prescaleHeight <= 0))
+ {
+ return (origsizeImageId);
+ }
+ if ((gimp_image_width(origsizeImageId) == prescaleWidth)
+ && (gimp_image_height(origsizeImageId) == prescaleHeight))
+ {
+ return (origsizeImageId);
+ }
+
+
+ retImageId = p_duplicate_image_for_prescale(origsizeImageId);
+ }
+ }
+
+ if (retImageId >= 0)
+ {
+ if ((gimp_image_width(retImageId) != prescaleWidth)
+ || (gimp_image_height(retImageId) != prescaleHeight))
+ {
+ if ((prescaleWidth > 0)
+ && (prescaleHeight >0))
+ {
+ gap_frame_fetch_image_scale(retImageId, prescaleWidth, prescaleHeight);
+ }
+ }
+ }
+
+ return (retImageId);
+
+
+} /* end gap_frame_fetch_prescaled_image */
+
+
/* ----------------------------
* gap_frame_fetch_dup_image
@@ -801,7 +1256,7 @@ gap_frame_fetch_dup_image(gint32 ffetch_user_id
if (stackpos < 0)
{
- dup_image_id = gimp_image_duplicate(image_id);
+ dup_image_id = gap_frame_fetch_image_duplicate(image_id);
gap_frame_fetch_remove_parasite(dup_image_id);
resulting_layer = gap_image_merge_visible_layers(dup_image_id, GIMP_CLIP_TO_IMAGE);
@@ -1079,6 +1534,31 @@ gap_frame_fetch_unregister_user(gint32 ffetch_user_id)
} /* end gap_frame_fetch_unregister_user */
+/* ---------------------------------
+ * gap_frame_fetch_is_image_in_cache
+ * ---------------------------------
+ * checks the image for presence of the parasite that marks the image as member
+ * of the gap frame fetcher cache.
+ * return TRUE if the parasite was found (e.g. image is cache member)
+ */
+gboolean
+gap_frame_fetch_is_image_in_cache(gint32 image_id)
+{
+ GimpParasite *l_parasite;
+
+ l_parasite = gimp_image_parasite_find(image_id, GAP_IMAGE_CACHE_PARASITE);
+
+ if(l_parasite)
+ {
+ gimp_parasite_free(l_parasite);
+ return(TRUE); /* isCacheMember */
+ }
+
+ return (FALSE);
+
+} /* end gap_frame_fetch_is_image_in_cache */
+
+
/* -------------------------------
* gap_frame_fetch_remove_parasite
* -------------------------------
@@ -1117,7 +1597,6 @@ static void
p_dump_resources_gvahand()
{
#ifdef GAP_ENABLE_VIDEOAPI_SUPPORT
- t_GVA_Handle *l_gvahand;
GapFFetchGvahandCacheElem *gvc_ptr;
GapFFetchGvahandCacheElem *gvc_last;
GapFFetchGvahandCache *gvcache;
@@ -1192,14 +1671,14 @@ p_dump_process_resource_usage()
if(rc == 0)
{
printf ("FrameFetcher sysinfo memory in bytes:\n"
- " memory total size: %u\n"
- " memory free: %u\n"
- " shared memory total size: %u\n"
- " memory used by buffers: %u\n"
- " swap total size: %u\n"
- " swap free: %u\n"
- " high memory total size: %u\n"
- " high memory free: %u\n"
+ " memory total size: %lu\n"
+ " memory free: %lu\n"
+ " shared memory total size: %lu\n"
+ " memory used by buffers: %lu\n"
+ " swap total size: %lu\n"
+ " swap free: %lu\n"
+ " high memory total size: %lu\n"
+ " high memory free: %lu\n"
, (info.mem_unit * info.totalram)
, (info.mem_unit * info.freeram)
, (info.mem_unit * info.sharedram)
@@ -1263,20 +1742,19 @@ gap_frame_fetch_dump_resources()
if(l_parasite)
{
- gint32 *mtime_ptr;
- gint32 *ffetch_id_ptr;
- gchar *filename_ptr;
+ GapImageCacheParasitePointers para;
+ GapImageCacheParasitePointers *paraPtr;
- mtime_ptr = (gint32 *) l_parasite->data;
- ffetch_id_ptr = (gint32 *)&l_parasite->data[sizeof(gint32)];
- filename_ptr = (gchar *)&l_parasite->data[sizeof(gint32) + sizeof(gint32)];
+ paraPtr = ¶
+ p_init_GapImageCacheParasitePointers(paraPtr, l_parasite->data);
+
l_number_of_cached_images++;
l_cacheInfoString = g_strdup_printf("Cache member: mtime:%d ffetchId:%d %s"
- ,*mtime_ptr
- ,*ffetch_id_ptr
- ,filename_ptr
+ ,*(paraPtr->mtime_ptr)
+ ,*(paraPtr->ffetch_id_ptr)
+ ,paraPtr->filename_ptr
);
gimp_parasite_free(l_parasite);
diff --git a/gap/gap_frame_fetcher.h b/gap/gap_frame_fetcher.h
index 1134e28..a757ca7 100644
--- a/gap/gap_frame_fetcher.h
+++ b/gap/gap_frame_fetcher.h
@@ -92,9 +92,6 @@ gap_frame_fetch_delete_list_of_duplicated_images(gint32 ffetch_user_id);
* ----------------------------
* returns image_id of the original cached image.
* RESTRICTION: the Caller must NOT not modify that image and shall not open a display for it!
- * In case this image is duplicated, the parasite that marks an image as member of the gap frame fetcher cache
- * must be removed (by calling procedure gap_frame_fetch_remove_parasite on the duplicate)
- * otherwise the duplicate might be unexpectedly deleted when the frame fetcher cache is full.
*/
gint32
gap_frame_fetch_orig_image(gint32 ffetch_user_id
@@ -102,6 +99,22 @@ gap_frame_fetch_orig_image(gint32 ffetch_user_id
,gboolean addToCache /* enable caching */
);
+/* -------------------------------
+ * gap_frame_fetch_prescaled_image
+ * -------------------------------
+ * returns image_id of the prescaled cached image.
+ * NOTE: the returned image MUST NOT not be changed by the caller
+ *
+ */
+gint32
+gap_frame_fetch_prescaled_image(gint32 ffetch_user_id
+ ,const char *filename /* full filename of the image */
+ ,gboolean addToCache /* enable caching on prescaled image */
+ ,gint32 prescaleWidth /* use 0 in case prescale size is not yet known */
+ ,gint32 prescaleHeight /* use 0 in case prescale size is not yet known */
+ ,gint32 *originalWidthPtr /* OUT: width of the unscaled original image file */
+ ,gint32 *originalHeightPtr /* OUT: width of the unscaled original image file */
+ );
/* ----------------------------
* gap_frame_fetch_dup_image
@@ -135,6 +148,30 @@ gap_frame_fetch_dup_video(gint32 ffetch_user_id
,const char *preferred_decoder
);
+/* -------------------------------
+ * gap_frame_fetch_image_scale
+ * -------------------------------
+ */
+void
+gap_frame_fetch_image_scale(gint32 imageId, gint32 width, gint32 height);
+
+/* -------------------------------
+ * gap_frame_fetch_image_duplicate
+ * -------------------------------
+ */
+gint32
+gap_frame_fetch_image_duplicate(gint32 imageId);
+
+
+/* ---------------------------------
+ * gap_frame_fetch_is_image_in_cache
+ * ---------------------------------
+ * checks the image for presence of the parasite that marks the image as member
+ * of the gap frame fetcher cache.
+ * return TRUE if the parasite was found (e.g. image is cache member)
+ */
+gboolean
+gap_frame_fetch_is_image_in_cache(gint32 image_id);
/* -------------------------------
* gap_frame_fetch_remove_parasite
diff --git a/gap/gap_image.c b/gap/gap_image.c
index 90a49b7..794748a 100644
--- a/gap/gap_image.c
+++ b/gap/gap_image.c
@@ -34,6 +34,7 @@
#include <gap_image.h>
+#include <gap_base.h>
#include <gap_layer_copy.h>
extern int gap_debug;
@@ -44,18 +45,32 @@ extern int gap_debug;
* delete image (with workaround to ensure that most of the
* allocatd memory is freed)
* ============================================================================
+ * The workaround disables undo and scales the image down to miniumum size
+ * before calling the gimp_image_delete procedure.
+ * this way memory resources for big layers will be freed up immediate.
*/
void
gap_image_delete_immediate (gint32 image_id)
{
- if(gap_debug) printf("gap_image_delete_immediate: SCALED down to 2x2 id = %d (workaround for gimp_image-delete problem)\n", (int)image_id);
-
+ gboolean imageDeleteWorkaroundDefault = TRUE;
+ if(gap_base_get_gimprc_gboolean_value("gap-image-delete-workaround"
+ , imageDeleteWorkaroundDefault))
+ {
+ if(gap_debug)
+ {
+ printf("gap_image_delete_immediate: SCALED down to 2x2 id = %d (workaround for gimp_image-delete problem)\n"
+ , (int)image_id
+ );
+ }
+
gimp_image_undo_disable(image_id);
- gimp_image_scale(image_id, 2, 2);
+ gimp_image_scale_full(image_id, 2, 2, 0 /*INTERPOLATION_NONE*/);
gimp_image_undo_enable(image_id); /* clear undo stack */
- gimp_image_delete(image_id);
+ }
+
+ gimp_image_delete(image_id);
} /* end gap_image_delete_immediate */
diff --git a/gap/gap_story_render_processor.c b/gap/gap_story_render_processor.c
index 0ade8ff..b9d7dd1 100644
--- a/gap/gap_story_render_processor.c
+++ b/gap/gap_story_render_processor.c
@@ -184,6 +184,8 @@ typedef struct StbMutexPool /* mutxp */
#define GVAHAND_HOLDER_RANK_4 4
#define GVAHAND_HOLDER_RANK_MAX_LEVEL 5
+#define GAP_VIDEO_STORYBOARD_PRESCALE_ENABLE_DOWNSCALE_CHAIN "video-storyboard-prescale-enable-downscale-chain"
+
extern int gap_debug; /* 1 == print debug infos , 0 dont print debug infos */
@@ -446,9 +448,30 @@ static void p_split_delace_value(gdouble delace
, gdouble *threshold_ptr);
static void p_conditional_delace_drawable(GapStbFetchData *gfd, gint32 drawable_id);
+static void p_init_gfd(GapStbFetchData *gfd);
+
+static gboolean p_is_larger_image_variant_expected(GapStbFetchData *gfdCurrent
+ , GapStoryRenderVidHandle *vidhand
+ , gint32 master_frame_nr
+ , gint32 vid_width
+ , gint32 vid_height
+ , gint32 originalWidth
+ , gint32 originalHeight
+ , gint32 currentPrescaleWidth
+ , gint32 currentPrescaleHeight
+ );
+
+static gint32 p_prescale_image_size_handling(GapStbFetchData *gfdCurrent
+ , GapStoryRenderVidHandle *vidhand
+ , gint32 master_frame_nr
+ , gint32 vid_width
+ , gint32 vid_height
+ );
+
static void p_stb_render_image_or_animimage(GapStbFetchData *gfd
, GapStoryRenderVidHandle *vidhand
- , gint32 master_frame_nr);
+ , gint32 master_frame_nr
+ , gint32 vid_width, gint32 vid_height);
static gboolean p_is_another_clip_playing_the_same_video_backwards(GapStoryRenderFrameRangeElem *frn_elem_ref);
static void p_check_and_open_video_handle(GapStoryRenderFrameRangeElem *frn_elem
, GapStoryRenderVidHandle *vidhand
@@ -489,7 +512,12 @@ static void p_stb_render_section(GapStbFetchData *gfd
, gint32 master_frame_nr
, gint32 vid_width, gint32 vid_height
, const char *section_name);
-static void p_stb_render_frame_images(GapStbFetchData *gfd, gint32 master_frame_nr);
+static gboolean p_check_next_composite_frame_includes_same_image(GapStbFetchData *gfdCurrent
+ , GapStoryRenderVidHandle *vidhand
+ , gint32 master_frame_nr);
+static void p_stb_render_frame_images(GapStbFetchData *gfd
+ , GapStoryRenderVidHandle *vidhand
+ , gint32 master_frame_nr, gint32 vid_width, gint32 vid_height);
static void p_stb_render_composite_image_postprocessing(GapStbFetchData *gfd
, GapStoryRenderVidHandle *vidhand
, gint32 master_frame_nr
@@ -5119,7 +5147,7 @@ p_transform_with_movepath_processing( gint32 comp_image_id
&& (result_height >= vid_height))
{
/* handle enlarge image in both dimensions scenario */
- gimp_image_scale(l_tmp_movpath_image_id, result_width, result_height);
+ gap_frame_fetch_image_scale(l_tmp_movpath_image_id, result_width, result_height);
offs_x = rint((result_width - vid_width) / 2.0);
offs_y = rint((result_height - vid_height) / 2.0);
gimp_image_crop(l_tmp_movpath_image_id, vid_width, vid_height, offs_x, offs_y);
@@ -5128,7 +5156,7 @@ p_transform_with_movepath_processing( gint32 comp_image_id
&& (result_height <= vid_height))
{
/* handle shrink image in both dimensions scenario */
- gimp_image_scale(l_tmp_movpath_image_id, result_width, result_height);
+ gap_frame_fetch_image_scale(l_tmp_movpath_image_id, result_width, result_height);
offs_x = rint((vid_width - result_width) / 2.0);
offs_y = rint((vid_height - result_height) / 2.0);
@@ -5140,13 +5168,13 @@ p_transform_with_movepath_processing( gint32 comp_image_id
{
/* handle enlarge width but shrink height scenario */
/* 1. enlarge width, keep same image height */
- gimp_image_scale(l_tmp_movpath_image_id, result_width, vid_height);
+ gap_frame_fetch_image_scale(l_tmp_movpath_image_id, result_width, vid_height);
offs_x = rint((result_width - vid_width) / 2.0);
offs_y = 0;
gimp_image_crop(l_tmp_movpath_image_id, vid_width, vid_height, offs_x, offs_y);
/* 2. shrink height, keep same image width */
- gimp_image_scale(l_tmp_movpath_image_id, vid_width, result_height);
+ gap_frame_fetch_image_scale(l_tmp_movpath_image_id, vid_width, result_height);
offs_x = 0;
offs_y = rint((vid_height - result_height) / 2.0);
@@ -5159,13 +5187,13 @@ p_transform_with_movepath_processing( gint32 comp_image_id
{
/* handle enlarge height but shrink width scenario */
/* 1. enlarge height, keep same image width */
- gimp_image_scale(l_tmp_movpath_image_id, vid_width, result_height);
+ gap_frame_fetch_image_scale(l_tmp_movpath_image_id, vid_width, result_height);
offs_x = 0;
offs_y = rint((result_height - vid_height) / 2.0);
gimp_image_crop(l_tmp_movpath_image_id, vid_width, vid_height, offs_x, offs_y);
/* 2. shrink width, keep same image height */
- gimp_image_scale(l_tmp_movpath_image_id, result_width, vid_height);
+ gap_frame_fetch_image_scale(l_tmp_movpath_image_id, result_width, vid_height);
offs_x = rint((vid_width - result_width) / 2.0);
offs_y = 0;
@@ -5596,6 +5624,16 @@ p_transform_and_add_layer( gint32 comp_image_id
|| (gimp_drawable_height(l_fsel_layer_id) != calculated->visible_height) )
{
GAP_TIMM_START_FUNCTION(funcIdClipScale);
+ if(gap_debug)
+ {
+ printf("DEBUG: p_transform_and_add_layer scaling floating sel layer from (%dx%d) to ==> (%dx%d)\n"
+ , (int)gimp_drawable_width(l_fsel_layer_id)
+ , (int)gimp_drawable_height(l_fsel_layer_id)
+ , (int)calculated->visible_width
+ , (int)calculated->visible_height
+ );
+
+ }
gimp_layer_scale(l_fsel_layer_id, calculated->visible_width, calculated->visible_height
, FALSE /* FALSE: centered at image TRUE: centered local on layer */
@@ -6544,10 +6582,460 @@ p_conditional_delace_drawable(GapStbFetchData *gfd, gint32 drawable_id)
#endif
} /* end p_conditional_delace_drawable */
+/* ------------
+ * p_init_gfd
+ * ------------
+ */
+static void
+p_init_gfd(GapStbFetchData *gfd)
+{
+ gfd->localframe_tween_rest = 0.0;
+ gfd->comp_image_id = -1;
+ gfd->tmp_image_id = -1;
+ gfd->layer_id = -1;
+ gfd->gapStoryFetchResult = NULL;
+ gfd->isRgb888Result = FALSE;
+} /* end p_init_gfd */
+
+
+/* -------------------------------------------------------------------
+ * p_is_larger_image_variant_expected
+ * -------------------------------------------------------------------
+ */
+static gboolean
+p_is_larger_image_variant_expected(GapStbFetchData *gfdCurrent
+ , GapStoryRenderVidHandle *vidhand
+ , gint32 master_frame_nr
+ , gint32 vid_width
+ , gint32 vid_height
+ , gint32 originalWidth
+ , gint32 originalHeight
+ , gint32 currentPrescaleWidth
+ , gint32 currentPrescaleHeight
+ )
+{
+ GapStbFetchData gapStbFetchData;
+ GapStbFetchData *gfd;
+ GapStoryCalcAttr calculate_attributes;
+ GapStoryCalcAttr *calculated;
+ gint32 lookForwardMasterFframeNr;
+ gboolean foundLargerVariant;
+
+
+ gfd = &gapStbFetchData;
+ p_init_gfd(gfd);
+ calculated = &calculate_attributes;
+
+ foundLargerVariant = FALSE;
+
+ /* check upto 500 further frames for usage of the same image
+ * to findout maximum required prescale size in the near rendering future..
+ * (note that in practice it is typical that one of the break conditions
+ * occurs much earlier before the 500 checks are done)
+ */
+ for(lookForwardMasterFframeNr = master_frame_nr + 1;
+ lookForwardMasterFframeNr < master_frame_nr + 500;
+ lookForwardMasterFframeNr++)
+ {
+ gint32 l_track;
+ gboolean nextCompositeFrameIncludesSameImage;
+
+ nextCompositeFrameIncludesSameImage = FALSE;
+
+ if(gap_debug)
+ {
+ printf(" o lookForwardMasterFframeNr:%d\n", lookForwardMasterFframeNr);
+ }
+
+ for(l_track = vidhand->maxVidTrack; l_track >= vidhand->minVidTrack; l_track--)
+ {
+ gfd->framename = p_fetch_framename(vidhand->frn_list
+ , lookForwardMasterFframeNr /* starts at 1 */
+ , l_track
+ , gfd
+ );
+ if (gfd->framename != NULL)
+ {
+ if((gfd->frn_type == GAP_FRN_ANIMIMAGE)
+ || (gfd->frn_type == GAP_FRN_IMAGE)
+ || (gfd->frn_type == GAP_FRN_FRAMES))
+ {
+ if (strcmp(gfd->framename, gfdCurrent->framename) == 0)
+ {
+ nextCompositeFrameIncludesSameImage = TRUE;
+ gap_story_file_calculate_render_attributes(&calculate_attributes
+ , vid_width
+ , vid_height
+ , vid_width
+ , vid_height
+ , originalWidth
+ , originalHeight
+ , gfd->keep_proportions
+ , gfd->fit_width
+ , gfd->fit_height
+ , gfd->rotate
+ , gfd->opacity
+ , gfd->scale_x
+ , gfd->scale_y
+ , gfd->move_x
+ , gfd->move_y
+ );
+ if ((calculated->width > currentPrescaleWidth)
+ || (calculated->height > currentPrescaleHeight))
+ {
+ foundLargerVariant = TRUE;
+ }
+
+
+ }
+ }
+
+ g_free(gfd->framename);
+ }
+ }
+
+ if ((nextCompositeFrameIncludesSameImage != TRUE)
+ || (foundLargerVariant == TRUE))
+ {
+ break;
+ }
+ }
+
+
+ return (foundLargerVariant);
+
+} /* end p_is_larger_image_variant_expected */
/* -------------------------------------------------------------------
+ * p_prescale_image_size_handling
+ * -------------------------------------------------------------------
+ * fetch a single image or animimage at prescaled size.
+ * this procedure checks some of the frames to be rendered next
+ * for usage of the same image and calculates the prescale size as the maximum
+ * refered size, except the size grows more than 150 percent.
+ * this calculated prescale size is used to (down) scale the cached image.
+ * this shall speedup rendering of large images
+ * when refered multiple times at video size (that is typically much smaller)
+ */
+static gint32
+p_prescale_image_size_handling(GapStbFetchData *gfdCurrent
+ , GapStoryRenderVidHandle *vidhand
+ , gint32 master_frame_nr
+ , gint32 vid_width
+ , gint32 vid_height
+ )
+{
+ gint32 l_fetched_image_id;
+ GapStbFetchData gapStbFetchData;
+ GapStbFetchData *gfd;
+ GapStoryCalcAttr calculate_attributes;
+ GapStoryCalcAttr *calculated;
+ gint32 currentPrescaleWidth;
+ gint32 currentPrescaleHeight;
+ gint32 maxPrescaleWidth;
+ gint32 maxPrescaleHeight;
+ gint32 originalWidth;
+ gint32 originalHeight;
+ gint32 lookForwardMasterFframeNr;
+ gboolean foundSmallerVariant;
+ gboolean foundTooLargeVariant; /* larger than 150% */
+
+ gfd = gfdCurrent;
+ l_fetched_image_id = gap_frame_fetch_prescaled_image(vidhand->ffetch_user_id
+ , gfdCurrent->framename /* full filename of the image */
+ , TRUE /* enable caching */
+ ,0 /* prescaleWidth is not yet known */
+ ,0 /* prescaleHeight is not yet known */
+ ,&originalWidth
+ ,&originalHeight
+ );
+ if (l_fetched_image_id < 0)
+ {
+ /* failed to fetch image */
+ return (l_fetched_image_id);
+ }
+
+ calculated = &calculate_attributes;
+
+ /* calculate scaling, offsets and opacity according to current attributes
+ */
+ gap_story_file_calculate_render_attributes(&calculate_attributes
+ , vid_width
+ , vid_height
+ , vid_width
+ , vid_height
+ , originalWidth
+ , originalHeight
+ , gfd->keep_proportions
+ , gfd->fit_width
+ , gfd->fit_height
+ , gfd->rotate
+ , gfd->opacity
+ , gfd->scale_x
+ , gfd->scale_y
+ , gfd->move_x
+ , gfd->move_y
+ );
+
+ currentPrescaleWidth = calculated->width;
+ currentPrescaleHeight = calculated->height;
+ maxPrescaleWidth = currentPrescaleWidth;
+ maxPrescaleHeight = currentPrescaleHeight;
+
+ if(gap_debug)
+ {
+ printf("p_prescale_image_size_handling master_frame_nr:%d\n"
+ "original:(%dx%d) currentPrescale:(%dx%d) imgId:%d actualSIZE: (%dx%d) filename:%s\n"
+ , (int)master_frame_nr
+ , (int)originalWidth
+ , (int)originalHeight
+ , (int)currentPrescaleWidth
+ , (int)currentPrescaleHeight
+ , (int)l_fetched_image_id
+ , (int)gimp_image_width(l_fetched_image_id)
+ , (int)gimp_image_height(l_fetched_image_id)
+ , gfdCurrent->framename
+ );
+ }
+
+ if ((gimp_image_width(l_fetched_image_id) == currentPrescaleWidth)
+ && (gimp_image_height(l_fetched_image_id) == currentPrescaleHeight))
+ {
+ /* wanted prescale size for rendering current frame
+ * is same as actual size of the cached image
+ * (no need for further checks so far..)
+ */
+ if(gap_debug)
+ {
+ printf("p_prescale_image_size_handling master_frame_nr:%d EXACT SAME SIZE filename:%s\n"
+ , (int)master_frame_nr
+ , gfdCurrent->framename
+ );
+ }
+ return (l_fetched_image_id);
+ }
+
+ if ((gimp_image_width(l_fetched_image_id) < originalWidth)
+ || (gimp_image_height(l_fetched_image_id) < originalHeight))
+ {
+ if ((gimp_image_width(l_fetched_image_id) >= currentPrescaleWidth)
+ && (gimp_image_height(l_fetched_image_id) >= currentPrescaleHeight))
+ {
+ /* fetched image is already down scaled.
+ * optional check for further (chained) downscale
+ */
+ gboolean foundLargerVariant = FALSE;
+ gboolean prescaleEnabledDownscaleChainDefault = TRUE;
+
+ if(gap_base_get_gimprc_gboolean_value(GAP_VIDEO_STORYBOARD_PRESCALE_ENABLE_DOWNSCALE_CHAIN
+ , prescaleEnabledDownscaleChainDefault))
+ {
+ foundLargerVariant = p_is_larger_image_variant_expected(gfdCurrent
+ , vidhand
+ , master_frame_nr
+ , vid_width
+ , vid_height
+ , originalWidth
+ , originalHeight
+ , currentPrescaleWidth
+ , currentPrescaleHeight
+ );
+ }
+ if(foundLargerVariant != TRUE)
+ {
+ /* there is no more reference to the same image
+ * that requires larger variant than currentPrescale size in near rendering future)
+ * ----
+ * this additional scale down step by step results in speed up downscale rendering sequences
+ * because downscale rendering sequences example would look like this:
+ * frame 000001 800x600 --> 750x550
+ * frame 000002 750x550 --> 700x500
+ * frame 000003 700x500 --> 650x450
+ * this behaviour (chained muliple downscales) could also result in some quality loss
+ * (and can be disabled via prescaleEnableChainedDownscale)
+ */
+ if(gap_debug)
+ {
+ printf("p_prescale_image_size_handling master_frame_nr:%d\n"
+ " CHAINED DOWNSCALE from:(%dx%d) --> to:(%dx%d) filename:%s\n"
+ , (int)master_frame_nr
+ , (int)gimp_image_width(l_fetched_image_id)
+ , (int)gimp_image_height(l_fetched_image_id)
+ , (int)currentPrescaleWidth
+ , (int)currentPrescaleHeight
+ , gfdCurrent->framename
+ );
+ }
+ gimp_image_undo_disable(l_fetched_image_id);
+ gap_frame_fetch_image_scale(l_fetched_image_id, currentPrescaleWidth, currentPrescaleHeight);
+ }
+ else
+ {
+ /* without the optional chek the same downscale rendering sequences example looks like this:
+ * frame 000001 800x600-->750x550
+ * frame 000002 800x600-->700x500
+ * frame 000003 800x600-->650x450
+ */
+ if(gap_debug)
+ {
+ printf("p_prescale_image_size_handling master_frame_nr:%d\n"
+ " ALREADY DOWNSCALED filename:%s\n"
+ , (int)master_frame_nr
+ , gfdCurrent->framename
+ );
+ }
+ }
+
+ return (l_fetched_image_id);
+ }
+ }
+
+
+ gfd = &gapStbFetchData;
+ p_init_gfd(gfd);
+
+ foundSmallerVariant = FALSE;
+ foundTooLargeVariant = FALSE;
+
+ /* check upto 500 further frames for usage of the same image
+ * to findout maximum required prescale size in the near rendering future..
+ * (note that in practice it is typical that one of the break conditions
+ * occurs much earlier before the 500 checks are done)
+ */
+ for(lookForwardMasterFframeNr = master_frame_nr + 1;
+ lookForwardMasterFframeNr < master_frame_nr + 500;
+ lookForwardMasterFframeNr++)
+ {
+ gint32 l_track;
+ gboolean nextCompositeFrameIncludesSameImage;
+ gboolean nextCompositeFrameIncludesSameImageAtSamePrescaleSize;
+
+ if(gap_debug)
+ {
+ printf(" lookForwardMasterFframeNr:%d\n", lookForwardMasterFframeNr);
+ }
+
+ nextCompositeFrameIncludesSameImage = FALSE;
+ nextCompositeFrameIncludesSameImageAtSamePrescaleSize = FALSE;
+ for(l_track = vidhand->maxVidTrack; l_track >= vidhand->minVidTrack; l_track--)
+ {
+ gfd->framename = p_fetch_framename(vidhand->frn_list
+ , lookForwardMasterFframeNr /* starts at 1 */
+ , l_track
+ , gfd
+ );
+ if (gfd->framename != NULL)
+ {
+ if((gfd->frn_type == GAP_FRN_ANIMIMAGE)
+ || (gfd->frn_type == GAP_FRN_IMAGE)
+ || (gfd->frn_type == GAP_FRN_FRAMES))
+ {
+ if (strcmp(gfd->framename, gfdCurrent->framename) == 0)
+ {
+ nextCompositeFrameIncludesSameImage = TRUE;
+
+ gap_story_file_calculate_render_attributes(&calculate_attributes
+ , vid_width
+ , vid_height
+ , vid_width
+ , vid_height
+ , originalWidth
+ , originalHeight
+ , gfd->keep_proportions
+ , gfd->fit_width
+ , gfd->fit_height
+ , gfd->rotate
+ , gfd->opacity
+ , gfd->scale_x
+ , gfd->scale_y
+ , gfd->move_x
+ , gfd->move_y
+ );
+ if ((calculated->width == maxPrescaleWidth)
+ && (calculated->height == maxPrescaleHeight))
+ {
+ nextCompositeFrameIncludesSameImageAtSamePrescaleSize = TRUE;
+ }
+ else
+ {
+ nextCompositeFrameIncludesSameImageAtSamePrescaleSize = FALSE;
+ }
+
+
+ if ((calculated->width < currentPrescaleWidth)
+ && (calculated->height < currentPrescaleHeight))
+ {
+ foundSmallerVariant = TRUE;
+ }
+
+ if (calculated->width > originalWidth)
+ {
+ foundTooLargeVariant = TRUE;
+ }
+ else
+ {
+ if (calculated->width > maxPrescaleWidth)
+ {
+ maxPrescaleWidth = calculated->width;
+ maxPrescaleHeight = calculated->height;
+ }
+ }
+
+
+ }
+ }
+
+ g_free(gfd->framename);
+ }
+ }
+ if ((nextCompositeFrameIncludesSameImage != TRUE)
+ || (nextCompositeFrameIncludesSameImageAtSamePrescaleSize == TRUE)
+ || (foundSmallerVariant == TRUE)
+ || (foundTooLargeVariant == TRUE))
+ {
+ /* stop looking forward when:
+ * o) next frame does not refere to same image
+ * o) or refers to same image at exact same prescale size
+ * o) or refers to smaller representation
+ * (assume zoom out where usage of larger variant is NOT expected in near future)
+ * o) or refers to much larger variant > 150 %
+ */
+ break;
+ }
+ }
+
+ /* the 2nd fetch call (with prescale size != 0) triggers prescaling */
+ l_fetched_image_id = gap_frame_fetch_prescaled_image(vidhand->ffetch_user_id
+ , gfdCurrent->framename /* full filename of the image */
+ , TRUE /* enable caching */
+ , maxPrescaleWidth
+ , maxPrescaleHeight
+ , &originalWidth
+ , &originalHeight
+ );
+ if(gap_debug)
+ {
+ printf("p_prescale_image_size_handling master_frame_nr:%d lookForwardMasterFframeNr:%d\n"
+ "maxPrescaleWidth:%d maxPrescaleHeight:%d imgId:%d (%dx%d) filename:%s\n"
+ , (int)master_frame_nr
+ , (int)lookForwardMasterFframeNr
+ , (int)maxPrescaleWidth
+ , (int)maxPrescaleHeight
+ , (int)l_fetched_image_id
+ , (int)gimp_image_width(l_fetched_image_id)
+ , (int)gimp_image_height(l_fetched_image_id)
+ , gfdCurrent->framename
+ );
+ }
+
+ return (l_fetched_image_id);
+
+} /* end p_prescale_image_size_handling */
+
+
+/* -------------------------------------------------------------------
* p_stb_render_image_or_animimage (GAP_FRN_ANIMIMAGE or GAP_FRN_IMAGE
* -------------------------------------------------------------------
* fetch a single image or animimage
@@ -6555,21 +7043,62 @@ p_conditional_delace_drawable(GapStbFetchData *gfd, gint32 drawable_id)
static void
p_stb_render_image_or_animimage(GapStbFetchData *gfd
, GapStoryRenderVidHandle *vidhand
- , gint32 master_frame_nr)
+ , gint32 master_frame_nr, gint32 vid_width, gint32 vid_height)
{
gint32 l_orig_image_id;
gint l_nlayers;
gint32 *l_layers_list;
- l_orig_image_id = gap_frame_fetch_orig_image(vidhand->ffetch_user_id
+
+ if(gap_debug)
+ {
+ printf("p_stb_render_image_or_animimage START master_frame_nr:%d\n"
+ ,(int)master_frame_nr
+ );
+ }
+
+
+ /* filtermacro shall be applied at original image size
+ * therefore disable prescale handling when filtermacro or
+ * complex movepath processing is present
+ */
+ if ((gfd->trak_filtermacro_file == NULL)
+ && (gfd->movepath_file_xml == NULL))
+ {
+ if(gap_debug)
+ {
+ printf("CALLING p_prescale_image_size_handling master_frame_nr:%d\n"
+ ,(int)master_frame_nr
+ );
+ }
+ /* prescale handling */
+ l_orig_image_id =
+ p_prescale_image_size_handling(gfd, vidhand, master_frame_nr, vid_width, vid_height);
+ }
+ else
+ {
+ if(gap_debug)
+ {
+ printf("CALLING gap_frame_fetch_orig_image master_frame_nr:%d\n"
+ ,(int)master_frame_nr
+ );
+ }
+ l_orig_image_id = gap_frame_fetch_orig_image(vidhand->ffetch_user_id
, gfd->framename /* full filename of the image */
, TRUE /* enable caching */
);
+ }
+
+ if (l_orig_image_id < 0)
+ {
+ printf("Error fetching image: %s", gfd->framename);
+ return;
+ }
gimp_selection_none(l_orig_image_id);
if(gfd->frn_type == GAP_FRN_IMAGE)
{
- gfd->tmp_image_id = gimp_image_duplicate(l_orig_image_id);
+ gfd->tmp_image_id = gap_frame_fetch_image_duplicate(l_orig_image_id);
gfd->layer_id = p_prepare_RGB_image(gfd->tmp_image_id);
gap_frame_fetch_remove_parasite(gfd->tmp_image_id);
if(gap_debug)
@@ -7790,6 +8319,55 @@ p_stb_render_section(GapStbFetchData *gfd
} /* end p_stb_render_section */
+/* -------------------------------------------------------------------
+ * p_check_next_composite_frame_includes_same_image
+ * -------------------------------------------------------------------
+ * return true in case the next composite frame includes the same image.
+ */
+static gboolean
+p_check_next_composite_frame_includes_same_image(GapStbFetchData *gfdCurrent
+ , GapStoryRenderVidHandle *vidhand
+ , gint32 master_frame_nr)
+{
+ gint32 l_track;
+ gboolean nextCompositeFrameIncludesSameImage;
+ GapStbFetchData gapStbFetchData;
+ GapStbFetchData *gfd;
+
+ nextCompositeFrameIncludesSameImage = FALSE;
+
+ gfd = &gapStbFetchData;
+ p_init_gfd(gfd);
+
+ for(l_track = vidhand->maxVidTrack; l_track >= vidhand->minVidTrack; l_track--)
+ {
+ gfd->framename = p_fetch_framename(vidhand->frn_list
+ , master_frame_nr + 1
+ , l_track
+ , gfd
+ );
+ if (gfd->framename != NULL)
+ {
+ if((gfd->frn_type == GAP_FRN_ANIMIMAGE)
+ || (gfd->frn_type == GAP_FRN_IMAGE)
+ || (gfd->frn_type == GAP_FRN_FRAMES))
+ {
+ if (strcmp(gfd->framename, gfdCurrent->framename) == 0)
+ {
+ nextCompositeFrameIncludesSameImage = TRUE;
+ }
+ }
+
+ g_free(gfd->framename);
+ }
+ }
+
+ return (nextCompositeFrameIncludesSameImage);
+}
+
+
+
+
/* -------------------------------------------
* p_stb_render_frame_images (GAP_FRN_FRAMES)
* -------------------------------------------
@@ -7798,8 +8376,19 @@ p_stb_render_section(GapStbFetchData *gfd
* and includes path name, numberpart and extension)
*/
static void
-p_stb_render_frame_images(GapStbFetchData *gfd, gint32 master_frame_nr)
+p_stb_render_frame_images(GapStbFetchData *gfd, GapStoryRenderVidHandle *vidhand, gint32 master_frame_nr
+ , gint32 vid_width, gint32 vid_height)
{
+ gboolean nextCompositeFrameIncludesSameImage;
+ gboolean enableCaching;
+ gint32 l_fetched_image_id;
+ gint32 originalWidth;
+ gint32 originalHeight;
+ gint32 prescaleWidth;
+ gint32 prescaleHeight;
+ GapStoryCalcAttr calculate_attributes;
+ GapStoryCalcAttr *calculated;
+
if(gap_debug)
{
printf("FRAME fetch gfd->framename: %s\n ===> master:%d from: %d to: %d\n"
@@ -7809,9 +8398,117 @@ p_stb_render_frame_images(GapStbFetchData *gfd, gint32 master_frame_nr)
,(int)gfd->frn_elem->frame_to
);
}
- gfd->tmp_image_id = gap_lib_load_image(gfd->framename);
+
+ nextCompositeFrameIncludesSameImage =
+ p_check_next_composite_frame_includes_same_image(gfd, vidhand, master_frame_nr);
+
+ enableCaching = nextCompositeFrameIncludesSameImage;
+
+ l_fetched_image_id = gap_frame_fetch_prescaled_image(vidhand->ffetch_user_id
+ , gfd->framename /* full filename of the image */
+ , enableCaching
+ ,0 /* prescaleWidth is not yet known */
+ ,0 /* prescaleHeight is not yet known */
+ ,&originalWidth
+ ,&originalHeight
+ );
+ calculated = &calculate_attributes;
+
+ /* calculate scaling, offsets and opacity according to current attributes
+ */
+ gap_story_file_calculate_render_attributes(&calculate_attributes
+ , vid_width
+ , vid_height
+ , vid_width
+ , vid_height
+ , originalWidth
+ , originalHeight
+ , gfd->keep_proportions
+ , gfd->fit_width
+ , gfd->fit_height
+ , gfd->rotate
+ , gfd->opacity
+ , gfd->scale_x
+ , gfd->scale_y
+ , gfd->move_x
+ , gfd->move_y
+ );
+
+ prescaleWidth = calculated->width;
+ prescaleHeight = calculated->height;
+
+ if (gap_frame_fetch_is_image_in_cache(l_fetched_image_id))
+ {
+ gboolean fetchedImageUsable;
+
+ fetchedImageUsable = FALSE;
+ if ((gimp_image_width(l_fetched_image_id) >= prescaleWidth)
+ && (gimp_image_height(l_fetched_image_id) >= prescaleHeight))
+ {
+ fetchedImageUsable = TRUE;
+ }
+ else
+ {
+ if ((gimp_image_width(l_fetched_image_id) == originalWidth)
+ && (gimp_image_height(l_fetched_image_id) == originalHeight))
+ {
+ fetchedImageUsable = TRUE;
+ }
+ }
+
+
+ if(fetchedImageUsable == TRUE)
+ {
+ if(gap_debug)
+ {
+ printf("p_stb_render_frame_images DUP cached image (%dx%d) at master_frame_nr:%d %s\n"
+ ,(int)gimp_image_width(l_fetched_image_id)
+ ,(int)gimp_image_height(l_fetched_image_id)
+ ,(int)master_frame_nr
+ , gfd->framename
+ );
+ }
+ gfd->tmp_image_id = gap_frame_fetch_image_duplicate(l_fetched_image_id);
+ gap_frame_fetch_remove_parasite(gfd->tmp_image_id);
+ }
+ else
+ {
+ if(gap_debug)
+ {
+ printf("p_stb_render_frame_images RE-LOAD cached image (%dx%d) too small at master_frame_nr:%d %s\n"
+ ,(int)gimp_image_width(l_fetched_image_id)
+ ,(int)gimp_image_height(l_fetched_image_id)
+ ,(int)master_frame_nr
+ , gfd->framename
+ );
+ }
+ /* the cached image is downscaled and smaller than required size.
+ * in this case load again from file (at original size to ensure
+ * best possible render quality)
+ */
+ gfd->tmp_image_id = gap_lib_load_image(gfd->framename);
+ }
+ }
+ else
+ {
+ if(gap_debug)
+ {
+ printf("p_stb_render_frame_images UNCACHED fetched image (%dx%d) at master_frame_nr:%d %s\n"
+ ,(int)gimp_image_width(l_fetched_image_id)
+ ,(int)gimp_image_height(l_fetched_image_id)
+ ,(int)master_frame_nr
+ , gfd->framename
+ );
+ }
+ /* use fetched image (that is not member of the cache
+ * directly without making a copy
+ */
+ gfd->tmp_image_id = l_fetched_image_id;
+ }
+
} /* end p_stb_render_frame_images */
+
/* -------------------------------------------
* p_stb_render_composite_image_postprocessing
@@ -7912,7 +8609,7 @@ p_stb_render_composite_image_postprocessing(GapStbFetchData *gfd
{
if(gap_debug) printf("DEBUG: p_stb_render_composite_image_postprocessing: scaling tmp image\n");
- gimp_image_scale(gfd->comp_image_id, vid_width, vid_height);
+ gap_frame_fetch_image_scale(gfd->comp_image_id, vid_width, vid_height);
}
/* check again for layerstack (macro could have add more layers)
@@ -8331,7 +9028,7 @@ p_do_insert_alpha_processing(GapStbFetchData *gfd
{
printf("DEBUG: p_do_insert_alpha_processing scaling alpha image\n");
}
- gimp_image_scale(gfd->comp_image_id, vid_width, vid_height);
+ gap_frame_fetch_image_scale(gfd->comp_image_id, vid_width, vid_height);
}
if(! gimp_drawable_has_alpha(gfd->layer_id))
@@ -8641,12 +9338,8 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
gfd = &gapStbFetchData;
- gfd->localframe_tween_rest = 0.0;
- gfd->comp_image_id = -1;
- gfd->tmp_image_id = -1;
- gfd->layer_id = -1;
- gfd->gapStoryFetchResult = NULL;
- gfd->isRgb888Result = FALSE;
+ p_init_gfd(gfd);
+
*layer_id = -1;
@@ -8752,7 +9445,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
if((gfd->frn_type == GAP_FRN_ANIMIMAGE)
|| (gfd->frn_type == GAP_FRN_IMAGE))
{
- p_stb_render_image_or_animimage(gfd, vidhand, master_frame_nr);
+ p_stb_render_image_or_animimage(gfd, vidhand, master_frame_nr, vid_width, vid_height);
}
else
{
@@ -8769,7 +9462,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
else
{
/* GAP_FRN_FRAMES */
- p_stb_render_frame_images(gfd, master_frame_nr);
+ p_stb_render_frame_images(gfd, vidhand, master_frame_nr, vid_width, vid_height);
}
}
}
@@ -8852,7 +9545,7 @@ p_story_render_fetch_composite_image_private(GapStoryRenderVidHandle *vidhand
{
printf("DEBUG: p_story_render_fetch_composite_image_private scaling composite image\n");
}
- gimp_image_scale(gfd->comp_image_id, vid_width, vid_height);
+ gap_frame_fetch_image_scale(gfd->comp_image_id, vid_width, vid_height);
GAP_TIMM_STOP_FUNCTION(funcIdDirectScale);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]