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(¤t);
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(¤t);
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