UNPKG

6.76 kBPlain TextView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#import "RCTConvert+ART.h"
9
10#import <React/RCTFont.h>
11#import <React/RCTLog.h>
12
13#import "ARTLinearGradient.h"
14#import "ARTPattern.h"
15#import "ARTRadialGradient.h"
16#import "ARTSolidColor.h"
17
18@implementation RCTConvert (ART)
19
20+ (CGPathRef)CGPath:(id)json
21{
22 NSArray *arr = [self NSNumberArray:json];
23
24 NSUInteger count = [arr count];
25
26#define NEXT_VALUE [self double:arr[i++]]
27
28 CGMutablePathRef path = CGPathCreateMutable();
29 CGPathMoveToPoint(path, NULL, 0, 0);
30
31 @try {
32 NSUInteger i = 0;
33 while (i < count) {
34 NSUInteger type = [arr[i++] unsignedIntegerValue];
35 switch (type) {
36 case 0:
37 CGPathMoveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
38 break;
39 case 1:
40 CGPathCloseSubpath(path);
41 break;
42 case 2:
43 CGPathAddLineToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
44 break;
45 case 3:
46 CGPathAddCurveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE);
47 break;
48 case 4:
49 CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
50 break;
51 default:
52 RCTLogError(@"Invalid CGPath type %llu at element %llu of %@", (unsigned long long)type, (unsigned long long)i, arr);
53 CGPathRelease(path);
54 return NULL;
55 }
56 }
57 }
58 @catch (NSException *exception) {
59 RCTLogError(@"Invalid CGPath format: %@", arr);
60 CGPathRelease(path);
61 return NULL;
62 }
63
64 return (CGPathRef)CFAutorelease(path);
65}
66
67RCT_ENUM_CONVERTER(CTTextAlignment, (@{
68 @"auto": @(kCTTextAlignmentNatural),
69 @"left": @(kCTTextAlignmentLeft),
70 @"center": @(kCTTextAlignmentCenter),
71 @"right": @(kCTTextAlignmentRight),
72 @"justify": @(kCTTextAlignmentJustified),
73}), kCTTextAlignmentNatural, integerValue)
74
75// This takes a tuple of text lines and a font to generate a CTLine for each text line.
76// This prepares everything for rendering a frame of text in ARTText.
77+ (ARTTextFrame)ARTTextFrame:(id)json
78{
79 NSDictionary *dict = [self NSDictionary:json];
80 ARTTextFrame frame;
81 frame.count = 0;
82
83 NSArray *lines = [self NSArray:dict[@"lines"]];
84 NSUInteger lineCount = [lines count];
85 if (lineCount == 0) {
86 return frame;
87 }
88
89 CTFontRef font = (__bridge CTFontRef)[self UIFont:dict[@"font"]];
90 if (!font) {
91 return frame;
92 }
93
94 // Create a dictionary for this font
95 CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
96 (NSString *)kCTFontAttributeName:(__bridge id)font,
97 (NSString *)kCTForegroundColorFromContextAttributeName: @YES
98 };
99
100 // Set up text frame with font metrics
101 CGFloat size = CTFontGetSize(font);
102 frame.count = lineCount;
103 frame.baseLine = size; // estimate base line
104 frame.lineHeight = size * 1.1; // Base on ART canvas line height estimate
105 frame.lines = malloc(sizeof(CTLineRef) * lineCount);
106 frame.widths = malloc(sizeof(CGFloat) * lineCount);
107
108 [lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) {
109
110 CFStringRef string = (__bridge CFStringRef)text;
111 CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
112 CTLineRef line = CTLineCreateWithAttributedString(attrString);
113 CFRelease(attrString);
114
115 frame.lines[i] = line;
116 frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
117 }];
118
119 return frame;
120}
121
122+ (ARTCGFloatArray)ARTCGFloatArray:(id)json
123{
124 NSArray *arr = [self NSNumberArray:json];
125 NSUInteger count = arr.count;
126
127 ARTCGFloatArray array;
128 array.count = count;
129 array.array = NULL;
130
131 if (count) {
132 // Ideally, these arrays should already use the same memory layout.
133 // In that case we shouldn't need this new malloc.
134 array.array = malloc(sizeof(CGFloat) * count);
135 for (NSUInteger i = 0; i < count; i++) {
136 array.array[i] = [arr[i] doubleValue];
137 }
138 }
139
140 return array;
141}
142
143+ (ARTBrush *)ARTBrush:(id)json
144{
145 NSArray *arr = [self NSArray:json];
146 NSUInteger type = [self NSUInteger:arr.firstObject];
147 switch (type) {
148 case 0: // solid color
149 // These are probably expensive allocations since it's often the same value.
150 // We should memoize colors but look ups may be just as expensive.
151 return [[ARTSolidColor alloc] initWithArray:arr];
152 case 1: // linear gradient
153 return [[ARTLinearGradient alloc] initWithArray:arr];
154 case 2: // radial gradient
155 return [[ARTRadialGradient alloc] initWithArray:arr];
156 case 3: // pattern
157 return [[ARTPattern alloc] initWithArray:arr];
158 default:
159 RCTLogError(@"Unknown brush type: %llu", (unsigned long long)type);
160 return nil;
161 }
162}
163
164+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset
165{
166 NSArray *arr = [self NSArray:json];
167 if (arr.count < offset + 2) {
168 RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(2 + offset), arr);
169 return CGPointZero;
170 }
171 return (CGPoint){
172 [self CGFloat:arr[offset]],
173 [self CGFloat:arr[offset + 1]],
174 };
175}
176
177+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset
178{
179 NSArray *arr = [self NSArray:json];
180 if (arr.count < offset + 4) {
181 RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(4 + offset), arr);
182 return CGRectZero;
183 }
184 return (CGRect){
185 {[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]},
186 {[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]},
187 };
188}
189
190+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset
191{
192 NSArray *arr = [self NSArray:json];
193 if (arr.count < offset + 4) {
194 RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(4 + offset), arr);
195 return NULL;
196 }
197 return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]];
198}
199
200+ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset
201{
202 NSArray *arr = [self NSArray:json];
203 if (arr.count < offset) {
204 RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)offset, arr);
205 return NULL;
206 }
207 arr = [arr subarrayWithRange:(NSRange){offset, arr.count - offset}];
208 ARTCGFloatArray colorsAndOffsets = [self ARTCGFloatArray:arr];
209 size_t stops = colorsAndOffsets.count / 5;
210 CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
211 CGGradientRef gradient = CGGradientCreateWithColorComponents(
212 rgb,
213 colorsAndOffsets.array,
214 colorsAndOffsets.array + stops * 4,
215 stops
216 );
217 CGColorSpaceRelease(rgb);
218 free(colorsAndOffsets.array);
219 return (CGGradientRef)CFAutorelease(gradient);
220}
221
222@end