gdi32: Add a font glyph cache in the DIB engine.
This commit is contained in:
parent
0c0b229717
commit
6050a025bb
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
|
Loading…
Reference in New Issue