resizewindowgtkvalagdkpixbuf

How to resize an Image to window size in Vala


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 :)


Solution

  • 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;
    }