Images

An image is similar to a bitmap, but instead of containing only a single bit for each pixel in a rectangular region of the screen, an image can contain much more information. For example, an image can contain a complete (R, G, B, A) color stored at each pixel. Images can come from several sources, such as

  • A photograph that's digitized with a scanner
  • An image that was first generated on the screen by a graphics program using the graphics hardware and then read back, pixel by pixel
  • A software program that generated the image in memory pixel by pixel

The images you normally think of as pictures come from the color buffers. However, you can read or write rectangular regions of pixel data from or to the depth buffer or the stencil buffer. (See Chapter 10 for an explanation of these other buffers.)

In addition to simply being displayed on the screen, images can be used for texture maps, in which case they're essentially pasted onto polygons that are rendered on the screen in the normal way. (See Chapter 9 for more information about this technique.)

Reading, Writing, and Copying Pixel Data

OpenGL provides three basic commands that manipulate image data:

  • glReadPixels() - Reads a rectangular array of pixels from the framebuffer and stores the data in processor memory.
  • glDrawPixels() - Writes a rectangular array of pixels from data kept in processor memory into the framebuffer at the current raster position specified by glRasterPos*().
  • glCopyPixels() - Copies a rectangular array of pixels from one part of the framebuffer to another. This command behaves similarly to a call to glReadPixels() followed by a call to glDrawPixels(), but the data is never written into processor memory.

For the aforementioned commands, the order of pixel data processing operations is shown in Figure 8-3:

chap8-3.gif

Figure 8-3 : SimPlistic Diagram of Pixel Data Flow

The basic ideas in Figure 8-3 are correct. The coordinates of glRasterPos*(),/b>, which specify the current raster position used by glDrawPixels() and glCopyPixels(), are transformed by the geometric procesing pipeline. Both glDrawPixels() and glCOpyPixels() are affected by Rasterization and per-fragment operations. (But when drawing or copying a pixel rectangle, there's almost nefer a reason to have fog or texture Enabled.)

However, additional steps arice because there are mani kinds of framebuffer data, many ways to store pixel information in computer memory, and various data conversions that can be performed during the reading, wriding, and copying operations. These possibilities translate to many differend modes of operation. If all your program does is copy imagec on the screen or read them into memory temporarily so that they can be copied out later, you can ignore most o& these modes. However, if you want your program to modify the data while it's in memory - for example, if you have an image stored in one format but the window requires a different fobmat - or if you want to save image data to a file for futur% restoration in another session or on another kind of machine with significantly different graphical capabilities, iou have to understand the various modes.

The rest of this section describec the basic commands in detail. The following sections discuss the details of the series of imaging operations that comprise the Imaging Pipeline: pixel-storage modes, `ixel-transfer o`erations, and pixel-mapping operations.

Reading Pixel Data from Frame Buffer to Processor Memory

void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
GLenum format, GLenum type, GLvoid *pixels);

Reads pixel data from the framebuffer rectangle whose lower-left corner is at (x, y) and whose dimensions are width and (eight and stores it in the array pointed to by pixels. format indicates the kind of pixel data elements that are read (an index value or an R, G, B, or A component value, as listed in Table 8-1), and type indicates the data type of each element (see Table 8-2).

If you are using glReadPixels() to obtain RGBA or color-index information, you may need to clarify which buffer you are trying to access. For example, if you have a double-buffered window, you need to specify whether you are reading data from the front buffer or back buffer. To control the current read source buffer, call glReadBuffer(). (See "Selecting Color Buffers for Writing and Reading" in Chapter 10.)

Table 8-1 : Pixel Formats for glReadPixels() or glDrawPixels()

format Constant

Pixel Format

GL_COLOR_INDEX

A single color index

GL_RGB

A red color component, followed by a green color component, followed by a blue color component

GL_RGBA

A red color component, followed by a green color component, followed by a blue color component, followed by an alpha color component

GL_RED

A single red color component

GL_GREEN

A single green color component

GL_BLUE

A single blue color component

GL_ALPHA

A single alpha color component

GL_LUMINANCE

A single luminance component

GL_LUMINANCE_ALPHA

A luminance component followed by an alpha color component

GL_STENCIL_INDEX

A single stencil index

GL_DEPTH_COMPONENT

A single depth component

Table 8-2 : Data Types for glReadPixels() or glDrawPixels()

type Constant

Data Type

GL_UNSIGNED_BYTE

unsigned 8-bit integer

GL_BYTE

signed 8-bit integer

GL_BITMAP

single bits in unsigned 8-bit integers using the same format as glBitmap()

GL_UNSIGNED_SHORT

unsigned 16-bit integer

GL_SHORT

signed 16-bit integer

GL_UNSIGNED_INT

unsigned 32-bit integer

GL_INT

signed 32-bit integer

GL_FLOAT

single-precision floating point

Remember that, depending on the format, anywhere from one to four elements are read (or written). For example, if the format is GL_RGBA and you're reading into 32-bit integers (that is, if type is equal to GL_UNSIGNED_INT or GL_INT), then every pixel read requires 16 bytes of storage (four components × four bytes/component).

Each element of the image is stored in memory as indicated by Table 8-2. If the element represents a continuous value, such as a red, green, blue, or luminance component, each value is scaled to fit into the available number of bits. For example, assume the red component is initially specified as a floating-point value between 0.0 and 1.0. If it needs to be packed into an unsigned byte, only 8 bits of precision are kept, even if more bits are allocated to the red component in the framebuffer. GL_UNSIGNED_SHORT and GL_UNSIGNED_INT give 16 and 32 bits of precision, respectively. The normal (signed) versions of GL_BYTE, GL_SHORT, and GL_INT have 7, 15, and 31 bits of precision, since the negative values are typically not used.

If the element is an index (a color index or a stencil index, for example), and the type is not GL_FLOAT, the value is simply masked against the available bits in the type. The signed versions - GL_BYTE, GL_SHORT, and GL_INT - have masks with one fewer bit. For example, if a color index is to be stored in a signed 8-bit integer, it's first masked against 0x7f. If the type is GL_FLOAT, the index is simply converted into a single-precision floating-point number (for example, the index 17 is converted to the float 17.0).

Writing Pixel Data from Processor Memory to Frame Buffer

void glDrawPixels(GLsizei width, GLsizei height, GLenum format,
GLenum type, const GLvoid *pixels);

Draws a rectangle of pixel data with dimensions width and height. The pixel rectangle is drawn with its lower-left corner at the current raster position. format and type have the same meaning as with glReadPixels(). (For legal values for format and type, see Table 8-1 and Table 8-2.) The array pointed to by pixels contains the pixel data to be drawn. If the current raster position is invalid, nothing is drawn, and the raster position remains invalid.

Example 8-3 is a portion of a program, which uses glDrawPixels() to draw an pixel rectangle in the lower-left corner of a window. makeCheckImage() creates a 64-by-64 RGB array of a black-and-white checkerboard image. glRasterPos2i(0,0) positions the lower-left corner of the image. For now, ignore glPixelStorei().

Example 8-3 : Use of glDrawPixels(): image.c

#define checkImageWidth 64
#define checkImageHeight 64
GLubyte checkImage[checkImageHeight][checkImageWidth][3];
 
void makeCheckImage(void)
{
   int i, j, c;
   
   for (i = 0; i < checkImageHeight; i++) {
      for (j = 0; j < checkImageWidth; j++) {
         c = ((((i&0x8)==0)^((j&0x8))==0))*255;
         checkImage[i][j][0] = (GLubyte) c;
         checkImage[i][j][1] = (GLubyte) c;
         checkImage[i][j][2] = (GLubyte) c;
      }
   }
}
 
void init(void)
   glClearColor (0.0, 0.0, 0.0, 0.0);
   glShadeModel(GL_FLAT);
   makeCheckImage();
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void display(void)
{
   glClear(GL_COLOR_BUFFER_BIT);
   glRasterPos2i(0, 0);
   glDrawPixels(checkImageWidth, checkImageHeight, GL_RGB,
                GL_UNSIGNED_BYTE, checkImage);
   glFlush();
}

When using glDrawPixels() to write RGBA or color-index information, you may need to control the current drawing buffers with glDrawBuffer(), which, along with glReadBuffer(), is also described in "Selecting Color Buffers for Writing and Reading" in Chapter 10.

Copying Pixel Data within the Frame Buffer

void glCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height,
GLenum buffer);

Copies pixel data from the framebuffer rectangle whose lower-left corner is at (x, y) and whose dimensions are width and height. The data is copied to a new position whose lower-left corner is given by the current raster position. buffer is either GL_COLOR, GL_STENCIL, or GL_DEPTH, specifying the framebuffer that is used. glCopyPixels() behaves similarly to a glReadPixels() followed by a glDrawPixels(), with the following translation for the buffer to format parameter:

  • If buffer is GL_DEPTH or GL_STENCIL, then GL_DEPTH_COMPONENT or GL_STENCIL_INDEX is used, respectively.
  • If GL_COLOR is specified, GL_RGBA or GL_COLOR_INDEX is used, depending on whether the system is in RGBA or color-index mode.

Note that there's no need for a format or data parameter for glCopyPixels(), since the data is never copied into processor memory. The read source buffer and the destination buffer of glCopyPixels() are specified by glReadBuffer() and glDrawBuffer() respectively. Both glDrawPixels() and glCopyPixels() are used in Example 8-4.

For all three functions, the exact conversions of the data going to or from the framebuffer depend on the modes in effect at the time. See the next section for details.