1. A brief introduction to the BMP file format
A bmp file is a pixel file that stores all pixels in an image. This file format can save a monochrome bitmap, a 16-or 256-color index mode pixel chart, and a 24-bit real-color image. The size of a single pixel in each mode is 1/8 bytes, 1/2 bytes, respectively, 1 byte and 3 byte. Currently, the most common are 256-color BMP and 24-bit BMP. This file format also defines several methods for storing pixels, including non-compression and RLE compression. Most common BMP files are not compressed.
For the sake of simplicity, we only discuss the 24-bit color and do not use compressed BMP. (If you use the built-in drawing program in Windows, it is easy to draw a BMP that meets the above requirements)
The BMP file used by Windows has a file header at the beginning, which is 54 bytes in size. The information including the file format identifier, number of colors, image size, and compression mode is saved. Because we only discuss 24-bit non-compressed BMP, we do not need to pay attention to the information in the file header, only the "size" option is useful to us. The width and height of the image are both a 32-bit integer. The addresses in the file are 0x0012 and 0x0016 respectively. Therefore, we can use the following code to read the image size information:
Glint width, height; // use the glint type of OpenGL, Which is 32-bit.
// The Int value of C language is not necessarily 32 bits.
File * pfile;
// Open a file here
Fseek (pfile, 0x0012, seek_set); // move to the 0x0012 position
Fread (& width, sizeof (width), 1, pfile); // read width
Fseek (pfile, 0x0016, seek_set); // move to the 0x0016 position
// Since the last statement should be at 0x0016
// This sentence can be omitted.
Fread (& height, sizeof (height), 1, pfile); // read height
After 54 bytes, if it is a 16-or 256-color BMP, there will be a color table, but 24-bit BMP does not have this, we will not consider it here. The next step is the actual pixel data. In a 24-bit BMP file, each three bytes represents a pixel color.
Note that OpenGL usually uses RGB to represent the color, but BMP files use BGR, that is, the order is reversed.
In addition, it should be noted that the pixel data volume is not necessarily equal to the Image Height multiplied by the width multiplied by the number of bytes per pixel, but may be slightly greater than this value. The reason is that the BMP file adopts an "alignment" mechanism. If the length of each line of pixel data is not a multiple of 4, some data is filled to make it a multiple of 4. In this way, the size of a 17*15 24-bit BMP should be 834 bytes (17 pixels per line, 51 bytes, supplemented as 52 bytes, multiply by 15 to get the total length of the pixel data 780, plus the 54 bytes starting from the file, get 834 bytes ). When allocating memory, be careful not to directly use "the Image Height multiplied by the width multiplied by the number of bytes per pixel" to calculate the length of the allocated space, otherwise, the allocated memory space may be insufficient, resulting in out-of-bounds access and various serious consequences.
A Simple Method for Calculating the data length is as follows:
Int linelength, totallength;
Linelength = imagewidth * bytesperpixel; // The length of each row is roughly the image width multiplied
// The number of bytes per pixel
While (linelength % 4! = 0) // corrected linelength to make it a multiple of 4
++ Linelenth;
Totallength = linelength * imageheight; // total Data Length = length of each line * Image Height
This is not the most efficient method, but because this correction does not involve a large amount of computing and is not frequently used, we do not need to consider a faster method.
2. Simple OpenGL pixel operations
OpenGL provides simple functions for Pixel operations:
Glreadpixels: read some pixels. Currently, it can be simply understood as "reading the drawn pixels (which may have been saved to the graphics card's video memory) to the memory ".
Gldrawpixels: draws some pixels. It can be simply understood as "using some data in the memory as pixel data for plotting ".
Glcopypixels: copy some pixels. It can be simply understood as "copying a drawn pixel from one position to another ". Although the function seems to be equivalent to reading the pixel and then drawing the pixel, in fact it does not need to map the pixel (it may have been saved to the video memory) conversion to memory data, and then re-drawing by memory data, so it is much faster than reading and then drawing.
These three functions can complete simple pixel read, draw, and copy tasks, but they can also complete more complex tasks. Currently, we only discuss some simple applications. As the number of parameters for these functions is large, we will introduce them separately.
3. glreadpixels usage and Examples
3.1 function parameters
This function has a total of seven parameters. You can obtain a rectangle from the first four parameters. All the pixels included in the rectangle are read. (The first and second parameters represent the horizontal and vertical coordinates of the lower left corner of the rectangle. The coordinates are zero in the lower left corner of the window and the maximum in the upper right corner. The third and fourth parameters represent the width and height of the rectangle)
The fifth parameter indicates the read content. For example, gl_rgb reads the red, green, and blue data of pixels in sequence, gl_rgba reads four data types of pixels: red, green, blue, and Alpha. gl_red reads only the Red Data of pixels (similar to gl_green, gl_blue, and gl_alpha ). If you use the color index mode instead of the rgba color mode, you can also use gl_color_index to read the pixel color index. Currently, you only need to know this, but you can actually read other content, such as the depth data of the depth buffer.
The sixth parameter indicates the format used to save the read content to the memory. For example, gl_unsigned_byte stores various data as glubyte, and gl_float stores various data as glfloat.
The seventh parameter indicates a pointer. After pixel data is read, it is saved to the address indicated by this pointer. Note: Make sure that the address has enough space to accommodate the read pixel data. For example, if an image with a size of 256*256 reads its RGB data and each data is saved as glubyte, the total size is 256*256*3 = 196608 bytes, that is, 192 KB. If you are reading rgba data, the total size is 256*256*4 = 262144 bytes, that is, 256 kb.
Note: glreadpixels actually reads data from the buffer. If a dual-buffer is used, the data is read from the displayed buffer (that is, the front buffer) by default, by default, the painting is drawn to the back buffer. Therefore, if you need to read the pixels that have been drawn, You need to swap the buffer before and after.
Let's look at the two notes in the previously mentioned BMP file:
3.2 solve the inconsistency between the RGB pixel data commonly used by OpenGL and the BGR pixel data sequence of BMP files
Some code can be used to swap the first and third bytes of each pixel, so that RGB data becomes BGR data. Of course, you can also use another method to solve the problem: in addition to using gl_rgb to read the red, green, and blue data of pixels, the new version of OpenGL, you can also use gl_bgr to read the blue, green, and Red Data of pixels in reverse order, which is consistent with the BMP file format. Even if the gl_bgr is not defined in your Gl/Gl. h header file, you can try gl_bgr_ext. Although some OpenGL implementations (especially those in earlier versions) cannot use gl_bgr_ext, all OpenGL implementations in windows I know support gl_bgr, after all, in windows, almost all the color data uses the BGR sequence, rather than the RGB sequence. This may be related to the hardware design of the IBM-PC.
3.3 eliminate the impact of "alignment" in BMP files
In fact, OpenGL also supports pixel data using this "alignment" method. You only need to use glpixelstore to modify the "alignment mode when storing pixels. Like this:
Int alignment = 4;
Glpixelstorei (gl_unpack_alignment, alignment );
The first parameter indicates "set the pixel alignment value", and the second parameter indicates the actual value. Here, pixels can be aligned in a single byte (in fact, they do not use alignment), two-byte alignment (if the length is odd, then add another byte), and four-byte alignment (if the length is not a multiple of four, is multiplied by four), and the eight-character section is aligned. The values corresponding to alignment are 1, 2, 4, and 8. In fact, the default value is 4, which is exactly the same as the alignment of the BMP file.
Glpixelstorei can also be used to set other parameters. But we do not need to discuss it in depth here.
Now we can read the pixels on the screen to the memory. If necessary, we can save the data in the memory to the file. Correct comparison with the BMP file format, our program can save the image on the screen as a BMP file to achieve the screen effect.
We didn't detail all the content of the 54 bytes starting with the BMP file, but it was harmless. Read the first 54 bytes from a correct BMP file and modify the width and height information to obtain the new file header. Assume that we first create a 1*1 small 24-bit BMP file named dummy.bmp, and the new BMP file named grab.bmp. You can write the following code:
File * poriginfile = fopen ("dummy.bmp", "rb );
File * pgrabfile = fopen ("grab.bmp", "WB ");
Char BMP _header [54];
Glint width, height;
/* Set the width and height of the image here, that is, the value of width and height, and calculate the total length of the pixel */
// Read the header 54 bytes from dummy.bmp to the array
Fread (BMP _header, sizeof (BMP _header), 1, poriginfile );
// Write the array content to the new BMP file
Fwrite (BMP _header, sizeof (BMP _header), 1, pgrabfile );
// Modify the size information.
Fseek (FIG, 0x0012, seek_set );
Fwrite (& width, sizeof (width), 1, pgrabfile );
Fwrite (& height, sizeof (height), 1, pgrabfile );
// Move to the end of the file and start writing pixel data
Fseek (pgrabfile, 0, seek_end );
/* Write pixel data to the file here */
Fclose (poriginfile );
Fclose (pgrabfile );
The complete code is provided to demonstrate how to capture the image of the entire window and save it as a BMP file.
# Define Daily wwidth 400
# Define wheight 400
# Include <stdio. h>
# Include <stdlib. h>
/* Function grab
* Capture pixels in the window
* Assume that the window width is megawwidth, and the height is always wheight.
*/
# Define BMP _header_length 54
Void grab (void)
{
File * pdummyfile;
File * pwritingfile;
Glubyte * ppixeldata;
Glubyte BMP _header [BMP _header_length];
Glint I, J;
Glint pixeldatalength;
// Calculate the actual length of the pixel data
I = bytes wwidth * 3; // get the pixel data length of each row
While (I % 4! = 0) // supplement the data until I is a multiple.
++ I; // There are still faster algorithms,
// But here we only want to be intuitive, and there is no high requirement on speed.
Pixeldatalength = I * handle wheight;
// Allocate memory and open files
Ppixeldata = (glubyte *) malloc (pixeldatalength );
If (ppixeldata = 0)
Exit (0 );
Pdummyfile = fopen ("dummy.bmp", "rb ");
If (pdummyfile = 0)
Exit (0 );
Pwritingfile = fopen ("grab.bmp", "WB ");
If (pwritingfile = 0)
Exit (0 );
// Read pixels
Glpixelstorei (gl_unpack_alignment, 4 );
Glreadpixels (0, 0, expires wwidth, interval wheight,
Gl_bgr_ext, gl_unsigned_byte, ppixeldata );
// Copy the dummy.bmp file header to the New File Header
Fread (BMP _header, sizeof (BMP _header), 1, pdummyfile );
Fwrite (BMP _header, sizeof (BMP _header), 1, pwritingfile );
Fseek (pwritingfile, 0x0012, seek_set );
I = descriwwidth;
J = running wheight;
Fwrite (& I, sizeof (I), 1, pwritingfile );
Fwrite (& J, sizeof (J), 1, pwritingfile );
// Write pixel data
Fseek (pwritingfile, 0, seek_end );
Fwrite (ppixeldata, pixeldatalength, 1, pwritingfile );
// Release memory and close files
Fclose (pdummyfile );
Fclose (pwritingfile );
Free (ppixeldata );
}
Copy this code to the sample program of any previous course and call the grab function at the end of the function to save the image content as a BMP file. (This code is used in many places when I write this tutorial. Once this code is written, it is very convenient to run .)
4. gldrawpixels usage and Examples
Compared with glreadpixels, The gldrawpixels function has the same parameter content. The first, second, third, and fourth parameters correspond to the third, fourth, fifth, and sixth parameters of the glreadpixels function, in turn, it indicates the image width, Image Height, pixel data content, and pixel data format in the memory. The last parameter of the two functions also corresponds. glreadpixels indicates the location where the pixels are read and stored in the memory. gldrawpixels indicates the location of the pixel data used for painting in the memory.
Note that the gldrawpixels function has two fewer parameters than the glreadpixels function. These two parameters indicate the starting position of the image in glreadpixels. In gldrawpixels, you do not need to explicitly specify the drawing position, because the drawing position is specified by the glrasterpos function. The parameters of the glrasterpos * function are similar to those of glvertex *. by specifying a two-dimensional/three-dimensional/four-dimensional coordinate, OpenGL automatically calculates the screen position corresponding to the coordinate, this position is used as the starting position of the pixel.
Naturally, we can read pixel data from the BMP file and draw it to the screen using gldrawpixels. We chose Windows XP desktop background bliss.bmp as the drawing content (if you are using Windows XP, you may find this file on the hard disk. Of course, you can also use other bmp files, as long as they are 24-bit BMP files. Note that you need to modify the filename definition at the beginning of the Code), first copy the file to the correct location, we will read the file at the beginning of the program, after obtaining the image size, create an appropriate OpenGL Window Based on the size and draw pixels.
Pixel rendering is a simple process, but this program is slightly different from the previous sample programs on the skeleton, so I plan to provide a complete piece of code.
# Include <Gl/glut. h>
# Define FILENAME "bliss.bmp"
Static glint imagewidth;
Static glint imageheight;
Static glint pixellength;
Static glubyte * pixeldata;
# Include <stdio. h>
# Include <stdlib. h>
Void display (void)
{
// It is not necessary to clear the screen
// The Screen overwrites the whole screen each time it is drawn.
// The result is the same no matter whether the screen is cleared or not
// Glclear (gl_color_buffer_bit );
// Draw pixels
Gldrawpixels (imagewidth, imageheight,
Gl_bgr_ext, gl_unsigned_byte, pixeldata );
// Complete the painting
Gluswapbuffers ();
}
Int main (INT argc, char * argv [])
{
// Open the file
File * pfile = fopen ("bliss.bmp", "rb ");
If (pfile = 0)
Exit (0 );
// Read the image size information
Fseek (pfile, 0x0012, seek_set );
Fread (& imagewidth, sizeof (imagewidth), 1, pfile );
Fread (& imageheight, sizeof (imageheight), 1, pfile );
// Calculate the pixel data length
Pixellength = imagewidth * 3;
While (pixellength % 4! = 0)
++ Pixellength;
Pixellength * = imageheight;
// Read pixel data
Pixeldata = (glubyte *) malloc (pixellength );
If (pixeldata = 0)
Exit (0 );
Fseek (pfile, 54, seek_set );
Fread (pixeldata, pixellength, 1, pfile );
// Close the file
Fclose (pfile );
// Initialize glut and run
Gluinit (& argc, argv );
Fig );
Gluinitwindowposition (100,100 );
Gluinitwindowsize (imagewidth, imageheight );
Ngcreatewindow (filename );
Gludisplayfunc (& Display );
Glumainloop ();
// Release the memory
// In fact, The glumainloop function will never return and will never arrive here
// Write here to release the memory just for a personal habit
// Do not worry that the memory cannot be released. The operating system automatically recycles all memory at the end of the program.
Free (pixeldata );
Return 0;
}
This is just a simple program for displaying 24-bit BMP images. If you are familiar with the BMP file format, you can also write a program for displaying various BMP images. During pixel processing, they use a similar method.
OpenGL can process several pixels before rendering them. The most common method is to zoom in or out the entire pixel image. Glpixelzoom is used to set the coefficient of amplification/reduction. This function has two parameters: horizontal and vertical. For example, if glpixelzoom (0.5f, 0.8f) is set, the horizontal direction is changed to the original 50% size, and the vertical direction is changed to the original 80% size. We can even use a negative coefficient to flip the entire image horizontally or vertically (from left to right by default, but from right to left after turning. By default, pixels are drawn from the bottom to the top, but the top to the bottom will be drawn after the flip. Therefore, the "Start position" set by the glrasterpos * function is not necessarily the lower left corner of the rectangle ).
5. glcopypixels usage and Examples
In terms of effect, glcopypixels performs pixel replication, which is equivalent to reading pixels to the memory and then plotting from the memory to another region, therefore, you can use glreadpixels and gldrawpixels to copy pixels. However, we know that pixel data usually has a large amount of data. For example, if a 1024*768 image is represented by a 24-bit BGR, it must be at least 1024*768*3 bytes, it is 2.25 MB. So much data requires one read operation and one write operation, and because the data formats set in glreadpixels and gldrawpixels are different, it may involve data format conversion. This is undoubtedly a huge burden on the CPU. Glcopypixels is used to directly copy new pixel data from pixel data, which avoids format conversion of redundant data and may reduce data replication operations (because the data may be directly copied by the video card, does not need to pass through the main memory), so the efficiency is relatively high.
The glcopypixels function also uses glrasterpos * series functions to set the drawing position. Because the master memory is not required, you do not need to specify the data format in the memory or use any pointers.
The glcopypixels function has five parameters. The first and second parameters represent the coordinates in the lower left corner of the source rectangle of the copied pixel. The third and fourth parameters represent the width and height of the source of the copied pixel, the fifth parameter usually uses gl_color, indicating the color of the copied pixel, but it can also be gl_depth or gl_stencer, indicating the copy depth buffering data or template buffering data respectively.
It is worth mentioning that the operations set in gldrawpixels and glreadpixels, such as glpixelzoom, are equally valid in the glcopypixels function.
The following is a simple example. After a triangle is drawn, the pixel is copied, and the horizontal and vertical directions are flipped at the same time. Then, the triangle is reduced to half of the original and drawn. After the painting is complete, use the grab to save all content on the screen as grab.bmp. In this example, expires wwidth and interval wheight are constants representing the window width and height.
Void display (void)
{
// Clear the screen
Glclear (gl_color_buffer_bit );
// Draw
Glbegin (gl_triangles );
Glcolor3f (1.0f, 0.0f, 0.0f); glvertex2f (0.0f, 0.0f );
Glcolor3f (0.0f, 1.0f, 0.0f); glvertex2f (1.0f, 0.0f );
Glcolor3f (0.0f, 0.0f, 1.0f); glvertex2f (0.5f, 1.0f );
Glend ();
Glpixelzoom (-0.5f,-0.5f );
Glrasterpos2i (1, 1 );
Glcopypixels (expires wwidth/2, interval wheight/2,
Optional wwidth/2, windowheight/2, gl_color );
// Complete the painting and capture the image and save it as a BMP file
Gluswapbuffers ();
Grab ();
}
Summary:
This section briefly introduces the pixel processing function of OpenGL based on common BMP image formats in windows. This includes using glreadpixels to read pixels, gldrawpixels to draw pixels, and glcopypixels to copy pixels.
This lesson only introduces some simple pixel processing applications, but I believe you can understand that there are still some "peripheral" functions around these three pixel processing functions, such as glpixelstore *, glrasterpos * And glpixelzoom. We only use a small part of these functions.
There are not many examples in this lesson. There are examples of three pixel processing functions. You can use these examples to understand them.
Appendix (Other bitwise BMP files ):
BMP file Composition
A bmp file consists of four parts: File Header, bitmap information header, color information, and graphic data.
BMP File Header
The data structure of the BMP file header contains information such as the type, size, and start position of the BMP file.
Its structure is defined as follows:
Typedef struct tagbitmapfileheader
{
Wordbftype; // The type of the bitmap file, which must be BM.
DWORD bfsize; // the size of the bitmap file, in bytes.
Wordbfreserved1; // reserved word for bitmap files, which must be 0
Wordbfreserved2; // reserved word for bitmap files, which must be 0
DWORD bfoffbits; // the starting position of the bitmap data, relative to the bitmap
// The offset of the file header, in bytes
} Bitmapfileheader;
Bitmap header
The BMP Bitmap header is used to describe the size and other information of the bitmap.
Typedef struct tagbitmapinfoheader {
DWORD bisize; // number of bytes occupied by the structure
Longbiwidth; // The width of the bitmap, in pixels.
Longbiheight; // The height of the bitmap, in pixels
Word biplanes; // The level of the target device, which must be 1
Word bibitcount // The number of digits required for each pixel, which must be 1 (two-color ),
// 4 (16 colors), 8 (256 colors), or 24 (true color)
DWORD bicompression; // bitmap compression type, which must be 0 (not compressed ),
// 1 (bi_rle8 compression type) or 2 (bi_rle4 compression type)
DWORD bisizeimage; // bitmap size, in bytes
Longbixpelspermeter; // horizontal bitmap resolution, number of workers per meter
Longbiypelspermeter; // bitmap vertical resolution, number of workers per meter
DWORD biclrused; // number of colors in the color table used by the bitmap
DWORD biclrimportant; // number of important colors in the bitmap display process
} Bitmapinfoheader;
Color Table
A color table is used to describe the color in a bitmap. It has several table items. Each table item is an rgbquad-type structure and defines a color. The rgbquad structure is defined as follows:
Typedef struct tagrgbquad {
Bytergbblue; // blue brightness (value range: 0-255)
Bytergbgreen; // The brightness of the green color (value range: 0-255)
Bytergbred; // The Red brightness (value range: 0-255)
Bytergbreserved; // reserved, must be 0
} Rgbquad;
The number of rgbquad structure data in the color table is determined by bibitcount:
When bibitcount is 1, 4, and 8, there are 2, 16, and 256 table items respectively;
When bibitcount = 24, there is no color table item.
The bitmap information header and the color table form bitmap information. The bitmapinfo structure is defined as follows:
Typedef struct tagbitmapinfo {
Bitmapinfoheader bmiheader; // Bitmap header
Rgbquad bmicolors [1]; // color table
} Bitmapinfo;
Bitmap data
The bitmap data records each pixel value of the bitmap. The record sequence is from left to right in the scan row, and the rows are from bottom to top. The number of bytes occupied by a pixel value of a bitmap:
When bibitcount = 1, 8 pixels constitute 1 byte;
When bibitcount = 4, 2 pixels constitute 1 byte;
When bibitcount = 8, 1 pixel occupies 1 byte;
When bibitcount = 24, one pixel occupies three bytes;
Windows requires that the number of bytes occupied by a scan row must be
A multiple of 4 (in the unit of long). The insufficient values are filled with 0,
Calculation of the number of bytes occupied by a scanned row:
Datasizeperline = (biwidth * bibitcount + 31)/8;
// The number of bytes that a scan row occupies
Datasizeperline = datasizeperline/4*4; // The number of bytes must be a multiple of 4
Bitmap data size (without compression ):
Datasize = datasizeperline * biheight;
Http://blog.csdn.net/ghost129/article/details/4409565