mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-06-01 21:51:55 +00:00
Reimplement using cache system for layout information.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@18478 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
58ec69b0ae
commit
3083c7c0f6
2 changed files with 471 additions and 127 deletions
15
ChangeLog
15
ChangeLog
|
@ -1,3 +1,18 @@
|
||||||
|
2004-01-25 16:16 Alexander Malmberg <alexander@malmberg.org>
|
||||||
|
|
||||||
|
* Source/NSStringDrawing.m: Big redesign.
|
||||||
|
|
||||||
|
(cache_match, cache_lookup_string, cache_lookup_attributed_string,
|
||||||
|
use_screen_fonts): New helper functions for caching layout
|
||||||
|
information.
|
||||||
|
|
||||||
|
(init_string_drawing, [NSAttributedString -drawAtPoint:],
|
||||||
|
[NSAttributedString -drawInRect:], [NSAttributedString -size],
|
||||||
|
[NSString -drawAtPoint:withAttributes:],
|
||||||
|
[NSString -drawInRect:withAttributes:],
|
||||||
|
[NSString -sizeWithAttributes:]): Reimplement using the cache system
|
||||||
|
and the new helper functions.
|
||||||
|
|
||||||
2004-01-25 Fred Kiefer <FredKiefer@gmx.de>
|
2004-01-25 Fred Kiefer <FredKiefer@gmx.de>
|
||||||
|
|
||||||
* Source/NSWindow.m (-orderWindow:relativeTo:): In OrderOut case
|
* Source/NSWindow.m (-orderWindow:relativeTo:): In OrderOut case
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<abstract>Categories which add drawing capabilities to NSAttributedString
|
<abstract>Categories which add drawing capabilities to NSAttributedString
|
||||||
and NSString.</abstract>
|
and NSString.</abstract>
|
||||||
|
|
||||||
Copyright (C) 1999, 2003 Free Software Foundation, Inc.
|
Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc.
|
||||||
|
|
||||||
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
||||||
Date: Mar 1999 - rewrite from scratch
|
Date: Mar 1999 - rewrite from scratch
|
||||||
|
@ -31,6 +31,8 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <Foundation/NSException.h>
|
||||||
|
|
||||||
#include "AppKit/NSAffineTransform.h"
|
#include "AppKit/NSAffineTransform.h"
|
||||||
#include "AppKit/NSLayoutManager.h"
|
#include "AppKit/NSLayoutManager.h"
|
||||||
#include "AppKit/NSTextContainer.h"
|
#include "AppKit/NSTextContainer.h"
|
||||||
|
@ -42,40 +44,328 @@
|
||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
these methods are _not_ reentrant. should they be?
|
these methods are _not_ reentrant. should they be?
|
||||||
|
|
||||||
We could save time by trying to avoid changing the text storage and
|
|
||||||
container size if the new rect and text are the same as the previous.
|
|
||||||
This might be a common case if lots of calls to -size... and -draw...
|
|
||||||
are paired.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#define LARGE_SIZE 8e6
|
#define LARGE_SIZE 4e6
|
||||||
/*
|
/*
|
||||||
8e6 is not as arbitrary as it seems. 8e6 is chosen because it's close to
|
4e6 is chosen because it's close to (half of, for extra margin) 1<<23-1, the
|
||||||
1<<23-1, the largest number that can be stored in a 32-bit float with an
|
largest number that can be stored in a 32-bit float with an ulp of 0.5, which
|
||||||
ulp of 0.5, which means things should round to the correct whole point.
|
means things should round to the correct whole point.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static NSTextStorage *textStorage;
|
|
||||||
static NSLayoutManager *layoutManager;
|
#define NUM_CACHE_ENTRIES 16
|
||||||
static NSTextContainer *textContainer;
|
#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).
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int used;
|
||||||
|
unsigned int string_hash;
|
||||||
|
int hasSize, useScreenFonts;
|
||||||
|
|
||||||
|
NSTextStorage *textStorage;
|
||||||
|
NSLayoutManager *layoutManager;
|
||||||
|
NSTextContainer *textContainer;
|
||||||
|
|
||||||
|
NSSize givenSize;
|
||||||
|
NSRect usedRect;
|
||||||
|
} cache_t;
|
||||||
|
|
||||||
|
|
||||||
|
static BOOL did_init;
|
||||||
|
static cache_t cache[NUM_CACHE_ENTRIES];
|
||||||
|
|
||||||
|
static NSTextStorage *scratchTextStorage;
|
||||||
|
static NSLayoutManager *scratchLayoutManager;
|
||||||
|
static NSTextContainer *scratchTextContainer;
|
||||||
|
|
||||||
|
|
||||||
|
static int total, hits, misses, hash_hits;
|
||||||
|
|
||||||
|
/* For collecting statistics. */
|
||||||
|
//#define STATS
|
||||||
|
|
||||||
|
#ifdef STATS
|
||||||
|
static void NSStringDrawing_dump_stats(void)
|
||||||
|
{
|
||||||
|
#define P(x) printf("%15i %s\n", x, #x);
|
||||||
|
P(total)
|
||||||
|
P(hits)
|
||||||
|
P(misses)
|
||||||
|
P(hash_hits)
|
||||||
|
#undef P
|
||||||
|
printf("%15.8f hit ratio\n", hits / (double)total);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static void init_string_drawing(void)
|
static void init_string_drawing(void)
|
||||||
{
|
{
|
||||||
if (textStorage)
|
int i;
|
||||||
return;
|
NSTextStorage *textStorage;
|
||||||
|
NSLayoutManager *layoutManager;
|
||||||
|
NSTextContainer *textContainer;
|
||||||
|
|
||||||
textStorage = [[NSTextStorage alloc] init];
|
if (did_init)
|
||||||
layoutManager = [[NSLayoutManager alloc] init];
|
return;
|
||||||
[textStorage addLayoutManager: layoutManager];
|
did_init = YES;
|
||||||
[layoutManager release];
|
|
||||||
textContainer = [[NSTextContainer alloc]
|
#ifdef STATS
|
||||||
initWithContainerSize: NSMakeSize(10,10)];
|
atexit(NSStringDrawing_dump_stats);
|
||||||
[layoutManager addTextContainer: textContainer];
|
#endif
|
||||||
[textContainer release];
|
|
||||||
|
for (i = 0; i < NUM_CACHE_ENTRIES + 1; i++)
|
||||||
|
{
|
||||||
|
textStorage = [[NSTextStorage alloc] init];
|
||||||
|
layoutManager = [[NSLayoutManager alloc] init];
|
||||||
|
[textStorage addLayoutManager: layoutManager];
|
||||||
|
[layoutManager release];
|
||||||
|
textContainer = [[NSTextContainer alloc]
|
||||||
|
initWithContainerSize: NSMakeSize(10, 10)];
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int cache_match(int hasSize, NSSize size, int useScreenFonts, int *matched)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
cache_t *c;
|
||||||
|
int least_used;
|
||||||
|
int replace;
|
||||||
|
int orig_used;
|
||||||
|
|
||||||
|
unsigned int string_hash = [[scratchTextStorage string] hash];
|
||||||
|
|
||||||
|
total++;
|
||||||
|
|
||||||
|
*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
|
||||||
|
unused entries, but the new entries keep replacing each other).
|
||||||
|
|
||||||
|
By starting at a random index, we avoid this kind of problem.
|
||||||
|
*/
|
||||||
|
j = random() % NUM_CACHE_ENTRIES;
|
||||||
|
for (i = 0; i < NUM_CACHE_ENTRIES; i++, j++)
|
||||||
|
{
|
||||||
|
if (j == NUM_CACHE_ENTRIES)
|
||||||
|
j = 0;
|
||||||
|
c = cache + j;
|
||||||
|
if (least_used == -1 || c->used < least_used)
|
||||||
|
{
|
||||||
|
least_used = c->used;
|
||||||
|
replace = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
hash_hits++;
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
c->used = orig_used + HIT_BOOST;
|
||||||
|
hits++;
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSCAssert(replace != -1, @"Couldn't find a cache entry to replace.");
|
||||||
|
|
||||||
|
misses++;
|
||||||
|
*matched = 0;
|
||||||
|
|
||||||
|
c = cache + replace;
|
||||||
|
c->used = 1;
|
||||||
|
c->string_hash = string_hash;
|
||||||
|
c->hasSize = hasSize;
|
||||||
|
c->useScreenFonts = useScreenFonts;
|
||||||
|
c->givenSize = size;
|
||||||
|
|
||||||
|
{
|
||||||
|
id temp;
|
||||||
|
|
||||||
|
#define SWAP(a, b) temp = a; a = b; b = temp;
|
||||||
|
SWAP(scratchTextStorage, c->textStorage)
|
||||||
|
SWAP(scratchLayoutManager, c->layoutManager)
|
||||||
|
SWAP(scratchTextContainer, c->textContainer)
|
||||||
|
#undef SWAP
|
||||||
|
}
|
||||||
|
|
||||||
|
return replace;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int cache_lookup_string(NSString *string, NSDictionary *attributes,
|
||||||
|
int hasSize, NSSize size, int useScreenFonts)
|
||||||
|
{
|
||||||
|
cache_t *c;
|
||||||
|
int ci, hit;
|
||||||
|
NSTextStorage *textStorage;
|
||||||
|
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: @""];
|
||||||
|
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];
|
||||||
|
|
||||||
|
textStorage = c->textStorage;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
cache_t *c;
|
||||||
|
int ci, hit;
|
||||||
|
NSTextStorage *textStorage;
|
||||||
|
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;
|
||||||
|
|
||||||
|
c = &cache[ci];
|
||||||
|
|
||||||
|
textStorage = c->textStorage;
|
||||||
|
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 use_screen_fonts(void)
|
||||||
|
{
|
||||||
|
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||||
|
NSAffineTransform *ctm = GSCurrentCTM(ctxt);
|
||||||
|
|
||||||
|
if (ctm->matrix.m11 != 1.0 || ctm->matrix.m12 != 0.0 ||
|
||||||
|
ctm->matrix.m21 != 0.0 || fabs(ctm->matrix.m22) != 1.0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is an ugly hack to get text to display correctly in non-flipped views.
|
This is an ugly hack to get text to display correctly in non-flipped views.
|
||||||
|
|
||||||
|
@ -87,40 +377,24 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
||||||
+(void) _setFontFlipHack: (BOOL)flip;
|
+(void) _setFontFlipHack: (BOOL)flip;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@implementation NSAttributedString (NSStringDrawing)
|
@implementation NSAttributedString (NSStringDrawing)
|
||||||
|
|
||||||
- (void) drawAtPoint: (NSPoint)point
|
- (void) drawAtPoint: (NSPoint)point
|
||||||
{
|
{
|
||||||
|
int ci;
|
||||||
|
cache_t *c;
|
||||||
|
|
||||||
NSRange r;
|
NSRange r;
|
||||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||||
NSAffineTransform *ctm = GSCurrentCTM(ctxt);
|
|
||||||
|
|
||||||
init_string_drawing();
|
ci = cache_lookup_attributed_string(self, 0, NSZeroSize, use_screen_fonts());
|
||||||
|
c = &cache[ci];
|
||||||
|
|
||||||
[textStorage replaceCharactersInRange: NSMakeRange(0, [textStorage length])
|
r = NSMakeRange(0, [c->layoutManager numberOfGlyphs]);
|
||||||
withString: @""];
|
|
||||||
|
|
||||||
[textContainer setContainerSize: NSMakeSize(LARGE_SIZE, LARGE_SIZE)];
|
|
||||||
|
|
||||||
if (ctm->matrix.m11 != 1.0 || ctm->matrix.m12 != 0.0 ||
|
|
||||||
ctm->matrix.m21 != 0.0 || fabs(ctm->matrix.m22) != 1.0)
|
|
||||||
{
|
|
||||||
[layoutManager setUsesScreenFonts: NO];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[layoutManager setUsesScreenFonts: YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
[textStorage replaceCharactersInRange: NSMakeRange(0, 0)
|
|
||||||
withAttributedString: self];
|
|
||||||
|
|
||||||
r = NSMakeRange(0, [layoutManager numberOfGlyphs]);
|
|
||||||
|
|
||||||
if (![[NSView focusView] isFlipped])
|
if (![[NSView focusView] isFlipped])
|
||||||
{
|
{
|
||||||
NSRect usedRect;
|
|
||||||
|
|
||||||
DPSscale(ctxt, 1, -1);
|
DPSscale(ctxt, 1, -1);
|
||||||
point.y = -point.y;
|
point.y = -point.y;
|
||||||
|
|
||||||
|
@ -128,17 +402,16 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
||||||
Adjust point.y so the lower left corner of the used rect is at the
|
Adjust point.y so the lower left corner of the used rect is at the
|
||||||
point that was passed to us.
|
point that was passed to us.
|
||||||
*/
|
*/
|
||||||
usedRect = [layoutManager usedRectForTextContainer: textContainer];
|
point.y -= NSMaxY(c->usedRect);
|
||||||
point.y -= NSMaxY(usedRect);
|
|
||||||
|
|
||||||
[NSFont _setFontFlipHack: YES];
|
[NSFont _setFontFlipHack: YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
[layoutManager drawBackgroundForGlyphRange: r
|
[c->layoutManager drawBackgroundForGlyphRange: r
|
||||||
atPoint: point];
|
atPoint: point];
|
||||||
|
|
||||||
[layoutManager drawGlyphsForGlyphRange: r
|
[c->layoutManager drawGlyphsForGlyphRange: r
|
||||||
atPoint: point];
|
atPoint: point];
|
||||||
|
|
||||||
if (![[NSView focusView] isFlipped])
|
if (![[NSView focusView] isFlipped])
|
||||||
{
|
{
|
||||||
|
@ -149,38 +422,18 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
||||||
|
|
||||||
- (void) drawInRect: (NSRect)rect
|
- (void) drawInRect: (NSRect)rect
|
||||||
{
|
{
|
||||||
|
int ci;
|
||||||
|
cache_t *c;
|
||||||
|
|
||||||
NSRange r;
|
NSRange r;
|
||||||
BOOL need_clip;
|
BOOL need_clip;
|
||||||
NSRect used;
|
|
||||||
NSGraphicsContext *ctxt = GSCurrentContext();
|
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||||
NSAffineTransform *ctm = GSCurrentCTM(ctxt);
|
|
||||||
|
|
||||||
init_string_drawing();
|
if (rect.size.width <= 0 || rect.size.height <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
[textStorage replaceCharactersInRange: NSMakeRange(0, [textStorage length])
|
ci = cache_lookup_attributed_string(self, 1, rect.size, use_screen_fonts());
|
||||||
withString: @""];
|
c = &cache[ci];
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: Use rect.size.heigth instead of LARGE_SIZE? Should make things faster,
|
|
||||||
since we'll only typeset what fits, but lines that used to fit partially
|
|
||||||
won't fit at all.
|
|
||||||
*/
|
|
||||||
[textContainer setContainerSize: NSMakeSize(rect.size.width, LARGE_SIZE)];
|
|
||||||
|
|
||||||
if (ctm->matrix.m11 != 1.0 || ctm->matrix.m12 != 0.0 ||
|
|
||||||
ctm->matrix.m21 != 0.0 || fabs(ctm->matrix.m22) != 1.0)
|
|
||||||
{
|
|
||||||
[layoutManager setUsesScreenFonts: NO];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[layoutManager setUsesScreenFonts: YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
[textStorage replaceCharactersInRange: NSMakeRange(0, 0)
|
|
||||||
withAttributedString: self];
|
|
||||||
|
|
||||||
used = [layoutManager usedRectForTextContainer: textContainer];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If the used rect fits completely in the rect we draw in, we save time
|
If the used rect fits completely in the rect we draw in, we save time
|
||||||
|
@ -189,8 +442,9 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
||||||
This isn't completely safe; the used rect isn't guaranteed to contain
|
This isn't completely safe; the used rect isn't guaranteed to contain
|
||||||
all parts of all glyphs.
|
all parts of all glyphs.
|
||||||
*/
|
*/
|
||||||
if (used.origin.x >= 0 && used.origin.y <= 0 &&
|
if (c->usedRect.origin.x >= 0 && c->usedRect.origin.y <= 0
|
||||||
NSMaxX(used) <= rect.size.width && NSMaxY(used) <= rect.size.height)
|
&& NSMaxX(c->usedRect) <= rect.size.width
|
||||||
|
&& NSMaxY(c->usedRect) <= rect.size.height)
|
||||||
{
|
{
|
||||||
need_clip = NO;
|
need_clip = NO;
|
||||||
}
|
}
|
||||||
|
@ -202,9 +456,10 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
||||||
rect.size.width, rect.size.height);
|
rect.size.width, rect.size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = [layoutManager
|
r = [c->layoutManager
|
||||||
glyphRangeForBoundingRect: NSMakeRect(0, 0, rect.size.width, rect.size.height)
|
glyphRangeForBoundingRect: NSMakeRect(0, 0, rect.size.width,
|
||||||
inTextContainer: textContainer];
|
rect.size.height)
|
||||||
|
inTextContainer: c->textContainer];
|
||||||
|
|
||||||
if (![[NSView focusView] isFlipped])
|
if (![[NSView focusView] isFlipped])
|
||||||
{
|
{
|
||||||
|
@ -213,11 +468,11 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
||||||
[NSFont _setFontFlipHack: YES];
|
[NSFont _setFontFlipHack: YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
[layoutManager drawBackgroundForGlyphRange: r
|
[c->layoutManager drawBackgroundForGlyphRange: r
|
||||||
atPoint: rect.origin];
|
atPoint: rect.origin];
|
||||||
|
|
||||||
[layoutManager drawGlyphsForGlyphRange: r
|
[c->layoutManager drawGlyphsForGlyphRange: r
|
||||||
atPoint: rect.origin];
|
atPoint: rect.origin];
|
||||||
|
|
||||||
[NSFont _setFontFlipHack: NO];
|
[NSFont _setFontFlipHack: NO];
|
||||||
if (![[NSView focusView] isFlipped])
|
if (![[NSView focusView] isFlipped])
|
||||||
|
@ -234,24 +489,14 @@ glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
||||||
|
|
||||||
- (NSSize) size
|
- (NSSize) size
|
||||||
{
|
{
|
||||||
NSRange r;
|
int ci;
|
||||||
NSRect rect;
|
ci = cache_lookup_attributed_string(self, 0, NSZeroSize, 1);
|
||||||
|
/*
|
||||||
init_string_drawing();
|
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
|
||||||
[textStorage replaceCharactersInRange: NSMakeRange(0, [textStorage length])
|
useful way and ignore indents.
|
||||||
withString: @""];
|
*/
|
||||||
|
return cache[ci].usedRect.size;
|
||||||
[textContainer setContainerSize: NSMakeSize(LARGE_SIZE, LARGE_SIZE)];
|
|
||||||
[layoutManager setUsesScreenFonts: YES];
|
|
||||||
|
|
||||||
[textStorage replaceCharactersInRange: NSMakeRange(0, 0)
|
|
||||||
withAttributedString: self];
|
|
||||||
|
|
||||||
r = NSMakeRange(0, [layoutManager numberOfGlyphs]);
|
|
||||||
|
|
||||||
rect = [layoutManager usedRectForTextContainer: textContainer];
|
|
||||||
return rect.size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -264,37 +509,121 @@ NSAttributedString to do the job.
|
||||||
|
|
||||||
- (void) drawAtPoint: (NSPoint)point withAttributes: (NSDictionary *)attrs
|
- (void) drawAtPoint: (NSPoint)point withAttributes: (NSDictionary *)attrs
|
||||||
{
|
{
|
||||||
NSAttributedString *a;
|
int ci;
|
||||||
|
cache_t *c;
|
||||||
|
|
||||||
a = [[NSAttributedString allocWithZone: NSDefaultMallocZone()]
|
NSRange r;
|
||||||
initWithString: self
|
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||||
attributes: attrs];
|
|
||||||
[a drawAtPoint: point];
|
ci = cache_lookup_string(self, attrs, 0, NSZeroSize, use_screen_fonts());
|
||||||
RELEASE(a);
|
c = &cache[ci];
|
||||||
|
|
||||||
|
r = NSMakeRange(0, [c->layoutManager numberOfGlyphs]);
|
||||||
|
|
||||||
|
if (![[NSView focusView] isFlipped])
|
||||||
|
{
|
||||||
|
DPSscale(ctxt, 1, -1);
|
||||||
|
point.y = -point.y;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Adjust point.y so the lower left corner of the used rect is at the
|
||||||
|
point that was passed to us.
|
||||||
|
*/
|
||||||
|
point.y -= NSMaxY(c->usedRect);
|
||||||
|
|
||||||
|
[NSFont _setFontFlipHack: YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
[c->layoutManager drawBackgroundForGlyphRange: r
|
||||||
|
atPoint: point];
|
||||||
|
|
||||||
|
[c->layoutManager drawGlyphsForGlyphRange: r
|
||||||
|
atPoint: point];
|
||||||
|
|
||||||
|
if (![[NSView focusView] isFlipped])
|
||||||
|
{
|
||||||
|
DPSscale(ctxt, 1, -1);
|
||||||
|
[NSFont _setFontFlipHack: NO];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) drawInRect: (NSRect)rect withAttributes: (NSDictionary *)attrs
|
- (void) drawInRect: (NSRect)rect withAttributes: (NSDictionary *)attrs
|
||||||
{
|
{
|
||||||
NSAttributedString *a;
|
int ci;
|
||||||
|
cache_t *c;
|
||||||
|
|
||||||
a = [[NSAttributedString allocWithZone: NSDefaultMallocZone()]
|
NSRange r;
|
||||||
initWithString: self
|
BOOL need_clip;
|
||||||
attributes: attrs];
|
NSGraphicsContext *ctxt = GSCurrentContext();
|
||||||
[a drawInRect: rect];
|
|
||||||
RELEASE(a);
|
if (rect.size.width <= 0 || rect.size.height <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ci = cache_lookup_string(self, attrs, 1, rect.size, use_screen_fonts());
|
||||||
|
c = &cache[ci];
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the used rect fits completely in the rect we draw in, we save time
|
||||||
|
by avoiding the DPSrectclip (and the state save and restore).
|
||||||
|
|
||||||
|
This isn't completely safe; the used rect isn't guaranteed to contain
|
||||||
|
all parts of all glyphs.
|
||||||
|
*/
|
||||||
|
if (c->usedRect.origin.x >= 0 && c->usedRect.origin.y <= 0
|
||||||
|
&& NSMaxX(c->usedRect) <= rect.size.width
|
||||||
|
&& NSMaxY(c->usedRect) <= rect.size.height)
|
||||||
|
{
|
||||||
|
need_clip = NO;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
need_clip = YES;
|
||||||
|
DPSgsave(ctxt);
|
||||||
|
DPSrectclip(ctxt, rect.origin.x, rect.origin.y,
|
||||||
|
rect.size.width, rect.size.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = [c->layoutManager
|
||||||
|
glyphRangeForBoundingRect: NSMakeRect(0, 0, rect.size.width,
|
||||||
|
rect.size.height)
|
||||||
|
inTextContainer: c->textContainer];
|
||||||
|
|
||||||
|
if (![[NSView focusView] isFlipped])
|
||||||
|
{
|
||||||
|
DPSscale(ctxt, 1, -1);
|
||||||
|
rect.origin.y = -NSMaxY(rect);
|
||||||
|
[NSFont _setFontFlipHack: YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
[c->layoutManager drawBackgroundForGlyphRange: r
|
||||||
|
atPoint: rect.origin];
|
||||||
|
|
||||||
|
[c->layoutManager drawGlyphsForGlyphRange: r
|
||||||
|
atPoint: rect.origin];
|
||||||
|
|
||||||
|
[NSFont _setFontFlipHack: NO];
|
||||||
|
if (![[NSView focusView] isFlipped])
|
||||||
|
{
|
||||||
|
DPSscale(ctxt, 1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_clip)
|
||||||
|
{
|
||||||
|
/* Restore the original clipping path. */
|
||||||
|
DPSgrestore(ctxt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSSize) sizeWithAttributes: (NSDictionary *)attrs
|
- (NSSize) sizeWithAttributes: (NSDictionary *)attrs
|
||||||
{
|
{
|
||||||
NSAttributedString *a;
|
int ci;
|
||||||
NSSize s;
|
ci = cache_lookup_string(self, attrs, 0, NSZeroSize, 1);
|
||||||
|
/*
|
||||||
a = [[NSAttributedString allocWithZone: NSDefaultMallocZone()]
|
An argument could be made for using NSMaxX/NSMaxY here, but that fails
|
||||||
initWithString: self
|
horribly on right-aligned strings (which may be the right thing). For now,
|
||||||
attributes: attrs];
|
we handle that case in a useful way and ignore indents.
|
||||||
s = [a size];
|
*/
|
||||||
RELEASE(a);
|
return cache[ci].usedRect.size;
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue