SVGGradientLayer.m 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. //
  2. // SVGGradientLayer.m
  3. // SVGKit-iOS
  4. //
  5. // Created by zhen ling tsai on 19/7/13.
  6. // Copyright (c) 2013 na. All rights reserved.
  7. //
  8. #import "SVGGradientLayer.h"
  9. #import "SVGRadialGradientElement.h"
  10. #import "SVGLinearGradientElement.h"
  11. #import "CALayerWithClipRender.h"
  12. @implementation SVGGradientLayer
  13. - (void)renderInContext:(CGContextRef)ctx {
  14. if (!self.gradientElement) {
  15. [super renderInContext:ctx];
  16. return;
  17. }
  18. if ([self.gradientElement isKindOfClass:[SVGRadialGradientElement class]]) {
  19. [self renderRadialGradientInContext:ctx];
  20. } else if ([self.gradientElement isKindOfClass:[SVGLinearGradientElement class]]) {
  21. [self renderLinearGradientInContext:ctx];
  22. }
  23. }
  24. - (CGGradientRef)createCGGradient {
  25. SVGGradientElement *gradientElement = self.gradientElement;
  26. if ([gradientElement isKindOfClass:[SVGRadialGradientElement class]]) {
  27. SVGLength *svgR = ((SVGRadialGradientElement *)gradientElement).r;
  28. if (svgR.value <= 0) {
  29. return nil;
  30. }
  31. }
  32. // CGGradient
  33. NSArray *colors = gradientElement.colors;
  34. NSArray *locations = gradientElement.locations;
  35. if (colors.count == 0) {
  36. SVGKitLogWarn(@"[%@] colors count is zero", [self class]);
  37. return NULL;
  38. }
  39. if (colors.count != locations.count) {
  40. SVGKitLogWarn(@"[%@] colors count : %lu != locations count : %lu", [self class], (unsigned long)colors.count, (unsigned long)locations.count);
  41. return NULL;
  42. }
  43. CGFloat locations_array[locations.count];
  44. CGColorSpaceRef colorSpace = NULL;
  45. for (int i = 0; i < locations.count; i++) {
  46. CGFloat location = [[locations objectAtIndex:i] doubleValue];
  47. CGColorRef colorRef = (__bridge CGColorRef)[colors objectAtIndex:i];
  48. locations_array[i] = location;
  49. if (!colorSpace) {
  50. colorSpace = CGColorGetColorSpace(colorRef);
  51. }
  52. }
  53. CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations_array);
  54. CGColorSpaceRelease(colorSpace);
  55. return gradient;
  56. }
  57. - (void)renderLinearGradientInContext:(CGContextRef)ctx {
  58. SVGLinearGradientElement *gradientElement = (SVGLinearGradientElement *)self.gradientElement;
  59. BOOL inUserSpace = gradientElement.gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE;
  60. CGRect objectRect = self.objectRect;
  61. CGRect rectForRelativeUnits = inUserSpace ? CGRectFromSVGRect( self.viewportRect ) : objectRect;
  62. CGFloat width = CGRectGetWidth(rectForRelativeUnits);
  63. CGFloat height = CGRectGetHeight(rectForRelativeUnits);
  64. CGFloat x1 = [gradientElement.x1 pixelsValueWithGradientDimension:width];
  65. CGFloat y1 = [gradientElement.y1 pixelsValueWithGradientDimension:height];
  66. CGFloat x2 = [gradientElement.x2 pixelsValueWithGradientDimension:width];
  67. CGFloat y2 = [gradientElement.y2 pixelsValueWithGradientDimension:height];
  68. CGPoint gradientStartPoint = CGPointMake(x1, y1);
  69. CGPoint gradientEndPoint = CGPointMake(x2, y2);
  70. // transforms
  71. CGAffineTransform selfTransform = gradientElement.transform;
  72. CGAffineTransform trans = CGAffineTransformMakeTranslation(-CGRectGetMinX(objectRect),
  73. -CGRectGetMinY(objectRect));
  74. CGAffineTransform absoluteTransform = CGAffineTransformConcat(self.absoluteTransform,trans);
  75. // CGGradient
  76. CGGradientRef gradient = [self createCGGradient];
  77. CGContextSaveGState(ctx);
  78. {
  79. // clip the mask
  80. if (self.mask)
  81. {
  82. [CALayerWithClipRender maskLayer:self inContext:ctx];
  83. }
  84. if(inUserSpace == YES) {
  85. #pragma mark User Space On Use
  86. // transform absolute - due to user space
  87. CGContextConcatCTM(ctx, absoluteTransform);
  88. } else {
  89. #pragma mark Object Bounding Box
  90. }
  91. // set the opacity
  92. CGContextSetAlpha(ctx, self.opacity);
  93. // transform the context
  94. CGContextConcatCTM(ctx, selfTransform);
  95. // draw the gradient
  96. CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation|
  97. kCGGradientDrawsAfterEndLocation;
  98. CGContextDrawLinearGradient(ctx, gradient, gradientStartPoint,
  99. gradientEndPoint, options);
  100. CGGradientRelease(gradient);
  101. };
  102. CGContextRestoreGState(ctx);
  103. }
  104. -(void)renderRadialGradientInContext:(CGContextRef)ctx {
  105. SVGRadialGradientElement *gradientElement = (SVGRadialGradientElement *)self.gradientElement;
  106. BOOL inUserSpace = gradientElement.gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE;
  107. CGRect objectRect = self.objectRect;
  108. CGRect rectForRelativeUnits = inUserSpace ? CGRectFromSVGRect( self.viewportRect ) : objectRect;
  109. CGFloat width = CGRectGetWidth(rectForRelativeUnits);
  110. CGFloat height = CGRectGetHeight(rectForRelativeUnits);
  111. CGFloat cx = [gradientElement.cx pixelsValueWithGradientDimension:width];
  112. CGFloat cy = [gradientElement.cy pixelsValueWithGradientDimension:height];
  113. CGPoint startPoint = CGPointMake(cx, cy);
  114. CGFloat val = MIN(width, height);
  115. CGFloat radius = [gradientElement.r pixelsValueWithGradientDimension:val];
  116. CGFloat focalRadius = [gradientElement.fr pixelsValueWithGradientDimension:val];
  117. CGFloat fx = [gradientElement.fx pixelsValueWithGradientDimension:width];
  118. CGFloat fy = [gradientElement.fy pixelsValueWithGradientDimension:height];
  119. CGPoint gradientEndPoint = CGPointMake(fx, fy);
  120. CGPoint gradientStartPoint = startPoint;
  121. // transforms
  122. CGAffineTransform selfTransform = gradientElement.transform;
  123. CGAffineTransform trans = CGAffineTransformMakeTranslation(-CGRectGetMinX(objectRect),
  124. -CGRectGetMinY(objectRect));
  125. CGAffineTransform absoluteTransform = CGAffineTransformConcat(self.absoluteTransform,trans);
  126. // CGGradient
  127. CGGradientRef gradient = [self createCGGradient];
  128. CGContextSaveGState(ctx);
  129. {
  130. // clip the mask
  131. if (self.mask)
  132. {
  133. [CALayerWithClipRender maskLayer:self inContext:ctx];
  134. }
  135. #pragma mark User Space On Use
  136. if(inUserSpace == YES) {
  137. // work out the new radius
  138. CGFloat rad = 2 * radius;
  139. CGRect rect = CGRectMake(startPoint.x, startPoint.y, rad, rad);
  140. rect = CGRectApplyAffineTransform(rect, selfTransform);
  141. rect = CGRectApplyAffineTransform(rect, absoluteTransform);
  142. radius = CGRectGetHeight(rect)/2.f;
  143. // transform absolute - due to user space
  144. CGContextConcatCTM(ctx, absoluteTransform);
  145. } else {
  146. #pragma mark Object Bounding Box
  147. // SVG spec: transform if width or height is not equal
  148. if(CGRectGetWidth(objectRect) != CGRectGetHeight(objectRect)) {
  149. CGAffineTransform tr = CGAffineTransformMakeTranslation(gradientStartPoint.x,
  150. gradientStartPoint.y);
  151. if(CGRectGetWidth(objectRect) > CGRectGetHeight(objectRect)) {
  152. tr = CGAffineTransformScale(tr, CGRectGetWidth(objectRect)/CGRectGetHeight(objectRect), 1);
  153. } else {
  154. tr = CGAffineTransformScale(tr, 1.f, CGRectGetHeight(objectRect)/CGRectGetWidth(objectRect));
  155. }
  156. tr = CGAffineTransformTranslate(tr, -gradientStartPoint.x, -gradientStartPoint.y);
  157. selfTransform = CGAffineTransformConcat(tr, selfTransform);
  158. }
  159. }
  160. // set the opacity
  161. CGContextSetAlpha(ctx, self.opacity);
  162. #pragma mark Default drawing
  163. // transform the context
  164. CGContextConcatCTM(ctx, selfTransform);
  165. if (gradient) {
  166. // draw the gradient
  167. CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation|
  168. kCGGradientDrawsAfterEndLocation;
  169. CGContextDrawRadialGradient(ctx, gradient,
  170. gradientEndPoint, focalRadius, gradientStartPoint,
  171. radius, options);
  172. CGGradientRelease(gradient);
  173. } else {
  174. // draw the background
  175. CGColorRef backgroundColor = self.backgroundColor;
  176. CGContextSetFillColorWithColor(ctx, backgroundColor);
  177. CGContextFillRect(ctx, CGRectMake(0, 0, CGRectGetWidth(objectRect), CGRectGetHeight(objectRect)));
  178. }
  179. };
  180. CGContextRestoreGState(ctx);
  181. }
  182. @end