summaryrefslogtreecommitdiff
path: root/server/image-cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/image-cache.c')
-rw-r--r--server/image-cache.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/server/image-cache.c b/server/image-cache.c
new file mode 100644
index 00000000..42370340
--- /dev/null
+++ b/server/image-cache.c
@@ -0,0 +1,214 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009-2015 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "image-cache.h"
+#include "red-parse-qxl.h"
+#include "display-channel.h"
+
+static ImageCacheItem *image_cache_find(ImageCache *cache, uint64_t id)
+{
+ ImageCacheItem *item = cache->hash_table[id % IMAGE_CACHE_HASH_SIZE];
+
+ while (item) {
+ if (item->id == id) {
+ return item;
+ }
+ item = item->next;
+ }
+ return NULL;
+}
+
+int image_cache_hit(ImageCache *cache, uint64_t id)
+{
+ ImageCacheItem *item;
+ if (!(item = image_cache_find(cache, id))) {
+ return FALSE;
+ }
+#ifdef IMAGE_CACHE_AGE
+ item->age = cache->age;
+#endif
+ ring_remove(&item->lru_link);
+ ring_add(&cache->lru, &item->lru_link);
+ return TRUE;
+}
+
+static void image_cache_remove(ImageCache *cache, ImageCacheItem *item)
+{
+ ImageCacheItem **now;
+
+ now = &cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
+ for (;;) {
+ spice_assert(*now);
+ if (*now == item) {
+ *now = item->next;
+ break;
+ }
+ now = &(*now)->next;
+ }
+ ring_remove(&item->lru_link);
+ pixman_image_unref(item->image);
+ free(item);
+#ifndef IMAGE_CACHE_AGE
+ cache->num_items--;
+#endif
+}
+
+#define IMAGE_CACHE_MAX_ITEMS 2
+
+static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, pixman_image_t *image)
+{
+ ImageCache *cache = (ImageCache *)spice_cache;
+ ImageCacheItem *item;
+
+#ifndef IMAGE_CACHE_AGE
+ if (cache->num_items == IMAGE_CACHE_MAX_ITEMS) {
+ ImageCacheItem *tail = (ImageCacheItem *)ring_get_tail(&cache->lru);
+ spice_assert(tail);
+ image_cache_remove(cache, tail);
+ }
+#endif
+
+ item = spice_new(ImageCacheItem, 1);
+ item->id = id;
+#ifdef IMAGE_CACHE_AGE
+ item->age = cache->age;
+#else
+ cache->num_items++;
+#endif
+ item->image = pixman_image_ref(image);
+ ring_item_init(&item->lru_link);
+
+ item->next = cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
+ cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE] = item;
+
+ ring_add(&cache->lru, &item->lru_link);
+}
+
+static pixman_image_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t id)
+{
+ ImageCache *cache = (ImageCache *)spice_cache;
+
+ ImageCacheItem *item = image_cache_find(cache, id);
+ if (!item) {
+ spice_error("not found");
+ }
+ return pixman_image_ref(item->image);
+}
+
+void image_cache_init(ImageCache *cache)
+{
+ static SpiceImageCacheOps image_cache_ops = {
+ image_cache_put,
+ image_cache_get,
+ };
+
+ cache->base.ops = &image_cache_ops;
+ memset(cache->hash_table, 0, sizeof(cache->hash_table));
+ ring_init(&cache->lru);
+#ifdef IMAGE_CACHE_AGE
+ cache->age = 0;
+#else
+ cache->num_items = 0;
+#endif
+}
+
+void image_cache_reset(ImageCache *cache)
+{
+ ImageCacheItem *item;
+
+ while ((item = (ImageCacheItem *)ring_get_head(&cache->lru))) {
+ image_cache_remove(cache, item);
+ }
+#ifdef IMAGE_CACHE_AGE
+ cache->age = 0;
+#endif
+}
+
+#define IMAGE_CACHE_DEPTH 4
+
+void image_cache_aging(ImageCache *cache)
+{
+#ifdef IMAGE_CACHE_AGE
+ ImageCacheItem *item;
+
+ cache->age++;
+ while ((item = (ImageCacheItem *)ring_get_tail(&cache->lru)) &&
+ cache->age - item->age > IMAGE_CACHE_DEPTH) {
+ image_cache_remove(cache, item);
+ }
+#endif
+}
+
+void image_cache_localize(ImageCache *cache, SpiceImage **image_ptr,
+ SpiceImage *image_store, Drawable *drawable)
+{
+ SpiceImage *image = *image_ptr;
+
+ if (image == NULL) {
+ spice_assert(drawable != NULL);
+ spice_assert(drawable->red_drawable->self_bitmap_image != NULL);
+ *image_ptr = drawable->red_drawable->self_bitmap_image;
+ return;
+ }
+
+ if (image_cache_hit(cache, image->descriptor.id)) {
+ image_store->descriptor = image->descriptor;
+ image_store->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
+ image_store->descriptor.flags = 0;
+ *image_ptr = image_store;
+ return;
+ }
+
+ switch (image->descriptor.type) {
+ case SPICE_IMAGE_TYPE_QUIC: {
+ image_store->descriptor = image->descriptor;
+ image_store->u.quic = image->u.quic;
+ *image_ptr = image_store;
+#ifdef IMAGE_CACHE_AGE
+ image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
+#else
+ if (image_store->descriptor.width * image->descriptor.height >= 640 * 480) {
+ image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
+ }
+#endif
+ break;
+ }
+ case SPICE_IMAGE_TYPE_BITMAP:
+ case SPICE_IMAGE_TYPE_SURFACE:
+ /* nothing */
+ break;
+ default:
+ spice_error("invalid image type");
+ }
+}
+
+void image_cache_localize_brush(ImageCache *cache, SpiceBrush *brush, SpiceImage *image_store)
+{
+ if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
+ image_cache_localize(cache, &brush->u.pattern.pat, image_store, NULL);
+ }
+}
+
+void image_cache_localize_mask(ImageCache *cache, SpiceQMask *mask, SpiceImage *image_store)
+{
+ if (mask->bitmap) {
+ image_cache_localize(cache, &mask->bitmap, image_store, NULL);
+ }
+}