Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » create_bitmap and classes

This thread is locked; no one can reply to it. rss feed Print
create_bitmap and classes
Justin Hamilton
Member #8,269
January 2007

Hello,

I am new to the Allegro library. I recently began putting together my first Tetris clone, and I set up a class for the shapes. One of the (currently public) data members of the class is BITMAP *images[4][2], which holds all possible rotations and a corresponding "eraser" image. I designed a public member function to set up bitmaps for the rotations of the shape. This is so that custom images can be put into the game. When I try to "create" bitmaps for the other elements of the images array to place a roated sprite in, the program crashes.

I do not understand a great deal about memory allocation and other related subjects, but I assume that there this is due to something with the compiler not being happy about a class trying to mess around with memory after the variables were already initialized. Of course, I could be completely wrong here.

Here are sections of the problematic code (again, I am somewhat new to this, so this may be very ugly):

1class shape:{
2public:
3 BITMAP *images[4][2];
4 //...other variables and method...
5 
6 int setup(char *image, char *eraser, int rotations);
7};
8
9int shape::setup(char *image, char *eraser, int rotations)
10{
11 //load the initial images
12 images[0][0] = load_bitmap(image, NULL);
13 images[0][1] = load_bitmap(eraser, NULL);
14 
15 //error checking
16 if (!images[0][0]){
17 allegro_message("Error loading shape.");
18 return 1;
19 }
20 if (!images[0][1]){
21 allegro_message("Error loading eraser.");
22 return 1;
23 }
24 
25 //setup all of the rotated images
26 for (int i = 1; i < rotations; i++){
27 if (i % 2 == 0){
28 images<i>[0] = create_bitmap(images[0][0]->w, images[0][0]->h);
29 images<i>[1] = create_bitmap(images[0][1]->w, images[0][1]->h);
30 }
31 else{
32 images<i>[0] = create_bitmap(images[0][0]->h, images[0][0]->w);
33 images<i>[1] = create_bitmap(images[0][1]->h, images[0][1]->w);
34 }
35
36 clear_bitmap(images<i>[0]);
37 rotate_sprite(images<i>[0], images[0][0], 0, 0, itofix(64 * i));
38 
39 clear_bitmap(images<i>[1]);
40 rotate_sprite(images<i>[1], images[0][1], 0, 0, itofix(64 * i));
41 }//end if
42 return 0;
43}//end setup

Are there any suggestions for keeping this sort of function as part of the class, or should I just setup another function outside of the class to take a shape object as a parameter and do all of the bitmap functions there?

Thank you for any advice, criticism, etc.

miran
Member #2,407
June 2002

Rather than answer your question, I'd more like to question your design concept. Why exactly do you need this "eraser" image? I have a feeling you aren't familiar with the concept of double buffering.

And why don't you simply rotate the images as needed during runtime? Since the rotation angles are multiples of 90 degrees, there will be almost no performance hit.

As for the actual code you posted, I can see only one problem. In the rotation for loop there are no curly braces around the if/else bodies. (does that even compile?)

Also when you do images[0][0]->w, I'd rather put some brackets in there to make sure it's obvious what exactly you are dereferencing, but I don't think that's actually a problem in your code.

Another thing you might want to do is initialize all pointers to 0 somewhere in the beginning. Just in case...

Quote:

Thank you for any advice, criticism, etc.

No need to give thanks in advance. When you're satisfied with the answers, just send some money. ;D

--
sig used to be here

Justin Hamilton
Member #8,269
January 2007

miran said:

I have a feeling you aren't familiar with the concept of double buffering.

Not terribly so, no :) I mean, I use a buffer to draw to the screen, but I don't understand a lot of the techniques related to it.

miran said:

And why don't you simply rotate the images as needed during runtime? Since the rotation angles are multiples of 90 degrees, there will be almost no performance hit.

Hmm, I could do that. I was just under the impression that it was more efficient to assign the rotations to bitmaps.

miran said:

As for the actual code you posted, I can see only one problem. In the rotation for loop there are no curly braces around the if/else bodies. (does that even compile?)

You're right, that wouldn't compile. I actually cleaned it up a little bit when copying over from my IDE, and ended up forgetting to brace the code together. I'll edit my original post, thank you.

miran said:

Another thing you might want to do is initialize all pointers to 0 somewhere in the beginning. Just in case...

Good idea, thanks.

Tobias Dammers
Member #2,604
August 2002
avatar

Quote:

Not terribly so, no I mean, I use a buffer to draw to the screen, but I don't understand a lot of the techniques related to it.

OK, then make sure you are familiar with the concept; it's one of the very basic techniques you need for game development.
The idea goes something like this. Say you want to display something on screen; the most straightforward way is to just draw it to the screen bitmap directly. This is all fine and dandy, but you get into trouble when you need to remove things from the screen, or things overlap - because the user can see everything you draw and erase.
The solution is to render your entire scene not directly to screen, but to a memory buffer (just an ordinary memory bitmap with the exact same size as the screen). When drawing is complete, you blit() the entire buffer to the screen in one go, overwriting everything that was there before. The process of scene composition (including the initial buffer clear) is hidden from the user, and there won't be any flicker.

Quote:

I set up a class for the shapes. One of the (currently public) data members of the class is BITMAP *images[4][2], which holds all possible rotations and a corresponding "eraser" image.

That doesn't sound like good design to me.
Each shape should have 4 4x4 array of ints or bools, one for each possible rotation. You should draw the actual blocks based on that array at run-time, that is, iterate through the array and draw a block where it contains a "1" or "true". The bitmaps for the blocks should be stored somewhere else.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

Kris Asick
Member #1,424
July 2001

I'm surprised you're not getting a compiler error for failing to return a value at the end of the routine.

But, your problem has nothing to do with the way you're doing it, and everything to do with the fact that it's a constructor, which means if that constructor is run before you've initialized Allegro, bad things will happen. (IE: Crashes.)

Calling Allegro functions before Allegro is initialized is never a good thing. ;)

And yes, doing all the rotations ahead of time would be faster, but uses more memory. A lot of speed considerations are like that, where the faster you want something the more memory you need to use.

And double buffering is an important thing to learn. I highly recommend you do so.

--- Kris Asick (Gemini)
--- http://www.pixelships.com

--- Kris Asick (Gemini)
--- http://www.pixelships.com

Tobias Dammers
Member #2,604
August 2002
avatar

Quote:

And yes, doing all the rotations ahead of time would be faster, but uses more memory.

This is tetris we're talking about here.
The amount of data here, if we use 4x4 grids of 32-bit ints (very inefficient, we could cram all that information into a single 16-bit int), each rotation takes up 7 * 4 * 4 * 4 bytes = 224 bytes. So the pre-calculation will cost us the immense amount of 672 bytes.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

HoHo
Member #4,534
April 2004
avatar

Actually he caches the rotated bitmaps, not the shapes themselves. I'm not sure how big the bitmaps are but I don't think they would take several megs of RAM.

Also I don't think that kind of optimizations are really needed in Tetris. Having a single variable showing the rotation in allegro degrees (0, 64, 128, 192) and using rotate_sprite everywhere would probably be much easier.

__________
In theory, there is no difference between theory and practice. But, in practice, there is - Jan L.A. van de Snepscheut
MMORPG's...Many Men Online Role Playing Girls - Radagar
"Is Java REALLY slower? Does STL really bloat your exes? Find out with your friendly host, HoHo, and his benchmarking machine!" - Jakub Wasilewski

Kikaru
Member #7,616
August 2006
avatar

I don't like rotate_sprite for extremely simple rotations, but that's just me. :P

Kris Asick
Member #1,424
July 2001

Tobias Dammers: I know that. 672 bytes in your example is still more memory, isn't it? ;)

--- Kris Asick (Gemini)
--- http://www.pixelships.com

--- Kris Asick (Gemini)
--- http://www.pixelships.com

Justin Hamilton
Member #8,269
January 2007

Tobias Dammers said:

OK, then make sure you are familiar with the concept; it's one of the very basic techniques you need for game development.
The idea goes something like this. Say you want to display something on screen; the most straightforward way is to just draw it to the screen bitmap directly. This is all fine and dandy, but you get into trouble when you need to remove things from the screen, or things overlap - because the user can see everything you draw and erase.
The solution is to render your entire scene not directly to screen, but to a memory buffer (just an ordinary memory bitmap with the exact same size as the screen). When drawing is complete, you blit() the entire buffer to the screen in one go, overwriting everything that was there before. The process of scene composition (including the initial buffer clear) is hidden from the user, and there won't be any flicker.

This much I understand. The problem that I am having with that is keeping the "resting" blocks on the screen, but being able to erase the active block, which is why I was using a corresponding "eraser" image with exactly the same dimensions as the active block.

Would it be more effective to draw inactive blocks on the buffer, blit the buffer to the screen (erasing the active), and then blit the active block to the screen instead of the buffer?

Thank you for the replies, everyone.

Kikaru
Member #7,616
August 2006
avatar

Clear the buffer, and draw all of the blocks, active and inactive alike, to the buffer before it goes to the screen. :)

std::vector may be of some use to you in this project. :)

Go to: