/XeeView.m
Objective C | 690 lines | 484 code | 171 blank | 35 comment | 69 complexity | 02580cebcbed85d1bea386d59d7eb3e2 MD5 | raw file
1#import "XeeView.h" 2#import "XeeImage.h" 3#import "XeeController.h" 4#import "XeeTool.h" 5#import "XeeGraphicsStuff.h" 6#import "CSKeyboardShortcuts.h" 7 8#import <OpenGL/GL.h> 9#import <OpenGL/GLu.h> 10#import <Carbon/Carbon.h> 11 12 13 14GLuint make_resize_texture(); 15 16@interface NSEvent (DeviceDelta) 17-(float)deviceDeltaX; 18-(float)deviceDeltaY; 19@end 20 21 22 23@implementation XeeView 24 25-(id)initWithFrame:(NSRect)frameRect 26{ 27 /*NSOpenGLPixelFormatAttribute attrs[]={ 28 NSOpenGLPFANoRecovery, 29 NSOpenGLPFADoubleBuffer, 30 //NSOpenGLPFAFullScreen, 31 NSOpenGLPFAAccelerated, 32// NSOpenGLPFASampleBuffers,1, 33// NSOpenGLPFASamples,1, 34 (NSOpenGLPixelFormatAttribute)0}; 35 NSOpenGLPixelFormat *format=[[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease]; 36*/ 37 if(self=[super initWithFrame:frameRect/* pixelFormat:format*/]) 38 { 39 image=nil; 40 tool=nil; 41 42 invalidated=NO; 43 44 drawresize=NO; 45 lowquality=NO; 46 inside=NO; 47 clicking=NO; 48 49 scrolltimer=nil; 50 up=down=left=right=NO; 51 52 delegate=nil; 53 54 NSRect bounds=[self bounds]; 55 width=bounds.size.width; 56 height=bounds.size.height; 57 58 [[self openGLContext] makeCurrentContext]; 59 resizetex=make_resize_texture(); 60 61 GLint val=1; 62 [[self openGLContext] setValues:&val forParameter:NSOpenGLCPSwapInterval]; 63 64 [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; 65 } 66 return self; 67} 68 69-(void)dealloc 70{ 71 [[self openGLContext] makeCurrentContext]; 72 glDeleteTextures(1,&resizetex); 73 74 [tool release]; 75 76 [image setAnimating:NO]; 77 [image setDelegate:nil]; 78 [image release]; 79 [scrolltimer release]; 80 81 [super dealloc]; 82} 83 84-(void)awakeFromNib 85{ 86 [[self window] setAcceptsMouseMovedEvents:YES]; 87} 88 89 90-(BOOL)acceptsFirstResponder { return YES; } 91 92-(BOOL)isOpaque { return YES; } 93 94-(BOOL)isFlipped { return YES; } 95 96 97 98-(void)reshape 99{ 100 [[self openGLContext] makeCurrentContext]; 101 [[self openGLContext] update]; 102 103 NSPoint focus=[self focus]; 104 105 NSRect bounds=[self bounds]; 106 width=bounds.size.width; 107 height=bounds.size.height; 108 109 invalidated=YES; 110 111 [self setFocus:focus]; 112} 113 114 115 116-(void)drawRect:(NSRect)rect 117{ 118 [[self openGLContext] makeCurrentContext]; 119 120 NSRect imgrect=[self imageRect]; 121 NSRect bounds; 122 123 if(invalidated||!image) // do a full update 124 { 125 invalidated=NO; 126 bounds=imgrect; 127 128 NSString *key; 129 if([[self window] isKindOfClass:[XeeFullScreenWindow class]]) key=@"fullScreenBackground"; 130 else key=@"windowBackground"; 131 132 NSColor *clear=[NSUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] dataForKey:key]]; 133 [clear glSetForClear]; 134 135 glClear(GL_COLOR_BUFFER_BIT); 136 } 137 else // do a partial update while loading an image 138 { 139 bounds=[image updatedAreaInRect:imgrect]; 140 } 141 142 // clip bounds to view 143 bounds=NSIntersectionRect(bounds,NSMakeRect(0,0,width,height)); 144 145 // setup partial view 146 glViewport(bounds.origin.x,height-bounds.origin.y-bounds.size.height,bounds.size.width,bounds.size.height); 147 glMatrixMode(GL_PROJECTION); 148 glLoadIdentity(); 149 gluOrtho2D(bounds.origin.x,bounds.origin.x+bounds.size.width,bounds.origin.y+bounds.size.height,bounds.origin.y); 150 glMatrixMode(GL_MODELVIEW); 151 152 if(image) [image drawInRect:imgrect bounds:bounds lowQuality:lowquality]; 153 154 [tool draw]; 155 156 if(drawresize) [self drawResizeHandle]; 157 158 //[[self openGLContext] flushBuffer]; 159 glFlush(); 160} 161 162 163 164-(void)drawResizeHandle 165{ 166 // setup a full view 167 glViewport(0,0,width,height); 168 glMatrixMode(GL_PROJECTION); 169 glLoadIdentity(); 170 gluOrtho2D(0,width,height,0); 171 glMatrixMode(GL_MODELVIEW); 172 173 // draw resize handle 174 glBindTexture(GL_TEXTURE_2D,resizetex); 175 176 glEnable(GL_BLEND); 177 glDisable(GL_TEXTURE_RECTANGLE_EXT); 178 glEnable(GL_TEXTURE_2D); 179 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); 180 181 float x1=width-16; 182 float y1=height-16; 183 float x2=width; 184 float y2=height; 185 186 glBegin(GL_QUADS); 187 glTexCoord2f(0,0); 188 glVertex2f(x1,y1); 189 glTexCoord2f(1,0); 190 glVertex2f(x2,y1); 191 glTexCoord2f(1,1); 192 glVertex2f(x2,y2); 193 glTexCoord2f(0,1); 194 glVertex2f(x1,y2); 195 glEnd(); 196} 197 198 199 200-(void)keyDown:(NSEvent *)event 201{ 202 CSAction *action=[[CSKeyboardShortcuts defaultShortcuts] actionForEvent:event ignoringModifiers:CSShift]; 203 if(action) 204 { 205 NSString *identifier=[action identifier]; 206 if([identifier isEqual:@"scrollUp"]) up=YES; 207 else if([identifier isEqual:@"scrollDown"]) down=YES; 208 else if([identifier isEqual:@"scrollLeft"]) left=YES; 209 else if([identifier isEqual:@"scrollRight"]) right=YES; 210 else [super keyDown:event]; 211 } 212 else [super keyDown:event]; 213 214 if(up||down||left||right) [self startScrolling]; 215} 216 217-(void)keyUp:(NSEvent *)event 218{ 219 CSAction *action=[[CSKeyboardShortcuts defaultShortcuts] actionForEvent:event ignoringModifiers:CSShift]; 220 if(action) 221 { 222 NSString *identifier=[action identifier]; 223 if([identifier isEqual:@"scrollUp"]) up=NO; 224 else if([identifier isEqual:@"scrollDown"]) down=NO; 225 else if([identifier isEqual:@"scrollLeft"]) left=NO; 226 else if([identifier isEqual:@"scrollRight"]) right=NO; 227 else [super keyUp:event]; 228 } 229 else [super keyUp:event]; 230 231 if(!up&&!down&&!left&&!right) [self stopScrolling]; 232} 233 234-(void)mouseDown:(NSEvent *)event 235{ 236 NSPoint pos=[self convertPoint:[event locationInWindow] fromView:nil]; 237 238 clicking=YES; 239 lowquality=YES; 240 241 if([event clickCount]==2) [tool mouseDoubleClickedAt:pos]; 242 else [tool mouseDownAt:pos]; 243 [self updateCursorForMousePosition:pos]; 244} 245 246-(void)mouseUp:(NSEvent *)event 247{ 248 NSPoint pos=[self convertPoint:[event locationInWindow] fromView:nil]; 249 250 clicking=NO; 251 lowquality=NO; 252 253 [tool mouseUpAt:pos]; 254 [self updateCursorForMousePosition:pos]; 255 256 [self invalidate]; 257} 258 259-(void)mouseMoved:(NSEvent *)event 260{ 261 NSPoint pos=[self convertPoint:[event locationInWindow] fromView:nil]; 262 263 [tool mouseMovedTo:pos relative:NSMakePoint([event deltaX],[event deltaY])]; 264 [self updateCursorForMousePosition:pos]; 265 266 if(hidecursor) 267 { 268 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideCursor) object:nil]; 269 [self performSelector:@selector(hideCursor) withObject:nil afterDelay:2]; 270 } 271} 272 273-(void)mouseDragged:(NSEvent *)event 274{ 275 NSPoint pos=[self convertPoint:[event locationInWindow] fromView:nil]; 276 277 [tool mouseDraggedTo:pos relative:NSMakePoint([event deltaX],[event deltaY])]; 278 [self updateCursorForMousePosition:pos]; 279} 280 281-(void)scrollWheel:(NSEvent *)event 282{ 283 if([[NSUserDefaults standardUserDefaults] integerForKey:@"scrollWheelFunction"]==1) 284 { 285 float dx,dy; 286 if(IsSmoothScrollEvent(event)) 287 { 288 dx=[event deviceDeltaX]; 289 dy=[event deviceDeltaY]; 290 } 291 else 292 { 293 dx=[event deltaX]*24; 294 dy=[event deltaY]*24; 295 } 296 297 //NSLog(@"scrollwheel: scrollEvent is %i, %f dx, %f dy, %f dx, %f dy", scrollEvent, dx, dy); 298 299 int old_x=x,old_y=y; 300 x-=dx; 301 y-=dy; 302 [self clampCoords]; 303 if(x!=old_x||y!=old_y) [self invalidateImageAndTool]; 304 } 305} 306 307-(void)xeeImageLoadingProgress:(XeeImage *)msgimage 308{ 309 [self setNeedsDisplay:YES]; 310} 311 312-(void)xeeImageDidChange:(XeeImage *)msgimage; 313{ 314 [self invalidate]; 315 [delegate xeeView:self imageDidChange:msgimage]; 316} 317 318-(void)xeeImageSizeDidChange:(XeeImage *)msgimage; 319{ 320 // cancel tool 321 [delegate xeeView:self imageSizeDidChange:msgimage]; 322} 323 324-(void)xeeImagePropertiesDidChange:(XeeImage *)msgimage; 325{ 326 [delegate xeeView:self imagePropertiesDidChange:msgimage]; 327} 328 329 330 331-(void)invalidate 332{ 333 invalidated=YES; 334 [self setNeedsDisplay:YES]; 335} 336 337-(void)invalidateTool 338{ 339 NSPoint position=[self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:nil]; 340 if(clicking) [tool mouseDraggedTo:position relative:NSMakePoint(0,0)]; 341 else [tool mouseMovedTo:position relative:NSMakePoint(0,0)]; 342 343 [self updateCursorForMousePosition:position]; 344} 345 346-(void)invalidateImageAndTool 347{ 348 [self invalidate]; 349 [self invalidateTool]; 350} 351 352-(void)updateCursorForMousePosition:(NSPoint)pos 353{ 354 BOOL wasinside=inside; 355 356 if(clicking) 357 { 358 inside=YES; 359 } 360 else if(drawresize) 361 { 362 NSRect rect1=[self bounds]; 363 NSRect rect2=[self bounds]; 364 rect1.size.width-=15; 365 rect2.size.height-=15; 366 367 inside=NSPointInRect(pos,rect1)||NSPointInRect(pos,rect2); 368 } 369 else 370 { 371 inside=NSPointInRect(pos,[self bounds]); 372 } 373 374 if(inside) [[tool cursor] set]; 375 else if(wasinside) [[NSCursor arrowCursor] set]; 376} 377 378 379-(void)startScrolling 380{ 381 if(!scrolltimer) 382 { 383 prevtime=XeeGetTime(); 384 scrolltimer=[[NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:@selector(scroll:) userInfo:nil repeats:YES] retain]; 385 386 lowquality=YES; 387 } 388} 389 390-(void)stopScrolling 391{ 392 if(scrolltimer) 393 { 394 [scrolltimer invalidate]; 395 [scrolltimer release]; 396 scrolltimer=nil; 397 398 lowquality=NO; 399 [self invalidate]; 400 } 401} 402 403-(void)scroll:(NSTimer *)timer 404{ 405 double time=XeeGetTime(); 406 double dt=time-prevtime; 407 prevtime=time; 408 409 double mult=1.0; 410 411 if(GetCurrentKeyModifiers()&shiftKey) mult=3.0; 412 413 int delta=1500.0*mult*dt; 414 415 int old_x=x,old_y=y; 416 417 if(left) x-=delta; 418 if(right) x+=delta; 419 if(up) y-=delta; 420 if(down) y+=delta; 421 422 [self clampCoords]; 423 if(x!=old_x||y!=old_y) [self invalidateImageAndTool]; 424} 425 426 427 428-(NSPoint)focus 429{ 430 NSPoint focus; 431 432 if(imgwidth<width) focus.x=width/2; 433 else focus.x=x+width/2; 434 435 if(imgheight<height) focus.y=height/2; 436 else focus.y=y+height/2; 437 438 return focus; 439} 440 441-(void)setFocus:(NSPoint)focus 442{ 443 int old_x=x,old_y=y; 444 445 x=focus.x-width/2; 446 y=focus.y-height/2; 447 [self clampCoords]; 448 449 if(x!=old_x||y!=old_y) [self invalidateImageAndTool]; 450} 451 452-(void)clampCoords 453{ 454 if(x>imgwidth-width) x=imgwidth-width; 455 if(y>imgheight-height) y=imgheight-height; 456 if(x<0) x=0; 457 if(y<0) y=0; 458} 459 460 461 462-(NSRect)imageRect 463{ 464 if(image) 465 { 466 int draw_x,draw_y; 467 if(imgwidth<width) draw_x=(width-imgwidth)/2; 468 else draw_x=-x; 469 470 if(imgheight<height) draw_y=(height-imgheight)/2; 471 else draw_y=-y; 472 473 return NSMakeRect(draw_x,draw_y,imgwidth,imgheight); 474 } 475 else return NSZeroRect; 476} 477 478-(XeeMatrix)imageToViewTransformMatrix 479{ 480 return XeeTransformRectToRectMatrix(NSMakeRect(0,0,[image width],[image height]),[self imageRect]); 481} 482 483-(XeeMatrix)viewToImageTransformMatrix 484{ 485 return XeeTransformRectToRectMatrix([self imageRect],NSMakeRect(0,0,[image width],[image height])); 486} 487 488-(id)delegate { return delegate; } 489 490-(XeeImage *)image { return image; } 491 492-(XeeTool *)tool { return tool; } 493 494 495 496-(void)setDelegate:(id)newdelegate 497{ 498 delegate=newdelegate; 499} 500 501-(void)setImage:(XeeImage *)img 502{ 503 [image setAnimating:NO]; 504 [image setDelegate:nil]; 505 506// cancel tool 507 508 if(image!=img) 509 { 510 [image release]; 511 image=[img retain]; 512 } 513 514 [image setDelegate:self]; 515 [image setAnimatingDefault]; 516 517 [self invalidate]; 518} 519 520-(void)setTool:(XeeTool *)newtool 521{ 522 if(newtool==tool) return; 523 XeeTool *oldtool=tool; 524 525 tool=[newtool retain]; 526 527 [oldtool end]; 528 [oldtool release]; 529 530 [newtool begin]; 531 532 [self invalidateImageAndTool]; 533} 534 535-(void)setImageSize:(NSSize)size 536{ 537 NSPoint focus=[self focus]; 538 539 int oldwidth=imgwidth; 540 int oldheight=imgheight; 541 542 imgwidth=size.width; 543 imgheight=size.height; 544 545 focus.x*=(float)imgwidth/(float)oldwidth; 546 focus.y*=(float)imgheight/(float)oldheight; 547 548 [self setFocus:focus]; 549 550 [self invalidate]; 551 [self performSelector:@selector(invalidateTool) withObject:nil afterDelay:0]; 552} 553 554-(void)setDrawResizeCorner:(BOOL)draw { drawresize=draw; } 555 556-(void)setCursorShouldHide:(BOOL)shouldhide 557{ 558 [NSCursor setHiddenUntilMouseMoves:shouldhide]; 559 hidecursor=shouldhide; 560 if(!hidecursor) [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideCursor) object:nil]; 561} 562 563 564-(void)hideCursor 565{ 566 [NSCursor setHiddenUntilMouseMoves:YES]; 567} 568 569static const void *XeeCopyGLGetBytePointer(void *bitmap) { return bitmap; } 570 571-(void)copyGLtoQuartz 572{ 573 int bytesperrow=width*4; 574 void *bitmap=malloc(bytesperrow*height); 575 576 [[self openGLContext] makeCurrentContext]; 577 578 glFinish(); // finish any pending OpenGL commands 579 glPushAttrib(GL_ALL_ATTRIB_BITS); 580 581 //glPixelStorei(GL_PACK_SWAP_BYTES, 0); 582 //glPixelStorei(GL_PACK_LSB_FIRST, 0); 583 //glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); 584 glPixelStorei(GL_PACK_ALIGNMENT,4); // force 4-byte alignment from RGBA framebuffer 585 glPixelStorei(GL_PACK_ROW_LENGTH,0); 586 glPixelStorei(GL_PACK_SKIP_PIXELS,0); 587 glPixelStorei(GL_PACK_SKIP_ROWS,0); 588 589 #ifdef __BIG_ENDIAN__ 590 glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8_REV,bitmap); 591 #else 592 glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8,bitmap); 593 #endif 594 glPopAttrib(); 595 596 [self lockFocus]; 597 598 CGDataProviderDirectAccessCallbacks callbacks={XeeCopyGLGetBytePointer,NULL,NULL,NULL}; 599 CGDataProviderRef provider=CGDataProviderCreateDirectAccess(bitmap,bytesperrow*height,&callbacks); 600 CGColorSpaceRef cs=CGColorSpaceCreateDeviceRGB(); 601 CGImageRef cgimage=CGImageCreate(width,height,8,32,bytesperrow,cs,kCGImageAlphaNoneSkipFirst,provider,NULL,NO,kCGRenderingIntentDefault); 602 603 CGContextRef gc=[[NSGraphicsContext currentContext] graphicsPort]; 604 CGContextDrawImage(gc,CGRectMake(0,0,width,height),cgimage); 605 606 CGImageRelease(cgimage); 607 CGDataProviderRelease(provider); 608 CGColorSpaceRelease(cs); 609 free(bitmap); 610 611 [self unlockFocus]; 612 613 [[self window] flushWindow]; 614} 615 616/*-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender 617{ 618 return NSDragOperationGeneric; 619} 620 621-(BOOL)performDragOperation:(id <NSDraggingInfo>)sender 622{ 623 NSPasteboard *pboard=[sender draggingPasteboard]; 624 NSArray *files=[pboard propertyListForType:NSFilenamesPboardType]; 625 626 [controller loadImage:[files objectAtIndex:0]]; 627 628 return YES; 629}*/ 630 631@end 632 633 634 635@implementation NSObject (XeeViewDelegate) 636 637-(void)xeeView:(XeeView *)view imageDidChange:(XeeImage *)image {} 638-(void)xeeView:(XeeView *)view imageSizeDidChange:(XeeImage *)image {} 639-(void)xeeView:(XeeView *)view imagePropertiesDidChange:(XeeImage *)image {} 640 641@end 642 643 644 645#define C0 0x00000000 646#define C1 0x25000000 647#define C2 0x8a222222 648#define C3 0x8bd5d5d5 649 650static uint32_t resize_data[256]= 651{ 652 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0, 653 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0, 654 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0, 655 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0, 656 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C1,C0, 657 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C1,C2,C0, 658 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C1,C2,C3,C0, 659 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C1,C2,C3,C0,C0, 660 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C1,C2,C3,C0,C1,C0, 661 C0,C0,C0,C0,C0,C0,C0,C0,C0,C1,C2,C3,C0,C1,C2,C0, 662 C0,C0,C0,C0,C0,C0,C0,C0,C1,C2,C3,C0,C1,C2,C3,C0, 663 C0,C0,C0,C0,C0,C0,C0,C1,C2,C3,C0,C1,C2,C3,C0,C0, 664 C0,C0,C0,C0,C0,C0,C1,C2,C3,C0,C1,C2,C3,C0,C1,C0, 665 C0,C0,C0,C0,C0,C1,C2,C3,C0,C1,C2,C3,C0,C1,C2,C0, 666 C0,C0,C0,C0,C1,C2,C3,C0,C1,C2,C3,C0,C1,C2,C3,C0, 667 C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0,C0, 668}; 669 670GLuint make_resize_texture() 671{ 672 GLuint tex; 673 glGenTextures(1,&tex), 674 glBindTexture(GL_TEXTURE_2D,tex); 675 676 glPixelStorei(GL_UNPACK_ROW_LENGTH,16); 677 glPixelStorei(GL_UNPACK_ALIGNMENT,1); 678 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE,GL_TRUE); 679 glPixelStorei(GL_UNPACK_SKIP_PIXELS,0); 680 glPixelStorei(GL_UNPACK_SKIP_ROWS,0); 681 glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,16,16,0,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8_REV,resize_data); 682 683 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); 684 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); 685 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); 686 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); 687 glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); 688 689 return tex; 690}