c++performanceallegroallegro5

ALLEGRO5 change pixel color of text based on background


First attempt:

void Tower::_DrawHealthBarText(std::string text, int x, int y, ALLEGRO_FONT* font)
{
    al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
    ALLEGRO_BITMAP* bmp = al_create_bitmap(196, 17);
    int bmpIndex[196][17] = { };

    al_set_target_bitmap(bmp);
    al_draw_rectangle(5, 20, 200, 20 + 15, al_map_rgb(255, 255, 255), 1.0f);
    al_draw_filled_rectangle(5, 20, 5 - 195.0f + ((195.0f / _player->playerMaxHealth) * _player->playerHealth) + 195.0f, 20 + 15, al_map_rgb(255, 255, 255));
    al_draw_filled_rectangle(4, 19, 201, 20 + 15 + 1, al_map_rgb(0, 0, 0));

    for (int x = 0; x < 196; x++)
    {
        for (int y = 0; y < 17; y++)
        {
            unsigned char r, g, b;
            al_unmap_rgb(al_get_pixel(bmp, x, y), &r, &g, &b);
            if ((r + g + b) / 3 == 255)
            {
                bmpIndex[x][y] = 0;
            }
            else if ((r + g + b) / 3 == 0)
            {
                bmpIndex[x][y] = 1;
            }
        }
    }
    // Clear to transparent background
    al_clear_to_color(rgba(0, 0, 0, 0));

    al_draw_text(getFont(12), al_map_rgb(255, 255, 255), 0, 0, 0, text.c_str());

    al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
    al_set_target_backbuffer(g_Window);
    al_draw_bitmap(bmp, x, y, 0);

    al_destroy_bitmap(bmp);
}

Second attempt:

int bmpIndex[196][17] = { };
void Tower::_DrawHealthBarText(std::string text, int _x, int _y, ALLEGRO_FONT* font)
{
    al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
    al_hold_bitmap_drawing(true);
    for (int x = 0; x < 196; x++)
    {
        for (int y = 0; y < 17; y++)
        {
            unsigned char r, g, b;
            al_unmap_rgb(al_get_pixel(al_get_backbuffer(g_Window), _x + x, _y + y), &r, &g, &b);
            if ((r + g + b) / 3 == 255)
            {
                bmpIndex[x][y] = 0;
            }
            else if ((r + g + b) / 3 == 0)
            {
                bmpIndex[x][y] = 1;
            }
        }
    }
    al_hold_bitmap_drawing(false);
    al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
}

I've googled and tried my own code as you can see above... However using this multi-leveled loop is very slow. My thought was to use a shader but I haven't the slightest clue how to use them yet.

I want the text to change color based off the background of where the text is located...

Basically it's a health bar and I want to show the text ontop of the health bar and change colors based on the background... If the background is black, the corresponding pixels are changed to white. Subsequently if the background pixels are white, the pixels of the text change to black.

like this: photoshop render concept

MY FIX:

        // Hacky bullshit
        _DrawText(std::to_string((int)_player->playerHealth), 5 + ((195.0f - al_get_text_width(getFont(12), std::to_string((int)_player->playerHealth).c_str())) / 2), py + 18, getFont(12), al_map_rgb(255, 0, 0), 0);
        al_draw_rectangle(px + 5, py + 20, px + 200, py + 20 + 15, al_map_rgb(255, 255, 255), 1.0f);
        ALLEGRO_BITMAP* bmp = al_create_bitmap(200, 20);
        {
            al_set_target_bitmap(bmp);
            al_draw_filled_rectangle(5, 0, 5 + ((195.0f / _player->playerMaxHealth) * _player->playerHealth), 15, al_map_rgb(255, 255, 255));
            _DrawText(std::to_string((int)_player->playerHealth), 5 + ((195.0f - al_get_text_width(getFont(12), std::to_string((int)_player->playerHealth).c_str())) / 2), -2, getFont(12), al_map_rgb(155, 0, 0), 0);
            al_set_target_backbuffer(g_Window);
            al_draw_bitmap_region(bmp, 0, 0, 5 + ((195.0f / _player->playerMaxHealth) * _player->playerHealth), 20, px, py + 20, 0);
        }
        al_destroy_bitmap(bmp);

Solution

  • The simplest way is to use the clipping rectangle, a common feature of most graphics APIs. When you set the clipping rectangle, nothing can be drawn outside of it.

    int x = 0, y = 0;
    int bar_width = 196, bar_height = 17;
    int bar_position = static_cast<int>(
        bar_width * _player->playerHealth / _player->playerMaxHealth);
    
    // Set the clipping rectangle so text only appears on the left
    // side of the bar (which is white),
    al_set_clipping_rectangle(x, y, bar_position, bar_height);
    
    // Draw black text.
    al_draw_text(getFont(12), al_map_rgb(0, 0, 0), x, y, 0, text.c_str());
    
    // Set the clipping rectangle so text only appears on the right
    // side of the bar (which is black).
    al_set_clipping_rectangle(x + bar_position, y, bar_width - bar_position, bar_height);
    
    // Draw white text in the same position as the black text.
    al_draw_text(getFont(12), al_map_rgb(255, 255, 255), x, y, 0, text.c_str());
    
    // Reset clipping rectangle so you can draw graphics as usual again.
    al_reset_clipping_rectangle();