画图问题。(GDI+相关,类似画图板)
小弟再做一个类似画图板的工具,现在有两个问题需要请教。
1.
画图板的画图区域可以缩小和拉伸,这个是怎么实现的啊。
(画图区域我用的Panel,建立了双缓冲区,怎么才能缩小和拉伸呢。)
2.
要在画图区域画一个矩形,然后在右侧弹出了一个入力框,要求输入行数,如果在框内输入4,矩形变成固定大小的4等分。这个我用表单可以实现,但是表单不能在画图区域拖动,我要实现等分后的矩形可以在画图区域任意拖动。
3.
类似与问题2,要在画图区域画一个椭圆,然后在右侧弹出了一个入力框,要求输入椭圆个数,如果在框内输入4,就会在椭圆列再生成等大的3个椭圆。
生成的椭圆可以在画图区域任意拖动,这个怎么实现呢?我现在只可以实现画一个椭圆。
求高手帮忙解答,给思路就行。
给的分数就是我所有的积分了。谢谢了。 --------------------编程问答-------------------- --------------------编程问答-------------------- 1 可以在每次绘制时按比例来绘制(不推荐使用GDI+的空间比例缩放)
2 可以仿图层渲染概念,用户看到的图像,是循环每一个图层一起绘制到屏幕上的,这样,鼠标操作时只操作其中一个图层,渲染部分只负责循环绘制。
3 同2 --------------------编程问答-------------------- 还有个问题:我用GDI画出的图形,别的程序覆盖在图形窗体上,移开后画的图就不见了,留一片空白 --------------------编程问答-------------------- dim B as new bitmap
using G as graphics=graphics.fromimage(b)
绘制。。。
end using
picturebox1.backgroundimage=B --------------------编程问答-------------------- 差不多,我现在遇到差不多的问题。 --------------------编程问答-------------------- 其实如果你想绘制出来的是对象(可拖动放大缩小),最好就绘制的时候就用对象,然后还可引入层的概念(panle一层一层叠,然后每层上绘制单一形状。),最后合并(把每一步绘制工作记录到一个列表,最后按层顺序绘制。)。 --------------------编程问答-------------------- 留下邮箱 我发给例子给你 --------------------编程问答--------------------
using System;--------------------编程问答--------------------
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
namespace 绘图程序
{
public partial class Draw : Form
{
public Draw()
{
InitializeComponent();
}
private DrawTools dt;
private string sType;//绘图样式
private string sFileName;//打开的文件名
private bool bReSize = false;//是否改变画布大小
private Size DefaultPicSize;//储存原始画布大小,用来新建文件时使用
//pbimg"鼠标按下"事件处理方法
private void pbImg_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (dt != null)
{
dt.startDraw = true;//相当于所选工具被激活,可以开始绘图
dt.startPointF = new PointF(e.X, e.Y);
}
}
}
//pbimg"鼠标移动"事件处理方法
private void pbImg_MouseMove(object sender, MouseEventArgs e)
{
Thread.Sleep(6);//减少cpu占用率
mousePostion.Text = e.Location.ToString();
if (dt.startDraw)
{
switch (sType)
{
case "Dot": dt.DrawDot(e); break;
case "Eraser": dt.Eraser(e); break;
default: dt.Draw(e, sType); break;
}
}
}
//pbimg"鼠标松开"事件处理方法
private void pbImg_MouseUp(object sender, MouseEventArgs e)
{
if (dt != null)
{
dt.EndDraw();
}
}
//"窗体加载"事件处理方法
private void Form1_Load(object sender, EventArgs e)
{
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
this.UpdateStyles();
Bitmap bmp = new Bitmap(pbImg.Width, pbImg.Height);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(pbImg.BackColor), new Rectangle(0, 0, pbImg.Width, pbImg.Height));
g.Dispose();
dt = new DrawTools(this.pbImg.CreateGraphics(), colorHatch1.HatchColor, bmp);//实例化工具类
DefaultPicSize = pbImg.Size;
}
//"打开文件"事件处理方法
private void openPic_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();//实例化文件打开对话框
ofd.Filter = "JPG|*.jpg|Bmp|*.bmp|所有文件|*.*";//设置对话框打开文件的括展名
if (ofd.ShowDialog() == DialogResult.OK)
{
Bitmap bmpformfile = new Bitmap(ofd.FileName);//获取打开的文件
panel2.AutoScrollPosition = new Point(0,0);//将滚动条复位
pbImg.Size = bmpformfile.Size;//调整绘图区大小为图片大小
reSize.Location = new Point(bmpformfile.Width, bmpformfile.Height);//reSize为我用来实现手动调节画布大小用的
//因为我们初始时的空白画布大小有限,"打开"操作可能引起画板大小改变,所以要将画板重新传入工具类
dt.DrawTools_Graphics = pbImg.CreateGraphics();
Bitmap bmp = new Bitmap(pbImg.Width, pbImg.Height);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(pbImg.BackColor), new Rectangle(0, 0, pbImg.Width, pbImg.Height));//不使用这句话,那么这个bmp的背景就是透明的
g.DrawImage(bmpformfile, 0, 0,bmpformfile.Width,bmpformfile.Height);//将图片画到画板上
g.Dispose();//释放画板所占资源
//不直接使用pbImg.Image = Image.FormFile(ofd.FileName)是因为这样会让图片一直处于打开状态,也就无法保存修改后的图片;详见http://www.wanxin.org/redirect.php?tid=3&goto=lastpost
bmpformfile.Dispose();//释放图片所占资源
g = pbImg.CreateGraphics();
g.DrawImage(bmp, 0, 0);
g.Dispose();
dt.OrginalImg = bmp;
bmp.Dispose();
sFileName = ofd.FileName;//储存打开的图片文件的详细路径,用来稍后能覆盖这个文件
ofd.Dispose();
}
}
//"保存"事件处理方法
private void savePic_Click(object sender, EventArgs e)
{
if (sFileName != null)
{
if (MessageBox.Show("是否要保存文件?", "系统提示", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
dt.OrginalImg.Save(sFileName);
}
}
else
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "JPG(*.jpg)|*.jpg|BMP(*.bmp)|*.bmp";
if (sfd.ShowDialog() == DialogResult.OK)
{
dt.OrginalImg.Save(sfd.FileName);
sFileName = sfd.FileName;
}
}
}
//窗体移动最小化等造成的pbimg"重画"事件处理方法
private void pbImg_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(dt.OrginalImg, 0, 0);
//g.Dispose();切不可使用,这个Graphics是系统传入的变量,不是我们自己创建的,如果dispose就会出错
}
//"绘图工具选用"事件处理方法
private void tool_Click(object sender, EventArgs e)
{
ToolStripButton tsb = sender as ToolStripButton;
if (tsb != null)
{
sType = tsb.Name;
currentDrawType.Text = tsb.Text;
if (sType == "Eraser")
{
pbImg.Cursor = new Cursor(Application.StartupPath + @"\img\pb.cur");
}
else
{
pbImg.Cursor = Cursors.Cross;
}
}
}
//"清除图像"事件处理方法
private void clearPic_Click(object sender, EventArgs e)
{
Bitmap newpic = new Bitmap(pbImg.Width, pbImg.Height);
Graphics g = Graphics.FromImage(newpic);
g.FillRectangle(new SolidBrush(Color.White), 0, 0, pbImg.Width, pbImg.Height);
g.Dispose();
g = pbImg.CreateGraphics();
g.DrawImage(newpic, 0, 0);
g.Dispose();
dt.OrginalImg = newpic;
}
//"另存为"事件处理方法
private void SaveAs_Click(object sender, EventArgs e)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "JPG(*.jpg)|*.jpg|BMP(*.bmp)|*.bmp";
if (sfd.ShowDialog() == DialogResult.OK)
{
dt.OrginalImg.Save(sfd.FileName);
sFileName = sfd.FileName;
}
}
//"退出"事件处理方法
private void Quit_Click(object sender, EventArgs e)
{
dt.ClearVar();
Application.Exit();
}
//"颜色改变"事件处理方法
private void colorHatch1_ColorChanged(object sender, ColorHatch.ColorChangedEventArgs e)
{
dt.DrawColor = e.GetColor;
}
private void reSize_MouseDown(object sender, MouseEventArgs e)
{
bReSize = true;//当鼠标按下时,说明要开始调节大小
}
private void reSize_MouseMove(object sender, MouseEventArgs e)
{
if (bReSize)
{
reSize.Location = new Point(reSize.Location.X + e.X, reSize.Location.Y + e.Y);
}
}
private void reSize_MouseUp(object sender, MouseEventArgs e)
{
bReSize = false;//大小改变结束
//调节大小可能造成画板大小超过屏幕区域,所以事先要设置autoScroll为true.
//但是滚动条的出现反而增加了我们的难度,因为滚动条上下移动并不会自动帮我们调整图片的坐标。
//这是因为GDI绘图的坐标系不只一个,好像有三个,没有仔细了解,一个是屏幕坐标,一个是客户区坐标,还个是文档坐标。
//滚动条的上下移动改变的是文档的坐标,但是客户区坐标不变,而location属性就属于客户区坐标,所以我们直接计算会出现错误
//这时我们就需要知道文档坐标与客户区坐标的偏移量,这就是AutoScrollPostion可以提供的
pbImg.Size = new Size(reSize.Location.X - (this.panel2.AutoScrollPosition.X), reSize.Location.Y - (this.panel2.AutoScrollPosition.Y));
dt.DrawTools_Graphics = pbImg.CreateGraphics();//因为画板的大小被改变所以必须重新赋值
//另外画布也被改变所以也要重新赋值
Bitmap bmp = new Bitmap(pbImg.Width, pbImg.Height);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(Color.White), 0, 0, pbImg.Width, pbImg.Height);
g.DrawImage(dt.OrginalImg, 0, 0);
g.Dispose();
g = pbImg.CreateGraphics();
g.DrawImage(bmp, 0, 0);
g.Dispose();
dt.OrginalImg = bmp;
bmp.Dispose();
}
private void BuildNewPic_Click(object sender, EventArgs e)
{
pbImg.Size = DefaultPicSize;
this.panel2.AutoScrollPosition = new Point(0, 0);
Bitmap bmp = new Bitmap(DefaultPicSize.Width, DefaultPicSize.Height);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(Color.White), 0, 0, DefaultPicSize.Width, DefaultPicSize.Height);
g.Dispose();
g = pbImg.CreateGraphics();
g.DrawImage(bmp, 0, 0);
g.Dispose();
reSize.Location = new Point(DefaultPicSize.Width, DefaultPicSize.Height);
dt.OrginalImg = bmp;
sFileName = null;
}
private void AttributePic_Click(object sender, EventArgs e)
{
MessageBox.Show("图像高:" + pbImg.Height + " px ,图像宽:" + pbImg.Width+" px", "图像属性");
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace 绘图程序
{
/// <summary>
/// 绘图工具包括直线,矩形,铅笔,圆形,橡皮
/// </summary>
class DrawTools
{
public Graphics DrawTools_Graphics;//目标绘图板
private Pen p;
private Image orginalImg;//原始画布,用来保存已完成的绘图过程
private Color drawColor = Color.Black;//绘图颜色
private Graphics newgraphics;//中间画板
private Image finishingImg;//中间画布,用来保存绘图过程中的痕迹
/// <summary>
/// 绘图颜色
/// </summary>
public Color DrawColor
{
get { return drawColor; }
set
{
drawColor = value;
p.Color = value;
}
}
/// <summary>
/// 原始画布
/// </summary>
public Image OrginalImg
{
get { return orginalImg; }
set
{
finishingImg = (Image)value.Clone();
orginalImg = (Image)value.Clone();
}
}
/// <summary>
/// 表示是否开始绘图
/// </summary>
public bool startDraw = false;
/// <summary>
/// 绘图起点
/// </summary>
public PointF startPointF;
/// <summary>
/// 初始化绘图工具
/// </summary>
/// <param name="g">绘图板</param>
/// <param name="c">绘图颜色</param>
/// <param name="img">初始画布</param>
public DrawTools(Graphics g, Color c, Image img)
{
DrawTools_Graphics = g;
drawColor = c;
p = new Pen(c, 1);
finishingImg = (Image)img.Clone();
orginalImg = (Image)img.Clone();
}
/// <summary>
/// 绘制直线,矩形,圆形
/// </summary>
/// <param name="e">鼠标参数</param>
/// <param name="sType">绘图类型</param>
public void Draw(MouseEventArgs e, string sType)
{
if (startDraw)
{
//为防止造成图片抖动,防止记录不必要的绘图过程中的痕迹,我们先在中间画板上将图片完成,然后在将绘制好的图片一次性画到目标画板上
//步骤1实例化中间画板,画布为上一次绘制结束时的画布的副本(如果第一次绘制,那画布就是初始时的画布副本)
//步骤2按照绘图样式在中间画板上进行绘制
//步骤3将绘制结束的图片画到中间画布上
//因为我们最终绘制结束时的图片应该是在鼠标松开时完成,所以鼠标移动中所绘制的图片都只画到中间画布上,但仍需要显示在目标画板上,否则鼠标移动过程中我们就看不到效果。
//当鼠标松开时,才把最后的那个中间图片画到原始画布上
Image img = (Image)orginalImg.Clone();//为防止直接改写原始画布,我们定义一个新的image去得到原始画布
newgraphics = Graphics.FromImage(img);//实例化中间画板
switch (sType)
{
case "Line":
{//画直线
newgraphics.DrawLine(p, startPointF, new PointF(e.X, e.Y)); break;
}
case "Rect":
{//画矩形
float width = Math.Abs(e.X - startPointF.X);//确定矩形的宽
float heigth = Math.Abs(e.Y - startPointF.Y);//确定矩形的高
PointF rectStartPointF = startPointF;
if (e.X < startPointF.X)
{
rectStartPointF.X = e.X;
}
if (e.Y < startPointF.Y)
{
rectStartPointF.Y = e.Y;
}
newgraphics.DrawRectangle(p, rectStartPointF.X, rectStartPointF.Y, width, heigth);
break;
}
case "Circle":
{//画圆形
newgraphics.DrawEllipse(p, startPointF.X, startPointF.Y, e.X - startPointF.X, e.Y - startPointF.Y); break;
}
case "FillRect":
{//画实心矩形
float width = Math.Abs(e.X - startPointF.X);//确定矩形的宽
float heigth = Math.Abs(e.Y - startPointF.Y);//确定矩形的高
PointF rectStartPointF = startPointF;
if (e.X < startPointF.X)
{
rectStartPointF.X = e.X;
}
if (e.Y < startPointF.Y)
{
rectStartPointF.Y = e.Y;
}
newgraphics.FillRectangle(new SolidBrush(drawColor), rectStartPointF.X, rectStartPointF.Y, width, heigth);
break;
}
case "FillCircle":
{//画实心圆
newgraphics.FillEllipse(new SolidBrush(drawColor), startPointF.X, startPointF.Y, e.X - startPointF.X, e.Y - startPointF.Y); break;
}
}
newgraphics.Dispose();//绘图完毕释放中间画板所占资源
newgraphics = Graphics.FromImage(finishingImg);//另建一个中间画板,画布为中间图片
newgraphics.DrawImage(img, 0, 0);//将图片画到中间图片
newgraphics.Dispose();
DrawTools_Graphics.DrawImage(img, 0, 0);//将图片画到目标画板上
img.Dispose();
}
}
public void EndDraw()
{
startDraw = false;
//为了让完成后的绘图过程保留下来,要将中间图片绘制到原始画布上
newgraphics = Graphics.FromImage(orginalImg);
newgraphics.DrawImage(finishingImg, 0, 0);
newgraphics.Dispose();
}
/// <summary>
/// 橡皮方法
/// </summary>
/// <param name="e">鼠标参数</param>
public void Eraser(MouseEventArgs e)
{
if (startDraw)
{
newgraphics = Graphics.FromImage(finishingImg);
newgraphics.FillRectangle(new SolidBrush(Color.White), e.X, e.Y, 20, 20);
newgraphics.Dispose();
DrawTools_Graphics.DrawImage(finishingImg, 0, 0);
}
}
/// <summary>
/// 铅笔方法
/// </summary>
/// <param name="e">鼠标参数</param>
public void DrawDot(MouseEventArgs e)
{
if (startDraw)
{
newgraphics = Graphics.FromImage(finishingImg);
PointF currentPointF = new PointF(e.X, e.Y);
newgraphics.DrawLine(p, startPointF, currentPointF);
startPointF = currentPointF;
newgraphics.Dispose();
DrawTools_Graphics.DrawImage(finishingImg, 0, 0);
}
}
/// <summary>
/// 清除变量,释放内存
/// </summary>
public void ClearVar()
{
DrawTools_Graphics.Dispose();
finishingImg.Dispose();
orginalImg.Dispose();
p.Dispose();
}
}
}
补充:.NET技术 , C#