CGLayerでRepeating Motif Generatorを改善
2007.11.23
CGLayerを発見
Cocoaフレームワークからは、ほとんどのCoreGraphicsの機能を使う事ができる様になっているのですが、中には例外もあります。そんな例外の中の一つがCGLayerというオブジェクトです。これを使うと同じ画像を繰り返し使う場合に、その画像をビデオカードにキャッシュしてくれるので、パフォーマンスが改善されるそうです。まさにRepeating Motif Generator向き。
ただし、これを使うとアプリケーションがPantherでは動かなくなりTiger以降対応となります。またビデオカードが対応している必要があります。
CocoaとCoreGraphicsの違いにとまどう
パフォーマンスが改善されるといっても果たしてどの程度なのか、やる価値がある程なのかどうかは試してみなくてはわかりません。そこで最も簡単なP1の対称性パターンだけCGLayerで描画する様に改造する事にしました。
機能が同じなのだから、CocoaとCoreGraphicsを混ぜて使うのも簡単かと思いきや、そうでもありません。たとえば原図を描画するのにNSBezierPathを使っているのですが、CoreGraphicsには対応するオブジェクトがありません。どうやらgraphics contextというものに対してパスを描画するコマンドを指定していく事で線を描いていく様です。肝心なところがいきなりCocoaと違うので、面食らいます。
CGLayerオブジェクトを作成して、そのgraphics contextに対して原図を一つ描きます。それから線の太さや線のつなぎ方、尖る限界、線の色、塗りつぶしの色などをCGLayerオブジェクトのgraphics contextに設定していきます。その後CGLayerオブジェクト自体を使って原図を並べていくという手順になります。
線の太さゼロの意味が違う
NSBezierPathでは線の太さをゼロにしても細い線が描かれていたのですが、CoreGraphicsで線の太さをゼロにすると線が全く描かれなくなります。
オブジェクトの変換も面倒
NSColorをCGColorに変換するのも簡単ではありません。色空間の違いなどを考慮すると面倒になってしまう様です。そのくらい簡単な方法を用意しておいてくれればいいのに・・・
cocoabuilder.comに以下の記事がありました。
Re: NSColor to CGColor
動く様になりました
さて肝心のパフォーマンスですが、はっきりと差がわかる結果となりました。原図のサイズを30×30にして線を太くすると描画にもたついていたのですが、瞬時に画像が更新される様になります。
PDFとして画像を書き出した場合のファイルサイズも小さくなりました。876KBが44KBになります。約20分の1です。原図一つ一つの描画コマンドが全部入っていたものが、一つの原図の描画コマンド+それの位置指定というデータになったと推測できます。原図のサイズを30×30にした場合なので差が極端にでているのでしょうが、それにしてもすごい差です。これならCGLayerを使う価値がありそうです。
ソースコード
参考までにコードを載せておきます。他のメソッドへの影響を抑える為になるべくここで全部の仕事をやる様にしているので、拡張する時にこれでは困ります。あくまでテスト用のコードです。
- (void)generateP1:(NSBezierPath *)path
{
int i;
float opacity = [repeatingMotifLayer opacity];
NSPoint p;
CGPoint layerOrigin;
NSColor *color;
CGLayerRef layer;
CGContextRef context,layerContext;
context = [[NSGraphicsContext currentContext] graphicsPort];
layer = CGLayerCreateWithContext(context,[self layerSize],NULL);
layerContext = CGLayerGetContext(layer);
CGContextBeginPath(layerContext);
[path elementAtIndex:0 associatedPoints:&p];
CGContextMoveToPoint(layerContext,p.x,p.y);
for(i=1;i<[path elementCount];i++)
{
[path elementAtIndex:i associatedPoints:&p];
CGContextAddLineToPoint(layerContext,p.x,p.y);
}
CGContextClosePath(layerContext);
switch([repeatingMotifLayer lineJoinStyle])
{
case NSMiterLineJoinStyle:
CGContextSetLineJoin(layerContext,kCGLineJoinMiter);
break;
case NSRoundLineJoinStyle:
CGContextSetLineJoin(layerContext,kCGLineJoinRound);
break;
case NSBevelLineJoinStyle:
CGContextSetLineJoin(layerContext,kCGLineJoinBevel);
break;
}
CGContextSetMiterLimit(layerContext,[repeatingMotifLayer miterLimit]);
CGContextSetLineWidth(layerContext,[repeatingMotifLayer strokeWidth]);
if([repeatingMotifLayer fill])
{
CGColorRef cgColor;
color = [self changeOpacity:opacity
withColor:[repeatingMotifLayer fillColor]];
cgColor = CGColorFromNSColor(color);
CGContextSetFillColorWithColor(layerContext,cgColor);
CGColorRelease(cgColor);
}
if([repeatingMotifLayer stroke])
{
CGColorRef cgColor;
color = [self changeOpacity:opacity
withColor:[repeatingMotifLayer strokeColor]];
cgColor = CGColorFromNSColor(color);
CGContextSetStrokeColorWithColor(layerContext,cgColor);
CGColorRelease(cgColor);
}
switch([repeatingMotifLayer windingRule])
{
case NSNonZeroWindingRule:
if([repeatingMotifLayer fill])
if([repeatingMotifLayer stroke])
CGContextDrawPath(layerContext,kCGPathFillStroke);
else
CGContextDrawPath(layerContext,kCGPathFill);
else
if([repeatingMotifLayer stroke])
CGContextDrawPath(layerContext,kCGPathStroke);
break;
case NSEvenOddWindingRule:
if([repeatingMotifLayer fill])
if([repeatingMotifLayer stroke])
CGContextDrawPath(layerContext,kCGPathEOFillStroke);
else
CGContextDrawPath(layerContext,kCGPathEOFill);
else
if([repeatingMotifLayer stroke])
CGContextDrawPath(layerContext,kCGPathStroke);
break;
}
for(i=0;;i++)
{
layerOrigin.x = 0;
layerOrigin.y = masterHeight*i;
if(layerOrigin.y > targetHeight)
break;
else
for(;;)
{
CGContextDrawLayerAtPoint(context,layerOrigin,layer);
layerOrigin.x += masterWidth;
if(layerOrigin.x > targetWidth)
break;
}
}
CGLayerRelease(layer);
}
CGColorRef CGColorFromNSColor(NSColor *color)
{
NSColor* deviceColor = [color colorUsingColorSpaceName:
NSDeviceRGBColorSpace];
float red = [deviceColor redComponent];
float green = [deviceColor greenComponent];
float blue = [deviceColor blueComponent];
float alpha = [deviceColor alphaComponent];
const float components[] = { red, green, blue, alpha };
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef cgColor = CGColorCreate(colorSpace, components);
CGColorSpaceRelease(colorSpace);
return cgColor;
}
- (CGSize)layerSize
{
float width,height;
switch([repeatingMotif symmetryTypeIndex])
{
case RMG_P1:
width = masterWidth;
height = masterHeight;
break;
case RMG_P2:
break;
.
.
(snip)
.
.
case RMG_P6M:
break;
}
return CGSizeMake(width,height);
}
HOME
前のページへ