0

UPDATE: Changed code to newer version.

I have found some code here on stackoverflow but am not very satisfied. I'm not very familiar with GDI+ so maybe it's a stupid question.

I have a class to set Brightness/Contrast/Gamma:

public class ImageManipulation : IDisposable
{
    Bitmap _internalBitmapMemory;

    public ImageManipulation(Bitmap bitmap)
    {
        _internalBitmapMemory = new Bitmap(bitmap);
    }

    public Bitmap AdjustContrast(float Contrast = 1, float Brightness = 1, float Gamma = 1)
    {
        float brightness = Brightness;      // no change in brightness
        float contrast = Contrast;          // twice the contrast
        float gamma = Gamma;                // no change in gamma

        float adjustedBrightness = brightness - 1.0f;

        // create matrix that will brighten and contrast the image
        float[][] ptsArray =
        {
            new float[] {contrast,              0,                      0,                      0,                  0},     // scale red
            new float[] {0,                     contrast,               0,                      0,                  0},     // scale green
            new float[] {0,                     0,                      contrast,               0,                  0},     // scale blue
            new float[] {0,                     0,                      0,                      1.0f,               0},     // don't scale alpha
            new float[] {adjustedBrightness,    adjustedBrightness,     adjustedBrightness,     0,                  1}
        };

        var imageAttributes = new ImageAttributes();

        imageAttributes.ClearColorMatrix();
        imageAttributes.SetColorMatrix(new ColorMatrix(ptsArray), ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
        imageAttributes.SetGamma(gamma, ColorAdjustType.Bitmap);

        Graphics g = Graphics.FromImage(_internalBitmapMemory);

        g.DrawImage(_internalBitmapMemory, new Rectangle(0, 0, _internalBitmapMemory.Width, _internalBitmapMemory.Height)
            , 0, 0, _internalBitmapMemory.Width, _internalBitmapMemory.Height,
            GraphicsUnit.Pixel, imageAttributes);

        g.Dispose();

        return _internalBitmapMemory;
    }

    public void Dispose()
    {
        _internalBitmapMemory.Dispose();
        _internalBitmapMemory = null;
    }
}

Now I got some problems:

1) Performance becomes lower and lower after n iterations. 2) Memory is blasted if the GC is too slow to clear it up. I now tried to solve this by using a class and creating the referece with a using statement.

This works so far but now my image is disposed before it's displayed. GRML!

How can I optimize / make it running the whole thing?

AllDayPiano
  • 414
  • 1
  • 4
  • 20
  • 1
    See [here](http://stackoverflow.com/questions/12709360/whats-the-difference-between-bitmap-clone-and-new-bitmapbitmap) for why Clone is useless for you. You also need to make sure to dispose of as many bitmaps as you create, eventually. `g.DrawImageUnscaled(backimage` draws an empty image because backimage is empty. – TaW Mar 30 '16 at 11:34
  • 1
    Never using the Dispose() method is a traditional .NET programming mistake. Yes, the Bitmap class is the singular class that reminds you that this is not really optional, it uses a lot of unmanaged memory. – Hans Passant Mar 30 '16 at 11:43
  • @Hans: And where can I dispose anything? – AllDayPiano Mar 30 '16 at 11:47
  • @TaW: By not using this clone function it will overwrite my passed image. Using clone it won't happen. So if it doesn't copy, why does it work? – AllDayPiano Mar 30 '16 at 11:48
  • @TaW: I read a lot of cloning and made a lot of experiments with .Clone() but were not able to find the point, why it should not help. On the contrairy it is definately cloning... Can you please point out how you did come to your opinion? – AllDayPiano Apr 04 '16 at 11:37
  • Well, if you works for you all is well; the link I gave explains that Clone actually does not clone the piexeldata themselve, but maybe you don't need that..? – TaW Apr 04 '16 at 12:08
  • @TaW: Sure I've read your link. the point about clone is that there's only a shallow copy and - what's more important for many purposes - the lock will persist. But, and that's the point that's interesting for me - clone will actually clone an image, if you start to manipulate the copy. this is what I found here on SO in a very interesting post which I - unfortunatly - can't find anymore :( – AllDayPiano Apr 06 '16 at 10:02

1 Answers1

0

I got it. Was no big deal but some mess with that graphics object:

Bitmap _bitmap;

private void BuildImage()
{
    using (ImageManipulation cls = new ImageManipulation(_bitmap))
    {
        ImageBox.Image.Dispose();
        ImageBox.Image = (Bitmap)cls.AdjustContrast((float)nudContrast.Value, (float)nudBrightness.Value, (float)nudGamma.Value).Clone();
    }
}

public class ImageManipulation : IDisposable
{
    Bitmap _internalBitmapMemory;

    public ImageManipulation(Bitmap bitmap)
    {
        _internalBitmapMemory = new Bitmap(bitmap);
    }

    public Bitmap AdjustContrast(float Contrast = 1, float Brightness = 1, float Gamma = 1)
    {
        float brightness = Brightness;      // no change in brightness
        float contrast = Contrast;          // twice the contrast
        float gamma = Gamma;                // no change in gamma

        float adjustedBrightness = brightness - 1.0f;

        // create matrix that will brighten and contrast the image
        float[][] ptsArray =
        {
            new float[] {contrast,              0,                      0,                      0,                  0},     // scale red
            new float[] {0,                     contrast,               0,                      0,                  0},     // scale green
            new float[] {0,                     0,                      contrast,               0,                  0},     // scale blue
            new float[] {0,                     0,                      0,                      1.0f,               0},     // don't scale alpha
            new float[] {adjustedBrightness,    adjustedBrightness,     adjustedBrightness,     0,                  1}
        };

        var imageAttributes = new ImageAttributes();

        imageAttributes.ClearColorMatrix();
        imageAttributes.SetColorMatrix(new ColorMatrix(ptsArray), ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
        imageAttributes.SetGamma(gamma, ColorAdjustType.Bitmap);

        Graphics g = Graphics.FromImage(_internalBitmapMemory);

        g.DrawImage(_internalBitmapMemory, new Rectangle(0, 0, _internalBitmapMemory.Width, _internalBitmapMemory.Height)
            , 0, 0, _internalBitmapMemory.Width, _internalBitmapMemory.Height,
            GraphicsUnit.Pixel, imageAttributes);

        g.Dispose();

        return _internalBitmapMemory;
    }

    public void Dispose()
    {
        _internalBitmapMemory.Dispose();
        _internalBitmapMemory = null;
    }
}

Problem about the mem-leak was the picture box, that's not disposing the image automatically if you add a new image but keeps it in memory until die GC will clean up the mess.

Now it works as expected.

What probably should be done is adding a deep copy if the image is used for more than simply displaying it on a picture box.

AllDayPiano
  • 414
  • 1
  • 4
  • 20
  • 3
    What's the reason for people giving bad ratings on my answer? If you want other people to improve theirself - don't you think it would help to give an answer instead of simply down rating them? As long as there's no suggestion for a better solution, there's no point in simply voting without helping! – AllDayPiano Apr 04 '16 at 11:34