From 263646a1f7e705766f7d46017679812d4b1406b8 Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Wed, 9 Jun 2010 11:40:25 +0200 Subject: JPEG support: introducing jpeg encoding for spice bitmaps --- server/jpeg_encoder.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 server/jpeg_encoder.c (limited to 'server/jpeg_encoder.c') diff --git a/server/jpeg_encoder.c b/server/jpeg_encoder.c new file mode 100644 index 00000000..95cf2400 --- /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 . +*/ + +#include "red_common.h" +#include "jpeg_encoder.h" +#include + +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; +} -- cgit