4

グレースケールで(Paintメソッドを上書きせずに)任意のフォームを描画することは可能ですか?

Modal()ダイアログでフォームを表示する場合、その親をグレースケールとして表示しません。Visual StudioExtensionManagerでこれに気づきました。プログレスバーがパッケージをダウンロードしている場合、下にあるウィンドウはグレー表示されます。

私はこれについて考えています:

private void Button1_Click(object sender, EventArgs e)
{
    using (var dialog = new Form2())
    {
        SetGrayscale(this, true);
        dialog.ShowDialog();
        SetGrayscale(this, false);
    }
}

アップデート

設定するだけForm.Enabled = false;では意図していません。それは私のフォームのグレースケール表現ほど良く見えません。compizLinuxのウィンドウデコレータは、応答しないアプリでこれを行ったと思います。

4

3 に答える 3

2

これを行う方法はすでに述べたように、既存のフォームの上に別のコントロール/フォームをオーバーレイし、これのグレースケールバージョンを上にレンダリングすることです。これは、元のフォームの上に正確に配置された追加のフォームを使用して行うことができます。 、またはPanel他のすべてのコントロールの上に配置されたようなものを使用します。

これは、最初のクライアント領域の真上に別のフォームを配置するときにこれを行う方法の実例です。それの使い方

using (Grayscale(this))
{
    MessageBox.Show("Test");
}

実装

public static Form Grayscale(Form tocover)
{
    var frm = new Form
        {
            FormBorderStyle = FormBorderStyle.None,
            ControlBox = false,
            ShowInTaskbar = false,
            StartPosition = FormStartPosition.Manual,
            AutoScaleMode = AutoScaleMode.None,
            Location = tocover.PointToScreen(tocover.ClientRectangle.Location),
            Size = tocover.ClientSize
        };
    frm.Paint += (sender, args) =>
        {
            var bmp = GetFormImageWithoutBorders(tocover);
            bmp = ConvertToGrayscale(bmp);
            args.Graphics.DrawImage(bmp, args.ClipRectangle.Location);
        };

    frm.Show(tocover);
    return frm;
}

private static Bitmap ConvertToGrayscale(Bitmap source)
{
    var bm = new Bitmap(source.Width, source.Height);
    for (int y = 0; y < bm.Height; y++)
    {
        for (int x = 0; x < bm.Width; x++)
        {
            Color c = source.GetPixel(x, y);
            var luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11);
            bm.SetPixel(x, y, Color.FromArgb(luma, luma, luma));
        }
    }
    return bm;
}

private static Bitmap GetControlImage(Control ctl)
{
    var bm = new Bitmap(ctl.Width, ctl.Height);
    ctl.DrawToBitmap(bm, new Rectangle(0, 0, ctl.Width, ctl.Height));
    return bm;
}

private static Bitmap GetFormImageWithoutBorders(Form frm)
{
    // Get the form's whole image.
    using (Bitmap wholeForm = GetControlImage(frm))
    {
        // See how far the form's upper left corner is
        // from the upper left corner of its client area.
        Point origin = frm.PointToScreen(new Point(0, 0));
        int dx = origin.X - frm.Left;
        int dy = origin.Y - frm.Top;

        // Copy the client area into a new Bitmap.
        int wid = frm.ClientSize.Width;
        int hgt = frm.ClientSize.Height;
        var bm = new Bitmap(wid, hgt);
        using (Graphics gr = Graphics.FromImage(bm))
        {
            gr.DrawImage(wholeForm, 0, 0,
                new Rectangle(dx, dy, wid, hgt),
                GraphicsUnit.Pixel);
        }
        return bm;
    }
}

ご了承ください:

  • の実装Paintはかなり貧弱です。実際には、ダブルバッファリングを使用して、グレースケール画像がバッファリングされたグラフィックスコンテキストに事前にレンダリングされるようにする必要があります。これにより、Paintメソッドは事前に描画されたバッファの内容をペイントするだけで済みます。C#のカスタム描画コントロール–手動ダブルバッファリングを参照してください
  • ConvertToGrayscale遅い側では少しですが、おそらくスピードアップすることができます
  • 誰かが何らかの理由で元のフォームを移動することに成功した場合、事態はうまくいきません
  • 画像は静的です。ベースコントロールが再描画された場合、理想的にはトップフォームも再描画する必要があります。別のフォームの一部が無効になったことを検出する最善の方法がわかりません。

時間があれば、これらの問題のいくつかを修正しようとしますが、上記は少なくとも一般的な考え方を示しています。

WPFでは、これがはるかに簡単になることに注意してください。

出典:

于 2012-06-14T12:52:45.197 に答える
1

直接行う方法はないと思います。すべてのフォームがsRGBでレンダリングされていると思います。ハッキーな方法は、フォームのコピーを画像としてフォームにオーバーレイし(これは、Control.DrawToBitMapを使用して簡単に実行できます)、単純なGDIマトリックスを通過させて、https://web.archive.org/webの彩度を下げることです。 /20141230145627/http://bobpowell.net/grayscale.aspx

于 2012-06-14T12:18:45.580 に答える
0

ほとんどの単純なコントロールで機能するこのようなものを試してください(すべてのコントロールを正しく切り替えるには、コンテナーに再帰する必要があります)。

    private void button1_Click(object sender, EventArgs e)
    {
        using (var dialog = new Form())
        {
            Dictionary<Control, Tuple<Color, Color>> oldcolors = new Dictionary<Control, Tuple<Color, Color>>();
            foreach (Control ctl in this.Controls)
            {
                oldcolors.Add(ctl, Tuple.Create(ctl.BackColor, ctl.ForeColor));
                // get rough avg intensity of color
                int bg = (ctl.BackColor.R + ctl.BackColor.G + ctl.BackColor.B) / 3;
                int fg = (ctl.ForeColor.R + ctl.ForeColor.G + ctl.ForeColor.B) / 3;
                ctl.BackColor = Color.FromArgb(bg, bg, bg);
                ctl.ForeColor = Color.FromArgb(fg, fg, fg);
            }

            dialog.ShowDialog();

            foreach (Control ctl in this.Controls)
            {
                ctl.BackColor = oldcolors[ctl].Item1;
                ctl.ForeColor = oldcolors[ctl].Item2;
            }
        }
    }
于 2012-06-14T12:02:24.157 に答える