You'll find a short tutorial on how to include paintlib functions in your programs on this page.

[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 MFC 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.
CAnyPicDecoder Decoder;

// Create a bitmap object.
CWinBmp Bmp;

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

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

Look at the code for a moment. It's not complicated. (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, so 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 CBmp as parameter to MakeBmpFromFile. Because CBmp 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 CWinBmp and pass it to MakeBmpFromFile, the storage format is a windows DIB (Device Independent Bitmap). We could also have passed a CAnyBmp and stored the data in an OS-independent format. There's a third bitmap class - CDIBSection - for use when you want to use a windows DIB section and if you're using DirectX, you can use CDDrawBmp to load bitmaps directly into DirectDraw surfaces.

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

The last parameter to MakeBmpFromFile is the destination bits per pixel you want. It defaults to 0, which means "just take what's in the file". All colormapped formats will be decoded to 8 bpp in this case, and formats with 15 or more bpp will be decoded to 32 bpp. (With the exception of 1 bpp tiff files, which are returned as 1 bpp.) If you specify 32 for this parameter, the decoded bitmap is promoted to true-color if necessary. Specifying 8 causes the decoder to report an error if the file contains more than 8 bpp.

Manipulating bitmaps

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

There are several methods defined in CBmp for changing the bitmap data. ResizeBilinear (w,h) does a medium-quality resize on the image. ResizeBox (w,h,r) and ResizeGaussian (w,h,r) blur the image (and resize while they're at it). The Crop (), MakeGrayscale () and Rotate () functions do what you would expect of them. There are also corresponding CreateXxx() functions that allow you to preserve the old image.

Doing the actual work behind the scenes are filter classes - one for each function. You can also use these filter classes directly:

  CFilterRotate Filter (angle, color);
  Filter.ApplyInPlace (pBmp);   // Replace with Apply(pSrc, pDst) if you want to
                                // keep the original bitmap.  

While more verbose, this method 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. This is a case of the Command design pattern from Gamma et al.'s design pattern book. (The test program 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:

BYTE ** pLineArray;
int     x,y;
BYTE  * pLine;

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

// 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...)
    // I'm assuming a 32 bpp bitmap here.
    pLine[x*4+RGBA_RED] = 0xFFh;
  }
}

pBmp points to an object of a CBmp-derived class. pBmp->GetLineArray() returns an array of pointers to the bitmap lines. Once you have the array, you can iterate through the lines at maximum speed. If you've decoded the bitmap with paintlib, the lines are organized in either 1-byte or 4-byte pixels. The order of the components of each pixel in 32 bpp mode is defined by the constants RGBA_BLUE, RGBA_GREEN, RGBA_RED, and RGBA_ALPHA. In 8 bpp mode, the color of each pixel is determined by the palette of the bitmap.

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

Saving an image

Easy:

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

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

You can also use a CPNGEncoder if you want to save in png format, a CBMPEncoder to save in windows bmp format or a CTIFFEncoder if you want to save in tiff format. CTIFFEncoderEx 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 CAnyPicDecoder::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. 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 (CTextException e)
{
  fprintf (stderr, 
           "Error %d decoding foo.jpg: %s",
           e.GetCode(),
           (const char *)e);
}

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 CTextException available which provides you with an error code and a descriptive error string. The error codes are explained in the reference section. (IMHO, exceptions are a very powerful C++ feature. If you don't know how they work, you can either work off the code above or learn how to use them effectively. I recommend the latter.)

Windows Stuff

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

CWinBmp * 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 is a bit weird. What you need to do 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. Be warned, however, that the sample code is messy in some places.