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 }