gdi32: Add a font glyph cache in the DIB engine.

This commit is contained in:
Alexandre Julliard 2012-11-27 23:19:29 +01:00
parent 0c0b229717
commit 6050a025bb
3 changed files with 226 additions and 60 deletions

View File

@ -342,6 +342,7 @@ static BOOL dibdrv_DeleteDC( PHYSDEV dev )
TRACE("(%p)\n", dev);
free_pattern_brush( &pdev->brush );
free_pattern_brush( &pdev->pen_brush );
release_cached_font( pdev->font );
HeapFree( GetProcessHeap(), 0, pdev );
return TRUE;
}

View File

@ -63,6 +63,7 @@ typedef struct
} rop_mask_bits;
struct dibdrv_physdev;
struct cached_font;
typedef struct dib_brush
{
@ -92,6 +93,7 @@ typedef struct dibdrv_physdev
HRGN clip;
RECT *bounds;
struct cached_font *font;
/* pen */
DWORD pen_style, pen_endcap, pen_join;
@ -250,6 +252,7 @@ extern int get_clipped_rects( const dib_info *dib, const RECT *rc, HRGN clip, st
extern void add_clipped_bounds( dibdrv_physdev *dev, const RECT *rect, HRGN clip ) DECLSPEC_HIDDEN;
extern int clip_line(const POINT *start, const POINT *end, const RECT *clip,
const bres_params *params, POINT *pt1, POINT *pt2) DECLSPEC_HIDDEN;
extern void release_cached_font( struct cached_font *font ) DECLSPEC_HIDDEN;
static inline void init_clipped_rects( struct clipped_rects *clip_rects )
{

View File

@ -22,10 +22,41 @@
#include "gdi_private.h"
#include "dibdrv.h"
#include "wine/unicode.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dib);
struct cached_glyph
{
GLYPHMETRICS metrics;
BYTE bits[1];
};
struct cached_font
{
struct list entry;
LONG ref;
DWORD hash;
LOGFONTW lf;
XFORM xform;
UINT aa_flags;
UINT nb_glyphs;
struct cached_glyph **glyphs;
};
static struct list font_cache = LIST_INIT( font_cache );
static CRITICAL_SECTION font_cache_cs;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &font_cache_cs,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": font_cache_cs") }
};
static CRITICAL_SECTION font_cache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
static BOOL brush_rect( dibdrv_physdev *pdev, dib_brush *brush, const RECT *rect, HRGN clip )
{
struct clipped_rects clipped_rects;
@ -419,6 +450,125 @@ static inline void get_aa_ranges( COLORREF col, struct intensity_range intensiti
}
}
static DWORD font_cache_hash( struct cached_font *font )
{
DWORD hash = 0, *ptr, two_chars;
WORD *pwc;
int i;
hash ^= font->aa_flags;
for(i = 0, ptr = (DWORD*)&font->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
hash ^= *ptr;
for(i = 0, ptr = (DWORD*)&font->lf; i < 7; i++, ptr++)
hash ^= *ptr;
for(i = 0, ptr = (DWORD*)font->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
two_chars = *ptr;
pwc = (WCHAR *)&two_chars;
if (!*pwc) break;
*pwc = toupperW(*pwc);
pwc++;
*pwc = toupperW(*pwc);
hash ^= two_chars;
if (!*pwc) break;
}
return hash;
}
static int font_cache_cmp( const struct cached_font *p1, const struct cached_font *p2 )
{
int ret = p1->hash - p2->hash;
if (!ret) ret = p1->aa_flags - p2->aa_flags;
if (!ret) ret = memcmp( &p1->xform, &p2->xform, sizeof(p1->xform) );
if (!ret) ret = memcmp( &p1->lf, &p2->lf, FIELD_OFFSET( LOGFONTW, lfFaceName ));
if (!ret) ret = strcmpiW( p1->lf.lfFaceName, p2->lf.lfFaceName );
return ret;
}
static struct cached_font *add_cached_font( HDC hdc, HFONT hfont, UINT aa_flags )
{
struct cached_font font, *ptr, *last_unused = NULL;
UINT i = 0;
GetObjectW( hfont, sizeof(font.lf), &font.lf );
GetTransform( hdc, 0x204, &font.xform );
font.xform.eDx = font.xform.eDy = 0; /* unused, would break hashing */
if (GetGraphicsMode( hdc ) == GM_COMPATIBLE && font.xform.eM11 * font.xform.eM22 < 0)
font.lf.lfOrientation = -font.lf.lfOrientation;
font.lf.lfWidth = abs( font.lf.lfWidth );
font.aa_flags = aa_flags;
font.hash = font_cache_hash( &font );
EnterCriticalSection( &font_cache_cs );
LIST_FOR_EACH_ENTRY( ptr, &font_cache, struct cached_font, entry )
{
if (!font_cache_cmp( &font, ptr ))
{
InterlockedIncrement( &ptr->ref );
list_remove( &ptr->entry );
goto done;
}
if (!ptr->ref)
{
i++;
last_unused = ptr;
}
}
if (i > 5) /* keep at least 5 of the most-recently used fonts around */
{
ptr = last_unused;
for (i = 0; i < ptr->nb_glyphs; i++) HeapFree( GetProcessHeap(), 0, ptr->glyphs[i] );
HeapFree( GetProcessHeap(), 0, ptr->glyphs );
list_remove( &ptr->entry );
}
else if (!(ptr = HeapAlloc( GetProcessHeap(), 0, sizeof(*ptr) )))
{
LeaveCriticalSection( &font_cache_cs );
return NULL;
}
*ptr = font;
ptr->ref = 1;
ptr->glyphs = NULL;
ptr->nb_glyphs = 0;
done:
list_add_head( &font_cache, &ptr->entry );
LeaveCriticalSection( &font_cache_cs );
TRACE( "%d %s -> %p\n", ptr->lf.lfHeight, debugstr_w(ptr->lf.lfFaceName), ptr );
return ptr;
}
void release_cached_font( struct cached_font *font )
{
if (font) InterlockedDecrement( &font->ref );
}
static void add_cached_glyph( struct cached_font *font, UINT index, struct cached_glyph *glyph )
{
if (index >= font->nb_glyphs)
{
UINT new_count = (index + 128) & ~127;
struct cached_glyph **new;
if (font->glyphs)
new = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
font->glyphs, new_count * sizeof(*new) );
else
new = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*new) );
if (!new) return;
font->glyphs = new;
font->nb_glyphs = new_count;
}
font->glyphs[index] = glyph;
}
static struct cached_glyph *get_cached_glyph( struct cached_font *font, UINT index )
{
if (index < font->nb_glyphs) return font->glyphs[index];
return NULL;
}
/**********************************************************************
* get_text_bkgnd_masks
*
@ -476,8 +626,7 @@ static int get_glyph_depth( UINT aa_flags )
{
switch (aa_flags)
{
case GGO_BITMAP: return 1;
case GGO_BITMAP: /* we'll convert non-antialiased 1-bpp bitmaps to 8-bpp */
case GGO_GRAY2_BITMAP:
case GGO_GRAY4_BITMAP:
case GGO_GRAY8_BITMAP:
@ -498,106 +647,104 @@ static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
static const int padding[4] = {0, 3, 2, 1};
/***********************************************************************
* get_glyph_bitmap
* cache_glyph_bitmap
*
* Retrieve a 17-level bitmap for the appropriate glyph.
*
* For non-antialiased bitmaps convert them to the 17-level format
* using only values 0 or 16.
*/
static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT aa_flags, GLYPHMETRICS *metrics,
dib_info *glyph_dib )
static struct cached_glyph *cache_glyph_bitmap( HDC hdc, struct cached_font *font, UINT index )
{
UINT ggo_flags = aa_flags | GGO_GLYPH_INDEX;
UINT ggo_flags = font->aa_flags | GGO_GLYPH_INDEX;
static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
UINT indices[3] = {0, 0, 0x20};
int i, x, y;
DWORD ret, size;
BYTE *buf, *dst, *src;
int pad = 0, depth = get_glyph_depth( aa_flags );
glyph_dib->bits.ptr = NULL;
glyph_dib->bits.is_copy = FALSE;
glyph_dib->bits.free = free_heap_bits;
glyph_dib->bits.param = NULL;
BYTE *dst, *src;
int pad = 0, stride, bit_count;
GLYPHMETRICS metrics;
struct cached_glyph *glyph;
indices[0] = index;
for (i = 0; i < sizeof(indices) / sizeof(indices[0]); i++)
{
index = indices[i];
ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, 0, NULL, &identity );
ret = GetGlyphOutlineW( hdc, index, ggo_flags, &metrics, 0, NULL, &identity );
if (ret != GDI_ERROR) break;
}
if (ret == GDI_ERROR) return NULL;
if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
if (!ret) return ERROR_SUCCESS; /* empty glyph */
bit_count = get_glyph_depth( font->aa_flags );
stride = get_dib_stride( metrics.gmBlackBoxX, bit_count );
size = metrics.gmBlackBoxY * stride;
glyph = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( struct cached_glyph, bits[size] ));
if (!glyph) return NULL;
if (!ret) goto done; /* zero-size glyph */
/* We'll convert non-antialiased 1-bpp bitmaps to 8-bpp, so these sizes relate to 8-bpp */
glyph_dib->bit_count = depth == 1 ? 8 : depth;
glyph_dib->width = metrics->gmBlackBoxX;
glyph_dib->height = metrics->gmBlackBoxY;
glyph_dib->rect.left = 0;
glyph_dib->rect.top = 0;
glyph_dib->rect.right = metrics->gmBlackBoxX;
glyph_dib->rect.bottom = metrics->gmBlackBoxY;
glyph_dib->stride = get_dib_stride( metrics->gmBlackBoxX, glyph_dib->bit_count );
if (bit_count == 8) pad = padding[ metrics.gmBlackBoxX % 4 ];
if (glyph_dib->bit_count == 8) pad = padding[ metrics->gmBlackBoxX % 4 ];
size = metrics->gmBlackBoxY * glyph_dib->stride;
buf = HeapAlloc( GetProcessHeap(), 0, size );
if (!buf) return ERROR_OUTOFMEMORY;
ret = GetGlyphOutlineW( hdc, index, ggo_flags, metrics, size, buf, &identity );
ret = GetGlyphOutlineW( hdc, index, ggo_flags, &metrics, size, glyph->bits, &identity );
if (ret == GDI_ERROR)
{
HeapFree( GetProcessHeap(), 0, buf );
return ERROR_NOT_FOUND;
HeapFree( GetProcessHeap(), 0, glyph );
return NULL;
}
if (aa_flags == GGO_BITMAP)
assert( ret <= size );
if (font->aa_flags == GGO_BITMAP)
{
for (y = metrics->gmBlackBoxY - 1; y >= 0; y--)
for (y = metrics.gmBlackBoxY - 1; y >= 0; y--)
{
src = buf + y * get_dib_stride( metrics->gmBlackBoxX, 1 );
dst = buf + y * glyph_dib->stride;
src = glyph->bits + y * get_dib_stride( metrics.gmBlackBoxX, 1 );
dst = glyph->bits + y * stride;
if (pad) memset( dst + metrics->gmBlackBoxX, 0, pad );
if (pad) memset( dst + metrics.gmBlackBoxX, 0, pad );
for (x = metrics->gmBlackBoxX - 1; x >= 0; x--)
for (x = metrics.gmBlackBoxX - 1; x >= 0; x--)
dst[x] = (src[x / 8] & masks[x % 8]) ? 0x10 : 0;
}
}
else if (pad)
{
for (y = 0, dst = buf; y < metrics->gmBlackBoxY; y++, dst += glyph_dib->stride)
memset( dst + metrics->gmBlackBoxX, 0, pad );
for (y = 0, dst = glyph->bits; y < metrics.gmBlackBoxY; y++, dst += stride)
memset( dst + metrics.gmBlackBoxX, 0, pad );
}
glyph_dib->bits.ptr = buf;
return ERROR_SUCCESS;
done:
glyph->metrics = metrics;
add_cached_glyph( font, index, glyph );
return glyph;
}
static void render_string( HDC hdc, dib_info *dib, INT x, INT y, UINT flags, UINT aa_flags,
const WCHAR *str, UINT count, const INT *dx, DWORD text_color,
static void render_string( HDC hdc, dib_info *dib, struct cached_font *font, INT x, INT y,
UINT flags, const WCHAR *str, UINT count, const INT *dx, DWORD text_color,
const struct intensity_range *ranges, const struct clipped_rects *clipped_rects,
RECT *bounds )
{
UINT i;
DWORD err;
GLYPHMETRICS metrics;
struct cached_glyph *glyph;
dib_info glyph_dib;
glyph_dib.bit_count = get_glyph_depth( font->aa_flags );
glyph_dib.rect.left = 0;
glyph_dib.rect.top = 0;
glyph_dib.bits.is_copy = FALSE;
glyph_dib.bits.free = NULL;
EnterCriticalSection( &font_cache_cs );
for (i = 0; i < count; i++)
{
err = get_glyph_bitmap( hdc, (UINT)str[i], aa_flags, &metrics, &glyph_dib );
if (err) continue;
if (!(glyph = get_cached_glyph( font, str[i] )) &&
!(glyph = cache_glyph_bitmap( hdc, font, str[i] ))) continue;
if (glyph_dib.bits.ptr)
draw_glyph( dib, x, y, &metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
glyph_dib.width = glyph->metrics.gmBlackBoxX;
glyph_dib.height = glyph->metrics.gmBlackBoxY;
glyph_dib.rect.right = glyph->metrics.gmBlackBoxX;
glyph_dib.rect.bottom = glyph->metrics.gmBlackBoxY;
glyph_dib.stride = get_dib_stride( glyph->metrics.gmBlackBoxX, glyph_dib.bit_count );
glyph_dib.bits.ptr = glyph->bits;
free_dib_info( &glyph_dib );
draw_glyph( dib, x, y, &glyph->metrics, &glyph_dib, text_color, ranges, clipped_rects, bounds );
if (dx)
{
@ -611,10 +758,11 @@ static void render_string( HDC hdc, dib_info *dib, INT x, INT y, UINT flags, UIN
}
else
{
x += metrics.gmCellIncX;
y += metrics.gmCellIncY;
x += glyph->metrics.gmCellIncX;
y += glyph->metrics.gmCellIncY;
}
}
LeaveCriticalSection( &font_cache_cs );
}
BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits *bits,
@ -627,6 +775,7 @@ BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits
DWORD fg_pixel, bg_pixel;
struct intensity_range glyph_intensities[17];
struct clipped_rects visrect;
struct cached_font *font;
assert( info->bmiHeader.biBitCount > 8 ); /* mono and indexed formats don't support anti-aliasing */
@ -652,8 +801,11 @@ BOOL render_aa_text_bitmapinfo( HDC hdc, BITMAPINFO *info, struct gdi_image_bits
dib.funcs->solid_rects( &dib, 1, &src->visrect, bkgnd_color.and, bkgnd_color.xor );
}
render_string( hdc, &dib, x, y, flags, aa_flags, str, count, dx,
if (!(font = add_cached_font( hdc, GetCurrentObject( hdc, OBJ_FONT ), aa_flags ))) return FALSE;
render_string( hdc, &dib, font, x, y, flags, str, count, dx,
fg_pixel, glyph_intensities, &visrect, NULL );
release_cached_font( font );
return TRUE;
}
@ -670,6 +822,8 @@ BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
DWORD text_color;
struct intensity_range ranges[17];
if (!pdev->font) return FALSE;
init_clipped_rects( &clipped_rects );
reset_bounds( &bounds );
@ -701,7 +855,7 @@ BOOL dibdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
get_aa_ranges( pdev->dib.funcs->pixel_to_colorref( &pdev->dib, text_color ), ranges );
dc = get_dc_ptr( dev->hdc );
render_string( dev->hdc, &pdev->dib, x, y, flags, dc->aa_flags, str, count, dx,
render_string( dev->hdc, &pdev->dib, pdev->font, x, y, flags, str, count, dx,
text_color, ranges, &clipped_rects, &bounds );
release_dc_ptr( dc );
@ -717,11 +871,19 @@ done:
HFONT dibdrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags )
{
dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
HFONT ret;
if (pdev->dib.bit_count <= 8) *aa_flags = GGO_BITMAP; /* no anti-aliasing on <= 8bpp */
dev = GET_NEXT_PHYSDEV( dev, pSelectFont );
return dev->funcs->pSelectFont( dev, font, aa_flags );
ret = dev->funcs->pSelectFont( dev, font, aa_flags );
if (ret)
{
struct cached_font *prev = pdev->font;
pdev->font = add_cached_font( dev->hdc, font, *aa_flags ? *aa_flags : GGO_BITMAP );
release_cached_font( prev );
}
return ret;
}
/***********************************************************************