Tuesday, 24 June 2008

Automatically rescale a Gtk::Image (and preserve aspect ratio)

Don't really have enough time to explain it all, but hopefully the comments help. Here are a couple of screenshots:





The image used in the examples above is here.

And the code:


/*
Problem:

Using GTK, display an image in a window and rescale when the window
resizes preserving the aspect ratio.

Analysis:

Gtk provides Gtk::Image which works on a Gdk::Pixbuf (pixel buffer)
which allows rescaling but without preserving aspect ratio.

Summary of solution:

Use a Gtk::Image embedded in a Gtk::AspectFrame and resize image
using dimensions determined by Gtk::AspectFrame. The AspectFrame
knows how to maintain a ratio so it does the messy work for us.

*/


#include <gtkmm.h>
#include <iostream>

/// This Widget only scales the image but does not preserve
/// aspect ratios.
struct ScalingImage : public Gtk::Image
{
explicit
ScalingImage(Glib::RefPtr<Gdk::Pixbuf> pixbuf,
Gdk::InterpType interp = Gdk::INTERP_BILINEAR):
Gtk::Image(pixbuf),
m_original(pixbuf),
m_interp(interp),
m_sized(false)
{
}

protected:
virtual
void
on_size_allocate(Gtk::Allocation & r)
{
// This event is fired on all Widgets when their rectangle gets
// resized. So we rescale our image. But rescaling our image fires
// another resize event, and another, and another... You see the
// problem!
//
// The most straightforward solution is to not rescale the image
// the second time the event is fired. Hence the m_size toggle.
//
// Ideally I'd not like to do this but this was the best way I
// found!
if(!m_sized)
{
Glib::RefPtr<Gdk::Pixbuf> scaled =
m_original->scale_simple(r.get_width(),
r.get_height(),
m_interp);
// Reset the image to be the new scaled image which
// will cause the second size-allocate signal
Gtk::Image::set(scaled);

// Make sure we don't treat it as a rescale request
// next time
m_sized = true;
}
else
{
// Reaction to set above. Call the base class's on_size_allocate
// function. Shouldn't I have to call this above too? Maybe it
// isn't necessary since it gets called anyway.
Gtk::Image::on_size_allocate(r);

// Treat next resize event as a rescale
m_sized = false;
}
}

private:
Glib::RefPtr<Gdk::Pixbuf> m_original;
Gdk::InterpType m_interp;
bool m_sized;
};

/// This widget automatically scales the image but preserves the
/// aspect ratio of the original image. This is accomplished via use
/// of the AspectFrame class
///
/// Ideally would like this to be a Gtk::Image subclass.
struct AspectPreservingScalingImage : public Gtk::AspectFrame
{
explicit
AspectPreservingScalingImage(Glib::RefPtr<Gdk::Pixbuf> const & pixbuf,
Gdk::InterpType interp = Gdk::INTERP_BILINEAR):
m_img(pixbuf,interp)
{
set(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER,
// Aspect ratio of frame
pixbuf->get_width()/float(pixbuf->get_height()));
// This makes it appear as if there is no frame
set_shadow_type(Gtk::SHADOW_NONE);
// This allows a minimum size
set_size_request(pixbuf->get_width()/2,pixbuf->get_height()/2);
// Finally, add the image to be managed
add(m_img);
}
private:
ScalingImage m_img;
};

int main(int argc, char* argv[])
{
if(argc != 2)
{
std::cout << "Usage: " << argv[0] << " <image-file>" << std::endl;
return 1;
}

Gtk::Main kit(argc,argv);

Gtk::Window win;
Gtk::VBox box;
win.add(box);

Gtk::LinkButton lb("http://www.utahskies.org/image_library/deepsky/constellations/orionwa.jpg");
AspectPreservingScalingImage apsi(Gdk::Pixbuf::create_from_file(argv[1]));

box.pack_start(lb,Gtk::PACK_SHRINK);
box.pack_start(apsi);

struct launch_browser
{
static
void
doit(Glib::ustring const & addr)
{
std::string command("firefox ");
command += addr.c_str();
std::system(command.c_str());
}
};

lb.signal_clicked().connect
(
sigc::bind(
sigc::ptr_fun(launch_browser::doit),
lb.get_uri()
)
);


win.show_all();

Gtk::Main::run(win);
}

Thursday, 5 June 2008

C++ Standard GUI


#include <gtkmm.h>

namespace std
{
namespace gui = Gtk;
}


I love gtkmm.

Monday, 2 June 2008

Linux printing: Brother MFC-465CN

I've been using Linux for a long time and as has anyone who has done so, printers, scanners and basically anything else that does not have a USB storage interface is a risky proposition.

Not so with the Brother MFC-465CN.

My setup process (on Ubuntu Hardy/8.04):


  • Set up hardware as suggested in manual

  • Skip software installation and connect printer to network via ethernet

  • sudo apt-get install brother-lpr-drivers-extra brother-cups-wrapper-extra

  • System->Administration->Printing->Test page

  • sudo apt-get install xsane sane

  • Get brsane2 driver (read the instructions!)

  • Launch xsane and test scanner


I don't even want to remember what it used to be LIKE before. Brother has GPLed their printer driver which is probably part of the reason it was so easy to set up. Great job Brother and many thanks to the Open Source fairy.