home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / gbmsrc.zip / gbmbmp.c < prev    next >
C/C++ Source or Header  |  1996-12-04  |  18KB  |  818 lines

  1. /*
  2.  
  3. gbmbmp.c - OS/2 1.1, 1.2, 2.0 and Windows 3.0 support
  4.  
  5. Reads and writes any OS/2 1.x bitmap.
  6. Will also read uncompressed, RLE4 and RLE8 Windows 3.x bitmaps too.
  7. There are horrific file structure alignment considerations hence each
  8. word,dword is read individually.
  9. Input options: index=# (default: 0)
  10.  
  11. */
  12.  
  13. /*...sincludes:0:*/
  14. #include <stdio.h>
  15. #include <ctype.h>
  16. #include <stddef.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <memory.h>
  20. #include <malloc.h>
  21. #include "gbm.h"
  22. #include "gbmhelp.h"
  23.  
  24. /*...vgbm\46\h:0:*/
  25. /*...vgbmhelp\46\h:0:*/
  26.  
  27. #ifndef min
  28. #define    min(a,b)    (((a)<(b))?(a):(b))
  29. #endif
  30. /*...e*/
  31.  
  32. /*...suseful:0:*/
  33. #define    low_byte(w)    ((byte)  ((w)&0x00ff)    )
  34. #define    high_byte(w)    ((byte) (((w)&0xff00)>>8))
  35. #define    make_word(a,b)    (((word)a) + (((word)b) << 8))
  36.  
  37. /*...sread_word:0:*/
  38. static BOOLEAN read_word(int fd, word *w)
  39.     {
  40.     byte low = 0, high = 0;
  41.  
  42.     if ( gbm_file_read(fd, (char *) &low, 1) != 1 )
  43.         return FALSE;
  44.     if ( gbm_file_read(fd, (char *) &high, 1) != 1 )
  45.         return FALSE;
  46.     *w = (word) (low + ((word) high << 8));
  47.     return TRUE;
  48.     }
  49. /*...e*/
  50. /*...sread_dword:0:*/
  51. static BOOLEAN read_dword(int fd, dword *d)
  52.     {
  53.     word low, high;
  54.     if ( !read_word(fd, &low) )
  55.         return FALSE;
  56.     if ( !read_word(fd, &high) )
  57.         return FALSE;
  58.     *d = low + ((dword) high << 16);
  59.     return TRUE;
  60.     }
  61. /*...e*/
  62. /*...swrite_word:0:*/
  63. static BOOLEAN write_word(int fd, word w)
  64.     {
  65.     byte    low  = (byte) w;
  66.     byte    high = (byte) (w >> 8);
  67.  
  68.     gbm_file_write(fd, &low, 1);
  69.     gbm_file_write(fd, &high, 1);
  70.     return TRUE;
  71.     }
  72. /*...e*/
  73. /*...swrite_dword:0:*/
  74. static BOOLEAN write_dword(int fd, dword d)
  75.     {
  76.     write_word(fd, (word) d);
  77.     write_word(fd, (word) (d >> 16));
  78.     return TRUE;
  79.     }
  80. /*...e*/
  81. /*...e*/
  82.  
  83. static GBMFT bmp_gbmft =
  84.     {
  85.     "Bitmap",
  86.     "OS/2 1.1, 1.2, 2.0 / Windows 3.0 bitmap",
  87.     "BMP VGA BGA RLE DIB RL4 RL8",
  88.     GBM_FT_R1|GBM_FT_R4|GBM_FT_R8|GBM_FT_R24|
  89.     GBM_FT_W1|GBM_FT_W4|GBM_FT_W8|GBM_FT_W24,
  90.     };
  91.  
  92. #define    GBM_ERR_BMP_PLANES    ((GBM_ERR) 300)
  93. #define    GBM_ERR_BMP_BITCOUNT    ((GBM_ERR) 301)
  94. #define    GBM_ERR_BMP_CBFIX    ((GBM_ERR) 302)
  95. #define    GBM_ERR_BMP_COMP    ((GBM_ERR) 303)
  96. #define    GBM_ERR_BMP_OFFSET    ((GBM_ERR) 304)
  97.  
  98. typedef struct
  99.     {
  100.     dword base;
  101.     BOOLEAN windows;
  102.     dword cbFix;
  103.     dword ulCompression;
  104.     dword cclrUsed;
  105.     dword offBits;
  106.     BOOLEAN inv, invb;
  107.     } BMP_PRIV;
  108.  
  109. #define    BFT_BMAP    0x4d42
  110. #define    BFT_BITMAPARRAY    0x4142
  111. #define    BCA_UNCOMP    0x00000000L
  112. #define    BCA_RLE8    0x00000001L
  113. #define    BCA_RLE4    0x00000002L
  114. #define    BCA_HUFFFMAN1D    0x00000003L
  115. #define    BCA_RLE24    0x00000004L
  116. #define    MSWCC_EOL    0
  117. #define    MSWCC_EOB    1
  118. #define    MSWCC_DELTA    2
  119.  
  120. /*...sinvert:0:*/
  121. static void invert(byte *buffer, unsigned count)
  122.     {
  123.     while ( count-- )
  124.         *buffer++ ^= (byte) 0xffU;
  125.     }
  126. /*...e*/
  127. /*...sswap_pal:0:*/
  128. static void swap_pal(GBMRGB *gbmrgb)
  129.     {
  130.     GBMRGB tmp = gbmrgb[0];
  131.     gbmrgb[0] = gbmrgb[1];
  132.     gbmrgb[1] = tmp;
  133.     }
  134. /*...e*/
  135.  
  136. /*...sbmp_qft:0:*/
  137. GBM_ERR bmp_qft(GBMFT *gbmft)
  138.     {
  139.     *gbmft = bmp_gbmft;
  140.     return GBM_ERR_OK;
  141.     }
  142. /*...e*/
  143. /*...sbmp_rhdr:0:*/
  144. GBM_ERR bmp_rhdr(const char *fn, int fd, GBM *gbm, const char *opt)
  145.     {
  146.     word usType, xHotspot, yHotspot;
  147.     dword cbSize, offBits, cbFix;
  148.     BMP_PRIV *bmp_priv = (BMP_PRIV *) gbm->priv;
  149.     bmp_priv->inv  = ( gbm_find_word(opt, "inv" ) != NULL );
  150.     bmp_priv->invb = ( gbm_find_word(opt, "invb") != NULL );
  151.  
  152.     fn=fn; /* Suppress 'unref arg' compiler warnings */
  153.  
  154.     if ( !read_word(fd, &usType) )
  155.         return GBM_ERR_READ;
  156.     if ( usType == BFT_BITMAPARRAY )
  157. /*...shandle bitmap arrays:16:*/
  158. {
  159. const char *index;
  160. int i;
  161.  
  162. if ( (index = gbm_find_word_prefix(opt, "index=")) != NULL )
  163.     sscanf(index + 6, "%d", &i);
  164. else
  165.     i = 0;
  166.  
  167. while ( i-- > 0 )
  168.     {
  169.     dword cbSize2, offNext;
  170.  
  171.     if ( !read_dword(fd, &cbSize2) )
  172.         return GBM_ERR_READ;
  173.     if ( !read_dword(fd, &offNext) )
  174.         return GBM_ERR_READ;
  175.     if ( offNext == 0L )
  176.         return GBM_ERR_BMP_OFFSET;
  177.     gbm_file_lseek(fd, (long) offNext, SEEK_SET);
  178.     if ( !read_word(fd, &usType) )
  179.         return GBM_ERR_READ;
  180.     if ( usType != BFT_BITMAPARRAY )
  181.         return GBM_ERR_BAD_MAGIC;
  182.     }
  183. gbm_file_lseek(fd, 4L + 4L + 2L + 2L, SEEK_CUR);
  184. if ( !read_word(fd, &usType) )
  185.     return GBM_ERR_READ;
  186. }
  187. /*...e*/
  188.  
  189.     if ( usType != BFT_BMAP )
  190.         return GBM_ERR_BAD_MAGIC;
  191.  
  192.     bmp_priv->base = (dword) ( gbm_file_lseek(fd, 0L, SEEK_CUR) - 2L );
  193.  
  194.     if ( !read_dword(fd, &cbSize) )
  195.         return GBM_ERR_READ;
  196.     if ( !read_word(fd, &xHotspot) )
  197.         return GBM_ERR_READ;
  198.     if ( !read_word(fd, &yHotspot) )
  199.         return GBM_ERR_READ;
  200.     if ( !read_dword(fd, &offBits) )
  201.         return GBM_ERR_READ;
  202.     if ( !read_dword(fd, &cbFix) )
  203.         return GBM_ERR_READ;
  204.  
  205.     bmp_priv->offBits = offBits;
  206.  
  207.     if ( cbFix == 12 )
  208. /*...sOS\47\2 1\46\1\44\ 1\46\2:16:*/
  209. /* OS/2 1.x uncompressed bitmap */
  210. {
  211. word cx, cy, cPlanes, cBitCount;
  212.  
  213. if ( !read_word(fd, &cx) )
  214.     return GBM_ERR_READ;
  215. if ( !read_word(fd, &cy) )
  216.     return GBM_ERR_READ;
  217. if ( !read_word(fd, &cPlanes) )
  218.     return GBM_ERR_READ;
  219. if ( !read_word(fd, &cBitCount) )
  220.     return GBM_ERR_READ;
  221.  
  222. if ( cx == 0 || cy == 0 )
  223.     return GBM_ERR_BAD_SIZE;
  224. if ( cPlanes != 1 )
  225.     return GBM_ERR_BMP_PLANES;
  226. if ( cBitCount != 1 && cBitCount != 4 && cBitCount != 8 && cBitCount != 24 )
  227.     return GBM_ERR_BMP_BITCOUNT;
  228.  
  229. gbm->w   = (int) cx;
  230. gbm->h   = (int) cy;
  231. gbm->bpp = (int) cBitCount;
  232.  
  233. bmp_priv->windows = FALSE;
  234. }
  235. /*...e*/
  236.     else if ( cbFix >= 16 && cbFix <= 64 &&
  237.               ((cbFix & 3) == 0 || cbFix == 42 || cbFix == 46) )
  238. /*...sOS\47\2 2\46\0 and Windows 3\46\0:16:*/
  239. {
  240. word cPlanes, cBitCount, usUnits, usReserved, usRecording, usRendering;
  241. dword ulWidth, ulHeight, ulCompression;
  242. dword ulSizeImage, ulXPelsPerMeter, ulYPelsPerMeter;
  243. dword cclrUsed, cclrImportant, cSize1, cSize2, ulColorEncoding, ulIdentifier;
  244. BOOLEAN ok;
  245.  
  246. ok  = read_dword(fd, &ulWidth);
  247. ok &= read_dword(fd, &ulHeight);
  248. ok &= read_word(fd, &cPlanes);
  249. ok &= read_word(fd, &cBitCount);
  250. if ( cbFix > 16 )
  251.     ok &= read_dword(fd, &ulCompression);
  252. else
  253.     ulCompression = BCA_UNCOMP;
  254. if ( cbFix > 20 )
  255.     ok &= read_dword(fd, &ulSizeImage);
  256. if ( cbFix > 24 )
  257.     ok &= read_dword(fd, &ulXPelsPerMeter);
  258. if ( cbFix > 28 )
  259.     ok &= read_dword(fd, &ulYPelsPerMeter);
  260. if ( cbFix > 32 )
  261.     ok &= read_dword(fd, &cclrUsed);
  262. else
  263.     cclrUsed = ( (dword)1 << cBitCount );
  264. if ( cBitCount != 24 && cclrUsed == 0 )
  265.     cclrUsed = ( (dword)1 << cBitCount );
  266.  
  267. /* Protect against badly written bitmaps! */
  268. if ( cclrUsed > ( (dword)1 << cBitCount ) )
  269.     cclrUsed = ( (dword)1 << cBitCount );
  270.  
  271. if ( cbFix > 36 )
  272.     ok &= read_dword(fd, &cclrImportant);
  273. if ( cbFix > 40 )
  274.     ok &= read_word(fd, &usUnits);
  275. if ( cbFix > 42 )
  276.     ok &= read_word(fd, &usReserved);
  277. if ( cbFix > 44 )
  278.     ok &= read_word(fd, &usRecording);
  279. if ( cbFix > 46 )
  280.     ok &= read_word(fd, &usRendering);
  281. if ( cbFix > 48 )
  282.     ok &= read_dword(fd, &cSize1);
  283. if ( cbFix > 52 )
  284.     ok &= read_dword(fd, &cSize2);
  285. if ( cbFix > 56 )
  286.     ok &= read_dword(fd, &ulColorEncoding);
  287. if ( cbFix > 60 )
  288.     ok &= read_dword(fd, &ulIdentifier);
  289.  
  290. if ( !ok )
  291.     return GBM_ERR_READ;
  292.  
  293. if ( ulWidth == 0L || ulHeight == 0L )
  294.     return GBM_ERR_BAD_SIZE;
  295. if ( cPlanes != 1 )
  296.     return GBM_ERR_BMP_PLANES;
  297. if ( cBitCount != 1 && cBitCount != 4 && cBitCount != 8 && cBitCount != 24 )
  298.     return GBM_ERR_BMP_BITCOUNT;
  299.  
  300. gbm->w   = (int) ulWidth;
  301. gbm->h   = (int) ulHeight;
  302. gbm->bpp = (int) cBitCount;
  303.  
  304. bmp_priv->windows       = TRUE;
  305. bmp_priv->cbFix         = cbFix;
  306. bmp_priv->ulCompression = ulCompression;
  307. bmp_priv->cclrUsed      = cclrUsed;
  308. }
  309. /*...e*/
  310.     else
  311.         return GBM_ERR_BMP_CBFIX;
  312.  
  313.     return GBM_ERR_OK;
  314.     }
  315. /*...e*/
  316. /*...sbmp_rpal:0:*/
  317. GBM_ERR bmp_rpal(int fd, GBM *gbm, GBMRGB *gbmrgb)
  318.     {
  319.     BMP_PRIV *bmp_priv = (BMP_PRIV *) gbm->priv;
  320.  
  321.     if ( gbm->bpp != 24 )
  322.         {
  323.         int i;
  324.         byte b[4];
  325.  
  326.         if ( bmp_priv->windows )
  327. /*...sOS\47\2 2\46\0 and Windows 3\46\0:24:*/
  328. {
  329. gbm_file_lseek(fd, (long) (bmp_priv->base + 14L + bmp_priv->cbFix), SEEK_SET);
  330. for ( i = 0; i < (int) bmp_priv->cclrUsed; i++ )
  331.     {
  332.     gbm_file_read(fd, b, 4);
  333.     gbmrgb[i].b = b[0];
  334.     gbmrgb[i].g = b[1];
  335.     gbmrgb[i].r = b[2];
  336.     }
  337. }
  338. /*...e*/
  339.         else
  340. /*...sOS\47\2 1\46\1\44\ 1\46\2:24:*/
  341. {
  342. gbm_file_lseek(fd, (long) (bmp_priv->base + 26L), SEEK_SET);
  343. for ( i = 0; i < (1 << gbm->bpp); i++ )
  344.     {
  345.     gbm_file_read(fd, b, 3);
  346.     gbmrgb[i].b = b[0];
  347.     gbmrgb[i].g = b[1];
  348.     gbmrgb[i].r = b[2];
  349.     }
  350. }
  351. /*...e*/
  352.         }
  353.  
  354.     if ( gbm->bpp == 1 && !bmp_priv->inv )
  355.         swap_pal(gbmrgb);
  356.  
  357.     return GBM_ERR_OK;
  358.     }
  359. /*...e*/
  360. /*...sbmp_rdata:0:*/
  361. GBM_ERR bmp_rdata(int fd, GBM *gbm, byte *data)
  362.     {
  363.     BMP_PRIV *bmp_priv = (BMP_PRIV *) gbm->priv;
  364.     int cLinesWorth = ((gbm->bpp * gbm->w + 31) / 32) * 4;
  365.  
  366.     if ( bmp_priv->windows )
  367. /*...sOS\47\2 2\46\0 and Windows 3\46\0:16:*/
  368. {
  369. gbm_file_lseek(fd, (long)bmp_priv->offBits, SEEK_SET);
  370.  
  371. switch ( (int) bmp_priv->ulCompression )
  372.     {
  373. /*...sBCA_UNCOMP:24:*/
  374. case BCA_UNCOMP:
  375.     gbm_file_read(fd, data, gbm->h * cLinesWorth);
  376.     break;
  377. /*...e*/
  378. /*...sBCA_RLE8:24:*/
  379. case BCA_RLE8:
  380.     {
  381.     AHEAD *ahead;
  382.     int x = 0, y = 0;
  383.     BOOLEAN eof8 = FALSE;
  384.  
  385.     if ( (ahead = gbm_create_ahead(fd)) == NULL )
  386.         return GBM_ERR_MEM;
  387.  
  388.     while ( !eof8 )
  389.         {
  390.         byte c = (byte) gbm_read_ahead(ahead);
  391.         byte d = (byte) gbm_read_ahead(ahead);
  392.  
  393.         if ( c )
  394.             {
  395.             memset(data, d, c);
  396.             x += c;
  397.             data += c;
  398.             }
  399.         else
  400.             switch ( d )
  401.                 {
  402. /*...sMSWCC_EOL:56:*/
  403. case MSWCC_EOL:
  404.     {
  405.     int to_eol = cLinesWorth - x;
  406.  
  407.     memset(data, 0, (size_t) to_eol);
  408.     data += to_eol;
  409.     x = 0;
  410.     if ( ++y == gbm->h )
  411.         eof8 = TRUE;
  412.     }
  413.     break;
  414. /*...e*/
  415. /*...sMSWCC_EOB:56:*/
  416. case MSWCC_EOB:
  417.     if ( y < gbm->h )
  418.         {
  419.         int to_eol = cLinesWorth - x;
  420.  
  421.         memset(data, 0, (size_t) to_eol);
  422.         x = 0; y++;
  423.         data += to_eol;
  424.         while ( y < gbm->h )
  425.             {
  426.             memset(data, 0, (size_t) cLinesWorth);
  427.             data += cLinesWorth;
  428.             y++;
  429.             }
  430.         }
  431.     eof8 = TRUE;
  432.     break;
  433. /*...e*/
  434. /*...sMSWCC_DELTA:56:*/
  435. case MSWCC_DELTA:
  436.     {
  437.     byte dx = (byte) gbm_read_ahead(ahead);
  438.     byte dy = (byte) gbm_read_ahead(ahead);
  439.     int fill = dx + dy * cLinesWorth;
  440.     memset(data, 0, (size_t) fill);
  441.     data += fill;
  442.     x += dx; y += dy;
  443.     if ( y == gbm->h )
  444.         eof8 = TRUE;
  445.     }
  446.     break;
  447. /*...e*/
  448. /*...sdefault:56:*/
  449. default:
  450.     {
  451.     int n = (int) d;
  452.  
  453.     while ( n-- > 0 )
  454.         *data++ = (byte) gbm_read_ahead(ahead);
  455.     x += d;
  456.     if ( d & 1 )
  457.         gbm_read_ahead(ahead); /* Align */
  458.     }
  459.     break;
  460. /*...e*/
  461.                 }
  462.         }
  463.  
  464.     gbm_destroy_ahead(ahead);
  465.     }
  466.     break;
  467. /*...e*/
  468. /*...sBCA_RLE4:24:*/
  469. case BCA_RLE4:
  470.     {
  471.     AHEAD *ahead;
  472.     int x = 0, y = 0;
  473.     BOOLEAN eof4 = FALSE;
  474.     int inx = 0;
  475.  
  476.     if ( (ahead = gbm_create_ahead(fd)) == NULL )
  477.         return GBM_ERR_MEM;
  478.  
  479.     memset(data, 0, (size_t) (gbm->h * cLinesWorth));
  480.  
  481.     while ( !eof4 )
  482.         {
  483.         byte c = (byte) gbm_read_ahead(ahead);
  484.         byte d = (byte) gbm_read_ahead(ahead);
  485.  
  486.         if ( c )
  487.             {
  488.             byte h, l;
  489.             int i;
  490.             if ( x & 1 )
  491.                 { h = (byte) (d >> 4); l = (byte) (d << 4); }
  492.             else
  493.                 { h = (byte) (d&0xf0); l = (byte) (d&0x0f); }
  494.             for ( i = 0; i < (int) c; i++, x++ )
  495.                 {
  496.                 if ( x & 1U )
  497.                     data[inx++] |= l;
  498.                 else
  499.                     data[inx]   |= h;
  500.                 }
  501.             }
  502.         else
  503.             switch ( d )
  504.                 {
  505. /*...sMSWCC_EOL:56:*/
  506. case MSWCC_EOL:
  507.     x = 0;
  508.     if ( ++y == gbm->h )
  509.         eof4 = TRUE;
  510.     inx = cLinesWorth * y;
  511.     break;
  512. /*...e*/
  513. /*...sMSWCC_EOB:56:*/
  514. case MSWCC_EOB:
  515.     eof4 = TRUE;
  516.     break;
  517. /*...e*/
  518. /*...sMSWCC_DELTA:56:*/
  519. case MSWCC_DELTA:
  520.     {
  521.     byte dx = (byte) gbm_read_ahead(ahead);
  522.     byte dy = (byte) gbm_read_ahead(ahead);
  523.  
  524.     x += dx; y += dy;
  525.     inx = y * cLinesWorth + (x/2);
  526.         
  527.     if ( y == gbm->h )
  528.         eof4 = TRUE;
  529.     }
  530.     break;
  531. /*...e*/
  532. /*...sdefault:56:*/
  533. default:
  534.     {
  535.     int i, nr = 0;
  536.  
  537.     if ( x & 1 )
  538.         {
  539.         for ( i = 0; i+2 <= (int) d; i += 2 )
  540.             {
  541.             byte b = (byte) gbm_read_ahead(ahead);
  542.             data[inx++] |= (b >> 4);
  543.             data[inx  ] |= (b << 4);
  544.             nr++;
  545.             }
  546.         if ( i < (int) d )
  547.             {
  548.             data[inx++] |= ((byte) gbm_read_ahead(ahead) >> 4);
  549.             nr++;
  550.             }
  551.         }
  552.     else
  553.         {
  554.         for ( i = 0; i+2 <= (int) d; i += 2 )
  555.             {
  556.             data[inx++] = (byte) gbm_read_ahead(ahead);
  557.             nr++;
  558.             }
  559.         if ( i < (int) d )
  560.             {
  561.             data[inx] = (byte) gbm_read_ahead(ahead);
  562.             nr++;
  563.             }
  564.         }
  565.     x += d;
  566.  
  567.     if ( nr & 1 )
  568.         gbm_read_ahead(ahead); /* Align input stream to next word */
  569.     }
  570.     break;
  571. /*...e*/
  572.                 }
  573.         }
  574.  
  575.     gbm_destroy_ahead(ahead);
  576.     }
  577.     break;
  578. /*...e*/
  579. /*...sdefault:24:*/
  580. default:
  581.     return GBM_ERR_BMP_COMP;
  582. /*...e*/
  583.     }
  584. }
  585. /*...e*/
  586.     else
  587. /*...sOS\47\2 1\46\1\44\ 1\46\2:16:*/
  588. {
  589. gbm_file_lseek(fd, (long) bmp_priv->offBits, SEEK_SET);
  590. gbm_file_read(fd, data, cLinesWorth * gbm->h);
  591. }
  592. /*...e*/
  593.  
  594.     if ( bmp_priv->invb )
  595.         invert(data, (unsigned) (cLinesWorth * gbm->h));
  596.  
  597.     return GBM_ERR_OK;
  598.     }
  599. /*...e*/
  600. /*...sbmp_w:0:*/
  601. /*...sbright:0:*/
  602. static int bright(const GBMRGB *gbmrgb)
  603.     {
  604.     return gbmrgb->r*30+gbmrgb->g*60+gbmrgb->b*10;
  605.     }
  606. /*...e*/
  607. /*...swrite_inv:0:*/
  608. static int write_inv(int fd, const char *buffer, int count)
  609.     {
  610.     char small_buf[1024];
  611.     int so_far = 0, this_go, written;
  612.  
  613.     while ( so_far < count )
  614.         {
  615.         this_go = min(count - so_far, 1024);
  616.         memcpy(small_buf, buffer + so_far, (size_t) this_go);
  617.         invert(small_buf, (unsigned) this_go);
  618.         if ( (written = gbm_file_write(fd, small_buf, this_go)) != this_go )
  619.             return so_far + written;
  620.         so_far += written;
  621.         }
  622.  
  623.     return so_far;
  624.     }
  625. /*...e*/
  626.  
  627. GBM_ERR bmp_w(const char *fn, int fd, const GBM *gbm, const GBMRGB *gbmrgb, const byte *data, const char *opt)
  628.     {
  629.     BOOLEAN    pm11    = ( gbm_find_word(opt, "1.1"    ) != NULL );
  630.     BOOLEAN    win     = ( gbm_find_word(opt, "win"    ) != NULL ||
  631.                         gbm_find_word(opt, "2.0"    ) != NULL );
  632.     BOOLEAN inv     = ( gbm_find_word(opt, "inv"    ) != NULL );
  633.     BOOLEAN invb    = ( gbm_find_word(opt, "invb"   ) != NULL );
  634.     BOOLEAN darkfg  = ( gbm_find_word(opt, "darkfg" ) != NULL );
  635.     BOOLEAN lightfg = ( gbm_find_word(opt, "lightfg") != NULL );
  636.     int cRGB;
  637.     GBMRGB gbmrgb_1bpp[2];
  638.  
  639.     if ( pm11 && win )
  640.         return GBM_ERR_BAD_OPTION;
  641.  
  642.     fn=fn; /* Suppress 'unref arg' compiler warning */
  643.  
  644.     cRGB = ( (1 << gbm->bpp) & 0x1ff );
  645.         /* 1->2, 4->16, 8->256, 24->0 */
  646.  
  647.     if ( cRGB == 2 )
  648. /*...shandle messy 1bpp case:16:*/
  649. {
  650. /*
  651. The palette entries inside a 1bpp PM bitmap are not honored, or handled
  652. correctly by most programs. Current thinking is that they have no actual
  653. meaning. Under OS/2 PM, bitmap 1's re fg and 0's are bg, and it is the job of
  654. the displayer to pick fg and bg. We will pick fg=black, bg=white in the bitmap
  655. file we save. If we do not write black and white, we find that most programs
  656. will incorrectly honor these entries giving unpredicatable (and often black on
  657. a black background!) results.
  658. */
  659.  
  660. gbmrgb_1bpp[0].r = gbmrgb_1bpp[0].g = gbmrgb_1bpp[0].b = 0xff;
  661. gbmrgb_1bpp[1].r = gbmrgb_1bpp[1].g = gbmrgb_1bpp[1].b = 0x00;
  662.  
  663. /*
  664. We observe these values must be the wrong way around to keep most PM
  665. programs happy, such as WorkPlace Shell WPFolder backgrounds.
  666. */
  667.  
  668. if ( !inv )
  669.     swap_pal(gbmrgb_1bpp);
  670.  
  671. /*
  672. If the user has picked the darkfg option, then they intend that the darkest
  673. colour in the image is to be the foreground. This is a very sensible option
  674. because the foreground will appear to be black when the image is reloaded.
  675. To acheive this we must invert the bitmap bits, if the palette dictates.
  676. */
  677.  
  678. if ( darkfg && bright(&gbmrgb[0]) < bright(&gbmrgb[1]) )
  679.     invb = !invb;
  680. if ( lightfg && bright(&gbmrgb[0]) >= bright(&gbmrgb[1]) )
  681.     invb = !invb;
  682.  
  683. gbmrgb = gbmrgb_1bpp;
  684. }
  685. /*...e*/
  686.  
  687.     if ( pm11 )
  688. /*...sOS\47\2 1\46\1:16:*/
  689. {
  690. word usType     = BFT_BMAP;
  691. word xHotspot   = 0;
  692. word yHotspot   = 0;
  693. dword cbFix     = (dword) 12;
  694. word cx         = (word) gbm->w;
  695. word cy         = (word) gbm->h;
  696. word cPlanes    = (word) 1;
  697. word cBitCount  = (word) gbm->bpp;
  698. int cLinesWorth = (((cBitCount * cx + 31) / 32) * cPlanes) * 4;
  699. dword offBits   = (dword) 26 + cRGB * (dword) 3;
  700. dword cbSize    = offBits + (dword) cy * (dword) cLinesWorth;
  701. int i, total, actual;
  702.  
  703. write_word(fd, usType);
  704. write_dword(fd, cbSize);
  705. write_word(fd, xHotspot);
  706. write_word(fd, yHotspot);
  707. write_dword(fd, offBits);
  708. write_dword(fd, cbFix);
  709. write_word(fd, cx);
  710. write_word(fd, cy);
  711. write_word(fd, cPlanes);
  712. write_word(fd, cBitCount);
  713.  
  714. for ( i = 0; i < cRGB; i++ )
  715.     {
  716.     byte b[3];
  717.  
  718.     b[0] = gbmrgb[i].b;
  719.     b[1] = gbmrgb[i].g;
  720.     b[2] = gbmrgb[i].r;
  721.     if ( gbm_file_write(fd, b, 3) != 3 )
  722.         return GBM_ERR_WRITE;
  723.     }
  724.  
  725. total = gbm->h * cLinesWorth;
  726. if ( invb )
  727.     actual = write_inv(fd, data, total);
  728. else
  729.     actual = gbm_file_write(fd, data, total);
  730. if ( actual != total )
  731.     return GBM_ERR_WRITE;
  732. }
  733. /*...e*/
  734.     else
  735. /*...sOS\47\2 2\46\0 and Windows 3\46\0:16:*/
  736. {
  737. word usType         = BFT_BMAP;
  738. word xHotspot       = 0;
  739. word yHotspot       = 0;
  740. dword cbFix         = (dword) 40;
  741. dword cx            = (dword) gbm->w;
  742. dword cy            = (dword) gbm->h;
  743. word cPlanes        = (word) 1;
  744. word cBitCount      = (word) gbm->bpp;
  745. int cLinesWorth     = (((cBitCount * (int) cx + 31) / 32) * cPlanes) * 4;
  746. dword offBits       = (dword) 54 + cRGB * (dword) 4;
  747. dword cbSize        = offBits + (dword) cy * (dword) cLinesWorth;
  748. dword ulCompression = BCA_UNCOMP;
  749. dword cbImage       = (dword) cLinesWorth * (dword) gbm->h;
  750. dword cxResolution  = 0;
  751. dword cyResolution  = 0;
  752. dword cclrUsed      = 0;
  753. dword cclrImportant = 0;
  754. int i, total, actual;
  755.  
  756. write_word(fd, usType);
  757. write_dword(fd, cbSize);
  758. write_word(fd, xHotspot);
  759. write_word(fd, yHotspot);
  760. write_dword(fd, offBits);
  761.  
  762. write_dword(fd, cbFix);
  763. write_dword(fd, cx);
  764. write_dword(fd, cy);
  765. write_word(fd, cPlanes);
  766. write_word(fd, cBitCount);
  767. write_dword(fd, ulCompression);
  768. write_dword(fd, cbImage);
  769. write_dword(fd, cxResolution);
  770. write_dword(fd, cyResolution);
  771. write_dword(fd, cclrUsed);
  772. write_dword(fd, cclrImportant);
  773.  
  774. for ( i = 0; i < cRGB; i++ )
  775.     {
  776.     byte b[4];
  777.  
  778.     b[0] = gbmrgb[i].b;
  779.     b[1] = gbmrgb[i].g;
  780.     b[2] = gbmrgb[i].r;
  781.     b[3] = 0;
  782.     if ( gbm_file_write(fd, b, 4) != 4 )
  783.         return GBM_ERR_WRITE;
  784.     }
  785.  
  786. total = gbm->h * cLinesWorth;
  787. if ( invb )
  788.     actual = write_inv(fd, data, total);
  789. else
  790.     actual = gbm_file_write(fd, data, total);
  791. if ( actual != total )
  792.     return GBM_ERR_WRITE;
  793. }
  794. /*...e*/
  795.  
  796.     return GBM_ERR_OK;
  797.     }
  798. /*...e*/
  799. /*...sbmp_err:0:*/
  800. const char *bmp_err(GBM_ERR rc)
  801.     {
  802.     switch ( (int) rc )
  803.         {
  804.         case GBM_ERR_BMP_PLANES:
  805.             return "number of bitmap planes is not 1";
  806.         case GBM_ERR_BMP_BITCOUNT:
  807.             return "bit count not 1, 4, 8 or 24";
  808.         case GBM_ERR_BMP_CBFIX:
  809.             return "cbFix bad";
  810.         case GBM_ERR_BMP_COMP:
  811.             return "compression type not uncompressed, RLE4 or RLE8";
  812.         case GBM_ERR_BMP_OFFSET:
  813.             return "less bitmaps in file than index requested";
  814.         }
  815.     return NULL;
  816.     }
  817. /*...e*/
  818.