3 * Image handling tools, (c) AJK 2001-2005
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
35 unsigned char const bbc[] = {
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
37 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // !
38 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, // "
39 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00, // #
40 0x0C, 0x3F, 0x68, 0x3E, 0x0B, 0x7E, 0x18, 0x00, // $
41 0x60, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x06, 0x00, // %
42 0x38, 0x6C, 0x6C, 0x38, 0x6D, 0x66, 0x3B, 0x00, // &
43 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, // '
44 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, // (
45 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, // )
46 0x00, 0x18, 0x7E, 0x3C, 0x7E, 0x18, 0x00, 0x00, // *
47 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // +
48 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, // ,
49 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, // -
50 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // .
51 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, // /
52 0x18, 0x24, 0x66, 0x66, 0x66, 0x24, 0x18, 0x00, // 0 (non crossed)
53 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, // 1
54 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x7E, 0x00, // 2
55 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00, // 3
56 0x0C, 0x1C, 0x3C, 0x6C, 0x7E, 0x0C, 0x0C, 0x00, // 4
57 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00, // 5
58 0x1C, 0x30, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, // 6
59 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, // 7
60 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // 8
61 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00, // 9
62 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, // :
63 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, // ;
64 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, // <
65 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, // =
66 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00, // >
67 0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00, // ?
68 0x3C, 0x66, 0x6E, 0x6A, 0x6E, 0x60, 0x3C, 0x00, // @
69 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // A
70 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, // B
71 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, // C
72 0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00, // D
73 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00, // E
74 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00, // F
75 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00, // G
76 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // H
77 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, // I
78 0x3E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, // J
79 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00, // K
80 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, // L
81 0x63, 0x77, 0x7F, 0x6B, 0x6B, 0x63, 0x63, 0x00, // M
82 0x66, 0x66, 0x76, 0x7E, 0x6E, 0x66, 0x66, 0x00, // N
83 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // O
84 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, // P
85 0x3C, 0x66, 0x66, 0x66, 0x6A, 0x6C, 0x36, 0x00, // Q
86 0x7C, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x00, // R
87 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, // S
88 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // T
89 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // U
90 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // V
91 0x63, 0x63, 0x6B, 0x6B, 0x7F, 0x77, 0x63, 0x00, // W
92 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, // X
93 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // Y
94 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, // Z
95 0x7C, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7C, 0x00, // [
96 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, //
97 0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, // ]
98 0x18, 0x3C, 0x66, 0x42, 0x00, 0x00, 0x00, 0x00, // ^
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // _
100 0x1C, 0x36, 0x30, 0x7C, 0x30, 0x30, 0x7E, 0x00, // `
101 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, // a
102 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00, // b
103 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00, // c
104 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, // d
105 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, // e
106 0x1C, 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x00, // f
107 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C, // g
108 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, // h
109 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, // i
110 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x70, // j
111 0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00, // k
112 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // l
113 0x00, 0x00, 0x36, 0x7F, 0x6B, 0x6B, 0x63, 0x00, // m
114 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, // n
115 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // o
116 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, // p
117 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x07, // q
118 0x00, 0x00, 0x6C, 0x76, 0x60, 0x60, 0x60, 0x00, // r
119 0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00, // s
120 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x1C, 0x00, // t
121 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, // u
122 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // v
123 0x00, 0x00, 0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, // w
124 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // x
125 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x3C, // y
126 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, // z
127 0x0C, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0C, 0x00, // {
128 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // |
129 0x30, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x30, 0x00, // }
130 0x31, 0x6B, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, // ~
131 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //
134 const char smallc[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-+&()/[];%";
135 unsigned char const small[] = {
137 0x1F, 0x11, 0x1F, //0
138 0x11, 0x1F, 0x10, //1
139 0x1D, 0x15, 0x17, //2
140 0x11, 0x15, 0x1F, //3
141 0x07, 0x04, 0x1F, //4
142 0x17, 0x15, 0x1D, //5
143 0x1F, 0x15, 0x1D, //6
144 0x01, 0x01, 0x1F, //7
145 0x1F, 0x15, 0x1F, //8
146 0x17, 0x15, 0x1F, //9
147 0x1E, 0x05, 0x1E, //A
148 0x1F, 0x15, 0x0A, //B
149 0x0E, 0x11, 0x11, //C
150 0x1F, 0x11, 0x0E, //D
151 0x1F, 0x15, 0x11, //E
152 0x1F, 0x05, 0x01, //F
153 0x0E, 0x11, 0x19, //G
154 0x1F, 0x04, 0x1F, //H
155 0x11, 0x1F, 0x11, //I
156 0x11, 0x0F, 0x01, //J
157 0x1F, 0x04, 0x1B, //K
158 0x1F, 0x10, 0x10, //L
159 0x1F, 0x03, 0x1F, //M
160 0x1F, 0x01, 0x1F, //N
161 0x0E, 0x11, 0x0E, //O
162 0x1F, 0x05, 0x02, //P
163 0x0E, 0x19, 0x1E, //Q
164 0x1F, 0x05, 0x1A, //R
165 0x12, 0x15, 0x09, //S
166 0x01, 0x1F, 0x01, //T
167 0x1F, 0x10, 0x1F, //U
168 0x0F, 0x10, 0x0F, //V
169 0x1F, 0x18, 0x1F, //W
170 0x1B, 0x04, 0x1B, //X
171 0x03, 0x1C, 0x03, //Y
172 0x19, 0x15, 0x13, //Z
173 0x04, 0x04, 0x04, //-
174 0x04, 0x0E, 0x04, //+
175 0x04, 0x0E, 0x04, //& (+)
176 0x00, 0x0E, 0x11, //(
177 0x11, 0x0E, 0x00, //)
178 0x08, 0x04, 0x02, ///
179 0x00, 0x1F, 0x11, //[
180 0x11, 0x1F, 0x00, //]
181 0x10, 0x0A, 0x00, //;
182 0x09, 0x04, 0x12, //%
185 Image *ImageNew(int w, int h, int c)
186 { // create a new blank image
190 i = malloc(sizeof(*i));
193 memset(i, 0, sizeof(*i));
198 i->Image = malloc((w + 1) * h);
203 memset(i->Image, 0, (w + 1) * h);
205 i->Colour = malloc(sizeof(Colour) * c);
211 memset(i->Colour, 0, sizeof(Colour) * c);
216 void ImageFree(Image * i)
228 typedef short LZW[256];
229 typedef LZW LZWTree[MAXLZW];
230 typedef struct strPrivate {
231 int cols; // number of colours, power of 2
232 unsigned char colbits; // number of bits for colours
233 int fh; // file handle
234 int lzwnext; // next code
235 int lzwlast; // last code in current bit size
236 int lzwbits; // current bit size
237 LZWTree lzw; // encode tree
238 unsigned char block[256]; // block so far, with count at start
239 int blockv; // pending value
240 int blockb; // bits used in pending value
241 short lzwcode; // which code we are on now
244 static LZWFlush(Private * p)
245 { // flush this block
246 write(p->fh, p->block, *p->block + 1);
250 static LZWOut(Private * p, short v)
252 p->blockv |= (v << p->blockb);
253 p->blockb += p->lzwbits;
254 while (p->blockb >= 8) {
255 p->block[++*p->block] = p->blockv; // last partial byte
258 if (*p->block == 255)
263 static LZWClear(Private * p)
266 p->lzwbits = p->colbits + 1;
267 p->lzwnext = p->cols + 2;
268 p->lzwlast = (1 << p->lzwbits) - 1;
269 p->lzwcode = p->cols; // starting point
270 for (c = 0; c < p->cols; c++) {
271 p->lzw[p->cols][c] = c; // links to literal entries
272 // links from literals, dead ends initially
273 memset(&p->lzw[c], -1, p->cols * 2);
277 static ImageStart(Private * p)
279 unsigned char b = p->colbits;
285 LZWOut(p, p->cols); // clear code
288 static ImageEnd(Private * p)
290 LZWOut(p, p->lzwcode); // last prefix
291 LZWOut(p, p->cols + 1); // end code
293 p->block[++*p->block] = p->blockv; // last partial byte
297 static ImageOut(Private * p, unsigned char c)
299 short next = p->lzw[p->lzwcode][c];
300 if (next == -1) { // dead end
301 LZWOut(p, p->lzwcode); // prefix
303 if (p->lzwnext + 1 == MAXLZW) {
304 LZWOut(p, p->cols); // clear code
308 if (p->lzwnext < MAXLZW) {
309 memset(p->lzw[p->lzwnext], -1, p->cols * 2); // init dead ends
310 p->lzw[p->lzwcode][c] = p->lzwnext;
311 if (p->lzwnext > p->lzwlast) { // bigger code
313 p->lzwlast = (1 << p->lzwbits) - 1;
319 p->lzwcode = next; // not a dead end
323 void ImageWriteGif(Image * i, int fh, int back, int trans, char *comment)
327 // count colours, min 4
328 for (p.colbits = 2, p.cols = 4; p.cols < i->C;
329 p.cols *= 2, p.colbits++) ;
333 strcpy(buf, "GIF87a");
335 if (comment || trans >= 0)
337 buf[4] = '9'; // needs gif89 format
339 buf[n++] = (i->W & 255);
340 buf[n++] = (i->W >> 8);
341 buf[n++] = (i->H & 255);
342 buf[n++] = (i->H >> 8);
343 buf[n++] = (i->Colour ? 0x80 : 0) + 0x70 + (p.colbits - 1);
344 buf[n++] = back; // background
345 buf[n++] = 0; // aspect
348 for (c = 0; c < p.cols; c++) {
350 buf[n++] = (i->Colour[c] >> 16 & 255);
351 buf[n++] = (i->Colour[c] >> 8 & 255);
352 buf[n++] = (i->Colour[c] & 255);
353 } else { // extra, unused, colour
361 if (comment && strlen(comment) < 256) { // comment
362 buf[n++] = 0x21; //extension
363 buf[n++] = 0xFE; //comment
364 buf[n++] = strlen(comment);
365 strcpy(buf + n, comment);
367 buf[n++] = 0; // end of block
369 if (trans >= 0) { // transparrent
370 buf[n++] = 0x21; // extension
371 buf[n++] = 0xF9; // graphic control
373 buf[n++] = 1; // transparrent
374 buf[n++] = 0; // delay
377 buf[n++] = 0; // terminator
381 buf[n++] = 0; // offset X
383 buf[n++] = 0; // offset Y
385 buf[n++] = (i->W & 255);
386 buf[n++] = (i->W >> 8);
387 buf[n++] = (i->H & 255);
388 buf[n++] = (i->H >> 8);
390 buf[n++] = 0x40; // interlaced, no local colour table
392 buf[n++] = 0x00; // non interlaced, no local colour table
402 for (y = 0; y < i->H; y += 8)
403 for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
405 for (y = 4; y < i->H; y += 8)
406 for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
408 for (y = 2; y < i->H; y += 4)
409 for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
411 for (y = 1; y < i->H; y += 2)
412 for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
415 for (y = 0; y < i->H; y++)
416 for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
421 write(fh, "\0", 1); // end of image data
422 write(fh, "\x3B", 1); // trailer
425 void ImageText(Image * i, int x, int y, int col, char *text)
429 if (*text >= ' ' && *text) {
431 unsigned const char *b =
432 bbc + (*text - ' ') * 8;
433 for (r = 0; r < 8; r++) {
434 unsigned char v = *b++;
436 &ImagePixel(i, x, y + r);
438 for (m = 0x80; m; m >>= 1, p++)
448 void ImageSmall(Image * i, int x, int y, int col, char *text)
449 { // writes 4x6 digits
452 char *p = strchr(smallc, toupper(*text));
456 unsigned const char *b =
457 small + (p - smallc) * 3;
458 for (r = 0; r < 5; r++) {
460 for (c = 0; c < 3; c++)
467 } else if (*text == '.') {
468 ImagePixel(i, x, y + 4) = col;
470 } else if (*text == ':') {
471 ImagePixel(i, x, y + 1) = col;
472 ImagePixel(i, x, y + 3) = col;
479 void ImageRect(Image * i, int x, int y, int w, int h, int c)
483 unsigned char *p = &ImagePixel(i, x, y);
494 /* Table of CRCs of all 8-bit messages. */
495 static unsigned int crc_table[256];
497 /* Make the table for a fast CRC. */
498 void make_crc_table(void)
502 for (n = 0; n < 256; n++) {
504 for (k = 0; k < 8; k++) {
506 c = 0xedb88320L ^ (c >> 1);
514 /* Update a running CRC with the bytes buf[0..len-1]--the CRC
515 should be initialized to all 1's, and the transmitted value
516 is the 1's complement of the final running CRC (see the
517 crc() routine below)). */
519 unsigned int update_crc(unsigned int crc, unsigned char *buf, int len)
521 unsigned int c = crc;
524 for (n = 0; n < len; n++)
525 c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
530 /* Return the CRC of the bytes buf[0..len-1]. */
531 unsigned int crc(unsigned char *buf, int len)
533 return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
536 unsigned int writecrc(int fh, char *ptr, int len, unsigned int c)
540 c = crc_table[(c ^ *ptr++) & 0xff] ^ (c >> 8);
544 void writechunk(int fh, char *typ, void *ptr, int len)
546 unsigned int v = htonl(len), crc;
548 crc = writecrc(fh, typ, 4, ~0);
550 crc = writecrc(fh, ptr, len, crc);
556 unsigned int adlersum(unsigned char *p, int l, unsigned int adler)
558 unsigned int s1 = (adler & 65535), s2 = (adler >> 16);
563 s1 %= 65521; // can be delayed due to sensible "l" values...
565 return (s2 << 16) + s1;
570 void ImageWritePNG(Image * i, int fh, int back, int trans, char *comment)
573 write(fh, "\211PNG\r\n\032\n", 8); // PNG header
579 unsigned char colour;
580 unsigned char compress;
581 unsigned char filter;
582 unsigned char interlace;
585 ihdr.width = htonl(i->W);
586 ihdr.height = htonl(i->H);
587 writechunk(fh, "IHDR", &ihdr, 13);
590 unsigned int v = htonl(i->C * 3), crc, n;
592 crc = writecrc(fh, "PLTE", 4, ~0);
593 for (n = 0; n < i->C; n++) {
594 v = htonl(i->Colour[n] << 8);
595 crc = writecrc(fh, (void *)&v, 3, crc);
600 if (back >= 0) { // bKGD
601 unsigned char b = back;
602 writechunk(fh, "bKGD", &b, 1);
604 if (*comment) { // tEXt
605 char c[] = "Comment";
606 unsigned int v = htonl(strlen(c) + strlen(comment) + 1), crc;
608 crc = writecrc(fh, "tEXt", 4, ~0);
609 crc = writecrc(fh, c, strlen(c) + 1, crc);
610 crc = writecrc(fh, comment, strlen(comment), crc);
615 unsigned char alpha[256];
617 for (n = 0; n < i->C; n++)
618 // 4th palette byte treated as 0=opaque, 255-transparren
619 alpha[n] = 255 - (i->Colour[n] >> 24);
620 if (trans >= 0 && trans < i->C)
621 // manual set of specific transparrent colour
623 writechunk(fh, "tRNS", alpha, i->C);
627 unsigned int v = htonl(i->H * (i->L + 5) + 6),
629 unsigned char *p = i->Image;
631 crc = writecrc(fh, "IDAT", 4, ~0);
632 crc = writecrc(fh, "\170\001", 2, crc); // zlib header for deflate
636 h[0] = (n ? 0 : 1); // last chunk in deflate, un compressed
637 h[1] = (i->L & 255); // Len, LSB first as per deflate spec
639 h[3] = ~(i->L & 255); // Inverse of Len
640 h[4] = ~(i->L / 256);
641 *p = 0; // filter 0 (NONE)
642 crc = writecrc(fh, h, 5, crc);
643 crc = writecrc(fh, p, i->L, crc);
644 adler = adlersum(p, i->L, adler);
648 crc = writecrc(fh, (void *)&v, 4, crc);
656 for (n = 0; n < i->H; n++)
657 i->Image[n * i->L] = 0; // filter 0
658 n = i->H * i->L * 1001 / 1000 + 12;
660 if (compress2(temp, &n, i->Image, i->L * i->H, 9) != Z_OK)
661 fprintf(stderr, "Deflate error\n");
663 writechunk(fh, "IDAT", temp, n);
667 writechunk(fh, "IEND", 0, 0); // IEND