iOS8でハマったこと.

以下の情報は物凄くちゃんと調べたわけでは無いのでご注意ください.
ちなみにXcodeはVersion 6.0.1 (6A317)での話. (後々古くなったときの為に追記)

Anti Taggingが動かない.

そんな一報から僕の平穏は終わった.
それは大変だ, でも俺のiPhone5sだと普通に動いてるし5cでも動いてるぞ?
まさか, あいつのせいか…

あいつというのは言うまでもない, iOS8へのアップデートである.
以前, 想ひ出ブレイカーがiOS7に上がった瞬間pngフォーマットで壊す事が出来なくなった辛い過去を持つ僕はすぐに気付いた.

しかし, 今僕の手元にはiOS8環境が無い. 持ってる端末をアップデートしてしまうとあと一年はiOS7対応もしなければならないので僕にiOS開発の仕事が来なくなって餓死してしまうからそれだけは避けなければならない…
more大きいiPhone, iPhone VentiことiPhone 6 Plusを9/12に発注したんだ, もうすぐ届くさ.
そう思って一週間, やっと届いた.

そろそろ物語調も疲れたので普通に.

まずやったことはAnti Taggingのjpgグリッチ→読み込み部分をUIImageJPEGRepresentation[UIImage imageWithData:]のlibjpegへの差し替え. 正確にはlibjpeg-turbo.
ここはすんなり行った.
imageWithData:で壊れたjpegデータを読み込むときに物凄く敷居が高くなってたのは回避.
しかし, 如何せん画像データがおかしい. 1pxが3byte or 4byteとか縦横のサイズ間違えたとかそんなん比較にならない程斜めってたりする. かと思えば意外と斜めってないこともある.
なんだこれ, と思ってCGImageRefをダンプするコードを書いてみた所

CGImageGetWidth:  648
CGImageGetHeight: 820
CGImageGetColorSpace:  (kCGColorSpaceDeviceRGB)
CGImageGetBitsPerPixel:     32
CGImageGetBitsPerComponent: 8
CGImageGetBytesPerRow:      3840
CGImageGetBitmapInfo: 0x00002006
  kCGBitmapAlphaInfoMask     = YES
  kCGBitmapFloatComponents   = NO
  kCGBitmapByteOrderMask     = 0x00002000
  kCGBitmapByteOrderDefault  = NO
  kCGBitmapByteOrder16Little = NO
  kCGBitmapByteOrder32Little = YES
  kCGBitmapByteOrder16Big    = NO
  kCGBitmapByteOrder32Big    = NO


みたいな. (上のデータは飽くまでも雰囲気)
注目すべきは CGImageGetWidth * CGImageGetBitsPerPixel / CGImageGetBitsPerComponent != CGImageGetBytesPerRow になってること.
顔の部分をクロップしてるんだけど, 横がおかしい.
何度かやってみてwidth, heightはちゃんと変わってるけどCGGetBytesPerRowが3840固定になってるっぽい.
クロップは CGImageCreateWithImageInRect でやってたんだけど, こいつが返すCGImageRefは与えたrectをメタ情報的に持って元のピクセルデータは元の画像のをそのまま参照してるんじゃないかと憶測. 縦に切り出すのはmemcpyでさくっといけるけどwidth側はheight分切り出してくっ付けてみたいな作業居るし大変だしね, うんうん. とか思いつつ, context作って素直に切り出し部分をきちんと生成するように変更.
具体的には

CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, rect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);


みたいなのを

UIGraphicsBeginImageContext(rect.size);
[image drawInRect:CGRectMake(-rect.origin.x, -rect.origin.y, image.size.width, image.size.height)];
UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();


みたいな感じに変更してみた.

うん. うまく行った. よしよし. 申請だ!
Xcode6での申請も色々はまりましたがprovとcertな問題だったので割愛. というかよく分かってないけど申請出来たから良いんだよ.

そして満足して海外で幾つか紹介されてるのニヤニヤしてたんだけど気付いてしまった.
想ひ出ブレイカーもjpg壊れねーじゃねーか.

悲しい. too 悲しい. I’m too 悲し過ぎる. それは too bad.
まあ, libjpeg-turboのビルドおじさんは済ませてるし, 想ひ出ブレイカーはクロップしねーし楽勝でしょう. ふふん. 秒殺だな.

等と思いつつ修正に入った所, ソースコードが古い. ARCじゃないし俺俺ライブラリも古いしiOS7のときにやったlibpngもarm64対応させてなかったぽいし酷い.
いや, 今は一刻を争ってるんだ, そこは目を瞑ろう. 取り敢えずlibjpegに差し替えよう.
うん. 動いた. ふー. 楽勝だったぜ. 申請するか.
その前にちゃんと一通り動作確認するかー.
およ?pngとtiffがおかしいぞ?斜めってるぞ?およ?

ということで苦悶の時間.
現実逃避でlibpngとかもビルドし直したし俺俺ライブラリも差し替えたし, ARCにも対応させたし, でもやっぱりpngとtiffがおかしい.
それで動作が変わらない事は分かってたけども.

色々やった所, max1024に合わせるリサイズモードにしてると斜める. ふむ.

でも, まあ, 結局斜めってるんでしょ. はいはい, cgimageダンプすりゃ良いんでしょ, はいはい.
ダンプした所

CGImageGetWidth:  769
CGImageGetHeight: 1025
CGImageGetBitsPerPixel:     32
CGImageGetBitsPerComponent: 8
CGImageGetBytesPerRow:      3104

うん. なんと言う事でしょう. 突っ込みどころが二つ.
769×1025って何. あたしはmax1024でリサイズしてるのよ?ソースコード見てごらんなさいよ.

    if(withResize) {
        int max = fmaxf(size.width, size.height);
        float scale = 1024.0f / max;
        size.width *= scale;
        size.height *= scale;
    }


なのよ?あなたおかしくない?
そしてまたCGImageGetBytesPerRowがズレてらっしゃるわよ?もうやだやだ…

取り敢えずちゃんと見てみましょうよと size.width, size.height をプリンツ.

LogTrace(@"size: %f, %f", size.width, size.height);


と.

768.000027, 1024.000036


やだ, これだから浮動小数点ってば…
でも普通切り捨てるじゃん. int width = size.width だったら切り捨てんじゃん!
とは言ってもどうやらUIGraphicsBeginImageContextWithOptionsで作ったcontextは切り上げられちゃう感じなのでもう良いよと

        size.width *= scale;
        size.height *= scale;
        size.width = (int)size.width;
        size.height = (int)size.height;


とだいぶやけっぱちなコードを書いて乙事主様の怒りを鎮めたのでした. めでたしめでたし.

と思ったけどやっぱり駄目. よく考えたら想ひ出ブレイカーさん, 任意の画像読み込むんだから縦長の画像で1024にフィットさせたら横幅が8の倍数じゃない感じの厭らしい子に当たったら同じ事起きるかもしれないじゃない!
それは美しくない!!きちんと礼儀正しくぶっ壊すのがアタシの使命なの!グリッチ風とかなんちゃってグリッチとかあたし認めないの!!世に蔓延るグリッチ風画像が作れるiOSアプリなんて糞食らえですのよ!!
ってことでlibpngとかに食わせるバッファーの読み込ませ方をCGImageGetBytesPerRowを意識したお行儀の良い感じのコードに変えて上のキャストしなくてもちゃんと動く様にもしました. 1025の画像とかマジファッキンだから上のキャストも最終的にやっぱり入れたけど.

実際の所, 上の問題はちゃんと追ってないから元々UIGraphicsBeginImageContextWithOptionsで作った画像はサイズ切り上げだったけど8byteパディングされてなかったのかもしれないんだけど. まあ解決したから良し.

とは言え, ちゃんと色んなサイズで試したわけじゃないので知らないけどね. まあ, そういう壊れ方も良いじゃん?って言い張れば良いしね. うん.

てことでCoreGraphics周り地味に変わってね?
ピクセルデータへのアクセスとかそんなたき火の周りで葉っぱ食ってラリってるシャーマンみたいなことやんなよボケってApple様に言われてる様な気もしますが, 僕はピクセルデータへのアクセスが大好きですし, GLSLとかいうよく分からん化け物を手懐けて

gl_FragColor  = f(texture2D(tex, pos))


とか書くよりも

for(int j = 0; j < height; j++) {
  for(int i = 0; i < width; i++) {
    for(unsigned char c = 0; c < bpp; c++) {
      buf[bpp * (i + j * width) + c] = f(pix[bpp * (i + j * width) + c]);
    }
  }
}


とか

memcpy(buf, pix, width * height * bpp);


みたいなレガシートロンみたいなソースコードを書き続けてたいんです. 死ぬまで.

Anti Taggingも想ひ出ブレイカーももう暫くでちゃんとアップデートされるはずなのでjpgグリッチフェティシズムな方々, もう少々お待ちください.

てことで, iOS8, 地味に変わってるっぽいので皆様ご注意を.

追記:
そういえばXcode6からiOSプロジェクトとか作ったときにprefix.pchが作られなくなりましたね. マジ不便.
イカれた解決策知ってる方はご一報ください.

コメントを残す