Focus speed
You should take some actions to make the program run as much as possible.
First, locking a surface is not the fastest, so you need to try to lock the smallest rectangular area on the surface that you want to operate. For many operations, including simple pixel rendering demo programs, you should lock the smallest rectangular area.
Second, in the 640x480X16 mode, the spacing is always 1280 bytes. You should try to consider whether there is a better way to express it. Of course, you cannot change the size of 1280 bytes, but we can optimize the formula and use displacement to replace multiplication is a consistent acceleration method. Our previous formula is as follows:
Buffer [y * npitch + x] = color; |
If we know that the npitch will be 640 (because the npitch is of the ushort type, not a byte), we can accelerate it (we knew it was 640 ). 640 is not an ideal displacement number, but 512 is the 9 power of 2 and 128 is the 7 power of 2. You have guessed that 512 + 128 = 640. ^_^ Is that great? We can replace the previous formula with the following faster formula:
Buffer [(Y <9) + (Y <7) + x] = color; |
Most of the solutions are broken down into several power pairs of 2, and some need a bit of brains, such as 800x600 (512 + 256 + 32 = 800! Displacement is the fastest operator in our application.
Finally, if you want to use two functions-one for multiplication and the other for displacement, you need to put the comparison judgment outside the loop. It cannot be like the following:
For (x = 0; x <1000; X ++) { If (npitch = 640) Plotpixelfast16 (); Else Plotpixel16 (); } |
The judgment part has exhausted your advantages. You should do this:
If (npitch = 640) { For (x = 0; x <1000; X ++) Plotpixelfast16 (parameters ); } Else { For (x = 0; x <1000; X ++) Plotpixel16 (parameters ); } |
Is it meaningful? Whenever a large loop is used, you should try to put the judgment outside the loop, and there is no need to make the same comparison judgment thousands of times. Similarly, if you want to draw a pixel to form a regular pattern, such as a horizontal or vertical line, or even a diagonal line, you do not need to repeat the pixel position every time. Let's look at the following example and draw a straight line of any color:
For (x = 0; x <640; X ++) Plotpixel16 (x, 50, color, buffer, pitch ); |
Each time the function repeats the calculation of the correct row, you can specify the row at a time. The following is a quicker approach:
// Get the address of the line Ushort * temp = & buffer [50 * pitch];// Plot the pixels For (x = 0; x <640; X ++) { * Temp = color; Temp ++; } |
You may think it is of little significance to save such a little time, but it is of great significance when you perform a million cycles. Game programmers always try to speed up the game.
Looking at the previous articles, we have been laying the groundwork for a long time. Now we know how to draw pixels. Let's see what we can do now.
Fade out operation
The most common screen operations in the game are to fade out in black or fade in from black. The two methods share the same mechanism: You simply draw your image and then apply for some screen conversions to change the brightness of the image. For fade-out, you reduce the brightness from 100% -- 0%; for fade-in, you increase the brightness from 0% -- 100%. If you work in the palette mode, this is easy to do, you just need to change the color of your palette. If you work in RGB mode, you have to consider some other methods.
Now I will talk about some better ways to fade in and out the screen. You can use direct3d, which supports Alpha mixing. First, you can set the texture of each frame and then set the transparent layer. Alternatively, you can use the color/gamma control of DirectDraw in an easier way. However, if you only want to fade in or out a part of the screen, or fade in or out a non-black color, and you are not a master of direct3d-I am not myself! -- The manual for specific practices is in front of you. Now, the most basic thing you need to do is read every pixel you need to control, and then divide it into red, green, and blue, then, multiply the three values by the fade-out or fade-in level, then combine the RGB values, and write the new color values back to the buffer zone. Sounds complicated? Don't be afraid. It's not as bad as you think. Take a look at the Demo code below. It demonstrates the fade-out effect of the 200 × 200 area in the upper left corner of the screen, which is a 16-bit color depth and 565 format:
Void applyfade16_565 (float PCT, ushort * buffer, int pitch) { Int X, Y; Uchar R, G, B; Ushort color;For (y = 0; y <200; y ++) { For (x = 0; x <200; X ++) { // First, get the pixel Color = buffer [y * Pitch + x]; // Now extract red, green, and blue R = (color & 0xf800)> 11; G = (color & 0x0730)> 5; B = (color & 0x001f ); // Apply the fade R = (uchar) (float) R * PCT ); G = (uchar) (float) g * PCT ); B = (uchar) (float) B * PCT ); // Write the new color back to the buffer Buffer [y * Pitch + x] = rgb_16bit565 (R, G, B ); } } } |
Currently, this function has many security issues. First, the formula for calculating the position of a pixel is included not only in a loop, but also twice! You can calculate it only once in the entire program, but now the code calculates it 80000 times! The following is what you should do: at the beginning of the function, you should declare a ushort * variable to make it equal to buffer (such as ushort * temp = buffer ;). In the internal loop, add a pointer to get the next pixel. In the External Loop, add a row (temp + = jump;) so that it can be transferred to the next row. The modified code is as follows:
Void applyfade16_565 (float PCT, ushort * buffer, int pitch) { Int X, Y; Uchar R, G, B; Ushort color; Ushort * temp = buffer; Int jump = fig-200;For (y = 0; y <200; y ++) { For (x = 0; x <200; X ++, temp ++) // move pointer to next pixel each time { // First, get the pixel Color = * temp; // Now extract red, green, and blue R = (color & 0xf800)> 11; G = (color & 0x0730)> 5; B = (color & 0x001f ); // Apply the fade R = (uchar) (float) R * PCT ); G = (uchar) (float) g * PCT ); B = (uchar) (float) B * PCT ); // Write the new color back to the buffer * Temp = rgb_16bit565 (R, G, B ); } // Move pointer to beginning of next line Temp + = jump; } } |
That's better! The jump value is of the ushort type and indicates the value starting from the end of the width of 200 pixels (200 pixels are not full) to the next row. However, there is no higher speed for floating point operations and color extraction/restoration. There should be a way to look at this:
Ushort clut [1, 65536] [20]; |
If you ask a DOS programmer to put such a large array into his program, he may cry in pain, or even die on the spot, at least to accelerate the natural death. But in windows, if you need to do so, you will not have any trouble. Because you have the available memory of the entire system. Is it a wonderful thing to replace the entire inner loop with the following line?
* Temp = clut [* temp] [Index]; |
This is faster! ^_^ You can pass an integer between 0--100 to replace the floating point number and pass it to the function. If it is 100, there is no need to fade out the operation, so "nothing to do" is returned. If it is 0, it is better to use the zeromemory () function to process all the work. In addition, divide the passed number by 5 as the second subscript of the array.
If you are curious about the dimension of the query table, I will tell you that 65536 is the 16 power of 2, so in 16-Bit mode, there are 65536 colors. Since our color values are unsigned values and their range is from 0--65535, we can use 20 as the fade-out increment value. Considering the memory, I think it is quite appropriate.
For the 24-bit and 32-bit modes, you obviously cannot directly query the table by color. Because the array is too large, you only need to use three smaller Arrays:
Uchar red [256]; Uchar green [2, 256]; Uchar blue [2, 256]; |
Then, every time you read a pixel, you will put the color values it breaks down into the corresponding array to form your own query table. After the changes, they will be combined, obtain the RGB color value. There are many ways to optimize the program. The best way is to continuously test which program is best suited to your purpose, sum up your experience, and remember it. Next I will briefly introduce other conversions you may need.
Transparent operation
If you overwrite a transparent image to a non-transparent image, you cannot use a color query table because it requires a total of 65536 query tables, A common computer needs GB of memory to handle this giant thing. So you have to calculate every pixel. I will give you a basic idea. Assume that you want to use image a to overwrite image B, and the transparent percentage of image a is pct. This is a floating point number between 0 and 1. When it is 0, it is completely invisible, when the value is 1, it is completely visible. So let's call the pixel of image a pixela. Correspondingly, the pixel of Image B is called pixelb. You will apply the following formula:
Color = (pixela * PCT) + (pixelb * (1-pct )); |
Basically, this is the average value of two pixel colors. Therefore, you can see that each pixel has 6 floating-point multiplication operations. You can use small query tables to reduce your workload. You really should try it!
Another thing you might want to do is to create a partially transparent solid color window. That effect can be achieved by querying a table with a color. For "Earth people", I only need to provide blue for the colors that may appear on the screen. In fact, I used the query table. I will tell you what I actually mean:
Void init_clut (void) { Int X, Y, bright; Uchar R, G, B;// Calculate textbox transparency clut For (x = 0; x <65536; X ++) { // Transform RGB data If (color_depth = 15) { R = (uchar) (X & 0x7c00)> 10 ); G = (uchar) (X & 0x03e0)> 5 ); B = (uchar) (X & 0x001f ); } Else // color_depth must be 16 { R = (uchar) (X & 0xf800)> 11 ); G = (uchar) (X & 0x07e0)> 6); // shifting 6 bits instead of 5 to put green B = (uchar) (X & 0x001f); // on a 0-31 scale instead of 0-63 } // Find brightness as a weighted average Y = (INT) R + (INT) g + (INT) B; Bright = (INT) (float) R * (float) r/(float) y) + (float) g * (float) g/(float) y) + (float) B * (float) B/(float) y) +. 5f ); // Write clut entry as 1 + one half of brightness Clut [x] = (ushort) (1 + (bright> 1 )); } } |
This code is from "Earth man" and creates a text box with a query table. For the sake of security, type modification is used everywhere. This code can be faster, but I didn't seriously optimize it because I only called it once at the beginning of the game. First, the red, green, and blue brightness values are extracted. Because it is in 16-Bit mode, we use a color_depth variable to check whether the display card is in the 555 or 565 format. Then, the pixel brightness is calculated using the following formula:
Y = R + G + B; Brightness = r * (R/y) + G * (G/y) + B * (B/y ); |
This is an ideal average value. I'm not sure if the color brightness value is correct, but it looks logical and works well. At the end of the formula, I added a. 5, because when you change the floating point number to an integer, the fractional part is removed and. 5 is added to make it rounded up. Finally, I divide the brightness by 2 and Add 1. This will not make the text box too bright, and Add 1 to make the text box not all black. Since the low position of the 16-Bit mode is blue, I can set the color to blue without using macros. Do you understand? Finally, I will show you how to create a text box:
Int text_box (ushort * PTR, int pitch, lprect box) { Int X, Y, jump; Rect ibox;// Leave room for the border Setrect (& ibox, box-> left + 3, box-> top + 3, box-> right-3, box-> bottom-3 ); // Update surface pointer and Jump Distance PTR + = (ibox. Top * Pitch + ibox. Left ); Jump = pitch-(ibox. Right-ibox. Left ); // Use clut to apply transparency For (y = ibox. Top; y <ibox. Bottom; y ++) { For (x = ibox. Left; x <ibox. Right; X ++, PTR ++) * PTR = clut [* PTR]; PTR + = jump; } Return (true ); } |
This is a query table. It looks more like the code for the fade-out operation, that is, the control value of the query table is different from the previous one. Here, a calculation is used instead of 20. By the way, a statement for querying a table is as follows:
Summary
This article serves pixel-based graphics. In the next chapter, we will learn about bitmap. Believe it or not, bitmap is much easier to use than pixels, and you will know it later. The next article will be the last chapter to learn the basics of DirectX. After that, we will compile an RPG Game. You will know the details.