1 /* Sarien - A Sierra AGI resource interpreter engine
2 * Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
3 *
4 * $Id: picture.c,v 1.44 2001/10/31 00:27:26 cmatsuoka Exp $
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; see docs/COPYING for further details.
9 */
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <assert.h>
14 #include "sarien.h"
15 #include "agi.h"
16 #include "graphics.h"
17 #include "rand.h"
18 #include "savegame.h"
19
20 #define next_byte data[foffs++]
21
22 static UINT8 *data;
23 static UINT32 flen;
24 static UINT32 foffs;
25
26 static UINT8 patCode;
27 static UINT8 patNum;
28 static UINT8 pri_on;
29 static UINT8 scr_on;
30 static UINT8 scr_colour;
31 static UINT8 pri_colour;
32
33
put_virt_pixel(int x,int y)34 static void put_virt_pixel (int x, int y)
35 {
36 UINT8 *p;
37
38 if (x >= _WIDTH || y >= _HEIGHT)
39 return;
40
41 p = &game.sbuf[y * _WIDTH + x];
42
43 if (pri_on) *p = (pri_colour << 4) | (*p & 0x0f);
44 if (scr_on) *p = scr_colour | (*p & 0xf0);
45 }
46
47
48 /* For the flood fill routines */
49
50 #define STACK_SEG_SIZE 0x1000
51
52 struct point_xy {
53 struct point_xy *next;
54 unsigned int x, y;
55 };
56
57 #define MAX_STACK_SEGS 16
58 static unsigned int stack_num_segs;
59 static unsigned int stack_seg;
60 static unsigned int stack_ptr;
61
62 static struct point_xy *stack[MAX_STACK_SEGS];
63
_PUSH(struct point_xy * c)64 static INLINE void _PUSH (struct point_xy *c)
65 {
66 if (stack_ptr >= STACK_SEG_SIZE) {
67 /* Allocate new stack segment */
68
69 assert (stack_num_segs < MAX_STACK_SEGS);
70
71 if (stack_num_segs <= ++stack_seg) {
72 _D ("new stack (#%d)", stack_num_segs);
73 stack[stack_num_segs] = malloc (sizeof (struct point_xy)
74 * STACK_SEG_SIZE);
75 assert (stack[stack_num_segs] != NULL);
76 stack_num_segs++;
77 }
78 stack_ptr = 0;
79 }
80
81 stack[stack_seg][stack_ptr].x = c->x;
82 stack[stack_seg][stack_ptr].y = c->y;
83 stack_ptr++;
84 }
85
86
_POP(struct point_xy * c)87 static INLINE void _POP (struct point_xy *c)
88 {
89 if (stack_ptr == 0) {
90 if (stack_seg == 0) {
91 c->x = c->y = 0xffff;
92 } else {
93 stack_seg--;
94 stack_ptr = STACK_SEG_SIZE - 1;
95 }
96
97 return;
98 }
99
100 stack_ptr--;
101 c->x = stack[stack_seg][stack_ptr].x;
102 c->y = stack[stack_seg][stack_ptr].y;
103 }
104
105
106 /**************************************************************************
107 ** drawline
108 ** Draws an AGI line.
109 **
110 ** line drawing routine sent to me by joshua neal
111 ** modified by stuart george, fixed >>2 to >>1 and some other bugs
112 ** like x1 instead of y1, etc
113 **************************************************************************/
draw_line(int x1,int y1,int x2,int y2)114 static void draw_line (int x1, int y1, int x2, int y2)
115 {
116 int i, x, y, deltaX, deltaY, stepX, stepY, errorX, errorY, detdelta;
117
118 /* CM: Do clipping */
119 #define clip(x, y) if((x)>=(y)) (x)=(y)
120 clip (x1, _WIDTH-1);
121 clip (x2, _WIDTH-1);
122 clip (y1, _HEIGHT-1);
123 clip (y2, _HEIGHT-1);
124
125 /* Vertical line */
126
127 if (x1 == x2) {
128 if(y1 > y2) {
129 y = y1;
130 y1 = y2;
131 y2 = y;
132 }
133
134 for ( ; y1 <= y2; y1++)
135 put_virt_pixel (x1, y1);
136
137 return;
138 }
139
140 /* Horizontal line */
141
142 if (y1 == y2) {
143 if (x1 > x2) {
144 x = x1;
145 x1 = x2;
146 x2 = x;
147 }
148
149 for( ; x1 <= x2; x1++)
150 put_virt_pixel (x1, y1);
151
152 return;
153 }
154
155 y = y1;
156 x = x1;
157
158 stepY = 1;
159 deltaY = y2-y1;
160 if (deltaY < 0) {
161 stepY = -1;
162 deltaY = -deltaY;
163 }
164
165 stepX = 1;
166 deltaX = x2 - x1;
167 if (deltaX < 0) {
168 stepX = -1;
169 deltaX = -deltaX;
170 }
171
172 if(deltaY > deltaX) {
173 i=deltaY;
174 detdelta=deltaY;
175 errorX=deltaY/2;
176 errorY=0;
177 } else {
178 i=deltaX;
179 detdelta=deltaX;
180 errorX=0;
181 errorY=deltaX/2;
182 }
183
184 put_virt_pixel (x, y);
185
186 do {
187 errorY += deltaY;
188 if (errorY >= detdelta) {
189 errorY -= detdelta;
190 y += stepY;
191 }
192
193 errorX += deltaX;
194 if (errorX >= detdelta) {
195 errorX -= detdelta;
196 x += stepX;
197 }
198
199 put_virt_pixel(x, y);
200 i--;
201 } while (i > 0);
202
203 /* put_virt_pixel (x, y); */
204 }
205
206 /**************************************************************************
207 ** relativeDraw
208 **
209 ** Draws short lines relative to last position. (drawing action 0xF7)
210 **************************************************************************/
dynamic_draw_line()211 static void dynamic_draw_line ()
212 {
213 int x1, y1, disp, dx, dy;
214
215 x1 = next_byte;
216 y1 = next_byte;
217
218 put_virt_pixel (x1, y1);
219
220 while (42) {
221 if ((disp = next_byte) >= 0xf0)
222 break;
223
224 dx= ((disp & 0xf0) >> 4) & 0x0f;
225 dy= (disp & 0x0f);
226
227 if (dx & 0x08)
228 dx = -(dx & 0x07);
229
230 if (dy & 0x08)
231 dy = -(dy & 0x07);
232
233 draw_line (x1, y1, x1 + dx, y1 + dy);
234 x1 += dx;
235 y1 += dy;
236 }
237 foffs--;
238 }
239
240 /**************************************************************************
241 ** absoluteLine
242 **
243 ** Draws long lines to actual locations (cf. relative) (drawing action 0xF6)
244 **************************************************************************/
absolute_draw_line()245 static void absolute_draw_line ()
246 {
247 int x1, y1, x2, y2;
248
249 x1 = next_byte;
250 y1 = next_byte;
251 put_virt_pixel (x1, y1);
252
253 while (42) {
254 if ((x2 = next_byte) >= 0xf0)
255 break;
256
257 if ((y2 = next_byte) >= 0xf0)
258 break;
259
260 draw_line (x1, y1, x2, y2);
261 x1 = x2;
262 y1 = y2;
263 }
264 foffs--;
265 }
266
267
268 /**************************************************************************
269 ** okToFill
270 **************************************************************************/
is_ok_fill_here(int x,int y)271 static INLINE int is_ok_fill_here (int x, int y)
272 {
273 unsigned int i;
274 UINT8 p;
275
276 if (!scr_on && !pri_on)
277 return FALSE;
278
279 i = y * _WIDTH + x;
280 p = game.sbuf[i];
281
282 if (!pri_on && scr_on && scr_colour != 15)
283 return (p & 0x0f) == 15;
284
285 if (pri_on && !scr_on && pri_colour != 4)
286 return (p >> 4) == 4;
287
288 return (scr_on && (p & 0x0f) == 15 && scr_colour != 15);
289 }
290
291 /**************************************************************************
292 ** agiFill
293 **************************************************************************/
agiFill(int x,int y)294 static void agiFill (int x, int y)
295 {
296 struct point_xy c;
297
298 c.x = x;
299 c.y = y;
300 _PUSH (&c);
301
302 while (42) {
303 _POP(&c);
304
305 /* Exit if stack is empty */
306 if (c.x == 0xffff || c.y == 0xffff)
307 break;
308
309 if (is_ok_fill_here (c.x, c.y)) {
310 put_virt_pixel (c.x, c.y);
311 if (c.x > 0 && is_ok_fill_here (c.x - 1, c.y)) {
312 c.x--; _PUSH (&c); c.x++;
313 }
314 if (c.x < _WIDTH - 1 && is_ok_fill_here (c.x + 1, c.y)) {
315 c.x++; _PUSH (&c); c.x--;
316 }
317 if (c.y < _HEIGHT - 1 && is_ok_fill_here (c.x, c.y + 1)) {
318 c.y++; _PUSH (&c); c.y--;
319 }
320 if (c.y > 0 && is_ok_fill_here (c.x, c.y - 1)) {
321 c.y--; _PUSH (&c); c.y++;
322 }
323 }
324 }
325
326 stack_ptr = 0;
327 stack_seg = 0;
328 }
329
330 /**************************************************************************
331 ** xCorner
332 **
333 ** Draws an xCorner (drawing action 0xF5)
334 **************************************************************************/
x_corner()335 static void x_corner ()
336 {
337 int x1, x2, y1, y2;
338
339 x1 = next_byte;
340 y1 = next_byte;
341 put_virt_pixel (x1, y1);
342
343 while (42) {
344 x2=next_byte;
345
346 if (x2 >= 0xf0)
347 break;
348
349 draw_line (x1, y1, x2, y1);
350 x1 = x2;
351 y2 = next_byte;
352
353 if (y2 >= 0xF0)
354 break;
355
356 draw_line (x1, y1, x1, y2);
357 y1 = y2;
358 }
359 foffs--;
360 }
361
362
363 /**************************************************************************
364 ** yCorner
365 **
366 ** Draws an yCorner (drawing action 0xF4)
367 **************************************************************************/
y_corner()368 static void y_corner ()
369 {
370 int x1, x2, y1, y2;
371
372 x1 = next_byte;
373 y1 = next_byte;
374 put_virt_pixel (x1, y1);
375
376 while (42) {
377 y2 = next_byte;
378
379 if (y2 >= 0xF0)
380 break;
381
382 draw_line (x1, y1, x1, y2);
383 y1 = y2;
384 x2 = next_byte;
385
386 if (x2 >= 0xF0)
387 break;
388
389 draw_line (x1, y1, x2, y1);
390 x1 = x2;
391 }
392
393 foffs--;
394 }
395
396 /**************************************************************************
397 ** fill
398 **
399 ** AGI flood fill. (drawing action 0xF8)
400 **************************************************************************/
fill()401 static void fill ()
402 {
403 int x1, y1;
404
405 while ((x1 = next_byte) < 0xF0 && (y1 = next_byte) < 0xF0)
406 agiFill (x1, y1);
407
408 foffs--;
409 }
410
411
412 #define plotPatternPoint() do { \
413 if (patCode & 0x20) { \
414 if ((splatterMap[bitPos>>3] >> (7-(bitPos&7))) & 1) \
415 put_virt_pixel(x1, y1); \
416 bitPos++; \
417 if (bitPos == 0xff) \
418 bitPos=0; \
419 } else put_virt_pixel(x1, y1); \
420 } while (0)
421
422 /**************************************************************************
423 ** plotPattern
424 **
425 ** Draws pixels, circles, squares, or splatter brush patterns depending
426 ** on the pattern code.
427 **************************************************************************/
plotPattern(unsigned int x,unsigned int y)428 void plotPattern(unsigned int x, unsigned int y)
429 {
430 static UINT8 circles[][15] = { /* agi circle bitmaps */
431 { 0x80 },
432 { 0xfc },
433 { 0x5f, 0xf4 },
434 { 0x66, 0xff, 0xf6, 0x60 },
435 { 0x23, 0xbf, 0xff, 0xff, 0xee, 0x20 },
436 { 0x31, 0xe7, 0x9e, 0xff, 0xff, 0xde, 0x79, 0xe3, 0x00 },
437 { 0x38, 0xf9, 0xf3, 0xef, 0xff, 0xff,
438 0xff, 0xfe, 0xf9, 0xf3, 0xe3, 0x80 },
439 { 0x18, 0x3c, 0x7e, 0x7e, 0x7e, 0xff, 0xff,
440 0xff, 0xff, 0xff, 0x7e, 0x7e, 0x7e, 0x3c, 0x18 }
441 };
442
443 static UINT8 splatterMap[32] = { /* splatter brush bitmaps */
444 0x20, 0x94, 0x02, 0x24, 0x90, 0x82, 0xa4, 0xa2,
445 0x82, 0x09, 0x0a, 0x22, 0x12, 0x10, 0x42, 0x14,
446 0x91, 0x4a, 0x91, 0x11, 0x08, 0x12, 0x25, 0x10,
447 0x22, 0xa8, 0x14, 0x24, 0x00, 0x50, 0x24, 0x04
448 };
449
450 static UINT8 splatterStart[128] = { /* starting bit position */
451 0x00, 0x18, 0x30, 0xc4, 0xdc, 0x65, 0xeb, 0x48,
452 0x60, 0xbd, 0x89, 0x05, 0x0a, 0xf4, 0x7d, 0x7d,
453 0x85, 0xb0, 0x8e, 0x95, 0x1f, 0x22, 0x0d, 0xdf,
454 0x2a, 0x78, 0xd5, 0x73, 0x1c, 0xb4, 0x40, 0xa1,
455 0xb9, 0x3c, 0xca, 0x58, 0x92, 0x34, 0xcc, 0xce,
456 0xd7, 0x42, 0x90, 0x0f, 0x8b, 0x7f, 0x32, 0xed,
457 0x5c, 0x9d, 0xc8, 0x99, 0xad, 0x4e, 0x56, 0xa6,
458 0xf7, 0x68, 0xb7, 0x25, 0x82, 0x37, 0x3a, 0x51,
459 0x69, 0x26, 0x38, 0x52, 0x9e, 0x9a, 0x4f, 0xa7,
460 0x43, 0x10, 0x80, 0xee, 0x3d, 0x59, 0x35, 0xcf,
461 0x79, 0x74, 0xb5, 0xa2, 0xb1, 0x96, 0x23, 0xe0,
462 0xbe, 0x05, 0xf5, 0x6e, 0x19, 0xc5, 0x66, 0x49,
463 0xf0, 0xd1, 0x54, 0xa9, 0x70, 0x4b, 0xa4, 0xe2,
464 0xe6, 0xe5, 0xab, 0xe4, 0xd2, 0xaa, 0x4c, 0xe3,
465 0x06, 0x6f, 0xc6, 0x4a, 0xa4, 0x75, 0x97, 0xe1
466 };
467
468 SINT32 circlePos = 0;
469 UINT32 x1, y1, penSize, bitPos = splatterStart[patNum];
470
471 penSize = (patCode & 7);
472
473 if (x < penSize)
474 x = penSize-1;
475 if (y < penSize)
476 y = penSize;
477
478 for (y1 = y - penSize; y1 <= y + penSize; y1++) {
479 for (x1 = x-(penSize+1)/2; x1<=x+penSize/2; x1++) {
480 if (patCode & 0x10) { /* Square */
481 plotPatternPoint();
482 } else { /* Circle */
483 if ((circles[patCode&7][circlePos>>3] >> (7-(circlePos&7)))&1)
484 plotPatternPoint();
485 circlePos++;
486 }
487 }
488 }
489 }
490
491 /**************************************************************************
492 ** plotBrush
493 **
494 ** Plots points and various brush patterns.
495 **************************************************************************/
plot_brush()496 static void plot_brush ()
497 {
498 int x1, y1;
499
500 while (42) {
501 if (patCode & 0x20) {
502 if ((patNum = next_byte) >= 0xF0)
503 break;
504 patNum = (patNum >> 1) & 0x7f;
505 }
506
507 if ((x1 = next_byte) >= 0xf0)
508 break;
509
510 if ((y1 = next_byte) >= 0xf0)
511 break;
512
513 plotPattern (x1, y1);
514 }
515
516 foffs--;
517 }
518
519 #ifdef USE_HIRES
520 #include "hirespic.c"
521 #endif
522
draw_picture()523 static void draw_picture ()
524 {
525 UINT8 act;
526 unsigned int i;
527 int drawing;
528 #ifdef USE_HIRES
529 int save_foffs;
530 #endif
531
532 patCode = 0;
533 patNum = 0;
534 pri_on = scr_on = FALSE;
535 scr_colour = 0xf;
536 pri_colour = 0x4;
537
538 drawing = 1;
539
540 stack[0] = calloc (sizeof (struct point_xy), STACK_SEG_SIZE);
541 stack_ptr = stack_seg = 0;
542 stack_num_segs = 1;
543
544 _D (_D_WARN "Drawing picture");
545 for (drawing = 1; drawing && foffs < flen; ) {
546
547 #ifdef USE_HIRES
548 save_foffs = foffs;
549 #endif
550
551 act = next_byte;
552 switch(act) {
553 case 0xf0: /* set colour on screen */
554 scr_colour = next_byte;
555 scr_colour &= 0xF; /* for v3 drawing diff */
556 scr_on = TRUE;
557 break;
558 case 0xf1: /* disable screen drawing */
559 scr_on = FALSE;
560 break;
561 case 0xf2: /* set colour on priority */
562 pri_colour = next_byte;
563 pri_colour &= 0xf; /* for v3 drawing diff */
564 pri_on = TRUE;
565 break;
566 case 0xf3: /* disable priority screen */
567 pri_on = FALSE;
568 break;
569 case 0xf4: /* y-corner */
570 y_corner ();
571 break;
572 case 0xf5: /* x-corner */
573 x_corner ();
574 break;
575 case 0xf6: /* absolute draw lines */
576 absolute_draw_line ();
577 break;
578 case 0xf7: /* dynamic draw lines */
579 dynamic_draw_line ();
580 break;
581 case 0xf8: /* fill */
582 fill ();
583 break;
584 case 0xf9: /* set pattern */
585 patCode = next_byte;
586 break;
587 case 0xfA: /* plot brush */
588 plot_brush ();
589 break;
590 case 0xFF: /* end of pic data */
591 default:
592 drawing = 0;
593 break;
594 }
595
596 #ifdef USE_HIRES
597 foffs = save_foffs;
598
599 act = next_byte;
600 switch(act) {
601 case 0xf0: /* set colour on screen */
602 scr_colour = next_byte;
603 scr_colour &= 0xF; /* for v3 drawing diff */
604 scr_on = TRUE;
605 break;
606 case 0xf1: /* disable screen drawing */
607 scr_on = FALSE;
608 break;
609 case 0xf2: /* set colour on priority */
610 pri_colour = next_byte;
611 pri_colour &= 0xf; /* for v3 drawing diff */
612 pri_on = TRUE;
613 break;
614 case 0xf3: /* disable priority screen */
615 pri_on = FALSE;
616 break;
617 case 0xf4: /* y-corner */
618 hires_y_corner ();
619 break;
620 case 0xf5: /* x-corner */
621 hires_x_corner ();
622 break;
623 case 0xf6: /* absolute draw lines */
624 absolute_hires_line ();
625 break;
626 case 0xf7: /* dynamic draw lines */
627 dynamic_hires_line ();
628 break;
629 case 0xf8: /* fill */
630 hires_fill ();
631 break;
632 case 0xf9: /* set pattern */
633 patCode = next_byte;
634 break;
635 case 0xfA: /* plot brush */
636 plot_hires_brush ();
637 break;
638 case 0xFF: /* end of pic data */
639 default:
640 drawing = 0;
641 break;
642 }
643 #endif
644 }
645
646 for (i = 0; i < stack_num_segs; i++)
647 free (stack[i]);
648 }
649
650 /*
651 * Public functions
652 */
653
654 /**
655 *
656 */
convert_v3_pic(UINT8 * data,UINT32 len)657 UINT8* convert_v3_pic (UINT8 *data, UINT32 len)
658 {
659 UINT8 d, old = 0, x, *in, *xdata, *out, mode = 0;
660 UINT32 i, ulen;
661
662 xdata = malloc (len + len / 2);
663
664 out = xdata;
665 in = data;
666
667 for (i = ulen = 0; i < len; i++, ulen++) {
668 d = *in++;
669
670 *out++ = x = mode ? ((d & 0xF0) >> 4) + ((old & 0x0F) << 4) : d;
671
672 if (x == 0xFF) {
673 ulen++;
674 break;
675 }
676
677 if (x == 0xf0 || x == 0xf2) {
678 if (mode) {
679 *out++ = d & 0x0F;
680 ulen++;
681 } else {
682 d = *in++;
683 *out++ = (d & 0xF0) >> 4;
684 i++, ulen++;
685 }
686
687 mode = !mode;
688 }
689
690 old = d;
691 }
692
693 free (data);
694 xdata = realloc (xdata, ulen);
695
696 return xdata;
697 }
698
699 /**
700 * Decode an AGI picture resource.
701 * This function decodes an AGI picture resource into the correct slot
702 * and draws it on the AGI screen, optionally cleaning the screen before
703 * drawing.
704 * @param n AGI picture resource number
705 * @param clear clear AGI screen before drawing
706 */
decode_picture(int n,int clear)707 int decode_picture (int n, int clear)
708 {
709 _D (_D_WARN "(%d)", n);
710
711 patCode = 0;
712 patNum = 0;
713 pri_on = scr_on = FALSE;
714 scr_colour = 0xF;
715 pri_colour = 0x4;
716
717 data = game.pictures[n].rdata;
718 flen = game.dir_pic[n].len;
719 foffs = 0;
720
721 if (clear) {
722 memset (game.sbuf, 0x4f, _WIDTH * _HEIGHT);
723 #ifdef USE_HIRES
724 memset (game.hires, 0x4f, _WIDTH * 2 * _HEIGHT);
725 #endif
726 }
727
728 draw_picture ();
729
730 #ifdef USE_HIRES
731 fix_hires_picture ();
732 #endif
733
734 if(clear)
735 clear_image_stack();
736 record_image_stack_call(ADD_PIC, n, clear, 0, 0, 0, 0, 0);
737
738 return err_OK;
739 }
740
741
742 /**
743 * Unload an AGI picture resource.
744 * This function unloads an AGI picture resource and deallocates
745 * resource data.
746 * @param n AGI picture resource number
747 */
unload_picture(int n)748 int unload_picture (int n)
749 {
750 /* remove visual buffer & priority buffer if they exist */
751 if (game.dir_pic[n].flags & RES_LOADED) {
752 free (game.pictures[n].rdata);
753 game.dir_pic[n].flags &= ~RES_LOADED;
754 }
755
756 return err_OK;
757 }
758
759 /**
760 * Show AGI picture.
761 * This function copies a ``hidden'' AGI picture to the output device.
762 */
show_pic()763 void show_pic ()
764 {
765 int i, y;
766 int offset;
767
768 #ifdef USE_HIRES
769 if (opt.hires) {
770 show_hires_pic ();
771 return;
772 }
773 #endif
774
775 i = 0;
776 offset = game.line_min_print * CHAR_LINES;
777 for (y = 0; y < _HEIGHT; y++) {
778 put_pixels_a (0, y + offset, _WIDTH, &game.sbuf[i]);
779 i += _WIDTH;
780 }
781
782 #if 0
783 /* Amiga/IIgs transitions. Too slow, and annoying after a few rooms.
784 * Also doesn't work well with console.
785 */
786 #define BRICK_W 4
787 #define BRICK_H 2
788 for (i = 0; i < 20000; i++) {
789 int bx = rnd (GFX_WIDTH / BRICK_W) * BRICK_W;
790 int by = rnd (GFX_HEIGHT / BRICK_H) * BRICK_H;
791 flush_block (bx, by, bx + BRICK_W - 1, by + BRICK_H - 1);
792 do_update ();
793 }
794 #endif
795
796 flush_screen ();
797 }
798
799
800 /* end: picture.c */
801