Image cache rewrite to using generic movie cache
authorSergey Sharybin <sergey.vfx@gmail.com>
Fri, 13 Dec 2013 10:22:08 +0000 (16:22 +0600)
committerSergey Sharybin <sergey.vfx@gmail.com>
Fri, 13 Dec 2013 10:30:00 +0000 (16:30 +0600)
Summary:
Behaves very much the same as cache for Movie Clip datablock:

- Image now have `MovieCache *cache` field which replaced
  legacy `ListBase ibufs`.

  This allows image datablock to easily keep of image
  buffers which are owned by itself. This field isn't
  saved to the file and getting restored on undo steps.

  However, cache limit is global for movies, sequences
  and image datablocks now. So overall cached image buffers
  size will not go above cache limit size in user
  preferences.

- Image buffers which are marked as BITMAPDIRTY will never
  be freed from the cache.

- Added utility function to iterate over image buffers
  saved in movie cache.

- Movie cache cleanup check callback now have ImBuf argument
  which can be used in a condition of cleanup.

- Added some utility functions which replaces legacy ibufs
  iterations with image cache iteration which happens from
  inside a lock.

- Fixed `image_mem_size()` which was only counting one of
  the buffers if both float and byte buffer present.

Additional notes:

- `BKE_image_get_first_ibuf()` is rather stupid, but direct
  access to ibufs->first was also the same stupid idea.

  Would consider avoid this function is another project.

- There are some places which doesn't look threadsafe, but
  they already were not so much threadsafe anyway before.

  So think not a big deal with solving this later.

Finally solves infinite memory usage by image sequences! :)

Reviewers: brecht, campbellbarton

Reviewed By: brecht

CC: sebastian_k
Differential Revision: http://developer.blender.org/D95

14 files changed:
source/blender/blenkernel/BKE_image.h
source/blender/blenkernel/intern/image.c
source/blender/blenkernel/intern/seqcache.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_image_proj.c
source/blender/editors/space_image/image_ops.c
source/blender/editors/space_info/info_ops.c
source/blender/gpu/intern/gpu_draw.c
source/blender/imbuf/IMB_moviecache.h
source/blender/imbuf/intern/moviecache.c
source/blender/makesdna/DNA_image_types.h
source/blender/makesrna/intern/rna_image.c
source/blender/render/intern/source/imagetexture.c

index 7b5abbba7f8cefceef3aedd6c7084704be10d8ce..b5171f8e0c2b4169213492387cda286222c93968 100644 (file)
@@ -235,6 +235,12 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame);
 
 /* Guess offset for the first frame in the sequence */
 int BKE_image_sequence_guess_offset(struct Image *image);
+
+bool BKE_image_is_dirty(struct Image *image);
+void BKE_image_file_format_set(struct Image *image, int ftype);
+bool BKE_image_has_loaded_ibuf(struct Image *image);
+struct ImBuf *BKE_image_get_ibuf_with_name(struct Image *image, const char *name);
+struct ImBuf *BKE_image_get_first_ibuf(struct Image *image);
 #ifdef __cplusplus
 }
 #endif
index 82ea3cb80b19696adf9b5f152f2628bd0e489a12..1e55b5ee962fa6ee315c96c6cdbcd0583c56f730 100644 (file)
@@ -50,6 +50,7 @@
 #include "IMB_colormanagement.h"
 #include "IMB_imbuf_types.h"
 #include "IMB_imbuf.h"
+#include "IMB_moviecache.h"
 
 #ifdef WITH_OPENEXR
 #  include "intern/openexr/openexr_multi.h"
@@ -109,6 +110,51 @@ static SpinLock image_spin;
 #define IMA_INDEX_FRAME(index)          (index >> 10)
 #define IMA_INDEX_PASS(index)           (index & ~1023)
 
+/* ******** IMAGE CACHE ************* */
+
+typedef struct ImageCacheKey {
+       int index;
+} ImageCacheKey;
+
+static unsigned int imagecache_hashhash(const void *key_v)
+{
+       const ImageCacheKey *key = (ImageCacheKey *) key_v;
+       return key->index;
+}
+
+static int imagecache_hashcmp(const void *a_v, const void *b_v)
+{
+       const ImageCacheKey *a = (ImageCacheKey *) a_v;
+       const ImageCacheKey *b = (ImageCacheKey *) b_v;
+
+       return a->index - b->index;
+}
+
+static void imagecache_put(Image *image, int index, ImBuf *ibuf)
+{
+       ImageCacheKey key;
+
+       if (image->cache == NULL) {
+               image->cache = IMB_moviecache_create("Image Datablock Cache", sizeof(ImageCacheKey),
+                                                    imagecache_hashhash, imagecache_hashcmp);
+       }
+
+       key.index = index;
+
+       IMB_moviecache_put(image->cache, &key, ibuf);
+}
+
+static struct ImBuf* imagecache_get(Image *image, int index)
+{
+       if (image->cache) {
+               ImageCacheKey key;
+               key.index = index;
+               return IMB_moviecache_get(image->cache, &key);
+       }
+
+       return NULL;
+}
+
 void BKE_images_init(void)
 {
        BLI_spin_init(&image_spin);
@@ -193,13 +239,9 @@ void BKE_image_de_interlace(Image *ima, int odd)
 
 static void image_free_cahced_frames(Image *image)
 {
-       ImBuf *ibuf;
-       while ((ibuf = BLI_pophead(&image->ibufs))) {
-               if (ibuf->userdata) {
-                       MEM_freeN(ibuf->userdata);
-                       ibuf->userdata = NULL;
-               }
-               IMB_freeImBuf(ibuf);
+       if (image->cache) {
+               IMB_moviecache_free(image->cache);
+               image->cache = NULL;
        }
 }
 
@@ -268,59 +310,30 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ
        return ima;
 }
 
-/* get the ibuf from an image cache, local use here only */
-static ImBuf *image_get_ibuf(Image *ima, int index, int frame)
+/* Get the ibuf from an image cache by it's index and frame.
+ * Local use here only.
+ *
+ * Returns referenced image buffer if it exists, callee is to
+ * call IMB_freeImBuf to de-reference the image buffer after
+ * it's done handling it.
+ */
+static ImBuf *image_get_cached_ibuf_for_index_frame(Image *ima, int index, int frame)
 {
-       /* this function is intended to be thread safe. with IMA_NO_INDEX this
-        * should be OK, but when iterating over the list this is more tricky
-        * */
-       if (index == IMA_NO_INDEX) {
-               return ima->ibufs.first;
-       }
-       else {
-               ImBuf *ibuf;
-
+       if (index != IMA_NO_INDEX) {
                index = IMA_MAKE_INDEX(frame, index);
-               for (ibuf = ima->ibufs.first; ibuf; ibuf = ibuf->next)
-                       if (ibuf->index == index)
-                               return ibuf;
        }
 
-       return NULL;
-}
-
-/* no ima->ibuf anymore, but listbase */
-static void image_remove_ibuf(Image *ima, ImBuf *ibuf)
-{
-       if (ibuf) {
-               BLI_remlink(&ima->ibufs, ibuf);
-               IMB_freeImBuf(ibuf);
-       }
+       return imagecache_get(ima, index);
 }
 
-
 /* no ima->ibuf anymore, but listbase */
 static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame)
 {
        if (ibuf) {
-               ImBuf *link;
-
                if (index != IMA_NO_INDEX)
                        index = IMA_MAKE_INDEX(frame, index);
 
-               /* insert based on index */
-               for (link = ima->ibufs.first; link; link = link->next)
-                       if (link->index >= index)
-                               break;
-
-               ibuf->index = index;
-
-               /* this function accepts (link == NULL) */
-               BLI_insertlinkbefore(&ima->ibufs, link, ibuf);
-
-               /* now we don't want copies? */
-               if (link && ibuf->index == link->index)
-                       image_remove_ibuf(ima, link);
+               imagecache_put(ima, index, ibuf);
        }
 }
 
@@ -521,14 +534,21 @@ void BKE_image_make_local(struct Image *ima)
 
 void BKE_image_merge(Image *dest, Image *source)
 {
-       ImBuf *ibuf;
-
        /* sanity check */
        if (dest && source && dest != source) {
-
-               while ((ibuf = BLI_pophead(&source->ibufs))) {
-                       image_assign_ibuf(dest, ibuf, IMA_INDEX_PASS(ibuf->index), IMA_INDEX_FRAME(ibuf->index));
+               BLI_spin_lock(&image_spin);
+               if (source->cache != NULL) {
+                       struct MovieCacheIter *iter;
+                       iter = IMB_moviecacheIter_new(source->cache);
+                       while (!IMB_moviecacheIter_done(iter)) {
+                               ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter);
+                               ImageCacheKey *key = IMB_moviecacheIter_getUserKey(iter);
+                               imagecache_put(dest, key->index, ibuf);
+                               IMB_moviecacheIter_step(iter);
+                       }
+                       IMB_moviecacheIter_free(iter);
                }
+               BLI_spin_unlock(&image_spin);
 
                BKE_libblock_free(&G.main->image, source);
        }
@@ -729,6 +749,9 @@ Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int hei
                ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings);
                image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
 
+               /* image_assign_ibuf puts buffer to the cache, which increments user counter. */
+               IMB_freeImBuf(ibuf);
+
                ima->ok = IMA_OK_LOADED;
        }
 
@@ -755,7 +778,7 @@ Image *BKE_image_add_from_imbuf(ImBuf *ibuf)
 /* packs rect from memory as PNG */
 void BKE_image_memorypack(Image *ima)
 {
-       ImBuf *ibuf = image_get_ibuf(ima, IMA_NO_INDEX, 0);
+       ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
 
        if (ibuf == NULL)
                return;
@@ -786,6 +809,8 @@ void BKE_image_memorypack(Image *ima)
                        ima->type = IMA_TYPE_IMAGE;
                }
        }
+
+       IMB_freeImBuf(ibuf);
 }
 
 void BKE_image_tag_time(Image *ima)
@@ -838,7 +863,7 @@ void free_old_images(void)
                                ima->lastused = ctime;
                        }
                        /* Otherwise, just kill the buffers */
-                       else if (ima->ibufs.first) {
+                       else {
                                image_free_buffers(ima);
                        }
                }
@@ -846,30 +871,47 @@ void free_old_images(void)
        }
 }
 
-static uintptr_t image_mem_size(Image *ima)
+static uintptr_t image_mem_size(Image *image)
 {
-       ImBuf *ibuf, *ibufm;
-       int level;
        uintptr_t size = 0;
 
-       size = 0;
-
        /* viewers have memory depending on other rules, has no valid rect pointer */
-       if (ima->source == IMA_SRC_VIEWER)
+       if (image->source == IMA_SRC_VIEWER)
                return 0;
 
-       for (ibuf = ima->ibufs.first; ibuf; ibuf = ibuf->next) {
-               if (ibuf->rect) size += MEM_allocN_len(ibuf->rect);
-               else if (ibuf->rect_float) size += MEM_allocN_len(ibuf->rect_float);
+       BLI_spin_lock(&image_spin);
+       if (image->cache != NULL) {
+               struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache);
+
+               while (!IMB_moviecacheIter_done(iter)) {
+                       ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter);
+                       ImBuf *ibufm;
+                       int level;
 
-               for (level = 0; level < IB_MIPMAP_LEVELS; level++) {
-                       ibufm = ibuf->mipmap[level];
-                       if (ibufm) {
-                               if (ibufm->rect) size += MEM_allocN_len(ibufm->rect);
-                               else if (ibufm->rect_float) size += MEM_allocN_len(ibufm->rect_float);
+                       if (ibuf->rect) {
+                               size += MEM_allocN_len(ibuf->rect);
+                       }
+                       if (ibuf->rect_float) {
+                               size += MEM_allocN_len(ibuf->rect_float);
                        }
+
+                       for (level = 0; level < IB_MIPMAP_LEVELS; level++) {
+                               ibufm = ibuf->mipmap[level];
+                               if (ibufm) {
+                                       if (ibufm->rect) {
+                                               size += MEM_allocN_len(ibufm->rect);
+                                       }
+                                       if (ibufm->rect_float) {
+                                               size += MEM_allocN_len(ibufm->rect_float);
+                                       }
+                               }
+                       }
+
+                       IMB_moviecacheIter_step(iter);
                }
+               IMB_moviecacheIter_free(iter);
        }
+       BLI_spin_unlock(&image_spin);
 
        return size;
 }
@@ -892,11 +934,20 @@ void BKE_image_print_memlist(void)
        }
 }
 
+static bool imagecache_check_dirty(ImBuf *ibuf, void *UNUSED(userkey), void *UNUSED(userdata))
+{
+       return (ibuf->userflags & IB_BITMAPDIRTY) == 0;
+}
+
 void BKE_image_free_all_textures(void)
 {
+#undef CHECK_FREED_SIZE
+
        Tex *tex;
        Image *ima;
-       /* unsigned int totsize = 0; */
+#ifdef CHECK_FREED_SIZE
+       uintptr_t tot_freed_size = 0;
+#endif
 
        for (ima = G.main->image.first; ima; ima = ima->id.next)
                ima->id.flag &= ~LIB_DOIT;
@@ -906,49 +957,35 @@ void BKE_image_free_all_textures(void)
                        tex->ima->id.flag |= LIB_DOIT;
 
        for (ima = G.main->image.first; ima; ima = ima->id.next) {
-               if (ima->ibufs.first && (ima->id.flag & LIB_DOIT)) {
-                       ImBuf *ibuf;
+               if (ima->cache && (ima->id.flag & LIB_DOIT)) {
+#ifdef CHECK_FREED_SIZE
+                       uintptr_t old_size = image_mem_size(ima);
+#endif
 
-                       for (ibuf = ima->ibufs.first; ibuf; ibuf = ibuf->next) {
-                               /* escape when image is painted on */
-                               if (ibuf->userflags & IB_BITMAPDIRTY)
-                                       break;
+                       IMB_moviecache_cleanup(ima->cache, imagecache_check_dirty, NULL);
 
-#if 0
-                               if (ibuf->mipmap[0])
-                                       totsize += 1.33 * ibuf->x * ibuf->y * 4;
-                               else
-                                       totsize += ibuf->x * ibuf->y * 4;
+#ifdef CHECK_FREED_SIZE
+                       tot_freed_size += old_size - image_mem_size(ima);
 #endif
-                       }
-                       if (ibuf == NULL)
-                               image_free_buffers(ima);
                }
        }
-       /* printf("freed total %d MB\n", totsize / (1024 * 1024)); */
+#ifdef CHECK_FREED_SIZE
+       printf("%s: freed total %lu MB\n", __func__, tot_freed_size / (1024 * 1024));
+#endif
+}
+
+static bool imagecache_check_free_anim(ImBuf *ibuf, void *UNUSED(userkey), void *userdata)
+{
+       int except_frame = *(int *)userdata;
+       return (ibuf->userflags & IB_BITMAPDIRTY) == 0 &&
+              (ibuf->index != IMA_NO_INDEX) &&
+              (except_frame != IMA_INDEX_FRAME(ibuf->index));
 }
 
 /* except_frame is weak, only works for seqs without offset... */
 void BKE_image_free_anim_ibufs(Image *ima, int except_frame)
 {
-       ImBuf *ibuf, *nbuf;
-
-       for (ibuf = ima->ibufs.first; ibuf; ibuf = nbuf) {
-               nbuf = ibuf->next;
-               if (ibuf->userflags & IB_BITMAPDIRTY)
-                       continue;
-               if (ibuf->index == IMA_NO_INDEX)
-                       continue;
-               if (except_frame != IMA_INDEX_FRAME(ibuf->index)) {
-                       BLI_remlink(&ima->ibufs, ibuf);
-
-                       if (ibuf->userdata) {
-                               MEM_freeN(ibuf->userdata);
-                               ibuf->userdata = NULL;
-                       }
-                       IMB_freeImBuf(ibuf);
-               }
-       }
+       IMB_moviecache_cleanup(ima->cache, imagecache_check_free_anim, &except_frame);
 }
 
 void BKE_image_all_free_anim_ibufs(int cfra)
@@ -2219,10 +2256,11 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal)
 
                        if (ima->source == IMA_SRC_GENERATED) {
                                if (ima->gen_x == 0 || ima->gen_y == 0) {
-                                       ImBuf *ibuf = image_get_ibuf(ima, IMA_NO_INDEX, 0);
+                                       ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
                                        if (ibuf) {
                                                ima->gen_x = ibuf->x;
                                                ima->gen_y = ibuf->y;
+                                               IMB_freeImBuf(ibuf);
                                        }
                                }
                        }
@@ -2784,7 +2822,7 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_
                }
        }
 
-       ibuf = image_get_ibuf(ima, IMA_NO_INDEX, 0);
+       ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
 
        /* make ibuf if needed, and initialize it */
        if (ibuf == NULL) {
@@ -2870,7 +2908,13 @@ static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *frame_r
        *index_r = index;
 }
 
-static ImBuf *image_get_ibuf_threadsafe(Image *ima, ImageUser *iuser, int *frame_r, int *index_r)
+/* Get the ibuf from an image cache for a given image user.
+ *
+ * Returns referenced image buffer if it exists, callee is to
+ * call IMB_freeImBuf to de-reference the image buffer after
+ * it's done handling it.
+ */
+static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *frame_r, int *index_r)
 {
        ImBuf *ibuf = NULL;
        int frame = 0, index = 0;
@@ -2878,7 +2922,7 @@ static ImBuf *image_get_ibuf_threadsafe(Image *ima, ImageUser *iuser, int *frame
        /* see if we already have an appropriate ibuf, with image source and type */
        if (ima->source == IMA_SRC_MOVIE) {
                frame = iuser ? iuser->framenr : ima->lastframe;
-               ibuf = image_get_ibuf(ima, 0, frame);
+               ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame);
                /* XXX temp stuff? */
                if (ima->lastframe != frame)
                        ima->tpageflag |= IMA_TPAGE_REFRESH;
@@ -2887,7 +2931,7 @@ static ImBuf *image_get_ibuf_threadsafe(Image *ima, ImageUser *iuser, int *frame
        else if (ima->source == IMA_SRC_SEQUENCE) {
                if (ima->type == IMA_TYPE_IMAGE) {
                        frame = iuser ? iuser->framenr : ima->lastframe;
-                       ibuf = image_get_ibuf(ima, 0, frame);
+                       ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame);
 
                        /* XXX temp stuff? */
                        if (ima->lastframe != frame) {
@@ -2898,17 +2942,17 @@ static ImBuf *image_get_ibuf_threadsafe(Image *ima, ImageUser *iuser, int *frame
                else if (ima->type == IMA_TYPE_MULTILAYER) {
                        frame = iuser ? iuser->framenr : ima->lastframe;
                        index = iuser ? iuser->multi_index : IMA_NO_INDEX;
-                       ibuf = image_get_ibuf(ima, index, frame);
+                       ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame);
                }
        }
        else if (ima->source == IMA_SRC_FILE) {
                if (ima->type == IMA_TYPE_IMAGE)
-                       ibuf = image_get_ibuf(ima, IMA_NO_INDEX, 0);
+                       ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
                else if (ima->type == IMA_TYPE_MULTILAYER)
-                       ibuf = image_get_ibuf(ima, iuser ? iuser->multi_index : IMA_NO_INDEX, 0);
+                       ibuf = image_get_cached_ibuf_for_index_frame(ima, iuser ? iuser->multi_index : IMA_NO_INDEX, 0);
        }
        else if (ima->source == IMA_SRC_GENERATED) {
-               ibuf = image_get_ibuf(ima, IMA_NO_INDEX, 0);
+               ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
        }
        else if (ima->source == IMA_SRC_VIEWER) {
                /* always verify entirely, not that this shouldn't happen
@@ -2957,7 +3001,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r)
        if (!image_quick_test(ima, iuser))
                return NULL;
 
-       ibuf = image_get_ibuf_threadsafe(ima, iuser, &frame, &index);
+       ibuf = image_get_cached_ibuf(ima, iuser, &frame, &index);
 
        if (ibuf == NULL) {
                /* we are sure we have to load the ibuf, using source and type */
@@ -3012,7 +3056,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r)
 
                                        /* XXX anim play for viewer nodes not yet supported */
                                        frame = 0; // XXX iuser ? iuser->framenr : 0;
-                                       ibuf = image_get_ibuf(ima, 0, frame);
+                                       ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame);
 
                                        if (!ibuf) {
                                                /* Composite Viewer, all handled in compositor */
@@ -3045,9 +3089,6 @@ ImBuf *BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r)
 
        ibuf = image_acquire_ibuf(ima, iuser, lock_r);
 
-       if (ibuf)
-               IMB_refImBuf(ibuf);
-
        BLI_spin_unlock(&image_spin);
 
        return ibuf;
@@ -3082,18 +3123,16 @@ int BKE_image_has_ibuf(Image *ima, ImageUser *iuser)
        if (!image_quick_test(ima, iuser))
                return FALSE;
 
-       ibuf = image_get_ibuf_threadsafe(ima, iuser, NULL, NULL);
+       BLI_spin_lock(&image_spin);
 
-       if (!ibuf) {
-               BLI_spin_lock(&image_spin);
+       ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL);
 
-               ibuf = image_get_ibuf_threadsafe(ima, iuser, NULL, NULL);
+       if (!ibuf)
+               ibuf = image_acquire_ibuf(ima, iuser, NULL);
 
-               if (!ibuf)
-                       ibuf = image_acquire_ibuf(ima, iuser, NULL);
+       BLI_spin_unlock(&image_spin);
 
-               BLI_spin_unlock(&image_spin);
-       }
+       IMB_freeImBuf(ibuf);
 
        return ibuf != NULL;
 }
@@ -3187,9 +3226,6 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool
 
                ibuf = image_acquire_ibuf(ima, iuser, NULL);
 
-               if (ibuf)
-                       IMB_refImBuf(ibuf);
-
                entry = MEM_callocN(sizeof(ImagePoolEntry), "Image Pool Entry");
                entry->image = ima;
                entry->frame = frame;
@@ -3455,3 +3491,120 @@ int BKE_image_sequence_guess_offset(Image *image)
 
        return atoi(num);
 }
+
+bool BKE_image_is_dirty(Image *image)
+{
+       bool is_dirty = false;
+
+       BLI_spin_lock(&image_spin);
+       if (image->cache != NULL) {
+               struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache);
+
+               while (!IMB_moviecacheIter_done(iter)) {
+                       ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter);
+                       if (ibuf->userflags & IB_BITMAPDIRTY) {
+                               is_dirty = true;
+                               break;
+                       }
+                       IMB_moviecacheIter_step(iter);
+               }
+               IMB_moviecacheIter_free(iter);
+       }
+       BLI_spin_unlock(&image_spin);
+
+       return is_dirty;
+}
+
+void BKE_image_file_format_set(Image *image, int ftype)
+{
+#if 0
+       ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL);
+       if (ibuf) {
+               ibuf->ftype = ftype;
+       }
+       BKE_image_release_ibuf(image, ibuf, NULL);
+#endif
+
+       BLI_spin_lock(&image_spin);
+       if (image->cache != NULL) {
+               struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache);
+
+               while (!IMB_moviecacheIter_done(iter)) {
+                       ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter);
+                       ibuf->ftype = ftype;
+                       IMB_moviecacheIter_step(iter);
+               }
+               IMB_moviecacheIter_free(iter);
+       }
+       BLI_spin_unlock(&image_spin);
+}
+
+bool BKE_image_has_loaded_ibuf(Image *image)
+{
+       bool has_loaded_ibuf = false;
+
+       BLI_spin_lock(&image_spin);
+       if (image->cache != NULL) {
+               struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache);
+
+               while (!IMB_moviecacheIter_done(iter)) {
+                       has_loaded_ibuf = true;
+                       break;
+               }
+               IMB_moviecacheIter_free(iter);
+       }
+       BLI_spin_unlock(&image_spin);
+
+       return has_loaded_ibuf;
+}
+
+/* References the result, IMB_freeImBuf is to be called to de-reference. */
+ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name)
+{
+       ImBuf *ibuf = NULL;
+
+       BLI_spin_lock(&image_spin);
+       if (image->cache != NULL) {
+               struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache);
+
+               while (!IMB_moviecacheIter_done(iter)) {
+                       ImBuf *current_ibuf = IMB_moviecacheIter_getImBuf(iter);
+                       if (STREQ(current_ibuf->name, name)) {
+                               ibuf = current_ibuf;
+                               IMB_refImBuf(ibuf);
+                               break;
+                       }
+               }
+               IMB_moviecacheIter_free(iter);
+       }
+       BLI_spin_unlock(&image_spin);
+
+       return ibuf;
+}
+
+/* References the result, IMB_freeImBuf is to be called to de-reference.
+ *
+ * TODO(sergey): This is actually "get first entry from the cache", which is
+ *               not so much predictable. But using first loaded image buffer
+ *               was also malicious logic and all the areas which uses this
+ *               function are to be re-considered.
+ */
+ImBuf *BKE_image_get_first_ibuf(Image *image)
+{
+       ImBuf *ibuf = NULL;
+
+       BLI_spin_lock(&image_spin);
+       if (image->cache != NULL) {
+               struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache);
+
+               while (!IMB_moviecacheIter_done(iter)) {
+                       ibuf = IMB_moviecacheIter_getImBuf(iter);
+                       IMB_refImBuf(ibuf);
+                       break;
+               }
+               IMB_moviecacheIter_free(iter);
+       }
+       BLI_spin_unlock(&image_spin);
+
+       return ibuf;
+}
index 33a76d0c03d3251c5d5a656818b65389f53322a2..919d38be9e0911d12b40007c8da207b36f160900 100644 (file)
@@ -195,7 +195,7 @@ void BKE_sequencer_cache_cleanup(void)
        BKE_sequencer_preprocessed_cache_cleanup();
 }
 
-static int seqcache_key_check_seq(void *userkey, void *userdata)
+static bool seqcache_key_check_seq(ImBuf *UNUSED(ibuf), void *userkey, void *userdata)
 {
        SeqCacheKey *key = (SeqCacheKey *) userkey;
        Sequence *seq = (Sequence *) userdata;
index 37d2ec787ed10a20e388443059ac5e7b5c0dfa81..6e64dcc0560f7af8a7b17389ef5b0bac8501d8f3 100644 (file)
@@ -1317,9 +1317,8 @@ void blo_make_image_pointer_map(FileData *fd, Main *oldmain)
        fd->imamap = oldnewmap_new();
        
        for (; ima; ima = ima->id.next) {
-               Link *ibuf = ima->ibufs.first;
-               for (; ibuf; ibuf = ibuf->next)
-                       oldnewmap_insert(fd->imamap, ibuf, ibuf, 0);
+               if (ima->cache)
+                       oldnewmap_insert(fd->imamap, ima->cache, ima->cache, 0);
                if (ima->gputexture)
                        oldnewmap_insert(fd->imamap, ima->gputexture, ima->gputexture, 0);
                if (ima->rr)
@@ -1355,19 +1354,7 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain)
        }
        
        for (; ima; ima = ima->id.next) {
-               Link *ibuf, *next;
-               
-               /* this mirrors direct_link_image */
-               for (ibuf = ima->ibufs.first; ibuf; ibuf = next) {
-                       next = ibuf->next;
-                       if (NULL == newimaadr(fd, ibuf)) {      /* so was restored */
-                               BLI_remlink(&ima->ibufs, ibuf);
-                               ima->bindcode = 0;
-                               ima->tpageflag &= ~IMA_GLBIND_IS_DATA;
-                               ima->gputexture = NULL;
-                               ima->rr = NULL;
-                       }
-               }
+               ima->cache = newmclipadr(fd, ima->cache);
                for (i = 0; i < IMA_MAX_RENDER_SLOT; i++)
                        ima->renders[i] = newimaadr(fd, ima->renders[i]);
                
@@ -3281,34 +3268,16 @@ static void lib_link_image(FileData *fd, Main *main)
        }
 }
 
-static void link_ibuf_list(FileData *fd, ListBase *lb)
-{
-       Link *ln, *prev;
-       
-       if (lb->first == NULL) return;
-       
-       lb->first = newimaadr(fd, lb->first);
-       ln = lb->first;
-       prev = NULL;
-       while (ln) {
-               ln->next = newimaadr(fd, ln->next);
-               ln->prev = prev;
-               prev = ln;
-               ln = ln->next;
-       }
-       lb->last = prev;
-}
-
 static void direct_link_image(FileData *fd, Image *ima)
 {
        /* for undo system, pointers could be restored */
        if (fd->imamap)
-               link_ibuf_list(fd, &ima->ibufs);
+               ima->cache = newmclipadr(fd, ima->cache);
        else
-               ima->ibufs.first = ima->ibufs.last = NULL;
+               ima->cache = NULL;
 
        /* if not restored, we keep the binded opengl index */
-       if (ima->ibufs.first == NULL) {
+       if (!fd->imamap) {
                ima->bindcode = 0;
                ima->tpageflag &= ~IMA_GLBIND_IS_DATA;
                ima->gputexture = NULL;
index 47ca3e5ce0c87209dc7e1c5c67b700c064397b8f..5b323a0a3962e0d11badf06a869ddb3c6a0898b7 100644 (file)
@@ -250,6 +250,7 @@ void ED_image_undo_restore(bContext *C, ListBase *lb)
 
        for (tile = lb->first; tile; tile = tile->next) {
                short use_float;
+               bool need_release = true;
 
                /* find image based on name, pointer becomes invalid with global undo */
                if (ima && strcmp(tile->idname, ima->id.name) == 0) {
@@ -268,8 +269,9 @@ void ED_image_undo_restore(bContext *C, ListBase *lb)
                         * matched file name in list of already loaded images */
 
                        BKE_image_release_ibuf(ima, ibuf, NULL);
+                       need_release = false;
 
-                       ibuf = BLI_findstring(&ima->ibufs, tile->ibufname, offsetof(ImBuf, name));
+                       ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname);
                }
 
                if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) {
@@ -298,7 +300,12 @@ void ED_image_undo_restore(bContext *C, ListBase *lb)
                        ibuf->userflags |= IB_MIPMAP_INVALID;  /* force mipmap recreatiom */
                ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
 
-               BKE_image_release_ibuf(ima, ibuf, NULL);
+               if (need_release) {
+                       BKE_image_release_ibuf(ima, ibuf, NULL);
+               }
+               else {
+                       IMB_freeImBuf(ibuf);
+               }
        }
 
        IMB_freeImBuf(tmpibuf);
index d6989c082a101ca6575b01a050bba281d02b7872..4b402bc174140ff67a600c032dab8092451256b7 100644 (file)
@@ -542,8 +542,7 @@ static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2],
        }
 
        ima = project_paint_face_image(ps, ps->dm_mtface, face_index);
-       ibuf = ima->ibufs.first; /* we must have got the imbuf before getting here */
-       if (!ibuf) return 0;
+       ibuf = BKE_image_get_first_ibuf(ima); /* we must have got the imbuf before getting here */
 
        if (interp) {
                float x, y;
@@ -599,6 +598,7 @@ static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2],
                        }
                }
        }
+       IMB_freeImBuf(ibuf);
        return 1;
 }
 
index bda4be40c5c40a520f46fa51e1b3abb971292bc6..a2f49af1730f5b62e2de1d6645b984b64c47f95e 100644 (file)
@@ -63,6 +63,7 @@
 #include "IMB_colormanagement.h"
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
+#include "IMB_moviecache.h"
 
 #include "RE_pipeline.h"
 
@@ -1628,9 +1629,10 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op)
 {
        Main *bmain = CTX_data_main(C);
        SpaceImage *sima = CTX_wm_space_image(C);
-       ImBuf *ibuf;
+       ImBuf *ibuf, *first_ibuf = NULL;
        int tot = 0;
        char di[FILE_MAX];
+       struct MovieCacheIter *iter;
        
        if (sima->image == NULL)
                return OPERATOR_CANCELLED;
@@ -1645,10 +1647,22 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op)
                return OPERATOR_CANCELLED;
        }
        
-       /* get total */
-       for (ibuf = sima->image->ibufs.first; ibuf; ibuf = ibuf->next)
-               if (ibuf->userflags & IB_BITMAPDIRTY)
-                       tot++;
+       /* get total dirty buffers and first dirty buffer which is used for menu */
+       ibuf = NULL;
+       if (sima->image->cache != NULL) {
+               iter = IMB_moviecacheIter_new(sima->image->cache);
+               while (!IMB_moviecacheIter_done(iter)) {
+                       ibuf = IMB_moviecacheIter_getImBuf(iter);
+                       if (ibuf->userflags & IB_BITMAPDIRTY) {
+                               if (first_ibuf == NULL) {
+                                       first_ibuf = ibuf;
+                               }
+                               tot++;
+                       }
+                       IMB_moviecacheIter_step(iter);
+               }
+               IMB_moviecacheIter_free(iter);
+       }
        
        if (tot == 0) {
                BKE_report(op->reports, RPT_WARNING, "No images have been changed");
@@ -1656,18 +1670,17 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op)
        }
 
        /* get a filename for menu */
-       for (ibuf = sima->image->ibufs.first; ibuf; ibuf = ibuf->next)
-               if (ibuf->userflags & IB_BITMAPDIRTY)
-                       break;
-
-       BLI_split_dir_part(ibuf->name, di, sizeof(di));
+       BLI_split_dir_part(first_ibuf->name, di, sizeof(di));
        BKE_reportf(op->reports, RPT_INFO, "%d image(s) will be saved in %s", tot, di);
 
-       for (ibuf = sima->image->ibufs.first; ibuf; ibuf = ibuf->next) {
+       iter = IMB_moviecacheIter_new(sima->image->cache);
+       while (!IMB_moviecacheIter_done(iter)) {
+               ibuf = IMB_moviecacheIter_getImBuf(iter);
+
                if (ibuf->userflags & IB_BITMAPDIRTY) {
                        char name[FILE_MAX];
                        BLI_strncpy(name, ibuf->name, sizeof(name));
-                       
+
                        BLI_path_abs(name, bmain->name);
 
                        if (0 == IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat)) {
@@ -1678,7 +1691,10 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op)
                        BKE_reportf(op->reports, RPT_INFO, "Saved %s", ibuf->name);
                        ibuf->userflags &= ~IB_BITMAPDIRTY;
                }
+
+               IMB_moviecacheIter_step(iter);
        }
+       IMB_moviecacheIter_free(iter);
 
        return OPERATOR_FINISHED;
 }
index 4e367c1d48d24c7eb13e46cbec07750b9bfc04b8..6c76ba64893c65c3d5cf41ae580742199795b270 100644 (file)
@@ -145,7 +145,7 @@ static int pack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev
        
        // first check for dirty images
        for (ima = bmain->image.first; ima; ima = ima->id.next) {
-               if (ima->ibufs.first) { /* XXX FIX */
+               if (BKE_image_has_loaded_ibuf(ima)) { /* XXX FIX */
                        ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
                        
                        if (ibuf && (ibuf->userflags & IB_BITMAPDIRTY)) {
index 0dba2cd50e83b0664b0d53c2c8a82b62dfdbe9f7..ee59cf418bf492cd479a0be322ea7e49de8275e6 100644 (file)
@@ -93,6 +93,7 @@ void GPU_render_text(MTFace *tface, int mode,
 {
        if ((mode & GEMAT_TEXT) && (textlen>0) && tface->tpage) {
                Image* ima = (Image *)tface->tpage;
+               ImBuf *first_ibuf;
                int index, character;
                float centerx, centery, sizex, sizey, transx, transy, movex, movey, advance;
                float advance_tab;
@@ -117,7 +118,8 @@ void GPU_render_text(MTFace *tface, int mode,
                glPushMatrix();
                
                /* get the tab width */
-               matrixGlyph((ImBuf *)ima->ibufs.first, ' ', & centerx, &centery,
+               first_ibuf = BKE_image_get_first_ibuf(ima);
+               matrixGlyph(first_ibuf, ' ', &centerx, &centery,
                        &sizex, &sizey, &transx, &transy, &movex, &movey, &advance);
                
                advance_tab= advance * 4; /* tab width could also be an option */
@@ -143,7 +145,7 @@ void GPU_render_text(MTFace *tface, int mode,
                        
                        // space starts at offset 1
                        // character = character - ' ' + 1;
-                       matrixGlyph((ImBuf *)ima->ibufs.first, character, & centerx, &centery,
+                       matrixGlyph(first_ibuf, character, & centerx, &centery,
                                &sizex, &sizey, &transx, &transy, &movex, &movey, &advance);
 
                        uv[0][0] = (tface->uv[0][0] - centerx) * sizex + transx;
@@ -184,6 +186,8 @@ void GPU_render_text(MTFace *tface, int mode,
                        line_start -= advance; /* so we can go back to the start of the line */
                }
                glPopMatrix();
+
+               IMB_freeImBuf(first_ibuf);
        }
 }
 
index 1c5697129688b103005a45a8dc69f5f357591c63..3432741596f87588bc22c4a70aa240eaad4c8728 100644 (file)
@@ -63,8 +63,18 @@ struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey);
 int IMB_moviecache_has_frame(struct MovieCache *cache, void *userkey);
 void IMB_moviecache_free(struct MovieCache *cache);
 
-void IMB_moviecache_cleanup(struct MovieCache *cache, int (cleanup_check_cb) (void *userkey, void *userdata), void *userdata);
+void IMB_moviecache_cleanup(struct MovieCache *cache,
+                            bool (cleanup_check_cb) (struct ImBuf *ibuf, void *userkey, void *userdata),
+                            void *userdata);
 
 void IMB_moviecache_get_cache_segments(struct MovieCache *cache, int proxy, int render_flags, int *totseg_r, int **points_r);
 
+struct MovieCacheIter;
+struct MovieCacheIter *IMB_moviecacheIter_new(struct MovieCache *cache);
+void IMB_moviecacheIter_free(struct MovieCacheIter *iter);
+bool IMB_moviecacheIter_done(struct MovieCacheIter *iter);
+void IMB_moviecacheIter_step(struct MovieCacheIter *iter);
+struct ImBuf *IMB_moviecacheIter_getImBuf(struct MovieCacheIter *iter);
+void *IMB_moviecacheIter_getUserKey(struct MovieCacheIter *iter);
+
 #endif
index 90eea438c5e9bcb52cd86cb9fed0eb532f2e40cc..c287cf4c005623c82fb33b3f554528b2a95bfe38 100644 (file)
@@ -443,23 +443,18 @@ void IMB_moviecache_free(MovieCache *cache)
        MEM_freeN(cache);
 }
 
-void IMB_moviecache_cleanup(MovieCache *cache, int (cleanup_check_cb) (void *userkey, void *userdata), void *userdata)
+void IMB_moviecache_cleanup(MovieCache *cache, bool (cleanup_check_cb) (ImBuf *ibuf, void *userkey, void *userdata), void *userdata)
 {
        GHashIterator *iter;
 
        iter = BLI_ghashIterator_new(cache->hash);
        while (!BLI_ghashIterator_done(iter)) {
                MovieCacheKey *key = BLI_ghashIterator_getKey(iter);
-               int remove;
+               MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
 
                BLI_ghashIterator_step(iter);
 
-               remove = cleanup_check_cb(key->userkey, userdata);
-
-               if (remove) {
-                       MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
-                       (void) item;  /* silence unused variable when not using debug */
-
+               if (cleanup_check_cb(item->ibuf, key->userkey, userdata)) {
                        PRINT("%s: cache '%s' remove item %p\n", __func__, cache->name, item);
 
                        BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
@@ -556,3 +551,36 @@ void IMB_moviecache_get_cache_segments(MovieCache *cache, int proxy, int render_
                MEM_freeN(frames);
        }
 }
+
+struct MovieCacheIter *IMB_moviecacheIter_new(MovieCache *cache)
+{
+       GHashIterator *iter = BLI_ghashIterator_new(cache->hash);
+       return (struct MovieCacheIter *) iter;
+}
+
+void IMB_moviecacheIter_free(struct MovieCacheIter *iter)
+{
+       BLI_ghashIterator_free((GHashIterator *) iter);
+}
+
+bool IMB_moviecacheIter_done(struct MovieCacheIter *iter)
+{
+       return BLI_ghashIterator_done((GHashIterator *) iter);
+}
+
+void IMB_moviecacheIter_step(struct MovieCacheIter *iter)
+{
+       BLI_ghashIterator_step((GHashIterator *) iter);
+}
+
+ImBuf *IMB_moviecacheIter_getImBuf(struct MovieCacheIter *iter)
+{
+       MovieCacheItem *item = BLI_ghashIterator_getValue((GHashIterator *) iter);
+       return item->ibuf;
+}
+
+void *IMB_moviecacheIter_getUserKey(struct MovieCacheIter *iter)
+{
+       MovieCacheKey *key = BLI_ghashIterator_getKey((GHashIterator *) iter);
+       return key->userkey;
+}
index 6b0299e6d50fdbd7c38d8988ba41a4baedc7627c..0b9dddd0ea5eadd6ca52b2e1d736301fd11a219c 100644 (file)
@@ -40,6 +40,7 @@ struct PackedFile;
 struct Scene;
 struct anim;
 struct ImBuf;
+struct MovieCache;
 struct RenderResult;
 struct GPUTexture;
 
@@ -74,7 +75,7 @@ typedef struct Image {
        
        char name[1024];                        /* file path, 1024 = FILE_MAX */
        
-       ListBase ibufs;                                 /* not written in file */
+       struct MovieCache *cache;       /* not written in file */
        struct GPUTexture *gputexture;  /* not written in file */
        
        /* sources from: */
index c901abc834ead898685c5f5b03fe5b6b71ca4e46..69b2d2d0227d3016a45adad0d901535f1ee4f74e 100644 (file)
@@ -79,14 +79,7 @@ static void rna_Image_animated_update(Main *UNUSED(bmain), Scene *UNUSED(scene),
 
 static int rna_Image_dirty_get(PointerRNA *ptr)
 {
-       Image *ima = (Image *)ptr->data;
-       ImBuf *ibuf;
-
-       for (ibuf = ima->ibufs.first; ibuf; ibuf = ibuf->next)
-               if (ibuf->userflags & IB_BITMAPDIRTY)
-                       return 1;
-       
-       return 0;
+       return BKE_image_is_dirty((Image *)ptr->data);
 }
 
 static void rna_Image_source_set(PointerRNA *ptr, int value)
@@ -210,31 +203,16 @@ static void rna_Image_file_format_set(PointerRNA *ptr, int value)
 {
        Image *image = (Image *)ptr->data;
        if (BKE_imtype_is_movie(value) == 0) { /* should be able to throw an error here */
-               ImBuf *ibuf;
                int ftype = BKE_imtype_to_ftype(value);
-
-#if 0
-               ibuf = BKE_image_acquire_ibuf(image, NULL, NULL);
-               if (ibuf)
-                       ibuf->ftype = ftype;
-#endif
-
-               /* to be safe change all buffer file types */
-               /* TODO: this is never threadsafe */
-               for (ibuf = image->ibufs.first; ibuf; ibuf = ibuf->next) {
-                       ibuf->ftype = ftype;
-               }
+               BKE_image_file_format_set(image, ftype);
        }
 }
 
 static int rna_Image_has_data_get(PointerRNA *ptr)
 {
-       Image *im = (Image *)ptr->data;
-
-       if (im->ibufs.first)
-               return 1;
+       Image *image = (Image *)ptr->data;
 
-       return 0;
+       return BKE_image_has_loaded_ibuf(image);
 }
 
 static void rna_Image_size_get(PointerRNA *ptr, int *values)
index bfc13bf615122a984f16dff3b7b47939a5d8c01c..37c6a5ffdd1c245107c6f1a5de9087c822f9ca44 100644 (file)
@@ -127,7 +127,7 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul
        if (ima) {
                
                /* hack for icon render */
-               if (ima->ibufs.first==NULL && (R.r.scemode & R_NO_IMAGE_LOAD))
+               if ((R.r.scemode & R_NO_IMAGE_LOAD) && !BKE_image_has_loaded_ibuf(ima))
                        return retval;
 
                ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool);
@@ -1094,7 +1094,9 @@ static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float tex
        if (ibuf==NULL && ima==NULL) return retval;
 
        if (ima) {      /* hack for icon render */
-               if ((ima->ibufs.first == NULL) && (R.r.scemode & R_NO_IMAGE_LOAD)) return retval;
+               if ((R.r.scemode & R_NO_IMAGE_LOAD) && !BKE_image_has_loaded_ibuf(ima)) {
+                       return retval;
+               }
                ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool);
        }
 
@@ -1515,7 +1517,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
        if (ima) {
 
                /* hack for icon render */
-               if (ima->ibufs.first==NULL && (R.r.scemode & R_NO_IMAGE_LOAD))
+               if ((R.r.scemode & R_NO_IMAGE_LOAD) && !BKE_image_has_loaded_ibuf(ima))
                        return retval;
                
                ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool);