Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » get pointer from al_map_rgb()?

This thread is locked; no one can reply to it. rss feed Print
get pointer from al_map_rgb()?
Shadowblitz16
Member #16,818
March 2018

is there a way to get a pointer to the returned ALLEGRO_COLOR with al_map_rgb()?
I have just picked up c++ again and I am making a wrapper library around allegro for fun

#SelectExpand
1col* Gfx::newCol(u8 r, u8 g, u8 b) 2{ 3 return al_map_rgb(r, g, b); 4}

Edgar Reynaldo
Member #8,592
May 2007
avatar

Whoa. Don't do that.

First of all, al_map_rgb returns an ALLEGRO_COLOR on the stack. If you return it's address, you are giving back a dangling pointer because the stack object will go out of scope and die and now you're pointing to a corpse.

However, you can use new to create a new ALLEGRO_COLOR that you will have to delete later.

ALLEGRO_COLOR* newcolor = new ALLEGRO_COLOR(al_map_rgb(r,g,b));
// later
delete newcolor;
newcolor = 0;

bamccaig
Member #7,536
July 2006
avatar

Edit: /beaten

A pointer doesn't allocate memory for the data. Only for the pointer itself. You need to concern yourself with where the data will live. If it is on the stack (as with local variables) then it will no longer "exist" once you return from the function and your pointer will be invalid (subsequent function calls will overwrite the data).

You basically have three viable options here:

  • Allocate the data globally (or statically). This is an easy way to do it, but it has limitations. Firstly, if it's global then any part of the program can access it and change it. Similarly, if it's static, even within the function, then subsequent calls to the same function will still overwrite it (so other parts of the program can still change it), which could surprise you if you still have pointers to other calls you've made and expect them not to change. (This is not recommended, particularly for something like colors)

    #SelectExpand
    1// Globally. 2ALLEGRO_COLOR g_new_col; 3 4ALLEGRO_COLOR * Gfx::newCol(u8 r, u8 g, u8 b) 5{ 6 g_new_col = al_map_rgb(r, g, b); 7 8 return &g_new_col; // Return pointer to global. 9} 10 11// OR: statically 12ALLEGRO_COLOR * Gfx::newCol(u8 r, u8 g, u8 b) 13{ 14 static ALLEGRO_COLOR new_col; 15 16 new_col = al_map_rgb(r, g, b); 17 18 return &new_col; // Return pointer to static. 19}

  • Pass a pointer to memory higher up the call stack. This is a good way to do it. There are really no downsides to it, other than it's a bit more advanced so beginners might be confused by it. Anyone with C or C++ experience should be fine with this though.

    void Gfx::makeCol(u8 r, u8 g, u8 b, ALLEGRO_COLOR * result)
    {
        *result = al_map_rgb(r, g, b);
    }
    
    int main(int argc, char * argv[])
    {
        ALLEGRO_COLOR my_col;
    
        Gfx().makeCol(255, 255, 255, &my_col); // Fills in the value in my_col.
    
        return 0;
    }
    

  • You can allocate memory on the heap. The last option you have is to allocate memory on the heap. This doesn't get automatically cleaned up when your function returns so it will still be valid afterward. The downside to this is that there's a negligible performance hit (so if you were to do this millions of times in a loop that hit might not be negligible), and it requires you to write extra code to clean up after it or else your program will leak memory (essentially you would have allocated memory that you are perhaps no longer using, but that your program cannot reallocate for other purposes).

    In C++ we use operator new to allocate memory on the heap, and operator delete to release it when we're done.

    You can solve the latter problem by using smart pointers (e.g., `boost::shared_ptr<T>` or `std::shared_ptr<T>`) to manage the memory for you. The former problem cannot be solved this way. You'd need to use the previous methods instead.

    #SelectExpand
    1ALLEGRO_COLOR * Gfx::newCol(u8 r, u8 g, u8 b) 2{ 3 return new ALLEGRO_COLOR(al_map_rgb(r, g, b)); 4} 5 6int main(int argc, char * argv[]) 7{ 8 ALLEGRO_COLOR * my_col = Gfx().newCol(255, 255, 255); 9 10 // ... later ... 11 12 delete my_col; // Remember to free the memory! 13 14 return 0; 15}

    Or with smart pointers:

    std::shared_ptr<ALLEGRO_COLOR> Gfx::newColSmart(u8 r, u8 g, u8 b)
    {
        std::shared_ptr<ALLEGRO_COLOR> result(new ALLEGRO_COLOR(al_map_rgb(r, g, b)));
    
        return result;
    }
    
    int main(int argc, char * argv[])
    {
        std::shared_ptr<ALLEGRO_COLOR> my_col(Gfx().newColSmart(255, 255, 255));
    
        return 0;
    } // RAII guarantees that my_col will be cleaned up here by the smart pointer.
    

Your results may vary trying to wrap Allegro with C++. It sounds easy at first, but it's not as easy as you think. Many have tried, and most have failed. :) Avoid globals, and try to design your API to guarantee the order of Allegro calls is valid (e.g., you initialize allegro, install the keyboard, create displays, etc., before loading bitmaps or doing other operations that may fail until you've done the others). I recommend you familiarize yourself with smart pointers and utilize them to help manage the memory. It's a lot safer than managing it yourself.

Append:

It should be noted that colors are relatively small, and that it makes sense to copy them around instead of passing pointers to them. There is a small performance hit also for dereferencing a pointer (first the CPU has to load the value for the pointer variable from RAM into the CPU, and then it has to load the actual data from RAM into the CPU). Again, this hit is negligible to do once, but it can add up if you're doing a lot of it, and in this case the data itself is probably about the same size as the pointer is so you're not really saving anything by passing a pointer instead of the data directly.

Shadowblitz16
Member #16,818
March 2018

EDIT: so what would be best for static calls like so...?

#SelectExpand
1col col1 = Gfx::newCol(0,0,0); 2// col1 gets deleted outside of function

I am trying to keep the library function based but I wanted to use classes as a way to tell what is coming from where.

bamccaig
Member #7,536
July 2006
avatar

Just return the color itself. Colors are small enough that copying them around is negligible, and using pointers gives you no advantages and plenty of disadvantages. If there was a reason to use a pointer for this then Allegro would be returning a pointer to you. :) The only time you'd want to use a pointer for a color is where the semantics require it, which is unlikely to occur with colors. A color is probably either an integer or a float under the hood. At worst, it's a small structure that is probably only between 4 and 16 bytes in size anyway.

Shadowblitz16
Member #16,818
March 2018

my issue is that I want to eventually do indexed color and I would think passing color pointers to images and draw functions would be a good way to do that.

bamccaig
Member #7,536
July 2006
avatar

I'm not even sure that you can do that with Allegro's bitmaps and buffers, but that's over my head. Edgar should be able to answer that... I think that Allegro 5 by default uses textures within your GPU for bitmaps. And when you draw to the screen similarly it gets sent to your GPU. This way you get video acceleration... I think there's no way you could access a pointer to a piece of that data, and even if you could, it wouldn't be an ALLEGRO_COLOR that you were pointing at (odds are the format of the bitmap or texture is independent of that, and likely defined by the graphics library or GPU).

Append:

Indexing apparently means palettes, which last time that I checked Allegro 5 doesn't support. If that's what you intend you might prefer to wrap Allegro 4.4 (which I think does support palettes; if not 4.2 was probably the most stable/popular 4.x there was, and Allegro version there was maybe..).

MikiZX
Member #17,092
June 2019

Possibly Allegro5 Color add-on can be of interest for its name-to-color feature?

https://liballeg.org/a5docs/trunk/color.html#al_color_name
https://github.com/liballeg/allegro5/blob/master/addons/color/color.c

Then I'd second what bamccaig said re: palettes though you can easily simulate those in A5 with an array of ALLEGRO_COLOR values and use it to convert from your calling function (that uses the index) to an ALLEGRO_COLOR value (that is used by Allegro5).

Edgar Reynaldo
Member #8,592
May 2007
avatar

I'm not sure what you want indexed color for, but A4 did this with palettes.

Basically, they are an array of colors which you would pass as a uniform to your shader. Then when you draw, you pass the index, which gets looked up in your uniform. This is with shaders. A4 didn't use shaders though, it used 8-bit palettes or color tables.

What are you using it for?

ZoriaRPG
Member #16,714
July 2017
avatar

Ugh.

I'd go with a global array of *GamePalette[number_of_palettes][palsize] pointers here, if you want to create a simulated/emulated indexed colour palette in Ag5 that you can change from anywhere. Keep it simple.

It's a primary part of your game software that you will be using everywhere, so stop using wrapper classes and all sorts of stuff that obfusticates it. Make it easy to know that you are accessing the palettes.

I'm not even certain that GamePalette need be a declared as a pointer. Can it be an ordinary array of GamePalette[number_of_palettes][palsize], with each index just storing the colour value as int?

Anyway...

#define NUMBER_PALETTES 1
#define PALETTE_SIZE 256
ALLEGRO_COLOR *GamePalette[NUMBER_PALETTES][PALETTE_SIZE];

//in main, first:
memset(GamePalette,0,sizeof(GamePalette));

Then, make functions to build the initial state of the palettes, and and add functions that have a local array of ALLEGRO_COLOR in the form of a single palette to copy a local instance of this type of array to the global one.:

//local function has
ALLEGRO_COLOR *TempPalette[PALETTE_SIZE];
//set up or read colours into the indices of TempPalette, then:
memcopy(&GamePalette[i], &LocalPalette, sizeof(LocalPalette));
//to copy the colour values to your game palette

You can make those functions part of a class/struct, for the sake of organisation, and you can make the *TempPalette a class member if you want, as you stop using it after the class instance is deconstructed.

Go to: