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 

Generated on Sun Nov 11 15:52:55 2007 for AllegroGL by  doxygen 1.5.2