今回は「保存」ボタンをタップすることで、対称処理した画像を「写真アルバム」(写真アプリのフォトライブラリー)に保存してみます。予定していた対称画像の「コピー」機能については、次回に解説します。
まずは対称処理した画像を得るための作戦を練ります。対称画像は2つの画像を張り合わせて1つの画像に合成します。張り合わせる上下もしくは左右の画像は、SymmetryViewクラスのdrawRect:メソッドでの描画で必要な物とほぼ同じです。ですから、まずは上下もしくは左右の画像を別々に得て、その後その2枚を張り合わせることにします。保存する対称画像はオリジナル画像の実寸サイズで得る必要がありますので、上記メソッドを少し改良して、以下のgetImageメソッドを作成しました。このメソッドを実装するソースファイルは、SymmetryView.mです。
- (UIImage *)getImage
{
CGContextRef context;
CGImageRef cgimage;
CGRect srt,drt;
UIImage *dimg;
cgimage=sy_image.CGImage; // CGImageRefを得る
drt=srt=CGRectMake( 0.0,0.0,sy_image.size.width,sy_image.size.height );
if( sy_type <=1 ) // 上下対称指定
{
drt.size.height-=sy_para;
if( sy_reverse==NO )
srt.origin.y-=sy_para;
}
else // 左右対称指定
{
drt.size.width-=sy_para;
if( sy_reverse==NO )
srt.origin.x-=sy_para;
}
UIGraphicsBeginImageContext( drt.size ); // オフスクリーンContext作成
context=UIGraphicsGetCurrentContext();
if( ( ( sy_type==0 || sy_type==3 ) && sy_reverse==YES ) || ( ( sy_type==1 || sy_type==2 ) && sy_reverse==NO ) )
{
CGContextTranslateCTM( context,0.0,drt.size.height );
CGContextScaleCTM( context,1.0,-1.0 );
}
else if( ( sy_type==3 && sy_reverse==NO ) || ( sy_type==2 && sy_reverse==YES ) )
{
CGContextTranslateCTM( context,drt.size.width,drt.size.height );
CGContextScaleCTM( context,-1.0,-1.0 );
}
CGContextDrawImage( context,srt,cgimage ); // 画像描画
dimg=UIGraphicsGetImageFromCurrentImageContext(); // 座標変換した画像作成
UIGraphicsEndImageContext(); // オフスクリーンContext解放
return dimg;
}
drawRect:メソッドと異なる点は、 描画した画像の内容から新しいUIImageを作成する手法を使うことです。この方法は、UITableViewに表示する画像サムネイルの作成時に用いたresizeUIImage()ルーチン(Utility.m)と同様です。作業実現のために、UIGraphics.hに定義されているUIGraphicsBeginImageContext()とUIGraphicsGetImageFromCurrentImageContext()とUIGraphicsEndImageContext()のトリオが登場しています。
続いて、上下もしくは左右のSymmetryViewから得られた2枚の画像を1枚にまとめる getSymmetryImageメソッドの紹介です。この処理でも先ほど紹介したトリオが活躍しています。このメソッドを使えば、最終的に1枚にまとめられた画像を新しいUIImageとして得ることが出来ます。こちらは、ImageViewController.mの方に実装します。
- (UIImage *)getSymmetryImage
{
UIImage *dimg,*image1,*image2;
CGSize size;
CGRect drt;
if( im_type.selectedSegmentIndex <=1 ) // 上下対称指定
{
image1=[im_view0 getImage]; // 上下2つの画像を得る
image2=[im_view1 getImage];
size=image1.size;
size.height*=2.0;
}
else // 左右対称指定
{
image1=[im_view2 getImage]; // 左右2つの画像を得る
image2=[im_view3 getImage];
size=image1.size;
size.width*=2.0;
}
UIGraphicsBeginImageContext( size ); // オフスクリーンContext作成
drt=CGRectMake( 0.0,0.0,image1.size.width,image1.size.height );
[image2 drawInRect:drt]; // 片方の画像を描画
if( im_type.selectedSegmentIndex <=1 )
drt=CGRectOffset( drt,0.0,image1.size.height );
else
drt=CGRectOffset( drt,image1.size.width,0.0 );
[image1 drawInRect:drt]; // もう片方の画像も描画
dimg=UIGraphicsGetImageFromCurrentImageContext(); // 合成画像を得る
UIGraphicsEndImageContext(); // オフスクリーンContext解放
return dimg;
}
最後に「保存」ボタンをタップした時に実行されるアクションメソッドsave:の実装です。最終的な対称画像を「写真アルバム」(写真アプリのフォトライブラリー)に保存するには、UINavigationController.hに定義されているUIImageWriteToSavedPhotosAlbum()ルーチンを利用します。
- (IBAction)save:(id)sender
{
UIImage *image;
if( image=[self getSymmetryImage] ); // 対称画像を得る
UIImageWriteToSavedPhotosAlbum( image,self,@selector(image:didFinishSavingWithError:contextInfo:),nil); // 写真アプリに保存
}

このルーチンにデリゲートとしてimage:didFinishSavingWithError:contextInfo:メソッドを定義しておくと、保存終了時点での処理を実装することが可能です。今回はエラーは気にせず、保存が終わったら何らかのメッセージを表示する処理を追加してみます。
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
NSString *str,*ok;
UIAlertView *alertView;
str=NSLocalizedString( @"IMG_SAVED",@"" ); // メッセージ内容
ok=NSLocalizedString( @"OK_BUTN",@"" ); // OKボタン
alertView=[[UIAlertView alloc] initWithTitle:str message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:ok,nil];
[alertView show]; // アラートを表示
[alertView release];
}
表示するメッセージは、日本語用と英語用を、それぞれのLocalizable.stringsファイルにキーで関連付け保存しておきます。この表記を変更することで、好みのメッセージを表示できるわけです。例えば、エラーが生じて画像が保存できなかった場合には(エラー内容はNSErrorに返る)メッセージ内容を変更するべきでしょう。 また、こうしたメッセージが邪魔であれば(いちいち五月蝿い)、エラー時だけ表示するように変更しても良いかもしれません。 色々とチャレンジしてみてください。
OK_BUTN="OK";
IMG_SAVED="対称画像が写真アルバムに保存されました。";
OK_BUTN="OK";
IMG_SAVED="Saved Symmetry Image.";

次回は、今回手が回らなかった対称画像の「コピー」機能にチャレンジする予定です。加えて「ペースト」機能にも対応して、クリップボードに保存されている画像を画像一覧へ読み込めるようにしたいと思います。