1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
|
<HTML>
<HEAD>
<TITLE>paintlib - C++ - Tutorial</TITLE>
<meta name="robots" content="noindex">
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000C0" VLINK="#8000FF" ALINK="#FF00FF">
<accessed silent>
<table width="350" border="0" cellspacing="0" cellpadding="0" align=right>
<tr>
<td>
<img src="pics/cpptutorial.gif" width=350 height=60 border=0
alt="" hspace=0 vspace=0>
<img src="pics/whitept.gif" width=31 height=21 hspace=0 vspace=0 border=0 alt="">
</td>
</tr>
</table>
<br clear=all>
<p align=left>
<table cellspacing="0" cellpadding="0" border="0">
<tr>
<td width="22">
<img src="pics/whitept.gif" width=21 height=1 hspace=0 vspace=0 border=0 alt="">
</td>
<td>
<P> 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.
<P> [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.]
<P><b>Decoding an Image</b>
<P>
In the simplest case, what you need to do to use paintlib is this:
<PRE>
#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);
</PRE>
<P>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.)
<P> 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.
<P> 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.
<P> 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.
;-)
<P> <b>Sources for image data</b>
<P>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:
<pre>
Decoder.MakeBmpFromURL ("http://www.libavg.de/foo.jpg", &Bmp);
Decoder.MakeBmpFromMemory (pMemRegion, MemRegionSize, &Bmp);
</pre>
<P> <b>Manipulating bitmaps</b>
<P>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.
<P>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.
<P>In addition, paintlib defines some filters that manipulate bitmaps. For
example, to resize a bitmap, use this:
<pre> PLFilterResizeBilinear Filter (XSize, YSize);
Filter.ApplyInPlace (pBmp); // Replace with Apply(pSrc, pDst) if you want to
// keep the original bitmap.
</pre>
<p>or:</p>
<pre> pBmp->ApplyFilter (PLFilterResizeBilinear (XSize, YSize));</pre>
<p>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.)</p>
<p>If you're interested in manipulating the data yourself, this is what
you can do: </p>
<PRE> // 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);
}
}
</PRE>
<P> 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 16 bpp pixels and corresponding GetLineArray() functions.
The order of the components of each pixel in 32, 24 and 16 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.
<P>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.
<P>These informational functions can also be called on the decoder after
a bitmap has been opened, but before it's been loaded :
<pre> Decoder.OpenFile ("test.bmp");
cerr << "Width: " << Decoder.GetWidth() << endl;
Decoder.MakeBmp(&Bmp);
Decoder.Close();
</pre>
<P>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.
<P><b>Saving an image</b>
<P>Easy:
<pre> PLJPEGEncoder Encoder;
Encoder.MakeFileFromBmp(sFName, pBmp);
</pre>
<P>PLJPEGEncoder also has some functions for setting image quality that
you can call before calling MakeFileFromBmp().
<P>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.
<P><b>Manipulating pixel data in arbitrary memory regions</b>
<P>If you have pixel data in a memory region, you can tell paintlib to treat
that memory region almost as if it were a PLBmp:
<pre> unsigned char * pData; // We've got 320x240 pixels with 24 bpp stored here ;-).
PLSubBmp Bmp;
Bmp.Create(320, 240, 24, false, pData, 320*3);
PLJPEGEncoder Encoder;
Encoder.MakeFileFromBmp(sFName, pBmp);
</pre>
<P>The last parameter to Create is the stride - the distance in bytes
from one line to the next. The stride allows you to specify rectangular
subsections of a bitmap or bitmaps with strange alignments, upside-down
bitmaps, etc. as memory region.
<P>To be precise, you can do anything with a PLSubBmp that you can do with a
PLBmp except operations that might cause a reallocation of the bitmap
memory - since the PLSubBmp doesn't own the memory, it can't reallocate
it. This means that resizing it or changing the bit depth is impossible.
Of course, it also means that if the bitmap data is deleted while the
PLSubBmp still exists, operations on the bitmap will cause memory
corruption. For this reason, I recommend keeping PLSubBmps in local variables -
not in members where managing the lifetime will become a hassle.
<P>On the other hand, this is a powerful mechanism if you've got lots of
data coming from sources other than paintlib.
<P>You can also use a rectangle in another PLBmp (or another PLSubBmp,
for that matter) as a memory region for a PLSubBmp by using the second
version of create:
<pre> Bmp.Create(SrcBmp, PLRect(10,10,50,50));
</pre>
<P>Using this allows you to apply bitmap operations to rectangles in bitmaps.
<P><b>Error Handling and Debugging </b>
<P>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).
<P>
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.
<P>
Errors which you can potentially recover from are handled by the
C++ exception mechanism. Here is sample code including error-handling:
<PRE>
try
{
Decoder.MakeBmpFromFile ("foo.jpg",
&Bmp);
}
catch (PLTextException e)
{
cerr << "Error " << e.GetCode() << " decoding foo.jpg: " << e << endl;
}
</PRE>
<P> 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.
<P><b>Photoshop layer support</b>
<P>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:
<pre> 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 ();
}
</pre>
<P><b>EXIF handling</b>
<p>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.
<p>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.
<p>The following code loads a jpeg and reads the model info:
<pre> 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();</pre>
<p>To save a jpeg containing EXIF data, you need to do the following:
<pre> Encoder.SetExifData(ExifData);
Encoder.MakeFileFromBmp("test.jpg", &Bmp);</pre>
<p>A list of all EXIF tags known to paintlib is at <a href="exiflist.txt">exiflist.txt</a>.
<p>The paintlib EXIF reference ist at <a href="exifref.txt">exifref.txt</a>.
<P><b>Windows-specific Stuff</b>
<P>If you've got a bitmap handle and a corresponding device context and
you need a PLWinBmp, do this:
<pre> PLWinBmp * pBmp;
pBmp->Create (Width, Height, bpp, FALSE);
GetDIBits( hdc, hBmp, 0, (WORD) pBmp->GetHeight(),
pBmp->GetBits(), pBmp->GetBMI(),
DIB_RGB_COLORS);
</pre>
<P>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
<PRE>
MakeBmpFromResource(IDR_PICTURE, &Bmp)
</PRE>
The line in the .rc file should look like this:
<PRE>
IDR_PICTURE RCDATA DISCARDABLE "picture.jpg"
</PRE>
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:
<OL>
<LI> Insert the file "res\projektname.rc2" in "View/Resource Includes".
<LI> Insert the resource reference in projectname.rc2 (the file
already exists).
<LI> Add the resource ID using "View/Resource Symbols" to resource.h.
</OL>
<P> For a real-life example of all this, see the sample programs. <br>
</td>
<td width="22">
<img src="pics/whitept.gif" width=21 height=1 hspace=0 vspace=0 border=0 alt="">
</td>
</tr>
</table>
</BODY>
</HTML>
|