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