From cd77525a7b2cba050423583a69bc92d55cd98b5c Mon Sep 17 00:00:00 2001 From: Horcrux Date: Wed, 27 Apr 2016 10:48:49 +0800 Subject: [PATCH] complete percentage coords refactor on ios --- elements/RadialGradient.js | 2 +- ios/Brushes/RNSVGBrush.h | 5 +++ ios/Brushes/RNSVGBrush.m | 24 +++++++++++ ios/Brushes/RNSVGLinearGradient.m | 45 +++++++++++-------- ios/Brushes/RNSVGRadialGradient.m | 72 ++++++++++++++++++++----------- ios/RCTConvert+RNSVG.m | 2 + ios/RNSVGShape.m | 2 +- lib/insertProcessor.js | 14 +++--- 8 files changed, 115 insertions(+), 51 deletions(-) diff --git a/elements/RadialGradient.js b/elements/RadialGradient.js index 14b670a6..825034c6 100644 --- a/elements/RadialGradient.js +++ b/elements/RadialGradient.js @@ -12,7 +12,7 @@ import insertColorStopsIntoArray from '../lib/insertProcessor'; function RadialGradientGenerator(stops, fx, fy, rx, ry, cx, cy) { var brushData = [RADIAL_GRADIENT, fx, fy, rx, ry, cx, cy]; - insertColorStopsIntoArray(stops, brushData, 7, 0.5); + insertColorStopsIntoArray(stops, brushData, 7); this._brush = brushData; } diff --git a/ios/Brushes/RNSVGBrush.h b/ios/Brushes/RNSVGBrush.h index 091a3008..aab07067 100644 --- a/ios/Brushes/RNSVGBrush.h +++ b/ios/Brushes/RNSVGBrush.h @@ -10,6 +10,9 @@ #import @interface RNSVGBrush : NSObject +{ + NSArray *_points; +} /* @abstract */ - (instancetype)initWithArray:(NSArray *)data NS_DESIGNATED_INITIALIZER; @@ -26,6 +29,8 @@ - (BOOL)applyStrokeColor:(CGContextRef)context; +- (CGFloat)getActualProp:(int)percent relative:(CGFloat)relative offset:(CGFloat)offset; + /** * paint fills the context with a brush. The context is assumed to * be clipped. diff --git a/ios/Brushes/RNSVGBrush.m b/ios/Brushes/RNSVGBrush.m index 5c16aa35..34543c13 100644 --- a/ios/Brushes/RNSVGBrush.m +++ b/ios/Brushes/RNSVGBrush.m @@ -29,6 +29,30 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) return NO; } +- (CGFloat)getActualProp:(int)index relative:(CGFloat)relative offset:(CGFloat)offset +{ + id coord = [_points objectAtIndex:index]; + + if ([coord isKindOfClass:[NSString class]]) { + __block float matched = [coord floatValue]; + NSString *percentage = (NSString *)coord; + NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@"^(\\-?\\d+(?:\\.\\d+)?)%$" options:0 error:NULL]; + [regex enumerateMatchesInString:percentage + options:0 + range:NSMakeRange(0, percentage.length) + usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) + { + + matched = [[percentage substringWithRange:NSMakeRange(result.range.location, result.range.length)] floatValue]; + matched = matched / 100 * relative + offset; + }]; + + return matched; + } else { + return [coord floatValue]; + } +} + - (void)paint:(CGContextRef)context { // abstract diff --git a/ios/Brushes/RNSVGLinearGradient.m b/ios/Brushes/RNSVGLinearGradient.m index 343ea180..fff50396 100644 --- a/ios/Brushes/RNSVGLinearGradient.m +++ b/ios/Brushes/RNSVGLinearGradient.m @@ -13,36 +13,47 @@ @implementation RNSVGLinearGradient { - CGGradientRef _gradient; - CGPoint _startPoint; - CGPoint _endPoint; + CGGradientRef _gradient; } -- (instancetype)initWithArray:(NSArray *)array +- (instancetype)initWithArray:(NSArray *)array { - if ((self = [super initWithArray:array])) { - if (array.count < 5) { - RCTLogError(@"-[%@ %@] expects 5 elements, received %@", - self.class, NSStringFromSelector(_cmd), array); - return nil; + if ((self = [super initWithArray:array])) { + if (array.count < 5) { + RCTLogError(@"-[%@ %@] expects 5 elements, received %@", + self.class, NSStringFromSelector(_cmd), array); + return nil; + } + + _points = [array subarrayWithRange:NSMakeRange(1, 4)]; + _gradient = CGGradientRetain([RCTConvert CGGradient:array offset:5]); } - _startPoint = [RCTConvert CGPoint:array offset:1]; - _endPoint = [RCTConvert CGPoint:array offset:3]; - _gradient = CGGradientRetain([RCTConvert CGGradient:array offset:5]); - } - return self; + return self; } - (void)dealloc { - CGGradientRelease(_gradient); + CGGradientRelease(_gradient); } - (void)paint:(CGContextRef)context { - CGGradientDrawingOptions extendOptions = + CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; - CGContextDrawLinearGradient(context, _gradient, _startPoint, _endPoint, extendOptions); + + CGRect box = CGContextGetClipBoundingBox(context); + float height = CGRectGetHeight(box); + float width = CGRectGetWidth(box); + float midX = CGRectGetMidX(box); + float midY = CGRectGetMidY(box); + float offsetX = (midX - width / 2); + float offsetY = (midY - height / 2); + + CGFloat x1 = [self getActualProp:0 relative:width offset:offsetX]; + CGFloat y1 = [self getActualProp:1 relative:height offset:offsetY]; + CGFloat x2 = [self getActualProp:2 relative:width offset:offsetX]; + CGFloat y2 = [self getActualProp:3 relative:height offset:offsetY]; + CGContextDrawLinearGradient(context, _gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions); } @end diff --git a/ios/Brushes/RNSVGRadialGradient.m b/ios/Brushes/RNSVGRadialGradient.m index f15fe904..6ae6f505 100644 --- a/ios/Brushes/RNSVGRadialGradient.m +++ b/ios/Brushes/RNSVGRadialGradient.m @@ -13,43 +13,65 @@ @implementation RNSVGRadialGradient { - CGGradientRef _gradient; - CGPoint _focusPoint; - CGPoint _centerPoint; - CGFloat _radius; - CGFloat _radiusRatio; + CGGradientRef _gradient; } -- (instancetype)initWithArray:(NSArray *)array +- (instancetype)initWithArray:(NSArray *)array { - if ((self = [super initWithArray:array])) { - if (array.count < 7) { - RCTLogError(@"-[%@ %@] expects 7 elements, received %@", - self.class, NSStringFromSelector(_cmd), array); - return nil; + if ((self = [super initWithArray:array])) { + if (array.count < 7) { + RCTLogError(@"-[%@ %@] expects 7 elements, received %@", + self.class, NSStringFromSelector(_cmd), array); + return nil; + } + _points = [array subarrayWithRange:NSMakeRange(1, 6)]; + + NSMutableArray *gradient = [[NSMutableArray alloc] init]; + int count = ([array count] - 7) / 5; + int startStops = [array count] - count; + for (int i = 0; i < count; i++) { + [gradient insertObject:[array objectAtIndex:(i * 4 + 7)] atIndex:(i * 4)]; + [gradient insertObject:[array objectAtIndex:(i * 4 + 8)] atIndex:(i * 4 + 1)]; + [gradient insertObject:[array objectAtIndex:(i * 4 + 9)] atIndex:(i * 4 + 2)]; + [gradient insertObject:[array objectAtIndex:(i * 4 + 10)] atIndex:(i * 4 + 3)]; + NSNumber *stop = [NSNumber numberWithFloat:[[array objectAtIndex:(startStops + (count - i - 1))] floatValue]]; + + [gradient insertObject:stop atIndex:i * 4 + 4]; + + } + + _gradient = CGGradientRetain([RCTConvert CGGradient:gradient offset:0]); } - _radius = [RCTConvert CGFloat:array[3]]; - _radiusRatio = [RCTConvert CGFloat:array[4]] / _radius; - _focusPoint.x = [RCTConvert CGFloat:array[1]]; - _focusPoint.y = [RCTConvert CGFloat:array[2]] / _radiusRatio; - _centerPoint.x = [RCTConvert CGFloat:array[5]]; - _centerPoint.y = [RCTConvert CGFloat:array[6]] / _radiusRatio; - _gradient = CGGradientRetain([RCTConvert CGGradient:array offset:7]); - } - return self; + return self; } - (void)dealloc { - CGGradientRelease(_gradient); + CGGradientRelease(_gradient); } - (void)paint:(CGContextRef)context { - CGAffineTransform transform = CGAffineTransformMakeScale(1, _radiusRatio); - CGContextConcatCTM(context, transform); - CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; - CGContextDrawRadialGradient(context, _gradient, _focusPoint, 0, _centerPoint, _radius, extendOptions); + + CGRect box = CGContextGetClipBoundingBox(context); + float height = CGRectGetHeight(box); + float width = CGRectGetWidth(box); + float midX = CGRectGetMidX(box); + float midY = CGRectGetMidY(box); + float offsetX = (midX - width / 2); + float offsetY = (midY - height / 2); + CGFloat rx = [self getActualProp:2 relative:width offset:0]; + CGFloat ry = [self getActualProp:3 relative:height offset:0]; + CGFloat fx = [self getActualProp:0 relative:width offset:offsetX]; + CGFloat fy = [self getActualProp:1 relative:height offset:offsetY] / (ry / rx); // fx == fy + CGFloat cx = [self getActualProp:4 relative:width offset:offsetX]; + CGFloat cy = [self getActualProp:5 relative:height offset:offsetY] / (ry / rx); // rx == ry + + CGAffineTransform transform = CGAffineTransformMakeScale(1, ry / rx); + CGContextConcatCTM(context, transform); + CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; + + CGContextDrawRadialGradient(context, _gradient, CGPointMake(fx, fy), 0, CGPointMake(cx, cy), rx, extendOptions); } @end diff --git a/ios/RCTConvert+RNSVG.m b/ios/RCTConvert+RNSVG.m index 1348630d..c8fb96c0 100644 --- a/ios/RCTConvert+RNSVG.m +++ b/ios/RCTConvert+RNSVG.m @@ -161,6 +161,8 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{ case 2: // radial gradient return [[RNSVGRadialGradient alloc] initWithArray:arr]; case 3: // pattern + // TODO: + return nil; return [[RNSVGPattern alloc] initWithArray:arr]; default: RCTLogError(@"Unknown brush type: %zd", type); diff --git a/ios/RNSVGShape.m b/ios/RNSVGShape.m index 0a9c2693..acd595a8 100644 --- a/ios/RNSVGShape.m +++ b/ios/RNSVGShape.m @@ -31,7 +31,7 @@ CGFloat cx = [self getActualProp:@"cx" relative:width]; CGFloat cy = [self getActualProp:@"cy" relative:height]; CGFloat r = [self getActualProp:@"r" relative:height]; - CGPathAddArc(path, nil, cx, cy, r, 0, 2*M_PI, YES); + CGPathAddArc(path, nil, cx, cy, r, -M_PI, M_PI, YES); break; } case 1: diff --git a/lib/insertProcessor.js b/lib/insertProcessor.js index eabced5e..132698f5 100644 --- a/lib/insertProcessor.js +++ b/lib/insertProcessor.js @@ -11,7 +11,6 @@ function insertColorIntoArray(color, targetArray, atIndex) { function insertColorsIntoArray(stops, targetArray, atIndex) { let i = 0; - if ('length' in stops) { while (i < stops.length) { insertColorIntoArray(stops[i], targetArray, atIndex + i * 4); @@ -28,27 +27,28 @@ function insertColorsIntoArray(stops, targetArray, atIndex) { return atIndex + i * 4; } -function insertColorStopsIntoArray(stops, targetArray, atIndex, multi = 1) { - let lastIndex = insertColorsIntoArray(stops, targetArray, atIndex, false); - insertOffsetsIntoArray(stops, targetArray, lastIndex, multi, false); +function insertColorStopsIntoArray(stops, targetArray, atIndex) { + let lastIndex = insertColorsIntoArray(stops, targetArray, atIndex); + insertOffsetsIntoArray(stops, targetArray, lastIndex); } -function insertOffsetsIntoArray(stops, targetArray, atIndex, multi) { +function insertOffsetsIntoArray(stops, targetArray, atIndex) { let offsetNumber; let i = 0; let arr = []; if ('length' in stops) { while (i < stops.length) { - offsetNumber = i / (stops.length - 1) * multi; + offsetNumber = i / (stops.length - 1); targetArray[atIndex + i] = offsetNumber; i++; } } else { _.forEach(stops, (stop, offsetString) => { - offsetNumber = (+offsetString) * multi; + offsetNumber = (+offsetString); arr.push(offsetNumber); i++; }); + arr.sort(); targetArray.splice(atIndex, 0, ...arr); }