/*
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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include "common/quic.h"
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();
}
static GByteArray *quic_encode_from_pixbuf(GdkPixbuf *pixbuf)
{
QuicData quic_data;
QuicContext *quic;
int encoded_size;
QuicImageType quic_type;
init_quic_data(&quic_data);
g_byte_array_set_size(quic_data.dest, 1024);
quic = quic_create(&quic_data.usr);
g_assert(quic != NULL);
switch (gdk_pixbuf_get_n_channels(pixbuf)) {
case 3:
quic_type = QUIC_IMAGE_TYPE_RGB24;
break;
case 4:
quic_type = QUIC_IMAGE_TYPE_RGBA;
break;
default:
g_assert_not_reached();
}
encoded_size = quic_encode(quic, quic_type,
gdk_pixbuf_get_width(pixbuf),
gdk_pixbuf_get_height(pixbuf),
gdk_pixbuf_get_pixels(pixbuf),
gdk_pixbuf_get_height(pixbuf),
gdk_pixbuf_get_rowstride(pixbuf),
(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 = gdk_pixbuf_get_n_channels(pixbuf_a);
int x;
int y;
guint8 *pixels_a = gdk_pixbuf_get_pixels(pixbuf_a);
guint8 *pixels_b = gdk_pixbuf_get_pixels(pixbuf_b);
g_assert(width == gdk_pixbuf_get_width(pixbuf_b));
g_assert(height == gdk_pixbuf_get_height(pixbuf_b));
g_assert(n_channels == gdk_pixbuf_get_n_channels(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;
guint8 *p_b = pixels_b + y*gdk_pixbuf_get_rowstride(pixbuf_b) + x*n_channels;
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 (gdk_pixbuf_get_has_alpha(pixbuf_a)) {
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);
compressed_data = quic_encode_from_pixbuf(pixbuf);
uncompressed_pixbuf = quic_decode_to_pixbuf(compressed_data);
g_assert(gdk_pixbuf_get_byte_length(pixbuf) == gdk_pixbuf_get_byte_length(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++) {
GdkPixbuf *pixbuf = gdk_pixbuf_new_random();
test_pixbuf(pixbuf);
g_object_unref(pixbuf);
}
} else {
g_assert_not_reached();
}
return 0;
}