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