2011年04月11日

【iPhone SDK】スクラッチのように指でこすって画像を削る

宝くじ売り場でスクラッチ♪というような
CMでよくあるあのスクラッチをiPhoneアプリで実現するにはどうするかを調べましたのでメモ

必要なフレームワークはCoreGraphicsです。

以下のヘッダをインポートしておきましょう
#import <CoreGraphics/CoreGraphics.h>

今回UIViewがもつ「drawRect」メソッドにて描画をおこなうため、
新しくUIViewを継承するクラスを作ります。

そしてメンバ変数に以下を追加しましょう

void* data;
CGContextRef context;

では、初期化関数内にて準備を行いましょう。

必要なメモリを確保します。
data = malloc(描画する幅 * 描画する高さ * 4);
※4は1x1のデータに必要なバイト数

コンテキストイメージハンドルを作成します

CGColorSpaceRef colorRef = CGColorSpaceCreateDeviceRGB();
context = CGBitmapContextCreate(data, 描画する幅, 描画する高さ
               , 描画色ビット数, 1行あたりのデータサイズ, colorRef, kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorRef);

これでイメージ描画ハンドルの準備は終わりです。

んで、以下の関数を作ります。



- (void)drawRect:(CGRect)rect {

   

    CGImageRef imageRef = CGBitmapContextCreateImage(context);

    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    CGContextDrawImage(currentContext, rect, imageRef);

    CGImageRelease(imageRef);

}

これでコンテキストに登録している画像を描画処理が終わりです。

初期準備処理完了後に以下の処理を明記しておくと、プログラム起動後図1のように起動します。
CGContextSetRGBFillColor(context, 0.5, 0.5, 0.5, 1);
CGContextFillRect(context, self.bounds);

図1
Screenshot 2011.04.11 14.43.35.png

はい、のっぺりグレー一色で描画されます。
今回iPhoneの画面いっぱいにまでFillRectするように
Rectをself.bounds指定している点を参考の際注意してください。

そして、スクラッチのように画像を削るため、
UIViewのタッチイベントをオーバーライドします。

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
       
    CGPoint p = [[touches anyObject] locationInView: self];
    CGPoint q = [[touches anyObject] previousLocationInView: self];
   
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextBeginPath(context);
   
    CGContextMoveToPoint(context, q.x, q.y);
    CGContextAddLineToPoint(context, p.x, p.y);
    CGContextStrokePath(context);
    [self setNeedsDisplay];
}

上記処理はコンテキスト内にて鉛筆のように指でなぞった部分にラインを引く処理です。
ただそれだけなのです。

では、スクラッチのような表現にはどのように+αするのか

それは以下の設定をいれておきます。

CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextSetLineWidth(context, 15);
CGContextSetRGBStrokeColor(context, 1, 1, 1, 0);

BlendModeでクリアするようにしておき、
SetLineWidthにてライン描画の太さを指定します。

ラインの色は関係ないようです。
ブレンドモードにて強制透過するようです。

以上の処理をいれて、指で画面をなぞってみた結果が図2です。

図2
Screenshot 2011.04.11 14.44.00.png

机の上にあったガムのボトルが写っているのがわかりますか?
こんな感じでスクラッチの表現ができました。ちゃんちゃん


【送料無料】iOS SDK Hacks

【送料無料】iOS SDK Hacks
価格:2,520円(税込、送料別)

ラベル:iPhone Xcode
posted by ヒイロ at 15:10| 福岡 ☁| Comment(7) | TrackBack(0) | プログラム | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
ホームページの通り、プログラムを作ったのですが、うまくいきません。

すいませんが、詳しく教えていただいていいですか。

行ったサンプルをいただければ幸いです。
Posted by hiros at 2011年12月03日 19:04
>hirosさん
まずViewの登録順はどうなっていますか?
UIWondow
 ┗UIViewController.view
  ┗UIView(サンプルプログラムのView)
   ┗UIImageView(ボトルガムの画像)

こんな感じで行なっていますでしょうか?

Posted by ヒイロ at 2011年12月03日 22:50
ご連絡が遅くなり、すいません。

登録順は、なっております。

UIViewには、新たに新規クラスを作成し、その中にプログラムを埋めこんでおります。
初期化関数内とは、viewDidLoadのことでしょうか。

すいませんが、プログラムを埋め込む場所を教えていただけないでしょうか。
宜しくお願いします。

Posted by hiro at 2011年12月08日 13:21
忙しいところすいません。
プログラムを添付しますので、お手数ですが、間違っている箇所を教えて頂けないでしょうか。

ViewController.hに
#import <CoreGraphics/CoreGraphics.h>を追加

クラスを作成、クラスをViewController.hのクラスに登録。

・・・.hの@interfaceに下記を入力。
void* data;
CGContextRef context;

・・・.mの初期設定に

self = [super initWithFrame:frame];
if (self) {
// Initialization code

data = malloc(300 * 300 * 4);

CGColorSpaceRef colorRef = CGColorSpaceCreateDeviceRGB();
context = CGBitmapContextCreate(data, 300, 300
, 200, 30, colorRef, kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorRef);

CGContextSetRGBFillColor(context, 0.5, 0.5, 0.5, 1);
CGContextFillRect(context, self.bounds);
}
return self;

を追加します。


- (void)drawRect:(CGRect)rectに

CGImageRef imageRef = CGBitmapContextCreateImage(context);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextDrawImage(currentContext, rect, imageRef);
CGImageRelease(imageRef);

を追加。

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event に

CGPoint p = [[touches anyObject] locationInView: self];
CGPoint q = [[touches anyObject] previousLocationInView: self];

CGContextSetLineCap(context, kCGLineCapRound);
CGContextBeginPath(context);

CGContextMoveToPoint(context, q.x, q.y);
CGContextAddLineToPoint(context, p.x, p.y);
CGContextStrokePath(context);

CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextSetLineWidth(context, 15);
CGContextSetRGBStrokeColor(context, 1, 1, 1, 0);


[self setNeedsDisplay];

を追加実行するとエラーになりました。

すいませんが、サンプルファイルがありましたら、
頂けないでしょうか。

お手数おかけいたしますが、宜しくお願いいたします。



Posted by hiro at 2011年12月08日 20:22
>hiroさん
この時のサンプルプロジェクトが紛失してしまったので、もう一度作りなおさないと検証ができない状態になってしまいました。

週末などにXCodeを使うことができれば、検証してみますね。

みたところ、コードに問題はなさそうですが
エラーというのは実行時にエラーで落ちるのでしょうか?
Posted by ヒイロ at 2011年12月10日 02:50
Iphoneのエミュレータを立ち上げると、
<Error>: CGBitmapContextCreateImage: invalid context 0x0

と表示されます。

画面をスライドさせると
<Error>: CGContextSetLineCap: invalid context 0x0
<Error>: CGContextBeginPath: invalid context 0x0
<Error>: CGContextMoveToPoint: invalid context 0x0

とtouchesMovedで宣言したのが
エラーになってしまいました。

CGBitmapContextCreateImageが間違えているんでしょうか。
他に設定する部分があるのでしょうか。
すいませんが、宜しくお願いいたします。
Posted by hiro at 2011年12月10日 09:26
>hiroさん
http://hiiro-game.seesaa.net/article/200815366.html
こちらの記事にサンプルビューのソースをアップしています。
とりあえずシミュレーターで動作確認をしています。
allocしてinitWithFrameしてaddSubviewしてみてください。

画面サイズ分でグレー色表示し、指でなぞるとなぞった部分が透明になって下のビューが表示されます。
Posted by ヒイロ at 2011年12月16日 20:59
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。