如何使用计时器将淡入淡出过渡效果应用于PictureBox图像? [英] How to apply a fade transition effect to PictureBox Images using a Timer?

查看:102
本文介绍了如何使用计时器将淡入淡出过渡效果应用于PictureBox图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在两个PictureBox控件之间进行淡入淡出过渡.
每次经过时,我都使用Timer来使用GetPixelSetPixel更改两个PictureBox的不透明度.

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 every time the time elapses.

在此阶段,问题在于此代码导致异常:

At this stage, the problem is that this code causes an exception:

System.InvalidOperationException:当前在其他地方使用该对象

我试图修改克隆的位图,而不是直接对设置为控件的图像"属性的位图进行操作,但是仍然无法正常工作.
这是我的代码:

I tried to modify cloned Bitmaps instead of directly operate on the Bitmaps set to the Image properties of the controls, but it doesn't work anyway.
Here is my code:

public Bitmap changeOpacity(Bitmap pic, int opacity)
{
    for (int w = 0; w < pic.Width; w++)
    {
        for (int h = 0; h < pic.Height; h++)
        {
            Color c = pic.GetPixel(w, h);
            Color newC = Color.FromArgb(opacity, c);
            pic.SetPixel(w, h, newC);
        }
    }
    return pic;
}

public void CrossFade(PictureBox pictureOut, PictureBox pictureIn, int duration)
{
    int outChange = 255; // opacity of pictureOut
    int inChange = 0;    // opacity of pictureIn
    int change = 55;     // change of opacity 
    fadeTimer.Interval = 10; // this timer's type is System.Timers.Timer
    Bitmap bmp = new Bitmap(pictureIn.Image);
    // make the pictureIn transparent first
    pictureIn.Image = changeOpacity((Bitmap)bmp.Clone(), 0);
    fadeTimer.Elapsed += (sender, e) => CrossFadeEvent(sender, e, pictureOut, pictureIn, outChange, inChange, change);
    fadeTimer.Start();
}

// being called every time interval
private void CrossFadeEvent(Object source, System.Timers.ElapsedEventArgs e, PictureBox pictureOut, PictureBox pictureIn, int oChange, int iChange, int change)
{
    if (iChange <= 255)
    {
        oChange -= change;
        iChange += change;
        textBox1.Text = iChange.ToString();
        pictureOut.Image = changeOpacity((Bitmap)pictureOut.Image.Clone(), oChange);
        pictureIn.Image = changeOpacity((Bitmap)pictureIn.Image.Clone(), iChange);
    }
    else if (iChange > 255)
    {
        pictureIn.Image = changeOpacity((Bitmap)pictureOut.Image.Clone(), 255);
        fadeTimer.Stop();
    }
}

推荐答案

这里有一些问题需要解决:

There are some problem to fix here:

fadeTimer.Interval = 10; :
您(可能)使用了错误的计时器:系统. Timers.Timer 已消逝.除非您已将 SynchronizingObject 设置为控件对象,然后用于封送处理程序调用,在处理程序中引用控件会引起麻烦(跨线程冲突异常). 在这种情况下,您可以使用 System.Windows.而是使用Forms.Timer :其Tick事件在UI线程中引发.
而且,计时器间隔太短. System.Windows.Forms.Timer的标准(官方)分辨率为 55ms (高于System.Timers.Timer).您最终会发生重叠的事件.

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() :
不能用于此任务.当顺序调用以设置多个像素时,这些方法太慢.它们用于修改一小组像素(或仅修改一个像素),而不是修改整个图像的像素.
Bitmap.LockBits()是常见的用于设置位图颜色字节的工具.

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); :
您正在使用控件的Image属性提供源位图,然后使用与修改后相同的源来设置提供源的相同属性.
这将永远不会给您完整的 fadding 图片,并且您正在寻找麻烦.

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.

有一个简单的工具可以很容易地完成此任务: ColorMatrix 类.此类处理标准的5x5矩阵,提供了一些简化的工具,可用于设置矩阵组件的值.
[3, 3]( Matrix3x3 )处的5x5矩阵分量表示所有RGB分量的Alpha值.
使用ColorMatrix类应用于位图> ImageAttributes 类的"> SetColorMatrix()方法,然后将其传递给

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.

由于此 Fade 过程在其他情况下可能很有用,因此我认为创建扩展方法是一个好主意:它将扩展方法添加到新的 SetOpacity() 方法位图类.
如果淡入淡出效果需要,它还可以同时更改Gamma.

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.

剩下的是加载两个位图,创建一个计时器,设置一个明智的 Interval(此处为100ms),然后在两个PictureBox控件的表面上绘制位图(此处为三个)测试一个简单的混合效果).

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 ).

不透明度增加/减少值设置为.025f,因此不透明度每秒更改0.0f-1.0f最大范围的1/4.根据需要进行调整.

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.


256种颜色不是很好

►将扩展类添加到项目中.
►使用3个PictureBox控件设置一个窗体,并将在此处找到的3个事件句柄分配给它们.
►不要在设计时将位图分配给PictureBox.如示例代码所示,将在运行时加载位图.
►添加一个按钮以启动计时器.
►当fading过程终止时,您将立即重启计时器,因为它本身会倒退(淡入淡出,将相反的效果应用于每个位图和混合的位图).

► 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).

将诊断工具保持打开状态:即使多次重复该操作,您也不会浪费单个MB的内存.

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

扩展方法:
扩展方法(C#编程指南)

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

这篇关于如何使用计时器将淡入淡出过渡效果应用于PictureBox图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆