【问题标题】:How to make a transparent background of multiple images in c#如何在c#中制作多张图片的透明背景
【发布时间】:2014-08-20 08:40:29
【问题描述】:

我有一组 Windows 形式的图像,并希望将它们设置为“非矩形” - 即透明背景。

但是,当使用transparencyKey 时,是的,两者都失去了背景颜色。但是,我在任何时候都只能看到一个而不能看到另一个(取决于哪个是“broughtToFront”)。

有没有办法在表单上显示两个图像,都是“非矩形”,并且都具有透明背景?

请注意:我已设法使背景透明(TransparencyKey 设置为 gray (RGB of 66,66,66),并且两个图像具有相同的背景。

我听说过“regions”这个词,但据我所知,这通常是 CPU 上非常“重”的方式。

非常感谢并乐于回答任何澄清问题的任何建议(因为我不太擅长解释自己)。

注意。我试图达到的“效果”类似于在项目启动时打开 X 战警的门——我的桌面在门后。

【问题讨论】:

  • 是否可以在同一个图像控件中拥有两个(或多个)图像,而不是为每个图像单独控制?你能解释一下 X 战警的门打开是什么意思吗?或者发布一张图片来显示你想要的结果。
  • @TomažTekavec 因为两个不同的门部件将朝不同的方向移动,并且不是“方形”。我打算让门开始“关闭”,当系统加载后,让它们“打开”。我不确定它们是否可以放在一个 picturebox 中,但它们目前处于不同的位置。
  • 这个项目必须使用 WinForms 吗?它有点过时,仅提供有限数量的功能。我相信您的场景可以使用 WPF 更轻松地实现。或者,您可以创建自己的类,从 Panel 派生并使用 Graphics 对象自己进行绘图(如果您需要,我可以提供一个小的 sn-p)。
  • 嗯,看看这里,也许这可以解决你的问题:stackoverflow.com/questions/19910172/…
  • @WeSt 它必须在 winforms 中(我知道这很痛苦)。 Tomaz,有点帮助,但“backImage”也需要透明(否则它仍然显示不正确)

标签: c# winforms customization transparency


【解决方案1】:

此尝试使用 UpdateLayeredWindow API:http://msdn.microsoft.com/en-us/library/ms997507.aspx#layerwin_topic2a

我的实现灵感来自 Corylulu:https://stackoverflow.com/a/8809657/1812199

这两个位图是动态创建的,但您可以从文件系统或其他任何地方加载它们。

覆盖窗口目前遍布整个主屏幕,但您也可以选择将表单的位置和大小设置为您需要的任何内容(例如,在另一个表单的顶部)。

我为这两个位图实现了一个非常基本的动画,但你是否比我更有创意:)

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace StackOverflow25400312
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            var bounds = Screen.PrimaryScreen.Bounds;
            var imagesSize = new Size(bounds.Width * 2 / 3, bounds.Height);
            var left = LoadLeftDoorTransparentPng(imagesSize);
            var right = LoadRightDoorTransparentPng(imagesSize);

            // Create a form with same dimension as the screen.
            var form = new OverlayForm(left, right, bounds);

            Application.Run(form);
        }

        private static Image GenerateImage(GraphicsPath path, Size sz)
        {
            var bitmap = new Bitmap(sz.Width, sz.Height, PixelFormat.Format32bppArgb);
            var graphics = Graphics.FromImage(bitmap);
            graphics.Clear(Color.Transparent);
            graphics.FillPath(Brushes.Red, path);

            return bitmap;
        }

        private static Image LoadRightDoorTransparentPng(Size sz)
        {
            int w = sz.Width;
            int h = sz.Height;

            var path = new GraphicsPath();
            path.AddLines(new[]
                {
                    new Point(w / 2, 0),
                    new Point(w, 0),
                    new Point(w, h),
                    new Point(w / 2, h),
                    new Point(0, h / 2),
                });

            // I will generate it by code but you should use Bitmap.FromFile(...)
            return GenerateImage(path, sz);
        }

        private static Image LoadLeftDoorTransparentPng(Size sz)
        {
            int w = sz.Width;
            int h = sz.Height;

            var path = new GraphicsPath();
            path.AddLines(new[]
                {
                    new Point(0, 0),
                    new Point(w, 0),
                    new Point(w / 2, h / 2),
                    new Point(w, h),
                    new Point(0, h),
                });

            // I will generate it by code but you should use Bitmap.FromFile(...)
            return GenerateImage(path, sz);
        }
    }

    public class OverlayForm : Form
    {
        public const int WS_EX_NOACTIVATE = 0x08000000;
        public const int WS_EX_TOOLWINDOW = 0x00000080;
        public const int WS_EX_TOPMOST = 0x00000008;
        public const int WS_EX_LAYERED = 0x00080000;
        public const int WS_EX_TRANSPARENT = 0x00000020;

        [DllImport("gdi32.dll")]
        public static extern bool DeleteDC(IntPtr hdc);
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr GetDC(IntPtr hWnd);
        [DllImport("gdi32.dll", SetLastError = true)]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
        [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
        [DllImport("user32.dll")]
        public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
        public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
           ref POINT pptDst, ref SIZE psize, IntPtr hdcSrc, ref POINT pptSrc, uint crKey,
           [In] ref BLENDFUNCTION pblend, uint dwFlags);

        public const int ULW_ALPHA = 2;

        [StructLayout(LayoutKind.Sequential)]
        public struct SIZE
        {
            public int cx;
            public int cy;

            public SIZE(int cx, int cy)
            {
                this.cx = cx;
                this.cy = cy;
            }
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;

            public POINT(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }

            public POINT(System.Drawing.Point pt) : this(pt.X, pt.Y) { }

            public static implicit operator System.Drawing.Point(POINT p)
            {
                return new System.Drawing.Point(p.X, p.Y);
            }

            public static implicit operator POINT(System.Drawing.Point p)
            {
                return new POINT(p.X, p.Y);
            }
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct BLENDFUNCTION
        {
            public byte BlendOp;
            public byte BlendFlags;
            public byte SourceConstantAlpha;
            public byte AlphaFormat;

            public BLENDFUNCTION(byte op, byte flags, byte alpha, byte format)
            {
                BlendOp = op;
                BlendFlags = flags;
                SourceConstantAlpha = alpha;
                AlphaFormat = format;
            }
        }

        public const int AC_SRC_OVER = 0x00;
        public const int AC_SRC_ALPHA = 0x01;

        private readonly Image _left;
        private readonly Image _right;
        private readonly Bitmap _backBuffer;
        private readonly Timer _timer;
        private int _offset;

        public OverlayForm(Image left, Image right, Rectangle bounds)
        {
            _left = left;
            _right = right;
            _backBuffer = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);
            _timer = new Timer();
            _offset = 0;

            Bounds = bounds;

            FormBorderStyle = FormBorderStyle.None;
            StartPosition = FormStartPosition.Manual;
            ShowInTaskbar = false;
        }

        protected override bool ShowWithoutActivation
        {
            get { return true; }
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= WS_EX_LAYERED; // This form has to have the WS_EX_LAYERED extended style
                cp.ExStyle |= WS_EX_TRANSPARENT; // Click through.
                cp.ExStyle |= WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
                return cp;
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            _timer.Interval = 16;
            _timer.Tick += _timer_Tick;
            _timer.Start();
        }

        private void UpdateLayeredWindow()
        {
            using (Graphics g = Graphics.FromImage(_backBuffer))
            {
                g.Clear(Color.Transparent);
                g.DrawImage(_left, 0 - _offset, 0);
                g.DrawImage(_right, Width - _right.Width + _offset, 0);
                SetBitmap(_backBuffer);
            }
        }

        private void SetBitmap(Bitmap bitmap)
        {
            // 1. Create a compatible DC with screen;
            // 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
            // 3. Call the UpdateLayeredWindow.

            IntPtr screenDc = GetDC(IntPtr.Zero);
            IntPtr memDc = CreateCompatibleDC(screenDc);
            IntPtr hBitmap = IntPtr.Zero;
            IntPtr oldBitmap = IntPtr.Zero;

            try
            {
                hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));  // grab a GDI handle from this GDI+ bitmap
                oldBitmap = SelectObject(memDc, hBitmap);

                SIZE size = new SIZE(bitmap.Width, bitmap.Height);
                POINT pointSource = new POINT(0, 0);
                POINT topPos = new POINT(Left, Top);
                BLENDFUNCTION blend = new BLENDFUNCTION();
                blend.BlendOp = AC_SRC_OVER;
                blend.BlendFlags = 0;
                blend.SourceConstantAlpha = 255;
                blend.AlphaFormat = AC_SRC_ALPHA;

                bool fail = UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, ULW_ALPHA);
            }
            finally
            {
                ReleaseDC(IntPtr.Zero, screenDc);
                if (hBitmap != IntPtr.Zero)
                {
                    SelectObject(memDc, oldBitmap);
                    DeleteObject(hBitmap);
                }
                DeleteDC(memDc);
            }
        }

        void _timer_Tick(object sender, EventArgs e)
        {
            int distance = _left.Width - _offset;

            // Is animation done?
            if (distance == 0)
            {
                _timer.Stop();
                Close();
                return;
            }

            // Step forward.
            // You can replace with any easing function of your choice,
            // but this one works well usually.
            int step = distance / 9;

            // Help it a little when the animation is about to end.
            if (step == 0)
                step = distance;

            _offset += step;

            UpdateLayeredWindow();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing)
            {
                _left.Dispose();
                _right.Dispose();
                _backBuffer.Dispose();
                _timer.Dispose();
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    看看this! 这是一个不同的问题,但在张贴的图像上,您可以看到三个不同的图像,并且它们之间以及背景之间都是透明的。

    您可以通过覆盖面板的某些方法来解决它,如下所示:

    abstract public class DrawingArea : Panel
    {
        protected Graphics graphics;
        abstract protected void OnDraw();
    
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x00000020; 
                return cp;
            }
        }
    
        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
    
        }
    
        protected override void OnPaint(PaintEventArgs e)
        {
            this.graphics = e.Graphics;
            this.graphics.TextRenderingHint =
                System.Drawing.Text.TextRenderingHint.AntiAlias;
            this.graphics.InterpolationMode =
                System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
            this.graphics.PixelOffsetMode =
                System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
            this.graphics.SmoothingMode =
                System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            OnDraw();
        } 
    }
    

    您可以重复使用这个“DrawingArea”类,将其扩展到您的门或您想要的任何类型的透明图像,例如以下示例:

    public class TransparentImage : DrawingArea
    {
    
        private bool draw = false;
    
        private Image Imagem;
    
        public Image imagem
        {
            get { return Imagem; }
            set
            {
                Imagem = value;
                draw = true;
                this.Size = Imagem.Size;
            }
        }
    
        protected override void InitLayout()
        {
            this.BringToFront();
        }
    
    
        public TransparentImage()
        {
    
    
        }
    
    
        protected override void OnDraw()
        {
            if (draw)
            {
                Rectangle location = new Rectangle(0, 0, Imagem.Width, Imagem.Height);
                this.graphics.DrawImage(Imagem, location);
            }
        }
    }
    

    这个“transparentImage”将作为一个组件出现在您的工具箱窗口中,以便您可以从那里实例化它。

    在开门/关门动画上,你可能会发现一些闪烁,所以,建议使用双缓冲来平滑动画。

    winforms 绝对不是最好的做法,我可以用我自己的错误来说话 :)

    【讨论】:

      【解决方案3】:

      您似乎正在寻找一个简单的解决方案。

      看看这个线程: Transparent images with C# WinForms

      要以“简单”的方式达到最终效果,我想您应该执行以下操作

      1 - 您需要制作一个新的启动屏幕表单来加载全屏。在您的主表单上,当您的表单正在加载时,您可以将此初始屏幕表单显示为“模态”表单。

      2 - 尝试覆盖 OnPaintBackground 以便不为背景绘制任何内容。

       protected override void OnPaintBackground(PaintEventArgs e)
       {
          //Do not paint background
       }
      

      3 - 放置图像并随着时间的推移制作效果。看看我在顶部发布的链接。如果它困扰您,它也可能有助于制作透明图像。

      4 - 在动画结束时,启动画面自行卸载。

      【讨论】:

        猜你喜欢
        • 2017-03-12
        • 2011-03-14
        • 2011-09-24
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        • 1970-01-01
        • 2022-01-28
        • 2013-08-16
        相关资源
        最近更新 更多