[[NSColor clearColor] set];
NSRectFillUsingOperation(win, NSCompositeCopy);
I also tried NSCompositeClear, NSCompositeDestinationOut, but none of them worked as I wish. I want to erase the background in the blue clip window, so its content will be brighter. I've learned Cocoa and obj-c for merely a week, so maybe I misunderstood something about it. I really want to know how to realize this in Cocoa Framework?
#import "ImageCutterView.h"
#define CONTROL_POINT_SIZE 6
#define CLIP_WINDOW_MIN_SIZE 100
#define CLIP_WINDOW_MAX_SIZE 200
@implementation ImageCutterView {
NSRect win;
NSRect controlPoints[4];
CGFloat gridLinePattern[2];
NSBezierPath* gridLines[4];
NSPoint p1,p2;
BOOL startDragging;
BOOL topLeftDragging;
BOOL topRightDragging;
BOOL bottomLeftDragging;
BOOL bottomRightDragging;
NSColor *borderColor;
NSColor *gridColor;
NSColor *controlPointBorderColor;
NSColor *controlPointFillColor;
NSColor *backgroundColor;
CGFloat distance, preDistance;
NSCursor *diagonal1, *diagonal2;
}
-(void)calculateControlPolints{
CGFloat w, h;
w = win.size.width;
h = win.size.height;
CGFloat ox,oy;
ox = win.origin.x;
oy = win.origin.y;
controlPoints[0].origin.x = ox - CONTROL_POINT_SIZE/2;
controlPoints[0].origin.y = oy - CONTROL_POINT_SIZE/2;
controlPoints[0].size.width = CONTROL_POINT_SIZE;
controlPoints[0].size.height = CONTROL_POINT_SIZE;
controlPoints[1].origin.x = ox - CONTROL_POINT_SIZE/2;
controlPoints[1].origin.y = oy + h - CONTROL_POINT_SIZE/2;
controlPoints[1].size.width = CONTROL_POINT_SIZE;
controlPoints[1].size.height = CONTROL_POINT_SIZE;
controlPoints[2].origin.x = ox + w - CONTROL_POINT_SIZE/2;
controlPoints[2].origin.y = oy + h - CONTROL_POINT_SIZE/2;
controlPoints[2].size.width = CONTROL_POINT_SIZE;
controlPoints[2].size.height = CONTROL_POINT_SIZE;
controlPoints[3].origin.x = ox + w - CONTROL_POINT_SIZE/2;
controlPoints[3].origin.y = oy - CONTROL_POINT_SIZE/2;
controlPoints[3].size.width = CONTROL_POINT_SIZE;
controlPoints[3].size.height = CONTROL_POINT_SIZE;
}
-(void)calculateGridLines{
CGFloat w, h, w3, h3;
w = win.size.width;
h = win.size.height;
w3 = w/3;
h3 = h/3;
CGFloat ox,oy;
ox = win.origin.x;
oy = win.origin.y;
gridLines[0] = [NSBezierPath bezierPath];
[gridLines[0] setLineDash:gridLinePattern count:2 phase:0.0];
[gridLines[0] moveToPoint:NSMakePoint(ox + w3, oy + h)];
[gridLines[0] lineToPoint:NSMakePoint(ox + w3, oy)];
gridLines[1] = [NSBezierPath bezierPath];
[gridLines[1] setLineDash:gridLinePattern count:2 phase:0.0];
[gridLines[1] moveToPoint:NSMakePoint(ox + w3*2, oy + h)];
[gridLines[1] lineToPoint:NSMakePoint(ox + w3*2, oy)];
gridLines[2] = [NSBezierPath bezierPath];
[gridLines[2] setLineDash:gridLinePattern count:2 phase:0.0];
[gridLines[2] moveToPoint:NSMakePoint(ox, oy + h3*2)];
[gridLines[2] lineToPoint:NSMakePoint(ox + w, oy + h3*2)];
gridLines[3] = [NSBezierPath bezierPath];
[gridLines[3] setLineDash:gridLinePattern count:2 phase:0.0];
[gridLines[3] moveToPoint:NSMakePoint(ox, oy + h3)];
[gridLines[3] lineToPoint:NSMakePoint(ox +w, oy + h3)];
}
-(NSCursor *)getSystemCursorByName:(NSString *)cursorName {
NSString *cursorPath = [@"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors" stringByAppendingPathComponent:cursorName];
NSImage *image = [[NSImage alloc] initByReferencingFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]];
NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"info.plist"]];
NSCursor *cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue], [[info valueForKey:@"hoty"] doubleValue])];
return cursor;
}
-(void)awakeFromNib {
_showGrid = YES;
CGFloat cx,cy;
cx = self.frame.size.width / 2;
cy = self.frame.size.height / 2;
win.size.width = (CLIP_WINDOW_MIN_SIZE + CLIP_WINDOW_MAX_SIZE) / 2;
win.size.height = (CLIP_WINDOW_MIN_SIZE + CLIP_WINDOW_MAX_SIZE) / 2;
win.origin = NSMakePoint(cx - win.size.width/2, cy - win.size.height/2);
gridLinePattern[0] = 6.0;
gridLinePattern[1] = 4.0;
startDragging = NO;
topLeftDragging = NO;
topRightDragging = NO;
bottomLeftDragging = NO;
bottomRightDragging = NO;
borderColor = [NSColor colorWithSRGBRed:(0x44 + 1.0)/256 green:(0xce + 1.0)/256 blue:(0xf6 + 1.0)/256 alpha:0.8f];
gridColor = [NSColor colorWithSRGBRed:(0x44 + 1.0)/256 green:(0xce + 1.0)/256 blue:(0xf6 + 1.0)/256 alpha:0.8f];
controlPointBorderColor = [NSColor colorWithSRGBRed:(0x44 + 1.0)/256 green:(0xce + 1.0)/256 blue:(0xf6 + 1.0)/256 alpha:0.8f];
controlPointFillColor = [NSColor whiteColor];
backgroundColor = [NSColor colorWithSRGBRed:0.7 green:0.7 blue:0.7 alpha:0.4f];
//backgroundColor = [NSColor blackColor];
[self calculateControlPolints];
[self calculateGridLines];
diagonal1 = [self getSystemCursorByName:@"resizenorthwestsoutheast"];
diagonal2 = [self getSystemCursorByName:@"resizenortheastsouthwest"];
};
//-(BOOL)isOpaque{
// return YES;
//}
- (void)resetCursorRects
{
[super resetCursorRects];
if (diagonal1) {
[self addCursorRect:controlPoints[1] cursor: diagonal1];
[self addCursorRect:controlPoints[3] cursor: diagonal1];
}
if(diagonal2){
[self addCursorRect:controlPoints[0] cursor: diagonal2];
[self addCursorRect:controlPoints[2] cursor: diagonal2];
}
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
NSGraphicsContext* ctx = [NSGraphicsContext currentContext];
[ctx setShouldAntialias:NO];
[NSBezierPath setDefaultLineWidth:0.0];
[backgroundColor set];
[NSBezierPath fillRect:dirtyRect];
/*
[ctx saveGraphicsState];
//[ctx setCompositingOperation:NSCompositeDestinationOut];
//[ctx setCompositingOperation:NSCompositeDestinationOver];
[ctx setCompositingOperation:NSCompositeCopy];
[[NSColor clearColor] set];
[NSBezierPath fillRect:win];
[ctx restoreGraphicsState];*/
[[NSColor clearColor] set];
//[[NSColor colorWithSRGBRed:1.0f green:1.0f blue:1.0f alpha:0.0f] set];
NSRectFillUsingOperation(win, NSCompositeCopy);
[[NSColor colorWithSRGBRed:(0x44 + 1.0)/256 green:(0xce + 1.0)/256 blue:(0xf6 + 1.0)/256 alpha:0.8f] set];
[NSBezierPath strokeRect:win];
if (self.showGrid) {
for (int i=0; i<4; i++) {
[gridLines[i] stroke];
}
}
[[NSColor colorWithSRGBRed:(0x44 + 1.0)/256 green:(0xce + 1.0)/256 blue:(0xf6 + 1.0)/256 alpha:1.0f] set];
for (int i=0; i<4; i++) {
[NSBezierPath strokeRect:controlPoints[i]];
}
}
-(void)mouseDown:(NSEvent *)theEvent {
p1 = [theEvent locationInWindow];
NSPoint tp = [self convertPoint:p1 fromView:nil];
if (NSPointInRect(tp, controlPoints[0])){
bottomLeftDragging = YES;
} else if (NSPointInRect(tp, controlPoints[1])){
topLeftDragging = YES;
p1 = NSMakePoint(win.origin.x, win.origin.y + win.size.height);
preDistance = ((tp.x-p1.x) - (tp.y - p1.y)) / 2;
} else if (NSPointInRect(tp, controlPoints[2])){
topRightDragging = YES;
} else if (NSPointInRect(tp, controlPoints[3])){
bottomRightDragging = YES;
} else if (NSPointInRect(tp, win)) {
startDragging = YES;
}
}
-(void)mouseDragged:(NSEvent *)theEvent {
p2 = [theEvent locationInWindow];
if (startDragging) {
NSAffineTransform *transfrom = [NSAffineTransform transform];
[transfrom translateXBy:p2.x - p1.x yBy:p2.y - p1.y];
win.origin = [transfrom transformPoint:win.origin];
for(int i=0;i<4;i++){
controlPoints[i].origin = [transfrom transformPoint:controlPoints[i].origin];
}
for (int i=0; i<4; i++) {
[gridLines[i] transformUsingAffineTransform:transfrom];
}
[self setNeedsDisplay:YES];
p1 = p2;
} else if (topLeftDragging) {
p2 = [self convertPoint:p2 fromView:nil];
distance = ((p2.x-p1.x) - (p2.y - p1.y)) / 2;
CGFloat dSize = distance - preDistance;
CGFloat newSize = win.size.width - dSize;
if (newSize >= CLIP_WINDOW_MIN_SIZE && newSize <= CLIP_WINDOW_MAX_SIZE){
preDistance = distance;
win.size.width = newSize;
win.size.height = newSize;
win.origin.x += dSize;
[self calculateControlPolints];
[self calculateGridLines];
[self setNeedsDisplay:YES];
}
} else if (topRightDragging) {
} else if (bottomLeftDragging) {
} else if (bottomRightDragging) {
}
}
- (void)mouseUp:(NSEvent *)theEvent {
if (startDragging){
startDragging = NO;
} else if (topLeftDragging){
topLeftDragging = NO;
} else if (topRightDragging){
topRightDragging = NO;
} else if (bottomLeftDragging) {
bottomLeftDragging = NO;
} else if (bottomRightDragging) {
bottomRightDragging = NO;
}
[self resetCursorRects];
}
@end
I found a problem same as mine asked 13 years ago. http://www.cocoabuilder.com/archive/cocoa/47367-clearing-an-nsview.html
[Update 1] I always wonder why NSCompositeClear does NOT clear the area of my source properly (don't show me a black hole). So I came up with an idea that the weird things happened because I draw to the screen. What if I draw to a NSGraphicsContext of a bitmap image?
@implementation FooView {
NSBitmapImageRep *offscreenRep;
NSRect imgRect;
NSSize imgSize;
}
-(void)awakeFromNib{
imgRect = NSMakeRect(0.0, 0.0, 100.0, 100.0);
imgSize = imgRect.size;
offscreenRep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:imgSize.width
pixelsHigh:imgSize.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bitmapFormat:NSAlphaFirstBitmapFormat
bytesPerRow:0
bitsPerPixel:0];
}
- (void)drawRect:(NSRect)dirtyRect {
// set offscreen context
NSGraphicsContext *g = [NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:g];
//draw something here
NSBezierPath *circle = [NSBezierPath bezierPathWithOvalInRect:NSMakeRect(20, 20, 50, 50)];
NSBezierPath *rectangle = [NSBezierPath bezierPathWithRect:NSMakeRect(5, 5, 40, 40)];
[[NSColor greenColor] set];
[circle fill];
[g setCompositingOperation:NSCompositeClear];
[[NSColor redColor] set];
[rectangle fill];
// done drawing, so set the current context back to what it was
[NSGraphicsContext restoreGraphicsState];
// create an NSImage and add the rep to it
NSImage *img = [[NSImage alloc] initWithSize:imgSize];
[img addRepresentation:offscreenRep];
// then go on to save or view the NSImage
[img drawAtPoint:NSMakePoint(0.0, 0.0)
fromRect: NSMakeRect(0.0, 0.0, 100.0, 100.0)
operation: NSCompositeSourceOver
fraction: 1.0];
}
...
I copied the drawing code from (Mac OS X: Drawing into an offscreen NSGraphicsContext using CGContextRef C functions has no effect. Why?) and made some changes. It worked! So it proved my assumption above. However, there are 2 new questions now.
- Why it is different between drawing to a screen and an image?
- The code above of drawing to an image is not efficient. The content flashes when I move the window.
[Update 2] Question 1: Maybe there are no differences. "layer 0": black, "layer 1": window content, "layer 2": my image layer. When I draw to screen, I actually draw on layer 1. When I draw to an image, I actually on my custom "layer" 2.
NSView
as opaque? – iCaramba