spice-common/tests/test-quic.c
Frediano Ziglio 2cec5f99af test-quic: Run 1 random quic test per color mode
Reduce execution time. No need to run so much tests, coverage
stays more or less the same.
We iterate twice to check RGB mode with alpha, otherwise the
coverage reduce about 10-20%.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@gmail.com>
2020-03-02 09:57:56 +00:00

415 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_END
} 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 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 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));
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));
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) {
pixbuf_unconvert_to_gray(pixbuf);
}
if (imgbuf->quic_type == QUIC_IMAGE_TYPE_RGB16) {
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 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 *pixbuf_new_random(int alpha)
{
gboolean has_alpha = alpha >= 0 ? alpha : g_random_boolean();
gint width = g_random_int_range(100, 2000);
gint height = g_random_int_range(100, 500);
GdkPixbuf *random_pixbuf;
guint i, size;
guint8 *pixels;
random_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, width, height);
pixels = gdk_pixbuf_get_pixels(random_pixbuf);
size = gdk_pixbuf_get_byte_length(random_pixbuf);
for (i = 0; i < size; 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)));
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) {
int alpha;
for (alpha = 0; alpha < 2; alpha++) {
for (color_mode = COLOR_MODE_RGB; color_mode < COLOR_MODE_END; color_mode++) {
/* alpha affects only COLOR_MODE_RGB more, reduce number of tests */
if (color_mode != COLOR_MODE_RGB && alpha) {
continue;
}
GdkPixbuf *pixbuf = pixbuf_new_random(alpha);
test_pixbuf(pixbuf);
g_object_unref(pixbuf);
}
}
} else {
g_assert_not_reached();
}
return 0;
}