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