home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / src / common / imagpcx.cpp < prev    next >
C/C++ Source or Header  |  2002-06-04  |  15KB  |  499 lines

  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: imagpcx.cpp,v 1.32 2002/05/22 23:14:47 VZ Exp $
  7. // Copyright:   (c) 1999 Guillermo Rodriguez Garcia
  8. // Licence:     wxWindows licence
  9. /////////////////////////////////////////////////////////////////////////////
  10.  
  11. #ifdef __GNUG__
  12. #pragma implementation "imagpcx.h"
  13. #endif
  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.  
  22. #ifndef WX_PRECOMP
  23. #  include "wx/defs.h"
  24. #endif
  25.  
  26. #if wxUSE_IMAGE && wxUSE_STREAMS && wxUSE_PCX
  27.  
  28. #include "wx/imagpcx.h"
  29. #include "wx/wfstream.h"
  30. #include "wx/module.h"
  31. #include "wx/log.h"
  32. #include "wx/intl.h"
  33.  
  34. #include "wx/hash.h"
  35. #include "wx/list.h"
  36. #include "wx/object.h"
  37.  
  38. //-----------------------------------------------------------------------------
  39. // RLE encoding and decoding
  40. //-----------------------------------------------------------------------------
  41.  
  42. void RLEencode(unsigned char *p, unsigned int size, wxOutputStream& s)
  43. {
  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
  60.         // a 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.             if ((cont > 1) || ((last & 0xC0) == 0xC0))
  70.                 s.PutC((char) (cont | 0xC0));
  71.  
  72.             s.PutC((char) last);
  73.             last = data;
  74.             cont = 1;
  75.         }
  76.     }
  77.  
  78.     // write the last one and return;
  79.     if ((cont > 1) || ((last & 0xC0) == 0xC0))
  80.         s.PutC((char) (cont | 0xC0));
  81.  
  82.     s.PutC((char) last);
  83. }
  84.  
  85. void RLEdecode(unsigned char *p, unsigned int size, wxInputStream& s)
  86. {
  87.     unsigned int i, data, cont;
  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.         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.             cont = data & 0x3F;
  111.             data = (unsigned char)s.GetC();
  112.             for (i = 1; i <= cont; i++)
  113.                 *(p++) = (unsigned char)data;
  114.             size -= cont;
  115.         }
  116.     }
  117. }
  118.  
  119.  
  120. //-----------------------------------------------------------------------------
  121. // PCX reading and saving
  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.  
  152. // ReadPCX:
  153. //  Loads a PCX file into the wxImage object pointed by image.
  154. //  Returns wxPCX_OK on success, or an error code otherwise
  155. //  (see above for error codes)
  156. //
  157. int ReadPCX(wxImage *image, wxInputStream& stream)
  158. {
  159.     unsigned char hdr[128];         // PCX header
  160.     unsigned char pal[768];         // palette for 8 bit images
  161.     unsigned char *p;               // space to store one scanline
  162.     unsigned char *dst;             // pointer into wxImage data
  163.     unsigned int width, height;     // size of the image
  164.     unsigned int bytesperline;      // bytes per line (each plane)
  165.     int bitsperpixel;               // bits per pixel (each plane)
  166.     int nplanes;                    // number of planes
  167.     int encoding;                   // is the image RLE encoded?
  168.     int format;                     // image format (8 bit, 24 bit)
  169.     unsigned int i, j;
  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
  200.     // a palette at the end of the image data. If we were
  201.     // working with a file, we could seek at the end to the
  202.     // end (SeekI(-769, wxFromEnd) and read the palette
  203.     // before proceeding. Unfortunately, this would prevent
  204.     // loading several PCXs in a single stream, so we can't
  205.     // do it. Thus, 8-bit images will have to be decoded in
  206.     // two passes: one to read and decode the image data,
  207.     // and another to replace 'colour indexes' with RGB
  208.     // values.
  209.  
  210.     // Resize the image and allocate memory for a scanline.
  211.  
  212.     image->Create(width, height);
  213.  
  214.     if (!image->Ok())
  215.         return wxPCX_MEMERR;
  216.  
  217.     if ((p = (unsigned char *) malloc(bytesperline * nplanes)) == NULL)
  218.         return wxPCX_MEMERR;
  219.  
  220.     // Now start reading the file, line by line, and store
  221.     // the data in the format required by wxImage.
  222.  
  223.     dst = image->GetData();
  224.  
  225.     for (j = height; j; j--)
  226.     {
  227.         if (encoding)
  228.             RLEdecode(p, bytesperline * nplanes, stream);
  229.         else
  230.             stream.Read(p, bytesperline * nplanes);
  231.  
  232.         switch (format)
  233.         {
  234.             case wxPCX_8BIT:
  235.             {
  236.                 for (i = 0; i < width; i++)
  237.                 {
  238.                     // first pass, just store the colour index
  239.                     *dst = p[i];
  240.                     dst += 3;
  241.                 }
  242.                 break;
  243.             }
  244.             case wxPCX_24BIT:
  245.             {
  246.                 for (i = 0; i < width; i++)
  247.                 {
  248.                     *(dst++) = p[i];
  249.                     *(dst++) = p[i + bytesperline];
  250.                     *(dst++) = p[i + 2 * bytesperline];
  251.                 }
  252.                 break;
  253.             }
  254.         }
  255.     }
  256.  
  257.     free(p);
  258.  
  259.     // For 8 bit images, we read the palette, and then do a second
  260.     // pass replacing indexes with their RGB values;
  261.  
  262.     if (format == wxPCX_8BIT)
  263.     {
  264.         unsigned char index;
  265.  
  266.         if (stream.GetC() != 12)
  267.             return wxPCX_INVFORMAT;
  268.  
  269.         stream.Read(pal, 768);
  270.  
  271.         p = image->GetData();
  272.         for (unsigned long k = height * width; k; k--)
  273.         {
  274.             index = *p;
  275.             *(p++) = pal[3 * index];
  276.             *(p++) = pal[3 * index + 1];
  277.             *(p++) = pal[3 * index + 2];
  278.         }
  279.  
  280. #if wxUSE_PALETTE
  281.         unsigned char r[256];
  282.         unsigned char g[256];
  283.         unsigned char b[256];
  284.         for (i = 0; i < 256; i++)
  285.         {
  286.             r[i] = pal[3*i + 0];
  287.             g[i] = pal[3*i + 1];
  288.             b[i] = pal[3*i + 2];
  289.         }
  290.         image->SetPalette(wxPalette(256, r, g, b));
  291. #endif // wxUSE_PALETTE
  292.     }
  293.  
  294.     return wxPCX_OK;
  295. }
  296.  
  297. // SavePCX:
  298. //  Saves a PCX file into the wxImage object pointed by image.
  299. //  Returns wxPCX_OK on success, or an error code otherwise
  300. //  (see above for error codes). Will try to save as 8-bit
  301. //  PCX if possible, and then fall back to 24-bit if there
  302. //  are more than 256 different colours.
  303. //
  304. int SavePCX(wxImage *image, wxOutputStream& stream)
  305. {
  306.     unsigned char hdr[128];         // PCX header
  307.     unsigned char pal[768];         // palette for 8 bit images
  308.     unsigned char *p;               // space to store one scanline
  309.     unsigned char *src;             // pointer into wxImage data
  310.     unsigned int width, height;     // size of the image
  311.     unsigned int bytesperline;      // bytes per line (each plane)
  312.     int nplanes = 3;                // number of planes
  313.     int format = wxPCX_24BIT;       // image format (8 bit, 24 bit)
  314.     wxImageHistogram histogram;     // image histogram
  315.     unsigned long key;              // key in the hashtable
  316.     unsigned int i;
  317.  
  318.     // See if we can save as 8 bit.
  319.  
  320.     if (image->CountColours(256) <= 256)
  321.     {
  322.         image->ComputeHistogram(histogram);
  323.         format = wxPCX_8BIT;
  324.         nplanes = 1;
  325.     }
  326.  
  327.     // Get image dimensions, calculate bytesperline (must be even,
  328.     // according to PCX specs) and allocate space for one complete
  329.     // scanline.
  330.  
  331.     if (!image->Ok())
  332.         return wxPCX_INVFORMAT;
  333.  
  334.     width = image->GetWidth();
  335.     height = image->GetHeight();
  336.     bytesperline = width;
  337.     if (bytesperline % 2)
  338.         bytesperline++;
  339.  
  340.     if ((p = (unsigned char *) malloc(bytesperline * nplanes)) == NULL)
  341.         return wxPCX_MEMERR;
  342.  
  343.     // Build header data and write it to the stream. Initially,
  344.     // set all bytes to zero (most values default to zero).
  345.  
  346.     memset(hdr, 0, sizeof(hdr));
  347.  
  348.     hdr[HDR_MANUFACTURER]     = 10;
  349.     hdr[HDR_VERSION]          = 5;
  350.     hdr[HDR_ENCODING]         = 1;
  351.     hdr[HDR_NPLANES]          = nplanes;
  352.     hdr[HDR_BITSPERPIXEL]     = 8;
  353.     hdr[HDR_BYTESPERLINE]     = (unsigned char)(bytesperline % 256);
  354.     hdr[HDR_BYTESPERLINE + 1] = (unsigned char)(bytesperline / 256);
  355.     hdr[HDR_XMAX]             = (unsigned char)((width - 1)  % 256);
  356.     hdr[HDR_XMAX + 1]         = (unsigned char)((width - 1)  / 256);
  357.     hdr[HDR_YMAX]             = (unsigned char)((height - 1) % 256);
  358.     hdr[HDR_YMAX + 1]         = (unsigned char)((height - 1) / 256);
  359.     hdr[HDR_PALETTEINFO]      = 1;
  360.  
  361.     stream.Write(hdr, 128);
  362.  
  363.     // Encode image data line by line and write it to the stream
  364.  
  365.     src = image->GetData();
  366.  
  367.     for (; height; height--)
  368.     {
  369.         switch (format)
  370.         {
  371.             case wxPCX_8BIT:
  372.             {
  373.                 unsigned char r, g, b;
  374.  
  375.                 for (i = 0; i < width; i++)
  376.                 {
  377.                     r = *(src++);
  378.                     g = *(src++);
  379.                     b = *(src++);
  380.                     key = (r << 16) | (g << 8) | b;
  381.  
  382.                     p[i] = (unsigned char)histogram[key].index;
  383.                 }
  384.                 break;
  385.             }
  386.             case wxPCX_24BIT:
  387.             {
  388.                 for (i = 0; i < width; i++)
  389.                 {
  390.                     p[i] = *(src++);
  391.                     p[i + bytesperline] = *(src++);
  392.                     p[i + 2 * bytesperline] = *(src++);
  393.                 }
  394.                 break;
  395.             }
  396.         }
  397.  
  398.         RLEencode(p, bytesperline * nplanes, stream);
  399.     }
  400.  
  401.     free(p);
  402.  
  403.     // For 8 bit images, build the palette and write it to the stream:
  404.     if (format == wxPCX_8BIT)
  405.     {
  406.         // zero unused colours
  407.         memset(pal, 0, sizeof(pal));
  408.  
  409.         unsigned long index;
  410.         
  411.         for (wxImageHistogram::iterator entry = histogram.begin();
  412.              entry != histogram.end(); entry++ )
  413.         {
  414.             key = entry->first;
  415.             index = entry->second.index;
  416.             pal[3 * index]     = (unsigned char)(key >> 16);
  417.             pal[3 * index + 1] = (unsigned char)(key >> 8);
  418.             pal[3 * index + 2] = (unsigned char)(key);
  419.         }
  420.  
  421.         stream.PutC(12);
  422.         stream.Write(pal, 768);
  423.     }
  424.  
  425.     return wxPCX_OK;
  426. }
  427.  
  428. //-----------------------------------------------------------------------------
  429. // wxPCXHandler
  430. //-----------------------------------------------------------------------------
  431.  
  432. IMPLEMENT_DYNAMIC_CLASS(wxPCXHandler,wxImageHandler)
  433.  
  434. bool wxPCXHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) )
  435. {
  436.     int error;
  437.  
  438.     if (!CanRead(stream))
  439.     {
  440.         if (verbose)
  441.             wxLogError(_("PCX: this is not a PCX file."));
  442.  
  443.         return FALSE;
  444.     }
  445.  
  446.     image->Destroy();
  447.  
  448.     if ((error = ReadPCX(image, stream)) != wxPCX_OK)
  449.     {
  450.         if (verbose)
  451.         {
  452.             switch (error)
  453.             {
  454.                 case wxPCX_INVFORMAT: wxLogError(_("PCX: image format unsupported")); break;
  455.                 case wxPCX_MEMERR:    wxLogError(_("PCX: couldn't allocate memory")); break;
  456.                 case wxPCX_VERERR:    wxLogError(_("PCX: version number too low")); break;
  457.                 default:              wxLogError(_("PCX: unknown error !!!"));
  458.             }
  459.         }
  460.         image->Destroy();
  461.         return FALSE;
  462.     }
  463.  
  464.     return TRUE;
  465. }
  466.  
  467. bool wxPCXHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
  468. {
  469.     int error;
  470.  
  471.     if ((error = SavePCX(image, stream)) != wxPCX_OK)
  472.     {
  473.         if (verbose)
  474.         {
  475.             switch (error)
  476.             {
  477.                 case wxPCX_INVFORMAT: wxLogError(_("PCX: invalid image")); break;
  478.                 case wxPCX_MEMERR:    wxLogError(_("PCX: couldn't allocate memory")); break;
  479.                 default:              wxLogError(_("PCX: unknown error !!!"));
  480.             }
  481.         }
  482.     }
  483.  
  484.     return (error == wxPCX_OK);
  485. }
  486.  
  487. bool wxPCXHandler::DoCanRead( wxInputStream& stream )
  488. {
  489.     unsigned char c = stream.GetC();
  490.     if ( !stream )
  491.         return FALSE;
  492.  
  493.     // not very safe, but this is all we can get from PCX header :-(
  494.     return c == 10;
  495. }
  496.  
  497. #endif // wxUSE_STREAMS && wxUSE_PCX
  498.  
  499.