/XeeView.m

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