API  1.0.0
CPGradient.j
Go to the documentation of this file.
1 /*
2  * CPGradient.j
3  * AppKit
4  *
5  * Created by Alexander Ljungberg.
6  * Copyright 2012, SlevenBits Ltd.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
27 
31 @implementation CPGradient : CPObject
32 {
33  CGGradient _gradient;
34 }
35 
36 - (id)initWithStartingColor:(CPColor)startingColor endingColor:(CPColor)endingColor
37 {
38  return [self initWithColors:[startingColor, endingColor]];
39 }
40 
41 - (id)initWithColors:(CPArray)someColors
42 {
43  var count = [someColors count];
44 
45  if (count < 2)
46  [CPException raise:CPInvalidArgumentException reason:@"at least 2 colors required"];
47 
48  var distance = 1.0 / (count - 1),
49  locations = [CPMutableArray array],
50  location = 0.0;
51 
52  for (var i = 0; i < count; i++)
53  {
54  [locations addObject:location];
55  location += distance;
56  }
57 
58  return [self initWithColors:someColors atLocations:locations colorSpace:nil];
59 }
60 
61 - (id)initWithColors:(CPArray)someColors atLocations:(CPArray)someLocations colorSpace:(CPColorSpace)aColorSpace
62 {
63  if (self = [super init])
64  {
65  var colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB,
66  cgColors = [someColors arrayByApplyingBlock:function(color)
67  {
68  return CGColorCreate(colorSpace, [color components])
69  }];
70 
71  _gradient = CGGradientCreateWithColors(colorSpace, cgColors, someLocations);
72  }
73 
74  return self;
75 }
76 
77 - (void)drawInRect:(CGRect)rect angle:(float)angle
78 {
80 
82  CGContextClipToRect(ctx, rect);
83  CGContextAddRect(ctx, rect);
84 
85  [self _drawInRect:rect atAngle:angle];
86 
88 }
89 
93 - (void)_drawInRect:(CGRect)rect atAngle:(float)angle
94 {
96 
97  startPoint,
98  endPoint;
99 
100  // Modulo of negative values doesn't work as expected in JS.
101  angle = ((angle % 360.0) + 360.0) % 360.0;
102 
103  if (angle < 90.0)
104  startPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
105  else if (angle < 180.0)
106  startPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
107  else if (angle < 270.0)
108  startPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
109  else
110  startPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
111 
112  // A line segment comes out of the starting point at the given angle, with the first colour
113  // at the starting point and the last at the end. To do what drawInRect: is supposed to do
114  // we want the opposite corner of the starting corner to just reach the final colour stop.
115  // So when the angle isn't a right angle, the segment has to extend beyond the edge of the
116  // rectangle just far enough. This is hard to describe without a picture but if we place
117  // another line through the opposite corner at -90 degrees, it'll help form some triangles
118  // from which we can derive this formula:
119  //
120  // length = cos(angle) * (rectWidth + rectHeight * tan(angle))
121  //
122  // This simplifies down to (in the first quadrant) rectWidth * cos(a) + rectHeight * sin(a).
123 
124  var radians = PI * angle / 180.0,
125  length = ABS(CGRectGetWidth(rect) * COS(radians)) + ABS(CGRectGetHeight(rect) * SIN(radians));
126 
127  endPoint = CGPointMake(startPoint.x + length * COS(radians),
128  startPoint.y + length * SIN(radians));
129 
130  [self drawFromPoint:startPoint toPoint:endPoint options:CPGradientDrawsBeforeStartingLocation | CPGradientDrawsAfterEndingLocation];
131 }
132 
133 - (void)drawInBezierPath:(CPBezierPath)aPath angle:(float)anAngle
134 {
136 
138 
139  // Nail down the path which CGContextDrawLinearGradient will cause to be filled.
140  // Note we don't do this by clipping to the path and then calling drawInRect:atAngle:
141  // as this would cut off any antialias of the drawing, plus any active context shadow.
142  CGContextBeginPath(ctx);
143  CGContextAddPath(ctx, aPath._path);
144  CGContextSetLineWidth(ctx, [aPath lineWidth]);
145  CGContextClosePath(ctx);
146 
147  [self _drawInRect:[aPath bounds] atAngle:anAngle];
148 
150 }
151 
152 - (void)drawFromPoint:(NSPoint)startingPoint toPoint:(NSPoint)endingPoint options:(NSGradientDrawingOptions)options
153 {
155 
156  // TODO kCGGradientDrawsBeforeStartLocation and kCGGradientDrawsAfterEndLocation are not actually supported
157  // by CGContextDrawLinearGradient yet.
158  CGContextDrawLinearGradient(ctx, _gradient, startingPoint, endingPoint, options);
159 }
160 
161 @end
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
function CGColorSpaceCreateDeviceRGB()
Definition: CGColorSpace.j:107
id init()
Definition: CALayer.j:126
CPGraphicsContext currentContext()
CPGradientDrawsAfterEndingLocation
Definition: CPGradient.j:26
function CGContextDrawLinearGradient(aContext, aGradient, aStartPoint, anEndPoint, options)
function CGContextRestoreGState(aContext)
Definition: CGContext.j:156
CGColorSpace CGColorSpace()
Definition: CPColorSpace.j:57
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
id initWithColors:atLocations:colorSpace:(CPArray someColors, [atLocations] CPArray someLocations, [colorSpace] CPColorSpace aColorSpace)
Definition: CPGradient.j:61
function CGContextSetLineWidth(aContext, aLineWidth)
Definition: CGContext.j:177
function CGContextClosePath(aContext)
Definition: CGContext.j:322
CGRect bounds()
Definition: CPBezierPath.j:178
function CGContextAddPath(aContext, aPath)
Definition: CGContext.j:258
id initWithColors:(CPArray someColors)
Definition: CPGradient.j:41
function CGContextBeginPath(aContext)
Definition: CGContext.j:311
CGGradient kCGGradientDrawsBeforeStartLocation
Definition: CGGradient.j:26
kCGGradientDrawsAfterEndLocation
Definition: CGGradient.j:27
CPGradientDrawsBeforeStartingLocation
Definition: CPGradient.j:25
function CGContextSaveGState(aContext)
Definition: CGContext.j:146
function CGContextAddRect(aContext, aRect)
Definition: CGContext.j:289
function CGGradientCreateWithColors(aColorSpace, colors, locations)
Definition: CGGradient.j:54