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 : /* */
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 7 : {
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 35 : for (i = 0; i < 4; i++) {
66 28 : ch = gdGetC(in);
67 28 : if (ch == EOF) {
68 0 : goto fail1;
69 : }
70 28 : id[i] = ch;
71 : }
72 7 : id[4] = 0;
73 :
74 : GD2_DBG(php_gd_error("Got file code: %s", id));
75 :
76 : /* Equiv. of 'magick'. */
77 7 : if (strcmp(id, GD2_ID) != 0) {
78 : GD2_DBG(php_gd_error("Not a valid gd2 file"));
79 0 : goto fail1;
80 : }
81 :
82 : /* Version */
83 7 : if (gdGetWord(vers, in) != 1) {
84 0 : goto fail1;
85 : }
86 : GD2_DBG(php_gd_error("Version: %d", *vers));
87 :
88 7 : if ((*vers != 1) && (*vers != 2)) {
89 : GD2_DBG(php_gd_error("Bad version: %d", *vers));
90 0 : goto fail1;
91 : }
92 :
93 : /* Image Size */
94 7 : if (!gdGetWord(sx, in)) {
95 : GD2_DBG(php_gd_error("Could not get x-size"));
96 0 : goto fail1;
97 : }
98 7 : if (!gdGetWord(sy, in)) {
99 : GD2_DBG(php_gd_error("Could not get y-size"));
100 0 : goto fail1;
101 : }
102 : GD2_DBG(php_gd_error("Image is %dx%d", *sx, *sy));
103 :
104 : /* Chunk Size (pixels, not bytes!) */
105 7 : if (gdGetWord(cs, in) != 1) {
106 0 : goto fail1;
107 : }
108 : GD2_DBG(php_gd_error("ChunkSize: %d", *cs));
109 :
110 7 : 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 7 : if (gdGetWord(fmt, in) != 1) {
117 0 : goto fail1;
118 : }
119 : GD2_DBG(php_gd_error("Format: %d", *fmt));
120 :
121 7 : 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 0 : goto fail1;
124 : }
125 :
126 : /* # of chunks wide */
127 7 : if (gdGetWord(ncx, in) != 1) {
128 0 : goto fail1;
129 : }
130 : GD2_DBG(php_gd_error("%d Chunks Wide", *ncx));
131 :
132 : /* # of chunks high */
133 7 : if (gdGetWord(ncy, in) != 1) {
134 0 : goto fail1;
135 : }
136 : GD2_DBG(php_gd_error("%d Chunks vertically", *ncy));
137 :
138 7 : if (gd2_compressed(*fmt)) {
139 0 : nc = (*ncx) * (*ncy);
140 : GD2_DBG(php_gd_error("Reading %d chunk index entries", nc));
141 0 : sidx = sizeof(t_chunk_info) * nc;
142 0 : if (sidx <= 0) {
143 0 : goto fail1;
144 : }
145 0 : cidx = gdCalloc(sidx, 1);
146 0 : for (i = 0; i < nc; i++) {
147 0 : if (gdGetInt(&cidx[i].offset, in) != 1) {
148 0 : goto fail1;
149 : }
150 0 : if (gdGetInt(&cidx[i].size, in) != 1) {
151 0 : goto fail1;
152 : }
153 : }
154 0 : *chunkIdx = cidx;
155 : }
156 :
157 : GD2_DBG(php_gd_error("gd2 header complete"));
158 :
159 7 : return 1;
160 :
161 0 : fail1:
162 0 : return 0;
163 : }
164 :
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 6 : {
167 : gdImagePtr im;
168 :
169 6 : if (_gd2GetHeader (in, sx, sy, cs, vers, fmt, ncx, ncy, cidx) != 1) {
170 : GD2_DBG(php_gd_error("Bad GD2 header"));
171 0 : goto fail1;
172 : }
173 :
174 12 : if (gd2_truecolor(*fmt)) {
175 6 : im = gdImageCreateTrueColor(*sx, *sy);
176 : } else {
177 0 : im = gdImageCreate(*sx, *sy);
178 : }
179 6 : if (im == NULL) {
180 : GD2_DBG(php_gd_error("Could not create gdImage"));
181 0 : goto fail1;
182 : }
183 :
184 6 : if (!_gdGetColors(in, im, (*vers) == 2)) {
185 : GD2_DBG(php_gd_error("Could not read color palette"));
186 0 : goto fail2;
187 : }
188 : GD2_DBG(php_gd_error("Image palette completed: %d colours", im->colorsTotal));
189 :
190 6 : return im;
191 :
192 0 : fail2:
193 0 : gdImageDestroy(im);
194 0 : return 0;
195 :
196 0 : fail1:
197 0 : return 0;
198 : }
199 :
200 : static int _gd2ReadChunk (int offset, char *compBuf, int compSize, char *chunkBuf, uLongf * chunkLen, gdIOCtx * in)
201 0 : {
202 : int zerr;
203 :
204 0 : if (gdTell(in) != offset) {
205 : GD2_DBG(php_gd_error("Positioning in file to %d", offset));
206 0 : 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 0 : if (gdGetBuf(compBuf, compSize, in) != compSize) {
214 0 : return FALSE;
215 : }
216 : GD2_DBG(php_gd_error("Got %d bytes. Uncompressing into buffer of %d bytes", compSize, (int)*chunkLen));
217 0 : zerr = uncompress((unsigned char *) chunkBuf, chunkLen, (unsigned char *) compBuf, compSize);
218 0 : if (zerr != Z_OK) {
219 : GD2_DBG(php_gd_error("Error %d from uncompress", zerr));
220 0 : return FALSE;
221 : }
222 : GD2_DBG(php_gd_error("Got chunk"));
223 :
224 0 : return TRUE;
225 : }
226 :
227 : gdImagePtr gdImageCreateFromGd2 (FILE * inFile)
228 3 : {
229 3 : gdIOCtx *in = gdNewFileCtx(inFile);
230 : gdImagePtr im;
231 :
232 3 : im = gdImageCreateFromGd2Ctx(in);
233 :
234 3 : in->gd_free(in);
235 :
236 3 : return im;
237 : }
238 :
239 : gdImagePtr gdImageCreateFromGd2Ptr (int size, void *data)
240 0 : {
241 : gdImagePtr im;
242 0 : gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
243 0 : im = gdImageCreateFromGd2Ctx(in);
244 0 : in->gd_free(in);
245 :
246 0 : return im;
247 : }
248 :
249 : gdImagePtr gdImageCreateFromGd2Ctx (gdIOCtxPtr in)
250 6 : {
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 6 : t_chunk_info *chunkIdx = NULL; /* So we can gdFree it with impunity. */
257 6 : unsigned char *chunkBuf = NULL; /* So we can gdFree it with impunity. */
258 6 : int chunkNum = 0;
259 6 : int chunkMax = 0;
260 : uLongf chunkLen;
261 6 : int chunkPos = 0;
262 6 : int compMax = 0;
263 : int bytesPerPixel;
264 6 : char *compBuf = NULL; /* So we can gdFree it with impunity. */
265 :
266 : gdImagePtr im;
267 :
268 : /* Get the header */
269 6 : if (!(im = _gd2CreateFromFile(in, &sx, &sy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx))) {
270 0 : return 0;
271 : }
272 :
273 6 : bytesPerPixel = im->trueColor ? 4 : 1;
274 6 : nc = ncx * ncy;
275 :
276 6 : if (gd2_compressed(fmt)) {
277 : /* Find the maximum compressed chunk size. */
278 0 : compMax = 0;
279 0 : for (i = 0; (i < nc); i++) {
280 0 : if (chunkIdx[i].size > compMax) {
281 0 : compMax = chunkIdx[i].size;
282 : }
283 : }
284 0 : compMax++;
285 :
286 : /* Allocate buffers */
287 0 : chunkMax = cs * bytesPerPixel * cs;
288 0 : if (chunkMax <= 0) {
289 0 : return 0;
290 : }
291 0 : chunkBuf = gdCalloc(chunkMax, 1);
292 0 : 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 14 : for (cy = 0; (cy < ncy); cy++) {
299 22 : for (cx = 0; (cx < ncx); cx++) {
300 14 : ylo = cy * cs;
301 14 : yhi = ylo + cs;
302 14 : if (yhi > im->sy) {
303 8 : 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 14 : if (gd2_compressed(fmt)) {
309 0 : chunkLen = chunkMax;
310 :
311 0 : if (!_gd2ReadChunk(chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, (char *) chunkBuf, &chunkLen, in)) {
312 : GD2_DBG(php_gd_error("Error reading comproessed chunk"));
313 0 : goto fail2;
314 : }
315 :
316 0 : chunkPos = 0;
317 : }
318 :
319 897 : for (y = ylo; (y < yhi); y++) {
320 883 : xlo = cx * cs;
321 883 : xhi = xlo + cs;
322 883 : if (xhi > im->sx) {
323 371 : xhi = im->sx;
324 : }
325 :
326 1766 : if (!gd2_compressed(fmt)) {
327 72444 : for (x = xlo; x < xhi; x++) {
328 71561 : if (im->trueColor) {
329 71561 : if (!gdGetInt(&im->tpixels[y][x], in)) {
330 0 : im->tpixels[y][x] = 0;
331 : }
332 : } else {
333 : int ch;
334 0 : if (!gdGetByte(&ch, in)) {
335 0 : ch = 0;
336 : }
337 0 : im->pixels[y][x] = ch;
338 : }
339 : }
340 : } else {
341 0 : for (x = xlo; x < xhi; x++) {
342 0 : if (im->trueColor) {
343 : /* 2.0.1: work around a gcc bug by being verbose. TBB */
344 0 : int a = chunkBuf[chunkPos++] << 24;
345 0 : int r = chunkBuf[chunkPos++] << 16;
346 0 : int g = chunkBuf[chunkPos++] << 8;
347 0 : int b = chunkBuf[chunkPos++];
348 0 : im->tpixels[y][x] = a + r + g + b;
349 : } else {
350 0 : im->pixels[y][x] = chunkBuf[chunkPos++];
351 : }
352 : }
353 : }
354 : }
355 14 : chunkNum++;
356 : }
357 : }
358 :
359 : GD2_DBG(php_gd_error("Freeing memory"));
360 :
361 6 : if (chunkBuf) {
362 0 : gdFree(chunkBuf);
363 : }
364 6 : if (compBuf) {
365 0 : gdFree(compBuf);
366 : }
367 6 : if (chunkIdx) {
368 0 : gdFree(chunkIdx);
369 : }
370 :
371 : GD2_DBG(php_gd_error("Done"));
372 :
373 6 : return im;
374 :
375 0 : fail2:
376 0 : gdImageDestroy(im);
377 0 : if (chunkBuf) {
378 0 : gdFree(chunkBuf);
379 : }
380 0 : if (compBuf) {
381 0 : gdFree(compBuf);
382 : }
383 0 : if (chunkIdx) {
384 0 : gdFree(chunkIdx);
385 : }
386 :
387 0 : return 0;
388 : }
389 :
390 : gdImagePtr gdImageCreateFromGd2PartPtr (int size, void *data, int srcx, int srcy, int w, int h)
391 0 : {
392 : gdImagePtr im;
393 0 : gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
394 0 : im = gdImageCreateFromGd2PartCtx(in, srcx, srcy, w, h);
395 0 : in->gd_free(in);
396 :
397 0 : return im;
398 : }
399 :
400 : gdImagePtr gdImageCreateFromGd2Part (FILE * inFile, int srcx, int srcy, int w, int h)
401 1 : {
402 : gdImagePtr im;
403 1 : gdIOCtx *in = gdNewFileCtx(inFile);
404 :
405 1 : im = gdImageCreateFromGd2PartCtx(in, srcx, srcy, w, h);
406 :
407 1 : in->gd_free(in);
408 :
409 1 : return im;
410 : }
411 :
412 : gdImagePtr gdImageCreateFromGd2PartCtx (gdIOCtx * in, int srcx, int srcy, int w, int h)
413 1 : {
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 1 : t_chunk_info *chunkIdx = NULL;
423 1 : unsigned char *chunkBuf = NULL;
424 : int chunkNum;
425 1 : int chunkMax = 0;
426 : uLongf chunkLen;
427 1 : int chunkPos = 0;
428 : int compMax;
429 1 : char *compBuf = NULL;
430 :
431 : gdImagePtr im;
432 :
433 1 : if (w<1 || h <1) {
434 0 : 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 1 : if (_gd2GetHeader(in, &fsx, &fsy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx) != 1) {
442 0 : 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 2 : if (gd2_truecolor(fmt)) {
449 1 : im = gdImageCreateTrueColor(w, h);
450 : } else {
451 0 : im = gdImageCreate(w, h);
452 : }
453 1 : if (im == NULL) {
454 0 : goto fail1;
455 : }
456 :
457 1 : if (!_gdGetColors(in, im, vers == 2)) {
458 0 : goto fail2;
459 : }
460 : GD2_DBG(php_gd_error("Image palette completed: %d colours", im->colorsTotal));
461 :
462 : /* Process the header info */
463 1 : nc = ncx * ncy;
464 :
465 1 : if (gd2_compressed(fmt)) {
466 : /* Find the maximum compressed chunk size. */
467 0 : compMax = 0;
468 0 : for (i = 0; (i < nc); i++) {
469 0 : if (chunkIdx[i].size > compMax) {
470 0 : compMax = chunkIdx[i].size;
471 : }
472 : }
473 0 : compMax++;
474 :
475 0 : if (im->trueColor) {
476 0 : chunkMax = cs * cs * 4;
477 : } else {
478 0 : chunkMax = cs * cs;
479 : }
480 0 : if (chunkMax <= 0) {
481 0 : goto fail2;
482 : }
483 :
484 0 : chunkBuf = gdCalloc(chunkMax, 1);
485 0 : compBuf = gdCalloc(compMax, 1);
486 : }
487 :
488 : /* Work out start/end chunks */
489 1 : scx = srcx / cs;
490 1 : scy = srcy / cs;
491 1 : if (scx < 0) {
492 0 : scx = 0;
493 : }
494 1 : if (scy < 0) {
495 0 : scy = 0;
496 : }
497 :
498 1 : ecx = (srcx + w) / cs;
499 1 : ecy = (srcy + h) / cs;
500 1 : if (ecx >= ncx) {
501 0 : ecx = ncx - 1;
502 : }
503 1 : if (ecy >= ncy) {
504 0 : ecy = ncy - 1;
505 : }
506 :
507 : /* Remember file position of image data. */
508 1 : dstart = gdTell(in);
509 : GD2_DBG(php_gd_error("Data starts at %d", dstart));
510 :
511 : /* Loop through the chunks. */
512 2 : for (cy = scy; (cy <= ecy); cy++) {
513 1 : ylo = cy * cs;
514 1 : yhi = ylo + cs;
515 1 : if (yhi > fsy) {
516 1 : yhi = fsy;
517 : }
518 :
519 2 : for (cx = scx; cx <= ecx; cx++) {
520 :
521 1 : xlo = cx * cs;
522 1 : xhi = xlo + cs;
523 1 : if (xhi > fsx) {
524 1 : xhi = fsx;
525 : }
526 :
527 : GD2_DBG(php_gd_error("Processing Chunk (%d, %d), from %d to %d", cx, cy, ylo, yhi));
528 :
529 2 : if (!gd2_compressed(fmt)) {
530 : GD2_DBG(php_gd_error("Using raw format data"));
531 1 : if (im->trueColor) {
532 1 : dpos = (cy * (cs * fsx) * 4 + cx * cs * (yhi - ylo) * 4) + dstart;
533 : } else {
534 0 : 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 1 : if (!gdSeek(in, dpos)) {
539 0 : php_gd_error_ex(E_WARNING, "Error from seek: %d", errno);
540 0 : goto fail2;
541 : }
542 : GD2_DBG(php_gd_error("Reading (%d, %d) from position %d", cx, cy, dpos - dstart));
543 : } else {
544 0 : chunkNum = cx + cy * ncx;
545 :
546 0 : chunkLen = chunkMax;
547 0 : if (!_gd2ReadChunk (chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, (char *)chunkBuf, &chunkLen, in)) {
548 0 : php_gd_error("Error reading comproessed chunk");
549 0 : goto fail2;
550 : }
551 0 : 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 11 : for (y = ylo; (y < yhi); y++) {
558 110 : for (x = xlo; x < xhi; x++) {
559 200 : if (!gd2_compressed(fmt)) {
560 100 : if (im->trueColor) {
561 100 : if (!gdGetInt((int *)&ch, in)) {
562 0 : ch = 0;
563 : }
564 : } else {
565 0 : ch = gdGetC(in);
566 0 : if ((int)ch == EOF) {
567 0 : ch = 0;
568 : }
569 : }
570 : } else {
571 0 : if (im->trueColor) {
572 0 : ch = chunkBuf[chunkPos++];
573 0 : ch = (ch << 8) + chunkBuf[chunkPos++];
574 0 : ch = (ch << 8) + chunkBuf[chunkPos++];
575 0 : ch = (ch << 8) + chunkBuf[chunkPos++];
576 : } else {
577 0 : ch = chunkBuf[chunkPos++];
578 : }
579 : }
580 :
581 : /* Only use a point that is in the image. */
582 100 : if ((x >= srcx) && (x < (srcx + w)) && (x < fsx) && (x >= 0) && (y >= srcy) && (y < (srcy + h)) && (y < fsy) && (y >= 0)) {
583 4 : if (im->trueColor) {
584 4 : im->tpixels[y - srcy][x - srcx] = ch;
585 : } else {
586 0 : im->pixels[y - srcy][x - srcx] = ch;
587 : }
588 : }
589 : }
590 : }
591 : }
592 : }
593 :
594 1 : if (chunkBuf) {
595 0 : gdFree(chunkBuf);
596 : }
597 1 : if (compBuf) {
598 0 : gdFree(compBuf);
599 : }
600 1 : if (chunkIdx) {
601 0 : gdFree(chunkIdx);
602 : }
603 :
604 1 : return im;
605 :
606 0 : fail2:
607 0 : gdImageDestroy(im);
608 0 : fail1:
609 0 : if (chunkBuf) {
610 0 : gdFree(chunkBuf);
611 : }
612 0 : if (compBuf) {
613 0 : gdFree(compBuf);
614 : }
615 0 : if (chunkIdx) {
616 0 : gdFree(chunkIdx);
617 : }
618 :
619 0 : return 0;
620 : }
621 :
622 : static void _gd2PutHeader (gdImagePtr im, gdIOCtx * out, int cs, int fmt, int cx, int cy)
623 7 : {
624 : int i;
625 :
626 : /* Send the gd2 id, to verify file format. */
627 35 : for (i = 0; i < 4; i++) {
628 28 : 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 7 : gdPutWord(GD2_VERS, out);
634 7 : gdPutWord(im->sx, out);
635 7 : gdPutWord(im->sy, out);
636 7 : gdPutWord(cs, out);
637 7 : gdPutWord(fmt, out);
638 7 : gdPutWord(cx, out);
639 7 : gdPutWord(cy, out);
640 7 : }
641 :
642 : static void _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)
643 7 : {
644 : int ncx, ncy, cx, cy;
645 : int x, y, ylo, yhi, xlo, xhi;
646 : int chunkLen;
647 7 : int chunkNum = 0;
648 7 : char *chunkData = NULL; /* So we can gdFree it with impunity. */
649 7 : char *compData = NULL; /* So we can gdFree it with impunity. */
650 : uLongf compLen;
651 7 : int idxPos = 0;
652 : int idxSize;
653 7 : t_chunk_info *chunkIdx = NULL; /* So we can gdFree it with impunity. */
654 : int posSave;
655 7 : int bytesPerPixel = im->trueColor ? 4 : 1;
656 7 : int compMax = 0;
657 :
658 : /* Force fmt to a valid value since we don't return anything. */
659 7 : if ((fmt != GD2_FMT_RAW) && (fmt != GD2_FMT_COMPRESSED)) {
660 0 : fmt = im->trueColor ? GD2_FMT_TRUECOLOR_COMPRESSED : GD2_FMT_COMPRESSED;
661 : }
662 7 : if (im->trueColor) {
663 5 : 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 7 : if (cs == 0) {
671 0 : cs = GD2_CHUNKSIZE;
672 7 : } else if (cs < GD2_CHUNKSIZE_MIN) {
673 0 : cs = GD2_CHUNKSIZE_MIN;
674 7 : } else if (cs > GD2_CHUNKSIZE_MAX) {
675 0 : cs = GD2_CHUNKSIZE_MAX;
676 : }
677 :
678 : /* Work out number of chunks. */
679 7 : ncx = im->sx / cs + 1;
680 7 : ncy = im->sy / cs + 1;
681 :
682 : /* Write the standard header. */
683 7 : _gd2PutHeader (im, out, cs, fmt, ncx, ncy);
684 :
685 7 : 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 0 : compMax = (int)(cs * bytesPerPixel * cs * 1.02f) + 12;
694 :
695 : /* Allocate the buffers. */
696 0 : chunkData = safe_emalloc(cs * bytesPerPixel, cs, 0);
697 0 : memset(chunkData, 0, cs * bytesPerPixel * cs);
698 0 : if (compMax <= 0) {
699 0 : goto fail;
700 : }
701 0 : 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 0 : idxPos = gdTell(out);
707 0 : idxSize = ncx * ncy * sizeof(t_chunk_info);
708 : GD2_DBG(php_gd_error("Index size is %d", idxSize));
709 0 : gdSeek(out, idxPos + idxSize);
710 :
711 0 : chunkIdx = safe_emalloc(idxSize, sizeof(t_chunk_info), 0);
712 0 : memset(chunkIdx, 0, idxSize * sizeof(t_chunk_info));
713 : }
714 :
715 7 : _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 16 : for (cy = 0; (cy < ncy); cy++) {
721 24 : for (cx = 0; (cx < ncx); cx++) {
722 15 : ylo = cy * cs;
723 15 : yhi = ylo + cs;
724 15 : if (yhi > im->sy) {
725 9 : 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 15 : chunkLen = 0;
730 1103 : for (y = ylo; (y < yhi); y++) {
731 : GD2_DBG(php_gd_error("y=%d: ",y));
732 1088 : xlo = cx * cs;
733 1088 : xhi = xlo + cs;
734 1088 : if (xhi > im->sx) {
735 576 : xhi = im->sx;
736 : }
737 :
738 1088 : if (gd2_compressed(fmt)) {
739 0 : for (x = xlo; x < xhi; x++) {
740 : GD2_DBG(php_gd_error("%d...",x));
741 0 : if (im->trueColor) {
742 0 : int p = im->tpixels[y][x];
743 0 : chunkData[chunkLen++] = gdTrueColorGetAlpha(p);
744 0 : chunkData[chunkLen++] = gdTrueColorGetRed(p);
745 0 : chunkData[chunkLen++] = gdTrueColorGetGreen(p);
746 0 : chunkData[chunkLen++] = gdTrueColorGetBlue(p);
747 : } else {
748 0 : chunkData[chunkLen++] = im->pixels[y][x];
749 : }
750 : }
751 : } else {
752 81574 : for (x = xlo; x < xhi; x++) {
753 : GD2_DBG(php_gd_error("%d, ",x));
754 :
755 80486 : if (im->trueColor) {
756 72361 : gdPutInt(im->tpixels[y][x], out);
757 : } else {
758 8125 : gdPutC((unsigned char) im->pixels[y][x], out);
759 : }
760 : }
761 : }
762 : GD2_DBG(php_gd_error("y=%d done.",y));
763 : }
764 :
765 15 : if (gd2_compressed(fmt)) {
766 0 : compLen = compMax;
767 0 : if (compress((unsigned char *) &compData[0], &compLen, (unsigned char *) &chunkData[0], chunkLen) != Z_OK) {
768 0 : php_gd_error("Error from compressing");
769 : } else {
770 0 : chunkIdx[chunkNum].offset = gdTell(out);
771 0 : 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 0 : if (gdPutBuf (compData, compLen, out) <= 0) {
775 : /* Any alternate suggestions for handling this? */
776 0 : php_gd_error_ex(E_WARNING, "Error %d on write", errno);
777 : }
778 : }
779 : }
780 : }
781 : }
782 :
783 7 : 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 0 : posSave = gdTell(out);
787 0 : gdSeek(out, idxPos);
788 : GD2_DBG(php_gd_error("Writing index"));
789 0 : 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 0 : gdPutInt(chunkIdx[x].offset, out);
792 0 : gdPutInt(chunkIdx[x].size, out);
793 : }
794 0 : gdSeek(out, posSave);
795 : }
796 7 : fail:
797 : GD2_DBG(php_gd_error("Freeing memory"));
798 7 : if (chunkData) {
799 0 : gdFree(chunkData);
800 : }
801 7 : if (compData) {
802 0 : gdFree(compData);
803 : }
804 7 : if (chunkIdx) {
805 0 : gdFree(chunkIdx);
806 : }
807 : GD2_DBG(php_gd_error("Done"));
808 7 : }
809 :
810 : void gdImageGd2 (gdImagePtr im, FILE * outFile, int cs, int fmt)
811 7 : {
812 7 : gdIOCtx *out = gdNewFileCtx(outFile);
813 :
814 7 : _gdImageGd2(im, out, cs, fmt);
815 :
816 7 : out->gd_free(out);
817 7 : }
818 :
819 : void *gdImageGd2Ptr (gdImagePtr im, int cs, int fmt, int *size)
820 0 : {
821 : void *rv;
822 0 : gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
823 :
824 0 : _gdImageGd2(im, out, cs, fmt);
825 0 : rv = gdDPExtractData(out, size);
826 0 : out->gd_free(out);
827 :
828 0 : return rv;
829 : }
|