@bobnoordam

Reduce an image with transparant background to the minimum required canvas

This static method reduces an image with a transparant background to the minimal canvas needed. This code is part of my graphics processing library (PDrawing) and is hereby donated to the public domain. This process is best performed in a pre-process or background processing way, since it may take several seconds to process a large image. (This can be optimized by using #unsafe and allocating a fixed memory block and directly changing data in the image memory block, removing the need to use GetPixel. However, i prefer fully managed code and have no need for on the fly processing, so this is what i use and have to offer).

/// <summary>
/// Reduce an image to the absolute minimum canvas size by removing tranparancy
/// from all sides of the image.
/// </summary>
/// <param name="sourceImage"></param>
/// <returns></returns>
public static Image CompactImage(Image sourceImage) {
    var src = new Bitmap(sourceImage);
    // scan for full alpha lines from the left
    var offsetLeft = 0;
    for(var x = 0; x < src.Width; x++) {
        var valid = true;
        for(var y = 0; y < src.Height; y++) {
            Color color = src.GetPixel(x, y);
            if(color.A != 0) {
                valid = false;
            }
        }
        if(valid) {
            offsetLeft = x; // store fist used pixel offset left side
        } else {
            break;
        }
    }
    // scan from the right
    int offsetRight = src.Width - 1;
    for(int x = src.Width - 1; x >= 0; x--) {
        var valid = true;
        for(var y = 0; y < src.Height; y++) {
            Color color = src.GetPixel(x, y);
            if(color.A != 0) {
                valid = false;
            }
        }
        if(valid) {
            offsetRight = x; // store first used pixel offste right side
        } else {
            break;
        }
    }
    // scan from top
    var topoffset = 0;
    for(var y = 0; y < src.Height; y++) {
        var valid = true;
        for(var x = 0; x < src.Width; x++) {
            Color color = src.GetPixel(x, y);
            if(color.A != 0) {
                valid = false;
            }
        }
        if(valid) {
            topoffset = y; // store top offset
        } else {
            break;
        }
    }
    // scan from bottom
    int targetHeight = src.Height;
    for(var y = targetHeight - 1; y >= 0; y--) {
        var valid = true;
        for(var x = 0; x < src.Width; x++) {
            Color color = src.GetPixel(x, y);
            if(color.A != 0) {
                valid = false;
            }
        }
        if(valid) {
            targetHeight = y; // store bottom offset
        } else {
            break;
        }
    }
    // the new image is defined by the stored offset values, and will contain
    // the first non fully transparant line from each side (aka teh absolute
    // minimum vanvas we need for the actual image)
    int targetWidth = offsetRight - offsetLeft;
    var target = new Bitmap(targetWidth, targetHeight, src.PixelFormat);
    var graphics = Graphics.FromImage(target);
    var srcRect = new Rectangle(offsetLeft, topoffset, targetWidth, targetHeight - topoffset);
    var tarRect = new Rectangle(0, 0, targetWidth, targetHeight);
    graphics.DrawImage(sourceImage, tarRect, srcRect, GraphicsUnit.Pixel);
    var stream = new MemoryStream();
    target.Save(stream, ImageFormat.Png);
    return Image.FromStream(stream);
}