GDK - jednoduchý prohlížeč obrázků
13.10.2020
V předchozím článku o programování v GDK jsme si ukázali jak s využitím knihovny Cairo vykreslit požadovaný obsah okna. Šlo o jednoduché úsečky a výpis textu. Nyní si ukážeme jak zobrazit obrázek uložený v souboru.
Využijme k tomu GdkPixbuf. Na začátku programu si soubor načteme pomicí funkce gdk_pixbuf_new_from_file.
// globalni promenna mimo funkci main
GdkPixbuf* pixbuf_orig = NULL;
pixbuf_orig = gdk_pixbuf_new_from_file(IMAGE_FILE_PATH, NULL);
if (NULL == pixbuf_orig)
{
printf("chyba načtení obrázku\n");
return EXIT_FAILURE;
}
Při vykreslování budeme chtít aby se obrázek přizpůsobil aktuálnímu rozměru okna se zachováním poměru stran. Proto si vždy vytvoříme pomocný PixBuff o rozměrech odpovídajících aktuálním rozměrům kreslící oblasti. Pak použijeme trochu aritmetiky pro vypočítání poměru velikostí, který pak použijeme ve funkci gdk_pixbuf_scale, kterou přeneseme načtený PixBuff do tohoto pomocného, který pak použijeme jako zdroj cairo kontextu a vykreslíme pomocí funkce cairo_rectangle. Vše je nejlépe vidět na zdrojovém kódu funkce obsluhující událost GDK_EXPOSE.
void on_expose(GdkEventExpose* ev)
{
if (NULL == ev->region)
return;
GdkDrawingContext* dc = gdk_window_begin_draw_frame(
window, ev->region);
cairo_t* cr = gdk_drawing_context_get_cairo_context(dc);
if (NULL == pixbuf_orig)
{
cairo_set_source_rgba(cr, 1.0, 1.0, 0.8, 1.0);
cairo_paint(cr);
gdk_window_end_draw_frame(window, dc);
return;
}
GdkPixbuf* pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
ev->area.width, ev->area.height);
double pomer;
double pocX = 0;
double pocY = 0;
double sirkacil = (double)ev->area.width;
double vyskacil = (double)ev->area.height;
if ( ((double)gdk_pixbuf_get_width(pixbuf_orig) / ev->area.width) >
((double)gdk_pixbuf_get_height(pixbuf_orig) / ev->area.height) )
{
pomer = (double)ev->area.width / gdk_pixbuf_get_width(pixbuf_orig);
vyskacil = ((double)gdk_pixbuf_get_height(pixbuf_orig) * pomer);
pocY = ((double)ev->area.height - vyskacil) / 2;
}
else
{
pomer = (double)ev->area.height / gdk_pixbuf_get_height(pixbuf_orig);
sirkacil = ((double)gdk_pixbuf_get_width(pixbuf_orig)*pomer);
pocX = ((double)ev->area.width - sirkacil)/2;
}
gdk_pixbuf_scale(pixbuf_orig, pb, 0, 0, ev->area.width, ev->area.height,
0, 0, pomer, pomer, GDK_INTERP_NEAREST);
gdk_cairo_set_source_pixbuf(cr, pb, pocX, pocY);
cairo_rectangle(cr, 0, 0,
ev->area.width - pocX, ev->area.height - pocY);
cairo_fill(cr);
gdk_window_end_draw_frame(window, dc);
g_object_unref(pb);
}
Pokud si program vyzkoušíte s nějakým výrazně velkým obrázkem, zjistíte že vykreslování pomocí knihovny Cairo je velice rychlé i při rychlém opakování při roztahování okna.
Na závěr opět celý výpis programu, který lze sestavit překladačem GCC následujícím příkazem (pro ladicí sestavení):
gcc gdk-okno.c -Wall -g `pkg-config --cflags --libs gdk-3.0` -ogdk-okno
#include <gdk/gdk.h>
#include <glib.h>
#include <stdlib.h>
#define IMAGE_FILE_PATH "kozel.png"
GMainLoop* main_loop;
GdkWindow* window;
GdkPixbuf* pixbuf_orig = NULL;
void on_expose(GdkEventExpose* ev)
{
if (NULL == ev->region)
return;
GdkDrawingContext* dc = gdk_window_begin_draw_frame(
window, ev->region);
cairo_t* cr = gdk_drawing_context_get_cairo_context(dc);
if (NULL == pixbuf_orig)
{
cairo_set_source_rgba(cr, 1.0, 1.0, 0.8, 1.0);
cairo_paint(cr);
gdk_window_end_draw_frame(window, dc);
return;
}
GdkPixbuf* pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
ev->area.width, ev->area.height);
double pomer;
double pocX = 0;
double pocY = 0;
double sirkacil = (double)ev->area.width;
double vyskacil = (double)ev->area.height;
if ( ((double)gdk_pixbuf_get_width(pixbuf_orig) / ev->area.width) >
((double)gdk_pixbuf_get_height(pixbuf_orig) / ev->area.height) )
{
pomer = (double)ev->area.width / gdk_pixbuf_get_width(pixbuf_orig);
vyskacil = ((double)gdk_pixbuf_get_height(pixbuf_orig) * pomer);
pocY = ((double)ev->area.height - vyskacil) / 2;
}
else
{
pomer = (double)ev->area.height / gdk_pixbuf_get_height(pixbuf_orig);
sirkacil = ((double)gdk_pixbuf_get_width(pixbuf_orig)*pomer);
pocX = ((double)ev->area.width - sirkacil)/2;
}
gdk_pixbuf_scale(pixbuf_orig, pb, 0, 0, ev->area.width, ev->area.height,
0, 0, pomer, pomer, GDK_INTERP_NEAREST);
gdk_cairo_set_source_pixbuf(cr, pb, pocX, pocY);
cairo_rectangle(cr, 0, 0,
ev->area.width - pocX, ev->area.height - pocY);
cairo_fill(cr);
gdk_window_end_draw_frame(window, dc);
g_object_unref(pb);
}
void on_end_app()
{
gdk_window_destroy(window);
g_main_loop_quit(main_loop);
}
void on_gdk_event(GdkEvent* event, gpointer data)
{
if (event->type == GDK_CONFIGURE)
{
gdk_window_invalidate_rect(window, NULL, TRUE);
}
else if (event->type == GDK_EXPOSE)
{
on_expose((GdkEventExpose*)event);
}
else if (event->type == GDK_DELETE)
{
on_end_app();
}
else if (event->type == GDK_KEY_PRESS)
{
if (((GdkEventKey*)event)->keyval == GDK_KEY_Escape)
on_end_app();
}
}
int main(int argc, char** argv)
{
if (!gdk_init_check(&argc, &argv))
return EXIT_FAILURE;
pixbuf_orig = gdk_pixbuf_new_from_file(IMAGE_FILE_PATH, NULL);
if (NULL == pixbuf_orig)
{
printf("chyba načtení obrázku\n");
return EXIT_FAILURE;
}
GdkWindowAttr attributes;
gint attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_TITLE | GDK_WA_WMCLASS;
memset(&attributes, 0, sizeof(attributes));
attributes.window_type = GDK_WINDOW_TOPLEVEL;
attributes.x = 100;
attributes.y = 50;
attributes.event_mask = GDK_ALL_EVENTS_MASK;
attributes.width = 1200;
attributes.height = 800;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.title = "Image view";
attributes.wclass = GDK_INPUT_OUTPUT;
gdk_event_handler_set(on_gdk_event, NULL, NULL);
window = gdk_window_new(NULL, &attributes, attr_mask);
gdk_window_show(window);
main_loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(main_loop);
g_main_loop_unref(main_loop);
g_object_unref(pixbuf_orig);
return EXIT_SUCCESS;
}