GNUstep CoreGraphics (2)
前回報告した 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
#endifQEB2OpenGLView.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;
@endClasses
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];
}
@endQEB2OpenGLView.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];
}
@endSupporting 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 の編集