summaryrefslogtreecommitdiffstats
path: root/server/dcc.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/dcc.c')
-rw-r--r--server/dcc.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/server/dcc.c b/server/dcc.c
index a14247c7..1e188140 100644
--- a/server/dcc.c
+++ b/server/dcc.c
@@ -309,3 +309,634 @@ void dcc_destroy_surface(DisplayChannelClient *dcc, uint32_t surface_id)
destroy = surface_destroy_item_new(channel, surface_id);
red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &destroy->pipe_item);
}
+
+/* if already exists, returns it. Otherwise allocates and adds it (1) to the ring tail
+ in the channel (2) to the Drawable*/
+static RedGlzDrawable *get_glz_drawable(DisplayChannelClient *dcc, Drawable *drawable)
+{
+ RedGlzDrawable *ret;
+ RingItem *item, *next;
+
+ // TODO - I don't really understand what's going on here, so doing the technical equivalent
+ // now that we have multiple glz_dicts, so the only way to go from dcc to drawable glz is to go
+ // over the glz_ring (unless adding some better data structure then a ring)
+ DRAWABLE_FOREACH_GLZ_SAFE(drawable, item, next, ret) {
+ if (ret->dcc == dcc) {
+ return ret;
+ }
+ }
+
+ ret = spice_new(RedGlzDrawable, 1);
+
+ ret->dcc = dcc;
+ ret->red_drawable = red_drawable_ref(drawable->red_drawable);
+ ret->drawable = drawable;
+ ret->group_id = drawable->group_id;
+ ret->instances_count = 0;
+ ring_init(&ret->instances);
+
+ ring_item_init(&ret->link);
+ ring_item_init(&ret->drawable_link);
+ ring_add_before(&ret->link, &dcc->glz_drawables);
+ ring_add(&drawable->glz_ring, &ret->drawable_link);
+ DCC_TO_DC(dcc)->glz_drawable_count++;
+ return ret;
+}
+
+/* allocates new instance and adds it to instances in the given drawable.
+ NOTE - the caller should set the glz_instance returned by the encoder by itself.*/
+static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable *glz_drawable)
+{
+ spice_assert(glz_drawable->instances_count < MAX_GLZ_DRAWABLE_INSTANCES);
+ // NOTE: We assume the additions are performed consecutively, without removals in the middle
+ GlzDrawableInstanceItem *ret = glz_drawable->instances_pool + glz_drawable->instances_count;
+ glz_drawable->instances_count++;
+
+ ring_item_init(&ret->free_link);
+ ring_item_init(&ret->glz_link);
+ ring_add(&glz_drawable->instances, &ret->glz_link);
+ ret->context = NULL;
+ ret->glz_drawable = glz_drawable;
+
+ return ret;
+}
+
+#define MIN_GLZ_SIZE_FOR_ZLIB 100
+
+int dcc_compress_image_glz(DisplayChannelClient *dcc,
+ SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
+ compress_send_data_t* o_comp_data)
+{
+ DisplayChannel *display_channel = DCC_TO_DC(dcc);
+#ifdef COMPRESS_STAT
+ stat_time_t start_time = stat_now(display_channel->glz_stat.clock);
+#endif
+ spice_assert(bitmap_fmt_is_rgb(src->format));
+ GlzData *glz_data = &dcc->glz_data;
+ ZlibData *zlib_data;
+ LzImageType type = MAP_BITMAP_FMT_TO_LZ_IMAGE_TYPE[src->format];
+ RedGlzDrawable *glz_drawable;
+ GlzDrawableInstanceItem *glz_drawable_instance;
+ int glz_size;
+ int zlib_size;
+
+ glz_data->data.bufs_tail = compress_buf_new();
+ glz_data->data.bufs_head = glz_data->data.bufs_tail;
+ glz_data->data.dcc = dcc;
+
+ glz_drawable = get_glz_drawable(dcc, drawable);
+ glz_drawable_instance = add_glz_drawable_instance(glz_drawable);
+
+ glz_data->data.u.lines_data.chunks = src->data;
+ glz_data->data.u.lines_data.stride = src->stride;
+ glz_data->data.u.lines_data.next = 0;
+ glz_data->data.u.lines_data.reverse = 0;
+
+ glz_size = glz_encode(dcc->glz, type, src->x, src->y,
+ (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL, 0,
+ src->stride, glz_data->data.bufs_head->buf.bytes,
+ sizeof(glz_data->data.bufs_head->buf),
+ glz_drawable_instance,
+ &glz_drawable_instance->context);
+
+ stat_compress_add(&display_channel->glz_stat, start_time, src->stride * src->y, glz_size);
+
+ if (!display_channel->enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
+ goto glz;
+ }
+#ifdef COMPRESS_STAT
+ start_time = stat_now(display_channel->zlib_glz_stat.clock);
+#endif
+ zlib_data = &dcc->zlib_data;
+
+ zlib_data->data.bufs_tail = compress_buf_new();
+ zlib_data->data.bufs_head = zlib_data->data.bufs_tail;
+ zlib_data->data.dcc = dcc;
+
+ zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head;
+ zlib_data->data.u.compressed_data.size_left = glz_size;
+
+ zlib_size = zlib_encode(dcc->zlib, dcc->zlib_level,
+ glz_size, zlib_data->data.bufs_head->buf.bytes,
+ sizeof(zlib_data->data.bufs_head->buf));
+
+ // the compressed buffer is bigger than the original data
+ if (zlib_size >= glz_size) {
+ while (zlib_data->data.bufs_head) {
+ RedCompressBuf *buf = zlib_data->data.bufs_head;
+ zlib_data->data.bufs_head = buf->send_next;
+ compress_buf_free(buf);
+ }
+ goto glz;
+ }
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB;
+ dest->u.zlib_glz.glz_data_size = glz_size;
+ dest->u.zlib_glz.data_size = zlib_size;
+
+ o_comp_data->comp_buf = zlib_data->data.bufs_head;
+ o_comp_data->comp_buf_size = zlib_size;
+
+ stat_compress_add(&display_channel->zlib_glz_stat, start_time, glz_size, zlib_size);
+ return TRUE;
+glz:
+ dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
+ dest->u.lz_rgb.data_size = glz_size;
+
+ o_comp_data->comp_buf = glz_data->data.bufs_head;
+ o_comp_data->comp_buf_size = glz_size;
+
+ return TRUE;
+}
+
+int dcc_compress_image_lz(DisplayChannelClient *dcc,
+ SpiceImage *dest, SpiceBitmap *src,
+ compress_send_data_t* o_comp_data, uint32_t group_id)
+{
+ LzData *lz_data = &dcc->lz_data;
+ LzContext *lz = dcc->lz;
+ LzImageType type = MAP_BITMAP_FMT_TO_LZ_IMAGE_TYPE[src->format];
+ int size; // size of the compressed data
+
+#ifdef COMPRESS_STAT
+ stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->lz_stat.clock);
+#endif
+
+ lz_data->data.bufs_tail = compress_buf_new();
+ lz_data->data.bufs_head = lz_data->data.bufs_tail;
+ lz_data->data.dcc = dcc;
+
+ if (setjmp(lz_data->data.jmp_env)) {
+ while (lz_data->data.bufs_head) {
+ RedCompressBuf *buf = lz_data->data.bufs_head;
+ lz_data->data.bufs_head = buf->send_next;
+ compress_buf_free(buf);
+ }
+ return FALSE;
+ }
+
+ lz_data->data.u.lines_data.chunks = src->data;
+ lz_data->data.u.lines_data.stride = src->stride;
+ lz_data->data.u.lines_data.next = 0;
+ lz_data->data.u.lines_data.reverse = 0;
+
+ size = lz_encode(lz, type, src->x, src->y,
+ !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
+ NULL, 0, src->stride,
+ lz_data->data.bufs_head->buf.bytes,
+ sizeof(lz_data->data.bufs_head->buf));
+
+ // the compressed buffer is bigger than the original data
+ if (size > (src->y * src->stride)) {
+ longjmp(lz_data->data.jmp_env, 1);
+ }
+
+ if (bitmap_fmt_is_rgb(src->format)) {
+ dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB;
+ dest->u.lz_rgb.data_size = size;
+
+ o_comp_data->comp_buf = lz_data->data.bufs_head;
+ o_comp_data->comp_buf_size = size;
+ } else {
+ /* masks are 1BIT bitmaps without palettes, but they are not compressed
+ * (see fill_mask) */
+ spice_assert(src->palette);
+ dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT;
+ dest->u.lz_plt.data_size = size;
+ dest->u.lz_plt.flags = src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
+ dest->u.lz_plt.palette = src->palette;
+ dest->u.lz_plt.palette_id = src->palette->unique;
+ o_comp_data->comp_buf = lz_data->data.bufs_head;
+ o_comp_data->comp_buf_size = size;
+
+ dcc_palette_cache_palette(dcc, dest->u.lz_plt.palette, &(dest->u.lz_plt.flags));
+ o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
+ }
+
+ stat_compress_add(&DCC_TO_DC(dcc)->lz_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+
+int dcc_compress_image_jpeg(DisplayChannelClient *dcc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data,
+ uint32_t group_id)
+{
+ JpegData *jpeg_data = &dcc->jpeg_data;
+ LzData *lz_data = &dcc->lz_data;
+ JpegEncoderContext *jpeg = dcc->jpeg;
+ LzContext *lz = dcc->lz;
+ volatile JpegEncoderImageType jpeg_in_type;
+ int jpeg_size = 0;
+ volatile int has_alpha = FALSE;
+ int alpha_lz_size = 0;
+ int comp_head_filled;
+ int comp_head_left;
+ int stride;
+ uint8_t *lz_out_start_byte;
+
+#ifdef COMPRESS_STAT
+ stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->jpeg_stat.clock);
+#endif
+ switch (src->format) {
+ case SPICE_BITMAP_FMT_16BIT:
+ jpeg_in_type = JPEG_IMAGE_TYPE_RGB16;
+ break;
+ case SPICE_BITMAP_FMT_24BIT:
+ jpeg_in_type = JPEG_IMAGE_TYPE_BGR24;
+ break;
+ case SPICE_BITMAP_FMT_32BIT:
+ jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
+ break;
+ case SPICE_BITMAP_FMT_RGBA:
+ jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
+ has_alpha = TRUE;
+ break;
+ default:
+ return FALSE;
+ }
+
+ jpeg_data->data.bufs_tail = compress_buf_new();
+ jpeg_data->data.bufs_head = jpeg_data->data.bufs_tail;
+ jpeg_data->data.dcc = dcc;
+
+ if (setjmp(jpeg_data->data.jmp_env)) {
+ while (jpeg_data->data.bufs_head) {
+ RedCompressBuf *buf = jpeg_data->data.bufs_head;
+ jpeg_data->data.bufs_head = buf->send_next;
+ compress_buf_free(buf);
+ }
+ return FALSE;
+ }
+
+ if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+ spice_chunks_linearize(src->data);
+ }
+
+ jpeg_data->data.u.lines_data.chunks = src->data;
+ jpeg_data->data.u.lines_data.stride = src->stride;
+ if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+ jpeg_data->data.u.lines_data.next = 0;
+ jpeg_data->data.u.lines_data.reverse = 0;
+ stride = src->stride;
+ } else {
+ jpeg_data->data.u.lines_data.next = src->data->num_chunks - 1;
+ jpeg_data->data.u.lines_data.reverse = 1;
+ stride = -src->stride;
+ }
+ jpeg_size = jpeg_encode(jpeg, dcc->jpeg_quality, jpeg_in_type,
+ src->x, src->y, NULL,
+ 0, stride, jpeg_data->data.bufs_head->buf.bytes,
+ sizeof(jpeg_data->data.bufs_head->buf));
+
+ // the compressed buffer is bigger than the original data
+ if (jpeg_size > (src->y * src->stride)) {
+ longjmp(jpeg_data->data.jmp_env, 1);
+ }
+
+ if (!has_alpha) {
+ dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
+ dest->u.jpeg.data_size = jpeg_size;
+
+ o_comp_data->comp_buf = jpeg_data->data.bufs_head;
+ o_comp_data->comp_buf_size = jpeg_size;
+ o_comp_data->is_lossy = TRUE;
+
+ stat_compress_add(&DCC_TO_DC(dcc)->jpeg_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+ }
+
+ lz_data->data.bufs_head = jpeg_data->data.bufs_tail;
+ lz_data->data.bufs_tail = lz_data->data.bufs_head;
+
+ comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf);
+ comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled;
+ lz_out_start_byte = lz_data->data.bufs_head->buf.bytes + comp_head_filled;
+
+ lz_data->data.dcc = dcc;
+
+ lz_data->data.u.lines_data.chunks = src->data;
+ lz_data->data.u.lines_data.stride = src->stride;
+ lz_data->data.u.lines_data.next = 0;
+ lz_data->data.u.lines_data.reverse = 0;
+
+ alpha_lz_size = lz_encode(lz, LZ_IMAGE_TYPE_XXXA, src->x, src->y,
+ !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
+ NULL, 0, src->stride,
+ lz_out_start_byte,
+ comp_head_left);
+
+ // the compressed buffer is bigger than the original data
+ if ((jpeg_size + alpha_lz_size) > (src->y * src->stride)) {
+ longjmp(jpeg_data->data.jmp_env, 1);
+ }
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA;
+ dest->u.jpeg_alpha.flags = 0;
+ if (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN) {
+ dest->u.jpeg_alpha.flags |= SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN;
+ }
+
+ dest->u.jpeg_alpha.jpeg_size = jpeg_size;
+ dest->u.jpeg_alpha.data_size = jpeg_size + alpha_lz_size;
+
+ o_comp_data->comp_buf = jpeg_data->data.bufs_head;
+ o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size;
+ o_comp_data->is_lossy = TRUE;
+ stat_compress_add(&DCC_TO_DC(dcc)->jpeg_alpha_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+
+#ifdef USE_LZ4
+int dcc_compress_image_lz4(DisplayChannelClient *dcc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data,
+ uint32_t group_id)
+{
+ DisplayChannel *display_channel = DCC_TO_DC(dcc);
+ Lz4Data *lz4_data = &dcc->lz4_data;
+ Lz4EncoderContext *lz4 = dcc->lz4;
+ int lz4_size = 0;
+
+#ifdef COMPRESS_STAT
+ stat_time_t start_time = stat_now(display_channel->lz4_stat.clock);
+#endif
+
+ lz4_data->data.bufs_tail = compress_buf_new();
+ lz4_data->data.bufs_head = lz4_data->data.bufs_tail;
+
+ if (!lz4_data->data.bufs_head) {
+ spice_warning("failed to allocate compress buffer");
+ return FALSE;
+ }
+
+ lz4_data->data.bufs_head->send_next = NULL;
+ lz4_data->data.dcc = dcc;
+
+ if (setjmp(lz4_data->data.jmp_env)) {
+ while (lz4_data->data.bufs_head) {
+ RedCompressBuf *buf = lz4_data->data.bufs_head;
+ lz4_data->data.bufs_head = buf->send_next;
+ compress_buf_free(buf);
+ }
+ return FALSE;
+ }
+
+ if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+ spice_chunks_linearize(src->data);
+ }
+
+ lz4_data->data.u.lines_data.chunks = src->data;
+ lz4_data->data.u.lines_data.stride = src->stride;
+ lz4_data->data.u.lines_data.next = 0;
+ lz4_data->data.u.lines_data.reverse = 0;
+
+ lz4_size = lz4_encode(lz4, src->y, src->stride, lz4_data->data.bufs_head->buf.bytes,
+ sizeof(lz4_data->data.bufs_head->buf),
+ src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN, src->format);
+
+ // the compressed buffer is bigger than the original data
+ if (lz4_size > (src->y * src->stride)) {
+ longjmp(lz4_data->data.jmp_env, 1);
+ }
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_LZ4;
+ dest->u.lz4.data_size = lz4_size;
+
+ o_comp_data->comp_buf = lz4_data->data.bufs_head;
+ o_comp_data->comp_buf_size = lz4_size;
+
+ stat_compress_add(&display_channel->lz4_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+#endif
+
+int dcc_compress_image_quic(DisplayChannelClient *dcc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data,
+ uint32_t group_id)
+{
+ QuicData *quic_data = &dcc->quic_data;
+ QuicContext *quic = dcc->quic;
+ volatile QuicImageType type;
+ int size, stride;
+
+#ifdef COMPRESS_STAT
+ stat_time_t start_time = stat_now(DCC_TO_DC(dcc)->quic_stat.clock);
+#endif
+
+ switch (src->format) {
+ case SPICE_BITMAP_FMT_32BIT:
+ type = QUIC_IMAGE_TYPE_RGB32;
+ break;
+ case SPICE_BITMAP_FMT_RGBA:
+ type = QUIC_IMAGE_TYPE_RGBA;
+ break;
+ case SPICE_BITMAP_FMT_16BIT:
+ type = QUIC_IMAGE_TYPE_RGB16;
+ break;
+ case SPICE_BITMAP_FMT_24BIT:
+ type = QUIC_IMAGE_TYPE_RGB24;
+ break;
+ default:
+ return FALSE;
+ }
+
+ quic_data->data.bufs_tail = compress_buf_new();
+ quic_data->data.bufs_head = quic_data->data.bufs_tail;
+ quic_data->data.dcc = dcc;
+
+ if (setjmp(quic_data->data.jmp_env)) {
+ while (quic_data->data.bufs_head) {
+ RedCompressBuf *buf = quic_data->data.bufs_head;
+ quic_data->data.bufs_head = buf->send_next;
+ compress_buf_free(buf);
+ }
+ return FALSE;
+ }
+
+ if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+ spice_chunks_linearize(src->data);
+ }
+
+ quic_data->data.u.lines_data.chunks = src->data;
+ quic_data->data.u.lines_data.stride = src->stride;
+ if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+ quic_data->data.u.lines_data.next = 0;
+ quic_data->data.u.lines_data.reverse = 0;
+ stride = src->stride;
+ } else {
+ quic_data->data.u.lines_data.next = src->data->num_chunks - 1;
+ quic_data->data.u.lines_data.reverse = 1;
+ stride = -src->stride;
+ }
+ size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride,
+ quic_data->data.bufs_head->buf.words,
+ G_N_ELEMENTS(quic_data->data.bufs_head->buf.words));
+
+ // the compressed buffer is bigger than the original data
+ if ((size << 2) > (src->y * src->stride)) {
+ longjmp(quic_data->data.jmp_env, 1);
+ }
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC;
+ dest->u.quic.data_size = size << 2;
+
+ o_comp_data->comp_buf = quic_data->data.bufs_head;
+ o_comp_data->comp_buf_size = size << 2;
+
+ stat_compress_add(&DCC_TO_DC(dcc)->quic_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+
+#define MIN_SIZE_TO_COMPRESS 54
+#define MIN_DIMENSION_TO_QUIC 3
+int dcc_compress_image(DisplayChannelClient *dcc,
+ SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
+ int can_lossy,
+ compress_send_data_t* o_comp_data)
+{
+ DisplayChannel *display_channel = DCC_TO_DC(dcc);
+ SpiceImageCompression image_compression = dcc->image_compression;
+ int quic_compress = FALSE;
+
+ if ((image_compression == SPICE_IMAGE_COMPRESSION_OFF) ||
+ ((src->y * src->stride) < MIN_SIZE_TO_COMPRESS)) { // TODO: change the size cond
+ return FALSE;
+ } else if (image_compression == SPICE_IMAGE_COMPRESSION_QUIC) {
+ if (bitmap_fmt_is_plt(src->format)) {
+ return FALSE;
+ } else {
+ quic_compress = TRUE;
+ }
+ } else {
+ /*
+ lz doesn't handle (1) bitmaps with strides that are larger than the width
+ of the image in bytes (2) unstable bitmaps
+ */
+ if (bitmap_has_extra_stride(src) || (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
+ if ((image_compression == SPICE_IMAGE_COMPRESSION_LZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESSION_GLZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESSION_LZ4) ||
+ bitmap_fmt_is_plt(src->format)) {
+ return FALSE;
+ } else {
+ quic_compress = TRUE;
+ }
+ } else {
+ if ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ)) {
+ if ((src->x < MIN_DIMENSION_TO_QUIC) || (src->y < MIN_DIMENSION_TO_QUIC)) {
+ quic_compress = FALSE;
+ } else {
+ if (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_INVALID) {
+ quic_compress = bitmap_fmt_has_graduality(src->format) &&
+ bitmap_get_graduality_level(src) == BITMAP_GRADUAL_HIGH;
+ } else {
+ quic_compress = (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_HIGH);
+ }
+ }
+ } else {
+ quic_compress = FALSE;
+ }
+ }
+ }
+
+ if (quic_compress) {
+#ifdef COMPRESS_DEBUG
+ spice_info("QUIC compress");
+#endif
+ // if bitmaps is picture-like, compress it using jpeg
+ if (can_lossy && display_channel->enable_jpeg &&
+ ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ))) {
+ // if we use lz for alpha, the stride can't be extra
+ if (src->format != SPICE_BITMAP_FMT_RGBA || !bitmap_has_extra_stride(src)) {
+ return dcc_compress_image_jpeg(dcc, dest,
+ src, o_comp_data, drawable->group_id);
+ }
+ }
+ return dcc_compress_image_quic(dcc, dest,
+ src, o_comp_data, drawable->group_id);
+ } else {
+ int glz;
+ int ret;
+ if ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESSION_GLZ)) {
+ glz = bitmap_fmt_has_graduality(src->format) && (
+ (src->x * src->y) < glz_enc_dictionary_get_size(
+ dcc->glz_dict->dict));
+ } else if ((image_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESSION_LZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESSION_LZ4)) {
+ glz = FALSE;
+ } else {
+ spice_error("invalid image compression type %u", image_compression);
+ return FALSE;
+ }
+
+ if (glz) {
+ /* using the global dictionary only if it is not frozen */
+ pthread_rwlock_rdlock(&dcc->glz_dict->encode_lock);
+ if (!dcc->glz_dict->migrate_freeze) {
+ ret = dcc_compress_image_glz(dcc,
+ dest, src,
+ drawable, o_comp_data);
+ } else {
+ glz = FALSE;
+ }
+ pthread_rwlock_unlock(&dcc->glz_dict->encode_lock);
+ }
+
+ if (!glz) {
+#ifdef USE_LZ4
+ if (image_compression == SPICE_IMAGE_COMPRESSION_LZ4 &&
+ bitmap_fmt_is_rgb(src->format) &&
+ red_channel_client_test_remote_cap(&dcc->common.base,
+ SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) {
+ ret = dcc_compress_image_lz4(dcc, dest, src, o_comp_data,
+ drawable->group_id);
+ } else
+#endif
+ ret = dcc_compress_image_lz(dcc, dest, src, o_comp_data,
+ drawable->group_id);
+#ifdef COMPRESS_DEBUG
+ spice_info("LZ LOCAL compress");
+#endif
+ }
+#ifdef COMPRESS_DEBUG
+ else {
+ spice_info("LZ global compress fmt=%d", src->format);
+ }
+#endif
+ return ret;
+ }
+}
+
+#define CLIENT_PALETTE_CACHE
+#include "cache_item.tmpl.c"
+#undef CLIENT_PALETTE_CACHE
+
+void dcc_palette_cache_palette(DisplayChannelClient *dcc, SpicePalette *palette,
+ uint8_t *flags)
+{
+ if (palette == NULL) {
+ return;
+ }
+ if (palette->unique) {
+ if (red_palette_cache_find(dcc, palette->unique)) {
+ *flags |= SPICE_BITMAP_FLAGS_PAL_FROM_CACHE;
+ return;
+ }
+ if (red_palette_cache_add(dcc, palette->unique, 1)) {
+ *flags |= SPICE_BITMAP_FLAGS_PAL_CACHE_ME;
+ }
+ }
+}
+
+void dcc_palette_cache_reset(DisplayChannelClient *dcc)
+{
+ red_palette_cache_reset(dcc, CLIENT_PALETTE_CACHE_SIZE);
+}