Отправляет email-рассылки с помощью сервиса Sendsay

Практические советы по GTK+

  Все выпуски  

Практические советы по GTK+ Image Viewer 1.2



Image Viewer 1.2

Напомню, что рассмотрение 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);
    
    // перерисовать рисунок
    gtk_widget_queue_draw(draw_area);
    
    g_free(filename_utf8);
    g_free(only_filename);
    g_free(str);
    if(!pict)    return FALSE;
    return TRUE;
}

Теперь создаётся два 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;

            // получаем новые размеры экрана
            if(size_x_first>dx)
            {
                size_x=dx;//(int)(size_x_first*koeff);
                size_y=(int)(dx/koeff);
            }
            // ещё недостаточно изменили размеры
            if(size_y>dy)
            {
                size_x=(int)(dy*koeff);
                size_y=dy;
            }
            // удаляем старую картинку
            g_object_unref (pict);
            // масштабируем картинку
            pict = gdk_pixbuf_scale_simple(pict_first, size_x, size_y, GDK_INTERP_BILINEAR);
        }
        // отрисовка картинки
        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_gc_set_line_attributes(gc,1,GDK_LINE_SOLID,GDK_CAP_ROUND,GDK_JOIN_ROUND);
    // Рисуем рамку
    gdk_draw_rectangle (draw_area->window,gc,FALSE,0,0,dx-1,dy-1);

    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, иначе при многократном изменении размеров картинки, она потеряет в качестве.

pict = gdk_pixbuf_scale_simple(pict_first, size_x, size_y, GDK_INTERP_BILINEAR);

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 - использовать не будем, не знаю для чего нужно, но влияние я не заметил

Под конец ещё нарисуем рамку на весь экран:

gdk_draw_rectangle (draw_area->window,gc,FALSE,0,0,dx-1,dy-1);

draw_area->window - GDK окно в которое рисуем
gc - контокст
FALSE означает, что не нужно заполнять область.
Координаты прямоугольника: (0,0)-(dx-1,dy-1)

Исходные коды приложения доступны по ссылке: image-viewer1.2.tar.gz [6 кб]


В избранное