試読コーナー

【夜間】超初心者向けセミナー:ゼロからはじめるiPhone/iPadアプリ開発
 MOSAでは、2010年8月25日(水)にiPhone/iPadアプリ開発を学びたい入門者を対象としたプログラミングセミナーを開催いたします。

【夜間】USTREAM基礎講座;USTREAMを利用した動画配信の始め方
 MOSAでは、2010年9月6日(月)にライブ動画配信サービス「USTREAM」を活用したい方を対象に「USTREAM基礎講座;USTREAMを利用した動画配信の始め方」を開催いたします。

第21回 二次元描画 その3 図形

2010年7月28日

線と多角形

 CoreGrapicsの世界ではパスと呼ばれる線で図形を表し、線の幅や色の指定、塗りつぶすなどして描画します。パスは強力でパラメータも多いので詳しくはご自身で確認してください。
 前回サンプルコードが直線です。矩形枠の表示と塗りつぶしのサンプルコードを最後に掲載します。
今回のコードで多角形は四角までです、星形などはAppleのサンプル「QuartzDemoを参照してください。(QuartzDemoはたびたびバージョンアップされています。)

線色指定 CGContextSetRGBStrokeColor
線幅指定 CGContextSetLineWidth
始点設定 CGContextMoveToPoint
次点設定 CGContextAddLineToPoint
枠線描画 CGContextStrokePath

UIKit関数

 矩形(Rect)は頻繁に利用するため関数が用意されています。
 ただし塗りつぶし・枠線ともに色の透明度は指定できません。
矩形枠線描画 void UIRectFrame (CGRect rect);
矩形塗りつぶし void UIRectFill (CGRect rect);
 高さまたは幅1の矩形の枠線描画で簡易直線も描くことは可能です。

塗りつぶしと枠線

 UIRectFrameで枠線を描くと指定したrectの内側だけに描きます。
 一方CGContextStrokePathで枠線を描くと数学的に各点を結んだ直線を中心線とする外側にも内側にも幅を取るように描きます。
 このため半透明な色で枠も塗りつぶしも描くと枠との交差部分の色がかわります
 不透明な色で描くと枠と塗りつぶしどちらを先にするかで結果が異なるので注意が必要です。
 枠を先に描き後から塗りつぶすと枠の幅が半分になります。塗りつぶしを先にして後から枠を描くと塗りつぶしの矩形が枠線の幅の1/2だけ小さくなります。
 CGContextDrawPath()は最後のパラメータで塗りつぶしと枠線を同時に描く指定も可能です。交差するパスの塗りつぶしに関しても複数の指定が可能です。

角丸矩形、円弧、円

 角丸はMacでは伝統的にボタン等UI部品の基本要素です。
 「QuartzDemo」サンプルでは4辺と4つの円弧を連続して描いています。これをサブルーチン化すると利用価値が高いでしょう。
 下記サンプルでは円弧と円は割愛しました「QuartzDemo」を参照してください。

カーブ
 カーブは有名なポストスクリプトでも採用された制御点を指定することで柔軟な描画が可能です。iPhoneでもiOS 3.2からUIBezierPathクラスが利用できるようになりました。サンプルは割愛しました。「QuartzDemo」を参照してください。

UIKit Function Reference

 Graphics関連のUIKit関数の一覧がドキュメントにあります。頻繁に使うものが多いのでドキュメントの存在は覚えておきましょう。
UIGraphicsGetCurrentContext
UIGraphicsPushContext
UIGraphicsPopContext
UIGraphicsBeginImageContext
UIGraphicsGetImageFromCurrentImageContext
UIGraphicsEndImageContext
UIRectClip
UIRectFill
UIRectFillUsingBlendMode
UIRectFrame
UIRectFrameUsingBlendMode
があります。

サンプル

#define	kLineWidth	(1.0)
- (void)drawRect:(CGRect)rect
{
	// 描画環境を得る
	CGContextRef	myContext = UIGraphicsGetCurrentContext();

	// 矩形を描く その1
	// 枠色指定
#if 1
	CGContextSetRGBStrokeColor(myContext, 1.0, 0.0, 0.0, 0.5);
#else
	CGContextSetRGBStrokeColor(myContext, 0.5, 0.0, 0.0, 1.0);
#endif

	// 塗り色指定
	CGContextSetRGBFillColor(myContext, 0.0, 1.0, 0.0, 0.8);

	CGRect	r1 = CGRectMake(10.0, 10.0, 70.0, 30.0);
	UIRectFill(r1);

	// 枠線幅指定
	CGContextSetLineWidth(myContext, 3.0);
	r1 = CGRectOffset(r1, 71.0, 0.0);
	// 色に注意 透明でない
	UIRectFrame(r1);

	CGContextSetLineWidth(myContext, 6.0);
	r1 = CGRectOffset(r1, 71.0, 0.0);
	UIRectFrame(r1);

	// 矩形を描く その2
	CGContextSetLineWidth(myContext, 6.0);
	CGFloat	x = 10.0;
	CGFloat	y = 50.0;
	CGFloat	w = 70.0;
	CGFloat	h = 30.0;

	CGContextMoveToPoint(myContext, x, y);
	CGContextAddLineToPoint(myContext, x + w, y);		// 1
	CGContextAddLineToPoint(myContext, x + w, y + h);	// 2
	CGContextAddLineToPoint(myContext, x, y + h);		// 3
	CGContextAddLineToPoint(myContext, x, y);		// 4
	CGContextStrokePath(myContext);

	CGContextBeginPath(myContext);
	x += w + 7.0;
	CGContextMoveToPoint(myContext, x, y);
	CGContextAddLineToPoint(myContext, x + w, y);		// 1
	CGContextAddLineToPoint(myContext, x + w, y + h);	// 2
	CGContextAddLineToPoint(myContext, x, y + h);		// 3
	CGContextClosePath(myContext);				// 始点と結ぶ
	CGContextStrokePath(myContext);

	CGRect	r2 = CGRectMake(10.0 + w + 7.0 + w + 7.0, y, w, h);
	CGContextStrokeRect(myContext, r2);

	r2 = CGRectOffset(r2, w + 7.0, 0.0);
	UIRectFill(r2);
	CGContextAddRect(myContext, r2);
	CGContextStrokePath(myContext);

// 角丸四角形(QuartzDemoより)
// If you were making this as a routine, you would probably accept a rectangle
// that defines its bounds, and a radius reflecting the "rounded-ness" of the rectangle.
	CGRect rrect = CGRectMake(210.0, 90.0, 60.0, 60.0);
	CGFloat radius = 10.0;
// NOTE: At this point you may want to verify that your radius is no more than half
// the width and height of your rectangle, as this technique degenerates for those cases.

// In order to draw a rounded rectangle, we will take advantage of the fact that
// CGContextAddArcToPoint will draw straight lines past the start and end of the arc
// in order to create the path from the current position and the destination position.

// In order to create the 4 arcs correctly, we need to know the min, mid and max positions
// on the x and y lengths of the given rectangle.
	CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect), maxx = CGRectGetMaxX(rrect);
	CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect), maxy = CGRectGetMaxY(rrect);

// Next, we will go around the rectangle in the order given by the figure below.
//       minx    midx    maxx
// miny    2       3       4
// midy   1 9              5
// maxy    8       7       6
// Which gives us a coincident start and end point, which is incidental to this technique, but still doesn't
// form a closed path, so we still need to close the path to connect the ends correctly.
// Thus we start by moving to point 1, then adding arcs through each pair of points that follows.
// You could use a similar tecgnique to create any shape with rounded corners.

// Start at 1
	CGContextMoveToPoint(myContext, minx, midy);
// Add an arc through 2 to 3
	CGContextAddArcToPoint(myContext, minx, miny, midx, miny, radius);
// Add an arc through 4 to 5
	CGContextAddArcToPoint(myContext, maxx, miny, maxx, midy, radius);
// Add an arc through 6 to 7
	CGContextAddArcToPoint(myContext, maxx, maxy, midx, maxy, radius);
// Add an arc through 8 to 9
	CGContextAddArcToPoint(myContext, minx, maxy, minx, midy, radius);
// Close the path
	CGContextClosePath(myContext);
// Fill & stroke the path
	CGContextDrawPath(myContext, kCGPathFillStroke);

	// 矩形表示 その3
	CGContextSetRGBFillColor(myContext, 0.0, 1.0, 0.0, 0.4);
	CGContextSetLineWidth(myContext, 10.0);
	r1 = CGRectMake(10.0, 200.0, 40.0, 30.0);
	UIRectFill(r1);
	r1 = CGRectOffset(r1, 50.0, 0.0);
	CGContextFillRect(myContext, r1);
	r1 = CGRectOffset(r1, 50.0, 0.0);
	UIRectFrame(r1);
	r1 = CGRectOffset(r1, 50.0, 0.0);
	CGContextStrokeRect(myContext, r1);

	// 簡易直線表示
	CGContextSetLineWidth(myContext, 1.0);
	r1 = CGRectMake(10.0, 260.0, 120.0, 1.0);
	UIRectFrame(r1);

	r1 = CGRectMake(10.0, 265.0, 1.0, 100.0);
	UIRectFrame(r1);
}

実行例

サンプル実行例

サンプル実行例