1 /*
2  * picttoppm.c -- convert a MacIntosh PICT file to PPM format.
3  *
4  * Copyright 1989,1992,1993 George Phillips
5  *
6  * Permission to use, copy, modify, and distribute this software and its
7  * documentation for any purpose and without fee is hereby granted, provided
8  * that the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation.  This software is provided "as is" without express or
11  * implied warranty.
12  *
13  * George Phillips <phillips@cs.ubc.ca>
14  * Department of Computer Science
15  * University of British Columbia
16  *
17  *
18  * 2003-02:    Handling for DirectBitsRgn opcode (0x9b) added by
19  *             kabe@sra-tohoku.co.jp.
20  *
21  * 2004-03-27: Several bugs fixed by Steve Summit, scs@eskimo.com.
22  *
23  */
24 
25 #define _XOPEN_SOURCE
26 
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <assert.h>
32 
33 #include "pm_c_util.h"
34 #include "ppm.h"
35 #include "pbmfont.h"
36 #include "mallocvar.h"
37 #include "nstring.h"
38 
39 
40 /*
41  * Typical byte, 2 byte and 4 byte integers.
42  */
43 typedef unsigned char Byte;
44 typedef char SignedByte;
45 typedef unsigned short Word;
46 typedef unsigned long Longword;
47 
48 
49 /*
50  * Data structures for QuickDraw (and hence PICT) stuff.
51  */
52 
53 struct Rect {
54 /*----------------------------------------------------------------------------
55    A rectangle - description of a region of an image raster.
56 
57    If last row or column is before first, it is a null rectangle - it
58    describes no pixels.
59 -----------------------------------------------------------------------------*/
60     Word top;     /* Start row */
61     Word left;    /* Start column */
62     Word bottom;  /* End row */
63     Word right;   /* End column */
64 
65     /* "End" means last plus one */
66 };
67 
68 struct pixMap {
69     struct Rect Bounds;
70     Word version;
71     Word packType;
72     Longword packSize;
73     Longword hRes;
74     Longword vRes;
75     Word pixelType;
76     Word pixelSize;
77     Word cmpCount;
78     Word cmpSize;
79     Longword planeBytes;
80     Longword pmTable;
81     Longword pmReserved;
82 };
83 
84 struct RGBColor {
85     Word red;
86     Word grn;
87     Word blu;
88 };
89 
90 struct Point {
91     Word x;
92     Word y;
93 };
94 
95 struct Pattern {
96     Byte pix[64];
97 };
98 
99 struct RgbPlanes {
100 /*----------------------------------------------------------------------------
101    A raster, as three planes: red, green, blue.
102 
103    Each plane is an array in row-major order.
104 -----------------------------------------------------------------------------*/
105     unsigned int width;
106     unsigned int height;
107     Word * red;
108     Word * grn;
109     Word * blu;
110 };
111 
112 struct canvas {
113     struct RgbPlanes planes;
114 };
115 
116 typedef void (*transfer_func) (struct RGBColor* src, struct RGBColor* dst);
117 
118 static const char * stage;
119 static struct Rect picFrame;
120 static Word rowlen;
121 static Word collen;
122 static int verbose;
123 static int fullres;
124 static int recognize_comment;
125 
126 static struct RGBColor black = { 0, 0, 0 };
127 static struct RGBColor white = { 0xffff, 0xffff, 0xffff };
128 
129 /* various bits of drawing state */
130 static struct RGBColor foreground = { 0, 0, 0 };
131 static struct RGBColor background = { 0xffff, 0xffff, 0xffff };
132 static struct RGBColor op_color;
133 static struct Pattern bkpat;
134 static struct Pattern fillpat;
135 static struct Rect clip_rect;
136 static struct Rect cur_rect;
137 static struct Point current;
138 static struct Pattern pen_pat;
139 static Word pen_width;
140 static Word pen_height;
141 static Word pen_mode;
142 static transfer_func pen_trf;
143 static Word text_font;
144 static Byte text_face;
145 static Word text_mode;
146 static transfer_func text_trf;
147 static Word text_size;
148 static struct font* tfont;
149 
150 /* state for magic printer comments */
151 static int ps_text;
152 static Byte ps_just;
153 static Byte ps_flip;
154 static Word ps_rotation;
155 static Byte ps_linespace;
156 static int ps_cent_x;
157 static int ps_cent_y;
158 static int ps_cent_set;
159 
160 struct raster {
161 /*----------------------------------------------------------------------------
162    An image raster.  May be either truecolor or paletted.
163 
164    This is an array of pixels in row-major order, with 'rowSize'
165    bytes per row, 'rowCount' high.
166 
167    Within a row, pixels go left to right.  The rows go top to bottom.
168 
169    Each pixel is either a palette index or an RGB triple, depending on
170    the format of the associated PICT.
171 
172    Each pixel is one byte if the associated PICT has 8 or fewer bits
173    per pixel.  If the associated PICT has 16 or 32 bits per pixel, an
174    element herein is 2 or 4 bytes, respectively.
175 
176    For 16 bits per pixel, the two bytes for each pixel encode RGB values
177    as described in decode16().
178 
179    For 32 bits per pixel, each row is divided into 4 planes.  Red,
180    green, blue, and something else, in that order.  The format of a
181    plane is one byte per pixel, left to right.
182 -----------------------------------------------------------------------------*/
183     unsigned char * bytes;  /* malloc'ed */
184     unsigned int rowSize;
185     unsigned int rowCount;
186 };
187 
188 
189 
190 static void
allocateRaster(struct raster * const rasterP,unsigned int const width,unsigned int const height,unsigned int const bitsPerPixel)191 allocateRaster(struct raster * const rasterP,
192                unsigned int    const width,
193                unsigned int    const height,
194                unsigned int    const bitsPerPixel) {
195 /*----------------------------------------------------------------------------
196    Allocate storage for a raster that can contain a 'width' x 'height'
197    pixel rectangle read from a PICT image with 'bitsPerPixel' bits
198    per pixel.
199 
200    Make the space large enough to round the number of pixels up to a
201    multiple of 16, because we've seen many images in which the PICT raster
202    does contain that much padding on the right.  I don't know why; I could
203    understand a multiple of 8, since in 1 bpp image, the smallest unit
204    expressible in PICT is 8 pixels.  But why 16?  The images we saw came
205    from Adobe Illustrator 10 in March 2007, supplied by
206    Guillermo Gomez Valcarcel.
207 -----------------------------------------------------------------------------*/
208     unsigned int const allocWidth = ROUNDUP(width, 16);
209 
210     if (width > UINT_MAX/4 - 16)
211         pm_error("Width %u pixels too large for arithmetic", width);
212 
213     rasterP->rowCount = height;
214 
215     switch (bitsPerPixel) {
216     case 32:
217         /* TODO: I'm still trying to figure out this format.
218 
219            My theory today:
220            The row data is in plane order (a row consists of red
221            plane, then, green, then blue, then some 4th plane).
222 
223            The old hack code said 3 bytes per pixel here, and could get
224            away with it because it never got to decoding the 4th plane.
225 
226            But the new clean code needs to tell it like it is and allocate
227            4 bytes per pixel.  If we say 3 bytes per pixel here, we get an
228            "invalid PICT" error because the image actually contains 4
229            bytes per pixel and as we decompress it, we run out of place
230            to put the data.
231 
232            We have yet to see if we can properly interpret the data.
233         */
234 
235         rasterP->rowSize = allocWidth * 4;
236         break;
237     case 16:
238         rasterP->rowSize = allocWidth * 2;
239         break;
240     case 8:
241     case 4:
242     case 2:
243     case 1:
244         rasterP->rowSize = allocWidth * 1;
245         break;
246     default:
247         pm_error("INTERNAL ERROR: impossible bitsPerPixel value in "
248                  "unpackbits(): %u", bitsPerPixel);
249     }
250     if (UINT_MAX / rasterP->rowSize < rasterP->rowCount)
251         pm_error("Arithmetic overflow computing size of %u x %u pixel "
252                  "array.", rasterP->rowSize, rasterP->rowCount);
253 
254     MALLOCARRAY(rasterP->bytes, rasterP->rowSize * rasterP->rowCount);
255     if (rasterP->bytes == NULL)
256         pm_error("unable to get memory for %u x %u pixel packbits rectangle",
257                  width, height);
258 }
259 
260 
261 static void
freeRaster(struct raster const raster)262 freeRaster(struct raster const raster) {
263 
264     free(raster.bytes);
265 }
266 
267 
268 struct blit_info {
269     struct Rect        srcRect;
270     struct Rect        srcBounds;
271     struct raster      srcplane;
272     int                pixSize;
273     struct Rect        dstRect;
274     struct RGBColor *  colorMap;
275     int                mode;
276     struct blit_info * next;
277 };
278 
279 typedef struct {
280     struct blit_info * firstP;
281     struct blit_info ** connectorP;
282     bool unblittableText;
283         /* The image contains text opcodes, and we don't know how to put that
284            in a blit list (I really don't even know what a blit _is_), so
285            the image information here is incomplete.
286         */
287 } blitList;
288 
289 
290 typedef void (drawFn)(struct canvas *, blitList *, int);
291 
292 struct opdef {
293     const char* name;
294     int len;
295         /* If non-negative, this is the length of the argument of the
296            instruction.  If negative, it has special meaning; WORD_LEN
297            is the only value negative value.
298         */
299     drawFn * impl;
300     const char* description;
301 };
302 
303 #define WORD_LEN (-1)
304 
305 /*
306  * a table of the first 194(?) opcodes.  The table is too empty.
307  *
308  * Probably could use an entry specifying if the opcode is valid in version
309  * 1, etc.
310  */
311 
312 /* for reserved opcodes of known length */
313 #define RESERVED_OP(length)                                   \
314 { "reserved", (length), NULL, "reserved for Apple use" }
315 
316 /* for reserved opcodes of length determined by a function */
317 #define RESERVED_OP_F(skipfunction) \
318 { "reserved", NA, (skipfunction), "reserved for Apple use" }
319 
320 /* seems like RGB colors are 6 bytes, but Apple says they're variable */
321 /* I'll use 6 for now as I don't care that much. */
322 #define RGB_LEN (6)
323 
324 
325 static FILE* ifp;
326 static int align = 0;
327 
328 
329 
330 static Byte
readByte(void)331 readByte(void) {
332     int c;
333 
334     if ((c = fgetc(ifp)) == EOF)
335         pm_error("EOF / read error while %s", stage);
336 
337     ++align;
338     return c & 255;
339 }
340 
341 
342 
343 static Word
readWord(void)344 readWord(void) {
345 
346     Byte const hi = readByte();
347     Byte const lo = readByte();
348 
349     return (hi << 8) | (lo << 0);
350 }
351 
352 
353 
readPoint(struct Point * const p)354 static void readPoint(struct Point * const p) {
355     p->y = readWord();
356     p->x = readWord();
357 }
358 
359 
360 
361 static Longword
readLong(void)362 readLong(void) {
363     Word const hi = readWord();
364     Word const lo = readWord();
365 
366     return (hi << 16) | (lo << 0);
367 }
368 
369 
370 
371 static SignedByte
readSignedByte(void)372 readSignedByte(void) {
373     return (SignedByte)readByte();
374 }
375 
376 
377 
378 static void
readShortPoint(struct Point * const p)379 readShortPoint(struct Point * const p) {
380     p->x = readSignedByte();
381     p->y = readSignedByte();
382 }
383 
384 
385 
386 static void
skip(int const byteCount)387 skip(int const byteCount) {
388     static Byte buf[1024];
389     int n;
390 
391     align += byteCount;
392 
393     for (n = byteCount; n > 0; n -= 1024)
394         if (fread(buf, n > 1024 ? 1024 : n, 1, ifp) != 1)
395             pm_error("EOF / read error while %s", stage);
396 }
397 
398 
399 
400 struct const_name {
401     int value;
402     const char * name;
403 };
404 
405 struct const_name const transfer_name[] = {
406     { 0,    "srcCopy" },
407     { 1,    "srcOr" },
408     { 2,    "srcXor" },
409     { 3,    "srcBic" },
410     { 4,    "notSrcCopy" },
411     { 5,    "notSrcOr" },
412     { 6,    "notSrcXor" },
413     { 7,    "notSrcBic" },
414     { 32,   "blend" },
415     { 33,   "addPin" },
416     { 34,   "addOver" },
417     { 35,   "subPin" },
418     { 36,   "transparent" },
419     { 37,   "adMax" },
420     { 38,   "subOver" },
421     { 39,   "adMin" },
422     { -1,   0 }
423 };
424 
425 struct const_name font_name[] = {
426     { 0,    "systemFont" },
427     { 1,    "applFont" },
428     { 2,    "newYork" },
429     { 3,    "geneva" },
430     { 4,    "monaco" },
431     { 5,    "venice" },
432     { 6,    "london" },
433     { 7,    "athens" },
434     { 8,    "sanFran" },
435     { 9,    "toronto" },
436     { 11,   "cairo" },
437     { 12,   "losAngeles" },
438     { 20,   "times" },
439     { 21,   "helvetica" },
440     { 22,   "courier" },
441     { 23,   "symbol" },
442     { 24,   "taliesin" },
443     { -1,   0 }
444 };
445 
446 struct const_name ps_just_name[] = {
447     { 0,    "no" },
448     { 1,    "left" },
449     { 2,    "center" },
450     { 3,    "right" },
451     { 4,    "full" },
452     { -1,   0 }
453 };
454 
455 struct const_name ps_flip_name[] = {
456     { 0,    "no" },
457     { 1,    "horizontal" },
458     { 2,    "vertical" },
459     { -1,   0 }
460 };
461 
462 
463 
464 static const char*
const_name(const struct const_name * const table,unsigned int const ct)465 const_name(const struct const_name * const table,
466            unsigned int              const ct) {
467 
468     static char numbuf[32];
469 
470     unsigned int i;
471 
472     for (i = 0; table[i].name; ++i)
473         if (table[i].value == ct)
474             return table[i].name;
475 
476     sprintf(numbuf, "? (%u)", ct);
477     return numbuf;
478 }
479 
480 
481 
482 static void
picComment(Word const type,int const length)483 picComment(Word const type,
484            int const length) {
485 
486     unsigned int remainingLength;
487 
488     switch (type) {
489     case 150:
490         if (verbose) pm_message("TextBegin");
491         if (length >= 6) {
492             ps_just = readByte();
493             ps_flip = readByte();
494             ps_rotation = readWord();
495             ps_linespace = readByte();
496             remainingLength = length - 5;
497             if (recognize_comment)
498                 ps_text = 1;
499             ps_cent_set = 0;
500             if (verbose) {
501                 pm_message("%s justification, %s flip, %d degree rotation, "
502                            "%d/2 linespacing",
503                            const_name(ps_just_name, ps_just),
504                            const_name(ps_flip_name, ps_flip),
505                            ps_rotation, ps_linespace);
506             }
507         } else
508             remainingLength = length;
509         break;
510     case 151:
511         if (verbose) pm_message("TextEnd");
512         ps_text = 0;
513         remainingLength = length;
514         break;
515     case 152:
516         if (verbose) pm_message("StringBegin");
517         remainingLength = length;
518         break;
519     case 153:
520         if (verbose) pm_message("StringEnd");
521         remainingLength = length;
522         break;
523     case 154:
524         if (verbose) pm_message("TextCenter");
525         if (length < 8)
526             remainingLength = length;
527         else {
528             ps_cent_y = readWord();
529             if (ps_cent_y > 32767)
530                 ps_cent_y -= 65536;
531             skip(2); /* ignore fractional part */
532             ps_cent_x = readWord();
533             if (ps_cent_x > 32767)
534                 ps_cent_x -= 65536;
535             skip(2); /* ignore fractional part */
536             remainingLength = length - 8;
537             if (verbose)
538                 pm_message("offset %d %d", ps_cent_x, ps_cent_y);
539         }
540         break;
541     case 155:
542         if (verbose) pm_message("LineLayoutOff");
543         remainingLength = length;
544         break;
545     case 156:
546         if (verbose) pm_message("LineLayoutOn");
547         remainingLength = length;
548         break;
549     case 160:
550         if (verbose) pm_message("PolyBegin");
551         remainingLength = length;
552         break;
553     case 161:
554         if (verbose) pm_message("PolyEnd");
555         remainingLength = length;
556         break;
557     case 163:
558         if (verbose) pm_message("PolyIgnore");
559         remainingLength = length;
560         break;
561     case 164:
562         if (verbose) pm_message("PolySmooth");
563         remainingLength = length;
564         break;
565     case 165:
566         if (verbose) pm_message("picPlyClo");
567         remainingLength = length;
568         break;
569     case 180:
570         if (verbose) pm_message("DashedLine");
571         remainingLength = length;
572         break;
573     case 181:
574         if (verbose) pm_message("DashedStop");
575         remainingLength = length;
576         break;
577     case 182:
578         if (verbose) pm_message("SetLineWidth");
579         remainingLength = length;
580         break;
581     case 190:
582         if (verbose) pm_message("PostScriptBegin");
583         remainingLength = length;
584         break;
585     case 191:
586         if (verbose) pm_message("PostScriptEnd");
587         remainingLength = length;
588         break;
589     case 192:
590         if (verbose) pm_message("PostScriptHandle");
591         remainingLength = length;
592         break;
593     case 193:
594         if (verbose) pm_message("PostScriptFile");
595         remainingLength = length;
596         break;
597     case 194:
598         if (verbose) pm_message("TextIsPostScript");
599         remainingLength = length;
600         break;
601     case 195:
602         if (verbose) pm_message("ResourcePS");
603         remainingLength = length;
604         break;
605     case 200:
606         if (verbose) pm_message("RotateBegin");
607         remainingLength = length;
608         break;
609     case 201:
610         if (verbose) pm_message("RotateEnd");
611         remainingLength = length;
612         break;
613     case 202:
614         if (verbose) pm_message("RotateCenter");
615         remainingLength = length;
616         break;
617     case 210:
618         if (verbose) pm_message("FormsPrinting");
619         remainingLength = length;
620         break;
621     case 211:
622         if (verbose) pm_message("EndFormsPrinting");
623         remainingLength = length;
624         break;
625     default:
626         if (verbose) pm_message("%d", type);
627         remainingLength = length;
628         break;
629     }
630     if (remainingLength > 0)
631         skip(remainingLength);
632 }
633 
634 
635 
636 static drawFn ShortComment;
637 
638 static void
ShortComment(struct canvas * const canvasP,blitList * const blitListP,int const version)639 ShortComment(struct canvas * const canvasP,
640              blitList *      const blitListP,
641              int             const version) {
642 
643     picComment(readWord(), 0);
644 }
645 
646 
647 
648 static drawFn LongComment;
649 
650 static void
LongComment(struct canvas * const canvasP,blitList * const blitListP,int const version)651 LongComment(struct canvas * const canvasP,
652             blitList *      const blitListP,
653             int             const version) {
654 
655     Word type;
656 
657     type = readWord();
658     picComment(type, readWord());
659 }
660 
661 
662 
663 static drawFn skip_poly_or_region;
664 
665 static void
skip_poly_or_region(struct canvas * const canvasP,blitList * const blitListP,int const version)666 skip_poly_or_region(struct canvas * const canvasP,
667                     blitList *      const blitListP,
668                     int             const version) {
669 
670     stage = "skipping polygon or region";
671     skip(readWord() - 2);
672 }
673 
674 
675 #define NA (0)
676 
677 #define FNT_BOLD    (1)
678 #define FNT_ITALIC  (2)
679 #define FNT_ULINE   (4)
680 #define FNT_OUTLINE (8)
681 #define FNT_SHADOW  (16)
682 #define FNT_CONDENSE    (32)
683 #define FNT_EXTEND  (64)
684 
685 /* Some font searching routines */
686 
687 struct fontinfo {
688     int font;
689     int size;
690     int style;
691     char* filename;
692     struct font* loaded;
693     struct fontinfo* next;
694 };
695 
696 static struct fontinfo* fontlist = 0;
697 static struct fontinfo** fontlist_ins = &fontlist;
698 
699 
700 
701 static void
tokenize(char * const s,const char ** const vec,unsigned int const vecSize,unsigned int * const nTokenP)702 tokenize(char *         const s,
703          const char **  const vec,
704          unsigned int   const vecSize,
705          unsigned int * const nTokenP) {
706 
707     unsigned int nToken;
708     char * p;
709 
710     p = &s[0];   /* start at beginning of string */
711     nToken = 0;  /* no tokens yet */
712 
713     while (*p && nToken < vecSize - 1) {
714         if (ISSPACE(*p))
715             *p++ = '\0';
716         else {
717             vec[nToken++] = p;
718             /* Skip to next non-space character or end */
719             while (*p && !ISSPACE(*p))
720                 ++p;
721         }
722     }
723     vec[nToken] = NULL;
724 
725     *nTokenP = nToken;
726 }
727 
728 
729 
730 static void
parseFontLine(const char ** const token,struct fontinfo ** const fontinfoPP)731 parseFontLine(const char **      const token,
732               struct fontinfo ** const fontinfoPP) {
733 
734     struct fontinfo * fontinfoP;
735 
736     MALLOCVAR(fontinfoP);
737     if (fontinfoP == NULL)
738         pm_error("out of memory for font information");
739     MALLOCARRAY(fontinfoP->filename, strlen(token[3] + 1));
740     if (fontinfoP->filename == NULL)
741         pm_error("out of memory for font information file name");
742 
743     fontinfoP->font  = atoi(token[0]);
744     fontinfoP->size  = atoi(token[1]);
745     fontinfoP->style = atoi(token[2]);
746     strcpy(fontinfoP->filename, token[3]);
747     fontinfoP->loaded = 0;
748 
749     *fontinfoPP = fontinfoP;
750 }
751 
752 
753 
754 static int
load_fontdir(const char * const dirfile)755 load_fontdir(const char * const dirfile) {
756 /*----------------------------------------------------------------------------
757    Load the font directory from file named 'dirfile'.  Add its contents
758    to the global list of fonts 'fontlist'.
759 -----------------------------------------------------------------------------*/
760     FILE * ifP;
761     unsigned int nFont;
762     char line[1024];
763 
764     ifP = pm_openr(dirfile);
765 
766     nFont = 0;
767     while (fgets(line, 1024, ifP) && nFont < INT_MAX) {
768         const char * token[10];
769         unsigned int nToken;
770 
771         tokenize(line, token, ARRAY_SIZE(token), &nToken);
772 
773         if (nToken == 0) {
774             /* blank line - ignore */
775         } else if (token[0][0] == '#') {
776             /* comment - ignore */
777         } else if (nToken != 4) {
778             /* Unrecognized format - ignore */
779         } else {
780             struct fontinfo * fontinfoP;
781 
782             parseFontLine(token, &fontinfoP);
783 
784             fontinfoP->next = 0;
785             *fontlist_ins = fontinfoP;
786             fontlist_ins = &fontinfoP->next;
787             ++nFont;
788         }
789     }
790     pm_close(ifP);
791 
792     return nFont;
793 }
794 
795 
796 
797 static void
dumpRect(const char * const label,struct Rect const rectangle)798 dumpRect(const char * const label,
799          struct Rect  const rectangle) {
800 
801     pm_message("%s (%u,%u) (%u,%u)",
802                label,
803                rectangle.left,  rectangle.top,
804                rectangle.right, rectangle.bottom);
805 }
806 
807 
808 
809 static void
readRect(struct Rect * const r)810 readRect(struct Rect * const r) {
811 
812     /* We don't have a formal specification for the Pict format, but we have
813        seen samples that have the rectangle corners either in top left, bottom
814        right order or bottom right, top left.  top left, bottom right is the
815        only one Picttoppm handled until October 2018, when we saw several
816        images in the bottom right, top left order and other Pict processing
817        programs considered that fine.
818 
819        So now we accept all 4 possibilities.
820     */
821 
822     Word const y1 = readWord();
823     Word const x1 = readWord();
824     Word const y2 = readWord();
825     Word const x2 = readWord();
826 
827     r->top    = MIN(y1, y2);
828     r->left   = MIN(x1, x2);
829     r->bottom = MAX(y1, y2);
830     r->right  = MAX(x1, x2);
831 }
832 
833 
834 
835 static int
rectisnull(struct Rect * const r)836 rectisnull(struct Rect * const r) {
837 
838     return r->top >= r->bottom || r->left >= r->right;
839 }
840 
841 
842 
843 static int
rectwidth(const struct Rect * const r)844 rectwidth(const struct Rect * const r) {
845 
846     return r->right - r->left;
847 }
848 
849 
850 
851 static bool
rectequal(const struct Rect * const comparand,const struct Rect * const comparator)852 rectequal(const struct Rect * const comparand,
853           const struct Rect * const comparator) {
854 
855     return
856         comparand->top    == comparator->top &&
857         comparand->bottom == comparator->bottom &&
858         comparand->left   == comparator->left &&
859         comparand->right  == comparator->right;
860 }
861 
862 
863 static int
rectheight(const struct Rect * const r)864 rectheight(const struct Rect * const r) {
865 
866     return r->bottom - r->top;
867 }
868 
869 
870 
871 static bool
rectsamesize(struct Rect const r1,struct Rect const r2)872 rectsamesize(struct Rect const r1,
873              struct Rect const r2) {
874 
875     return r1.right - r1.left == r2.right - r2.left &&
876            r1.bottom - r1.top == r2.bottom - r2.top ;
877 }
878 
879 
880 
881 static void
rectintersect(const struct Rect * const r1P,const struct Rect * const r2P,struct Rect * const intersectionP)882 rectintersect(const struct Rect * const r1P,
883               const struct Rect * const r2P,
884               struct Rect *       const intersectionP) {
885 /*----------------------------------------------------------------------------
886    Compute the intersection of two rectangles.
887 
888    Note that if the rectangles are disjoint, the result is a null rectangle.
889 -----------------------------------------------------------------------------*/
890     intersectionP->left   = MAX(r1P->left,   r2P->left);
891     intersectionP->top    = MAX(r1P->top,    r2P->top);
892     intersectionP->right  = MIN(r1P->right,  r2P->right);
893     intersectionP->bottom = MIN(r1P->bottom, r2P->bottom);
894 }
895 
896 
897 
898 static void
rectscale(struct Rect * const r,double const xscale,double const yscale)899 rectscale(struct Rect * const r,
900           double        const xscale,
901           double        const yscale) {
902 
903     r->left   *= xscale;
904     r->right  *= xscale;
905     r->top    *= yscale;
906     r->bottom *= yscale;
907 }
908 
909 
910 
911 static void
initBlitList(blitList * const blitListP)912 initBlitList(blitList * const blitListP) {
913 
914     blitListP->firstP          = NULL;
915     blitListP->connectorP      = &blitListP->firstP;
916     blitListP->unblittableText = false;
917 }
918 
919 
920 
921 static void
addBlitList(blitList * const blitListP,struct Rect const srcRect,struct Rect const srcBounds,struct raster const srcplane,int const pixSize,struct Rect const dstRect,struct RGBColor * const colorMap,int const mode)922 addBlitList(blitList *        const blitListP,
923             struct Rect       const srcRect,
924             struct Rect       const srcBounds,
925             struct raster     const srcplane,
926             int               const pixSize,
927             struct Rect       const dstRect,
928             struct RGBColor * const colorMap,
929             int               const mode) {
930 
931     struct blit_info * biP;
932 
933     MALLOCVAR(biP);
934     if (biP == NULL)
935         pm_error("out of memory for blit list");
936     else {
937         biP->srcRect   = srcRect;
938         biP->srcBounds = srcBounds;
939         biP->srcplane  = srcplane;
940         biP->pixSize   = pixSize;
941         biP->dstRect   = dstRect;
942         biP->colorMap  = colorMap;
943         biP->mode      = mode;
944 
945         biP->next = NULL;
946 
947         *blitListP->connectorP = biP;
948         blitListP->connectorP = &biP->next;
949     }
950 }
951 
952 
953 
954 /* Various transfer functions for blits.
955  *
956  * Note src[Not]{Or,Xor,Copy} only work if the source pixmap was originally
957  * a bitmap.
958  * There's also a small bug that the foreground and background colors
959  * are not used in a srcCopy; this wouldn't be hard to fix.
960  * It IS a problem since the foreground and background colors CAN be changed.
961  */
962 
963 static bool
rgbAllSame(const struct RGBColor * const colorP,unsigned int const value)964 rgbAllSame(const struct RGBColor * const colorP,
965            unsigned int            const value) {
966 
967     return (colorP->red == value &&
968             colorP->grn == value &&
969             colorP->blu == value);
970 }
971 
972 
973 static bool
rgbIsWhite(const struct RGBColor * const colorP)974 rgbIsWhite(const struct RGBColor * const colorP) {
975 
976     return rgbAllSame(colorP, 0xffff);
977 }
978 
979 static bool
rgbIsBlack(const struct RGBColor * const colorP)980 rgbIsBlack(const struct RGBColor * const colorP) {
981 
982     return rgbAllSame(colorP, 0);
983 }
984 
985 
986 static void
srcCopy(struct RGBColor * const src,struct RGBColor * const dst)987 srcCopy(struct RGBColor * const src,
988         struct RGBColor * const dst) {
989 
990     if (rgbIsBlack(src))
991         *dst = foreground;
992     else
993         *dst = background;
994 }
995 
996 
997 
998 static void
srcOr(struct RGBColor * const src,struct RGBColor * const dst)999 srcOr(struct RGBColor * const src,
1000       struct RGBColor * const dst) {
1001     if (rgbIsBlack(src))
1002         *dst = foreground;
1003 }
1004 
1005 
1006 
1007 static void
srcXor(struct RGBColor * const src,struct RGBColor * const dst)1008 srcXor(struct RGBColor * const src,
1009        struct RGBColor * const dst) {
1010     dst->red ^= ~src->red;
1011     dst->grn ^= ~src->grn;
1012     dst->blu ^= ~src->blu;
1013 }
1014 
1015 
1016 
1017 static void
srcBic(struct RGBColor * const src,struct RGBColor * const dst)1018 srcBic(struct RGBColor * const src,
1019        struct RGBColor * const dst) {
1020     if (rgbIsBlack(src))
1021         *dst = background;
1022 }
1023 
1024 
1025 
1026 static void
notSrcCopy(struct RGBColor * const src,struct RGBColor * const dst)1027 notSrcCopy(struct RGBColor * const src,
1028            struct RGBColor * const dst) {
1029     if (rgbIsWhite(src))
1030         *dst = foreground;
1031     else if (rgbIsBlack(src))
1032         *dst = background;
1033 }
1034 
1035 
1036 
1037 static void
notSrcOr(struct RGBColor * const src,struct RGBColor * const dst)1038 notSrcOr(struct RGBColor * const src,
1039          struct RGBColor * const dst) {
1040     if (rgbIsWhite(src))
1041         *dst = foreground;
1042 }
1043 
1044 
1045 
1046 static void
notSrcBic(struct RGBColor * const src,struct RGBColor * const dst)1047 notSrcBic(struct RGBColor * const src,
1048           struct RGBColor * const dst) {
1049     if (rgbIsWhite(src))
1050         *dst = background;
1051 }
1052 
1053 
1054 
1055 static void
notSrcXor(struct RGBColor * const src,struct RGBColor * const dst)1056 notSrcXor(struct RGBColor * const src,
1057           struct RGBColor * const dst) {
1058     dst->red ^= src->red;
1059     dst->grn ^= src->grn;
1060     dst->blu ^= src->blu;
1061 }
1062 
1063 
1064 
1065 static void
addOver(struct RGBColor * const src,struct RGBColor * const dst)1066 addOver(struct RGBColor * const src,
1067         struct RGBColor * const dst) {
1068     dst->red += src->red;
1069     dst->grn += src->grn;
1070     dst->blu += src->blu;
1071 }
1072 
1073 
1074 
1075 static void
addPin(struct RGBColor * const src,struct RGBColor * const dst)1076 addPin(struct RGBColor * const src,
1077        struct RGBColor * const dst) {
1078     if ((long)dst->red + (long)src->red > (long)op_color.red)
1079         dst->red = op_color.red;
1080     else
1081         dst->red = dst->red + src->red;
1082 
1083     if ((long)dst->grn + (long)src->grn > (long)op_color.grn)
1084         dst->grn = op_color.grn;
1085     else
1086         dst->grn = dst->grn + src->grn;
1087 
1088     if ((long)dst->blu + (long)src->blu > (long)op_color.blu)
1089         dst->blu = op_color.blu;
1090     else
1091         dst->blu = dst->blu + src->blu;
1092 }
1093 
1094 
1095 
1096 static void
subOver(struct RGBColor * const src,struct RGBColor * const dst)1097 subOver(struct RGBColor * const src,
1098         struct RGBColor * const dst) {
1099     dst->red -= src->red;
1100     dst->grn -= src->grn;
1101     dst->blu -= src->blu;
1102 }
1103 
1104 
1105 
1106 /* or maybe its src - dst; my copy of Inside Mac is unclear */
1107 
1108 
1109 static void
subPin(struct RGBColor * const src,struct RGBColor * const dst)1110 subPin(struct RGBColor * const src,
1111        struct RGBColor * const dst) {
1112     if ((long)dst->red - (long)src->red < (long)op_color.red)
1113         dst->red = op_color.red;
1114     else
1115         dst->red = dst->red - src->red;
1116 
1117     if ((long)dst->grn - (long)src->grn < (long)op_color.grn)
1118         dst->grn = op_color.grn;
1119     else
1120         dst->grn = dst->grn - src->grn;
1121 
1122     if ((long)dst->blu - (long)src->blu < (long)op_color.blu)
1123         dst->blu = op_color.blu;
1124     else
1125         dst->blu = dst->blu - src->blu;
1126 }
1127 
1128 
1129 
1130 static void
adMax(struct RGBColor * const src,struct RGBColor * const dst)1131 adMax(struct RGBColor * const src,
1132       struct RGBColor * const dst) {
1133     if (src->red > dst->red) dst->red = src->red;
1134     if (src->grn > dst->grn) dst->grn = src->grn;
1135     if (src->blu > dst->blu) dst->blu = src->blu;
1136 }
1137 
1138 
1139 
1140 static void
adMin(struct RGBColor * const src,struct RGBColor * const dst)1141 adMin(struct RGBColor * const src,
1142       struct RGBColor * const dst) {
1143     if (src->red < dst->red) dst->red = src->red;
1144     if (src->grn < dst->grn) dst->grn = src->grn;
1145     if (src->blu < dst->blu) dst->blu = src->blu;
1146 }
1147 
1148 
1149 
1150 static void
blend(struct RGBColor * const src,struct RGBColor * const dst)1151 blend(struct RGBColor * const src,
1152       struct RGBColor * const dst) {
1153 #define blend_component(cmp)    \
1154     ((long)src->cmp * (long)op_color.cmp) / 65536 +    \
1155     ((long)dst->cmp * (long)(65536 - op_color.cmp) / 65536)
1156 
1157     dst->red = blend_component(red);
1158     dst->grn = blend_component(grn);
1159     dst->blu = blend_component(blu);
1160 }
1161 
1162 
1163 
1164 static void
transparent(struct RGBColor * const src,struct RGBColor * const dst)1165 transparent(struct RGBColor * const src,
1166             struct RGBColor * const dst) {
1167     if (src->red != background.red ||
1168         src->grn != background.grn ||
1169         src->blu != background.blu) {
1170         *dst = *src;
1171     }
1172 }
1173 
1174 
1175 
1176 static transfer_func
transfer(int const mode)1177 transfer(int const mode) {
1178     switch (mode) {
1179     case  0: return srcCopy;
1180     case  1: return srcOr;
1181     case  2: return srcXor;
1182     case  3: return srcBic;
1183     case  4: return notSrcCopy;
1184     case  5: return notSrcOr;
1185     case  6: return notSrcXor;
1186     case  7: return notSrcBic;
1187     case 32: return blend;
1188     case 33: return addPin;
1189     case 34: return addOver;
1190     case 35: return subPin;
1191     case 36: return transparent;
1192     case 37: return adMax;
1193     case 38: return subOver;
1194     case 39: return adMin;
1195     default:
1196         if (mode != 0)
1197             pm_message("no transfer function for code %s, using srcCopy",
1198                 const_name(transfer_name, mode));
1199         return srcCopy;
1200     }
1201 }
1202 
1203 
1204 
1205 static pixval
redepth(pixval const c,pixval const oldMaxval)1206 redepth(pixval const c,
1207         pixval const oldMaxval) {
1208 
1209     return ROUNDDIV(c * PPM_MAXMAXVAL, oldMaxval);
1210 }
1211 
1212 
1213 
1214 static struct RGBColor
decode16(unsigned char * const sixteen)1215 decode16(unsigned char * const sixteen) {
1216 /*----------------------------------------------------------------------------
1217    Decode a 16 bit PICT encoding of RGB:
1218 
1219       Bit   0:    nothing
1220       Bits  1- 5: red
1221       Bits  6-10: green
1222       Bits 11-15: blue
1223 
1224    'sixteen' is a two byte array.
1225 -----------------------------------------------------------------------------*/
1226     struct RGBColor retval;
1227 
1228     retval.red = (sixteen[0] & 0x7c) >> 2;
1229     retval.grn = (sixteen[0] & 0x03) << 3 | (sixteen[1] & 0xe0) >> 5;
1230     retval.blu = (sixteen[1] & 0x1f) >> 0;
1231 
1232     return retval;
1233 }
1234 
1235 
1236 
1237 static void
closeValidatePamscalePipe(FILE * const pipeP)1238 closeValidatePamscalePipe(FILE * const pipeP) {
1239 
1240     int rc;
1241 
1242     rc = pclose(pipeP);
1243 
1244     if (rc != 0)
1245         pm_error("pamscale failed.  pclose() returned Errno %s (%d)",
1246                  strerror(errno), errno);
1247 }
1248 
1249 
1250 
1251 static void
convertScaledPpm(const char * const scaledFilename,transfer_func const trf,struct RgbPlanes const dst,unsigned int const dstadd)1252 convertScaledPpm(const char *      const scaledFilename,
1253                  transfer_func     const trf,
1254                  struct RgbPlanes  const dst,
1255                  unsigned int      const dstadd) {
1256 
1257     Word * reddst;
1258     Word * grndst;
1259     Word * bludst;
1260     FILE * scaledP;
1261     int cols, rows, format;
1262     pixval maxval;
1263     pixel * pixrow;
1264 
1265     reddst = &dst.red[0];  /* initial value */
1266     grndst = &dst.grn[0];  /* initial value */
1267     bludst = &dst.blu[0];  /* initial value */
1268 
1269     scaledP = pm_openr(scaledFilename);
1270 
1271     ppm_readppminit(scaledP, &cols, &rows, &maxval, &format);
1272 
1273     pixrow = ppm_allocrow(cols);
1274 
1275     if (trf) {
1276         unsigned int row;
1277 
1278         for (row = 0; row < rows; ++row) {
1279             unsigned int col;
1280 
1281             ppm_readppmrow(scaledP, pixrow, cols, maxval, format);
1282 
1283             for (col = 0; col < cols; ++col) {
1284                 struct RGBColor dst_c, src_c;
1285                 dst_c.red = *reddst;
1286                 dst_c.grn = *grndst;
1287                 dst_c.blu = *bludst;
1288                 src_c.red = PPM_GETR(pixrow[col]) * 65536L / (maxval + 1);
1289                 src_c.grn = PPM_GETG(pixrow[col]) * 65536L / (maxval + 1);
1290                 src_c.blu = PPM_GETB(pixrow[col]) * 65536L / (maxval + 1);
1291                 (*trf)(&src_c, &dst_c);
1292                 *reddst++ = dst_c.red;
1293                 *grndst++ = dst_c.grn;
1294                 *bludst++ = dst_c.blu;
1295             }
1296             reddst += dstadd;
1297             grndst += dstadd;
1298             bludst += dstadd;
1299         }
1300     } else {
1301         unsigned int row;
1302 
1303         for (row = 0; row < rows; ++row) {
1304             unsigned int col;
1305 
1306             ppm_readppmrow(scaledP, pixrow, cols, maxval, format);
1307 
1308             for (col = 0; col < cols; ++col) {
1309                 *reddst++ = PPM_GETR(pixrow[col]) * 65536L / (maxval + 1);
1310                 *grndst++ = PPM_GETG(pixrow[col]) * 65536L / (maxval + 1);
1311                 *bludst++ = PPM_GETB(pixrow[col]) * 65536L / (maxval + 1);
1312             }
1313             reddst += dstadd;
1314             grndst += dstadd;
1315             bludst += dstadd;
1316         }
1317     }
1318     assert(reddst == &dst.red[dst.height * dst.width]);
1319     assert(grndst == &dst.grn[dst.height * dst.width]);
1320     assert(bludst == &dst.blu[dst.height * dst.width]);
1321 
1322     ppm_freerow(pixrow);
1323     pm_close(scaledP);
1324 }
1325 
1326 
1327 
1328 static void
doDiffSize(struct Rect const srcRect,struct Rect const dstRect,unsigned int const pixSize,transfer_func const trf,struct RGBColor * const color_map,unsigned char * const src,unsigned int const srcwid,struct RgbPlanes const dst)1329 doDiffSize(struct Rect       const srcRect,
1330            struct Rect       const dstRect,
1331            unsigned int      const pixSize,
1332            transfer_func     const trf,
1333            struct RGBColor * const color_map,
1334            unsigned char *   const src,
1335            unsigned int      const srcwid,
1336            struct RgbPlanes  const dst) {
1337 /*----------------------------------------------------------------------------
1338    Generate the raster in the plane buffers indicated by 'dst'.
1339 
1340    'src' is the source pixels as a row-major array with rows 'srcwid' bytes
1341    long.
1342 -----------------------------------------------------------------------------*/
1343     FILE * pamscalePipeP;
1344     const char * command;
1345     FILE * tempFileP;
1346     const char * tempFilename;
1347 
1348     pm_make_tmpfile(&tempFileP, &tempFilename);
1349 
1350     pm_close(tempFileP);
1351 
1352     pm_asprintf(&command, "pamscale -xsize %u -ysize %u > %s",
1353                 rectwidth(&dstRect), rectheight(&dstRect), tempFilename);
1354 
1355     pm_message("running command '%s'", command);
1356 
1357     pamscalePipeP = popen(command, "w");
1358     if (pamscalePipeP == NULL)
1359         pm_error("cannot execute command '%s'  popen() errno = %s (%d)",
1360                  command, strerror(errno), errno);
1361 
1362     pm_strfree(command);
1363 
1364     fprintf(pamscalePipeP, "P6\n%u %u\n%u\n",
1365             rectwidth(&srcRect), rectheight(&srcRect), PPM_MAXMAXVAL);
1366 
1367     switch (pixSize) {
1368     case 8: {
1369         unsigned int row;
1370         for (row = 0; row < rectheight(&srcRect); ++row) {
1371             unsigned char * const rowBytes = &src[row * srcwid];
1372             unsigned int col;
1373             for (col = 0; col < rectwidth(&srcRect); ++col) {
1374                 unsigned int const colorIndex = rowBytes[col];
1375                 struct RGBColor * const ct = &color_map[colorIndex];
1376                 fputc(redepth(ct->red, 65535L), pamscalePipeP);
1377                 fputc(redepth(ct->grn, 65535L), pamscalePipeP);
1378                 fputc(redepth(ct->blu, 65535L), pamscalePipeP);
1379             }
1380         }
1381     } break;
1382     case 16: {
1383         unsigned int row;
1384         for (row = 0; row < rectheight(&srcRect); ++row) {
1385             unsigned char * const rowBytes = &src[row * srcwid];
1386             unsigned int col;
1387             for (col = 0; col < rectwidth(&srcRect); ++col) {
1388                 struct RGBColor const color = decode16(&rowBytes[col * 2]);
1389                 fputc(redepth(color.red, 32), pamscalePipeP);
1390                 fputc(redepth(color.grn, 32), pamscalePipeP);
1391                 fputc(redepth(color.blu, 32), pamscalePipeP);
1392             }
1393         }
1394     } break;
1395     case 32: {
1396         unsigned int const planeSize = srcwid / 4;
1397         unsigned int row;
1398 
1399         for (row = 0; row < rectheight(&srcRect); ++row) {
1400             unsigned char * const rowBytes = &src[row * srcwid];
1401             unsigned char * const redPlane = &rowBytes[planeSize * 0];
1402             unsigned char * const grnPlane = &rowBytes[planeSize * 1];
1403             unsigned char * const bluPlane = &rowBytes[planeSize * 2];
1404 
1405             unsigned int col;
1406             for (col = 0; col < rectwidth(&srcRect); ++col) {
1407                 fputc(redepth(redPlane[col], 256), pamscalePipeP);
1408                 fputc(redepth(grnPlane[col], 256), pamscalePipeP);
1409                 fputc(redepth(bluPlane[col], 256), pamscalePipeP);
1410             }
1411         }
1412     } break;
1413     } /* switch */
1414 
1415     closeValidatePamscalePipe(pamscalePipeP);
1416 
1417     convertScaledPpm(tempFilename, trf, dst, dst.width-rectwidth(&dstRect));
1418 
1419     pm_strfree(tempFilename);
1420     unlink(tempFilename);
1421 }
1422 
1423 
1424 
1425 static void
getRgb(struct RgbPlanes const planes,unsigned int const index,struct RGBColor * const rgbP)1426 getRgb(struct RgbPlanes  const planes,
1427        unsigned int      const index,
1428        struct RGBColor * const rgbP) {
1429 
1430     rgbP->red = planes.red[index];
1431     rgbP->grn = planes.grn[index];
1432     rgbP->blu = planes.blu[index];
1433 }
1434 
1435 
1436 
1437 static void
putRgb(struct RGBColor const rgb,unsigned int const index,struct RgbPlanes const planes)1438 putRgb(struct RGBColor  const rgb,
1439        unsigned int     const index,
1440        struct RgbPlanes const planes) {
1441 
1442     planes.red[index] = rgb.red;
1443     planes.grn[index] = rgb.grn;
1444     planes.blu[index] = rgb.blu;
1445 }
1446 
1447 
1448 
1449 static void
doSameSize(transfer_func trf,int const pixSize,struct Rect const srcRect,unsigned char * const src,unsigned int const srcwid,struct RGBColor * const color_map,struct RgbPlanes const dst,unsigned int const dstwid)1450 doSameSize(transfer_func           trf,
1451            int               const pixSize,
1452            struct Rect       const srcRect,
1453            unsigned char *   const src,
1454            unsigned int      const srcwid,
1455            struct RGBColor * const color_map,
1456            struct RgbPlanes  const dst,
1457            unsigned int      const dstwid) {
1458 /*----------------------------------------------------------------------------
1459    Transfer pixels from 'src' to 'dst', applying the transfer function
1460    'trf'.
1461 
1462    'src' has the same format as the 'bytes' member of struct raster.
1463    'srcwid' is the size in bytes of each row, like raster.rowSize.
1464 
1465    We use only the first 'ysize' rows and only the first 'xsize'
1466    pixels of each row.
1467 
1468    We really should clean this up so that we can take pixels out of
1469    the middle of a row and rows out of the middle of the raster.  As
1470    it stands, Caller achieves the same result by passing as 'src'
1471    a pointer into the middle of a raster -- the upper left corner of
1472    the rectangle he wants.  But that is messy and nonobvious.
1473 
1474    Each plane of 'dst' is one word per pixel and contains actual
1475    colors, never a palette index.  It is an array in row-major order
1476    with 'dstwid' words per row.
1477 -----------------------------------------------------------------------------*/
1478     unsigned int const xsize = rectwidth(&srcRect);
1479     unsigned int const ysize = rectheight(&srcRect);
1480 
1481     switch (pixSize) {
1482     case 8: {
1483         unsigned int rowNumber;
1484 
1485         for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
1486             unsigned char * const srcrow = &src[rowNumber * srcwid];
1487             unsigned int const dstRowCurs = rowNumber * dstwid;
1488 
1489             unsigned int colNumber;
1490             for (colNumber = 0; colNumber < xsize; ++colNumber) {
1491                 unsigned int const dstCursor = dstRowCurs + colNumber;
1492                 unsigned int const colorIndex = srcrow[colNumber];
1493                 struct RGBColor dstColor;
1494                 getRgb(dst, dstCursor, &dstColor);
1495                 (*trf)(&color_map[colorIndex], &dstColor);
1496                 putRgb(dstColor, dstCursor, dst);
1497             }
1498         }
1499     } break;
1500     case 16: {
1501         unsigned int rowNumber;
1502 
1503         for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
1504             unsigned char * const row = &src[rowNumber * srcwid];
1505             unsigned int const dstRowCurs = rowNumber * dstwid;
1506 
1507             unsigned int colNumber;
1508             for (colNumber = 0; colNumber < xsize; ++colNumber) {
1509                 unsigned int const dstCursor = dstRowCurs + colNumber;
1510                 struct RGBColor const srcColor = decode16(&row[colNumber*2]);
1511                 struct RGBColor dstColor;
1512                 struct RGBColor scaledSrcColor;
1513                 scaledSrcColor.red = srcColor.red << 11;
1514                 scaledSrcColor.grn = srcColor.grn << 11;
1515                 scaledSrcColor.blu = srcColor.blu << 11;
1516                 getRgb(dst, dstCursor, &dstColor);
1517                 (*trf)(&scaledSrcColor, &dstColor);
1518                 putRgb(dstColor, dstCursor, dst);
1519             }
1520         }
1521     } break;
1522     case 32: {
1523         unsigned int const planeSize = srcwid / 4;
1524         unsigned int rowNumber;
1525 
1526         for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
1527             unsigned char * const row = &src[rowNumber * srcwid];
1528             unsigned char * const redPlane = &row[planeSize * 0];
1529             unsigned char * const grnPlane = &row[planeSize * 1];
1530             unsigned char * const bluPlane = &row[planeSize * 2];
1531             unsigned int const dstRowCurs = rowNumber * dstwid;
1532 
1533             unsigned int colNumber;
1534 
1535             for (colNumber = 0; colNumber < xsize; ++colNumber) {
1536                 unsigned int const dstCursor = dstRowCurs + colNumber;
1537                 struct RGBColor srcColor, dstColor;
1538                 getRgb(dst, dstCursor, &dstColor);
1539                 srcColor.red = redPlane[colNumber] << 8;
1540                 srcColor.grn = grnPlane[colNumber] << 8;
1541                 srcColor.blu = bluPlane[colNumber] << 8;
1542                 (*trf)(&srcColor, &dstColor);
1543                 putRgb(dstColor, dstCursor, dst);
1544             }
1545         }
1546     } break;
1547     default:
1548         pm_error("Impossible value of pixSize: %u", pixSize);
1549     }
1550 }
1551 
1552 
1553 
1554 static void
blitIdempotent(unsigned int const pixSize,struct Rect const srcRect,unsigned char * const src,unsigned int const srcwid,struct RGBColor * const colorMap,struct RgbPlanes const dst,unsigned int const dstwid)1555 blitIdempotent(unsigned int          const pixSize,
1556                struct Rect           const srcRect,
1557                unsigned char *       const src,
1558                unsigned int          const srcwid,
1559                struct RGBColor *     const colorMap,
1560                struct RgbPlanes      const dst,
1561                unsigned int          const dstwid) {
1562 /*----------------------------------------------------------------------------
1563   This is the same as doSameSize(), except optimized for the case that
1564   the transfer function is idempotent (i.e. it's just a straight copy).
1565   The original author's comments suggest that this optimization isn't
1566   all that important -- that he just wrote this first and instead of
1567   expanding it to handle arbitrary transfer functions, added functions
1568   for that.
1569 -----------------------------------------------------------------------------*/
1570     unsigned int const xsize = rectwidth(&srcRect);
1571     unsigned int const ysize = rectheight(&srcRect);
1572 
1573     switch (pixSize) {
1574     case 8: {
1575         unsigned int rowNumber;
1576 
1577         for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
1578             unsigned char * const srcrow = &src[rowNumber * srcwid];
1579             unsigned int const dstRowCurs = rowNumber * dstwid;
1580             unsigned int colNumber;
1581             for (colNumber = 0; colNumber < xsize; ++colNumber) {
1582                 unsigned int const dstCursor = dstRowCurs + colNumber;
1583                 struct RGBColor * const ct = colorMap + srcrow[colNumber];
1584                 dst.red[dstCursor] = ct->red;
1585                 dst.grn[dstCursor] = ct->grn;
1586                 dst.blu[dstCursor] = ct->blu;
1587             }
1588         }
1589     } break;
1590     case 16: {
1591         unsigned int rowNumber;
1592 
1593         for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
1594             unsigned char * const srcrow = &src[rowNumber * srcwid];
1595             unsigned int const dstRowCurs = rowNumber * dstwid;
1596             unsigned int colNumber;
1597             for (colNumber = 0; colNumber < xsize; ++colNumber) {
1598                 unsigned int const dstCursor = dstRowCurs + colNumber;
1599                 struct RGBColor const srcColor =
1600                     decode16(&srcrow[colNumber * 2]);
1601                 dst.red[dstCursor] = srcColor.red << 11;
1602                 dst.grn[dstCursor] = srcColor.grn << 11;
1603                 dst.blu[dstCursor] = srcColor.blu << 11;
1604             }
1605         }
1606     } break;
1607     case 32: {
1608         unsigned int const planeSize = srcwid / 4;
1609         unsigned int rowNumber;
1610 
1611         for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
1612             unsigned char * const srcrow = &src[rowNumber * srcwid];
1613             unsigned char * const redPlane = &srcrow[planeSize * 0];
1614             unsigned char * const grnPlane = &srcrow[planeSize * 1];
1615             unsigned char * const bluPlane = &srcrow[planeSize * 2];
1616             unsigned int const dstRowCurs = rowNumber * dstwid;
1617 
1618             unsigned int colNumber;
1619             for (colNumber = 0; colNumber < xsize; ++colNumber) {
1620                 unsigned int const dstCursor = dstRowCurs + colNumber;
1621                 dst.red[dstCursor] = redPlane[colNumber] << 8;
1622                 dst.grn[dstCursor] = grnPlane[colNumber] << 8;
1623                 dst.blu[dstCursor] = bluPlane[colNumber] << 8;
1624             }
1625         }
1626     } break;
1627     default:
1628         pm_error("INTERNAL ERROR: invalid bits per pixel (%u) in "
1629                  "blitIdempotent()", pixSize);
1630     }
1631 }
1632 
1633 
1634 
1635 static void
doBlit(struct Rect const srcRect,struct Rect const dstRect,struct Rect const srcBounds,struct raster const srcplane,struct Rect const dstBounds,struct RgbPlanes const canvasPlanes,int const pixSize,int const dstwid,struct RGBColor * const color_map,int const mode)1636 doBlit(struct Rect       const srcRect,
1637        struct Rect       const dstRect,
1638        struct Rect       const srcBounds,
1639        struct raster     const srcplane,
1640        struct Rect       const dstBounds,
1641        struct RgbPlanes  const canvasPlanes,
1642        int               const pixSize,
1643        int               const dstwid,
1644        struct RGBColor * const color_map,
1645        int               const mode) {
1646 /*----------------------------------------------------------------------------
1647    Transfer some pixels from 'srcplane' to 'canvasPlanes', applying the
1648    transfer function 'trf'.
1649 
1650    'srcplane' contains the rectangle 'srcBounds' of the image.
1651    'canvasPlanes' contains the rectangle 'dstRect' of the image.
1652 
1653    Take the rectangle 'srcRect' of the source image and copy it to the
1654    rectangle 'dstRec' of the destination image.
1655 
1656    Each plane of 'canvasPlanes' is one word per pixel and contains actual
1657    colors, never a palette index.  It is an array in row-major order
1658    with 'dstwid' words per row.
1659 -----------------------------------------------------------------------------*/
1660     unsigned char * src;
1661     struct RgbPlanes dst;
1662     int dstoff;
1663     transfer_func trf;
1664 
1665     if (verbose) {
1666         dumpRect("copying from:", srcRect);
1667         dumpRect("to:          ", dstRect);
1668         pm_message("a %u x %u area to a %u x %u area",
1669                    rectwidth(&srcRect), rectheight(&srcRect),
1670                    rectwidth(&dstRect), rectheight(&dstRect));
1671     }
1672 
1673     {
1674         unsigned int const pkpixsize = pixSize == 16 ? 2 : 1;
1675         unsigned int const srcRowNumber = srcRect.top - srcBounds.top;
1676         unsigned int const srcRowOffset =
1677             (srcRect.left - srcBounds.left) * pkpixsize;
1678         assert(srcRowNumber < srcplane.rowCount);
1679         assert(srcRowOffset < srcplane.rowSize);
1680         src = srcplane.bytes + srcRowNumber * srcplane.rowSize + srcRowOffset;
1681     }
1682 
1683     /* This 'dstoff'/'dstadd' abomination has to be fixed.  We need to pass to
1684        'doDiffSize' the whole actual canvas, 'canvasPlanes', and tell it to
1685        what part of the canvas to write.  It can compute the location of each
1686        destination row as it comes to it.
1687      */
1688     dstoff = (dstRect.top - dstBounds.top) * dstwid +
1689         (dstRect.left - dstBounds.left);
1690     dst.height = canvasPlanes.height - (dstRect.top - dstBounds.top);
1691     dst.width  = canvasPlanes.width;
1692     dst.red = canvasPlanes.red + dstoff;
1693     dst.grn = canvasPlanes.grn + dstoff;
1694     dst.blu = canvasPlanes.blu + dstoff;
1695 
1696     /* get rid of Text mask mode bit, if (erroneously) set */
1697     if ((mode & ~64) == 0)
1698         trf = NULL;    /* optimized srcCopy */
1699     else
1700         trf = transfer(mode & ~64);
1701 
1702     if (!rectsamesize(srcRect, dstRect))
1703         doDiffSize(srcRect, dstRect, pixSize,
1704                    trf, color_map, src, srcplane.rowSize, dst);
1705     else {
1706         if (trf == NULL)
1707             blitIdempotent(pixSize, srcRect, src, srcplane.rowSize,
1708                            color_map, dst, dstwid);
1709         else
1710             doSameSize(trf, pixSize, srcRect, src, srcplane.rowSize,
1711                        color_map, dst, dstwid);
1712     }
1713 }
1714 
1715 
1716 
1717 static int
blit(struct Rect const srcRect,struct Rect const srcBounds,struct raster const srcplane,struct canvas * const canvasP,blitList * const blitListP,int const pixSize,struct Rect const dstRect,struct Rect const dstBounds,int const dstwid,struct RGBColor * const color_map,int const mode)1718 blit(struct Rect       const srcRect,
1719      struct Rect       const srcBounds,
1720      struct raster     const srcplane,
1721      struct canvas *   const canvasP,
1722      blitList *        const blitListP,
1723      int               const pixSize,
1724      struct Rect       const dstRect,
1725      struct Rect       const dstBounds,
1726      int               const dstwid,
1727      struct RGBColor * const color_map,
1728      int               const mode) {
1729 /*----------------------------------------------------------------------------
1730    'srcplane' contains the rectangle 'srcBounds' of the image.
1731 
1732    We transfer rectangle 'srcRect' from that.
1733 
1734    if 'blitListP' is non-null, we don't draw anything on 'canvasP'; instead,
1735    we add to the list *blitlistP a description of what needs to be drawn.
1736 -----------------------------------------------------------------------------*/
1737 
1738     /* I can't tell what the result value of this function is supposed to mean,
1739        but I found several return statements that did not set it to anything,
1740        and several calls that examine it.  I'm guessing that "1" is the
1741        appropriate thing to return in those cases, so I made it so.
1742        -Bryan 00.03.02
1743     */
1744 
1745     int retval;
1746 
1747     if (ps_text)
1748         retval = 1;
1749     else {
1750         /* Almost got it.  Clip source rect with source bounds.
1751            clip dest rect with dest bounds.
1752         */
1753         struct Rect clipsrc;
1754         struct Rect clipdst;
1755 
1756         rectintersect(&srcBounds, &srcRect, &clipsrc);
1757         rectintersect(&dstBounds, &dstRect, &clipdst);
1758 
1759         if (blitListP) {
1760             addBlitList(blitListP,
1761                         clipsrc, srcBounds, srcplane, pixSize,
1762                         clipdst, color_map, mode);
1763 
1764             retval = 0;
1765         } else {
1766             doBlit(clipsrc, clipdst,
1767                    srcBounds, srcplane, dstBounds, canvasP->planes,
1768                    pixSize, dstwid, color_map, mode);
1769 
1770             retval = 1;
1771         }
1772     }
1773     return retval;
1774 }
1775 
1776 
1777 
1778 /* allocation is same for version 1 or version 2.  We are super-duper
1779  * wasteful of memory for version 1 picts.  Someday, we'll separate
1780  * things and only allocate a byte per pixel for version 1 (or heck,
1781  * even only a bit, but that would require even more extra work).
1782  */
1783 
1784 static void
allocPlanes(unsigned int const width,unsigned int const height,struct RgbPlanes * const planesP)1785 allocPlanes(unsigned int       const width,
1786             unsigned int       const height,
1787             struct RgbPlanes * const planesP) {
1788 
1789     unsigned int const planelen = width * height;
1790 
1791     struct RgbPlanes planes;
1792 
1793     planes.width  = width;
1794     planes.height = height;
1795 
1796     MALLOCARRAY(planes.red, planelen);
1797     MALLOCARRAY(planes.grn, planelen);
1798     MALLOCARRAY(planes.blu, planelen);
1799     if (planes.red == NULL || planes.grn == NULL || planes.blu == NULL)
1800         pm_error("not enough memory to hold picture");
1801 
1802     /* initialize background to white */
1803     memset(planes.red, 255, planelen * sizeof(Word));
1804     memset(planes.grn, 255, planelen * sizeof(Word));
1805     memset(planes.blu, 255, planelen * sizeof(Word));
1806 
1807     *planesP = planes;
1808 }
1809 
1810 
1811 
1812 static void
freePlanes(struct RgbPlanes const planes)1813 freePlanes(struct RgbPlanes const planes) {
1814 
1815     free(planes.red);
1816     free(planes.grn);
1817     free(planes.blu);
1818 }
1819 
1820 
1821 
1822 static unsigned char
compact(Word const input)1823 compact(Word const input) {
1824 
1825     return (input >> 8) & 0xff;
1826 }
1827 
1828 
1829 
1830 static void
reportBlitList(blitList * const blitListP)1831 reportBlitList(blitList * const blitListP) {
1832 
1833     if (verbose) {
1834         unsigned int count;
1835         struct blit_info * biP;
1836 
1837         for (count = 0, biP = blitListP->firstP; biP; biP = biP->next)
1838             ++count;
1839 
1840         pm_message("# blits: %u", count);
1841     }
1842 }
1843 
1844 
1845 
1846 static void
doBlitList(struct canvas * const canvasP,blitList * const blitListP)1847 doBlitList(struct canvas * const canvasP,
1848            blitList *      const blitListP) {
1849 /*----------------------------------------------------------------------------
1850    Do the list of blits *blitListP, drawing on canvas *canvasP.
1851 
1852    We allocate new plane data structures in *canvasP.  We assume it doesn't
1853    have them already.
1854 -----------------------------------------------------------------------------*/
1855     struct blit_info * bi;
1856     int srcwidth, dstwidth, srcheight, dstheight;
1857     double  scale, scalelow, scalehigh;
1858     double  xscale = 1.0;
1859     double  yscale = 1.0;
1860     double  lowxscale, highxscale, lowyscale, highyscale;
1861     int     xscalecalc = 0, yscalecalc = 0;
1862 
1863     reportBlitList(blitListP);
1864 
1865     fullres = 0;
1866 
1867     for (bi = blitListP->firstP; bi; bi = bi->next) {
1868         srcwidth = rectwidth(&bi->srcRect);
1869         dstwidth = rectwidth(&bi->dstRect);
1870         srcheight = rectheight(&bi->srcRect);
1871         dstheight = rectheight(&bi->dstRect);
1872         if (srcwidth > dstwidth) {
1873             scalelow  = (double)(srcwidth      ) / (double)dstwidth;
1874             scalehigh = (double)(srcwidth + 1.0) / (double)dstwidth;
1875             switch (xscalecalc) {
1876             case 0:
1877                 lowxscale = scalelow;
1878                 highxscale = scalehigh;
1879                 xscalecalc = 1;
1880                 break;
1881             case 1:
1882                 if (scalelow < highxscale && scalehigh > lowxscale) {
1883                     if (scalelow > lowxscale) lowxscale = scalelow;
1884                     if (scalehigh < highxscale) highxscale = scalehigh;
1885                 }
1886                 else {
1887                     scale = (lowxscale + highxscale) / 2.0;
1888                     xscale = (double)srcwidth / (double)dstwidth;
1889                     if (scale > xscale) xscale = scale;
1890                     xscalecalc = 2;
1891                 }
1892                 break;
1893             case 2:
1894                 scale = (double)srcwidth / (double)dstwidth;
1895                 if (scale > xscale) xscale = scale;
1896                 break;
1897             }
1898         }
1899 
1900         if (srcheight > dstheight) {
1901             scalelow =  (double)(srcheight      ) / (double)dstheight;
1902             scalehigh = (double)(srcheight + 1.0) / (double)dstheight;
1903             switch (yscalecalc) {
1904             case 0:
1905                 lowyscale = scalelow;
1906                 highyscale = scalehigh;
1907                 yscalecalc = 1;
1908                 break;
1909             case 1:
1910                 if (scalelow < highyscale && scalehigh > lowyscale) {
1911                     if (scalelow > lowyscale) lowyscale = scalelow;
1912                     if (scalehigh < highyscale) highyscale = scalehigh;
1913                 }
1914                 else {
1915                     scale = (lowyscale + highyscale) / 2.0;
1916                     yscale = (double)srcheight / (double)dstheight;
1917                     if (scale > yscale) yscale = scale;
1918                     yscalecalc = 2;
1919                 }
1920                 break;
1921             case 2:
1922                 scale = (double)srcheight / (double)dstheight;
1923                 if (scale > yscale) yscale = scale;
1924                 break;
1925             }
1926         }
1927     }
1928 
1929     if (xscalecalc == 1) {
1930         if (1.0 >= lowxscale && 1.0 <= highxscale)
1931             xscale = 1.0;
1932         else
1933             xscale = lowxscale;
1934     }
1935     if (yscalecalc == 1) {
1936         if (1.0 >= lowyscale && 1.0 <= lowyscale)
1937             yscale = 1.0;
1938         else
1939             yscale = lowyscale;
1940     }
1941 
1942     if (xscale != 1.0 || yscale != 1.0) {
1943         struct blit_info * biP;
1944 
1945         for (biP = blitListP->firstP; biP; biP = biP->next)
1946             rectscale(&biP->dstRect, xscale, yscale);
1947 
1948         pm_message("Scaling output by %f in X and %f in Y", xscale, yscale);
1949         rectscale(&picFrame, xscale, yscale);
1950     }
1951 
1952     rowlen = picFrame.right  - picFrame.left;
1953     collen = picFrame.bottom - picFrame.top;
1954 
1955     allocPlanes(rowlen, collen, &canvasP->planes);
1956 
1957     clip_rect = picFrame;
1958 
1959     for (bi = blitListP->firstP; bi; bi = bi->next) {
1960         doBlit(bi->srcRect, bi->dstRect,
1961                bi->srcBounds, bi->srcplane, picFrame, canvasP->planes,
1962                bi->pixSize, rowlen, bi->colorMap, bi->mode);
1963     }
1964 }
1965 
1966 
1967 
1968 static void
outputPpm(FILE * const ofP,struct RgbPlanes const planes)1969 outputPpm(FILE *           const ofP,
1970           struct RgbPlanes const planes) {
1971 
1972     unsigned int width;
1973     unsigned int height;
1974     pixel * pixelrow;
1975     unsigned int row;
1976     unsigned int srcCursor;
1977 
1978     stage = "writing PPM";
1979 
1980     assert(picFrame.right  > picFrame.left);
1981     assert(picFrame.bottom > picFrame.top);
1982 
1983     width  = picFrame.right  - picFrame.left;
1984     height = picFrame.bottom - picFrame.top;
1985 
1986     ppm_writeppminit(ofP, width, height, PPM_MAXMAXVAL, 0);
1987     pixelrow = ppm_allocrow(width);
1988     srcCursor = 0;
1989     for (row = 0; row < height; ++row) {
1990         unsigned int col;
1991         for (col = 0; col < width; ++col) {
1992             PPM_ASSIGN(pixelrow[col],
1993                        compact(planes.red[srcCursor]),
1994                        compact(planes.grn[srcCursor]),
1995                        compact(planes.blu[srcCursor])
1996                 );
1997             ++srcCursor;
1998         }
1999         ppm_writeppmrow(ofP, pixelrow, width, PPM_MAXMAXVAL, 0);
2000     }
2001 }
2002 
2003 
2004 
2005 /*
2006  * All data in version 2 is 2-byte word aligned.  Odd size data
2007  * is padded with a null.
2008  */
2009 static Word
nextOp(int const version)2010 nextOp(int const version) {
2011 
2012     if ((align & 0x1) && version == 2) {
2013         stage = "aligning for opcode";
2014         readByte();
2015     }
2016 
2017     stage = "reading opcode";
2018 
2019     if (version == 1)
2020         return readByte();
2021     else
2022         return readWord();
2023 }
2024 
2025 
2026 
2027 static drawFn ClipRgn;
2028 
2029 static void
ClipRgn(struct canvas * const canvasP,blitList * const blitListP,int const version)2030 ClipRgn(struct canvas * const canvasP,
2031         blitList *      const blitListP,
2032         int             const version) {
2033 
2034     Word const len = readWord();
2035         /* Length in bytes of the parameter (including this word) */
2036 
2037     if (len == 10) {    /* null rgn */
2038         /* Parameter is 2 bytes of length, 8 bytes of rectangle corners */
2039 
2040         /* In March 2011, I saw a supposed PICT file (reported to work with
2041            Apple pictureViewer) with what looked like signed numbers for the
2042            rectangle: (-32767,-32767), (32767, 32767).  This code has always
2043            assumed all words in a PICT are unsigned.  But even when I changed
2044            it to accept this clip rectangle, this program found the image to
2045            have an invalid raster.
2046         */
2047         struct Rect clipRgnParm;
2048 
2049         readRect(&clipRgnParm);
2050 
2051         rectintersect(&clipRgnParm, &picFrame, &clip_rect);
2052 
2053         if (!rectequal(&clipRgnParm, &clip_rect)) {
2054             pm_message("ClipRgn opcode says to clip to a region which "
2055                        "is not contained within the picture frame.  "
2056                        "Ignoring the part outside the picture frame.");
2057             dumpRect("ClipRgn:", clipRgnParm);
2058             dumpRect("Picture frame:", picFrame);
2059         }
2060         if (verbose)
2061             dumpRect("clipping to", clip_rect);
2062     } else
2063         skip(len - 2);
2064 }
2065 
2066 
2067 
2068 static drawFn OpColor;
2069 
2070 static void
OpColor(struct canvas * const canvasP,blitList * const blitListP,int const version)2071 OpColor(struct canvas * const canvasP,
2072         blitList *      const blitListP,
2073         int             const version) {
2074 
2075     op_color.red = readWord();
2076     op_color.grn = readWord();
2077     op_color.blu = readWord();
2078 }
2079 
2080 
2081 
2082 static void
readPixmap(struct pixMap * const p)2083 readPixmap(struct pixMap * const p) {
2084 
2085     stage = "getting pixMap header";
2086 
2087     readRect(&p->Bounds);
2088     p->version    = readWord();
2089     p->packType   = readWord();
2090     p->packSize   = readLong();
2091     p->hRes       = readLong();
2092     p->vRes       = readLong();
2093     p->pixelType  = readWord();
2094     p->pixelSize  = readWord();
2095     p->cmpCount   = readWord();
2096     p->cmpSize    = readWord();
2097     p->planeBytes = readLong();
2098     p->pmTable    = readLong();
2099     p->pmReserved = readLong();
2100 
2101     if (verbose) {
2102         pm_message("pixelType: %d", p->pixelType);
2103         pm_message("pixelSize: %d", p->pixelSize);
2104         pm_message("cmpCount:  %d", p->cmpCount);
2105         pm_message("cmpSize:   %d", p->cmpSize);
2106         if (verbose)
2107             dumpRect("Bounds:", p->Bounds);
2108     }
2109 
2110     if (p->pixelType != 0)
2111         pm_error("sorry, I do only chunky format.  "
2112                  "This image has pixel type %hu", p->pixelType);
2113     if (p->cmpCount != 1)
2114         pm_error("sorry, cmpCount != 1");
2115     if (p->pixelSize != p->cmpSize)
2116         pm_error("oops, pixelSize != cmpSize");
2117 }
2118 
2119 
2120 
2121 static struct RGBColor*
readColorTable(void)2122 readColorTable(void) {
2123     Longword ctSeed;
2124     Word ctFlags;
2125     Word ctSize;
2126     Word val;
2127     int i;
2128     struct RGBColor* color_table;
2129 
2130     stage = "getting color table info";
2131 
2132     ctSeed = readLong();
2133     ctFlags = readWord();
2134     ctSize = readWord();
2135 
2136     if (verbose) {
2137         pm_message("ctSeed:  %ld", ctSeed);
2138         pm_message("ctFlags: %d", ctFlags);
2139         pm_message("ctSize:  %d", ctSize);
2140     }
2141 
2142     stage = "reading color table";
2143 
2144     MALLOCARRAY(color_table, ctSize + 1);
2145     if (color_table == NULL)
2146         pm_error("no memory for color table");
2147 
2148     for (i = 0; i <= ctSize; i++) {
2149         val = readWord();
2150         /* The indices in a device color table are bogus and usually == 0.
2151          * so I assume we allocate up the list of colors in order.
2152          */
2153         if (ctFlags & 0x8000)
2154             val = i;
2155         if (val > ctSize)
2156             pm_error("pixel value greater than color table size");
2157         color_table[val].red = readWord();
2158         color_table[val].grn = readWord();
2159         color_table[val].blu = readWord();
2160 
2161         if (verbose > 1)
2162             pm_message("Color %3u: [%u,%u,%u]", val,
2163                 color_table[val].red,
2164                 color_table[val].grn,
2165                 color_table[val].blu);
2166     }
2167 
2168     return color_table;
2169 }
2170 
2171 
2172 
2173 static void
readBytes(FILE * const ifP,unsigned int const n,unsigned char * const buf)2174 readBytes(FILE *          const ifP,
2175           unsigned int    const n,
2176           unsigned char * const buf) {
2177 
2178     align += n;
2179 
2180     if (fread(buf, n, 1, ifP) != 1)
2181         pm_error("EOF / read error while %s", stage);
2182 }
2183 
2184 
2185 
2186 static void
copyFullBytes(unsigned char * const packed,unsigned char * const expanded,unsigned int const packedLen)2187 copyFullBytes(unsigned char * const packed,
2188               unsigned char * const expanded,
2189               unsigned int    const packedLen) {
2190 
2191     unsigned int i;
2192 
2193     for (i = 0; i < packedLen; ++i)
2194         expanded[i] = packed[i];
2195 }
2196 
2197 
2198 
2199 static void
expand4Bits(unsigned char * const packed,unsigned char * const expanded,unsigned int const packedLen)2200 expand4Bits(unsigned char * const packed,
2201             unsigned char * const expanded,
2202             unsigned int    const packedLen) {
2203 
2204     unsigned int i;
2205     unsigned char * dst;
2206 
2207     dst = &expanded[0];
2208 
2209     for (i = 0; i < packedLen; ++i) {
2210         *dst++ = (packed[i] >> 4) & 0x0f;
2211         *dst++ = (packed[i] >> 0) & 0x0f;
2212     }
2213 }
2214 
2215 
2216 
2217 static void
expand2Bits(unsigned char * const packed,unsigned char * const expanded,unsigned int const packedLen)2218 expand2Bits(unsigned char * const packed,
2219             unsigned char * const expanded,
2220             unsigned int    const packedLen) {
2221 
2222     unsigned int i;
2223     unsigned char * dst;
2224 
2225     dst = &expanded[0];
2226 
2227     for (i = 0; i < packedLen; ++i) {
2228         *dst++ = (packed[i] >> 6) & 0x03;
2229         *dst++ = (packed[i] >> 4) & 0x03;
2230         *dst++ = (packed[i] >> 2) & 0x03;
2231         *dst++ = (packed[i] >> 0) & 0x03;
2232     }
2233 }
2234 
2235 
2236 
2237 static void
expand1Bit(unsigned char * const packed,unsigned char * const expanded,unsigned int const packedLen)2238 expand1Bit(unsigned char * const packed,
2239            unsigned char * const expanded,
2240            unsigned int    const packedLen) {
2241 
2242     unsigned int i;
2243     unsigned char * dst;
2244 
2245     dst = &expanded[0];
2246 
2247     for (i = 0; i < packedLen; ++i) {
2248         *dst++ = (packed[i] >> 7) & 0x01;
2249         *dst++ = (packed[i] >> 6) & 0x01;
2250         *dst++ = (packed[i] >> 5) & 0x01;
2251         *dst++ = (packed[i] >> 4) & 0x01;
2252         *dst++ = (packed[i] >> 3) & 0x01;
2253         *dst++ = (packed[i] >> 2) & 0x01;
2254         *dst++ = (packed[i] >> 1) & 0x01;
2255         *dst++ = (packed[i] >> 0) & 0x01;
2256     }
2257 }
2258 
2259 
2260 
2261 static void
unpackBuf(unsigned char * const packed,unsigned int const packedLen,int const bitsPerPixel,unsigned char ** const expandedP,unsigned int * const expandedLenP)2262 unpackBuf(unsigned char *  const packed,
2263           unsigned int     const packedLen,
2264           int              const bitsPerPixel,
2265           unsigned char ** const expandedP,
2266           unsigned int *   const expandedLenP) {
2267 /*----------------------------------------------------------------------------
2268    Expand the bit string 'packed', which is 'packedLen' bytes long
2269    into an array of bytes, with one byte per pixel.  Each 'bitsPerPixel'
2270    of 'packed' is a pixel.
2271 
2272    So e.g. if it's 4 bits per pixel and 'packed' is 0xabcdef01, we
2273    return 0x0a0b0c0d0e0f0001 as *expandedP.
2274 
2275    As a special case, if there are multiple bytes per pixel, we just
2276    return the exact same bit string.
2277 
2278    *expandedP is static storage.
2279 
2280    'packedLen' must not be greater than 256.
2281 -----------------------------------------------------------------------------*/
2282     static unsigned char expanded[256 * 8];
2283 
2284     assert(packedLen <= 256);
2285 
2286     switch (bitsPerPixel) {
2287     case 8:
2288     case 16:
2289     case 32:
2290         assert(packedLen <= sizeof(expanded));
2291         copyFullBytes(packed, expanded, packedLen);
2292         *expandedLenP = packedLen;
2293         break;
2294     case 4:
2295         assert(packedLen * 2 <= sizeof(expanded));
2296         expand4Bits(packed, expanded, packedLen);
2297         *expandedLenP = packedLen * 2;
2298         break;
2299     case 2:
2300         assert(packedLen * 4 <= sizeof(expanded));
2301         expand2Bits(packed, expanded, packedLen);
2302         *expandedLenP = packedLen * 4;
2303         break;
2304     case 1:
2305         assert(packedLen * 8 <= sizeof(expanded));
2306         expand1Bit(packed, expanded, packedLen);
2307         *expandedLenP = packedLen * 8;
2308         break;
2309     default:
2310         pm_error("INTERNAL ERROR: bitsPerPixel = %u in unpackBuf",
2311                  bitsPerPixel);
2312     }
2313     *expandedP = expanded;
2314 }
2315 
2316 
2317 
2318 static void
unpackUncompressedBits(FILE * const ifP,struct raster const raster,unsigned int const rowBytes,unsigned int const bitsPerPixel)2319 unpackUncompressedBits(FILE *          const ifP,
2320                        struct raster   const raster,
2321                        unsigned int    const rowBytes,
2322                        unsigned int    const bitsPerPixel) {
2323 /*----------------------------------------------------------------------------
2324    Read the raster from the file into 'raster'.  The data in the file is not
2325    compressed (but may still be packed multiple pixels per byte).
2326 
2327    In PICT terminology, it appears that compression is called
2328    "packing" and I don't know what packing is called.  But we don't
2329    use that confusing terminology in this program, except when talking
2330    to the user.
2331 -----------------------------------------------------------------------------*/
2332     unsigned int rowOfRect;
2333     unsigned char * linebuf;
2334 
2335     MALLOCARRAY(linebuf, rowBytes + 100);
2336     if (linebuf == NULL)
2337         pm_error("can't allocate memory for line buffer");
2338 
2339     for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) {
2340         unsigned char * bytePixels;
2341         unsigned int expandedByteCount;
2342         unsigned char * rasterRow;
2343         unsigned int i;
2344 
2345         rasterRow = raster.bytes + rowOfRect * raster.rowSize;
2346 
2347         readBytes(ifP, rowBytes, linebuf);
2348 
2349         unpackBuf(linebuf, rowBytes, bitsPerPixel,
2350                   &bytePixels, &expandedByteCount);
2351 
2352         assert(expandedByteCount <= raster.rowSize);
2353 
2354         for (i = 0; i < expandedByteCount; ++i)
2355             rasterRow[i] = bytePixels[i];
2356     }
2357     free(linebuf);
2358 }
2359 
2360 
2361 
2362 static void
reportValidateCompressedLineLen(unsigned int const row,unsigned int const linelen,unsigned int const rowSize)2363 reportValidateCompressedLineLen(unsigned int const row,
2364                                 unsigned int const linelen,
2365                                 unsigned int const rowSize) {
2366 /*----------------------------------------------------------------------------
2367    'row' is a row number in the raster.
2368 
2369    'linelen' is the number of bytes of PICT that the PICT says hold the
2370    compressed version of that row.
2371 
2372    'rowSize' is the number of bytes we expect the uncompressed line to
2373    be (includes pad pixels on the right).
2374 -----------------------------------------------------------------------------*/
2375     if (verbose > 1)
2376         pm_message("Row %u: %u-byte compressed line", row, linelen);
2377 
2378     /* When the line length value is garbage, it often causes the program to
2379        try to read beyond EOF.  To make that failure easier to diagnose,
2380        we sanity check the line length now.
2381     */
2382 
2383     /* In the worst case, a pixel is represented by two bytes: a one byte
2384        repeat count of one followed by a one byte pixel value (the byte could
2385        be up to 8 pixels) or a one byte block length of one followed by the
2386        pixel value.  So expansion factor two.
2387     */
2388 
2389     if (linelen > rowSize * 2)
2390         pm_error("Invalid PICT: compressed line of %u bytes for Row %u "
2391                  "is too big "
2392                  "to represent a %u-byte padded row, even with worse case "
2393                  "compression.", linelen, row, rowSize);
2394 }
2395 
2396 
2397 
2398 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)2399 expandRun(unsigned char * const block,
2400           unsigned int    const blockLimit,
2401           unsigned int    const bitsPerPixel,
2402           unsigned char * const expanded,
2403           unsigned int    const expandedSize,
2404           unsigned int *  const blockLengthP,
2405           unsigned int *  const expandedByteCountP) {
2406 /*----------------------------------------------------------------------------
2407    Expand a run (the data says, "repeat the next pixel N times").
2408 
2409    Return the expanded run as expanded[], which has room for 'expandedSize'
2410    elements.  Return as *expandedByteCountP the number of elements actually
2411    returned.
2412 -----------------------------------------------------------------------------*/
2413     unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1;
2414         /* The repetition unit size, in bytes.  The run consists of this many
2415            bytes of packed data repeated the specified number of times.
2416         */
2417 
2418     if (1 + pkpixsize > blockLimit)
2419         pm_error("PICT run block runs off the end of its line.  "
2420                  "Invalid PICT file.");
2421     else {
2422         unsigned int const runLength = (block[0] ^ 0xff) + 2;
2423 
2424         unsigned int i;
2425         unsigned char * bytePixels;  /* Points to static storage */
2426         unsigned int expandedByteCount;
2427         unsigned int outputCursor;
2428 
2429         assert(block[0] & 0x80);  /* It's a run */
2430 
2431         if (verbose > 1)
2432             pm_message("Block: run of %u packed %u-byte units",
2433                        runLength, pkpixsize);
2434 
2435         unpackBuf(&block[1], pkpixsize, bitsPerPixel,
2436                   &bytePixels, &expandedByteCount);
2437 
2438         /* I assume in a legal PICT the run never has padding for the
2439            case that the run is at the right edge of a row and the
2440            remaining columns in the row don't fill whole bytes.
2441            E.g. if there are 25 columns left in the row and 1 bit per
2442            pixel, we won't see a run of 4 bytes and have to ignore the
2443            last 7 pixels.  Instead, we'll see a run of 3 bytes
2444            followed by a non-run block for the remaining pixel.
2445 
2446            That is what I saw in a test image.
2447         */
2448 
2449         if (expandedByteCount * runLength > expandedSize)
2450             pm_error("Invalid PICT image.  It contains a row with more pixels "
2451                      "than the width of the rectangle containing it, "
2452                      "even padded up to a "
2453                      "multiple of 16 pixels.  Use -verbose to see details.");
2454 
2455         outputCursor = 0;
2456         for (i = 0; i < runLength; ++i) {
2457             unsigned int j;
2458             for (j = 0; j < expandedByteCount; ++j)
2459                 expanded[outputCursor++] = bytePixels[j];
2460         }
2461         *blockLengthP = 1 + pkpixsize;
2462         *expandedByteCountP = expandedByteCount * runLength;
2463     }
2464 }
2465 
2466 
2467 
2468 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)2469 copyPixelGroup(unsigned char * const block,
2470                unsigned int    const blockLimit,
2471                unsigned int    const bitsPerPixel,
2472                unsigned char * const dest,
2473                unsigned int    const destSize,
2474                unsigned int *  const blockLengthP,
2475                unsigned int *  const rasterBytesGeneratedP) {
2476 /*----------------------------------------------------------------------------
2477    Copy a group of pixels (the data says, "take the following N pixels").
2478 
2479    Copy them (unpacked) from block block[] to dest[].
2480 
2481    block[] self-describes its length.  Return that length as
2482    *blockLengthP.
2483 
2484    block[] contains at most 'blockLimit' valid array elements, so if
2485    the length information in block[] indicates the block is larger
2486    than that, the block is corrupt.
2487 
2488    Return the number of pixels placed in dest[] as *rasterBytesGeneratedP.
2489 
2490    The output array dest[] has 'destSize' elements of space.  Ignore
2491    any pixels on the right that won't fit in that.
2492 -----------------------------------------------------------------------------*/
2493     unsigned int const pkpixsize   = bitsPerPixel == 16 ? 2 : 1;
2494     unsigned int const groupLen    = block[0] + 1;
2495     unsigned int const blockLength = 1 + groupLen * pkpixsize;
2496 
2497     if (blockLength > blockLimit)
2498         pm_error("PICT non-run block (length %u) "
2499                  "runs off the end of its line.  "
2500                  "Invalid PICT file.", blockLength);
2501     else {
2502         unsigned int i;
2503         unsigned char * bytePixels;  /* Points to static storage */
2504         unsigned int bytePixelLen;
2505         unsigned int rasterBytesGenerated;
2506 
2507         assert(blockLimit >= 1);  /* block[0] exists */
2508         assert((block[0] & 0x80) == 0);  /* It's not a run */
2509 
2510         if (verbose > 1)
2511             pm_message("Block: %u explicit packed %u-byte units",
2512                        groupLen, pkpixsize);
2513 
2514         unpackBuf(&block[1], groupLen * pkpixsize, bitsPerPixel,
2515                   &bytePixels, &bytePixelLen);
2516 
2517         /* It is normal for the above to return more pixels than there
2518            are left in the row, because of padding.  E.g. there is one
2519            pixel left in the row, at one bit per pixel.  But a block
2520            contains full bytes, so it must contain at least 8 pixels.
2521            7 of them are padding, which we should ignore.
2522 
2523            BUT: I saw an image in which the block had _two_ data bytes
2524            (16 pixels) when only 1 pixel remained in the row.  I don't
2525            understand why, but ignoring the 15 extra seemed to work.
2526         */
2527         rasterBytesGenerated = MIN(bytePixelLen, destSize);
2528 
2529         for (i = 0; i < rasterBytesGenerated; ++i)
2530             dest[i] = bytePixels[i];
2531 
2532         *blockLengthP = blockLength;
2533         *rasterBytesGeneratedP = rasterBytesGenerated;
2534     }
2535 }
2536 
2537 
2538 
2539 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)2540 interpretOneRasterBlock(unsigned char * const block,
2541                         unsigned int    const blockLimit,
2542                         unsigned int    const bitsPerPixel,
2543                         unsigned char * const raster,
2544                         unsigned int    const rasterSize,
2545                         unsigned int *  const blockLengthP,
2546                         unsigned int *  const rasterBytesGeneratedP) {
2547 /*----------------------------------------------------------------------------
2548    Return the pixels described by the PICT block block[], assuming
2549    the PICT format is 'bitsPerPixel' bits per pixel.
2550 
2551    Return them as raster[], which has 'rasterSize' elements of space.
2552    Return as *rasterBytesGeneratedP the number of elements actually
2553    returned.
2554 
2555    block[] self-describes its length, and we return that as
2556    *blockLengthP.  But there are at most 'blockLimit' bytes there, so
2557    if block[] claims to be longer than that, some of the block is
2558    missing (i.e. corrupt PICT).
2559 -----------------------------------------------------------------------------*/
2560     if (block[0] & 0x80)
2561         expandRun(block, blockLimit, bitsPerPixel, raster, rasterSize,
2562                   blockLengthP, rasterBytesGeneratedP);
2563     else
2564         copyPixelGroup(block, blockLimit, bitsPerPixel, raster, rasterSize,
2565                        blockLengthP, rasterBytesGeneratedP);
2566 
2567     assert(*rasterBytesGeneratedP <= rasterSize);
2568 }
2569 
2570 
2571 
2572 static void
interpretCompressedLine(unsigned char * const linebuf,unsigned int const linelen,unsigned char * const rowRaster,unsigned int const rowSize,unsigned int const bitsPerPixel)2573 interpretCompressedLine(unsigned char * const linebuf,
2574                         unsigned int    const linelen,
2575                         unsigned char * const rowRaster,
2576                         unsigned int    const rowSize,
2577                         unsigned int    const bitsPerPixel) {
2578 /*----------------------------------------------------------------------------
2579    linebuf[] contains 'linelen' bytes from the PICT image that represents
2580    one row of the image, in compressed format.  Return the
2581    uncompressed pixels of that row as rowRaster[].
2582 
2583    rowRaster[] has 'rowSize' bytes of space.  Caller ensures that
2584    linebuf[] does not contain more pixels than that, unless the PICT
2585    image from which it comes is corrupt.
2586 -----------------------------------------------------------------------------*/
2587     unsigned int lineCursor;
2588         /* Cursor into linebuf[] -- the compressed data */
2589     unsigned int rasterCursor;
2590         /* Cursor into rowRaster[] -- the uncompressed data */
2591 
2592     for (lineCursor = 0, rasterCursor = 0; lineCursor < linelen; ) {
2593         unsigned int blockLength, rasterBytesGenerated;
2594 
2595         assert(lineCursor <= linelen);
2596 
2597         if (verbose > 2)
2598             pm_message("At Byte %u of line, Column %u of row",
2599                        lineCursor, rasterCursor);
2600 
2601         interpretOneRasterBlock(
2602             &linebuf[lineCursor], linelen - lineCursor,
2603             bitsPerPixel,
2604             &rowRaster[rasterCursor], rowSize - rasterCursor,
2605             &blockLength, &rasterBytesGenerated);
2606 
2607         lineCursor += blockLength;
2608         rasterCursor += rasterBytesGenerated;
2609         assert(rasterCursor <= rowSize);
2610     }
2611     if (verbose > 1)
2612         pm_message("Got %u pixels for row", rasterCursor);
2613 }
2614 
2615 
2616 /* There is some confusion about when, in PICT, a line length is one byte and
2617   when it is two.  An Apple document says it is two bytes when the number of
2618   pixels in the row, padded, is > 250.  Ppmtopict generated PICTs that way
2619   until January 2009.  Picttoppm assumed something similar until March 2004:
2620   It assumed the line length is two bytes when the number of pixels > 250 _or_
2621   bits per pixel > 8.  But in March 2004, Steve Summit did a bunch of
2622   experiments on existing PICT files and found that they all worked with the
2623   rule "pixels per row > 200 => 2 byte line length" and some did not work
2624   with the original rule.
2625 
2626   So in March 2004, Picttoppm changed to pixels per row > 200.  Ppmtopict
2627   didn't catch up until January 2009.
2628 
2629   http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-460.html#HEADING460-0
2630 
2631   Of course, neither 200 nor 250 make any logical sense.  In the worst case,
2632   you can represent 254 pixels of 8 bpp or less in a 255 byte line.
2633   In the worst case, you can represent 127 16bpp pixels in a 255 byte line.
2634   So with 200 being the cutoff, it's actually impossible to represent some
2635   16 bpp images with 200 pixels per row.
2636 
2637   We have not been able to find an official spec for PICT.
2638 
2639   Some day, we may have to make a user option for this.
2640 */
2641 
2642 
2643 static void
unpackCompressedBits(FILE * const ifP,struct raster const raster,unsigned int const rowBytes,unsigned int const bitsPerPixel)2644 unpackCompressedBits(FILE *          const ifP,
2645                      struct raster   const raster,
2646                      unsigned int    const rowBytes,
2647                      unsigned int    const bitsPerPixel) {
2648 /*----------------------------------------------------------------------------
2649    Read the raster on file *ifP and place it in 'raster'.
2650 
2651    The data in the file is compressed with run length encoding and
2652    possibly packed multiple pixels per byte as well.
2653 
2654    In PICT terminology, it appears that compression is called
2655    "packing" and I don't know what packing is called.  But we don't
2656    use that confusing terminology in this program, except when talking
2657    to the user.
2658 -----------------------------------------------------------------------------*/
2659     unsigned int const llsize = rowBytes > 200 ? 2 : 1;
2660         /* Width in bytes of the field at the beginning of a line that tells
2661            how long (in bytes) the line is.  See notes above about this
2662            computation.
2663         */
2664     unsigned int row;
2665     unsigned char * linebuf;
2666     unsigned int linebufSize;
2667 
2668     linebufSize = rowBytes;
2669     MALLOCARRAY(linebuf, linebufSize);
2670     if (linebuf == NULL)
2671         pm_error("can't allocate memory for line buffer");
2672 
2673     for (row = 0; row < raster.rowCount; ++row) {
2674         unsigned char * const rowRaster =
2675             &raster.bytes[row * raster.rowSize];
2676         unsigned int linelen;
2677 
2678         if (llsize == 2)
2679             linelen = readWord();
2680         else
2681             linelen = readByte();
2682 
2683         reportValidateCompressedLineLen(row, linelen, raster.rowSize);
2684 
2685         if (linelen > linebufSize) {
2686             linebufSize = linelen;
2687             REALLOCARRAY(linebuf, linebufSize);
2688             if (linebuf == NULL)
2689                 pm_error("can't allocate memory for line buffer");
2690         }
2691         readBytes(ifP, linelen, linebuf);
2692 
2693         interpretCompressedLine(linebuf, linelen, rowRaster, raster.rowSize,
2694                                 bitsPerPixel);
2695     }
2696     free(linebuf);
2697 }
2698 
2699 
2700 
2701 static void
unpackbits(FILE * const ifP,struct Rect * const boundsP,Word const rowBytesArg,int const bitsPerPixel,struct raster * const rasterP)2702 unpackbits(FILE *           const ifP,
2703            struct Rect *    const boundsP,
2704            Word             const rowBytesArg,
2705            int              const bitsPerPixel,
2706            struct raster *  const rasterP) {
2707 
2708     unsigned int const rectHeight = boundsP->bottom - boundsP->top;
2709     unsigned int const rectWidth  = boundsP->right  - boundsP->left;
2710 
2711     struct raster raster;
2712     unsigned int rowBytes;
2713 
2714     stage = "unpacking packbits";
2715 
2716     if (verbose)
2717         pm_message("rowBytes = %u, bitsPerPixel = %d",
2718                    rowBytesArg, bitsPerPixel);
2719 
2720     allocateRaster(&raster, rectWidth, rectHeight, bitsPerPixel);
2721 
2722     rowBytes = rowBytesArg ? rowBytesArg : raster.rowSize;
2723 
2724     if (verbose)
2725         pm_message("raster.rowSize: %u bytes; file row = %u bytes",
2726                    raster.rowSize, rowBytes);
2727 
2728     if (rowBytes < 8)
2729         unpackUncompressedBits(ifP, raster, rowBytes, bitsPerPixel);
2730     else
2731         unpackCompressedBits(ifP, raster, rowBytes, bitsPerPixel);
2732 
2733     *rasterP = raster;
2734 }
2735 
2736 
2737 
2738 static void
interpretRowBytesWord(Word const rowBytesWord,bool * const pixMapP,unsigned int * const rowBytesP)2739 interpretRowBytesWord(Word           const rowBytesWord,
2740                       bool *         const pixMapP,
2741                       unsigned int * const rowBytesP) {
2742 
2743     *pixMapP   = !!(rowBytesWord & 0x8000);
2744     *rowBytesP = rowBytesWord & 0x7fff;
2745 
2746     if (verbose)
2747         pm_message("PCT says %s, %u bytes per row",
2748                    *pixMapP ? "pixmap" : "bitmap", *rowBytesP);
2749 }
2750 
2751 
2752 
2753 /* this just skips over a version 2 pattern.  Probably will return
2754  * a pattern in the fabled complete version.
2755  */
2756 static void
readPattern(void)2757 readPattern(void) {
2758 
2759     Word PatType;
2760 
2761     stage = "Reading a pattern";
2762 
2763     PatType = readWord();
2764 
2765     switch (PatType) {
2766     case 2:
2767         skip(8); /* old pattern data */
2768         skip(5); /* RGB for pattern */
2769         break;
2770     case 1: {
2771         Word rowBytesWord;
2772         bool pixMap;
2773         unsigned int rowBytes;
2774         struct pixMap p;
2775         struct raster raster;
2776         struct RGBColor * ct;
2777 
2778         skip(8); /* old pattern data */
2779         rowBytesWord = readWord();
2780         interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
2781         readPixmap(&p);
2782         ct = readColorTable();
2783         unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster);
2784         freeRaster(raster);
2785         free(ct);
2786     } break;
2787     default:
2788         pm_error("unknown pattern type in readPattern");
2789     }
2790 }
2791 
2792 
2793 
2794 /* these 3 do nothing but skip over their data! */
2795 
2796 static drawFn BkPixPat;
2797 
2798 static void
BkPixPat(struct canvas * const canvasP,blitList * const blitListP,int const version)2799 BkPixPat(struct canvas * const canvasP,
2800          blitList *      const blitListP,
2801          int             const version) {
2802 
2803     readPattern();
2804 }
2805 
2806 
2807 
2808 static drawFn PnPixPat;
2809 
2810 static void
PnPixPat(struct canvas * const canvasP,blitList * const blitListP,int const version)2811 PnPixPat(struct canvas * const canvasP,
2812          blitList *      const blitListP,
2813          int             const version) {
2814 
2815     readPattern();
2816 }
2817 
2818 
2819 
2820 static drawFn FillPixPat;
2821 
2822 static void
FillPixPat(struct canvas * const canvasP,blitList * const blitListP,int const version)2823 FillPixPat(struct canvas * const canvasP,
2824            blitList *      const blitListP,
2825            int             const version) {
2826 
2827     readPattern();
2828 }
2829 
2830 
2831 
2832 static void
read8x8Pattern(struct Pattern * const pat)2833 read8x8Pattern(struct Pattern * const pat) {
2834     unsigned char buf[8];
2835     unsigned char * exp;
2836     unsigned int len;
2837     unsigned int expandedLen;
2838     unsigned int i;
2839 
2840     len = 8;  /* initial value */
2841     readBytes(ifp, len, buf);
2842     if (verbose) {
2843         pm_message("pattern: %02x%02x%02x%02x",
2844                    buf[0], buf[1], buf[2], buf[3]);
2845         pm_message("pattern: %02x%02x%02x%02x",
2846             buf[4], buf[5], buf[6], buf[7]);
2847     }
2848     unpackBuf(buf, len, 1, &exp, &expandedLen);
2849     for (i = 0; i < 64; ++i)
2850         pat->pix[i] = exp[i];
2851 }
2852 
2853 
2854 
2855 static drawFn BkPat;
2856 
2857 static void
BkPat(struct canvas * const canvasP,blitList * const blitListP,int const version)2858 BkPat(struct canvas * const canvasP,
2859       blitList *      const blitListP,
2860       int             const version) {
2861 
2862     read8x8Pattern(&bkpat);
2863 }
2864 
2865 
2866 
2867 static drawFn PnPat;
2868 
2869 static void
PnPat(struct canvas * const canvasP,blitList * const blitListP,int const version)2870 PnPat(struct canvas * const canvasP,
2871       blitList *      const blitListP,
2872       int             const version) {
2873 
2874     read8x8Pattern(&pen_pat);
2875 }
2876 
2877 
2878 
2879 static drawFn FillPat;
2880 
2881 static void
FillPat(struct canvas * const canvasP,blitList * const blitListP,int const version)2882 FillPat(struct canvas * const canvasP,
2883         blitList *      const blitListP,
2884         int             const version) {
2885 
2886     read8x8Pattern(&fillpat);
2887 }
2888 
2889 
2890 
2891 static drawFn PnSize;
2892 
2893 static void
PnSize(struct canvas * const canvasP,blitList * const blitListP,int const version)2894 PnSize(struct canvas * const canvasP,
2895        blitList *      const blitListP,
2896        int             const version) {
2897 
2898     pen_height = readWord();
2899     pen_width = readWord();
2900     if (verbose)
2901         pm_message("pen size %d x %d", pen_width, pen_height);
2902 }
2903 
2904 
2905 
2906 static drawFn PnSize;
2907 
2908 static void
PnMode(struct canvas * const canvasP,blitList * const blitListP,int const version)2909 PnMode(struct canvas * const canvasP,
2910        blitList *      const blitListP,
2911        int             const version) {
2912 
2913     pen_mode = readWord();
2914 
2915     if (pen_mode >= 8 && pen_mode < 15)
2916         pen_mode -= 8;
2917     if (verbose)
2918         pm_message("pen transfer mode = %s",
2919             const_name(transfer_name, pen_mode));
2920 
2921     pen_trf = transfer(pen_mode);
2922 }
2923 
2924 
2925 
2926 static void
readRgb(struct RGBColor * const rgb)2927 readRgb(struct RGBColor * const rgb) {
2928     rgb->red = readWord();
2929     rgb->grn = readWord();
2930     rgb->blu = readWord();
2931 }
2932 
2933 
2934 
2935 static drawFn RGBFgCol;
2936 
2937 static void
RGBFgCol(struct canvas * const canvasP,blitList * const blitListP,int const version)2938 RGBFgCol(struct canvas * const canvasP,
2939          blitList *      const blitListP,
2940          int             const version) {
2941 
2942     readRgb(&foreground);
2943     if (verbose)
2944         pm_message("foreground now [%d,%d,%d]",
2945             foreground.red, foreground.grn, foreground.blu);
2946 }
2947 
2948 
2949 
2950 static drawFn RGBBkCol;
2951 
2952 static void
RGBBkCol(struct canvas * const canvasP,blitList * const blitListP,int const version)2953 RGBBkCol(struct canvas * const canvasP,
2954          blitList *      const blitListP,
2955          int             const version) {
2956 
2957     readRgb(&background);
2958     if (verbose)
2959         pm_message("background now [%d,%d,%d]",
2960             background.red, background.grn, background.blu);
2961 }
2962 
2963 
2964 
2965 static unsigned int
pixelIndex(struct Rect const picFrame,unsigned int const x,unsigned int const y)2966 pixelIndex(struct Rect  const picFrame,
2967            unsigned int const x,
2968            unsigned int const y) {
2969 
2970     unsigned int const rowLen = picFrame.right - picFrame.left;
2971 
2972     assert(y >= picFrame.top  && y < picFrame.bottom);
2973     assert(x >= picFrame.left && x < picFrame.right);
2974 
2975     return (y - picFrame.top) * rowLen + (x - picFrame.left);
2976 }
2977 
2978 
2979 
2980 static void
drawPixel(struct canvas * const canvasP,int const x,int const y,struct RGBColor * const clr,transfer_func trf)2981 drawPixel(struct canvas *   const canvasP,
2982           int               const x,
2983           int               const y,
2984           struct RGBColor * const clr,
2985           transfer_func           trf) {
2986 
2987     if (x < clip_rect.left || x >= clip_rect.right ||
2988         y < clip_rect.top  || y >= clip_rect.bottom) {
2989     } else {
2990         unsigned int const i = pixelIndex(picFrame, x, y);
2991 
2992         struct RGBColor dst;
2993 
2994         dst.red = canvasP->planes.red[i];
2995         dst.grn = canvasP->planes.grn[i];
2996         dst.blu = canvasP->planes.blu[i];
2997 
2998         (*trf)(clr, &dst);
2999 
3000         canvasP->planes.red[i] = dst.red;
3001         canvasP->planes.grn[i] = dst.grn;
3002         canvasP->planes.blu[i] = dst.blu;
3003     }
3004 }
3005 
3006 
3007 
3008 static void
drawPenRect(struct canvas * const canvasP,struct Rect * const rP)3009 drawPenRect(struct canvas * const canvasP,
3010             struct Rect *   const rP) {
3011 
3012     if (!rectisnull(rP)) {
3013         unsigned int const rowadd = rowlen - (rP->right - rP->left);
3014 
3015         unsigned int i;
3016         unsigned int y;
3017 
3018         dumpRect("BRYAN: drawing rectangle ", *rP);
3019         i = pixelIndex(picFrame, rP->left, rP->top);  /* initial value */
3020 
3021         for (y = rP->top; y < rP->bottom; ++y) {
3022 
3023             unsigned int x;
3024 
3025             for (x = rP->left; x < rP->right; ++x) {
3026 
3027                 struct RGBColor dst;
3028 
3029                 assert(i < canvasP->planes.height * canvasP->planes.width);
3030 
3031                 dst.red = canvasP->planes.red[i];
3032                 dst.grn = canvasP->planes.grn[i];
3033                 dst.blu = canvasP->planes.blu[i];
3034 
3035                 if (pen_pat.pix[(x & 7) + (y & 7) * 8])
3036                     (*pen_trf)(&black, &dst);
3037                 else
3038                     (*pen_trf)(&white, &dst);
3039 
3040                 canvasP->planes.red[i] = dst.red;
3041                 canvasP->planes.grn[i] = dst.grn;
3042                 canvasP->planes.blu[i] = dst.blu;
3043 
3044                 ++i;
3045             }
3046             i += rowadd;
3047         }
3048     }
3049 }
3050 
3051 
3052 
3053 static void
drawPen(struct canvas * const canvasP,int const x,int const y)3054 drawPen(struct canvas * const canvasP,
3055         int             const x,
3056         int             const y) {
3057 
3058     struct Rect unclippedPenrect;
3059     struct Rect clippedPenrect;
3060 
3061     unclippedPenrect.left = x;
3062     unclippedPenrect.right = x + pen_width;
3063     unclippedPenrect.top = y;
3064     unclippedPenrect.bottom = y + pen_height;
3065 
3066     rectintersect(&unclippedPenrect, &clip_rect, &clippedPenrect);
3067 
3068     drawPenRect(canvasP, &clippedPenrect);
3069 }
3070 
3071 /*
3072  * Digital Line Drawing
3073  * by Paul Heckbert
3074  * from "Graphics Gems", Academic Press, 1990
3075  */
3076 
3077 /*
3078  * digline: draw digital line from (x1,y1) to (x2,y2),
3079  * calling a user-supplied procedure at each pixel.
3080  * Does no clipping.  Uses Bresenham's algorithm.
3081  *
3082  * Paul Heckbert    3 Sep 85
3083  */
3084 static void
scanLine(struct canvas * const canvasP,short const x1,short const y1,short const x2,short const y2)3085 scanLine(struct canvas * const canvasP,
3086          short           const x1,
3087          short           const y1,
3088          short           const x2,
3089          short           const y2) {
3090 
3091     int d, x, y, ax, ay, sx, sy, dx, dy;
3092 
3093     if (!(pen_width == 0 && pen_height == 0)) {
3094 
3095         dx = x2-x1;  ax = ABS(dx)<<1;  sx = SGN(dx);
3096         dy = y2-y1;  ay = ABS(dy)<<1;  sy = SGN(dy);
3097 
3098         x = x1;
3099         y = y1;
3100         if (ax>ay) {        /* x dominant */
3101             d = ay-(ax>>1);
3102             for (;;) {
3103                 drawPen(canvasP, x, y);
3104                 if (x==x2) return;
3105                 if ((x > rowlen) && (sx > 0)) return;
3106                 if (d>=0) {
3107                     y += sy;
3108                     d -= ax;
3109                 }
3110                 x += sx;
3111                 d += ay;
3112             }
3113         }
3114         else {          /* y dominant */
3115             d = ax-(ay>>1);
3116             for (;;) {
3117                 drawPen(canvasP, x, y);
3118                 if (y==y2) return;
3119                 if ((y > collen) && (sy > 0)) return;
3120                 if (d>=0) {
3121                     x += sx;
3122                     d -= ay;
3123                 }
3124                 y += sy;
3125                 d += ax;
3126             }
3127         }
3128     }
3129 }
3130 
3131 
3132 
3133 static drawFn Line;
3134 
3135 static void
Line(struct canvas * const canvasP,blitList * const blitListP,int const version)3136 Line(struct canvas * const canvasP,
3137      blitList *      const blitListP,
3138      int             const version) {
3139 
3140   struct Point p1;
3141   readPoint(&p1);
3142   readPoint(&current);
3143   if (verbose)
3144     pm_message("(%d,%d) to (%d, %d)",
3145            p1.x,p1.y,current.x,current.y);
3146   scanLine(canvasP, p1.x,p1.y,current.x,current.y);
3147 }
3148 
3149 
3150 
3151 static drawFn LineFrom;
3152 
3153 static void
LineFrom(struct canvas * const canvasP,blitList * const blitListP,int const version)3154 LineFrom(struct canvas * const canvasP,
3155          blitList *      const blitListP,
3156          int             const version) {
3157 
3158     struct Point p1;
3159     readPoint(&p1);
3160     if (verbose)
3161         pm_message("(%d,%d) to (%d, %d)", current.x, current.y, p1.x, p1.y);
3162 
3163     if (!blitListP)
3164         scanLine(canvasP, current.x, current.y, p1.x, p1.y);
3165 
3166     current.x = p1.x;
3167     current.y = p1.y;
3168 }
3169 
3170 
3171 
3172 static drawFn ShortLine;
3173 
3174 static void
ShortLine(struct canvas * const canvasP,blitList * const blitListP,int const version)3175 ShortLine(struct canvas * const canvasP,
3176           blitList *      const blitListP,
3177           int             const version) {
3178 
3179     struct Point p1;
3180     readPoint(&p1);
3181     readShortPoint(&current);
3182     if (verbose)
3183         pm_message("(%d,%d) delta (%d, %d)", p1.x, p1.y, current.x, current.y);
3184     current.x += p1.x;
3185     current.y += p1.y;
3186 
3187     if (!blitListP)
3188         scanLine(canvasP, p1.x, p1.y, current.x, current.y);
3189 }
3190 
3191 
3192 
3193 static drawFn ShortLineFrom;
3194 
3195 static void
ShortLineFrom(struct canvas * const canvasP,blitList * const blitListP,int const version)3196 ShortLineFrom(struct canvas * const canvasP,
3197               blitList *      const blitListP,
3198               int             const version) {
3199 
3200     struct Point p1;
3201     readShortPoint(&p1);
3202     if (verbose)
3203         pm_message("(%d,%d) delta (%d, %d)",
3204                    current.x,current.y,p1.x,p1.y);
3205     p1.x += current.x;
3206     p1.y += current.y;
3207     if (!blitListP)
3208         scanLine(canvasP, current.x, current.y, p1.x, p1.y);
3209     current.x = p1.x;
3210     current.y = p1.y;
3211 }
3212 
3213 
3214 
3215 static void
doPaintRect(struct canvas * const canvasP,struct Rect const prect)3216 doPaintRect(struct canvas * const canvasP,
3217             struct Rect     const prect) {
3218 
3219     struct Rect rect;
3220 
3221     if (verbose)
3222         dumpRect("painting", prect);
3223 
3224     rectintersect(&clip_rect, &prect, &rect);
3225 
3226     drawPenRect(canvasP, &rect);
3227 }
3228 
3229 
3230 
3231 static drawFn paintRect;
3232 
3233 static void
paintRect(struct canvas * const canvasP,blitList * const blitListP,int const version)3234 paintRect(struct canvas * const canvasP,
3235           blitList *      const blitListP,
3236           int             const version) {
3237 
3238     readRect(&cur_rect);
3239     if (!blitListP)
3240         doPaintRect(canvasP, cur_rect);
3241 }
3242 
3243 
3244 
3245 static drawFn paintSameRect;
3246 
3247 static void
paintSameRect(struct canvas * const canvasP,blitList * const blitListP,int const version)3248 paintSameRect(struct canvas * const canvasP,
3249               blitList *      const blitListP,
3250               int             const version) {
3251 
3252     if (!blitListP)
3253         doPaintRect(canvasP, cur_rect);
3254 }
3255 
3256 
3257 
3258 static void
doFrameRect(struct canvas * const canvasP,struct Rect const rect)3259 doFrameRect(struct canvas * const canvasP,
3260             struct Rect     const rect) {
3261 
3262     if (verbose)
3263         dumpRect("framing", rect);
3264 
3265     if (pen_width > 0 && pen_height > 0) {
3266         unsigned int x, y;
3267 
3268         for (x = rect.left; x <= rect.right - pen_width; x += pen_width) {
3269             drawPen(canvasP, x, rect.top);
3270             drawPen(canvasP, x, rect.bottom - pen_height);
3271         }
3272 
3273         for (y = rect.top; y <= rect.bottom - pen_height ; y += pen_height) {
3274             drawPen(canvasP, rect.left, y);
3275             drawPen(canvasP, rect.right - pen_width, y);
3276         }
3277     }
3278 }
3279 
3280 
3281 
3282 static drawFn frameRect;
3283 
3284 static void
frameRect(struct canvas * const canvasP,blitList * const blitListP,int const version)3285 frameRect(struct canvas * const canvasP,
3286           blitList *      const blitListP,
3287           int             const version) {
3288 
3289     readRect(&cur_rect);
3290 
3291     if (!blitListP)
3292         doFrameRect(canvasP, cur_rect);
3293 }
3294 
3295 
3296 
3297 static drawFn frameSameRect;
3298 
3299 static void
frameSameRect(struct canvas * const canvasP,blitList * const blitListP,int const version)3300 frameSameRect(struct canvas * const canvasP,
3301               blitList *      const blitListP,
3302               int             const version) {
3303 
3304     if (!blitListP)
3305         doFrameRect(canvasP, cur_rect);
3306 }
3307 
3308 
3309 
3310 /* a stupid shell sort - I'm so embarrassed  */
3311 
3312 static void
polySort(int const sort_index,struct Point points[])3313 polySort(int const sort_index, struct Point points[]) {
3314   int d, i, j, temp;
3315 
3316   /* initialize and set up sort interval */
3317   d = 4;
3318   while (d<=sort_index) d <<= 1;
3319   d -= 1;
3320 
3321   while (d > 1) {
3322     d >>= 1;
3323     for (j = 0; j <= (sort_index-d); j++) {
3324       for(i = j; i >= 0; i -= d) {
3325     if ((points[i+d].y < points[i].y) ||
3326         ((points[i+d].y == points[i].y) &&
3327          (points[i+d].x <= points[i].x))) {
3328       /* swap x1,y1 with x2,y2 */
3329       temp = points[i].y;
3330       points[i].y = points[i+d].y;
3331       points[i+d].y = temp;
3332       temp = points[i].x;
3333       points[i].x = points[i+d].x;
3334       points[i+d].x = temp;
3335     }
3336       }
3337     }
3338   }
3339 }
3340 
3341 
3342 
3343 /* Watch out for the lack of error checking in the next two functions ... */
3344 
3345 static void
scanPoly(struct canvas * const canvasP,int const np,struct Point pts[])3346 scanPoly(struct canvas * const canvasP,
3347          int             const np,
3348          struct Point          pts[]) {
3349 
3350   int dx,dy,dxabs,dyabs,i,scan_index,j,k,px,py;
3351   int sdx,sdy,x,y,toggle,old_sdy,sy0;
3352 
3353   /* This array needs to be at least as large as the largest dimension of
3354      the bounding box of the poly (but I don't check for overflows ...) */
3355   struct Point coord[5000];
3356 
3357   scan_index = 0;
3358 
3359   /* close polygon */
3360   px = pts[np].x = pts[0].x;
3361   py = pts[np].y = pts[0].y;
3362 
3363   /*  This section draws the polygon and stores all the line points
3364    *  in an array. This doesn't work for concave or non-simple polys.
3365    */
3366   /* are y levels same for first and second points? */
3367   if (pts[1].y == pts[0].y) {
3368     coord[scan_index].x = px;
3369     coord[scan_index].y = py;
3370     scan_index++;
3371   }
3372 
3373 #define sign(x) ((x) > 0 ? 1 : ((x)==0 ? 0:(-1)) )
3374 
3375   old_sdy = sy0 = sign(pts[1].y - pts[0].y);
3376   for (j=0; j<np; j++) {
3377     /* x,y difference between consecutive points and their signs  */
3378     dx = pts[j+1].x - pts[j].x;
3379     dy = pts[j+1].y - pts[j].y;
3380     sdx = SGN(dx);
3381     sdy = SGN(dy);
3382     dxabs = abs(dx);
3383     dyabs = abs(dy);
3384     x = y = 0;
3385 
3386     if (dxabs >= dyabs)
3387       {
3388     for (k=0; k < dxabs; k++) {
3389       y += dyabs;
3390       if (y >= dxabs) {
3391         y -= dxabs;
3392         py += sdy;
3393         if (old_sdy != sdy) {
3394           old_sdy = sdy;
3395           scan_index--;
3396         }
3397         coord[scan_index].x = px+sdx;
3398         coord[scan_index].y = py;
3399         scan_index++;
3400       }
3401       px += sdx;
3402       drawPen(canvasP, px, py);
3403     }
3404       }
3405     else
3406       {
3407     for (k=0; k < dyabs; k++) {
3408       x += dxabs;
3409       if (x >= dyabs) {
3410         x -= dyabs;
3411         px += sdx;
3412       }
3413       py += sdy;
3414       if (old_sdy != sdy) {
3415         old_sdy = sdy;
3416         if (sdy != 0) scan_index--;
3417       }
3418       drawPen(canvasP, px,py);
3419       coord[scan_index].x = px;
3420       coord[scan_index].y = py;
3421       scan_index++;
3422     }
3423       }
3424   }
3425 
3426   /* after polygon has been drawn now fill it */
3427 
3428   scan_index--;
3429   if (sy0 + sdy == 0) scan_index--;
3430 
3431   polySort(scan_index, coord);
3432 
3433   toggle = 0;
3434   for (i = 0; i < scan_index; i++) {
3435     if ((coord[i].y == coord[i+1].y) && (toggle == 0))
3436       {
3437     for (j = coord[i].x; j <= coord[i+1].x; j++)
3438       drawPen(canvasP, j, coord[i].y);
3439     toggle = 1;
3440       }
3441     else
3442       toggle = 0;
3443   }
3444 }
3445 
3446 
3447 
3448 static drawFn paintPoly;
3449 
3450 static void
paintPoly(struct canvas * const canvasP,blitList * const blitListP,int const version)3451 paintPoly(struct canvas * const canvasP,
3452           blitList *      const blitListP,
3453           int             const version) {
3454 
3455   struct Rect bb;
3456   struct Point pts[100];
3457   int i, np = (readWord() - 10) >> 2;
3458 
3459   readRect(&bb);
3460   for (i=0; i<np; ++i)
3461     readPoint(&pts[i]);
3462 
3463   /* scan convert poly ... */
3464   if (!blitListP)
3465       scanPoly(canvasP, np, pts);
3466 }
3467 
3468 
3469 
3470 static drawFn PnLocHFrac;
3471 
3472 static void
PnLocHFrac(struct canvas * const canvasP,blitList * const blitListP,int const version)3473 PnLocHFrac(struct canvas * const canvasP,
3474            blitList *      const blitListP,
3475            int             const version) {
3476 
3477     Word frac = readWord();
3478 
3479     if (verbose)
3480         pm_message("PnLocHFrac = %d", frac);
3481 }
3482 
3483 
3484 
3485 static drawFn TxMode;
3486 
3487 static void
TxMode(struct canvas * const canvasP,blitList * const blitListP,int const version)3488 TxMode(struct canvas * const canvasP,
3489        blitList *      const blitListP,
3490        int             const version) {
3491 
3492     text_mode = readWord();
3493 
3494     if (text_mode >= 8 && text_mode < 15)
3495         text_mode -= 8;
3496     if (verbose)
3497         pm_message("text transfer mode = %s",
3498             const_name(transfer_name, text_mode));
3499 
3500     /* ignore the text mask bit 'cause we don't handle it yet */
3501     text_trf = transfer(text_mode & ~64);
3502 }
3503 
3504 
3505 
3506 static drawFn TxFont;
3507 
3508 static void
TxFont(struct canvas * const canvasP,blitList * const blitListP,int const version)3509 TxFont(struct canvas * const canvasP,
3510        blitList *      const blitListP,
3511        int             const version) {
3512 
3513     text_font = readWord();
3514     if (verbose)
3515         pm_message("text font %s", const_name(font_name, text_font));
3516 }
3517 
3518 
3519 
3520 static drawFn TxFace;
3521 
3522 static void
TxFace(struct canvas * const canvasP,blitList * const blitListP,int const version)3523 TxFace(struct canvas * const canvasP,
3524        blitList *      const blitListP,
3525        int             const version) {
3526 
3527     text_face = readByte();
3528     if (verbose)
3529         pm_message("text face %d", text_face);
3530 }
3531 
3532 
3533 
3534 static drawFn TxSize;
3535 
3536 static void
TxSize(struct canvas * const canvasP,blitList * const blitListP,int const version)3537 TxSize(struct canvas * const canvasP,
3538        blitList *      const blitListP,
3539        int             const version) {
3540 
3541     text_size = readWord();
3542     if (verbose)
3543         pm_message("text size %d", text_size);
3544 }
3545 
3546 
3547 
3548 static void
skipText(blitList * const blitListP)3549 skipText(blitList * const blitListP) {
3550 
3551     skip(readByte());
3552 
3553     blitListP->unblittableText = true;
3554 }
3555 
3556 
3557 
3558 static int
absValue(int const x)3559 absValue(int const x) {
3560     if (x < 0)
3561         return -x;
3562     else
3563         return x;
3564 }
3565 
3566 
3567 
3568 static struct font*
getFont(int const font,int const size,int const style)3569 getFont(int const font,
3570         int const size,
3571         int const style) {
3572 
3573     int closeness, bestcloseness;
3574     struct fontinfo* fi, *best;
3575 
3576     best = 0;
3577     for (fi = fontlist; fi; fi = fi->next) {
3578         closeness = absValue(fi->font - font) * 10000 +
3579             absValue(fi->size - size) * 100 +
3580             absValue(fi->style - style);
3581         if (!best || closeness < bestcloseness) {
3582             best = fi;
3583             bestcloseness = closeness;
3584         }
3585     }
3586 
3587     if (best) {
3588         if (best->loaded)
3589             return best->loaded;
3590 
3591         if ((best->loaded = pbm_loadbdffont(best->filename)))
3592             return best->loaded;
3593     }
3594 
3595     /* It would be better to go looking for the nth best font, really */
3596     return 0;
3597 }
3598 
3599 
3600 
3601 /* This only does 0, 90, 180 and 270 degree rotations */
3602 
3603 static void
rotate(int * const x,int * const y)3604 rotate(int * const x,
3605        int * const y) {
3606     int tmp;
3607 
3608     if (ps_rotation >= 315 || ps_rotation <= 45)
3609         return;
3610 
3611     *x -= ps_cent_x;
3612     *y -= ps_cent_y;
3613 
3614     if (ps_rotation > 45 && ps_rotation < 135) {
3615         tmp = *x;
3616         *x = *y;
3617         *y = tmp;
3618     }
3619     else if (ps_rotation >= 135 && ps_rotation < 225) {
3620         *x = -*x;
3621     }
3622     else if (ps_rotation >= 225 && ps_rotation < 315) {
3623         tmp = *x;
3624         *x = *y;
3625         *y = -tmp;
3626     }
3627     *x += ps_cent_x;
3628     *y += ps_cent_y;
3629 }
3630 
3631 
3632 
3633 static void
doPsText(struct canvas * const canvasP,Word const tx,Word const ty)3634 doPsText(struct canvas * const canvasP,
3635          Word            const tx,
3636          Word            const ty) {
3637 
3638     int len, width, i, w, h, x, y, rx, ry, o;
3639     Byte str[256], ch;
3640     struct glyph* glyph;
3641 
3642     current.x = tx;
3643     current.y = ty;
3644 
3645     if (!ps_cent_set) {
3646         ps_cent_x += tx;
3647         ps_cent_y += ty;
3648         ps_cent_set = 1;
3649     }
3650 
3651     len = readByte();
3652 
3653     /* XXX this width calculation is not completely correct */
3654     width = 0;
3655     for (i = 0; i < len; i++) {
3656         ch = str[i] = readByte();
3657         if (tfont->glyph[ch])
3658             width += tfont->glyph[ch]->xadd;
3659     }
3660 
3661     if (verbose) {
3662         str[len] = '\0';
3663         pm_message("ps text: %s", str);
3664     }
3665 
3666     /* XXX The width is calculated in order to do different justifications.
3667      * However, I need the width of original text to finish the job.
3668      * In other words, font metrics for Quickdraw fonts
3669      */
3670 
3671     x = tx;
3672 
3673     for (i = 0; i < len; i++) {
3674         if (!(glyph = tfont->glyph[str[i]]))
3675             continue;
3676 
3677         y = ty - glyph->height - glyph->y;
3678         for (h = 0; h < glyph->height; h++) {
3679             for (w = 0; w < glyph->width; w++) {
3680                 rx = x + glyph->x + w;
3681                 ry = y;
3682                 rotate(&rx, &ry);
3683                 if ((rx >= picFrame.left) && (rx < picFrame.right) &&
3684                     (ry >= picFrame.top) && (ry < picFrame.bottom))
3685                 {
3686                     o = pixelIndex(picFrame, rx, ry);
3687                     if (glyph->bmap[h * glyph->width + w]) {
3688                         canvasP->planes.red[o] = foreground.red;
3689                         canvasP->planes.grn[o] = foreground.grn;
3690                         canvasP->planes.blu[o] = foreground.blu;
3691                     }
3692                 }
3693             }
3694             y++;
3695         }
3696         x += glyph->xadd;
3697     }
3698 }
3699 
3700 
3701 
3702 static void
doText(struct canvas * const canvasP,blitList * const blitListP,Word const startx,Word const starty)3703 doText(struct canvas *  const canvasP,
3704        blitList *       const blitListP,
3705        Word             const startx,
3706        Word             const starty) {
3707 
3708     if (blitListP)
3709         skipText(blitListP);
3710     else {
3711         if (!(tfont = getFont(text_font, text_size, text_face)))
3712             tfont = pbm_defaultfont("bdf");
3713 
3714         if (ps_text)
3715             doPsText(canvasP, startx, starty);
3716         else {
3717             int len;
3718             Word x, y;
3719 
3720             x = startx;
3721             y = starty;
3722             for (len = readByte(); len > 0; --len) {
3723                 struct glyph* const glyph = tfont->glyph[readByte()];
3724                 if (glyph) {
3725                     int dy;
3726                     int h;
3727                     for (h = 0, dy = y - glyph->height - glyph->y;
3728                          h < glyph->height;
3729                          ++h, ++dy) {
3730                         int w;
3731                         for (w = 0; w < glyph->width; ++w) {
3732                             struct RGBColor * const colorP =
3733                                 glyph->bmap[h * glyph->width + w] ?
3734                                 &black : &white;
3735                             drawPixel(canvasP,
3736                                       x + w + glyph->x, dy, colorP, text_trf);
3737                         }
3738                     }
3739                     x += glyph->xadd;
3740                 }
3741             }
3742             current.x = x;
3743             current.y = y;
3744         }
3745     }
3746 }
3747 
3748 
3749 
3750 static drawFn LongText;
3751 
3752 static void
LongText(struct canvas * const canvasP,blitList * const blitListP,int const version)3753 LongText(struct canvas * const canvasP,
3754          blitList *      const blitListP,
3755          int             const version) {
3756 
3757     struct Point p;
3758 
3759     readPoint(&p);
3760 
3761     doText(canvasP, blitListP, p.x, p.y);
3762 }
3763 
3764 
3765 
3766 static drawFn DHText;
3767 
3768 static void
DHText(struct canvas * const canvasP,blitList * const blitListP,int const version)3769 DHText(struct canvas * const canvasP,
3770        blitList *      const blitListP,
3771        int             const version) {
3772 
3773     current.x += readByte();
3774 
3775     doText(canvasP, blitListP, current.x, current.y);
3776 }
3777 
3778 
3779 
3780 static drawFn DVText;
3781 
3782 static void
DVText(struct canvas * const canvasP,blitList * const blitListP,int const version)3783 DVText(struct canvas * const canvasP,
3784        blitList *      const blitListP,
3785        int             const version) {
3786 
3787     current.y += readByte();
3788 
3789     doText(canvasP, blitListP, current.x, current.y);
3790 }
3791 
3792 
3793 
3794 static drawFn DHDVText;
3795 
3796 static void
DHDVText(struct canvas * const canvasP,blitList * const blitListP,int const version)3797 DHDVText(struct canvas * const canvasP,
3798          blitList *      const blitListP,
3799          int             const version) {
3800     Byte dh, dv;
3801 
3802     dh = readByte();
3803     dv = readByte();
3804 
3805     if (verbose)
3806         pm_message("dh, dv = %d, %d", dh, dv);
3807 
3808     current.x += dh;
3809     current.y += dv;
3810 
3811     doText(canvasP, blitListP, current.x, current.y);
3812 }
3813 
3814 
3815 
3816 /*
3817  * This could use readPixmap, but I'm too lazy to hack readPixmap.
3818  */
3819 
3820 static void
directBits(struct canvas * const canvasP,blitList * const blitListP,unsigned int const pictVersion,bool const skipRegion)3821 directBits(struct canvas * const canvasP,
3822            blitList *      const blitListP,
3823            unsigned int    const pictVersion,
3824            bool            const skipRegion) {
3825 
3826     struct pixMap   p;
3827     struct Rect     srcRect;
3828     struct Rect     dstRect;
3829     struct raster   raster;
3830     Word            mode;
3831 
3832     /* skip fake len, and fake EOF */
3833     skip(4);    /* Ptr baseAddr == 0x000000ff */
3834     readWord();    /* version */
3835     readRect(&p.Bounds);
3836     p.packType = readWord();
3837     p.packSize = readLong();
3838     p.hRes = readLong();
3839     p.vRes = readLong();
3840     p.pixelType = readWord();
3841     p.pixelSize = readWord();
3842     p.pixelSize = readWord();    /* XXX twice??? */
3843     p.cmpCount = readWord();
3844     p.cmpSize = readWord();
3845     p.planeBytes = readLong();
3846     p.pmTable = readLong();
3847     p.pmReserved = readLong();
3848 
3849     readRect(&srcRect);
3850     if (verbose)
3851         dumpRect("source rectangle:", srcRect);
3852 
3853     readRect(&dstRect);
3854     if (verbose)
3855         dumpRect("destination rectangle:", dstRect);
3856 
3857     mode = readWord();
3858     if (verbose)
3859         pm_message("transfer mode = %s", const_name(transfer_name, mode));
3860 
3861     if (skipRegion)
3862         skip_poly_or_region(canvasP, blitListP, pictVersion);
3863 
3864     unpackbits(ifp, &p.Bounds, 0, p.pixelSize, &raster);
3865 
3866     blit(srcRect, p.Bounds, raster, canvasP, blitListP, p.pixelSize,
3867          dstRect, picFrame, rowlen, NULL, mode);
3868 
3869     freeRaster(raster);
3870 }
3871 
3872 
3873 
3874 #define SKIP_REGION_TRUE TRUE
3875 #define SKIP_REGION_FALSE FALSE
3876 
3877 static drawFn DirectBitsRect;
3878 
3879 static void
DirectBitsRect(struct canvas * const canvasP,blitList * const blitListP,int const version)3880 DirectBitsRect(struct canvas * const canvasP,
3881                blitList *      const blitListP,
3882                int             const version) {
3883 
3884     directBits(canvasP, blitListP, version, SKIP_REGION_FALSE);
3885 }
3886 
3887 
3888 
3889 static drawFn DirectBitsRgn;
3890 
3891 static void
DirectBitsRgn(struct canvas * const canvasP,blitList * const blitListP,int const version)3892 DirectBitsRgn(struct canvas * const canvasP,
3893               blitList *      const blitListP,
3894               int             const version) {
3895 
3896     directBits(canvasP, blitListP, version, SKIP_REGION_TRUE);
3897 }
3898 
3899 
3900 
3901 static void
doPixmap(struct canvas * const canvasP,blitList * const blitListP,int const version,Word const rowBytes,int const is_region)3902 doPixmap(struct canvas * const canvasP,
3903          blitList *      const blitListP,
3904          int             const version,
3905          Word            const rowBytes,
3906          int             const is_region) {
3907 /*----------------------------------------------------------------------------
3908    Do a paletted image.
3909 -----------------------------------------------------------------------------*/
3910     Word mode;
3911     struct pixMap p;
3912     struct raster raster;
3913     struct RGBColor * color_table;
3914     struct Rect srcRect;
3915     struct Rect dstRect;
3916 
3917     readPixmap(&p);
3918 
3919     if (verbose)
3920         pm_message("%u x %u paletted image",
3921                    p.Bounds.right - p.Bounds.left,
3922                    p.Bounds.bottom - p.Bounds.top);
3923 
3924     color_table = readColorTable();
3925 
3926     readRect(&srcRect);
3927 
3928     if (verbose)
3929         dumpRect("source rectangle:", srcRect);
3930 
3931     readRect(&dstRect);
3932 
3933     if (verbose)
3934         dumpRect("destination rectangle:", dstRect);
3935 
3936     mode = readWord();
3937 
3938     if (verbose)
3939         pm_message("transfer mode = %s", const_name(transfer_name, mode));
3940 
3941     if (is_region)
3942         skip_poly_or_region(canvasP, blitListP, version);
3943 
3944     stage = "unpacking rectangle";
3945 
3946     unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster);
3947 
3948     blit(srcRect, p.Bounds, raster, canvasP, blitListP, 8,
3949          dstRect, picFrame, rowlen, color_table, mode);
3950 
3951     free(color_table);
3952     freeRaster(raster);
3953 }
3954 
3955 
3956 
3957 static void
doBitmap(FILE * const ifP,struct canvas * const canvasP,blitList * const blitListP,int const version,int const rowBytes,int const is_region)3958 doBitmap(FILE *          const ifP,
3959          struct canvas * const canvasP,
3960          blitList *      const blitListP,
3961          int             const version,
3962          int             const rowBytes,
3963          int             const is_region) {
3964 /*----------------------------------------------------------------------------
3965    Do a bitmap.  That's one bit per pixel, 0 is white, 1 is black.
3966 
3967    Read the raster from file 'ifP'.
3968 -----------------------------------------------------------------------------*/
3969     struct Rect Bounds;
3970     struct Rect srcRect;
3971     struct Rect dstRect;
3972     Word mode;
3973     struct raster raster;
3974         /* This raster contains padding on the right to make a multiple
3975            of 16 pixels per row.
3976         */
3977     static struct RGBColor color_table[] = {
3978         {65535L, 65535L, 65535L}, {0, 0, 0} };
3979 
3980     readRect(&Bounds);
3981     readRect(&srcRect);
3982     readRect(&dstRect);
3983     mode = readWord();
3984     if (verbose)
3985         pm_message("transfer mode = %s", const_name(transfer_name, mode));
3986 
3987     if (is_region)
3988         skip_poly_or_region(canvasP, blitListP, version);
3989 
3990     stage = "unpacking rectangle";
3991 
3992     unpackbits(ifP, &Bounds, rowBytes, 1, &raster);
3993 
3994     blit(srcRect, Bounds, raster, canvasP, blitListP, 8,
3995          dstRect, picFrame, rowlen, color_table, mode);
3996 
3997     freeRaster(raster);
3998 }
3999 
4000 
4001 
4002 static drawFn BitsRect;
4003 
4004 static void
BitsRect(struct canvas * const canvasP,blitList * const blitListP,int const version)4005 BitsRect(struct canvas * const canvasP,
4006          blitList *      const blitListP,
4007          int             const version) {
4008 
4009     Word rowBytesWord;
4010     bool pixMap;
4011     unsigned int rowBytes;
4012 
4013     stage = "Reading rowBytes word for bitsrect";
4014     rowBytesWord = readWord();
4015 
4016     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
4017 
4018     if (pixMap)
4019         doPixmap(canvasP, blitListP, version, rowBytes, 0);
4020     else
4021         doBitmap(ifp, canvasP, blitListP, version, rowBytes, 0);
4022 }
4023 
4024 
4025 
4026 static drawFn BitsRegion;
4027 
4028 static void
BitsRegion(struct canvas * const canvasP,blitList * const blitListP,int const version)4029 BitsRegion(struct canvas * const canvasP,
4030            blitList *      const blitListP,
4031            int             const version) {
4032 
4033     Word rowBytesWord;
4034     bool pixMap;
4035     unsigned int rowBytes;
4036 
4037     stage = "Reading rowBytes for bitsregion";
4038     rowBytesWord = readWord();
4039 
4040     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
4041 
4042     if (pixMap)
4043         doPixmap(canvasP, blitListP, version, rowBytes, 1);
4044     else
4045         doBitmap(ifp, canvasP, blitListP, version, rowBytes, 1);
4046 }
4047 
4048 
4049 
4050  /*
4051   * See http://developer.apple.com/techpubs/mac/QuickDraw/QuickDraw-461.html
4052   * for opcode description
4053   */
4054 static struct opdef const optable[] = {
4055 /* 0x00 */  { "NOP", 0, NULL, "nop" },
4056 /* 0x01 */  { "ClipRgn", NA, ClipRgn, "clip region" },
4057 /* 0x02 */  { "BkPat", 8, BkPat, "background pattern" },
4058 /* 0x03 */  { "TxFont", 2, TxFont, "text font (word)" },
4059 /* 0x04 */  { "TxFace", 1, TxFace, "text face (byte)" },
4060 /* 0x05 */  { "TxMode", 2, TxMode, "text mode (word)" },
4061 /* 0x06 */  { "SpExtra", 4, NULL, "space extra (fixed point)" },
4062 /* 0x07 */  { "PnSize", 4, PnSize, "pen size (point)" },
4063 /* 0x08 */  { "PnMode", 2, PnMode, "pen mode (word)" },
4064 /* 0x09 */  { "PnPat", 8, PnPat, "pen pattern" },
4065 /* 0x0a */  { "FillPat", 8, FillPat, "fill pattern" },
4066 /* 0x0b */  { "OvSize", 4, NULL, "oval size (point)" },
4067 /* 0x0c */  { "Origin", 4, NULL, "dh, dv (word)" },
4068 /* 0x0d */  { "TxSize", 2, TxSize, "text size (word)" },
4069 /* 0x0e */  { "FgColor", 4, NULL, "foreground color (longword)" },
4070 /* 0x0f */  { "BkColor", 4, NULL, "background color (longword)" },
4071 /* 0x10 */  { "TxRatio", 8, NULL, "numerator (point), denominator (point)" },
4072 /* 0x11 */  { "Version", 1, NULL, "version (byte)" },
4073 /* 0x12 */  { "BkPixPat", NA, BkPixPat, "color background pattern" },
4074 /* 0x13 */  { "PnPixPat", NA, PnPixPat, "color pen pattern" },
4075 /* 0x14 */  { "FillPixPat", NA, FillPixPat, "color fill pattern" },
4076 /* 0x15 */  { "PnLocHFrac", 2, PnLocHFrac, "fractional pen position" },
4077 /* 0x16 */  { "ChExtra", 2, NULL, "extra for each character" },
4078 /* 0x17 */  RESERVED_OP(0),
4079 /* 0x18 */  RESERVED_OP(0),
4080 /* 0x19 */  RESERVED_OP(0),
4081 /* 0x1a */  { "RGBFgCol", RGB_LEN, RGBFgCol, "RGB foreColor" },
4082 /* 0x1b */  { "RGBBkCol", RGB_LEN, RGBBkCol, "RGB backColor" },
4083 /* 0x1c */  { "HiliteMode", 0, NULL, "hilite mode flag" },
4084 /* 0x1d */  { "HiliteColor", RGB_LEN, NULL, "RGB hilite color" },
4085 /* 0x1e */  { "DefHilite", 0, NULL, "Use default hilite color" },
4086 /* 0x1f */  { "OpColor", NA, OpColor, "RGB OpColor for arithmetic modes" },
4087 /* 0x20 */  { "Line", 8, Line, "pnLoc (point), newPt (point)" },
4088 /* 0x21 */  { "LineFrom", 4, LineFrom, "newPt (point)" },
4089 /* 0x22 */  { "ShortLine", 6, ShortLine,
4090               "pnLoc (point, dh, dv (-128 .. 127))" },
4091 /* 0x23 */  { "ShortLineFrom", 2, ShortLineFrom, "dh, dv (-128 .. 127)" },
4092 /* 0x24 */  RESERVED_OP(WORD_LEN),
4093 /* 0x25 */  RESERVED_OP(WORD_LEN),
4094 /* 0x26 */  RESERVED_OP(WORD_LEN),
4095 /* 0x27 */  RESERVED_OP(WORD_LEN),
4096 /* 0x28 */  { "LongText", NA, LongText,
4097               "txLoc (point), count (0..255), text" },
4098 /* 0x29 */  { "DHText", NA, DHText, "dh (0..255), count (0..255), text" },
4099 /* 0x2a */  { "DVText", NA, DVText, "dv (0..255), count (0..255), text" },
4100 /* 0x2b */  { "DHDVText", NA, DHDVText,
4101               "dh, dv (0..255), count (0..255), text" },
4102 /* 0x2c */  RESERVED_OP(WORD_LEN),
4103 /* 0x2d */  RESERVED_OP(WORD_LEN),
4104 /* 0x2e */  RESERVED_OP(WORD_LEN),
4105 /* 0x2f */  RESERVED_OP(WORD_LEN),
4106 /* 0x30 */  { "frameRect", 8, frameRect, "rect" },
4107 /* 0x31 */  { "paintRect", 8, paintRect, "rect" },
4108 /* 0x32 */  { "eraseRect", 8, NULL, "rect" },
4109 /* 0x33 */  { "invertRect", 8, NULL, "rect" },
4110 /* 0x34 */  { "fillRect", 8, NULL, "rect" },
4111 /* 0x35 */  RESERVED_OP(8),
4112 /* 0x36 */  RESERVED_OP(8),
4113 /* 0x37 */  RESERVED_OP(8),
4114 /* 0x38 */  { "frameSameRect", 0, frameSameRect, "rect" },
4115 /* 0x39 */  { "paintSameRect", 0, paintSameRect, "rect" },
4116 /* 0x3a */  { "eraseSameRect", 0, NULL, "rect" },
4117 /* 0x3b */  { "invertSameRect", 0, NULL, "rect" },
4118 /* 0x3c */  { "fillSameRect", 0, NULL, "rect" },
4119 /* 0x3d */  RESERVED_OP(0),
4120 /* 0x3e */  RESERVED_OP(0),
4121 /* 0x3f */  RESERVED_OP(0),
4122 /* 0x40 */  { "frameRRect", 8, NULL, "rect" },
4123 /* 0x41 */  { "paintRRect", 8, NULL, "rect" },
4124 /* 0x42 */  { "eraseRRect", 8, NULL, "rect" },
4125 /* 0x43 */  { "invertRRect", 8, NULL, "rect" },
4126 /* 0x44 */  { "fillRRrect", 8, NULL, "rect" },
4127 /* 0x45 */  RESERVED_OP(8),
4128 /* 0x46 */  RESERVED_OP(8),
4129 /* 0x47 */  RESERVED_OP(8),
4130 /* 0x48 */  { "frameSameRRect", 0, NULL, "rect" },
4131 /* 0x49 */  { "paintSameRRect", 0, NULL, "rect" },
4132 /* 0x4a */  { "eraseSameRRect", 0, NULL, "rect" },
4133 /* 0x4b */  { "invertSameRRect", 0, NULL, "rect" },
4134 /* 0x4c */  { "fillSameRRect", 0, NULL, "rect" },
4135 /* 0x4d */  RESERVED_OP(0),
4136 /* 0x4e */  RESERVED_OP(0),
4137 /* 0x4f */  RESERVED_OP(0),
4138 /* 0x50 */  { "frameOval", 8, NULL, "rect" },
4139 /* 0x51 */  { "paintOval", 8, NULL, "rect" },
4140 /* 0x52 */  { "eraseOval", 8, NULL, "rect" },
4141 /* 0x53 */  { "invertOval", 8, NULL, "rect" },
4142 /* 0x54 */  { "fillOval", 8, NULL, "rect" },
4143 /* 0x55 */  RESERVED_OP(8),
4144 /* 0x56 */  RESERVED_OP(8),
4145 /* 0x57 */  RESERVED_OP(8),
4146 /* 0x58 */  { "frameSameOval", 0, NULL, "rect" },
4147 /* 0x59 */  { "paintSameOval", 0, NULL, "rect" },
4148 /* 0x5a */  { "eraseSameOval", 0, NULL, "rect" },
4149 /* 0x5b */  { "invertSameOval", 0, NULL, "rect" },
4150 /* 0x5c */  { "fillSameOval", 0, NULL, "rect" },
4151 /* 0x5d */  RESERVED_OP(0),
4152 /* 0x5e */  RESERVED_OP(0),
4153 /* 0x5f */  RESERVED_OP(0),
4154 /* 0x60 */  { "frameArc", 12, NULL, "rect, startAngle, arcAngle" },
4155 /* 0x61 */  { "paintArc", 12, NULL, "rect, startAngle, arcAngle" },
4156 /* 0x62 */  { "eraseArc", 12, NULL, "rect, startAngle, arcAngle" },
4157 /* 0x63 */  { "invertArc", 12, NULL, "rect, startAngle, arcAngle" },
4158 /* 0x64 */  { "fillArc", 12, NULL, "rect, startAngle, arcAngle" },
4159 /* 0x65 */  RESERVED_OP(12),
4160 /* 0x66 */  RESERVED_OP(12),
4161 /* 0x67 */  RESERVED_OP(12),
4162 /* 0x68 */  { "frameSameArc", 4, NULL, "rect, startAngle, arcAngle" },
4163 /* 0x69 */  { "paintSameArc", 4, NULL, "rect, startAngle, arcAngle" },
4164 /* 0x6a */  { "eraseSameArc", 4, NULL, "rect, startAngle, arcAngle" },
4165 /* 0x6b */  { "invertSameArc", 4, NULL, "rect, startAngle, arcAngle" },
4166 /* 0x6c */  { "fillSameArc", 4, NULL, "rect, startAngle, arcAngle" },
4167 /* 0x6d */  RESERVED_OP(4),
4168 /* 0x6e */  RESERVED_OP(4),
4169 /* 0x6f */  RESERVED_OP(4),
4170 /* 0x70 */  { "framePoly", NA, skip_poly_or_region, "poly" },
4171 /* 0x71 */  { "paintPoly", NA, paintPoly, "poly" },
4172 /* 0x72 */  { "erasePoly", NA, skip_poly_or_region, "poly" },
4173 /* 0x73 */  { "invertPoly", NA, skip_poly_or_region, "poly" },
4174 /* 0x74 */  { "fillPoly", NA, skip_poly_or_region, "poly" },
4175 /* 0x75 */  RESERVED_OP_F(skip_poly_or_region),
4176 /* 0x76 */  RESERVED_OP_F(skip_poly_or_region),
4177 /* 0x77 */  RESERVED_OP_F(skip_poly_or_region),
4178 /* 0x78 */  { "frameSamePoly", 0, NULL, "poly (NYI)" },
4179 /* 0x79 */  { "paintSamePoly", 0, NULL, "poly (NYI)" },
4180 /* 0x7a */  { "eraseSamePoly", 0, NULL, "poly (NYI)" },
4181 /* 0x7b */  { "invertSamePoly", 0, NULL, "poly (NYI)" },
4182 /* 0x7c */  { "fillSamePoly", 0, NULL, "poly (NYI)" },
4183 /* 0x7d */  RESERVED_OP(0),
4184 /* 0x7e */  RESERVED_OP(0),
4185 /* 0x7f */  RESERVED_OP(0),
4186 /* 0x80 */  { "frameRgn", NA, skip_poly_or_region, "region" },
4187 /* 0x81 */  { "paintRgn", NA, skip_poly_or_region, "region" },
4188 /* 0x82 */  { "eraseRgn", NA, skip_poly_or_region, "region" },
4189 /* 0x83 */  { "invertRgn", NA, skip_poly_or_region, "region" },
4190 /* 0x84 */  { "fillRgn", NA, skip_poly_or_region, "region" },
4191 /* 0x85 */  RESERVED_OP_F(skip_poly_or_region),
4192 /* 0x86 */  RESERVED_OP_F(skip_poly_or_region),
4193 /* 0x87 */  RESERVED_OP_F(skip_poly_or_region),
4194 /* 0x88 */  { "frameSameRgn", 0, NULL, "region (NYI)" },
4195 /* 0x89 */  { "paintSameRgn", 0, NULL, "region (NYI)" },
4196 /* 0x8a */  { "eraseSameRgn", 0, NULL, "region (NYI)" },
4197 /* 0x8b */  { "invertSameRgn", 0, NULL, "region (NYI)" },
4198 /* 0x8c */  { "fillSameRgn", 0, NULL, "region (NYI)" },
4199 /* 0x8d */  RESERVED_OP(0),
4200 /* 0x8e */  RESERVED_OP(0),
4201 /* 0x8f */  RESERVED_OP(0),
4202 /* 0x90 */  { "BitsRect", NA, BitsRect, "copybits, rect clipped" },
4203 /* 0x91 */  { "BitsRgn", NA, BitsRegion, "copybits, rgn clipped" },
4204 /* 0x92 */  RESERVED_OP(WORD_LEN),
4205 /* 0x93 */  RESERVED_OP(WORD_LEN),
4206 /* 0x94 */  RESERVED_OP(WORD_LEN),
4207 /* 0x95 */  RESERVED_OP(WORD_LEN),
4208 /* 0x96 */  RESERVED_OP(WORD_LEN),
4209 /* 0x97 */  RESERVED_OP(WORD_LEN),
4210 /* 0x98 */  { "PackBitsRect", NA, BitsRect, "packed copybits, rect clipped" },
4211 /* 0x99 */  { "PackBitsRgn", NA, BitsRegion, "packed copybits, rgn clipped" },
4212 /* 0x9a */  { "DirectBitsRect", NA, DirectBitsRect,
4213               "PixMap, srcRect, dstRect, int copymode, PixData" },
4214 /* 0x9b */  { "DirectBitsRgn", NA, DirectBitsRgn,
4215               "PixMap, srcRect, dstRect, int copymode, maskRgn, PixData" },
4216 /* 0x9c */  RESERVED_OP(WORD_LEN),
4217 /* 0x9d */  RESERVED_OP(WORD_LEN),
4218 /* 0x9e */  RESERVED_OP(WORD_LEN),
4219 /* 0x9f */  RESERVED_OP(WORD_LEN),
4220 /* 0xa0 */  { "ShortComment", 2, ShortComment, "kind (word)" },
4221 /* 0xa1 */  { "LongComment", NA, LongComment,
4222               "kind (word), size (word), data" }
4223 };
4224 
4225 
4226 
4227 static void
processOpcode(Word const opcode,struct canvas * const canvasP,blitList * const blitListP,unsigned int const version)4228 processOpcode(Word            const opcode,
4229               struct canvas * const canvasP,
4230               blitList *      const blitListP,
4231               unsigned int    const version) {
4232 
4233     if (opcode < 0xa2) {
4234         stage = optable[opcode].name;
4235         if (verbose) {
4236             if (streq(stage, "reserved"))
4237                 pm_message("reserved opcode=0x%x", opcode);
4238             else
4239                 pm_message("Opcode: %s", optable[opcode].name);
4240         }
4241 
4242         if (optable[opcode].impl != NULL)
4243             (*optable[opcode].impl)(canvasP, blitListP, version);
4244         else if (optable[opcode].len >= 0)
4245             skip(optable[opcode].len);
4246         else {
4247             /* It's a special length code */
4248             switch (optable[opcode].len) {
4249             case WORD_LEN: {
4250                 Word const len = readWord();
4251                 skip(len);
4252             } break;
4253             default:
4254                 pm_error("can't do length %d", optable[opcode].len);
4255             }
4256         }
4257     } else if (opcode == 0xc00) {
4258         if (verbose)
4259             pm_message("HeaderOp");
4260         stage = "HeaderOp";
4261         skip(24);
4262     } else if (opcode >= 0xa2 && opcode <= 0xaf) {
4263         stage = "skipping reserved";
4264         if (verbose)
4265             pm_message("%s 0x%x", stage, opcode);
4266         skip(readWord());
4267     } else if (opcode >= 0xb0 && opcode <= 0xcf) {
4268         /* just a reserved opcode, no data */
4269         if (verbose)
4270             pm_message("reserved 0x%x", opcode);
4271     } else if (opcode >= 0xd0 && opcode <= 0xfe) {
4272         stage = "skipping reserved";
4273         if (verbose)
4274             pm_message("%s 0x%x", stage, opcode);
4275         skip(readLong());
4276     } else if (opcode >= 0x100 && opcode <= 0x7fff) {
4277         stage = "skipping reserved";
4278         if (verbose)
4279             pm_message("%s 0x%x", stage, opcode);
4280         skip((opcode >> 7) & 255);
4281     } else if (opcode >= 0x8000 && opcode <= 0x80ff) {
4282         /* just a reserved opcode, no data */
4283         if (verbose)
4284             pm_message("reserved 0x%x", opcode);
4285     } else if (opcode >= 0x8100) {
4286         stage = "skipping reserved";
4287         if (verbose)
4288             pm_message("%s 0x%x", stage, opcode);
4289         skip(readLong());
4290     } else
4291         pm_error("This program does not understand opcode 0x%04x", opcode);
4292 }
4293 
4294 
4295 
4296 static void
interpretPict(FILE * const ofP)4297 interpretPict(FILE * const ofP) {
4298 
4299     Byte ch;
4300     Word picSize;
4301     Word opcode;
4302     unsigned int version;
4303     unsigned int i;
4304     struct canvas canvas;
4305     blitList blitList;
4306 
4307     initBlitList(&blitList);
4308 
4309     for (i = 0; i < 64; i++)
4310         pen_pat.pix[i] = bkpat.pix[i] = fillpat.pix[i] = 1;
4311     pen_width = pen_height = 1;
4312     pen_mode = 0; /* srcCopy */
4313     pen_trf = transfer(pen_mode);
4314     text_mode = 0; /* srcCopy */
4315     text_trf = transfer(text_mode);
4316 
4317     stage = "Reading picture size";
4318     picSize = readWord();
4319 
4320     if (verbose)
4321         pm_message("picture size = %u (0x%x)", picSize, picSize);
4322 
4323     stage = "reading picture frame";
4324     readRect(&picFrame);
4325 
4326     if (verbose) {
4327         dumpRect("Picture frame:", picFrame);
4328         pm_message("Picture size is %u x %u",
4329                    picFrame.right - picFrame.left,
4330                    picFrame.bottom - picFrame.top);
4331     }
4332 
4333     if (!fullres) {
4334         rowlen = picFrame.right  - picFrame.left;
4335         collen = picFrame.bottom - picFrame.top;
4336 
4337         allocPlanes(rowlen, collen, &canvas.planes);
4338 
4339         clip_rect = picFrame;
4340     }
4341 
4342     while ((ch = readByte()) == 0)
4343         ;
4344     if (ch != 0x11)
4345         pm_error("No version number");
4346 
4347     version = readByte();
4348 
4349     switch (version) {
4350     case 1:
4351         break;
4352     case 2: {
4353         unsigned char const subcode = readByte();
4354         if (subcode != 0xff)
4355             pm_error("The only Version 2 PICT images this program "
4356                      "undertands are subcode 0xff.  This image has "
4357                      "subcode 0x%02x", subcode);
4358     } break;
4359     default:
4360         pm_error("Unrecognized PICT version %u", version);
4361     }
4362 
4363     if (verbose)
4364         pm_message("PICT version %u", version);
4365 
4366     while((opcode = nextOp(version)) != 0xff)
4367         processOpcode(opcode, &canvas, fullres ? &blitList : NULL, version);
4368 
4369     if (fullres) {
4370         if (blitList.unblittableText)
4371             pm_message("Warning: text is omitted from the output because "
4372                        "we don't know how to do text with -fullres.");
4373         doBlitList(&canvas, &blitList);
4374     }
4375     outputPpm(ofP, canvas.planes);
4376 
4377     freePlanes(canvas.planes);
4378 }
4379 
4380 
4381 
4382 static void
loadDefaultFontDir(void)4383 loadDefaultFontDir(void) {
4384 /*----------------------------------------------------------------------------
4385    Load the fonts from the font directory file "fontdir" (in the current
4386    directory), if it exists.
4387 -----------------------------------------------------------------------------*/
4388     struct stat statbuf;
4389     int rc;
4390 
4391     rc = stat("fontdir", &statbuf);
4392 
4393     if (rc == 0)
4394         load_fontdir("fontdir");
4395 }
4396 
4397 
4398 
4399 int
main(int argc,char * argv[])4400 main(int argc, char * argv[]) {
4401     int argn;
4402     int header;
4403     const char* const usage =
4404 "[-verbose] [-fullres] [-noheader] [-quickdraw] [-fontdir file] [pictfile]";
4405 
4406     ppm_init( &argc, argv );
4407 
4408     argn = 1;
4409     verbose = 0;
4410     fullres = 0;
4411     header = 1;
4412     recognize_comment = 1;
4413 
4414     while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
4415         if (pm_keymatch(argv[argn], "-verbose", 2))
4416             verbose++;
4417         else if (pm_keymatch(argv[argn], "-fullres", 3))
4418             fullres = 1;
4419         else if (pm_keymatch(argv[argn], "-noheader", 2))
4420             header = 0;
4421         else if (pm_keymatch(argv[argn], "-quickdraw", 2))
4422             recognize_comment = 0;
4423         else if (pm_keymatch(argv[argn], "-fontdir", 3)) {
4424             argn++;
4425             if (!argv[argn])
4426                 pm_usage(usage);
4427             else
4428                 load_fontdir(argv[argn]);
4429         }
4430         else
4431             pm_usage(usage);
4432         ++argn;
4433     }
4434 
4435     if (argn < argc) {
4436         ifp = pm_openr(argv[argn]);
4437         ++argn;
4438     } else
4439         ifp = stdin;
4440 
4441     if (argn != argc)
4442         pm_usage(usage);
4443 
4444     loadDefaultFontDir();
4445 
4446     if (header) {
4447         stage = "Reading 512 byte header";
4448         /* Note that the "header" in PICT is entirely comment! */
4449         skip(512);
4450     }
4451 
4452     interpretPict(stdout);
4453 
4454     pm_close(stdout);
4455 
4456     return 0;
4457 }
4458 
4459 
4460 
4461