/ fdf / fdf / mlx / mlx_png.c
mlx_png.c
  1  
  2  
  3  #include <stdlib.h>
  4  #include <stdio.h>
  5  #include <sys/mman.h>
  6  #include <unistd.h>
  7  #include <fcntl.h>
  8  #include <err.h>
  9  #include <string.h>
 10  #include <arpa/inet.h>
 11  
 12  #include "zlib.h"
 13  
 14  #include "mlx.h"
 15  
 16  #define UNIQ_BPP 4
 17  
 18  #define	PNG_MAGIC_SIZE	8
 19  unsigned char magic[PNG_MAGIC_SIZE] = {137, 80, 78, 71, 13, 10, 26, 10};
 20  #define PNG_HDR_SIZE	13
 21  
 22  #define	Z_CHUNK	32768
 23  
 24  #define	ERR_MAGIC_SIZE	1
 25  #define	ERR_MAGIC_WRONG	2
 26  #define	ERR_STRUCT_INCOMPLETE	3
 27  #define	ERR_STRUCT_HDR	4
 28  #define	ERR_STRUCT_END	5
 29  #define	ERR_STRUCT_CRC	6
 30  #define	ERR_STRUCT_INCIMPL 7
 31  #define	ERR_STRUCT_DAT	8
 32  #define	ERR_STRUCT_MISSCHK	9
 33  #define	ERR_ZLIB	10
 34  #define	ERR_DATA_MISMATCH	11
 35  #define	ERR_DATA_FILTER	12
 36  #define ERR_MALLOC	13
 37  char *(mipng_err[]) =
 38  {
 39    "No error",
 40    "Not enough size for magic",
 41    "Wrong magic",
 42    "Incomplete chunk structure",
 43    "Duplicate or incorrect header",
 44    "Duplicate or incorrect end",
 45    "Invalid CRC in chunk",
 46    "Incorrect header or configuration not implemented",
 47    "Non consecutive dat chunks",
 48    "Missing header/dat/end chunk(s)",
 49    "Zlib inflate error",
 50    "Inflated data size mismatch",
 51    "Unknown scanline filter",
 52    "Can't malloc"
 53  };
 54  
 55  typedef struct png_info_s
 56  {
 57    unsigned int	width;
 58    unsigned int	height;
 59    int		depth;
 60    int		color;
 61    int		interlace;
 62    int		bpp;
 63  } png_info_t;
 64  
 65  
 66  int	mipng_is_type(unsigned char *ptr, char *type)
 67  {
 68    if (ptr[4] == type[0] && ptr[5] == type[1] && ptr[6] == type[2] && ptr[7] == type[3])
 69      return (1);
 70    return (0);
 71  }
 72  
 73  
 74  unsigned char mipng_defilter_none(unsigned char *buff, int pos, int a, int b, int c)
 75  { return (buff[pos]); }
 76  unsigned char mipng_defilter_sub(unsigned char *buff, int pos, int a, int b, int c)
 77  { return (buff[pos]+(unsigned int)a); }
 78  unsigned char mipng_defilter_up(unsigned char *buff, int pos, int a, int b, int c)
 79  { return (buff[pos]+(unsigned int)b); }
 80  unsigned char mipng_defilter_average(unsigned char *buff, int pos, int a, int b, int c)
 81  { return (buff[pos]+((unsigned int)a+(unsigned int)b)/2); }
 82  unsigned char mipng_defilter_paeth(unsigned char *buff, int pos, int a, int b, int c)
 83  {
 84    int	p;
 85    int	result;
 86  
 87    p = a + b - c;
 88    if (abs(b - c) <= abs(a - c) && abs(b - c) <= abs(a + b - c - c))
 89      result = a;
 90    else
 91      if (abs(a - c) <= abs(a + b - c - c))
 92        result = b;
 93      else
 94        result = c;
 95   return (buff[pos]+result);
 96  }
 97  
 98  
 99  
100  unsigned char (*(mipng_defilter[]))(unsigned char *buff, int pos, int a, int b, int c) =
101  {
102    mipng_defilter_none,
103    mipng_defilter_sub,
104    mipng_defilter_up,
105    mipng_defilter_average,
106    mipng_defilter_paeth
107  };
108  
109  // only work for mlx mac or img 32bpp
110  int	mipng_fill_img(void *img, unsigned char *buf, png_info_t *pi)
111  {
112    unsigned int	current_filter;
113    int	ipos;
114    int	bpos;
115    int	ilen;
116    int	iline;
117    int	blen;
118    int   bpp;
119    int   endian;
120    unsigned char tmp;
121    unsigned char *ibuf;
122  
123    ibuf = (unsigned char *)mlx_get_data_addr(img, &bpp, &iline, &endian);
124    //  iline = img->width * UNIQ_BPP;
125    // ilen = img->width * img->height * UNIQ_BPP;
126    ilen = iline*pi->height;
127    ipos = 0;
128    blen = pi->width * pi->height * pi->bpp + pi->height;  // ??? why + pi->height ??
129    bpos = 0;
130    while (ipos < ilen && bpos < blen)
131      {
132        if (ipos % iline == 0)
133  	{
134  	  // printf("ipos %d iline %d pi->width %d bpos %d\n", ipos, iline, pi->width, bpos);
135  	  if ((current_filter = buf[bpos++]) > 4)
136  	    {  
137  	    return (ERR_DATA_FILTER);
138  	    }
139  	}
140        ibuf[ipos] = mipng_defilter[current_filter](buf, bpos,
141  						  ipos%iline>3?ibuf[ipos-UNIQ_BPP]:0,
142  				 (ipos>=iline)?ibuf[ipos-iline]:0,
143  						  (ipos>=iline && ipos%iline>3)?ibuf[ipos-iline-UNIQ_BPP]:0);
144        ipos ++;
145        bpos ++;
146        if (pi->depth == 16)
147  	bpos ++;
148        if (ipos % 4 == 3 && pi->color == 2)  // no alpha
149  	ibuf[ipos++] = 0xFF;
150        if (ipos % iline == pi->width * 4)
151  	ipos += iline-pi->width*4;
152      }
153    if (ipos != ilen || bpos != blen)
154      {
155        //      printf("fill err ipos %d vs %d, bpos %d vs %d\n", ipos, ilen, bpos, blen);
156        return (ERR_DATA_MISMATCH);
157      }
158    ipos = 0;
159    while (ipos < ilen)
160      {
161        tmp = ibuf[ipos];
162        ibuf[ipos] = ibuf[ipos+2];
163        ibuf[ipos+2] = tmp;
164        ibuf[ipos+3] = 0xFF - ibuf[ipos+3];
165        ipos += UNIQ_BPP;
166      }
167    return (0);
168  }
169  
170  
171  int	mipng_data(void *img, unsigned char *dat, png_info_t *pi)
172  {
173    unsigned int	len;
174    int		b_pos;
175    unsigned char *buffer;
176    int		ret;
177    int z_ret;
178    unsigned z_have;
179    z_stream z_strm;
180    unsigned char z_out[Z_CHUNK];
181  
182    b_pos = 0;
183    if (!(buffer = malloc((long long)pi->width*(long long)pi->height*(long long)pi->bpp + pi->height)))
184      return (ERR_MALLOC);
185    z_strm.zalloc = Z_NULL;
186    z_strm.zfree = Z_NULL;
187    z_strm.opaque = Z_NULL;
188    z_strm.avail_in = 0;
189    z_strm.next_in = Z_NULL;
190    z_ret = inflateInit(&z_strm);
191    if (z_ret != Z_OK)
192      {
193        free(buffer);
194        return (ERR_ZLIB);
195      }
196  
197    while (mipng_is_type(dat, "IDAT"))
198      {
199        len = *((unsigned int *)dat);
200        len = ntohl(len);
201        z_strm.avail_in = len;
202        z_strm.next_in = dat + 8;
203        z_strm.avail_out = 0;
204        while (z_strm.avail_out == 0)
205  	{
206  	  z_strm.avail_out = Z_CHUNK;
207  	  z_strm.next_out = z_out;
208  	  z_ret = inflate(&z_strm, Z_NO_FLUSH);
209  	  //	  printf("inflate ret %d avail_out %d\n", z_ret, z_strm.avail_out);
210  	  if (z_ret != Z_OK && z_ret != Z_STREAM_END)
211  	    {
212  	      inflateEnd(&z_strm);
213  	      free(buffer);
214  	      return (ERR_ZLIB);
215  	    }
216  	  if (b_pos + Z_CHUNK - z_strm.avail_out > pi->width*pi->height*pi->bpp+pi->height)
217  	    {
218  	      inflateEnd(&z_strm);
219  	      free(buffer);
220  	      return (ERR_DATA_MISMATCH);
221  	    }
222  	  bcopy(z_out, buffer+b_pos, Z_CHUNK - z_strm.avail_out);
223  	  b_pos += Z_CHUNK - z_strm.avail_out;
224  	}
225        dat += len + 4 + 4 + 4;
226      } 
227    inflateEnd(&z_strm);
228    if (b_pos != pi->width*pi->height*pi->bpp+pi->height)
229      {
230        //      printf("pb : bpos %d vs expected %d\n", b_pos, img->width*img->height*pi->bpp+img->height);
231        free(buffer);
232        return (ERR_DATA_MISMATCH);
233      }
234    ret = mipng_fill_img(img, buffer, pi);
235    free(buffer);
236    return (ret);
237  }
238  
239  
240  
241  int	mipng_magic(unsigned char *ptr, int size)
242  {
243    int	i;
244  
245    if (size < PNG_MAGIC_SIZE)
246      return (ERR_MAGIC_SIZE);
247    i = 0;
248    while (i < PNG_MAGIC_SIZE)
249      if (*(ptr++) != magic[i++])
250        return (ERR_MAGIC_WRONG);
251    return (0);
252  }
253  
254  
255  unsigned long crc_table[256] = { 0, 0x77073096, 0xee0e612c, 0x990951ba, 0x76dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0xedb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x9b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x1db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x6b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0xf00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x86d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x3b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x4db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0xd6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0xa00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x26d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x5005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0xcb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0xbdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };
256  
257  // From http://www.w3.org/TR/PNG/#D-CRCAppendix
258  int	mipng_crc(unsigned char *ptr, int len)
259  {
260    unsigned int	file_crc;
261    unsigned long crc;
262    int		i;
263  
264    file_crc = *((unsigned int *)(ptr+4+4+len));
265    file_crc = ntohl(file_crc);
266    
267    crc = 0xffffffffL;
268    i = 0;
269    while (i < len+4)
270      crc = crc_table[(crc ^ ptr[(i++)+4]) & 0xff] ^ (crc >> 8);
271    crc ^= 0xffffffffL;
272  
273    if (file_crc != crc)
274      return (1);
275    return (0);
276  }
277  
278  
279  int	mipng_structure(unsigned char *ptr, int size, unsigned char **hdr, unsigned char **dat)
280  {
281    unsigned int	len;
282    int		dat_state;
283    int		end;
284  
285    dat_state = 0;
286    *hdr = NULL;
287    *dat = NULL;
288    end = 0;
289    while (size)
290      {
291        if (size >= 4) // length present
292  	{
293  	  len = *((unsigned int *)ptr);
294  	  len = ntohl(len);
295  	  if (size < 4 + 4 + 4 + len)
296  	    return (ERR_STRUCT_INCOMPLETE);
297  	  if (mipng_crc(ptr, len))
298  	    return (ERR_STRUCT_CRC);
299  	  //	  printf("found chunk len %d type %c%c%c%c\n", len, *(ptr+4), *(ptr+5), *(ptr+6), *(ptr+7));
300  	  if (mipng_is_type(ptr, "IHDR"))
301  	    {
302  	      if (*hdr || len != PNG_HDR_SIZE)
303  		return (ERR_STRUCT_HDR);
304  	      *hdr = ptr;
305  	    }
306  	  if (mipng_is_type(ptr, "IEND"))
307  	    {
308  	      if (len != 0 || size != 4+4+4)
309  		return (ERR_STRUCT_END);
310  	      end = 1;
311  	    }
312  	  if (mipng_is_type(ptr, "IDAT"))
313  	    {
314  	      if (dat_state == 0)
315  		{
316  		  dat_state = 1;
317  		  *dat = ptr;
318  		}
319  	      if (dat_state == 2)
320  		return (ERR_STRUCT_DAT);
321  	    }
322  	  else
323  	    if (dat_state == 1)
324  	      dat_state = 2;
325  	  size -= 4+4+4+len;
326  	  ptr += 4+4+4+len;
327  	}
328        else
329  	return (ERR_STRUCT_INCOMPLETE);
330      }
331    if (*hdr == 0 || *dat == 0 || end == 0)
332      return (ERR_STRUCT_MISSCHK);
333    return (0);
334  }
335  
336  
337  int	mipng_verif_hdr(unsigned char *hdr, png_info_t *pi)
338  {
339    unsigned int	compress;
340    unsigned int	filter;
341  
342    hdr += 8;
343    pi->width = ntohl(*((unsigned long *)hdr));
344    pi->height = ntohl(*((unsigned long *)(hdr+4)));
345    pi->depth = *(hdr+8);
346    pi->color = *(hdr+9);
347    compress = *(hdr+10);
348    filter = *(hdr+11);
349    pi->interlace = *(hdr+12);
350    if (pi->width <= 0 || pi->height <= 0 || (pi->depth != 8 && pi->depth != 16)
351        || (pi->color != 2 && pi->color != 6) || compress != 0 || filter != 0 || pi->interlace != 0)
352      return (ERR_STRUCT_INCIMPL);
353    pi->bpp = pi->depth / 8;
354    if (pi->color == 2)
355      pi->bpp *= 3;
356    if (pi->color == 6)
357      pi->bpp *= 4;
358    //  printf("hdr info : %d x %d, depth %d, col type %d, comp %d, filter %d, interlace %d\nbpp is %d\n",
359    //	 pi->width, pi->height, pi->depth, pi->color, compress, filter, pi->interlace, pi->bpp);
360    return (0);
361  }
362  
363  
364  void	*mlx_int_parse_png(void *xvar, unsigned char *fptr, int size, int *width, int *height)
365  {
366    int		err;
367    unsigned char *hdr;
368    unsigned char *dat;
369    png_info_t	pi;
370    void          *img;
371  
372    if ((err = mipng_magic(fptr, size)))
373      {
374        warnx("mlx PNG error : %s", mipng_err[err]);
375        return ((void *)0);
376      }
377    fptr += PNG_MAGIC_SIZE;
378    size -= PNG_MAGIC_SIZE;
379    if ((err = mipng_structure(fptr, size, &hdr, &dat)))
380      {
381        warnx("mlx PNG error : %s", mipng_err[err]);
382        return ((void *)0);
383      }
384    if ((err = mipng_verif_hdr(hdr, &pi)))
385      {
386        warnx("mlx PNG error : %s", mipng_err[err]);
387        return ((void *)0);
388      }
389    if (!(img = mlx_new_image(xvar, pi.width, pi.height)))
390      {
391        warnx("mlx PNG error : Can't create mlx image");
392        return ((void *)0);
393      }
394    *width = pi.width;
395    *height = pi.height;
396    if ((err = mipng_data(img, dat, &pi)))
397      {
398        mlx_destroy_image(xvar, img);
399        warnx("mlx PNG error : %s", mipng_err[err]);
400        return ((void *)0);
401      }
402    return (img);
403  }
404  
405  
406  
407  
408  void	*mlx_png_file_to_image(void *xvar, char *file, int *width, int *height)
409  {
410    int			fd;
411    int			size;
412    unsigned char		*ptr;
413    void                  *img;
414  
415    if ((fd = open(file, O_RDONLY)) == -1 || (size = lseek(fd, 0, SEEK_END)) == -1 ||
416        (ptr = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0)) == (void *)MAP_FAILED)
417      {
418        if (fd >= 0)
419          close(fd);
420        warnx("Can't map png file '%s'", file);
421        return ((void *)0);
422      }
423    if (!(img = mlx_int_parse_png(xvar, ptr, size, width, height)))
424      {
425        *width = 0;
426        *height = 0;
427      }
428    munmap(ptr,size);
429    close(fd);
430    return (img);
431  }