iOS
CoreGraphics
iPadPro

64bit/pixelなCGBitmapを読む

More than 1 year has passed since last update.


嘆き

全ては、内部処理が48bit-colorになったからって、それをスクショ時にそのまま書き出すiOSが悪い!

そんなあっさり48bit-colorのPNGなんて渡ってくると思わねえよ!


bitmapのpixel formatの基本

各ピクセルのRGBAのデータがひたすら並んでいる。

RGBAの順番については色々。

大概は24bit-color(RGB各8bit)+alpha(8bit)で32bit/pixel。


iOSでbitmap読むとき

CGBitmapContextGetAlphaInfo()CGBitmapContextGetBitmapInfo()の値を見比べてやる


AlphaInfo

typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {

kCGImageAlphaNone, /* For example, RGB. */
kCGImageAlphaPremultipliedLast, /* For example, premultiplied RGBA */
kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */
kCGImageAlphaLast, /* For example, non-premultiplied RGBA */
kCGImageAlphaFirst, /* For example, non-premultiplied ARGB */
kCGImageAlphaNoneSkipLast, /* For example, RGBX. */
kCGImageAlphaNoneSkipFirst, /* For example, XRGB. */
kCGImageAlphaOnly /* No color data, alpha data only */
};

↑ 元のヘッダファイルのkCGImageAlphaNoneSkipLastのコメント、「R B G X」は奴らのtypoなので注意。 (上の引用では修正済)


  • Aの位置がどこにあるか

  • Aはあるのか

  • premultiplied(アルファ乗算済み)かどうか


ByteOrderInfo

typedef CF_ENUM(uint32_t, CGImageByteOrderInfo) {

kCGImageByteOrderMask = 0x7000,
kCGImageByteOrder16Little = (1 << 12),
kCGImageByteOrder32Little = (2 << 12),
kCGImageByteOrder16Big = (3 << 12),
kCGImageByteOrder32Big = (4 << 12)
};


  • 区切りが16bitか32bitか。

  • byteorderがbig(そのまま)かlittle(逆順)か


BitsPer

size_t CGImageGetBitsPerComponent(CGImageRef image);

size_t CGImageGetBitsPerPixel(CGImageRef image);

これが今回のミソ。

普通に8bit/compoment、32bit/pixelが返ってくるものと思っていたが、いやいや。

ただスクショを撮って、それをImagePickerで読み込むだけで。

16bit/component、64bit/pixelのデータが来るわけですよ。まじかよ。さすが未来に生きてるな、Apple。


結論

0
1
2
3
4
5
6
7

24bit-color RGBA
R
G
B
A

24bit-color RGBA / LE32
A
B
G
R

48bit-color RGBA
R
r
G
g
B
b
A
a

48bit-color RGBA / LE16
r
R
g
G
b
B
a
A

UInt8のポインタでアクセスして、以下な感じで済ますのが良さげだった

#define u16(p,i,le) (le?p[2*i]+(p[2*i+1]<<8):(p[2*i]<<8)+p[2*i+1])

あとpremultipiledな場合は

CGFloat fa = p[3] / UINT8_MAX;

UInt8 r = p[0] / fa;
UInt8 g = p[1] / fa;
UInt8 b = p[2] / fa;
UInt8 a = p[3];

な感じで使う前にalphaを逆算してあげてね


その他

他にも値がUnsigned intでなくfloating pointだったりすることもあるらしい(kCGBitmapFloatInfoMask)のだが、さすがに対応を割愛。