summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/Makefile.am2
-rw-r--r--client/canvas.cpp1
-rw-r--r--client/canvas.h4
-rw-r--r--client/jpeg_decoder.cpp147
-rw-r--r--client/jpeg_decoder.h91
-rw-r--r--client/red_gdi_canvas.cpp3
-rw-r--r--client/red_gl_canvas.cpp3
-rw-r--r--client/red_sw_canvas.cpp6
-rw-r--r--client/screen.cpp2
-rw-r--r--client/windows/redc.vcproj8
-rw-r--r--client/x11/Makefile.am2
-rw-r--r--common/canvas_base.c82
-rw-r--r--common/canvas_base.h18
-rw-r--r--common/gdi_canvas.c4
-rw-r--r--common/gdi_canvas.h3
-rw-r--r--common/gl_canvas.c2
-rw-r--r--common/gl_canvas.h1
-rw-r--r--common/sw_canvas.c6
-rw-r--r--common/sw_canvas.h2
-rw-r--r--server/Makefile.am2
-rw-r--r--server/jpeg_encoder.c242
-rw-r--r--server/jpeg_encoder.h61
-rw-r--r--server/red_worker.c309
23 files changed, 956 insertions, 45 deletions
diff --git a/client/Makefile.am b/client/Makefile.am
index e1c31fd..55bc0f8 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -43,6 +43,8 @@ RED_COMMON_SRCS = \
inputs_channel.cpp \
inputs_channel.h \
inputs_handler.h \
+ jpeg_decoder.cpp \
+ jpeg_decoder.h \
lz.cpp \
monitor.cpp \
monitor.h \
diff --git a/client/canvas.cpp b/client/canvas.cpp
index 419bfc9..0a4d8e5 100644
--- a/client/canvas.cpp
+++ b/client/canvas.cpp
@@ -88,6 +88,7 @@ void Canvas::localalize_image(SPICE_ADDRESS* in_bitmap)
case SPICE_IMAGE_TYPE_LZ_RGB:
case SPICE_IMAGE_TYPE_GLZ_RGB:
case SPICE_IMAGE_TYPE_QUIC:
+ case SPICE_IMAGE_TYPE_JPEG:
break;
case SPICE_IMAGE_TYPE_FROM_CACHE:
break;
diff --git a/client/canvas.h b/client/canvas.h
index 135783b..8d64ca3 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -29,6 +29,7 @@
#include "canvas_utils.h"
#include "glz_decoded_image.h"
#include "glz_decoder.h"
+#include "jpeg_decoder.h"
enum CanvasType {
CANVAS_TYPE_INVALID,
@@ -423,6 +424,7 @@ protected:
CSurfaces& csurfaces() { return _csurfaces; }
GlzDecoder& glz_decoder() {return _glz_decoder;}
+ JpegDecoder& jpeg_decoder() { return _jpeg_decoder;}
private:
void access_test(void* ptr, size_t size);
@@ -445,6 +447,8 @@ private:
GlzDecoderCanvasDebug _glz_debug;
GlzDecoder _glz_decoder;
+ JpegDecoder _jpeg_decoder;
+
CSurfaces& _csurfaces;
unsigned long _base;
diff --git a/client/jpeg_decoder.cpp b/client/jpeg_decoder.cpp
new file mode 100644
index 0000000..a7824a9
--- /dev/null
+++ b/client/jpeg_decoder.cpp
@@ -0,0 +1,147 @@
+/*
+ Copyright (C) 2009 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/>.
+*/
+
+#include "common.h"
+#include "jpeg_decoder.h"
+#include "debug.h"
+#include "utils.h"
+
+static void op_begin_decode(SpiceJpegDecoder *decoder,
+ uint8_t* data,
+ int data_size,
+ int* out_width,
+ int* out_height)
+{
+ JpegDecoder* _decoder = static_cast<JpegDecoder*>(decoder);
+ _decoder->begin_decode(data, data_size, *out_width, *out_height);
+}
+
+static void op_decode(SpiceJpegDecoder *decoder,
+ uint8_t* dest,
+ int stride,
+ int format)
+{
+ JpegDecoder* _decoder = static_cast<JpegDecoder*>(decoder);
+ _decoder->decode(dest, stride, format);
+}
+
+extern "C" {
+
+ void jpeg_decoder_init_source(j_decompress_ptr cinfo)
+ {
+ }
+
+ static boolean jpeg_decoder_fill_input_buffer(j_decompress_ptr cinfo)
+ {
+ PANIC("no more data for jpeg");
+ return FALSE;
+ }
+
+ static void jpeg_decoder_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+ {
+ ASSERT(num_bytes < (long)cinfo->src->bytes_in_buffer);
+ cinfo->src->next_input_byte += num_bytes;
+ cinfo->src->bytes_in_buffer -= num_bytes;
+ }
+
+ static void jpeg_decoder_term_source (j_decompress_ptr cinfo)
+ {
+ return;
+ }
+}
+
+
+JpegDecoder::JpegDecoder()
+ : _data (NULL)
+ , _data_size (0)
+{
+ _cinfo.err = jpeg_std_error(&_jerr);
+ jpeg_create_decompress(&_cinfo);
+
+ _cinfo.src = &_jsrc;
+ _cinfo.src->init_source = jpeg_decoder_init_source;
+ _cinfo.src->fill_input_buffer = jpeg_decoder_fill_input_buffer;
+ _cinfo.src->skip_input_data = jpeg_decoder_skip_input_data;
+ _cinfo.src->resync_to_restart = jpeg_resync_to_restart;
+ _cinfo.src->term_source = jpeg_decoder_term_source;
+
+ static SpiceJpegDecoderOps decoder_ops = {
+ op_begin_decode,
+ op_decode,
+ };
+
+ ops = &decoder_ops;
+}
+
+JpegDecoder::~JpegDecoder()
+{
+ jpeg_destroy_decompress(&_cinfo);
+}
+
+void JpegDecoder::begin_decode(uint8_t* data, int data_size, int& out_width, int& out_height)
+{
+ ASSERT(data);
+ ASSERT(data_size);
+
+ if (_data) {
+ jpeg_abort_decompress(&_cinfo);
+ }
+
+ _data = data;
+ _data_size = data_size;
+
+ _cinfo.src->next_input_byte = _data;
+ _cinfo.src->bytes_in_buffer = _data_size;
+
+ jpeg_read_header(&_cinfo, TRUE);
+
+ _cinfo.out_color_space = JCS_RGB;
+ _width = _cinfo.image_width;
+ _height = _cinfo.image_height;
+
+ out_width = _width;
+ out_height = _height;
+}
+
+void JpegDecoder::decode(uint8_t *dest, int stride, int format)
+{
+ uint8_t* scan_line = new uint8_t[_width*3];
+ RGBConverter* rgb_converter;
+
+ switch (format) {
+ case SPICE_BITMAP_FMT_24BIT:
+ rgb_converter = &_rgb2bgr;
+ break;
+ case SPICE_BITMAP_FMT_32BIT:
+ rgb_converter = &_rgb2bgrx;
+ break;
+ default:
+ THROW("bad bitmap format, %d", format);
+ }
+
+ jpeg_start_decompress(&_cinfo);
+
+ for (int row = 0; row < _height; row++) {
+ jpeg_read_scanlines(&_cinfo, &scan_line, 1);
+ rgb_converter->convert(scan_line, dest, _width);
+ dest += stride;
+ }
+
+ delete [] scan_line;
+
+ jpeg_finish_decompress(&_cinfo);
+}
diff --git a/client/jpeg_decoder.h b/client/jpeg_decoder.h
new file mode 100644
index 0000000..5c38cfa
--- /dev/null
+++ b/client/jpeg_decoder.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 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/>.
+*/
+
+#ifndef _H_JPEG_DECODER
+#define _H_JPEG_DECODER
+
+#include "common.h"
+#include "canvas_base.h"
+
+#ifdef WIN32
+/* We need some hacks to avoid warnings from the jpeg headers */
+#define XMD_H
+#undef FAR
+#endif
+
+extern "C" {
+#include <jpeglib.h>
+}
+
+class RGBConverter {
+public:
+ virtual ~RGBConverter() {}
+ virtual void convert(uint8_t* src, uint8_t* dest, int width) = 0;
+};
+
+class RGBToBGRConverter : public RGBConverter {
+public:
+ void convert(uint8_t* src, uint8_t* dest, int width)
+ {
+ for (int x = 0; x < width; x++) {
+ *dest++ = src[2];
+ *dest++ = src[1];
+ *dest++ = src[0];
+ src += 3;
+ }
+ }
+};
+
+class RGBToBGRXConverter : public RGBConverter {
+public:
+ void convert(uint8_t* src, uint8_t* dest, int width)
+ {
+ for (int x = 0; x < width; x++) {
+ *dest++ = src[2];
+ *dest++ = src[1];
+ *dest++ = src[0];
+ *dest++ = 0;
+ src += 3;
+ }
+ }
+};
+
+class JpegDecoder : public SpiceJpegDecoder {
+public:
+ JpegDecoder();
+ ~JpegDecoder();
+
+ void begin_decode(uint8_t* data, int data_size, int& out_width, int& out_height);
+ /* format is SPICE_BITMAP_FMT_<X> for the dest; currently, only
+ x=32BIT and x=24BIT are supported */
+ void decode(uint8_t* dest, int stride, int format);
+
+private:
+ struct jpeg_decompress_struct _cinfo;
+ struct jpeg_error_mgr _jerr;
+ struct jpeg_source_mgr _jsrc;
+
+ uint8_t* _data;
+ int _data_size;
+ int _width;
+ int _height;
+
+ RGBToBGRConverter _rgb2bgr;
+ RGBToBGRXConverter _rgb2bgrx;
+};
+#endif \ No newline at end of file
diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index 391883b..453023e 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -37,7 +37,8 @@ GDICanvas::GDICanvas(int width, int height, uint32_t format,
format, &pixmap_cache.base,
&palette_cache.base,
&csurfaces.base,
- &glz_decoder()))) {
+ &glz_decoder(),
+ &jpeg_decoder()))) {
THROW("create canvas failed");
}
}
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index 13e4723..1a219cd 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -40,7 +40,8 @@ GCanvas::GCanvas(int width, int height, uint32_t format, RedWindow *win,
&pixmap_cache.base,
&palette_cache.base,
&csurfaces.base,
- &glz_decoder()))) {
+ &glz_decoder(),
+ &jpeg_decoder()))) {
THROW("create canvas failed");
}
}
diff --git a/client/red_sw_canvas.cpp b/client/red_sw_canvas.cpp
index 05da430..7a8daf4 100644
--- a/client/red_sw_canvas.cpp
+++ b/client/red_sw_canvas.cpp
@@ -42,13 +42,15 @@ SCanvas::SCanvas(bool onscreen,
&pixmap_cache.base,
&palette_cache.base,
&csurfaces.base,
- &glz_decoder());
+ &glz_decoder(),
+ &jpeg_decoder());
} else {
_canvas = canvas_create(width, height, format,
&pixmap_cache.base,
&palette_cache.base,
&csurfaces.base,
- &glz_decoder());
+ &glz_decoder(),
+ &jpeg_decoder());
}
if (_canvas == NULL) {
THROW("create canvas failed");
diff --git a/client/screen.cpp b/client/screen.cpp
index 1567978..bc87646 100644
--- a/client/screen.cpp
+++ b/client/screen.cpp
@@ -692,7 +692,7 @@ void RedScreen::on_mouse_button_release(SpiceMouseButton button, unsigned int bu
void RedScreen::on_pointer_leave()
{
- ASSERT(!_mouse_captured);
+// ASSERT(!_mouse_captured);
if (_pointer_layer) {
_pointer_layer->on_pointer_leave();
diff --git a/client/windows/redc.vcproj b/client/windows/redc.vcproj
index d2540ce..c0ba905 100644
--- a/client/windows/redc.vcproj
+++ b/client/windows/redc.vcproj
@@ -252,6 +252,10 @@
>
</File>
<File
+ RelativePath="..\jpeg_decoder.cpp"
+ >
+ </File>
+ <File
RelativePath="..\lines.cpp"
>
</File>
@@ -524,6 +528,10 @@
>
</File>
<File
+ RelativePath="..\jpeg_decoder.h"
+ >
+ </File>
+ <File
RelativePath="..\menu.h"
>
</File>
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index 904fb42..26140f4 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -69,6 +69,8 @@ RED_COMMON_SRCS = \
$(CLIENT_DIR)/inputs_channel.cpp \
$(CLIENT_DIR)/inputs_channel.h \
$(CLIENT_DIR)/inputs_handler.h \
+ $(CLIENT_DIR)/jpeg_decoder.cpp \
+ $(CLIENT_DIR)/jpeg_decoder.h \
$(CLIENT_DIR)/lz.cpp \
$(CLIENT_DIR)/lines.cpp \
$(CLIENT_DIR)/monitor.cpp \
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 9d9c977..8180f09 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -199,6 +199,7 @@ typedef struct CanvasBase {
LzData lz_data;
GlzData glz_data;
+ SpiceJpegDecoder* jpeg;
void *usr_data;
spice_destroy_fn_t usr_data_destroy;
@@ -547,6 +548,78 @@ static pixman_image_t *canvas_get_quic(CanvasBase *canvas, SpiceQUICImage *image
return surface;
}
+
+//#define DUMP_JPEG
+#ifdef DUMP_JPEG
+static int jpeg_id = 0;
+static void dump_jpeg(uint8_t* data, int data_size)
+{
+ char file_str[200];
+ uint32_t id = ++jpeg_id;
+
+#ifdef WIN32
+ sprintf(file_str, "c:\\tmp\\spice_dump\\%u.jpg", id);
+#else
+ sprintf(file_str, "/tmp/spice_dump/%u.jpg", id);
+#endif
+
+ FILE *f = fopen(file_str, "wb");
+ if (!f) {
+ return;
+ }
+
+ fwrite(data, 1, data_size, f);
+ fclose(f);
+}
+#endif
+
+static pixman_image_t *canvas_get_jpeg(CanvasBase *canvas, SpiceJPEGImage *image, int invers)
+{
+ pixman_image_t *surface = NULL;
+ int stride;
+ int width;
+ int height;
+ uint8_t *dest;
+
+ canvas->jpeg->ops->begin_decode(canvas->jpeg, image->jpeg.data, image->jpeg.data_size,
+ &width, &height);
+ ASSERT((uint32_t)width == image->descriptor.width);
+ ASSERT((uint32_t)height == image->descriptor.height);
+
+ surface = surface_create(
+#ifdef WIN32
+ canvas->dc,
+#endif
+ PIXMAN_x8r8g8b8,
+ width, height, FALSE);
+ if (surface == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ dest = (uint8_t *)pixman_image_get_data(surface);
+ stride = pixman_image_get_stride(surface);
+
+ canvas->jpeg->ops->decode(canvas->jpeg, dest, stride, SPICE_BITMAP_FMT_32BIT);
+
+ if (invers) {
+ uint8_t *end = dest + height * stride;
+ for (; dest != end; dest += stride) {
+ uint32_t *pix;
+ uint32_t *end_pix;
+
+ pix = (uint32_t *)dest;
+ end_pix = pix + width;
+ for (; pix < end_pix; pix++) {
+ *pix ^= 0x00ffffff;
+ }
+ }
+ }
+#ifdef DUMP_JPEG
+ dump_jpeg(image->jpeg.data, image->jpeg.data_size);
+#endif
+ return surface;
+}
+
static pixman_image_t *canvas_bitmap_to_surface(CanvasBase *canvas, SpiceBitmap* bitmap,
SpicePalette *palette, int want_original)
{
@@ -1001,7 +1074,12 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
break;
}
#endif
-
+ case SPICE_IMAGE_TYPE_JPEG: {
+ SpiceJPEGImage *image = (SpiceJPEGImage *)descriptor;
+ access_test(canvas, descriptor, sizeof(SpiceJPEGImage));
+ surface = canvas_get_jpeg(canvas, image, 0);
+ break;
+ }
#if defined(SW_CANVAS_CACHE)
case SPICE_IMAGE_TYPE_GLZ_RGB: {
access_test(canvas, descriptor, sizeof(SpiceLZRGBImage));
@@ -3234,6 +3312,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, SpiceVirtMapping *virt_mapping
#endif
@@ -3267,6 +3346,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
#endif
canvas->surfaces = surfaces;
canvas->glz_data.decoder = glz_decoder;
+ canvas->jpeg = jpeg_decoder;
canvas->format = format;
diff --git a/common/canvas_base.h b/common/canvas_base.h
index f78b0b8..4eaafbb 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -31,6 +31,7 @@ typedef struct _SpiceImageCache SpiceImageCache;
typedef struct _SpiceImageSurfaces SpiceImageSurfaces;
typedef struct _SpicePaletteCache SpicePaletteCache;
typedef struct _SpiceGlzDecoder SpiceGlzDecoder;
+typedef struct _SpiceJpegDecoder SpiceJpegDecoder;
typedef struct _SpiceVirtMapping SpiceVirtMapping;
typedef struct _SpiceCanvas SpiceCanvas;
@@ -79,6 +80,23 @@ struct _SpiceGlzDecoder {
SpiceGlzDecoderOps *ops;
};
+
+typedef struct SpiceJpegDecoderOps {
+ void (*begin_decode)(SpiceJpegDecoder *decoder,
+ uint8_t* data,
+ int data_size,
+ int* out_width,
+ int* out_height);
+ void (*decode)(SpiceJpegDecoder *decoder,
+ uint8_t* dest,
+ int stride,
+ int format);
+} SpiceJpegDecoderOps;
+
+struct _SpiceJpegDecoder {
+ SpiceJpegDecoderOps *ops;
+};
+
typedef struct {
void *(*get_virt)(SpiceVirtMapping *mapping, unsigned long addr, uint32_t add_size);
void (*validate_virt)(SpiceVirtMapping *mapping, unsigned long virt,
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index af1de95..d01d9cd 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -1866,6 +1866,7 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
)
{
GdiCanvas *canvas;
@@ -1884,7 +1885,8 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
, bits_cache
#endif
, surfaces
- , glz_decoder);
+ , glz_decoder
+ , jpeg_decoder);
canvas->dc = dc;
canvas->lock = lock;
return (SpiceCanvas *)canvas;
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index 7af0e02..02e053d 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -31,7 +31,8 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
SpiceImageCache *bits_cache,
SpicePaletteCache *palette_cache,
SpiceImageSurfaces *surfaces,
- SpiceGlzDecoder *glz_decoder);
+ SpiceGlzDecoder *glz_decoder,
+ SpiceJpegDecoder *jpeg_decoder);
void gdi_canvas_init();
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index ea10c96..f98c72a 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -830,6 +830,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, SpiceVirtMapping *virt_mapping
#endif
@@ -857,6 +858,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
#endif
, surfaces
, glz_decoder
+ , jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, virt_mapping
#endif
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index 6dd25e9..cd76f8d 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -30,6 +30,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, SpiceVirtMapping *virt_mapping
#endif
diff --git a/common/sw_canvas.c b/common/sw_canvas.c
index a541c7d..8280362 100644
--- a/common/sw_canvas.c
+++ b/common/sw_canvas.c
@@ -1180,6 +1180,7 @@ static SpiceCanvas *canvas_create_common(pixman_image_t *image,
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, SpiceVirtMapping *virt_mapping
#endif
@@ -1207,6 +1208,7 @@ static SpiceCanvas *canvas_create_common(pixman_image_t *image,
#endif
, surfaces
, glz_decoder
+ , jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, virt_mapping
#endif
@@ -1228,6 +1230,7 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, SpiceVirtMapping *virt_mapping
#endif
@@ -1247,6 +1250,7 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format
#endif
, surfaces
, glz_decoder
+ , jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, virt_mapping
#endif
@@ -1263,6 +1267,7 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format,
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, SpiceVirtMapping *virt_mapping
#endif
@@ -1282,6 +1287,7 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format,
#endif
, surfaces
, glz_decoder
+ , jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, virt_mapping
#endif
diff --git a/common/sw_canvas.h b/common/sw_canvas.h
index c3aef24..63a7863 100644
--- a/common/sw_canvas.h
+++ b/common/sw_canvas.h
@@ -35,6 +35,7 @@ SpiceCanvas *canvas_create(int width, int height, uint32_t format
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, SpiceVirtMapping *virt_mapping
#endif
@@ -49,6 +50,7 @@ SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, uint
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
#ifndef SW_CANVAS_NO_CHUNKS
, SpiceVirtMapping *virt_mapping
#endif
diff --git a/server/Makefile.am b/server/Makefile.am
index 77f533a..8d93618 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -66,6 +66,8 @@ libspice_server_la_SOURCES = \
glz_encoder_dictionary.h \
glz_encoder_dictionary_protected.h \
glz_encoder.h \
+ jpeg_encoder.c \
+ jpeg_encoder.h \
mjpeg_encoder.h \
mjpeg_encoder.c \
red_bitmap_utils.h \
diff --git a/server/jpeg_encoder.c b/server/jpeg_encoder.c
new file mode 100644
index 0000000..95cf240
--- /dev/null
+++ b/server/jpeg_encoder.c
@@ -0,0 +1,242 @@
+/*
+ Copyright (C) 2009 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/>.
+*/
+
+#include "red_common.h"
+#include "jpeg_encoder.h"
+#include <jpeglib.h>
+
+typedef struct JpegEncoder {
+ JpegEncoderUsrContext *usr;
+
+ struct jpeg_destination_mgr dest_mgr;
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+
+ struct {
+ JpegEncoderImageType type;
+ int width;
+ int height;
+ int stride;
+ unsigned int out_size;
+ void (*convert_line_to_RGB24) (uint8_t *line, int width, uint8_t **out_line);
+ } cur_image;
+} JpegEncoder;
+
+/* jpeg destination manager callbacks */
+
+static void dest_mgr_init_destination(j_compress_ptr cinfo)
+{
+ JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
+ if (enc->dest_mgr.free_in_buffer == 0) {
+ enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
+ &enc->dest_mgr.next_output_byte);
+
+ if (enc->dest_mgr.free_in_buffer == 0) {
+ red_error("not enough space");
+ }
+ }
+
+ enc->cur_image.out_size = enc->dest_mgr.free_in_buffer;
+}
+
+static boolean dest_mgr_empty_output_buffer(j_compress_ptr cinfo)
+{
+ JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
+ enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
+ &enc->dest_mgr.next_output_byte);
+
+ if (enc->dest_mgr.free_in_buffer == 0) {
+ red_error("not enough space");
+ }
+ enc->cur_image.out_size += enc->dest_mgr.free_in_buffer;
+ return TRUE;
+}
+
+static void dest_mgr_term_destination(j_compress_ptr cinfo)
+{
+ JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
+ enc->cur_image.out_size -= enc->dest_mgr.free_in_buffer;
+}
+
+JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr)
+{
+ JpegEncoder *enc;
+ if (!usr->more_space || !usr->more_lines) {
+ return NULL;
+ }
+
+ enc = spice_new0(JpegEncoder, 1);
+
+ enc->usr = usr;
+
+ enc->dest_mgr.init_destination = dest_mgr_init_destination;
+ enc->dest_mgr.empty_output_buffer = dest_mgr_empty_output_buffer;
+ enc->dest_mgr.term_destination = dest_mgr_term_destination;
+
+ enc->cinfo.err = jpeg_std_error(&enc->jerr);
+
+ jpeg_create_compress(&enc->cinfo);
+ enc->cinfo.client_data = enc;
+ enc->cinfo.dest = &enc->dest_mgr;
+ return (JpegEncoderContext*)enc;
+}
+
+void jpeg_encoder_destroy(JpegEncoderContext* encoder)
+{
+ jpeg_destroy_compress(&((JpegEncoder*)encoder)->cinfo);
+ free(encoder);
+}
+
+static void convert_RGB16_to_RGB24(uint8_t *line, int width, uint8_t **out_line)
+{
+ uint16_t *src_line = (uint16_t *)line;
+ uint8_t *out_pix;
+ int x;
+
+ ASSERT(out_line && *out_line);
+
+ out_pix = *out_line;
+
+ for (x = 0; x < width; x++) {
+ uint16_t pixel = *src_line++;
+ *out_pix++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
+ *out_pix++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
+ *out_pix++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
+ }
+}
+
+static void convert_BGR24_to_RGB24(uint8_t *line, int width, uint8_t **out_line)
+{
+ int x;
+ uint8_t *out_pix;
+
+ ASSERT(out_line && *out_line);
+
+ out_pix = *out_line;
+
+ for (x = 0; x < width; x++) {
+ *out_pix++ = line[2];
+ *out_pix++ = line[1];
+ *out_pix++ = line[0];
+ line += 3;
+ }
+}
+
+static void convert_BGRX32_to_RGB24(uint8_t *line, int width, uint8_t **out_line)
+{
+ uint32_t *src_line = (uint32_t *)line;
+ uint8_t *out_pix;
+ int x;
+
+ ASSERT(out_line && *out_line);
+
+ out_pix = *out_line;
+
+ for (x = 0; x < width; x++) {
+ uint32_t pixel = *src_line++;
+ *out_pix++ = (pixel >> 16) & 0xff;
+ *out_pix++ = (pixel >> 8) & 0xff;
+ *out_pix++ = pixel & 0xff;
+ }
+}
+
+static void convert_RGB24_to_RGB24(uint8_t *line, int width, uint8_t **out_line)
+{
+ *out_line = line;
+}
+
+
+#define FILL_LINES() { \
+ if (lines == lines_end) { \
+ int n = jpeg->usr->more_lines(jpeg->usr, &lines); \
+ if (n <= 0) { \
+ red_error("more lines failed\n"); \
+ } \
+ lines_end = lines + n * stride; \
+ } \
+}
+
+static void do_jpeg_encode(JpegEncoder *jpeg, uint8_t *lines, unsigned int num_lines)
+{
+ uint8_t *lines_end;
+ uint8_t *RGB24_line;
+ int stride, width, height;
+ JSAMPROW row_pointer[1];
+ width = jpeg->cur_image.width;
+ height = jpeg->cur_image.height;
+ stride = jpeg->cur_image.stride;
+
+ if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
+ RGB24_line = (uint8_t *)spice_malloc(width*3);
+ }
+
+ lines_end = lines + (stride * num_lines);
+
+ for (;jpeg->cinfo.next_scanline < jpeg->cinfo.image_height; lines += stride) {
+ FILL_LINES();
+ jpeg->cur_image.convert_line_to_RGB24(lines, width, &RGB24_line);
+ row_pointer[0] = RGB24_line;
+ jpeg_write_scanlines(&jpeg->cinfo, row_pointer, 1);
+ }
+}
+
+int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
+ int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
+ uint8_t *io_ptr, unsigned int num_io_bytes)
+{
+ JpegEncoder *enc = (JpegEncoder *)jpeg;
+
+ enc->cur_image.type = type;
+ enc->cur_image.width = width;
+ enc->cur_image.height = height;
+ enc->cur_image.stride = stride;
+ enc->cur_image.out_size = 0;
+
+ switch (type) {
+ case JPEG_IMAGE_TYPE_RGB16:
+ enc->cur_image.convert_line_to_RGB24 = convert_RGB16_to_RGB24;
+ break;
+ case JPEG_IMAGE_TYPE_RGB24:
+ enc->cur_image.convert_line_to_RGB24 = convert_RGB24_to_RGB24;
+ break;
+ case JPEG_IMAGE_TYPE_BGR24:
+ enc->cur_image.convert_line_to_RGB24 = convert_BGR24_to_RGB24;
+ break;
+ case JPEG_IMAGE_TYPE_BGRX32:
+ enc->cur_image.convert_line_to_RGB24 = convert_BGRX32_to_RGB24;
+ break;
+ default:
+ red_error("bad image type");
+ }
+
+ enc->cinfo.image_width = width;
+ enc->cinfo.image_height = height;
+ enc->cinfo.input_components = 3;
+ enc->cinfo.in_color_space = JCS_RGB;
+ jpeg_set_defaults(&enc->cinfo);
+ jpeg_set_quality(&enc->cinfo, quality, TRUE);
+
+ enc->dest_mgr.next_output_byte = io_ptr;
+ enc->dest_mgr.free_in_buffer = num_io_bytes;
+
+ jpeg_start_compress(&enc->cinfo, TRUE);
+
+ do_jpeg_encode(enc, lines, num_lines);
+
+ jpeg_finish_compress(&enc->cinfo);
+ return enc->cur_image.out_size;
+}
diff --git a/server/jpeg_encoder.h b/server/jpeg_encoder.h
new file mode 100644
index 0000000..2b8d8b2
--- /dev/null
+++ b/server/jpeg_encoder.h
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef _H_JPEG_ENCODER
+#define _H_JPEG_ENCODER
+
+#include <spice/types.h>
+
+typedef enum {
+ JPEG_IMAGE_TYPE_INVALID,
+ JPEG_IMAGE_TYPE_RGB16,
+ /* in byte per color types, the notation is according to the order of the
+ colors in the memory */
+ JPEG_IMAGE_TYPE_RGB24,
+ JPEG_IMAGE_TYPE_BGR24,
+ JPEG_IMAGE_TYPE_BGRX32,
+} JpegEncoderImageType;
+
+typedef void* JpegEncoderContext;
+typedef struct JpegEncoderUsrContext JpegEncoderUsrContext;
+
+struct JpegEncoderUsrContext {
+ int (*more_space)(JpegEncoderUsrContext *usr, uint8_t **io_ptr);
+ int (*more_lines)(JpegEncoderUsrContext *usr, uint8_t **lines);
+};
+
+JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr);
+void jpeg_encoder_destroy(JpegEncoderContext *encoder);
+
+/* returns the total size of the encoded data. Images must be supplied from the the
+ top line to the bottom */
+int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
+ int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
+ uint8_t *io_ptr, unsigned int num_io_bytes);
+#endif
diff --git a/server/red_worker.c b/server/red_worker.c
index c2c78ea..aac1d27 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -48,6 +48,7 @@
#include "ring.h"
#include "mjpeg_encoder.h"
#include "red_memslots.h"
+#include "jpeg_encoder.h"
//#define COMPRESS_STAT
//#define DUMP_BITMAP
@@ -167,6 +168,7 @@ static inline void stat_add(stat_info_t *info, stat_time_t start)
static const char *lz_stat_name = "lz";
static const char *glz_stat_name = "glz";
static const char *quic_stat_name = "quic";
+static const char *jpeg_stat_name = "jpeg";
static inline void stat_compress_init(stat_info_t *info, const char *name)
{
@@ -464,6 +466,7 @@ typedef struct __attribute__ ((__packed__)) RedImage {
SpiceLZRGBData lz_rgb;
SpiceLZPLTData lz_plt;
SpiceSurface surface;
+ SpiceJPEGData jpeg;
};
} RedImage;
@@ -577,6 +580,11 @@ typedef struct {
EncoderData data;
} GlzData;
+typedef struct {
+ JpegEncoderUsrContext usr;
+ EncoderData data;
+} JpegData;
+
/**********************************/
/* LZ dictionary related entities */
/**********************************/
@@ -708,6 +716,7 @@ struct DisplayChannel {
stat_info_t lz_stat;
stat_info_t glz_stat;
stat_info_t quic_stat;
+ stat_info_t jpeg_stat;
#endif
};
@@ -987,6 +996,9 @@ typedef struct RedWorker {
LzData lz_data;
LzContext *lz;
+ JpegData jpeg_data;
+ JpegEncoderContext *jpeg;
+
#ifdef PIPE_DEBUG
uint32_t last_id;
#endif
@@ -1003,6 +1015,9 @@ typedef struct RedWorker {
uint64_t *command_counter;
#endif
SpiceVirtMapping preload_group_virt_mapping;
+
+ int enable_jpeg;
+ int jpeg_quality;
} RedWorker;
static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable);
@@ -1068,19 +1083,29 @@ static void print_compress_stats(DisplayChannel *display_channel)
stat_byte_to_mega(display_channel->lz_stat.comp_size),
stat_cpu_time_to_sec(display_channel->lz_stat.total)
);
+ red_printf("JPEG \t%8d\t%13.2f\t%12.2f\t%12.2f",
+ display_channel->jpeg_stat.count,
+ stat_byte_to_mega(display_channel->jpeg_stat.orig_size),
+ stat_byte_to_mega(display_channel->jpeg_stat.comp_size),
+ stat_cpu_time_to_sec(display_channel->jpeg_stat.total)
+ );
red_printf("-------------------------------------------------------------------");
red_printf("Total \t%8d\t%13.2f\t%12.2f\t%12.2f",
display_channel->lz_stat.count + display_channel->glz_stat.count +
- display_channel->quic_stat.count,
+ display_channel->quic_stat.count +
+ display_channel->jpeg_stat.count,
stat_byte_to_mega(display_channel->lz_stat.orig_size +
display_channel->glz_stat.orig_size +
- display_channel->quic_stat.orig_size),
+ display_channel->quic_stat.orig_size +
+ display_channel->jpeg_stat.orig_size),
stat_byte_to_mega(display_channel->lz_stat.comp_size +
display_channel->glz_stat.comp_size +
- display_channel->quic_stat.comp_size),
+ display_channel->quic_stat.comp_size +
+ display_channel->jpeg_stat.comp_size),
stat_cpu_time_to_sec(display_channel->lz_stat.total +
display_channel->glz_stat.total +
- display_channel->quic_stat.total)
+ display_channel->quic_stat.total +
+ display_channel->jpeg_stat.total)
);
}
@@ -5517,6 +5542,12 @@ static int glz_usr_more_space(GlzEncoderUsrContext *usr, uint8_t **io_ptr)
return (encoder_usr_more_space(usr_data, (uint32_t **)io_ptr) << 2);
}
+static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
+{
+ EncoderData *usr_data = &(((JpegData *)usr)->data);
+ return (encoder_usr_more_space(usr_data, (uint32_t **)io_ptr) << 2);
+}
+
static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t **lines)
{
uint32_t data_size;
@@ -5564,63 +5595,91 @@ static int glz_usr_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines)
return encoder_usr_more_lines(usr_data, lines);
}
-static int quic_usr_more_lines_revers(QuicUsrContext *usr, uint8_t **lines)
+static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
+{
+ EncoderData *usr_data = &(((JpegData *)usr)->data);
+ return encoder_usr_more_lines(usr_data, lines);
+}
+
+static int encoder_usr_more_lines_reverse(EncoderData *enc_data, uint8_t **lines)
{
uint8_t *data;
uint32_t data_size;
- EncoderData *quic_data = &(((QuicData *)usr)->data);
- if (!quic_data->u.lines_data.next) {
+ if (!enc_data->u.lines_data.next) {
return 0;
}
- QXLDataChunk *chunk = (QXLDataChunk *)quic_data->u.lines_data.enc_get_virt(
- quic_data->u.lines_data.enc_get_virt_opaque,
- quic_data->u.lines_data.next,
- sizeof(QXLDataChunk), quic_data->u.lines_data.group_id);
+ QXLDataChunk *chunk = (QXLDataChunk *)enc_data->u.lines_data.enc_get_virt(
+ enc_data->u.lines_data.enc_get_virt_opaque,
+ enc_data->u.lines_data.next,
+ sizeof(QXLDataChunk), enc_data->u.lines_data.group_id);
data_size = chunk->data_size;
data = chunk->data;
- if (data_size % quic_data->u.lines_data.stride) {
+ if (data_size % enc_data->u.lines_data.stride) {
return 0;
}
- quic_data->u.lines_data.enc_validate_virt(quic_data->u.lines_data.enc_validate_virt_opaque,
- (unsigned long)data,
- quic_data->u.lines_data.next, data_size,
- quic_data->u.lines_data.group_id);
+ enc_data->u.lines_data.enc_validate_virt(enc_data->u.lines_data.enc_validate_virt_opaque,
+ (unsigned long)data,
+ enc_data->u.lines_data.next, data_size,
+ enc_data->u.lines_data.group_id);
- quic_data->u.lines_data.next = chunk->prev_chunk;
- *lines = data + data_size - quic_data->u.lines_data.stride;
- return data_size / quic_data->u.lines_data.stride;
+ enc_data->u.lines_data.next = chunk->prev_chunk;
+ *lines = data + data_size - enc_data->u.lines_data.stride;
+ return data_size / enc_data->u.lines_data.stride;
}
-static int quic_usr_more_lines_unstable(QuicUsrContext *usr, uint8_t **out_lines)
+static int quic_usr_more_lines_reverse(QuicUsrContext *usr, uint8_t **lines)
{
- EncoderData *quic_data = &(((QuicData *)usr)->data);
+ EncoderData *usr_data = &(((QuicData *)usr)->data);
+ return encoder_usr_more_lines_reverse(usr_data, lines);
+
+}
+
+static int jpeg_usr_more_lines_reverse(JpegEncoderUsrContext *usr, uint8_t **lines)
+{
+ EncoderData *usr_data = &(((JpegData *)usr)->data);
+ return encoder_usr_more_lines_reverse(usr_data, lines);
+}
- if (!quic_data->u.unstable_lines_data.lines) {
+static int encoder_usr_more_lines_unstable(EncoderData *enc_data, uint8_t **out_lines)
+{
+ if (!enc_data->u.unstable_lines_data.lines) {
return 0;
}
- uint8_t *src = quic_data->u.unstable_lines_data.next;
- int lines = MIN(quic_data->u.unstable_lines_data.lines,
- quic_data->u.unstable_lines_data.max_lines_bunch);
- quic_data->u.unstable_lines_data.lines -= lines;
- uint8_t *end = src + lines * quic_data->u.unstable_lines_data.src_stride;
- quic_data->u.unstable_lines_data.next = end;
+ uint8_t *src = enc_data->u.unstable_lines_data.next;
+ int lines = MIN(enc_data->u.unstable_lines_data.lines,
+ enc_data->u.unstable_lines_data.max_lines_bunch);
+ enc_data->u.unstable_lines_data.lines -= lines;
+ uint8_t *end = src + lines * enc_data->u.unstable_lines_data.src_stride;
+ enc_data->u.unstable_lines_data.next = end;
- uint8_t *out = (uint8_t *)quic_data->u.unstable_lines_data.input_bufs[
- quic_data->u.unstable_lines_data.input_bufs_pos++ & 1]->buf;
+ uint8_t *out = (uint8_t *)enc_data->u.unstable_lines_data.input_bufs[
+ enc_data->u.unstable_lines_data.input_bufs_pos++ & 1]->buf;
uint8_t *dest = out;
- for (; src != end; src += quic_data->u.unstable_lines_data.src_stride,
- dest += quic_data->u.unstable_lines_data.dest_stride) {
- memcpy(dest, src, quic_data->u.unstable_lines_data.dest_stride);
+ for (; src != end; src += enc_data->u.unstable_lines_data.src_stride,
+ dest += enc_data->u.unstable_lines_data.dest_stride) {
+ memcpy(dest, src, enc_data->u.unstable_lines_data.dest_stride);
}
*out_lines = out;
return lines;
}
+static int quic_usr_more_lines_unstable(QuicUsrContext *usr, uint8_t **lines)
+{
+ EncoderData *usr_data = &(((QuicData *)usr)->data);
+ return encoder_usr_more_lines_unstable(usr_data, lines);
+}
+
+static int jpeg_usr_more_lines_unstable(JpegEncoderUsrContext *usr, uint8_t **lines)
+{
+ EncoderData *usr_data = &(((JpegData *)usr)->data);
+ return encoder_usr_more_lines_unstable(usr_data, lines);
+}
+
static int quic_usr_no_more_lines(QuicUsrContext *usr, uint8_t **lines)
{
return 0;
@@ -5636,6 +5695,11 @@ static int glz_usr_no_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines)
return 0;
}
+static int jpeg_usr_no_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
+{
+ return 0;
+}
+
static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *image)
{
GlzData *lz_data = (GlzData *)usr;
@@ -5698,6 +5762,22 @@ static inline void red_display_init_glz_data(DisplayChannel *display)
display->glz_data.usr.free_image = glz_usr_free_image;
}
+static inline void red_init_jpeg(RedWorker *worker)
+{
+ worker->jpeg_data.usr.more_space = jpeg_usr_more_space;
+ worker->jpeg_data.usr.more_lines = jpeg_usr_more_lines;
+
+ worker->jpeg = jpeg_encoder_create(&worker->jpeg_data.usr);
+
+ if (!worker->jpeg) {
+ PANIC("create jpeg encoder failed");
+ }
+
+ // TODO: configure via qemu command line and monitor, and activate only on WAN
+ worker->enable_jpeg = TRUE;
+ worker->jpeg_quality = 85;
+}
+
#ifdef __GNUC__
#define ATTR_PACKED __attribute__ ((__packed__))
#else
@@ -6046,6 +6126,148 @@ static inline int red_lz_compress_image(DisplayChannel *display_channel,
return TRUE;
}
+static int red_jpeg_compress_image(DisplayChannel *display_channel, RedImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data,
+ uint32_t group_id)
+{
+ RedWorker *worker = display_channel->base.worker;
+ JpegData *jpeg_data = &worker->jpeg_data;
+ JpegEncoderContext *jpeg = worker->jpeg;
+ JpegEncoderImageType jpeg_in_type;
+ int size;
+#ifdef COMPRESS_STAT
+ stat_time_t start_time = stat_now();
+#endif
+ switch (src->format) {
+ case SPICE_BITMAP_FMT_32BIT:
+ jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
+ break;
+ 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;
+ default:
+ return FALSE;
+ }
+
+ jpeg_data->data.bufs_tail = red_display_alloc_compress_buf(display_channel);
+ jpeg_data->data.bufs_head = jpeg_data->data.bufs_tail;
+
+ if (!jpeg_data->data.bufs_head) {
+ red_printf("failed to allocate compress buffer");
+ return FALSE;
+ }
+
+ jpeg_data->data.bufs_head->send_next = NULL;
+ jpeg_data->data.display_channel = display_channel;
+
+ 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;
+ red_display_free_compress_buf(display_channel, buf);
+ }
+ return FALSE;
+ }
+
+ if ((src->flags & QXL_BITMAP_DIRECT)) {
+ int stride;
+ uint8_t *data;
+
+ if (!(src->flags & QXL_BITMAP_TOP_DOWN)) {
+ data = (uint8_t*)get_virt(&worker->mem_slots, src->data, src->stride * src->y, group_id) +
+ src->stride * (src->y - 1);
+ stride = -src->stride;
+ } else {
+ data = (uint8_t*)get_virt(&worker->mem_slots, src->data, src->stride * src->y, group_id);
+ stride = src->stride;
+ }
+
+ if ((src->flags & QXL_BITMAP_UNSTABLE)) {
+ jpeg_data->data.u.unstable_lines_data.next = data;
+ jpeg_data->data.u.unstable_lines_data.src_stride = stride;
+ jpeg_data->data.u.unstable_lines_data.dest_stride = src->stride;
+ jpeg_data->data.u.unstable_lines_data.lines = src->y;
+ jpeg_data->data.u.unstable_lines_data.input_bufs_pos = 0;
+ if (!(jpeg_data->data.u.unstable_lines_data.input_bufs[0] =
+ red_display_alloc_compress_buf(display_channel)) ||
+ !(jpeg_data->data.u.unstable_lines_data.input_bufs[1] =
+ red_display_alloc_compress_buf(display_channel))) {
+ return FALSE;
+ }
+ jpeg_data->data.u.unstable_lines_data.max_lines_bunch =
+ sizeof(jpeg_data->data.u.unstable_lines_data.input_bufs[0]->buf) /
+ jpeg_data->data.u.unstable_lines_data.dest_stride;
+ jpeg_data->usr.more_lines = jpeg_usr_more_lines_unstable;
+ size = jpeg_encode(jpeg, worker->jpeg_quality, jpeg_in_type, src->x, src->y, NULL, 0, src->stride,
+ (uint8_t*)jpeg_data->data.bufs_head->buf,
+ sizeof(jpeg_data->data.bufs_head->buf));
+ } else {
+ jpeg_data->usr.more_lines = jpeg_usr_no_more_lines;
+ size = jpeg_encode(jpeg, worker->jpeg_quality, jpeg_in_type, src->x, src->y, data, src->y, stride,
+ (uint8_t*)jpeg_data->data.bufs_head->buf,
+ sizeof(jpeg_data->data.bufs_head->buf));
+ }
+ } else {
+ int stride;
+
+ if ((src->flags & QXL_BITMAP_UNSTABLE)) {
+ red_printf_once("unexpected unstable bitmap");
+ return FALSE;
+ }
+ jpeg_data->data.u.lines_data.enc_get_virt = cb_get_virt;
+ jpeg_data->data.u.lines_data.enc_get_virt_opaque = &worker->mem_slots;
+ jpeg_data->data.u.lines_data.enc_validate_virt = cb_validate_virt;
+ jpeg_data->data.u.lines_data.enc_validate_virt_opaque = &worker->mem_slots;
+ jpeg_data->data.u.lines_data.stride = src->stride;
+ jpeg_data->data.u.lines_data.group_id = group_id;
+
+ if ((src->flags & QXL_BITMAP_TOP_DOWN)) {
+ jpeg_data->data.u.lines_data.next = src->data;
+ jpeg_data->usr.more_lines = jpeg_usr_more_lines;
+ stride = src->stride;
+ } else {
+ SPICE_ADDRESS prev_addr = src->data;
+ QXLDataChunk *chunk = (QXLDataChunk *)get_virt(&worker->mem_slots, src->data,
+ sizeof(QXLDataChunk), group_id);
+ while (chunk->next_chunk) {
+ prev_addr = chunk->next_chunk;
+ chunk = (QXLDataChunk *)get_virt(&worker->mem_slots, chunk->next_chunk, sizeof(QXLDataChunk),
+ group_id);
+ ASSERT(chunk->prev_chunk);
+ }
+ jpeg_data->data.u.lines_data.next = (SPICE_ADDRESS)prev_addr -
+ get_virt_delta(&worker->mem_slots,
+ get_memslot_id(&worker->mem_slots, src->data),
+ group_id);
+ jpeg_data->usr.more_lines = jpeg_usr_more_lines_reverse;
+ stride = -src->stride;
+ }
+ size = jpeg_encode(jpeg, worker->jpeg_quality, jpeg_in_type, src->x, src->y, NULL, 0, stride,
+ (uint8_t*)jpeg_data->data.bufs_head->buf,
+ sizeof(jpeg_data->data.bufs_head->buf));
+ }
+
+ // the compressed buffer is bigger than the original data
+ if (size > (src->y * src->stride)) {
+ longjmp(jpeg_data->data.jmp_env, 1);
+ }
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
+ dest->jpeg.data_size = size;
+
+ o_comp_data->raw_size = sizeof(SpiceJPEGImage);
+ o_comp_data->comp_buf = jpeg_data->data.bufs_head;
+ o_comp_data->comp_buf_size = size;
+ o_comp_data->plt_ptr = NULL;
+ o_comp_data->flags_ptr = NULL;
+ stat_compress_add(&display_channel->jpeg_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+
static inline int red_quic_compress_image(DisplayChannel *display_channel, RedImage *dest,
SpiceBitmap *src, compress_send_data_t* o_comp_data,
uint32_t group_id)
@@ -6166,7 +6388,7 @@ static inline int red_quic_compress_image(DisplayChannel *display_channel, RedIm
get_virt_delta(&worker->mem_slots,
get_memslot_id(&worker->mem_slots, src->data),
group_id);
- quic_data->usr.more_lines = quic_usr_more_lines_revers;
+ quic_data->usr.more_lines = quic_usr_more_lines_reverse;
stride = -src->stride;
}
size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride,
@@ -6249,7 +6471,17 @@ static inline int red_compress_image(DisplayChannel *display_channel,
#ifdef COMPRESS_DEBUG
red_printf("QUIC compress");
#endif
- return red_quic_compress_image(display_channel, dest, src, o_comp_data, drawable->group_id);
+ // if bitmaps is picture-like, compress it using jpeg
+ if (display_channel->base.worker->enable_jpeg &&
+ ((image_compression == SPICE_IMAGE_COMPRESS_AUTO_LZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESS_AUTO_GLZ))) {
+ if (src->format != SPICE_BITMAP_FMT_RGBA) {
+ return red_jpeg_compress_image(display_channel, dest,
+ src, o_comp_data, drawable->group_id);
+ }
+ }
+ return red_quic_compress_image(display_channel, dest,
+ src, o_comp_data, drawable->group_id);
} else {
int glz;
int ret;
@@ -7915,7 +8147,7 @@ static SpiceCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, ui
oglctx_make_current(ctx);
if (!(canvas = gl_canvas_create(width, height, depth, &worker->image_cache.base,
- &worker->image_surfaces, NULL,
+ &worker->image_surfaces, NULL, NULL,
&worker->preload_group_virt_mapping))) {
return NULL;
}
@@ -7973,7 +8205,7 @@ static inline void *create_canvas_for_surface(RedWorker *worker, RedSurface *sur
canvas = canvas_create_for_data(width, height, format,
line_0, stride,
&worker->image_cache.base,
- &worker->image_surfaces, NULL,
+ &worker->image_surfaces, NULL, NULL,
&worker->preload_group_virt_mapping);
surface->context.top_down = TRUE;
surface->context.canvas_draws_on_surface = TRUE;
@@ -8773,6 +9005,7 @@ static void handle_new_display_channel(RedWorker *worker, RedsStreamContext *pee
stat_compress_init(&display_channel->lz_stat, lz_stat_name);
stat_compress_init(&display_channel->glz_stat, glz_stat_name);
stat_compress_init(&display_channel->quic_stat, quic_stat_name);
+ stat_compress_init(&display_channel->jpeg_stat, jpeg_stat_name);
}
static void red_disconnect_cursor(RedChannel *channel)
@@ -9321,6 +9554,7 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
stat_reset(&worker->display_channel->quic_stat);
stat_reset(&worker->display_channel->lz_stat);
stat_reset(&worker->display_channel->glz_stat);
+ stat_reset(&worker->display_channel->jpeg_stat);
}
#endif
break;
@@ -9483,6 +9717,7 @@ void *red_worker_main(void *arg)
red_init(&worker, (WorkerInitData *)arg);
red_init_quic(&worker);
red_init_lz(&worker);
+ red_init_jpeg(&worker);
worker.epoll_timeout = INF_EPOLL_WAIT;
for (;;) {
struct epoll_event events[MAX_EPOLL_SOURCES];