这里有一些问题需要解决:
→ fadeTimer.Interval = 10;
:
您(可能)使用了错误的计时器:系统.定时器.定时器 https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer's Elapsed https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer.elapsed事件在 ThreadPool 线程中引发。除非你已经设置了同步对象 https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer.synchronizingobject到 Control 对象 - 然后将其用作编组器 - 在处理程序中引用 Control 将导致麻烦(跨线程违规异常)。
在这种情况下,您可以使用系统.Windows.Forms.定时器 https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.timer相反:它的Tick
事件在 UI 线程中引发。
此外,计时器间隔太低。你最终会遇到重叠的事件。
→ GetPixel()
/ SetPixel()
:
不能用于此任务。当顺序调用这些方法来设置多个像素时,这些方法太慢。它们用于修改一小组像素(或仅一个),而不是修改整个图像的像素。
位图.LockBits() https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits是用于设置位图颜色字节的常用工具。
→ pictureOut.Image = changeOpacity((Bitmap)pictureOut.Image.Clone(), oChange);
:
您使用控件的 Image 属性来提供源位图,然后使用修改后的相同源设置提供源的相同属性。
这永远不会给你完全faded图像和你正在寻找麻烦。
有一些标准工具可以用来非常轻松地完成此任务:色彩矩阵 https://learn.microsoft.com/en-us/dotnet/api/system.drawing.imaging.colormatrix and 图像属性 https://learn.microsoft.com/en-us/dotnet/api/system.drawing.imaging.imageattributes类。前者代表一个标准的5x5 Matrix;后者提供了允许设置矩阵组件值的方法。
5x5 矩阵组件位于[3, 3]
(Matrix3x3
) 表示所有的 Alpha 值RGB
成分。
The ColorMatrix
类应用到位图使用设置颜色矩阵() https://learn.microsoft.com/en-us/dotnet/api/system.drawing.imaging.imageattributes.setcolormatrix的方法ImageAttributes
类,然后传递给Graphics.DrawImage() https://learn.microsoft.com/en-us/dotnet/api/system.drawing.graphics.drawimage接受一个重载ImageAttributes
对象作为参数。
从此Fade过程在其他情况下可能很有用,我认为创建一个扩展方法是个好主意:它添加了一个新的SetOpacity()
Bitmap 类的方法。
如果淡入淡出效果需要,它还可以同时更改伽玛值。
剩下的就是加载两个位图,创建一个计时器,设置一个sensible Interval
(100ms
此处)并在两个 PictureBox 控件(此处为三个,以测试简单的混合效果)的表面上绘制位图。
不透明度增量/减量值设置为.025f
,所以不透明度发生变化1/4
of the 0.0f-1.0f
每秒最大范围。根据需要进行调整。
The quality is reduced since the GIF animation can only use 256 colors
▶ 将扩展类添加到项目中。
▶ 设置一个包含 3 个 PictureBox 控件的表单,并将您在此处找到的 3 个事件句柄分配给每个控件。
▶ 不要在设计时将位图分配给PictureBox。位图在运行时加载,如示例代码所示。
▶ 添加一个按钮来启动计时器。
▶ 当fading
程序终止后,您立即重新启动计时器,因为它rewinds本身(淡入淡出重新开始,将相反的效果应用于每个位图和混合位图)。
保持诊断工具打开:您会发现即使多次重复该操作,也不会浪费一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);
}
}
扩展方法:
扩展方法(C# 编程指南) https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
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;
}
}
}