]> git.saurik.com Git - wxWidgets.git/blob - src/common/imagpcx.cpp
Avoid unexpected negative array index (CID 41).
[wxWidgets.git] / src / common / imagpcx.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: imagpcx.cpp
3 // Purpose: wxImage PCX handler
4 // Author: Guillermo Rodriguez Garcia <guille@iies.es>
5 // Version: 1.1
6 // CVS-ID: $Id$
7 // Copyright: (c) 1999 Guillermo Rodriguez Garcia
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #ifndef WX_PRECOMP
19 # include "wx/defs.h"
20 # include "wx/palette.h"
21 #endif
22
23 #if wxUSE_IMAGE && wxUSE_PCX
24
25 #include "wx/imagpcx.h"
26 #include "wx/wfstream.h"
27 #include "wx/module.h"
28 #include "wx/log.h"
29 #include "wx/intl.h"
30
31 #include "wx/hash.h"
32 #include "wx/list.h"
33 #include "wx/object.h"
34
35 //-----------------------------------------------------------------------------
36 // wxPCXHandler
37 //-----------------------------------------------------------------------------
38
39 IMPLEMENT_DYNAMIC_CLASS(wxPCXHandler,wxImageHandler)
40
41 #if wxUSE_STREAMS
42
43 //-----------------------------------------------------------------------------
44 // RLE encoding and decoding
45 //-----------------------------------------------------------------------------
46
47 void RLEencode(unsigned char *p, unsigned int size, wxOutputStream& s)
48 {
49 unsigned int data, last, cont;
50
51 // Write 'size' bytes. The PCX official specs say there will be
52 // a decoding break at the end of each scanline, so in order to
53 // force this decoding break use this function to write, at most,
54 // _one_ complete scanline at a time.
55
56 last = (unsigned char) *(p++);
57 cont = 1;
58 size--;
59
60 while (size-- > 0)
61 {
62 data = (unsigned char) *(p++);
63
64 // Up to 63 bytes with the same value can be stored using
65 // a single { cont, value } pair.
66 //
67 if ((data == last) && (cont < 63))
68 {
69 cont++;
70 }
71 else
72 {
73 // need to write a 'counter' byte?
74 if ((cont > 1) || ((last & 0xC0) == 0xC0))
75 s.PutC((char) (cont | 0xC0));
76
77 s.PutC((char) last);
78 last = data;
79 cont = 1;
80 }
81 }
82
83 // write the last one and return;
84 if ((cont > 1) || ((last & 0xC0) == 0xC0))
85 s.PutC((char) (cont | 0xC0));
86
87 s.PutC((char) last);
88 }
89
90 void RLEdecode(unsigned char *p, unsigned int size, wxInputStream& s)
91 {
92 unsigned int i, data, cont;
93
94 // Read 'size' bytes. The PCX official specs say there will be
95 // a decoding break at the end of each scanline (but not at the
96 // end of each plane inside a scanline). Only use this function
97 // to read one or more _complete_ scanlines. Else, more than
98 // 'size' bytes might be read and the buffer might overflow.
99
100 while (size > 0)
101 {
102 data = (unsigned char)s.GetC();
103
104 // If ((data & 0xC0) != 0xC0), then the value read is a data
105 // byte. Else, it is a counter (cont = val & 0x3F) and the
106 // next byte is the data byte.
107
108 if ((data & 0xC0) != 0xC0)
109 {
110 *(p++) = (unsigned char)data;
111 size--;
112 }
113 else
114 {
115 cont = data & 0x3F;
116 data = (unsigned char)s.GetC();
117 for (i = 1; i <= cont; i++)
118 *(p++) = (unsigned char)data;
119 size -= cont;
120 }
121 }
122 }
123
124
125 //-----------------------------------------------------------------------------
126 // PCX reading and saving
127 //-----------------------------------------------------------------------------
128
129 // PCX header
130 #define HDR_MANUFACTURER 0
131 #define HDR_VERSION 1
132 #define HDR_ENCODING 2
133 #define HDR_BITSPERPIXEL 3
134 #define HDR_XMIN 4
135 #define HDR_YMIN 6
136 #define HDR_XMAX 8
137 #define HDR_YMAX 10
138 #define HDR_NPLANES 65
139 #define HDR_BYTESPERLINE 66
140 #define HDR_PALETTEINFO 68
141
142 // image formats
143 enum {
144 wxPCX_8BIT, // 8 bpp, 1 plane (8 bit)
145 wxPCX_24BIT // 8 bpp, 3 planes (24 bit)
146 };
147
148 // error codes
149 enum {
150 wxPCX_OK = 0, // everything was OK
151 wxPCX_INVFORMAT = 1, // error in pcx file format
152 wxPCX_MEMERR = 2, // error allocating memory
153 wxPCX_VERERR = 3 // error in pcx version number
154 };
155
156
157 // ReadPCX:
158 // Loads a PCX file into the wxImage object pointed by image.
159 // Returns wxPCX_OK on success, or an error code otherwise
160 // (see above for error codes)
161 //
162 int ReadPCX(wxImage *image, wxInputStream& stream)
163 {
164 unsigned char hdr[128]; // PCX header
165 unsigned char pal[768]; // palette for 8 bit images
166 unsigned char *p; // space to store one scanline
167 unsigned char *dst; // pointer into wxImage data
168 unsigned int width, height; // size of the image
169 unsigned int bytesperline; // bytes per line (each plane)
170 int bitsperpixel; // bits per pixel (each plane)
171 int nplanes; // number of planes
172 int encoding; // is the image RLE encoded?
173 int format; // image format (8 bit, 24 bit)
174 unsigned int i, j;
175
176 // Read PCX header and check the version number (it must
177 // be at least 5 or higher for 8 bit and 24 bit images).
178
179 stream.Read(hdr, 128);
180
181 if (hdr[HDR_VERSION] < 5) return wxPCX_VERERR;
182
183 // Extract all image info from the PCX header.
184
185 encoding = hdr[HDR_ENCODING];
186 nplanes = hdr[HDR_NPLANES];
187 bitsperpixel = hdr[HDR_BITSPERPIXEL];
188 bytesperline = hdr[HDR_BYTESPERLINE] + 256 * hdr[HDR_BYTESPERLINE + 1];
189 width = (hdr[HDR_XMAX] + 256 * hdr[HDR_XMAX + 1]) -
190 (hdr[HDR_XMIN] + 256 * hdr[HDR_XMIN + 1]) + 1;
191 height = (hdr[HDR_YMAX] + 256 * hdr[HDR_YMAX + 1]) -
192 (hdr[HDR_YMIN] + 256 * hdr[HDR_YMIN + 1]) + 1;
193
194 // Check image format. Currently supported formats are
195 // 8 bits (8 bpp, 1 plane) and 24 bits (8 bpp, 3 planes).
196
197 if ((nplanes == 3) && (bitsperpixel == 8))
198 format = wxPCX_24BIT;
199 else if ((nplanes == 1) && (bitsperpixel == 8))
200 format = wxPCX_8BIT;
201 else
202 return wxPCX_INVFORMAT;
203
204 // If the image is of type wxPCX_8BIT, then there is
205 // a palette at the end of the image data. If we were
206 // working with a file, we could seek at the end to the
207 // end (SeekI(-769, wxFromEnd) and read the palette
208 // before proceeding. Unfortunately, this would prevent
209 // loading several PCXs in a single stream, so we can't
210 // do it. Thus, 8-bit images will have to be decoded in
211 // two passes: one to read and decode the image data,
212 // and another to replace 'colour indexes' with RGB
213 // values.
214
215 // Resize the image and allocate memory for a scanline.
216
217 image->Create(width, height);
218
219 if (!image->Ok())
220 return wxPCX_MEMERR;
221
222 if ((p = (unsigned char *) malloc(bytesperline * nplanes)) == NULL)
223 return wxPCX_MEMERR;
224
225 // Now start reading the file, line by line, and store
226 // the data in the format required by wxImage.
227
228 dst = image->GetData();
229
230 for (j = height; j; j--)
231 {
232 if (encoding)
233 RLEdecode(p, bytesperline * nplanes, stream);
234 else
235 stream.Read(p, bytesperline * nplanes);
236
237 switch (format)
238 {
239 case wxPCX_8BIT:
240 {
241 for (i = 0; i < width; i++)
242 {
243 // first pass, just store the colour index
244 *dst = p[i];
245 dst += 3;
246 }
247 break;
248 }
249 case wxPCX_24BIT:
250 {
251 for (i = 0; i < width; i++)
252 {
253 *(dst++) = p[i];
254 *(dst++) = p[i + bytesperline];
255 *(dst++) = p[i + 2 * bytesperline];
256 }
257 break;
258 }
259 }
260 }
261
262 free(p);
263
264 // For 8 bit images, we read the palette, and then do a second
265 // pass replacing indexes with their RGB values;
266
267 if (format == wxPCX_8BIT)
268 {
269 unsigned char index;
270
271 if (stream.GetC() != 12)
272 return wxPCX_INVFORMAT;
273
274 stream.Read(pal, 768);
275
276 p = image->GetData();
277 for (unsigned long k = height * width; k; k--)
278 {
279 index = *p;
280 *(p++) = pal[3 * index];
281 *(p++) = pal[3 * index + 1];
282 *(p++) = pal[3 * index + 2];
283 }
284
285 #if wxUSE_PALETTE
286 unsigned char r[256];
287 unsigned char g[256];
288 unsigned char b[256];
289 for (i = 0; i < 256; i++)
290 {
291 r[i] = pal[3*i + 0];
292 g[i] = pal[3*i + 1];
293 b[i] = pal[3*i + 2];
294 }
295 image->SetPalette(wxPalette(256, r, g, b));
296 #endif // wxUSE_PALETTE
297 }
298
299 return wxPCX_OK;
300 }
301
302 // SavePCX:
303 // Saves a PCX file into the wxImage object pointed by image.
304 // Returns wxPCX_OK on success, or an error code otherwise
305 // (see above for error codes). Will try to save as 8-bit
306 // PCX if possible, and then fall back to 24-bit if there
307 // are more than 256 different colours.
308 //
309 int SavePCX(wxImage *image, wxOutputStream& stream)
310 {
311 unsigned char hdr[128]; // PCX header
312 unsigned char pal[768]; // palette for 8 bit images
313 unsigned char *p; // space to store one scanline
314 unsigned char *src; // pointer into wxImage data
315 unsigned int width, height; // size of the image
316 unsigned int bytesperline; // bytes per line (each plane)
317 unsigned char nplanes = 3; // number of planes
318 int format = wxPCX_24BIT; // image format (8 bit, 24 bit)
319 wxImageHistogram histogram; // image histogram
320 unsigned long key; // key in the hashtable
321 unsigned int i;
322
323 // See if we can save as 8 bit.
324
325 if (image->CountColours(256) <= 256)
326 {
327 image->ComputeHistogram(histogram);
328 format = wxPCX_8BIT;
329 nplanes = 1;
330 }
331
332 // Get image dimensions, calculate bytesperline (must be even,
333 // according to PCX specs) and allocate space for one complete
334 // scanline.
335
336 if (!image->Ok())
337 return wxPCX_INVFORMAT;
338
339 width = image->GetWidth();
340 height = image->GetHeight();
341 bytesperline = width;
342 if (bytesperline % 2)
343 bytesperline++;
344
345 if ((p = (unsigned char *) malloc(bytesperline * nplanes)) == NULL)
346 return wxPCX_MEMERR;
347
348 // Build header data and write it to the stream. Initially,
349 // set all bytes to zero (most values default to zero).
350
351 memset(hdr, 0, sizeof(hdr));
352
353 hdr[HDR_MANUFACTURER] = 10;
354 hdr[HDR_VERSION] = 5;
355 hdr[HDR_ENCODING] = 1;
356 hdr[HDR_NPLANES] = nplanes;
357 hdr[HDR_BITSPERPIXEL] = 8;
358 hdr[HDR_BYTESPERLINE] = (unsigned char)(bytesperline % 256);
359 hdr[HDR_BYTESPERLINE + 1] = (unsigned char)(bytesperline / 256);
360 hdr[HDR_XMAX] = (unsigned char)((width - 1) % 256);
361 hdr[HDR_XMAX + 1] = (unsigned char)((width - 1) / 256);
362 hdr[HDR_YMAX] = (unsigned char)((height - 1) % 256);
363 hdr[HDR_YMAX + 1] = (unsigned char)((height - 1) / 256);
364 hdr[HDR_PALETTEINFO] = 1;
365
366 stream.Write(hdr, 128);
367
368 // Encode image data line by line and write it to the stream
369
370 src = image->GetData();
371
372 for (; height; height--)
373 {
374 switch (format)
375 {
376 case wxPCX_8BIT:
377 {
378 unsigned char r, g, b;
379
380 for (i = 0; i < width; i++)
381 {
382 r = *(src++);
383 g = *(src++);
384 b = *(src++);
385 key = (r << 16) | (g << 8) | b;
386
387 p[i] = (unsigned char)histogram[key].index;
388 }
389 break;
390 }
391 case wxPCX_24BIT:
392 {
393 for (i = 0; i < width; i++)
394 {
395 p[i] = *(src++);
396 p[i + bytesperline] = *(src++);
397 p[i + 2 * bytesperline] = *(src++);
398 }
399 break;
400 }
401 }
402
403 RLEencode(p, bytesperline * nplanes, stream);
404 }
405
406 free(p);
407
408 // For 8 bit images, build the palette and write it to the stream:
409 if (format == wxPCX_8BIT)
410 {
411 // zero unused colours
412 memset(pal, 0, sizeof(pal));
413
414 unsigned long index;
415
416 for (wxImageHistogram::iterator entry = histogram.begin();
417 entry != histogram.end(); ++entry )
418 {
419 key = entry->first;
420 index = entry->second.index;
421 pal[3 * index] = (unsigned char)(key >> 16);
422 pal[3 * index + 1] = (unsigned char)(key >> 8);
423 pal[3 * index + 2] = (unsigned char)(key);
424 }
425
426 stream.PutC(12);
427 stream.Write(pal, 768);
428 }
429
430 return wxPCX_OK;
431 }
432
433 //-----------------------------------------------------------------------------
434 // wxPCXHandler
435 //-----------------------------------------------------------------------------
436
437 bool wxPCXHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) )
438 {
439 int error;
440
441 if (!CanRead(stream))
442 {
443 if (verbose)
444 wxLogError(_("PCX: this is not a PCX file."));
445
446 return false;
447 }
448
449 image->Destroy();
450
451 if ((error = ReadPCX(image, stream)) != wxPCX_OK)
452 {
453 if (verbose)
454 {
455 switch (error)
456 {
457 case wxPCX_INVFORMAT: wxLogError(_("PCX: image format unsupported")); break;
458 case wxPCX_MEMERR: wxLogError(_("PCX: couldn't allocate memory")); break;
459 case wxPCX_VERERR: wxLogError(_("PCX: version number too low")); break;
460 default: wxLogError(_("PCX: unknown error !!!"));
461 }
462 }
463 image->Destroy();
464 return false;
465 }
466
467 return true;
468 }
469
470 bool wxPCXHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
471 {
472 int error;
473
474 if ((error = SavePCX(image, stream)) != wxPCX_OK)
475 {
476 if (verbose)
477 {
478 switch (error)
479 {
480 case wxPCX_INVFORMAT: wxLogError(_("PCX: invalid image")); break;
481 case wxPCX_MEMERR: wxLogError(_("PCX: couldn't allocate memory")); break;
482 default: wxLogError(_("PCX: unknown error !!!"));
483 }
484 }
485 }
486
487 return (error == wxPCX_OK);
488 }
489
490 bool wxPCXHandler::DoCanRead( wxInputStream& stream )
491 {
492 unsigned char c = stream.GetC();
493 if ( !stream )
494 return false;
495
496 // not very safe, but this is all we can get from PCX header :-(
497 return c == 10;
498 }
499
500 #endif // wxUSE_STREAMS
501
502 #endif // wxUSE_IMAGE && wxUSE_PCX
503