writepng.c 4.2 KB

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