C# 如何使用计时器对PictureBox图像应用淡入淡出过渡效果?

C# 如何使用计时器对PictureBox图像应用淡入淡出过渡效果?,c#,winforms,timer,picturebox,fade,C#,Winforms,Timer,Picturebox,Fade,我正在尝试在两个PictureBox控件之间进行淡入淡出转换。 每次时间流逝时,我都会使用GetPixel和SetPixel来更改两个图片框的不透明度 在此阶段,问题是此代码导致异常: System.InvalidOperationException:对象当前正在其他地方使用 我试图修改克隆的位图,而不是直接对设置为控件图像属性的位图进行操作,但无论如何都不起作用。 这是我的密码: public Bitmap changeOpacity(Bitmap pic, int opacity) {

我正在尝试在两个PictureBox控件之间进行淡入淡出转换。
每次时间流逝时,我都会使用
GetPixel
SetPixel
来更改两个图片框的不透明度

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

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

我试图修改克隆的位图,而不是直接对设置为控件图像属性的位图进行操作,但无论如何都不起作用。
这是我的密码:

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();
    }
}
公共位图更改不透明度(位图pic,int不透明度)
{
对于(int w=0;wCrossFadeEvent(发送方,e,pictureOut,pictureIn,outChange,inChange,change);
fadeTimer.Start();
}
//每次都被呼叫
私有void CrossFadeEvent(对象源、System.Timers.ElapsedEventArgs e、PictureBox pictureOut、PictureBox pictureIn、int-oChange、int-iChange、int-change)
{
如果(iChange 255)
{
pictureIn.Image=changeOpacity((位图)pictureOut.Image.Clone(),255);
fadeTimer.Stop();
}
}

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

fadeTimer.Interval=10
您(可能)使用了错误的计时器:在线程池线程中引发了。除非已将设置为控件对象,然后使用该对象封送处理程序调用,否则在处理程序中引用控件将导致问题(跨线程冲突异常)。 在此上下文中,您可以改为使用:它的
勾选
事件在UI线程中引发。
此外,计时器间隔过低。
System.Windows.Forms.Timer
的标准(官方)分辨率为
55ms
(高于
System.Timers.Timer
)。你会以重叠的事件结束

GetPixel()
/
SetPixel()

无法用于此任务。这些方法在按顺序调用以设置多个像素时速度太慢。它们用于修改一小部分像素(或仅一个像素),而不是修改整个图像的像素。
是用于设置位图颜色字节的常用工具

pictureOut.Image=changeOpacity((位图)pictureOut.Image.Clone(),oChange)
您使用控件的Image属性来提供源位图,然后使用相同的源设置提供源位图的相同属性,已修改。
这将永远不会给你完全褪色的形象,你正在寻找麻烦

有一个简单的工具可以很容易地完成这个任务:类。此类处理标准5x5矩阵,提供一些简化的工具,允许设置矩阵组件的值。
[3,3]
矩阵x3x3
)处的5x5矩阵分量表示所有
RGB
分量的Alpha值。
使用
类的方法将
ColorMatrix
类应用于位图,然后将该类传递给接受
ImageAttributes
对象作为参数的重载

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

剩下的就是加载两个位图,创建一个计时器,设置一个合理的
间隔(此处为
100ms
),并在两个PictureBox控件的表面绘制位图(此处为三个,用于测试简单的混合效果)

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



由于GIF动画只能使用256色,因此质量会降低

▶ 将扩展类添加到项目中。
▶ 使用3个PictureBox控件设置窗体,并将您在此处找到的3个事件句柄分配给每个控件。
▶ 在设计时不要将位图指定给PictureBoxes。位图在运行时加载,如示例代码所示。
▶ 添加一个按钮以启动计时器。
▶ 当
淡入淡出过程终止时
会立即重新启动计时器,因为它会自动倒回(淡入淡出会重新开始,对每个位图和混合位图应用反向效果)

保持诊断工具处于打开状态:您将看到即使多次重复该操作,也不会浪费一MB内存

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