PageRenderTime 61ms CodeModel.GetById 23ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/google-toolbox-for-mac/AppKit/GTMLinearRGBShading.m

http://macfuse.googlecode.com/
Objective C | 192 lines | 121 code | 18 blank | 53 comment | 26 complexity | a5d3c56c81631c2cb40b960cab015ad3 MD5 | raw file
  1//
  2//  GTMLinearRGBShading.m
  3//
  4//  Copyright 2006-2008 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9// 
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11// 
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19#import "GTMLinearRGBShading.h"
 20#import "GTMDefines.h"
 21
 22// Carbon callback function required for CoreGraphics
 23static void cShadeFunction(void *info, const CGFloat *inPos, CGFloat *outVals);
 24
 25@implementation GTMLinearRGBShading
 26+ (id)shadingFromColor:(NSColor *)begin toColor:(NSColor *)end 
 27        fromSpaceNamed:(NSString*)colorSpaceName {
 28  NSColor *theColors[] = { begin, end };
 29  CGFloat thePositions[] = { 0.0, 1.0 };
 30  return [[self class] shadingWithColors:theColors
 31                          fromSpaceNamed:colorSpaceName
 32                            atPositions:thePositions
 33                                  count:(sizeof(thePositions)/sizeof(CGFloat))];
 34}
 35
 36+ (id)shadingWithColors:(NSColor **)colors fromSpaceNamed:(NSString*)colorSpaceName
 37            atPositions:(CGFloat *)positions count:(NSUInteger)count {
 38
 39  GTMLinearRGBShading *theShading = [[[self alloc] initWithColorSpaceName:colorSpaceName] autorelease];
 40  for (NSUInteger i = 0; i < count; ++i) {
 41    [theShading insertStop:colors[i] atPosition:positions[i]];
 42  }
 43  return theShading;
 44}
 45
 46- (id)initWithColorSpaceName:(NSString*)colorSpaceName {
 47  if ((self = [super init])) {
 48    if ([colorSpaceName isEqualToString:NSDeviceRGBColorSpace]) {
 49      isCalibrated_ = NO;
 50    } else if ([colorSpaceName isEqualToString:NSCalibratedRGBColorSpace]) {
 51      isCalibrated_ = YES;
 52    }
 53    else {
 54      [self release];
 55      self = nil;
 56    }
 57  }
 58  return self;
 59}
 60
 61- (void)dealloc {
 62  if (nil != function_) {
 63    CGFunctionRelease(function_);
 64  }
 65  if (nil != colorSpace_) {
 66    CGColorSpaceRelease(colorSpace_);
 67  }
 68  [super dealloc];
 69}
 70
 71
 72- (void)insertStop:(id)item atPosition:(CGFloat)position {
 73  NSString *colorSpaceName = isCalibrated_ ? NSCalibratedRGBColorSpace : NSDeviceRGBColorSpace;
 74  NSColor *tempColor = [item colorUsingColorSpaceName: colorSpaceName];
 75  if (nil != tempColor) {
 76    [super insertStop:tempColor atPosition:position];
 77  }
 78}
 79
 80//  Calculate a linear value based on our stops
 81- (id)valueAtPosition:(CGFloat)position {
 82  NSUInteger positionIndex = 0;
 83  NSUInteger colorCount = [self stopCount];
 84  CGFloat stop1Position = 0.0;
 85  NSColor *stop1Color = [self stopAtIndex:positionIndex position:&stop1Position];
 86  positionIndex += 1;
 87  CGFloat stop2Position = 0.0;
 88  NSColor *stop2Color = nil;
 89  if (colorCount > 1) {
 90    stop2Color = [self stopAtIndex:positionIndex position:&stop2Position];
 91    positionIndex += 1;
 92  } else {
 93    // if we only have one value, that's what we return
 94    stop2Position = stop1Position;
 95    stop2Color = stop1Color;
 96  }
 97
 98  while (positionIndex < colorCount && stop2Position < position) {
 99    stop1Color = stop2Color;
100    stop1Position = stop2Position;
101    stop2Color = [self stopAtIndex:positionIndex position:&stop2Position];
102    positionIndex += 1;
103  }
104
105  if (position <= stop1Position) {
106    // if we are less than our lowest position, return our first color
107    [stop1Color getRed:&colorValue_[0] green:&colorValue_[1] 
108                  blue:&colorValue_[2] alpha:&colorValue_[3]];
109  } else if (position >= stop2Position) {
110    // likewise if we are greater than our highest position, return the last color
111    [stop2Color getRed:&colorValue_[0] green:&colorValue_[1] 
112                  blue:&colorValue_[2] alpha:&colorValue_[3]];
113  } else {
114    // otherwise interpolate between the two
115    position = (position - stop1Position) / (stop2Position - stop1Position);
116    CGFloat red1, red2, green1, green2, blue1, blue2, alpha1, alpha2;
117    [stop1Color getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1];
118    [stop2Color getRed:&red2 green:&green2 blue:&blue2 alpha:&alpha2];
119    
120    colorValue_[0] = (red2 - red1) * position + red1;
121    colorValue_[1] = (green2 - green1) * position + green1;
122    colorValue_[2] = (blue2 - blue1) * position + blue1;
123    colorValue_[3] = (alpha2 - alpha1) * position + alpha1;
124  }
125  
126  // Yes, I am casting a CGFloat[] to an id to pass it by the compiler. This
127  // significantly improves performance though as I avoid creating an NSColor
128  // for every scanline which later has to be cleaned up in an autorelease pool
129  // somewhere. Causes guardmalloc to run significantly faster.
130  return (id)colorValue_;
131}
132
133//
134//  switch from C to obj-C. The callback to a shader is a c function
135//  but we want to call our objective c object to do all the
136//  calculations for us. We have passed our function our
137//  GTMLinearRGBShading as an obj-c object in the |info| so
138//  we just turn around and ask it to calculate our value based
139//  on |inPos| and then stick the results back in |outVals|
140//
141//   Args: 
142//    info: is the GTMLinearRGBShading as an
143//          obj-C object. 
144//    inPos: the position to calculate values for. This is a pointer to
145//           a single float value
146//    outVals: where we store our return values. Since we are calculating
147//             an RGBA color, this is a pointer to an array of four float values
148//             ranging from 0.0 to 1.0
149//      
150//
151static void cShadeFunction(void *info, const CGFloat *inPos, CGFloat *outVals) {
152  id object = (id)info;
153  CGFloat *colorValue = (CGFloat*)[object valueAtPosition:*inPos];
154  outVals[0] = colorValue[0];
155  outVals[1] = colorValue[1];
156  outVals[2] = colorValue[2];
157  outVals[3] = colorValue[3];
158}
159
160- (CGFunctionRef) shadeFunction {
161  // lazily create the function as necessary
162  if (nil == function_) {
163    // We have to go to carbon here, and create the CGFunction. Note that this
164    // diposed if necessary in the dealloc call.
165    const CGFunctionCallbacks shadeFunctionCallbacks = { 0, &cShadeFunction, NULL };
166    
167    // TODO: this code assumes that we have a range from 0.0 to 1.0
168    // which may not be true according to the stops that the user has given us.
169    // In general you have stops at 0.0 and 1.0, so this will do for right now
170    // but may be an issue in the future.
171    const CGFloat inRange[2] = { 0.0, 1.0 };
172    const CGFloat outRange[8] = { 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 };
173    function_ = CGFunctionCreate(self,
174                                  sizeof(inRange) / (sizeof(CGFloat) * 2), inRange,
175                                  sizeof(outRange) / (sizeof(CGFloat) * 2), outRange,
176                                  &shadeFunctionCallbacks);
177  }
178  return function_;
179}  
180
181- (CGColorSpaceRef)colorSpace {
182  // lazily create the colorspace as necessary
183  if (nil == colorSpace_) {
184    if (isCalibrated_) {
185      colorSpace_ = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
186    } else {
187      colorSpace_ = CGColorSpaceCreateDeviceRGB(); 
188    }
189  } 
190  return colorSpace_;
191}
192@end