UNPKG

13.9 kBPlain TextView Raw
1/**
2 * Copyright (c) 2015-present, Horcrux.
3 * All rights reserved.
4 *
5 * This source code is licensed under the MIT-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9#import "RNSVGNode.h"
10#import "RNSVGContainer.h"
11#import "RNSVGClipPath.h"
12#import "RNSVGGroup.h"
13#import "RNSVGGlyphContext.h"
14
15@interface RNSVGNode()
16@property (nonatomic, readwrite, weak) RNSVGSvgView *svgView;
17@property (nonatomic, readwrite, weak) RNSVGGroup *textRoot;
18@end
19
20@implementation RNSVGNode
21{
22 RNSVGGlyphContext *glyphContext;
23 BOOL _transparent;
24 RNSVGClipPath *_clipNode;
25 CGPathRef _cachedClipPath;
26 CGImageRef _clipMask;
27 CGFloat canvasWidth;
28 CGFloat canvasHeight;
29 CGFloat canvasDiagonal;
30}
31
32CGFloat const RNSVG_M_SQRT1_2l = (CGFloat)0.707106781186547524400844362104849039;
33CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12;
34
35- (instancetype)init
36{
37 if (self = [super init]) {
38 self.opacity = 1;
39 self.matrix = CGAffineTransformIdentity;
40 self.transforms = CGAffineTransformIdentity;
41 self.invTransform = CGAffineTransformIdentity;
42 _merging = false;
43 _dirty = false;
44 }
45 return self;
46}
47
48- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
49{
50 [super insertReactSubview:subview atIndex:atIndex];
51 [self insertSubview:subview atIndex:atIndex];
52 [self invalidate];
53}
54
55- (void)removeReactSubview:(UIView *)subview
56{
57 [super removeReactSubview:subview];
58 [self invalidate];
59}
60
61- (void)didUpdateReactSubviews
62{
63 // Do nothing, as subviews are inserted by insertReactSubview:
64}
65
66- (void)invalidate
67{
68 if (_dirty || _merging) {
69 return;
70 }
71 _dirty = true;
72 id<RNSVGContainer> container = (id<RNSVGContainer>)self.superview;
73 [container invalidate];
74 [self clearPath];
75 canvasWidth = -1;
76 canvasHeight = -1;
77 canvasDiagonal = -1;
78}
79
80- (void)clearPath
81{
82 CGPathRelease(_path);
83 self.path = nil;
84}
85
86- (void)clearChildCache
87{
88 [self clearPath];
89 for (__kindof RNSVGNode *node in self.subviews) {
90 if ([node isKindOfClass:[RNSVGNode class]]) {
91 [node clearChildCache];
92 }
93 }
94}
95
96- (void)clearParentCache
97{
98 RNSVGNode* node = self;
99 while (node != nil) {
100 UIView* parent = [node superview];
101
102 if (![parent isKindOfClass:[RNSVGNode class]]) {
103 return;
104 }
105 node = (RNSVGNode*)parent;
106 if (!node.path) {
107 return;
108 }
109 [node clearPath];
110 }
111}
112
113- (RNSVGGroup *)textRoot
114{
115 if (_textRoot) {
116 return _textRoot;
117 }
118
119 RNSVGNode* node = self;
120 while (node != nil) {
121 if ([node isKindOfClass:[RNSVGGroup class]] && [((RNSVGGroup*) node) getGlyphContext] != nil) {
122 _textRoot = (RNSVGGroup*)node;
123 break;
124 }
125
126 UIView* parent = [node superview];
127
128 if (![node isKindOfClass:[RNSVGNode class]]) {
129 node = nil;
130 } else {
131 node = (RNSVGNode*)parent;
132 }
133 }
134
135 return _textRoot;
136}
137
138- (RNSVGGroup *)getParentTextRoot
139{
140 RNSVGNode* parent = (RNSVGGroup*)[self superview];
141 if (![parent isKindOfClass:[RNSVGGroup class]]) {
142 return nil;
143 } else {
144 return parent.textRoot;
145 }
146}
147
148- (CGFloat)getFontSizeFromContext
149{
150 RNSVGGroup* root = self.textRoot;
151 if (root == nil) {
152 return RNSVG_DEFAULT_FONT_SIZE;
153 }
154
155 if (glyphContext == nil) {
156 glyphContext = [root getGlyphContext];
157 }
158
159 return [glyphContext getFontSize];
160}
161
162- (void)reactSetInheritedBackgroundColor:(UIColor *)inheritedBackgroundColor
163{
164 self.backgroundColor = inheritedBackgroundColor;
165}
166
167- (void)setName:(NSString *)name
168{
169 if ([name isEqualToString:_name]) {
170 return;
171 }
172
173 [self invalidate];
174 _name = name;
175}
176
177- (void)setOpacity:(CGFloat)opacity
178{
179 if (opacity == _opacity) {
180 return;
181 }
182
183 if (opacity <= 0) {
184 opacity = 0;
185 } else if (opacity > 1) {
186 opacity = 1;
187 }
188
189 [self invalidate];
190 _transparent = opacity < 1;
191 _opacity = opacity;
192}
193
194- (void)setMatrix:(CGAffineTransform)matrix
195{
196 if (CGAffineTransformEqualToTransform(matrix, _matrix)) {
197 return;
198 }
199 _matrix = matrix;
200 _invmatrix = CGAffineTransformInvert(matrix);
201 id<RNSVGContainer> container = (id<RNSVGContainer>)self.superview;
202 [container invalidate];
203}
204
205- (void)setClientRect:(CGRect)clientRect {
206 if (CGRectEqualToRect(_clientRect, clientRect)) {
207 return;
208 }
209 _clientRect = clientRect;
210 if (self.onLayout) {
211 self.onLayout(@{
212 @"layout": @{
213 @"x": @(_clientRect.origin.x),
214 @"y": @(_clientRect.origin.y),
215 @"width": @(_clientRect.size.width),
216 @"height": @(_clientRect.size.height),
217 }
218 });
219
220 }
221}
222
223- (void)setClipPath:(NSString *)clipPath
224{
225 if ([_clipPath isEqualToString:clipPath]) {
226 return;
227 }
228 CGPathRelease(_cachedClipPath);
229 CGImageRelease(_clipMask);
230 _cachedClipPath = nil;
231 _clipPath = clipPath;
232 _clipMask = nil;
233 [self invalidate];
234}
235
236- (void)setClipRule:(RNSVGCGFCRule)clipRule
237{
238 if (_clipRule == clipRule) {
239 return;
240 }
241 CGPathRelease(_cachedClipPath);
242 CGImageRelease(_clipMask);
243 _cachedClipPath = nil;
244 _clipRule = clipRule;
245 _clipMask = nil;
246 [self invalidate];
247}
248
249- (void)setMask:(NSString *)mask
250{
251 if ([_mask isEqualToString:mask]) {
252 return;
253 }
254 _mask = mask;
255 [self invalidate];
256}
257
258- (void)setMarkerStart:(NSString *)markerStart
259{
260 if ([_markerStart isEqualToString:markerStart]) {
261 return;
262 }
263 _markerStart = markerStart;
264 [self invalidate];
265}
266
267- (void)setMarkerMid:(NSString *)markerMid
268{
269 if ([_markerMid isEqualToString:markerMid]) {
270 return;
271 }
272 _markerMid = markerMid;
273 [self invalidate];
274}
275
276- (void)setMarkerEnd:(NSString *)markerEnd
277{
278 if ([_markerEnd isEqualToString:markerEnd]) {
279 return;
280 }
281 _markerEnd = markerEnd;
282 [self invalidate];
283}
284
285- (void)beginTransparencyLayer:(CGContextRef)context
286{
287 if (_transparent) {
288 CGContextBeginTransparencyLayer(context, NULL);
289 }
290}
291
292- (void)endTransparencyLayer:(CGContextRef)context
293{
294 if (_transparent) {
295 CGContextEndTransparencyLayer(context);
296 }
297}
298
299- (void)renderTo:(CGContextRef)context rect:(CGRect)rect
300{
301 self.dirty = false;
302 // abstract
303}
304
305- (CGPathRef)getClipPath
306{
307 return _cachedClipPath;
308}
309
310- (CGPathRef)getClipPath:(CGContextRef)context
311{
312 if (self.clipPath) {
313 _clipNode = (RNSVGClipPath*)[self.svgView getDefinedClipPath:self.clipPath];
314 if (_cachedClipPath) {
315 CGPathRelease(_cachedClipPath);
316 }
317 _cachedClipPath = CGPathRetain([_clipNode getPath:context]);
318 if (_clipMask) {
319 CGImageRelease(_clipMask);
320 }
321 if ([_clipNode isSimpleClipPath] || _clipNode.clipRule == kRNSVGCGFCRuleEvenodd) {
322 _clipMask = nil;
323 } else {
324 CGRect bounds = CGContextGetClipBoundingBox(context);
325 CGSize size = bounds.size;
326
327 UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
328 CGContextRef newContext = UIGraphicsGetCurrentContext();
329 CGContextTranslateCTM(newContext, 0.0, size.height);
330 CGContextScaleCTM(newContext, 1.0, -1.0);
331
332 [_clipNode renderLayerTo:newContext rect:bounds];
333 _clipMask = CGBitmapContextCreateImage(newContext);
334 UIGraphicsEndImageContext();
335 }
336 }
337
338 return _cachedClipPath;
339}
340
341- (void)clip:(CGContextRef)context
342{
343 CGPathRef clipPath = [self getClipPath:context];
344
345 if (clipPath) {
346 if (!_clipMask) {
347 CGContextAddPath(context, clipPath);
348 if (_clipNode.clipRule == kRNSVGCGFCRuleEvenodd) {
349 CGContextEOClip(context);
350 } else {
351 CGContextClip(context);
352 }
353 } else {
354 CGRect bounds = CGContextGetClipBoundingBox(context);
355 CGContextClipToMask(context, bounds, _clipMask);
356 }
357 }
358}
359
360- (CGPathRef)getPath: (CGContextRef)context
361{
362 // abstract
363 return nil;
364}
365
366- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
367{
368 // abstract
369}
370
371// hitTest delagate
372- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
373{
374
375 // abstract
376 return nil;
377}
378
379- (RNSVGSvgView *)svgView
380{
381 if (_svgView) {
382 return _svgView;
383 }
384
385 __kindof UIView *parent = self.superview;
386
387 if ([parent class] == [RNSVGSvgView class]) {
388 _svgView = parent;
389 } else if ([parent isKindOfClass:[RNSVGNode class]]) {
390 _svgView = ((RNSVGNode *)parent).svgView;
391 } else {
392 RCTLogError(@"RNSVG: %@ should be descendant of a SvgViewShadow.", NSStringFromClass(self.class));
393 }
394
395 return _svgView;
396}
397
398- (CGFloat)relativeOnWidthString:(NSString *)length
399{
400 return [RNSVGPropHelper fromRelativeWithNSString:length
401 relative:[self getCanvasWidth]
402 fontSize:[self getFontSizeFromContext]];
403}
404
405- (CGFloat)relativeOnHeightString:(NSString *)length
406{
407 return [RNSVGPropHelper fromRelativeWithNSString:length
408 relative:[self getCanvasHeight]
409 fontSize:[self getFontSizeFromContext]];
410}
411
412- (CGFloat)relativeOnOtherString:(NSString *)length
413{
414 return [RNSVGPropHelper fromRelativeWithNSString:length
415 relative:[self getCanvasDiagonal]
416 fontSize:[self getFontSizeFromContext]];
417}
418
419- (CGFloat)relativeOn:(RNSVGLength *)length relative:(CGFloat)relative
420{
421 RNSVGLengthUnitType unit = length.unit;
422 if (unit == SVG_LENGTHTYPE_NUMBER){
423 return length.value;
424 } else if (unit == SVG_LENGTHTYPE_PERCENTAGE){
425 return length.value / 100 * relative;
426 }
427 return [self fromRelative:length];
428}
429
430- (CGFloat)relativeOnWidth:(RNSVGLength *)length
431{
432 RNSVGLengthUnitType unit = length.unit;
433 if (unit == SVG_LENGTHTYPE_NUMBER){
434 return length.value;
435 } else if (unit == SVG_LENGTHTYPE_PERCENTAGE){
436 return length.value / 100 * [self getCanvasWidth];
437 }
438 return [self fromRelative:length];
439}
440
441- (CGFloat)relativeOnHeight:(RNSVGLength *)length
442{
443 RNSVGLengthUnitType unit = length.unit;
444 if (unit == SVG_LENGTHTYPE_NUMBER){
445 return length.value;
446 } else if (unit == SVG_LENGTHTYPE_PERCENTAGE){
447 return length.value / 100 * [self getCanvasHeight];
448 }
449 return [self fromRelative:length];
450}
451
452- (CGFloat)relativeOnOther:(RNSVGLength *)length
453{
454 RNSVGLengthUnitType unit = length.unit;
455 if (unit == SVG_LENGTHTYPE_NUMBER){
456 return length.value;
457 } else if (unit == SVG_LENGTHTYPE_PERCENTAGE){
458 return length.value / 100 * [self getCanvasDiagonal];
459 }
460 return [self fromRelative:length];
461}
462
463- (CGFloat)fromRelative:(RNSVGLength*)length {
464 CGFloat unit;
465 switch (length.unit) {
466 case SVG_LENGTHTYPE_EMS:
467 unit = [self getFontSizeFromContext];
468 break;
469 case SVG_LENGTHTYPE_EXS:
470 unit = [self getFontSizeFromContext] / 2;
471 break;
472
473 case SVG_LENGTHTYPE_CM:
474 unit = (CGFloat)35.43307;
475 break;
476 case SVG_LENGTHTYPE_MM:
477 unit = (CGFloat)3.543307;
478 break;
479 case SVG_LENGTHTYPE_IN:
480 unit = 90;
481 break;
482 case SVG_LENGTHTYPE_PT:
483 unit = 1.25;
484 break;
485 case SVG_LENGTHTYPE_PC:
486 unit = 15;
487 break;
488
489 default:
490 unit = 1;
491 }
492 return length.value * unit;
493}
494
495- (CGRect)getContextBounds
496{
497 return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext());
498}
499
500- (CGFloat)getContextWidth
501{
502 return CGRectGetWidth([self getContextBounds]);
503}
504
505- (CGFloat)getContextHeight
506{
507 return CGRectGetHeight([self getContextBounds]);
508}
509
510- (CGFloat)getContextDiagonal {
511 CGRect bounds = [self getContextBounds];
512 CGFloat width = CGRectGetWidth(bounds);
513 CGFloat height = CGRectGetHeight(bounds);
514 CGFloat powX = width * width;
515 CGFloat powY = height * height;
516 CGFloat r = sqrt(powX + powY) * RNSVG_M_SQRT1_2l;
517 return r;
518}
519
520- (CGFloat) getCanvasWidth {
521 if (canvasWidth != -1) {
522 return canvasWidth;
523 }
524 RNSVGGroup* root = [self textRoot];
525 if (root == nil) {
526 canvasWidth = [self getContextWidth];
527 } else {
528 canvasWidth = [[root getGlyphContext] getWidth];
529 }
530
531 return canvasWidth;
532}
533
534- (CGFloat) getCanvasHeight {
535 if (canvasHeight != -1) {
536 return canvasHeight;
537 }
538 RNSVGGroup* root = [self textRoot];
539 if (root == nil) {
540 canvasHeight = [self getContextHeight];
541 } else {
542 canvasHeight = [[root getGlyphContext] getHeight];
543 }
544
545 return canvasHeight;
546}
547
548- (CGFloat) getCanvasDiagonal {
549 if (canvasDiagonal != -1) {
550 return canvasDiagonal;
551 }
552 CGFloat width = [self getCanvasWidth];
553 CGFloat height = [self getCanvasHeight];
554 CGFloat powX = width * width;
555 CGFloat powY = height * height;
556 canvasDiagonal = sqrt(powX + powY) * RNSVG_M_SQRT1_2l;
557 return canvasDiagonal;
558}
559
560- (void)parseReference
561{
562 self.dirty = false;
563 if (self.name) {
564 typeof(self) __weak weakSelf = self;
565 [self.svgView defineTemplate:weakSelf templateName:self.name];
566 }
567}
568
569- (void)traverseSubviews:(BOOL (^)(__kindof UIView *node))block
570{
571 for (UIView *node in self.subviews) {
572 if (!block(node)) {
573 break;
574 }
575 }
576}
577
578- (void)dealloc
579{
580 CGPathRelease(_cachedClipPath);
581 CGPathRelease(_strokePath);
582 CGImageRelease(_clipMask);
583 CGPathRelease(_path);
584 _clipMask = nil;
585}
586
587@end