|
Packit |
a4058c |
/* -*- Mode: C; c-basic-offset: 2; -*- */
|
|
Packit |
a4058c |
/* GdkPixbuf library - test loaders
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* Copyright (C) 2016 Red Hat, Inc.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
a4058c |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
a4058c |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
a4058c |
* (at your option) any later version.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
a4058c |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
a4058c |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
a4058c |
* GNU General Public License for more details.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* You should have received a copy of the GNU General Public License
|
|
Packit |
a4058c |
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* Author: Andrey Tsyvarev
|
|
Packit |
a4058c |
* Bastien Nocera
|
|
Packit |
a4058c |
*/
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#include <stdio.h>
|
|
Packit |
a4058c |
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* maximum number of frames in animation(before repeating) */
|
|
Packit |
a4058c |
#define MAX_NUMBER_FRAMES 1000
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* Information about currently loading frame of animation.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* pixbuf == NULL means that animation is fully loaded
|
|
Packit |
a4058c |
* (no currently loading frames exist).
|
|
Packit |
a4058c |
*/
|
|
Packit |
a4058c |
typedef struct {
|
|
Packit |
a4058c |
GdkPixbufAnimationIter* iter; /* iterator pointed to the frame */
|
|
Packit |
a4058c |
GTimeVal time; /* time when this frame appears */
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf; /* current content of the frame */
|
|
Packit |
a4058c |
} FrameData;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* Auxiliary function, returns numeric representation of pixel.
|
|
Packit |
a4058c |
* For RGB format it is rrggbb(in hex), for RGBA - rrggbbaa. */
|
|
Packit |
a4058c |
static guint32
|
|
Packit |
a4058c |
get_pixel (GdkPixbuf *pixbuf, int x, int y)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
guchar *colors;
|
|
Packit |
a4058c |
guint32 pixel;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
colors = ((guchar*)gdk_pixbuf_get_pixels(pixbuf)
|
|
Packit |
a4058c |
+ gdk_pixbuf_get_n_channels(pixbuf) * x
|
|
Packit |
a4058c |
+ gdk_pixbuf_get_rowstride(pixbuf) * y);
|
|
Packit |
a4058c |
pixel = (colors[0] << 16) | (colors[1] << 8) | colors[2];
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (gdk_pixbuf_get_n_channels (pixbuf) == 4)
|
|
Packit |
a4058c |
pixel = (pixel << 8) | colors[3];
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return pixel;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
/* Verify whether all pixels outside the updated area
|
|
Packit |
a4058c |
* have the same values in pixbuf_old and pixbuf_new. */
|
|
Packit |
a4058c |
static gboolean
|
|
Packit |
a4058c |
pixbuf_not_changed_outside_area (GdkPixbuf *pixbuf_new,
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf_old,
|
|
Packit |
a4058c |
int x,
|
|
Packit |
a4058c |
int y,
|
|
Packit |
a4058c |
int width,
|
|
Packit |
a4058c |
int height)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
int pixbuf_width, pixbuf_height;
|
|
Packit |
a4058c |
int x_curr, y_curr;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
pixbuf_width = gdk_pixbuf_get_width (pixbuf_new);
|
|
Packit |
a4058c |
pixbuf_height = gdk_pixbuf_get_height (pixbuf_new);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
for (x_curr = 0; x_curr < pixbuf_width; x_curr++) {
|
|
Packit |
a4058c |
for (y_curr = 0; y_curr < pixbuf_height; y_curr++) {
|
|
Packit |
a4058c |
if ((x_curr >= x)
|
|
Packit |
a4058c |
&& (x_curr < x + width)
|
|
Packit |
a4058c |
&& (y_curr >= y)
|
|
Packit |
a4058c |
&& (y_curr < y + height)) {
|
|
Packit |
a4058c |
continue; /* inside area - don't compare pixels. */
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
if (get_pixel (pixbuf_new, x_curr, y_curr) != get_pixel (pixbuf_old, x_curr, y_curr)) {
|
|
Packit |
a4058c |
printf("Pixel at (%d, %d) changed from %x to %x,\n",
|
|
Packit |
a4058c |
x_curr, y_curr,
|
|
Packit |
a4058c |
get_pixel (pixbuf_old, x_curr, y_curr),
|
|
Packit |
a4058c |
get_pixel (pixbuf_new, x_curr, y_curr));
|
|
Packit |
a4058c |
printf("But it is outside of area with ");
|
|
Packit |
a4058c |
printf("x=%d, y=%d, width=%d, height=%d\n",
|
|
Packit |
a4058c |
x, y, width, height);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#if 0
|
|
Packit |
a4058c |
gdk_pixbuf_save (pixbuf_old, "old.png", "png", NULL, NULL);
|
|
Packit |
a4058c |
gdk_pixbuf_save (pixbuf_new, "new.png", "png", NULL, NULL);
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_assert_not_reached ();
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return TRUE;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
callback_area_updated (GdkPixbufLoader *loader,
|
|
Packit |
a4058c |
int x,
|
|
Packit |
a4058c |
int y,
|
|
Packit |
a4058c |
int width,
|
|
Packit |
a4058c |
int height,
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf_old)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf_new;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
pixbuf_new = gdk_pixbuf_loader_get_pixbuf (loader);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
pixbuf_not_changed_outside_area (pixbuf_new, pixbuf_old, x, y, width, height);
|
|
Packit |
a4058c |
/* update copy of pixbuf */
|
|
Packit |
a4058c |
gdk_pixbuf_copy_area (pixbuf_new, x, y, width, height, pixbuf_old, x, y);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* free copy of pixbuf used in area-updated callback. */
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
callback_closed (GdkPixbufLoader *loader,
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf_copy)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
g_object_unref (pixbuf_copy);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* prepare copy of pixbuf and connect other callbacks */
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
callback_area_prepared (GdkPixbufLoader *loader)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf_copy;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
pixbuf_copy = gdk_pixbuf_copy (gdk_pixbuf_loader_get_pixbuf (loader));
|
|
Packit |
a4058c |
/* connect callbacks for another signals for not use pointer to pointer in them. */
|
|
Packit |
a4058c |
g_signal_connect (loader, "area-updated",
|
|
Packit |
a4058c |
(GCallback) callback_area_updated, (gpointer) pixbuf_copy);
|
|
Packit |
a4058c |
g_signal_connect (loader, "closed",
|
|
Packit |
a4058c |
(GCallback) callback_closed, (gpointer) pixbuf_copy);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* Read count_bytes from the channel and write them to the loader.
|
|
Packit |
a4058c |
* Returns number of bytes written.
|
|
Packit |
a4058c |
* count_bytes = G_MAXSIZE means read as many bytes as possible. */
|
|
Packit |
a4058c |
static gsize
|
|
Packit |
a4058c |
loader_write_from_channel (GdkPixbufLoader *loader,
|
|
Packit |
a4058c |
GIOChannel *channel,
|
|
Packit |
a4058c |
gsize count_bytes)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
guchar* buffer;
|
|
Packit |
a4058c |
gsize bytes_read;
|
|
Packit |
a4058c |
GIOStatus read_status;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if(count_bytes < G_MAXSIZE) {
|
|
Packit |
a4058c |
/* read no more than 'count_bytes' bytes */
|
|
Packit |
a4058c |
buffer = g_malloc(count_bytes);
|
|
Packit |
a4058c |
read_status = g_io_channel_read_chars (channel, (gchar*) buffer, count_bytes, &bytes_read, NULL);
|
|
Packit |
a4058c |
} else {
|
|
Packit |
a4058c |
/*read up to end */
|
|
Packit |
a4058c |
read_status = g_io_channel_read_to_end (channel, (gchar**) &buffer, &bytes_read, NULL);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if ((read_status != G_IO_STATUS_NORMAL) && (read_status != G_IO_STATUS_EOF))
|
|
Packit |
a4058c |
g_assert_not_reached ();
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (!gdk_pixbuf_loader_write(loader, buffer, bytes_read, NULL))
|
|
Packit |
a4058c |
g_assert_not_reached ();
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_free (buffer);
|
|
Packit |
a4058c |
return bytes_read;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
test_area_updated_ico (gconstpointer data)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
const char *filename;
|
|
Packit |
a4058c |
GIOChannel *channel;
|
|
Packit |
a4058c |
GdkPixbufLoader *loader;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
filename = g_test_get_filename (G_TEST_DIST, data, NULL);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* create channel */
|
|
Packit |
a4058c |
channel = g_io_channel_new_file(filename, "r", NULL);
|
|
Packit |
a4058c |
g_assert_nonnull (channel);
|
|
Packit |
a4058c |
g_io_channel_set_encoding (channel, NULL, NULL);
|
|
Packit |
a4058c |
/* create loader */
|
|
Packit |
a4058c |
loader = gdk_pixbuf_loader_new ();
|
|
Packit |
a4058c |
g_assert_nonnull (loader);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_signal_connect(loader, "area-prepared",
|
|
Packit |
a4058c |
(GCallback) callback_area_prepared, NULL);
|
|
Packit |
a4058c |
/* callbacks for "area-updated" and "closed" signals will be connected
|
|
Packit |
a4058c |
* in callback_area_prepared() */
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* read image byte by byte */
|
|
Packit |
a4058c |
while (loader_write_from_channel(loader, channel, 1) == 1);
|
|
Packit |
a4058c |
/* or read full image at once */
|
|
Packit |
a4058c |
#if 0
|
|
Packit |
a4058c |
loader_write_from_channel(loader, channel, G_MAXSIZE);
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* free resources */
|
|
Packit |
a4058c |
g_io_channel_unref (channel);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
gdk_pixbuf_loader_close (loader, NULL);
|
|
Packit |
a4058c |
g_object_unref (loader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* Auxiliary function - look for frame that's currently loading. */
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
update_currently_loaded_frame (FrameData* frame)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
int tmp_count;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (gdk_pixbuf_animation_iter_on_currently_loading_frame(frame->iter))
|
|
Packit |
a4058c |
return; /* frame is currently being loaded */
|
|
Packit |
a4058c |
/* clear old content of pixbuf */
|
|
Packit |
a4058c |
if (frame->pixbuf)
|
|
Packit |
a4058c |
g_object_unref (frame->pixbuf);
|
|
Packit |
a4058c |
frame->pixbuf = NULL;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
tmp_count = 0;
|
|
Packit |
a4058c |
do {
|
|
Packit |
a4058c |
int delay_time;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (++tmp_count > MAX_NUMBER_FRAMES) {
|
|
Packit |
a4058c |
/* protection against frames repeating */
|
|
Packit |
a4058c |
return;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
delay_time = gdk_pixbuf_animation_iter_get_delay_time (frame->iter);
|
|
Packit |
a4058c |
if (delay_time < 0) {
|
|
Packit |
a4058c |
/* this is last frame in the animation */
|
|
Packit |
a4058c |
return;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
g_time_val_add (&frame->time, delay_time * 1000);
|
|
Packit |
a4058c |
gdk_pixbuf_animation_iter_advance (frame->iter, &frame->time);
|
|
Packit |
a4058c |
} while (!gdk_pixbuf_animation_iter_on_currently_loading_frame (frame->iter));
|
|
Packit |
a4058c |
/* store current content of the frame */
|
|
Packit |
a4058c |
frame->pixbuf = gdk_pixbuf_copy (gdk_pixbuf_animation_iter_get_pixbuf (frame->iter));
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
callback_area_updated_anim (GdkPixbufLoader *loader,
|
|
Packit |
a4058c |
int x,
|
|
Packit |
a4058c |
int y,
|
|
Packit |
a4058c |
int width,
|
|
Packit |
a4058c |
int height,
|
|
Packit |
a4058c |
FrameData *frame_old)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf_new;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* "area-updated" signal was emitted after animation had fully loaded. */
|
|
Packit |
a4058c |
g_assert_nonnull (frame_old->pixbuf);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
pixbuf_new = gdk_pixbuf_animation_iter_get_pixbuf (frame_old->iter);
|
|
Packit |
a4058c |
pixbuf_not_changed_outside_area (pixbuf_new, frame_old->pixbuf, x, y, width, height);
|
|
Packit |
a4058c |
gdk_pixbuf_copy_area (pixbuf_new, x, y, width, height, frame_old->pixbuf, x, y);
|
|
Packit |
a4058c |
update_currently_loaded_frame (frame_old);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* free resources used in callback_area_updated_anim */
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
callback_closed_anim(GdkPixbufLoader *loader, FrameData *frame_copy)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
g_object_unref (frame_copy->iter);
|
|
Packit |
a4058c |
if(frame_copy->pixbuf != NULL)
|
|
Packit |
a4058c |
g_object_unref (frame_copy->pixbuf);
|
|
Packit |
a4058c |
g_free (frame_copy);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
/* prepare frame information and register other callbacks */
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
callback_area_prepared_anim (GdkPixbufLoader* loader)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GdkPixbufAnimation *anim;
|
|
Packit |
a4058c |
FrameData* frame_copy = g_new (FrameData, 1);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_signal_connect (loader, "area-updated",
|
|
Packit |
a4058c |
(GCallback) callback_area_updated_anim, (gpointer) frame_copy);
|
|
Packit |
a4058c |
g_signal_connect (loader, "closed",
|
|
Packit |
a4058c |
(GCallback) callback_closed_anim, (gpointer) frame_copy);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
frame_copy->time.tv_sec = frame_copy->time.tv_usec = 0; /* some time */
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
anim = gdk_pixbuf_loader_get_animation (loader);
|
|
Packit |
a4058c |
frame_copy->iter = gdk_pixbuf_animation_get_iter (anim, &frame_copy->time);
|
|
Packit |
a4058c |
frame_copy->pixbuf = gdk_pixbuf_copy (gdk_pixbuf_animation_iter_get_pixbuf (frame_copy->iter));
|
|
Packit |
a4058c |
update_currently_loaded_frame (frame_copy);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
test_area_updated_anim (gconstpointer data)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
const char *filename;
|
|
Packit |
a4058c |
GIOChannel *channel;
|
|
Packit |
a4058c |
GdkPixbufLoader *loader;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
filename = g_test_get_filename (G_TEST_DIST, data, NULL);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
channel = g_io_channel_new_file (filename, "r", NULL);
|
|
Packit |
a4058c |
g_assert_nonnull (channel);
|
|
Packit |
a4058c |
g_io_channel_set_encoding(channel, NULL, NULL);
|
|
Packit |
a4058c |
/*create loader */
|
|
Packit |
a4058c |
loader = gdk_pixbuf_loader_new ();
|
|
Packit |
a4058c |
g_assert_nonnull (loader);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_signal_connect (loader, "area-prepared",
|
|
Packit |
a4058c |
(GCallback) callback_area_prepared_anim, NULL);
|
|
Packit |
a4058c |
/* other callbacks will be registered inside callback_area_prepared_anim */
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/*read animation by portions of bytes */
|
|
Packit |
a4058c |
#if 0
|
|
Packit |
a4058c |
while(loader_write_from_channel(loader, channel, 20) == 20);
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
/* or read it at once */
|
|
Packit |
a4058c |
loader_write_from_channel (loader, channel, G_MAXSIZE);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* free resources */
|
|
Packit |
a4058c |
g_io_channel_unref (channel);
|
|
Packit |
a4058c |
gdk_pixbuf_loader_close (loader, NULL);
|
|
Packit |
a4058c |
g_object_unref (loader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
int main(int argc, char **argv)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
g_test_init (&argc, &argv, NULL);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_test_add_data_func ("/pixbuf/area-updated/ico", (gconstpointer) "test-images/reftests/squares.ico", test_area_updated_ico);
|
|
Packit |
a4058c |
g_test_add_data_func ("/pixbuf/area-updated/gif", (gconstpointer) "aero.gif", test_area_updated_anim);
|
|
Packit |
a4058c |
g_test_add_data_func ("/pixbuf/area-updated/gif2", (gconstpointer) "1_partyanimsm2.gif", test_area_updated_anim);
|
|
Packit |
a4058c |
g_test_add_data_func ("/pixbuf/area-updated/gif3", (gconstpointer) "test-animation.gif", test_area_updated_anim);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return g_test_run ();
|
|
Packit |
a4058c |
}
|