AllegroGL: fontconv.c Source File

- Main Page
- Modules
- Data Structures
- Files
- Related Pages

- File List
- Globals

# fontconv.c

Go to the documentation of this file.

    00001 /* This code is (C) AllegroGL contributors, and double licensed under
    00002  * the GPL and zlib licenses. See gpl.txt or zlib.txt for details.
    00003  */
    00013 #include <math.h>
    00014 #include <string.h>
    00015 #include <stdio.h>
    00016
    00017 #include <allegro.h>
    00018 #include <allegro/internal/aintern.h>
    00019
    00020 #include "alleggl.h"
    00021 #include "allglint.h"
    00022
    00023 #ifdef ALLEGRO_MACOSX
    00024 #include <OpenGL/glu.h>
    00025 #else
    00026 #include <GL/glu.h>
    00027 #endif
    00028
    00029 #if defined ALLEGRO_WITH_XWINDOWS && !defined ALLEGROGL_GENERIC_DRIVER
    00030 #include <xalleg.h>
    00031 #include <GL/glx.h>
    00032 #endif
    00033
    00034 #define PREFIX_I                "agl-font INFO: "
    00035 #define PREFIX_W                "agl-font WARNING: "
    00036 #define PREFIX_E                "agl-font ERROR: "
    00037
    00038
    00039 /* Number of pixels between characters in a textured font.
    00040  */
    00041 #define FONT_CHARACTER_SPACING 2
    00042
    00043 /* Uncomment to have the font generator dump screenshots of the textures it
    00044  * generates.
    00045  */
    00046 /* #define SAVE_FONT_SCREENSHOT */
    00047
    00048
    00049 static int agl_get_font_height(AL_CONST FONT *f);
    00050 static int agl_char_length(const FONT *f, int ch);
    00051 static int agl_text_length(const FONT *f, const char *str);
    00052
    00053 static int agl_get_font_ranges(FONT *f);
    00054 static int agl_get_font_range_begin(FONT *f, int range);
    00055 static int agl_get_font_range_end(FONT *f, int range);
    00056 static FONT *agl_extract_font_range(FONT *f, int start, int end);
    00057 static FONT *agl_merge_fonts(FONT *f1, FONT *f2);
    00058
    00059 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 0)
    00060 static int agl_transpose_font(FONT *f, int drange);
    00061 #endif
    00062
    00063
    00064 FONT_VTABLE _agl_font_vtable = {
    00065     agl_get_font_height,
    00066     agl_char_length,
    00067     agl_text_length,
    00068     NULL, /* render_char */
    00069     NULL, /* render */
    00070     allegro_gl_destroy_font,
    00071     agl_get_font_ranges,
    00072     agl_get_font_range_begin,
    00073     agl_get_font_range_end,
    00074     agl_extract_font_range,
    00075     agl_merge_fonts,
    00076 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 0)
    00077     agl_transpose_font
    00078 #endif
    00079 };
    00080
    00081
    00082 FONT_VTABLE *font_vtable_agl = &_agl_font_vtable;
    00083
    00084 static void aglf_convert_allegro_font_to_bitmap(FONT_AGL_DATA *dest, FONT *f,
    00085                                                 void *src, int *height);
    00086 static void aglf_convert_allegro_font_to_texture(FONT_AGL_DATA **dest, FONT *f,
    00087                              void *src, int *height, float scale, GLint format);
    00088 static GLuint aglf_upload_texture(BITMAP *bmp, GLint format, int has_alpha);
    00089 static int aglf_check_texture(BITMAP *bmp, GLint format, int has_alpha);
    00090 static BITMAP* look_for_texture(int beg, int end, AGL_GLYPH *glyphs,
    00091                                 int max_w, int max_h, int total_area,
    00092                                 GLint format, int has_alpha);
    00093
    00094
    00095
    00096 union mixed_ptr {
    00097     FONT_MONO_DATA* mf;
    00098     FONT_COLOR_DATA* cf;
    00099     void *ptr;
    00100 };
    00101
    00102
    00103 /* Stores info about a texture size */
    00104 typedef struct texture_size {
    00105     int w, h;
    00106 } texture_size;
    00107
    00108
    00109
    00110 static int agl_get_font_height(AL_CONST FONT *f) {
    00111     return f->height;
    00112 }
    00113
    00114
    00115 /* iroundf:
    00116  * Round float to nearest integer, away from zero.
    00117  */
    00118 static int iroundf(float v) {
    00119     float f = floor(v);
    00120     float c = ceil(v);
    00121
    00122     if (v >= 0) {
    00123         /* distance to ceil smaller than distance to floor */
    00124         if ((c - v) < (v - f))
    00125             return (int)c;
    00126         else
    00127             return (int)f;
    00128     }
    00129     else {
    00130         /* distance to ceil smaller than distance to floor */
    00131         if ((c - v) < (v - f))
    00132             return (int)f;
    00133         else
    00134             return (int)c;
    00135     }
    00136 }
    00137
    00138
    00139
    00140 /* agl_char_length_fractional:
    00141  * Returns the width, in fractional pixels of the given character.
    00142  */
    00143 static float agl_char_length_fractional(const FONT *f, int ch) {
    00144     FONT_AGL_DATA *fad = f->data;
    00145
    00146     if (fad->type == AGL_FONT_TYPE_TEXTURED) {
    00147         while (fad) {
    00148             if (ch >= fad->start && ch < fad->end) {
    00149                 AGL_GLYPH *coords = &(fad->glyph_coords[ch - fad->start]);
    00150                 return (coords->offset_x + coords->w + coords->offset_w)
    00151                        / fabs(fad->scale);
    00152             }
    00153
    00154             fad = fad->next;
    00155         }
    00156     }
    00157     else if (fad->type == AGL_FONT_TYPE_BITMAP) {
    00158         while (fad) {
    00159             if (ch >= fad->start && ch < fad->end) {
    00160                 FONT_GLYPH **gl = fad->data;
    00161                 return gl[ch - fad->start]->w;
    00162             }
    00163
    00164             fad = fad->next;
    00165         }
    00166     }
    00167
    00168     /* if we don't find the character, then search for the missing
    00169      * glyph, but don't get stuck in a loop. */
    00170     if (ch != allegro_404_char)
    00171         return agl_char_length_fractional(f, allegro_404_char);
    00172
    00173     return 0;
    00174 }
    00175
    00176
    00177
    00178 /* agl_char_length:
    00179  * font vtable entry
    00180  * Returns the width, in pixels of the given character.
    00181  */
    00182 static int agl_char_length(const FONT *f, int ch) {
    00183     return iroundf(agl_char_length_fractional(f, ch));
    00184 }
    00185
    00186
    00187
    00188 /* agl_text_length:
    00189  * font vtable entry
    00190  * Returns the length, in pixels, of a string as rendered in a font.
    00191  */
    00192 static int agl_text_length(const FONT *f, const char *str) {
    00193     int ch = 0;
    00194     float l = 0;
    00195     const char *p = str;
    00196     ASSERT(f);
    00197     ASSERT(str);
    00198
    00199     while ( (ch = ugetxc(&p)) ) {
    00200         l += agl_char_length_fractional(f, ch);
    00201     }
    00202
    00203     return iroundf(l);
    00204 }
    00205
    00206
    00207
    00208 /* agl_get_font_ranges:
    00209  * font vtable entry
    00210  * Returns the number of character ranges in a font, or -1 if that information
    00211  *   is not available.
    00212  */
    00213 static int agl_get_font_ranges(FONT *f) {
    00214     FONT_AGL_DATA *fad;
    00215     int ranges = 0;
    00216
    00217     if (!f)
    00218         return 0;
    00219
    00220     fad = (FONT_AGL_DATA*)(f->data);
    00221
    00222     while (fad) {
    00223         FONT_AGL_DATA *next = fad->next;
    00224
    00225         ranges++;
    00226         if (!next)
    00227             return ranges;
    00228         fad = next;
    00229     }
    00230
    00231     return -1;
    00232 }
    00233
    00234
    00235
    00236 /* agl_get_font_range_begin:
    00237  * font vtable entry
    00238  * Get first character for font.
    00239  */
    00240 static int agl_get_font_range_begin(FONT *f, int range) {
    00241     FONT_AGL_DATA *fad;
    00242     int n = 0;
    00243
    00244     if (!f || !f->data)
    00245         return -1;
    00246
    00247     if (range < 0)
    00248         range = 0;
    00249
    00250     fad = (FONT_AGL_DATA*)(f->data);
    00251     while (fad && n <= range) {
    00252         FONT_AGL_DATA *next = fad->next;
    00253
    00254         if (!next || range == n)
    00255             return fad->start;
    00256         fad = next;
    00257         n++;
    00258     }
    00259
    00260     return -1;
    00261 }
    00262
    00263
    00264
    00265 /* agl_get_font_range_end:
    00266  * font vtable entry
    00267  * Get last character for font range.
    00268  */
    00269 static int agl_get_font_range_end(FONT *f, int range) {
    00270     FONT_AGL_DATA* fad = 0;
    00271     int n = 0;
    00272
    00273     if (!f || !f->data)
    00274         return -1;
    00275
    00276     fad = (FONT_AGL_DATA*)(f->data);
    00277
    00278     while (fad && (n <= range || range == -1)) {
    00279         FONT_AGL_DATA *next = fad->next;
    00280         if (!next || range == n)
    00281             return fad->end - 1;
    00282         fad = next;
    00283         n++;
    00284     }
    00285
    00286     return -1;
    00287 }
    00288
    00289
    00290
    00291 /* Creates a call lists from given glyph coords. Returns list base.*/
    00292 static int create_textured_font_call_lists(AGL_GLYPH *coords, int max, BITMAP *bmp,
    00293                                                     float scale, int *height) {
    00294     GLuint list;
    00295     int i;
    00296
    00297     int rev = scale < 0 ? 1 : 0;
    00298     scale = fabs(scale);
    00299
    00300     list = glGenLists(max);
    00301
    00302     for (i = 0; i < max; i++) {
    00303         /* Coords of glyph in texture (texture coords) */
    00304         float tx = (float)coords[i].x / bmp->w;
    00305         float ty = 1.0 - (float)coords[i].y / bmp->h;
    00306         /* Size of glyph in texture (texture coords) */
    00307         float dtx = (float)(coords[i].w) / bmp->w;
    00308         float dty = (float)(coords[i].h) / bmp->h;
    00309
    00310         /* Offset to apply to glyph (output coords) */
    00311         float xoffs = (float)coords[i].offset_x / scale;
    00312         float yoffs = (float)coords[i].offset_y / scale;
    00313         /* Size of rendered glyph (output coords) */
    00314         float woffs = (float)coords[i].w / scale;
    00315         float hoffs = (float)coords[i].h / scale;
    00316
    00317         /* Size of overall screen character including dead space */
    00318         float sizew = (float)(coords[i].offset_x + coords[i].w
    00319                     + coords[i].offset_w) / scale;
    00320         int sizeh = iroundf((coords[i].offset_y + coords[i].h
    00321                     + coords[i].offset_h) / scale);
    00322
    00323         if ((*height) < sizeh)
    00324             *height = sizeh;
    00325
    00326         if (rev) {
    00327             hoffs = -hoffs;
    00328             yoffs = -yoffs;
    00329         }
    00330
    00331         glNewList(list + i, GL_COMPILE);
    00332
    00333         glBegin(GL_QUADS);
    00334             glTexCoord2f(tx, ty);
    00335             glVertex2f(xoffs, -yoffs);
    00336
    00337             glTexCoord2f(tx + dtx, ty);
    00338             glVertex2f(xoffs + woffs, -yoffs);
    00339
    00340             glTexCoord2f(tx + dtx, ty - dty);
    00341             glVertex2f(xoffs + woffs, -yoffs - hoffs);
    00342
    00343             glTexCoord2f(tx, ty - dty);
    00344             glVertex2f(xoffs, -yoffs - hoffs);
    00345         glEnd();
    00346
    00347         glTranslatef(sizew, 0, 0);
    00348
    00349         glEndList();
    00350     }
    00351
    00352     return list;
    00353 }
    00354
    00355
    00356
    00357 /* copy_glyph_range:
    00358  * Copies part of glyph range.
    00359  */
    00360 static FONT_AGL_DATA* copy_glyph_range(FONT_AGL_DATA *fad, int start, int end,
    00361                                                                 int *height) {
    00362     int i, count, w = 0, h = 0;
    00363     AGL_GLYPH *coords;
    00364     BITMAP *bmp, *srcbmp;
    00365     FONT_AGL_DATA *newfad = NULL;
    00366
    00367     if (fad->type != AGL_FONT_TYPE_TEXTURED)
    00368         return NULL;
    00369
    00370     count = end - start;
    00371
    00372     coords = malloc(count * sizeof (AGL_GLYPH));
    00373
    00374     /* for now, just copy glyph coords of the range */
    00375     for (i = 0; i < count; i++) {
    00376         coords[i] = fad->glyph_coords[start - fad->start + i];
    00377         coords[i].glyph_num = i;
    00378     }
    00379
    00380     /* calculate the width of the glyphs and find the max height */
    00381     for (i = 0; i < count; i++) {
    00382         int hh = coords[i].h + coords[i].offset_y + coords[i].offset_h;
    00383         if (h < hh)
    00384             h = hh;
    00385         w += coords[i].w + coords[i].offset_w + coords[i].offset_x;
    00386     }
    00387
    00388     srcbmp = (BITMAP*)fad->data;
    00389
    00390     /* allocate a new bitmap to hold new glyphs */
    00391     w = __allegro_gl_make_power_of_2(w);
    00392     h = __allegro_gl_make_power_of_2(h);
    00393     bmp = create_bitmap_ex(bitmap_color_depth(srcbmp), w, h);
    00394     if (!bmp) {
    00395         TRACE(PREFIX_E "copy_glyph_range: Unable to create bitmap of size"
    00396                 "%ix%i pixels!\n", w, h);
    00397         free(coords);
    00398         return NULL;
    00399     }
    00400
    00401     if (__allegro_gl_get_num_channels(fad->format) == 4) {
    00402         clear_to_color(bmp, bitmap_mask_color(bmp));
    00403     }
    00404     else {
    00405         clear_bitmap(bmp);
    00406     }
    00407
    00408     /* blit every glyph from the range to the new bitmap */
    00409     w = 0;
    00410     for (i = 0; i < count; i++) {
    00411         int ch = start - fad->start + i;
    00412         int ww = coords[i].w + coords[i].offset_w + coords[i].offset_x;
    00413         blit(srcbmp, bmp, fad->glyph_coords[ch].x, 0, w, 0, ww, bmp->h);
    00414         /* fix new glyphs coords while here */
    00415         coords[i].x = w;
    00416         w += ww;
    00417     }
    00418
    00419     newfad = malloc(sizeof(struct FONT_AGL_DATA));
    00420
    00421     newfad->type = AGL_FONT_TYPE_TEXTURED;
    00422     newfad->is_free_chunk = 0;
    00423     newfad->scale = fad->scale;
    00424     newfad->format = fad->format;
    00425     newfad->has_alpha = fad->has_alpha;
    00426     newfad->start = start;
    00427     newfad->end = end;
    00428     newfad->data = bmp;
    00429     newfad->glyph_coords = coords;
    00430     newfad->next = NULL;
    00431     newfad->list_base = create_textured_font_call_lists(coords, count, bmp,
    00432                                                     newfad->scale, height);
    00433     newfad->texture = aglf_upload_texture(bmp, newfad->format, newfad->has_alpha);
    00434
    00435     return newfad;
    00436 }
    00437
    00438
    00439
    00440 /* agl_extract_font_range:
    00441  * font vtable entry
    00442  * Extracts a glyph range from a given font and makes a new font of it.
    00443  */
    00444 static FONT *agl_extract_font_range(FONT *f, int start, int end) {
    00445     FONT *retval = NULL;
    00446     FONT_AGL_DATA *fad, *next, *newfad = NULL;
    00447     int count;
    00448
    00449     if (!f)
    00450         return NULL;
    00451
    00452     /* check if range boundaries make sense */
    00453     if (start == -1 && end == -1) {
    00454     }
    00455     else if (start == -1 && end > agl_get_font_range_begin(f, -1)) {
    00456     }
    00457     else if (end == -1 && start <= agl_get_font_range_end(f, -1)) {
    00458     }
    00459     else if (start <= end && start != -1 && end != -1) {
    00460     }
    00461     else
    00462         return NULL;
    00463
    00464     fad = (FONT_AGL_DATA*)f->data;
    00465
    00466     /* only textured fonts are supported */
    00467     if (fad->type != AGL_FONT_TYPE_TEXTURED)
    00468         return NULL;
    00469
    00470     /* anticipate invalid range values */
    00471     start = MAX(start, agl_get_font_range_begin(f, -1));
    00472     if (end > -1) {
    00473         end = MIN(end, agl_get_font_range_end(f, -1));
    00474     }
    00475     else {
    00476         end = agl_get_font_range_end(f, -1);
    00477     }
    00478     end++;
    00479
    00480     retval = malloc(sizeof (struct FONT));
    00481     retval->height = 0;
    00482     retval->vtable = font_vtable_agl;
    00483
    00484     next = fad;
    00485     count = end - start;
    00486
    00487     while (next) {
    00488         /* find the range that is covered by the requested range
    00489          * check if the requested and processed ranges at least overlap
    00490          *    or if the requested range wraps processed range.
    00491          */
    00492         if ((start >= next->start && start < next->end)
    00493          || (end   <= next->end   && end   > next->start)
    00494          || (start <  next->start && end   > next->end)) {
    00495             int local_start, local_end;
    00496
    00497             /* extract the overlapping range */
    00498             local_start = MAX(next->start, start);
    00499             local_end = MIN(next->end, end);
    00500
    00501             if (newfad) {
    00502                 newfad->next = copy_glyph_range(next, local_start, local_end,
    00503                                                             &(retval->height));
    00504                 newfad = newfad->next;
    00505                 newfad->is_free_chunk = TRUE;
    00506             }
    00507             else {
    00508                 newfad = copy_glyph_range(next, local_start, local_end,
    00509                                                             &(retval->height));
    00510                 retval->data = newfad;
    00511             }
    00512         }
    00513
    00514         next = next->next;
    00515     }
    00516
    00517     return retval;
    00518 }
    00519
    00520
    00521
    00522 /* agl_merge_fonts:
    00523  * font vtable entry
    00524  * Merges f2 with f1 and returns a new font.
    00525  */
    00526 static FONT *agl_merge_fonts(FONT *f1, FONT *f2) {
    00527     FONT *retval;
    00528     FONT_AGL_DATA *fad1, *fad2, *fad = NULL;
    00529     int phony = 0;
    00530
    00531     if (!f1 || !f2)
    00532         return NULL;
    00533
    00534     fad1 = (FONT_AGL_DATA*)f1->data;
    00535     fad2 = (FONT_AGL_DATA*)f2->data;
    00536
    00537     /* fonts must be textured and of the same format */
    00538     if (fad1->type != AGL_FONT_TYPE_TEXTURED ||
    00539         fad2->type != AGL_FONT_TYPE_TEXTURED)
    00540         return NULL;
    00541
    00542     if (fad1->format != fad2->format)
    00543         return NULL;
    00544
    00545     /* alloc output font */
    00546     retval = malloc(sizeof(struct FONT));
    00547     retval->vtable = font_vtable_agl;
    00548     retval->height = MAX(f1->height, f2->height);
    00549
    00550     while (fad1 || fad2) {
    00551         if (fad1 && (!fad2 || fad1->start < fad2->start)) {
    00552             if (fad) {
    00553                 fad->next = copy_glyph_range(fad1, fad1->start, fad1->end,
    00554                                                                         &phony);
    00555                 fad = fad->next;
    00556                 fad->is_free_chunk = TRUE;
    00557             }
    00558             else {
    00559                 fad = copy_glyph_range(fad1, fad1->start, fad1->end, &phony);
    00560                 retval->data = fad;
    00561             }
    00562             fad1 = fad1->next;
    00563         }
    00564         else {
    00565             if (fad) {
    00566                 fad->next = copy_glyph_range(fad2, fad2->start, fad2->end,
    00567                                                                     &phony);
    00568                 fad = fad->next;
    00569                 fad->is_free_chunk = TRUE;
    00570             }
    00571             else {
    00572                 fad = copy_glyph_range(fad2, fad2->start, fad2->end, &phony);
    00573                 retval->data = fad;
    00574             }
    00575             fad2 = fad2->next;
    00576         }
    00577     }
    00578
    00579     return retval;
    00580 }
    00581
    00582
    00583
    00584 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 0)
    00585 /* agl_transpose_font:
    00586  * font vtable entry
    00587  * Transposes characters in a font.
    00588  */
    00589 static int agl_transpose_font(FONT *f, int drange) {
    00590     FONT_AGL_DATA* fad = 0;
    00591
    00592     if (!f)
    00593         return -1;
    00594
    00595     fad = (FONT_AGL_DATA*)(f->data);
    00596
    00597     while(fad) {
    00598         FONT_AGL_DATA* next = fad->next;
    00599         fad->start += drange;
    00600         fad->end += drange;
    00601         fad = next;
    00602     }
    00603
    00604     return 0;
    00605 }
    00606 #endif
    00607
    00608
    00609
    00610 /* allegro_gl_convert_allegro_font_ex(FONT *f, int type, float scale, GLint format) */
    00623 FONT *allegro_gl_convert_allegro_font(FONT *f, int type, float scale) {
    00624     GLint format = allegro_gl_get_texture_format(NULL);
    00625     return allegro_gl_convert_allegro_font_ex(f, type, scale, format);
    00626 }
    00627
    00628
    00629
    00630 /* allegro_gl_convert_allegro_font_ex(FONT *f, int type, float scale, GLint format) */
    00685 FONT *allegro_gl_convert_allegro_font_ex(FONT *f, int type, float scale,
    00686                                          GLint format) {
    00687     int max = 0, height = 0;
    00688     int i;
    00689     FONT *dest;
    00690     FONT_AGL_DATA *destdata;
    00691     int has_alpha = 0;
    00692
    00693     union {
    00694         FONT_MONO_DATA* mf;
    00695         FONT_COLOR_DATA* cf;
    00696         void *ptr;
    00697     } dat;
    00698
    00699     if (!__allegro_gl_valid_context) {
    00700         return NULL;
    00701     }
    00702
    00703     if (!f) {
    00704         TRACE(PREFIX_E "convert_allegro_font: Null source\n");
    00705         return NULL;
    00706     }
    00707
    00708     /* Make sure it's an Allegro font - we don't want any surprises */
    00709 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 1)
    00710     if (f->vtable != font_vtable_mono && f->vtable != font_vtable_color && f->vtable != font_vtable_trans) {
    00711 #else
    00712     if (f->vtable != font_vtable_mono && f->vtable != font_vtable_color) {
    00713 #endif
    00714         TRACE(PREFIX_I "convert_allegro_font: Source font is not "
    00715               "in Allegro format\n");
    00716         return NULL;
    00717     }
    00718
    00719     /* No vector fonts allowed as destination */
    00720     if (type == AGL_FONT_TYPE_OUTLINE) {
    00721         /* Can't convert bitmap to vector font */
    00722         TRACE(PREFIX_I "convert_allegro_font: Unable to convert a "
    00723               "pixmap font to a vector font.\n");
    00724         return NULL;
    00725     }
    00726
    00727     /* Make sure the scaling factor is appropreate */
    00728     if (fabs(scale) < 0.001) {
    00729         TRACE(PREFIX_W "convert_allegro_font: Scaling factor might be "
    00730               "too small: %f\n", scale);
    00731     }
    00732
    00733     /* Count number of ranges */
    00734     max = get_font_ranges(f);
    00735
    00736     /* There should really be an API for this */
    00737     dest = (FONT*)malloc(sizeof(FONT));
    00738     if (!dest) {
    00739         TRACE(PREFIX_E "convert_allegro_font: Ran out of memory "
    00740               "while allocating %i bytes\n", (int)sizeof(FONT));
    00741         return NULL;
    00742     }
    00743     destdata = (FONT_AGL_DATA*)malloc(sizeof(FONT_AGL_DATA) * max);
    00744     if (!destdata) {
    00745         TRACE(PREFIX_E "convert_allegro_font: Ran out of memory "
    00746               "while allocating %i bytes\n", (int)sizeof(FONT_AGL_DATA) * max);
    00747         return NULL;
    00748     }
    00749     memset(destdata, 0, sizeof(FONT_AGL_DATA) * max);
    00750
    00751     /* Construct the linked list */
    00752     for (i = 0; i < max - 1; i++) {
    00753         destdata[i].next = &destdata[i + 1];
    00754     }
    00755     destdata[max - 1].next = NULL;
    00756
    00757     /* Set up the font */
    00758     dest->data = destdata;
    00759     dest->vtable = font_vtable_agl;
    00760     dest->height = 0;
    00761
    00762     destdata->type = type;
    00763
    00764     if (type == AGL_FONT_TYPE_DONT_CARE) {
    00765         destdata->type = AGL_FONT_TYPE_TEXTURED;
    00766     }
    00767
    00768 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 1)
    00769     has_alpha = (f->vtable == font_vtable_trans);
    00770 #endif
    00771
    00772     /* Convert each range */
    00773     dat.ptr = f->data;
    00774
    00775     while (dat.ptr) {
    00776
    00777         destdata->has_alpha = has_alpha;
    00778
    00779         if (type == AGL_FONT_TYPE_BITMAP) {
    00780             aglf_convert_allegro_font_to_bitmap(destdata, f, dat.ptr, &height);
    00781         }
    00782         else if (type == AGL_FONT_TYPE_TEXTURED) {
    00783             aglf_convert_allegro_font_to_texture(&destdata, f, dat.ptr, &height,
    00784                                                  scale, format);
    00785         }
    00786
    00787         if (height > dest->height) {
    00788             dest->height = height;
    00789         }
    00790
    00791         dat.ptr = (is_mono_font(f) ? (void*)dat.mf->next : (void*)dat.cf->next);
    00792
    00793         destdata = destdata->next;
    00794     }
    00795
    00796     return dest;
    00797 }
    00798
    00799
    00800
    00801 /* QSort helper for sorting glyphs according to width,
    00802  * then height - largest first.
    00803  */
    00804 static int sort_glyphs(const void *c1, const void *c2) {
    00805     AGL_GLYPH *g1 = (AGL_GLYPH*)c1;
    00806     AGL_GLYPH *g2 = (AGL_GLYPH*)c2;
    00807
    00808     if (g1->w < g2->w) {
    00809         return 1;
    00810     }
    00811     else if (g1->w == g2->w) {
    00812         return -g1->h + g2->h;
    00813     }
    00814     else {
    00815         return -1;
    00816     }
    00817 }
    00818
    00819
    00820
    00821 /* QSort helper for unsorting glyphs.
    00822  */
    00823 static int unsort_glyphs(const void *c1, const void *c2) {
    00824     AGL_GLYPH *g1 = (AGL_GLYPH*)c1;
    00825     AGL_GLYPH *g2 = (AGL_GLYPH*)c2;
    00826
    00827     return g1->glyph_num - g2->glyph_num;
    00828 }
    00829
    00830
    00831
    00832 /* QSort helper for sorting textures by area.
    00833  */
    00834 static int sort_textures(const void *c1, const void *c2) {
    00835     texture_size *t1 = (texture_size*)c1;
    00836     texture_size *t2 = (texture_size*)c2;
    00837
    00838     return t1->w * t1->h - t2->w * t2->h;
    00839 }
    00840
    00841
    00842
    00843 #ifdef SAVE_FONT_SCREENSHOT
    00844 static void save_shot(BITMAP *bmp) {
    00845
    00846     int i;
    00847     char name[128];
    00848
    00849     for (i = 0; i < 1000; i++) {
    00850         /* TGA, in case it is a truecolor font with alpha */
    00851         sprintf(name, "fonttest_%02i.tga", i);
    00852         if (!exists(name)) {
    00853             save_tga(name, bmp, NULL);
    00854             break;
    00855         }
    00856     }
    00857 }
    00858 #endif
    00859
    00860
    00861
    00862 /* Helper function. This will try to place all the glyphs in the bitmap the
    00863  * best way it can
    00864  */
    00865 static int aglf_sort_out_glyphs(BITMAP *bmp, AGL_GLYPH *glyphs, const int beg,
    00866                                 const int end) {
    00867
    00868     int i, j;
    00869     int last_line = 0;
    00870     int last_x = 0;
    00871
    00872     /* We now try to make all the glyphs fit on the bitmap */
    00873     for (i = 0; i < end - beg; i++) {
    00874         int collide = FALSE;
    00875
    00876         /* Place glyphs on last_line */
    00877         glyphs[i].x = last_x;
    00878         glyphs[i].y = last_line;
    00879
    00880         /* Check for collision */
    00881
    00882         for (j = 0; j < i; j++) {
    00883             if ((glyphs[i].x >= glyphs[j].x + glyphs[j].w)
    00884                     || (glyphs[i].y >= glyphs[j].y + glyphs[j].h)
    00885                     || (glyphs[j].x >= glyphs[i].x + glyphs[i].w)
    00886                     || (glyphs[j].y >= glyphs[i].y + glyphs[i].h)) {
    00887                 continue;
    00888             }
    00889             last_x = glyphs[j].x + glyphs[j].w;
    00890             glyphs[i].x = last_x;
    00891             j = 0;
    00892         }
    00893
    00894         if ((last_x + glyphs[i].w > bmp->w)
    00895          || (last_line + glyphs[i].h > bmp->h)) {
    00896             collide = TRUE;
    00897         }
    00898
    00899         if (collide) {
    00900             /* If there was a collision, we need to find the sprite with
    00901              * the smallest height that is still grater than last_line.
    00902              * We also need to redo this glyph.
    00903              */
    00904             int min_line = bmp->h + 1;
    00905             int min_glyph = -1;
    00906
    00907             for (j = 0; j < i; j++) {
    00908                 if ( glyphs[j].y + glyphs[j].h < min_line
    00909                   && glyphs[j].y + glyphs[j].h
    00910                   > last_line - FONT_CHARACTER_SPACING) {
    00911
    00912                     min_line = glyphs[j].y + glyphs[j].h
    00913                              + FONT_CHARACTER_SPACING;
    00914                     min_glyph = j;
    00915                 }
    00916             }
    00917             /* If it can't possibly all fit, failure */
    00918             if (min_glyph == -1) {
    00919                 TRACE(PREFIX_I "sort_out_glyphs: Unable to fit all glyphs into "
    00920                       "the texture.\n");
    00921                 return FALSE;
    00922             }
    00923             /* Otherwise, start over at the top of that glyph */
    00924             last_x = glyphs[min_glyph].x;
    00925             last_line = min_line;
    00926
    00927             /* Redo this glyph */
    00928             i--;
    00929         }
    00930         else {
    00931             last_x += glyphs[i].w + FONT_CHARACTER_SPACING;
    00932         }
    00933     }
    00934
    00935     /* All ok */
    00936     return TRUE;
    00937 }
    00938
    00939
    00940
    00941 static int split_font(FONT *f, void *source, void **dest1, void **dest2) {
    00942
    00943     union mixed_ptr range1, range2, src;
    00944     int colored;
    00945     int i;
    00946
    00947     (*dest1) = NULL;
    00948     (*dest2) = NULL;
    00949     src.ptr = source;
    00950
    00951     colored = (is_mono_font(f) ? FALSE : TRUE);
    00952
    00953     /* Allocate the ranges that we need */
    00954     range1.ptr = malloc(colored ? sizeof(FONT_COLOR_DATA)
    00955                                 : sizeof(FONT_MONO_DATA));
    00956     if (!range1.ptr) {
    00957         TRACE(PREFIX_E "split_font() - Ran out of memory while "
    00958               "trying ot allocate %i bytes.\n",
    00959               colored ? (int)sizeof(FONT_COLOR_DATA)
    00960               : (int)sizeof(FONT_MONO_DATA));
    00961         return FALSE;
    00962     }
    00963
    00964     range2.ptr = malloc(colored ? sizeof(FONT_COLOR_DATA)
    00965                                 : sizeof(FONT_MONO_DATA));
    00966     if (!range2.ptr) {
    00967         TRACE(PREFIX_E "split_font() - Ran out of memory while "
    00968               "trying to allocate %i bytes.\n",
    00969               colored ? (int)sizeof(FONT_COLOR_DATA)
    00970                       : (int)sizeof(FONT_MONO_DATA));
    00971         free(range1.ptr);
    00972         return FALSE;
    00973     }
    00974
    00975     (*dest1) = range1.ptr;
    00976     (*dest2) = range2.ptr;
    00977
    00978     /* Now we split up the range */
    00979     if (colored) {
    00980         /* Half the range */
    00981         int mid = src.cf->begin + (src.cf->end - src.cf->begin) / 2;
    00982
    00983         range1.cf->begin = src.cf->begin;
    00984         range1.cf->end = mid;
    00985         range2.cf->begin = mid;
    00986         range2.cf->end = src.cf->end;
    00987
    00988         range1.cf->next = NULL;
    00989         range2.cf->next = NULL;
    00990
    00991         /* Split up the bitmaps */
    00992         range1.cf->bitmaps = malloc(sizeof(BITMAP*)
    00993                                         * (range1.cf->end - range1.cf->begin));
    00994         if (!range1.cf->bitmaps) {
    00995             TRACE(PREFIX_E "split_font() - Ran out of memory "
    00996                   "while trying to allocate %i bytes.\n",
    00997                   (int)sizeof(BITMAP*) * (range1.cf->end - range1.cf->begin));
    00998             free(range1.ptr);
    00999             free(range2.ptr);
    01000             return FALSE;
    01001         }
    01002
    01003         range2.cf->bitmaps = malloc(sizeof(BITMAP*)
    01004                                          * (range2.cf->end - range2.cf->begin));
    01005         if (!range2.cf->bitmaps) {
    01006             TRACE(PREFIX_E "split_font() - Ran out of memory "
    01007                   "while trying to allocate %i bytes.\n",
    01008                   (int)sizeof(BITMAP*) * (range2.cf->end - range2.cf->begin));
    01009             free(range1.cf->bitmaps);
    01010             free(range1.ptr);
    01011             free(range2.ptr);
    01012             return FALSE;
    01013         }
    01014
    01015
    01016         for (i = 0; i < (range1.cf->end - range1.cf->begin); i++) {
    01017             range1.cf->bitmaps[i] = src.cf->bitmaps[i];
    01018         }
    01019         for (i = 0; i < (range2.cf->end - range2.cf->begin); i++) {
    01020             range2.cf->bitmaps[i] =
    01021                        src.cf->bitmaps[i + range2.cf->begin - range1.cf->begin];
    01022         }
    01023     }
    01024     else {
    01025         /* Half the range */
    01026         int mid = src.mf->begin + (src.mf->end - src.mf->begin) / 2;
    01027
    01028         range1.mf->begin = src.mf->begin;
    01029         range1.mf->end = mid;
    01030         range2.mf->begin = mid;
    01031         range2.mf->end = src.mf->end;
    01032
    01033         range1.mf->next = NULL;
    01034         range2.mf->next = NULL;
    01035
    01036         /* Split up the bitmaps */
    01037         range1.mf->glyphs = malloc(sizeof(FONT_GLYPH*)
    01038                                          * (range1.mf->end - range1.mf->begin));
    01039         if (!range1.mf->glyphs) {
    01040             TRACE(PREFIX_E "split_font() - Ran out of memory "
    01041                   "while trying to allocate %i bytes.\n",
    01042                 (int)sizeof(FONT_GLYPH*) * (range1.mf->end - range1.mf->begin));
    01043             free(range1.ptr);
    01044             free(range2.ptr);
    01045             return FALSE;
    01046         }
    01047
    01048         range2.mf->glyphs = malloc(sizeof(FONT_GLYPH*)
    01049                                          * (range2.mf->end - range2.mf->begin));
    01050         if (!range2.mf->glyphs) {
    01051             TRACE(PREFIX_E "split_font() - Ran out of memory "
    01052                   "while trying to allocate %i bytes.\n",
    01053                 (int)sizeof(FONT_GLYPH*) * (range2.mf->end - range2.mf->begin));
    01054             free(range1.mf->glyphs);
    01055             free(range1.ptr);
    01056             free(range2.ptr);
    01057             return FALSE;
    01058         }
    01059
    01060         for (i = 0; i < (range1.mf->end - range1.mf->begin); i++) {
    01061             range1.mf->glyphs[i] = src.mf->glyphs[i];
    01062         }
    01063         for (i = 0; i < (range2.mf->end - range2.mf->begin); i++) {
    01064             range2.mf->glyphs[i] =
    01065                         src.mf->glyphs[i + range2.mf->begin - range1.mf->begin];
    01066         }
    01067     }
    01068
    01069     return TRUE;
    01070 }
    01071
    01072
    01073
    01074 /* Destroys a split font */
    01075 static void destroy_split_font(FONT *f, union mixed_ptr range1,
    01076                                         union mixed_ptr range2) {
    01077
    01078     if (!is_mono_font(f)) {
    01079         free(range1.cf->bitmaps);
    01080         free(range2.cf->bitmaps);
    01081     }
    01082     else {
    01083         free(range1.mf->glyphs);
    01084         free(range2.mf->glyphs);
    01085     }
    01086
    01087     free(range1.ptr);
    01088     free(range2.ptr);
    01089
    01090     return;
    01091 }
    01092
    01093
    01094
    01095 static int do_crop_font_range(FONT *f, AGL_GLYPH *glyphs, int beg, int end) {
    01096
    01097     int i, j, k;
    01098     int max = end - beg;
    01099     char buf[32];
    01100
    01101     /* Allocate a temp bitmap to work with */
    01102     BITMAP *temp = create_bitmap(32, 32);
    01103
    01104     if (!temp) {
    01105         TRACE(PREFIX_E "crop_font_range - Unable to create "
    01106               "bitmap of size: %ix%i!\n", 32, 32);
    01107         goto error;
    01108     }
    01109
    01110     /* Crop glyphs */
    01111     for (i = 0; i < max; i++) {
    01112         int used = 0;
    01113
    01114         if (glyphs[i].w > temp->w || glyphs[i].h > temp->h) {
    01115             int old_w = temp->w, old_h = temp->h;
    01116             destroy_bitmap(temp);
    01117             temp = create_bitmap(old_w * 2, old_h * 2);
    01118             if (!temp) {
    01119                 TRACE(PREFIX_E "crop_font_range - Unable to "
    01120                       "create bitmap of size: %ix%i!\n", old_w * 2, old_h * 2);
    01121                 goto error;
    01122             }
    01123         }
    01124         clear(temp);
    01125
    01126         usetc(buf + usetc(buf, glyphs[i].glyph_num + beg), 0);
    01127
    01128         textout_ex(temp, f, buf, 0, 0,
    01129                     makecol_depth(bitmap_color_depth(temp), 255, 255, 255), 0);
    01130
    01131         /* Crop top */
    01132         for (j = 0; j < glyphs[i].h; j++) {
    01133             used = 0;
    01134
    01135             for (k = 0; k < glyphs[i].w; k++) {
    01136                 if (getpixel(temp, k, j)) {
    01137                     used = 1;
    01138                     glyphs[i].offset_y += j;
    01139                     glyphs[i].h -= j;
    01140                     break;
    01141                 }
    01142             }
    01143             if (used)
    01144                 break;
    01145         }
    01146
    01147         /* If just the top crop killed our glyph, then skip it entirely */
    01148         if (!used) {
    01149             TRACE(PREFIX_I "crop_font_range: skipping glyph %i\n", i);
    01150             glyphs[i].offset_y = 0;
    01151             glyphs[i].offset_h = glyphs[i].h - 1;
    01152             glyphs[i].offset_w = glyphs[i].w - 2;
    01153             glyphs[i].h = 1;
    01154             glyphs[i].w = 1;
    01155             continue;
    01156         }
    01157
    01158         /* Crop bottom */
    01159         j = glyphs[i].h + glyphs[i].offset_y - 1;
    01160         for ( /* above */; j >= glyphs[i].offset_y; j--) {
    01161             used = 0;
    01162
    01163             for (k = 0; k < glyphs[i].w; k++) {
    01164                 if (getpixel(temp, k, j)) {
    01165                     used = 1;
    01166                     glyphs[i].offset_h +=
    01167                                        glyphs[i].h + glyphs[i].offset_y - j - 2;
    01168                     glyphs[i].h -= glyphs[i].h + glyphs[i].offset_y - j - 1;
    01169                     break;
    01170                 }
    01171             }
    01172             if (used)
    01173                 break;
    01174         }
    01175
    01176         /* Crop Left */
    01177         for (j = 0; j < glyphs[i].w; j++) {
    01178             used = 0;
    01179
    01180             k = MAX(glyphs[i].offset_y - 1, 0);
    01181             for (/* above */; k < glyphs[i].offset_y + glyphs[i].h + 1; k++) {
    01182                 if (getpixel(temp, j, k)) {
    01183                     used = 1;
    01184                     glyphs[i].offset_x += j;
    01185                     glyphs[i].w -= j;
    01186                     break;
    01187                 }
    01188             }
    01189             if (used)
    01190                 break;
    01191         }
    01192
    01193         /* Crop Right */
    01194         j = glyphs[i].w + glyphs[i].offset_x - 1;
    01195         for (/* above */; j >= glyphs[i].offset_x; j--) {
    01196             used = 0;
    01197
    01198             k = MAX(glyphs[i].offset_y - 1, 0);
    01199             for (/* above */; k < glyphs[i].offset_y + glyphs[i].h + 1; k++) {
    01200                 if (getpixel(temp, j, k)) {
    01201                     used = 1;
    01202                     glyphs[i].offset_w +=
    01203                                        glyphs[i].w + glyphs[i].offset_x - 1 - j;
    01204                     glyphs[i].w -= glyphs[i].w + glyphs[i].offset_x - j - 1;
    01205                     break;
    01206                 }
    01207             }
    01208             if (used)
    01209                 break;
    01210         }
    01211 #ifdef LOGLEVEL
    01212         TRACE(PREFIX_I "crop_font_range: Glyph %i (%c) offs: x: %i  y: %i, "
    01213               "w: %i  h: %i,  offs: w: %i  h: %i\n", i, i + beg,
    01214               glyphs[i].offset_x, glyphs[i].offset_y, glyphs[i].w, glyphs[i].h,
    01215               glyphs[i].offset_w, glyphs[i].offset_h);
    01216 #endif
    01217     }
    01218
    01219     destroy_bitmap(temp);
    01220
    01221     return TRUE;
    01222
    01223 error:
    01224     if (temp) {
    01225         destroy_bitmap(temp);
    01226     }
    01227
    01228     return FALSE;
    01229 }
    01230
    01231
    01232
    01233 /* Crops a font over a particular range */
    01234 static int crop_font_range(FONT *f, void *src, int beg, int end,
    01235                            AGL_GLYPH *glyphs,
    01236                            int *net_area, int *gross_area,
    01237                            int *max_w, int *max_h) {
    01238
    01239     int i;
    01240     int crop = 1;
    01241     int max = end - beg;
    01242     int ret = TRUE;
    01243
    01244     union mixed_ptr dat;
    01245     dat.ptr = src;
    01246
    01247     /* Disable cropping for trucolor fonts. */
    01248     if (is_color_font(f)) {
    01249         FONT_COLOR_DATA *fcd = f->data;
    01250         if (bitmap_color_depth(fcd->bitmaps[0]) != 8) {
    01251             crop = 0;
    01252         }
    01253     }
    01254
    01255     /* Load default sizes */
    01256     for (i = 0; i < max; i++) {
    01257         glyphs[i].glyph_num = i;
    01258
    01259         if (is_mono_font(f)) {
    01260             glyphs[i].w = dat.mf->glyphs[i]->w + 1;
    01261             glyphs[i].h = dat.mf->glyphs[i]->h + 1;
    01262         } else {
    01263             glyphs[i].w = dat.cf->bitmaps[i]->w + 1;
    01264             glyphs[i].h = dat.cf->bitmaps[i]->h + 1;
    01265         }
    01266         glyphs[i].offset_w = -1;
    01267         glyphs[i].offset_h = -1;
    01268
    01269         /* Not placed yet */
    01270         glyphs[i].x = -1;
    01271     }
    01272
    01273     if (crop) {
    01274         ret = do_crop_font_range(f, glyphs, beg, end);
    01275     }
    01276
    01277     (*gross_area) = 0;
    01278     (*net_area) = 0;
    01279     (*max_w) = 0;
    01280     (*max_h) = 0;
    01281
    01282     /* Find max w and h, total area covered by the bitmaps, and number of
    01283      * glyphs
    01284      */
    01285     for (i = 0; i < max; i++) {
    01286         if (glyphs[i].w > *max_w) (*max_w) = glyphs[i].w;
    01287         if (glyphs[i].h > *max_h) (*max_h) = glyphs[i].h;
    01288         (*net_area) += glyphs[i].w * glyphs[i].h;
    01289         (*gross_area) += (glyphs[i].w + FONT_CHARACTER_SPACING)
    01290                        * (glyphs[i].h + FONT_CHARACTER_SPACING);
    01291     }
    01292     return ret;
    01293
    01294 }
    01295
    01296
    01297
    01298 /* Tries to find a texture that will fit the font
    01299  */
    01300 static BITMAP* look_for_texture(int beg, int end, AGL_GLYPH *glyphs,
    01301             int max_w, int max_h, int total_area, GLint format, int has_alpha) {
    01302
    01303     BITMAP *bmp = NULL;
    01304     int i, j;
    01305
    01306     /* Max texture size (1 << n) */
    01307     /* XXX <rohannessian> We should use ARB_np2 if we can
    01308      *
    01309      * Other note: w*h shouldn't exceed 31 bits; otherwise, we get funny
    01310      * behavior on 32-bit architectures. Limit texture sizes to 32k*32k
    01311      * (30 bits).
    01312      */
    01313 #define MIN_TEXTURE_SIZE 2
    01314 #define NUM_TEXTURE_SIZE 13
    01315     texture_size texture_sizes[NUM_TEXTURE_SIZE * NUM_TEXTURE_SIZE];
    01316
    01317     /* Set up texture sizes */
    01318     for (i = 0; i < NUM_TEXTURE_SIZE; i++) {
    01319         for (j = 0; j < NUM_TEXTURE_SIZE; j++) {
    01320             texture_sizes[j + i * NUM_TEXTURE_SIZE].w =
    01321                                                     1 << (j + MIN_TEXTURE_SIZE);
    01322             texture_sizes[j + i * NUM_TEXTURE_SIZE].h =
    01323                                                     1 << (i + MIN_TEXTURE_SIZE);
    01324         }
    01325     }
    01326
    01327     /* Sort texture sizes by area */
    01328     qsort(texture_sizes, NUM_TEXTURE_SIZE * NUM_TEXTURE_SIZE,
    01329                                           sizeof(texture_size), &sort_textures);
    01330
    01331     for (i = 0; i < NUM_TEXTURE_SIZE * NUM_TEXTURE_SIZE; i++) {
    01332         int num_channels;
    01333
    01334         /* Check the area - it must be larger than
    01335          * all the glyphs
    01336          */
    01337         texture_size *t = &texture_sizes[i];
    01338         int area = t->w * t->h;
    01339         int depth = 24;
    01340
    01341         if (area < total_area) {
    01342             continue;
    01343         }
    01344
    01345         /* Check against max values */
    01346         if ((t->h < max_h) || (t->w < max_w)) {
    01347             continue;
    01348         }
    01349
    01350         TRACE(PREFIX_I "look_for_texture: candidate size: %ix%i\n", t->w, t->h);
    01351
    01352         /* Check that the texture can, in fact, be created */
    01353         num_channels = __allegro_gl_get_num_channels(format);
    01354         if (num_channels == 1) {
    01355             depth = 8;
    01356         }
    01357         else if (num_channels == 4) {
    01358             depth = 32;
    01359         }
    01360         else {
    01361             depth = 24;
    01362         }
    01363         bmp = create_bitmap_ex(depth, t->w, t->h);
    01364
    01365         if (!bmp) {
    01366             TRACE(PREFIX_W "look_for_texture: Out of memory while "
    01367                   "creating bitmap\n");
    01368             continue;
    01369         }
    01370
    01371         if (!aglf_check_texture(bmp, format, has_alpha)) {
    01372             TRACE(PREFIX_I "look_for_texture: Texture rejected by driver\n");
    01373             destroy_bitmap(bmp);
    01374             bmp = NULL;
    01375             continue;
    01376         }
    01377
    01378         /* Sort out the glyphs */
    01379         TRACE(PREFIX_I "look_for_texture: Sorting on bmp: %p, beg: %i, "
    01380               "end: %i\n", bmp, beg, end);
    01381
    01382         if (aglf_sort_out_glyphs(bmp, glyphs, beg, end) == TRUE) {
    01383             /* Success? */
    01384             return bmp;
    01385         }
    01386
    01387         /* Failure? Try something else */
    01388         TRACE(PREFIX_I "look_for_texture: Conversion failed\n");
    01389         destroy_bitmap(bmp);
    01390         bmp = NULL;
    01391     }
    01392
    01393     return NULL;
    01394 }
    01395
    01396
    01397
    01398 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 1)
    01399 /* This is only used to render chars from an Allegro font which has the
    01400  * font_vtable_trans vtable. If the target is an 8-bit bitmap, only the alpha
    01401  * channel is used. Otherwise, blit is used, to preserve the alpha channel.
    01402  */
    01403 static int dummy_render_char(AL_CONST FONT* f, int ch, int fg, int bg,
    01404     BITMAP* bmp, int x, int y)
    01405 {
    01406     FONT_COLOR_DATA* cf = (FONT_COLOR_DATA*)(f->data);
    01407     BITMAP *glyph = NULL;
    01408
    01409     while(cf) {
    01410         if(ch >= cf->begin && ch < cf->end) {
    01411             glyph = cf->bitmaps[ch - cf->begin];
    01412             break;
    01413         }
    01414         cf = cf->next;
    01415     }
    01416
    01417     if (glyph)
    01418     {
    01419         if (bitmap_color_depth(bmp) == 8) {
    01420             int gx, gy;
    01421             for (gy = 0; gy < bmp->h; gy++) {
    01422                 for (gx = 0; gx < bmp->w; gx++) {
    01423                     int c = getpixel(glyph, gx, gy);
    01424                     int a = geta(c);
    01425                     putpixel(bmp, x + gx, y + gy, a);
    01426                 }
    01427             }
    01428         }
    01429         else
    01430             blit(glyph, bmp, 0, 0, x, y, glyph->w, glyph->h);
    01431         return bmp->w;
    01432     }
    01433     return 0;
    01434 }
    01435 #endif
    01436
    01437
    01438
    01439 /* Function to draw a character in a bitmap for conversion */
    01440 static int draw_glyphs(BITMAP *bmp, FONT *f, GLint format, int beg, int end,
    01441                        AGL_GLYPH *glyphs) {
    01442     char buf[32];
    01443     int i, j;
    01444
    01445 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 1)
    01446     if (bitmap_color_depth(bmp) == 8 && f->vtable != font_vtable_trans) {
    01447 #else
    01448     if (bitmap_color_depth(bmp) == 8) {
    01449 #endif
    01450         /* Generate an alpha font */
    01451         BITMAP *rgbbmp = create_bitmap_ex(24, bmp->w, bmp->h);
    01452
    01453         if (!rgbbmp) {
    01454             TRACE(PREFIX_E "convert_allegro_font_to_texture: "
    01455                   "Ran out of memory while creating %ix%ix%i bitmap!\n",
    01456                   bmp->w, bmp->h, 24);
    01457             return FALSE;
    01458         }
    01459
    01460         clear_bitmap(rgbbmp);
    01461
    01462         for (i = 0; i < end - beg; i++) {
    01463             usetc(buf + usetc(buf, glyphs[i].glyph_num + beg), 0);
    01464
    01465             textout_ex(rgbbmp, f, buf, glyphs[i].x - glyphs[i].offset_x,
    01466                                       glyphs[i].y - glyphs[i].offset_y, -1, -1);
    01467         }
    01468
    01469         /* Convert back to 8bpp */
    01470         for (j = 0; j < bmp->h; j++) {
    01471             for (i = 0; i < bmp->w; i++) {
    01472                 int pix = _getpixel24(rgbbmp, i, j);
    01473                 int r = getr24(pix);
    01474                 int g = getg24(pix);
    01475                 int b = getb24(pix);
    01476                 int gray = (r * 77 + g * 150 + b * 28 + 255) >> 8;
    01477                 _putpixel(bmp, i, j, MID(0, gray, 255));
    01478             }
    01479         }
    01480         destroy_bitmap(rgbbmp);
    01481     }
    01482     else {
    01483 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 1)
    01484         int (*borrowed_color_vtable)(AL_CONST FONT*, int, int, int, BITMAP*, int, int) = NULL;
    01485
    01486         //In order to keep the alpha channel in textout_ex we borrow
    01487         //the color font vtable which uses maked_blit() instead of
    01488         //draw_trans_sprite() to draw glyphs.
    01489         if (f->vtable == font_vtable_trans) {
    01490             borrowed_color_vtable = f->vtable->render_char;
    01491             f->vtable->render_char = dummy_render_char;
    01492         }
    01493 #endif
    01494
    01495         if (__allegro_gl_get_num_channels(format) == 4) {
    01496             clear_to_color(bmp, bitmap_mask_color(bmp));
    01497         }
    01498         else {
    01499             clear_bitmap(bmp);
    01500         }
    01501
    01502         for (i = 0; i < end - beg; i++) {
    01503             usetc(buf + usetc(buf, glyphs[i].glyph_num + beg), 0);
    01504             textout_ex(bmp, f, buf, glyphs[i].x - glyphs[i].offset_x,
    01505                      glyphs[i].y - glyphs[i].offset_y, -1, -1);
    01506         }
    01507
    01508 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 1)
    01509         if (borrowed_color_vtable) {
    01510             f->vtable->render_char = borrowed_color_vtable;
    01511         }
    01512 #endif
    01513     }
    01514
    01515     return TRUE;
    01516 }
    01517
    01518
    01519
    01520 /* Converts a single font range to a texture.
    01521  * dest - Receives the result.
    01522  * f - The original font.
    01523  * src - The original font data.
    01524  */
    01525 static void aglf_convert_allegro_font_to_texture(FONT_AGL_DATA **dest, FONT *f,
    01526                             void *src, int *height, float scale, GLint format) {
    01527
    01528     int max = 0;
    01529     BITMAP *bmp = NULL;
    01530     int beg = 0, end = 0;
    01531     int max_w, max_h;
    01532     int total_area, gross_area;
    01533
    01534     AGL_GLYPH *glyph_coords;
    01535
    01536     union mixed_ptr dat;
    01537     dat.ptr = src;
    01538
    01539     if (is_mono_font(f)) {
    01540         beg = dat.mf->begin;
    01541         end = dat.mf->end;
    01542         max = dat.mf->end - dat.mf->begin;
    01543         if (format == -1) {
    01544             format = GL_INTENSITY4;
    01545         }
    01546     }
    01547     else if (is_color_font(f)) {
    01548         beg = dat.cf->begin;
    01549         end = dat.cf->end;
    01550         max = dat.cf->end - dat.cf->begin;
    01551         if (format == -1) {
    01552 #if GET_ALLEGRO_VERSION() >= MAKE_VER(4, 2, 1)
    01553             format = (f->vtable == font_vtable_trans ? GL_RGBA8 : GL_RGB8);
    01554 #else
    01555             format = GL_RGB8;
    01556 #endif
    01557         }
    01558     }
    01559
    01560     /* Allocate glyph sizes */
    01561     glyph_coords = malloc(max * sizeof(AGL_GLYPH));
    01562     memset(glyph_coords, 0, max * sizeof(AGL_GLYPH));
    01563
    01564     if (crop_font_range(f, dat.ptr, beg, end, glyph_coords,
    01565                           &total_area, &gross_area, &max_w, &max_h) == FALSE) {
    01566         TRACE(PREFIX_I "convert_allegro_font_to_texture: Unable to crop font "
    01567               "range\n");
    01568         free(glyph_coords);
    01569         return;
    01570     }
    01571
    01572     TRACE(PREFIX_I "convert_allegro_font_to_texture: Total area of glyphs: "
    01573           "%i pixels (%i pixels gross) - max_w: %i, max_h: %i\n",
    01574           total_area, gross_area, max_w, max_h);
    01575
    01576     /* Sort glyphs by width, then height */
    01577     qsort(glyph_coords, end - beg, sizeof(AGL_GLYPH), &sort_glyphs);
    01578
    01579
    01580     /* Now, we look for the appropriate texture size */
    01581     bmp = look_for_texture(beg, end, glyph_coords, max_w, max_h,
    01582                            total_area, format, (*dest)->has_alpha);
    01583
    01584     /* No texture sizes were found - we should split the font up */
    01585     if (!bmp) {
    01586         int height1;
    01587         union mixed_ptr f1, f2;
    01588         FONT_AGL_DATA *dest1, *dest2;
    01589
    01590         free(glyph_coords);
    01591
    01592         dest1 = *(dest);
    01593         dest2 = malloc(sizeof(FONT_AGL_DATA));
    01594
    01595         if (!dest2) {
    01596             TRACE(PREFIX_E "convert_allegro_font_to_texture: "
    01597                   "Out of memory while trying to allocate %i bytes.\n",
    01598                   (int)sizeof(FONT_AGL_DATA));
    01599             return;
    01600         }
    01601
    01602         memset(dest2, 0, sizeof(FONT_AGL_DATA));
    01603
    01604         dest2->next = dest1->next;
    01605         dest1->next = dest2;
    01606         dest2->is_free_chunk = TRUE;
    01607         dest2->format = dest1->format;
    01608         dest2->has_alpha = dest1->has_alpha;
    01609
    01610         if (split_font(f, dat.ptr, &f1.ptr, &f2.ptr) == FALSE) {
    01611             TRACE(PREFIX_E "convert_allegro_font_to_texture: Unable "
    01612                   "to split font!\n");
    01613             dest1->next = dest2->next;
    01614             free(dest2);
    01615             return;
    01616         }
    01617
    01618         aglf_convert_allegro_font_to_texture(&dest1, f, f1.ptr, height, scale,
    01619                                              format);
    01620         height1 = (*height);
    01621         aglf_convert_allegro_font_to_texture(&dest2, f, f2.ptr, height, scale,
    01622                                              format);
    01623         destroy_split_font(f, f1, f2);
    01624
    01625         if (height1 > (*height))
    01626             (*height) = height1;
    01627         (*dest) = dest2;
    01628
    01629         return;
    01630     }
    01631
    01632     TRACE(PREFIX_I "convert_allegro_font_to_texture: Using texture "
    01633           "%ix%ix%i for font conversion.\n", bmp->w, bmp->h,
    01634           bitmap_color_depth(bmp));
    01635
    01636     /* Now that all the glyphs are in place, we draw them into the bitmap */
    01637     if (draw_glyphs(bmp, f, format, beg, end, glyph_coords) == FALSE) {
    01638         destroy_bitmap(bmp);
    01639         free(glyph_coords);
    01640         return;
    01641     }
    01642
    01643     /* Un-Sort glyphs  */
    01644     qsort(glyph_coords, end - beg, sizeof(AGL_GLYPH), &unsort_glyphs);
    01645
    01646 #if (defined SAVE_FONT_SCREENSHOT)
    01647         save_shot(bmp);
    01648 #endif
    01649
    01650     (*dest)->list_base =
    01651              create_textured_font_call_lists(glyph_coords, max, bmp,
    01652                                              scale, height);
    01653
    01654     (*dest)->texture = aglf_upload_texture(bmp, format, (*dest)->has_alpha);
    01655     (*dest)->type = AGL_FONT_TYPE_TEXTURED;
    01656     (*dest)->format = format;
    01657     (*dest)->scale = scale;
    01658     (*dest)->start = beg;
    01659     (*dest)->end = end;
    01660     (*dest)->data = bmp;
    01661     (*dest)->glyph_coords = glyph_coords;
    01662
    01663     return;
    01664 }
    01665
    01666
    01667
    01668 static void aglf_convert_allegro_font_to_bitmap(FONT_AGL_DATA *dest, FONT *f,
    01669                                                        void *src, int *height) {
    01670
    01671     int max = 0;
    01672     int i, j, k;
    01673     int beg = 0, end = 0;
    01674     int mask;
    01675     FONT_GLYPH **glyph;
    01676
    01677     union {
    01678         FONT_MONO_DATA* mf;
    01679         FONT_COLOR_DATA* cf;
    01680         void *ptr;
    01681     } dat;
    01682
    01683     dat.ptr = src;
    01684
    01685     if (is_mono_font(f))
    01686         max = dat.mf->end - dat.mf->begin;
    01687     else if (is_color_font(f))
    01688         max = dat.cf->end - dat.cf->begin;
    01689     else
    01690         return;
    01691
    01692     glyph = malloc(sizeof(FONT_GLYPH*) * max);
    01693
    01694     if (!glyph) {
    01695         TRACE(PREFIX_E "convert_allegro_font_to_bitmap: Ran out of "
    01696               "memory while allocating %i bytes\n", (int)sizeof(FONT_GLYPH));
    01697         return;
    01698     }
    01699
    01700     *height = f->height;
    01701
    01702     if (is_mono_font(f)) {
    01703
    01704         /* for each glyph */
    01705         for (i = 0; i < max; i++) {
    01706             FONT_GLYPH *oldgl = dat.mf->glyphs[i];
    01707
    01708             int size = sizeof(FONT_GLYPH) + ((oldgl->w + 31) / 32) * 4
    01709                                                                      * oldgl->h;
    01710
    01711             /* create new glyph */
    01712             FONT_GLYPH *newgl = (FONT_GLYPH*)malloc(size);
    01713
    01714             if (!newgl)
    01715                 break;
    01716
    01717             memset(newgl, 0, size);
    01718
    01719             newgl->w = oldgl->w;
    01720             newgl->h = oldgl->h;
    01721
    01722             /* update the data */
    01723             for (j = 0; j < oldgl->h; j++) {
    01724                 for (k = 0; k < ((oldgl->w + 7) / 8); k++) {
    01725                     int addr = (oldgl->h - j - 1) * ((oldgl->w + 31) / 32) * 4
    01726                                + k;
    01727                     newgl->dat[addr] = oldgl->dat[j * ((oldgl->w + 7) / 8) + k];
    01728                 }
    01729             }
    01730
    01731             glyph[i] = newgl;
    01732         }
    01733     }
    01734     /* Reduce to 1 bit */
    01735     else if (is_color_font(f)) {
    01736         /* for each glyph */
    01737         for (i = 0; i < max; i++) {
    01738
    01739             int size;
    01740             BITMAP *oldgl = dat.cf->bitmaps[i];
    01741             FONT_GLYPH *newgl;
    01742
    01743             mask = bitmap_mask_color(oldgl);
    01744
    01745             size = sizeof(FONT_GLYPH) + ((oldgl->w + 31) / 32) * 4 * oldgl->h;
    01746
    01747             /* create new glyph */
    01748             newgl = (FONT_GLYPH*)malloc(size);
    01749
    01750             if (!newgl)
    01751                 break;
    01752
    01753             memset(newgl, 0, size);
    01754
    01755             newgl->w = oldgl->w;
    01756             newgl->h = oldgl->h;
    01757
    01758             /* update the data */
    01759             for (j = 0; j < oldgl->h; j++) {
    01760                 for (k = 0; k < oldgl->w; k++) {
    01761                     int addr = (oldgl->h - j - 1) * ((oldgl->w + 31) / 32) * 4
    01762                              + (k / 8);
    01763                     newgl->dat[addr] |= (getpixel(oldgl, k, j) == mask)
    01764                                      ? 0 : (1 << (k & 7));
    01765                 }
    01766             }
    01767
    01768             glyph[i] = newgl;
    01769         }
    01770     }
    01771     /* Create call lists */
    01772     {
    01773         GLuint list = glGenLists(max);
    01774
    01775         for (i = 0; i < max; i++) {
    01776             glNewList(list + i, GL_COMPILE);
    01777
    01778             glBitmap(glyph[i]->w, glyph[i]->h, 0, 0, glyph[i]->w, 0,
    01779                      glyph[i]->dat);
    01780
    01781             glEndList();
    01782         }
    01783         dest->list_base = list;
    01784     }
    01785
    01786     dest->is_free_chunk = 0;
    01787     dest->type = AGL_FONT_TYPE_BITMAP;
    01788     dest->start = beg;
    01789     dest->end = end;
    01790     dest->data = glyph;
    01791
    01792     return;
    01793 }
    01794
    01795
    01796
    01797 static int aglf_check_texture(BITMAP *bmp, GLint format, int has_alpha) {
    01798
    01799     int flags = AGL_TEXTURE_FLIP | AGL_TEXTURE_MIPMAP;
    01800
    01801     if (format == GL_ALPHA4 || format == GL_ALPHA8 || format == GL_ALPHA
    01802      || format == GL_INTENSITY4 || format == GL_INTENSITY8
    01803      || format == GL_INTENSITY
    01804      || format == GL_LUMINANCE4 || format == GL_LUMINANCE8
    01805      || format == GL_LUMINANCE
    01806      || format == 1) {
    01807         flags |= AGL_TEXTURE_ALPHA_ONLY;
    01808     }
    01809     else if (format == GL_RGBA8) {
    01810         if (has_alpha) {
    01811             flags |= AGL_TEXTURE_HAS_ALPHA;
    01812         }
    01813         else {
    01814             flags |= AGL_TEXTURE_MASKED;
    01815         }
    01816     }
    01817
    01818     return allegro_gl_check_texture_ex(flags, bmp, format);
    01819 }
    01820
    01821
    01822
    01823 static GLuint aglf_upload_texture(BITMAP *bmp, GLint format, int has_alpha) {
    01824
    01825     int flags = AGL_TEXTURE_FLIP | AGL_TEXTURE_MIPMAP;
    01826     GLuint texture;
    01827
    01828     if (format == GL_ALPHA4 || format == GL_ALPHA8 || format == GL_ALPHA
    01829      || format == GL_INTENSITY4 || format == GL_INTENSITY8
    01830      || format == GL_INTENSITY
    01831      || format == GL_LUMINANCE4 || format == GL_LUMINANCE8
    01832      || format == GL_LUMINANCE
    01833      || format == 1) {
    01834         flags |= AGL_TEXTURE_ALPHA_ONLY;
    01835     }
    01836     else if (__allegro_gl_get_num_channels(format) == 4) {
    01837         if (has_alpha) {
    01838             flags |= AGL_TEXTURE_HAS_ALPHA;
    01839         }
    01840         else {
    01841             flags |= AGL_TEXTURE_MASKED;
    01842         }
    01843     }
    01844
    01845     TRACE(PREFIX_I "Want texture format: %s\n",
    01846         __allegro_gl_get_format_description(format));
    01847     texture = allegro_gl_make_texture_ex(flags, bmp, format);
    01848     TRACE(PREFIX_I "Texture ID is: %u\n", texture);
    01849
    01850     return texture;
    01851 }
    01852