2

First of all, I'd like to suggest that it is not a duplicate of THIS question. At least that's my opinion :)

What I want to achieve is a series of frames to "fade" animation.

I choose two PNG files (let' say they are the same size), for example:

I want to "simulate" merging them like layers in graphic editor. I put Pic1 on the top with opacity 255, Pic2 below with opacity 0, so at first I see only Pic1. Then I change their opacity, like this:

Is there any simple way for it?

Community
  • 1
  • 1
WRonX
  • 161
  • 3
  • 15

2 Answers2

4

In a winforms app this can be done pretty easily. Create a user control with a few properties:

public Image FromImage { get; set; }
public Image ToImage { get; set; }

private float opacity = 1;

Now override OnPaint

 protected override void OnPaint(PaintEventArgs e)
    {
        if (FromImage != null && ToImage != null)
        {
            ColorMatrix matrix1 = new ColorMatrix();
            matrix1.Matrix33 = opacity;
            ImageAttributes attributes1 = new ImageAttributes();
            attributes1.SetColorMatrix(matrix1, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);


            ColorMatrix matrix2 = new ColorMatrix();
            matrix2.Matrix33 = 1 - opacity;
            ImageAttributes attributes2 = new ImageAttributes();
            attributes2.SetColorMatrix(matrix2, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

            e.Graphics.DrawImage(FromImage, new Rectangle(0, 0, this.Width, this.Height), 0, 0, this.Width,
                                 this.Height, GraphicsUnit.Pixel, attributes1);
            e.Graphics.DrawImage(ToImage, new Rectangle(0, 0, this.Width, this.Height), 0, 0, this.Width,
                                 this.Height, GraphicsUnit.Pixel, attributes2);
        }
        base.OnPaint(e);
    }

Now drop a timer onto the control, set its to enabled with an elapsed time of something like 100ms. Handle the tick event:

private void timer_Tick(object sender, EventArgs e)
    {
        if(opacity == 0)
        {
            this.timer.Stop();
            return;
        }

        this.opacity -= 0.01f;
        this.Invalidate();
    }

et voila. However, there's one thing to be aware of. This makes quite a flickery transition, which can be alieviated somewhat with this line in the control's constructor:

this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint,true);

Update based on Edit: You could turn this into a utility that takes 2 images and, using much the same code, outputs each step to a new image. Somthing like:

public class ImageUtility
{
    private Image image1;
    private Image image2;

    public ImageUtility(Image image1, Image image2)
    {
        this.image1 = image1;
        this.image2 = image2;
    }

    public void SaveTransitions(int numSteps, string outDir)
    {
        var opacityChange = 1.0f/(float) numSteps;

        for(float opacity = 1,i=0;opacity>0;opacity-=opacityChange,i++)
        {
            using(var image = new Bitmap(image1.Width,image2.Width))
            {
                Graphics g = Graphics.FromImage(image);
                ColorMatrix matrix1 = new ColorMatrix();
                matrix1.Matrix33 = opacity;
                ImageAttributes attributes1 = new ImageAttributes();
                attributes1.SetColorMatrix(matrix1, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);


                ColorMatrix matrix2 = new ColorMatrix();
                matrix2.Matrix33 = 1 - opacity;
                ImageAttributes attributes2 = new ImageAttributes();
                attributes2.SetColorMatrix(matrix2, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

                g.DrawImage(image1, new Rectangle(0, 0, image1.Width, image1.Height), 0, 0, image1.Width,
                                     image1.Height, GraphicsUnit.Pixel, attributes1);
                g.DrawImage(image2, new Rectangle(0, 0, image2.Width, image2.Height), 0, 0, image2.Width,
                                     image2.Height, GraphicsUnit.Pixel, attributes2);

                image.Save(Path.Combine(outDir,"Image" + i + ".png"),ImageFormat.Png);
            }
        }
    }

Usage:

ImageUtility util = new ImageUtility(Image.FromFile(@"C:\path\pic1.png"), Image.FromFile(@"C:\path\pic2.png"));
util.SaveTransitions(100, @"C:\path\output"); // saves 100 images 
Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • Not sure if I understand it - where's the "second" image? I want to fade one image to another and save every "frame" of the animation. – WRonX Sep 02 '11 at 12:38
  • @WRonX - see update. Pretty easy to turn the same code to a util that saves each transition out to disk. – Jamiec Sep 02 '11 at 13:08
  • I'll check it, but I suppose it can be the answer. Thank you! My solution (in comments to question) fails at transparent PNGs, so I'll check yours. I assume if I need a preview, I can just cast and assign, I mean pictureBox1.Image=(Image)image; ?? – WRonX Sep 02 '11 at 13:43
  • You *suppose* it can be the answer? Dont sound too enthusiastic or anything. – Jamiec Sep 02 '11 at 13:47
  • Maybe it sounded that way, because: 1. My English isn't good. 2. I'm usually not that good at C# to know if it is what I meant only by reading the code and I can't check it now (different place, different machine). Sorry, if it offended you, I didn't mean that way. I *AM* enthusiastic about ending what I started, so once again: thank you very much! – WRonX Sep 02 '11 at 14:36
  • @WRonX - you're very welcome. I was only being a bit sarcastic with my comment above :) – Jamiec Sep 02 '11 at 14:39
1

Using you can use Graphics.DrawImage, using the overload that takes an ImageAttributes parameter. That class can specify manipulation to the colour (and alpha) values.

The example on the ImageAttributes page is nearly what you want. Just draw the original and transformed one in the same place, and change the colour matrix to only change the alpha level.

George Duckett
  • 31,770
  • 9
  • 95
  • 162