[ANE][iPhone]AIR for iOSにて、スクリーンショットをカメラロールに保存するANEを作ってみた、がiOS6では機能しなかったという悲しい現実
以前書いた記事、Air for iOSにて、スクリーンショットをカメラロールに保存するANEを作ってみた、への追加メモです。
まず結論から、
iOS6では「スクリーンショットをカメラロールに保存するANE」は正常に機能しなくなりました。。
(iOS5では機能します)
そして色々検証してみたところ、
現状AIR for iOS(AIR3.4SDK)ではANE内でスクリーンショット(UIImageとして取得)することは無理じゃね?
という結論に達しました。
以下、無理じゃね?と思う理由が続きマス。
(解決策わかる方がいました教えてください。。)
■Objective-Cでのスクリーンショットを取るコードをiOS6上で動かしてみる
上記aneで使ったこちらのコード。
これを使って、Xcodeを使ったObjective-Cプロジェクト(Xcode4.5のOpenGL Gameテンプレートを使用)でOpenGLで描画したもののキャプチャを取ってみようとしましたが、iOS6実機ではキャプチャが正常に取れません。
(Xcodeのシミュレーター上では正常にキャプチャできるんですが、iOS6実機ではキャプチャ画像は真っ白になってしまう)
※iOS5実機ではなんら問題なくキャプチャできます。
あら、そもそもiOS6ではOpenGL描画のキャプチャができなくなったの?
と思いましたが、そういうわけでもありません。
■Apple公式のキャプチャ手順
こちらにOpenGLの描画についてのキャプチャの取り方について記載されているのですが、やり方としては2種類あるようす。
1) Call glReadPixels before calling EAGLContext/-presentRenderbuffer
2) Set the CAEAGLLayer’s retained backing property to true to retain the contents of the layer. However, this can have adverse performance implications so you are cautioned to use this setting only when necessary.
上記2通りのやり方を、まずはObjective-Cプロジェクトを使って実装してみると、どちらを使ってもではOpenGL描画のキャプチャは取れました。
じゃあ、その2通りのどちらかをANE使って実装したらいいじゃん、ってことなんだけどこれがどうやっていいかわからず。
以下、それぞれの無理じゃねって理由です。
■やり方1)について
1) Call glReadPixels before calling EAGLContext/-presentRenderbuffer
これはglReadPixels()は、presentRenderbufferする前に呼んでね、ってことなんだと思います。
glReadPixelsはスクリーンショットを取るときに呼ぶメソッドなので、presentRenderbuffer()を実行する前のタイミングでスクリーンショットを取る処理を実行するようにしたいわけですが、presentRenderbuffer()はOpenGL描画の更新毎に呼ぶメソッドっぽい。
で、AIR for iOSでは描画更新処理というコアな部分の処理が具体的にどうなっているのかわからんのです。
(LLVMが具体的にどのようにコンパイル(AS3→ObjC)してるのかわからんので)
描画処理のコアな部分の処理をANEからオーバーライドなりして、修正することができればいけそうなんですけどね。どういう実装になってるのかわからんのでできません。
というわけで1)の案はANEで実装する方法がわからず。
■やり方2)について
2) Set the CAEAGLLayer’s retained backing property to true to retain the contents of the layer. However, this can have adverse performance implications so you are cautioned to use this setting only when necessary.
これは、CAEAGLLayer.drawablePropertiesにkEAGLDrawablePropertyRetainedBacking:YESを突っ込め、と。
ただそうするとパフォーマンス落ちるかもしれないから必要なときにだけYESにしてね、ということみたい。
CAEAGLLayerはANEからでも取得できました。
//UIWindowの下にCALayerがあってその下にCAEAGLLayerがありました。 UIWindow* win = [UIApplication sharedApplication].keyWindow; UIView* uv = [[win subviews] objectAtIndex:0]; UIView* uv2 = [[uv subviews] objectAtIndex:0]; CAEAGLLayer* eagllayer = (CAEAGLLayer *) uv2.layer;
CAEAGLLayerが取得できればdrawablePropertiesにkEAGLDrawablePropertyRetainedBacking:YESを上書きするところまではできるのだけど、drawablePropertiesを変更した際にはEAGLContext.renderbufferStorage:fromDrawableを実行しないと反映されない。
で、これにはEAGLContextのインスタンスを取得しないとなのだけど、ここが無理っぽい。
先ほどと同様でLLVM後のコアな部分の処理が具体的にどうなっているのかわからんのです。
CAEAGLLayerをもつUIViewにEAGLContextは使われてると思うけど変数名がわからず、そもそも外部からアクセスできる変数かどうかもわからずです。
というわけで、Apple推奨のふたつの手順がAIR for iOSでは実装できないんじゃないかと思っているわけです。
というか、なんでiOS5ではできていたのかも謎です。
気になったのはTechnical Q&A QA1704 OpenGL ES View Snapshotに書かれたこの文章
Important: You must call glReadPixels before calling EAGLContext/-presentRenderbuffer: to get defined results unless you’re using a retained back buffer.
iOS6のベータが出回ったころに追記されたとかなら、iOS6で仕様が変わったのだと納得がいくんですけども。
これができないとiOSのTwitterAPIとかfacebookAPIのAPI使ってもアプリのキャプチャ画像添付ができないと思うんですよね。
Flash側で画面をBitmapDataでキャプチャしてそれをaneに送ってane側でBitmapDataをUIViewに変換することができればなんとかなりそうな気もするんですけども、Alternativa3Dで描画してる部分をBitmapDataにキャプチャしようとしたら(Web上ならできたのに)iOS上だとキャプチャとれず。
なんとかなる方法ないかなぁ・・・
p.s
iOS5以上から追加されたDLKViewクラス(CAEAGLLayerをもつUIView系クラス)にはsnapshotってメソッドがあって、これを実行するとUIImageがさくっととれちゃうんですよね。
AIR for iOSはiOS4もサポートしてるからDLKViewは使われてないんでしょうけど。
(ちなみにAIR for iOSではCAEAGLLayerをもつUIView系クラスはCTStageViewというクラスで定義されてるっぽい)
CTStageViewにもsnapshotみたいなAPI仕込まれていないかなぁ。。。