readpng.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // work in progress... this version only good enough to read
  2. // non-interleaved, 24bit RGB images
  3. #include <u.h>
  4. #include <libc.h>
  5. #include <ctype.h>
  6. #include <bio.h>
  7. #include <flate.h>
  8. #include <draw.h>
  9. #include "imagefile.h"
  10. int debug;
  11. enum{ IDATSIZE=1000000,
  12. FilterNone = 0,
  13. PropertyBit = 1<<5,
  14. };
  15. typedef struct ZlibR{
  16. Biobuf *bi;
  17. uchar *buf;
  18. uchar *b; // next byte to decompress
  19. uchar *e; // past end of buf
  20. } ZlibR;
  21. typedef struct ZlibW{
  22. uchar *r, *g, *b; // Rawimage channels
  23. int chan; // next channel to write
  24. int col; // column index of current pixel
  25. // -1 = one-byte pseudo-column for filter spec
  26. int row; // row index of current pixel
  27. int ncol, nrow; // image width, height
  28. } ZlibW;
  29. static ulong *crctab;
  30. static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
  31. static char readerr[] = "ReadPNG: read error: %r";
  32. static char memerr[] = "ReadPNG: malloc failed: %r";
  33. static ulong
  34. get4(uchar *a)
  35. {
  36. return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
  37. }
  38. static
  39. void
  40. pnginit(void)
  41. {
  42. static int inited;
  43. if(inited)
  44. return;
  45. inited = 1;
  46. crctab = mkcrctab(0xedb88320);
  47. if(crctab == nil)
  48. sysfatal("mkcrctab error");
  49. inflateinit();
  50. }
  51. static
  52. void*
  53. pngmalloc(ulong n, int clear)
  54. {
  55. void *p;
  56. p = malloc(n);
  57. if(p == nil)
  58. sysfatal(memerr);
  59. if(clear)
  60. memset(p, 0, n);
  61. return p;
  62. }
  63. static int
  64. getchunk(Biobuf *b, char *type, uchar *d, int m)
  65. {
  66. uchar buf[8];
  67. ulong crc = 0, crc2;
  68. int n, nr;
  69. if(Bread(b, buf, 8) != 8)
  70. return -1;
  71. n = get4(buf);
  72. memmove(type, buf+4, 4);
  73. type[4] = 0;
  74. if(n > m)
  75. sysfatal("getchunk needed %d, had %d", n, m);
  76. nr = Bread(b, d, n);
  77. if(nr != n)
  78. sysfatal("getchunk read %d, expected %d", nr, n);
  79. crc = blockcrc(crctab, crc, type, 4);
  80. crc = blockcrc(crctab, crc, d, n);
  81. if(Bread(b, buf, 4) != 4)
  82. sysfatal("getchunk tlr failed");
  83. crc2 = get4(buf);
  84. if(crc != crc2)
  85. sysfatal("getchunk crc failed");
  86. return m;
  87. }
  88. static int
  89. zread(void *va)
  90. {
  91. ZlibR *z = va;
  92. char type[5];
  93. int n;
  94. if(z->b >= z->e){
  95. refill_buffer:
  96. z->b = z->buf;
  97. n = getchunk(z->bi, type, z->b, z->e - z->b);
  98. if(n < 0 || strcmp(type, "IEND") == 0)
  99. return -1;
  100. if(type[0] & PropertyBit)
  101. goto refill_buffer; /* skip auxiliary chunks for now */
  102. if(strcmp(type,"IDAT") != 0)
  103. sysfatal("unrecognized mandatory chunk %s", type);
  104. }
  105. return *z->b++;
  106. }
  107. static int
  108. zwrite(void *va, void *vb, int n)
  109. {
  110. ZlibW *z = va;
  111. uchar *buf = vb;
  112. int i;
  113. for(i=0; i<n; i++){
  114. if(z->col == -1){
  115. // skip filter byte
  116. buf++;
  117. z->col++;
  118. continue;
  119. }
  120. switch(z->chan){
  121. case 0:
  122. *z->r++ = *buf++;
  123. z->chan = 1;
  124. break;
  125. case 1:
  126. *z->g++ = *buf++;
  127. z->chan = 2;
  128. break;
  129. case 2:
  130. *z->b++ = *buf++;
  131. z->chan = 0;
  132. z->col++;
  133. if(z->col == z->ncol){
  134. z->col = -1;
  135. z->row++;
  136. if((z->row >= z->nrow) && (i < n-1) )
  137. sysfatal("header said %d rows; data goes further", z->nrow);
  138. }
  139. break;
  140. }
  141. }
  142. return n;
  143. }
  144. static Rawimage*
  145. readslave(Biobuf *b)
  146. {
  147. ZlibR zr;
  148. ZlibW zw;
  149. Rawimage *image;
  150. char type[5];
  151. uchar *buf, *h;
  152. int k, n, nrow, ncol, err;
  153. buf = pngmalloc(IDATSIZE, 0);
  154. Bread(b, buf, sizeof PNGmagic);
  155. if(memcmp(PNGmagic, buf, sizeof PNGmagic) != 0)
  156. sysfatal("bad PNGmagic");
  157. n = getchunk(b, type, buf, IDATSIZE);
  158. if(n < 13 || strcmp(type,"IHDR") != 0)
  159. sysfatal("missing IHDR chunk");
  160. h = buf;
  161. ncol = get4(h); h += 4;
  162. nrow = get4(h); h += 4;
  163. if(debug)
  164. fprint(2, "readpng nrow=%d ncol=%d\n", nrow, ncol);
  165. if(*h++ != 8)
  166. sysfatal("only 24 bit per pixel supported for now [%d]", h[-1]);
  167. if(*h++ != 2)
  168. sysfatal("only rgb supported for now [%d]", h[-1]);
  169. if(*h++ != 0)
  170. sysfatal("only deflate supported for now [%d]", h[-1]);
  171. if(*h++ != FilterNone)
  172. sysfatal("only FilterNone supported for now [%d]", h[-1]);
  173. if(*h != 0)
  174. sysfatal("only non-interlaced supported for now [%d]", h[-1]);
  175. image = pngmalloc(sizeof(Rawimage), 1);
  176. image->r = Rect(0, 0, ncol, nrow);
  177. image->cmap = nil;
  178. image->cmaplen = 0;
  179. image->chanlen = ncol*nrow;
  180. image->fields = 0;
  181. image->gifflags = 0;
  182. image->gifdelay = 0;
  183. image->giftrindex = 0;
  184. image->chandesc = CRGB;
  185. image->nchans = 3;
  186. for(k=0; k<3; k++)
  187. image->chans[k] = pngmalloc(ncol*nrow, 0);
  188. zr.bi = b;
  189. zr.buf = buf;
  190. zr.b = zr.e = buf + IDATSIZE;
  191. zw.r = image->chans[0];
  192. zw.g = image->chans[1];
  193. zw.b = image->chans[2];
  194. zw.chan = 0;
  195. zw.col = -1;
  196. zw.row = 0;
  197. zw.ncol = ncol;
  198. zw.nrow = nrow;
  199. err = inflatezlib(&zw, zwrite, &zr, zread);
  200. if(err)
  201. sysfatal("inflatezlib %s\n", flateerr(err));
  202. free(buf);
  203. return image;
  204. }
  205. Rawimage**
  206. Breadpng(Biobuf *b, int colorspace)
  207. {
  208. Rawimage *r, **array;
  209. char buf[ERRMAX];
  210. buf[0] = '\0';
  211. if(colorspace != CRGB){
  212. errstr(buf, sizeof buf); /* throw it away */
  213. werrstr("ReadPNG: unknown color space %d", colorspace);
  214. return nil;
  215. }
  216. pnginit();
  217. array = malloc(2*sizeof(*array));
  218. if(array==nil)
  219. return nil;
  220. errstr(buf, sizeof buf); /* throw it away */
  221. r = readslave(b);
  222. array[0] = r;
  223. array[1] = nil;
  224. return array;
  225. }
  226. Rawimage**
  227. readpng(int fd, int colorspace)
  228. {
  229. Rawimage** a;
  230. Biobuf b;
  231. if(Binit(&b, fd, OREAD) < 0)
  232. return nil;
  233. a = Breadpng(&b, colorspace);
  234. Bterm(&b);
  235. return a;
  236. }