前回報告した ProjectCenter を使って作成した CoreGraphics のプログラムです。
実行結果
起動時の画面 (Draw1)
Draw2(メニューから Draw → Draw2 を選択)
Draw3
Draw4
プログラム
(プロジェクト生成時以降、編集したプログラム)
Headers
AppController.h
#ifndef _PCAPPPROJ_APPCONTROLLER_H #define _PCAPPPROJ_APPCONTROLLER_H #import <Foundation/Foundation.h> #import <AppKit/AppKit.h> #import "QEB2OpenGLView.h" @interface AppController : NSObject { NSWindow *window; QEB2OpenGLView * openGLView; } + (void) initialize; - (id) init; - (void) dealloc; - (void) awakeFromNib; - (void) applicationDidFinishLaunching: (NSNotification *)aNotif; - (BOOL) applicationShouldTerminate: (id)sender; - (void) applicationWillTerminate: (NSNotification *)aNotif; - (BOOL) application: (NSApplication *)application openFile: (NSString *)fileName; - (void) showPrefPanel: (id)sender; - (void) drawAction1: (id)sender; - (void) drawAction2: (id)sender; - (void) drawAction3: (id)sender; - (void) drawAction4: (id)sender; @end #endif
QEB2OpenGLView.h (新しく生成)
#import <GL/gl.h> #import <GL/glu.h> #import <cairo/cairo.h> #import <AppKit/AppKit.h> #import <Foundation/Foundation.h> #import <CoreGraphics/CoreGraphics.h> @interface QEB2OpenGLView : NSOpenGLView { cairo_surface_t * _cairoSurface; CGContextRef _opalContext; GLuint _texture; CGRect rootRect; } - (void) draw1; - (void) draw2; - (void) draw3; - (void) draw4; - (void) reDraw; - (void) clearContextWithRect:(CGRect)rect; @end
Classes
AppController.m
#import "AppController.h" #import <Foundation/Foundation.h> #import <AppKit/AppKit.h> @implementation AppController + (void) initialize { NSMutableDictionary *defaults = [NSMutableDictionary dictionary]; /* * Register your app's defaults here by adding objects to the * dictionary, eg * * [defaults setObject:anObject forKey:keyForThatObject]; * */ [[NSUserDefaults standardUserDefaults] registerDefaults: defaults]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (id) init { if ((self = [super init])) { } return self; } - (void) dealloc { RELEASE (openGLView); RELEASE (window); [super dealloc]; } - (void) awakeFromNib { } - (void) applicationDidFinishLaunching: (NSNotification *)aNotif { openGLView = [[QEB2OpenGLView alloc] initWithFrame: [[window contentView] frame] pixelFormat: [QEB2OpenGLView defaultPixelFormat]]; [window setContentView: openGLView]; [openGLView reDraw]; } - (BOOL) applicationShouldTerminate: (id)sender { return YES; } - (void) applicationWillTerminate: (NSNotification *)aNotif { } - (BOOL) application: (NSApplication *)application openFile: (NSString *)fileName { return NO; } - (void) showPrefPanel: (id)sender { } - (void) drawAction1: (id)sender { NSLog(@"drawAction1"); [openGLView draw1]; } - (void) drawAction2: (id)sender { NSLog(@"drawAction2"); [openGLView draw2]; } - (void) drawAction3: (id)sender { NSLog(@"drawAction3"); [openGLView draw3]; } - (void) drawAction4: (id)sender { NSLog(@"drawAction4"); [openGLView draw4]; } @end
QEB2OpenGLView.m (新しく生成)
#import "QEB2OpenGLView.h" #define TEXTURE_TARGET GL_TEXTURE_2D #define PI 3.14159265358979323846 CGContextRef opal_new_CGContext(cairo_surface_t *target, CGSize device_size); void drawRandomPaths(CGContextRef context, int w, int h) { for (int i = 0; i < 20; i++) { int numberOfSegments = rand() % 8; int j; CGFloat sx, sy; CGContextBeginPath(context); sx = rand()%w; sy = rand()%h; CGContextMoveToPoint(context, rand()%w, rand()%h); for (j = 0; j < numberOfSegments; j++) { if (j % 2) { CGContextAddLineToPoint(context, rand()%w, rand()%h); } else { CGContextAddCurveToPoint(context, rand()%w, rand()%h, rand()%w, rand()%h, rand()%h, rand()%h); } } if(i % 2) { CGContextAddCurveToPoint(context, rand()%w, rand()%h, rand()%w, rand()%h, sx, sy); CGContextClosePath(context); CGContextSetRGBFillColor(context, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255); CGContextFillPath(context); } else { CGContextSetLineWidth(context, (rand()%10)+2); CGContextSetRGBStrokeColor(context, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255); CGContextStrokePath(context); } } } @implementation QEB2OpenGLView - (void) draw1 { NSLog(@"GLView draw1"); [self clearContextWithRect:rootRect]; int width = rootRect.size.width; int height = rootRect.size.height; for (int i = 0; i < 20; i++) { if(i % 2) { CGContextSetRGBFillColor(_opalContext, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255); CGContextFillRect(_opalContext, CGRectMake(rand()%width, rand()%height, rand()%width, rand()%height)); } else { CGContextSetLineWidth(_opalContext, (rand()%10)+2); CGContextSetRGBStrokeColor(_opalContext, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255); CGContextStrokeRect(_opalContext, CGRectMake(rand()%width, rand()%height, rand()%width, rand()%height)); } } [self reDraw]; } - (void) draw2 { NSLog(@"GLView draw2"); [self clearContextWithRect:rootRect]; int width = rootRect.size.width; int height = rootRect.size.height; // Draw random circles (some stroked, some filled) for (int i = 0; i < 20; i++) { CGContextBeginPath(_opalContext); CGContextAddArc(_opalContext, rand()%width, rand()%height, rand()%((width>height) ? height : width), 0, 2*PI, 0); CGContextClosePath(_opalContext); if(i % 2) { CGContextSetRGBFillColor(_opalContext, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255); CGContextFillPath(_opalContext); } else { CGContextSetLineWidth(_opalContext, (rand()%10)+2); CGContextSetRGBStrokeColor(_opalContext, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255); CGContextStrokePath(_opalContext); } } [self reDraw]; } - (void) draw3 { NSLog(@"GLView draw3"); [self clearContextWithRect:rootRect]; drawRandomPaths(_opalContext, rootRect.size.width, rootRect.size.height); [self reDraw]; } - (void) draw4 { NSLog(@"GLView draw4"); [self clearContextWithRect:rootRect]; int width = rootRect.size.width; int height = rootRect.size.height; /* Clipping example - draw random path through a circular clip */ CGContextBeginPath(_opalContext); CGContextAddArc(_opalContext, width/2, height/2, ((width>height) ? height : width)/2, 0, 2*PI, 0); CGContextClosePath(_opalContext); CGContextClip(_opalContext); // Draw something into the clip drawRandomPaths(_opalContext, width, height); // Draw an clip path on top as a black stroked circle. CGContextBeginPath(_opalContext); CGContextAddArc(_opalContext, width/2, height/2, ((width>height) ? height : width)/2, 0, 2*PI, 0); CGContextClosePath(_opalContext); CGContextSetLineWidth(_opalContext, 1); CGContextSetRGBStrokeColor(_opalContext, 0, 0, 0, 1); CGContextStrokePath(_opalContext); [self reDraw]; OPContextResetClip(_opalContext); } -(void) reDraw { [self reshape]; } - (void) clearContextWithRect:(CGRect)rect { CGContextSetRGBFillColor(_opalContext, 1, 1, 1, 1); CGContextFillRect(_opalContext, rect); } - (void) prepareOpenGL { [super prepareOpenGL]; int width = [self frame].size.width; int height = [self frame].size.height; _cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); _opalContext = opal_new_CGContext(_cairoSurface, CGSizeMake(width, height)); /* Draw some content into the context */ rootRect = CGRectMake(0, 0, width, height); [self clearContextWithRect:rootRect]; // Draw random rectangles (some stroked some filled) for (int i = 0; i < 20; i++) { if(i % 2) { CGContextSetRGBFillColor(_opalContext, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255); CGContextFillRect(_opalContext, CGRectMake(rand()%width, rand()%height, rand()%width, rand()%height)); } else { CGContextSetLineWidth(_opalContext, (rand()%10)+2); CGContextSetRGBStrokeColor(_opalContext, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255, (CGFloat)(rand()%256)/255); CGContextStrokeRect(_opalContext, CGRectMake(rand()%width, rand()%height, rand()%width, rand()%height)); } } } - (void) dealloc { cairo_surface_finish(_cairoSurface); CGContextRelease(_opalContext); [super dealloc]; } -(void) reshape { NSLog(@"reshaping"); NSRect rect = [self bounds]; glViewport(0, 0, rect.size.width, rect.size.height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, rect.size.width/rect.size.height, 0.2, 7); [self setNeedsDisplay:YES]; } - (void) drawRect: (NSRect)r { [[self openGLContext] makeCurrentContext]; int width = [self frame].size.width; int height = [self frame].size.height; glGenTextures(1, &_texture); glBindTexture(TEXTURE_TARGET, _texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); unsigned char * data = cairo_image_surface_get_data(_cairoSurface); gluBuild2DMipmaps(TEXTURE_TARGET, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); glViewport(0, 0, [self frame].size.width, [self frame].size.height); glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_TEXTURE_2D); glEnable(TEXTURE_TARGET); glBindTexture(TEXTURE_TARGET, _texture); glColor3f(1,1,1); glBegin(GL_QUADS); glTexCoord2f(0.0, 1.0); glVertex2f(-1.0, -1.0); glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, 1.0); glTexCoord2f(1.0, 0.0); glVertex2f(1.0, 1.0); glTexCoord2f(1.0, 1.0); glVertex2f(1.0, -1.0); glEnd(); [[self openGLContext] flushBuffer]; } @end
Supporting Files
GNUmakefile.preamble(変更箇所のみ)
# Additional flags to pass to Objective C compiler ADDITIONAL_OBJCFLAGS += -std=gnu99 # Additional flags to pass to C compiler ADDITIONAL_CFLAGS += -std=gnu99 # Additional GUI libraries to link ADDITIONAL_GUI_LIBS += -lm -lGL -lGLU -lopal -lcairo
Interfaces
(プロジェクト名).gorm
変更は下参照
ProjectCenter と Gorm によるプログラム作成
1 ProjectCenter でプロジェクトを作成
Project Types は Application を選択
ビルドとランを実行
[Main Menu] が表示される。
2 window を追加
Interfaces の (プロジェクト名).gorm をダブルクリック
Gorm が起動する。
Gorm のControls window -> Windows から [Window] を
Resources window の Objects にドロップ
Window(0) インスタンスが生成される。
size, attributes (Visual at launch をチェック)を設定する。
設定を保存する場合
Gorm メインメニュー -> Document -> Save
AppController と Window の接続
Window の View の設定は AppController で行うので、AppController と
Window を接続する。
(1)AppController Outlet の作成
(作成した Outlet 名が winsow instance の名前になる、Action は要らない)
Resources window の Classes で、NSObject -> AppController を選択
Inspector window の Outlet add ボタンで作成(Outlet名: window)
(AppController が表示されていない場合は、下のスクロールバーを
左にする)
(2)接続
Resources window の Objects で、 AppController(S) から Window(0)(T) に接続
接続は、[Ctrl]キーを押しながらドラッグする。
Inspector window で、Outlet window を選択し接続[Connect] する。
3 Draw menu の追加
interfaces の gorm file をダブルクリックし、 Gorm を起動
・[Main Menu](作成プログラムのメニュー)に [Submenu] を追加
Controls window -> Menus から、 [Submenu>] を [Main Menu] の目的の
場所にドラッグする。
[Submanu] が追加され、メニューに [item] が1個できる。
Item 名を変更する。(Draw1 など)
( 2つ目い以降は、 Menus の [item] をドッラグして追加する)
・Outlet/Action の追加と接続
[Submenu] のコントロールは AppController で行うようにするので、
[Submenu] を AppController に接続する。
*Outlet/Action の追加
AppController に
Outlet: draw1, draw2, draw3, draw4
Action: drawAction1:, drawAction2:, drawAction3:, drawAction4
を追加する。
*接続
[Main Menu] の Draw1(draw1) を AppController(drawAction1:) に接続
接続方法は window の場合と同様。(残りのメニューも同様に接続する)
4 プログラムの生成と編集
QEB2OpenGLView.h, QEB2OpenGLView.m の生成と編集
(新しく作成したクラスファイルのプロジェクトへの追加は、Classes を
選択しメニューから Project -> Add Files を実行する。)
AppController.h, AppController.m の編集
GNUmakefile.preamble の編集