From d2631e9fe92874338940519811bcbff0b667ac99 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Wed, 2 Nov 2011 15:46:24 +0100 Subject: [PATCH] Replacing old font codepage-map loader with a loader which allocates space for glyphs on textures using the lightmap-block allocator. No unloading or cleanup done yet - that's a TODO --- ft2.c | 615 ++++++++++++++++++++++++++++++++++++++++++++++++- ft2.h | 74 +++++- ft2_fontdefs.h | 51 +++- gl_draw.c | 131 ++++++++--- gl_textures.c | 30 +++ model_brush.c | 2 +- model_shared.c | 8 +- model_shared.h | 2 +- 8 files changed, 856 insertions(+), 57 deletions(-) diff --git a/ft2.c b/ft2.c index e1c2fe49..7c869c69 100644 --- a/ft2.c +++ b/ft2.c @@ -142,6 +142,11 @@ static mempool_t *font_mempool= NULL; /// FreeType library handle static FT_Library font_ft2lib = NULL; +/// GlyphTex texture list +static ft2_glyphtex_t *font_glyphtex_start = NULL; +static ft2_glyphtex_t *font_glyphtex_end = NULL; +static int font_glyphtex_counter = 0; + #define POSTPROCESS_MAXRADIUS 8 typedef struct { @@ -313,6 +318,8 @@ void font_start(void) Font_CloseLibrary(); return; } + + font_glyphtex_start = font_glyphtex_end = NULL; } void font_shutdown(void) @@ -363,6 +370,88 @@ ft2_font_t *Font_Alloc(void) return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t)); } +ft2_glyphtex_t *Font_New_GlyphTex(int width, int height) +{ + int bytesPerPixel = 4; + int tp; + char vabuf[512]; + unsigned char *gtdata; + int gtpitch; + + ft2_glyphtex_t *gt = (ft2_glyphtex_t *)Mem_Alloc(font_mempool, sizeof(ft2_glyphtex_t)); + if (!font_glyphtex_start) + font_glyphtex_start = gt; + if (font_glyphtex_end) { + font_glyphtex_end->next = gt; + gt->prev = font_glyphtex_end; + font_glyphtex_end = gt; + } else { + font_glyphtex_end = gt; + } + + if (r_font_use_alpha_textures.integer) + bytesPerPixel = 1; + + gt->width = width; + gt->height = height; + gt->texflags = TEXF_ALPHA | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0); + gt->bytesPerPixel = bytesPerPixel; + gtpitch = width * bytesPerPixel; + gt->tex = NULL; + gtdata = (unsigned char*)Mem_Alloc(font_mempool, width * height * bytesPerPixel); + //memset(gt->data, 0, width * height * bytesPerPixel); + // Initialize as white texture with zero alpha + tp = 0; + while (tp < height*gtpitch) + { + if (bytesPerPixel == 4) + { + gtdata[tp++] = 0xFF; + gtdata[tp++] = 0xFF; + gtdata[tp++] = 0xFF; + } + gtdata[tp++] = 0x00; + } + // now load the texture + gt->tex = R_LoadTexture2D(drawtexturepool, va(vabuf, sizeof(vabuf), "*font-%i", font_glyphtex_counter), + gt->width, gt->height, gtdata, + (r_font_use_alpha_textures.integer ? TEXTYPE_ALPHA : TEXTYPE_RGBA), + gt->texflags, -1, NULL); + Mem_Free(gtdata); + + Mod_AllocLightmap_Init(>->blockstate, width, height, font_mempool); + + gt->glyph_count = 0; + //gt->lastusedframe = 0; + + gt->first_glyph = gt->last_glyph = NULL; + + ++font_glyphtex_counter; + Con_Printf("Glyphtextures: %i\n", font_glyphtex_counter); + return gt; +} + +void Font_Free_GlyphTex(ft2_glyphtex_t *gt); +void Font_Free_GlyphTex(ft2_glyphtex_t *gt) +{ + ft2_glyphtex_t *prev = gt->prev; + ft2_glyphtex_t *next = gt->next; + + if (prev) + prev->next = next; + if (next) + next->prev = prev; + + //Mem_Free(gt->data); + Mod_AllocLightmap_Free(>->blockstate); + + // FIXME: Go through glyphs now + + R_FreeTexture(gt->tex); + gt->tex = NULL; + Mem_Free(gt); +} + static qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment) { ft2_attachment_t *na; @@ -405,7 +494,8 @@ float Font_SnapTo(float val, float snapwidth) } static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font); -static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only); +static void Font_Postprocess(ft2_font_t *fnt, unsigned char *imagedata, int pitch, int bpp, int w, int h, int *pad_l, int *pad_r, int *pad_t, int *pad_b); +//static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only); qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt) { int s, count, i; @@ -469,7 +559,10 @@ qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt) count = 0; for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s) { - if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true)) + fb->req_sizes[s] = Font_VirtualToRealSize(dpfnt->req_sizes[s]); + if (fb->req_sizes[s] < 2 || fb->req_sizes[s] > 200) { + // Bogus size check needs to be done here already + } else ++count; } if (!count) @@ -496,8 +589,25 @@ qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt) count = 0; for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s) { - if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false)) + int gpad_l, gpad_r, gpad_t, gpad_b; + ft2_font_size_t *fsize = &ft2->font_sizes[s]; + ft2->req_sizes[s] = Font_VirtualToRealSize(dpfnt->req_sizes[s]); + if (ft2->req_sizes[s] < 2 || ft2->req_sizes[s] > 200) { + // Bogus size check needs to be done here already + } else ++count; + + // Fill fontsize here now, since we have no Font_LoadSize anymore + Font_Postprocess(ft2, NULL, 0, 4, fsize->size*2, fsize->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b); + fsize->size = ft2->req_sizes[s]; + fsize->sfx = (1.0/64.0)/(double)fsize->size; + fsize->sfy = (1.0/64.0)/(double)fsize->size; + fsize->glyphSize = fsize->size * 2 + max(gpad_l + gpad_r, gpad_t + gpad_b); + /* Not required anymore, all textures are 1024x1024 for now + if (!(r_font_nonpoweroftwo.integer && vid.support.arb_texture_non_power_of_two)) + fsize->glyphSize = CeilPowerOf2(fsize->glyphSize); + */ + fsize->intSize = -1; } if (!count) { @@ -790,7 +900,9 @@ static void Font_Postprocess(ft2_font_t *fnt, unsigned char *imagedata, int pitc } static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size); -static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap); +//static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap); +#if 0 +// DEPRECATED: static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only) { int map_index; @@ -877,6 +989,7 @@ static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only) } return true; } +#endif int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh) { @@ -886,7 +999,6 @@ int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh) int matchsize = -10000; int m; float fsize_x, fsize_y; - ft2_font_map_t **maps = font->font_maps; fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value; if(outw && *outw) @@ -909,15 +1021,15 @@ int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh) for (m = 0; m < MAX_FONT_SIZES; ++m) { - if (!maps[m]) + if (font->req_sizes[m] < 1) continue; // "round up" to the bigger size if two equally-valued matches exist - nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y)); - if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size)) + nval = 0.5 * (fabs(font->font_sizes[m].size - fsize_x) + fabs(font->font_sizes[m].size - fsize_y)); + if (match == -1 || nval < value || (nval == value && matchsize < font->font_sizes[m].size)) { value = nval; match = m; - matchsize = maps[m]->size; + matchsize = font->font_sizes[m].size; if (value == 0) // there is no better match break; } @@ -925,18 +1037,21 @@ int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh) if (value <= r_font_size_snapping.value) { // do NOT keep the aspect for perfect rendering - if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height; - if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width; + if (outh) *outh = font->font_sizes[match].size * vid_conheight.value / vid.height; + if (outw) *outw = font->font_sizes[match].size * vid_conwidth.value / vid.width; } return match; } +#if 0 +// DEPRECATED: ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index) { if (index < 0 || index >= MAX_FONT_SIZES) return NULL; return font->font_maps[index]; } +#endif static qboolean Font_SetSize(ft2_font_t *font, float w, float h) { @@ -964,6 +1079,16 @@ static qboolean Font_SetSize(ft2_font_t *font, float w, float h) return true; } +qboolean Font_GetKerning(ft2_font_t *font, int size_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy) +{ + // FIXME: Currently dummied because we are focusing on glyph loading + if (outx) *outx = 0; + if (outy) *outy = 0; + return false; +} + +#if 0 +// DEPRECATED: qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy) { ft2_font_map_t *fmap; @@ -1026,7 +1151,9 @@ qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, { return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy); } +#endif +/* static void UnloadMapRec(ft2_font_map_t *map) { if (map->pic) @@ -1038,6 +1165,7 @@ static void UnloadMapRec(ft2_font_map_t *map) UnloadMapRec(map->next); Mem_Free(map); } +*/ void Font_UnloadFont(ft2_font_t *font) { @@ -1057,6 +1185,7 @@ void Font_UnloadFont(ft2_font_t *font) font->attachmentcount = 0; font->attachments = NULL; } + /* for (i = 0; i < MAX_FONT_SIZES; ++i) { if (font->font_maps[i]) @@ -1065,6 +1194,7 @@ void Font_UnloadFont(ft2_font_t *font) font->font_maps[i] = NULL; } } + */ if (ft2_dll) { if (font->face) @@ -1100,6 +1230,465 @@ static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size) } } +static glyph_t* Font_Glyph_Find(ft2_font_size_t *fsize, Uchar ch) +{ + ft2_glyph_tree_t *tree; + unsigned int i, idx; + + if (ch < 256) + return fsize->main_glyphs[ch]; + + tree = &fsize->glyphtree; + for (i = 0; i < (2 * sizeof(Uchar) - 1); ++i) { + idx = (ch & 0x0F); + if (!tree->next[idx]) + return NULL; + tree = tree->next[idx]; + ch >>= 4; + } + + idx = (ch & 0x0F); + return tree->next[idx]->endglyph; +} + +static void Font_Glyph_Insert(ft2_font_size_t *fsize, Uchar ch, glyph_t *glyph) +{ + ft2_glyph_tree_t *tree; + unsigned int i, idx; + if (ch < 256) + { + fsize->main_glyphs[ch] = glyph; + return; + } + + tree = &fsize->glyphtree; + for (i = 0; i < (2 * sizeof(Uchar)); ++i) { + idx = (ch & 0x0F); + if (!tree->next[idx]) { + tree->next[idx] = (ft2_glyph_tree_t*)Mem_Alloc(font_mempool, sizeof(*tree)); + memset(tree->next[idx], 0, sizeof(*(tree->next[idx]))); + } + tree = tree->next[idx]; + ch >>= 4; + } + tree->endglyph = glyph; + return; +} + +static void Font_Glyph_Free(glyph_t *glyph); +static void Font_GlyphTree_Free(ft2_glyph_tree_t *tree) +{ + unsigned int i; + for (i = 0; i < sizeof(tree->next) / sizeof(tree->next[0]); ++i) + { + if (tree->next[i]) + Font_GlyphTree_Free(tree->next[i]); + } + if (tree->endglyph) + { + Font_Glyph_Free(tree->endglyph); + } + Mem_Free(tree); +} + +static void Font_Glyph_Free(glyph_t *glyph) +{ + // FIXME: tell the glyphtexture that this glyph is now gone... + Mem_Free(glyph); +} + +glyph_t* Font_GetGlyph(ft2_font_t *font, int sizeindex, float _w, float _h, Uchar _ch) +{ + // Currently we only care about size_index rather than w, h + // In the future we might allow a cvar to allocat glyphs for any size directly, but + // this would be dangerous unless we also free glyph frequently. + // Also it's a bad idea when there's scaling involved. + // ... Unless it all performs very well ... + + //assert(sizeindex >= 0 && sizeindex < MAX_FONT_SIZES && "Font_GetGlyph: Invalid size index: out of array bounds!"); + + ft2_font_size_t *fsize; + glyph_t *glyph; + + ft2_font_t *usefont; + + ft2_glyphtex_t *glyphtex = font_glyphtex_end; + int status; + FT_ULong ch; + FT_Int32 load_flags; + int gpad_l, gpad_r, gpad_t, gpad_b; + unsigned char *data; + int gR, gC; // glyph position: row, column - now x, y actually + FT_Face fontface; + + int allocw = 0, alloch = 0; + int tp; + + // Freetype vars: + FT_ULong ft_glyphIndex; + int w, h, x, y; + FT_GlyphSlot ft_glyph; + FT_Bitmap *bmp; + unsigned char *imagedata = NULL, *dst, *src; + FT_Face ft_face; + int pad_l, pad_r, pad_t, pad_b; + + // info copied from glyphtex later on: + int bytesPerPixel; + int pitch; + + // In case the first glyphtex texture was not yet loaded, load it. + if (!glyphtex) { + glyphtex = Font_New_GlyphTex(FONT_GLYPHTEX_WIDTH, FONT_GLYPHTEX_HEIGHT); + } + + if (sizeindex < 0 || sizeindex > MAX_FONT_SIZES) { + Con_Printf("ERROR: out of bounds size-index in GetGlyph: %i\n", sizeindex); + return NULL; + } + + fsize = &font->font_sizes[sizeindex]; + + if ( (glyph = Font_Glyph_Find(fsize, _ch)) ) { + return glyph; + } + + // Glyph is new: create it + ch = (FT_ULong)_ch; + + // NOTE: When rendering: r_font_use_alpha_textures is NOT to be used, but instead + // the texture's alpha-texture value. + // Also: We might want to actually finish alpha-textures for fonts... + // We'd require a shader change to make proper use of it tho: to treat that single alpha value and assume + // the color to be white + quakecolor(^x) + + if (font->image_font) + fontface = (FT_Face)font->next->face; + else + fontface = (FT_Face)font->face; + + switch(font->settings->antialias) + { + case 0: + switch(font->settings->hinting) + { + case 0: + load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME; + break; + case 1: + case 2: + load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME; + break; + default: + case 3: + load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME; + break; + } + break; + default: + case 1: + switch(font->settings->hinting) + { + case 0: + load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL; + break; + case 1: + load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; + break; + case 2: + load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL; + break; + default: + case 3: + load_flags = FT_LOAD_TARGET_NORMAL; + break; + } + break; + } + + if (font->image_font && fsize->intSize < 0) + fsize->intSize = fsize->size; + + if (fsize->intSize < 0) + { + if ((fsize->intSize = Font_SearchSize(font, fontface, fsize->size)) <= 0) { + return NULL; + } + Con_DPrintf("Using size: %f for requested size %f\n", fsize->intSize, fsize->size); + } + + if (!font->image_font && !Font_SetSize(font, fsize->intSize, fsize->intSize)) + { + Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, fsize->size); + return NULL; + } + + glyph = (glyph_t*)Mem_Alloc(font_mempool, sizeof(glyph_t)); + if (!glyph) + { + Con_Printf("ERROR: Out of memory when loading glyph %u for %s\n", (unsigned int)_ch, font->name); + return NULL; + } + + // Freetype loading + ft_face = (FT_Face)font->face; + usefont = NULL; + if (font->image_font && ch <= 0xFF && img_fontmap[ch]) + { + glyph->image = true; + return glyph; + } + + ft_glyphIndex = qFT_Get_Char_Index(ft_face, ch); + if (ft_glyphIndex == 0) + { + // by convention, 0 is the "missing-glyph"-glyph + // try to load from a fallback font + for (usefont = font->next; usefont != NULL; usefont = usefont->next) + { + if (!Font_SetSize(usefont, fsize->intSize, fsize->intSize)) + continue; + ft_face = (FT_Face)usefont->face; + ft_glyphIndex = qFT_Get_Char_Index(ft_face, ch); + if (ft_glyphIndex == 0) + continue; + status = qFT_Load_Glyph(ft_face, ft_glyphIndex, FT_LOAD_RENDER | load_flags); + if (status) + continue; + break; + } + if (!usefont) + { + // Use the missing-glyph glyph + ft_face = (FT_Face)font->face; + ft_glyphIndex = 0; + } + } + + if (!usefont) + { + usefont = font; + ft_face = (FT_Face)font->face; + status = qFT_Load_Glyph(ft_face, ft_glyphIndex, FT_LOAD_RENDER | load_flags); + if (status) + { + Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name); + Mem_Free(glyph); + return NULL; + } + } + + Font_Postprocess(font, NULL, 0, glyphtex->bytesPerPixel, fsize->size*2, fsize->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b); + + //pitch = glyphtex->pitch; + //data = glyphtex->data; + + ft_glyph = ft_face->glyph; + bmp = &ft_glyph->bitmap; + + w = bmp->width; + h = bmp->rows; + + if (w > (fsize->glyphSize - gpad_l - gpad_r) || h > (fsize->glyphSize - gpad_t - gpad_b)) + { + Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g, %i x %i\n", (unsigned long)ch, font->name, fsize->size, w, h); + if (w > fsize->glyphSize) + w = fsize->glyphSize - gpad_l - gpad_r; + if (h > fsize->glyphSize) + h = fsize->glyphSize; + } + + // Allocate a block on the glyph texture: + allocw = w + gpad_l + gpad_r; + alloch = h + gpad_t + gpad_b; + if (allocw < w || alloch < h) { + Con_Printf("ERROR: Glyph bitmap is bigger than expected: %i < %i, %i < %i\n", allocw, w, alloch, h); + Mem_Free(glyph); + return NULL; + } + if (!Mod_AllocLightmap_Block(&glyphtex->blockstate, allocw, alloch, &gC, &gR)) + { + // Texture full: make new one + glyphtex = Font_New_GlyphTex(FONT_GLYPHTEX_WIDTH, FONT_GLYPHTEX_HEIGHT); + if (!Mod_AllocLightmap_Block(&glyphtex->blockstate, allocw, alloch, &gC, &gR)) + { + Con_Printf("ERROR: Glyph %lu is too big to fit on a single texture, this is a bogus size. Font %s, size %g, %i x %i\n", + (unsigned long)ch, font->name, fsize->size, w, h); + Mem_Free(glyph); + return NULL; + } + } + + glyph->glyphtex = glyphtex; + glyph->tex = glyphtex->tex; + + // Glyph is rendered and we have space allocated on a glyph-texture. + // We're good to go, ready for postprocessing and the rest. + + bytesPerPixel = glyphtex->bytesPerPixel; + //pitch = glyphtex->pitch; + //imagedata = glyphtex->data + gR * pitch + gC * bytesPerPixel; + pitch = glyphtex->bytesPerPixel * allocw; + data = (unsigned char *)Mem_Alloc(font_mempool, alloch * pitch); + imagedata = data + gpad_t * pitch + gpad_l * bytesPerPixel; + + tp = 0; + while (tp < pitch * alloch) + { + if (bytesPerPixel == 4) { + data[tp++] = 0xFF; + data[tp++] = 0xFF; + data[tp++] = 0xFF; + } + data[tp++] = 0x00; + } + + switch (bmp->pixel_mode) + { + case FT_PIXEL_MODE_MONO: + if (developer_font.integer) + Con_DPrint("glyphinfo: Pixel Mode: MONO\n"); + break; + case FT_PIXEL_MODE_GRAY2: + if (developer_font.integer) + Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n"); + break; + case FT_PIXEL_MODE_GRAY4: + if (developer_font.integer) + Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n"); + break; + case FT_PIXEL_MODE_GRAY: + if (developer_font.integer) + Con_DPrint("glyphinfo: Pixel Mode: GRAY\n"); + break; + default: + if (developer_font.integer) + Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode); + Mem_Free(data); + Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, fsize->size, bmp->pixel_mode); + Mem_Free(glyph); + return false; + } + for (y = 0; y < h; ++y) + { + dst = imagedata + y * pitch; + src = bmp->buffer + y * bmp->pitch; + + switch (bmp->pixel_mode) + { + case FT_PIXEL_MODE_MONO: + dst += bytesPerPixel - 1; // shift to alpha byte + for (x = 0; x < bmp->width; x += 8) + { + unsigned char ch = *src++; + *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel; + *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel; + *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel; + *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel; + *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel; + *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel; + *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel; + *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel; + } + break; + case FT_PIXEL_MODE_GRAY2: + dst += bytesPerPixel - 1; // shift to alpha byte + for (x = 0; x < bmp->width; x += 4) + { + unsigned char ch = *src++; + *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel; + *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel; + *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel; + *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel; + } + break; + case FT_PIXEL_MODE_GRAY4: + dst += bytesPerPixel - 1; // shift to alpha byte + for (x = 0; x < bmp->width; x += 2) + { + unsigned char ch = *src++; + *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel; + *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel; + } + break; + case FT_PIXEL_MODE_GRAY: + // in this case pitch should equal width + for (tp = 0; tp < bmp->pitch; ++tp) + dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes + + //memcpy((void*)dst, (void*)src, bmp->pitch); + //dst += bmp->pitch; + break; + default: + break; + } + } + + pad_l = gpad_l; + pad_r = gpad_r; + pad_t = gpad_t; + pad_b = gpad_b; + Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b); + + R_UpdateTexture(glyphtex->tex, data, gC, gR, 0, allocw, alloch, 8 * bytesPerPixel); + Mem_Free(data); + + R_SaveTextureTGAFile(glyphtex->tex, "font-texture.tga", true); + + glyph->image = false; + { + // old way + // double advance = (double)glyph->metrics.horiAdvance * map->sfx; + + double bearingX = (ft_glyph->metrics.horiBearingX / 64.0) / fsize->size; + //double bearingY = (ft_glyph->metrics.horiBearingY >> 6) / fsize->size; + double advance = (ft_glyph->advance.x / 64.0) / fsize->size; + //double mWidth = (ft_glyph->metrics.width >> 6) / fsize->size; + //double mHeight = (ft_glyph->metrics.height >> 6) / fsize->size; + + glyph->txmin = ( (double)(gC /* fsize->glyphSize*/) + (double)(gpad_l - pad_l) ) / ( (double)glyphtex->width ); + glyph->txmax = glyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( glyphtex->width ); + glyph->tymin = ( (double)(gR /* fsize->glyphSize*/) + (double)(gpad_r - pad_r) ) / ( (double)glyphtex->height ); + glyph->tymax = glyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)glyphtex->height ); + //Con_Printf("%f %f %f %f (%i - %i and %i - %i) %c\n", glyph->txmin, glyph->txmax, glyph->tymin, glyph->tymax, gpad_l, pad_l, gpad_r, pad_r, (char)_ch); + //glyph->vxmin = bearingX; + //glyph->vxmax = bearingX + mWidth; + glyph->vxmin = (ft_glyph->bitmap_left - pad_l) / fsize->size; + glyph->vxmax = glyph->vxmin + (bmp->width + pad_l + pad_r) / fsize->size; // don't ask + //glyph->vymin = -bearingY; + //glyph->vymax = mHeight - bearingY; + glyph->vymin = (-ft_glyph->bitmap_top - pad_t) / fsize->size; + glyph->vymax = glyph->vymin + (bmp->rows + pad_t + pad_b) / fsize->size; + //Con_Printf("dpi = %f %f (%f %d) %d %d\n", bmp->width / (glyph->vxmax - glyph->vxmin), bmp->rows / (glyph->vymax - glyph->vymin), map->size, map->ft_glyphSize, (int)fontface->size->metrics.x_ppem, (int)fontface->size->metrics.y_ppem); + //glyph->advance_x = advance * usefont->size; + //glyph->advance_x = advance; + glyph->advance_x = Font_SnapTo(advance, 1 / fsize->size); + glyph->advance_y = 0; + + if (developer_font.integer) + { + Con_DPrintf("glyphinfo: glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR); + Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, fsize->sfx, (unsigned long)ft_glyph->metrics.horiBearingX); + if (ch >= 32 && ch <= 128) + Con_DPrintf("glyphinfo: Character: %c\n", (int)ch); + Con_DPrintf("glyphinfo: Vertex info:\n"); + Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", glyph->vxmin, glyph->vxmax); + Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", glyph->vymin, glyph->vymax); + Con_DPrintf("glyphinfo: Texture info:\n"); + Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", glyph->txmin, glyph->txmax); + Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", glyph->tymin, glyph->tymax); + Con_DPrintf("glyphinfo: Advance: %f, %f\n", glyph->advance_x, glyph->advance_y); + } + } + + // Insert the glyph into the glyph tree + Font_Glyph_Insert(fsize, _ch, glyph); + + return glyph; +} + +#if 0 +// DEPRECATED: static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap) { char map_identifier[MAX_QPATH]; @@ -1572,7 +2161,10 @@ static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ *outmap = map; return true; } +#endif +#if 0 +// DEPRECATED: qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap) { if (map_index < 0 || map_index >= MAX_FONT_SIZES) @@ -1591,3 +2183,4 @@ ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch) return NULL; return start; } +#endif diff --git a/ft2.h b/ft2.h index e8110a72..8b4f3498 100644 --- a/ft2.h +++ b/ft2.h @@ -20,6 +20,7 @@ * Contains the non-FreeType2 version of characters. */ +typedef struct ft2_glyphtex_s ft2_glyphtex_t; typedef struct ft2_font_map_s ft2_font_map_t; typedef struct ft2_attachment_s ft2_attachment_t; #define ft2_oldstyle_map ((ft2_font_map_t*)-1) @@ -30,6 +31,34 @@ typedef struct ft2_kerning_s ft2_kernvec kerning[256][256]; /* kerning[left char][right char] */ } ft2_kerning_t; +typedef struct ft2_glyph_tree_s +{ + struct ft2_glyph_tree_s* next[0x10]; + struct glyph_s *endglyph; +} ft2_glyph_tree_t; + +typedef struct ft2_font_size_s +{ + float size; + // the actual size used in the freetype code + // by convention, the requested size is the height of the font's bounding box. + float intSize; + int glyphSize; + + // For quick access, the first 256 glyphs + // are stored here - but not preloaded! + struct glyph_s *main_glyphs[256]; + ft2_glyph_tree_t glyphtree; + + // contains the kerning information for the first 256 characters + // for the other characters, we will lookup the kerning information + // ft2_kerning_t kerning; + // safes us the trouble of calculating these over and over again + double sfx, sfy; + // the width_of for the image-font, pixel-snapped for this size + float width_of[256]; +} ft2_font_size_t; + typedef struct ft2_font_s { char name[64]; @@ -47,20 +76,49 @@ typedef struct ft2_font_s //fs_offset_t datasize; void *face; + ft2_font_size_t font_sizes[MAX_FONT_SIZES]; + int num_size; + +/* DEPRECATED: // an unordered array of ordered linked lists of glyph maps for a specific size ft2_font_map_t *font_maps[MAX_FONT_SIZES]; int num_sizes; +*/ // attachments size_t attachmentcount; ft2_attachment_t *attachments; ft2_settings_t *settings; - // fallback mechanism struct ft2_font_s *next; + + // Virtual2Real translated sizes + float req_sizes[MAX_FONT_SIZES]; } ft2_font_t; +typedef struct glyph_s +{ + qboolean image; + // we keep the quad coords here only currently + // if you need other info, make Font_LoadMapForIndex fill it into this slot + float txmin; // texture coordinate in [0,1] + float txmax; + float tymin; + float tymax; + float vxmin; + float vxmax; + float vymin; + float vymax; + float advance_x; + float advance_y; + + ft2_glyphtex_t *glyphtex; + // So we don't need to care about what glyphtex looks like outside the font code: + rtexture_t *tex; +} glyph_t; + + void Font_CloseLibrary(void); void Font_Init(void); qboolean Font_OpenLibrary(void); @@ -70,12 +128,18 @@ void Font_UnloadFont(ft2_font_t *font); // for example, you render at a size of 12.4, and a font of size 12 has been loaded // in such a case, *outw and *outh are set to 12, which is often a good alternative size int Font_IndexForSize(ft2_font_t *font, float size, float *outw, float *outh); -ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index); qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt); -qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy); -qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy); +// We use sizeindex to make sure we don't try finding the index anew every time... +qboolean Font_GetKerning(ft2_font_t *font, int sizeindex, float w, float h, Uchar left, Uchar right, float *outx, float *outy); +glyph_t* Font_GetGlyph(ft2_font_t *font, int sizeindex, float w, float h, Uchar ch); float Font_VirtualToRealSize(float sz); float Font_SnapTo(float val, float snapwidth); + + + + +//ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index); +//qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy); // since this is used on a font_map_t, let's name it FontMap_* -ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch); +//ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch); #endif // DP_FREETYPE2_H__ diff --git a/ft2_fontdefs.h b/ft2_fontdefs.h index 3f08187d..e475a63a 100644 --- a/ft2_fontdefs.h +++ b/ft2_fontdefs.h @@ -1,6 +1,55 @@ #ifndef FT2_PRIVATE_H__ #define FT2_PRIVATE_H__ +// FIXME: When all this is done: rename +// this to glyph_slot_s again... + +// How big textures should be... +#define FONT_GLYPHTEX_WIDTH 512 +#define FONT_GLYPHTEX_HEIGHT 512 + +typedef struct ft2_glyphtex_s +{ + int width, height; + int texflags; + + rtexture_t *tex; + //unsigned char *data; + int bytesPerPixel; + //int pitch; + /* FIXME: remove this, I have it here so I don't need to switch over to model_shared.h to see the defs :P +void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, int width, int height); +void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state); +void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state); +qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy); +*/ + mod_alloclightmap_state_t blockstate; + + // This acts as a reference count for glyphs + // When a glyph is loaded, this gets increased + // When a font is unloaded, this is decreased + // for every glyph on this texture. If we hit + // 0, we remove the texture completely. + // The goal is that if there's only few characters + // being used, but many characters allocated, we + // destroy this texture to have the used glyphs be + // moved to a more-active texture. + int glyph_count; + //int lastusedframe; // <- useful, we unload unused images + + // NOTE: We also need a way to remove glyphs + // from still-existing fonts when we remove + // this texture, to force them to reload them. + // NOTE: Rather then re-rendering, we could just copy them directly. + struct glyph_s *first_glyph; + struct glyph_s *last_glyph; // <- for faster insertion + + struct ft2_glyphtex_s *prev; + struct ft2_glyphtex_s *next; +} ft2_glyphtex_t; + +ft2_glyphtex_t *Font_New_GlyphTex(int width, int height); + // anything should work, but I recommend multiples of 8 // since the texture size should be a power of 2 #define FONT_CHARS_PER_LINE 16 @@ -55,7 +104,7 @@ struct ft2_attachment_s }; //qboolean Font_LoadMapForIndex(ft2_font_t *font, Uchar _ch, ft2_font_map_t **outmap); -qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap); +//qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap); void font_start(void); void font_shutdown(void); diff --git a/gl_draw.c b/gl_draw.c index dab25100..6ed29870 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -765,11 +765,18 @@ void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, { for (i = 0; i < MAX_FONT_SIZES; ++i) { + /* ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i); if (!map) break; for(ch = 0; ch < 256; ++ch) map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size); + */ + ft2_font_size_t *ftsize = &fnt->ft2->font_sizes[i]; + for (ch = 0; ch < 256; ++ch) { + if (ftsize->size > 0) + ftsize->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/ftsize->size); + } } } @@ -1323,16 +1330,24 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max int colorindex = STRING_COLOR_DEFAULT; size_t i; float x = 0; - Uchar ch, mapch, nextch; + Uchar ch, nextch; Uchar prevch = 0; // used for kerning int tempcolorindex; float kx; - int map_index = 0; size_t bytes_left; + /* DEPRECATED: + int map_index = 0; ft2_font_map_t *fontmap = NULL; ft2_font_map_t *map = NULL; //ft2_font_map_t *prevmap = NULL; + */ + int size_index = 0; ft2_font_t *ft2 = fnt->ft2; + ft2_font_size_t *ft2size = NULL; + glyph_t *glyph = NULL; + rtexture_t *ft2tex = NULL; + qboolean oldstyle_char = false; + //qboolean oldstyle_char = false; // float ftbase_x; qboolean snap = true; qboolean least_one = false; @@ -1353,10 +1368,12 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max if (ft2 != NULL) { if (snap) - map_index = Font_IndexForSize(ft2, h, &w, &h); + size_index = Font_IndexForSize(ft2, h, &w, &h); else - map_index = Font_IndexForSize(ft2, h, NULL, NULL); - fontmap = Font_MapForIndex(ft2, map_index); + size_index = Font_IndexForSize(ft2, h, NULL, NULL); + //fontmap = Font_MapForIndex(ft2, map_index); + if (size_index >= 0) + ft2size = &ft2->font_sizes[size_index]; } dw = w * sw; @@ -1382,8 +1399,8 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max //if (snap) // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway - if (fontmap) - width_of = fontmap->width_of; + if (ft2size) + width_of = ft2size->width_of; else width_of = fnt->width_of; @@ -1395,7 +1412,7 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max i = text - text_start; if (!ch) break; - if (ch == ' ' && !fontmap) + if (ch == ' ' && !ft2size) { if(!least_one || i0) // never skip the first character if(x + width_of[(int) ' '] * dw > maxwidth) @@ -1460,14 +1477,16 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max } ch = nextch; - if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF)) + if (ft2size) + glyph = Font_GetGlyph(ft2, size_index, w, h, ch); + + if (!ft2size || (ch <= 0xFF && glyph->image) || (ch >= 0xE000 && ch <= 0xE0FF)) { if (ch > 0xE000) ch -= 0xE000; if (ch > 0xFF) continue; - if (fontmap) - map = ft2_oldstyle_map; + oldstyle_char = true; prevch = 0; if(!least_one || i0) // never skip the first character if(x + width_of[ch] * dw > maxwidth) @@ -1477,6 +1496,7 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max } x += width_of[ch] * dw; } else { + /* if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP) { map = FontMap_FindForChar(fontmap, ch); @@ -1492,7 +1512,15 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL)) x += kx * dw; x += map->glyphs[mapch].advance_x * dw; + */ + if (oldstyle_char || glyph->tex != ft2tex) { + oldstyle_char = false; + ft2tex = glyph->tex; + } //prevmap = map; + if (prevch && Font_GetKerning(ft2, size_index, w, h, prevch, ch, &kx, NULL)) + x += kx * dw; + x += glyph->advance_x * dw; prevch = ch; } } @@ -1516,17 +1544,25 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma static float vertex3f[QUADELEMENTS_MAXQUADS*4*3]; static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2]; static float color4f[QUADELEMENTS_MAXQUADS*4*4]; - Uchar ch, mapch, nextch; + Uchar ch, nextch; Uchar prevch = 0; // used for kerning int tempcolorindex; + /* int map_index = 0; //ft2_font_map_t *prevmap = NULL; // the previous map ft2_font_map_t *map = NULL; // the currently used map ft2_font_map_t *fontmap = NULL; // the font map for the size + */ + qboolean oldstyle_char = false; + int size_index = 0; float ftbase_y; const char *text_start = text; float kx, ky; ft2_font_t *ft2 = fnt->ft2; + ft2_font_size_t *ft2size = NULL; + //ft2_glyphtex_t *ft2tex = NULL; // Used to reduce SetupShader calls and maximize batches + rtexture_t *ft2tex = NULL; + glyph_t *glyph = NULL; qboolean snap = true; float pix_x, pix_y; size_t bytes_left; @@ -1550,10 +1586,12 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma if (ft2 != NULL) { if (snap) - map_index = Font_IndexForSize(ft2, h, &w, &h); + size_index = Font_IndexForSize(ft2, h, &w, &h); else - map_index = Font_IndexForSize(ft2, h, NULL, NULL); - fontmap = Font_MapForIndex(ft2, map_index); + size_index = Font_IndexForSize(ft2, h, NULL, NULL); + //fontmap = Font_MapForIndex(ft2, map_index); + if (size_index >= 0) + ft2size = &ft2->font_sizes[size_index]; } dw = w * sw; @@ -1571,7 +1609,7 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000); // R_Mesh_ResetTextureState(); - if (!fontmap) + if (!ft2size) R_Mesh_TexBind(0, fnt->tex); R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); @@ -1591,8 +1629,8 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma pix_x = vid.width / vid_conwidth.value; pix_y = vid.height / vid_conheight.value; - if (fontmap) - width_of = fontmap->width_of; + if (ft2size) + width_of = ft2size->width_of; else width_of = fnt->width_of; @@ -1623,7 +1661,7 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma i = text - text_start; if (!ch) break; - if (ch == ' ' && !fontmap) + if (ch == ' ' && !ft2size) { x += width_of[(int) ' '] * dw; continue; @@ -1689,15 +1727,18 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma x += 1.0/pix_x * r_textshadow.value; y += 1.0/pix_y * r_textshadow.value; } - if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF)) + + if (ft2size) + glyph = Font_GetGlyph(ft2, size_index, w, h, ch); + if (!ft2size || (ch <= 0xFF && glyph->image) || (ch >= 0xE000 && ch <= 0xE0FF)) { if (ch >= 0xE000) ch -= 0xE000; if (ch > 0xFF) goto out; - if (fontmap) + if (ft2size) { - if (map != ft2_oldstyle_map) + if (!oldstyle_char) { if (batchcount) { @@ -1710,7 +1751,7 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma av = vertex3f; } R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); - map = ft2_oldstyle_map; + oldstyle_char = true; } } prevch = 0; @@ -1759,6 +1800,7 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma } x += width_of[ch] * dw; } else { + /* DEPRECATED: if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP) { // new charmap - need to render @@ -1790,13 +1832,34 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma } R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); } + */ + + // DEPRECATED: mapch = ch - map->start; + // DEPRECATED: thisw = map->glyphs[mapch].advance_x; - mapch = ch - map->start; - thisw = map->glyphs[mapch].advance_x; + // this is done above to get the actual glyph's .image value + // glyph = Font_GetGlyph(ft2, size_index, w, h, ch); + thisw = glyph->advance_x; + + if (oldstyle_char || glyph->tex != ft2tex) { + oldstyle_char = false; + ft2tex = glyph->tex; + if (batchcount) + { + R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f); + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0); + batchcount = 0; + ac = color4f; + at = texcoord2f; + av = vertex3f; + } + R_SetupShader_Generic(glyph->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); + } //x += ftbase_x; y += ftbase_y; - if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky)) + //if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky)) + if (prevch && Font_GetKerning(ft2, size_index, w, h, prevch, ch, &kx, &ky)) { x += kx * dw; y += ky * dh; @@ -1807,14 +1870,14 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3]; ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3]; ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3]; - at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin; - at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin; - at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax; - at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax; - av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10; - av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10; - av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10; - av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10; + at[0] = glyph->txmin; at[1] = glyph->tymin; + at[2] = glyph->txmax; at[3] = glyph->tymin; + at[4] = glyph->txmax; at[5] = glyph->tymax; + at[6] = glyph->txmin; at[7] = glyph->tymax; + av[ 0] = x + dw * glyph->vxmin; av[ 1] = y + dh * glyph->vymin; av[ 2] = 10; + av[ 3] = x + dw * glyph->vxmax; av[ 4] = y + dh * glyph->vymin; av[ 5] = 10; + av[ 6] = x + dw * glyph->vxmax; av[ 7] = y + dh * glyph->vymax; av[ 8] = 10; + av[ 9] = x + dw * glyph->vxmin; av[10] = y + dh * glyph->vymax; av[11] = 10; //x -= ftbase_x; y -= ftbase_y; diff --git a/gl_textures.c b/gl_textures.c index 57c257f2..7afac05d 100644 --- a/gl_textures.c +++ b/gl_textures.c @@ -2015,6 +2015,36 @@ rtexture_t *R_LoadTextureRenderBuffer(rtexturepool_t *rtexturepool, const char * return (rtexture_t *)glt; } +void R_SaveTextureTGAFile(rtexture_t *rt, const char *filename, qboolean hasalpha) +{ +#ifdef USE_GLES2 + return -1; +#else + GLint internalformat; + GLint oldbindtexnum; + unsigned char *data; + gltexture_t *glt = (gltexture_t *)rt; + + GL_ActiveTexture(0); + oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]); + qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR + qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat); + if (hasalpha) + { + data = Mem_Alloc(tempmempool, glt->tilewidth * glt->tileheight * 4); + qglGetTexImage(gltexturetypeenums[glt->texturetype], 0, GL_BGRA, GL_UNSIGNED_BYTE, data); CHECKGLERROR + Image_WriteTGABGRA(filename, glt->tilewidth, glt->tileheight, data); + Mem_Free(data); + } else { + data = Mem_Alloc(tempmempool, glt->tilewidth * glt->tileheight * 3); + qglGetTexImage(gltexturetypeenums[glt->texturetype], 0, GL_BGR, GL_UNSIGNED_BYTE, data); CHECKGLERROR + Image_WriteTGABGR_preflipped(filename, glt->tilewidth, glt->tileheight, data); + Mem_Free(data); + } + qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR +#endif +} + int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed, qboolean hasalpha) { #ifdef USE_GLES2 diff --git a/model_brush.c b/model_brush.c index 7a15c8ac..cafbd184 100644 --- a/model_brush.c +++ b/model_brush.c @@ -2632,7 +2632,7 @@ static void Mod_Q1BSP_LoadFaces(sizebuf_t *sb) int stainmapsize = 0; mod_alloclightmap_state_t allocState; - Mod_AllocLightmap_Init(&allocState, lightmapsize, lightmapsize); + Mod_AllocLightmap_Init(&allocState, lightmapsize, lightmapsize, loadmodel->mempool); for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++) { int i, iu, iv, lightmapx = 0, lightmapy = 0; diff --git a/model_shared.c b/model_shared.c index 5d06edc5..74e04122 100644 --- a/model_shared.c +++ b/model_shared.c @@ -3462,14 +3462,14 @@ static void Mod_Decompile_f(void) } } -void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, int width, int height) +void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, int width, int height, mempool_t *mempool) { int y; memset(state, 0, sizeof(*state)); state->width = width; state->height = height; state->currentY = 0; - state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(loadmodel->mempool, state->height * sizeof(*state->rows)); + state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows)); for (y = 0;y < state->height;y++) { state->rows[y].currentX = 0; @@ -4181,7 +4181,7 @@ static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model) lm_borderpixels = mod_generatelightmaps_borderpixels.integer; lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d); //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1); - Mod_AllocLightmap_Init(&lmstate, lm_texturesize, lm_texturesize); + Mod_AllocLightmap_Init(&lmstate, lm_texturesize, lm_texturesize, model->mempool); lightmapnumber = 0; for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++) { @@ -4229,7 +4229,7 @@ static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model) surfaceindex = -1; lightmapnumber = 0; Mod_AllocLightmap_Free(&lmstate); - Mod_AllocLightmap_Init(&lmstate, lm_texturesize, lm_texturesize); + Mod_AllocLightmap_Init(&lmstate, lm_texturesize, lm_texturesize, model->mempool); break; } // if we have maxed out the lightmap size, and this triangle does diff --git a/model_shared.h b/model_shared.h index f4e2e3c5..85865b6e 100644 --- a/model_shared.h +++ b/model_shared.h @@ -1153,7 +1153,7 @@ typedef struct mod_alloclightmap_state_s } mod_alloclightmap_state_t; -void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, int width, int height); +void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, int width, int height, mempool_t *mempool); void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state); void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state); qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy); -- 2.39.2