カラーコードの違いについて

タグの編集
投稿者 masaX  (社会人) 投稿日時 2016/11/10 19:18:05
カラーコードについてなのですが
Colorから、10進数のカラーコードを取り出した場合、例えば赤の場合ですと負の数字で「-65536」と出る場合と正の数字で「16711680」と出る場合があるのですが、どんな場合に負になるのか?、あるいは正になるのかが分かりません。

下記のプログラムはあまり意味の無いプログラムですが、その状態を再現するために書いてみました。例えば、最初のカラーコードも、後のカラーコードもどちらも正の数にするか、あるいはどちらも負の数にする方法は在るのでしょうか?

**サンプルコード*****************************

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        'カラーダイアログを表示
        Color1.ShowDialog()
        Dim col As Color = Color1.Color

        'カラーコードを取り出す
        Dim coltx10 As String = col.ToArgb

        MsgBox("最初のカラーコード=" & coltx10)



        Dim txHex As String = Hex(coltx10)
        Dim myRGB(2) As String

        '16進数の下6桁を取り出す
        Dim colTx As String = Microsoft.VisualBasic.Right(txHex, 6)
        'RGB に該当する値を取り出す。
        myRGB(0) = Mid(colTx, 1, 2)
        myRGB(1) = Mid(colTx, 3, 2)
        myRGB(2) = Mid$(colTx, 5, 2)

        'RGB 形式でバックカラーを表示
        Dim tx As String = CStr(CByte("&H" & myRGB(0))) & "," & CStr(CByte("&H" & myRGB(1))) & "," & CStr(CByte("&H" & myRGB(2)))

        'RGBのカラーコードから「R」「G」「B」それぞれを16進数で取り出し
        Dim tt(2) As String
        tt = Split(tx, ",")

        Dim hx(2) As String
        hx(0) = Hex(tt(0))
        If Len(hx(0)) = 1 Then
            hx(0) = "0" & hx(0)
        End If
        hx(1) = Hex(tt(1))
        If Len(hx(1)) = 1 Then
            hx(1) = "0" & hx(1)
        End If
        hx(2) = Hex(tt(2))
        If Len(hx(2)) = 1 Then
            hx(2) = "0" & hx(2)
        End If

        '16進数にする
        Dim ans As String = hx(0) & hx(1) & hx(2)

        Dim col2 As Color

        '16進数のマークを付けて10進数に変換
        Dim num16 As Long = CLng("&H" & colTx)

        '変換した10進数をIntegerに変換してカラーに変換
        col2 = Color.FromArgb(CInt(num16))
        Text1.ForeColor = col2

        'Text1のForeColorから10進数のカラーコードを取り出す
        Dim coltx10_2 As String = Text1.ForeColor.ToArgb
        MsgBox("後のカラーコード=" & coltx10_2)

        Dim col_A As Color = Color.FromArgb(CInt(coltx10))
        Dim col_B As Color = Color.FromArgb(CInt(coltx10_2))

        LabelMae.ForeColor = col_A
        LabelAto.ForeColor = col_B
   '↑どちらのラベルも同じ色になる

    End Sub
******************************************

投稿者 masaX  (社会人) 投稿日時 2016/11/11 10:45:25
      '16進数の下6桁を取り出す
        Dim colTx As String = Microsoft.VisualBasic.Right(txHex, 6)

この時点で「負」の数値が「正」に変わっており
この数値でForeColorを入力したTextboxから取りだしたForeColorのカラーコードも
同じ色ではあるけれども「負」から「正」に変わってしまうという事なんですね。?

あまりスッキリしないところも有りますが、一応自己解決という事にしておきます。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/11/12 12:01:55
> 負の数字で「-65536」と出る場合と正の数字で「16711680」と出る場合があるのですが、
16711680 は &H00FF0000 相当、
-65536 だと &HFFFF0000 相当ですよね。


RGB を各色 8bit で表した場合、合計 24bit なので、Integer型(32bit)には 8bit足りません。
その 8 bit をどのように扱うのか、という話になります。


【ARGB Color の場合】透明度対応:あり、既知カラー対応:なし
16 進数表記で aarrggbb という並びになります。R,G,B各色を8bitで表し、
残り8bit を透明度/不透明度(Alpha)として扱います。
完全不透明だと Alpha = &HFF、完全透過だと Alpha = &H00 です。

Color から ARGB Color 値への変換は Color の .ToArgb() メソッドで行えます。
ARGB Color 値から Color への変換は Color.FromArgb( argb ) メソッドです。

Color.Red と Color.FromArgb(&HFFFF0000) は、どちらも不透明な純赤を示しており、
ARGB としては同じ値ですが、Color 構造体としては別の色であることに注意が必要です。

透明度を扱わない処理系では、32bitではなく24bit範囲の 00rrggbb が要求される
ケースがありますが、そのような場合は Alpha 部を切り落としてしまうのが良いでしょう。

Dim rgb24 As Integer = color値.ToArgb() And &H00FFFFFF




【OLE Color の場合】透明度対応:なし、既知カラー対応:システムカラーのみ
VB6 や VBA でいうところの vbRed や vbButtonFace などはこれに当たります。透明度はありません。
&H80000000~&H8000001E の範囲がシステムカラーを表し、
それ以外は 00bbggrr の形式で RGB カラーを表します。R,G,B の並び順に注意。
たとえば FF0000 という色は、ARGB Color では青ですが、OLE Color では赤を意味します。

Color から OLE Color 値への変換は ColorTranslator.ToOle( c ) メソッドで行えます。
OLE Color 値から Color への変換は ColorTranslator.FromOle( oleColor ) メソッドです。

なお、ここでいうシステムカラーとは、.NET では SystemColors クラスのメンバーのことを指し、
デスクトップ背景色やタイトルバーの色など、実行環境によって設定色が異なる色のことです。


【Win32 Color の場合】透明度対応:なし、既知カラー対応:なし
GDI32.DLL の API などで使われる色指定です。
16進数で 00bbggrr の形式で RGB カラーを表します。この点では OLE Color と同じですが、
システムカラーを変換した場合は、その実行環境における RGB カラーとして変換されるため、
他の環境で実行した場合のシステムカラーとは必ずしも一致しません。

Color から OLE Color 値への変換は ColorTranslator.ToOle( c ) メソッドで行えます。
OLE Color 値から Color への変換は ColorTranslator.FromOle( oleColor ) メソッドです。



【HTML Color の場合】透明度対応:文字列からの復元時のみ対応、既知カラー対応:WebColorとWebシステムカラーに対応
Web の HTML や CSS で使われる色指定です。
数値としてではなく、"#F00"、"#FF0000"、"Red"、"activeborder" などの文字列で表現される形式です。

Color から HTML Color 値への変換は ColorTranslator.ToHtml( c ) メソッドで行えます。
HTML Color 値から Color への変換は ColorTranslator.FromHtml( htmlColor ) メソッドです。



【QBColor の場合】透明度対応:なし、既知カラー対応:なし
QuickBASIC という古い BASIC 言語で使われていた色指定であり、
コンソール画面で使われいた 16色のみを扱う表現です。

使用できるカラーコードは 0~15 の 16 種であり、
QBColor( 0~15 ) 関数を使うことで、OLE Color な整数値に復元できます。

これを Color 構造体に復元するには、ColorTranslator を使う必要があります。
(Color.FromArgb で変換しているサンプルをたびたび見掛けますが、それは間違いです)

たとえば、カラーコード12(明るい赤)を Color にする場合は、
Dim c As Color = ColorTranslator.FromOle( QBColor(12) )
となります。



【プロパティ永続化の場合】透明度対応:あり、既知カラー対応:あり
Color を文字列として表現するための、完全なる文字列永続化形式であり、
Form デザイナ等でも使われる表現です。

Color を文字列に変換する場合は、ColorConverter の ConvertToInvariantString メソッドを使い、
その文字列を Color に復元するには ColorConverter の ConvertFromInvariantString メソッドを使います。

既知カラーとRGBカラー、たとえば Color.Red と Color.FromArgb(&HFFFF0000) を区別して保持できますし、
システムカラーもシステムカラーのまま保持でき、透明度にも対応した形式です。

例えば、透明度を持つ色「Color.FromArgb(1, 2, 3, 4)」を変換した結果は
"1, 2, 3, 4" という Alpha, Red, Green, Blue な文字列になりますし、
「Color.Red」を変換した結果は "255, 0, 0" ではなく "Red" になります。
また、「SystemColors.Desktop」を変換した結果は "Desktop" です。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/11/12 12:21:57
>> 同じ色ではあるけれども「負」から「正」に変わってしまうという事なんですね。?
&H80000000 ~ &FFFFFFFF の範囲の値を負数として扱うのか、正数として扱うのか、
符号付きと符号なしの違いということになります。


たとえば Byte 型は 0~255 の範囲の 256種の値を示せます。
SByte 型は -128~127 の範囲の 256 種ですよね。

これらの型は 1バイト(8bit)幅であり、下記のような値をとります。

Byte  内部バイナリ    SByte
----  --------------  -----
0     &H00(00000000)  0
1     &H01(00000001)  +1
2     &H02(00000010)  +2
3     &H03(00000011)  +3
:    :              :
126   &H7E(01111110)  +126
127   &H7F(01111111)  +127
128   &H80(10000000)  -128
129   &H81(10000001)  -127
:    :              :
252   &HFE(11111100)  -4
253   &HFE(11111101)  -3
254   &HFE(11111110)  -2
255   &HFF(11111111)  -1


符号なし整数型(unsigned int)は負数を持たないため、値は0から始まります。
符号付き整数型(signed int)では、最上位ビットを 正負を表す符号ビットとして扱いますので、
最大値は符号なしの半分になってしまいますが、その分がマイナス側の値に割り当てられます。


32bit整数型である UInteger(UInt32) 型と Integer(Int32)も同じ関係です。

最上位ビットが 0 の範囲(&H00000000~&H7FFFFFFF)の範囲では
符号なし整数型も符号付整数型も、等しく 0~2147483647 の範囲を示しますが、
最上位ビットが 1 の範囲(&H80000000~&HFFFFFFFF)の範囲においては、
符号なし整数型は 2147483648~4294967295 を表し、
符号付き整数型は -2147483648~-1 を表すことになるのです。



それと、先の回答への補足ですが。

> 【QBColor の場合】
> コンソール画面で使われいた 16色のみを扱う表現です。
> 使用できるカラーコードは 0~15 の 16 種であり、

この 16 色の色番号は、.NET での ConsoleColor.Black ~ ConsoleColor.White に相当します。
QBColor が必要になることはあまり無いと思いますが、一応参考までに。
投稿者 (削除されました)  () 投稿日時 2016/11/12 14:22:11
(削除されました)
投稿者 masaX  (社会人) 投稿日時 2016/11/12 15:50:18
魔界の仮面弁士さん
非常に詳しい解説、どうもありがとうございます。

>16711680 は &H00FF0000 相当、

htmlなどのカラーコードでは24ビットが使われており、24ビットを取り出してカラーに変換すれば思い通りの色が再現できたので、上記のプログラムのように24ビットだけを抜き出してたのですが、本来は32ビット抜き出すべきなんですね?(透明度というものは全く頭に浮かびませんでした)

あと、そもそもどうしてこんな事が気になりだしたかと言いますと、日記のような物をデータベースに記録するアプリケーションを作っておりまして、文字の色をデータベースに記録してるのですが、そのデータベースを直接見たときに、integerの値で記録すると、その値を見てもさっぱり何色なのかが分からなかったのと、マイナスが付くのが気持ちが悪かったので、htmlのように「FF0000」という様なコードをデータベースに記録しようとして、いろいろ触ってるときに不思議に思ったところからなのですが、一般的にはデータベースに色を記録する場合、どんな値を記録しておくのが普通なんでしょう?

 因みに私は、少し前まで「-65536」という負の10進数で記録していたのを、プログラムを書き換えて「FF0000」という風な16進数で記録するように作り替えました。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/11/14 10:49:09
ちなみに RGB 以外の色空間についても、下記のようにして変換できます。
http://dobon.net/vb/dotnet/graphics/hsv.html


> (透明度というものは全く頭に浮かびませんでした)
普段あまり使わないとは思いますが、コントロールの BackColor や ForeColor でも
透明色を取り扱えるようになっています。

色選択の際に[Web]タブの一番上にある、Transparent は
"不透明度0%(完全透過)の純白"を表します。
この値をコードから指定する場合は、Color.Transparent で表せます。
(この他、"不透明度0%(完全透過)の純黒"を表す Color.Empty もあります)


Transparent 以外の透過色は、一覧に無いので直接入力することになります。
たとえば Form に PictureBox あるいは Panel を貼り付け、
その BackColor プロパティ欄に、直接
「128, 255, 0, 0」と書き込むと、50%透過の赤になり、
「64, 0, 0, 255」と書き込むと 25% 透過の青になります。

その状態で、Form の BackColor や BackgroundImage を
設定すると、半透明色を設定したコントロールにも反映されます。



> 本来は32ビット抜き出すべきなんですね?
RGB や ARGB だけで表しきれるものでも無いので、Color 型変数の値を
正確に表す場合、本来は FromArgb / ToArgb では不足だったりします。

先の回答でも触れていますが、文字列形式で正しく保持したいのであれば、
ColorConverter クラス(≠ColorTranslator クラス)を使って変換するのが望ましいですね。

もちろん、案件によっては RGB 24 bit で十分な場合もあります。
それぞれの違いを念頭において、適宜使い分ければてみてください。


ただ、その色をコントロールに割り当てるのであれば、32bit で保存して置いた方が便利だと思います。
たとえば、緑を &H00FF00 として保持させた場合、
Me.BackColor = Color.FromArgb(&HFF00)
のように指定すると、『透明な背景色をサポートしません。』というエラーになってしまいます。

しかし 32bit で保持していれば、不透明な緑のまま &HFF00FF00 として保持できますので、
Me.BackColor = Color.FromArgb(&HFF00FF00)
にてエラー無く復元できます。
不透明を前提とするのであれば、Alpha 値を自分で補っても良いですけれどね。
Me.BackColor = Color.FromArgb(&HFF, Color.FromArgb( rgb24 ))
'Me.BackColor = Color.FromArgb((-1 << 24) Or rgb24 ) 



> 24ビットだけを抜き出してたのですが
RGB を 24 bit で並べるなら、
Dim rgb1 As Integer = c.R + c.G * 256 + c.B * 256 * 256
Dim rgb2 As Integer = ((c.R * 256) + c.G * 256) + c.B
で計算できます。
16進数にしたいのなら、
 Label1.Text = rgb1.ToString("X6")
とすれば OK です。

前者 rgb1 は 00bbggrr の体系です。(RGB 関数や VBA の色指定など)
後者 rgb2 は 00rrggbb の体系です。(FromArgb メソッドなど)

この形式で渡された 16 進数文字列を Color に復元するのなら、こう書けます。
Dim sColor As String = "abcdef"        'rrggbb 形式 

Dim rgbColor As Integer = Convert.ToInt32(sColor, 16) 
Dim cColor As Color = Color.FromArgb(255, Color.FromArgb(rgbColor))
ただ、このように自前で計算するよりは、先の回答で述べた変換用メソッドを使った方が望ましいです。
プログラムを後で見た時に、変換・復元の処理が対象性的になりますから。
投稿者 masaX  (社会人) 投稿日時 2016/11/14 12:55:42
魔界の仮面弁士さん
大変勉強になります。

今のところ24bitで自分の思う通りに機能はしてるのですが
勉強のために、一度、32bitのコードを記録するプログラムに書き換えてみる事にします。