SCI/Specifications/Graphics/SCI0-SCI01 pic resource

From ScummVM :: Wiki
< SCI‎ | Specifications‎ | Graphics
Revision as of 13:04, 6 January 2009 by Timofonic (talk | contribs) (Merging of the SCI documentation)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

The pic (background picture) resource format used in SCI0 is rather complex in comparison to the other graphical resource formats. It is best described as a sequence of drawing operations on a set of four 320x200 canvases, three of which are later used in the game (visual, priority, and control), and one of which is used during the drawing process for auxiliary purposes[1]

In order to describe the process, we will first need to define a set of operations we base them on:

<syntax type="pascal"> FUNCTION peek_input(): Byte; /* returns the byte pointed to by the input pointer */ FUNCTION get_input(): Byte; /* works like peek_input(), but also increminates the

                           ** input pointer  */

FUNCTION skip_input(x): Byte; /* skips x input bytes */ </syntax>


Using these pre-defined functions, we will now define additional helper functions used for reading specifically encoded data tuples:

<syntax type="pascal"> FUNCTION GetAbsCoordinates(): (Integer, Integer) VAR

       x, y, coordinate_prefix : Integer;

BEGIN

       coordinate_prefix := get_input();
       x := get_input();
       y := get_input();
       x |= (coordinate_prefix & 0xf0) << 4;
       y |= (coordinate_prefix & 0x0f) << 8;
       RETURN (x,y)

END


FUNCTION GetRelCoordinates(x : Integer, y: Integer): (Integer, Integer) VAR

       input : Integer;

BEGIN

       input := get_input();
       IF (input & 0x80) THEN
               x -= (input >> 4);
       ELSE
               x += (input >> 4);
       FI
       IF (input & 0x08) THEN
               y -= (input & 0x7);
       ELSE
               y += (input & 0x7);
       FI
       RETURN (x,y)

END </syntax>


We also need some data types based on EGACOLOR and PRIORITY, which can be thought of as integers:

<syntax type="pascal"> TYPE Palette = ARRAY[0..39] of EGACOLOR[0..1] TYPE Priority_Table = ARRAY[0..39] of PRIORITY

Palette default_palette =

    <(0,0), (1,1), (2,2), (3,3), (4,4), (5,5), (6,6), (7,7),
     (8,8), (9,9), (a,a), (b,b), (c,c), (d,d), (e,e), (8,8),
     (8,8), (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (8,8),
     (8,8), (f,9), (f,a), (f,b), (f,c), (f,d), (f,e), (f,f),
     (0,8), (9,1), (2,a), (3,b), (4,c), (5,d), (6,e), (8,8)>;
  1. define DRAW_ENABLE_VISUAL 1
  2. define DRAW_ENABLE_PRIORITY 2
  3. define DRAW_ENABLE_CONTROL 4
  1. define PATTERN_FLAG_RECTANGLE 0x10
  2. define PATTERN_FLAG_USE_PATTERN 0x20

</syntax>


And now for the actual algorithm:

<syntax type="pascal"> FUNCTION DrawPic (cumulative, fill_in_black : Boolean; default_palette: Integer; visual_map, priority_map, control_map, aux_map : Map): Map^4 VAR

       palette : Array [0..3] of Palette;
       drawenable, priority, col1, col2, pattern_nr, pattern_code : Integer;

BEGIN

       palette := (default_palette * 4);
       drawenable := DRAW_ENABLE_VISUAL | DRAW_ENABLE_PRIORITY
       priority := 0;
       col1 := col2 := 0;
       pattern_nr := 0;
       pattern_code := 0;
       IF (!cumulative) THEN BEGIN
               visual_map := (0xf * 320 * 200);
               map control := map priority := map aux := (0 * 320 * 200);
       END
       FOREVER DO BEGIN
               opcode := get_input();
               COND opcode:
                       0xf0 => /* PIC_OP_SET_COLOR */
                               code := get_input();
                               (col1, col2) := palette[default_palette + (code / 40)][code % 40];
                               drawenable |= DRAW_ENABLE_VISUAL;
                       0xf1 => /* PIC_OP_DISABLE_VISUAL */
                               drawenable &= ~DRAW_ENABLE_VISUAL;
                       0xf2 => /* PIC_OP_SET_PRIORITY */
                               code := get_input();
                               priority := code & 0xf;
                               drawenable |= DRAW_ENABLE_PRIORITY;
                       0xf3 => /* PIC_OP_DISABLE_PRIORITY */
                               drawenable &= ~DRAW_ENABLE_PRIORITY;
                       0xf4 => /* PIC_OP_RELATIVE_PATTERNS */
                               IF (pattern_code & PATTERN_FLAG_USE_PATTERN) THEN
                                       pattern_nr := (get_input() >> 1) & 0x7f
                               FI
                               (x,y) := GetAbsCoordinates();
                               DrawPattern(x, y, col1, col2, priority, control, drawenable,
                                                pattern_code & PATTERN_FLAG_USE_PATTERN,
                                                pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
                               WHILE (peek_input() < 0xf0) DO BEGIN
                                       IF (pattern_code & PATTERN_FLAG_USE_PATTERN) THEN
                                               pattern_nr := (get_input() >> 1) & 0x7f
                                       FI
                                       (x,y) =  GetRelCoordinates(x,y);
                                       DrawPattern(x, y, col1, col2, priority, control, drawenable,
                                                        pattern_code & PATTERN_FLAG_USE_PATTERN,
                                                        pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
                               END
                       0xf5 => /* PIC_OP_RELATIVE_MEDIUM_LINES */
                               (oldx, oldy) := GetAbsCoordinates();
                               WHILE (peek_input() < 0xf0) DO BEGIN
                                       temp := get_input();
                                       IF (temp & 0x80) THEN
                                               y := oldy - (temp & 0x7f)
                                       ELSE
                                               y := oldy + temp
                                       FI
                                       x = oldx + get_input();
                                        DitherLine(oldx, oldy, x, y, col1, col2, priority, special, drawenable);
                                       (oldx, oldy) := (x, y);
                               END
                       0xf6 => /* PIC_OP_RELATIVE_LONG_LINES */
                               (oldx, oldy) :=  GetAbsCoordinates()
                               WHILE (peek_input() < 0xf0) DO BEGIN
                                       (x, y) := GetAbsCoordinates();
                                       DitherLine(oldx, oldy, x, y, col1, col2, priority, special, drawenable);
                                       (oldx, oldy) := (x, y);
                               END
                       0xf7 => /* PIC_OP_RELATIVE_SHORT_LINES */
                               (oldx, oldy) =  GetAbsCoordinates()
                               WHILE (peek_input() < 0xf0) DO BEGIN
                                       (x, y) := GetRelCoordinates(oldx, oldy);
                                       DitherLine(oldx, oldy, x, y, col1, col2, priority, special, drawenable);
                                       (oldx, oldy) := (x, y);
                               END
                       0xf8 => /* PIC_OP_FILL */
                               IF (fill_in_black) THEN
                                       (oldc1, oldc2) := (c1, c2);
                               FI
                               WHILE (peek_unput() < 0xf0) DO BEGIN
                                       (x, y) := GetAbsCoordinates();
                                       DitherFill(x, y, col1, col2, priority, special, drawenable);
                               END
                               IF (fill_in_black) THEN
                                       (c1, c2) := (oldc1, oldc2);
                               FI
                       0xf9 => /* PIC_OP_SET_PATTERN */
                               pattern_code := get_input() & 0x37;
                               pattern_size := pattern_code & 0x7;
                       0xfa => /* PIC_OP_ABSOLUTE_PATTERNS */
                               WHILE (peek_input() < 0xf0) DO
                                       IF (pattern_code & PATTERN_FLAG_USE_PATTERN)
                                               pattern_nr := (get_input() >> 1) & 0x7f
                                       FI
                                       (x, y) := GetAbsCoordinates();
                                       DrawPattern(x, y, col1, col2, priority, control, drawenable,
                                                        pattern_code & PATTERN_FLAG_USE_PATTERN,
                                                        pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
                                       END
                       0xfb => /* PIC_OP_SET_CONTROL */
                               control := get_input() & 0x0f;
                               drawenable |= DRAW_ENABLE_CONTROL;
                       0xfc => /* PIC_OP_DISABLE_CONTROL */
                               drawenable &= ~DRAW_ENABLE_CONTROL;
                       0xfd => /* PIC_OP_RELATIVE_MEDIUM_PATTERNS */
                               IF (pattern_code & PATTERN_FLAG_USE_PATTERN) THEN
                                       pattern_nr := (get_input() >> 1) & 0x7f;
                               FI
                               (oldx, oldy) := GetAbsCoordinates();
                               DrawPattern(x, y, col1, col2, priority, control, drawenable,
                                                pattern_code & PATTERN_FLAG_USE_PATTERN,
                                                pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
                               WHILE (peek_input() < 0xf0) DO BEGIN
                                       IF (pattern_code & PATTERN_FLAG_USE_PATTERN) THEN
                                               pattern_nr := (get_input() >> 1) & 0x7f;
                                       FI
                       
                                       temp := get_input();
                                       IF (temp & 0x80)
                                               y := oldy - (temp & 0x7f)
                                       ELSE
                                               y := oldy + temp
                                       FI
                                       x := oldx + get_input();
                                       DrawPattern(x, y, col1, col2, priority, control, drawenable,
                                                        pattern_code & PATTERN_FLAG_USE_PATTERN,
                                                        pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
                               END
                       0xfd => /* PIC_OP_OPX */
                               COND get_input():
                                       0x00 => /* PIC_OPX_SET_PALETTE_ENTRY */
                                               WHILE peek_input() < 0xf0 DO BEGIN
                                                       index := get_input();
                                                       color := get_input();
                                                       palette[index / 40][color % 40] := color;
                                               END
                                       0x01 => /* PIC_OPX_SET_PALETTE */
                                               palette_number := get_input();
                                               FOR i := 0 TO 39 DO
                                                       palette[palette_number][i] := get_input();
                                               OD
                                       0x02 => /* PIC_OPX_MONO0 */
                                               skip_input(41);
                                       0x03 => /* PIC_OPX_MONO1 */
                                               skip_input(1);
                                       0x04 => /* PIC_OPX_MONO2 */
                                       0x05 => /* PIC_OPX_MONO3 */
                                               skip_input(1);
                                       0x06 => /* PIC_OPX_MONO4 */
                                       0x07 => /* PIC_OPX_EMBEDDED_VIEW */ /* SCI01 operation */
                                       0x08 => /* PIC_OPX_SET_PRIORITY_TABLE */ /* SCI01 operation */
                       0xff => return (visual, control, priority, aux);
               END OF COND
     END

END </syntax>


This algorithm uses three auxiliary algorithms, DrawPattern, DitherLine, and DitherFill, which are sketched below. All of these functions are supposed to take the four maps as implicit parameters.

<syntax type="pascal"> PROCEDURE DrawPattern(x, y, col1, col2, priority, control, drawenable : Integer; solid : Boolean ; pattern_size, pattern_nr : Integer; rectangle : Boolean) </syntax>

Alters (x,y) so that 0 <= (x - pattern_size), 319 >= (x + pattern_size), 189 >= (y + pattern_size) and 0 <= (y - pattern_size), then draws a rectangle or a circle filled with col1, col2, priority, control, as determined by drawenable.

If rectangle is not set, it will draw a rectangle, otherwise a circle of size pattern_size. pattern_nr is used to specify the start index in the random bit table (256 random bits)


<syntax type="pascal"> PROCEDURE DitherLine(x, y, xend, yend, color1, color2, priority, control, drawenable : Integer)</syntax>

Draws a dithered line between (x, y+10) and (xend, yend+10). If the appropriate drawenable flags are set, it draws 'priority' to the priority map, 'control' to the control map, and 'color1' and 'color2' (alternating) to the visual map. The auxiliary map is bitwise-or'd with the drawenable flag while this is done.


<syntax type="pascal"> PROCEDURE DitherFill(x, y, col0, col1, priority, control, drawenable : Integer)</syntax>

Fills all layers for which drawenable is set with the appropriate content. Diagonal filling is not allowed. Boundaries are determined as follows: x<0, x>319, y<10, y>199 are hard boundaries. We now determine the 'boundary map' bound_map and the allowed color legal_color. If bound_map[coordinates] = legal_color, then the pixel may be filled.

<syntax type="pascal">IF (drawenable & DRAW_ENABLE_VISUAL)

       bound_map = visual;
       legal_color = 0xf;

ELSIF (drawenable & DRAW_ENABLE_PRIORITY)

       bound_map = priority;
       legal_color = 0;

ELSIF (drawenable & DRAW_ENABLE_CONTROL)

       bound_map = control;
       legal_color = 0;

ELSE

       return;

FI </syntax>

  1. Due to the vector graphics nature of these drawing operations, they are inherently more scaleable than pixmaps.