Swift UIColor Extension でよくある 16進数カラーコードを UIColor に変換する時のビット演算について

前談

import UIKit

extension UIColor {
    convenience init(red: Int, green: Int, blue: Int) {
        self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
    }

    convenience init(rgb: Int) {
        self.init(
            red: (rgb >> 16) & 0xFF,
            green: (rgb >> 8) & 0xFF,
            blue: rgb & 0xFF
        )
    }
}

16進数カラーコードを UIColor に変換する時の UIColor Extension で上記のようなコードを見かけませんか? UIColor Hex とかで検索すると上記のような UIColor の Extensionが出てきます。 こんなの

stackoverflow.com

今回は上記のコードで使用されているビット演算についてです。
普段あまりビット演算を使う場面がないので、
自分なりに何をしているか理解した範囲で話します。間違いがありましたらコメントで教えてください。
最低限、2進数、10進数、16進数の理解がないと難しいかもしれません。

例として16進数のカラーコード #3a294b を入れて説明します。
ちなみに#3a294b は 10進数 RGB で表すと Red: 58, Green: 41, Blue: 75 (0 ~ 255) となります。

UIColor.init(rgb: 0x3a294b)

Swiftの 0xプリフィックスで、16進数の定数の前には 0x を付けます。10進数の場合はプリフィックスは不要です。したがって Swiftで let a = 8 と書いた場合は10進数になります。

16進数 3a294b を2進数に変換すると以下のようになります。

00111010 / 00101001 / 01001011

※わかりやすく 8bitごとに / で区切って、桁数が揃うように先頭の00を補っています。

他にも例えば 16進数 FFFFFF を2進数に変換すると以下のようになります。

11111111 / 11111111 / 11111111

さっと2進数を確認したい場合は、以下のように 16進数から2進数に変換できます

String(0x3a294b, radix: 2) // "1110100010100101001011"

本題

こちらの方から1つずつ見て行きます。

    convenience init(rgb: Int) {
        self.init(
            red: (rgb >> 16) & 0xFF,
            green: (rgb >> 8) & 0xFF,
            blue: rgb & 0xFF
        )
    }

Red

red: (rgb >> 16) & 0xFF,

今回は rgb には 0x3a294b が入ります。

0x3a294b >> 16

>> は右ビットシフトを表すビットシフト演算子です。
したがって、式が表す意味は 16進数 3a294b16bit 分を右シフトさせる。

00111010 / 00101001 / 01001011

16進数 3a294b16bit 分を右シフトさせると

00000000 / 00000000 / 00111010

16進数 3a294b の Redの部分 16進数 3a を取り出せました。

次に

0x3a & 0xFF

& はビットANDを表すビットシフト演算子です。
したがって、式が表す意味は 16進数 0x3a0xFF とビットANDする。

これは 0x3a0xFF で ビットANDすることで最大数を16進数 FF にするためです。

00111010

11111111

の ビットANDをとると

00111010

となり、これを10進数に変換すると58 になり, Red は 58 です。合っていますね。

Green

green: (rgb >> 8) & 0xFF,

同様に見て行くと

0x3a294b >> 8

式が表す意味は 16進数 3a294b8bit 分を右シフトさせる。

00111010 / 00101001 / 01001011

16進数 3a294b8bit 分を右シフトさせると

00000000 / 00111010 / 00101001

16進数 3a294b の RedとGreenの部分 16進数 3a29 を取り出せました。

次に

0x3a29 & 0xFF

式が表す意味は 16進数 0x3a290xFF とビットANDする。

00000000 / 00111010 / 00101001

00000000 / 00000000 / 11111111

の ビットANDをとると

00101001

となり、これを10進数に変換すると41 になり, Green は 41 です。合っていますね。

Blue

次に

blue: rgb & 0xFF

同様に見て行くと

0x3a294b & 0xFF

式が表す意味は 16進数 0x3a294b0xFF とビットANDする。 Blue 特に右シフトせずに、ビットANDを取っています。 Blue は 8bit 部分なので、ビットシフトする必要がありません。

00111010 / 00101001 / 01001011

00000000 / 00000000 / 11111111

の ビットANDをとると

00000000 / 00000000 / 01001011

つまり

01001011

となり、これを10進数に変換すると75 になり, Green は 75 です。合っていますね。

よって、今回の例だと

UIColor.init(red: 58, green: 41, blue: 75)

という形で

    convenience init(red: Int, green: Int, blue: Int) {
        self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
    }

こちらのメソッドを読んでいることになります。

また

    convenience init(red: Int, green: Int, blue: Int) {
        self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
    }

こちらの関数は、UIKitで実装されている UIColor.init に合うように
RGB のそれぞれの値が 0 ~ 1 になるように 255で 割っています。

以上です。
サンプルソースコードはこちらにあげています。

github.com

参考