PageRenderTime 172ms CodeModel.GetById 15ms app.highlight 141ms RepoModel.GetById 7ms app.codeStats 0ms

/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
  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}