writepng.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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. #include <u.h>
  6. #include <libc.h>
  7. #include <draw.h>
  8. #include <memdraw.h>
  9. #include <ctype.h>
  10. #include <bio.h>
  11. #include <flate.h>
  12. #include "imagefile.h"
  13. enum{ IDATSIZE = 20000,
  14. FilterNone = 0,
  15. };
  16. typedef struct ZlibR{
  17. uchar *data;
  18. int nrow, ncol;
  19. int row, col; // next pixel to send
  20. } ZlibR;
  21. typedef struct ZlibW{
  22. Biobuf *bo;
  23. uchar *buf;
  24. uchar *b; // next place to write
  25. uchar *e; // past end of buf
  26. } ZlibW;
  27. static ulong *crctab;
  28. static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
  29. static void
  30. put4(uchar *a, ulong v)
  31. {
  32. a[0] = v>>24;
  33. a[1] = v>>16;
  34. a[2] = v>>8;
  35. a[3] = v;
  36. }
  37. static void
  38. chunk(Biobuf *bo, char *type, uchar *d, int n)
  39. {
  40. uchar buf[4];
  41. ulong crc = 0;
  42. if(strlen(type) != 4)
  43. return;
  44. put4(buf, n);
  45. Bwrite(bo, buf, 4);
  46. Bwrite(bo, type, 4);
  47. Bwrite(bo, d, n);
  48. crc = blockcrc(crctab, crc, type, 4);
  49. crc = blockcrc(crctab, crc, d, n);
  50. put4(buf, crc);
  51. Bwrite(bo, buf, 4);
  52. }
  53. static int
  54. zread(void *va, void *buf, int n)
  55. {
  56. ZlibR *z = va;
  57. int nrow = z->nrow;
  58. int ncol = z->ncol;
  59. uchar *b = buf, *e = b+n, *img;
  60. int i, pixels; // number of pixels in row that can be sent now
  61. while(b+3 <= e){ // loop over image rows
  62. if(z->row >= nrow)
  63. break;
  64. if(z->col==0)
  65. *b++ = FilterNone;
  66. pixels = (e-b)/3;
  67. if(pixels > ncol - z->col)
  68. pixels = ncol - z->col;
  69. img = z->data + 3 * z->row * ncol + 3 * z->col;
  70. // Plan 9 image format is BGR?!!!
  71. // memmove(b, img, 3*pixels);
  72. // b += 3*pixels;
  73. for(i=0; i<pixels; i++, img += 3){
  74. *b++ = img[2];
  75. *b++ = img[1];
  76. *b++ = img[0];
  77. }
  78. z->col += pixels;
  79. if(z->col >= ncol){
  80. z->col = 0;
  81. z->row++;
  82. }
  83. }
  84. return b - (uchar*)buf;
  85. }
  86. static void
  87. IDAT(ZlibW *z)
  88. {
  89. chunk(z->bo, "IDAT", z->buf, z->b - z->buf);
  90. z->b = z->buf;
  91. }
  92. static int
  93. zwrite(void *va, void *buf, int n)
  94. {
  95. ZlibW *z = va;
  96. uchar *b = buf, *e = b+n;
  97. int m;
  98. while(b < e){ // loop over IDAT chunks
  99. m = z->e - z->b;
  100. if(m > e - b)
  101. m = e - b;
  102. memmove(z->b, b, m);
  103. z->b += m;
  104. b += m;
  105. if(z->b >= z->e)
  106. IDAT(z);
  107. }
  108. return n;
  109. }
  110. static Memimage*
  111. memRGB(Memimage *i)
  112. {
  113. Memimage *ni;
  114. if(i->chan == RGB24)
  115. return i;
  116. ni = allocmemimage(i->r, RGB24);
  117. if(ni == nil)
  118. return ni;
  119. memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
  120. return ni;
  121. }
  122. char*
  123. memwritepng(Biobuf *bo, Memimage *r, ImageInfo *II)
  124. {
  125. uchar buf[200], *h;
  126. ulong vgamma;
  127. int err, n;
  128. ZlibR zr;
  129. ZlibW zw;
  130. int nrow = r->r.max.y - r->r.min.y;
  131. int ncol = r->r.max.x - r->r.min.x;
  132. Tm *tm;
  133. Memimage *rgb;
  134. rgb = memRGB(r);
  135. if(rgb == nil)
  136. return "allocmemimage nil";
  137. crctab = mkcrctab(0xedb88320);
  138. if(crctab == nil)
  139. sysfatal("mkcrctab error");
  140. deflateinit();
  141. Bwrite(bo, PNGmagic, sizeof PNGmagic);
  142. // IHDR chunk
  143. h = buf;
  144. put4(h, ncol); h += 4;
  145. put4(h, nrow); h += 4;
  146. *h++ = 8; // bit depth = 24 bit per pixel
  147. *h++ = 2; // color type = rgb
  148. *h++ = 0; // compression method = deflate
  149. *h++ = 0; // filter method
  150. *h++ = 0; // interlace method = no interlace
  151. chunk(bo, "IHDR", buf, h-buf);
  152. tm = gmtime(time(0));
  153. h = buf;
  154. *h++ = (tm->year + 1900)>>8;
  155. *h++ = (tm->year + 1900)&0xff;
  156. *h++ = tm->mon + 1;
  157. *h++ = tm->mday;
  158. *h++ = tm->hour;
  159. *h++ = tm->min;
  160. *h++ = tm->sec;
  161. chunk(bo, "tIME", buf, h-buf);
  162. if(II->fields_set & II_GAMMA){
  163. vgamma = II->gamma*100000;
  164. put4(buf, vgamma);
  165. chunk(bo, "gAMA", buf, 4);
  166. }
  167. if(II->fields_set & II_COMMENT){
  168. strncpy((char*)buf, "Comment", sizeof buf);
  169. n = strlen((char*)buf)+1; // leave null between Comment and text
  170. strncpy((char*)(buf+n), II->comment, sizeof buf - n);
  171. chunk(bo, "tEXt", buf, n+strlen((char*)buf+n));
  172. }
  173. // image chunks
  174. zr.nrow = nrow;
  175. zr.ncol = ncol;
  176. zr.data = rgb->data->bdata;
  177. zr.row = zr.col = 0;
  178. zw.bo = bo;
  179. zw.buf = malloc(IDATSIZE);
  180. zw.b = zw.buf;
  181. zw.e = zw.b + IDATSIZE;
  182. err = deflatezlib(&zw, zwrite, &zr, zread, 6, 0);
  183. if(zw.b > zw.buf)
  184. IDAT(&zw);
  185. free(zw.buf);
  186. if(err)
  187. sysfatal("deflatezlib %s\n", flateerr(err));
  188. chunk(bo, "IEND", nil, 0);
  189. if(r != rgb)
  190. freememimage(rgb);
  191. return nil;
  192. }