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