writepng.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. * See PNG 1.2 spec, also RFC 2083.
  3. */
  4. #include <u.h>
  5. #include <libc.h>
  6. #include <draw.h>
  7. #include <memdraw.h>
  8. #include <ctype.h>
  9. #include <bio.h>
  10. #include <flate.h>
  11. #include "imagefile.h"
  12. enum
  13. {
  14. IDATSIZE = 20000,
  15. FilterNone = 0,
  16. };
  17. typedef struct ZlibR ZlibR;
  18. typedef struct ZlibW ZlibW;
  19. struct ZlibR
  20. {
  21. uchar *data;
  22. int width;
  23. int dx;
  24. int dy;
  25. int x;
  26. int y;
  27. int pixwid;
  28. };
  29. struct ZlibW
  30. {
  31. Biobuf *io;
  32. uchar *buf;
  33. uchar *b;
  34. uchar *e;
  35. };
  36. static ulong *crctab;
  37. static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
  38. static void
  39. put4(uchar *a, ulong v)
  40. {
  41. a[0] = v>>24;
  42. a[1] = v>>16;
  43. a[2] = v>>8;
  44. a[3] = v;
  45. }
  46. static void
  47. chunk(Biobuf *bo, char *type, uchar *d, int n)
  48. {
  49. uchar buf[4];
  50. ulong crc = 0;
  51. if(strlen(type) != 4)
  52. return;
  53. put4(buf, n);
  54. Bwrite(bo, buf, 4);
  55. Bwrite(bo, type, 4);
  56. Bwrite(bo, d, n);
  57. crc = blockcrc(crctab, crc, type, 4);
  58. crc = blockcrc(crctab, crc, d, n);
  59. put4(buf, crc);
  60. Bwrite(bo, buf, 4);
  61. }
  62. static int
  63. zread(void *va, void *buf, int n)
  64. {
  65. int a, i, pixels, pixwid;
  66. uchar *b, *e, *img;
  67. ZlibR *z;
  68. z = va;
  69. pixwid = z->pixwid;
  70. b = buf;
  71. e = b+n;
  72. while(b+pixwid < e){ /* one less for filter alg byte */
  73. if(z->y >= z->dy)
  74. break;
  75. if(z->x == 0)
  76. *b++ = FilterNone;
  77. pixels = (e-b)/pixwid;
  78. if(pixels > z->dx - z->x)
  79. pixels = z->dx - z->x;
  80. img = z->data + z->width*z->y + pixwid*z->x;
  81. memmove(b, img, pixwid*pixels);
  82. if(pixwid == 4){
  83. /*
  84. * Convert to non-premultiplied alpha.
  85. */
  86. for(i=0; i<pixels; i++, b+=4){
  87. a = b[3];
  88. if(a == 0)
  89. b[0] = b[1] = b[2] = 0;
  90. else if(a != 255){
  91. if(b[0] >= a)
  92. b[0] = a;
  93. b[0] = (b[0]*255)/a;
  94. if(b[1] >= a)
  95. b[1] = a;
  96. b[1] = (b[1]*255)/a;
  97. if(b[2] >= a)
  98. b[2] = a;
  99. b[2] = (b[2]*255)/a;
  100. }
  101. }
  102. }else
  103. b += pixwid*pixels;
  104. z->x += pixels;
  105. if(z->x >= z->dx){
  106. z->x = 0;
  107. z->y++;
  108. }
  109. }
  110. return b - (uchar*)buf;
  111. }
  112. static void
  113. IDAT(ZlibW *z)
  114. {
  115. chunk(z->io, "IDAT", z->buf, z->b - z->buf);
  116. z->b = z->buf;
  117. }
  118. static int
  119. zwrite(void *va, void *buf, int n)
  120. {
  121. int m;
  122. uchar *b, *e;
  123. ZlibW *z;
  124. z = va;
  125. b = buf;
  126. e = b+n;
  127. while(b < e){
  128. m = z->e - z->b;
  129. if(m > e - b)
  130. m = e - b;
  131. memmove(z->b, b, m);
  132. z->b += m;
  133. b += m;
  134. if(z->b >= z->e)
  135. IDAT(z);
  136. }
  137. return n;
  138. }
  139. static Memimage*
  140. memRGBA(Memimage *i)
  141. {
  142. Memimage *ni;
  143. char buf[32];
  144. ulong dst;
  145. /*
  146. * [A]BGR because we want R,G,B,[A] in big-endian order. Sigh.
  147. */
  148. chantostr(buf, i->chan);
  149. if(strchr(buf, 'a'))
  150. dst = ABGR32;
  151. else
  152. dst = BGR24;
  153. if(i->chan == dst)
  154. return i;
  155. ni = allocmemimage(i->r, dst);
  156. if(ni == nil)
  157. return ni;
  158. memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
  159. return ni;
  160. }
  161. char*
  162. memwritepng(Biobuf *io, Memimage *m, ImageInfo *II)
  163. {
  164. int err, n;
  165. uchar buf[200], *h;
  166. ulong vgamma;
  167. Tm *tm;
  168. Memimage *rgb;
  169. ZlibR zr;
  170. ZlibW zw;
  171. crctab = mkcrctab(0xedb88320);
  172. if(crctab == nil)
  173. sysfatal("mkcrctab error");
  174. deflateinit();
  175. rgb = memRGBA(m);
  176. if(rgb == nil)
  177. return "allocmemimage nil";
  178. Bwrite(io, PNGmagic, sizeof PNGmagic);
  179. /* IHDR chunk */
  180. h = buf;
  181. put4(h, Dx(m->r)); h += 4;
  182. put4(h, Dy(m->r)); h += 4;
  183. *h++ = 8; /* 8 bits per channel */
  184. if(rgb->chan == BGR24)
  185. *h++ = 2; /* RGB */
  186. else
  187. *h++ = 6; /* RGBA */
  188. *h++ = 0; /* compression - deflate */
  189. *h++ = 0; /* filter - none */
  190. *h++ = 0; /* interlace - none */
  191. chunk(io, "IHDR", buf, h-buf);
  192. /* time - using now is suspect */
  193. tm = gmtime(time(0));
  194. h = buf;
  195. *h++ = (tm->year + 1900)>>8;
  196. *h++ = (tm->year + 1900)&0xff;
  197. *h++ = tm->mon + 1;
  198. *h++ = tm->mday;
  199. *h++ = tm->hour;
  200. *h++ = tm->min;
  201. *h++ = tm->sec;
  202. chunk(io, "tIME", buf, h-buf);
  203. /* gamma */
  204. if(II->fields_set & II_GAMMA){
  205. vgamma = II->gamma*100000;
  206. put4(buf, vgamma);
  207. chunk(io, "gAMA", buf, 4);
  208. }
  209. /* comment */
  210. if(II->fields_set & II_COMMENT){
  211. strncpy((char*)buf, "Comment", sizeof buf);
  212. n = strlen((char*)buf)+1; // leave null between Comment and text
  213. strncpy((char*)(buf+n), II->comment, sizeof buf - n);
  214. chunk(io, "tEXt", buf, n+strlen((char*)buf+n));
  215. }
  216. /* image data */
  217. zr.dx = Dx(m->r);
  218. zr.dy = Dy(m->r);
  219. zr.width = rgb->width * sizeof(ulong);
  220. zr.data = rgb->data->bdata;
  221. zr.x = 0;
  222. zr.y = 0;
  223. zr.pixwid = chantodepth(rgb->chan)/8;
  224. zw.io = io;
  225. zw.buf = malloc(IDATSIZE);
  226. if(zw.buf == nil)
  227. sysfatal("malloc: %r");
  228. zw.b = zw.buf;
  229. zw.e = zw.b + IDATSIZE;
  230. if((err=deflatezlib(&zw, zwrite, &zr, zread, 6, 0)) < 0)
  231. sysfatal("deflatezlib %s", flateerr(err));
  232. if(zw.b > zw.buf)
  233. IDAT(&zw);
  234. free(zw.buf);
  235. chunk(io, "IEND", nil, 0);
  236. if(m != rgb)
  237. freememimage(rgb);
  238. return nil;
  239. }