3

I have a collection of images in a windows form, and want to make them 'non rectangular' - i.e, a transparent background.

However, when using the transparencyKey, yes both loose their background color. However, I can only see one and not the other at any one time (depending on which is 'broughtToFront').

Is there a way of having two images displayed on the form, both being 'non rectangular', and both have transparent backgrounds?

Please note: I have managed to get the background to be transparent (TransparencyKey is set to a gray (RGB of 66,66,66), and both images have the same background.

I have heard of the term 'regions', but from what I can tell, this is usually a very 'Heavy' way on the CPU.

Any advice much appreciated and happy to answer any clarification questions (as I am not very good at explaining myself).

Note. The 'effect' I am trying to achieve is similar to that of the X-Men doors opening on start up of the project - with my desktop being behind the doors.

Where 'leftDoor' moves to left and 'rightDoor' moves to right

  • Would it be possible to have both (or several) images in the same image control instead of separate control for each image? Can you explain what do you mean with the X-Men doors opening or post an image which will show the desired result. – Tomaz Tekavec Aug 20 '14 at 09:01
  • @TomažTekavec Since the two different door parts are going to be moving in different directions, as well as not being 'square'. I was going to have the doors start 'closed' and when the system has loaded, for them to 'open'. I'm not sure if they can be in a single **picturebox**, but they are currently in separate ones. –  Aug 20 '14 at 09:07
  • 1
    Do you have to use WinForms for this project? It is a bit outdated and provides only a limited number of features. I'm sure you scenario can be more easily implemented using WPF. Alternatively, you could create you own class, deriving from Panel and do the drawing yourself by using the Graphics object (I can provide a small snippet if you require one). – WeSt Aug 20 '14 at 09:19
  • Hmm, take a look here, maybe this could solve your problem: http://stackoverflow.com/questions/19910172/how-to-make-picturebox-transparent – Tomaz Tekavec Aug 20 '14 at 09:20
  • @WeSt It has to be in winforms (it's a pain, I know). Tomaz, that SORT of helps, but the 'backImage' also needs to be transparent (as otherwise it still shows up incorrectly) –  Aug 20 '14 at 09:27
  • I hope it to look something similar to **rainmeter** when it's complete –  Aug 20 '14 at 09:50
  • Depending what exactly you want to achieve you may be happy with this: Put one image into a PictureBox as its BackgroundImage and the other one as its Image. Won't work with animation though. Go WPF or ownerDraw! – TaW Aug 20 '14 at 12:14
  • So basically you want to animate the opening of the doors? The background is the desktop? – γηράσκω δ' αεί πολλά διδασκόμε Aug 22 '14 at 12:06
  • Yes, pretty much. desktop/another a 'invisible form' with almost widget-like buttons –  Aug 22 '14 at 12:08
  • Well, technically MrCoder didn't accepted my answer. I should acheive the desired result though. – Romain Hautefeuille Nov 04 '14 at 13:39

3 Answers3

6

This attempt uses the UpdateLayeredWindow API : http://msdn.microsoft.com/en-us/library/ms997507.aspx#layerwin_topic2a

I inspired my implementation with this one from Corylulu : https://stackoverflow.com/a/8809657/1812199

The two bitmaps are created on the fly but you can load them from the file system or whatever.

The overlay window currently spreads over the whole primary screen but you can also choose to set the location and size of the form to whatever your need is (e.g. on top of another form).

I implemented a very basic animation for the two bitmaps but it is up to you to be more creative than me :)

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();
            }
        }
    }
}
Community
  • 1
  • 1
-1

take a look at this! Its a different problem, but on the image posted there, you can see three different images and all are transparent between each others and between the background also.

You can solve it by overriding some methods of the panel, as you can see here:

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();
    } 
}

And you can reuse this "DrawingArea" class extending it to your door or any kind of transparent image you want, such the following example:

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);
        }
    }
}

This "transparentImage" will appear on your toolbox window as a component, so that you can instantiate it from there.

On the door open/close animation, you may find some flickering, so, it is recommended to use double buffering to smooth the animation.

Definitely winforms is not the best practice for this, and i can talk by my own mistakes :)

Community
  • 1
  • 1
Jorge Oliveira
  • 345
  • 1
  • 2
  • 13
-1

It seems like you are looking to a simple solution.

Take a look at this thread: Transparent images with C# WinForms

To achieve the final effect in an "easy" way, I guess you should do the following

1 - You will need to make a new splash screen form that loads fullscreen. On your main form, while your form is loading, you could show this splash screen form as a 'modal' form.

2 - Try overriding the OnPaintBackground in order to don't draw anything for is background.

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

3 - Place the images and make the effect as times goes by. Take a look at the link I posted at the top. It might help making transparent images as well if it is bugging you.

4 - At the end of the animation, the splash screen unloads itself.

Community
  • 1
  • 1
rodrigogq
  • 1,943
  • 1
  • 16
  • 25