From 584937db01cafe186e3be48bd6dc52d26966f5b4 Mon Sep 17 00:00:00 2001 From: Jjgod Jiang Date: Mon, 21 Jan 2008 21:54:58 +0100 Subject: [PATCH] Experimental ATSUI text rendering support The ATSUI renderer can be enabled by setting the user default MMAtsuiRenderer. At the moment only very basic text drawing is supported. --- src/MacVim/MMAtsuiTextView.h | 13 +- src/MacVim/MMAtsuiTextView.m | 208 ++++++++++++++++++++++++++-- src/MacVim/MMVimController.m | 2 +- src/MacVim/MacVim.xcodeproj/project.pbxproj | 2 + 4 files changed, 210 insertions(+), 15 deletions(-) diff --git a/src/MacVim/MMAtsuiTextView.h b/src/MacVim/MMAtsuiTextView.h index abae6d85..a3bf52a5 100644 --- a/src/MacVim/MMAtsuiTextView.h +++ b/src/MacVim/MMAtsuiTextView.h @@ -13,7 +13,15 @@ enum { MMMaxCellsPerChar = 2 }; - +// TODO: What does DRAW_TRANSP flag do? If the background isn't drawn when +// this flag is set, then sometimes the character after the cursor becomes +// blank. Everything seems to work fine by just ignoring this flag. +#define DRAW_TRANSP 0x01 /* draw with transparant bg */ +#define DRAW_BOLD 0x02 /* draw bold text */ +#define DRAW_UNDERL 0x04 /* draw underline text */ +#define DRAW_UNDERC 0x08 /* draw undercurl text */ +#define DRAW_ITALIC 0x10 /* draw italic text */ +#define DRAW_CURSOR 0x20 @interface MMAtsuiTextView : NSView { // From MMTextStorage @@ -26,6 +34,9 @@ enum { MMMaxCellsPerChar = 2 }; float linespace; // From vim-cocoa + NSImage *contentImage; + NSSize imageSize; + ATSUStyle atsuStyles[MMMaxCellsPerChar]; } - (id)initWithFrame:(NSRect)frame; diff --git a/src/MacVim/MMAtsuiTextView.m b/src/MacVim/MMAtsuiTextView.m index c5e5d9d6..495d4265 100644 --- a/src/MacVim/MMAtsuiTextView.m +++ b/src/MacVim/MMAtsuiTextView.m @@ -18,20 +18,24 @@ #import "MMVimController.h" #import "MacVim.h" - - static char MMKeypadEnter[2] = { 'K', 'A' }; static NSString *MMKeypadEnterString = @"KA"; - +@interface NSFont (AppKitPrivate) +- (ATSUFontID) _atsFontID; +@end @interface MMAtsuiTextView (Private) +- (void)initAtsuStyles; +- (void)disposeAtsuStyles; +- (void)updateAtsuStyles; - (void)dispatchKeyEvent:(NSEvent *)event; - (void)sendKeyDown:(const char *)chars length:(int)len modifiers:(int)flags; - (MMVimController *)vimController; @end @interface MMAtsuiTextView (Drawing) +- (void)fitImageToSize; - (void)beginDrawing; - (void)endDrawing; - (void)drawString:(UniChar *)string length:(UniCharCount)length @@ -59,6 +63,10 @@ static NSString *MMKeypadEnterString = @"KA"; // own font on startup anyway. Just set some bogus values. font = [[NSFont userFixedPitchFontOfSize:0] retain]; cellSize.width = cellSize.height = 1; + contentImage = nil; + imageSize = NSZeroSize; + + [self initAtsuStyles]; } return self; @@ -66,6 +74,7 @@ static NSString *MMKeypadEnterString = @"KA"; - (void)dealloc { + [self disposeAtsuStyles]; [font release]; font = nil; [defaultBackgroundColor release]; defaultBackgroundColor = nil; [defaultForegroundColor release]; defaultForegroundColor = nil; @@ -205,6 +214,8 @@ static NSString *MMKeypadEnterString = @"KA"; // width will not match. cellSize.width = ceilf(em * cellWidthMultiplier); cellSize.height = linespace + [newFont defaultLineHeightForFont]; + + [self updateAtsuStyles]; } } @@ -468,18 +479,21 @@ static NSString *MMKeypadEnterString = @"KA"; - (BOOL)isFlipped { - return YES; + return NO; } - (void)drawRect:(NSRect)rect { - NSColor *color = defaultBackgroundColor ? defaultBackgroundColor - : [NSColor lightGrayColor]; - [color set]; - [NSBezierPath fillRect:rect]; + [contentImage drawInRect: rect + fromRect: rect + operation: NSCompositeCopy + fraction: 1.0]; } - +- (BOOL) wantsDefaultClipping +{ + return NO; +} #define MM_DEBUG_DRAWING 0 @@ -489,6 +503,9 @@ static NSString *MMKeypadEnterString = @"KA"; const void *bytes = [data bytes]; const void *end = bytes + [data length]; + if (! NSEqualSizes(imageSize, [self size])) + [self fitImageToSize]; + #if MM_DEBUG_DRAWING NSLog(@"====> BEGIN %s", _cmd); #endif @@ -541,17 +558,26 @@ static NSString *MMKeypadEnterString = @"KA"; int cells = *((int*)bytes); bytes += sizeof(int); int flags = *((int*)bytes); bytes += sizeof(int); int len = *((int*)bytes); bytes += sizeof(int); - UniChar *string = (UniChar*)bytes; bytes += len; - + // UniChar *string = (UniChar*)bytes; bytes += len; + NSString *string = [[NSString alloc] initWithBytesNoCopy:(void*)bytes + length:len + encoding:NSUTF8StringEncoding + freeWhenDone:NO]; + bytes += len; #if MM_DEBUG_DRAWING NSLog(@" Draw string at (%d,%d) length=%d flags=%d fg=0x%x " "bg=0x%x sp=0x%x", row, col, len, flags, fg, bg, sp); #endif - [self drawString:string length:len atRow:row column:col + unichar *characters = malloc(sizeof(unichar) * [string length]); + [string getCharacters:characters]; + + [self drawString:characters length:[string length] atRow:row column:col cells:cells withFlags:flags foregroundColor:[NSColor colorWithRgbInt:fg] backgroundColor:[NSColor colorWithArgbInt:bg] specialColor:[NSColor colorWithRgbInt:sp]]; + free(characters); + [string release]; } else if (InsertLinesDrawType == type) { unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned); int row = *((int*)bytes); bytes += sizeof(int); @@ -588,7 +614,8 @@ static NSString *MMKeypadEnterString = @"KA"; // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows // and columns are changed (due to ipc delays). Force a redraw here. - [self displayIfNeeded]; + [self setNeedsDisplay:YES]; + // [self displayIfNeeded]; #if MM_DEBUG_DRAWING NSLog(@"<==== END %s", _cmd); @@ -602,6 +629,71 @@ static NSString *MMKeypadEnterString = @"KA"; @implementation MMAtsuiTextView (Private) +- (void)initAtsuStyles +{ + int i; + for (i = 0; i < MMMaxCellsPerChar; i++) + ATSUCreateStyle(&atsuStyles[i]); +} + +- (void)disposeAtsuStyles +{ + int i; + + for (i = 0; i < MMMaxCellsPerChar; i++) + if (atsuStyles[i] != NULL) + { + if (ATSUDisposeStyle(atsuStyles[i]) != noErr) + atsuStyles[i] = NULL; + } +} + +- (void)updateAtsuStyles +{ + ATSUFontID fontID; + Fixed fontSize; + Fixed fontWidth; + int i; + CGAffineTransform transform = CGAffineTransformMakeScale(1, -1); + ATSStyleRenderingOptions options; + + fontID = [font _atsFontID]; + fontSize = Long2Fix([font pointSize]); + options = kATSStyleApplyAntiAliasing; + + ATSUAttributeTag attribTags[] = + { + kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag, + kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag, + kATSUMaxATSUITagValue + 1 + }; + + ByteCount attribSizes[] = + { + sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth), + sizeof(CGAffineTransform), sizeof(ATSStyleRenderingOptions), + sizeof(font) + }; + + ATSUAttributeValuePtr attribValues[] = + { + &fontID, &fontSize, &fontWidth, &transform, &options, &font + }; + + for (i = 0; i < MMMaxCellsPerChar; i++) + { + fontWidth = Long2Fix(cellSize.width * (i + 1)); + + if (ATSUSetAttributes(atsuStyles[i], + (sizeof attribTags) / sizeof(ATSUAttributeTag), + attribTags, attribSizes, attribValues) != noErr) + { + ATSUDisposeStyle(atsuStyles[i]); + atsuStyles[i] = NULL; + } + } +} + - (void)dispatchKeyEvent:(NSEvent *)event { // Only handle the command if it came from a keyDown event @@ -679,12 +771,31 @@ static NSString *MMKeypadEnterString = @"KA"; @implementation MMAtsuiTextView (Drawing) +- (NSRect)rectFromRow:(int)row1 column:(int)col1 + toRow:(int)row2 column:(int)col2 +{ + return NSMakeRect(col1 * cellSize.width, row1 * cellSize.height, + (col2 + 1 - col1) * cellSize.width, + (row2 + 1 - row1) * cellSize.height); +} + - (void)beginDrawing { + [contentImage lockFocus]; } - (void)endDrawing { + [contentImage unlockFocus]; +} + +- (void)fitImageToSize +{ + NSLog(@"fitImageToSize"); + [contentImage release]; + contentImage = [[NSImage alloc] initWithSize:[self size]]; + [contentImage setFlipped: YES]; + imageSize = [self size]; } - (void)drawString:(UniChar *)string length:(UniCharCount)length @@ -695,27 +806,98 @@ static NSString *MMKeypadEnterString = @"KA"; // 'string' consists of 'length' utf-16 code pairs and should cover 'cells' // display cells (a normal character takes up one display cell, a wide // character takes up two) + ATSUStyle style = atsuStyles[0]; + ATSUTextLayout layout; + + // NSLog(@"drawString: %d", length); + + ATSUCreateTextLayout(&layout); + ATSUSetTextPointerLocation(layout, string, + kATSUFromTextBeginning, kATSUToTextEnd, + length); + ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd); + + NSRect rect = NSMakeRect(col * cellSize.width, row * cellSize.height, + length * cellSize.width, cellSize.height); + CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; + + ATSUAttributeTag tags[] = { kATSUCGContextTag }; + ByteCount sizes[] = { sizeof(CGContextRef) }; + ATSUAttributeValuePtr values[] = { &context }; + ATSUSetLayoutControls(layout, 1, tags, sizes, values); + + if (! (flags & DRAW_TRANSP)) + { + [bg set]; + NSRectFill(rect); + } + + [fg set]; + + ATSUSetTransientFontMatching(layout, TRUE); + ATSUDrawText(layout, + kATSUFromTextBeginning, + kATSUToTextEnd, + X2Fix(rect.origin.x), + X2Fix(rect.origin.y + [font ascender])); + ATSUDisposeTextLayout(layout); +} + +- (void)scrollRect:(NSRect)rect lineCount:(int)count +{ + NSPoint destPoint = rect.origin; + destPoint.y += count * cellSize.height; + + NSCopyBits(0, rect, destPoint); } - (void)deleteLinesFromRow:(int)row lineCount:(int)count scrollBottom:(int)bottom left:(int)left right:(int)right color:(NSColor *)color { + NSRect rect = [self rectFromRow:row + count + column:left + toRow:bottom + column:right]; + [color set]; + // move rect up for count lines + [self scrollRect:rect lineCount:-count]; + [self clearBlockFromRow:bottom - count + 1 + column:left + toRow:bottom + column:right + color:color]; } - (void)insertLinesAtRow:(int)row lineCount:(int)count scrollBottom:(int)bottom left:(int)left right:(int)right color:(NSColor *)color { + NSRect rect = [self rectFromRow:row + column:left + toRow:bottom - count + column:right]; + [color set]; + // move rect down for count lines + [self scrollRect:rect lineCount:count]; + [self clearBlockFromRow:row + column:left + toRow:row + count - 1 + column:right + color:color]; } - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2 column:(int)col2 color:(NSColor *)color { + [color set]; + NSRectFill([self rectFromRow:row1 column:col1 toRow:row2 column:col2]); } - (void)clearAll { + [defaultBackgroundColor set]; + NSRectFill(NSMakeRect(0, 0, imageSize.width, imageSize.height)); } @end // MMAtsuiTextView (Drawing) diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index 57ac6f45..4a036cbd 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -556,7 +556,7 @@ static NSTimeInterval MMResendInterval = 0.5; } else if (BatchDrawMsgID == msgid) { if ([[NSUserDefaults standardUserDefaults] boolForKey:MMAtsuiRendererKey]) - [[windowController textView] performBatchDrawWithData:data]; + [(MMAtsuiTextView *)[windowController textView] performBatchDrawWithData:data]; else [self performBatchDrawWithData:data]; } else if (SelectTabMsgID == msgid) { diff --git a/src/MacVim/MacVim.xcodeproj/project.pbxproj b/src/MacVim/MacVim.xcodeproj/project.pbxproj index da1640dd..211e55a9 100644 --- a/src/MacVim/MacVim.xcodeproj/project.pbxproj +++ b/src/MacVim/MacVim.xcodeproj/project.pbxproj @@ -430,6 +430,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "MacVim" */; + compatibilityVersion = "Xcode 2.4"; hasScannedForEncodings = 1; mainGroup = 29B97314FDCFA39411CA2CEA /* MacVim */; projectDirPath = ""; @@ -439,6 +440,7 @@ ProjectRef = 1D493DB30C52533B00AB718C /* PSMTabBarControl.xcodeproj */; }, ); + projectRoot = ""; targets = ( 8D1107260486CEB800E47090 /* MacVim */, ); -- 2.11.4.GIT