[AD] Proposal: Wordwrapped justified text drawing

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Hi there!

I just started out to use Allegro 5 for my little game project. Since I need a 
lot of text output, I was happy to see a function like al_draw_justified_ustr 
and used it. But i was very unhappy with the results. This function actually 
only justifies to one line, ignores any newline character and does no word wrap. 



So I began to hack my own little function doing word wrap on the given 
ALLEGRO_USTR and uses the al_draw_justified_ustr function to render the text. 
I'll send you my piece of code (including some demo) and perhaps you guys can 
include it into the font addon. At the moment there are still some issues with 
my code.
1) it could be faster, because I'll split the string already into words and 
calling al_draw_justified_ustr does the same again
2) there's only a ALLEGRO_USTR function
3) a new ALLEGRO_USTR is allocated every call to buffer the lines (malloc 
pressure if it's used often for texts...)

Please tell me, what you think :)

And writing here the first time, I've to thank you guys for doing a great job 
with Allegro.

(btw. I've another proposal for the audio codec addon but I'll write another 
mail for that soon)

Greetings,
Sebastian

 ----
Minister: What is more dangerous than a locked room full of angry Narns ?
Vir: I don't know, what is more dangerous than a locked room full of angry Narns 
?
Minister: One angry Narn with a key !


#include <stdio.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <allegro5/allegro_primitives.h>

void al_draw_wordwrapped_ustr(const ALLEGRO_FONT* font,
        ALLEGRO_COLOR color, float x1, float y1,
        float x2, float y2, float hdiff, float vdiff, int flags,
        const ALLEGRO_USTR* ustr)
{
    const char* whitespace = " \t";
    ALLEGRO_USTR_INFO word_info;
    ALLEGRO_USTR* word;
    ALLEGRO_USTR_INFO line_info;
    ALLEGRO_USTR* line;
    ALLEGRO_USTR* line_buffer;
    float y;
    float line_height, line_advance;
    int space_advance;
    float text_width;
    int line_width, word_width;
    int pos1, pos2, pos3, pos4;

    /* init some values for "quick" calculation */
    line_buffer = al_ustr_new("");
    text_width = x2 - x1;
    line_height = al_get_font_line_height(font);
    line_advance = vdiff + line_height;
    space_advance = al_get_text_width(font, " ");
    y = y1;

    /* iterate on lines */
    pos1 = 0;
    for (;;) {
        pos2 = al_ustr_find_chr(ustr, pos1, '\n');
        if (pos2 == -1) {
            line = al_ref_ustr(&line_info, ustr, pos1, al_ustr_size(ustr));
        } else {
            line = al_ref_ustr(&line_info, ustr, pos1, pos2);
        }

        /* iterate on words */
        pos3 = 0;
        line_width = 0;
        for (;;) {
            pos3 = al_ustr_find_cset_cstr(line, pos3, whitespace);
            if (pos3 == -1)
                break;
            pos4 = al_ustr_find_set_cstr(line, pos3, whitespace);
            if (pos4 == -1)
                pos4 = al_ustr_size(line);

            word = al_ref_ustr(&word_info, line, pos3, pos4);
            word_width = al_get_ustr_width(font, word);
            line_width += word_width;
            line_width += space_advance;

            if (line_width < text_width) {
                if (al_ustr_length(line_buffer) > 0)
                    al_ustr_append_chr(line_buffer, ' ');
                al_ustr_append(line_buffer, word);
            } else {
                /* we hit against the border! draw text justified... */
                al_draw_justified_ustr(font, color, x1, x2, y, hdiff, flags, line_buffer);
                y += line_advance;
                if (y + line_height > y2) {
                    al_ustr_free(line_buffer);
                    return;
                }
                line_width = word_width;
                al_ustr_assign(line_buffer, word);
            }

            pos3 = pos4;
        }

        /* draw left over text */
        al_draw_justified_ustr(font, color, x1, x2, y, hdiff, flags, line_buffer);
        y += line_advance;
        if (y + line_height > y2)
            break;
        al_ustr_assign_cstr(line_buffer, "");

        /* is there any line left?! If so begin searching next character to last \n */
        if (pos2 == -1) {
            break;
        } else {
            pos1 = pos2 + 1;
        }
    }

    al_ustr_free(line_buffer);
}

int main(int argc, char* argv[])
{
    ALLEGRO_DISPLAY* display = NULL;
    ALLEGRO_EVENT_QUEUE* event_queue = NULL;
    ALLEGRO_FONT* font = NULL;

    if (!al_init()) {
        printf("al_init() failed!\n");
        return -1;
    }

    al_init_font_addon();
    al_init_ttf_addon();
    al_init_primitives_addon();

    al_set_new_display_flags(ALLEGRO_WINDOWED | ALLEGRO_OPENGL);
    display = al_create_display(800, 600);
    if (!display) {
        printf("al_create_display() failed!\n");
        return -1;
    }

    event_queue = al_create_event_queue();
    al_register_event_source(event_queue, al_get_display_event_source(display));

    font = al_load_font("data/DejaVuSans.ttf", -30, 0);
    if (!font) {
        printf("Error! Font not loaded!\n");
        return -1;
    }

    ALLEGRO_USTR* text = al_ustr_new("Multiline wordwrapped text\n\nAnd now there's a really long text, that should automatically wrap around and smoothly display within the given borders. I hope some of you guys might find that function useful. There's a little overhead in calling al_draw_justified_ustr(), because the words there are counted again (which is already done at al_draw_wordwrapped_ustr() ) but the code is more readable. So have fun with that piece of code...I'd like to see that in Allegro 5.0\n--steini\nThis text should not appear...because we hit the bottom *g");



    while (1) {
        ALLEGRO_EVENT event;
        ALLEGRO_TIMEOUT timeout;

        al_init_timeout(&timeout, 0.1f);
        bool got_event = al_wait_for_event_until(event_queue, &event, &timeout);

        if (got_event && event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) break;

        al_clear_to_color(al_map_rgb(128, 32, 64));
        al_draw_filled_rectangle(50, 80, 750, 520, al_map_rgb(64, 64, 64));
        al_draw_wordwrapped_ustr(font, al_map_rgb(255, 255, 255), 50, 80, 750, 520, 200, 5, ALLEGRO_ALIGN_LEFT, text);
        al_flip_display();
    }

    al_destroy_display(display);
    al_destroy_event_queue(event_queue);

    al_shutdown_font_addon();

    return 0;
}



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/