How to apply a fade transition effect to PictureBox Images using a Timer?

There are some problem to fix here:

fadeTimer.Interval = 10;: You're (possibly) using the wrong Timer: the System.Timers.Timer's Elapsed is raised in a ThreadPool Thread. Unless you have set the SynchronizingObject to a Control object, which is then used to marshal the handler calls, referencing a Control in the handler will cause trouble (cross-thread violation exceptions). In this context, you can use a System.Windows.Forms.Timer instead: its Tick event is raised in the UI thread. Also, the timer interval is to low. The standard (official) resolution of the System.Windows.Forms.Timer is 55ms (higher than the System.Timers.Timer). You end up with overlapping events.

GetPixel() / SetPixel(): cannot be used for this task. These methods are too slow when called sequentially to set multiple pixels. They're used to modify a small set of pixels (or just one), not to modify the pixels of a whole image. Bitmap.LockBits() is the common tool used to set the color bytes of Bitmaps.

pictureOut.Image = changeOpacity((Bitmap)pictureOut.Image.Clone(), oChange);: You're using the Image property of a Control to provide the source Bitmap, then you're setting the same property that provided the source using the same source, modified. This will never give you fully faded Image and you're looking for trouble.

There's a simple tool that can be used to accomplish this task quite easily: the ColorMatrix class. This class handles a standard 5x5 Matrix, providing some simplified tools that allow to set the values of the Matrix components. The 5x5 Matrix component at [3, 3] (Matrix3x3) represents the Alpha value of all the RGB components. The ColorMatrix class is applied to a Bitmap using the SetColorMatrix() method of the ImageAttributes class, which is then passed to the Graphics.DrawImage() overload that accepts an ImageAttributes object as argument.

Since this Fade procedure could be useful in other situations, I think it's a good idea to create an Extension Method: it adds a new SetOpacity() method to the Bitmap class. It can also change the Gamma at the same time, should the fade effect require it.

What's left is to load two Bitmaps, create a Timer, set a sensible Interval (100ms here) and paint the Bitmap on the surface of two PictureBox Controls (three here, to test a simple blending effect ).

The Opacity increment/decrement value is set to .025f, so the opacity changes 1/4 of the 0.0f-1.0f max range each second. To adjust as required.


The quality is reduced since the GIF animation can only use 256 colors

▶ Add the extension class to the Project. ▶ Setup a Form with 3 PictureBox Controls and assign the 3 event handles you find here to each of them. ▶ Don't assign a Bitmap to the PictureBoxes at design-time. The Bitmaps are loaded at run-time, as shown in the sample code. ▶ Add a Button to start the Timer. ▶ when the fading procedure terminates, you restart the Timer immediately, since it rewinds itself (the fading starts over, applying the inverse effect to each Bitmap and to the blended Bitmaps).

Keep the Diagnostics Tools open: you'll see that you don't waste a single MB of memory, even if you repeat the operation multiple times.

using System.Drawing;
using System.IO;
using System.Windows.Forms;

public partial class FormBitmaFadeTest : Form
{
    Bitmap sourceBmp1 = null;
    Bitmap sourceBmp2 = null;
    Bitmap fadeBmp1 = null;
    Bitmap fadeBmp2 = null;

    float opacity1 = 0.0f;
    float opacity2 = 1.0f;
    float increment = .025f;
    Timer timer = null;

    public FormBitmaFadeTest()
    {
        InitializeComponent();

        if (components == null) components = new System.ComponentModel.Container();
        components.Add(timer);

        string image1Path = [Source Image 1 Path];
        string image2Path = [Source Image 2 Path];

        sourceBmp1 = (Bitmap)Image.FromStream(new MemoryStream(File.ReadAllBytes(image1Path)));
        sourceBmp2 = (Bitmap)Image.FromStream(new MemoryStream(File.ReadAllBytes(image2Path)));
        fadeBmp1 = sourceBmp1.Clone() as Bitmap;
        fadeBmp2 = sourceBmp2.Clone() as Bitmap;
        timer = new Timer() { Interval = 100 };
        timer.Tick += this.TimerTick;
    }

    private void TimerTick(object sender, EventArgs e)
    {
        opacity1 += increment;
        opacity2 -= increment;
        if ((opacity1 >= 1.0f || opacity1 <= .0f) || (opacity2 >= 1.0f || opacity2 <= .0f)) {
            increment *= -1;
            timer.Stop();
        }
        fadeBmp1?.Dispose();
        fadeBmp2?.Dispose();
        fadeBmp1 = sourceBmp1.SetOpacity(opacity1);
        fadeBmp2 = sourceBmp2.SetOpacity(opacity2);
        pictureBox1.Invalidate();
        pictureBox2.Invalidate();
        pictureBox3.Invalidate();
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        if (fadeBmp1 == null) return;
        var units = GraphicsUnit.Pixel;
        e.Graphics.DrawImage(fadeBmp1, new RectangleF(PointF.Empty, pictureBox1.ClientSize), fadeBmp1.GetBounds(ref units), units);
    }

    private void pictureBox2_Paint(object sender, PaintEventArgs e)
    {
        if (fadeBmp2 == null) return;
        var units = GraphicsUnit.Pixel;
        e.Graphics.DrawImage(fadeBmp2, new RectangleF(PointF.Empty, pictureBox2.ClientSize), fadeBmp2.GetBounds(ref units), units);
    }

    private void pictureBox3_Paint(object sender, PaintEventArgs e)
    {
        if (fadeBmp1 == null || fadeBmp2 == null) return;
        var units = GraphicsUnit.Pixel;
        e.Graphics.DrawImage(fadeBmp2, new RectangleF(PointF.Empty, pictureBox3.ClientSize), fadeBmp2.GetBounds(ref units), units);
        e.Graphics.DrawImage(fadeBmp1, new RectangleF(PointF.Empty, pictureBox3.ClientSize), fadeBmp1.GetBounds(ref units), units);
    }
}

The extension method: Extension Methods (C# Programming Guide)

using System.Drawing;
using System.Drawing.Imaging;

public static class BitmapExtensions
{
    static float[][] fadeMatrix = {
        new float[] {1, 0, 0, 0, 0},
        new float[] {0, 1, 0, 0, 0},
        new float[] {0, 0, 1, 0, 0},
        new float[] {0, 0, 0, 1, 0},
        new float[] {0, 0, 0, 0, 1}
    };

    public static Bitmap SetOpacity(this Bitmap bitmap, float Opacity, float Gamma = 1.0f)
    {
        var mx = new ColorMatrix(fadeMatrix);
        mx.Matrix33 = Opacity;
        var bmp = new Bitmap(bitmap.Width, bitmap.Height);

        using (var g = Graphics.FromImage(bmp))
        using (var attributes = new ImageAttributes()) {
            attributes.SetGamma(Gamma, ColorAdjustType.Bitmap);
            attributes.SetColorMatrix(mx, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
            g.Clear(Color.Transparent);
            g.DrawImage(bitmap, new Rectangle(0, 0, bmp.Width, bmp.Height),
                0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, attributes);
            return bmp;
        }
    }
}

Comments

  1. Dylan

    • 2018/12/27

    There are some problem to fix here: → fadeTimer.Interval = 10; : You're (possibly) using the wrong Timer: the System.Timers.

  2. Malakhi

    • 2019/6/8

    The Bitmaps are loaded at run-time, as shown in the sample code. Add a Button to start the Timer. when the fading procedure terminates, you restart the Timer immediately, since it rewinds itself (the fading starts over, applying the inverse effect to each Bitmap and to the blended Bitmaps).

  3. Callan

    • 2021/2/9

    I am trying to make fade transition between two PictureBox Controls.I used a Timer to change the opacity of two PictureBoxes using GetPixel and SetPixel 

  4. Reuben

    • 2015/4/16

    Image Fade Using Timer. 04-24-2020 10:19 PM. I can get a color filled rectangle to fade using a timer using this below. Fill = RGBA (6, 43, 87, 1- 'Fade In Logo'.Value /'Fade In Logo'.Duration) This results in a rectangle over the logo fading completely to expose the logo. But I am hoping to use the logo's Transparency as shown in the image to

  5. Pellegrini

    • 2015/12/15

    You can do this with CSS (without an image or extra markup) using a ::before pseudo-element for the overlay, and linear-gradient for the fade with rgba() 

  6. Kingsley

    • 2018/3/10

    To make the image transition with a fading effect we use the asynchronous function. Inside the asynchronous function, use another setInterval() method to slowly decrease the opacity of the topmost image till opacity becomes 0. By doing this, the topmost image will appear to fade away slowly. Once, the topmost image is completely faded away

  7. Di Stefano

    • 2017/11/29

    as you can see, that code use 3 PictureBox as sample, If I populate PictureBox3 with the image in PictureBox1 before starting the timer, 

  8. Jamari

    • 2019/5/12

    Timer t1 = new Timer (); private void Form1_Load (object sender, EventArgs e) {Opacity = 0; //first the opacity is 0 t1. Interval = 10; //we'll increase the opacity every 10ms t1. Tick += new EventHandler (fadeIn); //this calls the function that changes opacity t1. Start ();} void fadeIn (object sender, EventArgs e) {if (Opacity >= 1) t1.

  9. Moore

    • 2016/2/1

    how animate picturebox change 1 image on mouse hover event fade-in effect? i create animated picturebox control. toolbox item ease-of-use.

  10. Allen

    • 2018/11/25

    Private Sub FadeForm(ByVal TotalSeconds As Single) If TotalSeconds = 0 Then Me.Opacity = 1 Exit Sub End If Dim [then] As Double = DateAndTime.Timer Dim difference As Double = 0 'difference is the percentage of the total seconds elapsed Do While difference < 1 Me.Opacity = difference difference = (DateAndTime.Timer - [then]) / TotalSeconds System.Threading.Thread.Sleep(10) Loop Me.Opacity = 1 End Sub Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown

  11. Alden

    • 2020/5/6

    I am wondering how to use timing in order to allow for a smooth transition from picture box to picture box, and vice versa?

  12. Castiel

    • 2016/11/4

    The transition-timing-function property specifies the speed curve of the transition effect. The transition-timing-function property can have the following values: ease - specifies a transition effect with a slow start, then fast, then end slowly (this is default) linear - specifies a transition effect with the same speed from start to end; ease

  13. Martinez

    • 2020/10/21

    intCount is a Counter we will use in the Timer. Add the Form_Load event. It is the sequence, if you may, in which the images are faded.

  14. Aiden

    • 2015/8/25

    Define the transition bitmap. Define the transition rectangle. Define and calculate the transition scale factor used in the matrix. Define the matrix class and pass the scale factor to it, the effect will start from the rectangle mid-point. Pass the matrix to the graphics and draw the effect.

  15. Denver

    • 2017/4/12

    I'm trying to implement a way to animate (translate, fade) controls around To slide controls in WinForms, you can use a Timer to gradually change the 

  16. Graham

    • 2017/9/4

    It's also available on nuget as dot-net-transitions. To slide controls in WinForms, you can use a Timer to gradually change the Top and/or Left 

  17. Rylan

    • 2020/2/26

    Use graphics programs to create these effects, then import them into the form's and controls' BackgroundImage or Image properties. Use a Timer, for example, 

Comments are closed.

Recent Posts