忍者ブログ
  • 2024.10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 2024.12
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

【2024/11/23 07:30 】 |
CoreGraphicsで自前のコンテキストを使う
CoreGraphics描画の詳細。

大まかな流れは前回の記事参照。


////////////////////////////////
コンテキストを自分で作成する

カレントコンテキストはもともとあるので、そこに直接描画すれば画面に表示することはできる。
しかし、自分でコンテキストを作ることももちろんできる。


ファイルSimpleCoreGraphicsView.h

@interface SimpleCoreGraphicsView : UIView {
CGContextRef ctxCanvas_;
void *dataCanvas_;
CGSize sizeCanvas_;
}
@end


ファイルCoreGraphicsView.mm

-(id) initWithFrame:(CGRect) rect
{
if ((self = [super initWithFrame:rect])){

//////////////////////////
//コンテキスト作成

sizeCanvas_ = CGSizeMake(rect.size.width, rect.size.height);

const int BYTES_PER_PIXEL = 4; //1pixelを4byteで表す(RGBA各1byte)
dataCanvas_ = malloc(sizeCanvas_.width * sizeCanvas_.height * BYTES_PER_PIXEL);

const int BITS_PER_COMPONENT = 8; //1コンポーネントあたりのビット数
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
ctxCanvas_ = CGBitmapContextCreate
(dataCanvas_,
sizeCanvas_.width, sizeCanvas_.height,
BITS_PER_COMPONENT,
BYTES_PER_PIXEL * sizeCanvas_.width,
colorSpace,
kCGImageAlphaPremultipliedFirst);

CGColorSpaceRelease(colorSpace);
}
return self;
}

-(void) dealloc
{
free(dataCanvas_);
CGContextRelease(ctxCanvas_);

[super dealloc];
}



*解説
コンテキストを生成する関数は、CGBitmapContextCreate()である。
宣言は
CG_EXTERN CGContextRef CGBitmapContextCreate(
void *data,
size_t width, size_t height,
size_t bitsPerComponent,
size_t bytesPerRow,
CGColorSpaceRef space,
CGBitmapInfo bitmapInfo);


ファイルのコメント中で記述されている解説は
Create a bitmap context.
The context draws into a bitmap which is `width' pixels wide and `height' pixels high.
The number of components for each pixel is specified by `space', which may also specify a destination color profile.
The number of bits for each component of a pixel is specified by `bitsPerComponent'.
The number of bytes per pixel is equal to `(bitsPerComponent * number of components + 7)/8'.
Each row of the bitmap consists of `bytesPerRow' bytes, which must be at least `width * bytes per pixel' bytes; in addition, `bytesPerRow' must be an integer multiple of the number of bytes per pixel.
`data', if non-NULL, points to a block of memory at least `bytesPerRow * height' bytes.
If `data' is NULL, the data for context is allocated automatically and freed when the context is deallocated.
`bitmapInfo' specifies whether the bitmap should contain an alpha channel and how it's to be generated, along with whether the components are floating-point or integer.

○だいたいまとめると、

dataの指定は任意。
指定があれば、そこにコンテキストのデータが保持される。必ずbytesPerRow * heightバイトを確保しておくこと。
NULLにしてあれば、自動でメモリ確保、破棄される。

width,heightはコンテキストのピクセルサイズ。

bitsPerComponentは、1コンポーネントを何ビットで表現するかの値、
bytesPerRowは、1行あたりのバイト数。1ピクセルあたりのバイト数×widthの値。

spaceは、ピクセルあたりのコンポーネント数を表す。また、描画先のカラープロファイルの情報も持つ。

bitmapInfoは、ビットマップがアルファを含むか否か、含むならどう生成されるのか、またコンポーネントがfloatかintかの情報を持っている。RGB,RGBA,ARGBといったことをenumで示す。

enum CGImageAlphaInfo {
kCGImageAlphaNone, /* For example, RGB. */
kCGImageAlphaPremultipliedLast, /* For example, premultiplied RGBA */
kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */
kCGImageAlphaLast, /* For example, non-premultiplied RGBA */
kCGImageAlphaFirst, /* For example, non-premultiplied ARGB */
kCGImageAlphaNoneSkipLast, /* For example, RBGX. */
kCGImageAlphaNoneSkipFirst, /* For example, XRGB. */
kCGImageAlphaOnly /* No color data, alpha data only */
};
typedef enum CGImageAlphaInfo CGImageAlphaInfo;

enum {
kCGBitmapAlphaInfoMask = 0x1F,
kCGBitmapFloatComponents = (1 << 8),

kCGBitmapByteOrderMask = 0x7000,
kCGBitmapByteOrderDefault = (0 << 12),
kCGBitmapByteOrder16Little = (1 << 12),
kCGBitmapByteOrder32Little = (2 << 12),
kCGBitmapByteOrder16Big = (3 << 12),
kCGBitmapByteOrder32Big = (4 << 12)
};
typedef uint32_t CGBitmapInfo; /* Available in MAC OS X 10.4 & later. */

指定する時は、
kCGImageAlpha*** | kCGBitmapFloatComponents | kCGBitmapByteOrder***
のように書くのだろう。


1pixelの表現に必要なbyte数は、以下の式で表される。
(bitsPerComponent * space + 7)/8
また、bytesPerRow(1行あたりのbyte数)は以下の式で表される。
(bitsPerComponent * space + 7)/8 * width
そして、dataのメモリ確保に必要なbyte数は、以下の式で表される。
(bitsPerComponent * space + 7)/8 * width * height


///////////////////////
自前コンテキストに描いたものを、カレントコンテキストに描画する

自前コンテキストをctxCanvas, カレントコンテキストをctxScreenとする。

- (void)drawRect:(CGRect)rect
{
//コンテキスト取得
CGContextRef ctxScreen = UIGraphicsGetCurrentContext();

//クリア
CGContextClearRect(ctxScreen, CGRectMake(0, 0, 320, 480));

//カンバス描画
CGImageRef image = CGBitmapContextCreateImage(ctxCanvas_);
CGContextDrawImage(ctxScreen, CGRectMake(0, 0, 320, 480), image);
CGImageRelease(image);
}

自前コンテキストからCGImageを作り、それをカレントコンテキストに貼付ける。


////////////////////////////
再描画命令

drawRect()は、明示的に呼ぶ必要がある。
[self setNeedsDisplay];
が再描画命令である。これを呼ぶと、drawRectが呼ばれる。

新たな図形を描画する、画面をタッチする、などのイベントのたびにこれを呼ぶか、
ゲームのように定期的に再描画させたい場合は

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];
[timer fire];

ということをしても良いだろう。

なお、drawRectの中でsetNeedsDisplayを呼んでも再描画はされないし、無限ループにもならない。

PR
【2011/03/18 17:17 】 | iPhone | 有り難いご意見(0) | トラックバック()
<<CoreGraphics描画命令いろいろ | ホーム | 5分でCoreGraphicsで描画する>>
有り難いご意見
貴重なご意見の投稿














虎カムバック
トラックバックURL

<<前ページ | ホーム | 次ページ>>