mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-27 07:29:32 +00:00
As the input line could be uint8_t*, uint16_t* or uint32_t*, changing
the default from uint8_t* to void* seems the correct choice to deal with
upcasting warnings.
Regarding chunks->data allocation, I quote Frediano explantion:
"Lines came from spice_bitmap_get_line. This function assume that bitmap
data is split among chunks each containing some lines
(always full lines). If chunk->data is allocated using malloc or similar
SHOULD (not 100% sure) be 4 bytes aligned so in our cases
(8, 16, 24 or 32 bit images) should be aligned enough.
All the casts unfortunately came from the fact we compute based on
pixel bytes to make it generic so we use uint8_t*."
and
"Looking at code looks like these chunks came from the virtual machine.
So the question is... why should the virtual machine give use some
not-pixel align data?
I would put a large comment to state that we assume VM send aligned
data, would be stupid for the VM to not align it!"
clang output:
jpeg_encoder.c:109:26: error: cast from 'uint8_t *'
(aka 'unsigned char *') to 'uint16_t *' (aka 'unsigned short *')
increases required alignment from 1 to 2 [-Werror,-Wcast-align]
uint16_t *src_line = (uint16_t *)line;
^~~~~~~~~~~~~~~~
jpeg_encoder.c:144:26: error: cast from 'uint8_t *'
(aka 'unsigned char *') to 'uint32_t *' (aka 'unsigned int *')
increases required alignment from 1 to 4 [-Werror,-Wcast-align]
uint32_t *src_line = (uint32_t *)line;
^~~~~~~~~~~~~~~~
mjpeg_encoder.c:260:23: error: cast from 'uint8_t *'
(aka 'unsigned char *') to 'uint16_t *' (aka 'unsigned short *')
increases required alignment from 1 to 2 [-Werror,-Wcast-align]
uint16_t pixel = *(uint16_t *)src;
^~~~~~~~~~~~~~~
249 lines
7.3 KiB
C
249 lines
7.3 KiB
C
/*
|
|
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/>.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#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) (void *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) {
|
|
spice_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) {
|
|
spice_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(void *line, int width, uint8_t **out_line)
|
|
{
|
|
uint16_t *src_line = line;
|
|
uint8_t *out_pix;
|
|
int x;
|
|
|
|
spice_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(void *in_line, int width, uint8_t **out_line)
|
|
{
|
|
int x;
|
|
uint8_t *out_pix;
|
|
uint8_t *line = in_line;
|
|
spice_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(void *line, int width, uint8_t **out_line)
|
|
{
|
|
uint32_t *src_line = line;
|
|
uint8_t *out_pix;
|
|
int x;
|
|
|
|
spice_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(void *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) { \
|
|
spice_error("more lines failed"); \
|
|
} \
|
|
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;
|
|
JSAMPROW row_pointer[1];
|
|
width = jpeg->cur_image.width;
|
|
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);
|
|
}
|
|
|
|
if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
|
|
free(RGB24_line);
|
|
}
|
|
}
|
|
|
|
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:
|
|
spice_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;
|
|
}
|