Plan 9 from Bell Labs’s /usr/web/sources/contrib/lucio/pub/npng.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#ifdef _PLAN_9
#define	_RESEARCH_SOURCE
#include <u.h>

// Plan 9 drawing procedures
#include <draw.h>
#include <event.h>
#ifndef _BSD_EXTENSION
#define _BSD_EXTENSION
#endif
#include <bsd.h>
#endif

// Needed, at least at first
#include <stdio.h>
#include <stdlib.h>

// PNG library
#include <pub/png.h>

static int debug = 0;
static Image *image = nil;

enum {
	Border	= 2,
	Edge	= 5
};

enum {
	AGREY16	= CHAN2 (CAlpha, 8, CGrey, 8),
	XGREY16	= CHAN2 (CIgnore, 8, CGrey, 8),
};

void
sysfatal (char *fmt, ...) {
	char buf[1024];
	int out;
	va_list arg;

	out = snprintf (buf, sizeof (buf), "Fatal error: ");
	va_start(arg, fmt);
	vsnprint(buf + out, sizeof (buf) - out, fmt, arg);
	va_end(arg);
	fprintf (stderr, "%s\n", buf);
	abort ();
}

void
Debug (char *fmt, ...) {
	char buf[1024];
	va_list arg;

	if (debug > 0) {
		va_start (arg, fmt);
		vsnprint (buf, sizeof (buf), fmt, arg);
		va_end (arg);
		fprintf (stderr, "%s\n", buf);
	}
}

void
eresized (int new) {
	Rectangle r;

	if(new && getwindow (display, Refnone) < 0){
		sysfatal ("npng: can't reattach to window");
	}
	if(image == nil)
		return;
	r = insetrect (screen->clipr, Edge + Border);
	r.max.x = r.min.x + Dx (image->r);
	r.max.y = r.min.y + Dy (image->r);
	border (screen, r, -Border, nil, ZP);
	draw (screen, r, image, nil, image->r.min);
	flushimage (display, 1);
}

void
err_func (png_struct *, char *) {
}

void
wrn_func (png_struct *, char *) {
}

static char err_buf[256];

unsigned char
amult (unsigned char a, unsigned char b) {
	long pixel = a * b;
	return (pixel >> 8) & 0xFF;
}

void
c2rgbv (png_structp, png_row_infop info, png_bytep data) {
	int width = info->width;
	int rowbytes = info->rowbytes;
	int depth = rowbytes / width;
	unsigned char *dpp, *dp = data;

	dpp = data;
	while (dp < data + rowbytes) {
		if (width == 4) {
			dp[0] = amult (dp[0], dp[3]);
			dp[1] = amult (dp[1], dp[3]);
			dp[2] = amult (dp[2], dp[3]);
		}
		*dpp++ = rgb2cmap (dp[0], dp[1], dp[2]);
		dp += depth;
	}
}

void
blend (png_structp, png_row_infop info, png_bytep data) {
	int width = info->width;
	int rowbytes = info->rowbytes;
	int depth = rowbytes / width;
	unsigned char *dp = data;

	while (dp < data + rowbytes) {
		switch (depth) {
			case 4:
				dp[0] = amult (dp[0], dp[3]);
				dp[1] = amult (dp[1], dp[3]);
				dp[2] = amult (dp[2], dp[3]);
				break;
			case 2:
				dp[0] = amult (dp[0], dp[1]);
				break;
		}
		dp += depth;
	}
}

int
cspace () {
	FILE *fd = fopen("/dev/screen", "r");
	char buf[12];
	int colorspace = 0;

	if (fd != nil){
		buf[12] = '\0';
			if (fread (buf, 1, 12, fd) == 12 && chantodepth (strtochan (buf)) > 8)
				colorspace = 16;
		fclose(fd);
	}
	return colorspace;
}

int
main (int argc, char *argv[]) {
	FILE *fp = 0;
	int cflag = 0, dflag = 0, kflag = 0, nflag = 0, tflag = 0, threeflag = 0, vflag = 0;
	int	nineflag = 0;
	png_color_16 own_background = 0xffff;
	int own_alpha = -1;
	int  ch;
	char chan[10];
	unsigned long outchan;
	extern char *optarg;
	extern int optind;

	while ((ch = getopt (argc, argv, "a:cDdeknrtv39")) != -1) {
		switch (ch) {
			case 'D':
				debug++;
				break;
			case 'a':		/* add/replace alpha channel */
				if (nflag) {
					fprintf (stderr, "usage: usage: option conflict (-%c/-n)\n", ch);
					return 2;
				}
				own_alpha = atoi (optarg);
				break;
			case 'n':		/* drop alpha channel */
				if (own_alpha > -1) {
					fprintf (stderr, "usage: usage: option conflict (-%c/-a)\n", ch);
					return 2;
				}
				nflag++;
				break;
			case 'c':		/* encoded, compressed, bitmap file to stdout*/
				dflag++;
				if (nineflag) {
					fprintf (stderr, "usage: usage: option conflict (-%c/-9)\n", ch);
					return 2;
				}
				cflag++;
				break;
			case '9':		/* plan 9, uncompressed, bitmap fileto stdout */
				dflag++;
				if (cflag) {
					fprintf (stderr, "usage: usage: option conflict (-%c/-c)\n", ch);
					return 2;
				}
				nineflag++;
				break;
			case 'd':		/* suppress display of image */
				dflag++;
				break;
			case 'k':		/* force black and white */
				if (tflag || threeflag || vflag) {
					fprintf (stderr, "usage: option conflict (-%c/-[t3v])\n", ch);
					return 2;
				}
				kflag++;
				break;
			case 't':		/* force True Colour */
				if (kflag | threeflag | vflag) {
					fprintf (stderr, "usage: option conflict (-%c/-[k3v])\n", ch);
					return 2;
				}
				tflag++;
				break;
			case '3':		/* force True Colour even from greyscale */
				if (kflag | tflag | vflag) {
					fprintf (stderr, "usage: option conflict (-%c/-[ktv])\n", ch);
					return 2;
				}
				threeflag++;
				tflag++;
				break;
			case 'v':		/* force RGBV */
				if (kflag | tflag | threeflag) {
					fprintf (stderr, "usage: option conflict (-%c/-[kt3])\n", ch);
					return 2;
				}
				vflag++;
				break;
			case '?':
			default:
				fprintf (stderr, "usage: png [-39cdkntv] [-a α-val] [file.png ...]\n");
				return 2;
		}
	}
	argc -= optind;
	argv += optind;
	if (argc == 0) {
		fp = stdin;
		argc = 1;
	} else if (argc > 1 && dflag) {
		fprintf (stderr, "usage: only one image allowed\n");
		return 2;
	}

	initdraw (0, 0, 0);
	if (!dflag) {
		einit (Ekeyboard | Emouse);
	}
	while (argc > 0) {
		unsigned char *buf, *bp;
#define SIGSIZE	(4)
		unsigned char sig[SIGSIZE];
		int n, row;
		png_structp png_ptr;
		png_infop info_ptr;
		png_uint_32 width, height;
		int bit_depth, color_type, color;
		png_color_16p image_background;
		png_bytep *row_pointers;
		int row_bytes;
		Rectangle r;
		Image *m, *m2;

		if (fp == 0) {
			Debug ("Open: %s\n", *argv);
			fp = fopen (*argv, "r");
			if (fp == 0) {
				sysfatal ("Image file error: %r");
			}
		}
		n = fread (sig, 1, sizeof (sig), fp);
		if (n != sizeof (sig)) {
			sysfatal ("Not a PNG image: too small: %d", n);
		}
		n = png_sig_cmp (sig, (png_size_t) 0, sizeof (sig));
		if (n != 0) {
			char *strsig = (char *) malloc (sizeof (sig) * 3);
			int x;

			for (x = 0, n = 0; x < sizeof (sig); x++) {
				n += sprintf (strsig + n, " %02x", sig[n]);
			}
			sysfatal ("Not a PNG image: wrong signature:%s", strsig);
		}

		png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, (png_voidp) err_buf, err_func, wrn_func);
		if (png_ptr == nil) {
			fclose (fp);
			sysfatal ("PNG initialisation failed");
		}
		png_init_io (png_ptr, fp);	// png_set_read_fn (png_ptr, (void *) user_io_ptr, user_read_fn);
		png_set_sig_bytes (png_ptr, 4);
		info_ptr = png_create_info_struct (png_ptr);
		png_read_info (png_ptr, info_ptr);
	
		png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, (int *) 0, (int *) 0, (int *) 0);
		row_bytes = png_get_rowbytes (png_ptr, info_ptr);
		Debug ("PNG image details:");
		Debug ("\tDim: %d x %d x %d", width, height, bit_depth);
		Debug ("\tColor: %d", color_type);
		Debug ("\tRow bytes: %d(%d)", row_bytes, width);
		Debug ("--");
		if (bit_depth == 16) {
			png_set_strip_16 (png_ptr);
			Debug ("png_set_strip_16");
		} else if (bit_depth < 8) {
			if (color_type == PNG_COLOR_TYPE_GRAY) {
				png_set_gray_1_2_4_to_8 (png_ptr);
				Debug ("png_set_gray_1_2_4_to_8");
			} else {
				png_set_packing (png_ptr);
				Debug ("png_set_packing");
			}
		}

		color = color_type & ~PNG_COLOR_MASK_ALPHA;
		if (color == PNG_COLOR_TYPE_PALETTE) {
			png_set_palette_to_rgb (png_ptr);
			Debug ("png_set_palette_to_rgb");
			color = PNG_COLOR_MASK_COLOR;
			if (png_get_bKGD (png_ptr, info_ptr, &image_background)) {
				png_set_background (png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
				Debug ("png_set_background(IMAGE)");
			} else {
				png_set_background (png_ptr, &own_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
				Debug ("png_set_background(OWN)");
			}
		} else if (color == PNG_COLOR_TYPE_GRAY) {
			if (threeflag) {
				png_set_gray_to_rgb (png_ptr);
				Debug ("png_set_gray_to_rgb");
				color = PNG_COLOR_MASK_COLOR;
			}
		}
		if (color == PNG_COLOR_TYPE_RGB) {
			if (kflag) {
				png_set_rgb_to_gray_fixed (png_ptr, 1, -1, -1);
				color = PNG_COLOR_TYPE_GRAY;
			}
		}
		if (nflag && (color_type & PNG_COLOR_MASK_ALPHA) == PNG_COLOR_MASK_ALPHA) {
			png_set_strip_alpha (png_ptr);
			Debug ("png_set_strip_alpha");
			color_type &= ~PNG_COLOR_MASK_ALPHA;
		}
		if (own_alpha > -1) {
			if ((color_type & PNG_COLOR_MASK_ALPHA) == PNG_COLOR_MASK_ALPHA) {
				png_set_strip_alpha (png_ptr);
				Debug ("png_set_strip_alpha");
			}
			png_set_add_alpha (png_ptr, own_alpha & 0xFF, PNG_FILLER_AFTER);
			Debug ("png_set_add_alpha %d (AFTER)", own_alpha);
			color_type |= PNG_COLOR_MASK_ALPHA;
		}
		if (color == PNG_COLOR_MASK_COLOR) {
			png_set_bgr (png_ptr);
			Debug ("png_set_bgr");
		}
		if (vflag) {
			png_set_read_user_transform_fn (png_ptr, c2rgbv);
			Debug ("png_set_read_user_transform_fn(c2rgbv)");
			png_set_user_transform_info (png_ptr, (void *) 0, 8, 1);
			color = PNG_COLOR_MASK_PALETTE;
		} else if ((color_type & PNG_COLOR_MASK_ALPHA) == PNG_COLOR_MASK_ALPHA) {
				png_set_read_user_transform_fn (png_ptr, blend);
				Debug ("png_set_read_user_transform_fn(blend)");
		}
		png_read_update_info (png_ptr, info_ptr);
		row_pointers = (png_bytep *) malloc (sizeof (png_bytep) * height);
		row_bytes = png_get_rowbytes (png_ptr, info_ptr);
		Debug ("H: %d, W: %d, RB: %d\n", height, width, row_bytes);
		bp = buf = (unsigned char *) malloc (height * row_bytes);
		for (row = 0; row < height; row++) {
			row_pointers[row] = bp;
			bp += row_bytes;
		}
		png_read_image (png_ptr, row_pointers);

		if (color == PNG_COLOR_MASK_COLOR) {
			outchan = RGB24;
			if ((color_type & PNG_COLOR_MASK_ALPHA) == PNG_COLOR_MASK_ALPHA) {
				outchan = ARGB32;
			}
		} else if (color == PNG_COLOR_MASK_PALETTE) {
			row_bytes = width;
			outchan = CMAP8;
		} else {
			outchan = GREY8;
			if ((color_type & PNG_COLOR_MASK_ALPHA) == PNG_COLOR_MASK_ALPHA) {
				outchan = AGREY16;
			}
		}
		chantostr ((char *) chan, outchan);
		chan[9] = '\0';
		Debug ("Chan: %s", chan);
		r.min = Pt (0, 0);
		r.max.x = width;
		r.max.y = height;
		if (!dflag) {		// display requested
			if ((m = allocimage (display, r, outchan, 0, DWhite)) == 0) {
				sysfatal ("No image");
			}
			if (loadimage (m, m->r, buf, height * row_bytes) < 0) {
				sysfatal ("Colour Image load failed: %r");
			}
			if ((m2 = allocimage (display, r, outchan, 0, DWhite)) == 0) {
				sysfatal ("No backup image");
			}
			draw (m2, m2->r, display->white, nil, ZP);
			draw (m2, m2->r, m, nil, m->r.min);
			image = m2;
			eresized(0);
			if ((ch = ekbd()) == 'q' || ch == 0x7F || ch == 0x04)
				return 0;
			draw (screen, screen->clipr, display->white, nil, ZP);
			image = nil;
			freeimage (m);
			freeimage (m2);
		} else if (nineflag) {
			printf ("%11s %11d %11d %11d %11d ", chan, 0, 0, width, height);
			for (row = 0; row < height; ++row) {
				if (fwrite (row_pointers[row], 1, row_bytes, stdout) != row_bytes) {
					sysfatal ("Write error: %r");
				}
			}
		} else if (cflag){
			if ((m = allocimage (display, r, outchan, 0, DWhite)) == 0) {
				sysfatal ("No image");
			}
			if (loadimage (m, m->r, buf, height * row_bytes) < 0) {
				sysfatal ("Colour image load failed: %r");
			}
			if (writeimage (1, m, 0) < 0) {
				sysfatal ("Write image error: %r");
			}
			freeimage (m);
		}
		draw (screen, screen->clipr, display->transparent, nil, ZP);
		free (row_pointers);
		free (buf);
		fclose (fp);
		fp = 0;
		++argv;
		--argc;
	}
	return 0;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.