Это не очень сложно:
- Установите цветное изображение как
PictureBox
BackgroundImage
.
- Установите черное изображение как его
Image
.
- И рисуйте на изображении, используя обычные события мыши и прозрачный
Pen
..
![введите здесь описание изображения](https://i.stack.imgur.com/WhdOu.png)
Нам нужен список точек для использования DrawCurve
:
List<Point> currentLine = new List<Point>();
Нам нужно подготовить и очистить черный слой:
private void ClearSheet()
{
if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
Bitmap bmp = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
using (Graphics G = Graphics.FromImage(bmp)) G.Clear(Color.Black);
pictureBox1.Image = bmp;
currentLine.Clear();
}
private void cb_clear_Click(object sender, EventArgs e)
{
ClearSheet();
}
Чтобы рисовать в Image
, нам нужно использовать связанный объект Graphics
..:
void drawIntoImage()
{
using (Graphics G = Graphics.FromImage(pictureBox1.Image))
{
// we want the tranparency to copy over the black pixels
G.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
G.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
G.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
using (Pen somePen = new Pen(Color.Transparent, penWidth))
{
somePen.MiterLimit = penWidth / 2;
somePen.EndCap = System.Drawing.Drawing2D.LineCap.Round;
somePen.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
somePen.StartCap = System.Drawing.Drawing2D.LineCap.Round;
if (currentLine.Count > 1)
G.DrawCurve(somePen, currentLine.ToArray());
}
}
// enforce the display:
pictureBox1.Image = pictureBox1.Image;
}
Обычные события мыши:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
currentLine.Add(e.Location);
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
currentLine.Add(e.Location);
drawIntoImage();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
currentLine.Clear();
}
Это все, что нужно. Обязательно сохраните SizeMode = Normal
PB, иначе пиксели не будут совпадать..!
Обратите внимание, что есть несколько проблем, когда вы хотите получить мягкие края, больше инструментов рисования, позволить простому щелчку рисовать точку или отмену или другие более мелкие детали для работы. Но основы совсем не сложные..
Кстати, изменение Alpha
никаким не отличается от изменения цветовых каналов.
В качестве альтернативы вы можете поиграть с TextureBrush
:
TextureBrush brush = new TextureBrush(pictureBox1.BackgroundImage);
using (Pen somePen = new Pen(brush) )
{
// basically
// the same drawing code..
}
Но я обнаружил, что это довольно медленно.
Обновление:
Использование файла png
в качестве пользовательской подсказки немного сложнее; основная причина в том, что рисунок перевернут: мы не хотим рисовать пиксели, мы хотим их очистить. GDI+
не поддерживает такие режимы композиции, поэтому нам нужно сделать это в коде.
Чтобы быть быстрым, мы используем два трюка: LockBits
будет настолько быстрым, насколько это возможно, и ограничение области нашим пользовательским кончиком кисти предотвратит трату времени.
Предположим, у вас есть файл, который нужно использовать, и загрузите его в растровое изображение:
string stampFile = @"yourStampFile.png";
Bitmap stamp = null;
private void Form1_Load(object sender, EventArgs e)
{
stamp = (Bitmap) Bitmap.FromFile(stampFile);
}
Теперь нам нужна новая функция, чтобы нарисовать его в нашем Image
; вместо DrawCurve
нам нужно использовать DrawImage
:
void stampIntoImage(Point pt)
{
Point point = new Point(pt.X - stamp.Width / 2, pt.Y - stamp.Height / 2);
using (Bitmap stamped = new Bitmap(stamp.Width, stamp.Height) )
{
using (Graphics G = Graphics.FromImage(stamped))
{
stamp.SetResolution(stamped.HorizontalResolution, stamped.VerticalResolution);
G.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
G.DrawImage(pictureBox1.Image, 0, 0,
new Rectangle(point, stamped.Size), GraphicsUnit.Pixel);
writeAlpha(stamped, stamp);
}
using (Graphics G = Graphics.FromImage(pictureBox1.Image))
{
G.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
G.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
G.CompositingQuality =
System.Drawing.Drawing2D.CompositingQuality.HighQuality;
G.DrawImage(stamped, point);
}
}
pictureBox1.Image = pictureBox1.Image;
}
Несколько замечаний: я обнаружил, что не должен делать явное SetResolution
, так как файл штампа, который я отфотошопил, был 72dpi
, а растровые изображения по умолчанию в моей программе были 120dpi
. Обратите внимание на эти различия!
Я начинаю отрисовку растрового изображения, копируя правую часть текущего изображения.
Затем я вызываю быструю процедуру, которая применяет к ней альфу штампа:
void writeAlpha(Bitmap target, Bitmap source)
{
// this method assumes the bitmaps both are 32bpp and have the same size
int Bpp = 4;
var bmpData0 = target.LockBits(
new Rectangle(0, 0, target.Width, target.Height),
ImageLockMode.ReadWrite, target.PixelFormat);
var bmpData1 = source.LockBits(
new Rectangle(0, 0, source.Width, source.Height),
ImageLockMode.ReadOnly, source.PixelFormat);
int len = bmpData0.Height * bmpData0.Stride;
byte[] data0 = new byte[len];
byte[] data1 = new byte[len];
Marshal.Copy(bmpData0.Scan0, data0, 0, len);
Marshal.Copy(bmpData1.Scan0, data1, 0, len);
for (int i = 0; i < len; i += Bpp)
{
int tgtA = data0[i+3]; // opacity
int srcA = 255 - data1[i+3]; // transparency
if (srcA > 0) data0[i + 3] = (byte)(tgtA < srcA ? 0 : tgtA - srcA);
}
Marshal.Copy(data0, 0, bmpData0.Scan0, len);
target.UnlockBits(bmpData0);
source.UnlockBits(bmpData1);
}
Я использую простое правило: уменьшите непрозрачность цели на прозрачность источника и убедитесь, что мы не получаем отрицательное значение. Вы можете поиграть с этим.
Теперь все, что нам нужно, это адаптировать MouseMove
; для своих тестов я добавил два RadioButtons
для переключения между оригинальной круглой ручкой и пользовательским наконечником штампа:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (rb_pen.Checked)
{
currentLine.Add(e.Location);
drawIntoImage();
}
else if (rb_stamp.Checked) { stampIntoImage(e.Location); };
}
}
Я не использовал рыбу, но вы можете видеть мягкие края:
![введите здесь описание изображения](https://i.stack.imgur.com/CUj6z.png)
Обновление 2. Вот MouseDown
, позволяющий выполнять простые клики:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (rb_pen.Checked) currentLine.Add(e.Location);
else if (rb_stamp.Checked)
{
{ stampIntoImage(e.Location); };
}
}
person
TaW
schedule
03.09.2015