]> git.saurik.com Git - wxWidgets.git/blame - src/common/imagpcx.cpp
don't set minimal column width, it should be possible to set it even to 0 if desired...
[wxWidgets.git] / src / common / imagpcx.cpp
CommitLineData
56c66755
GRG
1/////////////////////////////////////////////////////////////////////////////
2// Name: imagpcx.cpp
3// Purpose: wxImage PCX handler
4// Author: Guillermo Rodriguez Garcia <guille@iies.es>
0bddb3cc 5// Version: 1.1
4df78dc3
GRG
6// CVS-ID: $Id$
7// Copyright: (c) 1999 Guillermo Rodriguez Garcia
65571936 8// Licence: wxWindows licence
56c66755
GRG
9/////////////////////////////////////////////////////////////////////////////
10
56c66755
GRG
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
15#pragma hdrstop
16#endif
17
b9b32d5c
GRG
18#ifndef WX_PRECOMP
19# include "wx/defs.h"
ed39ff57 20# include "wx/palette.h"
b9b32d5c
GRG
21#endif
22
e30285ab 23#if wxUSE_IMAGE && wxUSE_PCX
b9b32d5c 24
8f493002 25#include "wx/imagpcx.h"
56c66755
GRG
26#include "wx/wfstream.h"
27#include "wx/module.h"
28#include "wx/log.h"
f6b77239 29#include "wx/intl.h"
1044a386 30
0848b0dd
GRG
31#include "wx/hash.h"
32#include "wx/list.h"
33#include "wx/object.h"
34
e30285ab
VZ
35//-----------------------------------------------------------------------------
36// wxPCXHandler
37//-----------------------------------------------------------------------------
38
39IMPLEMENT_DYNAMIC_CLASS(wxPCXHandler,wxImageHandler)
40
41#if wxUSE_STREAMS
42
4df78dc3 43//-----------------------------------------------------------------------------
d32548aa 44// RLE encoding and decoding
4df78dc3
GRG
45//-----------------------------------------------------------------------------
46
528dad23 47void RLEencode(unsigned char *p, unsigned int size, wxOutputStream& s)
4df78dc3 48{
528dad23
GRG
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
0bddb3cc
GRG
64 // Up to 63 bytes with the same value can be stored using
65 // a single { cont, value } pair.
528dad23
GRG
66 //
67 if ((data == last) && (cont < 63))
68 {
69 cont++;
70 }
71 else
72 {
0bddb3cc 73 // need to write a 'counter' byte?
528dad23
GRG
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
0bddb3cc 83 // write the last one and return;
528dad23
GRG
84 if ((cont > 1) || ((last & 0xC0) == 0xC0))
85 s.PutC((char) (cont | 0xC0));
86
87 s.PutC((char) last);
4df78dc3
GRG
88}
89
90void 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.
0bddb3cc 99
4df78dc3
GRG
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.
7beb59f3 107
4df78dc3
GRG
108 if ((data & 0xC0) != 0xC0)
109 {
33ac7e6f 110 *(p++) = (unsigned char)data;
4df78dc3
GRG
111 size--;
112 }
113 else
114 {
115 cont = data & 0x3F;
116 data = (unsigned char)s.GetC();
117 for (i = 1; i <= cont; i++)
33ac7e6f 118 *(p++) = (unsigned char)data;
4df78dc3
GRG
119 size -= cont;
120 }
121 }
122}
123
124
d32548aa
GRG
125//-----------------------------------------------------------------------------
126// PCX reading and saving
127//-----------------------------------------------------------------------------
128
129// PCX header
528dad23 130#define HDR_MANUFACTURER 0
995612e2 131#define HDR_VERSION 1
4df78dc3
GRG
132#define HDR_ENCODING 2
133#define HDR_BITSPERPIXEL 3
134#define HDR_XMIN 4
135#define HDR_YMIN 6
995612e2 136#define HDR_XMAX 8
4df78dc3
GRG
137#define HDR_YMAX 10
138#define HDR_NPLANES 65
139#define HDR_BYTESPERLINE 66
528dad23 140#define HDR_PALETTEINFO 68
4df78dc3 141
e4b8154a
GRG
142// image formats
143enum {
144 wxPCX_8BIT, // 8 bpp, 1 plane (8 bit)
145 wxPCX_24BIT // 8 bpp, 3 planes (24 bit)
146};
147
148// error codes
149enum {
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};
4df78dc3 155
faa7a70e 156
4df78dc3
GRG
157// ReadPCX:
158// Loads a PCX file into the wxImage object pointed by image.
e4b8154a
GRG
159// Returns wxPCX_OK on success, or an error code otherwise
160// (see above for error codes)
4df78dc3
GRG
161//
162int 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)
51dba3f8 174 unsigned int i, j;
4df78dc3
GRG
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).
0bddb3cc 178
4df78dc3
GRG
179 stream.Read(hdr, 128);
180
e4b8154a 181 if (hdr[HDR_VERSION] < 5) return wxPCX_VERERR;
4df78dc3
GRG
182
183 // Extract all image info from the PCX header.
0bddb3cc 184
4df78dc3
GRG
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).
0bddb3cc 196
4df78dc3 197 if ((nplanes == 3) && (bitsperpixel == 8))
e4b8154a 198 format = wxPCX_24BIT;
4df78dc3 199 else if ((nplanes == 1) && (bitsperpixel == 8))
e4b8154a 200 format = wxPCX_8BIT;
4df78dc3 201 else
e4b8154a 202 return wxPCX_INVFORMAT;
4df78dc3 203
51dba3f8
GRG
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.
4df78dc3 214
51dba3f8 215 // Resize the image and allocate memory for a scanline.
0bddb3cc 216
4df78dc3
GRG
217 image->Create(width, height);
218
219 if (!image->Ok())
e4b8154a 220 return wxPCX_MEMERR;
4df78dc3
GRG
221
222 if ((p = (unsigned char *) malloc(bytesperline * nplanes)) == NULL)
e4b8154a 223 return wxPCX_MEMERR;
4df78dc3
GRG
224
225 // Now start reading the file, line by line, and store
226 // the data in the format required by wxImage.
0bddb3cc 227
4df78dc3
GRG
228 dst = image->GetData();
229
51dba3f8 230 for (j = height; j; j--)
4df78dc3
GRG
231 {
232 if (encoding)
233 RLEdecode(p, bytesperline * nplanes, stream);
234 else
235 stream.Read(p, bytesperline * nplanes);
236
237 switch (format)
238 {
e4b8154a 239 case wxPCX_8BIT:
4df78dc3
GRG
240 {
241 for (i = 0; i < width; i++)
242 {
51dba3f8
GRG
243 // first pass, just store the colour index
244 *dst = p[i];
245 dst += 3;
4df78dc3
GRG
246 }
247 break;
248 }
e4b8154a 249 case wxPCX_24BIT:
4df78dc3
GRG
250 {
251 for (i = 0; i < width; i++)
252 {
253 *(dst++) = p[i];
254 *(dst++) = p[i + bytesperline];
995612e2 255 *(dst++) = p[i + 2 * bytesperline];
4df78dc3
GRG
256 }
257 break;
258 }
259 }
260 }
261
262 free(p);
263
51dba3f8
GRG
264 // For 8 bit images, we read the palette, and then do a second
265 // pass replacing indexes with their RGB values;
0bddb3cc 266
51dba3f8
GRG
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 }
3f4fc796 284
b11e8fb6
VZ
285#if wxUSE_PALETTE
286 unsigned char r[256];
287 unsigned char g[256];
288 unsigned char b[256];
3f4fc796
JS
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));
b11e8fb6 296#endif // wxUSE_PALETTE
51dba3f8
GRG
297 }
298
e4b8154a 299 return wxPCX_OK;
4df78dc3
GRG
300}
301
528dad23
GRG
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
0bddb3cc
GRG
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.
528dad23
GRG
308//
309int SavePCX(wxImage *image, wxOutputStream& stream)
310{
311 unsigned char hdr[128]; // PCX header
0848b0dd 312 unsigned char pal[768]; // palette for 8 bit images
528dad23
GRG
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)
7ac31c42 317 unsigned char nplanes = 3; // number of planes
d32548aa 318 int format = wxPCX_24BIT; // image format (8 bit, 24 bit)
952ae1e8 319 wxImageHistogram histogram; // image histogram
0848b0dd 320 unsigned long key; // key in the hashtable
528dad23 321 unsigned int i;
479cd5de 322
d32548aa 323 // See if we can save as 8 bit.
0bddb3cc 324
d32548aa 325 if (image->CountColours(256) <= 256)
528dad23 326 {
952ae1e8 327 image->ComputeHistogram(histogram);
528dad23
GRG
328 format = wxPCX_8BIT;
329 nplanes = 1;
330 }
528dad23
GRG
331
332 // Get image dimensions, calculate bytesperline (must be even,
333 // according to PCX specs) and allocate space for one complete
334 // scanline.
0bddb3cc 335
528dad23
GRG
336 if (!image->Ok())
337 return wxPCX_INVFORMAT;
338
339 width = image->GetWidth();
340 height = image->GetHeight();
528dad23
GRG
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).
0bddb3cc 350
528dad23
GRG
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;
33ac7e6f
KB
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);
528dad23
GRG
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
0bddb3cc 369
528dad23
GRG
370 src = image->GetData();
371
372 for (; height; height--)
373 {
374 switch (format)
375 {
376 case wxPCX_8BIT:
528dad23 377 {
0848b0dd 378 unsigned char r, g, b;
0848b0dd 379
528dad23
GRG
380 for (i = 0; i < width; i++)
381 {
0848b0dd
GRG
382 r = *(src++);
383 g = *(src++);
384 b = *(src++);
385 key = (r << 16) | (g << 8) | b;
386
952ae1e8 387 p[i] = (unsigned char)histogram[key].index;
528dad23
GRG
388 }
389 break;
390 }
528dad23
GRG
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 }
d32548aa 402
528dad23
GRG
403 RLEencode(p, bytesperline * nplanes, stream);
404 }
479cd5de 405
528dad23
GRG
406 free(p);
407
952ae1e8 408 // For 8 bit images, build the palette and write it to the stream:
528dad23
GRG
409 if (format == wxPCX_8BIT)
410 {
0848b0dd
GRG
411 // zero unused colours
412 memset(pal, 0, sizeof(pal));
413
952ae1e8 414 unsigned long index;
7beb59f3 415
952ae1e8 416 for (wxImageHistogram::iterator entry = histogram.begin();
60d8e886 417 entry != histogram.end(); ++entry )
0848b0dd 418 {
952ae1e8
VS
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);
0848b0dd
GRG
424 }
425
528dad23 426 stream.PutC(12);
0848b0dd 427 stream.Write(pal, 768);
528dad23 428 }
528dad23
GRG
429
430 return wxPCX_OK;
431}
432
56c66755
GRG
433//-----------------------------------------------------------------------------
434// wxPCXHandler
435//-----------------------------------------------------------------------------
436
700ec454 437bool wxPCXHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) )
56c66755 438{
4df78dc3
GRG
439 int error;
440
441 if (!CanRead(stream))
442 {
443 if (verbose)
58c837a4 444 wxLogError(_("PCX: this is not a PCX file."));
4df78dc3 445
7beb59f3 446 return false;
4df78dc3
GRG
447 }
448
56c66755
GRG
449 image->Destroy();
450
e4b8154a 451 if ((error = ReadPCX(image, stream)) != wxPCX_OK)
4df78dc3
GRG
452 {
453 if (verbose)
454 {
455 switch (error)
456 {
add95ac3
VS
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 !!!"));
4df78dc3
GRG
461 }
462 }
463 image->Destroy();
7beb59f3 464 return false;
4df78dc3 465 }
56c66755 466
7beb59f3 467 return true;
56c66755
GRG
468}
469
528dad23 470bool wxPCXHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
56c66755 471{
528dad23
GRG
472 int error;
473
474 if ((error = SavePCX(image, stream)) != wxPCX_OK)
475 {
476 if (verbose)
477 {
478 switch (error)
479 {
add95ac3
VS
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 !!!"));
528dad23
GRG
483 }
484 }
485 }
56c66755 486
528dad23 487 return (error == wxPCX_OK);
56c66755
GRG
488}
489
995612e2 490bool wxPCXHandler::DoCanRead( wxInputStream& stream )
56c66755 491{
79fa2374
VZ
492 unsigned char c = stream.GetC();
493 if ( !stream )
7beb59f3 494 return false;
4df78dc3 495
4df78dc3 496 // not very safe, but this is all we can get from PCX header :-(
79fa2374 497 return c == 10;
56c66755
GRG
498}
499
e30285ab
VZ
500#endif // wxUSE_STREAMS
501
502#endif // wxUSE_IMAGE && wxUSE_PCX
56c66755 503