+/**
+ *
+ * Image handling tools, (c) AJK 2001-2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+#include <ctype.h>
+#include "image.h"
+
+#define INTERLACE
+#define CLEAR
+#define USEZLIB
+
+#ifdef USEZLIB
+#include <zlib.h>
+#endif
+
+unsigned char const bbc[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // !
+ 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, // "
+ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00, // #
+ 0x0C, 0x3F, 0x68, 0x3E, 0x0B, 0x7E, 0x18, 0x00, // $
+ 0x60, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x06, 0x00, // %
+ 0x38, 0x6C, 0x6C, 0x38, 0x6D, 0x66, 0x3B, 0x00, // &
+ 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, // '
+ 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, // (
+ 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, // )
+ 0x00, 0x18, 0x7E, 0x3C, 0x7E, 0x18, 0x00, 0x00, // *
+ 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // +
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, // ,
+ 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, // -
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // .
+ 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, // /
+ 0x18, 0x24, 0x66, 0x66, 0x66, 0x24, 0x18, 0x00, // 0 (non crossed)
+ 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, // 1
+ 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x7E, 0x00, // 2
+ 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00, // 3
+ 0x0C, 0x1C, 0x3C, 0x6C, 0x7E, 0x0C, 0x0C, 0x00, // 4
+ 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00, // 5
+ 0x1C, 0x30, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, // 6
+ 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, // 7
+ 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // 8
+ 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00, // 9
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, // :
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, // ;
+ 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, // <
+ 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, // =
+ 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00, // >
+ 0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00, // ?
+ 0x3C, 0x66, 0x6E, 0x6A, 0x6E, 0x60, 0x3C, 0x00, // @
+ 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // A
+ 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, // B
+ 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, // C
+ 0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00, // D
+ 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00, // E
+ 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00, // F
+ 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00, // G
+ 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // H
+ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, // I
+ 0x3E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, // J
+ 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00, // K
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, // L
+ 0x63, 0x77, 0x7F, 0x6B, 0x6B, 0x63, 0x63, 0x00, // M
+ 0x66, 0x66, 0x76, 0x7E, 0x6E, 0x66, 0x66, 0x00, // N
+ 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // O
+ 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, // P
+ 0x3C, 0x66, 0x66, 0x66, 0x6A, 0x6C, 0x36, 0x00, // Q
+ 0x7C, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x00, // R
+ 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, // S
+ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // T
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // U
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // V
+ 0x63, 0x63, 0x6B, 0x6B, 0x7F, 0x77, 0x63, 0x00, // W
+ 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, // X
+ 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // Y
+ 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, // Z
+ 0x7C, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7C, 0x00, // [
+ 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, //
+ 0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, // ]
+ 0x18, 0x3C, 0x66, 0x42, 0x00, 0x00, 0x00, 0x00, // ^
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // _
+ 0x1C, 0x36, 0x30, 0x7C, 0x30, 0x30, 0x7E, 0x00, // `
+ 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, // a
+ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00, // b
+ 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00, // c
+ 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, // d
+ 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, // e
+ 0x1C, 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x00, // f
+ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C, // g
+ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, // h
+ 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, // i
+ 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x70, // j
+ 0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00, // k
+ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // l
+ 0x00, 0x00, 0x36, 0x7F, 0x6B, 0x6B, 0x63, 0x00, // m
+ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, // n
+ 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // o
+ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, // p
+ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x07, // q
+ 0x00, 0x00, 0x6C, 0x76, 0x60, 0x60, 0x60, 0x00, // r
+ 0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00, // s
+ 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x1C, 0x00, // t
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, // u
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // v
+ 0x00, 0x00, 0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, // w
+ 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // x
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x3C, // y
+ 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, // z
+ 0x0C, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0C, 0x00, // {
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // |
+ 0x30, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x30, 0x00, // }
+ 0x31, 0x6B, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, // ~
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //
+};
+
+const char smallc[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-+&()/[];%";
+unsigned char const small[] = {
+ 0x00, 0x00, 0x00, //
+ 0x1F, 0x11, 0x1F, //0
+ 0x11, 0x1F, 0x10, //1
+ 0x1D, 0x15, 0x17, //2
+ 0x11, 0x15, 0x1F, //3
+ 0x07, 0x04, 0x1F, //4
+ 0x17, 0x15, 0x1D, //5
+ 0x1F, 0x15, 0x1D, //6
+ 0x01, 0x01, 0x1F, //7
+ 0x1F, 0x15, 0x1F, //8
+ 0x17, 0x15, 0x1F, //9
+ 0x1E, 0x05, 0x1E, //A
+ 0x1F, 0x15, 0x0A, //B
+ 0x0E, 0x11, 0x11, //C
+ 0x1F, 0x11, 0x0E, //D
+ 0x1F, 0x15, 0x11, //E
+ 0x1F, 0x05, 0x01, //F
+ 0x0E, 0x11, 0x19, //G
+ 0x1F, 0x04, 0x1F, //H
+ 0x11, 0x1F, 0x11, //I
+ 0x11, 0x0F, 0x01, //J
+ 0x1F, 0x04, 0x1B, //K
+ 0x1F, 0x10, 0x10, //L
+ 0x1F, 0x03, 0x1F, //M
+ 0x1F, 0x01, 0x1F, //N
+ 0x0E, 0x11, 0x0E, //O
+ 0x1F, 0x05, 0x02, //P
+ 0x0E, 0x19, 0x1E, //Q
+ 0x1F, 0x05, 0x1A, //R
+ 0x12, 0x15, 0x09, //S
+ 0x01, 0x1F, 0x01, //T
+ 0x1F, 0x10, 0x1F, //U
+ 0x0F, 0x10, 0x0F, //V
+ 0x1F, 0x18, 0x1F, //W
+ 0x1B, 0x04, 0x1B, //X
+ 0x03, 0x1C, 0x03, //Y
+ 0x19, 0x15, 0x13, //Z
+ 0x04, 0x04, 0x04, //-
+ 0x04, 0x0E, 0x04, //+
+ 0x04, 0x0E, 0x04, //& (+)
+ 0x00, 0x0E, 0x11, //(
+ 0x11, 0x0E, 0x00, //)
+ 0x08, 0x04, 0x02, ///
+ 0x00, 0x1F, 0x11, //[
+ 0x11, 0x1F, 0x00, //]
+ 0x10, 0x0A, 0x00, //;
+ 0x09, 0x04, 0x12, //%
+};
+
+Image *
+ImageNew (int w, int h, int c)
+{ // create a new blank image
+ Image *i;
+ if (!w || !h)
+ return 0;
+ i = malloc (sizeof (*i));
+ if (!i)
+ return 0;
+ memset (i, 0, sizeof (*i));
+ i->W = w;
+ i->L = w + 1;
+ i->H = h;
+ i->C = c;
+ i->Image = malloc ((w + 1) * h);
+ if (!i->Image)
+ {
+ free (i);
+ return 0;
+ }
+ memset (i->Image, 0, (w + 1) * h);
+ if (c)
+ {
+ i->Colour = malloc (sizeof (Colour) * c);
+ if (!i->Colour)
+ {
+ free (i->Image);
+ free (i);
+ return 0;
+ }
+ memset (i->Colour, 0, sizeof (Colour) * c);
+ }
+ return i;
+}
+
+void
+ImageFree (Image * i)
+{ // free an image
+ if (i)
+ {
+ if (i->Image)
+ free (i->Image);
+ if (i->Colour)
+ free (i->Colour);
+ free (i);
+ }
+}
+
+#define MAXLZW 4096
+typedef short LZW[256];
+typedef LZW LZWTree[MAXLZW];
+typedef struct strPrivate
+{
+ int cols; // number of colours, power of 2
+ unsigned char colbits; // number of bits for colours
+ int fh; // file handle
+ int lzwnext; // next code
+ int lzwlast; // last code in current bit size
+ int lzwbits; // current bit size
+ LZWTree lzw; // encode tree
+ unsigned char block[256]; // block so far, with count at start
+ int blockv; // pending value
+ int blockb; // bits used in pending value
+ short lzwcode; // which code we are on now
+}
+Private;
+
+static
+LZWFlush (Private * p)
+{ // flush this block
+ write (p->fh, p->block, *p->block + 1);
+ *p->block = 0;
+}
+
+static
+LZWOut (Private * p, short v)
+{ // output a value
+ p->blockv |= (v << p->blockb);
+ p->blockb += p->lzwbits;
+ while (p->blockb >= 8)
+ {
+ p->block[++*p->block] = p->blockv; // last partial byte
+ p->blockv >>= 8;
+ p->blockb -= 8;
+ if (*p->block == 255)
+ LZWFlush (p);
+ }
+}
+
+static
+LZWClear (Private * p)
+{
+ int c;
+ p->lzwbits = p->colbits + 1;
+ p->lzwnext = p->cols + 2;
+ p->lzwlast = (1 << p->lzwbits) - 1;
+ p->lzwcode = p->cols; // starting point
+ for (c = 0; c < p->cols; c++)
+ {
+ p->lzw[p->cols][c] = c; // links to literal entries
+ memset (&p->lzw[c], -1, p->cols * 2); // links from literals, dead ends initially
+ }
+}
+
+static
+ImageStart (Private * p)
+{
+ unsigned char b = p->colbits;
+ write (p->fh, &b, 1);
+ *p->block = 0;
+ p->blockb = 0;
+ p->blockv = 0;
+ LZWClear (p);
+ LZWOut (p, p->cols); // clear code
+}
+
+static
+ImageEnd (Private * p)
+{
+ LZWOut (p, p->lzwcode); // last prefix
+ LZWOut (p, p->cols + 1); // end code
+ if (p->blockb)
+ p->block[++*p->block] = p->blockv; // last partial byte
+ LZWFlush (p);
+}
+
+static
+ImageOut (Private * p, unsigned char c)
+{
+ short next = p->lzw[p->lzwcode][c];
+ if (next == -1)
+ { // dead end
+ LZWOut (p, p->lzwcode); // prefix
+#ifdef CLEAR
+ if (p->lzwnext + 1 == MAXLZW)
+ {
+ LZWOut (p, p->cols); // clear code
+ LZWClear (p);
+ } else
+#endif
+ if (p->lzwnext < MAXLZW)
+ {
+ memset (p->lzw[p->lzwnext], -1, p->cols * 2); // init dead ends
+ p->lzw[p->lzwcode][c] = p->lzwnext;
+ if (p->lzwnext > p->lzwlast)
+ { // bigger code
+ p->lzwbits++;
+ p->lzwlast = (1 << p->lzwbits) - 1;
+ }
+ p->lzwnext++;
+ }
+ p->lzwcode = c;
+ } else
+ p->lzwcode = next; // not a dead end
+}
+
+// write GIF image
+void
+ImageWriteGif (Image * i, int fh, int back, int trans, char *comment)
+{
+ struct strPrivate p;
+ p.fh = fh;
+ for (p.colbits = 2, p.cols = 4; p.cols < i->C; p.cols *= 2, p.colbits++); // count colours, min 4
+ { // headers
+ char buf[1500];
+ int n = 0;
+ strcpy (buf, "GIF87a");
+#ifndef INTERLACE
+ if (comment || trans >= 0)
+#endif
+ buf[4] = '9'; // needs gif89 format
+ n = 6;
+ buf[n++] = (i->W & 255);
+ buf[n++] = (i->W >> 8);
+ buf[n++] = (i->H & 255);
+ buf[n++] = (i->H >> 8);
+ buf[n++] = (i->Colour ? 0x80 : 0) + 0x70 + (p.colbits - 1);
+ buf[n++] = back; // background
+ buf[n++] = 0; // aspect
+ if (i->Colour)
+ {
+ int c;
+ for (c = 0; c < p.cols; c++)
+ {
+ if (c < i->C)
+ {
+ buf[n++] = (i->Colour[c] >> 16 & 255);
+ buf[n++] = (i->Colour[c] >> 8 & 255);
+ buf[n++] = (i->Colour[c] & 255);
+ } else
+ { // extra, unused, colour
+ buf[n++] = 0;
+ buf[n++] = 0;
+ buf[n++] = 0;
+ }
+ }
+ }
+ // comment
+ if (comment && strlen (comment) < 256)
+ { // comment
+ buf[n++] = 0x21; //extension
+ buf[n++] = 0xFE; //comment
+ buf[n++] = strlen (comment);
+ strcpy (buf + n, comment);
+ n += buf[n - 1];
+ buf[n++] = 0; // end of block
+ }
+ if (trans >= 0)
+ { // transparrent
+ buf[n++] = 0x21; // extension
+ buf[n++] = 0xF9; // graphic control
+ buf[n++] = 4; // len
+ buf[n++] = 1; // transparrent
+ buf[n++] = 0; // delay
+ buf[n++] = 0;
+ buf[n++] = trans;
+ buf[n++] = 0; // terminator
+ }
+ // image
+ buf[n++] = 0x2C;
+ buf[n++] = 0; // offset X
+ buf[n++] = 0;
+ buf[n++] = 0; // offset Y
+ buf[n++] = 0;
+ buf[n++] = (i->W & 255);
+ buf[n++] = (i->W >> 8);
+ buf[n++] = (i->H & 255);
+ buf[n++] = (i->H >> 8);
+#ifdef INTERLACE
+ buf[n++] = 0x40; // interlaced, no local colour table
+#else
+ buf[n++] = 0x00; // non interlaced, no local colour table
+#endif
+ write (fh, buf, n);
+ }
+ // image data
+ {
+ unsigned char *b;
+ int x,
+ y;
+ ImageStart (&p);
+#ifdef INTERLACE
+ for (y = 0; y < i->H; y += 8)
+ for (b = &ImagePixel (i, 0, y), x = 0; x < i->W; x++)
+ ImageOut (&p, *b++);
+ for (y = 4; y < i->H; y += 8)
+ for (b = &ImagePixel (i, 0, y), x = 0; x < i->W; x++)
+ ImageOut (&p, *b++);
+ for (y = 2; y < i->H; y += 4)
+ for (b = &ImagePixel (i, 0, y), x = 0; x < i->W; x++)
+ ImageOut (&p, *b++);
+ for (y = 1; y < i->H; y += 2)
+ for (b = &ImagePixel (i, 0, y), x = 0; x < i->W; x++)
+ ImageOut (&p, *b++);
+#else
+ for (y = 0; y < i->H; y++)
+ for (b = &ImagePixel (i, 0, y), x = 0; x < i->W; x++)
+ ImageOut (&p, *b++);
+#endif
+ ImageEnd (&p);
+ }
+ write (fh, "\0", 1); // end of image data
+ write (fh, "\x3B", 1); // trailer
+}
+
+void
+ImageText (Image * i, int x, int y, int col, char *text)
+{ // writes 8x8 text
+ if (i && text)
+ while (*text)
+ {
+ if (*text >= ' ' && *text)
+ {
+ int r;
+ unsigned const char *b = bbc + (*text - ' ') * 8;
+ for (r = 0; r < 8; r++)
+ {
+ unsigned char v = *b++;
+ unsigned char *p = &ImagePixel (i, x, y + r);
+ unsigned char m;
+ for (m = 0x80; m; m >>= 1, p++)
+ if (v & m)
+ *p = col;
+ }
+ }
+ x += 8;
+ text++;
+ }
+}
+
+void
+ImageSmall (Image * i, int x, int y, int col, char *text)
+{ // writes 4x6 digits
+ if (i && text)
+ while (*text)
+ {
+ char *p = strchr (smallc, toupper (*text));
+ if (p)
+ {
+ int r;
+ char m = 1;
+ unsigned const char *b = small + (p - smallc) * 3;
+ for (r = 0; r < 5; r++)
+ {
+ int c;
+ for (c = 0; c < 3; c++)
+ if (b[c] & m)
+ ImagePixel (i, x + c, y + r) = col;
+ m <<= 1;
+ }
+ x += 4;
+ } else if (*text == '.')
+ {
+ ImagePixel (i, x, y + 4) = col;
+ x += 2;
+ } else if (*text == ':')
+ {
+ ImagePixel (i, x, y + 1) = col;
+ ImagePixel (i, x, y + 3) = col;
+ x += 2;
+ }
+ text++;
+ }
+}
+
+void
+ImageRect (Image * i, int x, int y, int w, int h, int c)
+{ // fill a box
+ if (i && w && h)
+ {
+ while (h--)
+ {
+ unsigned char *p = &ImagePixel (i, x, y);
+ int n = w;
+ while (n--)
+ *p++ = c;
+ y++;
+ }
+ }
+}
+
+// PNG code
+
+ /* Table of CRCs of all 8-bit messages. */
+static unsigned int crc_table[256];
+
+ /* Make the table for a fast CRC. */
+void
+make_crc_table (void)
+{
+ unsigned int c;
+ int n,
+ k;
+ for (n = 0; n < 256; n++)
+ {
+ c = (unsigned int) n;
+ for (k = 0; k < 8; k++)
+ {
+ if (c & 1)
+ c = 0xedb88320L ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ crc_table[n] = c;
+ }
+}
+
+ /* Update a running CRC with the bytes buf[0..len-1]--the CRC
+ should be initialized to all 1's, and the transmitted value
+ is the 1's complement of the final running CRC (see the
+ crc() routine below)). */
+
+unsigned int
+update_crc (unsigned int crc, unsigned char *buf, int len)
+{
+ unsigned int c = crc;
+ int n;
+
+ for (n = 0; n < len; n++)
+ c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
+
+ return c;
+}
+
+ /* Return the CRC of the bytes buf[0..len-1]. */
+unsigned int
+crc (unsigned char *buf, int len)
+{
+ return update_crc (0xffffffffL, buf, len) ^ 0xffffffffL;
+}
+
+unsigned int
+writecrc (int fh, char *ptr, int len, unsigned int c)
+{
+ write (fh, ptr, len);
+ while (len--)
+ c = crc_table[(c ^ *ptr++) & 0xff] ^ (c >> 8);
+ return c;
+}
+
+void
+writechunk (int fh, char *typ, void *ptr, int len)
+{
+ unsigned int v = htonl (len),
+ crc;
+ write (fh, &v, 4);
+ crc = writecrc (fh, typ, 4, ~0);
+ if (len)
+ crc = writecrc (fh, ptr, len, crc);
+ v = htonl (~crc);
+ write (fh, &v, 4);
+}
+
+#ifndef USEZLIB
+unsigned int
+adlersum (unsigned char *p, int l, unsigned int adler)
+{
+ unsigned int s1 = (adler & 65535),
+ s2 = (adler >> 16);
+ while (l--)
+ {
+ s1 += *p++;
+ s2 += s1;
+ }
+ s1 %= 65521; // can be delayed due to sensible "l" values...
+ s2 %= 65521;
+ return (s2 << 16) + s1;
+}
+#endif
+
+// write PNG image
+void
+ImageWritePNG (Image * i, int fh, int back, int trans, char *comment)
+{
+ make_crc_table ();
+ write (fh, "\211PNG\r\n\032\n", 8); // PNG header
+ { // IHDR
+ struct
+ {
+ unsigned int width;
+ unsigned int height;
+ unsigned char depth;
+ unsigned char colour;
+ unsigned char compress;
+ unsigned char filter;
+ unsigned char interlace;
+ }
+ ihdr =
+ {
+ 0, 0, 8, 3, 0, 0};
+ ihdr.width = htonl (i->W);
+ ihdr.height = htonl (i->H);
+ writechunk (fh, "IHDR", &ihdr, 13);
+ }
+ { // PLTE
+ unsigned int v = htonl (i->C * 3),
+ crc,
+ n;
+ write (fh, &v, 4);
+ crc = writecrc (fh, "PLTE", 4, ~0);
+ for (n = 0; n < i->C; n++)
+ {
+ v = htonl (i->Colour[n] << 8);
+ crc = writecrc (fh, (void *) &v, 3, crc);
+ }
+ v = htonl (~crc);
+ write (fh, &v, 4);
+ }
+ if (back >= 0)
+ { // bKGD
+ unsigned char b = back;
+ writechunk (fh, "bKGD", &b, 1);
+ }
+ if (*comment)
+ { // tEXt
+ char c[] = "Comment";
+ unsigned int v = htonl (strlen (c) + strlen (comment) + 1),
+ crc;
+ write (fh, &v, 4);
+ crc = writecrc (fh, "tEXt", 4, ~0);
+ crc = writecrc (fh, c, strlen (c) + 1, crc);
+ crc = writecrc (fh, comment, strlen (comment), crc);
+ v = htonl (~crc);
+ write (fh, &v, 4);
+ }
+ { // tRNS
+ unsigned char alpha[256];
+ int n;
+ for (n = 0; n < i->C; n++)
+ alpha[n] = 255 - (i->Colour[n] >> 24); // 4th palette byte treated as 0=opaque, 255-transparrent
+ if (trans >= 0 && trans < i->C)
+ alpha[trans] = 0; // manual set of specific transparrent colour
+ writechunk (fh, "tRNS", alpha, i->C);
+ }
+#ifndef USEZLIB
+ { // IDAT
+ unsigned int v = htonl (i->H * (i->L + 5) + 6),
+ crc,
+ adler = 1,
+ n;
+ unsigned char *p = i->Image;
+ write (fh, &v, 4);
+ crc = writecrc (fh, "IDAT", 4, ~0);
+ crc = writecrc (fh, "\170\001", 2, crc); // zlib header for deflate
+ n = i->H;
+ while (n--)
+ {
+ unsigned char h[5];
+ h[0] = (n ? 0 : 1); // last chunk in deflate, un compressed
+ h[1] = (i->L & 255); // Len, LSB first as per deflate spec
+ h[2] = (i->L / 256);
+ h[3] = ~(i->L & 255); // Inverse of Len
+ h[4] = ~(i->L / 256);
+ *p = 0; // filter 0 (NONE)
+ crc = writecrc (fh, h, 5, crc);
+ crc = writecrc (fh, p, i->L, crc);
+ adler = adlersum (p, i->L, adler);
+ p += i->L;
+ }
+ v = htonl (adler);
+ crc = writecrc (fh, (void *) &v, 4, crc);
+ v = htonl (~crc);
+ write (fh, &v, 4);
+ }
+#else
+ { // IDAT
+ unsigned char *temp;
+ unsigned long n;
+ for (n = 0; n < i->H; n++)
+ i->Image[n * i->L] = 0; // filter 0
+ n = i->H * i->L * 1001 / 1000 + 12;
+ temp = malloc (n);
+ if (compress2 (temp, &n, i->Image, i->L * i->H, 9) != Z_OK)
+ fprintf (stderr, "Deflate error\n");
+ else
+ writechunk (fh, "IDAT", temp, n);
+ free (temp);
+ }
+#endif
+ writechunk (fh, "IEND", 0, 0); // IEND
+}