home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / gd201.zip / gd-2.0.1 / gd_gd2.c < prev    next >
C/C++ Source or Header  |  2001-04-03  |  20KB  |  924 lines

  1. /*
  2.    * gd_gd2.c
  3.    *
  4.    * Implements the I/O and support for the GD2 format.
  5.    *
  6.    * Changing the definition of GD2_DBG (below) will cause copious messages
  7.    * to be displayed while it processes requests.
  8.    *
  9.    * Designed, Written & Copyright 1999, Philip Warner.
  10.    *
  11.  */
  12.  
  13. #include <stdio.h>
  14. #include <errno.h>
  15. #include <math.h>
  16. #include <string.h>
  17. #include <stdlib.h>
  18. #include <zlib.h>
  19. #include "gd.h"
  20. #include "gdhelpers.h"
  21.  
  22. #define TRUE 1
  23. #define FALSE 0
  24.  
  25. /* Use this for commenting out debug-print statements. */
  26. /* Just use the first '#define' to allow all the prints... */
  27. /*#define GD2_DBG(s) (s) */
  28. #define GD2_DBG(s)
  29.  
  30. typedef struct
  31.   {
  32.     int offset;
  33.     int size;
  34.   }
  35. t_chunk_info;
  36.  
  37. extern int _gdGetColors (gdIOCtx * in, gdImagePtr im, int gd2xFlag);
  38. extern void _gdPutColors (gdImagePtr im, gdIOCtx * out);
  39.  
  40. /* */
  41. /* Read the extra info in the gd2 header. */
  42. /* */
  43. static
  44. int
  45. _gd2GetHeader (gdIOCtxPtr in, int *sx, int *sy,
  46.  int *cs, int *vers, int *fmt, int *ncx, int *ncy, t_chunk_info ** chunkIdx)
  47. {
  48.   int i;
  49.   int ch;
  50.   char id[5];
  51.   t_chunk_info *cidx;
  52.   int sidx;
  53.   int nc;
  54.  
  55.   GD2_DBG (printf ("Reading gd2 header info\n"));
  56.  
  57.   for (i = 0; i < 4; i++)
  58.     {
  59.       ch = gdGetC (in);
  60.       if (ch == EOF)
  61.     {
  62.       goto fail1;
  63.     };
  64.       id[i] = ch;
  65.     };
  66.   id[4] = 0;
  67.  
  68.   GD2_DBG (printf ("Got file code: %s\n", id));
  69.  
  70.   /* Equiv. of 'magick'.  */
  71.   if (strcmp (id, GD2_ID) != 0)
  72.     {
  73.       GD2_DBG (printf ("Not a valid gd2 file\n"));
  74.       goto fail1;
  75.     };
  76.  
  77.   /* Version */
  78.   if (gdGetWord (vers, in) != 1)
  79.     {
  80.       goto fail1;
  81.     };
  82.   GD2_DBG (printf ("Version: %d\n", *vers));
  83.  
  84.   if ((*vers != 1) && (*vers != 2))
  85.     {
  86.       GD2_DBG (printf ("Bad version: %d\n", *vers));
  87.       goto fail1;
  88.     };
  89.  
  90.   /* Image Size */
  91.   if (!gdGetWord (sx, in))
  92.     {
  93.       GD2_DBG (printf ("Could not get x-size\n"));
  94.       goto fail1;
  95.     }
  96.   if (!gdGetWord (sy, in))
  97.     {
  98.       GD2_DBG (printf ("Could not get y-size\n"));
  99.       goto fail1;
  100.     }
  101.   GD2_DBG (printf ("Image is %dx%d\n", *sx, *sy));
  102.  
  103.   /* Chunk Size (pixels, not bytes!) */
  104.   if (gdGetWord (cs, in) != 1)
  105.     {
  106.       goto fail1;
  107.     };
  108.   GD2_DBG (printf ("ChunkSize: %d\n", *cs));
  109.  
  110.   if ((*cs < GD2_CHUNKSIZE_MIN) || (*cs > GD2_CHUNKSIZE_MAX))
  111.     {
  112.       GD2_DBG (printf ("Bad chunk size: %d\n", *cs));
  113.       goto fail1;
  114.     };
  115.  
  116.   /* Data Format */
  117.   if (gdGetWord (fmt, in) != 1)
  118.     {
  119.       goto fail1;
  120.     };
  121.   GD2_DBG (printf ("Format: %d\n", *fmt));
  122.  
  123.   if ((*fmt != GD2_FMT_RAW) && (*fmt != GD2_FMT_COMPRESSED))
  124.     {
  125.       GD2_DBG (printf ("Bad data format: %d\n", *fmt));
  126.       goto fail1;
  127.     };
  128.  
  129.  
  130.   /* # of chunks wide */
  131.   if (gdGetWord (ncx, in) != 1)
  132.     {
  133.       goto fail1;
  134.     };
  135.   GD2_DBG (printf ("%d Chunks Wide\n", *ncx));
  136.  
  137.   /* # of chunks high */
  138.   if (gdGetWord (ncy, in) != 1)
  139.     {
  140.       goto fail1;
  141.     };
  142.   GD2_DBG (printf ("%d Chunks vertically\n", *ncy));
  143.  
  144.   if ((*fmt) == GD2_FMT_COMPRESSED)
  145.     {
  146.       nc = (*ncx) * (*ncy);
  147.       GD2_DBG (printf ("Reading %d chunk index entries\n", nc));
  148.       sidx = sizeof (t_chunk_info) * nc;
  149.       cidx = gdCalloc (sidx, 1);
  150.       for (i = 0; i < nc; i++)
  151.     {
  152.       if (gdGetInt (&cidx[i].offset, in) != 1)
  153.         {
  154.           goto fail1;
  155.         };
  156.       if (gdGetInt (&cidx[i].size, in) != 1)
  157.         {
  158.           goto fail1;
  159.         };
  160.     };
  161.       *chunkIdx = cidx;
  162.     };
  163.  
  164.   GD2_DBG (printf ("gd2 header complete\n"));
  165.  
  166.   return 1;
  167.  
  168. fail1:
  169.   return 0;
  170. }
  171.  
  172. static
  173.   gdImagePtr
  174. _gd2CreateFromFile (gdIOCtxPtr in, int *sx, int *sy,
  175.             int *cs, int *vers, int *fmt,
  176.             int *ncx, int *ncy, t_chunk_info ** cidx)
  177. {
  178.   gdImagePtr im;
  179.  
  180.   if (_gd2GetHeader (in, sx, sy, cs, vers, fmt, ncx, ncy, cidx) != 1)
  181.     {
  182.       GD2_DBG (printf ("Bad GD2 header\n"));
  183.       goto fail1;
  184.     }
  185.  
  186.   im = gdImageCreate (*sx, *sy);
  187.   if (im == NULL)
  188.     {
  189.       GD2_DBG (printf ("Could not create gdImage\n"));
  190.       goto fail1;
  191.     };
  192.  
  193.   if (!_gdGetColors (in, im, (*vers) == 2))
  194.     {
  195.       GD2_DBG (printf ("Could not read color palette\n"));
  196.       goto fail2;
  197.     }
  198.   GD2_DBG (printf ("Image palette completed: %d colours\n", im->colorsTotal));
  199.  
  200.   return im;
  201.  
  202. fail2:
  203.   gdImageDestroy (im);
  204.   return 0;
  205.  
  206. fail1:
  207.   return 0;
  208.  
  209. }
  210.  
  211. static
  212. int
  213. _gd2ReadChunk (int offset, char *compBuf, int compSize, char *chunkBuf, uLongf * chunkLen, gdIOCtx * in)
  214. {
  215.   int zerr;
  216.  
  217.   if (gdTell (in) != offset)
  218.     {
  219.       GD2_DBG (printf ("Positioning in file to %d\n", offset));
  220.       gdSeek (in, offset);
  221.     }
  222.   else
  223.     {
  224.       GD2_DBG (printf ("Already Positioned in file to %d\n", offset));
  225.     };
  226.  
  227.   /* Read and uncompress an entire chunk. */
  228.   GD2_DBG (printf ("Reading file\n"));
  229.   if (gdGetBuf (compBuf, compSize, in) != compSize)
  230.     {
  231.       return FALSE;
  232.     };
  233.   GD2_DBG (printf ("Got %d bytes. Uncompressing into buffer of %d bytes\n", compSize, *chunkLen));
  234.   zerr = uncompress ((unsigned char *) chunkBuf, chunkLen,
  235.              (unsigned char *) compBuf, compSize);
  236.   if (zerr != Z_OK)
  237.     {
  238.       GD2_DBG (printf ("Error %d from uncompress\n", zerr));
  239.       return FALSE;
  240.     };
  241.   GD2_DBG (printf ("Got chunk\n"));
  242.   return TRUE;
  243. }
  244.  
  245. gdImagePtr
  246. gdImageCreateFromGd2 (FILE * inFile)
  247. {
  248.   gdIOCtx *in = gdNewFileCtx (inFile);
  249.   gdImagePtr im;
  250.  
  251.   im = gdImageCreateFromGd2Ctx (in);
  252.  
  253.   in->free (in);
  254.  
  255.   return im;
  256. }
  257.  
  258. gdImagePtr
  259. gdImageCreateFromGd2Ctx (gdIOCtxPtr in)
  260. {
  261.   int sx, sy;
  262.   int i;
  263.   int ncx, ncy, nc, cs, cx, cy;
  264.   int x, y, ylo, yhi, xlo, xhi;
  265.   int ch, vers, fmt;
  266.   t_chunk_info *chunkIdx = NULL;    /* So we can gdFree it with impunity. */
  267.   unsigned char *chunkBuf = NULL;    /* So we can gdFree it with impunity. */
  268.   int chunkNum = 0;
  269.   int chunkMax;
  270.   uLongf chunkLen;
  271.   int chunkPos;
  272.   int compMax;
  273.   int bytesPerPixel;
  274.   char *compBuf = NULL;        /* So we can gdFree it with impunity. */
  275.  
  276.   gdImagePtr im;
  277.  
  278.   /* Get the header */
  279.   im = _gd2CreateFromFile (in, &sx, &sy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx);
  280.  
  281.   if (im == NULL)
  282.     {
  283.       return 0;
  284.     };
  285.   bytesPerPixel = im->trueColor ? 4 : 1;
  286.   nc = ncx * ncy;
  287.  
  288.   if (fmt == GD2_FMT_COMPRESSED)
  289.     {
  290.       /* Find the maximum compressed chunk size. */
  291.       compMax = 0;
  292.       for (i = 0; (i < nc); i++)
  293.     {
  294.       if (chunkIdx[i].size > compMax)
  295.         {
  296.           compMax = chunkIdx[i].size;
  297.         };
  298.     };
  299.       compMax++;
  300.  
  301.       /* Allocate buffers */
  302.       chunkMax = cs * bytesPerPixel * cs;
  303.       chunkBuf = gdCalloc (chunkMax, 1);
  304.       compBuf = gdCalloc (compMax, 1);
  305.       GD2_DBG (printf ("Largest compressed chunk is %d bytes\n", compMax));
  306.     };
  307.  
  308. /*      if ( (ncx != sx / cs) || (ncy != sy / cs)) { */
  309. /*              goto fail2; */
  310. /*      }; */
  311.  
  312.   /* Read the data... */
  313.   for (cy = 0; (cy < ncy); cy++)
  314.     {
  315.       for (cx = 0; (cx < ncx); cx++)
  316.     {
  317.  
  318.       ylo = cy * cs;
  319.       yhi = ylo + cs;
  320.       if (yhi > im->sy)
  321.         {
  322.           yhi = im->sy;
  323.         };
  324.  
  325.       GD2_DBG (printf ("Processing Chunk %d (%d, %d), y from %d to %d\n", chunkNum, cx, cy, ylo, yhi));
  326.  
  327.       if (fmt == GD2_FMT_COMPRESSED)
  328.         {
  329.  
  330.           chunkLen = chunkMax;
  331.  
  332.           if (!_gd2ReadChunk (chunkIdx[chunkNum].offset,
  333.                   compBuf,
  334.                   chunkIdx[chunkNum].size,
  335.                   chunkBuf, &chunkLen, in))
  336.         {
  337.           GD2_DBG (printf ("Error reading comproessed chunk\n"));
  338.           goto fail2;
  339.         };
  340.  
  341.           chunkPos = 0;
  342.         };
  343.  
  344.       for (y = ylo; (y < yhi); y++)
  345.         {
  346.  
  347.           xlo = cx * cs;
  348.           xhi = xlo + cs;
  349.           if (xhi > im->sx)
  350.         {
  351.           xhi = im->sx;
  352.         };
  353.           /*GD2_DBG(printf("y=%d: ",y)); */
  354.           if (fmt == GD2_FMT_RAW)
  355.         {
  356.           for (x = xlo; x < xhi; x++)
  357.             {
  358.  
  359.               if (im->trueColor)
  360.             {
  361.               if (!gdGetInt (&im->tpixels[y][x], in))
  362.                 {
  363.                   /*printf("EOF while reading\n"); */
  364.                   /*gdImageDestroy(im); */
  365.                   /*return 0; */
  366.                   im->tpixels[y][x] = 0;
  367.                 }
  368.             }
  369.               else
  370.             {
  371.               int ch;
  372.               if (!gdGetByte (&ch, in))
  373.                 {
  374.                   /*printf("EOF while reading\n"); */
  375.                   /*gdImageDestroy(im); */
  376.                   /*return 0; */
  377.                   ch = 0;
  378.                 }
  379.               im->pixels[y][x] = ch;
  380.             }
  381.             }
  382.         }
  383.           else
  384.         {
  385.           for (x = xlo; x < xhi; x++)
  386.             {
  387.               if (im->trueColor)
  388.             {
  389.               /* 2.0.1: work around a gcc bug by being verbose.
  390.                  TBB */
  391.               int a = chunkBuf[chunkPos++] << 24;
  392.               int r = chunkBuf[chunkPos++] << 16;
  393.               int g = chunkBuf[chunkPos++] << 8;
  394.               int b = chunkBuf[chunkPos++];
  395.               im->pixels[y][x] = a + r + g + b;
  396.             }
  397.               else
  398.             {
  399.               im->pixels[y][x] = chunkBuf[chunkPos++];
  400.             }
  401.             };
  402.         };
  403.           /*GD2_DBG(printf("\n")); */
  404.         };
  405.       chunkNum++;
  406.     };
  407.     };
  408.  
  409.   GD2_DBG (printf ("Freeing memory\n"));
  410.  
  411.   gdFree (chunkBuf);
  412.   gdFree (compBuf);
  413.   gdFree (chunkIdx);
  414.  
  415.   GD2_DBG (printf ("Done\n"));
  416.  
  417.   return im;
  418.  
  419. fail2:
  420.   gdImageDestroy (im);
  421.   gdFree (chunkBuf);
  422.   gdFree (compBuf);
  423.   gdFree (chunkIdx);
  424.   return 0;
  425.  
  426. }
  427.  
  428. gdImagePtr
  429. gdImageCreateFromGd2Part (FILE * inFile, int srcx, int srcy, int w, int h)
  430. {
  431.   gdImagePtr im;
  432.   gdIOCtx *in = gdNewFileCtx (inFile);
  433.  
  434.   im = gdImageCreateFromGd2PartCtx (in, srcx, srcy, w, h);
  435.  
  436.   in->free (in);
  437.  
  438.   return im;
  439. }
  440.  
  441. gdImagePtr
  442. gdImageCreateFromGd2PartCtx (gdIOCtx * in, int srcx, int srcy, int w, int h)
  443. {
  444.   int scx, scy, ecx, ecy, fsx, fsy;
  445.   int nc, ncx, ncy, cs, cx, cy;
  446.   int x, y, ylo, yhi, xlo, xhi;
  447.   int dstart, dpos;
  448.   int i;
  449.   int ch, vers, fmt;
  450.   t_chunk_info *chunkIdx = NULL;
  451.   char *chunkBuf = NULL;
  452.   int chunkNum;
  453.   int chunkMax;
  454.   uLongf chunkLen;
  455.   int chunkPos;
  456.   int compMax;
  457.   char *compBuf = NULL;
  458.  
  459.   gdImagePtr im;
  460.  
  461.   /* */
  462.   /* The next few lines are basically copied from gd2CreateFromFile */
  463.   /* - we change the file size, so don't want to use the code directly. */
  464.   /*   but we do need to know the file size. */
  465.   /* */
  466.   if (_gd2GetHeader (in, &fsx, &fsy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx) != 1)
  467.     {
  468.       goto fail1;
  469.     }
  470.  
  471.   GD2_DBG (printf ("File size is %dx%d\n", fsx, fsy));
  472.  
  473.   /* This is the difference - make a file based on size of chunks. */
  474.   im = gdImageCreate (w, h);
  475.   if (im == NULL)
  476.     {
  477.       goto fail1;
  478.     };
  479.  
  480.   if (!_gdGetColors (in, im, vers == 2))
  481.     {
  482.       goto fail2;
  483.     }
  484.   GD2_DBG (printf ("Image palette completed: %d colours\n", im->colorsTotal));
  485.  
  486.   /* Process the header info */
  487.   nc = ncx * ncy;
  488.  
  489.   if (fmt == GD2_FMT_COMPRESSED)
  490.     {
  491.       /* Find the maximum compressed chunk size. */
  492.       compMax = 0;
  493.       for (i = 0; (i < nc); i++)
  494.     {
  495.       if (chunkIdx[i].size > compMax)
  496.         {
  497.           compMax = chunkIdx[i].size;
  498.         };
  499.     };
  500.       compMax++;
  501.  
  502.       if (im->trueColor)
  503.     {
  504.       chunkMax = cs * cs * 4;
  505.     }
  506.       else
  507.     {
  508.       chunkMax = cs * cs;
  509.     }
  510.       chunkBuf = gdCalloc (chunkMax, 1);
  511.       compBuf = gdCalloc (compMax, 1);
  512.     };
  513.  
  514. /*      Don't bother with this... */
  515. /*      if ( (ncx != sx / cs) || (ncy != sy / cs)) { */
  516. /*              goto fail2; */
  517. /*      }; */
  518.  
  519.  
  520.   /* Work out start/end chunks */
  521.   scx = srcx / cs;
  522.   scy = srcy / cs;
  523.   if (scx < 0)
  524.     {
  525.       scx = 0;
  526.     };
  527.   if (scy < 0)
  528.     {
  529.       scy = 0;
  530.     };
  531.  
  532.   ecx = (srcx + w) / cs;
  533.   ecy = (srcy + h) / cs;
  534.   if (ecx >= ncx)
  535.     {
  536.       ecx = ncx - 1;
  537.     };
  538.   if (ecy >= ncy)
  539.     {
  540.       ecy = ncy - 1;
  541.     };
  542.  
  543.   /* Remember file position of image data. */
  544.   dstart = gdTell (in);
  545.   GD2_DBG (printf ("Data starts at %d\n", dstart));
  546.  
  547.   /* Loop through the chunks. */
  548.   for (cy = scy; (cy <= ecy); cy++)
  549.     {
  550.  
  551.       ylo = cy * cs;
  552.       yhi = ylo + cs;
  553.       if (yhi > fsy)
  554.     {
  555.       yhi = fsy;
  556.     };
  557.  
  558.       for (cx = scx; (cx <= ecx); cx++)
  559.     {
  560.  
  561.       xlo = cx * cs;
  562.       xhi = xlo + cs;
  563.       if (xhi > fsx)
  564.         {
  565.           xhi = fsx;
  566.         };
  567.  
  568.       GD2_DBG (printf ("Processing Chunk (%d, %d), from %d to %d\n", cx, cy, ylo, yhi));
  569.  
  570.       if (fmt == GD2_FMT_RAW)
  571.         {
  572.           GD2_DBG (printf ("Using raw format data\n"));
  573.           if (im->trueColor)
  574.         {
  575.           dpos = (cy * (cs * fsx) + cx * cs * (yhi - ylo) * 4) + dstart;
  576.         }
  577.           else
  578.         {
  579.           dpos = cy * (cs * fsx) + cx * cs * (yhi - ylo) + dstart;
  580.         }
  581.  
  582.           if (gdSeek (in, dpos) != 0)
  583.         {
  584.           printf ("Error from seek: %d\n", errno);
  585.           goto fail2;
  586.         };
  587.           GD2_DBG (printf ("Reading (%d, %d) from position %d\n", cx, cy, dpos - dstart));
  588.         }
  589.       else
  590.         {
  591.           chunkNum = cx + cy * ncx;
  592.  
  593.           chunkLen = chunkMax;
  594.           if (!_gd2ReadChunk (chunkIdx[chunkNum].offset,
  595.                   compBuf,
  596.                   chunkIdx[chunkNum].size,
  597.                   chunkBuf, &chunkLen, in))
  598.         {
  599.           printf ("Error reading comproessed chunk\n");
  600.           goto fail2;
  601.         };
  602.           chunkPos = 0;
  603.           GD2_DBG (printf ("Reading (%d, %d) from chunk %d\n", cx, cy, chunkNum));
  604.         };
  605.  
  606.       GD2_DBG (printf ("   into (%d, %d) - (%d, %d)\n", xlo, ylo, xhi, yhi));
  607.       for (y = ylo; (y < yhi); y++)
  608.         {
  609.  
  610.           for (x = xlo; x < xhi; x++)
  611.         {
  612.           if (fmt == GD2_FMT_RAW)
  613.             {
  614.               if (im->trueColor)
  615.             {
  616.               if (!gdGetInt (&ch, in))
  617.                 {
  618.                   ch = 0;
  619.                   /*printf("EOF while reading file\n"); */
  620.                   /*goto fail2; */
  621.                 }
  622.             }
  623.               else
  624.             {
  625.               ch = gdGetC (in);
  626.               if (ch == EOF)
  627.                 {
  628.                   ch = 0;
  629.                   /*printf("EOF while reading file\n"); */
  630.                   /*goto fail2; */
  631.                 }
  632.             }
  633.             }
  634.           else
  635.             {
  636.               if (im->trueColor)
  637.             {
  638.               ch = chunkBuf[chunkPos++] << 24 +
  639.                 chunkBuf[chunkPos++] << 16 +
  640.                 chunkBuf[chunkPos++] << 8 +
  641.                 chunkBuf[chunkPos++];
  642.             }
  643.               else
  644.             {
  645.               ch = chunkBuf[chunkPos++];
  646.             }
  647.             };
  648.  
  649.           /* Only use a point that is in the image. */
  650.           if ((x >= srcx) && (x < (srcx + w)) && (x < fsx) && (x >= 0)
  651.           && (y >= srcy) && (y < (srcy + h)) && (y < fsy) && (y >= 0)
  652.             )
  653.             {
  654.               im->pixels[y - srcy][x - srcx] = ch;
  655.             }
  656.         };
  657.         };
  658.     };
  659.     };
  660.  
  661.   gdFree (chunkBuf);
  662.   gdFree (compBuf);
  663.   gdFree (chunkIdx);
  664.  
  665.   return im;
  666.  
  667. fail2:
  668.   gdImageDestroy (im);
  669. fail1:
  670.   gdFree (chunkBuf);
  671.   gdFree (compBuf);
  672.   gdFree (chunkIdx);
  673.  
  674.   return 0;
  675.  
  676. }
  677.  
  678. static
  679. void
  680. _gd2PutHeader (gdImagePtr im, gdIOCtx * out, int cs, int fmt, int cx, int cy)
  681. {
  682.   int i;
  683.  
  684.   /* Send the gd2 id, to verify file format. */
  685.   for (i = 0; i < 4; i++)
  686.     {
  687.       gdPutC ((unsigned char) (GD2_ID[i]), out);
  688.     };
  689.  
  690.   /* */
  691.   /* We put the version info first, so future versions can easily change header info. */
  692.   /* */
  693.   gdPutWord (GD2_VERS, out);
  694.   gdPutWord (im->sx, out);
  695.   gdPutWord (im->sy, out);
  696.   gdPutWord (cs, out);
  697.   gdPutWord (fmt, out);
  698.   gdPutWord (cx, out);
  699.   gdPutWord (cy, out);
  700.  
  701. }
  702.  
  703. static void
  704. _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)
  705. {
  706.   int ncx, ncy, cx, cy;
  707.   int x, y, ylo, yhi, xlo, xhi;
  708.   int chunkLen;
  709.   int chunkNum = 0;
  710.   char *chunkData = NULL;    /* So we can gdFree it with impunity. */
  711.   char *compData = NULL;    /* So we can gdFree it with impunity. */
  712.   uLongf compLen;
  713.   int idxPos;
  714.   int idxSize;
  715.   t_chunk_info *chunkIdx = NULL;
  716.   int posSave;
  717.   int bytesPerPixel = im->trueColor ? 4 : 1;
  718.   int compMax;
  719.  
  720.   /*printf("Trying to write GD2 file\n"); */
  721.  
  722.   /* */
  723.   /* Force fmt to a valid value since we don't return anything. */
  724.   /* */
  725.   if ((fmt == 0) || ((fmt != GD2_FMT_RAW) && (fmt != GD2_FMT_COMPRESSED)))
  726.     {
  727.       fmt = GD2_FMT_COMPRESSED;
  728.     };
  729.  
  730.   /* */
  731.   /* Make sure chunk size is valid. These are arbitrary values; 64 because it seems */
  732.   /* a little silly to expect performance improvements on a 64x64 bit scale, and  */
  733.   /* 4096 because we buffer one chunk, and a 16MB buffer seems a little largei - it may be */
  734.   /* OK for one user, but for another to read it, they require the buffer. */
  735.   /* */
  736.   if (cs == 0)
  737.     {
  738.       cs = GD2_CHUNKSIZE;
  739.     }
  740.   else if (cs < GD2_CHUNKSIZE_MIN)
  741.     {
  742.       cs = GD2_CHUNKSIZE_MIN;
  743.     }
  744.   else if (cs > GD2_CHUNKSIZE_MAX)
  745.     {
  746.       cs = GD2_CHUNKSIZE_MAX;
  747.     };
  748.  
  749.   /* Work out number of chunks. */
  750.   ncx = im->sx / cs + 1;
  751.   ncy = im->sy / cs + 1;
  752.  
  753.   /* Write the standard header. */
  754.   _gd2PutHeader (im, out, cs, fmt, ncx, ncy);
  755.  
  756.   if (fmt == GD2_FMT_COMPRESSED)
  757.     {
  758.       /* */
  759.       /* Work out size of buffer for compressed data, If CHUNKSIZE is large, */
  760.       /* then these will be large! */
  761.       /* */
  762.       /* The zlib notes say output buffer size should be (input size) * 1.01 * 12 */
  763.       /* - we'll use 1.02 to be paranoid. */
  764.       /* */
  765.       compMax = cs * bytesPerPixel * cs * 1.02 + 12;
  766.  
  767.       /* */
  768.       /* Allocate the buffers.  */
  769.       /* */
  770.       chunkData = gdCalloc (cs * bytesPerPixel * cs, 1);
  771.       compData = gdCalloc (compMax, 1);
  772.  
  773.       /* */
  774.       /* Save the file position of chunk index, and allocate enough space for */
  775.       /* each chunk_info block . */
  776.       /* */
  777.       idxPos = gdTell (out);
  778.       idxSize = ncx * ncy * sizeof (t_chunk_info);
  779.       GD2_DBG (printf ("Index size is %d\n", idxSize));
  780.       gdSeek (out, idxPos + idxSize);
  781.  
  782.       chunkIdx = gdCalloc (idxSize * sizeof (t_chunk_info), 1);
  783.     };
  784.  
  785.   _gdPutColors (im, out);
  786.  
  787.   GD2_DBG (printf ("Size: %dx%d\n", im->sx, im->sy));
  788.   GD2_DBG (printf ("Chunks: %dx%d\n", ncx, ncy));
  789.  
  790.   for (cy = 0; (cy < ncy); cy++)
  791.     {
  792.       for (cx = 0; (cx < ncx); cx++)
  793.     {
  794.  
  795.       ylo = cy * cs;
  796.       yhi = ylo + cs;
  797.       if (yhi > im->sy)
  798.         {
  799.           yhi = im->sy;
  800.         };
  801.  
  802.       GD2_DBG (printf ("Processing Chunk (%dx%d), y from %d to %d\n", cx, cy, ylo, yhi));
  803.       chunkLen = 0;
  804.       for (y = ylo; (y < yhi); y++)
  805.         {
  806.  
  807.           /*GD2_DBG(printf("y=%d: ",y)); */
  808.  
  809.           xlo = cx * cs;
  810.           xhi = xlo + cs;
  811.           if (xhi > im->sx)
  812.         {
  813.           xhi = im->sx;
  814.         };
  815.  
  816.           if (fmt == GD2_FMT_COMPRESSED)
  817.         {
  818.           for (x = xlo; x < xhi; x++)
  819.             {
  820.               int p = im->pixels[y][x];
  821.               /*GD2_DBG(printf("%d...",x)); */
  822.               if (im->trueColor)
  823.             {
  824.               chunkData[chunkLen++] = gdTrueColorGetAlpha (p);
  825.               chunkData[chunkLen++] = gdTrueColorGetRed (p);
  826.               chunkData[chunkLen++] = gdTrueColorGetGreen (p);
  827.               chunkData[chunkLen++] = gdTrueColorGetBlue (p);
  828.             }
  829.               else
  830.             {
  831.               chunkData[chunkLen++] = p;
  832.             }
  833.             };
  834.         }
  835.           else
  836.         {
  837.           for (x = xlo; x < xhi; x++)
  838.             {
  839.               /*GD2_DBG(printf("%d, ",x)); */
  840.  
  841.               if (im->trueColor)
  842.             {
  843.               gdPutInt (im->tpixels[y][x], out);
  844.             }
  845.               else
  846.             {
  847.               gdPutC ((unsigned char) im->pixels[y][x], out);
  848.             }
  849.             };
  850.         };
  851.           /*GD2_DBG(printf("y=%d done.\n",y)); */
  852.         };
  853.       if (fmt == GD2_FMT_COMPRESSED)
  854.         {
  855.           compLen = compMax;
  856.           if (compress ((unsigned char *)
  857.                 &compData[0], &compLen,
  858.                 (unsigned char *) &chunkData[0],
  859.                 chunkLen) != Z_OK)
  860.         {
  861.           printf ("Error from compressing\n");
  862.         }
  863.           else
  864.         {
  865.           chunkIdx[chunkNum].offset = gdTell (out);
  866.           chunkIdx[chunkNum++].size = compLen;
  867.           GD2_DBG (printf ("Chunk %d size %d offset %d\n", chunkNum, chunkIdx[chunkNum - 1].size, chunkIdx[chunkNum - 1].offset));
  868.  
  869.           if (gdPutBuf (compData, compLen, out) <= 0)
  870.             {
  871.               /* Any alternate suggestions for handling this? */
  872.               printf ("Error %d on write\n", errno);
  873.             };
  874.         };
  875.         };
  876.     };
  877.     };
  878.   if (fmt == GD2_FMT_COMPRESSED)
  879.     {
  880.       /* Save the position, write the index, restore position (paranoia). */
  881.       GD2_DBG (printf ("Seeking %d to write index\n", idxPos));
  882.       posSave = gdTell (out);
  883.       gdSeek (out, idxPos);
  884.       GD2_DBG (printf ("Writing index\n"));
  885.       for (x = 0; x < chunkNum; x++)
  886.     {
  887.       GD2_DBG (printf ("Chunk %d size %d offset %d\n", x, chunkIdx[x].size, chunkIdx[x].offset));
  888.       gdPutInt (chunkIdx[x].offset, out);
  889.       gdPutInt (chunkIdx[x].size, out);
  890.     };
  891.       /* We don't use fwrite for *endian reasons. */
  892.       /*fwrite(chunkIdx, sizeof(int)*2, chunkNum, out); */
  893.       gdSeek (out, posSave);
  894.     };
  895.  
  896.   GD2_DBG (printf ("Freeing memory\n"));
  897.   gdFree (chunkData);
  898.   gdFree (compData);
  899.   gdFree (chunkIdx);
  900.   GD2_DBG (printf ("Done\n"));
  901.  
  902.   /*printf("Memory block size is %d\n",gdTell(out)); */
  903.  
  904. }
  905.  
  906. void
  907. gdImageGd2 (gdImagePtr im, FILE * outFile, int cs, int fmt)
  908. {
  909.   gdIOCtx *out = gdNewFileCtx (outFile);
  910.   _gdImageGd2 (im, out, cs, fmt);
  911.   out->free (out);
  912. }
  913.  
  914. void *
  915. gdImageGd2Ptr (gdImagePtr im, int cs, int fmt, int *size)
  916. {
  917.   void *rv;
  918.   gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
  919.   _gdImageGd2 (im, out, cs, fmt);
  920.   rv = gdDPExtractData (out, size);
  921.   out->free (out);
  922.   return rv;
  923. }
  924.