1/* GSWheelColorPicker.m 2 3 Copyright (C) 2001 Free Software Foundation, Inc. 4 5 Author: Fred Kiefer <FredKiefer@gmx.de> 6 Date: Febuary 2001 7 Author: Alexander Malmberg <alexander@malmberg.org> 8 Date: May 2002 9 10 This file is part of GNUstep. 11 12 This library is free software; you can redistribute it and/or 13 modify it under the terms of the GNU Lesser General Public 14 License as published by the Free Software Foundation; either 15 version 2 of the License, or (at your option) any later version. 16 17 This library is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public 23 License along with this library; see the file COPYING.LIB. 24 If not, see <http://www.gnu.org/licenses/> or write to the 25 Free Software Foundation, 51 Franklin Street, Fifth Floor, 26 Boston, MA 02110-1301, USA. 27*/ 28 29#include <math.h> 30 31#ifndef PI 32#define PI 3.141592653589793 33#endif 34 35#include <Foundation/Foundation.h> 36#include <AppKit/AppKit.h> 37#include <GNUstepGUI/GSHbox.h> 38#include "GSStandardColorPicker.h" 39 40@interface GSColorWheelMarker : NSView 41{ 42} 43 44@end 45 46@implementation GSColorWheelMarker : NSView 47 48-(BOOL) isOpaque 49{ 50 return YES; 51} 52 53-(void) drawRect: (NSRect)rect 54{ 55 NSRect bounds = [self bounds]; 56 [[NSColor whiteColor] set]; 57 NSRectFill(bounds); 58 [[NSColor blackColor] set]; 59 NSFrameRect(bounds); 60} 61 62@end 63 64 65@interface GSColorWheel : NSView 66{ 67 float hue, saturation, brightness; 68 69 id target; 70 SEL action; 71 72 GSColorWheelMarker *marker; 73 NSImage *image; 74} 75 76-(float) hue; 77-(float) saturation; 78 79-(void) regenerateImage; 80-(NSRect) markerRect; 81 82-(void) setHue: (float)h saturation: (float)s brightness: (float)brightness; 83 84-(void) setTarget: (id)t; 85-(void) setAction: (SEL)a; 86 87@end 88 89@implementation GSColorWheel 90 91-(id) initWithFrame: (NSRect)frame 92{ 93 self = [super initWithFrame: frame]; 94 if (nil == self) 95 { 96 return nil; 97 } 98 99 [self setPostsFrameChangedNotifications: YES]; 100 [[NSNotificationCenter defaultCenter] 101 addObserver: self 102 selector: @selector(_frameChanged:) 103 name: NSViewFrameDidChangeNotification 104 object: self]; 105 106 return self; 107} 108 109-(void) _frameChanged: (id)sender 110{ 111 [self regenerateImage]; 112 [marker setFrame: [self markerRect]]; 113} 114 115-(void) dealloc 116{ 117 [image release]; 118 [[NSNotificationCenter defaultCenter] removeObserver: self]; 119 [super dealloc]; 120} 121 122-(void) setTarget: (id)t 123{ 124 target = t; 125} 126-(void) setAction: (SEL)a 127{ 128 action = a; 129} 130 131-(float) hue 132{ 133 return hue; 134} 135-(float) saturation 136{ 137 return saturation; 138} 139 140-(NSRect) markerRect 141{ 142 NSRect frame = [self bounds]; 143 float a,r,x,y,cr,cx,cy; 144 145 cx = (frame.origin.x + frame.size.width) / 2; 146 cy = (frame.origin.y + frame.size.height) / 2; 147 148 cr = frame.size.width; 149 if (cr > frame.size.height) 150 cr = frame.size.height; 151 152 cr = cr / 2 - 2; 153 154 a = hue * 2 * PI; 155 r = saturation * cr; 156 157 x = cos(a) * r + cx; 158 y = sin(a) * r + cy; 159 160 return NSMakeRect(x-2,y-2,4,4); 161} 162 163-(void) setHue: (float)h saturation: (float)s brightness: (float)b 164{ 165 if (nil == marker) 166 { 167 marker = [[[GSColorWheelMarker alloc] initWithFrame: [self markerRect]] autorelease]; 168 [self addSubview: marker]; 169 } 170 171 if (hue != h || saturation != s || brightness != b) 172 { 173 BOOL regenerate = (brightness != b); 174 175 hue = h; 176 saturation = s; 177 brightness = b; 178 179 if (regenerate) 180 [self regenerateImage]; 181 182 [marker setFrame: [self markerRect]]; 183 184 [self setNeedsDisplay: YES]; 185 } 186} 187 188-(void) regenerateImage 189{ 190 NSSize size = [self convertSizeToBase: [self bounds].size]; 191 CGFloat cx, cy, cr; 192 193 [image release]; 194 image = nil; 195 196 cx = (size.width) / 2; 197 cy = (size.height) / 2; 198 199 cr = size.width; 200 if (cr > size.height) 201 cr = size.height; 202 203 cr = cr / 2 - 2; 204 205 { 206 NSUInteger width = size.width; 207 NSUInteger height = size.height; 208 NSUInteger bytesPerRow; 209 NSBitmapImageRep *bmp; 210 unsigned char *data; 211 NSUInteger x, y; 212 213 if (width < 1 || height < 1) 214 return; 215 216 bmp = [[NSBitmapImageRep alloc] 217 initWithBitmapDataPlanes: NULL 218 pixelsWide: width 219 pixelsHigh: height 220 bitsPerSample: 8 221 samplesPerPixel: 4 222 hasAlpha: YES 223 isPlanar: NO 224 colorSpaceName: NSCalibratedRGBColorSpace 225 bytesPerRow: 0 226 bitsPerPixel: 32]; 227 228 bytesPerRow = [bmp bytesPerRow]; 229 data = [bmp bitmapData]; 230 231 for (y = 0; y < height; y++) 232 { 233 uint32_t *row = (uint32_t*)(data + (y * bytesPerRow)); 234 235 for (x = 0; x < width; x++) 236 { 237 CGFloat dx, dy, dist; 238 CGFloat h, s, v; 239 CGFloat R, G, B, A; 240 241 dx = x - cx; 242 dy = cy - y; // compensate for flipped coordinates 243 dist = sqrt(dx * dx + dy * dy); 244 245 // calculate h,s,v from x,y 246 { 247 h = atan2(dy, dx) / 2.0 / PI; 248 if (h < 0) 249 h += 1; 250 251 s = dist/cr; 252 if (s > 1) 253 s = 1; 254 255 v = brightness; 256 } 257 258 // calculate R,G,B from h,s,v 259 { 260 int I = (int)(h * 6); 261 CGFloat V = v; 262 CGFloat S = s; 263 CGFloat F = (h * 6) - I; 264 CGFloat M = V * (1 - S); 265 CGFloat N = V * (1 - S * F); 266 CGFloat K = M - N + V; 267 268 switch (I) 269 { 270 default: R = V; G = K; B = M; break; 271 case 1: R = N; G = V; B = M; break; 272 case 2: R = M; G = V; B = K; break; 273 case 3: R = M; G = N; B = V; break; 274 case 4: R = K; G = M; B = V; break; 275 case 5: R = V; G = M; B = N; break; 276 } 277 } 278 279 // calculate alpha 280 { 281 A = (cr - dist) + 0.5; 282 if (A > 1) A = 1; 283 if (A < 0) A = 0; 284 } 285 286 // premultiply color with alpha 287 R *= A; 288 G *= A; 289 B *= A; 290 291 // store pixel 292#if GS_WORDS_BIGENDIAN 293 row[x] = ((uint32_t)(255 * R) << 24) 294 | (((uint32_t)(255 * G)) << 16) 295 | (((uint32_t)(255 * B)) << 8) 296 | (((uint32_t)(255 * A))); 297#else 298 row[x] = ((uint32_t)(255 * R)) 299 | (((uint32_t)(255 * G)) << 8) 300 | (((uint32_t)(255 * B)) << 16) 301 | (((uint32_t)(255 * A)) << 24); 302#endif 303 } 304 } 305 306 image = [[NSImage alloc] initWithSize: [self bounds].size]; 307 [image addRepresentation: bmp]; 308 [bmp release]; 309 } 310} 311 312-(void) drawRect: (NSRect)rect 313{ 314 if (nil == image) 315 { 316 [self regenerateImage]; 317 } 318 319 [image drawInRect: [self bounds] 320 fromRect: NSZeroRect 321 operation: NSCompositeSourceOver 322 fraction: 1.0]; 323} 324 325-(BOOL) acceptsFirstMouse: (NSEvent *)theEvent 326{ 327 return YES; 328} 329 330-(BOOL) acceptsFirstResponder 331{ 332 return NO; 333} 334 335-(void) handleMouseAtPoint: (NSPoint)point 336{ 337 NSRect frame = [self bounds]; 338 CGFloat cx, cy, cr, dx, dy, new_hue, new_saturation; 339 340 cx = (frame.origin.x + frame.size.width) / 2; 341 cy = (frame.origin.y + frame.size.height) / 2; 342 cr = frame.size.width; 343 if (cr > frame.size.height) 344 cr = frame.size.height; 345 cr = cr / 2 - 2; 346 347 dx = point.x - cx; 348 dy = point.y - cy; 349 350 new_saturation = sqrt(dx * dx + dy * dy) / cr; 351 if (new_saturation > 1) 352 new_saturation = 1; 353 354 new_hue = atan2(dy, dx) / 2.0 / PI; 355 if (new_hue < 0) 356 new_hue += 1; 357 358 [self setHue: new_hue saturation: new_saturation brightness: brightness]; 359 360 if (target) 361 { 362 [target performSelector: action withObject: self]; 363 } 364} 365 366-(void) mouseDown: (NSEvent *)theEvent 367{ 368 if ([theEvent type] == NSLeftMouseDown) 369 { 370 [self handleMouseAtPoint: [self convertPoint: [theEvent locationInWindow] fromView: nil]]; 371 } 372} 373 374-(void) mouseDragged: (NSEvent *)theEvent 375{ 376 if ([theEvent type] == NSLeftMouseDragged) 377 { 378 [self handleMouseAtPoint: [self convertPoint: [theEvent locationInWindow] fromView: nil]]; 379 } 380} 381 382@end 383 384 385@interface GSWheelColorPicker: NSColorPicker <NSColorPickingCustom> 386{ 387 GSHbox *baseView; 388 NSSlider *brightnessSlider; 389 GSColorWheel *wheel; 390} 391 392- (void) sliderChanged: (id) sender; 393- (void) loadViews; 394 395@end 396 397@implementation GSWheelColorPicker 398 399- (void) dealloc 400{ 401 RELEASE(baseView); 402 [super dealloc]; 403} 404 405- (id)initWithPickerMask:(int)aMask 406 colorPanel:(NSColorPanel *)colorPanel 407{ 408 if (aMask & NSColorPanelWheelModeMask) 409 return [super initWithPickerMask: aMask 410 colorPanel: colorPanel]; 411 RELEASE(self); 412 return nil; 413} 414 415- (int)currentMode 416{ 417 return NSWheelModeColorPanel; 418} 419 420- (BOOL)supportsMode:(int)mode 421{ 422 return mode == NSWheelModeColorPanel; 423} 424 425- (NSView *)provideNewView:(BOOL)initialRequest 426{ 427 if (initialRequest) 428 { 429 [self loadViews]; 430 } 431 return baseView; 432} 433 434- (void)setColor:(NSColor *)color 435{ 436 CGFloat hue, saturation, brightness, alpha; 437 NSColor *c; 438 439 c = [color colorUsingColorSpaceName: NSCalibratedRGBColorSpace]; 440 [c getHue: &hue saturation: &saturation brightness: &brightness alpha: &alpha]; 441 442 [(GSColorSliderCell *)[brightnessSlider cell] 443 _setColorSliderCellValues: hue : saturation : brightness]; 444 [brightnessSlider setNeedsDisplay: YES]; 445 [brightnessSlider setFloatValue: brightness]; 446 [wheel setHue: hue saturation: saturation brightness: brightness]; 447} 448 449- (void) loadViews 450{ 451 NSSlider *s; 452 NSCell *c; 453 454 baseView = [[GSHbox alloc] init]; 455 [baseView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; 456 457 wheel = [[GSColorWheel alloc] init]; 458 [wheel setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; 459 [wheel setTarget: self]; 460 [wheel setAction: @selector(sliderChanged:)]; 461 [baseView addView: wheel]; 462 463 s = brightnessSlider = [[NSSlider alloc] initWithFrame: NSMakeRect(0,0,16,0)]; 464 [s setAutoresizingMask: NSViewHeightSizable]; 465 c = [[GSColorSliderCell alloc] init]; 466 [s setCell: c]; 467 RELEASE(c); 468 [(GSColorSliderCell *)[s cell] _setColorSliderCellMode: 10]; 469 [s setContinuous: YES]; 470 [s setMinValue: 0.0]; 471 [s setMaxValue: 1.0]; 472 [s setTarget: self]; 473 [s setAction: @selector(sliderChanged:)]; 474 [[s cell] setBezeled: YES]; 475 476 [baseView addView: brightnessSlider enablingXResizing: NO]; 477} 478 479- (void) sliderChanged: (id) sender 480{ 481 float brightness = [brightnessSlider floatValue]; 482 float hue = [wheel hue]; 483 float saturation = [wheel saturation]; 484 float alpha = [_colorPanel alpha]; 485 NSColor *c; 486 487 [(GSColorSliderCell *)[brightnessSlider cell] 488 _setColorSliderCellValues: hue : saturation : brightness]; 489 [brightnessSlider setNeedsDisplay: YES]; 490 491 c = [NSColor colorWithCalibratedHue: hue 492 saturation: saturation 493 brightness: brightness 494 alpha: alpha]; 495 [_colorPanel setColor: c]; 496} 497 498@end 499 500