Difference between revisions of "Picture Resource (AGI)"
m (Collector moved page PICTURE Resource to PIC Resource) |
Revision as of 21:09, 25 December 2013
PICTURE resources are full-screen images, usually used for backgrounds. They are vector-based rather than bitmaps, which means they are drawn using drawing commands such as lines and brushes, rather than by plotting pixels.
Each PICTURE has two images (screens) - the visual screen and the priority screen. The visual screen is what the player sees when playing the game. Views are placed on top of the PICTUREs during the game and can be moved around the screen. The priority screen is what makes the game appear to be 3-dimensional. On the AGI screen, there are 12 priority bands in which objects can be placed. The priority given to different parts of the PICTURE (i.e. the color of the pixels on the priority screen) determines where in 3-dimensional space objects appear. For example, a tree placed right at the front of the screen would be given a priority of 15 (the highest priority). A wall which is far away in the background would be given a priority of 4 (the lowest priority). If an object with a priority of 12 is displayed on the screen, it would be behind the tree but in front of the wall. The priority of a view is usually determined by its y-position.
Colors 0, 1, 2 and 3 on the priority screen are used for “control lines” which determine where the ego (the object controlled by the player) can walk.
Format
Note: this section includes technical details about the PICTURE resource format. Game authors generally do not need to know this information.
Introduction
PICTURE is usually used for background PICTUREs and other full screen images. PICTUREs in AGI and early SCI games are not stored as a complete PICTURE. Instead they're constructed and stored as coordinates and vectors. Vectors give the instructions for drawing a PICTURE and they have the advantage of taking less space than would a bit image of a complete PICTURE.
PICTUREs are drawn using nine different drawing actions. These actions are given values from 0xF0 to 0xFA and are defined as follows:
- 0xF0: Change PICTURE color and enable PICTURE draw.
- 0xF1: Disable PICTURE draw.
- 0xF2: Change priority color and enable priority draw.
- 0xF3: Disable priority draw.
- 0xF4: Draw a Y corner.
- 0xF5: Draw an X corner.
- 0xF6: Absolute line (long lines).
- 0xF7: Relative line (short lines).
- 0xF8: Fill.
- 0xF9: Change pen size and style.
- 0xFA: Plot with pen.
- 0xFB--0xFE: Unused in most AGI games.
Note: SQ2 appears to be the only AGI version 2 game that uses 0xF9 and 0xFA. The AGI interpreters before this game will most likely not support these two drawing actions.
In the following detailed descriptions of each action, the word "PICTURE" refers to the screen that is seen by the player when they play the AGI screen. The word "priority" refers to the screen that is held in memory and contains control information invisible to the player. As a PICTURE is drawn, both screens are updated depending on whether drawing is enabled from each screen.
The PICTURE data can be processed byte by byte. Whenever a value of 0xF0 or greater is encountered, the action is changed to the one given and then all the bytes between this code and the next action code are arguments to this action. Half of the actions have a set number of arguments, the other half can have an unlimited number of arguments. The special code 0xFF says that the end of the PICTURE data has been reached. All other values are used by the various drawing actions to tell them what to draw and will always be less than 0xF0.
Color palette
The following colors are used in AGI. RGB is given in 6 bit values.
Code Color R G B ----- ---------------- ---- ---- ---- 0 black 0x00 0x00 0x00 1 blue 0x00 0x00 0x2A 2 green 0x00 0x2A 0x00 3 cyan 0x00 0x2A 0x2A 4 red 0x2A 0x00 0x00 5 magenta 0x2A 0x00 0x2A 6 brown 0x2A 0x15 0x00 7 light gray 0x2A 0x2A 0x2A 8 dark gray 0x15 0x15 0x15 9 light blue 0x15 0x15 0x3F 10 light green 0x15 0x3F 0x15 11 light cyan 0x15 0x3F 0x3F 12 light red 0x3F 0x15 0x15 13 light magenta 0x3F 0x15 0x3F 14 yellow 0x3F 0x3F 0x15 15 white 0x3F 0x3F 0x3F |
General actions
0xF0: Change PICTURE color and enable PICTURE draw
Function: Changes the current drawing color for the PICTURE screen to that given by the one and only argument, and enables subsequent actions to draw to the PICTURE screen.
Initially all pixels of the background are white and have priority 4. After this command is executed, all the subsequent graphic commands draw using the color set by the command.
Example: F0 0D changes PICTURE screen drawing color to light magenta and enables drawing to the PICTURE screen.
0xF1: Disable PICTURE draw
Function: Disables drawing to the PICTURE screen. This is done whenever there is something which only needs to be drawn on the priority screen such as the control lines. There are no arguments for this action.
0xF2: Change priority color and enable priority draw
Function: Changes the current drawing color for the priority screen to that given by the one and only argument, and enables subsequent actions to draw to the priority screen.
Example: F0 04 changes priority screen drawing color to red and enables drawing to the priority screen.
0xF3: Disable priority draw
Function: Disables drawing to the priority screen. This is done whenever there is something which only needs to be drawn on the PICTURE screen such as the finer details of the PICTURE. There are no arguments for this action.
The corner action
I call the following two actions the ``corner actions because they do not draw diagonal lines at all but instead alternate from horizontal line to vertical line (or vice versa) giving rise to a series of right angled corners.
_________ | | B__ | |_____ | | |_| A
The above diagram shows the type of pattern created. If A were the starting coordinate, then it would be called a Y corner. This is because the Y or vertical component is changed first. If B were the starting coordinate, then it would be called an X corner. This is because the X or horizontal component is changed first.
0xF4: Draw a Y corner
Function: The first two arguments for this action are the coordinates of the starting position on the screen in the order x and then y. The remaining arguments are in the order y1, x1, y2, x2, ...
Note that the y component is the first to be changed and also note that this action does not necessarily end on either component, it just ends when the next byte of 0xF0 or above is encountered. A line is drawn after each byte is processed.
Example: F4 16 16 18 12 16 F?
(0x12, 0x16) (0x16, 0x16) E S S = Start X X E = End XXXXX X = normal piXel (0x12, 0x18) (0x16, 0x18)
0xF5: Draw an X corner
Function: The first two arguments for this action are the coordinates of the starting position on the screen in the order x and then y. The remaining arguments are in the order x1, y1, x2, y2, ...
Note that the x component is the first to be changed and also note that this action does not necessarily end on either component, it just ends when the next byte of 0xF0 or above is encountered. A line is drawn after each byte is processed.
Example: F5 16 16 18 12 16 F?
(0x16, 0x12) (0x18, 0x12) EXX X S = Start X E = End X X = normal piXel SXX (0x16, 0x16) (0x18, 0x16)
0xF6: Absolute line
Function: Draws lines between points. The first two arguments are the starting coordinates. The remaining arguments are in groups of two which give the coordinates of the next location to draw a line to. There can be any number of arguments but there should always be an even number.
Example: F6 30 50 34 51 38 53 F?
This sequence draws a line from (48, 80) to (52, 81), and a line from (52, 81) to (56, 83).
0xF7: Relative line
Function: Draw short relative lines. By relative we mean that the data gives displacements which are relative from the current location. The first argument gives the standard starting coordinates. All the arguments which follow these first two are of the following format:
+---+-----------+---+-----------+ | S | Xdisp | S | Ydisp | +---+---+---+---+---+---+---+---+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +---+---+---+---+---+---+---+---+
This gives a displacement range of between -7 and 7 for the Y direction, and between -6 and 7 for the X direction. (-7 in the X direction is not possible, since this would result in a byte value >= 0xF0, which AGI would interpret as a new drawing command.)
Example: F7 10 10 22 40 06 CC F?
S + S = Start X+++X X = End of each line + + = pixels in each line E + E = End + + + + Remember that CC = (x-4, y-4). ++ X
0xF8: Fill
Function: Flood fill from the locations given. Arguments are given in groups of two bytes which give the coordinates of the location to start the fill at. If PICTURE drawing is enabled then it flood fills from that location on the PICTURE screen to all pixels locations that it can reach which are white in color. The boundary is given by any pixels which are not white.
If priority drawing is enabled, and PICTURE drawing is not enabled, then it flood fills from that location on the priority screen to all pixels that it can reach which are red in color. The boundary in this case is given by any pixels which are not red.
If both PICTURE drawing and priority drawing are enabled, then a flood fill naturally enough takes place on both screens. In this case there is a difference in the way the fill takes place in the priority screen. The difference is that it not only looks for its own boundary, but also stops if it reaches a boundary that exists in the PICTURE screen but does not necessarily exist in the priority screen.
Brush style
Drawing actions 0xF9 and 0xFA deal with plotting patterns. Most drawing programs have options to change the size, and style of the pen or brush. The style covers different shapes and textures. AGI PICTURES provide these tools as well.
0xF9: Change pen size and style
Function: Change the characteristics of the pattern plotted by drawing action 0xFA. If bit 5 is not set, then the pattern is a solid shape. If bit 5 is set, then the pattern is like a splatter. Bit 4 selects whether the brush is a circle or a rectangle. Bits 0-2 give the size of the shape which will be a value from 0 to 7. These characteristics appear to only affect drawing action 0xFA.
The default brush is a solid circle or rectangle of size 0, which should be used until an 0xF9 action is encountered.
___ ___ ___ ___ ___ ___ ___ ___ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |___|___|___|___|___|___|___|___| | | |___|___| 0 = Solid _________| | | 1 = Splatter | |______ Pen size | 0 = Circle ____________| 1 = Rectangle
RECTANGLE SIZES X XX XXX XXXX XXXXX XXXXXX XXXXXXX XXXXXXXX size+1 0 X* XXX XXXX XXXXX XXXXXX XXXXXXX XXXXXXXX _____________ XX X*X XXXX XXXXX XXXXXX XXXXXXX XXXXXXXX | | 1 XXX XX*X XXXXX XXXXXX XXXXXXX XXXXXXXX | | XXX XXXX XX*XX XXXXXX XXXXXXX XXXXXXXX | | 2 XXXX XXXXX XXX*XX XXXXXXX XXXXXXXX | | (size*2)+1 XXXX XXXXX XXXXXX XXX*XXX XXXXXXXX | | 3 XXXXX XXXXXX XXXXXXX XXXX*XXX | | XXXXX XXXXXX XXXXXXX XXXXXXXX | | 4 XXXXXX XXXXXXX XXXXXXXX | | XXXXXX XXXXXXX XXXXXXXX | | WHERE 5 XXXXXXX XXXXXXXX |_____________| XXXXXXX XXXXXXXX X = agi pixels 6 XXXXXXXX IN GENERAL * = coordinates given XXXXXXXX for plot 7
CIRCLE SIZES X XX X XX X XX XXX XX size+1 0 X* XXX XX XXX XXXX XXXXX XXXX _____________ XX X*X XXXX XXXXX XXXX XXXXX XXXXXX | | 1 XXX XX*X XXXXX XXXX XXXXX XXXXXX | | X XXXX XX*XX XXXXXX XXXXXXX XXXXXX | | 2 XX XXXXX XXX*XX XXXXXXX XXXXXXXX | | (size*2)+1 XX XXXXX XXXXXX XXX*XXX XXXXXXXX | | 3 XXX XXXX XXXXXXX XXXX*XXX | | X XXXX XXXXXXX XXXXXXXX | | 4 XXXX XXXXX XXXXXXXX | | XX XXXXX XXXXXX | | WHERE 5 XXXXX XXXXXX |_____________| XXX XXXXXX X = agi pixels 6 XXXX IN GENERAL * = coordinates given XX for plot 7
To implement this you will need to store bitmaps for each of these of these circles.
0xFA: Plot with pen
Function: Plots points with the pen defined with drawing action 0xF9. If the pen style is set to solid, then the arguments are just a list of coordinates to be plotted. If the pen style is set to splatter brush (texture), then the arguments are in groups of three with the first argument giving the texture number and the other two giving the coordinates. The texture number determines in what way the pixels will splatter within the defined shape. Bits 1-7 seem to give the actual texture number. Bit 0 does not do anything. This means that there are 120 different pixel splatter bitmaps (values 0xF0 and above can not be used as they are treated as drawing actions). There is actually only 32 bytes of texture data which means that most of the splatter bitmaps overlap.
Texture data
All of the data needed for the 128 texture patterns is included in the following 32 bytes (256 bits):
0x20, 0x94, 0x02, 0x24, 0x90, 0x82, 0xa4, 0xa2, 0x82, 0x09, 0x0a, 0x22, 0x12, 0x10, 0x42, 0x14, 0x91, 0x4a, 0x91, 0x11, 0x08, 0x12, 0x25, 0x10, 0x22, 0xa8, 0x14, 0x24, 0x00, 0x50, 0x24, 0x04
The only difference between each texture pattern is its starting position within this table. The following table gives the starting bit position in the above table for each texture pattern number given as the first argument of each pen plot:
0x00, 0x18, 0x30, 0xc4, 0xdc, 0x65, 0xeb, 0x48, 0x60, 0xbd, 0x89, 0x04, 0x0a, 0xf4, 0x7d, 0x6d, 0x85, 0xb0, 0x8e, 0x95, 0x1f, 0x22, 0x0d, 0xdf, 0x2a, 0x78, 0xd5, 0x73, 0x1c, 0xb4, 0x40, 0xa1, 0xb9, 0x3c, 0xca, 0x58, 0x92, 0x34, 0xcc, 0xce, 0xd7, 0x42, 0x90, 0x0f, 0x8b, 0x7f, 0x32, 0xed, 0x5c, 0x9d, 0xc8, 0x99, 0xad, 0x4e, 0x56, 0xa6, 0xf7, 0x68, 0xb7, 0x25, 0x82, 0x37, 0x3a, 0x51, 0x69, 0x26, 0x38, 0x52, 0x9e, 0x9a, 0x4f, 0xa7, 0x43, 0x10, 0x80, 0xee, 0x3d, 0x59, 0x35, 0xcf, 0x79, 0x74, 0xb5, 0xa2, 0xb1, 0x96, 0x23, 0xe0, 0xbe, 0x05, 0xf5, 0x6e, 0x19, 0xc5, 0x66, 0x49, 0xf0, 0xd1, 0x54, 0xa9, 0x70, 0x4b, 0xa4, 0xe2, 0xe6, 0xe5, 0xab, 0xe4, 0xd2, 0xaa, 0x4c, 0xe3, 0x06, 0x6f, 0xc6, 0x4a, 0x75, 0xa3, 0x97, 0xe1
Important note: When drawing the brush, if the bit position in the texture data (first table above) reaches 255, it should loop round to 0, instead of looping at 256 as you would normally expect. This may be because of a bug in the PICTURE drawing code in the interpreter. If you loop at 256 then some of the patterns will not be correct.
When a texture pattern is drawn in the shape of a circle, the texture pattern 'fills' the shape of the circle. This diagram will explain what I mean:
X.XX X. X.X. XX .... X.X. .X.X .... X... .X.X ..X. X. XXXX .. Rectangle Circle
The corner pixels of the circle which aren't part of the circle are totally ignored. The circle isn't just a cut out of the equivalent rectangle. A bit hard to explain. Look at the source of Showpic for more info.
Implementation
Writing code to interpret the PICTURE data in order to draw the PICTURE on the screen is easier said than done. It turns out that you have to have a line drawing algorithm which exactly matches the one that Sierra uses. A pixel out of place can mean that a fill overflows or doesn't work at all.
You will also have to write your own fill routine because not many of the standard fill routines can stop at a multicolored boundary. You are also dealing with two screens both of which will probably be stored in memory somewhere rather than the screen.
The PICTURE screen has a starting state of being completely white. The priority screen has starting state of being completely red. It is important that you set all pixels in each screen to the relevant background color else you won't get the right result.
General guidelines
The screen mode used by the AGI games is the 320x200x16 standard EGA mode. However, all graphics is designed to be shown on a 160x200x16 mode. This was apparently the resolution that the original PCjr interpreter used. They stuck with it when they started supporting EGA and thus have a situation where each AGI pixel has a width of two normal 320x200 pixels.
Line drawing
This routine is relatively straight forward and I suggest that you look at it and try to understand it or you'll be having headaches trying to get you're routines acting like the Sierra ones. Basically it draws a line from (x1, y1) to (x2, y2) using a function called pset to draw a single pixel. The function round() is what makes it act like the Sierra. Essentially when it comes down to a 50:50 decision about where to put a pixel, the direction in which the line is being drawn is taken into account. I've only noticed one pixel out of place in all the screens I've tried Showpic on which makes me believe its probably not a fault in this algorithm, but somewhere else in the code.
Note that Sarien uses a different line drawing algorithm written by Joshua Neal and Stuart George.
<syntax type="C++"> int round(float aNumber, float dirn) {
if (dirn < 0) return ((aNumber - floor(aNumber) <= 0.501) ? floor(aNumber) : ceil(aNumber)); return ((aNumber - floor(aNumber) < 0.499) ? floor(aNumber) : ceil(aNumber));
}
void drawline(word x1, word y1, word x2, word y2) {
int height, width; float x, y, addX, addY;
height = (y2 - y1); width = (x2 - x1); addX = (height==0? height:(float)width/abs(height)); addY = (width==0? width:(float)height/abs(width));
if (abs(width) > abs(height)) { y = y1; addX = (width == 0? 0 : (width/abs(width))); for (x=x1; x!=x2; x+=addX) { pset(round(x, addX), round(y, addY)); y+=addY; } pset(x2,y2); } else { x = x1; addY = (height == 0? 0 : (height/abs(height))); for (y=y1; y!=y2; y+=addY) { pset(round(x, addX), round(y, addY)); x+=addX; } pset(x2,y2); }
} </syntax>
Flood filling
I have discovered that using a queue in a flood fill routine works quite well. It is also the easiest method to understand as far as I'm concerned. I just thought about what needed to be done and this method took shape.
Basically you start at a particular location. If its the desired background color (white or red depending on the screen), then set that pixel. You then check the pixels immediately up, left, down, and right to see if they are of the desired background color. If they are, store them in the queue. You then retrieve the first pixel position from the queue and repeat the above steps.
Using higher resolution modes
I've often wondered if it would be possible to show PICTUREs in a higher resolution, for example, 640x400. Since the data is stored as vectors, it should be possible to multiply all the x components by four and all the y components by two and then draw the lines. This would give less blocky PICTUREs. There would be a number of problems to overcome. Firstly, the fill action (or tool) may cause problems because pixels could be in the wrong places. There will also be a need to draw end pixels of a line with a width of four so that there are no holes for the flood fill to flow out of.
Sierra's PICTURE editor
The PICTURE editor that Sierra used back in the vector PICTURE days was much like a CAD program. I've seen a few photos of it in The Official Book of King's Quest. It has a status bar at the top which gives the current tool being used (Line, Fill, etc), the current X and Y locations, and four others which I explain below.
Status bar examples:
Tool:Line V:8 P:A C:o X=249 Y=89 Pri:5 Tool:Fill V:B P:0 C:B X=96 Y=99 Pri:6 Tool:Line V:A P:o C:o X=199 Y=55 Pri:2 o = off (or disabled)
Pri looks like it could be giving the current priority band that the cursor location is in. The above status lines are for the SCI PICTURE Editor. I ran these values past SQ3 and the values given for Pri are indeed the values of the priority band at the locations given.
I think that V, P, and C refer to the colors being used on the three different screens (the SCI games have a separate screen for the control lines rather than having both the priority bands and control lines on the same screen. This is why there were three screens and not the two that we are used to in AGI games). This would mean that V = Visual, P = Priority and C = Control.
In an AGI PICTURE Editor, there would only be the Visual screen and the Priority screen. The PICTURE editor would obviously be able to switch between the two screens. I've also noticed that the early vector based SCI PICTURE editor supports a feature which removes solid colors (Fills) with a single keystroke and I presume another keystroke puts them back. When the fills have been removed, they are represented as a tiny cross. Apparently removing the solid colors makes it easier to add small details like flowers.
Sources
- AGI Studio help file
- AGI Specs version 3.0