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