Как создать картинку, если ее размер заранее неизвестен?

375
06 февраля 2017, 14:36

Задача - штамповать разные изображения на полотно. Вот только размер полотна заранее неизвестен. Штампую при помощи Graphics

var bmp = new Bitmap(1, 1);
var g = Graphics.FromImage(bmp);
g.DrawImage(someImage, 0,0, someImage.Width,someImage.Height);

Последнее действие может быть не единственным. То есть мне может понадобиться вставить много картинок, текста и еще хрен знает чего. И при этом я не хочу заморачиваться вычислением размера картинки. Есть ли для этого автоматические средства?

Answer 1

Скорее всего велосипед, но раз решений не предложили, изобрел сам. Вдруг кому пригодится.

Вот класс самой канвы.

public class InfinityCanvas
{
    List<ImageLayer> _layers { get; } = new List<ImageLayer>();
    int _minLeftTopX = int.MaxValue;
    int _minLeftTopY = int.MaxValue;
    int _maxRightBottomX = int.MinValue;
    int _maxRightBottomY = int.MinValue;
    public void AddImage(Image image, Point position)
    {
        AddImage(image, position.X, position.Y);
    }
    public void AddImage(Image image, int x, int y)
    {
        _layers.Add(new ImageLayer(image, x, y));
        if (_minLeftTopX >= x)
            _minLeftTopX = x;
        if (_minLeftTopY >= y)
            _minLeftTopY = y;
        if (_maxRightBottomX <= x + image.Width)
            _maxRightBottomX = x + image.Width;
        if (_maxRightBottomY <= y + image.Height)
            _maxRightBottomY = y + image.Height;
    }
    public Image GetResult()
    {
        if (_layers.Count == 0)
        {
            var b = new Bitmap(1, 1);
            return b;
        }
        var res = new Bitmap(_maxRightBottomX - _minLeftTopX, _maxRightBottomY - _minLeftTopY);
        var g = Graphics.FromImage(res);
        for (var i = 0; i < _layers.Count; i++)
        {
            var lay = _layers[i];
            g.DrawImage(lay.Image, lay.LeftTopX - _minLeftTopX, lay.LeftTopY - _minLeftTopY, lay.Image.Width, lay.Image.Height);
        }
        return res;
    }
}

Вот класс слоя

class ImageLayer
{
    public Image Image { get; set; }
    public int LeftTopX { get; set; }
    public int LeftTopY { get; set; }
    public ImageLayer(Image image, int x, int y)
    {
        Image = image;
        LeftTopX = x;
        LeftTopY = y;
    }
}

Вот так используем

var ic = new InfinityCanvas();
ic.AddImage(someImage, -100, -100);
ic.AddImage(someImage, someImage.Width, someImage.Height);
ic.GetResult().Save("1.png");

В результате получаем рисунок, в левом верхнем и в правом нижнем углах которого изображение, содержащееся в someImage. Между изображениями расстояние 100 по обоим осям.

Answer 2

Вы можете создать достаточно большое полотно, к примеру 10000 на 10000 пикселей и рисовать на нем сколько нужно. Затем обрезаете ненужную часть(и) с помощью этого:

static Bitmap TrimBitmap(Bitmap source)
{
Rectangle srcRect = default(Rectangle);
BitmapData data = null;
try
{
    data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    byte[] buffer = new byte[data.Height * data.Stride];
    Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
    int xMin = int.MaxValue;
    int xMax = 0;
    int yMin = int.MaxValue;
    int yMax = 0;
    for (int y = 0; y < data.Height; y++)
    {
        for (int x = 0; x < data.Width; x++)
        {
            byte alpha = buffer[y * data.Stride + 4 * x + 3];
            if (alpha != 0)
            {
                if (x < xMin) xMin = x;
                if (x > xMax) xMax = x;
                if (y < yMin) yMin = y;
                if (y > yMax) yMax = y;
            }
        }
    }
    if (xMax < xMin || yMax < yMin)
    {
        // Image is empty...
        return null;
    }
    srcRect = Rectangle.FromLTRB(xMin, yMin, xMax, yMax);
}
finally
{
    if (data != null)
        source.UnlockBits(data);
}
Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(dest))
{
    graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel);
}
return dest;
}
READ ALSO
Как встроить 7z.dll в exe?

Как встроить 7z.dll в exe?

argsName Почему-то содержит = "Project_file_backup

476
Модульное тестирование в C#

Модульное тестирование в C#

Как правильно протестировать Data access layer с помощью Mock, а именно репозитории, предоставляющие доступ к базе данных через контекст?

424
R*-tree реализация на C# [требует правки]

R*-tree реализация на C# [требует правки]

Хоть и есть несколько статей на хабре, есть реализация на C++, не могу понять как переделать это под С#Может быть вдруг кто-то реализовал?

436
Как получить письма пользователя gmail?

Как получить письма пользователя gmail?

Добрый день! Мне нужно получить gmail письма пользователяПрограмма открывает ссылку авторизации в WebBrowser, пользователь входит, я получаю код

416