From acbe7c3149947fe07dee3c2ff0f25e7a16c3a676 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 03:26:32 -0400 Subject: [PATCH] Implemented uiAttributeTypeBackground for OS X 10.12 and newer using the attribute included with Core Text. Also laid out a non-block-based API for drawing backgrounds on older versions of OS X (not implemented here yet, however; that will require bringing back the old metrics code). --- darwin/attrstr.h | 16 +++++++++++-- darwin/attrstr.m | 52 ++++++++++++++++++++++-------------------- darwin/drawtext.m | 35 +++++++++++++++++++++++----- darwin/future.m | 4 ++++ darwin/uipriv_darwin.h | 1 + 5 files changed, 75 insertions(+), 33 deletions(-) diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 384a607b..f77b4d86 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -74,5 +74,17 @@ extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axi // attrstr.m extern void uiprivInitUnderlineColors(void); extern void uiprivUninitUnderlineColors(void); -typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); -extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); +extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams); + +// drawtext.m +@interface uiprivDrawTextBackgroundParams : NSObject { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +} +- (id)initWithStart:(size_t)s end:(size_t)e r:(double)red g:(double)green b:(double)blue a:(double)alpha; +- (void)draw:(CGContextRef)c layout:(uiDrawTextLayout *)layout at:(double)x y:(double)y utf8Mapping:(const size_t *)u16tou8; +@end diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 2c27f12c..530e6a22 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -65,7 +65,7 @@ void uiprivUninitUnderlineColors(void) // TODO in fact I should just write something to explain everything in this file... struct foreachParams { CFMutableAttributedStringRef mas; - NSMutableArray *backgroundBlocks; + NSMutableArray *backgroundParams; }; // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute @@ -271,20 +271,6 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ } } -static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, double g, double b, double a) -{ - return Block_copy(^(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { - uiDrawBrush brush; - - brush.Type = uiDrawBrushTypeSolid; - brush.R = r; - brush.G = g; - brush.B = b; - brush.A = a; -//TODO drawTextBackground(c, x, y, layout, start, end, &brush, 0); - }); -} - static CGColorRef mkcolor(double r, double g, double b, double a) { CGColorSpaceRef colorspace; @@ -305,20 +291,38 @@ static CGColorRef mkcolor(double r, double g, double b, double a) return color; } +static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t end, double r, double g, double b, double a) +{ + uiprivDrawTextBackgroundParams *dtb; + + // TODO make sure this works properly with line paragraph spacings (after figuring out what that means, of course) + if (FUTURE_kCTBackgroundColorAttributeName != NULL) { + CGColorRef color; + CFRange range; + + color = mkcolor(r, g, b, a); + range.location = start; + range.length = end - start; + CFAttributedStringSetAttribute(p->mas, range, *FUTURE_kCTBackgroundColorAttributeName, color); + CFRelease(color); + return; + } + + dtb = [[uiprivDrawTextBackgroundParams alloc] initWithStart:start end:end r:r g:g b:b a:a]; + [p->backgroundParams addObject:dtb]; + [dtb release]; +} + static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; CGColorRef color; - size_t ostart, oend; - backgroundBlock block; int32_t us; CFNumberRef num; double r, g, b, a; uiUnderlineColor colorType; - ostart = start; - oend = end; start = uiprivAttributedStringUTF8ToUTF16(s, start); end = uiprivAttributedStringUTF8ToUTF16(s, end); range.location = start; @@ -340,9 +344,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute break; case uiAttributeTypeBackground: uiAttributeColor(attr, &r, &g, &b, &a); - block = mkBackgroundBlock(ostart, oend, r, g, b, a); - [p->backgroundBlocks addObject:block]; - Block_release(block); + addBackgroundAttribute(p, start, end, r, g, b, a); break; // TODO turn into a class, like we did with the font attributes, or even integrate *into* the font attributes case uiAttributeTypeUnderline: @@ -453,7 +455,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) } // TODO either rename this to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else... -CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) +CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams) { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; @@ -493,11 +495,11 @@ CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayou CFAttributedStringBeginEditing(mas); fep.mas = mas; - fep.backgroundBlocks = [NSMutableArray new]; + fep.backgroundParams = [NSMutableArray new]; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyFontAttributes(mas, p->DefaultFont); CFAttributedStringEndEditing(mas); - *backgroundBlocks = fep.backgroundBlocks; + *backgroundParams = fep.backgroundParams; return mas; } diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 21c6e45e..5406617c 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -8,7 +8,7 @@ // in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within @interface uiprivTextFrame : NSObject { CFAttributedStringRef attrstr; - NSArray *backgroundBlocks; + NSArray *backgroundParams; CTFramesetterRef framesetter; CGSize size; CGPathRef path; @@ -20,6 +20,29 @@ - (CFArrayRef)lines; @end +@implementation uiprivDrawTextBackgroundParams + +- (id)initWithStart:(size_t)s end:(size_t)e r:(double)red g:(double)green b:(double)blue a:(double)alpha +{ + self = [super init]; + if (self) { + self->start = s; + self->end = e; + self->r = red; + self->g = green; + self->b = blue; + self->a = alpha; + } + return self; +} + +- (void)draw:(CGContextRef)c layout:(uiDrawTextLayout *)layout at:(double)x y:(double)y utf8Mapping:(const size_t *)u16tou8 +{ + // TODO +} + +@end + @implementation uiprivTextFrame - (id)initWithLayoutParams:(uiDrawTextLayoutParams *)p @@ -31,7 +54,7 @@ self = [super init]; if (self) { - self->attrstr = uiprivAttributedStringToCFAttributedString(p, &(self->backgroundBlocks)); + self->attrstr = uiprivAttributedStringToCFAttributedString(p, &(self->backgroundParams)); // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing self->framesetter = CTFramesetterCreateWithAttributedString(self->attrstr); if (self->framesetter == NULL) { @@ -71,22 +94,22 @@ CFRelease(self->frame); CFRelease(self->path); CFRelease(self->framesetter); - [self->backgroundBlocks release]; + [self->backgroundParams release]; CFRelease(self->attrstr); [super dealloc]; } - (void)draw:(uiDrawContext *)c textLayout:(uiDrawTextLayout *)tl at:(double)x y:(double)y { - backgroundBlock b; + uiprivDrawTextBackgroundParams *dtb; CGAffineTransform textMatrix; CGContextSaveGState(c->c); // save the text matrix because it's not part of the graphics state textMatrix = CGContextGetTextMatrix(c->c); - for (b in self->backgroundBlocks) - b(c, tl, x, y); + for (dtb in self->backgroundParams) + /* TODO */; // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) diff --git a/darwin/future.m b/darwin/future.m index 60936f40..a262d009 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -8,6 +8,9 @@ CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue = NULL; +// added in OS X 10.12; we need 10.8 +CFStringRef *FUTURE_kCTBackgroundColorAttributeName = NULL; + // note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) void loadFutures(void) { @@ -20,6 +23,7 @@ void loadFutures(void) #define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) GET(FUTURE_kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureTag); GET(FUTURE_kCTFontOpenTypeFeatureValue, kCTFontOpenTypeFeatureValue); + GET(FUTURE_kCTBackgroundColorAttributeName, kCTBackgroundColorAttributeName); dlclose(handle); } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 0711c68b..8b95e315 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -145,6 +145,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // future.m extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; +extern CFStringRef *FUTURE_kCTBackgroundColorAttributeName; extern void loadFutures(void); extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent);