forked from greatyingzi/MTerminal-Jailed
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMTRowView.m
153 lines (150 loc) · 5.02 KB
/
MTRowView.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "MTRowView.h"
#include <stdatomic.h>
typedef struct hspan_t {
volatile _Atomic(int32_t) retain_count;
CGFloat x,y,width;
} hspan_t;
static hspan_t* hspan_retain(CFAllocatorRef allocator,hspan_t* span) {
atomic_fetch_add(&span->retain_count, 1);
return span;
}
static void hspan_release(CFAllocatorRef allocator,hspan_t* span) {
if(atomic_fetch_add(&span->retain_count, 1)==0){free(span);}
}
@implementation MTRowView
-(id)init {
if((self=[super init])){
bgMap=CFDictionaryCreateMutable(NULL,0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
stMap=CFDictionaryCreateMutable(NULL,0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
self.clearsContextBeforeDrawing=NO;
self.opaque=YES;
}
return self;
}
-(void)renderString:(CFAttributedStringRef)string withBGColor:(CGColorRef)_bgColor {
if(bgColor!=_bgColor){
CGColorRelease(bgColor);
bgColor=CGColorRetain(_bgColor);
}
if(ctLine){
CFRelease(ctLine);
CFDictionaryRemoveAllValues(bgMap);
CFDictionaryRemoveAllValues(stMap);
}
ctLine=CTLineCreateWithAttributedString(string);
CFArrayRef runs=CTLineGetGlyphRuns(ctLine);
CFIndex nruns=CFArrayGetCount(runs),i;
CGFloat x=0,y=0;
CFMutableDictionaryRef colormap[]={bgMap,stMap};
const int nmaps=sizeof(colormap)/sizeof(CFDictionaryRef);
for (i=0;i<nruns;i++){
CTRunRef run=CFArrayGetValueAtIndex(runs,i);
CGFloat ascent,width=CTRunGetTypographicBounds(run,
CFRangeMake(0,0),&ascent,NULL,NULL);
CFDictionaryRef attr=CTRunGetAttributes(run);
const void* colorkey[]={
CFDictionaryGetValue(attr,kMTBackgroundColorAttributeName),
CFDictionaryGetValue(attr,kMTStrikethroughColorAttributeName)};
hspan_t* span=NULL;
int j;
for (j=0;j<nmaps;j++){
const void* key=colorkey[j];
if(!key){continue;}
CFMutableArrayRef spans=(void*)CFDictionaryGetValue(colormap[j],key);
if(!spans){
spans=CFArrayCreateMutable(NULL,0,&(CFArrayCallBacks){
.retain=(CFArrayRetainCallBack)hspan_retain,
.release=(CFArrayReleaseCallBack)hspan_release});
CFDictionaryAddValue(colormap[j],key,spans);
CFRelease(spans);
}
if(!span){
span=malloc(sizeof(hspan_t));
span->retain_count=0;// CFArray will retain it
span->x=x;
span->width=width;
}
// calculate strikethrough position if needed
if(j==1) span->y=ascent-CTFontGetXHeight(
CFDictionaryGetValue(attr,kCTFontAttributeName))/2;
CFArrayAppendValue(spans,span);
}
x+=width;
y+=ascent;
}
lineAscent=y/nruns;
[self setNeedsDisplay];
}
-(void)drawRect:(CGRect)drawRect {
CGContextRef context=UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context,bgColor);
CGContextFillRect(context,drawRect);
CGFloat xmin=CGRectGetMinX(drawRect),xmax=CGRectGetMaxX(drawRect);
CFIndex nbg=CFDictionaryGetCount(bgMap);
if(nbg){// draw background rectangles as needed
const void** keys=malloc(nbg*sizeof(CGColorRef));
const void** values=malloc(nbg*sizeof(CFArrayRef));
CFDictionaryGetKeysAndValues(bgMap,keys,values);
CFIndex i;
for (i=0;i<nbg;i++){
CFIndex nvalues=CFArrayGetCount(values[i]),nrects=0,j;
CGRect* rects=malloc(nvalues*sizeof(CGRect));
for (j=0;j<nvalues;j++){
hspan_t* span=(hspan_t*)CFArrayGetValueAtIndex(values[i],j);
CGFloat x=span->x,width=span->width,xend=x+width;
if((x<xmin && xend<xmin) || (x>xmax && xend>xmax)){continue;}
rects[nrects++]=CGRectMake(x,drawRect.origin.y,
width,drawRect.size.height);
}
if(nrects){
CGContextSetFillColorWithColor(context,(CGColorRef)keys[i]);
CGContextFillRects(context,rects,nrects);
}
free(rects);
}
free(keys);
free(values);
}
CGContextSetTextMatrix(context,
CGAffineTransformMake(1,0,0,-1,0,lineAscent));
CTLineDraw(ctLine,context);
CFIndex nst=CFDictionaryGetCount(stMap);
if(nst){// draw strikethrough lines as needed
const void** keys=malloc(nst*sizeof(CGColorRef));
const void** values=malloc(nst*sizeof(CFArrayRef));
CFDictionaryGetKeysAndValues(stMap,keys,values);
CGFloat ymin=CGRectGetMinY(drawRect),ymax=CGRectGetMaxY(drawRect);
CFIndex i;
for (i=0;i<nst;i++){
CFIndex nvalues=CFArrayGetCount(values[i]),j;
BOOL first=YES;
for (j=0;j<nvalues;j++){
hspan_t* span=(hspan_t*)CFArrayGetValueAtIndex(values[i],j);
CGFloat x=span->x,xend=x+span->width,y=span->y;
if((x<xmin && xend<xmin) || (x>xmax && xend>xmax)
|| y<ymin || y>ymax){continue;}
if(first){
CGContextSetStrokeColorWithColor(context,(CGColorRef)keys[i]);
first=NO;
}
CGContextMoveToPoint(context,x,y);
CGContextAddLineToPoint(context,xend,y);
CGContextStrokePath(context);
}
}
free(keys);
free(values);
}
}
-(void)dealloc {
CGColorRelease(bgColor);
if(ctLine){CFRelease(ctLine);}
CFRelease(bgMap);
CFRelease(stMap);
[super dealloc];
}
@end