home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / gbmsrc.zip / gbmpcx.c < prev    next >
C/C++ Source or Header  |  1998-04-28  |  11KB  |  539 lines

  1. /*
  2.  
  3. gbmpcx.c - ZSoft PC Paintbrush support
  4.  
  5. Reads and writes 1,4,8 and 24 bit colour files.
  6.  
  7. */
  8.  
  9. /*...sincludes:0:*/
  10. #include <stdio.h>
  11. #include <ctype.h>
  12. #include <stddef.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <memory.h>
  16. #include <malloc.h>
  17. #include "gbm.h"
  18. #include "gbmhelp.h"
  19.  
  20. /*...vgbm\46\h:0:*/
  21. /*...vgbmhelp\46\h:0:*/
  22. /*...e*/
  23.  
  24. /*...suseful:0:*/
  25. #define    low_byte(w)    ((byte)  (          (w)&0x00ffU)    )
  26. #define    high_byte(w)    ((byte) (((unsigned)(w)&0xff00U)>>8))
  27. #define    make_word(a,b)    (((word)a) + (((word)b) << 8))
  28. /*...e*/
  29.  
  30. static GBMFT pcx_gbmft =
  31.     {
  32.     "PCX",
  33.     "ZSoft PC Paintbrush Image format",
  34.     "PCX PCC",
  35.     GBM_FT_R1|GBM_FT_R4|GBM_FT_R8|GBM_FT_R24|
  36.     GBM_FT_W1|GBM_FT_W4|GBM_FT_W8|GBM_FT_W24,
  37.     };
  38.  
  39. #define    GBM_ERR_PCX_BAD_VERSION    ((GBM_ERR) 700)
  40. #define    GBM_ERR_PCX_BAD_ENCMODE    ((GBM_ERR) 701)
  41. #define    GBM_ERR_PCX_BAD_BITS    ((GBM_ERR) 702)
  42. #define    GBM_ERR_PCX_BAD_TRAILER    ((GBM_ERR) 703)
  43.  
  44. typedef struct
  45.     {
  46.     byte version, bpppp, planes;
  47.     int bytes_per_line;
  48.     BOOLEAN    trunc;
  49.     } PCX_PRIV;
  50.  
  51. /*...spcx_qft:0:*/
  52. GBM_ERR pcx_qft(GBMFT *gbmft)
  53.     {
  54.     *gbmft = pcx_gbmft;
  55.     return GBM_ERR_OK;
  56.     }
  57. /*...e*/
  58. /*...spcx_rhdr:0:*/
  59. GBM_ERR pcx_rhdr(const char *fn, int fd, GBM *gbm, const char *opt)
  60.     {
  61.     PCX_PRIV *pcx_priv = (PCX_PRIV *) gbm->priv;
  62.     byte hdr[70];
  63.     word x1, y1, x2, y2;
  64.     int w, h, bpp;
  65.  
  66.     fn=fn; /* Suppress 'unref arg' compiler warning */
  67.  
  68.     pcx_priv->trunc = ( gbm_find_word(opt, "trunc" ) != NULL );
  69.     
  70.     gbm_file_read(fd, hdr, 70);
  71.     if ( hdr[0] != 0x0a )
  72.         return GBM_ERR_BAD_MAGIC;
  73.     pcx_priv->version = hdr[1];
  74.     if ( pcx_priv->version == 4 || pcx_priv->version > 5 )
  75.         return GBM_ERR_PCX_BAD_VERSION;
  76.     if ( hdr[2] != 1 )
  77.         return GBM_ERR_PCX_BAD_ENCMODE;
  78.  
  79.     pcx_priv->bpppp = hdr[3]; pcx_priv->planes = hdr[65];
  80. #define    SWITCH2(a,b)    (((a)<<8)|(b))
  81.     switch ( SWITCH2(pcx_priv->bpppp, pcx_priv->planes) )
  82.         {
  83.         case SWITCH2(1,1): bpp =  1; break;
  84.         case SWITCH2(4,1): bpp =  4; break;
  85.         case SWITCH2(8,1): bpp =  8; break;
  86.         case SWITCH2(8,3): bpp = 24; break; /* Extended 24 bit style */
  87.         case SWITCH2(1,4): bpp =  4; break; /* EGA RGBI style */
  88.         default: return GBM_ERR_PCX_BAD_BITS;
  89.         }
  90.  
  91.     x1 = make_word(hdr[ 4], hdr[ 5]);
  92.     y1 = make_word(hdr[ 6], hdr[ 7]);
  93.     x2 = make_word(hdr[ 8], hdr[ 9]);
  94.     y2 = make_word(hdr[10], hdr[11]);
  95.  
  96.     w = x2 - x1 + 1;
  97.     h = y2 - y1 + 1;
  98.  
  99.     if ( w <= 0 || h <= 0 )
  100.         return GBM_ERR_BAD_SIZE;
  101.  
  102.     pcx_priv->bytes_per_line = make_word(hdr[66], hdr[67]);
  103.  
  104.     gbm->w   = w;
  105.     gbm->h   = h;
  106.     gbm->bpp = bpp;
  107.  
  108.     return GBM_ERR_OK;
  109.     }
  110. /*...e*/
  111. /*...spcx_rpal:0:*/
  112. GBM_ERR pcx_rpal(int fd, GBM *gbm, GBMRGB *gbmrgb)
  113.     {
  114.     switch ( gbm->bpp )
  115.         {
  116. /*...s1 \45\ fixed b\47\w palette:16:*/
  117. case 1:
  118.     gbmrgb[0].r = gbmrgb[0].g = gbmrgb[0].b = 0x00;
  119.     gbmrgb[1].r = gbmrgb[1].g = gbmrgb[1].b = 0xff;
  120.     break;
  121. /*...e*/
  122. /*...s4 \45\ read palette if 1 plane\44\ fix one if 4 plane RGBI:16:*/
  123. case 4:
  124.     /* Use inline palette */
  125.     {
  126.     byte b[16*3];
  127.     int i;
  128.  
  129.     gbm_file_lseek(fd, 16L, SEEK_SET);
  130.     gbm_file_read(fd, b, 16 * 3);
  131.     for ( i = 0; i < 16; i++ )
  132.         {
  133.         gbmrgb[i].r = b[i * 3 + 0];
  134.         gbmrgb[i].g = b[i * 3 + 1];
  135.         gbmrgb[i].b = b[i * 3 + 2];
  136.         }
  137.     }
  138.     break;
  139. /*...e*/
  140. /*...s8 \45\ read palette from end of file:16:*/
  141. case 8:
  142.     {
  143.     byte trailer_id;
  144.     byte b[0x100*3];
  145.     int i;
  146.  
  147.     gbm_file_lseek(fd, -0x301L, SEEK_END);
  148.     gbm_file_read(fd, &trailer_id, 1);
  149.     if ( trailer_id != 0x0c )
  150.         return GBM_ERR_PCX_BAD_TRAILER;
  151.  
  152.     gbm_file_read(fd, b, 0x100 * 3);
  153.     for ( i = 0; i < 0x100; i++ )
  154.         {
  155.         gbmrgb[i].r = b[i * 3 + 0];
  156.         gbmrgb[i].g = b[i * 3 + 1];
  157.         gbmrgb[i].b = b[i * 3 + 2];
  158.         }
  159.     }
  160.     break;
  161. /*...e*/
  162.         }
  163.     return GBM_ERR_OK;
  164.     }
  165. /*...e*/
  166. /*...spcx_rdata:0:*/
  167. /*...sread_pcx_line:0:*/
  168. static void read_pcx_line(
  169.     AHEAD *ahead, byte *line, int bytes_per_line,
  170.     byte *runleft, byte *runval
  171.     )
  172.     {
  173.     /* Handle left overs from previous line */
  174.  
  175.     while ( *runleft > 0 && bytes_per_line > 0 )
  176.         {
  177.         *line++ = *runval;
  178.         (*runleft)--;
  179.         bytes_per_line--;
  180.         }
  181.  
  182.     /* Normal code */
  183.  
  184.     while ( bytes_per_line )
  185.         {
  186.         byte b1 = (byte) gbm_read_ahead(ahead);
  187.  
  188.         if ( (b1 & 0xc0) == 0xc0 )
  189.             {
  190.             byte b2 = (byte) gbm_read_ahead(ahead);
  191.  
  192.             b1 &= 0x3f;
  193.             if ( b1 > bytes_per_line )
  194.                 {
  195.                 (*runleft) = (byte) (b1 - bytes_per_line);
  196.                 (*runval) = b2;
  197.                 b1 = bytes_per_line;
  198.                 }
  199.             memset(line, b2, b1);
  200.             line += b1;
  201.             bytes_per_line -= b1;
  202.             }
  203.         else
  204.             {
  205.             *line++ = b1;
  206.             bytes_per_line--;
  207.             }
  208.         }
  209.     }
  210. /*...e*/
  211. /*...sspread:0:*/
  212. static void spread(byte b, byte bit_to_set, byte *dest)
  213.     {
  214.     if ( b & 0x80 ) dest[0] |= (bit_to_set & 0xf0);
  215.     if ( b & 0x40 ) dest[0] |= (bit_to_set & 0x0f);
  216.     if ( b & 0x20 ) dest[1] |= (bit_to_set & 0xf0);
  217.     if ( b & 0x10 ) dest[1] |= (bit_to_set & 0x0f);
  218.     if ( b & 0x08 ) dest[2] |= (bit_to_set & 0xf0);
  219.     if ( b & 0x04 ) dest[2] |= (bit_to_set & 0x0f);
  220.     if ( b & 0x02 ) dest[3] |= (bit_to_set & 0xf0);
  221.     if ( b & 0x01 ) dest[3] |= (bit_to_set & 0x0f);
  222.     }
  223. /*...e*/
  224.  
  225. GBM_ERR pcx_rdata(int fd, GBM *gbm, byte *data)
  226.     {
  227.     PCX_PRIV *pcx_priv = (PCX_PRIV *) gbm->priv;
  228.     BOOLEAN trunc = pcx_priv->trunc;
  229.     int bytes_per_line = pcx_priv->bytes_per_line;
  230.     int stride, y;
  231.     byte *line;
  232.     AHEAD *ahead;
  233.     byte runleft = 0, runval;
  234.  
  235.     if ( (ahead = gbm_create_ahead(fd)) == NULL )
  236.         return GBM_ERR_MEM;
  237.  
  238.     gbm_file_lseek(fd, 128L, SEEK_SET);
  239.  
  240.     if ( (line = malloc((size_t) bytes_per_line)) == NULL )
  241.         {
  242.         gbm_destroy_ahead(ahead);
  243.         return GBM_ERR_MEM;
  244.         }
  245.  
  246.     switch ( gbm->bpp )
  247.         {
  248. /*...s1:16:*/
  249. case 1:
  250.     stride = ((gbm->w + 31) / 32) * 4;
  251.     for ( y = gbm->h - 1; y >= 0; y-- )
  252.         {
  253.         read_pcx_line(ahead, data + y * stride, bytes_per_line, &runleft, &runval);
  254.         if ( trunc )
  255.             runleft = 0;
  256.         }
  257.     break;
  258. /*...e*/
  259. /*...s4:16:*/
  260. case 4:
  261.     stride = ((gbm->w * 4 + 31) / 32) * 4;
  262.     if ( pcx_priv->planes == 1 )
  263.         for ( y = gbm->h - 1; y >= 0; y-- )
  264.             {
  265.             read_pcx_line(ahead, data + y * stride, bytes_per_line, &runleft, &runval);
  266.             if ( trunc )
  267.                 runleft = 0;
  268.             }
  269.     else
  270.         {
  271.         int p, x;
  272.         int bytes = (gbm->w / 8);
  273.         int bits  = (gbm->w & 7);
  274.  
  275.         memset(data, 0, gbm->h * stride);
  276.         for ( y = gbm->h - 1; y >= 0; y-- )
  277.             for ( p = 0x11; p <= 0x88 ; p <<= 1 )
  278.                 {
  279.                 byte *dest = data + y * stride;
  280.  
  281.                 read_pcx_line(ahead, line, bytes_per_line, &runleft, &runval);
  282.                 if ( trunc )
  283.                     runleft = 0;
  284.                 for ( x = 0; x < bytes; x++, dest += 4 )
  285.                     spread(line[x], (byte) p, dest);
  286.                 if ( bits )
  287.                     spread((byte) (line[x] & (0xff00U >> bits)), (byte) p, dest);
  288.                 }
  289.         }
  290.     break;
  291. /*...e*/
  292. /*...s8:16:*/
  293. case 8:
  294.     stride = ((gbm->w + 3) & ~3);
  295.     for ( y = gbm->h - 1; y >= 0; y-- )
  296.         {
  297.         read_pcx_line(ahead, data + y * stride, bytes_per_line, &runleft, &runval);
  298.         if ( trunc )
  299.             runleft = 0;
  300.         }
  301.     break;
  302. /*...e*/
  303. /*...s24:16:*/
  304. case 24:
  305.     {
  306.     int    p, x;
  307.  
  308.     stride = ((gbm->w * 3 + 3) & ~3);
  309.     for ( y = gbm->h - 1; y >= 0; y-- )
  310.         for ( p = 2; p >= 0; p-- )
  311.             {
  312.             read_pcx_line(ahead, line, bytes_per_line, &runleft, &runval);
  313.             if ( trunc )
  314.                 runleft = 0;
  315.             for ( x = 0; x < gbm->w; x++ )
  316.                 data[y * stride + p + x * 3] = line[x];
  317.             }
  318.     }
  319.     break;
  320. /*...e*/
  321.         }
  322.  
  323.     free(line);
  324.  
  325.     gbm_destroy_ahead(ahead);
  326.  
  327.     return GBM_ERR_OK;
  328.     }
  329. /*...e*/
  330. /*...spcx_w:0:*/
  331. /*...sbright:0:*/
  332. static int bright(const GBMRGB *gbmrgb)
  333.     {
  334.     return gbmrgb->r*30+gbmrgb->g*60+gbmrgb->b*10;
  335.     }
  336. /*...e*/
  337. /*...spcx_rle:0:*/
  338. static byte pcx_run(const byte *src, int n_src)
  339.     {
  340.     byte cnt = 1;
  341.     byte b = *src++;
  342.  
  343.     --n_src;
  344.     while ( cnt < 0x3f && n_src > 0 && *src == b )
  345.         { cnt++; n_src--; src++; }
  346.  
  347.     return cnt;
  348.     }
  349.  
  350. static void pcx_rle(const byte *src, int n_src, byte *dst, int *n_dst)
  351.     {
  352.     *n_dst = 0;    
  353.     while ( n_src )
  354.         {
  355.         byte    len;
  356.  
  357.         if ( (len = pcx_run(src, n_src)) > 1 || (*src & 0xc0) == 0xc0 )
  358.             {
  359.             *dst++ = (byte) (0xc0 | len);
  360.             *dst++ = *src;
  361.             (*n_dst) += 2;
  362.             }
  363.         else
  364.             {
  365.             *dst++ = *src;
  366.             (*n_dst)++;
  367.             }
  368.         src += len;
  369.         n_src -= len;
  370.         }
  371.     }
  372. /*...e*/
  373.  
  374. GBM_ERR pcx_w(const char *fn, int fd, const GBM *gbm, const GBMRGB *gbmrgb, const byte *data, const char *opt)
  375.     {
  376.     int i, y, stride = ((gbm->bpp * gbm->w + 31) / 32) * 4;
  377.     byte *line;
  378.     byte hdr[128];
  379.     int bytes_per_line, cnt;
  380.  
  381.     fn=fn; opt=opt; /* Suppress 'unref arg' compiler warning */
  382.  
  383.     memset(hdr, 0, 128);
  384.     hdr[ 0] = 0x0a;                /* Magic # */
  385.     hdr[ 1] = 5;                /* Version 5 */
  386.     hdr[ 2] = 1;                /* RLE compression */
  387.     hdr[ 3] = (byte) ( ( gbm->bpp == 24 ) ? 8 : gbm->bpp );
  388.                         /* Bits per plane */
  389.     hdr[ 4] = low_byte(0);
  390.     hdr[ 5] = high_byte(0);            /* Top left x */
  391.     hdr[ 6] = low_byte(0);
  392.     hdr[ 7] = high_byte(0);            /* Top left y */
  393.     hdr[ 8] = low_byte(gbm->w - 1);
  394.     hdr[ 9] = high_byte(gbm->w - 1);    /* Bottom right x */
  395.     hdr[10] = low_byte(gbm->h - 1);
  396.     hdr[11] = high_byte(gbm->h - 1);    /* Bottom right y */
  397.     hdr[12] = low_byte(0);
  398.     hdr[13] = high_byte(0);            /* Horizontal resolution ??? */
  399.     hdr[14] = low_byte(0);
  400.     hdr[15] = high_byte(0);            /* Vertical resolution ??? */
  401.  
  402.     if ( gbm->bpp == 4 )
  403.         for ( i = 0; i < 16; i++ )
  404.             {
  405.             hdr[16 + i * 3    ] = gbmrgb[i].r;
  406.             hdr[16 + i * 3 + 1] = gbmrgb[i].g;
  407.             hdr[16 + i * 3 + 2] = gbmrgb[i].b;
  408.             }
  409.  
  410.     hdr[65] = (byte) ( ( gbm->bpp == 24 ) ? 3 : 1 );
  411.                         /* Planes */
  412.     bytes_per_line = (gbm->w * hdr[3] + 7) / 8;
  413.     if ( bytes_per_line & 1 )
  414.         bytes_per_line++;
  415.     hdr[66] = low_byte(bytes_per_line);
  416.     hdr[67] = high_byte(bytes_per_line);
  417.     hdr[68] = 1;                /* Colour or b/w */
  418.  
  419.     gbm_file_write(fd, hdr, 128);
  420.  
  421.     if ( (line = malloc((size_t) (bytes_per_line * 2))) == NULL )
  422.         return GBM_ERR_MEM;
  423.  
  424.     switch ( gbm->bpp )
  425.         {
  426. /*...s1:16:*/
  427. case 1:
  428.     if ( bright(&gbmrgb[0]) > bright(&gbmrgb[1]) )
  429.         /* Need to invert bitmap bits */
  430.         {
  431.         byte *b;
  432.         if ( (b = malloc(bytes_per_line)) == NULL )
  433.             {
  434.             free(line);
  435.             return GBM_ERR_MEM;
  436.             }
  437.         for ( y = gbm->h - 1; y >= 0; y-- )
  438.             {
  439.             int i;
  440.             for ( i = 0; i < bytes_per_line; i++ )
  441.                 b[i] = data[y*stride+i] ^ 0xffU;
  442.             pcx_rle(b, bytes_per_line, line, &cnt);
  443.             if ( gbm_file_write(fd, line, cnt) != cnt )
  444.                 {
  445.                 free(b);
  446.                 free(line);
  447.                 return GBM_ERR_WRITE;
  448.                 }
  449.             }
  450.         free(b);
  451.         break;
  452.         }
  453.     /* Fall through to regular non-invert case */
  454. /*...e*/
  455. /*...s4\44\8:16:*/
  456. case 4:
  457. case 8:
  458.     for ( y = gbm->h - 1; y >= 0; y-- )
  459.         {
  460.         pcx_rle(data + y * stride, bytes_per_line, line, &cnt);
  461.         if ( gbm_file_write(fd, line, cnt) != cnt )
  462.             {
  463.             free(line);
  464.             return GBM_ERR_WRITE;
  465.             }
  466.         }
  467.     break;
  468. /*...e*/
  469. /*...s24:16:*/
  470. case 24:
  471.     {
  472.     byte *line2;
  473.     int p, x;
  474.  
  475.     if ( (line2 = malloc((size_t) bytes_per_line)) == NULL )
  476.         {
  477.         free(line);
  478.         return GBM_ERR_MEM;
  479.         }
  480.  
  481.     for ( y = gbm->h - 1; y >= 0; y-- )
  482.         for ( p = 2; p >= 0; p-- )
  483.             {
  484.             const byte *src = data + y * stride;
  485.  
  486.             for ( x = 0; x < gbm->w; x++ )
  487.                 line2[x] = src[x * 3 + p];
  488.  
  489.             pcx_rle(line2, bytes_per_line, line, &cnt);
  490.             if ( gbm_file_write(fd, line, cnt) != cnt )
  491.                 {
  492.                 free(line2);
  493.                 free(line);
  494.                 return GBM_ERR_WRITE;
  495.                 }
  496.             }
  497.     free(line2);
  498.     }
  499.     break;
  500. /*...e*/
  501.         }
  502.  
  503.     free(line);
  504.  
  505.     if ( gbm->bpp == 8 )
  506.         {
  507.         byte    pal[1 + 0x100 * 3];
  508.  
  509.         pal[0] = 0x0c;
  510.         for ( i = 0; i < 0x100; i++ )
  511.             {
  512.             pal[i * 3 + 1] = gbmrgb[i].r;
  513.             pal[i * 3 + 2] = gbmrgb[i].g;
  514.             pal[i * 3 + 3] = gbmrgb[i].b;
  515.             }
  516.         gbm_file_write(fd, pal, 1 + 0x100 * 3);
  517.         }
  518.  
  519.     return GBM_ERR_OK;
  520.     }
  521. /*...e*/
  522. /*...spcx_err:0:*/
  523. const char *pcx_err(GBM_ERR rc)
  524.     {
  525.     switch ( (int) rc )
  526.         {
  527.         case GBM_ERR_PCX_BAD_VERSION:
  528.             return "version number not 4 or 5";
  529.         case GBM_ERR_PCX_BAD_ENCMODE:
  530.             return "encoding mode not 1";
  531.         case GBM_ERR_PCX_BAD_BITS:
  532.             return "unsupported bpp/plane / plane combination";
  533.         case GBM_ERR_PCX_BAD_TRAILER:
  534.             return "corrupt file trailer";
  535.         }
  536.     return NULL;
  537.     }
  538. /*...e*/
  539.