我正在编写一个应用程序,其中有一个面板,我在其中显示:
面板平移和缩放。该面板是双缓冲的。
所以一切都运转良好 - 事情看起来很好,没有闪烁,但我正处于我试图专注于表现的地步。我用不到1毫秒来绘制我的物体(每个物体)但是当缩小时我可以有超过500个物体来绘制,这开始使得从绘图到命中检测的一切都变得缓慢。
我已经做了一些尝试来提高性能,比如制作一个只有屏幕(可绘制)对象的列表 - 但如前所述,当缩小时,它仍然可能很多。我得到了一个伟大的想法,也许不是每次都更新每个对象,我可以实现一个简单的Invalidate(),它会告诉对象它做了一个值得绘制的视觉变化。在对象绘制代码(通过e.graphics)中,我试图将剪切区域设置为对象的大小,并且只更新面板BG的那部分(同样在完成时,我重置了e.Grpahics.clip) 。结果只是无效的对象绘制 - 其他一切都是空白的。
我确信这是C#/ GDI noob出现在我身上,你们都会告诉我我可能忽略的那个愚蠢的错误。
在撰写本文时,我意识到我可以通过使对象的路径保持不变来保存一些时间,而不是每次在绘制函数中都使它新鲜(因为所有对象路径都是相同)。我会随着时间的推移更新,我很快就会刮掉。
以下是绘制BG图像(并确定要绘制哪个OBJS)和OBJ绘画代码的代码。
private void PaintImage(PaintEventArgs e)
{
int scale = 10; //TARGET ICON BASE SCALE....
var watch2 = System.Diagnostics.Stopwatch.StartNew();
if (bitmap != null && redrawBG)
{
float widthZoomed = TgtPanel.Width / Zoom;
float heigthZoomed = TgtPanel.Height / Zoom;
//Do checks the reason 30,000 is used is because
//much over this will cause DrawImage to crash
if (widthZoomed > 30000.0f)
{
Zoom = TgtPanel.Width / 30000.0f;
widthZoomed = 30000.0f;
}
if (heigthZoomed > 30000.0f)
{
Zoom = TgtPanel.Height / 30000.0f;
heigthZoomed = 30000.0f;
}
//we stop at 2 because at this point you have almost zoomed into a single pixel
if (widthZoomed < 2.0f)
{
Zoom = TgtPanel.Width / 2.0f;
widthZoomed = 2.0f;
}
if (heigthZoomed < 2.0f)
{
Zoom = TgtPanel.Height / 2.0f;
heigthZoomed = 2.0f;
}
float wz2 = widthZoomed / 2.0f;
float hz2 = heigthZoomed / 2.0f;
Rectangle drawRect = new Rectangle(
(int)(viewPortCenter.X - wz2),
(int)(viewPortCenter.Y - hz2),
(int)(widthZoomed),
(int)(heigthZoomed));
e.Graphics.Clear(Color.White); //Clear the Back buffer
//Draw the image, Write image to back buffer, and [render back buffer - no longer using my own backbuffer]
e.Graphics.DrawImage(bitmap,
this.TgtPanel.DisplayRectangle, drawRect, GraphicsUnit.Pixel);
// e.Graphics.ScaleTransform(Zoom, Zoom);
// e.Graphics.DrawImage(bitmap,
// 0,0);
if (draging)
{ //line to visualize the drag
e.Graphics.DrawLine(new Pen(Color.Yellow, 10), StartDrag.X, StartDrag.Y, lastMouse.X, lastMouse.Y);
}
//this checks for offscreen - works
if (drawRect.X > iconbitmap.Width || drawRect.X < -(drawRect.Width) ||
drawRect.Y > 0 + iconbitmap.Height || drawRect.Y < -(drawRect.Height))
{
label1.Text = "OFF";
}
} //if bitmap != null & redraw
// Font and Brush for the text graphics
Point mypoint = WorldToScreen(0.75f * scale, 7.0f * scale);
RectangleF bgrect = new RectangleF();
bgrect.X = mypoint.X;
bgrect.Y = mypoint.Y;
bgrect.Width = (3.5f * scale * Zoom);
bgrect.Height = (2.0f * scale * Zoom);
int aFontSizeDefault = 40;
int aFontSizeMinimum = 2;
String adrawString = "AAA"; //test this length
Font aDefaultFont = new Font("MS Sans Serif", aFontSizeDefault, FontStyle.Regular);
FontAdjustment afa = new FontAdjustment();
Font AAdjustedFont = afa.GetAdjustedFont(e.Graphics, adrawString,
aDefaultFont, Convert.ToInt32(bgrect.Width), aFontSizeDefault, aFontSizeMinimum, true);
//DRAW TGT BG
var Point1 = ScreenToWorld(0, 0);
var Point2 = ScreenToWorld(TgtPanel.Width, TgtPanel.Height);
//getVisible Screen == on;y draw visible targets
if (redrawBG)
{
VISIBLETARGETS.Clear(); //erase visible tgts array - we're going to update it
foreach (Target TGT in THETARGETS)
if (TGT.PosX >= Point1.X - 40 && TGT.PosX <= Point2.X - 9)
if (TGT.PosY >= Point1.Y - 83 && TGT.PosY <= Point2.Y - 5)
{
TGT.OnScreen = true;
//drawTarget(TGT, AAdjustedFont, e);
VISIBLETARGETS.Add(TGT); //update as visible
}
else TGT.OnScreen = false;
//redrawBG = false;
}
var watch = System.Diagnostics.Stopwatch.StartNew();
foreach(Target TGT in VISIBLETARGETS)
{
if(TGT.Invalidated || redrawBG) // THIS IS DRAWING ONLY TGT -- NOT OTHERS, OR BG - FIX THIS WITH CLIPPING?
drawTarget(TGT, AAdjustedFont, e);
}
watch.Stop();
watch2.Stop();
var elapsedMs = watch.ElapsedMilliseconds;//ElapsedTicks;
label6.Text = "TotalDrawTime = " + watch2.ElapsedMilliseconds.ToString();
label4.Text = "AvgDrawTime = " + elapsedMs.ToString();
label5.Text = "VisibleTgts = " + VISIBLETARGETS.Count.ToString();
AAdjustedFont.Dispose();
aDefaultFont.Dispose();
//------------- DRAWING TGT WITH GDI - WITH ORIGINAL BACKBUFFER
/// myBuffer.Render(this.TgtPanel.CreateGraphics());
redrawBG = false;
}
public void drawTarget(Target Tgt, Font AAdjustedFont, PaintEventArgs e)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
const float scale = 10; //10 is at 1 zoom
var bgrect = new RectangleF();
Point mypoint = WorldToScreen(Tgt.PosX + 0.75f * scale, Tgt.PosY + 1.0f * scale);
bgrect.X = mypoint.X;
bgrect.Y = mypoint.Y;
bgrect.Width = 3.5f * scale * Zoom;
bgrect.Height = 7.5f * scale * Zoom;
//PLAY WITH CLIP
e.Graphics.Clip = new Region(bgrect);
//var hbrush = new HatchBrush(HatchStyle.DarkDownwardDiagonal, Color.White);
var hbrush = new SolidBrush(Color.White);
//if(WantToDrawIconBG() ....
//e.Graphics.FillRectangle(hbrush, bgrect); //ICON BACKGROUND
//ADDR RECT
// mypoint = WorldToScreen(0, Tgt.PosY + 7.0f * scale);
mypoint = WorldToScreen(0, Tgt.PosY + 6.90f * scale); //moved Y up a bit from above
bgrect.Y = mypoint.Y;
bgrect.Height = 1.5f * scale * Zoom;
/////brush.Color = (Color.GhostWhite);
e.Graphics.FillRectangle(hbrush, bgrect);
hbrush.Dispose();
string adrawString = Tgt.Address;
System.Drawing.Font adrawFont = new System.Drawing.Font("Arial", 16);
System.Drawing.SolidBrush adrawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Red);
System.Drawing.StringFormat adrawFormat = new System.Drawing.StringFormat();
adrawFormat.Alignment = StringAlignment.Center;
e.Graphics.DrawString(adrawString, AAdjustedFont, adrawBrush, bgrect, adrawFormat); //draw addr
//======= LETS MAKE THE TGT ICON SHAPE =======
GraphicsPath path = new GraphicsPath(FillMode.Alternate);
//TARGET POINTS (w = 3, h = 6)
var h1 = WorldToScreen(Tgt.PosX + 2 * scale, Tgt.PosY + 1.1f * scale);
var h2 = WorldToScreen(Tgt.PosX + 3 * scale, Tgt.PosY + 1.1f * scale);
var n1 = WorldToScreen(Tgt.PosX + 2 * scale, Tgt.PosY + 2 * scale);
var n2 = WorldToScreen(Tgt.PosX + 3 * scale, Tgt.PosY + 2 * scale);
var s1 = WorldToScreen(Tgt.PosX + 1 * scale, Tgt.PosY + 3 * scale);
var s2 = WorldToScreen(Tgt.PosX + 4 * scale, Tgt.PosY + 3 * scale);
var b1 = WorldToScreen(Tgt.PosX + 1 * scale, Tgt.PosY + 7 * scale);
var b2 = WorldToScreen(Tgt.PosX + 4 * scale, Tgt.PosY + 7 * scale);
var controln2 = WorldToScreen(Tgt.PosX + 1 * scale, (Convert.ToInt32(Tgt.PosY + 0.5 * scale)));
var controls2 = WorldToScreen(Tgt.PosX + 1 * scale, Tgt.PosY + 1 * scale);
Pen pen = new Pen(Color.FromArgb(255, 0, 0, 255));
PointF[] npoints = { n2, s2, b2 };
PointF[] npoints2 = { n1, s1, b1 };
e.Graphics.DrawCurve(pen, npoints, 0.5f);
path.AddCurve(npoints, 0.5f); /////
e.Graphics.DrawLine(pen, b2, b1);
path.AddLine(b2, b1); /////
e.Graphics.DrawCurve(pen, npoints2, 0.5f);
path.AddCurve(npoints2, 0.5f); /////
PointF[] hpoints = { n1, h1, h2, n2 };
e.Graphics.DrawCurve(pen, hpoints, 0.1f);
path.AddCurve(hpoints, 0.1f); /////
path.CloseAllFigures();
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (Zoom > 0.9) //only draw stroke if big enough to see (and there arent a million targets to draw
{
pen.Color = Tgt.Selected ? Color.Chartreuse : Color.FromName(comboBox1.Text); //black default
pen.Width = Tgt.Selected ? 2 * Zoom : 1 * Zoom; //draw thicker border if selected
e.Graphics.DrawPath(pen, path); ///------------------------------------------STROKE PATH.....
//e.Graphics.FillPath(new SolidBrush(Color.ForestGreen), path);
}
// how much time is wasted making 2 gradient brushes? One we wont even use.
LinearGradientBrush linGrBrush = new LinearGradientBrush(
WorldToScreen(Tgt.PosX + 0 * scale, Tgt.PosY + 5 * scale),
WorldToScreen(Tgt.PosX + 5.5f * scale, Tgt.PosY + 5 * scale),
Color.ForestGreen, // Opaque red
Color.FromArgb(255, 0, 255, 0)); // Opaque blue
LinearGradientBrush linRedBrush = new LinearGradientBrush(
WorldToScreen(Tgt.PosX + 0 * scale, Tgt.PosY + 5 * scale),
WorldToScreen(Tgt.PosX + 5.5f * scale, Tgt.PosY + 5 * scale),
Color.FromArgb(255, 255, 0, 0), // Opaque red
Color.Firebrick); // Opaque blue
//FILL TARGET ICON WITH COLOR - UP or DOWN
if (Tgt.IsUp) e.Graphics.FillPath(linGrBrush, path);
else e.Graphics.FillPath(linRedBrush, path);
//------------
//tgt lines (cosmetic only)
if (Zoom > 0.9) //only draw if big enough to see (and there arent a million targets to draw
{
var transPen = new Pen(Color.FromArgb(150, 200, 200, 200));
var l1a = WorldToScreen(Tgt.PosX + 2.5f * scale, Tgt.PosY + 1.5f * scale);
var l1b = WorldToScreen(Tgt.PosX + 2.5f * scale, Tgt.PosY + 6 * scale);
e.Graphics.DrawLine(transPen, l1a, l1b);
var l2a = WorldToScreen(Tgt.PosX + 1.5f * scale, Tgt.PosY + 2.5f * scale);
var l2b = WorldToScreen(Tgt.PosX + 1.5f * scale, Tgt.PosY + 6.5f * scale);
e.Graphics.DrawLine(transPen, l2a, l2b);
var l3a = WorldToScreen(Tgt.PosX + 3.5f * scale, Tgt.PosY + 2.5f * scale);
var l3b = WorldToScreen(Tgt.PosX + 3.5f * scale, Tgt.PosY + 6.5f * scale);
e.Graphics.DrawLine(transPen, l3a, l3b);
}
//Draw Hits....
mypoint = WorldToScreen(Tgt.PosX + 1.0f * scale, Tgt.PosY + 3.0f * scale);
bgrect.X = mypoint.X;
bgrect.Y = mypoint.Y;
bgrect.Width = 3.0f * scale * Zoom;
bgrect.Height = 1.5f * scale * Zoom;
adrawString = Tgt.Hits.ToString();
adrawFormat.Alignment = StringAlignment.Center;
if (Zoom > 0.9) //only draw if big enough to see (and there arent a million targets to draw
{
adrawBrush.Color = Color.FromArgb(100, 100, 100, 100);
e.Graphics.FillRectangle(adrawBrush, bgrect);
}
adrawBrush.Color = Color.White;
e.Graphics.DrawString(adrawString, AAdjustedFont, adrawBrush, bgrect, adrawFormat); //draw hits
//Draw Score....
mypoint = WorldToScreen(Tgt.PosX + 1.0f * scale, Tgt.PosY + 5.0f * scale);
bgrect.X = mypoint.X;
bgrect.Y = mypoint.Y;
bgrect.Width = 3.0f * scale * Zoom;
bgrect.Height = 1.5f * scale * Zoom;
adrawString = Tgt.Score.ToString();
adrawFormat.Alignment = StringAlignment.Center;
if (Zoom > 0.9) //only draw if big enough to see (and there arent a million targets to draw
{
adrawBrush.Color = Color.FromArgb(100, 100, 100, 100);
e.Graphics.FillRectangle(adrawBrush, bgrect);
}
adrawBrush.Color = Color.White;
e.Graphics.DrawString(adrawString, AAdjustedFont, adrawBrush, bgrect, adrawFormat); //draw hits
adrawFont.Dispose();
adrawBrush.Dispose();
adrawFormat.Dispose();
path.Dispose();
watch.Stop();
var elapsedMs = watch.ElapsedTicks;
//14279 original ticks
//12764 removing label and reducing font size calcs...
// 1695 ! removed font size to external calc so it happens only once
e.Graphics.ResetClip();
e.Graphics.Clip.Dispose();
Tgt.Invalidated = false; //dont draw again until change
}