Back to November Calendar

Previous Entry Next Entry→

November 28, 2005

Pixels and Bitmaps

The visual elements of a computer screen are derived from manipulating frame buffers.  Each pixel point on the screen has (x,y) coordinates which corresponds to a group of bits, say 32 bits for the front RGBA buffer, another 32 bits for the back RGBA buffer and 32 more for the depth values.  There may also be an accumulation buffer, a stencil buffer or extra color buffers.

As illustrated below, you can think of these buffers as layers of 1-bit mXn planes, where mn is the resolution of the screen.  Groups of planes like this correspond to various OpenGL buffers.   All the bits for a given x,y location form the pixel for that location.

As a programmer, typically I'll want to read and write rectangular arrays of pixels, or only specific groups of bits in the generalized pixel that correspond to one of the OpenGL buffers, using a single function execution called a bit block transfer operation, or simply "bitblt."

The functions for manipulating bitmaps are quite different.  A bitmap, like a pixel, is thought of as a fundamental primitive.  While vertices are fundamental to geometric processing, pixels and bitmaps are fundamental to pixel operations and the results of these two separate processing are combined in rasterization and finally, display. 

At first blush, it may seem that dealing with bitmaps would be fairly simple: you put the rectangular chunk where you want it.  Trouble is, you can run into conflicts of format; how the pixels are stored in the bitmap, and the different types of pixels that might be involved.  Is the pixel RGB, RGBA, depth or luminance or what?  And are the represented by bytes, integers or floats?  Do we need to rescale the bitmap or run it through some kind of filter?

The most common use of a bitmap is to represent a character, as "x" is represented at right, say.  Starting at the lower left, we'd encode this character as a sequence of bytes like so: 

GLubyte letterX[] = {0xc0, 0x20, 0xe0, 0x60, 0xf0, 0xc0, 0x79, 0x80, 0x3f, 0x00, 0x1e, 0x00, 0x0f, 0x00, 0x1f, 0x80, 0x33, 0xc0, 0x61, 0xe0, 0xc0, 0xe0, 0x80, 0x60}

A way to see this is the first 8 bits (11000000) is encoded as "0xc0" (recall that c represents the number 12, which in binary from is 1100.)  This is followed by the three bits "001" which are read as the first three bits of the bite "0x20."  Going up a row we find "11100000" = "0xe0," and so on.

1 0 0 0 0 0 0 0 0 1 1
1 1 0 0 0 0 0 0 1 1 1
0 1 1 0 0 0 0 1 1 1 1
0 0 1 1 0 0 1 1 1 1 0
0 0 0 1 1 1 1 1 1 0 0
0 0 0 0 1 1 1 1 0 0 0
0 0 0 1 1 1 1 0 0 0 0
0 0 1 1 1 1 1 1 0 0 0
0 1 1 1 1 0 0 1 1 0 0
1 1 1 1 0 0 0 0 1 1 0
1 1 1 0 0 0 0 0 0 1 1
1 1 0 0 0 0 0 0 0 0 1

Bitmaps are also used to construct cursors, mouse pointers and crosshairs.  Also, as masks to cause action at a location.

To define a bitmap for use in a program, it helps to put the bits in a one-dimensional array.  This snippet generates 64*64 = 4096 bits for an 8x8 checkerboard where each square is either and 8x8 block of 1's or and 8x8 block of 0's. 

GLubyte checkers[2] = {0x00, 0xff};
GLubyte board[512];
int i, j;
for(i = 0; i < 64; ++i) for(j = 0; j < 8; ++j)
     board[i+8*j] = checkers[(i/8+j)%2];

To display the board in a 64x64 bitmap use the function call

void glBitmap( GLsizei width,
               GLsizei height,
               GLfloat xorig,
               GLfloat yorig,
               GLfloat xmove,
               GLfloat ymove,
               const GLubyte *bitmap )

In this instance,

glBitmap(64,64,0.0,0.0,0.0,0.0,board)

Where is the board positioned?  The function glRasterPos*() will set the current raster position.  The third and fourth parameters are offsets added to the current raster position the lower left corner to determine where to position the bitmap in the color buffer.  Then the raster position is incremented by the fifth and sixth parameters--these are all zero in the function call above.  The main purpose for these parameters is in creating text we typically want to advance in an orderly, regular fashion. This is not as simple as it might seem at first: some characters extend below the baseline and you may want superscripts, etc.

The raster position can be specified in two, three or four dimensions using either floats or integers.  As it passes through the geometric pipeline it's transformed by both the model-view and projection matrices before finding its place on the screen.  It may actually be outside the viewport, in which case it won't be drawn.

Transparency is inherent in that a 1 pixel, the result also depends on the current state of the pixel where it is placed.  A zero pixel in a bitmap will be totally transparent.  If the color buffer is currently all red and the drawing color is set to green, then the code above will draw a green and red checkerboard.

Bitmap and geometric figures can be mixed in the same graphic, but the geometric objects will be rescaled when you resize the window while bitmapped figures will not.  The figures below illustrate this.  The code follows.

 

Here's an implementation of these ideas so far:

/* Draw a mix of bitmap and geometric objects */

#include <GL/glut.h> // This includes gl.h and glu.h

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT);
   
glColor3f(0.0, 0.0, 1.0);
   
glBegin(GL_POLYGON);
     
glVertex2f(32.0,-32.0);
     
glVertex2f(96.0,32.0);
     
glVertex2f(32.0,96.0);
     
glVertex2f(-32.0,32.0);
   
glEnd();
   
glColor3f(1.0, 0.0, 0.0);
   
glRasterPos2i(0,0);
   
GLubyte wb[2]={0x00,0xff};
   
GLubyte checkerBoard[512];
   
int i, j;
   
for(i=0;i<64;++i) for(j=0;j<8;++j)
   
    checkerBoard[i*8+j] = wb[(i/8+j)%2];
   
//glBegin(GL_POLYGON)
    glBitmap(64,64,0.0,0.0,0.0,0.0,checkerBoard);
   
glColor3f(0.0, 0.5, 0.5);
   
glBegin(GL_POLYGON);
      glVertex2f(80.0,70.0);
      glVertex2f(10.0,30.0);
      glVertex2f(90.0,10.0);
   
glEnd();
   
glFlush();
}

void init() {
   
//set clear color to white
   
glClearColor(1.0,1.0,1.0,1.0);
   
//set standard orthogonal (look straight at) clipping view
    glMatrixMode(GL_PROJECTION);
   
glLoadIdentity();
   
// Scale window
   
gluOrtho2D(-50.0,100.0,-50.0,100.0);
}

int main(int argc, char** argv) {
   
//init mode and open window in upper-left corner
    glutInit(&argc, argv);
   
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
   
glutInitWindowSize(200,200);
   
glutInitWindowPosition(0,0);
   
glutCreateWindow("dimple");
   
glutDisplayFunc(display);
   
init();|
   
glutMainLoop();
}

Btw, I recall having fussed with this some months ago, though I'm not sure just what entry it was.  There are functions for printing letters like

void glutBitmapCharacter( void *font, int character)

There are several fonts available, two fixed width ones:

GLUT_BITMAP_8_BY_13
GLUT_BITMAP_9_BY_15

and five proportional

GLUT_BITMAP_TIMES_ROMAN_10
GLUT_BITMAP_TIMES_ROMAN_24
GLUT_BITMAP_HELVETICA_10
GLUT_BITMAP_HELVETICA_12
GLUT_BITMAP_HELVETICA_18

The following code will display string string at (x, y).  I used it to print "howdy!" on top of the frame buffer as seen at right:

void DrawString(int x, int y, char *string)
{
  int len, i;
  glRasterPos2f(x, y);
  len = (int)strlen(string);
  for (i = 0; i < len; i++)
    {glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,string[i]);
  }
}

Bringing the mouse into play is simple enough.  I'm not sure this is the best way to go here.  I added the line

glutMouseFunc(maotzer);

to the main() function and then transfer some of the functionality from display() to maotzer() more or less as follows:

void maotzer(int button, int state, int x, int y) {
    string getBack = "Get back!";
    GLubyte wb[2]={0x00,0xff};
    GLubyte checkerBoard[512];
    int i, j;
    for(i=0;i<64;++i) for(j=0;j<8;++j)
    checkerBoard[i*8+j] = wb[(i/8+j)%2];
    if(state == GLUT_DOWN&&button == GLUT_LEFT_BUTTON)
        //exit(0);
        drawString(-20,50,getBack);
    if(state == GLUT_DOWN&&button == GLUT_RIGHT_BUTTON)
        glBitmap(64,64,0.0,0.0,0.0,0.0,checkerBoard);
}

As the result indicates, the outcome was a little unpredictable?!  Anyway, I've been trying to figure out how to bring in existing graphics files in, say, jpeg format, but it's proving to be quite difficult!  I found this:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnopen/html/msdn_gl5.asp

My problem at this point is that I'd like to draw .bmp files, but that doesn't seem to be readily facilitated, perhaps due to the complex issues of pixel formats, etc. 

Images

The main limitation of a bitmap, it seems to me, is that it only allows a single bit (get it: bit!!!) per pixel.  Most perplexing. For an image, you might want each pixel to contain R,G,B,A color.  This is more in the realm of  texture maps.