Image cache rewrite to using generic movie cache
[blender.git] / source / blender / blenkernel / intern / image.c
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;
+}