Подтвердить что ты не робот

Альфа-маскирование в С# System.Drawing?

Я пытаюсь сделать изображение с источником Bitmap и альфа-маской Bitmap, используя объект System.Drawing.Graphics. На данный момент я зацикливаю X и Y и использую GetPixel и SetPixel, чтобы записать цвет источника и маску альфа в третий Bitmap, а затем визуализировать это. Однако это очень неэффективно, и мне интересно, есть ли более быстрый способ достичь этого?

Эффект, который я получил, выглядит следующим образом:

Effect I’m after

Шаблон сетки представляет прозрачность; вы, наверное, знали это.

4b9b3361

Ответ 1

Да, более быстрый способ сделать это - использовать Bitmap.LockBits и использовать арифметику указателя для извлечения значений вместо GetPixel и SetPixel. Недостатком, конечно же, является то, что вы должны использовать небезопасный код; если вы допустили ошибку, вы можете вызвать некоторые очень плохие сбои в своей программе. Но если вы держите его простым и самодостаточным, это должно быть хорошо (эй, если я могу это сделать, вы тоже можете это сделать).

Например, вы можете сделать что-то вроде этого (не тестировать, использовать на свой страх и риск):

Bitmap mask = ...;
Bitmap input = ...;

Bitmap output = new Bitmap(input.Width, input.Height, PixelFormat.Format32bppArgb);
var rect = new Rectangle(0, 0, input.Width, input.Height);
var bitsMask = mask.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var bitsInput = input.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var bitsOutput = output.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
unsafe
{
    for (int y = 0; y < input.Height; y++)
    {
        byte* ptrMask = (byte*) bitsMask.Scan0 + y * bitsMask.Stride;
        byte* ptrInput = (byte*) bitsInput.Scan0 + y * bitsInput.Stride;
        byte* ptrOutput = (byte*) bitsOutput.Scan0 + y * bitsOutput.Stride;
        for (int x = 0; x < input.Width; x++)
        {
            ptrOutput[4 * x] = ptrInput[4 * x];           // blue
            ptrOutput[4 * x + 1] = ptrInput[4 * x + 1];   // green
            ptrOutput[4 * x + 2] = ptrInput[4 * x + 2];   // red
            ptrOutput[4 * x + 3] = ptrMask[4 * x];        // alpha
        }
    }
}
mask.UnlockBits(bitsMask);
input.UnlockBits(bitsInput);
output.UnlockBits(bitsOutput);

output.Save(...);

Этот пример выводит альфа-канал на выходе из канала синий в изображении маски. Я уверен, что вы можете изменить его, чтобы использовать маски красного или альфа-канала, если это необходимо.

Ответ 2

В зависимости от ваших требований это может быть намного проще:

  • Инвертируйте маску, чтобы круг был прозрачным, а остальное - от цвета, не используемого в вашем битовом изображении ввода (например, красный в вашем примере).
  • Нарисуйте свою маску поверх своего изображения, используя Graphics.FromImage(изображение).DrawImage(маска)...
  • Установите цвет маски как прозрачный на вашем изображении (Image.MakeTransparent(Color.Red))

Единственным недостатком этого метода является то, что вам нужно убедиться, что цвет маски не используется в вашем изображении. Я не знаю, медленнее или быстрее, чем на ручном пути.