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 /**
14  * File: GD2 IO
15  *
16  * Read and write GD2 images.
17  *
18  * The GD2 image format is a proprietary image format of libgd. *It has to be*
19  * *regarded as being obsolete, and should only be used for development and*
20  * *testing purposes.*
21  *
22  * Structure of a GD2 image file:
23  *  - file header
24  *  - chunk headers (only for compressed data)
25  *  - color header (either truecolor or palette)
26  *  - chunks of image data (chunk-row-major, top to bottom, left to right)
27  *
28  * All numbers are stored in big-endian format.
29  *
30  * File header structure:
31  *  signature     - 4 bytes (always "gd2\0")
32  *  version       - 1 word (e.g. "\0\002")
33  *  width         - 1 word
34  *  height        - 1 word
35  *  chunk_size    - 1 word
36  *  format        - 1 word
37  *  x_chunk_count - 1 word
38  *  y_chunk_count - 1 word
39  *
40  * Recognized formats:
41  *  1 - raw palette image data
42  *  2 - compressed palette image data
43  *  3 - raw truecolor image data
44  *  4 - compressed truecolor image data
45  *
46  * Chunk header:
47  *  offset - 1 dword
48  *  size   - 1 dword
49  *
50  * There are x_chunk_count * y_chunk_count chunk headers.
51  *
52  * Truecolor image color header:
53  *  truecolor   - 1 byte (always "\001")
54  *  transparent - 1 dword (ARGB color); "\377\377\377\377" means that no
55  *				  transparent color is set
56  *
57  * Palette image color header:
58  *  truecolor   - 1 byte (always "\0")
59  *  count       - 1 word (the number of used palette colors)
60  *  transparent - 1 dword (palette index); "\377\377\377\377" means that no
61  *				  transparent color is set
62  *  palette     - 256 dwords (RGBA colors)
63  *
64  * Chunk structure:
65  *  Sequential pixel data of a rectangular area (chunk_size x chunk_size),
66  *  row-major from top to bottom, left to right:
67  *  - 1 byte per pixel for palette images
68  *  - 1 dword (ARGB) per pixel for truecolor images
69  *
70  *  Depending on format, the chunk may be ZLIB compressed.
71  */
72 
73 #ifdef HAVE_CONFIG_H
74 #include "config.h"
75 #endif
76 
77 /* 2.0.29: no more errno.h, makes windows happy */
78 #include <math.h>
79 #include <limits.h>
80 #include <string.h>
81 #include "gd.h"
82 #include "gd_errors.h"
83 #include "gdhelpers.h"
84 
85 /* 2.03: gd2 is no longer mandatory */
86 /* JCE - test after including gd.h so that HAVE_LIBZ can be set in
87  * a config.h file included by gd.h */
88 #if defined(HAVE_LIBZ) && ENABLE_GD_FORMATS
89 #include <zlib.h>
90 
91 #define TRUE 1
92 #define FALSE 0
93 
94 /* 2.11: not part of the API, as the save routine can figure it out
95 	from im->trueColor, and the load routine doesn't need to tell
96 	the end user the saved format. NOTE: adding 2 is assumed
97 	to result in the correct format value for truecolor! */
98 #define GD2_FMT_TRUECOLOR_RAW 3
99 #define GD2_FMT_TRUECOLOR_COMPRESSED 4
100 
101 #define gd2_compressed(fmt) (((fmt) == GD2_FMT_COMPRESSED) || \
102 	((fmt) == GD2_FMT_TRUECOLOR_COMPRESSED))
103 
104 #define gd2_truecolor(fmt) (((fmt) == GD2_FMT_TRUECOLOR_RAW) || \
105 	((fmt) == GD2_FMT_TRUECOLOR_COMPRESSED))
106 
107 /* Use this for commenting out debug-print statements. */
108 /* Just use the first '#define' to allow all the prints... */
109 /*#define GD2_DBG(s) (s) */
110 #define GD2_DBG(s)
111 
112 typedef struct {
113 	int offset;
114 	int size;
115 }
116 t_chunk_info;
117 
118 extern int _gdGetColors (gdIOCtx * in, gdImagePtr im, int gd2xFlag);
119 extern void _gdPutColors (gdImagePtr im, gdIOCtx * out);
120 
121 /* */
122 /* Read the extra info in the gd2 header. */
123 /* */
124 static int
_gd2GetHeader(gdIOCtxPtr in,int * sx,int * sy,int * cs,int * vers,int * fmt,int * ncx,int * ncy,t_chunk_info ** chunkIdx)125 _gd2GetHeader (gdIOCtxPtr in, int *sx, int *sy,
126                int *cs, int *vers, int *fmt, int *ncx, int *ncy,
127                t_chunk_info ** chunkIdx)
128 {
129 	int i;
130 	int ch;
131 	char id[5];
132 	t_chunk_info *cidx;
133 	int sidx;
134 	int nc;
135 
136 	GD2_DBG (printf ("Reading gd2 header info\n"));
137 
138 	for (i = 0; i < 4; i++) {
139 		ch = gdGetC (in);
140 		if (ch == EOF) {
141 			goto fail1;
142 		};
143 		id[i] = ch;
144 	};
145 	id[4] = 0;
146 
147 	GD2_DBG (printf ("Got file code: %s\n", id));
148 
149 	/* Equiv. of 'magick'.  */
150 	if (strcmp (id, GD2_ID) != 0) {
151 		GD2_DBG (printf ("Not a valid gd2 file\n"));
152 		goto fail1;
153 	};
154 
155 	/* Version */
156 	if (gdGetWord (vers, in) != 1) {
157 		goto fail1;
158 	};
159 	GD2_DBG (printf ("Version: %d\n", *vers));
160 
161 	if ((*vers != 1) && (*vers != 2)) {
162 		GD2_DBG (printf ("Bad version: %d\n", *vers));
163 		goto fail1;
164 	};
165 
166 	/* Image Size */
167 	if (!gdGetWord (sx, in)) {
168 		GD2_DBG (printf ("Could not get x-size\n"));
169 		goto fail1;
170 	}
171 	if (!gdGetWord (sy, in)) {
172 		GD2_DBG (printf ("Could not get y-size\n"));
173 		goto fail1;
174 	}
175 	GD2_DBG (printf ("Image is %dx%d\n", *sx, *sy));
176 
177 	/* Chunk Size (pixels, not bytes!) */
178 	if (gdGetWord (cs, in) != 1) {
179 		goto fail1;
180 	};
181 	GD2_DBG (printf ("ChunkSize: %d\n", *cs));
182 
183 	if ((*cs < GD2_CHUNKSIZE_MIN) || (*cs > GD2_CHUNKSIZE_MAX)) {
184 		GD2_DBG (printf ("Bad chunk size: %d\n", *cs));
185 		goto fail1;
186 	};
187 
188 	/* Data Format */
189 	if (gdGetWord (fmt, in) != 1) {
190 		goto fail1;
191 	};
192 	GD2_DBG (printf ("Format: %d\n", *fmt));
193 
194 	if ((*fmt != GD2_FMT_RAW) && (*fmt != GD2_FMT_COMPRESSED) &&
195 	        (*fmt != GD2_FMT_TRUECOLOR_RAW) &&
196 	        (*fmt != GD2_FMT_TRUECOLOR_COMPRESSED)) {
197 		GD2_DBG (printf ("Bad data format: %d\n", *fmt));
198 		goto fail1;
199 	};
200 
201 
202 	/* # of chunks wide */
203 	if (gdGetWord (ncx, in) != 1) {
204 		goto fail1;
205 	};
206 	GD2_DBG (printf ("%d Chunks Wide\n", *ncx));
207 
208 	/* # of chunks high */
209 	if (gdGetWord (ncy, in) != 1) {
210 		goto fail1;
211 	};
212 	GD2_DBG (printf ("%d Chunks vertically\n", *ncy));
213 
214 	if (gd2_compressed (*fmt)) {
215 		if (overflow2(*ncx, *ncy)) {
216 			GD2_DBG(printf ("Illegal chunk counts: %d * %d\n", *ncx, *ncy));
217 			goto fail1;
218 		}
219 		nc = (*ncx) * (*ncy);
220 
221 		GD2_DBG (printf ("Reading %d chunk index entries\n", nc));
222 		if (overflow2(sizeof(t_chunk_info), nc)) {
223 			goto fail1;
224 		}
225 		sidx = sizeof (t_chunk_info) * nc;
226 		if (sidx <= 0) {
227 			goto fail1;
228 		}
229 
230 		cidx = gdCalloc (sidx, 1);
231 		if (cidx == NULL) {
232 			goto fail1;
233 		}
234 		for (i = 0; i < nc; i++) {
235 			if (gdGetInt (&cidx[i].offset, in) != 1) {
236 				goto fail2;
237 			};
238 			if (gdGetInt (&cidx[i].size, in) != 1) {
239 				goto fail2;
240 			};
241 			if (cidx[i].offset < 0 || cidx[i].size < 0 || cidx[i].size == INT_MAX)
242 				goto fail2;
243 		};
244 		*chunkIdx = cidx;
245 	};
246 
247 	GD2_DBG (printf ("gd2 header complete\n"));
248 
249 	return 1;
250 fail2:
251 	gdFree(cidx);
252 fail1:
253 	return 0;
254 }
255 
256 static gdImagePtr
_gd2CreateFromFile(gdIOCtxPtr in,int * sx,int * sy,int * cs,int * vers,int * fmt,int * ncx,int * ncy,t_chunk_info ** cidx)257 _gd2CreateFromFile (gdIOCtxPtr in, int *sx, int *sy,
258                     int *cs, int *vers, int *fmt,
259                     int *ncx, int *ncy, t_chunk_info ** cidx)
260 {
261 	gdImagePtr im;
262 
263 	if (_gd2GetHeader (in, sx, sy, cs, vers, fmt, ncx, ncy, cidx) != 1) {
264 		GD2_DBG (printf ("Bad GD2 header\n"));
265 		goto fail1;
266 	}
267 	if (gd2_truecolor (*fmt)) {
268 		im = gdImageCreateTrueColor (*sx, *sy);
269 	} else {
270 		im = gdImageCreate (*sx, *sy);
271 	}
272 	if (im == NULL) {
273 		GD2_DBG (printf ("Could not create gdImage\n"));
274 		goto fail2;
275 	};
276 
277 	if (!_gdGetColors (in, im, (*vers) == 2)) {
278 		GD2_DBG (printf ("Could not read color palette\n"));
279 		goto fail3;
280 	}
281 	GD2_DBG (printf ("Image palette completed: %d colours\n", im->colorsTotal));
282 
283 	return im;
284 
285 fail3:
286 	gdImageDestroy (im);
287 fail2:
288 	gdFree(*cidx);
289 fail1:
290 	return 0;
291 
292 }
293 
294 static int
_gd2ReadChunk(int offset,char * compBuf,int compSize,char * chunkBuf,uLongf * chunkLen,gdIOCtx * in)295 _gd2ReadChunk (int offset, char *compBuf, int compSize, char *chunkBuf,
296                uLongf * chunkLen, gdIOCtx * in)
297 {
298 	int zerr;
299 
300 	if (gdTell (in) != offset) {
301 		GD2_DBG (printf ("Positioning in file to %d\n", offset));
302 		gdSeek (in, offset);
303 	} else {
304 		GD2_DBG (printf ("Already Positioned in file to %d\n", offset));
305 	};
306 
307 	/* Read and uncompress an entire chunk. */
308 	GD2_DBG (printf ("Reading file\n"));
309 	if (gdGetBuf (compBuf, compSize, in) != compSize) {
310 		return FALSE;
311 	};
312 	GD2_DBG (printf
313 	         ("Got %d bytes. Uncompressing into buffer of %d bytes\n", compSize,
314 	          *chunkLen));
315 	zerr =
316 	    uncompress ((unsigned char *) chunkBuf, chunkLen,
317 	                (unsigned char *) compBuf, compSize);
318 	if (zerr != Z_OK) {
319 		GD2_DBG (printf ("Error %d from uncompress\n", zerr));
320 		return FALSE;
321 	};
322 	GD2_DBG (printf ("Got chunk\n"));
323 	return TRUE;
324 }
325 
326 
327 /*
328   Function: gdImageCreateFromGd2
329 
330     <gdImageCreateFromGd2> is called to load images from gd2 format
331     files. Invoke <gdImageCreateFromGd2> with an already opened
332     pointer to a file containing the desired image in the gd2 file
333     format, which is specific to gd2 and intended for fast loading of
334     parts of large images. (It is a compressed format, but generally
335     not as good as maximum compression of the entire image would be.)
336 
337     <gdImageCreateFromGd2> returns a <gdImagePtr> to the new image, or
338     NULL if unable to load the image (most often because the file is
339     corrupt or does not contain a gd format
340     image). <gdImageCreateFromGd2> does not close the file. You can
341     inspect the sx and sy members of the image to determine its
342     size. The image must eventually be destroyed using
343     <gdImageDestroy>.
344 
345 
346   Variants:
347 
348     <gdImageCreateFromGd2Ptr> creates an image from GD data (i.e. the
349     contents of a GD2 file) already in memory.
350 
351     <gdImageCreateFromGd2Ctx> reads in an image using the functions in
352     a <gdIOCtx> struct.
353 
354   Parameters:
355 
356     infile - The input FILE pointer
357 
358   Returns:
359 
360     A pointer to the new image or NULL if an error occurred.
361 
362   Example:
363 
364     > gdImagePtr im;
365     > FILE *in;
366     > in = fopen("mygd.gd2", "rb");
367     > im = gdImageCreateFromGd2(in);
368     > fclose(in);
369     > // ... Use the image ...
370     > gdImageDestroy(im);
371 */
gdImageCreateFromGd2(FILE * inFile)372 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2 (FILE * inFile)
373 {
374 	gdIOCtx *in = gdNewFileCtx (inFile);
375 	gdImagePtr im;
376 
377 	if (in == NULL) return NULL;
378 	im = gdImageCreateFromGd2Ctx (in);
379 
380 	in->gd_free (in);
381 
382 	return im;
383 }
384 
385 /*
386   Function: gdImageCreateFromGd2Ptr
387 
388   Parameters:
389 
390     size - size of GD2 data in bytes.
391     data - GD2 data (i.e. contents of a GIF file).
392 
393   See <gdImageCreateFromGd2>.
394 */
gdImageCreateFromGd2Ptr(int size,void * data)395 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ptr (int size, void *data)
396 {
397 	gdImagePtr im;
398 	gdIOCtx *in = gdNewDynamicCtxEx (size, data, 0);
399 	if(!in)
400 		return 0;
401 	im = gdImageCreateFromGd2Ctx (in);
402 	in->gd_free (in);
403 	return im;
404 }
405 
406 /*
407   Function: gdImageCreateFromGd2Ctx
408 
409   Reads in a GD2 image via a <gdIOCtx> struct.  See
410   <gdImageCreateFromGd2>.
411 */
gdImageCreateFromGd2Ctx(gdIOCtxPtr in)412 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ctx (gdIOCtxPtr in)
413 {
414 	int sx, sy;
415 	int i;
416 	int ncx, ncy, nc, cs, cx, cy;
417 	int x, y, ylo, yhi, xlo, xhi;
418 	int vers, fmt;
419 	t_chunk_info *chunkIdx = NULL;	/* So we can gdFree it with impunity. */
420 	unsigned char *chunkBuf = NULL;	/* So we can gdFree it with impunity. */
421 	int chunkNum = 0;
422 	int chunkMax = 0;
423 	uLongf chunkLen;
424 	int chunkPos = 0;
425 	int compMax = 0;
426 	int bytesPerPixel;
427 	char *compBuf = NULL;		/* So we can gdFree it with impunity. */
428 
429 	gdImagePtr im;
430 
431 	/* Get the header */
432 	im =
433 	    _gd2CreateFromFile (in, &sx, &sy, &cs, &vers, &fmt, &ncx, &ncy,
434 	                        &chunkIdx);
435 	if (im == NULL) {
436 		/* No need to free chunkIdx as _gd2CreateFromFile does it for us. */
437 		return 0;
438 	}
439 
440 	bytesPerPixel = im->trueColor ? 4 : 1;
441 	if (overflow2(ncx, ncy))
442 		goto fail;
443 	nc = ncx * ncy;
444 
445 	if (overflow2(ncy, cs) || overflow2(ncx, cs) || overflow2(bytesPerPixel, cs))
446 		goto fail;
447 
448 	if (gd2_compressed (fmt)) {
449 		/* Find the maximum compressed chunk size. */
450 		compMax = 0;
451 		for (i = 0; (i < nc); i++) {
452 			if (chunkIdx[i].size > compMax) {
453 				compMax = chunkIdx[i].size;
454 			};
455 		};
456 		compMax++;
457 
458 		/* Allocate buffers */
459 		chunkMax = cs * bytesPerPixel * cs;
460 		chunkBuf = gdCalloc (chunkMax, 1);
461 		if (!chunkBuf) {
462 			goto fail;
463 		}
464 		compBuf = gdCalloc (compMax, 1);
465 		if (!compBuf) {
466 			goto fail;
467 		}
468 
469 		GD2_DBG (printf ("Largest compressed chunk is %d bytes\n", compMax));
470 	};
471 
472 	/*      if ( (ncx != sx / cs) || (ncy != sy / cs)) { */
473 	/*              goto fail2; */
474 	/*      }; */
475 
476 	/* Read the data... */
477 	for (cy = 0; (cy < ncy); cy++) {
478 		for (cx = 0; (cx < ncx); cx++) {
479 
480 			ylo = cy * cs;
481 			yhi = ylo + cs;
482 			if (yhi > im->sy) {
483 				yhi = im->sy;
484 			};
485 
486 			GD2_DBG (printf
487 			         ("Processing Chunk %d (%d, %d), y from %d to %d\n",
488 			          chunkNum, cx, cy, ylo, yhi));
489 
490 			if (gd2_compressed (fmt)) {
491 
492 				chunkLen = chunkMax;
493 
494 				if (!_gd2ReadChunk (chunkIdx[chunkNum].offset,
495 				                    compBuf,
496 				                    chunkIdx[chunkNum].size,
497 				                    (char *) chunkBuf, &chunkLen, in)) {
498 					GD2_DBG (printf ("Error reading comproessed chunk\n"));
499 					goto fail;
500 				};
501 
502 				chunkPos = 0;
503 			};
504 
505 			for (y = ylo; (y < yhi); y++) {
506 
507 				xlo = cx * cs;
508 				xhi = xlo + cs;
509 				if (xhi > im->sx) {
510 					xhi = im->sx;
511 				};
512 				/*GD2_DBG(printf("y=%d: ",y)); */
513 				if (!gd2_compressed (fmt)) {
514 					for (x = xlo; x < xhi; x++) {
515 
516 						if (im->trueColor) {
517 							if (!gdGetInt (&im->tpixels[y][x], in)) {
518 								gd_error("gd2: EOF while reading\n");
519 								goto fail;
520 							}
521 						} else {
522 							int ch;
523 							if (!gdGetByte (&ch, in)) {
524 								gd_error("gd2: EOF while reading\n");
525 								goto fail;
526 							}
527 							im->pixels[y][x] = ch;
528 						}
529 					}
530 				} else {
531 					for (x = xlo; x < xhi; x++) {
532 						if (im->trueColor) {
533 							/* 2.0.1: work around a gcc bug by being verbose.
534 							   TBB */
535 							int a = chunkBuf[chunkPos++] << 24;
536 							int r = chunkBuf[chunkPos++] << 16;
537 							int g = chunkBuf[chunkPos++] << 8;
538 							int b = chunkBuf[chunkPos++];
539 							/* 2.0.11: tpixels */
540 							im->tpixels[y][x] = a + r + g + b;
541 						} else {
542 							im->pixels[y][x] = chunkBuf[chunkPos++];
543 						}
544 					};
545 				};
546 				/*GD2_DBG(printf("\n")); */
547 			};
548 			chunkNum++;
549 		};
550 	};
551 
552 	GD2_DBG (printf ("Freeing memory\n"));
553 
554 	gdFree (chunkBuf);
555 	gdFree (compBuf);
556 	gdFree (chunkIdx);
557 
558 	GD2_DBG (printf ("Done\n"));
559 
560 	return im;
561 
562 fail:
563 	gdImageDestroy (im);
564 	if (chunkBuf) {
565 		gdFree (chunkBuf);
566 	}
567 	if (compBuf) {
568 		gdFree (compBuf);
569 	}
570 	if (chunkIdx) {
571 		gdFree (chunkIdx);
572 	}
573 	return 0;
574 }
575 
576 
577 /*
578   Function: gdImageCreateFromGd2Part
579 
580     <gdImageCreateFromGd2Part> is called to load parts of images from
581     gd2 format files. Invoked in the same way as <gdImageCreateFromGd2>,
582     but with extra parameters indicating the source (x, y) and
583     width/height of the desired image. <gdImageCreateFromGd2Part>
584     returns a <gdImagePtr> to the new image, or NULL if unable to load
585     the image. The image must eventually be destroyed using
586     <gdImageDestroy>.
587 
588   Variants:
589 
590     <gdImageCreateFromGd2PartPtr> creates an image from GD2 data
591     (i.e. the contents of a GD2 file) already in memory.
592 
593     <gdImageCreateFromGd2Ctx> reads in an image using the functions in
594     a <gdIOCtx> struct.
595 
596   Parameters:
597 
598     infile      - The input FILE pointer
599     srcx, srcy  - The source X and Y coordinates
600     w, h        - The resulting image's width and height
601 
602   Returns:
603 
604     A pointer to the new image or NULL if an error occurred.
605 
606 */
gdImageCreateFromGd2Part(FILE * inFile,int srcx,int srcy,int w,int h)607 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Part (FILE * inFile, int srcx, int srcy, int w, int h)
608 {
609 	gdImagePtr im;
610 	gdIOCtx *in = gdNewFileCtx (inFile);
611 
612 	if (in == NULL) return NULL;
613 	im = gdImageCreateFromGd2PartCtx (in, srcx, srcy, w, h);
614 
615 	in->gd_free (in);
616 
617 	return im;
618 }
619 
620 /*
621   Function: gdImageCreateFromGd2PartPtr
622 
623   Parameters:
624 
625     size        - size of GD data in bytes.
626     data        - GD data (i.e. contents of a GIF file).
627     srcx, srcy  - The source X and Y coordinates
628     w, h        - The resulting image's width and height
629 
630   Reads in part of a GD2 image file stored from memory. See
631   <gdImageCreateFromGd2Part>.
632 */
gdImageCreateFromGd2PartPtr(int size,void * data,int srcx,int srcy,int w,int h)633 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2PartPtr (int size, void *data, int srcx, int srcy, int w,
634         int h)
635 {
636 	gdImagePtr im;
637 	gdIOCtx *in = gdNewDynamicCtxEx (size, data, 0);
638 	if(!in)
639 		return 0;
640 	im = gdImageCreateFromGd2PartCtx (in, srcx, srcy, w, h);
641 	in->gd_free (in);
642 	return im;
643 }
644 
645 
646 /*
647   Function: gdImageCreateFromGd2PartCtx
648 
649   Parameters:
650 
651     in          - The data source.
652     srcx, srcy  - The source X and Y coordinates
653     w, h        - The resulting image's width and height
654 
655   Reads in part of a GD2 data image file via a <gdIOCtx> struct.  See
656   <gdImageCreateFromGd2Part>.
657 */
gdImageCreateFromGd2PartCtx(gdIOCtx * in,int srcx,int srcy,int w,int h)658 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2PartCtx (gdIOCtx * in, int srcx, int srcy, int w, int h)
659 {
660 	int scx, scy, ecx, ecy, fsx, fsy;
661 	int nc, ncx, ncy, cs, cx, cy;
662 	int x, y, ylo, yhi, xlo, xhi;
663 	int dstart, dpos;
664 	int i;
665 	/* 2.0.12: unsigned is correct; fixes problems with color munging.
666 	   Thanks to Steven Brown. */
667 	unsigned int ch;
668 	int vers, fmt;
669 	t_chunk_info *chunkIdx = NULL;
670 	unsigned char *chunkBuf = NULL;
671 	int chunkNum;
672 	int chunkMax = 0;
673 	uLongf chunkLen;
674 	int chunkPos = 0;
675 	int compMax;
676 	char *compBuf = NULL;
677 
678 	gdImagePtr im;
679 
680 	/* */
681 	/* The next few lines are basically copied from gd2CreateFromFile */
682 	/* - we change the file size, so don't want to use the code directly. */
683 	/*   but we do need to know the file size. */
684 	/* */
685 	if (_gd2GetHeader (in, &fsx, &fsy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx)
686 	        != 1) {
687 		goto fail1;
688 	}
689 
690 	GD2_DBG (printf ("File size is %dx%d\n", fsx, fsy));
691 
692 	/* This is the difference - make a file based on size of chunks. */
693 	if (gd2_truecolor (fmt)) {
694 		im = gdImageCreateTrueColor (w, h);
695 	} else {
696 		im = gdImageCreate (w, h);
697 	}
698 	if (im == NULL) {
699 		goto fail1;
700 	};
701 
702 	if (!_gdGetColors (in, im, vers == 2)) {
703 		goto fail2;
704 	}
705 	GD2_DBG (printf ("Image palette completed: %d colours\n", im->colorsTotal));
706 
707 	/* Process the header info */
708 	nc = ncx * ncy;
709 
710 	if (gd2_compressed (fmt)) {
711 		/* Find the maximum compressed chunk size. */
712 		compMax = 0;
713 		for (i = 0; (i < nc); i++) {
714 			if (chunkIdx[i].size > compMax) {
715 				compMax = chunkIdx[i].size;
716 			};
717 		};
718 		compMax++;
719 
720 		if (im->trueColor) {
721 			chunkMax = cs * cs * 4;
722 		} else {
723 			chunkMax = cs * cs;
724 		}
725 		chunkBuf = gdCalloc (chunkMax, 1);
726 		if (!chunkBuf) {
727 			goto fail2;
728 		}
729 		compBuf = gdCalloc (compMax, 1);
730 		if (!compBuf) {
731 			goto fail2;
732 		}
733 
734 	};
735 
736 	/*      Don't bother with this... */
737 	/*      if ( (ncx != sx / cs) || (ncy != sy / cs)) { */
738 	/*              goto fail2; */
739 	/*      }; */
740 
741 
742 	/* Work out start/end chunks */
743 	scx = srcx / cs;
744 	scy = srcy / cs;
745 	if (scx < 0) {
746 		scx = 0;
747 	};
748 	if (scy < 0) {
749 		scy = 0;
750 	};
751 
752 	ecx = (srcx + w) / cs;
753 	ecy = (srcy + h) / cs;
754 	if (ecx >= ncx) {
755 		ecx = ncx - 1;
756 	};
757 	if (ecy >= ncy) {
758 		ecy = ncy - 1;
759 	};
760 
761 	/* Remember file position of image data. */
762 	dstart = gdTell (in);
763 	GD2_DBG (printf ("Data starts at %d\n", dstart));
764 
765 	/* Loop through the chunks. */
766 	for (cy = scy; (cy <= ecy); cy++) {
767 
768 		ylo = cy * cs;
769 		yhi = ylo + cs;
770 		if (yhi > fsy) {
771 			yhi = fsy;
772 		};
773 
774 		for (cx = scx; (cx <= ecx); cx++) {
775 
776 			xlo = cx * cs;
777 			xhi = xlo + cs;
778 			if (xhi > fsx) {
779 				xhi = fsx;
780 			};
781 
782 			GD2_DBG (printf
783 			         ("Processing Chunk (%d, %d), from %d to %d\n", cx, cy, ylo,
784 			          yhi));
785 
786 			if (!gd2_compressed (fmt)) {
787 				GD2_DBG (printf ("Using raw format data\n"));
788 				if (im->trueColor) {
789 					dpos =
790 					    (cy * (cs * fsx) * 4 + cx * cs * (yhi - ylo) * 4) +
791 					    dstart;
792 				} else {
793 					dpos = cy * (cs * fsx) + cx * cs * (yhi - ylo) + dstart;
794 				}
795 				/* gd 2.0.11: gdSeek returns TRUE on success, not 0.
796 				   Longstanding bug. 01/16/03 */
797 				if (!gdSeek (in, dpos)) {
798 					gd_error("Seek error\n");
799 					goto fail2;
800 				};
801 				GD2_DBG (printf
802 				         ("Reading (%d, %d) from position %d\n", cx, cy,
803 				          dpos - dstart));
804 			} else {
805 				chunkNum = cx + cy * ncx;
806 
807 				chunkLen = chunkMax;
808 				if (!_gd2ReadChunk (chunkIdx[chunkNum].offset,
809 				                    compBuf,
810 				                    chunkIdx[chunkNum].size,
811 				                    (char *) chunkBuf, &chunkLen, in)) {
812 					printf ("Error reading comproessed chunk\n");
813 					goto fail2;
814 				};
815 				chunkPos = 0;
816 				GD2_DBG (printf
817 				         ("Reading (%d, %d) from chunk %d\n", cx, cy,
818 				          chunkNum));
819 			};
820 
821 			GD2_DBG (printf
822 			         ("   into (%d, %d) - (%d, %d)\n", xlo, ylo, xhi, yhi));
823 			for (y = ylo; (y < yhi); y++) {
824 
825 				for (x = xlo; x < xhi; x++) {
826 					if (!gd2_compressed (fmt)) {
827 						if (im->trueColor) {
828 							if (!gdGetInt ((int *) &ch, in)) {
829 								ch = 0;
830 								/*printf("EOF while reading file\n"); */
831 								/*goto fail2; */
832 							}
833 						} else {
834 							ch = gdGetC (in);
835 							if ((int) ch == EOF) {
836 								ch = 0;
837 								/*printf("EOF while reading file\n"); */
838 								/*goto fail2; */
839 							}
840 						}
841 					} else {
842 						if (im->trueColor) {
843 							ch = chunkBuf[chunkPos++];
844 							ch = (ch << 8) + chunkBuf[chunkPos++];
845 							ch = (ch << 8) + chunkBuf[chunkPos++];
846 							ch = (ch << 8) + chunkBuf[chunkPos++];
847 						} else {
848 							ch = chunkBuf[chunkPos++];
849 						}
850 					};
851 
852 					/* Only use a point that is in the image. */
853 					if ((x >= srcx) && (x < (srcx + w)) && (x < fsx) && (x >= 0)
854 					        && (y >= srcy) && (y < (srcy + h)) && (y < fsy)
855 					        && (y >= 0)) {
856 						/* 2.0.11: tpixels */
857 						if (im->trueColor) {
858 							im->tpixels[y - srcy][x - srcx] = ch;
859 						} else {
860 							im->pixels[y - srcy][x - srcx] = ch;
861 						}
862 					}
863 				};
864 			};
865 		};
866 	};
867 
868 	gdFree (chunkBuf);
869 	gdFree (compBuf);
870 	gdFree (chunkIdx);
871 
872 	return im;
873 
874 fail2:
875 	gdImageDestroy (im);
876 fail1:
877 	if (chunkBuf) {
878 		gdFree (chunkBuf);
879 	}
880 	if (compBuf) {
881 		gdFree (compBuf);
882 	}
883 	if (chunkIdx) {
884 		gdFree (chunkIdx);
885 	}
886 	return 0;
887 
888 }
889 
890 static void
_gd2PutHeader(gdImagePtr im,gdIOCtx * out,int cs,int fmt,int cx,int cy)891 _gd2PutHeader (gdImagePtr im, gdIOCtx * out, int cs, int fmt, int cx, int cy)
892 {
893 	int i;
894 
895 	/* Send the gd2 id, to verify file format. */
896 	for (i = 0; i < 4; i++) {
897 		gdPutC ((unsigned char) (GD2_ID[i]), out);
898 	};
899 
900 	/* */
901 	/* We put the version info first, so future versions can easily change header info. */
902 	/* */
903 	gdPutWord (GD2_VERS, out);
904 	gdPutWord (im->sx, out);
905 	gdPutWord (im->sy, out);
906 	gdPutWord (cs, out);
907 	gdPutWord (fmt, out);
908 	gdPutWord (cx, out);
909 	gdPutWord (cy, out);
910 
911 }
912 
913 static void
_gdImageGd2(gdImagePtr im,gdIOCtx * out,int cs,int fmt)914 _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)
915 {
916 	int ncx, ncy, cx, cy;
917 	int x, y, ylo, yhi, xlo, xhi;
918 	int chunkLen;
919 	int chunkNum = 0;
920 	char *chunkData = NULL;	/* So we can gdFree it with impunity. */
921 	char *compData = NULL;	/* So we can gdFree it with impunity. */
922 	uLongf compLen;
923 	int idxPos = 0;
924 	int idxSize;
925 	t_chunk_info *chunkIdx = NULL;
926 	int posSave;
927 	int bytesPerPixel = im->trueColor ? 4 : 1;
928 	int compMax = 0;
929 
930 	/*printf("Trying to write GD2 file\n"); */
931 
932 	/* */
933 	/* Force fmt to a valid value since we don't return anything. */
934 	/* */
935 	if ((fmt != GD2_FMT_RAW) && (fmt != GD2_FMT_COMPRESSED)) {
936 		fmt = GD2_FMT_COMPRESSED;
937 	};
938 	if (im->trueColor) {
939 		fmt += 2;
940 	}
941 	/* */
942 	/* Make sure chunk size is valid. These are arbitrary values; 64 because it seems */
943 	/* a little silly to expect performance improvements on a 64x64 bit scale, and  */
944 	/* 4096 because we buffer one chunk, and a 16MB buffer seems a little large - it may be */
945 	/* OK for one user, but for another to read it, they require the buffer. */
946 	/* */
947 	if (cs == 0) {
948 		cs = GD2_CHUNKSIZE;
949 	} else if (cs < GD2_CHUNKSIZE_MIN) {
950 		cs = GD2_CHUNKSIZE_MIN;
951 	} else if (cs > GD2_CHUNKSIZE_MAX) {
952 		cs = GD2_CHUNKSIZE_MAX;
953 	};
954 
955 	/* Work out number of chunks. */
956 	ncx = (im->sx + cs - 1) / cs;
957 	ncy = (im->sy + cs - 1) / cs;
958 
959 	/* Write the standard header. */
960 	_gd2PutHeader (im, out, cs, fmt, ncx, ncy);
961 
962 	if (gd2_compressed (fmt)) {
963 		/* */
964 		/* Work out size of buffer for compressed data, If CHUNKSIZE is large, */
965 		/* then these will be large! */
966 		/* */
967 		/* The zlib notes say output buffer size should be (input size) * 1.01 * 12 */
968 		/* - we'll use 1.02 to be paranoid. */
969 		/* */
970 		compMax = cs * bytesPerPixel * cs * 1.02 + 12;
971 
972 		/* */
973 		/* Allocate the buffers.  */
974 		/* */
975 		chunkData = gdCalloc (cs * bytesPerPixel * cs, 1);
976 		if (!chunkData) {
977 			goto fail;
978 		}
979 		compData = gdCalloc (compMax, 1);
980 		if (!compData) {
981 			goto fail;
982 		}
983 
984 		/* */
985 		/* Save the file position of chunk index, and allocate enough space for */
986 		/* each chunk_info block . */
987 		/* */
988 		idxPos = gdTell (out);
989 		idxSize = ncx * ncy * sizeof (t_chunk_info);
990 		GD2_DBG (printf ("Index size is %d\n", idxSize));
991 		gdSeek (out, idxPos + idxSize);
992 
993 		chunkIdx = gdCalloc (idxSize * sizeof (t_chunk_info), 1);
994 		if (!chunkIdx) {
995 			goto fail;
996 		}
997 	};
998 
999 	_gdPutColors (im, out);
1000 
1001 	GD2_DBG (printf ("Size: %dx%d\n", im->sx, im->sy));
1002 	GD2_DBG (printf ("Chunks: %dx%d\n", ncx, ncy));
1003 
1004 	for (cy = 0; (cy < ncy); cy++) {
1005 		for (cx = 0; (cx < ncx); cx++) {
1006 
1007 			ylo = cy * cs;
1008 			yhi = ylo + cs;
1009 			if (yhi > im->sy) {
1010 				yhi = im->sy;
1011 			};
1012 
1013 			GD2_DBG (printf
1014 			         ("Processing Chunk (%dx%d), y from %d to %d\n", cx, cy,
1015 			          ylo, yhi));
1016 			chunkLen = 0;
1017 			for (y = ylo; (y < yhi); y++) {
1018 
1019 				/*GD2_DBG(printf("y=%d: ",y)); */
1020 
1021 				xlo = cx * cs;
1022 				xhi = xlo + cs;
1023 				if (xhi > im->sx) {
1024 					xhi = im->sx;
1025 				};
1026 
1027 				if (gd2_compressed (fmt)) {
1028 					for (x = xlo; x < xhi; x++) {
1029 						/* 2.0.11: use truecolor pixel array. TBB */
1030 						/*GD2_DBG(printf("%d...",x)); */
1031 						if (im->trueColor) {
1032 							int p = im->tpixels[y][x];
1033 							chunkData[chunkLen++] = gdTrueColorGetAlpha (p);
1034 							chunkData[chunkLen++] = gdTrueColorGetRed (p);
1035 							chunkData[chunkLen++] = gdTrueColorGetGreen (p);
1036 							chunkData[chunkLen++] = gdTrueColorGetBlue (p);
1037 						} else {
1038 							int p = im->pixels[y][x];
1039 							chunkData[chunkLen++] = p;
1040 						}
1041 					};
1042 				} else {
1043 					for (x = xlo; x < xhi; x++) {
1044 						/*GD2_DBG(printf("%d, ",x)); */
1045 
1046 						if (im->trueColor) {
1047 							gdPutInt (im->tpixels[y][x], out);
1048 						} else {
1049 							gdPutC ((unsigned char) im->pixels[y][x], out);
1050 						}
1051 					};
1052 				};
1053 				/*GD2_DBG(printf("y=%d done.\n",y)); */
1054 			};
1055 			if (gd2_compressed (fmt)) {
1056 				compLen = compMax;
1057 				if (compress ((unsigned char *)
1058 				              &compData[0], &compLen,
1059 				              (unsigned char *) &chunkData[0],
1060 				              chunkLen) != Z_OK) {
1061 					printf ("Error from compressing\n");
1062 				} else {
1063 					chunkIdx[chunkNum].offset = gdTell (out);
1064 					chunkIdx[chunkNum++].size = compLen;
1065 					GD2_DBG (printf
1066 					         ("Chunk %d size %d offset %d\n", chunkNum,
1067 					          chunkIdx[chunkNum - 1].size,
1068 					          chunkIdx[chunkNum - 1].offset));
1069 
1070 					if (gdPutBuf (compData, compLen, out) <= 0) {
1071 						gd_error("gd write error\n");
1072 					};
1073 				};
1074 			};
1075 		};
1076 	};
1077 	if (gd2_compressed (fmt)) {
1078 		/* Save the position, write the index, restore position (paranoia). */
1079 		GD2_DBG (printf ("Seeking %d to write index\n", idxPos));
1080 		posSave = gdTell (out);
1081 		gdSeek (out, idxPos);
1082 		GD2_DBG (printf ("Writing index\n"));
1083 		for (x = 0; x < chunkNum; x++) {
1084 			GD2_DBG (printf
1085 			         ("Chunk %d size %d offset %d\n", x, chunkIdx[x].size,
1086 			          chunkIdx[x].offset));
1087 			gdPutInt (chunkIdx[x].offset, out);
1088 			gdPutInt (chunkIdx[x].size, out);
1089 		};
1090 		/* We don't use fwrite for *endian reasons. */
1091 		/*fwrite(chunkIdx, sizeof(int)*2, chunkNum, out); */
1092 		gdSeek (out, posSave);
1093 	};
1094 
1095 	/*printf("Memory block size is %d\n",gdTell(out)); */
1096 fail:
1097 	GD2_DBG (printf ("Freeing memory\n"));
1098 
1099 	if (chunkData) {
1100 		gdFree (chunkData);
1101 	}
1102 	if (compData) {
1103 		gdFree (compData);
1104 	}
1105 	if (chunkIdx) {
1106 		gdFree (chunkIdx);
1107 	}
1108 	GD2_DBG (printf ("Done\n"));
1109 
1110 }
1111 
1112 /*
1113 	Function: gdImageGd2
1114 */
gdImageGd2(gdImagePtr im,FILE * outFile,int cs,int fmt)1115 BGD_DECLARE(void) gdImageGd2 (gdImagePtr im, FILE * outFile, int cs, int fmt)
1116 {
1117 	gdIOCtx *out = gdNewFileCtx (outFile);
1118 	if (out == NULL) return;
1119 	_gdImageGd2 (im, out, cs, fmt);
1120 	out->gd_free (out);
1121 }
1122 
1123 /*
1124 	Function: gdImageGd2Ptr
1125 */
gdImageGd2Ptr(gdImagePtr im,int cs,int fmt,int * size)1126 BGD_DECLARE(void *) gdImageGd2Ptr (gdImagePtr im, int cs, int fmt, int *size)
1127 {
1128 	void *rv;
1129 	gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
1130 	if (out == NULL) return NULL;
1131 	_gdImageGd2 (im, out, cs, fmt);
1132 	rv = gdDPExtractData (out, size);
1133 	out->gd_free (out);
1134 	return rv;
1135 }
1136 
1137 #else /* no HAVE_LIBZ or !ENABLE_GD_FORMATS */
_noGd2Error(void)1138 static void _noGd2Error (void)
1139 {
1140 #if !ENABLE_GD_FORMATS
1141 	gd_error("GD2 image support has been disabled\n");
1142 #else
1143 	gd_error("GD2 support is not available - no libz\n");
1144 #endif
1145 }
1146 
gdImageCreateFromGd2(FILE * inFile)1147 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2 (FILE * inFile)
1148 {
1149 	_noGd2Error();
1150 	return NULL;
1151 }
1152 
gdImageCreateFromGd2Ctx(gdIOCtxPtr in)1153 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ctx (gdIOCtxPtr in)
1154 {
1155 	_noGd2Error();
1156 	return NULL;
1157 }
1158 
gdImageCreateFromGd2Part(FILE * inFile,int srcx,int srcy,int w,int h)1159 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Part (FILE * inFile, int srcx, int srcy, int w, int h)
1160 {
1161 	_noGd2Error();
1162 	return NULL;
1163 }
1164 
gdImageCreateFromGd2Ptr(int size,void * data)1165 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ptr (int size, void *data)
1166 {
1167 	_noGd2Error();
1168 	return NULL;
1169 }
1170 
gdImageCreateFromGd2PartCtx(gdIOCtx * in,int srcx,int srcy,int w,int h)1171 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2PartCtx (gdIOCtx * in, int srcx, int srcy, int w, int h)
1172 {
1173 	_noGd2Error();
1174 	return NULL;
1175 }
1176 
gdImageCreateFromGd2PartPtr(int size,void * data,int srcx,int srcy,int w,int h)1177 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2PartPtr (int size, void *data, int srcx, int srcy, int w,
1178         int h)
1179 {
1180 	_noGd2Error();
1181 	return NULL;
1182 }
1183 
gdImageGd2(gdImagePtr im,FILE * outFile,int cs,int fmt)1184 BGD_DECLARE(void) gdImageGd2 (gdImagePtr im, FILE * outFile, int cs, int fmt)
1185 {
1186 	_noGd2Error();
1187 }
1188 
gdImageGd2Ptr(gdImagePtr im,int cs,int fmt,int * size)1189 BGD_DECLARE(void *) gdImageGd2Ptr (gdImagePtr im, int cs, int fmt, int *size)
1190 {
1191 	_noGd2Error();
1192 	return NULL;
1193 }
1194 #endif /* HAVE_LIBZ */
1195