10.2. Manipulating Images

You can load image files in common formats such as PNG and JPEG (see Tables 10.1a and 10.1b for a complete list of supported formats) into buffers accessed by QPixmap or QImage classes using image-loading routines built into Qt or added by the KDE library libksycoca. You can also save arbitrary QImage or QPixmap buffers in various formats.


Table 10.1a. Image Formats Supported by Qt

PNGPortable Network Graphics
BMPWindows Bitmap
XBMX Bitmap
XPMX Pixmap
PNMPortable Anymap format
GIF[a]Graphics Interchange Format
Notes:
a. GIF support is included in Qt only if Qt is appropriately configured before its source is compiled. To see whether your installed version of Qt was compiled with GIF support, change to the directory containing Qt include files (/usr/local/qt/include, /usr/include/qt, or another directory) and type


Table 10.1b. Additional Image Formats Supported by libksycoca

JPEGJoint Photographic Experts Group
XVXView Graphics Format
EPSEncapsulated Postscript
PCXIBM PC Paintbrush
IFFSun TAAC Image File Format
TGATarga Image File
TIFFTagged-Image File Format (read only)
KRLKellogg Radiation Laboratory (read only)


   1 
   2 grep BUILTIN_GIF qgif.h

If the last line displayed reads


   1 
   2 #define QT_BUILTIN_GIF_READER 1

then Qt was compiled with GIF support. Otherwise, the line will read


   1 
   2 #define QT_BUILTIN_GIF_READER 0
   3 

10.2.1. Comparison of QImage and QPixmap

QImage and QPixmap serve different purposes. QImage stores image data in a simple buffer, and QPixmap stores image data on the X server. This difference means that images will load and save to or from a QImage more quickly than to or from a QPixmap. It also means that you will be able to directly manipulate pixel data in a QImage more quickly, but use drawing functions (through QPainter) only on QPixmap. (Drawing functions are carried out by the X server, so they can operate only on data owned by the X server—that is, a QPixmap, but not a QImage.)

Because a QPixmap is stored on the X server, it can store only images that have "depths" allowed by the X server, and these depths are usually constrained by the specifications of the computer's video card. (An image's depth is the number of bits used to tell the color of one pixel. For example, pseudocolor (256 color) images have a depth of 8 bits, and truecolor (16 million color) images have a depth of 24 bits).

Furthermore, you can bitblt() to and from a QPixmap, but not to and from a QImage (see Chapter 9, "Constructing a Responsive User Interface" for more information about bitblt()).

10.2.2. An Image Viewer/Converter

To see how some of the image-manipulation functions work, let's write a widget that loads an image (in any of the acceptable formats listed in Table 10.1), draws an ugly green frame around it, saves it as a PNG file, and then displays it on the widget's window. This widget is shown in Listings 10.4 and 10.5.


Example 10.4. kimageview.h: Class Definition for KImageView, a Widget that Loads, Modifies, Saves, and Displays an Image

   1 
   2  1: #ifndef __KIMAGEVIEW_H__
   3  2: #define __KIMAGEVIEW_H__
   4  3:
   5  4: #include <qwidget.h>
   6  5:
   7  6: /**
   8  7:  * KImageView
   9  8:  * Display an image.
  10  9:  **/
  11 10: class KImageView : public QWidget
  12 11: {
  13 12:  public:
  14 13:   KImageView (const QString &filename, QWidget *parent, const char *name=0);
  15 14:
  16 15:  protected:
  17 16:   QPixmap *qpixmap;
  18 17:   QString filename;
  19 18:   void paintEvent (QPaintEvent *);
  20 19:
  21 20: };
  22 21:
  23 22: #endif

In this widget, most of the work is done in the constructor, but paintEvent() handles drawing the image onscreen. You store the image in a QPixmap so that you can draw on it with a QPainter.


Example 10.5. kimageview.cpp: Class Declaration for KImageView

   1 
   2  1: #include <qpixmap.h>
   3  2: #include <qpainter.h>
   4  3: #include <qfileinfo.h>
   5  4:
   6  5: #include <kimgio.h>
   7  6:
   8  7: #include "kimageview.h"
   9  8:
  10  9: KImageView::KImageView (const QString &_filename,
  11 10:             QWidget *parent, const char *name=0) :
  12 11:   QWidget (parent, name)
  13 12: {
  14 13:   filename = _filename;
  15 14:
  16 15:   KImageIO::registerFormats();
  17 16:
  18 17:   qpixmap = new QPixmap;
  19 18:   qpixmap->load (filename);
  20 19:
  21 20:   QPainter qpainter (qpixmap);
  22 21:   qpainter.setPen (QPen (Qt::green, 10));
  23 22:   qpainter.drawRect (qpixmap->rect());
  24 23:
  25 24:   QFileInfo qfileinfo (filename);
  26 25:   qpixmap->save (qfileinfo.baseName() + ".png", "PNG");
  27 26: }
  28 27:
  29 28: void
  30 29: KImageView::paintEvent (QPaintEvent *)
  31 30: {
  32 31:   QPainter qpainter (this);
  33 32:
  34 33:   qpainter.drawPixmap (0, 0, *qpixmap);
  35 34: }
  36 

To load the image formats that are part of libksycoca, you need to call the static method


   1 
   2 KImageIO::registerFormats();
   3 

as shown on line 15. Conveniently enough, this is all you have to do! This call registers the reading and writing routines for all the libksycoca formats with Qt and makes further use of them transparent. For example, on line 18 you make a call to the method QPixmap::load() to load the image. This method automatically determines the image format of the file and uses the appropriate reading routine, whether it resides in the Qt or KDE libraries (for example, libksycoca).

Next, you draw a frame around the image using a green QPen of width 10 (lines 20–22). At this point you could draw anything on the pixmap that QPainter allows. You can't read in pixel color values, however, so you are limited simply to drawing on the image.

Note

If you want to perform an image transformation that requires reading in pixel color values (such as changing the image's brightness or contrast), you need to load the image into a QImage rather than a QPixmap.

To save the file (lines 24 and 25), you use QPixmap::save(). The first argument to this method is the name of the file and the second is the file type, given as a string. The file type strings are given in Tables 10.1a and 10.1b.

Note

The file type string must be all capitals. For example, use PNG, not png or Png.

We've made use here of a Qt class called QFileInfo. This class lets you find a file's name and extension, among other things. We've created a string with


   1 
   2 qfileinfo.baseName() + ".png"
   3 

in line 25 that contains the name of the loaded file, with the extension changed to png. See the Qt reference documentation for details on QFileInfo.

You can use the following main() function to try out KImageView on an image file. You pass the image file's name to the program on the command line. If you compile the program to an executable with the name kimageviewtest and type


   1 
   2 ./kimageview $KDEDIR/share/wallpapers/Marble01.jpg

you will see something similar to Figure 10.2. (The file Marble01.jpg is included with KDE 2.0.)


Example 10.6. main.cpp: A main() Function that Instantiates KImageView and Passes a Filename to It from the Command Line

   1 
   2  1: #include <kapp.h>/
   3  2:
   4  3: #include "kimageview.h"
   5  4:
   6  5: int
   7  6: main (int argc, char *argv[])
   8  7: {
   9  8:   KApplication kapplication (argc, argv, "kimageviewtest");
  10  9:
  11 10:   if (! (argc>1) )
  12 11:     exit (1);
  13 12:   KImageView *kimageview  = new KImageView (argv[1], 0);
  14 13:
  15 14:   kapplication.setMainWidget (kimageview);
  16 15:   kimageview->show();
  17 16:
  18 17:   return kapplication.exec();
  19 18: }
  20 


Figure 10.2. KImageView displaying the KDE wallpaper Marble01.jpg.