SVGLinearGradientElement.m 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. //
  2. // SVGLinearGradientElement.m
  3. // SVGKit-iOS
  4. //
  5. // Created by lizhuoli on 2018/10/15.
  6. // Copyright © 2018年 na. All rights reserved.
  7. //
  8. #import "SVGLinearGradientElement.h"
  9. #import "SVGElement_ForParser.h"
  10. #import "SVGGradientLayer.h"
  11. @interface SVGLinearGradientElement ()
  12. @property (nonatomic) BOOL hasSynthesizedProperties;
  13. @property (nonatomic) SVGLength *x1;
  14. @property (nonatomic) SVGLength *y1;
  15. @property (nonatomic) SVGLength *x2;
  16. @property (nonatomic) SVGLength *y2;
  17. @end
  18. @implementation SVGLinearGradientElement
  19. - (SVGGradientLayer *)newGradientLayerForObjectRect:(CGRect)objectRect viewportRect:(SVGRect)viewportRect transform:(CGAffineTransform)absoluteTransform {
  20. SVGGradientLayer *gradientLayer = [[SVGGradientLayer alloc] init];
  21. BOOL inUserSpace = self.gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE;
  22. CGRect rectForRelativeUnits = inUserSpace ? CGRectFromSVGRect( viewportRect ) : objectRect;
  23. gradientLayer.frame = objectRect;
  24. NSString *attrX1 = [self getAttributeInheritedIfNil:@"x1"];
  25. NSString *attrY1 = [self getAttributeInheritedIfNil:@"y1"];
  26. NSString* attrX2 = [self getAttributeInheritedIfNil:@"x2"];
  27. NSString* attrY2 = [self getAttributeInheritedIfNil:@"y2"];
  28. SVGLength* svgX1 = [SVGLength svgLengthFromNSString:attrX1.length > 0 ? attrX1 : @"0%"];
  29. SVGLength* svgY1 = [SVGLength svgLengthFromNSString:attrY1.length > 0 ? attrY1 : @"0%"];
  30. SVGLength* svgX2 = [SVGLength svgLengthFromNSString:attrX2.length > 0 ? attrX2 : @"100%"];
  31. SVGLength* svgY2 = [SVGLength svgLengthFromNSString:attrY2.length > 0 ? attrY2 : @"0%"];
  32. self.x1 = svgX1;
  33. self.y1 = svgY1;
  34. self.x2 = svgX2;
  35. self.y2 = svgY2;
  36. // these should really be two separate code paths (objectBoundingBox and userSpaceOnUse), but we simplify the logic using `rectForRelativeUnits`
  37. CGFloat x1 = [svgX1 pixelsValueWithGradientDimension:CGRectGetWidth(rectForRelativeUnits)];
  38. CGFloat y1 = [svgY1 pixelsValueWithGradientDimension:CGRectGetHeight(rectForRelativeUnits)];
  39. CGFloat x2 = [svgX2 pixelsValueWithGradientDimension:CGRectGetWidth(rectForRelativeUnits)];
  40. CGFloat y2 = [svgY2 pixelsValueWithGradientDimension:CGRectGetHeight(rectForRelativeUnits)];
  41. CGPoint startPoint = CGPointMake(x1, y1);
  42. CGPoint endPoint = CGPointMake(x2, y2);
  43. startPoint = CGPointApplyAffineTransform(startPoint, self.transform);
  44. endPoint = CGPointApplyAffineTransform(endPoint, self.transform);
  45. if (inUserSpace)
  46. {
  47. startPoint = CGPointApplyAffineTransform(startPoint, absoluteTransform);
  48. startPoint.x = startPoint.x - CGRectGetMinX(objectRect);
  49. startPoint.y = startPoint.y - CGRectGetMinY(objectRect);
  50. endPoint = CGPointApplyAffineTransform(endPoint, absoluteTransform);
  51. endPoint.x = endPoint.x - CGRectGetMaxX(objectRect) + CGRectGetWidth(objectRect);
  52. endPoint.y = endPoint.y - CGRectGetMaxY(objectRect) + CGRectGetHeight(objectRect);
  53. }
  54. CGPoint gradientStartPoint = startPoint;
  55. CGPoint gradientEndPoint = endPoint;
  56. // convert to percent
  57. gradientStartPoint.x = startPoint.x / CGRectGetWidth(objectRect);
  58. gradientStartPoint.y = startPoint.y / CGRectGetHeight(objectRect);
  59. gradientEndPoint.x = endPoint.x / CGRectGetWidth(objectRect);
  60. gradientEndPoint.y = endPoint.y / CGRectGetHeight(objectRect);
  61. // Suck on iOS. When using `SVGFastImageView`, the layer software-rendering `drawInContext:` will contains strange boundingRect, while it works fine on macOS. So we need to use custom soft-rendering as well.
  62. gradientLayer.startPoint = gradientStartPoint;
  63. gradientLayer.endPoint = gradientEndPoint;
  64. gradientLayer.type = kCAGradientLayerAxial;
  65. // custom value (match the SVG spec)
  66. gradientLayer.gradientElement = self;
  67. gradientLayer.objectRect = objectRect;
  68. gradientLayer.viewportRect = viewportRect;
  69. gradientLayer.absoluteTransform = absoluteTransform;
  70. if (self.colors.count == 1) {
  71. // SVG Spec: It is necessary that at least two stops defined to have a gradient effect. If no stops are defined, then painting shall occur as if 'none' were specified as the paint style. If one stop is defined, then paint with the solid color fill using the color defined for that gradient stop.
  72. // However, `CAGradientLayer` will show empty when `colors.count` <= 1
  73. SVGGradientStop *lastStop = self.stops.lastObject;
  74. gradientLayer.backgroundColor = CGColorWithSVGColor(lastStop.stopColor);
  75. gradientLayer.opacity = lastStop.stopOpacity;
  76. } else {
  77. [gradientLayer setColors:self.colors];
  78. [gradientLayer setLocations:self.locations];
  79. }
  80. SVGKitLogVerbose(@"[%@] set gradient layer start = %@", [self class], NSStringFromCGPoint(gradientLayer.startPoint));
  81. SVGKitLogVerbose(@"[%@] set gradient layer end = %@", [self class], NSStringFromCGPoint(gradientLayer.endPoint));
  82. SVGKitLogVerbose(@"[%@] set gradient layer colors = %@", [self class], self.colors);
  83. SVGKitLogVerbose(@"[%@] set gradient layer locations = %@", [self class], self.locations);
  84. return gradientLayer;
  85. }
  86. - (void)synthesizeProperties {
  87. if (self.hasSynthesizedProperties)
  88. return;
  89. self.hasSynthesizedProperties = YES;
  90. NSString* gradientID = [self getAttributeNS:@"http://www.w3.org/1999/xlink" localName:@"href"];
  91. if ([gradientID length])
  92. {
  93. if ([gradientID hasPrefix:@"#"])
  94. gradientID = [gradientID substringFromIndex:1];
  95. SVGLinearGradientElement* baseGradient = (SVGLinearGradientElement*) [self.rootOfCurrentDocumentFragment getElementById:gradientID];
  96. NSString* svgNamespace = @"http://www.w3.org/2000/svg";
  97. if (baseGradient)
  98. {
  99. [baseGradient synthesizeProperties];
  100. if (!self.stops && baseGradient.stops)
  101. {
  102. for (SVGGradientStop* stop in baseGradient.stops)
  103. [self addStop:stop];
  104. }
  105. NSArray *keys = [NSArray arrayWithObjects:@"x1", @"y1", @"x2", @"y2", @"gradientUnits", @"gradientTransform", @"spreadMethod", nil];
  106. for (NSString* key in keys)
  107. {
  108. if (![self hasAttribute:key] && [baseGradient hasAttribute:key])
  109. [self setAttributeNS:svgNamespace qualifiedName:key value:[baseGradient getAttribute:key]];
  110. }
  111. }
  112. }
  113. }
  114. @end