mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-24 11:40:47 +00:00
* Source/NSStringDrawing.m: Clean up string drawing cache code.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@40334 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
a6299e436e
commit
aaa703260d
2 changed files with 116 additions and 128 deletions
|
@ -1,3 +1,7 @@
|
|||
2017-02-12 Fred Kiefer <FredKiefer@gmx.de>
|
||||
|
||||
* Source/NSStringDrawing.m: Clean up string drawing cache code.
|
||||
|
||||
2017-01-08 Fred Kiefer <FredKiefer@gmx.de>
|
||||
|
||||
* Source/NSMenu.m (-_isVisible, -_isMain): Add two helper
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<abstract>Categories which add drawing capabilities to NSAttributedString
|
||||
and NSString.</abstract>
|
||||
|
||||
Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc.
|
||||
Copyright (C) 1999, 2003, 2004, 2017 Free Software Foundation, Inc.
|
||||
|
||||
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
||||
Date: Mar 1999 - rewrite from scratch
|
||||
|
@ -53,6 +53,8 @@ For bigger values the width gets ignored.
|
|||
A size of 16 and these constants give a hit rate of 80%-90% for normal app
|
||||
use (based on real world statistics gathered with the help of some users
|
||||
from #GNUstep).
|
||||
We use the last entry of the cache as a scratch element to set up an initial
|
||||
text network.
|
||||
*/
|
||||
#define NUM_CACHE_ENTRIES 16
|
||||
#define HIT_BOOST 2
|
||||
|
@ -62,8 +64,8 @@ from #GNUstep).
|
|||
typedef struct
|
||||
{
|
||||
int used;
|
||||
unsigned int string_hash;
|
||||
int hasSize, useScreenFonts;
|
||||
NSUInteger string_hash;
|
||||
BOOL hasSize, useScreenFonts;
|
||||
|
||||
NSTextStorage *textStorage;
|
||||
NSLayoutManager *layoutManager;
|
||||
|
@ -75,11 +77,8 @@ typedef struct
|
|||
|
||||
|
||||
static BOOL did_init = NO;
|
||||
static cache_t cache[NUM_CACHE_ENTRIES];
|
||||
static cache_t cache[NUM_CACHE_ENTRIES + 1];
|
||||
|
||||
static NSTextStorage *scratchTextStorage;
|
||||
static NSLayoutManager *scratchLayoutManager;
|
||||
static NSTextContainer *scratchTextContainer;
|
||||
|
||||
static NSRecursiveLock *cacheLock = nil;
|
||||
|
||||
|
@ -128,18 +127,9 @@ static void init_string_drawing(void)
|
|||
[layoutManager addTextContainer: textContainer];
|
||||
[textContainer release];
|
||||
|
||||
if (i < NUM_CACHE_ENTRIES)
|
||||
{
|
||||
cache[i].textStorage = textStorage;
|
||||
cache[i].layoutManager = layoutManager;
|
||||
cache[i].textContainer = textContainer;
|
||||
}
|
||||
else
|
||||
{
|
||||
scratchTextStorage = textStorage;
|
||||
scratchLayoutManager = layoutManager;
|
||||
scratchTextContainer = textContainer;
|
||||
}
|
||||
cache[i].textStorage = textStorage;
|
||||
cache[i].layoutManager = layoutManager;
|
||||
cache[i].textContainer = textContainer;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,13 +153,11 @@ static inline void cache_unlock()
|
|||
[cacheLock unlock];
|
||||
}
|
||||
|
||||
static inline BOOL is_size_match(cache_t *c, int hasSize, NSSize size)
|
||||
static inline BOOL is_size_match(cache_t *c, cache_t *scratch)
|
||||
{
|
||||
if ((!c->hasSize && !hasSize) ||
|
||||
(c->hasSize && hasSize && c->givenSize.width == size.width
|
||||
&& c->givenSize.height == size.height) /* ||
|
||||
(!c->hasSize && hasSize && size.width >= NSMaxX(c->usedRect)
|
||||
&& size.height >= NSMaxY(c->usedRect))*/)
|
||||
if ((!c->hasSize && !scratch->hasSize) ||
|
||||
(c->hasSize && scratch->hasSize
|
||||
&& NSEqualSizes(c->givenSize, scratch->givenSize)))
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
@ -179,22 +167,34 @@ static inline BOOL is_size_match(cache_t *c, int hasSize, NSSize size)
|
|||
}
|
||||
}
|
||||
|
||||
static int cache_match(int hasSize, NSSize size, int useScreenFonts, int *matched)
|
||||
static inline BOOL is_match(cache_t *c, cache_t *scratch)
|
||||
{
|
||||
if (c->string_hash != scratch->string_hash
|
||||
|| c->useScreenFonts != scratch->useScreenFonts)
|
||||
return NO;
|
||||
|
||||
#ifdef STATS
|
||||
hash_hits++;
|
||||
#endif
|
||||
|
||||
if (![scratch->textStorage isEqualToAttributedString: c->textStorage])
|
||||
return NO;
|
||||
|
||||
/* String and attributes match, check size. */
|
||||
return is_size_match(c, scratch);
|
||||
}
|
||||
|
||||
static cache_t *cache_match(cache_t *scratch, BOOL *matched)
|
||||
{
|
||||
int i, j;
|
||||
cache_t *c;
|
||||
int least_used;
|
||||
int replace;
|
||||
int orig_used;
|
||||
unsigned int string_hash = [[scratchTextStorage string] hash];
|
||||
int least_used = -1;
|
||||
int replace = -1;
|
||||
|
||||
#ifdef STATS
|
||||
total++;
|
||||
#endif
|
||||
|
||||
*matched = 1;
|
||||
replace = least_used = -1;
|
||||
|
||||
/*
|
||||
A deterministic pattern for replacing cache entries can hit ugly worst
|
||||
cases on certain matching use patterns (where the cache is full of old
|
||||
|
@ -206,7 +206,9 @@ static int cache_match(int hasSize, NSSize size, int useScreenFonts, int *matche
|
|||
for (i = 0; i < NUM_CACHE_ENTRIES; i++, j++)
|
||||
{
|
||||
if (j == NUM_CACHE_ENTRIES)
|
||||
j = 0;
|
||||
{
|
||||
j = 0;
|
||||
}
|
||||
c = cache + j;
|
||||
if (least_used == -1 || c->used < least_used)
|
||||
{
|
||||
|
@ -217,32 +219,27 @@ static int cache_match(int hasSize, NSSize size, int useScreenFonts, int *matche
|
|||
if (!c->used)
|
||||
continue;
|
||||
|
||||
orig_used = c->used;
|
||||
if (c->used > MISS_COST)
|
||||
c->used -= MISS_COST;
|
||||
else
|
||||
c->used = 1;
|
||||
|
||||
if (c->string_hash != string_hash
|
||||
|| c->useScreenFonts != useScreenFonts)
|
||||
continue;
|
||||
|
||||
#ifdef STATS
|
||||
hash_hits++;
|
||||
#endif
|
||||
|
||||
if (![scratchTextStorage isEqualToAttributedString: c->textStorage])
|
||||
continue;
|
||||
|
||||
/* String and attributes match, check size. */
|
||||
if (is_size_match(c, hasSize, size))
|
||||
if (is_match(c, scratch))
|
||||
{
|
||||
c->used = orig_used + HIT_BOOST;
|
||||
#ifdef STATS
|
||||
hits++;
|
||||
#endif
|
||||
return j;
|
||||
|
||||
c->used += HIT_BOOST;
|
||||
*matched = YES;
|
||||
return c;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c->used > MISS_COST)
|
||||
{
|
||||
c->used -= MISS_COST;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->used = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSCAssert(replace != -1, @"Couldn't find a cache entry to replace.");
|
||||
|
@ -250,30 +247,56 @@ static int cache_match(int hasSize, NSSize size, int useScreenFonts, int *matche
|
|||
#ifdef STATS
|
||||
misses++;
|
||||
#endif
|
||||
*matched = 0;
|
||||
*matched = NO;
|
||||
|
||||
c = cache + replace;
|
||||
c->used = 1;
|
||||
c->string_hash = string_hash;
|
||||
c->hasSize = hasSize;
|
||||
c->useScreenFonts = useScreenFonts;
|
||||
c->givenSize = size;
|
||||
/* We did not find a matching entry, return the least used one */
|
||||
return cache + replace;
|
||||
}
|
||||
|
||||
{
|
||||
id temp;
|
||||
static cache_t *cache_lookup(BOOL hasSize, NSSize size, BOOL useScreenFonts)
|
||||
{
|
||||
BOOL hit;
|
||||
cache_t *c;
|
||||
cache_t *scratch = cache + NUM_CACHE_ENTRIES;
|
||||
|
||||
#define SWAP(a, b) temp = a; a = b; b = temp;
|
||||
SWAP(scratchTextStorage, c->textStorage)
|
||||
SWAP(scratchLayoutManager, c->layoutManager)
|
||||
SWAP(scratchTextContainer, c->textContainer)
|
||||
#undef SWAP
|
||||
}
|
||||
scratch->used = 1;
|
||||
scratch->string_hash = [[scratch->textStorage string] hash];
|
||||
scratch->hasSize = hasSize;
|
||||
scratch->useScreenFonts = useScreenFonts;
|
||||
scratch->givenSize = size;
|
||||
|
||||
c = cache_match(scratch, &hit);
|
||||
if (!hit)
|
||||
{
|
||||
// Swap c and scratch
|
||||
cache_t temp;
|
||||
|
||||
return replace;
|
||||
temp = *c;
|
||||
*c = *scratch;
|
||||
*scratch = temp;
|
||||
|
||||
// Cache miss, need to set up the text system
|
||||
if (hasSize)
|
||||
{
|
||||
[c->textContainer setContainerSize: NSMakeSize(size.width, size.height)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[c->textContainer setContainerSize: NSMakeSize(LARGE_SIZE, LARGE_SIZE)];
|
||||
}
|
||||
[c->layoutManager setUsesScreenFonts: useScreenFonts];
|
||||
|
||||
c->usedRect = [c->layoutManager usedRectForTextContainer: c->textContainer];
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void prepare_string(NSString *string, NSDictionary *attributes)
|
||||
{
|
||||
cache_t *scratch = cache + NUM_CACHE_ENTRIES;
|
||||
NSTextStorage *scratchTextStorage = scratch->textStorage;
|
||||
|
||||
[scratchTextStorage beginEditing];
|
||||
[scratchTextStorage replaceCharactersInRange: NSMakeRange(0, [scratchTextStorage length])
|
||||
withString: string];
|
||||
|
@ -287,39 +310,14 @@ static inline void prepare_string(NSString *string, NSDictionary *attributes)
|
|||
|
||||
static inline void prepare_attributed_string(NSAttributedString *string)
|
||||
{
|
||||
cache_t *scratch = cache + NUM_CACHE_ENTRIES;
|
||||
NSTextStorage *scratchTextStorage = scratch->textStorage;
|
||||
|
||||
[scratchTextStorage replaceCharactersInRange: NSMakeRange(0, [scratchTextStorage length])
|
||||
withAttributedString: string];
|
||||
}
|
||||
|
||||
static int cache_lookup(int hasSize, NSSize size, int useScreenFonts)
|
||||
{
|
||||
cache_t *c;
|
||||
int ci, hit;
|
||||
NSLayoutManager *layoutManager;
|
||||
NSTextContainer *textContainer;
|
||||
|
||||
ci = cache_match(hasSize, size, useScreenFonts, &hit);
|
||||
if (hit)
|
||||
{
|
||||
return ci;
|
||||
}
|
||||
// Cache miss, need to set up the text system
|
||||
c = &cache[ci];
|
||||
layoutManager = c->layoutManager;
|
||||
textContainer = c->textContainer;
|
||||
|
||||
if (hasSize)
|
||||
[textContainer setContainerSize: NSMakeSize(size.width, size.height)];
|
||||
else
|
||||
[textContainer setContainerSize: NSMakeSize(LARGE_SIZE, LARGE_SIZE)];
|
||||
[layoutManager setUsesScreenFonts: useScreenFonts];
|
||||
|
||||
c->usedRect = [layoutManager usedRectForTextContainer: textContainer];
|
||||
|
||||
return ci;
|
||||
}
|
||||
|
||||
static int use_screen_fonts(void)
|
||||
static BOOL use_screen_fonts(void)
|
||||
{
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
NSAffineTransform *ctm = GSCurrentCTM(ctxt);
|
||||
|
@ -327,11 +325,11 @@ static int use_screen_fonts(void)
|
|||
|
||||
if (ts.m11 != 1.0 || ts.m12 != 0.0 || ts.m21 != 0.0 || fabs(ts.m22) != 1.0)
|
||||
{
|
||||
return 0;
|
||||
return NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,19 +349,15 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|||
|
||||
- (void) drawAtPoint: (NSPoint)point
|
||||
{
|
||||
int ci;
|
||||
cache_t *c;
|
||||
|
||||
NSRange r;
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
|
||||
cache_lock();
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
prepare_attributed_string(self);
|
||||
ci = cache_lookup(0, NSZeroSize, use_screen_fonts());
|
||||
c = &cache[ci];
|
||||
c = cache_lookup(NO, NSZeroSize, use_screen_fonts());
|
||||
|
||||
r = NSMakeRange(0, [c->layoutManager numberOfGlyphs]);
|
||||
|
||||
|
@ -412,9 +406,7 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|||
options: (NSStringDrawingOptions)options
|
||||
{
|
||||
// FIXME: This ignores options
|
||||
int ci;
|
||||
cache_t *c;
|
||||
|
||||
NSRange r;
|
||||
BOOL need_clip = NO;
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
|
@ -423,12 +415,10 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|||
return;
|
||||
|
||||
cache_lock();
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
prepare_attributed_string(self);
|
||||
ci = cache_lookup(1, rect.size, use_screen_fonts());
|
||||
c = &cache[ci];
|
||||
c = cache_lookup(YES, rect.size, use_screen_fonts());
|
||||
|
||||
/*
|
||||
If the used rect fits completely in the rect we draw in, we save time
|
||||
|
@ -500,16 +490,16 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|||
options: (NSStringDrawingOptions)options
|
||||
{
|
||||
// FIXME: This ignores options
|
||||
int ci;
|
||||
cache_t *c;
|
||||
NSRect result = NSZeroRect;
|
||||
int hasSize = NSEqualSizes(NSZeroSize, size) ? 0 : 1;
|
||||
BOOL hasSize = !NSEqualSizes(NSZeroSize, size);
|
||||
|
||||
cache_lock();
|
||||
NS_DURING
|
||||
{
|
||||
prepare_attributed_string(self);
|
||||
ci = cache_lookup(hasSize, size, 1);
|
||||
result = cache[ci].usedRect;
|
||||
c = cache_lookup(hasSize, size, NO);
|
||||
result = c->usedRect;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
|
@ -529,9 +519,7 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|||
|
||||
- (void) drawAtPoint: (NSPoint)point withAttributes: (NSDictionary *)attrs
|
||||
{
|
||||
int ci;
|
||||
cache_t *c;
|
||||
|
||||
NSRange r;
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
|
||||
|
@ -539,8 +527,7 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|||
NS_DURING
|
||||
{
|
||||
prepare_string(self, attrs);
|
||||
ci = cache_lookup(0, NSZeroSize, use_screen_fonts());
|
||||
c = &cache[ci];
|
||||
c = cache_lookup(NO, NSZeroSize, use_screen_fonts());
|
||||
|
||||
r = NSMakeRange(0, [c->layoutManager numberOfGlyphs]);
|
||||
|
||||
|
@ -590,9 +577,7 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|||
attributes: (NSDictionary *)attrs
|
||||
{
|
||||
// FIXME: This ignores options
|
||||
int ci;
|
||||
cache_t *c;
|
||||
|
||||
NSRange r;
|
||||
BOOL need_clip = NO;
|
||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||
|
@ -604,8 +589,7 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|||
NS_DURING
|
||||
{
|
||||
prepare_string(self, attrs);
|
||||
ci = cache_lookup(1, rect.size, use_screen_fonts());
|
||||
c = &cache[ci];
|
||||
c = cache_lookup(YES, rect.size, use_screen_fonts());
|
||||
|
||||
/*
|
||||
If the used rect fits completely in the rect we draw in, we save time
|
||||
|
@ -679,16 +663,16 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|||
attributes: (NSDictionary *)attrs
|
||||
{
|
||||
// FIXME: This ignores options
|
||||
int ci;
|
||||
cache_t *c;
|
||||
NSRect result = NSZeroRect;
|
||||
int hasSize = NSEqualSizes(NSZeroSize, size) ? 0 : 1;
|
||||
BOOL hasSize = !NSEqualSizes(NSZeroSize, size);
|
||||
|
||||
cache_lock();
|
||||
NS_DURING
|
||||
{
|
||||
prepare_string(self, attrs);
|
||||
ci = cache_lookup(hasSize, size, 1);
|
||||
result = cache[ci].usedRect;
|
||||
c = cache_lookup(hasSize, size, NO);
|
||||
result = c->usedRect;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue