Напомню, что рассмотрение GTK происходит на примере написания
реального приложения с нуля, имеющего практическое применение - программа для
просмотра графических файлов В этом выпуске продолжим писать программу
image_viewer. На этот раз добавим основную функцию - отображение
графического файла на экране, причём, если изображение будет слишком большое, то
мы его уменьшим до размеров окна, а маленькие картинки расширять не будем,
такими и оставим.
В прошлый раз мы загружали картинку в GdkPixbuf *pict, теперь в *pict будет хранится готовая картинка
для отображения на экран, уже в нужном масштабе, а из файла будем загружать в
новую переменную GdkPixbuf *pict_firstиз
которой и будем делать *pict. В
первую очередь изменится фунция open_file(), вот её код:
//
открываем картинку gboolean open_file(char *filename_ansi) { guint
cx,cy; GdkPixbufFormat *info_pbuf=NULL; GdkPixbuf
*pixbuf=NULL; gchar *filename_utf8; gchar *only_filename;
gchar *str; // переводим в кодировку UTF8, ведь имя файла будет
отображено на экране filename_utf8 = g_filename_to_utf8(filename_ansi,
-1, NULL, NULL, NULL); // если уже есть картинка, то удаляем её
if(pict_first) g_object_unref (pict_first); if(pict)
g_object_unref (pict); pict_first = NULL; pict = NULL; //
получаем информацию о картинке info_pbuf =
gdk_pixbuf_get_file_info(filename_ansi,&cx,&cy); if(info_pbuf)//
файл распознался как картинка { pict_first =
gdk_pixbuf_new_from_file(filename_ansi, NULL); pict =
gdk_pixbuf_copy(pict_first); } // обновить информацию о
файле only_filename = g_path_get_basename(filename_utf8);// имя файла,
без путей if(pict) { int bits =
gdk_pixbuf_get_bits_per_sample(pict); gboolean alpha =
gdk_pixbuf_get_has_alpha(pict); str = g_strdup_printf("Файл: '%s'
Размер: %dx%d Глубина цвета: %d бит
Прозрачность:%s",only_filename,cx,cy,bits,(alpha)?"есть":"нет"); }
else str = g_strdup_printf("Файл: '%s' не является распознанной
картинкой",only_filename);
gtk_label_set_text(GTK_LABEL(label_info),str);
Теперь создаётся два GdkPixbuf-a pict_first и
pict с одинакомым содержимым . В конце open_file() обновляем картинку на
экране вызовом функции gtk_widget_queue_draw(draw_area), которая пошлёт сигнал
перерисовки окна виджету draw_area.
Теперь перепишем функию перерисовки виджета draw_area:
//
перерисовка виджета с картинкой static gboolean map_expose
(GtkWidget *draw_area,GdkEventExpose *event,gpointer data) { GdkGC *gc; GdkColor
color; // размеры виджета
draw_area gint dx; gint dy; // Узнаём размеры
области вывода gdk_drawable_get_size
(draw_area->window, &dx,&dy); gc = gdk_gc_new
(draw_area->window); // выбираем цвет для
рисования; color.red =
65535; color.green=
0; color.blue =
0;
gdk_gc_set_rgb_fg_color (gc, &color);
// рисуем
картинку; if(pict) { // размеры
первоначальной картинки int size_x_first
=gdk_pixbuf_get_width(pict_first); int size_y_first
=gdk_pixbuf_get_height(pict_first); // размеры текущей
картинки int size_x
=gdk_pixbuf_get_width(pict); int size_y
=gdk_pixbuf_get_height(pict); // Масштабирование
картинки if( (size_x!=dx
&& size_y!=dy) || size_x>dx || size_y>dy )// Если картинка ещё не
масшабирована { GdkPixbuf
*pixbuf=NULL; double koeff =
(double)(1.*size_x_first/size_y_first); size_x =
size_x_first; size_y =
size_y_first;
g_object_unref
(gc); // return TRUE because
we've handled this event, so no further processing is required. return TRUE; }
Первым делом узнаём размеры области вывода виджета draw_area (dx,
dy), потом
определяем размеры нашей картинки (size_x, size_y) и если они меньше размеров
области вывода, тогда уменьшаем картинку, ведь мы хотим увидеть её
целиком. Масшабируем всегда из
первоначальной картинки pict_first, иначе при многократном изменении размеров
картинки, она потеряет в качестве.
size_x, size_y - новые размеры картинки. GDK_INTERP_BILINEAR -
метод интерполяции, в наилучшим отношением качество/скорость , но есть и другие
- GDK_INTERP_NEAREST (самый простой быстрый), GDK_INTERP_TILES,
GDK_INTERP_BILINEAR, GDK_INTERP_HYPER (самый медленный и с наилучшим
качаеством)
GdkPixbuf будем выводить на экран с помощью
кода: gdk_draw_pixbuf(draw_area->window,gc,pict,0,0,dx/2-size_x/2,dy/2-size_y/2,size_x,size_y,GDK_RGB_DITHER_NONE,0,0);
gdk_draw_pixbuf()
выводит часть или весь GdkPixbuf в область для рисования
GdkDrawable.
void gdk_draw_pixbuf (GdkDrawable
*drawable, GdkGC *gc, GdkPixbuf
*pixbuf, gint src_x,gint
src_y, gint dest_x,gint
dest_y, gint width,gint
height, GdkRgbDither
dither, gint x_dither,gint
y_dither ); drawable - это куда будем выводить картинку;
к GdkDrawable относятся GdkWindow и GdkPixmap. gc - контекст для
рисования pixbuf - GdkPixbuf с картинкой src_x, src_y - смещение в
GdkPixbufот начала dest_x, dest_y - координаты в GdkDrawable (в окне), откуда
начнём рисовать картинку width, height - размеры добавляемой картинки;
(src_x+width должно быть меньше или равно ширине всего GdkPixbuf) dither,
x_dither, y_dither - использовать не будем, не знаю для чего нужно, но влияние я
не заметил
draw_area->window - GDK окно в которое рисуем gc - контокст FALSE
означает, что не нужно заполнять область. Координаты прямоугольника:
(0,0)-(dx-1,dy-1)