diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
commit | 1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch) | |
tree | 64c8c96cf54d8718847339a403e5e67b922e8c3f /converter/ppm/picttoppm.c | |
download | netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip |
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/ppm/picttoppm.c')
-rw-r--r-- | converter/ppm/picttoppm.c | 3788 |
1 files changed, 3788 insertions, 0 deletions
diff --git a/converter/ppm/picttoppm.c b/converter/ppm/picttoppm.c new file mode 100644 index 00000000..cfc5760e --- /dev/null +++ b/converter/ppm/picttoppm.c @@ -0,0 +1,3788 @@ +/* + * picttoppm.c -- convert a MacIntosh PICT file to PPM format. + * + * Copyright 1989,1992,1993 George Phillips + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + * + * George Phillips <phillips@cs.ubc.ca> + * Department of Computer Science + * University of British Columbia + * + * + * 2003-02: Handling for DirectBitsRgn opcode (0x9b) added by + * kabe@sra-tohoku.co.jp. + * + * 2004-03-27: Several bugs fixed by Steve Summit, scs@eskimo.com. + * + */ + +#define _XOPEN_SOURCE + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "pm_c_util.h" +#include "ppm.h" +#include "pbmfont.h" +#include "mallocvar.h" +#include "nstring.h" + + +/* + * Typical byte, 2 byte and 4 byte integers. + */ +typedef unsigned char byte; +typedef char signed_byte; +typedef unsigned short word; +typedef unsigned long longword; + + +/* + * Data structures for QuickDraw (and hence PICT) stuff. + */ + +struct Rect { + word top; + word left; + word bottom; + word right; +}; + +struct pixMap { + struct Rect Bounds; + word version; + word packType; + longword packSize; + longword hRes; + longword vRes; + word pixelType; + word pixelSize; + word cmpCount; + word cmpSize; + longword planeBytes; + longword pmTable; + longword pmReserved; +}; + +struct RGBColor { + word red; + word grn; + word blu; +}; + +struct Point { + word x; + word y; +}; + +struct Pattern { + byte pix[64]; +}; + +struct rgbPlanes { + word * red; + word * grn; + word * blu; +}; + +typedef void (*transfer_func) (struct RGBColor* src, struct RGBColor* dst); + +static const char* stage; +static struct Rect picFrame; +static word* red; +static word* green; +static word* blue; +static word rowlen; +static word collen; +static longword planelen; +static int verbose; +static int fullres; +static int recognize_comment; + +static struct RGBColor black = { 0, 0, 0 }; +static struct RGBColor white = { 0xffff, 0xffff, 0xffff }; + +/* various bits of drawing state */ +static struct RGBColor foreground = { 0, 0, 0 }; +static struct RGBColor background = { 0xffff, 0xffff, 0xffff }; +static struct RGBColor op_color; +static struct Pattern bkpat; +static struct Pattern fillpat; +static struct Rect clip_rect; +static struct Rect cur_rect; +static struct Point current; +static struct Pattern pen_pat; +static word pen_width; +static word pen_height; +static word pen_mode; +static transfer_func pen_trf; +static word text_font; +static byte text_face; +static word text_mode; +static transfer_func text_trf; +static word text_size; +static struct font* tfont; + +/* state for magic printer comments */ +static int ps_text; +static byte ps_just; +static byte ps_flip; +static word ps_rotation; +static byte ps_linespace; +static int ps_cent_x; +static int ps_cent_y; +static int ps_cent_set; + +struct opdef { + const char* name; + int len; + void (*impl) (int); + const char* description; +}; + +struct raster { +/*---------------------------------------------------------------------------- + An image raster. May be either truecolor or paletted. + + This is an array of pixels in row-major order, with 'rowSize' + bytes per row, 'rowCount' high. + + Within a row, pixels go left to right. The rows go top to bottom. + + Each pixel is either a palette index or an RGB triple, depending on + the format of the associated PICT. + + Each pixel is one byte if the associated PICT has 8 or fewer bits + per pixel. If the associated PICT has 16 or 32 bits per pixel, an + element herein is 2 or 4 bytes, respectively. + + For 16 bits per pixel, the two bytes for each pixel encode RGB values + as described in decode16(). + + For 32 bits per pixel, each row is divided into 4 planes. Red, + green, blue, and something else, in that order. The format of a + plane is one byte per pixel, left to right. +-----------------------------------------------------------------------------*/ + unsigned char * bytes; /* malloc'ed */ + unsigned int rowSize; + unsigned int rowCount; +}; + + +static void +allocateRaster(struct raster * const rasterP, + unsigned int const width, + unsigned int const height, + unsigned int const bitsPerPixel) { + + if (width > UINT_MAX/4) + pm_error("Width %u pixels too large for arithmetic", width); + + rasterP->rowCount = height; + + switch (bitsPerPixel) { + case 32: + /* TODO: I'm still trying to figure out this format. + + My theory today: + The row data is in plane order (a row consists of red + plane, then, green, then blue, then some 4th plane). + + The old hack code said 3 bytes per pixel here, and could get + away with it because it never got to decoding the 4th plane. + + But the new clean code needs to tell it like it is and allocate + 4 bytes per pixel. If we say 3 bytes per pixel here, we get an + "invalid PICT" error because the image actually contains 4 + bytes per pixel and as we decompress it, we run out of place + to put the data. + + We have yet to see if we can properly interpret the data. + */ + + rasterP->rowSize = width * 4; + break; + case 16: + rasterP->rowSize = width * 2; + break; + case 8: + case 4: + case 2: + case 1: + rasterP->rowSize = width * 1; + break; + default: + pm_error("INTERNAL ERROR: impossible bitsPerPixel value in " + "unpackbits(): %u", bitsPerPixel); + } + if (UINT_MAX / rasterP->rowSize < rasterP->rowCount) + pm_error("Arithmetic overflow computing size of %u x %u pixel " + "array.", rasterP->rowSize, rasterP->rowCount); + + MALLOCARRAY(rasterP->bytes, rasterP->rowSize * rasterP->rowCount); + if (rasterP->bytes == NULL) + pm_error("unable to get memory for %u x %u pixel packbits rectangle", + width, height); +} + + +static void +freeRaster(struct raster const raster) { + + free(raster.bytes); +} + + +struct blit_info { + struct Rect srcRect; + struct Rect srcBounds; + struct raster srcplane; + int pixSize; + struct Rect dstRect; + struct RGBColor * color_map; + int mode; + struct blit_info * next; +}; + +static struct blit_info* blit_list = 0; +static struct blit_info** last_bl = &blit_list; + +#define WORD_LEN (-1) + +/* + * a table of the first 194(?) opcodes. The table is too empty. + * + * Probably could use an entry specifying if the opcode is valid in version + * 1, etc. + */ + +/* for reserved opcodes of known length */ +#define res(length) \ +{ "reserved", (length), NULL, "reserved for Apple use" } + +/* for reserved opcodes of length determined by a function */ +#define resf(skipfunction) \ +{ "reserved", NA, (skipfunction), "reserved for Apple use" } + +/* seems like RGB colors are 6 bytes, but Apple says they're variable */ +/* I'll use 6 for now as I don't care that much. */ +#define RGB_LEN (6) + + +static FILE* ifp; +static int align = 0; + + + +static byte +read_byte(void) { + int c; + + if ((c = fgetc(ifp)) == EOF) + pm_error("EOF / read error while %s", stage); + + ++align; + return c & 255; +} + + + +static word +read_word(void) { + byte b; + + b = read_byte(); + + return (b << 8) | read_byte(); +} + + + +static void read_point(struct Point * const p) { + p->y = read_word(); + p->x = read_word(); +} + + + +static longword +read_long(void) { + word i; + + i = read_word(); + return (i << 16) | read_word(); +} + + + +static signed_byte +read_signed_byte(void) { + return (signed_byte)read_byte(); +} + + + +static void +read_short_point(struct Point * const p) { + p->x = read_signed_byte(); + p->y = read_signed_byte(); +} + + + +static void +skip(int const byteCount) { + static byte buf[1024]; + int n; + + align += byteCount; + + for (n = byteCount; n > 0; n -= 1024) + if (fread(buf, n > 1024 ? 1024 : n, 1, ifp) != 1) + pm_error("EOF / read error while %s", stage); +} + + + +struct const_name { + int value; + const char * name; +}; + +struct const_name const transfer_name[] = { + { 0, "srcCopy" }, + { 1, "srcOr" }, + { 2, "srcXor" }, + { 3, "srcBic" }, + { 4, "notSrcCopy" }, + { 5, "notSrcOr" }, + { 6, "notSrcXor" }, + { 7, "notSrcBic" }, + { 32, "blend" }, + { 33, "addPin" }, + { 34, "addOver" }, + { 35, "subPin" }, + { 36, "transparent" }, + { 37, "adMax" }, + { 38, "subOver" }, + { 39, "adMin" }, + { -1, 0 } +}; + +struct const_name font_name[] = { + { 0, "systemFont" }, + { 1, "applFont" }, + { 2, "newYork" }, + { 3, "geneva" }, + { 4, "monaco" }, + { 5, "venice" }, + { 6, "london" }, + { 7, "athens" }, + { 8, "sanFran" }, + { 9, "toronto" }, + { 11, "cairo" }, + { 12, "losAngeles" }, + { 20, "times" }, + { 21, "helvetica" }, + { 22, "courier" }, + { 23, "symbol" }, + { 24, "taliesin" }, + { -1, 0 } +}; + +struct const_name ps_just_name[] = { + { 0, "no" }, + { 1, "left" }, + { 2, "center" }, + { 3, "right" }, + { 4, "full" }, + { -1, 0 } +}; + +struct const_name ps_flip_name[] = { + { 0, "no" }, + { 1, "horizontal" }, + { 2, "vertical" }, + { -1, 0 } +}; + + + +static const char* +const_name(const struct const_name * const table, + unsigned int const ct) { + + static char numbuf[32]; + + unsigned int i; + + for (i = 0; table[i].name; ++i) + if (table[i].value == ct) + return table[i].name; + + sprintf(numbuf, "? (%u)", ct); + return numbuf; +} + + + +static void +picComment(word const type, + int const length) { + + unsigned int remainingLength; + + switch (type) { + case 150: + if (verbose) pm_message("TextBegin"); + if (length >= 6) { + ps_just = read_byte(); + ps_flip = read_byte(); + ps_rotation = read_word(); + ps_linespace = read_byte(); + remainingLength = length - 5; + if (recognize_comment) + ps_text = 1; + ps_cent_set = 0; + if (verbose) { + pm_message("%s justification, %s flip, %d degree rotation, " + "%d/2 linespacing", + const_name(ps_just_name, ps_just), + const_name(ps_flip_name, ps_flip), + ps_rotation, ps_linespace); + } + } else + remainingLength = length; + break; + case 151: + if (verbose) pm_message("TextEnd"); + ps_text = 0; + remainingLength = length; + break; + case 152: + if (verbose) pm_message("StringBegin"); + remainingLength = length; + break; + case 153: + if (verbose) pm_message("StringEnd"); + remainingLength = length; + break; + case 154: + if (verbose) pm_message("TextCenter"); + if (length < 8) + remainingLength = length; + else { + ps_cent_y = read_word(); + if (ps_cent_y > 32767) + ps_cent_y -= 65536; + skip(2); /* ignore fractional part */ + ps_cent_x = read_word(); + if (ps_cent_x > 32767) + ps_cent_x -= 65536; + skip(2); /* ignore fractional part */ + remainingLength = length - 8; + if (verbose) + pm_message("offset %d %d", ps_cent_x, ps_cent_y); + } + break; + case 155: + if (verbose) pm_message("LineLayoutOff"); + remainingLength = length; + break; + case 156: + if (verbose) pm_message("LineLayoutOn"); + remainingLength = length; + break; + case 160: + if (verbose) pm_message("PolyBegin"); + remainingLength = length; + break; + case 161: + if (verbose) pm_message("PolyEnd"); + remainingLength = length; + break; + case 163: + if (verbose) pm_message("PolyIgnore"); + remainingLength = length; + break; + case 164: + if (verbose) pm_message("PolySmooth"); + remainingLength = length; + break; + case 165: + if (verbose) pm_message("picPlyClo"); + remainingLength = length; + break; + case 180: + if (verbose) pm_message("DashedLine"); + remainingLength = length; + break; + case 181: + if (verbose) pm_message("DashedStop"); + remainingLength = length; + break; + case 182: + if (verbose) pm_message("SetLineWidth"); + remainingLength = length; + break; + case 190: + if (verbose) pm_message("PostScriptBegin"); + remainingLength = length; + break; + case 191: + if (verbose) pm_message("PostScriptEnd"); + remainingLength = length; + break; + case 192: + if (verbose) pm_message("PostScriptHandle"); + remainingLength = length; + break; + case 193: + if (verbose) pm_message("PostScriptFile"); + remainingLength = length; + break; + case 194: + if (verbose) pm_message("TextIsPostScript"); + remainingLength = length; + break; + case 195: + if (verbose) pm_message("ResourcePS"); + remainingLength = length; + break; + case 200: + if (verbose) pm_message("RotateBegin"); + remainingLength = length; + break; + case 201: + if (verbose) pm_message("RotateEnd"); + remainingLength = length; + break; + case 202: + if (verbose) pm_message("RotateCenter"); + remainingLength = length; + break; + case 210: + if (verbose) pm_message("FormsPrinting"); + remainingLength = length; + break; + case 211: + if (verbose) pm_message("EndFormsPrinting"); + remainingLength = length; + break; + default: + if (verbose) pm_message("%d", type); + remainingLength = length; + break; + } + if (remainingLength > 0) + skip(remainingLength); +} + + + +static void +ShortComment(int const version) { + picComment(read_word(), 0); +} + + + +static void +LongComment(int const version) { + word type; + + type = read_word(); + picComment(type, read_word()); +} + + + +static void +skip_poly_or_region(int const version) { + stage = "skipping polygon or region"; + skip(read_word() - 2); +} + + +#define NA (0) + +#define FNT_BOLD (1) +#define FNT_ITALIC (2) +#define FNT_ULINE (4) +#define FNT_OUTLINE (8) +#define FNT_SHADOW (16) +#define FNT_CONDENSE (32) +#define FNT_EXTEND (64) + +/* Some font searching routines */ + +struct fontinfo { + int font; + int size; + int style; + char* filename; + struct font* loaded; + struct fontinfo* next; +}; + +static struct fontinfo* fontlist = 0; +static struct fontinfo** fontlist_ins = &fontlist; + + + +static int +load_fontdir(const char * const dirfile) { +/*---------------------------------------------------------------------------- + Load the font directory from file named 'dirfile'. Add its contents + to the global list of fonts 'fontlist'. +-----------------------------------------------------------------------------*/ + FILE* fp; + int n, nfont; + char* arg[5], line[1024]; + struct fontinfo* fontinfo; + + if (!(fp = fopen(dirfile, "rb"))) + return -1; + + nfont = 0; + while (fgets(line, 1024, fp)) { + if ((n = mk_argvn(line, arg, 5)) == 0 || arg[0][0] == '#') + continue; + if (n != 4) + continue; + MALLOCVAR(fontinfo); + if (fontinfo == NULL) + pm_error("out of memory for font information"); + MALLOCARRAY(fontinfo->filename, strlen(arg[3] + 1)); + if (fontinfo->filename == NULL) + pm_error("out of memory for font information file name"); + + fontinfo->font = atoi(arg[0]); + fontinfo->size = atoi(arg[1]); + fontinfo->style = atoi(arg[2]); + strcpy(fontinfo->filename, arg[3]); + fontinfo->loaded = 0; + + fontinfo->next = 0; + *fontlist_ins = fontinfo; + fontlist_ins = &fontinfo->next; + nfont++; + } + + return nfont; +} + + + +static void +read_rect(struct Rect * const r) { + r->top = read_word(); + r->left = read_word(); + r->bottom = read_word(); + r->right = read_word(); +} + + + +static void +dumpRect(const char * const label, + struct Rect const rectangle) { + + pm_message("%s (%u,%u) (%u,%u)", + label, + rectangle.left, rectangle.top, + rectangle.right, rectangle.bottom); +} + + + +static int +rectwidth(const struct Rect * const r) { + return r->right - r->left; +} + + + +static int +rectheight(const struct Rect * const r) { + return r->bottom - r->top; +} + + + +static bool +rectsamesize(const struct Rect * const r1, + const struct Rect * const r2) { + return r1->right - r1->left == r2->right - r2->left && + r1->bottom - r1->top == r2->bottom - r2->top ; +} + + + +static void +rectinter(struct Rect const r1, + struct Rect const r2, + struct Rect * const intersectionP) { + + intersectionP->left = MAX(r1.left, r2.left); + intersectionP->top = MAX(r1.top, r2.top); + intersectionP->right = MIN(r1.right, r2.right); + intersectionP->bottom = MIN(r1.bottom, r2.bottom); +} + + + +static void +rectscale(struct Rect * const r, + double const xscale, + double const yscale) { + r->left *= xscale; + r->right *= xscale; + r->top *= yscale; + r->bottom *= yscale; +} + + + +static struct blit_info* +add_blit_list(void) { + + struct blit_info * bi; + + MALLOCVAR(bi); + if (bi == NULL) + pm_error("out of memory for blit list"); + + bi->next = 0; + *last_bl = bi; + last_bl = &bi->next; + + return bi; +} + + + +/* Various transfer functions for blits. + * + * Note src[Not]{Or,Xor,Copy} only work if the source pixmap was originally + * a bitmap. + * There's also a small bug that the foreground and background colors + * are not used in a srcCopy; this wouldn't be hard to fix. + * It IS a problem since the foreground and background colors CAN be changed. + */ + +static bool +rgbAllSame(const struct RGBColor * const colorP, + unsigned int const value) { + + return (colorP->red == value && + colorP->grn == value && + colorP->blu == value); +} + + +static bool +rgbIsWhite(const struct RGBColor * const colorP) { + + return rgbAllSame(colorP, 0xffff); +} + +static bool +rgbIsBlack(const struct RGBColor * const colorP) { + + return rgbAllSame(colorP, 0); +} + + +static void +srcCopy(struct RGBColor * const src, + struct RGBColor * const dst) { + + if (rgbIsBlack(src)) + *dst = foreground; + else + *dst = background; +} + + + +static void +srcOr(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsBlack(src)) + *dst = foreground; +} + + + +static void +srcXor(struct RGBColor * const src, + struct RGBColor * const dst) { + dst->red ^= ~src->red; + dst->grn ^= ~src->grn; + dst->blu ^= ~src->blu; +} + + + +static void +srcBic(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsBlack(src)) + *dst = background; +} + + + +static void +notSrcCopy(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsWhite(src)) + *dst = foreground; + else if (rgbIsBlack(src)) + *dst = background; +} + + + +static void +notSrcOr(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsWhite(src)) + *dst = foreground; +} + + + +static void +notSrcBic(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsWhite(src)) + *dst = background; +} + + + +static void +notSrcXor(struct RGBColor * const src, + struct RGBColor * const dst) { + dst->red ^= src->red; + dst->grn ^= src->grn; + dst->blu ^= src->blu; +} + + + +static void +addOver(struct RGBColor * const src, + struct RGBColor * const dst) { + dst->red += src->red; + dst->grn += src->grn; + dst->blu += src->blu; +} + + + +static void +addPin(struct RGBColor * const src, + struct RGBColor * const dst) { + if ((long)dst->red + (long)src->red > (long)op_color.red) + dst->red = op_color.red; + else + dst->red = dst->red + src->red; + + if ((long)dst->grn + (long)src->grn > (long)op_color.grn) + dst->grn = op_color.grn; + else + dst->grn = dst->grn + src->grn; + + if ((long)dst->blu + (long)src->blu > (long)op_color.blu) + dst->blu = op_color.blu; + else + dst->blu = dst->blu + src->blu; +} + + + +static void +subOver(struct RGBColor * const src, + struct RGBColor * const dst) { + dst->red -= src->red; + dst->grn -= src->grn; + dst->blu -= src->blu; +} + + + +/* or maybe its src - dst; my copy of Inside Mac is unclear */ + + +static void +subPin(struct RGBColor * const src, + struct RGBColor * const dst) { + if ((long)dst->red - (long)src->red < (long)op_color.red) + dst->red = op_color.red; + else + dst->red = dst->red - src->red; + + if ((long)dst->grn - (long)src->grn < (long)op_color.grn) + dst->grn = op_color.grn; + else + dst->grn = dst->grn - src->grn; + + if ((long)dst->blu - (long)src->blu < (long)op_color.blu) + dst->blu = op_color.blu; + else + dst->blu = dst->blu - src->blu; +} + + + +static void +adMax(struct RGBColor * const src, + struct RGBColor * const dst) { + if (src->red > dst->red) dst->red = src->red; + if (src->grn > dst->grn) dst->grn = src->grn; + if (src->blu > dst->blu) dst->blu = src->blu; +} + + + +static void +adMin(struct RGBColor * const src, + struct RGBColor * const dst) { + if (src->red < dst->red) dst->red = src->red; + if (src->grn < dst->grn) dst->grn = src->grn; + if (src->blu < dst->blu) dst->blu = src->blu; +} + + + +static void +blend(struct RGBColor * const src, + struct RGBColor * const dst) { +#define blend_component(cmp) \ + ((long)src->cmp * (long)op_color.cmp) / 65536 + \ + ((long)dst->cmp * (long)(65536 - op_color.cmp) / 65536) + + dst->red = blend_component(red); + dst->grn = blend_component(grn); + dst->blu = blend_component(blu); +} + + + +static void +transparent(struct RGBColor * const src, + struct RGBColor * const dst) { + if (src->red != background.red || + src->grn != background.grn || + src->blu != background.blu) { + *dst = *src; + } +} + + + +static transfer_func +transfer(int const mode) { + switch (mode) { + case 0: return srcCopy; + case 1: return srcOr; + case 2: return srcXor; + case 3: return srcBic; + case 4: return notSrcCopy; + case 5: return notSrcOr; + case 6: return notSrcXor; + case 7: return notSrcBic; + case 32: return blend; + case 33: return addPin; + case 34: return addOver; + case 35: return subPin; + case 36: return transparent; + case 37: return adMax; + case 38: return subOver; + case 39: return adMin; + default: + if (mode != 0) + pm_message("no transfer function for code %s, using srcCopy", + const_name(transfer_name, mode)); + return srcCopy; + } +} + + + +static pixval +redepth(pixval const c, + pixval const oldMaxval) { + + return (c * PPM_MAXMAXVAL + oldMaxval / 2) / oldMaxval; +} + + + +static struct RGBColor +decode16(unsigned char * const sixteen) { +/*---------------------------------------------------------------------------- + Decode a 16 bit PICT encoding of RGB: + + Bit 0: nothing + Bits 1- 5: red + Bits 6-10: green + Bits 11-15: blue + + 'sixteen' is a two byte array. +-----------------------------------------------------------------------------*/ + struct RGBColor retval; + + retval.red = (sixteen[0] & 0x7c) >> 2; + retval.grn = (sixteen[0] & 0x03) << 3 | (sixteen[1] & 0xe0) >> 5; + retval.blu = (sixteen[1] & 0x1f) >> 0; + + return retval; +} + + + +static void +doDiffSize(struct Rect const clipsrc, + struct Rect const clipdst, + int const pixSize, + int const xsize, + int const ysize, + transfer_func const trf, + struct RGBColor * const color_map, + unsigned char * const src, + int const srcwid, + struct rgbPlanes const dst, + unsigned int const dstwid) { + + unsigned int const dstadd = dstwid - xsize; + + FILE * pnmscalePipeP; + const char * command; + FILE * scaled; + int cols, rows, format; + pixval maxval; + pixel * row; + pixel * rowp; + FILE * tempFileP; + const char * tempFilename; + word * reddst; + word * grndst; + word * bludst; + + reddst = dst.red; /* initial value */ + grndst = dst.grn; /* initial value */ + bludst = dst.blu; /* initial value */ + + pm_make_tmpfile(&tempFileP, &tempFilename); + + pm_close(tempFileP); + + asprintfN(&command, "pnmscale -xsize %d -ysize %d > %s", + rectwidth(&clipdst), rectheight(&clipdst), tempFilename); + + pm_message("running command '%s'", command); + + pnmscalePipeP = popen(command, "w"); + if (pnmscalePipeP == NULL) + pm_error("cannot execute command '%s' popen() errno = %s (%d)", + command, strerror(errno), errno); + + strfree(command); + + fprintf(pnmscalePipeP, "P6\n%d %d\n%d\n", + rectwidth(&clipsrc), rectheight(&clipsrc), PPM_MAXMAXVAL); + + switch (pixSize) { + case 8: { + unsigned int rowNumber; + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const colorIndex = row[colNumber]; + struct RGBColor * const ct = &color_map[colorIndex]; + fputc(redepth(ct->red, 65535L), pnmscalePipeP); + fputc(redepth(ct->grn, 65535L), pnmscalePipeP); + fputc(redepth(ct->blu, 65535L), pnmscalePipeP); + } + } + } + break; + case 16: { + unsigned int rowNumber; + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + struct RGBColor const color = decode16(&row[colNumber * 2]); + fputc(redepth(color.red, 32), pnmscalePipeP); + fputc(redepth(color.grn, 32), pnmscalePipeP); + fputc(redepth(color.blu, 32), pnmscalePipeP); + } + } + } + break; + case 32: { + unsigned int const planeSize = srcwid / 4; + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned char * const redPlane = &row[planeSize * 0]; + unsigned char * const grnPlane = &row[planeSize * 1]; + unsigned char * const bluPlane = &row[planeSize * 2]; + + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + fputc(redepth(redPlane[colNumber], 256), pnmscalePipeP); + fputc(redepth(grnPlane[colNumber], 256), pnmscalePipeP); + fputc(redepth(bluPlane[colNumber], 256), pnmscalePipeP); + } + } + } + break; + } + + if (pclose(pnmscalePipeP)) + pm_error("pnmscale failed. pclose() returned Errno %s (%d)", + strerror(errno), errno); + + ppm_readppminit(scaled = pm_openr(tempFilename), &cols, &rows, + &maxval, &format); + row = ppm_allocrow(cols); + /* couldn't hurt to assert cols, rows and maxval... */ + + if (trf == NULL) { + while (rows-- > 0) { + unsigned int i; + ppm_readppmrow(scaled, row, cols, maxval, format); + for (i = 0, rowp = row; i < cols; ++i, ++rowp) { + *reddst++ = PPM_GETR(*rowp) * 65536L / (maxval + 1); + *grndst++ = PPM_GETG(*rowp) * 65536L / (maxval + 1); + *bludst++ = PPM_GETB(*rowp) * 65536L / (maxval + 1); + } + reddst += dstadd; + grndst += dstadd; + bludst += dstadd; + } + } + else { + while (rows-- > 0) { + unsigned int i; + ppm_readppmrow(scaled, row, cols, maxval, format); + for (i = 0, rowp = row; i < cols; i++, rowp++) { + struct RGBColor dst_c, src_c; + dst_c.red = *reddst; + dst_c.grn = *grndst; + dst_c.blu = *bludst; + src_c.red = PPM_GETR(*rowp) * 65536L / (maxval + 1); + src_c.grn = PPM_GETG(*rowp) * 65536L / (maxval + 1); + src_c.blu = PPM_GETB(*rowp) * 65536L / (maxval + 1); + (*trf)(&src_c, &dst_c); + *reddst++ = dst_c.red; + *grndst++ = dst_c.grn; + *bludst++ = dst_c.blu; + } + reddst += dstadd; + grndst += dstadd; + bludst += dstadd; + } + } + + pm_close(scaled); + ppm_freerow(row); + strfree(tempFilename); + unlink(tempFilename); +} + + + +static void +getRgb(struct rgbPlanes const planes, + unsigned int const index, + struct RGBColor * const rgbP) { + + rgbP->red = planes.red[index]; + rgbP->grn = planes.grn[index]; + rgbP->blu = planes.blu[index]; +} + + + +static void +putRgb(struct RGBColor const rgb, + unsigned int const index, + struct rgbPlanes const planes) { + + planes.red[index] = rgb.red; + planes.grn[index] = rgb.grn; + planes.blu[index] = rgb.blu; +} + + + +static void +doSameSize(transfer_func trf, + int const pixSize, + int const xsize, + int const ysize, + unsigned char * const src, + unsigned int const srcwid, + struct RGBColor * const color_map, + struct rgbPlanes const dst, + unsigned int const dstwid) { +/*---------------------------------------------------------------------------- + Generalized (but slow) blit. + + Transfer pixels from 'src' to 'dst', applying the transfer function + 'trf'. + + 'src' has the same format as the 'bytes' member of struct raster. + 'srcwid' is the size in bytes of each row, like raster.rowSize. + + We use only the first 'ysize' rows and only the first 'xsize' + pixels of each row. + + We really should clean this up so that we can take pixels out of + the middle of a row and rows out of the middle of the raster. As + it stands, Caller achieves the same result by passing as 'src' + a pointer into the middle of a raster -- the upper left corner of + the rectangle he wants. But that is messy and nonobvious. + + Each plane of 'dst' is one word per pixel and contains actual + colors, never a palette index. It is an array in row-major order + with 'dstwid' words per row. +-----------------------------------------------------------------------------*/ + switch (pixSize) { + case 8: { + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const srcrow = &src[rowNumber * srcwid]; + unsigned int const dstRowCurs = rowNumber * dstwid; + + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + unsigned int const colorIndex = srcrow[colNumber]; + struct RGBColor dstColor; + getRgb(dst, dstCursor, &dstColor); + (*trf)(&color_map[colorIndex], &dstColor); + putRgb(dstColor, dstCursor, dst); + } + } + } break; + case 16: { + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned int const dstRowCurs = rowNumber * dstwid; + + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + struct RGBColor const srcColor = decode16(&row[colNumber*2]); + struct RGBColor dstColor; + struct RGBColor scaledSrcColor; + scaledSrcColor.red = srcColor.red << 11; + scaledSrcColor.grn = srcColor.grn << 11; + scaledSrcColor.blu = srcColor.blu << 11; + getRgb(dst, dstCursor, &dstColor); + (*trf)(&scaledSrcColor, &dstColor); + putRgb(dstColor, dstCursor, dst); + } + } + } break; + case 32: { + unsigned int const planeSize = srcwid / 4; + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned char * const redPlane = &row[planeSize * 0]; + unsigned char * const grnPlane = &row[planeSize * 1]; + unsigned char * const bluPlane = &row[planeSize * 2]; + unsigned int const dstRowCurs = rowNumber * dstwid; + + unsigned int colNumber; + + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + struct RGBColor srcColor, dstColor; + getRgb(dst, dstCursor, &dstColor); + srcColor.red = redPlane[colNumber] << 8; + srcColor.grn = grnPlane[colNumber] << 8; + srcColor.blu = bluPlane[colNumber] << 8; + (*trf)(&srcColor, &dstColor); + putRgb(dstColor, dstCursor, dst); + } + } + } break; + default: + pm_error("Impossible value of pixSize: %u", pixSize); + } +} + + + +static void +blitIdempotent(unsigned int const pixSize, + unsigned int const xsize, + unsigned int const ysize, + unsigned char * const src, + unsigned int const srcwid, + struct RGBColor * const colorMap, + struct rgbPlanes const dst, + unsigned int const dstwid) { +/*---------------------------------------------------------------------------- + This is the same as doSameSize(), except optimized for the case that + the transfer function is idempotent (i.e. it's just a straight copy). + The original author's comments suggest that this optimization isn't + all that important -- that he just wrote this first and instead of + expanding it to handle arbitrary transfer functions, added functions + for that. +-----------------------------------------------------------------------------*/ + switch (pixSize) { + case 8: { + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const srcrow = &src[rowNumber * srcwid]; + unsigned int const dstRowCurs = rowNumber * dstwid; + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + struct RGBColor * const ct = colorMap + srcrow[colNumber]; + dst.red[dstCursor] = ct->red; + dst.grn[dstCursor] = ct->grn; + dst.blu[dstCursor] = ct->blu; + } + } + } break; + case 16: { + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const srcrow = &src[rowNumber * srcwid]; + unsigned int const dstRowCurs = rowNumber * dstwid; + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + struct RGBColor const srcColor = + decode16(&srcrow[colNumber * 2]); + dst.red[dstCursor] = srcColor.red << 11; + dst.grn[dstCursor] = srcColor.grn << 11; + dst.blu[dstCursor] = srcColor.blu << 11; + } + } + } break; + case 32: { + unsigned int const planeSize = srcwid / 4; + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const srcrow = &src[rowNumber * srcwid]; + unsigned char * const redPlane = &srcrow[planeSize * 0]; + unsigned char * const grnPlane = &srcrow[planeSize * 1]; + unsigned char * const bluPlane = &srcrow[planeSize * 2]; + unsigned int const dstRowCurs = rowNumber * dstwid; + + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + dst.red[dstCursor] = redPlane[colNumber] << 8; + dst.grn[dstCursor] = grnPlane[colNumber] << 8; + dst.blu[dstCursor] = bluPlane[colNumber] << 8; + } + } + } break; + default: + pm_error("INTERNAL ERROR: invalid bits per pixel (%u) in " + "blitIdempotent()", pixSize); + } +} + + + +static void +generalBlit(struct Rect const srcRect, + struct Rect const srcBounds, + struct raster const srcplane, + int const pixSize, + struct Rect const dstRect, + struct Rect const dstBounds, + int const dstwid, + struct RGBColor * const color_map, + int const mode, + struct Rect const clipsrc, + struct Rect const clipdst) { + + unsigned char * src; + struct rgbPlanes dst; + int dstoff; + int xsize; + int ysize; + int srcadd; + transfer_func trf; + + if (verbose) { + dumpRect("copying from:", clipsrc); + dumpRect("to: ", clipdst); + pm_message("a %u x %u area to a %u x %u area", + rectwidth(&clipsrc), rectheight(&clipsrc), + rectwidth(&clipdst), rectheight(&clipdst)); + } + + { + unsigned int const pkpixsize = pixSize == 16 ? 2 : 1; + unsigned int const srcRowNumber = clipsrc.top - srcBounds.top; + unsigned int const srcRowOffset = + (clipsrc.left - srcBounds.left) * pkpixsize; + assert(srcRowNumber < srcplane.rowCount); + assert(srcRowOffset < srcplane.rowSize); + src = srcplane.bytes + srcRowNumber * srcplane.rowSize + srcRowOffset; + xsize = clipsrc.right - clipsrc.left; + ysize = clipsrc.bottom - clipsrc.top; + srcadd = srcplane.rowSize - xsize * pkpixsize; + } + + dstoff = (clipdst.top - dstBounds.top) * dstwid + + (clipdst.left - dstBounds.left); + dst.red = red + dstoff; + dst.grn = green + dstoff; + dst.blu = blue + dstoff; + + /* get rid of Text mask mode bit, if (erroneously) set */ + if ((mode & ~64) == 0) + trf = NULL; /* optimized srcCopy */ + else + trf = transfer(mode & ~64); + + if (!rectsamesize(&clipsrc, &clipdst)) + doDiffSize(clipsrc, clipdst, pixSize, xsize, ysize, + trf, color_map, src, srcplane.rowSize, dst, dstwid); + else { + if (trf == NULL) + blitIdempotent(pixSize, xsize, ysize, src, srcplane.rowSize, + color_map, dst, dstwid); + else + doSameSize(trf, pixSize, xsize, ysize, src, srcplane.rowSize, + color_map, dst, dstwid); + } +} + + + +static int +blit(struct Rect const srcRect, + struct Rect const srcBounds, + struct raster const srcplane, + int const pixSize, + struct Rect const dstRect, + struct Rect const dstBounds, + int const dstwid, + struct RGBColor * const color_map, + int const mode) { + + /* I can't tell what the result value of this function is supposed to mean, + but I found several return statements that did not set it to anything, + and several calls that examine it. I'm guessing that "1" is the + appropriate thing to return in those cases, so I made it so. + -Bryan 00.03.02 + */ + + int retval; + + if (ps_text) + retval = 1; + else { + /* Almost got it. Clip source rect with source bounds. + clip dest rect with dest bounds. If source and + destination are not the same size, use Pnmscale + to get a nicely sized rectangle. + */ + struct Rect clipsrc; + struct Rect clipdst; + + rectinter(srcBounds, srcRect, &clipsrc); + rectinter(dstBounds, dstRect, &clipdst); + + if (fullres) { + struct blit_info * bi; + bi = add_blit_list(); + bi->srcRect = clipsrc; + bi->srcBounds = srcBounds; + bi->srcplane = srcplane; + bi->pixSize = pixSize; + bi->dstRect = clipdst; + bi->color_map = color_map; + bi->mode = mode; + + retval = 0; + } else { + generalBlit(srcRect, srcBounds, srcplane, pixSize, + dstRect, dstBounds, dstwid, color_map, mode, + clipsrc, clipdst); + + retval = 1; + } + } + return retval; +} + + + +/* allocation is same for version 1 or version 2. We are super-duper + * wasteful of memory for version 1 picts. Someday, we'll separate + * things and only allocate a byte per pixel for version 1 (or heck, + * even only a bit, but that would require even more extra work). + */ + +static void +allocPlanes(struct rgbPlanes * const planesP) { + + struct rgbPlanes planes; + + rowlen = picFrame.right - picFrame.left; + collen = picFrame.bottom - picFrame.top; + + clip_rect = picFrame; + + planelen = rowlen * collen; + MALLOCARRAY(planes.red, planelen); + MALLOCARRAY(planes.grn, planelen); + MALLOCARRAY(planes.blu, planelen); + if (planes.red == NULL || planes.grn == NULL || planes.blu == NULL) + pm_error("not enough memory to hold picture"); + + /* initialize background to white */ + memset(planes.red, 255, planelen * sizeof(word)); + memset(planes.grn, 255, planelen * sizeof(word)); + memset(planes.blu, 255, planelen * sizeof(word)); + + /* Until we wean this program off of global variables, we have to + set these: + */ + + red = planes.red; + green = planes.grn; + blue = planes.blu; +} + + + +static void +freePlanes(struct rgbPlanes const planes) { + + free(planes.red); + free(planes.grn); + free(planes.blu); +} + + + +static unsigned char +compact(word const input) { + + return (input >> 8) & 0xff; +} + + + +static void +do_blits(struct rgbPlanes * const planesP) { + + struct blit_info* bi; + int srcwidth, dstwidth, srcheight, dstheight; + double scale, scalelow, scalehigh; + double xscale = 1.0; + double yscale = 1.0; + double lowxscale, highxscale, lowyscale, highyscale; + int xscalecalc = 0, yscalecalc = 0; + + if (!blit_list) return; + + fullres = 0; + + for (bi = blit_list; bi; bi = bi->next) { + srcwidth = rectwidth(&bi->srcRect); + dstwidth = rectwidth(&bi->dstRect); + srcheight = rectheight(&bi->srcRect); + dstheight = rectheight(&bi->dstRect); + if (srcwidth > dstwidth) { + scalelow = (double)(srcwidth ) / (double)dstwidth; + scalehigh = (double)(srcwidth + 1.0) / (double)dstwidth; + switch (xscalecalc) { + case 0: + lowxscale = scalelow; + highxscale = scalehigh; + xscalecalc = 1; + break; + case 1: + if (scalelow < highxscale && scalehigh > lowxscale) { + if (scalelow > lowxscale) lowxscale = scalelow; + if (scalehigh < highxscale) highxscale = scalehigh; + } + else { + scale = (lowxscale + highxscale) / 2.0; + xscale = (double)srcwidth / (double)dstwidth; + if (scale > xscale) xscale = scale; + xscalecalc = 2; + } + break; + case 2: + scale = (double)srcwidth / (double)dstwidth; + if (scale > xscale) xscale = scale; + break; + } + } + + if (srcheight > dstheight) { + scalelow = (double)(srcheight ) / (double)dstheight; + scalehigh = (double)(srcheight + 1.0) / (double)dstheight; + switch (yscalecalc) { + case 0: + lowyscale = scalelow; + highyscale = scalehigh; + yscalecalc = 1; + break; + case 1: + if (scalelow < highyscale && scalehigh > lowyscale) { + if (scalelow > lowyscale) lowyscale = scalelow; + if (scalehigh < highyscale) highyscale = scalehigh; + } + else { + scale = (lowyscale + highyscale) / 2.0; + yscale = (double)srcheight / (double)dstheight; + if (scale > yscale) yscale = scale; + yscalecalc = 2; + } + break; + case 2: + scale = (double)srcheight / (double)dstheight; + if (scale > yscale) yscale = scale; + break; + } + } + } + + if (xscalecalc == 1) { + if (1.0 >= lowxscale && 1.0 <= highxscale) + xscale = 1.0; + else + xscale = lowxscale; + } + if (yscalecalc == 1) { + if (1.0 >= lowyscale && 1.0 <= lowyscale) + yscale = 1.0; + else + yscale = lowyscale; + } + + if (xscale != 1.0 || yscale != 1.0) { + for (bi = blit_list; bi; bi = bi->next) + rectscale(&bi->dstRect, xscale, yscale); + + pm_message("Scaling output by %f in X and %f in Y", + xscale, yscale); + rectscale(&picFrame, xscale, yscale); + } + + allocPlanes(planesP); + + for (bi = blit_list; bi; bi = bi->next) { + blit(bi->srcRect, bi->srcBounds, bi->srcplane, + bi->pixSize, + bi->dstRect, picFrame, rowlen, + bi->color_map, + bi->mode); + } +} + + + +static void +outputPpm(struct rgbPlanes const planes) { + + unsigned int width; + unsigned int height; + pixel * pixelrow; + unsigned int row; + unsigned int srcCursor; + + stage = "writing PPM"; + + assert(picFrame.right > picFrame.left); + assert(picFrame.bottom > picFrame.top); + + width = picFrame.right - picFrame.left; + height = picFrame.bottom - picFrame.top; + + ppm_writeppminit(stdout, width, height, PPM_MAXMAXVAL, 0); + pixelrow = ppm_allocrow(width); + srcCursor = 0; + for (row = 0; row < height; ++row) { + unsigned int col; + for (col = 0; col < width; ++col) { + PPM_ASSIGN(pixelrow[col], + compact(planes.red[srcCursor]), + compact(planes.grn[srcCursor]), + compact(planes.blu[srcCursor]) + ); + ++srcCursor; + } + ppm_writeppmrow(stdout, pixelrow, width, PPM_MAXMAXVAL, 0); + } + pm_close(stdout); +} + + + +/* + * All data in version 2 is 2-byte word aligned. Odd size data + * is padded with a null. + */ +static word +get_op(int const version) { + if ((align & 1) && version == 2) { + stage = "aligning for opcode"; + read_byte(); + } + + stage = "reading opcode"; + + if (version == 1) + return read_byte(); + else + return read_word(); +} + + + +static void +Clip(int const version) { + word len; + + len = read_word(); + + if (len == 0x000a) { /* null rgn */ + read_rect(&clip_rect); + /* XXX should clip this by picFrame */ + if (verbose) + dumpRect("clipping to", clip_rect); + } + else + skip(len - 2); +} + + + +static void +OpColor(int const version) { + op_color.red = read_word(); + op_color.grn = read_word(); + op_color.blu = read_word(); +} + + + +static void +read_pixmap(struct pixMap * const p) { + + stage = "getting pixMap header"; + + read_rect(&p->Bounds); + p->version = read_word(); + p->packType = read_word(); + p->packSize = read_long(); + p->hRes = read_long(); + p->vRes = read_long(); + p->pixelType = read_word(); + p->pixelSize = read_word(); + p->cmpCount = read_word(); + p->cmpSize = read_word(); + p->planeBytes = read_long(); + p->pmTable = read_long(); + p->pmReserved = read_long(); + + if (verbose) { + pm_message("pixelType: %d", p->pixelType); + pm_message("pixelSize: %d", p->pixelSize); + pm_message("cmpCount: %d", p->cmpCount); + pm_message("cmpSize: %d", p->cmpSize); + } + + if (p->pixelType != 0) + pm_error("sorry, I do only chunky format. " + "This image has pixel type %hu", p->pixelType); + if (p->cmpCount != 1) + pm_error("sorry, cmpCount != 1"); + if (p->pixelSize != p->cmpSize) + pm_error("oops, pixelSize != cmpSize"); +} + + + +static struct RGBColor* +read_color_table(void) { + longword ctSeed; + word ctFlags; + word ctSize; + word val; + int i; + struct RGBColor* color_table; + + stage = "getting color table info"; + + ctSeed = read_long(); + ctFlags = read_word(); + ctSize = read_word(); + + if (verbose) { + pm_message("ctSeed: %ld", ctSeed); + pm_message("ctFlags: %d", ctFlags); + pm_message("ctSize: %d", ctSize); + } + + stage = "reading color table"; + + MALLOCARRAY(color_table, ctSize + 1); + if (color_table == NULL) + pm_error("no memory for color table"); + + for (i = 0; i <= ctSize; i++) { + val = read_word(); + /* The indices in a device color table are bogus and usually == 0. + * so I assume we allocate up the list of colors in order. + */ + if (ctFlags & 0x8000) + val = i; + if (val > ctSize) + pm_error("pixel value greater than color table size"); + color_table[val].red = read_word(); + color_table[val].grn = read_word(); + color_table[val].blu = read_word(); + + if (verbose > 1) + pm_message("%d: [%d,%d,%d]", val, + color_table[val].red, + color_table[val].grn, + color_table[val].blu); + } + + return color_table; +} + + + +static void +readBytes(FILE * const ifP, + unsigned int const n, + unsigned char * const buf) { + + align += n; + + if (fread(buf, n, 1, ifP) != 1) + pm_error("EOF / read error while %s", stage); +} + + + +static void +copyFullBytes(unsigned char * const packed, + unsigned char * const expanded, + unsigned int const packedLen) { + + unsigned int i; + + for (i = 0; i < packedLen; ++i) + expanded[i] = packed[i]; +} + + + +static void +expand4Bits(unsigned char * const packed, + unsigned char * const expanded, + unsigned int const packedLen) { + + unsigned int i; + unsigned char * dst; + + dst = &expanded[0]; + + for (i = 0; i < packedLen; ++i) { + *dst++ = (packed[i] >> 4) & 0x0f; + *dst++ = (packed[i] >> 0) & 0x0f; + } +} + + + +static void +expand2Bits(unsigned char * const packed, + unsigned char * const expanded, + unsigned int const packedLen) { + + unsigned int i; + unsigned char * dst; + + dst = &expanded[0]; + + for (i = 0; i < packedLen; ++i) { + *dst++ = (packed[i] >> 6) & 0x03; + *dst++ = (packed[i] >> 4) & 0x03; + *dst++ = (packed[i] >> 2) & 0x03; + *dst++ = (packed[i] >> 0) & 0x03; + } +} + + + +static void +expand1Bit(unsigned char * const packed, + unsigned char * const expanded, + unsigned int const packedLen) { + + unsigned int i; + unsigned char * dst; + + dst = &expanded[0]; + + for (i = 0; i < packedLen; ++i) { + *dst++ = (packed[i] >> 7) & 0x01; + *dst++ = (packed[i] >> 6) & 0x01; + *dst++ = (packed[i] >> 5) & 0x01; + *dst++ = (packed[i] >> 4) & 0x01; + *dst++ = (packed[i] >> 3) & 0x01; + *dst++ = (packed[i] >> 2) & 0x01; + *dst++ = (packed[i] >> 1) & 0x01; + *dst++ = (packed[i] >> 0) & 0x01; + } +} + + + +static void +unpackBuf(unsigned char * const packed, + unsigned int const packedLen, + int const bitsPerPixel, + unsigned char ** const expandedP, + unsigned int * const expandedLenP) { +/*---------------------------------------------------------------------------- + Expand the bit string 'packed', which is 'packedLen' bytes long + into an array of bytes, with one byte per pixel. Each 'bitsPerPixel' + of 'packed' is a pixel. + + So e.g. if it's 4 bits per pixel and 'packed' is 0xabcdef01, we + return 0x0a0b0c0d0e0f0001 as *expandedP. + + As a special case, if there are multiple bytes per pixel, we just + return the exact same bit string. + + *expandedP is static storage. + + 'packedLen' must not be greater than 256. +-----------------------------------------------------------------------------*/ + static unsigned char expanded[256 * 8]; + unsigned char * src; + unsigned char * dst; + + assert(packedLen <= 256); + + src = &packed[0]; + dst = &expanded[0]; + + switch (bitsPerPixel) { + case 8: + case 16: + case 32: + assert(packedLen <= sizeof(expanded)); + copyFullBytes(packed, expanded, packedLen); + *expandedLenP = packedLen; + break; + case 4: + assert(packedLen * 2 <= sizeof(expanded)); + expand4Bits(packed, expanded, packedLen); + *expandedLenP = packedLen * 2; + break; + case 2: + assert(packedLen * 4 <= sizeof(expanded)); + expand2Bits(packed, expanded, packedLen); + *expandedLenP = packedLen * 4; + break; + case 1: + assert(packedLen * 8 <= sizeof(expanded)); + expand1Bit(packed, expanded, packedLen); + *expandedLenP = packedLen * 8; + break; + default: + pm_error("INTERNAL ERROR: bitsPerPixel = %u in unpackBuf", + bitsPerPixel); + } + *expandedP = expanded; +} + + + +static void +unpackUncompressedBits(FILE * const ifP, + struct raster const raster, + unsigned int const rowBytes, + unsigned int const bitsPerPixel) { +/*---------------------------------------------------------------------------- + Read the raster from the file into 'raster'. The data in the file is not + compressed (but may still be packed multiple pixels per byte). + + In PICT terminology, it appears that compression is called + "packing" and I don't know what packing is called. But we don't + use that confusing terminology in this program, except when talking + to the user. +-----------------------------------------------------------------------------*/ + unsigned int rowOfRect; + unsigned char * linebuf; + + MALLOCARRAY(linebuf, rowBytes + 100); + if (linebuf == NULL) + pm_error("can't allocate memory for line buffer"); + + for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) { + unsigned char * bytePixels; + unsigned int expandedByteCount; + unsigned char * rasterRow; + unsigned int i; + + rasterRow = raster.bytes + rowOfRect * raster.rowSize; + + readBytes(ifP, rowBytes, linebuf); + + unpackBuf(linebuf, rowBytes, bitsPerPixel, + &bytePixels, &expandedByteCount); + + assert(expandedByteCount <= raster.rowSize); + + for (i = 0; i < expandedByteCount; ++i) + rasterRow[i] = bytePixels[i]; + } + free(linebuf); +} + + + +static void +expandRun(unsigned char * const block, + unsigned int const blockLimit, + unsigned int const bitsPerPixel, + unsigned char * const expanded, + unsigned int const expandedSize, + unsigned int * const blockLengthP, + unsigned int * const expandedByteCountP) { +/*---------------------------------------------------------------------------- + Expand a run (the data says, "repeat the next pixel N times"). + + Return the expanded run as expanded[], which has room for 'expandedSize' + elements. Return as *expandedByteCountP the number of elements actually + returned. +-----------------------------------------------------------------------------*/ + unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1; + + if (1 + pkpixsize > blockLimit) + pm_error("PICT run block runs off the end of its line. " + "Invalid PICT file."); + else { + unsigned int const runLength = (block[0] ^ 0xff) + 2; + + unsigned int i; + unsigned char * bytePixels; /* Points to static storage */ + unsigned int expandedByteCount; + unsigned int outputCursor; + + assert(block[0] & 0x80); /* It's a run */ + + if (verbose > 1) + pm_message("Block: run of %u pixels or plane samples", runLength); + + unpackBuf(&block[1], pkpixsize, bitsPerPixel, + &bytePixels, &expandedByteCount); + + if (expandedByteCount > expandedSize) + pm_error("Invalid PICT image. It contains a row with more pixels " + "than the width of the image"); + + outputCursor = 0; + for (i = 0; i < runLength; ++i) { + unsigned int j; + for (j = 0; j < expandedByteCount; ++j) + expanded[outputCursor++] = bytePixels[j]; + } + *blockLengthP = 1 + pkpixsize; + *expandedByteCountP = expandedByteCount * runLength; + } +} + + + +static void +copyPixelGroup(unsigned char * const block, + unsigned int const blockLimit, + unsigned int const bitsPerPixel, + unsigned char * const dest, + unsigned int const destSize, + unsigned int * const blockLengthP, + unsigned int * const rasterBytesGeneratedP) { +/*---------------------------------------------------------------------------- + Copy a group of pixels (the data says, "take the following N pixels"). +-----------------------------------------------------------------------------*/ + unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1; + unsigned int const groupLen = block[0] + 1; + unsigned int const blockLength = 1 + groupLen * pkpixsize; + + if (blockLength > blockLimit) + pm_error("PICT non-run block (length %u) " + "runs off the end of its line. " + "Invalid PICT file.", blockLength); + else { + unsigned int i; + unsigned char * bytePixels; /* Points to static storage */ + unsigned int bytePixelLen; + + assert(blockLimit >= 1); /* block[0] exists */ + assert((block[0] & 0x80) == 0); /* It's not a run */ + + if (verbose > 1) + pm_message("Block: %u individual pixels or plane samples", + groupLen); + + unpackBuf(&block[1], groupLen * pkpixsize, bitsPerPixel, + &bytePixels, &bytePixelLen); + + if (bytePixelLen > destSize) + pm_error("Invalid PICT image. It contains a row with more pixels " + "than the width of the image"); + + for (i = 0; i < bytePixelLen; ++i) + dest[i] = bytePixels[i]; + + *blockLengthP = blockLength; + *rasterBytesGeneratedP = bytePixelLen; + } +} + + + +static void +interpretOneRasterBlock(unsigned char * const block, + unsigned int const blockLimit, + unsigned int const bitsPerPixel, + unsigned char * const raster, + unsigned int const rasterSize, + unsigned int * const blockLengthP, + unsigned int * const rasterBytesGeneratedP) { +/*---------------------------------------------------------------------------- + Return the pixels described by the PICT block block[], assuming + the PICT format is 'bitsPerPixel' bits per pixel. + + Return them as raster[], which has 'rasterSize' elements of space. + Return as *rasterBytesGeneratedP the number of elements actually + returned. + + block[] self-describes its length, and we return that as + *blockLengthP. But there are at most 'blockLimit' bytes there, so + if block[] claims to be longer than that, some of the block is + missing (i.e. corrupt PICT). +-----------------------------------------------------------------------------*/ + if (block[0] & 0x80) + expandRun(block, blockLimit, bitsPerPixel, raster, rasterSize, + blockLengthP, rasterBytesGeneratedP); + else + copyPixelGroup(block, blockLimit, bitsPerPixel, raster, rasterSize, + blockLengthP, rasterBytesGeneratedP); + + assert(*rasterBytesGeneratedP <= rasterSize); +} + + + +static unsigned int const maxPixelBytesPerBlock = 1024; + + + +static void +unpackCompressedBits(FILE * const ifP, + struct raster const raster, + unsigned int const rowBytes, + unsigned int const bitsPerPixel) { +/*---------------------------------------------------------------------------- + Read the raster of on file *ifP and place it in 'raster'. + + The data in the file is compressed with run length encoding and + possibly packed multiple pixels per byte as well. + + In PICT terminology, it appears that compression is called + "packing" and I don't know what packing is called. But we don't + use that confusing terminology in this program, except when talking + to the user. + + *boundsP describes the rectangle. +-----------------------------------------------------------------------------*/ + unsigned int const llsize = rowBytes > 200 ? 2 : 1; + unsigned int rowOfRect; + unsigned char * linebuf; + unsigned int linebufSize; + + linebufSize = rowBytes; + MALLOCARRAY(linebuf, linebufSize); + if (linebuf == NULL) + pm_error("can't allocate memory for line buffer"); + + for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) { + unsigned char * const rowRaster = + &raster.bytes[rowOfRect * raster.rowSize]; + unsigned int linelen; + unsigned int lineCursor; + unsigned int rasterCursor; + + if (llsize == 2) + linelen = read_word(); + else + linelen = read_byte(); + + if (verbose > 1) + pm_message("linelen: %u", linelen); + + if (linelen > linebufSize) { + linebufSize = linelen; + REALLOCARRAY(linebuf, linebufSize); + if (linebuf == NULL) + pm_error("can't allocate memory for line buffer"); + } + readBytes(ifP, linelen, linebuf); + + for (lineCursor = 0, rasterCursor = 0; lineCursor < linelen; ) { + unsigned int blockLength, rasterBytesGenerated; + + assert(lineCursor <= linelen); + + interpretOneRasterBlock( + &linebuf[lineCursor], linelen - lineCursor, + bitsPerPixel, + &rowRaster[rasterCursor], raster.rowSize - rasterCursor, + &blockLength, &rasterBytesGenerated); + + lineCursor += blockLength; + rasterCursor += rasterBytesGenerated; + assert(rasterCursor <= raster.rowSize); + } + if (verbose > 1) + pm_message("row %u: got %u", rowOfRect, rasterCursor); + } + free(linebuf); +} + + + +static void +unpackbits(FILE * const ifP, + struct Rect * const boundsP, + word const rowBytesArg, + int const bitsPerPixel, + struct raster * const rasterP) { + + unsigned int const rectHeight = boundsP->bottom - boundsP->top; + unsigned int const rectWidth = boundsP->right - boundsP->left; + + struct raster raster; + unsigned int rowBytes; + + stage = "unpacking packbits"; + + if (verbose) + pm_message("rowBytes = %u, bitsPerPixel = %d", + rowBytesArg, bitsPerPixel); + + allocateRaster(&raster, rectWidth, rectHeight, bitsPerPixel); + + rowBytes = rowBytesArg ? rowBytesArg : raster.rowSize; + + if (verbose) + pm_message("raster.rowSize: %u bytes; file row = %u bytes", + raster.rowSize, rowBytes); + + if (rowBytes < 8) + unpackUncompressedBits(ifP, raster, rowBytes, bitsPerPixel); + else + unpackCompressedBits(ifP, raster, rowBytes, bitsPerPixel); + + *rasterP = raster; +} + + + +static void +interpretRowBytesWord(word const rowBytesWord, + bool * const pixMapP, + unsigned int * const rowBytesP) { + + *pixMapP = !!(rowBytesWord & 0x8000); + *rowBytesP = rowBytesWord & 0x7fff; + + if (verbose) + pm_message("PCT says %s, %u bytes per row", + *pixMapP ? "pixmap" : "bitmap", *rowBytesP); +} + + + +/* this just skips over a version 2 pattern. Probably will return + * a pattern in the fabled complete version. + */ +static void +read_pattern(void) { + + word PatType; + + stage = "Reading a pattern"; + + PatType = read_word(); + + switch (PatType) { + case 2: + skip(8); /* old pattern data */ + skip(5); /* RGB for pattern */ + break; + case 1: { + word rowBytesWord; + bool pixMap; + unsigned int rowBytes; + struct pixMap p; + struct raster raster; + struct RGBColor * ct; + + skip(8); /* old pattern data */ + rowBytesWord = read_word(); + interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes); + read_pixmap(&p); + ct = read_color_table(); + unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster); + freeRaster(raster); + free(ct); + } break; + default: + pm_error("unknown pattern type in read_pattern"); + } +} + + + +/* these 3 do nothing but skip over their data! */ + +static void +BkPixPat(int const version) { + read_pattern(); +} + +static void +PnPixPat(int const version) { + read_pattern(); +} + +static void +FillPixPat(int const version) { + read_pattern(); +} + +static void +read_8x8_pattern(struct Pattern * const pat) { + unsigned char buf[8]; + unsigned char * exp; + unsigned int len; + unsigned int expandedLen; + unsigned int i; + + len = 8; /* initial value */ + readBytes(ifp, len, buf); + if (verbose) { + pm_message("pattern: %02x%02x%02x%02x", + buf[0], buf[1], buf[2], buf[3]); + pm_message("pattern: %02x%02x%02x%02x", + buf[4], buf[5], buf[6], buf[7]); + } + unpackBuf(buf, len, 1, &exp, &expandedLen); + for (i = 0; i < 64; ++i) + pat->pix[i] = exp[i]; +} + + + +static void +BkPat(int const version) { + read_8x8_pattern(&bkpat); +} + + + +static void +PnPat(int const version) { + read_8x8_pattern(&pen_pat); +} + + + +static void +FillPat(int const version) { + read_8x8_pattern(&fillpat); +} + + + +static void +PnSize(int const version) { + pen_height = read_word(); + pen_width = read_word(); + if (verbose) + pm_message("pen size %d x %d", pen_width, pen_height); +} + + + +static void +PnMode(int const version) { + + pen_mode = read_word(); + + if (pen_mode >= 8 && pen_mode < 15) + pen_mode -= 8; + if (verbose) + pm_message("pen transfer mode = %s", + const_name(transfer_name, pen_mode)); + + pen_trf = transfer(pen_mode); +} + + + +static void +read_rgb(struct RGBColor * const rgb) { + rgb->red = read_word(); + rgb->grn = read_word(); + rgb->blu = read_word(); +} + + + +static void +RGBFgCol(int const v) { + read_rgb(&foreground); + if (verbose) + pm_message("foreground now [%d,%d,%d]", + foreground.red, foreground.grn, foreground.blu); +} + + + +static void +RGBBkCol(int const v) { + read_rgb(&background); + if (verbose) + pm_message("background now [%d,%d,%d]", + background.red, background.grn, background.blu); +} + + + +#define PIXEL_INDEX(x,y) ((y) - picFrame.top) * rowlen + (x) - picFrame.left + +static void +draw_pixel(int const x, + int const y, + struct RGBColor * const clr, + transfer_func trf) { + + int i; + struct RGBColor dst; + + if (x < clip_rect.left || x >= clip_rect.right || + y < clip_rect.top || y >= clip_rect.bottom) + { + return; + } + + i = PIXEL_INDEX(x, y); + dst.red = red[i]; + dst.grn = green[i]; + dst.blu = blue[i]; + (*trf)(clr, &dst); + red[i] = dst.red; + green[i] = dst.grn; + blue[i] = dst.blu; +} + + + +static void +draw_pen_rect(struct Rect * const r) { + int const rowadd = rowlen - (r->right - r->left); + + int i; + int x, y; + struct RGBColor dst; + + i = PIXEL_INDEX(r->left, r->top); /* initial value */ + + for (y = r->top; y < r->bottom; y++) { + for (x = r->left; x < r->right; x++) { + dst.red = red[i]; + dst.grn = green[i]; + dst.blu = blue[i]; + if (pen_pat.pix[(x & 7) + (y & 7) * 8]) + (*pen_trf)(&black, &dst); + else + (*pen_trf)(&white, &dst); + red[i] = dst.red; + green[i] = dst.grn; + blue[i] = dst.blu; + + i++; + } + i += rowadd; + } +} + + + +static void +draw_pen(int const x, + int const y) { + struct Rect penrect; + + penrect.left = x; + penrect.right = x + pen_width; + penrect.top = y; + penrect.bottom = y + pen_height; + + rectinter(penrect, clip_rect, &penrect); + + draw_pen_rect(&penrect); +} + +/* + * Digital Line Drawing + * by Paul Heckbert + * from "Graphics Gems", Academic Press, 1990 + */ + +/* + * digline: draw digital line from (x1,y1) to (x2,y2), + * calling a user-supplied procedure at each pixel. + * Does no clipping. Uses Bresenham's algorithm. + * + * Paul Heckbert 3 Sep 85 + */ +static void +scan_line(short const x1, + short const y1, + short const x2, + short const y2) { + int d, x, y, ax, ay, sx, sy, dx, dy; + + if (!(pen_width == 0 && pen_height == 0)) { + + dx = x2-x1; ax = ABS(dx)<<1; sx = SGN(dx); + dy = y2-y1; ay = ABS(dy)<<1; sy = SGN(dy); + + x = x1; + y = y1; + if (ax>ay) { /* x dominant */ + d = ay-(ax>>1); + for (;;) { + draw_pen(x, y); + if (x==x2) return; + if ((x > rowlen) && (sx > 0)) return; + if (d>=0) { + y += sy; + d -= ax; + } + x += sx; + d += ay; + } + } + else { /* y dominant */ + d = ax-(ay>>1); + for (;;) { + draw_pen(x, y); + if (y==y2) return; + if ((y > collen) && (sy > 0)) return; + if (d>=0) { + x += sx; + d -= ay; + } + y += sy; + d += ax; + } + } + } +} + + + +static void +Line(int const v) { + struct Point p1; + read_point(&p1); + read_point(¤t); + if (verbose) + pm_message("(%d,%d) to (%d, %d)", + p1.x,p1.y,current.x,current.y); + scan_line(p1.x,p1.y,current.x,current.y); +} + + + +static void +LineFrom(int const v) { + struct Point p1; + read_point(&p1); + if (verbose) + pm_message("(%d,%d) to (%d, %d)", + current.x,current.y,p1.x,p1.y); + + if (!fullres) + scan_line(current.x,current.y,p1.x,p1.y); + + current.x = p1.x; + current.y = p1.y; +} + + + +static void +ShortLine(int const v) { + struct Point p1; + read_point(&p1); + read_short_point(¤t); + if (verbose) + pm_message("(%d,%d) delta (%d, %d)", + p1.x,p1.y,current.x,current.y); + current.x += p1.x; + current.y += p1.y; + + if (!fullres) + scan_line(p1.x,p1.y,current.x,current.y); +} + + + +static void +ShortLineFrom(int const v) { + struct Point p1; + read_short_point(&p1); + if (verbose) + pm_message("(%d,%d) delta (%d, %d)", + current.x,current.y,p1.x,p1.y); + p1.x += current.x; + p1.y += current.y; + if (!fullres) + scan_line(current.x,current.y,p1.x,p1.y); + current.x = p1.x; + current.y = p1.y; +} + +static void +do_paintRect(struct Rect const prect) { + struct Rect rect; + + if (fullres) + return; + + if (verbose) + dumpRect("painting", prect); + + rectinter(clip_rect, prect, &rect); + + draw_pen_rect(&rect); +} + + + +static void +paintRect(int const v) { + read_rect(&cur_rect); + do_paintRect(cur_rect); +} + + + +static void +paintSameRect(int const v) { + do_paintRect(cur_rect); +} + + + +static void +do_frameRect(struct Rect const rect) { + int x, y; + + if (fullres) + return; + + if (verbose) + dumpRect("framing", rect); + + if (pen_width == 0 || pen_height == 0) + return; + + for (x = rect.left; x <= rect.right - pen_width; x += pen_width) { + draw_pen(x, rect.top); + draw_pen(x, rect.bottom - pen_height); + } + + for (y = rect.top; y <= rect.bottom - pen_height ; y += pen_height) { + draw_pen(rect.left, y); + draw_pen(rect.right - pen_width, y); + } +} + + + +static void +frameRect(int const v) { + read_rect(&cur_rect); + do_frameRect(cur_rect); +} + + + +static void +frameSameRect(int const v) { + do_frameRect(cur_rect); +} + + + +/* a stupid shell sort - I'm so embarassed */ + +static void +poly_sort(int const sort_index, struct Point points[]) { + int d, i, j, temp; + + /* initialize and set up sort interval */ + d = 4; + while (d<=sort_index) d <<= 1; + d -= 1; + + while (d > 1) { + d >>= 1; + for (j = 0; j <= (sort_index-d); j++) { + for(i = j; i >= 0; i -= d) { + if ((points[i+d].y < points[i].y) || + ((points[i+d].y == points[i].y) && + (points[i+d].x <= points[i].x))) { + /* swap x1,y1 with x2,y2 */ + temp = points[i].y; + points[i].y = points[i+d].y; + points[i+d].y = temp; + temp = points[i].x; + points[i].x = points[i+d].x; + points[i+d].x = temp; + } + } + } + } +} + + + +/* Watch out for the lack of error checking in the next two functions ... */ + +static void +scan_poly(int const np, + struct Point pts[]) { + int dx,dy,dxabs,dyabs,i,scan_index,j,k,px,py; + int sdx,sdy,x,y,toggle,old_sdy,sy0; + + /* This array needs to be at least as large as the largest dimension of + the bounding box of the poly (but I don't check for overflows ...) */ + struct Point coord[5000]; + + scan_index = 0; + + /* close polygon */ + px = pts[np].x = pts[0].x; + py = pts[np].y = pts[0].y; + + /* This section draws the polygon and stores all the line points + * in an array. This doesn't work for concave or non-simple polys. + */ + /* are y levels same for first and second points? */ + if (pts[1].y == pts[0].y) { + coord[scan_index].x = px; + coord[scan_index].y = py; + scan_index++; + } + +#define sign(x) ((x) > 0 ? 1 : ((x)==0 ? 0:(-1)) ) + + old_sdy = sy0 = sign(pts[1].y - pts[0].y); + for (j=0; j<np; j++) { + /* x,y difference between consecutive points and their signs */ + dx = pts[j+1].x - pts[j].x; + dy = pts[j+1].y - pts[j].y; + sdx = SGN(dx); + sdy = SGN(dy); + dxabs = abs(dx); + dyabs = abs(dy); + x = y = 0; + + if (dxabs >= dyabs) + { + for (k=0; k < dxabs; k++) { + y += dyabs; + if (y >= dxabs) { + y -= dxabs; + py += sdy; + if (old_sdy != sdy) { + old_sdy = sdy; + scan_index--; + } + coord[scan_index].x = px+sdx; + coord[scan_index].y = py; + scan_index++; + } + px += sdx; + draw_pen(px, py); + } + } + else + { + for (k=0; k < dyabs; k++) { + x += dxabs; + if (x >= dyabs) { + x -= dyabs; + px += sdx; + } + py += sdy; + if (old_sdy != sdy) { + old_sdy = sdy; + if (sdy != 0) scan_index--; + } + draw_pen(px,py); + coord[scan_index].x = px; + coord[scan_index].y = py; + scan_index++; + } + } + } + + /* after polygon has been drawn now fill it */ + + scan_index--; + if (sy0 + sdy == 0) scan_index--; + + poly_sort(scan_index, coord); + + toggle = 0; + for (i = 0; i < scan_index; i++) { + if ((coord[i].y == coord[i+1].y) && (toggle == 0)) + { + for (j = coord[i].x; j <= coord[i+1].x; j++) + draw_pen(j, coord[i].y); + toggle = 1; + } + else + toggle = 0; + } +} + + + +static void +paintPoly(int const v) { + struct Rect bb; + struct Point pts[100]; + int i, np = (read_word() - 10) >> 2; + + read_rect(&bb); + for (i=0; i<np; ++i) + read_point(&pts[i]); + + /* scan convert poly ... */ + if (!fullres) + scan_poly(np, pts); +} + + + +static void +PnLocHFrac(int const version) { + word frac = read_word(); + + if (verbose) + pm_message("PnLocHFrac = %d", frac); +} + + + +static void +TxMode(int const version) { + text_mode = read_word(); + + if (text_mode >= 8 && text_mode < 15) + text_mode -= 8; + if (verbose) + pm_message("text transfer mode = %s", + const_name(transfer_name, text_mode)); + + /* ignore the text mask bit 'cause we don't handle it yet */ + text_trf = transfer(text_mode & ~64); +} + + + +static void +TxFont(int const version) { + text_font = read_word(); + if (verbose) + pm_message("text font %s", const_name(font_name, text_font)); +} + + + +static void +TxFace(int const version) { + text_face = read_byte(); + if (verbose) + pm_message("text face %d", text_face); +} + + + +static void +TxSize(int const version) { + text_size = read_word(); + if (verbose) + pm_message("text size %d", text_size); +} + + + +static void +skip_text(void) { + skip(read_byte()); +} + + + +static int +abs_value(int const x) { + if (x < 0) + return -x; + else + return x; +} + + + +static struct font* +get_font(int const font, + int const size, + int const style) { + int closeness, bestcloseness; + struct fontinfo* fi, *best; + + best = 0; + for (fi = fontlist; fi; fi = fi->next) { + closeness = abs_value(fi->font - font) * 10000 + + abs_value(fi->size - size) * 100 + + abs_value(fi->style - style); + if (!best || closeness < bestcloseness) { + best = fi; + bestcloseness = closeness; + } + } + + if (best) { + if (best->loaded) + return best->loaded; + + if ((best->loaded = pbm_loadbdffont(best->filename))) + return best->loaded; + } + + /* It would be better to go looking for the nth best font, really */ + return 0; +} + + + +/* This only does 0, 90, 180 and 270 degree rotations */ + +static void +rotate(int * const x, + int * const y) { + int tmp; + + if (ps_rotation >= 315 || ps_rotation <= 45) + return; + + *x -= ps_cent_x; + *y -= ps_cent_y; + + if (ps_rotation > 45 && ps_rotation < 135) { + tmp = *x; + *x = *y; + *y = tmp; + } + else if (ps_rotation >= 135 && ps_rotation < 225) { + *x = -*x; + } + else if (ps_rotation >= 225 && ps_rotation < 315) { + tmp = *x; + *x = *y; + *y = -tmp; + } + *x += ps_cent_x; + *y += ps_cent_y; +} + + + +static void +do_ps_text(word const tx, + word const ty) { + int len, width, i, w, h, x, y, rx, ry, o; + byte str[256], ch; + struct glyph* glyph; + + current.x = tx; + current.y = ty; + + if (!ps_cent_set) { + ps_cent_x += tx; + ps_cent_y += ty; + ps_cent_set = 1; + } + + len = read_byte(); + + /* XXX this width calculation is not completely correct */ + width = 0; + for (i = 0; i < len; i++) { + ch = str[i] = read_byte(); + if (tfont->glyph[ch]) + width += tfont->glyph[ch]->xadd; + } + + if (verbose) { + str[len] = '\0'; + pm_message("ps text: %s", str); + } + + /* XXX The width is calculated in order to do different justifications. + * However, I need the width of original text to finish the job. + * In other words, font metrics for Quickdraw fonts + */ + + x = tx; + + for (i = 0; i < len; i++) { + if (!(glyph = tfont->glyph[str[i]])) + continue; + + y = ty - glyph->height - glyph->y; + for (h = 0; h < glyph->height; h++) { + for (w = 0; w < glyph->width; w++) { + rx = x + glyph->x + w; + ry = y; + rotate(&rx, &ry); + if ((rx >= picFrame.left) && (rx < picFrame.right) && + (ry >= picFrame.top) && (ry < picFrame.bottom)) + { + o = PIXEL_INDEX(rx, ry); + if (glyph->bmap[h * glyph->width + w]) { + red[o] = foreground.red; + green[o] = foreground.grn; + blue[o] = foreground.blu; + } + } + } + y++; + } + x += glyph->xadd; + } +} + + + +static void +do_text(word const startx, + word const starty) { + if (fullres) + skip_text(); + else { + if (!(tfont = get_font(text_font, text_size, text_face))) + tfont = pbm_defaultfont("bdf"); + + if (ps_text) + do_ps_text(startx, starty); + else { + int len; + word x, y; + + x = startx; + y = starty; + for (len = read_byte(); len > 0; --len) { + struct glyph* const glyph = tfont->glyph[read_byte()]; + if (glyph) { + int dy; + int h; + for (h = 0, dy = y - glyph->height - glyph->y; + h < glyph->height; + ++h, ++dy) { + int w; + for (w = 0; w < glyph->width; ++w) { + struct RGBColor * const colorP = + glyph->bmap[h * glyph->width + w] ? + &black : &white; + draw_pixel(x + w + glyph->x, dy, colorP, text_trf); + } + } + x += glyph->xadd; + } + } + current.x = x; + current.y = y; + } + } +} + + + +static void +LongText(int const version) { + struct Point p; + + read_point(&p); + do_text(p.x, p.y); +} + + + +static void +DHText(int const version) { + current.x += read_byte(); + do_text(current.x, current.y); +} + + + +static void +DVText(int const version) { + current.y += read_byte(); + do_text(current.x, current.y); +} + + + +static void +DHDVText(int const version) { + byte dh, dv; + + dh = read_byte(); + dv = read_byte(); + + if (verbose) + pm_message("dh, dv = %d, %d", dh, dv); + + current.x += dh; + current.y += dv; + do_text(current.x, current.y); +} + + + +/* + * This could use read_pixmap, but I'm too lazy to hack read_pixmap. + */ + +static void +directBits(unsigned int const pictVersion, + bool const skipRegion) { + + struct pixMap p; + struct Rect srcRect; + struct Rect dstRect; + struct raster raster; + word mode; + unsigned int rectWidth; + + /* skip fake len, and fake EOF */ + skip(4); /* Ptr baseAddr == 0x000000ff */ + read_word(); /* version */ + read_rect(&p.Bounds); + rectWidth = p.Bounds.right - p.Bounds.left; + p.packType = read_word(); + p.packSize = read_long(); + p.hRes = read_long(); + p.vRes = read_long(); + p.pixelType = read_word(); + p.pixelSize = read_word(); + p.pixelSize = read_word(); /* XXX twice??? */ + p.cmpCount = read_word(); + p.cmpSize = read_word(); + p.planeBytes = read_long(); + p.pmTable = read_long(); + p.pmReserved = read_long(); + + read_rect(&srcRect); + if (verbose) + dumpRect("source rectangle:", srcRect); + + read_rect(&dstRect); + if (verbose) + dumpRect("destination rectangle:", dstRect); + + mode = read_word(); + if (verbose) + pm_message("transfer mode = %s", const_name(transfer_name, mode)); + + if (skipRegion) + skip_poly_or_region(pictVersion); + + unpackbits(ifp, &p.Bounds, 0, p.pixelSize, &raster); + + blit(srcRect, p.Bounds, raster, p.pixelSize, + dstRect, picFrame, rowlen, NULL, mode); + + freeRaster(raster); +} + + + +#define SKIP_REGION_TRUE TRUE +#define SKIP_REGION_FALSE FALSE + +static void +DirectBitsRect(int const version) { + + directBits(version, SKIP_REGION_FALSE); +} + + + +static void +DirectBitsRgn(int const version) { + + directBits(version, SKIP_REGION_TRUE); +} + + + +static void +do_pixmap(int const version, + word const rowBytes, + int const is_region) { +/*---------------------------------------------------------------------------- + Do a paletted image. +-----------------------------------------------------------------------------*/ + word mode; + struct pixMap p; + struct raster raster; + struct RGBColor * color_table; + struct Rect srcRect; + struct Rect dstRect; + + read_pixmap(&p); + + if (verbose) + pm_message("%u x %u paletted image", + p.Bounds.right - p.Bounds.left, + p.Bounds.bottom - p.Bounds.top); + + color_table = read_color_table(); + + read_rect(&srcRect); + + if (verbose) + dumpRect("source rectangle:", srcRect); + + read_rect(&dstRect); + + if (verbose) + dumpRect("destination rectangle:", dstRect); + + mode = read_word(); + + if (verbose) + pm_message("transfer mode = %s", const_name(transfer_name, mode)); + + if (is_region) + skip_poly_or_region(version); + + stage = "unpacking rectangle"; + + unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster); + + blit(srcRect, p.Bounds, raster, 8, + dstRect, picFrame, rowlen, color_table, mode); + + free(color_table); + freeRaster(raster); +} + + + +static void +do_bitmap(int const version, + int const rowBytes, + int const is_region) { +/*---------------------------------------------------------------------------- + Do a bitmap. That's one bit per pixel, 0 is white, 1 is black. +-----------------------------------------------------------------------------*/ + struct Rect Bounds; + struct Rect srcRect; + struct Rect dstRect; + word mode; + struct raster raster; + static struct RGBColor color_table[] = { + {65535L, 65535L, 65535L}, {0, 0, 0} }; + + read_rect(&Bounds); + read_rect(&srcRect); + read_rect(&dstRect); + mode = read_word(); + if (verbose) + pm_message("transfer mode = %s", const_name(transfer_name, mode)); + + if (is_region) + skip_poly_or_region(version); + + stage = "unpacking rectangle"; + + unpackbits(ifp, &Bounds, rowBytes, 1, &raster); + + blit(srcRect, Bounds, raster, 8, + dstRect, picFrame, rowlen, color_table, mode); + + freeRaster(raster); +} + + + +static void +BitsRect(int const version) { + + word rowBytesWord; + bool pixMap; + unsigned int rowBytes; + + stage = "Reading rowBytes word for bitsrect"; + rowBytesWord = read_word(); + + interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes); + + if (pixMap) + do_pixmap(version, rowBytes, 0); + else + do_bitmap(version, rowBytes, 0); +} + + + +static void +BitsRegion(int const version) { + + word rowBytesWord; + bool pixMap; + unsigned int rowBytes; + + stage = "Reading rowBytes for bitsregion"; + rowBytesWord = read_word(); + + interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes); + + if (pixMap) + do_pixmap(version, rowBytes, 1); + else + do_bitmap(version, rowBytes, 1); +} + + + + /* + * See http://developer.apple.com/techpubs/mac/QuickDraw/QuickDraw-461.html + * for opcode description + */ +static struct opdef const optable[] = { +/* 0x00 */ { "NOP", 0, NULL, "nop" }, +/* 0x01 */ { "Clip", NA, Clip, "clip" }, +/* 0x02 */ { "BkPat", 8, BkPat, "background pattern" }, +/* 0x03 */ { "TxFont", 2, TxFont, "text font (word)" }, +/* 0x04 */ { "TxFace", 1, TxFace, "text face (byte)" }, +/* 0x05 */ { "TxMode", 2, TxMode, "text mode (word)" }, +/* 0x06 */ { "SpExtra", 4, NULL, "space extra (fixed point)" }, +/* 0x07 */ { "PnSize", 4, PnSize, "pen size (point)" }, +/* 0x08 */ { "PnMode", 2, PnMode, "pen mode (word)" }, +/* 0x09 */ { "PnPat", 8, PnPat, "pen pattern" }, +/* 0x0a */ { "FillPat", 8, FillPat, "fill pattern" }, +/* 0x0b */ { "OvSize", 4, NULL, "oval size (point)" }, +/* 0x0c */ { "Origin", 4, NULL, "dh, dv (word)" }, +/* 0x0d */ { "TxSize", 2, TxSize, "text size (word)" }, +/* 0x0e */ { "FgColor", 4, NULL, "foreground color (longword)" }, +/* 0x0f */ { "BkColor", 4, NULL, "background color (longword)" }, +/* 0x10 */ { "TxRatio", 8, NULL, "numerator (point), denominator (point)" }, +/* 0x11 */ { "Version", 1, NULL, "version (byte)" }, +/* 0x12 */ { "BkPixPat", NA, BkPixPat, "color background pattern" }, +/* 0x13 */ { "PnPixPat", NA, PnPixPat, "color pen pattern" }, +/* 0x14 */ { "FillPixPat", NA, FillPixPat, "color fill pattern" }, +/* 0x15 */ { "PnLocHFrac", 2, PnLocHFrac, "fractional pen position" }, +/* 0x16 */ { "ChExtra", 2, NULL, "extra for each character" }, +/* 0x17 */ res(0), +/* 0x18 */ res(0), +/* 0x19 */ res(0), +/* 0x1a */ { "RGBFgCol", RGB_LEN, RGBFgCol, "RGB foreColor" }, +/* 0x1b */ { "RGBBkCol", RGB_LEN, RGBBkCol, "RGB backColor" }, +/* 0x1c */ { "HiliteMode", 0, NULL, "hilite mode flag" }, +/* 0x1d */ { "HiliteColor", RGB_LEN, NULL, "RGB hilite color" }, +/* 0x1e */ { "DefHilite", 0, NULL, "Use default hilite color" }, +/* 0x1f */ { "OpColor", NA, OpColor, "RGB OpColor for arithmetic modes" }, +/* 0x20 */ { "Line", 8, Line, "pnLoc (point), newPt (point)" }, +/* 0x21 */ { "LineFrom", 4, LineFrom, "newPt (point)" }, +/* 0x22 */ { "ShortLine", 6, ShortLine, + "pnLoc (point, dh, dv (-128 .. 127))" }, +/* 0x23 */ { "ShortLineFrom", 2, ShortLineFrom, "dh, dv (-128 .. 127)" }, +/* 0x24 */ res(WORD_LEN), +/* 0x25 */ res(WORD_LEN), +/* 0x26 */ res(WORD_LEN), +/* 0x27 */ res(WORD_LEN), +/* 0x28 */ { "LongText", NA, LongText, + "txLoc (point), count (0..255), text" }, +/* 0x29 */ { "DHText", NA, DHText, "dh (0..255), count (0..255), text" }, +/* 0x2a */ { "DVText", NA, DVText, "dv (0..255), count (0..255), text" }, +/* 0x2b */ { "DHDVText", NA, DHDVText, + "dh, dv (0..255), count (0..255), text" }, +/* 0x2c */ res(WORD_LEN), +/* 0x2d */ res(WORD_LEN), +/* 0x2e */ res(WORD_LEN), +/* 0x2f */ res(WORD_LEN), +/* 0x30 */ { "frameRect", 8, frameRect, "rect" }, +/* 0x31 */ { "paintRect", 8, paintRect, "rect" }, +/* 0x32 */ { "eraseRect", 8, NULL, "rect" }, +/* 0x33 */ { "invertRect", 8, NULL, "rect" }, +/* 0x34 */ { "fillRect", 8, NULL, "rect" }, +/* 0x35 */ res(8), +/* 0x36 */ res(8), +/* 0x37 */ res(8), +/* 0x38 */ { "frameSameRect", 0, frameSameRect, "rect" }, +/* 0x39 */ { "paintSameRect", 0, paintSameRect, "rect" }, +/* 0x3a */ { "eraseSameRect", 0, NULL, "rect" }, +/* 0x3b */ { "invertSameRect", 0, NULL, "rect" }, +/* 0x3c */ { "fillSameRect", 0, NULL, "rect" }, +/* 0x3d */ res(0), +/* 0x3e */ res(0), +/* 0x3f */ res(0), +/* 0x40 */ { "frameRRect", 8, NULL, "rect" }, +/* 0x41 */ { "paintRRect", 8, NULL, "rect" }, +/* 0x42 */ { "eraseRRect", 8, NULL, "rect" }, +/* 0x43 */ { "invertRRect", 8, NULL, "rect" }, +/* 0x44 */ { "fillRRrect", 8, NULL, "rect" }, +/* 0x45 */ res(8), +/* 0x46 */ res(8), +/* 0x47 */ res(8), +/* 0x48 */ { "frameSameRRect", 0, NULL, "rect" }, +/* 0x49 */ { "paintSameRRect", 0, NULL, "rect" }, +/* 0x4a */ { "eraseSameRRect", 0, NULL, "rect" }, +/* 0x4b */ { "invertSameRRect", 0, NULL, "rect" }, +/* 0x4c */ { "fillSameRRect", 0, NULL, "rect" }, +/* 0x4d */ res(0), +/* 0x4e */ res(0), +/* 0x4f */ res(0), +/* 0x50 */ { "frameOval", 8, NULL, "rect" }, +/* 0x51 */ { "paintOval", 8, NULL, "rect" }, +/* 0x52 */ { "eraseOval", 8, NULL, "rect" }, +/* 0x53 */ { "invertOval", 8, NULL, "rect" }, +/* 0x54 */ { "fillOval", 8, NULL, "rect" }, +/* 0x55 */ res(8), +/* 0x56 */ res(8), +/* 0x57 */ res(8), +/* 0x58 */ { "frameSameOval", 0, NULL, "rect" }, +/* 0x59 */ { "paintSameOval", 0, NULL, "rect" }, +/* 0x5a */ { "eraseSameOval", 0, NULL, "rect" }, +/* 0x5b */ { "invertSameOval", 0, NULL, "rect" }, +/* 0x5c */ { "fillSameOval", 0, NULL, "rect" }, +/* 0x5d */ res(0), +/* 0x5e */ res(0), +/* 0x5f */ res(0), +/* 0x60 */ { "frameArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x61 */ { "paintArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x62 */ { "eraseArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x63 */ { "invertArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x64 */ { "fillArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x65 */ res(12), +/* 0x66 */ res(12), +/* 0x67 */ res(12), +/* 0x68 */ { "frameSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x69 */ { "paintSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x6a */ { "eraseSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x6b */ { "invertSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x6c */ { "fillSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x6d */ res(4), +/* 0x6e */ res(4), +/* 0x6f */ res(4), +/* 0x70 */ { "framePoly", NA, skip_poly_or_region, "poly" }, +/* 0x71 */ { "paintPoly", NA, paintPoly, "poly" }, +/* 0x72 */ { "erasePoly", NA, skip_poly_or_region, "poly" }, +/* 0x73 */ { "invertPoly", NA, skip_poly_or_region, "poly" }, +/* 0x74 */ { "fillPoly", NA, skip_poly_or_region, "poly" }, +/* 0x75 */ resf(skip_poly_or_region), +/* 0x76 */ resf(skip_poly_or_region), +/* 0x77 */ resf(skip_poly_or_region), +/* 0x78 */ { "frameSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x79 */ { "paintSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x7a */ { "eraseSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x7b */ { "invertSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x7c */ { "fillSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x7d */ res(0), +/* 0x7e */ res(0), +/* 0x7f */ res(0), +/* 0x80 */ { "frameRgn", NA, skip_poly_or_region, "region" }, +/* 0x81 */ { "paintRgn", NA, skip_poly_or_region, "region" }, +/* 0x82 */ { "eraseRgn", NA, skip_poly_or_region, "region" }, +/* 0x83 */ { "invertRgn", NA, skip_poly_or_region, "region" }, +/* 0x84 */ { "fillRgn", NA, skip_poly_or_region, "region" }, +/* 0x85 */ resf(skip_poly_or_region), +/* 0x86 */ resf(skip_poly_or_region), +/* 0x87 */ resf(skip_poly_or_region), +/* 0x88 */ { "frameSameRgn", 0, NULL, "region (NYI)" }, +/* 0x89 */ { "paintSameRgn", 0, NULL, "region (NYI)" }, +/* 0x8a */ { "eraseSameRgn", 0, NULL, "region (NYI)" }, +/* 0x8b */ { "invertSameRgn", 0, NULL, "region (NYI)" }, +/* 0x8c */ { "fillSameRgn", 0, NULL, "region (NYI)" }, +/* 0x8d */ res(0), +/* 0x8e */ res(0), +/* 0x8f */ res(0), +/* 0x90 */ { "BitsRect", NA, BitsRect, "copybits, rect clipped" }, +/* 0x91 */ { "BitsRgn", NA, BitsRegion, "copybits, rgn clipped" }, +/* 0x92 */ res(WORD_LEN), +/* 0x93 */ res(WORD_LEN), +/* 0x94 */ res(WORD_LEN), +/* 0x95 */ res(WORD_LEN), +/* 0x96 */ res(WORD_LEN), +/* 0x97 */ res(WORD_LEN), +/* 0x98 */ { "PackBitsRect", NA, BitsRect, "packed copybits, rect clipped" }, +/* 0x99 */ { "PackBitsRgn", NA, BitsRegion, "packed copybits, rgn clipped" }, +/* 0x9a */ { "DirectBitsRect", NA, DirectBitsRect, + "PixMap, srcRect, dstRect, int copymode, PixData" }, +/* 0x9b */ { "DirectBitsRgn", NA, DirectBitsRgn, + "PixMap, srcRect, dstRect, int copymode, maskRgn, PixData" }, +/* 0x9c */ res(WORD_LEN), +/* 0x9d */ res(WORD_LEN), +/* 0x9e */ res(WORD_LEN), +/* 0x9f */ res(WORD_LEN), +/* 0xa0 */ { "ShortComment", 2, ShortComment, "kind (word)" }, +/* 0xa1 */ { "LongComment", NA, LongComment, + "kind (word), size (word), data" } +}; + + + +static void +interpret_pict(void) { + byte ch; + word picSize; + word opcode; + word len; + unsigned int version; + int i; + struct rgbPlanes planes; + + for (i = 0; i < 64; i++) + pen_pat.pix[i] = bkpat.pix[i] = fillpat.pix[i] = 1; + pen_width = pen_height = 1; + pen_mode = 0; /* srcCopy */ + pen_trf = transfer(pen_mode); + text_mode = 0; /* srcCopy */ + text_trf = transfer(text_mode); + + stage = "Reading picture size"; + picSize = read_word(); + + if (verbose) + pm_message("picture size = %d (0x%x)", picSize, picSize); + + stage = "reading picture frame"; + read_rect(&picFrame); + + if (verbose) { + dumpRect("Picture frame:", picFrame); + pm_message("Picture size is %d x %d", + picFrame.right - picFrame.left, + picFrame.bottom - picFrame.top); + } + + if (!fullres) + allocPlanes(&planes); + + while ((ch = read_byte()) == 0) + ; + if (ch != 0x11) + pm_error("No version number"); + + version = read_byte(); + + switch (version) { + case 1: + break; + case 2: { + unsigned char const subcode = read_byte(); + if (subcode != 0xff) + pm_error("The only Version 2 PICT images this program " + "undertands are subcode 0xff. This image has " + "subcode 0x%02x", subcode); + } break; + default: + pm_error("Unrecognized PICT version %u", version); + } + + if (verbose) + pm_message("PICT version %u", version); + + while((opcode = get_op(version)) != 0xff) { + if (opcode < 0xa2) { + stage = optable[opcode].name; + if (verbose) { + if (STREQ(stage, "reserved")) + pm_message("reserved opcode=0x%x", opcode); + else + pm_message("Opcode: %s", optable[opcode].name); + } + + if (optable[opcode].impl != NULL) + (*optable[opcode].impl)(version); + else if (optable[opcode].len >= 0) + skip(optable[opcode].len); + else switch (optable[opcode].len) { + case WORD_LEN: + len = read_word(); + skip(len); + break; + default: + pm_error("can't do length %u", optable[opcode].len); + } + } else if (opcode == 0xc00) { + if (verbose) + pm_message("HeaderOp"); + stage = "HeaderOp"; + skip(24); + } else if (opcode >= 0xa2 && opcode <= 0xaf) { + stage = "skipping reserved"; + if (verbose) + pm_message("%s 0x%x", stage, opcode); + skip(read_word()); + } else if (opcode >= 0xb0 && opcode <= 0xcf) { + /* just a reserved opcode, no data */ + if (verbose) + pm_message("reserved 0x%x", opcode); + } else if (opcode >= 0xd0 && opcode <= 0xfe) { + stage = "skipping reserved"; + if (verbose) + pm_message("%s 0x%x", stage, opcode); + skip(read_long()); + } else if (opcode >= 0x100 && opcode <= 0x7fff) { + stage = "skipping reserved"; + if (verbose) + pm_message("%s 0x%x", stage, opcode); + skip((opcode >> 7) & 255); + } else if (opcode >= 0x8000 && opcode <= 0x80ff) { + /* just a reserved opcode */ + if (verbose) + pm_message("reserved 0x%x", opcode); + } else if (opcode >= 0x8100) { + stage = "skipping reserved"; + if (verbose) + pm_message("%s 0x%x", stage, opcode); + skip(read_long()); + } else + pm_error("This program does not understand opcode 0x%04x", opcode); + } + + if (fullres) + do_blits(&planes); + + outputPpm(planes); + + freePlanes(planes); +} + + + +int +main(int argc, char * argv[]) { + int argn; + int header; + const char* const usage = +"[-verbose] [-fullres] [-noheader] [-quickdraw] [-fontdir file] [pictfile]"; + + ppm_init( &argc, argv ); + + argn = 1; + verbose = 0; + fullres = 0; + header = 1; + recognize_comment = 1; + + while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { + if (pm_keymatch(argv[argn], "-verbose", 2)) + verbose++; + else if (pm_keymatch(argv[argn], "-fullres", 3)) + fullres = 1; + else if (pm_keymatch(argv[argn], "-noheader", 2)) + header = 0; + else if (pm_keymatch(argv[argn], "-quickdraw", 2)) + recognize_comment = 0; + else if (pm_keymatch(argv[argn], "-fontdir", 3)) { + argn++; + if (!argv[argn]) + pm_usage(usage); + else + load_fontdir(argv[argn]); + } + else + pm_usage(usage); + ++argn; + } + + load_fontdir("fontdir"); + + if (argn < argc) { + ifp = pm_openr(argv[argn]); + ++argn; + } else + ifp = stdin; + + if (argn != argc) + pm_usage(usage); + + if (header) { + stage = "Reading 512 byte header"; + skip(512); + } + + interpret_pict(); + + return 0; +} |