mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-25 07:11:00 +00:00
* ColorPickers/GSWheelColorPicker.m: Rewrite to draw the HSV
wheel in a bitmap. This gives a pretty large performance improvement. * Source/NSColorPanel.m: Set a sensible min and max size for the color panel. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@31195 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
394071203e
commit
b5fc41a47b
3 changed files with 266 additions and 141 deletions
|
@ -1,3 +1,10 @@
|
|||
2010-08-23 Eric Wasylishen <ewasylishen@gmail.com>
|
||||
|
||||
* ColorPickers/GSWheelColorPicker.m: Rewrite to draw the HSV
|
||||
wheel in a bitmap. This gives a pretty large performance improvement.
|
||||
* Source/NSColorPanel.m: Set a sensible min and max size for the
|
||||
color panel.
|
||||
|
||||
2010-08-19 Wolfgang Lux <wolfgang.lux@gmail.com>
|
||||
|
||||
* Source/NSOutlineView.m (-mouseDown): If the alt modifier is
|
||||
|
|
|
@ -37,6 +37,30 @@
|
|||
#include <GNUstepGUI/GSHbox.h>
|
||||
#include "GSStandardColorPicker.h"
|
||||
|
||||
@interface GSColorWheelMarker : NSView
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GSColorWheelMarker : NSView
|
||||
|
||||
-(BOOL) isOpaque
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void) drawRect: (NSRect)rect
|
||||
{
|
||||
NSRect bounds = [self bounds];
|
||||
[[NSColor whiteColor] set];
|
||||
NSRectFill(bounds);
|
||||
[[NSColor blackColor] set];
|
||||
NSFrameRect(bounds);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface GSColorWheel : NSView
|
||||
{
|
||||
|
@ -44,11 +68,17 @@
|
|||
|
||||
id target;
|
||||
SEL action;
|
||||
|
||||
GSColorWheelMarker *marker;
|
||||
NSImage *image;
|
||||
}
|
||||
|
||||
-(float) hue;
|
||||
-(float) saturation;
|
||||
|
||||
-(void) regenerateImage;
|
||||
-(NSRect) markerRect;
|
||||
|
||||
-(void) setHue: (float)h saturation: (float)s brightness: (float)brightness;
|
||||
|
||||
-(void) setTarget: (id)t;
|
||||
|
@ -58,6 +88,37 @@
|
|||
|
||||
@implementation GSColorWheel
|
||||
|
||||
-(id) initWithFrame: (NSRect)frame
|
||||
{
|
||||
self = [super initWithFrame: frame];
|
||||
if (nil == self)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self setPostsFrameChangedNotifications: YES];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver: self
|
||||
selector: @selector(_frameChanged:)
|
||||
name: NSViewFrameDidChangeNotification
|
||||
object: self];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) _frameChanged: (id)sender
|
||||
{
|
||||
[self regenerateImage];
|
||||
[marker setFrame: [self markerRect]];
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
[image release];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
-(void) setTarget: (id)t
|
||||
{
|
||||
target = t;
|
||||
|
@ -76,28 +137,10 @@
|
|||
return saturation;
|
||||
}
|
||||
|
||||
-(void) setHue: (float)h saturation: (float)s brightness: (float)b
|
||||
{
|
||||
if (hue == h && saturation == s && brightness == b)
|
||||
return;
|
||||
hue = h;
|
||||
saturation = s;
|
||||
brightness = b;
|
||||
[self setNeedsDisplay: YES];
|
||||
}
|
||||
|
||||
-(BOOL) isOpaque
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void) drawRect: (NSRect)rect
|
||||
-(NSRect) markerRect
|
||||
{
|
||||
NSRect frame = [self bounds];
|
||||
float cx, cy, cr;
|
||||
float x, y;
|
||||
float r, a;
|
||||
float dx, dy;
|
||||
float a,r,x,y,cr,cx,cy;
|
||||
|
||||
cx = (frame.origin.x + frame.size.width) / 2;
|
||||
cy = (frame.origin.y + frame.size.height) / 2;
|
||||
|
@ -108,74 +151,185 @@
|
|||
|
||||
cr = cr / 2 - 2;
|
||||
|
||||
rect.origin.x = floor(rect.origin.x);
|
||||
rect.origin.y = floor(rect.origin.y);
|
||||
rect.size.width = ceil(rect.size.width) + 1;
|
||||
rect.size.height = ceil(rect.size.height) + 1;
|
||||
|
||||
[[NSColor windowBackgroundColor] set];
|
||||
NSRectFill(rect);
|
||||
|
||||
for (y = rect.origin.y; y < rect.origin.y + rect.size.height; y++)
|
||||
{
|
||||
for (x = rect.origin.x; x < rect.origin.x + rect.size.width; x++)
|
||||
{
|
||||
dx = x - cx;
|
||||
dy = y - cy;
|
||||
|
||||
r = dx * dx + dy * dy;
|
||||
r = sqrt(r);
|
||||
r /= cr;
|
||||
if (r > 1)
|
||||
continue;
|
||||
|
||||
a = atan2(dy, dx);
|
||||
a = a / 2.0 / PI;
|
||||
if (a < 0)
|
||||
a += 1;
|
||||
|
||||
PSsethsbcolor(a, r, brightness);
|
||||
PSrectfill(x,y,1,1);
|
||||
}
|
||||
}
|
||||
|
||||
a = hue * 2 * PI;
|
||||
r = saturation * cr;
|
||||
|
||||
x = cos(a) * r + cx;
|
||||
y = sin(a) * r + cy;
|
||||
|
||||
PSsetgray(0);
|
||||
PSrectstroke(x - 2, y - 2, 4, 4);
|
||||
PSsetgray(1);
|
||||
PSrectfill(x - 1, y - 1, 2, 2);
|
||||
return NSMakeRect(x-2,y-2,4,4);
|
||||
}
|
||||
|
||||
- (BOOL) acceptsFirstMouse: (NSEvent *)theEvent
|
||||
-(void) setHue: (float)h saturation: (float)s brightness: (float)b
|
||||
{
|
||||
if (nil == marker)
|
||||
{
|
||||
marker = [[[GSColorWheelMarker alloc] initWithFrame: [self markerRect]] autorelease];
|
||||
[self addSubview: marker];
|
||||
}
|
||||
|
||||
if (hue != h || saturation != s || brightness != b)
|
||||
{
|
||||
BOOL regenerate = (brightness != b);
|
||||
|
||||
hue = h;
|
||||
saturation = s;
|
||||
brightness = b;
|
||||
|
||||
if (regenerate)
|
||||
[self regenerateImage];
|
||||
|
||||
[marker setFrame: [self markerRect]];
|
||||
|
||||
[self setNeedsDisplay: YES];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) regenerateImage
|
||||
{
|
||||
NSRect frame = [self bounds];
|
||||
CGFloat cx, cy, cr;
|
||||
|
||||
[image release];
|
||||
image = nil;
|
||||
|
||||
cx = (frame.origin.x + frame.size.width) / 2;
|
||||
cy = (frame.origin.y + frame.size.height) / 2;
|
||||
|
||||
cr = frame.size.width;
|
||||
if (cr > frame.size.height)
|
||||
cr = frame.size.height;
|
||||
|
||||
cr = cr / 2 - 2;
|
||||
|
||||
{
|
||||
NSUInteger width = frame.size.width;
|
||||
NSUInteger height = frame.size.height;
|
||||
NSUInteger bytesPerRow;
|
||||
NSBitmapImageRep *bmp;
|
||||
unsigned char *data;
|
||||
NSUInteger x, y;
|
||||
|
||||
if (width < 1 || height < 1)
|
||||
return;
|
||||
|
||||
bmp = [[NSBitmapImageRep alloc]
|
||||
initWithBitmapDataPlanes: NULL
|
||||
pixelsWide: width
|
||||
pixelsHigh: height
|
||||
bitsPerSample: 8
|
||||
samplesPerPixel: 4
|
||||
hasAlpha: YES
|
||||
isPlanar: NO
|
||||
colorSpaceName: NSCalibratedRGBColorSpace
|
||||
bytesPerRow: 0
|
||||
bitsPerPixel: 32];
|
||||
|
||||
bytesPerRow = [bmp bytesPerRow];
|
||||
data = [bmp bitmapData];
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
uint32_t *row = (uint32_t*)(data + (y * bytesPerRow));
|
||||
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
CGFloat dx, dy, dist;
|
||||
CGFloat h, s, v;
|
||||
CGFloat R, G, B, A;
|
||||
|
||||
dx = x - cx;
|
||||
dy = cy - y; // compensate for flipped coordinates
|
||||
dist = sqrt(dx * dx + dy * dy);
|
||||
|
||||
// calculate h,s,v from x,y
|
||||
{
|
||||
h = atan2(dy, dx) / 2.0 / PI;
|
||||
if (h < 0)
|
||||
h += 1;
|
||||
|
||||
s = dist/cr;
|
||||
if (s > 1)
|
||||
s = 1;
|
||||
|
||||
v = brightness;
|
||||
}
|
||||
|
||||
// caluclate R,G,B from h,s,v
|
||||
{
|
||||
int I = (int)(h * 6);
|
||||
CGFloat V = v;
|
||||
CGFloat S = s;
|
||||
CGFloat F = (h * 6) - I;
|
||||
CGFloat M = V * (1 - S);
|
||||
CGFloat N = V * (1 - S * F);
|
||||
CGFloat K = M - N + V;
|
||||
|
||||
switch (I)
|
||||
{
|
||||
default: R = V; G = K; B = M; break;
|
||||
case 1: R = N; G = V; B = M; break;
|
||||
case 2: R = M; G = V; B = K; break;
|
||||
case 3: R = M; G = N; B = V; break;
|
||||
case 4: R = K; G = M; B = V; break;
|
||||
case 5: R = V; G = M; B = N; break;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate alpha
|
||||
{
|
||||
A = (cr - dist) + 0.5;
|
||||
if (A > 1) A = 1;
|
||||
if (A < 0) A = 0;
|
||||
}
|
||||
|
||||
// premultiply color with alpha
|
||||
R *= A;
|
||||
G *= A;
|
||||
B *= A;
|
||||
|
||||
// store pixel
|
||||
row[x] = ((uint32_t)(255 * R))
|
||||
| (((uint32_t)(255 * G)) << 8)
|
||||
| (((uint32_t)(255 * B)) << 16)
|
||||
| (((uint32_t)(255 * A)) << 24);
|
||||
}
|
||||
}
|
||||
|
||||
image = [[NSImage alloc] initWithSize: frame.size];
|
||||
[image addRepresentation: bmp];
|
||||
[bmp release];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) drawRect: (NSRect)rect
|
||||
{
|
||||
if (nil == image)
|
||||
{
|
||||
[self regenerateImage];
|
||||
}
|
||||
|
||||
[image drawInRect: [self bounds]
|
||||
fromRect: NSZeroRect
|
||||
operation: NSCompositeSourceOver
|
||||
fraction: 1.0];
|
||||
}
|
||||
|
||||
-(BOOL) acceptsFirstMouse: (NSEvent *)theEvent
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) acceptsFirstResponder
|
||||
-(BOOL) acceptsFirstResponder
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) mouseDown: (NSEvent *)theEvent
|
||||
-(void) handleMouseAtPoint: (NSPoint)point
|
||||
{
|
||||
unsigned int eventMask = NSLeftMouseDownMask | NSLeftMouseUpMask
|
||||
| NSLeftMouseDraggedMask;
|
||||
NSPoint point = [self convertPoint: [theEvent locationInWindow]
|
||||
fromView: nil];
|
||||
NSEventType eventType = [theEvent type];
|
||||
NSEvent *presentEvent = theEvent;
|
||||
|
||||
float new_hue, new_saturation;
|
||||
float old_x, old_y;
|
||||
|
||||
NSRect frame = [self bounds];
|
||||
float cx, cy, cr;
|
||||
float dx, dy;
|
||||
CGFloat cx, cy, cr, dx, dy, new_hue, new_saturation;
|
||||
|
||||
cx = (frame.origin.x + frame.size.width) / 2;
|
||||
cy = (frame.origin.y + frame.size.height) / 2;
|
||||
cr = frame.size.width;
|
||||
|
@ -183,79 +337,41 @@
|
|||
cr = frame.size.height;
|
||||
cr = cr / 2 - 2;
|
||||
|
||||
new_hue = hue;
|
||||
new_saturation = saturation;
|
||||
dx = point.x - cx;
|
||||
dy = point.y - cy;
|
||||
|
||||
do
|
||||
new_saturation = sqrt(dx * dx + dy * dy) / cr;
|
||||
if (new_saturation > 1)
|
||||
new_saturation = 1;
|
||||
|
||||
new_hue = atan2(dy, dx) / 2.0 / PI;
|
||||
if (new_hue < 0)
|
||||
new_hue += 1;
|
||||
|
||||
[self setHue: new_hue saturation: new_saturation brightness: brightness];
|
||||
|
||||
if (target)
|
||||
{
|
||||
/* Inner loop that gets and (quickly) handles all events that have
|
||||
already arrived. */
|
||||
while (theEvent && eventType != NSLeftMouseUp)
|
||||
{
|
||||
/* Note the event here. Don't do any expensive handling. */
|
||||
presentEvent = theEvent;
|
||||
|
||||
theEvent = [NSApp nextEventMatchingMask: eventMask
|
||||
untilDate: [NSDate distantPast] /* Only get events that have arrived */
|
||||
inMode: NSEventTrackingRunLoopMode
|
||||
dequeue: YES];
|
||||
eventType = [theEvent type];
|
||||
}
|
||||
|
||||
/* No more events right now. Do expensive handling, like drawing,
|
||||
* here.
|
||||
*/
|
||||
point = [self convertPoint: [presentEvent locationInWindow]
|
||||
fromView: nil];
|
||||
|
||||
dx = point.x - cx;
|
||||
dy = point.y - cy;
|
||||
|
||||
new_saturation = dx * dx + dy * dy;
|
||||
new_saturation = sqrt(new_saturation);
|
||||
new_saturation /= cr;
|
||||
if (new_saturation > 1)
|
||||
new_saturation = 1;
|
||||
|
||||
new_hue = atan2(dy, dx);
|
||||
new_hue = new_hue / 2.0 / PI;
|
||||
if (new_hue < 0)
|
||||
new_hue += 1;
|
||||
|
||||
if (new_hue != hue || new_saturation != saturation)
|
||||
{
|
||||
old_x = cos(hue * 2 * PI) * saturation * cr + cx;
|
||||
old_y = sin(hue * 2 * PI) * saturation * cr + cy;
|
||||
|
||||
hue = new_hue;
|
||||
saturation = new_saturation;
|
||||
|
||||
[self lockFocus];
|
||||
[self drawRect: NSMakeRect(old_x - 3, old_y - 3, 6, 6)];
|
||||
[self drawRect: NSMakeRect(point.x - 3, point.y - 3, 6, 6)];
|
||||
[self unlockFocus];
|
||||
[_window flushWindow];
|
||||
|
||||
if (target)
|
||||
[target performSelector: action withObject: self];
|
||||
}
|
||||
|
||||
/*
|
||||
* If our current event is actually the mouse up (perhaps the inner
|
||||
* loop got to this point) we want to update with the last info and
|
||||
* then quit.
|
||||
*/
|
||||
if (eventType == NSLeftMouseUp)
|
||||
break;
|
||||
|
||||
/* Get the next event, blocking if necessary. */
|
||||
theEvent = [NSApp nextEventMatchingMask: eventMask
|
||||
untilDate: nil /* No limit, block until we get an event. */
|
||||
inMode: NSEventTrackingRunLoopMode
|
||||
dequeue: YES];
|
||||
eventType = [theEvent type];
|
||||
} while (eventType != NSLeftMouseUp);
|
||||
[target performSelector: action withObject: self];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) mouseDown: (NSEvent *)theEvent
|
||||
{
|
||||
if ([theEvent type] == NSLeftMouseDown)
|
||||
{
|
||||
[self handleMouseAtPoint: [self convertPoint: [theEvent locationInWindow] fromView: nil]];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) mouseDragged: (NSEvent *)theEvent
|
||||
{
|
||||
if ([theEvent type] == NSLeftMouseDragged)
|
||||
{
|
||||
[self handleMouseAtPoint: [self convertPoint: [theEvent locationInWindow] fromView: nil]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
|
|
@ -207,6 +207,7 @@ static int _gs_gui_color_picker_mode = NSRGBModeColorPanel;
|
|||
- (id) _initWithoutGModel
|
||||
{
|
||||
NSRect contentRect = {{352, 519}, {200, 270}};
|
||||
NSSize maxContentSize = {500, 675};
|
||||
NSRect topViewRect = {{0, 0}, {200, 270}};
|
||||
NSRect magnifyRect = {{4, 230}, {50, 36}};
|
||||
NSRect wellRect = {{58, 230}, {138, 36}};
|
||||
|
@ -232,7 +233,8 @@ static int _gs_gui_color_picker_mode = NSRGBModeColorPanel;
|
|||
screen: nil];
|
||||
[self setTitle: _(@"Colors")];
|
||||
[self setBecomesKeyOnlyIfNeeded: YES];
|
||||
|
||||
[self setContentMinSize: contentRect.size];
|
||||
[self setContentMaxSize: maxContentSize];
|
||||
v = [self contentView];
|
||||
|
||||
_topView = [[NSView alloc] initWithFrame: topViewRect];
|
||||
|
|
Loading…
Reference in a new issue