これまでCALayerの機能使えば元画像を分割しなくても、パズル作れるよってやってきたわけだけど、さすがにパズルの途中の画像をもらって、それやるとややこしい。
なので、素直にパネルごとに分割した画像を持たせる事にします。
まあ、今までどおり、分割しなくても、やろうと思えばやれるけどね。
やりたい人は各自でチャレンジしてみてください。
で、分割するとなると、一つ問題があります。
今使ってる画像も含め、いつでも分割数で割り切れる矩形とは限らんわけですよ。例えば100 x 100の画像を3 x 3のパネルにするなら、ひとつのパネルは33.333… x 33.333… になってしまうわけです。
で、網膜ディスプレィといえど、画素サイズ以上の解像度はないわけで、そういう場合にはたいがい四捨五入された33個目の画素で切り取る事になります。
ただ切り取るだけではなく、34個目の画素の情報を0.333…分加味した情報を、新しい33個目の画素としたりするなんてシステムもあります。
描く場合も同じで0.333…分の情報を34個目のピクセルに加味させたりします。
あんま気にしなくていいけど、iPhone 4より前の機種で、きっちりピクセルの切れ目で垂線なんかを引きたい場合、座標として0.5を与えた方がきっちり綺麗に描かれるんですな。
アンチエリアス機能の一部として、ここらへんがどう働くかまでは調べてないんですが、そういうわけで、あんま小数点で画像の矩形サイズを持ちたくないんですよ。
なので、3で割るなら、3で割り切れるよう、元のサイズにもっとも近い小さめのサイズに画像を変更してからパネルを作ります。
パネルの分割数をint型のdivH,divVとし、画像の矩形サイズがCGSize型のimageSizeとしたら、以下のようにすれば、CGSize型のnewSizeに最適なサイズが入るわけです。
int型の割り算は、切り捨てになる事を利用するわけですな。
でもって分割前に、この新しいnewSizeに元の画像を縮小するか、切り取るかしよう、ちゅーわけです。
画像全体を残したいなら、多少の縦横比率のゆがみをあきらめ縮小、縦横比を残したいなら、端の画像をあきらめ切り取る事になります。
私は、切り取る方を使います。
だいたい、こんな感じ。
最適なsizeを計算したら、そのsizeで一時的なCGContextRefを用意しておいて、そこに受け取ったimageをdrawPtで描画すれば、割り切れない余白が切り取られる事になります。
一時的なCGContextRefの用意はUIGraphicBitmapBeginを使い、CGContextRefからのUIImage取り出しはUIGraphicGetImageを使えばいい。
最後に用がなくなった一時的なCGContextRefはUIGraphicBitmapEndで解放っす。
これでできた画像を、あらためて各パネル(PuzzleTileLayerクラス)用の画像に分割するわけだ。
分解はさっきと同じで、パネルサイズの一時的なCGContextRefを用意してdrawPtの位置を、パネルの位置に合わせてずらしながら新しくできた画像UIImageを描画してやればいいわけです。
これで取り出したUIImageのCGImageをPuzzleTileLayerのcontentに設定してやれば、いままでどおり自在に動かせるわけですね。
で、保存時は最適サイズで一時的なCGContextRefを用意して、drawPtの位置を、パネルの位置に合わせてずらしながら、パネル側の画像を描画してやればいいわけです。
この時に出来上がった画像とパネルの順を保存すれば、この画像を読み込んだ時にパネルの位置を再現できるわけですな。
パズルの途中でアプリをホームボタン終了すれば、書き出されるTIFファイルも、今回はちゃんとかき混ぜられた状態で保存される。
で、起動しなおす(シミュレータ終了させて再度実行ね)とちゃんと、続きから始められるぞっと。
わけですな。
それは、つまりTIF画像をメールで送って、これをパズルメーカーで開けば、パズルが再開できる準備が整った事を意味します。
とりあえず、サンプルをアップ。
TIF書き出し、TIF読み込みでやってる事の解説は次回。
------------
サンプルプロジェクト:puzzle-08.zip
なので、素直にパネルごとに分割した画像を持たせる事にします。
まあ、今までどおり、分割しなくても、やろうと思えばやれるけどね。
やりたい人は各自でチャレンジしてみてください。
で、分割するとなると、一つ問題があります。
今使ってる画像も含め、いつでも分割数で割り切れる矩形とは限らんわけですよ。例えば100 x 100の画像を3 x 3のパネルにするなら、ひとつのパネルは33.333… x 33.333… になってしまうわけです。
で、網膜ディスプレィといえど、画素サイズ以上の解像度はないわけで、そういう場合にはたいがい四捨五入された33個目の画素で切り取る事になります。
ただ切り取るだけではなく、34個目の画素の情報を0.333…分加味した情報を、新しい33個目の画素としたりするなんてシステムもあります。
描く場合も同じで0.333…分の情報を34個目のピクセルに加味させたりします。
あんま気にしなくていいけど、iPhone 4より前の機種で、きっちりピクセルの切れ目で垂線なんかを引きたい場合、座標として0.5を与えた方がきっちり綺麗に描かれるんですな。
アンチエリアス機能の一部として、ここらへんがどう働くかまでは調べてないんですが、そういうわけで、あんま小数点で画像の矩形サイズを持ちたくないんですよ。
なので、3で割るなら、3で割り切れるよう、元のサイズにもっとも近い小さめのサイズに画像を変更してからパネルを作ります。
パネルの分割数をint型のdivH,divVとし、画像の矩形サイズがCGSize型のimageSizeとしたら、以下のようにすれば、CGSize型のnewSizeに最適なサイズが入るわけです。
newSize.width = ((int)imageSize.width / divH) * divH;
newSize.height = ((int)imageSize.height / divV) * divV;
newSize.height = ((int)imageSize.height / divV) * divV;
int型の割り算は、切り捨てになる事を利用するわけですな。
100を3で割る:100 / 3 = 33 → 33 * 3 = 99
100を30で割る:100 / 30 = 3 → 3 * 30 = 90
100を30で割る:100 / 30 = 3 → 3 * 30 = 90
でもって分割前に、この新しいnewSizeに元の画像を縮小するか、切り取るかしよう、ちゅーわけです。
画像全体を残したいなら、多少の縦横比率のゆがみをあきらめ縮小、縦横比を残したいなら、端の画像をあきらめ切り取る事になります。
私は、切り取る方を使います。
- (UIImage*)optimumImage:(UIImage*)image withHCount:(int)hCount vCount:(int)vCount
{
CGSize size = image.size;
int optimumWidth = ((int)size.width / hCount) * hCount;
int optimumHeight = ((int)size.height / vCount) * vCount;
UIGraphicsBeginImageContext(CGSizeMake(optimumWidth, optimumHeight));
int left = (int)(size.width - optimumWidth) / 2;
int top = (int)(size.height - optimumHeight) / 2;
[image drawAtPoint:CGPointMake(left, top)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
{
CGSize size = image.size;
int optimumWidth = ((int)size.width / hCount) * hCount;
int optimumHeight = ((int)size.height / vCount) * vCount;
UIGraphicsBeginImageContext(CGSizeMake(optimumWidth, optimumHeight));
int left = (int)(size.width - optimumWidth) / 2;
int top = (int)(size.height - optimumHeight) / 2;
[image drawAtPoint:CGPointMake(left, top)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
だいたい、こんな感じ。
最適なsizeを計算したら、そのsizeで一時的なCGContextRefを用意しておいて、そこに受け取ったimageをdrawPtで描画すれば、割り切れない余白が切り取られる事になります。
一時的なCGContextRefの用意はUIGraphicBitmapBeginを使い、CGContextRefからのUIImage取り出しはUIGraphicGetImageを使えばいい。
最後に用がなくなった一時的なCGContextRefはUIGraphicBitmapEndで解放っす。
これでできた画像を、あらためて各パネル(PuzzleTileLayerクラス)用の画像に分割するわけだ。
分解はさっきと同じで、パネルサイズの一時的なCGContextRefを用意してdrawPtの位置を、パネルの位置に合わせてずらしながら新しくできた画像UIImageを描画してやればいいわけです。
これで取り出したUIImageのCGImageをPuzzleTileLayerのcontentに設定してやれば、いままでどおり自在に動かせるわけですね。
で、保存時は最適サイズで一時的なCGContextRefを用意して、drawPtの位置を、パネルの位置に合わせてずらしながら、パネル側の画像を描画してやればいいわけです。
この時に出来上がった画像とパネルの順を保存すれば、この画像を読み込んだ時にパネルの位置を再現できるわけですな。
パズルの途中でアプリをホームボタン終了すれば、書き出されるTIFファイルも、今回はちゃんとかき混ぜられた状態で保存される。
で、起動しなおす(シミュレータ終了させて再度実行ね)とちゃんと、続きから始められるぞっと。
TIF画像として、かき混ぜられた状態の画像を保存し、このファイルを元にパズルを再現できるようになった
わけですな。
それは、つまりTIF画像をメールで送って、これをパズルメーカーで開けば、パズルが再開できる準備が整った事を意味します。
とりあえず、サンプルをアップ。
TIF書き出し、TIF読み込みでやってる事の解説は次回。
------------
サンプルプロジェクト:puzzle-08.zip