Back to index

php5  5.3.10
gd_gd2.c
Go to the documentation of this file.
00001 /*
00002    * gd_gd2.c
00003    *
00004    * Implements the I/O and support for the GD2 format.
00005    *
00006    * Changing the definition of GD2_DBG (below) will cause copious messages
00007    * to be displayed while it processes requests.
00008    *
00009    * Designed, Written & Copyright 1999, Philip Warner.
00010    *
00011  */
00012 
00013 #include <stdio.h>
00014 #include <errno.h>
00015 #include <math.h>
00016 #include <string.h>
00017 #include <stdlib.h>
00018 #include "gd.h"
00019 #include "gdhelpers.h"
00020 
00021 #include <zlib.h>
00022 
00023 #define TRUE 1
00024 #define FALSE 0
00025 
00026 /* 2.11: not part of the API, as the save routine can figure it out
00027  *     from im->trueColor, and the load routine doesn't need to tell
00028  *     the end user the saved format. NOTE: adding 2 is assumed
00029  *     to result in the correct format value for truecolor!
00030 */
00031 #define GD2_FMT_TRUECOLOR_RAW 3
00032 #define GD2_FMT_TRUECOLOR_COMPRESSED 4
00033 
00034 #define gd2_compressed(fmt) (((fmt) == GD2_FMT_COMPRESSED) || ((fmt) == GD2_FMT_TRUECOLOR_COMPRESSED))
00035 #define gd2_truecolor(fmt) (((fmt) == GD2_FMT_TRUECOLOR_RAW) || ((fmt) == GD2_FMT_TRUECOLOR_COMPRESSED))
00036 
00037 /* Use this for commenting out debug-print statements. */
00038 /* Just use the first '#define' to allow all the prints... */
00039 /* #define GD2_DBG(s) (s) */
00040 #define GD2_DBG(s)
00041 
00042 typedef struct
00043 {
00044        int offset;
00045        int size;
00046 } t_chunk_info;
00047 
00048 extern int _gdGetColors(gdIOCtx * in, gdImagePtr im, int gd2xFlag);
00049 extern void _gdPutColors(gdImagePtr im, gdIOCtx * out);
00050 
00051 /* */
00052 /* Read the extra info in the gd2 header. */
00053 /* */
00054 static int _gd2GetHeader(gdIOCtxPtr in, int *sx, int *sy, int *cs, int *vers, int *fmt, int *ncx, int *ncy, t_chunk_info ** chunkIdx)
00055 {
00056        int i;
00057        int ch;
00058        char id[5];
00059        t_chunk_info *cidx;
00060        int sidx;
00061        int nc;
00062 
00063        GD2_DBG(php_gd_error("Reading gd2 header info"));
00064 
00065        for (i = 0; i < 4; i++) {
00066               ch = gdGetC(in);
00067               if (ch == EOF) {
00068                      goto fail1;
00069               }
00070               id[i] = ch;
00071        }
00072        id[4] = 0;
00073 
00074        GD2_DBG(php_gd_error("Got file code: %s", id));
00075 
00076        /* Equiv. of 'magick'.  */
00077        if (strcmp(id, GD2_ID) != 0) {
00078               GD2_DBG(php_gd_error("Not a valid gd2 file"));
00079               goto fail1;
00080        }
00081 
00082        /* Version */
00083        if (gdGetWord(vers, in) != 1) {
00084               goto fail1;
00085        }
00086        GD2_DBG(php_gd_error("Version: %d", *vers));
00087 
00088        if ((*vers != 1) && (*vers != 2)) {
00089               GD2_DBG(php_gd_error("Bad version: %d", *vers));
00090               goto fail1;
00091        }
00092 
00093        /* Image Size */
00094        if (!gdGetWord(sx, in)) {
00095               GD2_DBG(php_gd_error("Could not get x-size"));
00096               goto fail1;
00097        }
00098        if (!gdGetWord(sy, in)) {
00099               GD2_DBG(php_gd_error("Could not get y-size"));
00100               goto fail1;
00101        }
00102        GD2_DBG(php_gd_error("Image is %dx%d", *sx, *sy));
00103 
00104        /* Chunk Size (pixels, not bytes!) */
00105        if (gdGetWord(cs, in) != 1) {
00106               goto fail1;
00107        }
00108        GD2_DBG(php_gd_error("ChunkSize: %d", *cs));
00109 
00110        if ((*cs < GD2_CHUNKSIZE_MIN) || (*cs > GD2_CHUNKSIZE_MAX)) {
00111               GD2_DBG(php_gd_error("Bad chunk size: %d", *cs));
00112               goto fail1;
00113        }
00114 
00115        /* Data Format */
00116        if (gdGetWord(fmt, in) != 1) {
00117               goto fail1;
00118        }
00119        GD2_DBG(php_gd_error("Format: %d", *fmt));
00120 
00121        if ((*fmt != GD2_FMT_RAW) && (*fmt != GD2_FMT_COMPRESSED) && (*fmt != GD2_FMT_TRUECOLOR_RAW) && (*fmt != GD2_FMT_TRUECOLOR_COMPRESSED)) {
00122               GD2_DBG(php_gd_error("Bad data format: %d", *fmt));
00123               goto fail1;
00124        }
00125 
00126        /* # of chunks wide */
00127        if (gdGetWord(ncx, in) != 1) {
00128               goto fail1;
00129        }
00130        GD2_DBG(php_gd_error("%d Chunks Wide", *ncx));
00131 
00132        /* # of chunks high */
00133        if (gdGetWord(ncy, in) != 1) {
00134               goto fail1;
00135        }
00136        GD2_DBG(php_gd_error("%d Chunks vertically", *ncy));
00137 
00138        if (gd2_compressed(*fmt)) {
00139               nc = (*ncx) * (*ncy);
00140               GD2_DBG(php_gd_error("Reading %d chunk index entries", nc));
00141               sidx = sizeof(t_chunk_info) * nc;
00142               if (sidx <= 0) {
00143                      goto fail1;
00144               }
00145               cidx = gdCalloc(sidx, 1);
00146               for (i = 0; i < nc; i++) {
00147                      if (gdGetInt(&cidx[i].offset, in) != 1) {
00148                             goto fail1;
00149                      }
00150                      if (gdGetInt(&cidx[i].size, in) != 1) {
00151                             goto fail1;
00152                      }
00153               }
00154               *chunkIdx = cidx;
00155        }
00156 
00157        GD2_DBG(php_gd_error("gd2 header complete"));
00158 
00159        return 1;
00160 
00161 fail1:
00162        return 0;
00163 }
00164 
00165 static gdImagePtr _gd2CreateFromFile (gdIOCtxPtr in, int *sx, int *sy, int *cs, int *vers, int *fmt, int *ncx, int *ncy, t_chunk_info ** cidx)
00166 {
00167        gdImagePtr im;
00168 
00169        if (_gd2GetHeader (in, sx, sy, cs, vers, fmt, ncx, ncy, cidx) != 1) {
00170               GD2_DBG(php_gd_error("Bad GD2 header"));
00171               goto fail1;
00172        }
00173 
00174        if (gd2_truecolor(*fmt)) {
00175               im = gdImageCreateTrueColor(*sx, *sy);
00176        } else {
00177               im = gdImageCreate(*sx, *sy);
00178        }
00179        if (im == NULL) {
00180               GD2_DBG(php_gd_error("Could not create gdImage"));
00181               goto fail1;
00182        }
00183 
00184        if (!_gdGetColors(in, im, (*vers) == 2)) {
00185               GD2_DBG(php_gd_error("Could not read color palette"));
00186               goto fail2;
00187        }
00188        GD2_DBG(php_gd_error("Image palette completed: %d colours", im->colorsTotal));
00189 
00190        return im;
00191 
00192 fail2:
00193        gdImageDestroy(im);
00194        return 0;
00195 
00196 fail1:
00197        return 0;
00198 }
00199 
00200 static int _gd2ReadChunk (int offset, char *compBuf, int compSize, char *chunkBuf, uLongf * chunkLen, gdIOCtx * in)
00201 {
00202        int zerr;
00203 
00204        if (gdTell(in) != offset) {
00205               GD2_DBG(php_gd_error("Positioning in file to %d", offset));
00206               gdSeek(in, offset);
00207        } else {
00208               GD2_DBG(php_gd_error("Already Positioned in file to %d", offset));
00209        }
00210 
00211        /* Read and uncompress an entire chunk. */
00212        GD2_DBG(php_gd_error("Reading file"));
00213        if (gdGetBuf(compBuf, compSize, in) != compSize) {
00214               return FALSE;
00215        }
00216        GD2_DBG(php_gd_error("Got %d bytes. Uncompressing into buffer of %d bytes", compSize, (int)*chunkLen));
00217        zerr = uncompress((unsigned char *) chunkBuf, chunkLen, (unsigned char *) compBuf, compSize);
00218        if (zerr != Z_OK) {
00219               GD2_DBG(php_gd_error("Error %d from uncompress", zerr));
00220               return FALSE;
00221        }
00222        GD2_DBG(php_gd_error("Got chunk"));
00223 
00224        return TRUE;
00225 }
00226 
00227 gdImagePtr gdImageCreateFromGd2 (FILE * inFile)
00228 {
00229        gdIOCtx *in = gdNewFileCtx(inFile);
00230        gdImagePtr im;
00231 
00232        im = gdImageCreateFromGd2Ctx(in);
00233 
00234        in->gd_free(in);
00235 
00236        return im;
00237 }
00238 
00239 gdImagePtr gdImageCreateFromGd2Ptr (int size, void *data)
00240 {
00241        gdImagePtr im;
00242        gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
00243        im = gdImageCreateFromGd2Ctx(in);
00244        in->gd_free(in);
00245 
00246        return im;
00247 }
00248 
00249 gdImagePtr gdImageCreateFromGd2Ctx (gdIOCtxPtr in)
00250 {
00251        int sx, sy;
00252        int i;
00253        int ncx, ncy, nc, cs, cx, cy;
00254        int x, y, ylo, yhi, xlo, xhi;
00255        int vers, fmt;
00256        t_chunk_info *chunkIdx = NULL;     /* So we can gdFree it with impunity. */
00257        unsigned char *chunkBuf = NULL;    /* So we can gdFree it with impunity. */
00258        int chunkNum = 0;
00259        int chunkMax = 0;
00260        uLongf chunkLen;
00261        int chunkPos = 0;
00262        int compMax = 0;
00263        int bytesPerPixel;
00264        char *compBuf = NULL;              /* So we can gdFree it with impunity. */
00265 
00266        gdImagePtr im;
00267 
00268        /* Get the header */
00269        if (!(im = _gd2CreateFromFile(in, &sx, &sy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx))) {
00270                return 0;
00271        }
00272 
00273        bytesPerPixel = im->trueColor ? 4 : 1;
00274        nc = ncx * ncy;
00275 
00276        if (gd2_compressed(fmt)) {
00277               /* Find the maximum compressed chunk size. */
00278               compMax = 0;
00279               for (i = 0; (i < nc); i++) {
00280                      if (chunkIdx[i].size > compMax) {
00281                             compMax = chunkIdx[i].size;
00282                      }
00283               }
00284               compMax++;
00285 
00286               /* Allocate buffers */
00287               chunkMax = cs * bytesPerPixel * cs;
00288               if (chunkMax <= 0) {
00289                      return 0;
00290               }
00291               chunkBuf = gdCalloc(chunkMax, 1);
00292               compBuf = gdCalloc(compMax, 1);
00293 
00294               GD2_DBG(php_gd_error("Largest compressed chunk is %d bytes", compMax));
00295        }
00296 
00297        /* Read the data... */
00298        for (cy = 0; (cy < ncy); cy++) {
00299               for (cx = 0; (cx < ncx); cx++) {
00300                      ylo = cy * cs;
00301                      yhi = ylo + cs;
00302                      if (yhi > im->sy) {
00303                             yhi = im->sy;
00304                      }
00305 
00306                      GD2_DBG(php_gd_error("Processing Chunk %d (%d, %d), y from %d to %d", chunkNum, cx, cy, ylo, yhi));
00307 
00308                      if (gd2_compressed(fmt)) {
00309                             chunkLen = chunkMax;
00310 
00311                             if (!_gd2ReadChunk(chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, (char *) chunkBuf, &chunkLen, in)) {
00312                                    GD2_DBG(php_gd_error("Error reading comproessed chunk"));
00313                                    goto fail2;
00314                             }
00315 
00316                             chunkPos = 0;
00317                      }
00318 
00319                      for (y = ylo; (y < yhi); y++) {
00320                             xlo = cx * cs;
00321                             xhi = xlo + cs;
00322                             if (xhi > im->sx) {
00323                                    xhi = im->sx;
00324                             }
00325 
00326                             if (!gd2_compressed(fmt)) {
00327                                    for (x = xlo; x < xhi; x++) {
00328                                           if (im->trueColor) {
00329                                                  if (!gdGetInt(&im->tpixels[y][x], in)) {
00330                                                         im->tpixels[y][x] = 0;
00331                                                  }
00332                                           } else {
00333                                                  int ch;
00334                                                  if (!gdGetByte(&ch, in)) {
00335                                                         ch = 0;
00336                                                  }
00337                                                  im->pixels[y][x] = ch;
00338                                           }
00339                                    }
00340                             } else {
00341                                    for (x = xlo; x < xhi; x++) {
00342                                           if (im->trueColor) {
00343                                                  /* 2.0.1: work around a gcc bug by being verbose. TBB */
00344                                                  int a = chunkBuf[chunkPos++] << 24;
00345                                                  int r = chunkBuf[chunkPos++] << 16;
00346                                                  int g = chunkBuf[chunkPos++] << 8;
00347                                                  int b = chunkBuf[chunkPos++];
00348                                                  im->tpixels[y][x] = a + r + g + b;
00349                                           } else {
00350                                                  im->pixels[y][x] = chunkBuf[chunkPos++];
00351                                           }
00352                                    }
00353                             }
00354                      }
00355                      chunkNum++;
00356               }
00357        }
00358 
00359        GD2_DBG(php_gd_error("Freeing memory"));
00360 
00361        if (chunkBuf) {
00362               gdFree(chunkBuf);
00363        }
00364        if (compBuf) {
00365               gdFree(compBuf);
00366        }
00367        if (chunkIdx) {
00368               gdFree(chunkIdx);
00369        }
00370 
00371        GD2_DBG(php_gd_error("Done"));
00372 
00373        return im;
00374 
00375 fail2:
00376        gdImageDestroy(im);
00377        if (chunkBuf) {
00378               gdFree(chunkBuf);
00379        }
00380        if (compBuf) {
00381               gdFree(compBuf);
00382        }
00383        if (chunkIdx) {
00384               gdFree(chunkIdx);
00385        }
00386 
00387        return 0;
00388 }
00389 
00390 gdImagePtr gdImageCreateFromGd2PartPtr (int size, void *data, int srcx, int srcy, int w, int h)
00391 {
00392        gdImagePtr im;
00393        gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
00394        im = gdImageCreateFromGd2PartCtx(in, srcx, srcy, w, h);
00395        in->gd_free(in);
00396 
00397        return im;
00398 }
00399 
00400 gdImagePtr gdImageCreateFromGd2Part (FILE * inFile, int srcx, int srcy, int w, int h)
00401 {
00402        gdImagePtr im;
00403        gdIOCtx *in = gdNewFileCtx(inFile);
00404 
00405        im = gdImageCreateFromGd2PartCtx(in, srcx, srcy, w, h);
00406 
00407        in->gd_free(in);
00408 
00409        return im;
00410 }
00411 
00412 gdImagePtr gdImageCreateFromGd2PartCtx (gdIOCtx * in, int srcx, int srcy, int w, int h)
00413 {
00414        int scx, scy, ecx, ecy, fsx, fsy;
00415        int nc, ncx, ncy, cs, cx, cy;
00416        int x, y, ylo, yhi, xlo, xhi;
00417        int dstart, dpos;
00418        int i;
00419        /* 2.0.12: unsigned is correct; fixes problems with color munging. Thanks to Steven Brown. */
00420        unsigned int ch;
00421        int vers, fmt;
00422        t_chunk_info *chunkIdx = NULL;
00423        unsigned char *chunkBuf = NULL;
00424        int chunkNum;
00425        int chunkMax = 0;
00426        uLongf chunkLen;
00427        int chunkPos = 0;
00428        int compMax;
00429        char *compBuf = NULL;
00430 
00431        gdImagePtr im;
00432 
00433        if (w<1 || h <1) {
00434               return 0;
00435        }
00436 
00437        /* The next few lines are basically copied from gd2CreateFromFile
00438         * we change the file size, so don't want to use the code directly.
00439         * but we do need to know the file size.
00440         */
00441        if (_gd2GetHeader(in, &fsx, &fsy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx) != 1) {
00442               goto fail1;
00443        }
00444 
00445        GD2_DBG(php_gd_error("File size is %dx%d", fsx, fsy));
00446 
00447        /* This is the difference - make a file based on size of chunks. */
00448        if (gd2_truecolor(fmt)) {
00449               im = gdImageCreateTrueColor(w, h);
00450        } else {
00451               im = gdImageCreate(w, h);
00452        }
00453        if (im == NULL) {
00454               goto fail1;
00455        }
00456 
00457        if (!_gdGetColors(in, im, vers == 2)) {
00458               goto fail2;
00459        }
00460        GD2_DBG(php_gd_error("Image palette completed: %d colours", im->colorsTotal));
00461 
00462        /* Process the header info */
00463        nc = ncx * ncy;
00464 
00465        if (gd2_compressed(fmt)) {
00466               /* Find the maximum compressed chunk size. */
00467               compMax = 0;
00468               for (i = 0; (i < nc); i++) {
00469                      if (chunkIdx[i].size > compMax) {
00470                             compMax = chunkIdx[i].size;
00471                      }
00472               }
00473               compMax++;
00474 
00475               if (im->trueColor) {
00476                      chunkMax = cs * cs * 4;
00477               } else {
00478                      chunkMax = cs * cs;
00479               }
00480               if (chunkMax <= 0) {
00481                      goto fail2;
00482               }
00483 
00484               chunkBuf = gdCalloc(chunkMax, 1);
00485               compBuf = gdCalloc(compMax, 1);
00486        }
00487 
00488        /* Work out start/end chunks */
00489        scx = srcx / cs;
00490        scy = srcy / cs;
00491        if (scx < 0) {
00492               scx = 0;
00493        }
00494        if (scy < 0) {
00495               scy = 0;
00496        }
00497 
00498        ecx = (srcx + w) / cs;
00499        ecy = (srcy + h) / cs;
00500        if (ecx >= ncx) {
00501               ecx = ncx - 1;
00502        }
00503        if (ecy >= ncy) {
00504               ecy = ncy - 1;
00505        }
00506 
00507        /* Remember file position of image data. */
00508        dstart = gdTell(in);
00509        GD2_DBG(php_gd_error("Data starts at %d", dstart));
00510 
00511        /* Loop through the chunks. */
00512        for (cy = scy; (cy <= ecy); cy++) {
00513               ylo = cy * cs;
00514               yhi = ylo + cs;
00515               if (yhi > fsy) {
00516                      yhi = fsy;
00517               }
00518 
00519               for (cx = scx; cx <= ecx; cx++) {
00520 
00521                      xlo = cx * cs;
00522                      xhi = xlo + cs;
00523                      if (xhi > fsx) {
00524                             xhi = fsx;
00525                      }
00526 
00527                      GD2_DBG(php_gd_error("Processing Chunk (%d, %d), from %d to %d", cx, cy, ylo, yhi));
00528 
00529                      if (!gd2_compressed(fmt)) {
00530                             GD2_DBG(php_gd_error("Using raw format data"));
00531                             if (im->trueColor) {
00532                                    dpos = (cy * (cs * fsx) * 4 + cx * cs * (yhi - ylo) * 4) + dstart;
00533                             } else {
00534                                    dpos = cy * (cs * fsx) + cx * cs * (yhi - ylo) + dstart;
00535                             }
00536 
00537                             /* gd 2.0.11: gdSeek returns TRUE on success, not 0. Longstanding bug. 01/16/03 */
00538                             if (!gdSeek(in, dpos)) {
00539                                    php_gd_error_ex(E_WARNING, "Error from seek: %d", errno);
00540                                    goto fail2;
00541                             }
00542                             GD2_DBG(php_gd_error("Reading (%d, %d) from position %d", cx, cy, dpos - dstart));
00543                      } else {
00544                             chunkNum = cx + cy * ncx;
00545 
00546                             chunkLen = chunkMax;
00547                             if (!_gd2ReadChunk (chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, (char *)chunkBuf, &chunkLen, in)) {
00548                                    php_gd_error("Error reading comproessed chunk");
00549                                    goto fail2;
00550                             }
00551                             chunkPos = 0;
00552                             GD2_DBG(php_gd_error("Reading (%d, %d) from chunk %d", cx, cy, chunkNum));
00553                      }
00554 
00555                      GD2_DBG(php_gd_error("   into (%d, %d) - (%d, %d)", xlo, ylo, xhi, yhi));
00556 
00557                      for (y = ylo; (y < yhi); y++) {
00558                             for (x = xlo; x < xhi; x++) {
00559                                    if (!gd2_compressed(fmt)) {
00560                                           if (im->trueColor) {
00561                                                  if (!gdGetInt((int *)&ch, in)) {
00562                                                         ch = 0;
00563                                                  }
00564                                           } else {
00565                                                  ch = gdGetC(in);
00566                                                  if ((int)ch == EOF) {
00567                                                         ch = 0;
00568                                                  }
00569                                           }
00570                                    } else {
00571                                           if (im->trueColor) {
00572                                                  ch = chunkBuf[chunkPos++];
00573                                                  ch = (ch << 8) + chunkBuf[chunkPos++];
00574                                                  ch = (ch << 8) + chunkBuf[chunkPos++];
00575                                                  ch = (ch << 8) + chunkBuf[chunkPos++];
00576                                           } else {
00577                                                  ch = chunkBuf[chunkPos++];
00578                                           }
00579                                    }
00580 
00581                                    /* Only use a point that is in the image. */
00582                                    if ((x >= srcx) && (x < (srcx + w)) && (x < fsx) && (x >= 0) && (y >= srcy) && (y < (srcy + h)) && (y < fsy) && (y >= 0)) {
00583                                           if (im->trueColor) {
00584                                                  im->tpixels[y - srcy][x - srcx] = ch;
00585                                           } else {
00586                                                  im->pixels[y - srcy][x - srcx] = ch;
00587                                           }
00588                                    }
00589                             }
00590                      }
00591               }
00592        }
00593 
00594        if (chunkBuf) {
00595               gdFree(chunkBuf);
00596        }
00597        if (compBuf) {
00598               gdFree(compBuf);
00599        }
00600        if (chunkIdx) {
00601               gdFree(chunkIdx);
00602        }
00603 
00604        return im;
00605 
00606 fail2:
00607        gdImageDestroy(im);
00608 fail1:
00609        if (chunkBuf) {
00610               gdFree(chunkBuf);
00611        }
00612        if (compBuf) {
00613               gdFree(compBuf);
00614        }
00615        if (chunkIdx) {
00616               gdFree(chunkIdx);
00617        }
00618 
00619        return 0;
00620 }
00621 
00622 static void _gd2PutHeader (gdImagePtr im, gdIOCtx * out, int cs, int fmt, int cx, int cy)
00623 {
00624        int i;
00625 
00626        /* Send the gd2 id, to verify file format. */
00627        for (i = 0; i < 4; i++) {
00628               gdPutC((unsigned char) (GD2_ID[i]), out);
00629        }
00630 
00631        /* We put the version info first, so future versions can easily change header info. */
00632 
00633        gdPutWord(GD2_VERS, out);
00634        gdPutWord(im->sx, out);
00635        gdPutWord(im->sy, out);
00636        gdPutWord(cs, out);
00637        gdPutWord(fmt, out);
00638        gdPutWord(cx, out);
00639        gdPutWord(cy, out);
00640 }
00641 
00642 static void _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)
00643 {
00644        int ncx, ncy, cx, cy;
00645        int x, y, ylo, yhi, xlo, xhi;
00646        int chunkLen;
00647        int chunkNum = 0;
00648        char *chunkData = NULL;     /* So we can gdFree it with impunity. */
00649        char *compData = NULL;      /* So we can gdFree it with impunity. */
00650        uLongf compLen;
00651        int idxPos = 0;
00652        int idxSize;
00653        t_chunk_info *chunkIdx = NULL; /* So we can gdFree it with impunity. */
00654        int posSave;
00655        int bytesPerPixel = im->trueColor ? 4 : 1;
00656        int compMax = 0;
00657 
00658        /* Force fmt to a valid value since we don't return anything. */
00659        if ((fmt != GD2_FMT_RAW) && (fmt != GD2_FMT_COMPRESSED)) {
00660               fmt = im->trueColor ? GD2_FMT_TRUECOLOR_COMPRESSED : GD2_FMT_COMPRESSED;
00661        }
00662        if (im->trueColor) {
00663               fmt += 2;
00664        }
00665        /* Make sure chunk size is valid. These are arbitrary values; 64 because it seems
00666         * a little silly to expect performance improvements on a 64x64 bit scale, and
00667         * 4096 because we buffer one chunk, and a 16MB buffer seems a little large - it may be
00668         * OK for one user, but for another to read it, they require the buffer.
00669         */
00670        if (cs == 0) {
00671               cs = GD2_CHUNKSIZE;
00672        } else if (cs < GD2_CHUNKSIZE_MIN) {
00673               cs = GD2_CHUNKSIZE_MIN;
00674        } else if (cs > GD2_CHUNKSIZE_MAX) {
00675               cs = GD2_CHUNKSIZE_MAX;
00676        }
00677 
00678        /* Work out number of chunks. */
00679        ncx = im->sx / cs + 1;
00680        ncy = im->sy / cs + 1;
00681 
00682        /* Write the standard header. */
00683        _gd2PutHeader (im, out, cs, fmt, ncx, ncy);
00684 
00685        if (gd2_compressed(fmt)) {
00686               /* Work out size of buffer for compressed data, If CHUNKSIZE is large,
00687                * then these will be large!
00688                */
00689 
00690               /* The zlib notes say output buffer size should be (input size) * 1.01 * 12
00691                * - we'll use 1.02 to be paranoid.
00692                */
00693               compMax = (int)(cs * bytesPerPixel * cs * 1.02f) + 12;
00694 
00695               /* Allocate the buffers.  */
00696               chunkData = safe_emalloc(cs * bytesPerPixel, cs, 0);
00697               memset(chunkData, 0, cs * bytesPerPixel * cs);
00698               if (compMax <= 0) {
00699                      goto fail;
00700               }
00701               compData = gdCalloc(compMax, 1);
00702 
00703               /* Save the file position of chunk index, and allocate enough space for
00704                * each chunk_info block .
00705                */
00706               idxPos = gdTell(out);
00707               idxSize = ncx * ncy * sizeof(t_chunk_info);
00708               GD2_DBG(php_gd_error("Index size is %d", idxSize));
00709               gdSeek(out, idxPos + idxSize);
00710 
00711               chunkIdx = safe_emalloc(idxSize, sizeof(t_chunk_info), 0);
00712               memset(chunkIdx, 0, idxSize * sizeof(t_chunk_info));
00713        }
00714 
00715        _gdPutColors (im, out);
00716 
00717        GD2_DBG(php_gd_error("Size: %dx%d", im->sx, im->sy));
00718        GD2_DBG(php_gd_error("Chunks: %dx%d", ncx, ncy));
00719 
00720        for (cy = 0; (cy < ncy); cy++) {
00721               for (cx = 0; (cx < ncx); cx++) {
00722                      ylo = cy * cs;
00723                      yhi = ylo + cs;
00724                      if (yhi > im->sy) {
00725                             yhi = im->sy;
00726                      }
00727 
00728                      GD2_DBG(php_gd_error("Processing Chunk (%dx%d), y from %d to %d", cx, cy, ylo, yhi));
00729                      chunkLen = 0;
00730                      for (y = ylo; (y < yhi); y++) {
00731                             GD2_DBG(php_gd_error("y=%d: ",y));
00732                             xlo = cx * cs;
00733                             xhi = xlo + cs;
00734                             if (xhi > im->sx) {
00735                                    xhi = im->sx;
00736                             }
00737 
00738                             if (gd2_compressed(fmt)) {
00739                                    for (x = xlo; x < xhi; x++) {
00740                                           GD2_DBG(php_gd_error("%d...",x));
00741                                           if (im->trueColor) {
00742                                                  int p = im->tpixels[y][x];
00743                                                  chunkData[chunkLen++] = gdTrueColorGetAlpha(p);
00744                                                  chunkData[chunkLen++] = gdTrueColorGetRed(p);
00745                                                  chunkData[chunkLen++] = gdTrueColorGetGreen(p);
00746                                                  chunkData[chunkLen++] = gdTrueColorGetBlue(p);
00747                                           } else {
00748                                                  chunkData[chunkLen++] = im->pixels[y][x];
00749                                           }
00750                                    }
00751                             } else {
00752                                    for (x = xlo; x < xhi; x++) {
00753                                           GD2_DBG(php_gd_error("%d, ",x));
00754 
00755                                           if (im->trueColor) {
00756                                                  gdPutInt(im->tpixels[y][x], out);
00757                                           } else {
00758                                                  gdPutC((unsigned char) im->pixels[y][x], out);
00759                                           }
00760                                    }
00761                             }
00762                             GD2_DBG(php_gd_error("y=%d done.",y));
00763                      }
00764 
00765                      if (gd2_compressed(fmt)) {
00766                             compLen = compMax;
00767                             if (compress((unsigned char *) &compData[0], &compLen, (unsigned char *) &chunkData[0], chunkLen) != Z_OK) {
00768                                    php_gd_error("Error from compressing");
00769                             } else {
00770                                    chunkIdx[chunkNum].offset = gdTell(out);
00771                                    chunkIdx[chunkNum++].size = compLen;
00772                                    GD2_DBG(php_gd_error("Chunk %d size %d offset %d", chunkNum, chunkIdx[chunkNum - 1].size, chunkIdx[chunkNum - 1].offset));
00773 
00774                                    if (gdPutBuf (compData, compLen, out) <= 0) {
00775                                           /* Any alternate suggestions for handling this? */
00776                                           php_gd_error_ex(E_WARNING, "Error %d on write", errno);
00777                                    }
00778                             }
00779                      }
00780               }
00781        }
00782 
00783        if (gd2_compressed(fmt)) {
00784               /* Save the position, write the index, restore position (paranoia). */
00785               GD2_DBG(php_gd_error("Seeking %d to write index", idxPos));
00786               posSave = gdTell(out);
00787               gdSeek(out, idxPos);
00788               GD2_DBG(php_gd_error("Writing index"));
00789               for (x = 0; x < chunkNum; x++) {
00790                      GD2_DBG(php_gd_error("Chunk %d size %d offset %d", x, chunkIdx[x].size, chunkIdx[x].offset));
00791                      gdPutInt(chunkIdx[x].offset, out);
00792                      gdPutInt(chunkIdx[x].size, out);
00793               }
00794               gdSeek(out, posSave);
00795        }
00796 fail:
00797        GD2_DBG(php_gd_error("Freeing memory"));
00798        if (chunkData) {
00799               gdFree(chunkData);
00800        }
00801        if (compData) {
00802               gdFree(compData);
00803        }
00804        if (chunkIdx) {
00805               gdFree(chunkIdx);
00806        }
00807        GD2_DBG(php_gd_error("Done"));
00808 }
00809 
00810 void gdImageGd2 (gdImagePtr im, FILE * outFile, int cs, int fmt)
00811 {
00812        gdIOCtx *out = gdNewFileCtx(outFile);
00813 
00814        _gdImageGd2(im, out, cs, fmt);
00815 
00816        out->gd_free(out);
00817 }
00818 
00819 void *gdImageGd2Ptr (gdImagePtr im, int cs, int fmt, int *size)
00820 {
00821        void *rv;
00822        gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
00823 
00824        _gdImageGd2(im, out, cs, fmt);
00825        rv = gdDPExtractData(out, size);
00826        out->gd_free(out);
00827 
00828        return rv;
00829 }