Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » How to Copy-Construct Sub-Bitmaps

Credits go to Steve++ for helping out!
This thread is locked; no one can reply to it. rss feed Print
How to Copy-Construct Sub-Bitmaps
ADmiral
Member #6,092
August 2005

How should i copy one sub-bitmap to another (unallocated) BITMAP * ?
The documentation only tells me how I create one from a set of coordinates and a parent bitmap.

Jonny Cook
Member #4,055
November 2003

The docmuentation on blit said:

Copies a rectangular area of the source bitmap to the destination bitmap.

The blit function allows you to blit individual parts of a bitmap.

The face of a child can say it all, especially the mouth part of the face.

ADmiral
Member #6,092
August 2005

Thanks for your reply.

The problem is that I don't want to copy a bitmap with all it's pixel data, but a sub-bitmap, which I use for tiles to store their location in my tileset. The new sub-bitmap needs to have all the attributes of the original sub-bitmap.

Edit:

I wrote a little sample program which copies a sub-bitmap using simple, ugly memcpy() and it worked. Thus, hardly surprising, I'm still worried about it. Am I doing the right thing? Will it work in a bigger program with lots of memory allocation / deallocation taking place between those acrobatic actions? If you are interested, here is my testing code:

1#include <allegro.h>
2 
3#include <cstring>
4#include <cstdlib>
5 
6int main(int argc, char *argv[])
7{
8 if (allegro_init()) {
9 std::exit(0);
10 }
11 
12 set_color_depth(32);
13
14 if ( set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 800, 600, 0, 0) ) {
15 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
16 allegro_message("Could not set gfx mode: %s\n", allegro_error);
17 allegro_exit();
18 std::exit(0);
19 }
20 
21 install_keyboard();
22 
23 {
24 try {
25 
26 BITMAP *sbmp1 = create_sub_bitmap(screen, 100, 100, 16, 16);
27 
28 BITMAP *sbmp2 = reinterpret_cast<BITMAP *> (std::malloc(sizeof(BITMAP)));
29 std::memcpy(sbmp2, sbmp1, sizeof(BITMAP));
30
31 destroy_bitmap(sbmp1);
32 
33 while(! key[KEY_ESC]) {
34 
35 for (int x = 0; x < 10; x++)
36 for (int y = 0; y < 10; y++)
37 putpixel(sbmp2, x, y, makecol(200, 0, 0));
38 
39 for (int x = 0; x < 10; x++)
40 for (int y = 0; y < 10; y++)
41 getpixel(sbmp2, x, y);
42 
43 }
44 
45 std::free(sbmp2);
46 
47 } catch (...) {
48 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
49 allegro_message("Program aborted by exception.");
50 allegro_exit();
51 std::exit(0);
52 }
53
54 }
55 
56 allegro_exit();
57
58 return 0;
59}
60END_OF_MAIN();

Any suggestions on a better / more reliable solution?

Arthur Kalliokoski
Second in Command
February 2005
avatar

Can't you just create the new bitmap from the sub bitmap height & width? Then blit the sub bitmap to it. A sub bitmap is pretty much like a normal one, the only difference here would be the creation.

“Throughout history, poverty is the normal condition of man. Advances which permit this norm to be exceeded — here and there, now and then — are the work of an extremely small minority, frequently despised, often condemned, and almost always opposed by all right-thinking people. Whenever this tiny minority is kept from creating, or (as sometimes happens) is driven out of a society, the people then slip back into abject poverty. This is known as "bad luck.”

― Robert A. Heinlein

ADmiral
Member #6,092
August 2005

Of course I could, but the point is that I don't want to. Look at this piece of code:

1// Tile.hpp
2// [...]
3class Tile {
4 // contains the SubBitmaps which make up the tile.
5 // The tileset contains the actual bitmap data.
6 mutable std::vector < SubBitmap * > tile_layers;
7 // [...]
8};
9// [...]
10 
11// Layer.hpp
12// [...]
13class Layer {
14 // If all these Tiles contained real bitmaps,
15 // I couldn't load big levels without several terabytes of RAM
16 Matrix < Tile * > *grid;
17 // [...]
18};
19// [...]

Matrix is pretty self-explanatory: It is my 2-dimensional array class.
SubBitmap is a wrapper class for Allegro's sub-bitmaps. And it really needs a proper copy constructor. Here's the (almost) full SubBitmap class code (almost because I cut out the header comments):

1// SubBitmap.hpp
2#pragma once
3 
4#include "Bitmap.hpp"
5 
6class SubBitmap : public Bitmap {
7 
8private:
9 
10 using Bitmap::load; // Loading would change bitmap type
11 using Bitmap::resize; // Also not allowed for SubBitmap
12 using Bitmap::operator=; // No assignment of SubBitmaps (yet)
13 using Bitmap::operator*=; // No self-stretch for SubBitmaps
14
15 bool self_destruct_sub; // Whether or not to destroy_bitmap the sub-bitmap
16 
17public:
18 
19 SubBitmap (const SubBitmap & ) throw();
20 SubBitmap (const Bitmap & , int, int, int, int) throw();
21 
22 ~SubBitmap () throw();
23};
24 
25// SubBitmap.cpp
26#include "SubBitmap.hpp"
27 
28#include <allegro.h>
29 
30SubBitmap::SubBitmap
31(const SubBitmap &rhs) throw() :
32Bitmap(rhs.get_raw_bmp()), // this is where I need to copy the sub-bitmap, not the bitmap
33self_destruct_sub(false) {
34 Debug::objlog.log
35 ("Constructor: SubBitmap (copy), Orig. ID is ", Debug::Logger::trivial);
36 Debug::objlog << rhs.id() << ".\n";
37}
38 
39SubBitmap::SubBitmap
40(const Bitmap &parent,
41int x, int y,
42int wdt, int hgt) throw() :
43Bitmap(::create_sub_bitmap(parent.get_raw_bmp(), x, y, wdt, hgt)),
44self_destruct_sub(true) {
45 Debug::objlog << Debug::Logger::trivial
46 << "Constructor: SubBitmap (Parent, x=" << x << ", y=" << y << ", wdt="
47 << wdt << ", hgt=" << hgt << ")\n";
48// ::log_progress("%4Constructor: SubBitmap (data)");
49}
50 
51SubBitmap::~SubBitmap () throw() {
52 if (this->self_destruct_sub) ::destroy_bitmap(this->get_raw_bmp());
53 Debug::objlog.log("Destructor: SubBitmap\n", Debug::Logger::trivial);
54// ::log_progress("%4Destructor: SubBitmap");
55}

Don't care about the Bitmap class; It has proven to work perfectly and the only thing you might want to know is that Bitmap::get_raw_bmp() returns the Allegro BITMAP *.
I kept the logging in because I'm proud of it ;D

And I have tested my project using the memcpy() method. It crashes while copy-constructing a Bitmap from a SubBitmap which was copy-constructed from a SubBitmap which was then deleted (before the Bitmap(SubBitmap) c'tor call). Thus, I really need a way to copy the sub-bitmap itself and not the pixels I can access with it.

Additional note: Layer is a map layer which contains Tiles; The vector called layers in Tile class is a vector of the Bitmaps used by a single Tile.

Arthur Kalliokoski
Second in Command
February 2005
avatar

OK, I see what you're doing now. I've been paid a midnight visit by the Allegro "Be Good" Mafia after I accessed the bitmap data directly, so I just use Allegro to read in pixels from standard bmp, pcx, tga, whatever and write them to my own proprietary file formats now. Of course I'm not using Allegro to develop a game per se, more like a handy toolbox to make windows specific games.

“Throughout history, poverty is the normal condition of man. Advances which permit this norm to be exceeded — here and there, now and then — are the work of an extremely small minority, frequently despised, often condemned, and almost always opposed by all right-thinking people. Whenever this tiny minority is kept from creating, or (as sometimes happens) is driven out of a society, the people then slip back into abject poverty. This is known as "bad luck.”

― Robert A. Heinlein

CGamesPlay
Member #2,559
July 2002
avatar

I can't find any way to do this short of memcpy. I think your problem may have been you didn't copy the line data, though I can't be sure.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Steve++
Member #1,816
January 2002

Quote:

Thus, I really need a way to copy the sub-bitmap itself and not the pixels I can access with it.

I'm not quite sure what you mean by this.

Also, I think your use of inheritance here is flawed. Can you really say a sub-bitmap is a bitmap? Sure, you can blit them both, but a bitmap owns a raw bitmap and a sub-bitmap doesn't. The relationship may be better represented by association. A bitmap could be some sort of container that holds a collection of sub-bitmaps. Then it would make sense that when you delete a bitmap, its sub-bitmaps are also deleted.

Think of what you do with bitmaps and what you do with sub-bitmaps. If you don't use them seemlessly interchangably, then there's nothing to be gained by inheritance in this case.

EDIT: If you do use them seemlessly interchangibly, but you agree that their relationship isn't one of inheritance, you could create an abstract class with only pure virtual functions (an interface), then have both classes inherit from that abstract class and implement its interface. But whatever you do, if you're going to put a sub-bitmap container in your bitmap class, don't let it contain objects of the abstract type. That's just asking for a big mess.

ADmiral
Member #6,092
August 2005

The concept problem is one which I have considered negligible, since I didn't want to spend even more time on my Bitmap classes but move forward instead. It works, and the point at which I should have fixed the problem was when I designed the classes. Back then I had read the description of sub-bitmaps in the manual, but I didn't know how they were implemented. If I were to change them, I would go for the abstract base class approach.

Also, this still doesn't answer my question. Maybe Allegro is missing a create_sub_bitmap_copy() function?!
The real problem is: The original sub-bitmap will be destroyed before I use the copy.
A possible workaround: I could have the Tileset class split up it's bitmap into several SubBitmaps or even Tiles, and have other classes request them from the Tileset. It doesn't solve the problem, It would only support Tiles of one size in one tileset, and I would need to rewrite a lot of code, but it saves memory and might run faster (Which isn't really an issue yet, anyway).
A possible solution: As far as I understand it, Allegro dynamically allocates an array of pointers to the start of each pixel row in the parent bitmap which need to be accessed by the sub-bitmaps. This is why memcpy() fails; It contains a pointer to an array which is deleted. But if I just have the copy-constructor assume the original sub-bitmap as a parent bitmap and take a sub-bitmap of the same size out of it, it would correctly build the pointer array, which then still points to the actual bitmap, which is the parent of the original sub-bitmap. I could then destroy the first and work with the second sub-bitmap. Could this work? (I'm going to try it out on the next occasion)

Steve++
Member #1,816
January 2002

I looked into the Allegro source code. If you create a sub-bitmap of a sub-bitmap being the same size, there may be some differences in the BITMAP struct, namely the x_ofs and y_ofs values. Probably some other stuff too. Perhaps you need to store a reference to the parent BITMAP in the SubBitmap class.

Quote:

The concept problem is one which I have considered negligible, since I didn't want to spend even more time on my Bitmap classes but move forward instead.

That could be a mistake. The sooner you fix a problem, the less time it will take to fix it. Put it off now and it will really kick you a$$ later.

ADmiral
Member #6,092
August 2005

I have tried the sub-sub-bitmap method, and it plain works. I don't care about offset values. I just hope there's no pointers to the first sub-bitmap hanging around anywhere in the second sub-bitmap, but blitting can be done and the copy constructor does what it is supposed to do.
I don't know if I should fix those classes; It means reprogramming some parts of the implementation and maybe even the interface (though I hope not), and it's so incredibly boring... since it works now, why change it? A SubBitmap is a Bitmap now, and that's just fine. I'll regret the moment I run into problems... May this point never come until I do better in my next project ;D
Let's consider this done for now. Thanks for the numerous replies... though few of you understood what I was trying to tell... I know I'm no good at explaining problems ;D

Thomas Fjellstrom
Member #476
June 2000
avatar

Steve++, afaik creating a sub bitmap of a sub bitmap actually just creates a sub bitmap of the original bitmap. Sub Bitmaps store the original bitmap, and the creation code uses that instead.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Steve++
Member #1,816
January 2002

Thanks for the cookie!
Good luck with your project. I hope you don't run into any problems with this.

Quote:

Steve++, afaik creating a sub bitmap of a sub bitmap actually just creates a sub bitmap of the original bitmap. Sub Bitmaps store the original bitmap, and the creation code uses that instead.

Yeah, I was wondering if that could be the case. That would mean the line[] array in a sub-bitmap is exactly the same as its host, and the offset values are used in blits, right? If so, it would be a very bad idea to access the line[] array of a sub-bitmap directly.

Thomas Fjellstrom
Member #476
June 2000
avatar

I assume the line[] array is setup as it should be, offset by the position you gave it.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Go to: