URI:
       twritepng.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       twritepng.c (4923B)
       ---
            1 /* based on PNG 1.2 specification, July 1999  (see also rfc2083) */
            2 /* Alpha is not supported yet because of lack of industry acceptance and */
            3 /* because Plan9 Image uses premultiplied alpha, so png can't be lossless. */
            4 /* Only 24bit color supported, because 8bit may as well use GIF. */
            5 
            6 #include <u.h>
            7 #include <libc.h>
            8 #include <draw.h>
            9 #include <memdraw.h>
           10 #include <ctype.h>
           11 #include <bio.h>
           12 #include <flate.h>
           13 #include "imagefile.h"
           14 
           15 enum{        IDATSIZE =         20000,
           16         FilterNone =        0
           17 };
           18 
           19 typedef struct ZlibR{
           20         uchar *data;
           21         int width;
           22         int nrow, ncol;
           23         int row, col;        /* next pixel to send */
           24         int pixwid;
           25 } ZlibR;
           26 
           27 typedef struct ZlibW{
           28         Biobuf *bo;
           29         uchar *buf;
           30         uchar *b;        /* next place to write */
           31         uchar *e;        /* past end of buf */
           32 } ZlibW;
           33 
           34 static uint32 *crctab;
           35 static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
           36 
           37 static void
           38 put4(uchar *a, uint32 v)
           39 {
           40         a[0] = v>>24;
           41         a[1] = v>>16;
           42         a[2] = v>>8;
           43         a[3] = v;
           44 }
           45 
           46 static void
           47 chunk(Biobuf *bo, char *type, uchar *d, int n)
           48 {
           49         uchar buf[4];
           50         uint32 crc = 0;
           51 
           52         if(strlen(type) != 4)
           53                 return;
           54         put4(buf, n);
           55         Bwrite(bo, buf, 4);
           56         Bwrite(bo, type, 4);
           57         Bwrite(bo, d, n);
           58         crc = blockcrc(crctab, crc, type, 4);
           59         crc = blockcrc(crctab, crc, d, n);
           60         put4(buf, crc);
           61         Bwrite(bo, buf, 4);
           62 }
           63 
           64 static int
           65 zread(void *va, void *buf, int n)
           66 {
           67         ZlibR *z = va;
           68         int nrow = z->nrow;
           69         int ncol = z->ncol;
           70         uchar *b = buf, *e = b+n, *img;
           71         int pixels;  /* number of pixels in row that can be sent now */
           72         int i, a, pixwid;
           73 
           74         pixwid = z->pixwid;
           75         while(b+pixwid <= e){ /* loop over image rows */
           76                 if(z->row >= nrow)
           77                         break;
           78                 if(z->col==0)
           79                         *b++ = FilterNone;
           80                 pixels = (e-b)/pixwid;
           81                 if(pixels > ncol - z->col)
           82                         pixels = ncol - z->col;
           83                 img = z->data + z->width * z->row + pixwid * z->col;
           84 
           85                 memmove(b, img, pixwid*pixels);
           86                 if(pixwid == 4){
           87                         /*
           88                          * Convert to non-premultiplied alpha.
           89                          */
           90                         for(i=0; i<pixels; i++, b+=4){
           91                                 a = b[3];
           92                                 if(a == 255 || a == 0)
           93                                         ;
           94                                 else{
           95                                         if(b[0] >= a)
           96                                                 b[0] = a;
           97                                         b[0] = (b[0]*255)/a;
           98                                         if(b[1] >= a)
           99                                                 b[1] = a;
          100                                         b[1] = (b[1]*255)/a;
          101                                         if(b[2] >= a)
          102                                                 b[2] = a;
          103                                         b[2] = (b[2]*255)/a;
          104                                 }
          105                         }
          106                 }else
          107                         b += pixwid*pixels;
          108 
          109                 z->col += pixels;
          110                 if(z->col >= ncol){
          111                         z->col = 0;
          112                         z->row++;
          113                 }
          114         }
          115         return b - (uchar*)buf;
          116 }
          117 
          118 static void
          119 IDAT(ZlibW *z)
          120 {
          121         chunk(z->bo, "IDAT", z->buf, z->b - z->buf);
          122         z->b = z->buf;
          123 }
          124 
          125 static int
          126 zwrite(void *va, void *buf, int n)
          127 {
          128         ZlibW *z = va;
          129         uchar *b = buf, *e = b+n;
          130         int m;
          131 
          132         while(b < e){ /* loop over IDAT chunks */
          133                 m = z->e - z->b;
          134                 if(m > e - b)
          135                         m = e - b;
          136                 memmove(z->b, b, m);
          137                 z->b += m;
          138                 b += m;
          139                 if(z->b >= z->e)
          140                         IDAT(z);
          141         }
          142         return n;
          143 }
          144 
          145 static Memimage*
          146 memRGBA(Memimage *i)
          147 {
          148         Memimage *ni;
          149         char buf[32];
          150         ulong dst;
          151 
          152         /*
          153          * [A]BGR because we want R,G,B,[A] in big-endian order.  Sigh.
          154          */
          155         chantostr(buf, i->chan);
          156         if(strchr(buf, 'a'))
          157                 dst = ABGR32;
          158         else
          159                 dst = BGR24;
          160 
          161         if(i->chan == dst)
          162                 return i;
          163 
          164         ni = allocmemimage(i->r, dst);
          165         if(ni == nil)
          166                 return ni;
          167         memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
          168         return ni;
          169 }
          170 
          171 char*
          172 memwritepng(Biobuf *bo, Memimage *r, ImageInfo *II)
          173 {
          174         uchar buf[200], *h;
          175         ulong vgamma;
          176         int err, n;
          177         ZlibR zr;
          178         ZlibW zw;
          179         int nrow = r->r.max.y - r->r.min.y;
          180         int ncol = r->r.max.x - r->r.min.x;
          181         Tm *tm;
          182         Memimage *rgb;
          183 
          184         rgb = memRGBA(r);
          185         if(rgb == nil)
          186                 return "allocmemimage nil";
          187         crctab = mkcrctab(0xedb88320);
          188         if(crctab == nil)
          189                 sysfatal("mkcrctab error");
          190         deflateinit();
          191 
          192         Bwrite(bo, PNGmagic, sizeof PNGmagic);
          193         /* IHDR chunk */
          194         h = buf;
          195         put4(h, ncol); h += 4;
          196         put4(h, nrow); h += 4;
          197         *h++ = 8; /* bit depth = 24 bit per pixel */
          198         *h++ = rgb->chan==BGR24 ? 2 : 6; /* color type = rgb */
          199         *h++ = 0; /* compression method = deflate */
          200         *h++ = 0; /* filter method */
          201         *h++ = 0; /* interlace method = no interlace */
          202         chunk(bo, "IHDR", buf, h-buf);
          203 
          204         tm = gmtime(time(0));
          205         h = buf;
          206         *h++ = (tm->year + 1900)>>8;
          207         *h++ = (tm->year + 1900)&0xff;
          208         *h++ = tm->mon + 1;
          209         *h++ = tm->mday;
          210         *h++ = tm->hour;
          211         *h++ = tm->min;
          212         *h++ = tm->sec;
          213         chunk(bo, "tIME", buf, h-buf);
          214 
          215         if(II->fields_set & II_GAMMA){
          216                 vgamma = II->gamma*100000;
          217                 put4(buf, vgamma);
          218                 chunk(bo, "gAMA", buf, 4);
          219         }
          220 
          221         if(II->fields_set & II_COMMENT){
          222                 strncpy((char*)buf, "Comment", sizeof buf);
          223                 n = strlen((char*)buf)+1; /* leave null between Comment and text */
          224                 strncpy((char*)(buf+n), II->comment, sizeof buf - n);
          225                 chunk(bo, "tEXt", buf, n+strlen((char*)buf+n));
          226         }
          227 
          228         /* image chunks */
          229         zr.nrow = nrow;
          230         zr.ncol = ncol;
          231         zr.width = rgb->width * sizeof(uint32);
          232         zr.data = rgb->data->bdata;
          233         zr.row = zr.col = 0;
          234         zr.pixwid = chantodepth(rgb->chan)/8;
          235         zw.bo = bo;
          236         zw.buf = malloc(IDATSIZE);
          237         zw.b = zw.buf;
          238         zw.e = zw.b + IDATSIZE;
          239         err = deflatezlib(&zw, zwrite, &zr, zread, 6, 0);
          240         if(zw.b > zw.buf)
          241                 IDAT(&zw);
          242         free(zw.buf);
          243         if(err)
          244                 sysfatal("deflatezlib %s\n", flateerr(err));
          245         chunk(bo, "IEND", nil, 0);
          246 
          247         if(r != rgb)
          248                 freememimage(rgb);
          249         return nil;
          250 }