2

I am trying to create a small application which displays an Image that you selected in a File chooser. It should then resize when the user resizes the window.

My app works up to the point where I add this code to the constructor of my class which should give the image the ability to resize when the window is resized.

window.size_allocate.connect(() => {

        resize_image(); //<-- a problem

    });

this "should" call the method resize_image when the window changes its size but everytime I add this code, my virtual machine on which I run elementary os crashes and stops working ( I have to restart everytime I try to run my program).

the method resize_image() works as following:

public void resize_image()
{
    try
    {       if(buf.get_width() < window.get_allocated_width()){
            buf = buf.scale_simple(window.get_allocated_width(), window.get_allocated_width(), Gdk.InterpType.NEAREST); 
            image.set_from_pixbuf(buf);
            }      
    }catch(Error e)
    {
    }
}

(I know that my resizing "alogrithm" isnt the best yet but I just used this method for testing.)

Now my question: Why is my program crashing? Is the conversion from pixbuf to image too slow for the user? Is there another way to resize the image to the window size?

Any help would be appreciated :)

ben
  • 49
  • 5

1 Answers1

1

The trick here is to add a layout and set the resize callback not to the window but to the layout. It's not perfect, it's a bit dirty but works. Initial positioning not working good but there's rooms to improvement. Must check Gtk.Widget and Gtk.Containers for requested, allocated and natural sizes or even use Gdk methods. Getting late, hope this will lead you in the right direction.

PS: I'm using a endless.png image but feel free to use another one, just change the code to reflect it.

using Gtk;

public int main (string[] args) {
    Gtk.Image image;
    Gtk.Layout layout;
    Gtk.Window window;
    Gdk.Pixbuf pixbuf;

    Gtk.init (ref args);

    window = new Gtk.Window ();
    layout = new Gtk.Layout (); 
    image  = new Gtk.Image ();

    try {
        pixbuf = new Gdk.Pixbuf.from_file ("endless.png");
        image = new Gtk.Image.from_pixbuf (pixbuf); 
        layout.put (image, 0,0);
        window.add (layout);

        layout.size_allocate.connect ((allocation) => {
            print ("Width: %d Height: %d\n", allocation.width, allocation.height);
            var pxb = pixbuf.scale_simple (allocation.width, allocation.height, Gdk.InterpType.BILINEAR);
            image.set_from_pixbuf (pxb);
        });

        window.destroy.connect (Gtk.main_quit);

        window.show_all ();

        Gtk.main ();

        return 0;
    } catch (Error e) {
        stderr.printf ("Could not load file...exit (%s)\n", e.message);
        return 1;
    }
}

EDIT:

A simple cairo version:

using Gtk;
using Cairo;

public int main (string[] args) {
    Cairo.ImageSurface image;

    image = new Cairo.ImageSurface.from_png ("endless.png");

    Gtk.init (ref args);

    var window = new Gtk.Window ();
    var darea  = new DrawingArea ();
    window.add (darea);
    window.show_all ();

    darea.draw.connect ((cr) => {
        float xscale;
        float yscale;

        cr.save ();

        xscale = (float) darea.get_allocated_width () / image.get_width ();
        yscale = (float) darea.get_allocated_height () / image.get_height ();

        cr.scale (xscale, yscale);
        cr.set_source_surface (image, 0, 0);
        cr.paint ();

        cr.restore ();
        return true;
    });

    window.destroy.connect (Gtk.main_quit);

    Gtk.main ();

    return 0;
}

EDIT 2: I've created another version to toggle between 2 images and check if while doing this quite a few times and check if the memory increases, but it does not. Added a couple of Boxes, and added 2 buttons.

using Gtk;
using Cairo;

public int main (string[] args) {
    Cairo.ImageSurface image;

    image = new Cairo.ImageSurface.from_png ("endless.png");

    Gtk.init (ref args);

    var window = new Gtk.Window ();
    var box1   = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); 
    var box2   = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); 
    var b1     = new Gtk.Button.with_label ("Image1");
    var b2     = new Gtk.Button.with_label ("Image2");
    box2.pack_start (b1, true, true, 0);
    box2.pack_end (b2, true, true, 0);
    var darea  = new DrawingArea ();
    box1.pack_start (box2, false, false, 0);
    box1.pack_end (darea, true, true, 0);
    window.add (box1);
    window.show_all ();

    b1.clicked.connect (() => {
        image = new Cairo.ImageSurface.from_png ("endless.png");
        darea.queue_draw ();
    });

    b2.clicked.connect (() => {
        image = new Cairo.ImageSurface.from_png ("Gnome-logo.png");
        darea.queue_draw ();
    });

    darea.draw.connect ((cr) => {
        float xscale;
        float yscale;

        cr.save ();

        xscale = (float) darea.get_allocated_width () / image.get_width ();
        yscale = (float) darea.get_allocated_height () / image.get_height ();

        cr.scale (xscale, yscale);
        cr.set_source_surface (image, 0, 0);
        cr.paint ();

        cr.restore ();
        return true;
    });

    window.destroy.connect (Gtk.main_quit);

    Gtk.main ();

    return 0;
}
José Fonte
  • 4,016
  • 2
  • 16
  • 28
  • Thank you very very much :) But I have one question: Why did you say that it's a dirty way? And if you have time could you briefly explain how to do it the right way? – ben Apr 21 '17 at 20:13
  • @guardian sry just read ur comment. Well, not so dirty but does not reflect the best way to do it. A good idea would be to check the code from [eog (eye of gnome, the default image viewer)](https://github.com/GNOME/eog). Another aproach would be to create a Image view widget based on a Drawing area and let cairo, e.g., resize and draw the image. There are several aproaches. Of course, this one works and can be improved. In the past i've implemented something like this, even professionally and it worked. – José Fonte Apr 21 '17 at 20:39
  • Also note, that i keep the original pixbuf and resize from there. Retrieving the pixbuf from the image would add accumulated resampling loss and the image quality would degrade. – José Fonte Apr 21 '17 at 20:41
  • also check this, little old, repository for a [GtkImageView](https://github.com/GNOME/gtkimageview) – José Fonte Apr 21 '17 at 20:44
  • If i have time i'll try a Cairo aproach, no promises though :) – José Fonte Apr 21 '17 at 20:45
  • @guardian Here is a simple cairo version, very similar to the previous example. Check the edit. – José Fonte Apr 22 '17 at 12:21
  • I tried your Cairo version and it really works way better with one image but I can't get it to work with a filechooser so that it always displays the chosen image. The terminal always says that my widgets are out of memory. Can you help me with that too? – ben Apr 23 '17 at 15:56
  • Changing the image maybe implies the need to destroy allocated resources. Must check if the application memory is growing every time you change image. Probably we need to destroy the previous Cairo Surface. Let me check. – José Fonte Apr 23 '17 at 21:57
  • I've tried 'toggling' between 2 images for quite some time and memory does not increase. Maybe there's something wrong on what you are doing. Check Edit 2, above. – José Fonte Apr 23 '17 at 23:04
  • Thank you so so much, you helped me a lot!! – ben Apr 24 '17 at 09:00