bobp@amiga.UUCP (Robert S. Pariseau) (11/14/85)
TITLE: More Fun with 6 Planes (LONG SOURCE) The source below is a bit of eye-candy that shows off the use of Hold and Modify graphics display mode on the Amiga. Hold and Modify (or HAM for short) is a compromise between the limited direct color access of an inexpensive color-palette, frame buffer graphics engine like the Amiga, and the high cost of a direct reading frame buffer graphic engine like some of the more expensive CAD systems. On the Amiga, the bits associated with a given pixel select one of the 32 color registers. Each color register contains a color value consisting of 4 bits of Red component, 4 bits of Green, and 4 bits of Blue -- yielding 4096 different colors from black to white. The Amiga hardware has sufficient bandwidth to retrieve 6 bit-planes worth of data for a given pixel (4 in high resolution display). The combination of these two constraints says that in a static display (no dynamic color changing going on in the background), you can display up to 32 colors simultaneously in low res and up to 16 colors simultaneously in highs res choosen out of the palette of 4096 possible colors. [Note: as discussed earlier, you can actually get up to 64 distinct colors in low res using 6 planes and EXTRA_HALFBRITE display mode. Further, note that dynamic color changing goes on all the time in normal Amiga displays -- that's how each of the separately slideable screens gets its own color palette for instance.] If the Amiga were only somehow capable of pulling in 12 planes of data per pixel, then we could avoid using the color palette altogether. We could encode the desired pixel color (4 bits each R, G, and B) directly into the frame buffer. This would allow us to independently choose any of the 4096 colors for each pixel -- with no limit on how many different colors appeared in a given display. [Note: there's a downside to this as well. Since the display doesn't feed through a color palette, there's no easy way of swapping or changing colors in your picture -- you have to actually go out and change all those pixels. With a color palette, you just change the color values in the registers. This trick is fundamental to the technique of "color cycle animation".] HAM is a color modulation technique which approaches the flexibility of a direct reading frame buffer. Using HAM, you can specify any desired color out of the 4096 available within 3 horizontally adjacent pixels. Here is how it works. Consider a 4 plane image. The 4 bits of each pixel select one of the first 16 color registers. Now consider the extra 2 bits per pixel of a 6 plane HAM image. Those extra 2 bits are used as control bits, selecting between the following 4 choices: 0) Use the lower 4 bits as a color register selector, just as normal. 1) Use the lower 4 bits as the Blue component of the color for this pixel. Copy the Red and Green components of the color for this pixel from the color of the Playfield pixel immediately to the left of this one. 2) Use the lower 4 bits as the Red component of the color for this pixel. Copy the Blue and Green components of the color for this pixel from the color of the Playfield pixel immediately to the left of this one. 3) Use the lower 4 bits as the Green component of the color for this pixel. Copy the Red and Blue components of the color for this pixel from the color of the Playfield pixel immediately to the left of this one. Note that only the color produced for the pixel is varied. The contents of the color registers themselves are NOT changed by HAM. Also note that the effects are cumulative. Thus, in 3 adjacent pixels, you can Modify-Red, Modify-Green, and Modify-Blue, thereby producing any color you want. Finally, note that a Modify-x pixel takes its "Hold" colors from the color that is OR WOULD HAVE BEEN produced for the PLAYFIELD (i.e., frame buffer) pixel immediately to its left. This means that sprite images moving over the display do NOT mess up your carefully choosen HAM colors. There is a hardware mode bit that controls HAM. A HAM display requires 5 or 6 bit planes. If you provide a 5 plane image, the hardware assumes a 6th bit value of 0 for each pixel -- this means you can only choose normal and Modify-Blue out of the choices above. The graphics kernel software automatically supports HAM as a ViewPort attribute. That means that each of your separately slideable ViewPorts (Screens in Intuition lingo) can have, or not have, HAM turned on without affecting the others. BEWARE! Some of the older developer machines and store demo machines do not produce reliable HAM images. ALL of the consumer machines do HAM just fine. ----------------------------Program Notes: The Colorful program will compile and link cleanly using the native Amiga Lattice C tools on the standard V1.0 C disk. The Make script in the examples directory will do all the work for you. As usual, I recommend that you copy the source into ram: disk and then direct Make at it by typing: 1> execute Make ram:Colorful The program displays 256 boxes each having a different color out of the possible 4096. Colorful listens for left mouse button events and switches between two display modes. The first is an ordered display with labelled color components. The second is a random display where boxes randomly selected have their color randomly changed. Note that at no time do any 2 boxes share the same color. If you are sharp-eyed, you will notice a narrow stripe of red and a narrow stripe of green at the leading (leftmost) edge of each of the boxes. These are the color modifiers in action. Each box consists of a vertical line of Modify-Red, followed by a vertical line of Modify-Green, followed by a rectangle of Modify-Blue as follows: RGBBBBBBBBB RGBBBBBBBBB RGBBBBBBBBB RGBBBBBBBBB RGBBBBBBBBB RGBBBBBBBBB The routine hamBox() produces such a box. For further information, see the Amiga Hardware Manual, the Amiga ROM Kernel Manual, and the manual Intuition: The Amiga User Interface. ----------------------------Program Source Follows: /*********************************************************************** * Colorful -- A demo of the Amiga's Hold and Modify mode, showing, at * all times, a different subset of 256 of the 4096 * colors available on the Amiga. At any moment, no two * squares have the same exact color in them. * * Rob Peck -- November 5, 1985 * Bob Pariseau -- November 10, 1985 (Rework for tutorial) * **********************************************************************/ #include <exec/types.h> #include <exec/tasks.h> #include <exec/libraries.h> #include <exec/devices.h> #include <devices/keymap.h> #include <graphics/copper.h> #include <graphics/display.h> #include <graphics/gfxbase.h> #include <graphics/text.h> #include <graphics/view.h> #include <graphics/gels.h> #include <graphics/regions.h> #include <hardware/blit.h> #include <intuition/intuition.h> #include <intuition/intuitionbase.h> #define XSIZE 11 /* Color box sizes */ #define YSIZE 6 struct GfxBase *GfxBase; /* Export the library pointers */ struct IntuitionBase *IntuitionBase; struct RastPort *rp; /* Graphics structures */ struct ViewPort *vp; struct TextAttr TestFont = { "topaz.font", /* Standard system font */ 8, 0, 0 }; struct Window *w; /* Intuition structures */ struct Screen *screen; struct IntuiMessage *message; struct NewScreen ns = { 0, 0, /* start position */ 320, 200, 6, /* width, height, depth */ 0, 1, /* detail pen, block pen */ HAM, /* Hold and Modify ViewMode */ CUSTOMSCREEN, /* screen type */ &TestFont, /* font to use */ " 256 different out of 4096", /* default title for screen */ NULL /* pointer to additional gadgets */ }; struct NewWindow nw = { 0, 11, /* start position */ 320, 186, /* width, height */ -1, -1, /* detail pen, block pen */ MOUSEBUTTONS|CLOSEWINDOW, /* IDCMP flags */ ACTIVATE|WINDOWCLOSE, /* window flags */ NULL, /* pointer to first user gadget */ NULL, /* pointer to user checkmark */ "colors at any given moment", /* window title */ NULL, /* pointer to screen (set below) */ NULL, /* pointer to superbitmap */ 0, 0, 320, 186, /* ignored since not sizeable */ CUSTOMSCREEN /* type of screen desired */ }; LONG squarecolor[16 * 16], freecolors[4096-(16*16)]; SHORT squares[16 * 16]; SHORT xpos[16], ypos[16]; char *number[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" }; SHORT sStop, cStop, sequence; BOOL textneeded; main() { ULONG class; USHORT code, i; BOOL wheelmode; for(i=0; i<16; i++) /* Establish color square positions */ { xpos[i] = (XSIZE + 4) * i + 20; ypos[i] = (YSIZE + 3) * i + 21; } GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0); if (GfxBase == NULL) exit(); IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0); if (IntuitionBase == NULL) { CloseLibrary(GfxBase); exit(); } screen = (struct Screen *)OpenScreen(&ns); if (screen == NULL) { CloseLibrary(IntuitionBase); CloseLibrary(GfxBase); exit(); } nw.Screen = screen; /* Open window in our new screen */ w = (struct Window *)OpenWindow(&nw); if (w == NULL) { CloseScreen(screen); CloseLibrary(IntuitionBase); CloseLibrary(GfxBase); exit(); } vp = &screen->ViewPort; /* Set colors in screen's VP */ rp = w->RPort; /* Render into the window's RP */ /* Set the color registers: Black, Red, Green, Blue, White */ SetRGB4(vp, 0, 00, 00, 00); SetRGB4(vp, 1, 15, 00, 00); SetRGB4(vp, 2, 00, 15, 00); SetRGB4(vp, 3, 00, 00, 15); SetRGB4(vp, 4, 15, 15, 15); SetBPen(rp, 0); /* Insure clean text */ textneeded = TRUE; wheelmode = TRUE; /* Start with Color Wheel display */ FOREVER { /* Process any and all messages in the queue, then update the */ /* display colors once, then come back here to look at the queue */ /* again. If you see a left-mouse-button-down event, then switch */ /* display modes. If you see a Close-Window-gadget event, then */ /* clean up and exit the program. NOTE: This is a BUSY LOOP so */ /* that the colors will cycle as quickly as possible. */ while((message = (struct IntuiMessage *)GetMsg(w->UserPort)) != NULL) { class = message->Class; code = message->Code; ReplyMsg(message); /* Can't reply until done using it! */ if(class == CLOSEWINDOW) /* Exit the program */ { CloseWindow(w); CloseScreen(screen); CloseLibrary(IntuitionBase); CloseLibrary(GfxBase); exit(); }; if(class == MOUSEBUTTONS && code == SELECTDOWN) /* swap modes */ { wheelmode = NOT wheelmode; SetAPen(rp, 0); /* Clear the drawing area */ SetDrMd(rp, JAM1); RectFill(rp, 3, 12, 318, 183); textneeded = TRUE; } } if(wheelmode) colorWheel(); else colorFull(); } } colorFull() /* Display a ramdomized set of colors */ { SHORT sChoice, cChoice, usesquare; LONG usecolor; if(textneeded) /* First call since mode change? */ { prompt(); sStop = 255; /* Top of list of squares yet to change */ cStop = 4095 - 256; /* Top of list of colors still needing use */ for(usecolor=0; usecolor<256; usecolor++) /* Initialize colors */ { usesquare = usecolor; squares[usesquare] = usesquare; squarecolor[usesquare] = usecolor; hamBox(usecolor, xpos[usesquare % 16], ypos[usesquare / 16]); } for(usecolor=256; usecolor<4095; usecolor++) /* Ones not yet used */ freecolors[usecolor - 256] = usecolor; } /************************************************************************* * Randomly choose next square to change such that all squares change * color at least once before any square changes twice. squares[0] * through squares[sStop] are the square numbers that have not yet * changed in this pass. RangeRand(r) is an integer function provided * in "amiga.lib" which produces a random result in the range 0 to * (r-1) given an integer r in the range 1 to 65535. ************************************************************************/ sChoice = RangeRand(sStop + 1); /* Pick a remaining square */ usesquare = squares[sChoice]; /* Extract square number */ squares[sChoice] = squares[sStop]; /* Swap it with sStop slot */ squares[sStop] = usesquare; if(NOT sStop--) sStop = 255; /* Only one change per pass */ /************************************************************************ * Randomly choose new color for selected square such that all colors * are used once before any color is used again, and such that no two * squares simultaneously have the same color. freecolors[0] through * freecolors[cStop] are the colors that have not yet been choosen in * this pass. Note that the 256 colors in use at the end of the * previous pass are not available for choice in this pass. ***********************************************************************/ cChoice = RangeRand(cStop + 1); usecolor = freecolors[cChoice]; freecolors[cChoice] = freecolors[cStop]; freecolors[cStop] = squarecolor[usesquare]; squarecolor[usesquare] = usecolor; if(NOT cStop--) cStop = 4095 - 256; hamBox(usecolor, xpos[usesquare % 16], ypos[usesquare / 16]); } colorWheel() /* Display an ordered set of colors */ { SHORT i, j; if(textneeded) { prompt(); SetAPen(rp, 2); /* Green pen for green color numbers */ Move(rp, 260, ypos[15]+17); Text(rp, "Green", 5); for(i=0; i<16; i++) { Move(rp, xpos[i]+3, ypos[15]+17); Text(rp, number[i], 1); } SetAPen(rp, 3); /* Blue pen for blue color numbers */ Move(rp, 4, 18); Text(rp, "Blue", 4); for(i=0; i<16; i++) { Move(rp, 7, ypos[i]+6); Text(rp, number[i], 1); } SetAPen(rp, 1); /* Red pen for red color numbers */ Move(rp, 271, 100); Text(rp, "Red", 3); sequence = 0; } SetAPen(rp, 1); /* Identify the red color in use */ SetDrMd(rp, JAM2); Move(rp, 280, 115); Text(rp, number[sequence], 1); for(j=0; j<16; j++) /* Update all of the squares */ for(i=0; i<16; i++) hamBox((sequence<<8 | i<<4 | j), xpos[i], ypos[j]); if(++sequence == 16) sequence=0; } prompt() /* Display mode changing prompt */ { SetDrMd(rp, JAM2); SetAPen(rp, 4); Move(rp, 23, 183); Text(rp, "[left mouse button = new mode]", 30); textneeded = FALSE; } /********************************************************************** * hamBox() -- routine to draw a colored box in Hold and Modify mode. * Draws a box of size XSIZE by YSIZE with an upper left * corner at (x,y). The desired color is achieved in 3 * steps on each horizontal line of the box. First we * set the red component, then the green, then the blue. * We achieve this by drawing a vertical line of Modify-Red, * followed by a vertical line of Modify-Green, followed by * a rectangle of Modify-Blue. Note that the resulting * color for the first two vertical lines depends upon the * color(s) of the pixels immediately to the left of that * line. By the time we reach the rectangle we are assured * of getting (and maintaining) the desired color because * we have set all 3 components (R, G, and B) straight from * the bit map. ***********************************************************************/ hamBox(color, x, y) LONG color, x, y; { SHORT c; SetDrMd(rp, JAM1); /* Establish Drawing Mode in RastPort */ c=((color & 0xf00)>>8); /* Extract desired Red color component. */ SetAPen(rp, c + 0x20); /* Hold G, B from previous pixel. Set R=n. */ Move(rp, x, y); Draw(rp, x, y+YSIZE); x++; c=((color & 0xf0)>>4); /* Extract desired Green color component. */ SetAPen(rp, c + 0x30); /* Hold R, B from previous pixel. Set G=n. */ Move(rp, x, y); Draw(rp, x, y+YSIZE); x++; c=(color & 0xf); /* Extract desired Blue color component. */ SetAPen(rp, c + 0x10); /* Hold R, G from previous pixel. Set B=n. */ RectFill(rp, x, y, x+XSIZE-2, y+YSIZE); } -----------------------------That's all for now!