#ifdef _PLAN_9 #define _RESEARCH_SOURCE #include // Plan 9 drawing procedures #include #include #ifndef _BSD_EXTENSION #define _BSD_EXTENSION #endif #include #endif // Needed, at least at first #include #include // PNG library #include 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; }