]> git.sur5r.net Git - iec16022/blob - image.c
Import Debian changes 0.2.4-1.2
[iec16022] / image.c
1 /**
2  *
3  * Image handling tools, (c) AJK 2001-2005
4  *
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.
9  *
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.
14  *
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
18  *
19  */
20
21 #include <stdio.h>
22 #include <malloc.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include "image.h"
26
27 #define INTERLACE
28 #define CLEAR
29 #define USEZLIB
30
31 #ifdef USEZLIB
32 #include <zlib.h>
33 #endif
34
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, //
132 };
133
134 const char smallc[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-+&()/[];%";
135 unsigned char const small[] = {
136         0x00, 0x00, 0x00,       //
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,       //%
183 };
184
185 Image *ImageNew(int w, int h, int c)
186 {                               // create a new blank image
187         Image *i;
188         if (!w || !h)
189                 return 0;
190         i = malloc(sizeof(*i));
191         if (!i)
192                 return 0;
193         memset(i, 0, sizeof(*i));
194         i->W = w;
195         i->L = w + 1;
196         i->H = h;
197         i->C = c;
198         i->Image = malloc((w + 1) * h);
199         if (!i->Image) {
200                 free(i);
201                 return 0;
202         }
203         memset(i->Image, 0, (w + 1) * h);
204         if (c) {
205                 i->Colour = malloc(sizeof(Colour) * c);
206                 if (!i->Colour) {
207                         free(i->Image);
208                         free(i);
209                         return 0;
210                 }
211                 memset(i->Colour, 0, sizeof(Colour) * c);
212         }
213         return i;
214 }
215
216 void ImageFree(Image * i)
217 {                               // free an image
218         if (i) {
219                 if (i->Image)
220                         free(i->Image);
221                 if (i->Colour)
222                         free(i->Colour);
223                 free(i);
224         }
225 }
226
227 #define MAXLZW  4096
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
242 } Private;
243
244 static LZWFlush(Private * p)
245 {                               // flush this block
246         write(p->fh, p->block, *p->block + 1);
247         *p->block = 0;
248 }
249
250 static LZWOut(Private * p, short v)
251 {                               // output a value
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
256                 p->blockv >>= 8;
257                 p->blockb -= 8;
258                 if (*p->block == 255)
259                         LZWFlush(p);
260         }
261 }
262
263 static LZWClear(Private * p)
264 {
265         int c;
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);
274         }
275 }
276
277 static ImageStart(Private * p)
278 {
279         unsigned char b = p->colbits;
280         write(p->fh, &b, 1);
281         *p->block = 0;
282         p->blockb = 0;
283         p->blockv = 0;
284         LZWClear(p);
285         LZWOut(p, p->cols);     // clear code
286 }
287
288 static ImageEnd(Private * p)
289 {
290         LZWOut(p, p->lzwcode);  // last prefix
291         LZWOut(p, p->cols + 1); // end code
292         if (p->blockb)
293                 p->block[++*p->block] = p->blockv;      // last partial byte
294         LZWFlush(p);
295 }
296
297 static ImageOut(Private * p, unsigned char c)
298 {
299         short next = p->lzw[p->lzwcode][c];
300         if (next == -1) {       // dead end
301                 LZWOut(p, p->lzwcode);  // prefix
302 #ifdef CLEAR
303                 if (p->lzwnext + 1 == MAXLZW) {
304                         LZWOut(p, p->cols);     // clear code
305                         LZWClear(p);
306                 } else
307 #endif
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
312                                 p->lzwbits++;
313                                 p->lzwlast = (1 << p->lzwbits) - 1;
314                         }
315                         p->lzwnext++;
316                 }
317                 p->lzwcode = c;
318         } else
319                 p->lzwcode = next;      // not a dead end
320 }
321
322 // write GIF image
323 void ImageWriteGif(Image * i, int fh, int back, int trans, char *comment)
324 {
325         struct strPrivate p;
326         p.fh = fh;
327         // count colours, min 4
328         for (p.colbits = 2, p.cols = 4; p.cols < i->C;
329              p.cols *= 2, p.colbits++) ;
330         {                       // headers
331                 char buf[1500];
332                 int n = 0;
333                 strcpy(buf, "GIF87a");
334 #ifndef INTERLACE
335                 if (comment || trans >= 0)
336 #endif
337                         buf[4] = '9';   // needs gif89 format
338                 n = 6;
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
346                 if (i->Colour) {
347                         int c;
348                         for (c = 0; c < p.cols; c++) {
349                                 if (c < i->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
354                                         buf[n++] = 0;
355                                         buf[n++] = 0;
356                                         buf[n++] = 0;
357                                 }
358                         }
359                 }
360                 // comment
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);
366                         n += buf[n - 1];
367                         buf[n++] = 0;   // end of block
368                 }
369                 if (trans >= 0) {       // transparrent
370                         buf[n++] = 0x21;        // extension
371                         buf[n++] = 0xF9;        // graphic control
372                         buf[n++] = 4;   // len
373                         buf[n++] = 1;   // transparrent
374                         buf[n++] = 0;   // delay
375                         buf[n++] = 0;
376                         buf[n++] = trans;
377                         buf[n++] = 0;   // terminator
378                 }
379                 // image
380                 buf[n++] = 0x2C;
381                 buf[n++] = 0;   // offset X
382                 buf[n++] = 0;
383                 buf[n++] = 0;   // offset Y
384                 buf[n++] = 0;
385                 buf[n++] = (i->W & 255);
386                 buf[n++] = (i->W >> 8);
387                 buf[n++] = (i->H & 255);
388                 buf[n++] = (i->H >> 8);
389 #ifdef INTERLACE
390                 buf[n++] = 0x40;        // interlaced, no local colour table
391 #else
392                 buf[n++] = 0x00;        // non interlaced, no local colour table
393 #endif
394                 write(fh, buf, n);
395         }
396         // image data
397         {
398                 unsigned char *b;
399                 int x, y;
400                 ImageStart(&p);
401 #ifdef INTERLACE
402                 for (y = 0; y < i->H; y += 8)
403                         for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
404                                 ImageOut(&p, *b++);
405                 for (y = 4; y < i->H; y += 8)
406                         for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
407                                 ImageOut(&p, *b++);
408                 for (y = 2; y < i->H; y += 4)
409                         for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
410                                 ImageOut(&p, *b++);
411                 for (y = 1; y < i->H; y += 2)
412                         for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
413                                 ImageOut(&p, *b++);
414 #else
415                 for (y = 0; y < i->H; y++)
416                         for (b = &ImagePixel(i, 0, y), x = 0; x < i->W; x++)
417                                 ImageOut(&p, *b++);
418 #endif
419                 ImageEnd(&p);
420         }
421         write(fh, "\0", 1);     // end of image data
422         write(fh, "\x3B", 1);   // trailer
423 }
424
425 void ImageText(Image * i, int x, int y, int col, char *text)
426 {                               // writes 8x8 text
427         if (i && text)
428                 while (*text) {
429                         if (*text >= ' ' && *text) {
430                                 int r;
431                                 unsigned const char *b =
432                                     bbc + (*text - ' ') * 8;
433                                 for (r = 0; r < 8; r++) {
434                                         unsigned char v = *b++;
435                                         unsigned char *p =
436                                             &ImagePixel(i, x, y + r);
437                                         unsigned char m;
438                                         for (m = 0x80; m; m >>= 1, p++)
439                                                 if (v & m)
440                                                         *p = col;
441                                 }
442                         }
443                         x += 8;
444                         text++;
445                 }
446 }
447
448 void ImageSmall(Image * i, int x, int y, int col, char *text)
449 {                               // writes 4x6 digits
450         if (i && text)
451                 while (*text) {
452                         char *p = strchr(smallc, toupper(*text));
453                         if (p) {
454                                 int r;
455                                 char m = 1;
456                                 unsigned const char *b =
457                                     small + (p - smallc) * 3;
458                                 for (r = 0; r < 5; r++) {
459                                         int c;
460                                         for (c = 0; c < 3; c++)
461                                                 if (b[c] & m)
462                                                         ImagePixel(i, x + c,
463                                                                    y + r) = col;
464                                         m <<= 1;
465                                 }
466                                 x += 4;
467                         } else if (*text == '.') {
468                                 ImagePixel(i, x, y + 4) = col;
469                                 x += 2;
470                         } else if (*text == ':') {
471                                 ImagePixel(i, x, y + 1) = col;
472                                 ImagePixel(i, x, y + 3) = col;
473                                 x += 2;
474                         }
475                         text++;
476                 }
477 }
478
479 void ImageRect(Image * i, int x, int y, int w, int h, int c)
480 {                               // fill a box
481         if (i && w && h) {
482                 while (h--) {
483                         unsigned char *p = &ImagePixel(i, x, y);
484                         int n = w;
485                         while (n--)
486                                 *p++ = c;
487                         y++;
488                 }
489         }
490 }
491
492 // PNG code
493
494       /* Table of CRCs of all 8-bit messages. */
495 static unsigned int crc_table[256];
496
497       /* Make the table for a fast CRC. */
498 void make_crc_table(void)
499 {
500         unsigned int c;
501         int n, k;
502         for (n = 0; n < 256; n++) {
503                 c = (unsigned int)n;
504                 for (k = 0; k < 8; k++) {
505                         if (c & 1)
506                                 c = 0xedb88320L ^ (c >> 1);
507                         else
508                                 c = c >> 1;
509                 }
510                 crc_table[n] = c;
511         }
512 }
513
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)). */
518
519 unsigned int update_crc(unsigned int crc, unsigned char *buf, int len)
520 {
521         unsigned int c = crc;
522         int n;
523
524         for (n = 0; n < len; n++)
525                 c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
526
527         return c;
528 }
529
530       /* Return the CRC of the bytes buf[0..len-1]. */
531 unsigned int crc(unsigned char *buf, int len)
532 {
533         return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
534 }
535
536 unsigned int writecrc(int fh, char *ptr, int len, unsigned int c)
537 {
538         write(fh, ptr, len);
539         while (len--)
540                 c = crc_table[(c ^ *ptr++) & 0xff] ^ (c >> 8);
541         return c;
542 }
543
544 void writechunk(int fh, char *typ, void *ptr, int len)
545 {
546         unsigned int v = htonl(len), crc;
547         write(fh, &v, 4);
548         crc = writecrc(fh, typ, 4, ~0);
549         if (len)
550                 crc = writecrc(fh, ptr, len, crc);
551         v = htonl(~crc);
552         write(fh, &v, 4);
553 }
554
555 #ifndef USEZLIB
556 unsigned int adlersum(unsigned char *p, int l, unsigned int adler)
557 {
558         unsigned int s1 = (adler & 65535), s2 = (adler >> 16);
559         while (l--) {
560                 s1 += *p++;
561                 s2 += s1;
562         }
563         s1 %= 65521;            // can be delayed due to sensible "l" values...
564         s2 %= 65521;
565         return (s2 << 16) + s1;
566 }
567 #endif
568
569 // write PNG image
570 void ImageWritePNG(Image * i, int fh, int back, int trans, char *comment)
571 {
572         make_crc_table();
573         write(fh, "\211PNG\r\n\032\n", 8);      // PNG header
574         {                       // IHDR
575                 struct {
576                         unsigned int width;
577                         unsigned int height;
578                         unsigned char depth;
579                         unsigned char colour;
580                         unsigned char compress;
581                         unsigned char filter;
582                         unsigned char interlace;
583                 } ihdr = {
584                 0, 0, 8, 3, 0, 0};
585                 ihdr.width = htonl(i->W);
586                 ihdr.height = htonl(i->H);
587                 writechunk(fh, "IHDR", &ihdr, 13);
588         }
589         {                       // PLTE
590                 unsigned int v = htonl(i->C * 3), crc, n;
591                 write(fh, &v, 4);
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);
596                 }
597                 v = htonl(~crc);
598                 write(fh, &v, 4);
599         }
600         if (back >= 0) {        // bKGD
601                 unsigned char b = back;
602                 writechunk(fh, "bKGD", &b, 1);
603         }
604         if (*comment) {         // tEXt
605                 char c[] = "Comment";
606                 unsigned int v = htonl(strlen(c) + strlen(comment) + 1), crc;
607                 write(fh, &v, 4);
608                 crc = writecrc(fh, "tEXt", 4, ~0);
609                 crc = writecrc(fh, c, strlen(c) + 1, crc);
610                 crc = writecrc(fh, comment, strlen(comment), crc);
611                 v = htonl(~crc);
612                 write(fh, &v, 4);
613         }
614         {                       // tRNS
615                 unsigned char alpha[256];
616                 int n;
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
622                         alpha[trans] = 0;
623                 writechunk(fh, "tRNS", alpha, i->C);
624         }
625 #ifndef USEZLIB
626         {                       // IDAT
627                 unsigned int v = htonl(i->H * (i->L + 5) + 6),
628                     crc, adler = 1, n;
629                 unsigned char *p = i->Image;
630                 write(fh, &v, 4);
631                 crc = writecrc(fh, "IDAT", 4, ~0);
632                 crc = writecrc(fh, "\170\001", 2, crc); // zlib header for deflate
633                 n = i->H;
634                 while (n--) {
635                         unsigned char h[5];
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
638                         h[2] = (i->L / 256);
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);
645                         p += i->L;
646                 }
647                 v = htonl(adler);
648                 crc = writecrc(fh, (void *)&v, 4, crc);
649                 v = htonl(~crc);
650                 write(fh, &v, 4);
651         }
652 #else
653         {                       // IDAT
654                 unsigned char *temp;
655                 unsigned long n;
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;
659                 temp = malloc(n);
660                 if (compress2(temp, &n, i->Image, i->L * i->H, 9) != Z_OK)
661                         fprintf(stderr, "Deflate error\n");
662                 else
663                         writechunk(fh, "IDAT", temp, n);
664                 free(temp);
665         }
666 #endif
667         writechunk(fh, "IEND", 0, 0);   // IEND
668 }