SVGGradientLayer.m 8.5 KB

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