xref: /PHP-5.3/ext/gd/libgd/gd_gd2.c (revision 6dbb90af)
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