9 int alloc_image(struct image *img, int x, int y, int bpp)
11 memset(img, 0, sizeof *img);
15 img->scansz = img->pitch = x * bpp / 8;
17 if(!(img->pixels = malloc(y * img->scansz))) {
18 fprintf(stderr, "failed to allocate %dx%d (%dbpp) pixel buffer\n", x, y, bpp);
22 /* just a guess, assume the user will fill the details, but set up reasonable
23 * defaults just in case...
27 img->cmap_ncolors = 1 << bpp;
28 } else if(bpp <= 24) {
36 int load_image(struct image *img, const char *fname)
42 int chan_bits, color_type;
45 unsigned char **scanline;
48 if(!(fp = fopen(fname, "rb"))) {
49 fprintf(stderr, "failed to open: %s: %s\n", fname, strerror(errno));
53 if(!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
57 if(!(info = png_create_info_struct(png))) {
59 png_destroy_read_struct(&png, 0, 0);
62 if(setjmp(png_jmpbuf(png))) {
64 png_destroy_read_struct(&png, &info, 0);
69 png_read_png(png, info, 0, 0);
71 png_get_IHDR(png, info, &xsz, &ysz, &chan_bits, &color_type, 0, 0, 0);
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;
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);
84 if(!(img->pixels = malloc(ysz * img->scansz))) {
85 perror("failed to allocate pixel buffer");
87 png_destroy_read_struct(&png, &info, 0);
92 scanline = (unsigned char**)png_get_rows(png, info);
93 for(i=0; i<ysz; i++) {
94 memcpy(dptr, scanline[i], img->scansz);
99 png_destroy_read_struct(&png, &info, 0);
103 int save_image(struct image *img, const char *fname)
105 int i, chan_bits, coltype;
110 unsigned char **scanline = 0;
113 if(!(fp = fopen(fname, "wb"))) {
114 fprintf(stderr, "save_image: failed to open: %s: %s\n", fname, strerror(errno));
118 if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
122 if(!(info = png_create_info_struct(png))) {
123 png_destroy_write_struct(&png, 0);
128 txt.compression = PNG_TEXT_COMPRESSION_NONE;
129 txt.key = "Software";
130 txt.text = "pngdump";
133 if(setjmp(png_jmpbuf(png))) {
134 png_destroy_write_struct(&png, &info);
142 if(img->cmap_ncolors > 0) {
143 coltype = PNG_COLOR_TYPE_PALETTE;
145 coltype = PNG_COLOR_TYPE_GRAY;
149 coltype = PNG_COLOR_TYPE_GRAY_ALPHA;
152 coltype = PNG_COLOR_TYPE_RGB;
155 coltype = PNG_COLOR_TYPE_RGB_ALPHA;
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);
164 if(img->cmap_ncolors > 0) {
165 png_set_PLTE(png, info, (png_color*)img->cmap, img->cmap_ncolors);
168 if(!(scanline = malloc(img->height * sizeof *scanline))) {
169 png_destroy_write_struct(&png, &info);
175 for(i=0; i<img->height; i++) {
179 png_set_rows(png, info, scanline);
181 png_init_io(png, fp);
182 png_write_png(png, info, 0, 0);
183 png_destroy_write_struct(&png, &info);
190 int cmp_image(struct image *a, struct image *b)
193 unsigned char *aptr = a->pixels;
194 unsigned char *bptr = b->pixels;
196 if(a->width != b->width || a->height != b->height || a->bpp != b->bpp || a->nchan != b->nchan) {
200 for(i=0; i<a->height; i++) {
201 if(memcmp(aptr, bptr, a->scansz) != 0) {
210 void blit(struct image *src, int sx, int sy, int w, int h, struct image *dst, int dx, int dy)
213 unsigned char *sptr, *dptr;
215 assert(src->bpp == dst->bpp);
216 assert(src->nchan == dst->nchan);
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;
227 if(w <= 0 || h <= 0) return;
229 sptr = src->pixels + sy * src->pitch + sx * src->bpp / 8;
230 dptr = dst->pixels + dy * dst->pitch + dx * dst->bpp / 8;
233 memcpy(dptr, sptr, w * dst->bpp / 8);
239 unsigned int get_pixel(struct image *img, int x, int y)
241 unsigned int r, g, b;
243 unsigned short *pptr16;
244 unsigned int *pptr32;
248 pptr = img->pixels + y * img->pitch + x / 2;
249 return x & 1 ? *pptr & 0xf : *pptr >> 4;
251 pptr = img->pixels + y * img->pitch + x;
255 pptr16 = (unsigned short*)(img->pixels + y * img->pitch + x * 2);
258 pptr = img->pixels + y * img->pitch + x * 3;
262 return r | (g << 8) | (b << 16);
264 pptr32 = (unsigned int*)(img->pixels + y * img->pitch + x * 4);
268 fprintf(stderr, "get_pixel not implemented for %d bpp\n", img->bpp);
274 unsigned int get_pixel_rgb(struct image *img, int x, int y, unsigned int *rgb)
276 unsigned int pix = get_pixel(img, x, y);
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);
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);
300 rgb[1] = (pix >> 8) & 0xff;
301 rgb[2] = (pix >> 16) & 0xff;
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;
314 void put_pixel(struct image *img, int x, int y, unsigned int pix)
317 unsigned short *pptr16;
321 pptr = img->pixels + y * img->pitch + x / 2;
323 *pptr = (*pptr & 0xf0) | pix;
325 *pptr = (*pptr & 0xf) | (pix << 4);
330 pptr = img->pixels + y * img->pitch + x;
335 pptr16 = (unsigned short*)(img->pixels + y * img->pitch + x * 2);
340 fprintf(stderr, "put_pixel not implemented for %d bpp\n", img->bpp);
344 void overlay_key(struct image *src, unsigned int key, struct image *dst)
349 assert(src->bpp == dst->bpp);
350 assert(src->width == dst->width);
351 assert(src->height == dst->height);
353 for(i=0; i<dst->height; i++) {
354 for(j=0; j<dst->width; j++) {
355 pix = get_pixel(src, j, i);
357 put_pixel(dst, j, i, pix);
364 /* ---- color quantization ---- */
368 struct octnode *root;
369 struct octnode *levn[8];
378 struct octnode *sub[8];
379 struct octnode *next;
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);
391 void quantize_image(struct image *img, int maxcol)
395 struct octree ot = {0};
396 struct image newimg = *img;
401 newimg.scansz = newimg.width;
402 newimg.pitch = 8 * img->pitch / img->bpp;
405 ot.root = new_node(&ot, 0);
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]);
413 while(count_leaves(ot.root) > ot.maxcol) {
414 //while(ot.ncol > ot.maxcol) {
420 /* use created octree to generate the palette */
421 newimg.cmap_ncolors = assign_colors(ot.root, 0, newimg.cmap);
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);
436 static int subidx(int bit, int r, int g, int b)
438 assert(bit >= 0 && bit < 8);
440 return ((r >> bit) & 1) | ((g >> (bit - 1)) & 2) | ((b >> (bit - 2)) & 4);
443 static int tree_height(struct octnode *on)
445 int i, subh, max = 0;
450 subh = tree_height(on->sub[i]);
451 if(subh > max) max = subh;
456 static void add_color(struct octree *ot, int r, int g, int b)
463 idx = subidx(i, r, g, b);
466 on->sub[idx] = new_node(ot, i + 1);
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
490 static int count_nodes(struct octnode *n)
500 static int count_leaves(struct octnode *n)
505 if(n->nsub <= 0) return 1;
509 cnt += count_leaves(n->sub[i]);
514 static void reduce_colors(struct octree *ot)
516 int i, lvl, best_nref;
517 struct octnode *n, *best;
526 if(n->nref < best_nref && n->nsub) {
536 del_node(best->sub[i], lvl + 1);
541 /* this wasn't previously a leaf, but now it is */
550 static int assign_colors(struct octnode *on, int next, struct cmapent *cmap)
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;
565 next = assign_colors(on->sub[i], next, cmap);
570 static int lookup_color(struct octree *ot, int r, int g, int b)
573 struct octnode *on = ot->root;
576 idx = subidx(i, r, g, b);
577 if(!on->sub[idx]) break;
584 static int have_node(struct octnode *list, struct octnode *n)
587 if(list == n) return 1;
593 static struct octnode *new_node(struct octree *ot, int lvl)
597 if(!(on = calloc(1, sizeof *on))) {
598 perror("failed to allocate octree node");
606 if(have_node(ot->levn[lvl], on)) {
607 fprintf(stderr, "double-insertion!\n");
610 on->next = ot->levn[lvl];
616 static void del_node(struct octnode *on, int lvl)
620 struct octnode dummy, *prev;
626 ot->ncol--; /* removing a leaf removes a color */
630 del_node(on->sub[i], lvl + 1);
634 dummy.next = ot->levn[lvl];
638 if(prev->next == on) {
639 prev->next = on->next;
644 ot->levn[lvl] = dummy.next;
650 static void print_tree(struct octnode *n, int lvl)
656 for(i=0; i<lvl; i++) {
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);
665 print_tree(n->sub[i], lvl + 1);