#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;
}
|