Copyright (c) Hyperion Entertainment and contributors.
Intuition Images
Contents
Creating Images
With an Image structure an application can create graphic objects quickly and easily and display them almost anywhere. Images have an additional attribute that makes them even more economical–by changing two simple data elements in the Image structure, the color of the image may be changed.
Images are rectangular bitmaps which individually define the color of each pixel represented. Images may not be masked to allow part of the background to show through. The entire rectangular image is drawn into the target element, overwriting any data it may overlap. All bitplanes defined in the target RastPort within the image’s rectangle are overwritten either with image data, ones or zeros.
Images may be directly drawn by the application by using the DrawImage() function, described below. The image may be rendered into any screen or window RastPort with this function. (DrawImageState() can also be used to draw the image, but this is an advanced topic discussed later in the “BOOPSI” chapter.
The visual imagery for an Image can be removed from the display by calling EraseImage(). For a normal Image structure, this will call the graphics function EraseRect(), which clears the Image rectangle by using the layer’s backfill pen to overwrite it.
Alternately, images can be used indirectly by attaching them to menus, gadgets or requesters when they are initialized. For instance, in menus the MenuItem structure has the ItemFill and SelectFill fields. If the ITEMTEXT flag is cleared and the HIGHIMAGE flag is set, the application may place a pointer to a list of Image structures in each of these fields. The system will display the ItemFill images when the menu item is not selected and the SelectFill images when the menu item is selected. The application does not have to take any specific action to display these images. Once the menus have been added to a window, their management and display is under Intuition control.
The number of bitplanes in an image does not have to match the number of bitplanes in the display element in which the image is rendered. This provides great flexibility in using Image structures, as the same image may be reused in many places.
If the application’s window is on the Workbench or some other public screen, it must use caution with hard-coded or constant image data, as the color palette of that screen is subject to change. If the application has its own custom screen, and it is appropriate for the colors of that screen to change, the same situation arises. Intuition allows the screen opener to provide a mapping of pen number and rendering functions. For example, pens are specified for the bright and dark edges of three dimensional objects. Applications can obtain this mapping from the DrawInfo structure. See Intuition Screens for more information on DrawInfo.
A suitably designed image may be drawn into a screen or window of any depth. To accomplish this, the application must ensure that detail is not lost when the image is displayed in a single bitplane RastPort, where only the first bitplane of image data will be displayed. This is important if the image will ever be displayed on the Workbench screen or any other public screen.
Image Structure
For images, the application must create one or more instances of the Image structure.
struct Image { WORD LeftEdge; WORD TopEdge; WORD Width; WORD Height; WORD Depth; UWORD *ImageData; UBYTE PlanePick, PlaneOnOff; struct Image *NextImage; };
The meanings of the fields in the Image structure are:
- LeftEdge, TopEdge
- The location of the image relative to its base position when it is drawn. These offsets are added to the base position to determine the final location of the image data.
- The base position for images rendered with DrawImage() is taken from arguments passed in the function call. For gadgets and menus, the base position is always the upper, left corner of the select box. For requesters the base position is always the upper, left corner of the requester.
- Negative values of LeftEdge and TopEdge move the position up and to the left of the base position. Positive values move down and to the right.
- Width, Height
- The width and height of the image. Width contains the actual width of the image in pixels. Height specifies the height of the image in pixels.
- The Width field of the Image structure contains the actual width in pixels of the widest part of the image, not how many pixels are contained in the words that define the image.
- Depth
- The depth of the image, or the number of bitplanes used to define it. This is not the depth of the screen or window in which the image will be displayed, it is the actual number of bitplanes that are defined in the ImageData.
- ImageData
- This is a pointer to the bits that define the image and determine the colors of each pixel. Image data must be placed in Chip memory. The data is organized as an array of 16 bit words whose size can be computed as follows:
- WordWidth = ((Width + 16) / 16);
- NumImageWords = WordWidth * Height * Depth;
- The width of the image is rounded up to the nearest word (16 bits) and extra trailing bits are ignored. Each line of each bitplane must have enough words to contain the image width, with extra bits at the end of each line set to zero. For example, an image 7 bits wide requires one word for each line in the bitplane, whereas an image 17 bits wide requires two words for each line in the bitplane.
- PlanePick
- PlanePick tells which planes of the target BitMap are to receive planes of image data. This field is a bit-wise representation of bitplane numbers. For each bit set in PlanePick, there should be a corresponding bitplane in the image data.
- PlaneOnOff
- PlaneOnOff tells whether to set or clear bits in the planes in the target BitMap that receive no image data. This field is a bit-wise representation of bitplane numbers.
- NextImage
- This field is a pointer to another instance of an Image structure. Set this field to NULL if this is the last Image structure in the linked list.
Directly Drawing the Image
As noted above, you use the DrawImage() call to directly draw an image into a screen or window RastPort.
VOID DrawImage( struct RastPort *rp, struct Image *image, LONG leftOffset, LONG topOffset );
The rp argument is a pointer to the RastPort into which the image should be drawn. This RastPort may come from a Window or Screen structure.
The image argument is a pointer to the list of Image structures that are to be rendered. The list may contain a single Image structure.
The leftOffset and topOffset arguments are the external component, or the base position, for this list of images. The LeftEdge and TopEdge values of each Image structure are added to these values to determine the final position of each image.
Image Data
Image data must be in Chip memory. The Image structure itself may be in any memory, but the actual data referenced by ImageData field must be in Chip memory. This may be done by using compiler specific options, such as the __chip keyword of SAS/C, or by allocating memory with the MEMF_CHIP attribute and copying the image data to that memory.
Defining Image Data
Image data consists of binary data organized into a series of 16-bit words. The words must be sequential, where each successive word represents bits that are displayed later in the image. The image is defined as follows:
The image is broken down into bitplanes. Each bitplane is considered separately.
Within a single bitplane, each row of pixels is taken separately. First, round the number of pixels up to the next even multiple of 16. This determines the number of words used to represent a single row of data. For instance, an image that is 17 bits wide will require two 16-bit words to represent each row.
The leftmost 16 pixel values are placed in the first word, followed by the next 16 pixel values, and so on. Any extra pixels at the end of the last word of the ImageData should be set to zero.
The first row of data is the topmost row of the low order bitplane. This is immediately followed by the second row, then the third, until all rows in the bitplane have been represented.
The data for the low order bitplane is followed immediately by the next to lowest, then the next, etc.
The color of each pixel in the image is directly related to the value in one or more memory bits, depending upon how many bitplanes there are in the image data and in which bitplanes of the screen or window the display is displayed.
The color for a single pixel may be determined by combining the bits taken from the same relative position within each of the bitplanes used to define the image. For each pixel, the system combines all the bits in the same position to create a binary value that corresponds to one of the system color registers. This method of determining pixel color is called color indirection, because the actual color value is not in the display memory. Instead, it is in color registers that are located somewhere else in memory.
In many situations, the image and display will have different number of bitplanes, which complicates the determination of the color value for a given pixel. For now, assume that the image and display have the same number of bitplanes. The more complex example will be covered below, in the section “Picking Bitplanes for the Image Display”.
If an image consists of only one bitplane and is displayed in a one bitplane display, then wherever there is a 0 bit in the image data, the color in color register zero is displayed and wherever there is a 1 bit, the color in color register one is displayed.
In an image composed of two bitplanes, the color of each pixel is obtained from a binary number formed by the values in two bits, one from the first bitplane and one from the second bitplane. If the bit in the first bitplane is a 1 and the bit in the second bitplane is a 0, then the color of that pixel will be taken from color register two (since 10 in binary is two in decimal). Again, the first bitplane describes all of the low order bits for each pixel. The second bitplane describes the next higher bit, and so on. This can be extended to any number of bitplanes.
/* ** simpleimage.c - program to show the use of a simple Intuition Image. */ #include <exec/types.h> #include <intuition/intuition.h> #include <intuition/intuitionbase.h> #include <proto/exec.h> #include <proto/dos.h> #include <proto/intuition.h> struct IntuitionIFace *IIntuition = NULL; #define MYIMAGE_LEFT (0) #define MYIMAGE_TOP (0) #define MYIMAGE_WIDTH (24) #define MYIMAGE_HEIGHT (10) #define MYIMAGE_DEPTH (1) /* This is the image data. It is a one bit-plane open rectangle which is 24 ** pixels wide and 10 high. */ uint16 myImageData[] = { 0xFFFF, 0xFF00, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xFFFF, 0xFF00, }; /* ** main routine. Open required library and window and draw the images. ** This routine opens a very simple window with no IDCMP. See the ** chapters on "Windows" and "Input and Output Methods" for more info. ** Free all resources when done. */ int main() { struct Window *win; struct Image myImage; struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50); IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL); if (IIntuitionBase != NULL) { if (NULL != (win = IIntuition->OpenWindowTags(NULL, WA_Width, 200, WA_Height, 100, WA_RMBTrap, TRUE, TAG_END))) { myImage.LeftEdge = MYIMAGE_LEFT; myImage.TopEdge = MYIMAGE_TOP; myImage.Width = MYIMAGE_WIDTH; myImage.Height = MYIMAGE_HEIGHT; myImage.Depth = MYIMAGE_DEPTH; myImage.ImageData = myImageData; myImage.PlanePick = 0x1; /* use first bit-plane */ myImage.PlaneOnOff = 0x0; /* clear all unused planes */ myImage.NextImage = NULL; /* Draw the 1 bit-plane image into the first bit-plane (color 1) */ IIntuition->DrawImage(win->RPort,&myImage,10,10); /* Draw the same image at a new location */ IIntuition->DrawImage(win->RPort,&myImage,100,10); /* Wait a bit, then quit. ** In a real application, this would be an event loop, like the ** one described in the Intuition Input and Output Methods chapter. */ IDOS->Delay(200); IIntuition->CloseWindow(win); } } IExec->DropInterface((struct Interface*)IIntuition); IExec->CloseLibrary(IntuitionBase); return 0; }
Picking Bitplanes for Image Display
A single image may be displayed in different colors without changing the underlying image data. This is done by selecting which of the target bitplanes are to receive the image data, and what to do with the target bitplanes that do not receive any image data.
PlanePick and PlaneOnOff are used to control the bitplane rendering of the image. The bits in each of these variables have a direct correspondence to the bitplanes of the target bitmap. The lowest bit position corresponds to the lowest numbered bitplane, the next highest bit position corresponds to the next bitplane, etc.
For example, for a window or screen with three bitplanes (consisting of planes 0, 1, and 2), all the possible values for PlanePick or PlaneOnOff and the planes picked are as follows:
PlanePick or PlaneOnOff | Planes Picked |
---|---|
000 | No planes |
001 | Plane 0 |
010 | Plane 1 |
011 | Planes 0 and 1 |
100 | Plane 2 |
101 | Planes 0 and 2 |
110 | Planes 1 and 2 |
111 | Planes 0, 1, and 2 |
PlanePick picks the bitplanes of the containing RastPort that will receive the bitplanes of the image. For each plane that is picked to receive data, the next successive plane of image data is drawn there. For example, if an image with two bitplanes is drawn into a window with four bitplanes with a PlanePick of binary 1010, the first bitplane of the image will be drawn into the second bitplane of the window and the second bitplane of the image will be drawn into the fourth bitplane of the window. Do not set more bits in PlanePick than there are bitplanes in the image data.
PlaneOnOff specifies what to do with the bitplanes that are not picked to receive image data. If the PlaneOnOff bit is zero, then the associated bitplane will be filled with zeros. If the PlaneOnOff bit is one, then the associated bitplane will be filled with ones. Of course, only bits that fall within the rectangle defined by the image are affected by this manipulation.
Only the bits not set in PlanePick are used in PlaneOnOff, that is, PlaneOnOff only applies to those bitplanes not picked to receive image data. For example, if PlanePick is 1010 and PlaneOnOff is 1100, then PlaneOnOff may be viewed as x1x0 (where the x positions are not taken into consideration). In this case, planes two and four would receive image data and planes one and three would be set by PlaneOnOff. Each bit in plane one would be set to zero and each bit in plane three would be set to one.
PlaneOnOff is only useful where an entire bitplane of an image may be set to the same value. If the bitplane is not all set to the same value, even for just a few bits, then image data must be specified for that plane.
A simple trick to create a filled rectangle of any color may be used by supplying no image data, where the color is controlled by PlaneOnOff. The Depth of such an image is set to zero, the size of the rectangle is specified in the Width and Height fields and the ImageData pointer may be NULL. PlanePick should be set to zero, as there are no planes of image data to pick. PlaneOnOff is then set to the color register which contains the desired color for the rectangle.
Image Example
A more complex example of the use of an Image is presented below.
/* ** compleximage.c - program to show the use of a complex Intuition Image. */ #include <exec/types.h> #include <intuition/intuition.h> #include <intuition/intuitionbase.h> #include <proto/exec.h> #include <proto/dos.h> #include <proto/intuition.h> struct IntuitionIFace *IIntuition = NULL; #define MYIMAGE_LEFT (0) #define MYIMAGE_TOP (0) #define MYIMAGE_WIDTH (24) #define MYIMAGE_HEIGHT (10) #define MYIMAGE_DEPTH (2) /* This is the image data. It is a two bitplane open rectangle which ** is 24 pixels wide and 10 high. */ uint16 myImageData[] = { /* first bitplane of data, ** open rectangle. */ 0xFFFF, 0xFF00, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xC000, 0x0300, 0xFFFF, 0xFF00, /* second bitplane of data, ** filled rectangle to appear within open rectangle. */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00FF, 0x0000, 0x00FF, 0x0000, 0x00FF, 0x0000, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; /* used to get the "new look" on a custom screen */ uint16 pens[] = { ~0 }; /* ** main routine. Open required library and window and draw the images. ** This routine opens a very simple window with no IDCMP. See the ** chapters on "Windows" and "Input and Output Methods" for more info. ** Free all resources when done. */ int main() { struct Screen *scr; struct Window *win; struct Image myImage; struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50); IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL); if (IIntuitionBase != NULL) { if (NULL != (scr = IIntuition->OpenScreenTags(NULL, SA_Depth, 4, SA_Pens, &pens, TAG_END))) { if (NULL != (win = IIntuition->OpenWindowTags(NULL, WA_RMBTrap, TRUE, WA_CustomScreen, scr, TAG_END))) { myImage.LeftEdge = MYIMAGE_LEFT; myImage.TopEdge = MYIMAGE_TOP; myImage.Width = MYIMAGE_WIDTH; myImage.Height = MYIMAGE_HEIGHT; myImage.Depth = MYIMAGE_DEPTH; myImage.ImageData = myImageData; myImage.PlanePick = 0x3; /* use first two bitplanes */ myImage.PlaneOnOff = 0x0; /* clear all unused planes */ myImage.NextImage = NULL; /* Draw the image into the first two bitplanes */ IIntuition->DrawImage(win->RPort,&myImage,10,10); /* Draw the same image at a new location */ IIntuition->DrawImage(win->RPort,&myImage,100,10); /* Change the image to use the second and fourth bitplanes, ** PlanePick is 1010 binary or 0xA, ** and draw it again at a different location */ myImage.PlanePick = 0xA; IIntuition->DrawImage(win->RPort,&myImage,10,50); /* Now set all the bits in the first bitplane with PlaneOnOff. ** This will make all the bits set in the second bitplane ** appear as color 3 (0011 binary), all the bits set in the ** fourth bitplane appear as color 9 (1001 binary) and all ** other pixels will be color 1 (0001 binary. If there were ** any points in the image where both bits were set, they ** would appear as color 11 (1011 binary). ** Draw the image at a different location. */ myImage.PlaneOnOff = 0x1; IIntuition->DrawImage(win->RPort,&myImage,100,50); /* Wait a bit, then quit. ** In a real application, this would be an event loop, like the ** one described in the Intuition Input and Output Methods chapter. */ IDOS->Delay(200); IIntuition->CloseWindow(win); } IIntuition->CloseScreen(scr); } } IExec->DropInterface((struct Interface*)IIntuition); IExec->CloseLibrary(IntuitionBase); return 0; }