C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: Bitmapの透明色のRGB値が255から0に変わる


(過去ログ 131 を表示中)

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■77366 / inTopicNo.1)  Bitmapの透明色のRGB値が255から0に変わる
  
□投稿者/ kurage (1回)-(2015/10/15(Thu) 10:58:31)

分類:[C#] 

開発環境: Visual Studio Community 2013
.NET Framework のバージョン: 4.5.1
----

こんにちは。
Bitmap の透明色 (アルファ値が 255 の色) の扱いについて、2 点質問させてください。

<<質問 1>>
new Bitmap() にて、新規に Bitmap を作成した時、全ピクセルの色は [A=0, R=0, G=0, B=0] となるようです。
これを、[A=0, R=255, G=255, B=255] で塗りつぶす方法が知りたいです。

Graphics.FillRectangle() や Graphics.Clear() を試してみましたが、[A=0, R=0, G=0, B=0] のままでした。
現状、仕方なく、全ピクセルに対してループ処理にて SetPixel(x, y, Color.Transparent) としています。
もっと良い方法 (一括で設定できる方法) はございませんでしょうか。

<<質問 2>>
ある Bitmap の [A=0, R=255, G=255, B=255] 色のピクセル上に 
別の Bitmap を Graphics.DrawImage() で [A=0, R=255, G=255, B=255] 色のピクセルを描画すると、
ピクセル色が [A=0, R=0, G=0, B=0] に変化してしまいます。
これの原因および回避方法がわかりません。
(質問 1 と同様、1 ピクセルずつ色をコピーしていけば良いのかもしれませんが…。)

----
[A=0, R=255, G=255, B=255] も [A=0, R=255, G=255, B=255] も、
見た目は同じ透明というのは理解しております。

ですから、「どっちでも良いでしょ」と言われればそれまでなのですが、
色値が勝手に変化しているという点が気になり、せめて理由だけでも知ることができればとの思いから、
質問させていただきました。

----
以下、実験に使用したソースコードです。
※試してみてダメだった部分はコメント化しております。

private static void Test()
{
    Bitmap bitmap1 = CreateTransparentBitmap();
    Bitmap bitmap2 = CreateTransparentBitmap();

    System.Diagnostics.Debug.WriteLine(bitmap1.GetPixel(0, 0)); // 出力: Color [A=0, R=255, G=255, B=255]

    using (var g = Graphics.FromImage(bitmap1))
    {
        // g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;

        g.DrawImage(bitmap2, 0, 0);
    }

    System.Diagnostics.Debug.WriteLine(bitmap1.GetPixel(0, 0)); // 出力: Color [A=0, R=0, G=0, B=0]
}

// すべてのピクセルが [A=0, R=255, G=255, B=255] であるビットマップを作成する
private static Bitmap CreateTransparentBitmap()
{
    // この時点では全てのピクセルが [A=0, R=0, G=0, B=0] 
    Bitmap bitmap = new Bitmap(50, 50); 

    // すべてのピクセルを [A=0, R=255, G=255, B=255] で塗りつぶす (もっと良い方法は?)
    for (var y = 0; y < bitmap.Height; y++)
    {
        for (var x = 0; x < bitmap.Width; x++)
        {
            bitmap.SetPixel(x, y, Color.Transparent);
        }
    }

    //// この方法はもNG ([A=0, R=0, G=0, B=0] のまま)
    //using (var g = Graphics.FromImage(bitmap))
    //{
    //    g.FillRectangle(Brushes.Transparent, 0, 0, bitmap.Width, bitmap.Height);
    //}

    //// この方法もNG ([A=0, R=0, G=0, B=0] のまま)
    //using (var g = Graphics.FromImage(bitmap))
    //{
    //    g.Clear(Color.Transparent);
    //}

    return bitmap;
}

引用返信 編集キー/
■77367 / inTopicNo.2)  Re[1]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ ズザー (1回)-(2015/10/15(Thu) 11:49:56)
2015/10/15(Thu) 11:56:00 編集(投稿者)

Color.Transparentはシステムカラーであり、ビットマップで定義されるべき透明色とは別物です。

あと、GraphicsのClear(Color.FromArgb(0, 255, 255, 255))でダメだったということでしょうか?

あまり詳しくないのですが、
Bitmapのコンストラクタでビットマップサイズのみを指定した場合、
このFormatはImaging.PixelFormat.Format32bppArgb等のアルファチャネルを含むものなのでしょうか?


ところで、BitmapのSetPixelは遅いので、LockBitsというものを使ってみてはどうでしょうか?

http://homepage2.nifty.com/nonnon/SoftSample/CS.NET/SampleBitmapPlus.html

その他、Bitmap, LockBitsをキーワードにググってみてください。
引用返信 編集キー/
■77368 / inTopicNo.3)  Re[1]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ kiku (70回)-(2015/10/15(Thu) 12:02:52)
回答ではなく、実験結果です。
下記のように明示的に指定してもダメでした。
A値が0の場合には、RGB値は無視される仕様と解釈するしかないのかな?

            // この方法はもNG ([A=0, R=0, G=0, B=0] のまま)
            using (var g = Graphics.FromImage(bitmap))
            {
                using (var b = new System.Drawing.SolidBrush(Color.FromArgb(0, 255, 255, 255)))
                {
                    g.FillRectangle(b, 0, 0, bitmap.Width, bitmap.Height);
                }
            }

引用返信 編集キー/
■77369 / inTopicNo.4)  Re[1]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ 魔界の仮面弁士 (524回)-(2015/10/15(Thu) 12:14:58)
No77366 (kurage さん) に返信
> Graphics.FillRectangle() や Graphics.Clear() を試してみましたが、[A=0, R=0, G=0, B=0] のままでした。

どうやら GDI+ の API (GdipGraphicsClear など)が、
そもそもそういう仕様のようですね。

Color.FromArgb(0, 1, 2, 3) で Clear したとしても、
0x00010203 になることはなく、やはり
0x00000000 (Color.Empty) になってしまいました。



> 現状、仕方なく、全ピクセルに対してループ処理にて SetPixel(x, y, Color.Transparent) としています。
> もっと良い方法 (一括で設定できる方法) はございませんでしょうか。

画像サイズが大きくなると SetPixcel では遅いので、
unsafe コードで高速化してみました。コードは読みにくくなりましたが…。

static Bitmap CreateBitmap(int width, int height, Color backColor)
{
  var bmp = new Bitmap(width, height);
  var argb = unchecked((UInt32)backColor.ToArgb());
  using (var g = Graphics.FromImage(bmp))
  {
    var bData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size),
      ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
    int scanOffset = bData.Stride / 4;
    unsafe
    {
      var p = (UInt32*)bData.Scan0;
      for (int y = 0; y < height; y++)
      {
        for (int x = 0; x < width; x++)
        {
          p[x] = argb;
        }
        p += scanOffset;
      }
    }
    bmp.UnlockBits(bData);
  }
  return bmp;
}
引用返信 編集キー/
■77370 / inTopicNo.5)  Re[2]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ Hongliang (357回)-(2015/10/15(Thu) 12:19:01)
無視されるではなく、計算された結果と言うべきかと。
例えばAを1にすると、RGBはそれぞれ0または255になります(元の値が128以上だと255、127以下だと0のよう)。
Aを2にすると、RGBは0, 127, 255の3通りのいずれかになります。
この結果から、Aが0ならRGBが0になるのは必然でしょう。
引用返信 編集キー/
■77371 / inTopicNo.6)  Re[2]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ 魔界の仮面弁士 (525回)-(2015/10/15(Thu) 12:24:50)
No77367 (ズザー さん) に返信
> Color.Transparentはシステムカラーであり、

Color.Transparent.IsSystemColor は false のはずですが…どういう意味でしょうか?


> Bitmapのコンストラクタでビットマップサイズのみを指定した場合、
> このFormatはImaging.PixelFormat.Format32bppArgb等のアルファチャネルを含むものなのでしょうか?


YES。常に Format32bppArgb であると下記に明記されています。
https://msdn.microsoft.com/ja-jp/library/7we6s1x3.aspx

ちなみに referencesource.microsoft.com では、こうなっていますね。

 public Bitmap(int width, int height)
  : this(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb) { }
引用返信 編集キー/
■77372 / inTopicNo.7)  Re[3]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ kiku (71回)-(2015/10/15(Thu) 12:43:43)
No77370 (Hongliang さん) に返信
> 無視されるではなく、計算された結果と言うべきかと。
> 例えばAを1にすると、RGBはそれぞれ0または255になります(元の値が128以上だと255、127以下だと0のよう)。
> Aを2にすると、RGBは0, 127, 255の3通りのいずれかになります。
> この結果から、Aが0ならRGBが0になるのは必然でしょう。

なるほど。
ご指摘のような解釈が正解ですね。
ご指摘ありがとうございます。
引用返信 編集キー/
■77373 / inTopicNo.8)  Re[2]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ 魔界の仮面弁士 (526回)-(2015/10/15(Thu) 12:44:57)
No77369 (魔界の仮面弁士) に追記
> 画像サイズが大きくなると SetPixcel では遅いので、
> unsafe コードで高速化してみました。コードは読みにくくなりましたが…。

こちらは unsafe を使わないバージョン。ついでに、行単位で着色するように書き換えてみました。
画像サイズによっては No77369 よりも早くなるようで。


static Bitmap CreateBitmap(int width, int height, Color backColor)
{
  var bmp = new Bitmap(width, height);
  using (var g = Graphics.FromImage(bmp))
  {
    var bData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size),
      ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
    var line = new byte[width * 4];
    var argb = BitConverter.GetBytes(backColor.ToArgb());
    for (int i = 0; i < bData.Stride; i += 4)
    {
      Array.Copy(argb, 0, line, i, 4);
    }
    int stride = bData.Stride;
    var p = bData.Scan0;
    for (int y = 0; y < height; y++, p += stride)
    {
      Marshal.Copy(line, 0, p, line.Length);
    }
    bmp.UnlockBits(bData);
  }
  return bmp;
}
引用返信 編集キー/
■77374 / inTopicNo.9)  Re[3]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ kurage (2回)-(2015/10/15(Thu) 13:25:55)
こんにちは。
皆様、多数のご返信有難うございます。

GDI+ の仕様ということがわかり、割り切ることができてすっきりしました。
1 ピクセルごとに、ポチポチ設定していく方法を採用することに決めました。

SetPixel については、とりあえず簡便な方法で実装していましたが、
皆様のアドバイス通り、本格的に採用するのであれば、
LockBits を使用する方法で実装しておいた方が良さそうですので、そのように変更致します。
(魔界の仮面弁士様、具体的なソースコードを有難うございます。)

●Honglian様

> 例えばAを1にすると、RGBはそれぞれ0または255になります(元の値が128以上だと255、127以下だと0のよう)。
> Aを2にすると、RGBは0, 127, 255の3通りのいずれかになります。
> この結果から、Aが0ならRGBが0になるのは必然でしょう。

につきまして、私の理解が追いつかず、申し訳ございませんが追加で質問させて下さい。
「A を 1 にする」件、下記のソースで試してみたのですが。
試してみましたところ、依然 [A=0, R=0, G=0, B=0] となってしまいます…。
どのようにセットすべきでしょうか。

----
Bitmap bitmap1 = CreateTransparentBitmap();
Bitmap bitmap2 = CreateTransparentBitmap();

System.Diagnostics.Debug.WriteLine(bitmap1.GetPixel(0, 0)); // 出力: Color [A=0, R=255, G=255, B=255]

bitmap1.SetPixel(0, 0, Color.FromArgb(1, 255, 255, 255));
// bitmap2.SetPixel(0, 0, Color.FromArgb(1, 255, 255, 255));

using (var g = Graphics.FromImage(bitmap1))
{
g.DrawImage(bitmap2, 0, 0);
}

System.Diagnostics.Debug.WriteLine(bitmap1.GetPixel(0, 0)); // 出力: Color [A=0, R=0, G=0, B=0]

----
※初めの投稿の質問文内「Bitmap の透明色 (アルファ値が 255 の色) の扱いについて、〜」は、誤りです。
 正しくは「Bitmap の透明色 (アルファ値が 0 の色) の扱いについて、〜」でした。
 申し訳ございませんでした。
引用返信 編集キー/
■77375 / inTopicNo.10)  Re[4]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ Hongliang (358回)-(2015/10/15(Thu) 13:55:16)
私が指摘したのは、FillRectangleの挙動についてですね。
SolidBrushのColor値と、FillRectangle後のピクセル値の話です。

>>77374
については、1x1のBitmapで試しましたが再現しません(Windows 8.1, .NET 4.6です)。
既定であるCompositingMode.SourceOverの場合、
・bmp1:(1, 255, 255, 255), bmp2:(0, 255, 255, 255) => (1, 255, 255, 255)
・bmp1:(1, 255, 255, 255), bmp2:(1, 255, 255, 255) => (2, 255, 255, 255)
・bmp1:(1, 255, 255, 255), bmp2:(0, 0, 0, 0) => (1, 255, 255, 255)
・bmp1:(0, 0, 0, 0), bmp2:(1, 255, 255, 255) => (1, 255, 255, 255)
といった感じになります。

引用返信 編集キー/
■77377 / inTopicNo.11)  Re[4]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ 魔界の仮面弁士 (527回)-(2015/10/15(Thu) 14:05:50)
No77374 (kurage さん) に返信
> 「A を 1 にする」件、下記のソースで試してみたのですが。

0x00ffffff の上に 0x00ffffff を SourceCopy で DrawImage => 0x00000000
0x00ffffff の上に 0x01ffffff を SourceCopy で DrawImage => 0x01ffffff
0x01ffffff の上に 0x00ffffff を SourceCopy で DrawImage => 0x00000000
0x01ffffff の上に 0x01ffffff を SourceCopy で DrawImage => 0x01ffffff

0x00ffffff の上に 0x00ffffff を SourceOver で DrawImage => 0x00000000
0x00ffffff の上に 0x01ffffff を SourceOver で DrawImage => 0x01ffffff
0x01ffffff の上に 0x00ffffff を SourceOver で DrawImage => 0x01ffffff
0x01ffffff の上に 0x01ffffff を SourceOver で DrawImage => 0x02ffffff
引用返信 編集キー/
■77380 / inTopicNo.12)  Re[1]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ 魔界の仮面弁士 (528回)-(2015/10/15(Thu) 14:57:44)
No77366 (kurage さん) に返信
> g.DrawImage(bitmap2, 0, 0);

やはり、Scan0 または SetPixel での力技しか無さそうですね…。

駄目元で ImageAttributes.SetColorMatrix メソッドで変換行列を指定し、
DrawImage メソッドに渡してみましたが、変換結果が完全透明色(Alpha = 0)の場合、
RGB が 0 であれ 0 以外であれ、描画時には RGB が 0 になってしまうようです。
引用返信 編集キー/
■77391 / inTopicNo.13)  Re[2]: Bitmapの透明色のRGB値が255から0に変わる
□投稿者/ kurage (3回)-(2015/10/16(Fri) 10:22:18)
> 例えばAを1にすると、RGBはそれぞれ0または255になります(元の値が128以上だと255、127以下だと0のよう)。
> Aを2にすると、RGBは0, 127, 255の3通りのいずれかになります。
> この結果から、Aが0ならRGBが0になるのは必然でしょう。

上記、FillRectangle についてだったのですね。
勉強になりました。有難うございます。

CompositingMode.SourceOver (既定の設定) であれば、
ベースになる色と掛け合わせるので、そのようになるのですね。

※なら、 CompositingMode.SourceCopy の場合、
 純粋に指定した色が設定されてくれたらいいのに、と思いますが、
 そこは仕様なのでしょう…。

> 駄目元で ImageAttributes.SetColorMatrix メソッドで変換行列を指定し、
> DrawImage メソッドに渡してみましたが、変換結果が完全透明色(Alpha = 0)の場合、
> RGB が 0 であれ 0 以外であれ、描画時には RGB が 0 になってしまうようです。

様々な角度からお試しくださり、有難うございます。
ここまでしていただいてもやっぱり 0 になるという事で、
「そういうもんだ」と納得です。

方針を決めることもできましたし、気持ち的にもすっきりしました。
皆様、本当に有難うございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -