diff options
-rw-r--r-- | client/Makefile.am | 2 | ||||
-rw-r--r-- | client/canvas.cpp | 1 | ||||
-rw-r--r-- | client/canvas.h | 4 | ||||
-rw-r--r-- | client/jpeg_decoder.cpp | 147 | ||||
-rw-r--r-- | client/jpeg_decoder.h | 91 | ||||
-rw-r--r-- | client/red_gdi_canvas.cpp | 3 | ||||
-rw-r--r-- | client/red_gl_canvas.cpp | 3 | ||||
-rw-r--r-- | client/red_sw_canvas.cpp | 6 | ||||
-rw-r--r-- | client/screen.cpp | 2 | ||||
-rw-r--r-- | client/windows/redc.vcproj | 8 | ||||
-rw-r--r-- | client/x11/Makefile.am | 2 | ||||
-rw-r--r-- | common/canvas_base.c | 82 | ||||
-rw-r--r-- | common/canvas_base.h | 18 | ||||
-rw-r--r-- | common/gdi_canvas.c | 4 | ||||
-rw-r--r-- | common/gdi_canvas.h | 3 | ||||
-rw-r--r-- | common/gl_canvas.c | 2 | ||||
-rw-r--r-- | common/gl_canvas.h | 1 | ||||
-rw-r--r-- | common/sw_canvas.c | 6 | ||||
-rw-r--r-- | common/sw_canvas.h | 2 | ||||
-rw-r--r-- | server/Makefile.am | 2 | ||||
-rw-r--r-- | server/jpeg_encoder.c | 242 | ||||
-rw-r--r-- | server/jpeg_encoder.h | 61 | ||||
-rw-r--r-- | server/red_worker.c | 309 |
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]; |