mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice-common
synced 2026-01-09 05:34:12 +00:00
Allows to check QUIC algorithm and code with multiple input/output formats. Was limited to just RGB and RGBA. Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
408 lines
12 KiB
C
408 lines
12 KiB
C
/*
|
|
Copyright (C) 2017 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 <config.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <glib.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
|
|
#include "common/quic.h"
|
|
|
|
typedef enum {
|
|
COLOR_MODE_RGB,
|
|
COLOR_MODE_RGB16,
|
|
COLOR_MODE_GRAY,
|
|
} color_mode_t;
|
|
|
|
static color_mode_t color_mode = COLOR_MODE_RGB;
|
|
|
|
typedef struct {
|
|
QuicUsrContext usr;
|
|
GByteArray *dest;
|
|
} QuicData;
|
|
|
|
static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
|
|
quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, fmt, ap);
|
|
va_end(ap);
|
|
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
static SPICE_GNUC_PRINTF(2, 3) void
|
|
quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static void *quic_usr_malloc(QuicUsrContext *usr, int size)
|
|
{
|
|
return g_malloc(size);
|
|
}
|
|
|
|
|
|
static void quic_usr_free(QuicUsrContext *usr, void *ptr)
|
|
{
|
|
g_free(ptr);
|
|
}
|
|
|
|
static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
|
|
{
|
|
QuicData *quic_data = (QuicData *)usr;
|
|
int initial_len = quic_data->dest->len;
|
|
|
|
g_byte_array_set_size(quic_data->dest, quic_data->dest->len*2);
|
|
|
|
*io_ptr = (uint32_t *)(quic_data->dest->data + initial_len);
|
|
return (quic_data->dest->len - initial_len)/4;
|
|
}
|
|
|
|
|
|
static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
|
|
{
|
|
g_return_val_if_reached(0);
|
|
}
|
|
|
|
|
|
static void init_quic_data(QuicData *quic_data)
|
|
{
|
|
quic_data->usr.error = quic_usr_error;
|
|
quic_data->usr.warn = quic_usr_warn;
|
|
quic_data->usr.info = quic_usr_warn;
|
|
quic_data->usr.malloc = quic_usr_malloc;
|
|
quic_data->usr.free = quic_usr_free;
|
|
quic_data->usr.more_space = quic_usr_more_space;
|
|
quic_data->usr.more_lines = quic_usr_more_lines;
|
|
quic_data->dest = g_byte_array_new();
|
|
}
|
|
|
|
// RGB luminosity (sum 256) 54, 184, 18
|
|
static inline uint8_t pixel_to_gray(uint8_t r, uint8_t g, uint8_t b)
|
|
{
|
|
return (54u * r + 184u * g + 18u * b) / 256u;
|
|
}
|
|
|
|
static inline void gray_to_pixel(uint8_t gray, uint8_t *p)
|
|
{
|
|
p[0] = p[1] = p[2] = gray;
|
|
}
|
|
|
|
static inline uint16_t pixel_to_rgb16(uint8_t r, uint8_t g, uint8_t b)
|
|
{
|
|
r = (r >> 3) & 0x1Fu;
|
|
g = (g >> 3) & 0x1Fu;
|
|
b = (b >> 3) & 0x1Fu;
|
|
return r * (32u*32u) + g * 32u + b;
|
|
}
|
|
|
|
static inline void rgb16_to_pixel(uint16_t color, uint8_t *p)
|
|
{
|
|
uint8_t comp;
|
|
comp = (color >> 10) & 0x1Fu;
|
|
*p++ = (comp << 3) | (comp >> 2);
|
|
color <<= 5;
|
|
comp = (color >> 10) & 0x1Fu;
|
|
*p++ = (comp << 3) | (comp >> 2);
|
|
color <<= 5;
|
|
comp = (color >> 10) & 0x1Fu;
|
|
*p++ = (comp << 3) | (comp >> 2);
|
|
}
|
|
|
|
#define CONVERT_PROC(TYPE, FUNC) \
|
|
static void gdk_pixbuf_convert_to_##FUNC(GdkPixbuf *pixbuf, TYPE *dest_line, int dest_stride) \
|
|
{ \
|
|
int width = gdk_pixbuf_get_width(pixbuf); \
|
|
int height = gdk_pixbuf_get_height(pixbuf); \
|
|
int n_channels = gdk_pixbuf_get_n_channels (pixbuf); \
|
|
int stride = gdk_pixbuf_get_rowstride(pixbuf); \
|
|
uint8_t *line = gdk_pixbuf_get_pixels(pixbuf); \
|
|
\
|
|
for (int y = 0; y < height; y++) { \
|
|
uint8_t *p = line; \
|
|
TYPE *dest = dest_line; \
|
|
for (int x = 0; x < width; x++) { \
|
|
\
|
|
*dest = pixel_to_##FUNC(p[0], p[1], p[2]); \
|
|
FUNC##_to_pixel(*dest, p); \
|
|
++dest; \
|
|
p += n_channels; \
|
|
} \
|
|
line += stride; \
|
|
dest_line = (TYPE*)((char*) dest_line + dest_stride); \
|
|
} \
|
|
}
|
|
|
|
CONVERT_PROC(uint8_t, gray)
|
|
CONVERT_PROC(uint16_t, rgb16)
|
|
|
|
#define UNCONVERT_PROC(TYPE, FUNC) \
|
|
static void gdk_pixbuf_unconvert_to_##FUNC(GdkPixbuf *pixbuf) \
|
|
{ \
|
|
const int width = gdk_pixbuf_get_width(pixbuf); \
|
|
const int height = gdk_pixbuf_get_height(pixbuf); \
|
|
const int n_channels = gdk_pixbuf_get_n_channels(pixbuf); \
|
|
const int stride = gdk_pixbuf_get_rowstride(pixbuf); \
|
|
uint8_t *line = gdk_pixbuf_get_pixels(pixbuf) + stride*height; \
|
|
\
|
|
for (int y = 0; y < height; y++) { \
|
|
line -= stride; \
|
|
uint8_t *p = line + width*n_channels; \
|
|
const TYPE *dest = (TYPE*) line + width; \
|
|
for (int x = 0; x < width; x++) { \
|
|
--dest; \
|
|
p -= n_channels; \
|
|
FUNC##_to_pixel(*dest, p); \
|
|
if (n_channels == 4) { \
|
|
p[3] = 255; \
|
|
} \
|
|
} \
|
|
} \
|
|
g_assert(line == gdk_pixbuf_get_pixels(pixbuf)); \
|
|
}
|
|
|
|
UNCONVERT_PROC(uint8_t, gray)
|
|
UNCONVERT_PROC(uint16_t, rgb16)
|
|
|
|
typedef struct {
|
|
QuicImageType quic_type;
|
|
uint8_t *pixels;
|
|
int stride;
|
|
GdkPixbuf *pixbuf;
|
|
} ImageBuf;
|
|
|
|
static ImageBuf *image_buf_init(ImageBuf *imgbuf, GdkPixbuf *pixbuf)
|
|
{
|
|
imgbuf->pixbuf = pixbuf;
|
|
switch (gdk_pixbuf_get_n_channels(pixbuf)) {
|
|
case 3:
|
|
imgbuf->quic_type = QUIC_IMAGE_TYPE_RGB24;
|
|
break;
|
|
case 4:
|
|
imgbuf->quic_type = QUIC_IMAGE_TYPE_RGBA;
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
imgbuf->pixels = gdk_pixbuf_get_pixels(pixbuf);
|
|
imgbuf->stride = gdk_pixbuf_get_rowstride(pixbuf);
|
|
|
|
if (color_mode == COLOR_MODE_GRAY) {
|
|
int stride = gdk_pixbuf_get_width(pixbuf);
|
|
uint8_t *pixels = g_malloc(stride * gdk_pixbuf_get_height(pixbuf));
|
|
gdk_pixbuf_convert_to_gray(pixbuf, pixels, stride);
|
|
imgbuf->stride = stride;
|
|
imgbuf->pixels = pixels;
|
|
imgbuf->quic_type = QUIC_IMAGE_TYPE_GRAY;
|
|
} else if (color_mode == COLOR_MODE_RGB16) {
|
|
int stride = gdk_pixbuf_get_width(pixbuf)*2;
|
|
uint16_t *pixels = g_malloc(stride * gdk_pixbuf_get_height(pixbuf));
|
|
gdk_pixbuf_convert_to_rgb16(pixbuf, pixels, stride);
|
|
imgbuf->stride = stride;
|
|
imgbuf->pixels = (uint8_t*)pixels;
|
|
imgbuf->quic_type = QUIC_IMAGE_TYPE_RGB16;
|
|
}
|
|
|
|
return imgbuf;
|
|
}
|
|
|
|
static void image_buf_free(ImageBuf *imgbuf, GdkPixbuf *pixbuf)
|
|
{
|
|
if (imgbuf->quic_type == QUIC_IMAGE_TYPE_GRAY) {
|
|
gdk_pixbuf_unconvert_to_gray(pixbuf);
|
|
}
|
|
|
|
if (imgbuf->quic_type == QUIC_IMAGE_TYPE_RGB16) {
|
|
gdk_pixbuf_unconvert_to_rgb16(pixbuf);
|
|
}
|
|
|
|
if (imgbuf->pixels != gdk_pixbuf_get_pixels(imgbuf->pixbuf)) {
|
|
g_free(imgbuf->pixels);
|
|
}
|
|
}
|
|
|
|
static GByteArray *quic_encode_from_pixbuf(GdkPixbuf *pixbuf, const ImageBuf *imgbuf)
|
|
{
|
|
QuicData quic_data;
|
|
QuicContext *quic;
|
|
int encoded_size;
|
|
|
|
init_quic_data(&quic_data);
|
|
g_byte_array_set_size(quic_data.dest, 1024);
|
|
|
|
quic = quic_create(&quic_data.usr);
|
|
g_assert(quic != NULL);
|
|
encoded_size = quic_encode(quic, imgbuf->quic_type,
|
|
gdk_pixbuf_get_width(pixbuf),
|
|
gdk_pixbuf_get_height(pixbuf),
|
|
imgbuf->pixels,
|
|
gdk_pixbuf_get_height(pixbuf),
|
|
imgbuf->stride,
|
|
(uint32_t *)quic_data.dest->data,
|
|
quic_data.dest->len/sizeof(uint32_t));
|
|
g_assert(encoded_size > 0);
|
|
encoded_size *= 4;
|
|
g_byte_array_set_size(quic_data.dest, encoded_size);
|
|
quic_destroy(quic);
|
|
|
|
return quic_data.dest;
|
|
}
|
|
|
|
static GdkPixbuf *quic_decode_to_pixbuf(GByteArray *compressed_data)
|
|
{
|
|
QuicData quic_data;
|
|
QuicContext *quic;
|
|
GdkPixbuf *pixbuf;
|
|
QuicImageType type;
|
|
int width;
|
|
int height;
|
|
int status;
|
|
|
|
init_quic_data(&quic_data);
|
|
g_byte_array_free(quic_data.dest, TRUE);
|
|
quic_data.dest = NULL;
|
|
|
|
quic = quic_create(&quic_data.usr);
|
|
g_assert(quic != NULL);
|
|
|
|
status = quic_decode_begin(quic,
|
|
(uint32_t *)compressed_data->data, compressed_data->len/4,
|
|
&type, &width, &height);
|
|
g_assert(status == QUIC_OK);
|
|
|
|
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
|
|
(type == QUIC_IMAGE_TYPE_RGBA), 8,
|
|
width, height);
|
|
status = quic_decode(quic, type,
|
|
gdk_pixbuf_get_pixels(pixbuf),
|
|
gdk_pixbuf_get_rowstride(pixbuf));
|
|
g_assert(status == QUIC_OK);
|
|
quic_destroy(quic);
|
|
|
|
return pixbuf;
|
|
}
|
|
|
|
static void gdk_pixbuf_compare(GdkPixbuf *pixbuf_a, GdkPixbuf *pixbuf_b)
|
|
{
|
|
int width = gdk_pixbuf_get_width(pixbuf_a);
|
|
int height = gdk_pixbuf_get_height(pixbuf_a);
|
|
int n_channels_a = gdk_pixbuf_get_n_channels(pixbuf_a);
|
|
int n_channels_b = gdk_pixbuf_get_n_channels(pixbuf_b);
|
|
int x;
|
|
int y;
|
|
guint8 *pixels_a = gdk_pixbuf_get_pixels(pixbuf_a);
|
|
guint8 *pixels_b = gdk_pixbuf_get_pixels(pixbuf_b);
|
|
bool check_alpha = gdk_pixbuf_get_has_alpha(pixbuf_a);
|
|
|
|
g_assert(width == gdk_pixbuf_get_width(pixbuf_b));
|
|
g_assert(height == gdk_pixbuf_get_height(pixbuf_b));
|
|
if (color_mode != COLOR_MODE_RGB) {
|
|
check_alpha = false;
|
|
} else {
|
|
g_assert(n_channels_a == n_channels_b);
|
|
g_assert(gdk_pixbuf_get_byte_length(pixbuf_a) == gdk_pixbuf_get_byte_length(pixbuf_b));
|
|
}
|
|
for (y = 0; y < height; y++) {
|
|
for (x = 0; x < width; x++) {
|
|
guint8 *p_a = pixels_a + y*gdk_pixbuf_get_rowstride(pixbuf_a) + x*n_channels_a;
|
|
guint8 *p_b = pixels_b + y*gdk_pixbuf_get_rowstride(pixbuf_b) + x*n_channels_b;
|
|
|
|
g_assert(p_a[0] == p_b[0]);
|
|
g_assert(p_a[1] == p_b[1]);
|
|
g_assert(p_a[2] == p_b[2]);
|
|
if (check_alpha) {
|
|
g_assert(p_a[3] == p_b[3]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static GdkPixbuf *gdk_pixbuf_new_random(void)
|
|
{
|
|
gboolean has_alpha = g_random_boolean();
|
|
gint width = g_random_int_range(100, 2000);
|
|
gint height = g_random_int_range(100, 2000);
|
|
GdkPixbuf *random_pixbuf;
|
|
guint i;
|
|
guint8 *pixels;
|
|
|
|
random_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, width, height);
|
|
pixels = gdk_pixbuf_get_pixels(random_pixbuf);
|
|
for (i = 0; i < gdk_pixbuf_get_byte_length(random_pixbuf); i++) {
|
|
pixels[i] = g_random_int_range(0, 256);
|
|
}
|
|
|
|
return random_pixbuf;
|
|
}
|
|
|
|
static void test_pixbuf(GdkPixbuf *pixbuf)
|
|
{
|
|
GdkPixbuf *uncompressed_pixbuf;
|
|
GByteArray *compressed_data;
|
|
g_assert(pixbuf != NULL);
|
|
g_assert(gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB);
|
|
g_assert(gdk_pixbuf_get_bits_per_sample(pixbuf) == 8);
|
|
|
|
ImageBuf imgbuf[1];
|
|
image_buf_init(imgbuf, pixbuf);
|
|
compressed_data = quic_encode_from_pixbuf(pixbuf, imgbuf);
|
|
|
|
uncompressed_pixbuf = quic_decode_to_pixbuf(compressed_data);
|
|
image_buf_free(imgbuf, uncompressed_pixbuf);
|
|
|
|
//g_assert(memcmp(gdk_pixbuf_get_pixels(pixbuf), gdk_pixbuf_get_pixels(uncompressed_pixbuf), gdk_pixbuf_get_byte_length(uncompressed_pixbuf)));
|
|
gdk_pixbuf_compare(pixbuf, uncompressed_pixbuf);
|
|
|
|
g_byte_array_free(compressed_data, TRUE);
|
|
g_object_unref(uncompressed_pixbuf);
|
|
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
if (argc >= 2) {
|
|
for (int i = 1; i < argc; ++i) {
|
|
GdkPixbuf *source_pixbuf;
|
|
|
|
source_pixbuf = gdk_pixbuf_new_from_file(argv[i], NULL);
|
|
test_pixbuf(source_pixbuf);
|
|
g_object_unref(source_pixbuf);
|
|
}
|
|
} else if (argc == 1) {
|
|
unsigned int count;
|
|
|
|
for (count = 0; count < 50; count++) {
|
|
color_mode = (color_mode_t) (count % 3);
|
|
GdkPixbuf *pixbuf = gdk_pixbuf_new_random();
|
|
test_pixbuf(pixbuf);
|
|
g_object_unref(pixbuf);
|
|
}
|
|
} else {
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
return 0;
|
|
}
|