Check if the mouse is over irregular shapes
shadyvillian

I'm trying to fancy up my ui in one of my programs and wanted to use other shapes than just squares and rectangles. But I'm having problems with the code crashing. Does this look like a good way to do it?

#SelectExpand
1void Framework_SelectableButton::RegisterIrregularShape() 2{ 3 IrregularShape = true; 4 vector<vector<ALLEGRO_COLOR>> Pixels(al_get_bitmap_height(UpImage), vector<ALLEGRO_COLOR>(al_get_bitmap_width(UpImage))); 5 al_lock_bitmap(UpImage, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READONLY); 6 7 for(int i = 0; i < al_get_bitmap_height(UpImage); i++) 8 { 9 for(int j = 0; j < al_get_bitmap_width(UpImage); j++) 10 { 11 Pixels[i][j] = al_get_pixel(UpImage, i, j); 12 } 13 } 14 15 al_unlock_bitmap(UpImage); 16 ImagePixels = Pixels; 17}

#SelectExpand
1void Framework_SelectableButton::IsMouseOver(int MouseX, int MouseY) 2{ 3 if(IrregularShape == false) 4 { 5 if((MouseX >= x1) && (MouseX <= x2) && (MouseY >= y1) && (MouseY <= y2)) 6 { 7 Over = true; 8 } 9 10 else 11 { 12 Over = false; 13 } 14 } 15 16 else if(IrregularShape == true) 17 { 18 if((MouseX >= x1) && (MouseX <= x2) && (MouseY >= y1) && (MouseY <= y2)) //check if the mouse is over the image at all 19 { 20 unsigned char r, g, b, alpha; 21 al_unmap_rgba(ImagePixels[MouseX-x1][MouseY-y1], &r, &g, &b, &alpha); 22 23 if(alpha == 0) 24 { 25 Over = false; 26 } 27 28 else // if the pixel that the mouse is over isnt completly transparent then over is true 29 { 30 Over = true; 31 } 32 } 33 34 else 35 { 36 Over = false; 37 } 38 } 39}

Heres

EDIT: I've attached what one of the images look like. Its pretty much looks like a quarter part of an o.

Arthur Kalliokoski

It seems to me that the active area for a mouse is still rectangular even if the widget is oval or whatever. It'd be rather hard to notice this unless your OCD or something.

jmasterx

It probably crashes because:

    for(int i = 0; i < al_get_bitmap_height(UpImage); i++)
    {
        for(int j = 0; j < al_get_bitmap_width(UpImage); j++)
        {
            Pixels[i][j] = al_get_pixel(UpImage, i, j);
        }
    }

should probably be:

    for(int j = 0; j < al_get_bitmap_height(UpImage); j++)
    {
        for(int i = 0; i < al_get_bitmap_width(UpImage); i++)
        {
            Pixels[i][j] = al_get_pixel(UpImage, i, j);
        }
    }

But really, why store every pixel?

Just translate the mouse so that it is relative to the bitmap eg: if bitmap is at 50,50 subtract 50,50 from mouse position.
Then do bounds check on your mouse so mouseX >= 0 && mouseX < width ... same for height. Then just make 1 call to al_get_pixel(mouseX,mouseY).

But if you want to do it your way, remember c++ is row major so [width][height]

shadyvillian

Yeah thats what I figured was causing the crash. I just wanted to store all the pixels because I thought that if the mouse is moved alot over the image that all the al_get_pixel calls would cause lag.

EDIT: The code seems to work fine. Its pixel perfect precision. But after I click on one button and then click on another on it crashes on al_unmap_rgba(ImagePixels[x][y], &r, &g, &b, &alpha);

EDIT2: Changing the code to this stops the crashes

            unsigned char r, g, b, alpha;
            al_unmap_rgba(al_get_pixel(UpImage, MouseX-x1, MouseY-y1), &r, &g, &b, &alpha);
            Over = true;

            if(alpha == 0) // if the pixel that the mouse is over is completly transparent then over is false
            {
                Over = false;
            }

Will locking the bitmap in this situation increase performance?(between the unmap_rgba)

jmasterx

I don't think so. I think the cost of locking it so often would outweigh the performance boost.

Trent Gamblin

Locking would probably help unless you're only doing a few pixels. Just make sure to only lock the region you need if it's not the whole bitmap. Sorry, I didn't really read your code so I don't know what's best for you, just that locking helps if you're accessing more than a few pixels and they're all adjacent.

shadyvillian

Well al_get_pixel is called everytime an allegro_event_mouse_axes event occurs and the mouse is over the bitmap.

Trent Gamblin

If it's just called once then it doesn't matter if you lock the bitmap.

The way it works is, al_get_pixel will lock a 1x1 pixel region of the bitmap for you if the bitmap isn't locked already. Then it will unlock it.

If it is already locked, it just reads from the lock data and doesn't unlock the bitmap. If you get a pixel outside of the locked region it will return 0s for the pixel data.

So for 1 pixel the behaviour is identical. For more than 1 pixel you can avoid locking operatings in al_get_pixel by locking yourself which will be faster, because locking is slow.

Thread #608905. Printed from Allegro.cc