5602354e291039b08f985c84d0a4962ec65a64b3
[gbajam21] / tools / pngdump / image.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <assert.h>
6 #include <png.h>
7 #include "image.h"
8
9 int alloc_image(struct image *img, int x, int y, int bpp)
10 {
11         memset(img, 0, sizeof *img);
12         img->width = x;
13         img->height = y;
14         img->bpp = bpp;
15         img->scansz = img->pitch = x * bpp / 8;
16
17         if(!(img->pixels = malloc(y * img->scansz))) {
18                 fprintf(stderr, "failed to allocate %dx%d (%dbpp) pixel buffer\n", x, y, bpp);
19                 return -1;
20         }
21
22         /* just a guess, assume the user will fill the details, but set up reasonable
23          * defaults just in case...
24          */
25         if(bpp <= 8) {
26                 img->nchan = 1;
27                 img->cmap_ncolors = 1 << bpp;
28         } else if(bpp <= 24) {
29                 img->nchan = 3;
30         } else {
31                 img->nchan = 4;
32         }
33         return 0;
34 }
35
36 int load_image(struct image *img, const char *fname)
37 {
38         int i;
39         FILE *fp;
40         png_struct *png;
41         png_info *info;
42         int chan_bits, color_type;
43         png_uint_32 xsz, ysz;
44         png_color *palette;
45         unsigned char **scanline;
46         unsigned char *dptr;
47
48         if(!(fp = fopen(fname, "rb"))) {
49                 fprintf(stderr, "failed to open: %s: %s\n", fname, strerror(errno));
50                 return -1;
51         }
52
53         if(!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
54                 fclose(fp);
55                 return -1;
56         }
57         if(!(info = png_create_info_struct(png))) {
58                 fclose(fp);
59                 png_destroy_read_struct(&png, 0, 0);
60                 return -1;
61         }
62         if(setjmp(png_jmpbuf(png))) {
63                 fclose(fp);
64                 png_destroy_read_struct(&png, &info, 0);
65                 return -1;
66         }
67
68         png_init_io(png, fp);
69         png_read_png(png, info, 0, 0);
70
71         png_get_IHDR(png, info, &xsz, &ysz, &chan_bits, &color_type, 0, 0, 0);
72         img->width = xsz;
73         img->height = ysz;
74         img->nchan = png_get_channels(png, info);
75         img->bpp = img->nchan * chan_bits;
76         img->scansz = img->pitch = xsz * img->bpp / 8;
77         img->cmap_ncolors = 0;
78
79         if(color_type == PNG_COLOR_TYPE_PALETTE) {
80                 png_get_PLTE(png, info, &palette, &img->cmap_ncolors);
81                 memcpy(img->cmap, palette, img->cmap_ncolors * sizeof *img->cmap);
82         }
83
84         if(!(img->pixels = malloc(ysz * img->scansz))) {
85                 perror("failed to allocate pixel buffer");
86                 fclose(fp);
87                 png_destroy_read_struct(&png, &info, 0);
88                 return -1;
89         }
90         dptr = img->pixels;
91
92         scanline = (unsigned char**)png_get_rows(png, info);
93         for(i=0; i<ysz; i++) {
94                 memcpy(dptr, scanline[i], img->scansz);
95                 dptr += img->pitch;
96         }
97
98         fclose(fp);
99         png_destroy_read_struct(&png, &info, 0);
100         return 0;
101 }
102
103 int save_image(struct image *img, const char *fname)
104 {
105         int i, chan_bits, coltype;
106         FILE *fp;
107         png_struct *png;
108         png_info *info;
109         png_text txt;
110         unsigned char **scanline = 0;
111         unsigned char *pptr;
112
113         if(!(fp = fopen(fname, "wb"))) {
114                 fprintf(stderr, "save_image: failed to open: %s: %s\n", fname, strerror(errno));
115                 return -1;
116         }
117
118         if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
119                 fclose(fp);
120                 return -1;
121         }
122         if(!(info = png_create_info_struct(png))) {
123                 png_destroy_write_struct(&png, 0);
124                 fclose(fp);
125                 return -1;
126         }
127
128         txt.compression = PNG_TEXT_COMPRESSION_NONE;
129         txt.key = "Software";
130         txt.text = "pngdump";
131         txt.text_length = 0;
132
133         if(setjmp(png_jmpbuf(png))) {
134                 png_destroy_write_struct(&png, &info);
135                 free(scanline);
136                 fclose(fp);
137                 return -1;
138         }
139
140         switch(img->nchan) {
141         case 1:
142                 if(img->cmap_ncolors > 0) {
143                         coltype = PNG_COLOR_TYPE_PALETTE;
144                 } else {
145                         coltype = PNG_COLOR_TYPE_GRAY;
146                 }
147                 break;
148         case 2:
149                 coltype = PNG_COLOR_TYPE_GRAY_ALPHA;
150                 break;
151         case 3:
152                 coltype = PNG_COLOR_TYPE_RGB;
153                 break;
154         case 4:
155                 coltype = PNG_COLOR_TYPE_RGB_ALPHA;
156                 break;
157         }
158
159         chan_bits = img->bpp / img->nchan;
160         png_set_IHDR(png, info, img->width, img->height, chan_bits, coltype, PNG_INTERLACE_NONE,
161                         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
162         png_set_text(png, info, &txt, 1);
163
164         if(img->cmap_ncolors > 0) {
165                 png_set_PLTE(png, info, (png_color*)img->cmap, img->cmap_ncolors);
166         }
167
168         if(!(scanline = malloc(img->height * sizeof *scanline))) {
169                 png_destroy_write_struct(&png, &info);
170                 fclose(fp);
171                 return -1;
172         }
173
174         pptr = img->pixels;
175         for(i=0; i<img->height; i++) {
176                 scanline[i] = pptr;
177                 pptr += img->pitch;
178         }
179         png_set_rows(png, info, scanline);
180
181         png_init_io(png, fp);
182         png_write_png(png, info, 0, 0);
183         png_destroy_write_struct(&png, &info);
184         free(scanline);
185         fclose(fp);
186         return 0;
187 }
188
189
190 int cmp_image(struct image *a, struct image *b)
191 {
192         int i;
193         unsigned char *aptr = a->pixels;
194         unsigned char *bptr = b->pixels;
195
196         if(a->width != b->width || a->height != b->height || a->bpp != b->bpp || a->nchan != b->nchan) {
197                 return -1;
198         }
199
200         for(i=0; i<a->height; i++) {
201                 if(memcmp(aptr, bptr, a->scansz) != 0) {
202                         return -1;
203                 }
204                 aptr += a->pitch;
205                 bptr += b->pitch;
206         }
207         return 0;
208 }
209
210 void blit(struct image *src, int sx, int sy, int w, int h, struct image *dst, int dx, int dy)
211 {
212         int i;
213         unsigned char *sptr, *dptr;
214
215         assert(src->bpp == dst->bpp);
216         assert(src->nchan == dst->nchan);
217
218         if(sx < 0) { w += sx; sx = 0; }
219         if(sy < 0) { h += sy; sy = 0; }
220         if(dx < 0) { w += dx; sx -= dx; dx = 0; }
221         if(dy < 0) { h += dy; sy -= dy; dy = 0; }
222         if(sx + w >= src->width) w = src->width - sx;
223         if(sy + h >= src->height) h = src->height - sy;
224         if(dx + w >= dst->width) w = dst->width - dx;
225         if(dy + h >= dst->height) h = dst->height - dy;
226
227         if(w <= 0 || h <= 0) return;
228
229         sptr = src->pixels + sy * src->pitch + sx * src->bpp / 8;
230         dptr = dst->pixels + dy * dst->pitch + dx * dst->bpp / 8;
231
232         for(i=0; i<h; i++) {
233                 memcpy(dptr, sptr, w * dst->bpp / 8);
234                 dptr += dst->pitch;
235                 sptr += src->pitch;
236         }
237 }
238
239 unsigned int get_pixel(struct image *img, int x, int y)
240 {
241         unsigned int r, g, b;
242         unsigned char *pptr;
243         unsigned short *pptr16;
244         unsigned int *pptr32;
245
246         switch(img->bpp) {
247         case 4:
248                 pptr = img->pixels + y * img->pitch + x / 2;
249                 return x & 1 ? *pptr & 0xf : *pptr >> 4;
250         case 8:
251                 pptr = img->pixels + y * img->pitch + x;
252                 return *pptr;
253         case 15:
254         case 16:
255                 pptr16 = (unsigned short*)(img->pixels + y * img->pitch + x * 2);
256                 return *pptr16;
257         case 24:
258                 pptr = img->pixels + y * img->pitch + x * 3;
259                 r = pptr[0];
260                 g = pptr[1];
261                 b = pptr[2];
262                 return r | (g << 8) | (b << 16);
263         case 32:
264                 pptr32 = (unsigned int*)(img->pixels + y * img->pitch + x * 4);
265                 return *pptr32;
266
267         default:
268                 fprintf(stderr, "get_pixel not implemented for %d bpp\n", img->bpp);
269         }
270
271         return 0;
272 }
273
274 unsigned int get_pixel_rgb(struct image *img, int x, int y, unsigned int *rgb)
275 {
276         unsigned int pix = get_pixel(img, x, y);
277
278         switch(img->bpp) {
279         case 15:
280                 rgb[0] = (pix & 0x7c00) >> 7;
281                 rgb[1] = (pix & 0x03e0) >> 2;
282                 rgb[2] = (pix & 0x001f) << 3;
283                 rgb[0] |= ((rgb[0] & 8) >> 1) | ((rgb[0] & 8) >> 2) | ((rgb[0] & 8) >> 3);
284                 rgb[1] |= ((rgb[1] & 8) >> 1) | ((rgb[1] & 8) >> 2) | ((rgb[1] & 8) >> 3);
285                 rgb[2] |= ((rgb[2] & 8) >> 1) | ((rgb[2] & 8) >> 2) | ((rgb[2] & 8) >> 3);
286                 break;
287
288         case 16:
289                 rgb[0] = (pix & 0xf800) >> 8;
290                 rgb[1] = (pix & 0x07e0) >> 3;
291                 rgb[2] = (pix & 0x001f) << 3;
292                 rgb[0] |= ((rgb[0] & 8) >> 1) | ((rgb[0] & 8) >> 2) | ((rgb[0] & 8) >> 3);
293                 rgb[1] |= ((rgb[1] & 4) >> 1) | ((rgb[1] & 4) >> 2);
294                 rgb[2] |= ((rgb[2] & 8) >> 1) | ((rgb[2] & 8) >> 2) | ((rgb[2] & 8) >> 3);
295                 break;
296
297         case 24:
298         case 32:
299                 rgb[0] = pix & 0xff;
300                 rgb[1] = (pix >> 8) & 0xff;
301                 rgb[2] = (pix >> 16) & 0xff;
302                 break;
303
304         default:
305                 assert(pix >= 0 && pix < img->cmap_ncolors);
306                 rgb[0] = img->cmap[pix].r;
307                 rgb[1] = img->cmap[pix].g;
308                 rgb[2] = img->cmap[pix].b;
309         }
310
311         return pix;
312 }
313
314 void put_pixel(struct image *img, int x, int y, unsigned int pix)
315 {
316         unsigned char *pptr;
317         unsigned short *pptr16;
318
319         switch(img->bpp) {
320         case 4:
321                 pptr = img->pixels + y * img->pitch + x / 2;
322                 if(x & 1) {
323                         *pptr = (*pptr & 0xf0) | pix;
324                 } else {
325                         *pptr = (*pptr & 0xf) | (pix << 4);
326                 }
327                 break;
328
329         case 8:
330                 pptr = img->pixels + y * img->pitch + x;
331                 *pptr = pix;
332                 break;
333
334         case 16:
335                 pptr16 = (unsigned short*)(img->pixels + y * img->pitch + x * 2);
336                 *pptr16 = pix;
337                 break;
338
339         default:
340                 fprintf(stderr, "put_pixel not implemented for %d bpp\n", img->bpp);
341         }
342 }
343
344 void overlay_key(struct image *src, unsigned int key, struct image *dst)
345 {
346         int i, j;
347         unsigned int pix;
348
349         assert(src->bpp == dst->bpp);
350         assert(src->width == dst->width);
351         assert(src->height == dst->height);
352
353         for(i=0; i<dst->height; i++) {
354                 for(j=0; j<dst->width; j++) {
355                         pix = get_pixel(src, j, i);
356                         if(pix != key) {
357                                 put_pixel(dst, j, i, pix);
358                         }
359                 }
360         }
361 }
362
363 #if 0
364 /* ---- color quantization ---- */
365 struct octnode;
366
367 struct octree {
368         struct octnode *root;
369         struct octnode *levn[8];
370         int ncol, maxcol;
371 };
372
373 struct octnode {
374         struct octree *tree;
375         int r, g, b, nref;
376         int palidx;
377         int nsub;
378         struct octnode *sub[8];
379         struct octnode *next;
380 };
381
382 static void add_color(struct octree *ot, int r, int g, int b);
383 static void reduce_colors(struct octree *ot);
384 static int assign_colors(struct octnode *on, int next, struct cmapent *cmap);
385 static int lookup_color(struct octree *ot, int r, int g, int b);
386 static struct octnode *new_node(struct octree *ot, int lvl);
387 static void del_node(struct octnode *on, int lvl);
388 static void print_tree(struct octnode *n, int lvl);
389 static int count_leaves(struct octnode *n);
390
391 void quantize_image(struct image *img, int maxcol)
392 {
393         int i, j, cidx;
394         unsigned int rgb[3];
395         struct octree ot = {0};
396         struct image newimg = *img;
397
398         if(img->bpp > 8) {
399                 newimg.bpp = 8;
400                 newimg.nchan = 1;
401                 newimg.scansz = newimg.width;
402                 newimg.pitch = 8 * img->pitch / img->bpp;
403         }
404
405         ot.root = new_node(&ot, 0);
406         ot.maxcol = maxcol;
407
408         for(i=0; i<img->height; i++) {
409                 for(j=0; j<img->width; j++) {
410                         get_pixel_rgb(img, j, i, rgb);
411                         add_color(&ot, rgb[0], rgb[1], rgb[2]);
412
413                         while(count_leaves(ot.root) > ot.maxcol) {
414                         //while(ot.ncol > ot.maxcol) {
415                                 reduce_colors(&ot);
416                         }
417                 }
418         }
419
420         /* use created octree to generate the palette */
421         newimg.cmap_ncolors = assign_colors(ot.root, 0, newimg.cmap);
422
423         /* replace image pixels */
424         for(i=0; i<img->height; i++) {
425                 for(j=0; j<img->width; j++) {
426                         get_pixel_rgb(img, j, i, rgb);
427                         cidx = lookup_color(&ot, rgb[0], rgb[1], rgb[2]);
428                         assert(cidx >= 0 && cidx < maxcol);
429                         put_pixel(&newimg, j, i, cidx);
430                 }
431         }
432
433         *img = newimg;
434 }
435
436 static int subidx(int bit, int r, int g, int b)
437 {
438         assert(bit >= 0 && bit < 8);
439         bit = 7 - bit;
440         return ((r >> bit) & 1) | ((g >> (bit - 1)) & 2) | ((b >> (bit - 2)) & 4);
441 }
442
443 static int tree_height(struct octnode *on)
444 {
445         int i, subh, max = 0;
446
447         if(!on) return 0;
448
449         for(i=0; i<8; i++) {
450                 subh = tree_height(on->sub[i]);
451                 if(subh > max) max = subh;
452         }
453         return max + 1;
454 }
455
456 static void add_color(struct octree *ot, int r, int g, int b)
457 {
458         int i, idx;
459         struct octnode *on;
460
461         on = ot->root;
462         for(i=0; i<8; i++) {
463                 idx = subidx(i, r, g, b);
464
465                 if(!on->sub[idx]) {
466                         on->sub[idx] = new_node(ot, i + 1);
467                         if(i == 7) {
468                                 /* this only adds a color if the parent node was previously not
469                                  * a leaf. Otherwise the new one just takes the parent's place
470                                  */
471                                 ot->ncol++;
472                         }
473                         on->nsub++;
474                 }
475
476                 on->r += r;
477                 on->g += g;
478                 on->b += b;
479                 on->nref++;
480
481                 on = on->sub[idx];
482         }
483
484         on->r += r;
485         on->g += g;
486         on->b += b;
487         on->nref++;
488 }
489
490 static int count_nodes(struct octnode *n)
491 {
492         int count = 0;
493         while(n) {
494                 count++;
495                 n = n->next;
496         }
497         return count;
498 }
499
500 static int count_leaves(struct octnode *n)
501 {
502         int i, cnt;
503
504         if(!n) return 0;
505         if(n->nsub <= 0) return 1;
506
507         cnt = 0;
508         for(i=0; i<8; i++) {
509                 cnt += count_leaves(n->sub[i]);
510         }
511         return cnt;
512 }
513
514 static void reduce_colors(struct octree *ot)
515 {
516         int i, lvl, best_nref;
517         struct octnode *n, *best;
518
519         lvl = 8;
520         while(--lvl >= 0) {
521                 best_nref = INT_MAX;
522                 best = 0;
523                 n = ot->levn[lvl];
524
525                 while(n) {
526                         if(n->nref < best_nref && n->nsub) {
527                                 best = n;
528                                 best_nref = n->nref;
529                         }
530                         n = n->next;
531                 }
532
533                 if(best) {
534                         for(i=0; i<8; i++) {
535                                 if(best->sub[i]) {
536                                         del_node(best->sub[i], lvl + 1);
537                                         best->sub[i] = 0;
538                                 }
539                         }
540                         if(best->nsub) {
541                                 /* this wasn't previously a leaf, but now it is */
542                                 ot->ncol++;
543                                 best->nsub = 0;
544                         }
545                         break;
546                 }
547         }
548 }
549
550 static int assign_colors(struct octnode *on, int next, struct cmapent *cmap)
551 {
552         int i;
553
554         if(!on) return next;
555
556         if(on->nsub <= 0) {
557                 assert(next < on->tree->maxcol);
558                 cmap[next].r = on->r / on->nref;
559                 cmap[next].g = on->g / on->nref;
560                 cmap[next].b = on->b / on->nref;
561                 on->palidx = next++;
562         }
563
564         for(i=0; i<8; i++) {
565                 next = assign_colors(on->sub[i], next, cmap);
566         }
567         return next;
568 }
569
570 static int lookup_color(struct octree *ot, int r, int g, int b)
571 {
572         int i, idx;
573         struct octnode *on = ot->root;
574
575         for(i=0; i<8; i++) {
576                 idx = subidx(i, r, g, b);
577                 if(!on->sub[idx]) break;
578                 on = on->sub[idx];
579         }
580
581         return on->palidx;
582 }
583
584 static int have_node(struct octnode *list, struct octnode *n)
585 {
586         while(list) {
587                 if(list == n) return 1;
588                 list = list->next;
589         }
590         return 0;
591 }
592
593 static struct octnode *new_node(struct octree *ot, int lvl)
594 {
595         struct octnode *on;
596
597         if(!(on = calloc(1, sizeof *on))) {
598                 perror("failed to allocate octree node");
599                 abort();
600         }
601
602         on->tree = ot;
603         on->palidx = -1;
604
605         if(lvl < 8) {
606                 if(have_node(ot->levn[lvl], on)) {
607                         fprintf(stderr, "double-insertion!\n");
608                         abort();
609                 }
610                 on->next = ot->levn[lvl];
611                 ot->levn[lvl] = on;
612         }
613         return on;
614 }
615
616 static void del_node(struct octnode *on, int lvl)
617 {
618         int i;
619         struct octree *ot;
620         struct octnode dummy, *prev;
621
622         if(!on) return;
623         ot = on->tree;
624
625         if(!on->nsub) {
626                 ot->ncol--;     /* removing a leaf removes a color */
627         }
628
629         for(i=0; i<8; i++) {
630                 del_node(on->sub[i], lvl + 1);
631         }
632
633         if(lvl < 8) {
634                 dummy.next = ot->levn[lvl];
635                 prev = &dummy;
636
637                 while(prev->next) {
638                         if(prev->next == on) {
639                                 prev->next = on->next;
640                                 break;
641                         }
642                         prev = prev->next;
643                 }
644                 ot->levn[lvl] = dummy.next;
645         }
646
647         free(on);
648 }
649
650 static void print_tree(struct octnode *n, int lvl)
651 {
652         int i;
653
654         if(!n) return;
655
656         for(i=0; i<lvl; i++) {
657                 fputs("|  ", stdout);
658         }
659
660         printf("+-%p: <%d %d %d> #%d", (void*)n, n->r, n->g, n->b, n->nref);
661         if(n->palidx >= 0) printf(" [%d]\n", n->palidx);
662         putchar('\n');
663
664         for(i=0; i<8; i++) {
665                 print_tree(n->sub[i], lvl + 1);
666         }
667 }
668 #endif