You'll find a short tutorial on how to include paintlib functions in your programs on this page. If you haven't gotten paintlib and the sample programs compiled yet, read the installing page first.

[Note for MSVC users: The place most people seem to get stuck in first is in the project settings. The best thing to do when you get obscure compiler and linker errors is probably to compare the project settings of your project with the settings in the sample projects. In particular, make sure paintlib and your program are being built with the same runtime library version (static or dynamic? multi- or single-threaded?) and use the same alignment settings.]

Decoding an Image

In the simplest case, what you need to do to use paintlib is this:

  #include "anydec.h"
  #include "winbmp.h"

  // Create a decoder.
  PLAnyPicDecoder Decoder;

  // Create a bitmap object.
  PLWinBmp Bmp;

  // Tell the decoder to fill
  // the bitmap object with data.
  Decoder.MakeBmpFromFile ("foo.jpg", &Bmp);

  // Do something with the bitmap.
  Bmp.Draw (pdc, 0, 0);

These four lines are enough to load a bitmap in any of the supported formats from disk and draw it to a windows device context. The MakeBmpFromFile() call auto-detects the file type and gives you a 32 bpp bitmap containing the image stored in the file - including alpha channel if there is one. (The example is for windows because there's an easy "Do something with the bitmap" step in this case. The fragment also crashes if an error occurs while decoding. I'll get to the rest in a minute.)

Of course, you can use one decoder to decode several bitmap objects. It makes sense to create a decoder during program initialization and use it until the program terminates. The decoder class is not thread-safe - you can't use one decoder to decode several images at once. If several threads need to decode images, it's best to create a decoder in each thread.

When you create the bitmap object, you're also selecting the storage format for the bitmap. Let me explain this. The decoder takes a PLBmp as parameter to MakeBmpFromFile. Because PLBmp is an abstract base class, there can be no objects of the class. It exists mainly to define an interface for derived classes. Whenever it needs to store data, it asks the bitmap object for the address to place it. So if you create a PLWinBmp and pass it to MakeBmpFromFile, the storage format is a windows DIB (Device Independent Bitmap). We could also have passed a PLAnyBmp and stored the data in an OS-independent format. There's a third bitmap class - PLDIBSection - for use when you want to use a windows DIB section. If you're using DirectX, DirectFB or libSDL, you can use PLDDrawBmp to load bitmaps directly into the surfaces that these libraries use.

Needless to say that you can also define your own bitmap storage formats by deriving from PLBmp. (If all this doesn't make sense, ignore what I've just said. Just remember that you can safely pass PLWinBmps, PLAnyBmps, or objects of any other PLBmp-derived class to MakeBmpFromFile() and the bitmap data will arrive in the object in the format defined by the class. ;-)

Sources for image data

Image data can come from a file, a url (e.g. a http or ftp address) or a memory region. Under windows, it can also come from a resource file (see below). The calls look like this:

  Decoder.MakeBmpFromURL ("http://www.libavg.de/foo.jpg", &Bmp);  
  Decoder.MakeBmpFromMemory (pMemRegion, MemRegionSize, &Bmp);
        

Manipulating bitmaps

Once you've decoded the bitmaps, you'll want to do something with them. If you're coding for windows and using PLWinBmp, you can do whatever DIBs (Device Independent Bitmaps) are capable of. PLWinBmp defines methods to draw the bitmap to a device context and to copy it to the clipboard. You can also load PLWinBmps from resources. The class PLDIBSection derives from PLWinBmp and has the ability to return a GDI bitmap handle in addition to the features of PLWinBmp. This means you can use GDI drawing functions to change the contents of a PLDIBSection.

If you're working with DirectFB or SDL, you can query a PLDirectFBBmp or a PLSDLBmp for a handle to the associated surface and use that surface as you would use any other DirectFB/SDL surface.

In addition, paintlib defines some filters that manipulate bitmaps. For example, to resize a bitmap, use this:

  PLFilterResizeBilinear Filter (XSize, YSize);
  Filter.ApplyInPlace (pBmp);   // Replace with Apply(pSrc, pDst) if you want to
                                // keep the original bitmap.

or:

  pBmp->ApplyFilter (PLFilterResizeBilinear (XSize, YSize));

Using filters allows you to do things like dynamically creating arrays of filters that you then apply to many bitmaps in succession etc. It also allows for simple undo/redo mechanisms: Keep all the filters you've applied to a bitmap in an array. When you need to undo, just reapply all but the last filter to the original image. (The test class PLTester takes advantage of this design at several points. Have a look.)

If you're interested in manipulating the data yourself, this is what you can do:

  // Assuming a 32 bpp bitmap.
  PLPixel32 ** pLineArray;
  int x,y;
  PLPixel32  * pLine;

  // Get the addresses of the bitmap lines
  pLineArray = pBmp->GetLineArray32();

  // Iterate through the lines
  for (y=0; y<pBmp->GetHeight(); y++)
  {
    pLine = pLineArray[y];

    // Iterate through the pixels
    for (x=0; x<pBmp->GetWidth(); x++)
    {
      // Set the red intensity to maximum.
      // (replace this with what you really
      // want to do...)
      pLine[x].SetR(0xFFh);
    }
  }

pBmp points to an object of a PLBmp-derived class. pBmp->GetLineArray32() returns an array of pointers to the bitmap lines. Once you have the array, you can iterate through the lines at maximum speed. The class PLPixel32 allows basic operations on pixels like setting and getting components, testing for equality and assigning one pixel to another. There are similar classes for 24 and 8 bpp pixels and corresponding GetLineArray() functions. The order of the components of each pixel in 32 and 24 bpp mode is defined by the constants PL_RGBA_BLUE, PL_RGBA_GREEN, PL_RGBA_RED, and PL_RGBA_ALPHA. 32 bpp bitmaps may or may not have valid data in the alpha channel. You can check this by calling pBmp->HasAlpha() and change the value by calling SetAlphaChannel() or SetHasAlpha(). You can copy bitmaps and convert them to different bit depths by calling CreateCopy() or the assignment operator. In 8 bpp mode, the color of each pixel is determined by the palette of the bitmap. The functions SetPalette(), SetGrayPalette() and SetPaletteEntry() allow you to set the color table.

There are several additional functions to get information about the bitmap: GetWidth(), GetHeight(), GetBitsPerPixel(), GetBytesPerLine(), GetMemUsed(), GetNumColors() and GetResolution(). The resolution (in dpi) can also be set using SetResolution() - this doesn't resample the image, it just allows programs to be smart about how big the image is supposed to be displayed or printed.

These informational functions can also be called on the decoder after a bitmap has been opened, but before it's been loaded :

    Decoder.OpenFile ("test.bmp");
    cerr << "Width: " << Decoder.GetWidth() << endl;
    Decoder.MakeBmp(&Bmp);
    Decoder.Close();

Note that implementing a filter isn't much more work than manipulating the data directly. What you need to do is create a new class derived from PLFilter, place the actual manipulation code in Apply() and code a constructor. The constructor takes any parameters that your algorithm needs and places them in member variables that Apply() can use. In general, the destructor will be empty.

Saving an image

Easy:

  PLJPEGEncoder Encoder;
  Encoder.MakeFileFromBmp(sFName, pBmp);

PLJPEGEncoder also has some functions for setting image quality that you can call before calling MakeFileFromBmp().

You can also use a PLPNGEncoder if you want to save in png format, a PLBMPEncoder to save in windows bmp format or a PLTIFFEncoder if you want to save in tiff format. PLTIFFEncoderEx provides advanced support for writing your own tiff tags.

Error Handling and Debugging

If you defined _DEBUG when you built the library, it was compiled to contain trace statements and asserts to help you debugging. In addition, debug info is included and optimizations are disabled. Trace statements give diagnostic output to a suitable medium. For MSVC, output is sent either to the MSVC debug log or to a file. Other systems send output to stderr or a file. The amount of information output and the destination can be changed by calling PLAnyPicDecoder::SetTraceConfig (Level, pszFName).

In addition to enabling trace output, defining _DEBUG also enables internal sanity checks and parameter validation via asserts. If an assertion fails, the program will stop execution and print out information about the file and line number causing the failure. Usually, there are comments at these places in the code to help you sort things out.

Errors which you can potentially recover from are handled by the C++ exception mechanism. Here is sample code including error-handling:

  try
  {
    Decoder.MakeBmpFromFile ("foo.jpg",
                             &Bmp);
  }
  catch (PLTextException e)
  {
    cerr << "Error " << e.GetCode() << " decoding foo.jpg: " << e << endl;
  }

If no error occurs, this code just amounts to one call to MakeBmpFromFile(). If something goes wrong, execution jumps to the catch block. In this block, you have a variable of type PLTextException available which provides you with an error code and a descriptive error string. The error codes are explained in the reference section.

Photoshop layer support

While the paintlib photoshop image (psd) decoder can be used to just get the composite image like the other decoders, it can also be used to load each of the layers in a multi-layer image into a separate PLBmp:

  PLPSDDecoder Decoder;
  Decoder.OpenFile (sFilePos.c_str());
  int n = Decoder.GetNumLayers ();
  vector<PLAnyBmp*> LayerBmps;
  for (int i = 0; i<n; i++)
  {
    LayerBmps.push_back(new PLAnyBmp);
    Decoder.GetNextLayer (*(LayerBmp[i]));
    PLPoint Pt = Decoder.GetLayerOffset ();
  }

EXIF handling

EXIF (digital camera) data is made up of a number of Tags. Each tag has a description and a value which may take many different forms. The PLExifTag class also provides a short name which is more efficient and convenient to use for identifying a tag (unfortunately once you get to the manufacturer spcific tags the tag number is no longer a unique identifier on its own). The PLExif class contains all the EXIF data and provides access to arrays of pointers to the tags or access to the values of any one individual tag. The PLExifTag class provides access to the description and value of one tag.

Whilst the interface to these two classes does use the C++ Standard Template Library vector and string classes, alternate methods have generally been provided (where possible) that just use arrays and char * for the strings.

The following code loads a jpeg and reads the model info:

    Decoder.OpenFile (sCompleteFName.c_str());
    PLExif ExifData;
    Decoder.GetExifData(ExifData);
    const PLExifTagList & ev = ExifData.GetAllTags();
    Test(ev.size()==NumTags);
    string sMake;
    ExifData.GetTagCommon("Main.Make", sMake);
    Decoder.MakeBmp(&Bmp);
    Decoder.Close();

To save a jpeg containing EXIF data, you need to do the following:

    Encoder.SetExifData(ExifData);
    Encoder.MakeFileFromBmp("test.jpg", &Bmp);

A list of all EXIF tags known to paintlib is at exiflist.txt.

The paintlib EXIF reference ist at exifref.txt.

Windows-specific Stuff

If you've got a bitmap handle and a corresponding device context and you need a PLWinBmp, do this:

  PLWinBmp * pBmp;
  pBmp->Create (Width, Height, bpp, FALSE);
  GetDIBits( hdc, hBmp, 0, (WORD) pBmp->GetHeight(),
             pBmp->GetBits(), pBmp->GetBMI(),
             DIB_RGB_COLORS);

Loading a picture (in any format) from a resource is also possible. Just place a resource of type RCDATA into the .rc file of the program and load the resource as a picture by calling

  MakeBmpFromResource(IDR_PICTURE, &Bmp)
The line in the .rc file should look like this:
  IDR_PICTURE  RCDATA  DISCARDABLE  "picture.jpg"
You will also need to define IDR_PICTURE in resource.h (or wherever resource IDs are defined in your project). Under MSVC, adding a user-defined resource to a project requires a bit of magic. What you need to do (this isver 6.0) is:
  1. Insert the file "res\projektname.rc2" in "View/Resource Includes".
  2. Insert the resource reference in projectname.rc2 (the file already exists).
  3. Add the resource ID using "View/Resource Symbols" to resource.h.

For a real-life example of all this, see the sample programs.