* Headers/Additions/GNUstepGUI/GSLayoutManager_internal.h: Make

-_invalidateEverything visible.
        * Source/GSLayoutManager.m
        * (-invalidateGlyphsForCharacterRange:...): Call
        -_invalidateEverything when the whole character range is
invalidated.
        * Source/NSStringDrawing.m: Remove workarounds for full layout
        * invalidation.
        Move initialisation check into cache_lock().
        Extract size match code in function.
        Put #ifdef around all statistics code.
        Use same LARGE_SIZE value as Apple does.
        Make the methods taking options the main ones.
        * Source/GSHorizontalTypesetter.m (-fullJustifyLine::,
        -rightAlignLine::, -centerAlignLine::): Don't align when the
line
        width is bigger than 1e7.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@35925 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Fred Kiefer 2012-12-30 15:48:21 +00:00
parent 60fcb3622e
commit 89fed0244c
5 changed files with 161 additions and 212 deletions

View file

@ -1,3 +1,19 @@
2012-12-30 Fred Kiefer <FredKiefer@gmx.de>
* Headers/Additions/GNUstepGUI/GSLayoutManager_internal.h: Make
-_invalidateEverything visible.
* Source/GSLayoutManager.m (-invalidateGlyphsForCharacterRange:...): Call
-_invalidateEverything when the whole character range is invalidated.
* Source/NSStringDrawing.m: Remove workarounds for full layout invalidation.
Move initialisation check into cache_lock().
Extract size match code in function.
Put #ifdef around all statistics code.
Use same LARGE_SIZE value as Apple does.
Make the methods taking options the main ones.
* Source/GSHorizontalTypesetter.m (-fullJustifyLine::,
-rightAlignLine::, -centerAlignLine::): Don't align when the line
width is bigger than 1e7.
2012-12-26 Fred Kiefer <FredKiefer@gmx.de>
* Headers/AppKit/NSOpenGL.h,

View file

@ -237,6 +237,7 @@ typedef struct GSLayoutManager_textcontainer_s
@interface GSLayoutManager (LayoutHelpers)
-(void) _freeLayout;
-(void) _invalidateLayoutFromContainer: (int)idx;
-(void) _invalidateEverything;
-(void) _doLayout; /* TODO: this is just a hack until proper incremental layout is done */
-(void) _doLayoutToGlyph: (unsigned int)glyphIndex;

View file

@ -320,6 +320,12 @@ struct GSHorizontalTypesetter_line_frag_s
};
typedef struct GSHorizontalTypesetter_line_frag_s line_frag_t;
/*
Apple uses this as the maximum width of an NSTextContainer.
For bigger values the width gets ignored.
*/
#define LARGE_SIZE 1e7
-(void) fullJustifyLine: (line_frag_t *)lf : (int)num_line_frags
{
unsigned int i, start;
@ -329,6 +335,11 @@ typedef struct GSHorizontalTypesetter_line_frag_s line_frag_t;
glyph_cache_t *g;
unichar ch;
if (lf->rect.size.width >= LARGE_SIZE)
{
return;
}
for (start = 0; num_line_frags; num_line_frags--, lf++)
{
num_spaces = 0;
@ -361,13 +372,17 @@ typedef struct GSHorizontalTypesetter_line_frag_s line_frag_t;
}
}
-(void) rightAlignLine: (line_frag_t *)lf : (int)num_line_frags
{
unsigned int i;
float delta;
glyph_cache_t *g;
if (lf->rect.size.width >= LARGE_SIZE)
{
return;
}
for (i = 0, g = cache; num_line_frags; num_line_frags--, lf++)
{
delta = lf->rect.size.width - lf->last_used;
@ -383,6 +398,11 @@ typedef struct GSHorizontalTypesetter_line_frag_s line_frag_t;
float delta;
glyph_cache_t *g;
if (lf->rect.size.width >= LARGE_SIZE)
{
return;
}
for (i = 0, g = cache; num_line_frags; num_line_frags--, lf++)
{
delta = (lf->rect.size.width - lf->last_used) / 2.0;

View file

@ -1271,6 +1271,13 @@ places where we switch.
[self _sanityChecks];
// [self _glyphDumpRuns];
if ((range.location == 0) && (range.length >= [_textStorage length]))
{
// Full invalidation
[self _invalidateEverything];
return;
}
/*
Find out what range we actually need to invalidate. This depends on how
context affects glyph generation.
@ -1869,6 +1876,12 @@ places where we switch.
[self _invalidateLayoutFromContainer: 0];
}
-(void) _invalidateEverything
{
[self _freeLayout];
[self _freeGlyphs];
[self _initGlyphs];
}
-(void) _doLayout
{
@ -2041,7 +2054,6 @@ places where we switch.
}
}
-(void) _didInvalidateLayout
{
int i;
@ -2049,7 +2061,8 @@ places where we switch.
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
{
tc->was_invalidated = NO;
// FIXME: This value never gets used
tc->was_invalidated = YES;
}
}
@ -2609,9 +2622,11 @@ forStartOfGlyphRange: (NSRange)glyphRange
NSLog(@"%s: doesn't own text container", __PRETTY_FUNCTION__);
return NSMakeRect(0, 0, 0, 0);
}
[self _doLayoutToContainer: i];
tc = textcontainers + i;
if (!tc->complete)
{
[self _doLayoutToContainer: i];
tc = textcontainers + i;
}
if (tc->usedRectValid)
return tc->usedRect;
@ -2746,15 +2761,16 @@ forStartOfGlyphRange: (NSRange)glyphRange
[self _didInvalidateLayout];
}
- (unsigned int) firstUnlaidCharacterIndex
{
return layout_char;
}
- (unsigned int) firstUnlaidGlyphIndex
{
return layout_glyph;
}
-(void) getFirstUnlaidCharacterIndex: (unsigned int *)cindex
glyphIndex: (unsigned int *)gindex
{
@ -2764,7 +2780,6 @@ forStartOfGlyphRange: (NSRange)glyphRange
*gindex = [self firstUnlaidGlyphIndex];
}
-(void) setExtraLineFragmentRect: (NSRect)linefrag
usedRect: (NSRect)used
textContainer: (NSTextContainer *)tc
@ -2938,16 +2953,6 @@ forStartOfGlyphRange: (NSRange)glyphRange
[super dealloc];
}
-(void) _invalidateEverything
{
[self _freeLayout];
[self _freeGlyphs];
[self _initGlyphs];
}
/**
* Sets the text storage for the layout manager.
* Use -replaceTextStorage: instead as a rule. - this method is really
@ -3045,6 +3050,7 @@ See [NSTextView -setTextContainer:] for more information about these calls.
{
return usesScreenFonts;
}
- (void) setUsesScreenFonts: (BOOL)flag
{
flag = !!flag;
@ -3091,6 +3097,7 @@ See [NSTextView -setTextContainer:] for more information about these calls.
[self _invalidateEverything];
[self _didInvalidateLayout];
}
- (BOOL) showsInvisibleCharacters
{
return showsInvisibleCharacters;
@ -3105,6 +3112,7 @@ See [NSTextView -setTextContainer:] for more information about these calls.
[self _invalidateEverything];
[self _didInvalidateLayout];
}
- (BOOL) showsControlCharacters
{
return showsControlCharacters;
@ -3136,7 +3144,6 @@ has).
here.
*/
[self _invalidateLayoutFromContainer: 0];
[self _didInvalidateLayout];
}

View file

@ -43,28 +43,20 @@
#import "AppKit/DPSOperators.h"
/*
TODO:
these methods are _not_ reentrant. should they be?
Apple uses this as the maximum width of an NSTextContainer.
For bigger values the width gets ignored.
*/
#define LARGE_SIZE 1e7
#define LARGE_SIZE 4e6
/*
4e6 is chosen because it's close to (half of, for extra margin) 1<<23-1, the
largest number that can be stored in a 32-bit float with an ulp of 0.5, which
means things should round to the correct whole point.
*/
#define NUM_CACHE_ENTRIES 16
#define HIT_BOOST 2
#define MISS_COST 1
/*
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).
*/
#define NUM_CACHE_ENTRIES 16
#define HIT_BOOST 2
#define MISS_COST 1
typedef struct
@ -82,7 +74,7 @@ typedef struct
} cache_t;
static BOOL did_init;
static BOOL did_init = NO;
static cache_t cache[NUM_CACHE_ENTRIES];
static NSTextStorage *scratchTextStorage;
@ -91,12 +83,12 @@ static NSTextContainer *scratchTextContainer;
static NSRecursiveLock *cacheLock = nil;
static int total, hits, misses, hash_hits;
/* For collecting statistics. */
//#define STATS
#ifdef STATS
static int total, hits, misses, hash_hits;
static void NSStringDrawing_dump_stats(void)
{
#define P(x) printf("%15i %s\n", x, #x);
@ -109,7 +101,6 @@ static void NSStringDrawing_dump_stats(void)
}
#endif
static void init_string_drawing(void)
{
int i;
@ -152,20 +143,42 @@ static void init_string_drawing(void)
}
}
static void cache_lock()
static inline void cache_lock()
{
if(cacheLock == nil)
// FIXME: Put all the init code into an +initialize method
// to let the runtime take care of it.
if (cacheLock == nil)
{
cacheLock = [[NSRecursiveLock alloc] init];
}
[cacheLock lock];
if (!did_init)
{
init_string_drawing();
}
}
static void cache_unlock()
static inline void cache_unlock()
{
[cacheLock unlock];
}
static inline BOOL is_size_match(cache_t *c, int hasSize, NSSize size)
{
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)))
{
return YES;
}
else
{
return NO;
}
}
static int cache_match(int hasSize, NSSize size, int useScreenFonts, int *matched)
{
int i, j;
@ -173,10 +186,11 @@ static int cache_match(int hasSize, NSSize size, int useScreenFonts, int *matche
int least_used;
int replace;
int orig_used;
unsigned int string_hash = [[scratchTextStorage string] hash];
#ifdef STATS
total++;
#endif
*matched = 1;
replace = least_used = -1;
@ -213,39 +227,29 @@ static int cache_match(int hasSize, NSSize size, int useScreenFonts, int *matche
|| c->useScreenFonts != useScreenFonts)
continue;
#ifdef STATS
hash_hits++;
#endif
if (![scratchTextStorage isEqualToAttributedString: c->textStorage])
continue;
/* String and attributes match. */
if (!c->hasSize && !hasSize)
{
c->used = orig_used + HIT_BOOST;
hits++;
return j;
}
if (c->hasSize && hasSize && c->givenSize.width == size.width
&& c->givenSize.height == size.height)
{
c->used = orig_used + HIT_BOOST;
hits++;
return j;
}
if (!c->hasSize && hasSize && size.width >= NSMaxX(c->usedRect)
&& size.height >= NSMaxY(c->usedRect))
/* String and attributes match, check size. */
if (is_size_match(c, hasSize, size))
{
c->used = orig_used + HIT_BOOST;
#ifdef STATS
hits++;
#endif
return j;
}
}
NSCAssert(replace != -1, @"Couldn't find a cache entry to replace.");
#ifdef STATS
misses++;
#endif
*matched = 0;
c = cache + replace;
@ -268,93 +272,44 @@ static int cache_match(int hasSize, NSSize size, int useScreenFonts, int *matche
return replace;
}
static int cache_lookup_string(NSString *string, NSDictionary *attributes,
int hasSize, NSSize size, int useScreenFonts)
static inline void prepare_string(NSString *string, NSDictionary *attributes)
{
cache_t *c;
int ci, hit;
NSLayoutManager *layoutManager;
NSTextContainer *textContainer;
if (!did_init)
init_string_drawing();
/*
This is a hack, but it's an efficient way of getting the layout manager
to just ditch all old information, and here, we don't want it to try to
be clever and cache or soft-invalidate anything since the new string has
nothing to do with the old one.
TODO: the layout manager should realize this by itself
*/
[scratchLayoutManager setTextStorage: scratchTextStorage];
[scratchTextStorage beginEditing];
[scratchTextStorage replaceCharactersInRange: NSMakeRange(0, [scratchTextStorage length])
withString: @""];
withString: string];
if ([string length])
{
[scratchTextStorage replaceCharactersInRange: NSMakeRange(0, 0)
withString: string];
[scratchTextStorage setAttributes: attributes
range: NSMakeRange(0, [string length])];
}
[scratchTextStorage endEditing];
ci = cache_match(hasSize, size, useScreenFonts, &hit);
if (hit)
{
return ci;
}
c = &cache[ci];
layoutManager = c->layoutManager;
textContainer = c->textContainer;
if (hasSize)
[textContainer setContainerSize: NSMakeSize(size.width, LARGE_SIZE)];
else
[textContainer setContainerSize: NSMakeSize(LARGE_SIZE, LARGE_SIZE)];
[layoutManager setUsesScreenFonts: useScreenFonts];
c->usedRect = [layoutManager usedRectForTextContainer: textContainer];
return ci;
}
static int cache_lookup_attributed_string(NSAttributedString *string,
int hasSize, NSSize size, int useScreenFonts)
static inline void prepare_attributed_string(NSAttributedString *string)
{
[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;
if (!did_init)
init_string_drawing();
[scratchTextStorage replaceCharactersInRange: NSMakeRange(0, [scratchTextStorage length])
withString: @""];
[scratchTextStorage replaceCharactersInRange: NSMakeRange(0, 0)
withAttributedString: string];
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, LARGE_SIZE)];
[textContainer setContainerSize: NSMakeSize(size.width, size.height)];
else
[textContainer setContainerSize: NSMakeSize(LARGE_SIZE, LARGE_SIZE)];
[layoutManager setUsesScreenFonts: useScreenFonts];
@ -364,7 +319,6 @@ static int cache_lookup_attributed_string(NSAttributedString *string,
return ci;
}
static int use_screen_fonts(void)
{
NSGraphicsContext *ctxt = GSCurrentContext();
@ -407,7 +361,8 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
NS_DURING
{
ci = cache_lookup_attributed_string(self, 0, NSZeroSize, use_screen_fonts());
prepare_attributed_string(self);
ci = cache_lookup(0, NSZeroSize, use_screen_fonts());
c = &cache[ci];
r = NSMakeRange(0, [c->layoutManager numberOfGlyphs]);
@ -449,6 +404,14 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
- (void) drawInRect: (NSRect)rect
{
[self drawWithRect: rect
options: NSStringDrawingUsesLineFragmentOrigin];
}
- (void) drawWithRect: (NSRect)rect
options: (NSStringDrawingOptions)options
{
// FIXME: This ignores options
int ci;
cache_t *c;
@ -459,12 +422,12 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
if (rect.size.width <= 0 || rect.size.height <= 0)
return;
cache_lock();
NS_DURING
{
ci = cache_lookup_attributed_string(self, 1, rect.size, use_screen_fonts());
prepare_attributed_string(self);
ci = cache_lookup(1, rect.size, use_screen_fonts());
c = &cache[ci];
/*
@ -502,7 +465,6 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
[c->layoutManager drawBackgroundForGlyphRange: r
atPoint: rect.origin];
[c->layoutManager drawGlyphsForGlyphRange: r
atPoint: rect.origin];
}
@ -527,58 +489,26 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
}
}
- (void) drawWithRect:(NSRect)rect
options:(NSStringDrawingOptions)options
{
// FIXME: This ignores options
[self drawInRect: rect];
}
- (NSSize) size
{
int ci;
NSSize result = NSZeroSize;
cache_lock();
NS_DURING
{
ci = cache_lookup_attributed_string(self, 0, NSZeroSize, 1);
/*
An argument could be made for using NSMaxX/NSMaxY here, but that fails
horribly on right-aligned strings. For now, we handle that case in a
useful way and ignore indents.
*/
result = cache[ci].usedRect.size;
}
NS_HANDLER
{
cache_unlock();
[localException raise];
}
NS_ENDHANDLER;
cache_unlock();
return result;
NSRect usedRect = [self boundingRectWithSize: NSZeroSize
options: NSStringDrawingUsesLineFragmentOrigin];
return usedRect.size;
}
- (NSRect) boundingRectWithSize: (NSSize)size
options: (NSStringDrawingOptions)options
{
// FIXME: This ignores options
int ci;
NSRect result = NSZeroRect;
int hasSize = NSEqualSizes(NSZeroSize, size) ? 0 : 1;
cache_lock();
NS_DURING
{
// FIXME: This ignores options
ci = cache_lookup_attributed_string(self, 1, size, 1);
/*
An argument could be made for using NSMaxX/NSMaxY here, but that fails
horribly on right-aligned strings. For now, we handle that case in a
useful way and ignore indents.
*/
prepare_attributed_string(self);
ci = cache_lookup(hasSize, size, 1);
result = cache[ci].usedRect;
}
NS_HANDLER
@ -594,10 +524,7 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
@end
/*
TODO: It's severely sub-optimal, but the NSString methods just use
NSAttributedString to do the job.
*/
@implementation NSString (NSStringDrawing)
- (void) drawAtPoint: (NSPoint)point withAttributes: (NSDictionary *)attrs
@ -611,7 +538,8 @@ NSAttributedString to do the job.
cache_lock();
NS_DURING
{
ci = cache_lookup_string(self, attrs, 0, NSZeroSize, use_screen_fonts());
prepare_string(self, attrs);
ci = cache_lookup(0, NSZeroSize, use_screen_fonts());
c = &cache[ci];
r = NSMakeRange(0, [c->layoutManager numberOfGlyphs]);
@ -632,7 +560,6 @@ NSAttributedString to do the job.
[c->layoutManager drawBackgroundForGlyphRange: r
atPoint: point];
[c->layoutManager drawGlyphsForGlyphRange: r
atPoint: point];
}
@ -653,6 +580,16 @@ NSAttributedString to do the job.
- (void) drawInRect: (NSRect)rect withAttributes: (NSDictionary *)attrs
{
[self drawWithRect: rect
options: NSStringDrawingUsesLineFragmentOrigin
attributes: attrs];
}
- (void) drawWithRect: (NSRect)rect
options: (NSStringDrawingOptions)options
attributes: (NSDictionary *)attrs
{
// FIXME: This ignores options
int ci;
cache_t *c;
@ -666,7 +603,8 @@ NSAttributedString to do the job.
cache_lock();
NS_DURING
{
ci = cache_lookup_string(self, attrs, 1, rect.size, use_screen_fonts());
prepare_string(self, attrs);
ci = cache_lookup(1, rect.size, use_screen_fonts());
c = &cache[ci];
/*
@ -704,7 +642,6 @@ NSAttributedString to do the job.
[c->layoutManager drawBackgroundForGlyphRange: r
atPoint: rect.origin];
[c->layoutManager drawGlyphsForGlyphRange: r
atPoint: rect.origin];
}
@ -729,60 +666,28 @@ NSAttributedString to do the job.
}
}
- (void) drawWithRect: (NSRect)rect
options: (NSStringDrawingOptions)options
attributes: (NSDictionary *)attributes
{
// FIXME: This ignores options
[self drawInRect: rect withAttributes: attributes];
}
- (NSSize) sizeWithAttributes: (NSDictionary *)attrs
{
int ci;
NSSize result = NSZeroSize;
cache_lock();
NS_DURING
{
ci = cache_lookup_string(self, attrs, 0, NSZeroSize, 1);
/*
An argument could be made for using NSMaxX/NSMaxY here, but that fails
horribly on right-aligned strings (which may be the right thing). For now,
we handle that case in a useful way and ignore indents.
*/
result = cache[ci].usedRect.size;
}
NS_HANDLER
{
cache_unlock();
[localException raise];
}
NS_ENDHANDLER;
cache_unlock();
return result;
NSRect usedRect = [self boundingRectWithSize: NSZeroSize
options: NSStringDrawingUsesLineFragmentOrigin
attributes: attrs];
return usedRect.size;
}
- (NSRect) boundingRectWithSize: (NSSize)size
options: (NSStringDrawingOptions)options
attributes: (NSDictionary *)attributes
attributes: (NSDictionary *)attrs
{
// FIXME: This ignores options
int ci;
NSRect result = NSZeroRect;
int hasSize = NSEqualSizes(NSZeroSize, size) ? 0 : 1;
cache_lock();
NS_DURING
{
// FIXME: This ignores options
ci = cache_lookup_string(self, attributes, 1, size, 1);
/*
An argument could be made for using NSMaxX/NSMaxY here, but that fails
horribly on right-aligned strings (which may be the right thing). For now,
we handle that case in a useful way and ignore indents.
*/
prepare_string(self, attrs);
ci = cache_lookup(hasSize, size, 1);
result = cache[ci].usedRect;
}
NS_HANDLER