简体   繁体   English

Laggy剪切工具

[英]Laggy snipping tool

I'm trying to create snipping tool like this one https://app.prntscr.com/en/index.html . 我正在尝试创建这样的剪贴工具https://app.prntscr.com/en/index.html I'm able to modify some codes and managed to show resize handles with selected rectangle(snip), handles work fine and i can move the selected rectangle and resize it. 我能够修改一些代码,并设法显示调整大小句柄与选定的矩形(剪辑),句柄工作正常,我可以移动选定的矩形并调整大小。 But when I compare it with tool mentioned above it is very slow, can someone point some problems in my code to make it fast? 但是,当我将它与上面提到的工具进行比较时,它非常慢,有人可以指出我的代码中的一些问题,以使其快速?

The tool i'm making look like this: 我正在制作的工具如下所示: 在此输入图像描述

    /* 
 * Credit for this portion of Imgur Snipping Tool goes to Hans Passant from stackoverflow.com
 * http://stackoverflow.com/questions/3123776/net-equivalent-of-snipping-tool
 * 
 * Modified to work with multiple monitors.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Free_Snipping_Tool.Classes;

namespace Free_Snipping_Tool
{
    public partial class SnippingTool2 : Form
    {
        public enum ScreenshotType
        {
            FULL, ACTIVE, DEFINED
        }
        private int oldX;
        private int oldY;
        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        Rectangle topLeft = new Rectangle();
        Rectangle topMiddle = new Rectangle();
        Rectangle topRight = new Rectangle();
        Rectangle bottomLeft = new Rectangle();
        Rectangle bottomMiddle = new Rectangle();
        Rectangle bottomRight = new Rectangle();
        Rectangle leftMiddle = new Rectangle();
        Rectangle rightMiddle = new Rectangle();

        bool topLeftSelected = false;
        bool topMiddleSelected = false;
        bool topRightSelected = false;
        bool bottomLeftSelected = false;
        bool bottomMiddleSelected = false;
        bool bottomRightSelected = false;
        bool leftMiddleSelected = false;
        bool rightMiddleSelected = false;
        bool rectangleSelected = false;

        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [DllImport("user32.dll")]
        private static extern int GetForegroundWindow();

        SnipSizeControl lbl = new SnipSizeControl();
        floatingControlRight fcR = new floatingControlRight();
        floatingControlBottom fcB = new floatingControlBottom();

        public static Image Snip(ScreenshotType type)
        {
            switch (type)
            {
                case ScreenshotType.FULL:
                    return CreateScreenshot(0, 0, SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height);

                case ScreenshotType.DEFINED:
                    SnippingTool2 snipper = new SnippingTool2(CreateScreenshot(0, 0, SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height), new Point(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top));
                    if (snipper.ShowDialog() == DialogResult.OK)
                    {
                        return snipper.Image;
                    }
                    break;
                case ScreenshotType.ACTIVE:
                    RECT windowRectangle;
                    GetWindowRect((System.IntPtr)GetForegroundWindow(), out windowRectangle);
                    return CreateScreenshot(windowRectangle.Left, windowRectangle.Top, windowRectangle.Right - windowRectangle.Left, windowRectangle.Bottom - windowRectangle.Top);
            }
            return null;
        }

        private static Bitmap CreateScreenshot(int left, int top, int width, int height)
        {
            Bitmap bmp = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bmp);
            g.CopyFromScreen(left, top, 0, 0, new Size(width, height));
            g.Dispose();
            return bmp;
        }


        public SnippingTool2(Image screenShot, Point startPos)
        {
            InitializeComponent();

            lbl.Text = "0x0";
            lbl.Visible = true;
            lbl.BackColor = Color.Black;
            lbl.ForeColor = Color.White;
            lbl.Visible = false;
            this.Controls.Add(lbl);

            this.BackgroundImage = screenShot;
            Size s = screenShot.Size;
            this.ShowInTaskbar = false;
            this.FormBorderStyle = FormBorderStyle.None;
            this.StartPosition = FormStartPosition.Manual;
            this.Size = s;
            this.Location = startPos;
            this.DoubleBuffered = true;
        }
        public Image Image { get; set; }

        private Rectangle rcSelect = new Rectangle();
        private Point pntStart;
        bool handleSelected = false;

        protected override void OnMouseDown(MouseEventArgs e)
        {

            if ((e.X > topLeft.X) && (e.X < topLeft.X + topLeft.Width) && (e.Y > topLeft.Y) && (e.Y < topLeft.Y + topLeft.Height))
            {
                this.Cursor = Cursors.SizeNWSE;
                topLeftSelected = true;
                handleSelected = true;
            }

            else if ((e.X > topMiddle.X) && (e.X < topMiddle.X + topMiddle.Width) && (e.Y > topMiddle.Y) && (e.Y < topMiddle.Y + topMiddle.Height))
            {
                this.Cursor = Cursors.SizeNS;
                topMiddleSelected = true;
                handleSelected = true;
            }

            else if ((e.X > topRight.X) && (e.X < topRight.X + topRight.Width) && (e.Y > topRight.Y) && (e.Y < topRight.Y + topRight.Height))
            {
                this.Cursor = Cursors.SizeNESW;
                topRightSelected = true;
                handleSelected = true;
            }

            else if ((e.X > bottomLeft.X) && (e.X < bottomLeft.X + bottomLeft.Width) && (e.Y > bottomLeft.Y) && (e.Y < bottomLeft.Y + bottomLeft.Height))
            {
                this.Cursor = Cursors.SizeNESW;

                bottomLeftSelected = true;
                handleSelected = true;

            }

            else if ((e.X > bottomMiddle.X) && (e.X < bottomMiddle.X + bottomMiddle.Width) && (e.Y > bottomMiddle.Y) && (e.Y < bottomMiddle.Y + bottomMiddle.Height))
            {
                this.Cursor = Cursors.SizeNS;
                bottomMiddleSelected = true;
                handleSelected = true;
            }

            else if ((e.X > bottomRight.X) && (e.X < bottomRight.X + bottomRight.Width) && (e.Y > bottomRight.Y) && (e.Y < bottomRight.Y + bottomRight.Height))
            {
                this.Cursor = Cursors.SizeNWSE;
                bottomRightSelected = true;
                handleSelected = true;

            }

            else if ((e.X > leftMiddle.X) && (e.X < leftMiddle.X + leftMiddle.Width) && (e.Y > leftMiddle.Y) && (e.Y < leftMiddle.Y + leftMiddle.Height))
            {
                this.Cursor = Cursors.SizeWE;
                leftMiddleSelected = true;
                handleSelected = true;
            }

            else if ((e.X > rightMiddle.X) && (e.X < rightMiddle.X + rightMiddle.Width) && (e.Y > rightMiddle.Y) && (e.Y < rightMiddle.Y + rightMiddle.Height))
            {
                this.Cursor = Cursors.SizeWE;
                rightMiddleSelected = true;
                handleSelected = true;
            }
            else if ((e.X > rcSelect.X) && (e.X < rcSelect.X + rcSelect.Width) && (e.Y > rcSelect.Y) && (e.Y < rcSelect.Y + rcSelect.Height))
            {
                this.Cursor = Cursors.SizeAll;
                rectangleSelected = true;
                handleSelected = true;
            }
            else
            {
                this.Cursor = Cursors.Cross;
                topLeftSelected = false;
                topMiddleSelected = false;
                topRightSelected = false;
                bottomLeftSelected = false;
                bottomMiddleSelected = false;
                bottomRightSelected = false;
                leftMiddleSelected = false;
                rightMiddleSelected = false;
                rectangleSelected = false;
                handleSelected = false;
            }

            // Start the snip on mouse down
            if (e.Button != MouseButtons.Left) return;
            if (handleSelected == true) return;

            pntStart = e.Location;
            rcSelect = new Rectangle(e.Location, new Size(0, 0));
            lbl.Visible = true;

            lbl.Location = new Point(e.X + 10, e.Y + 10); ;
            lbl.Value = string.Format("0x0");
            lbl.Refresh();
            fcR.Visible = false;

            oldX = e.X;
            oldY = e.Y;

            this.Refresh();
        }

        protected override CreateParams CreateParams
        {
            get
            {
                var cp = base.CreateParams;
                cp.ExStyle |= 0x02000000;    // Turn on WS_EX_COMPOSITED
                return cp;
            }
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            // Modify the selection on mouse move

            if ((e.X > topLeft.X) && (e.X < topLeft.X + topLeft.Width) && (e.Y > topLeft.Y) && (e.Y < topLeft.Y + topLeft.Height))
            {
                this.Cursor = Cursors.SizeNWSE;
            }

            else if ((e.X > topMiddle.X) && (e.X < topMiddle.X + topMiddle.Width) && (e.Y > topMiddle.Y) && (e.Y < topMiddle.Y + topMiddle.Height))
            {
                this.Cursor = Cursors.SizeNS;
            }

            else if ((e.X > topRight.X) && (e.X < topRight.X + topRight.Width) && (e.Y > topRight.Y) && (e.Y < topRight.Y + topRight.Height))
            {
                this.Cursor = Cursors.SizeNESW;
            }

            else if ((e.X > bottomLeft.X) && (e.X < bottomLeft.X + bottomLeft.Width) && (e.Y > bottomLeft.Y) && (e.Y < bottomLeft.Y + bottomLeft.Height))
            {
                this.Cursor = Cursors.SizeNESW;
            }

            else if ((e.X > bottomMiddle.X) && (e.X < bottomMiddle.X + bottomMiddle.Width) && (e.Y > bottomMiddle.Y) && (e.Y < bottomMiddle.Y + bottomMiddle.Height))
            {
                this.Cursor = Cursors.SizeNS;
            }

            else if ((e.X > bottomRight.X) && (e.X < bottomRight.X + bottomRight.Width) && (e.Y > bottomRight.Y) && (e.Y < bottomRight.Y + bottomRight.Height))
            {
                this.Cursor = Cursors.SizeNWSE;
            }

            else if ((e.X > leftMiddle.X) && (e.X < leftMiddle.X + leftMiddle.Width) && (e.Y > leftMiddle.Y) && (e.Y < leftMiddle.Y + leftMiddle.Height))
            {
                this.Cursor = Cursors.SizeWE;
            }

            else if ((e.X > rightMiddle.X) && (e.X < rightMiddle.X + rightMiddle.Width) && (e.Y > rightMiddle.Y) && (e.Y < rightMiddle.Y + rightMiddle.Height))
            {
                this.Cursor = Cursors.SizeWE;
            }
            else if ((e.X > rcSelect.X) && (e.X < rcSelect.X + rcSelect.Width) && (e.Y > rcSelect.Y) && (e.Y < rcSelect.Y + rcSelect.Height))
            {
                this.Cursor = Cursors.SizeAll;
            }
            else
            {
                this.Cursor = Cursors.Cross;
            }

            if (e.Button == MouseButtons.Left && handleSelected == true)
            {
                Rectangle backupRect = rcSelect;

                if (topLeftSelected == true)
                {
                    rcSelect.X += e.X - oldX;
                    rcSelect.Width -= e.X - oldX;
                    rcSelect.Y += e.Y - oldY;
                    rcSelect.Height -= e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }

                else if (topRightSelected == true)
                {
                    rcSelect.Width += e.X - oldX;
                    rcSelect.Y += e.Y - oldY;
                    rcSelect.Height -= e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }

                else if (topMiddleSelected == true)
                {

                    rcSelect.Y += e.Y - oldY;
                    rcSelect.Height -= e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();

                }

                else if (bottomLeftSelected == true)
                {
                    rcSelect.Width -= e.X - oldX;
                    rcSelect.X += e.X - oldX;
                    rcSelect.Height += e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (bottomMiddleSelected == true)
                {
                    rcSelect.Height += e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (bottomRightSelected == true)
                {
                    rcSelect.Width += e.X - oldX;
                    rcSelect.Height += e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (rightMiddleSelected == true)
                {
                    rcSelect.Width += e.X - oldX;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (leftMiddleSelected == true)
                {
                    rcSelect.X += e.X - oldX;
                    rcSelect.Width -= e.X - oldX;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (rectangleSelected == true)
                {
                    rcSelect.X = rcSelect.X + e.X - oldX;
                    rcSelect.Y = rcSelect.Y + e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
            }
            else if (e.Button == MouseButtons.Left)
            {
                int x1 = Math.Min(e.X, pntStart.X);
                int y1 = Math.Min(e.Y, pntStart.Y);
                int x2 = Math.Max(e.X, pntStart.X);
                int y2 = Math.Max(e.Y, pntStart.Y);

                rcSelect = new Rectangle(x1, y1, x2 - x1, y2 - y1);

                this.Refresh();

                lbl.Location = new Point(x1, y1 - 25);
                lbl.Value = string.Format("{0}x{1}", x2 - x1, y2 - y1);
                lbl.Refresh();

            }

            oldX = e.X;
            oldY = e.Y;

        }
        protected override void OnMouseUp(MouseEventArgs e)
        {
            // Complete the snip on mouse-up
            if (rcSelect.Width <= 0 || rcSelect.Height <= 0) return;
            Image = new Bitmap(rcSelect.Width, rcSelect.Height);
            using (Graphics gr = Graphics.FromImage(Image))
            {
                gr.DrawImage(this.BackgroundImage, new Rectangle(0, 0, Image.Width, Image.Height),
                    rcSelect, GraphicsUnit.Pixel);
            }

            topLeftSelected = false;
            topMiddleSelected = false;
            topRightSelected = false;
            bottomLeftSelected = false;
            bottomMiddleSelected = false;
            bottomRightSelected = false;
            leftMiddleSelected = false;
            rightMiddleSelected = false;
            rectangleSelected = false;
            handleSelected = false;

            //DialogResult = DialogResult.OK;
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            // Draw the current selection
            using (Brush br = new SolidBrush(Color.FromArgb(30, Color.Black)))
            {
                int x1 = rcSelect.X; int x2 = rcSelect.X + rcSelect.Width;
                int y1 = rcSelect.Y; int y2 = rcSelect.Y + rcSelect.Height;
                e.Graphics.FillRectangle(br, new Rectangle(0, 0, x1, this.Height));
                e.Graphics.FillRectangle(br, new Rectangle(x2, 0, this.Width - x2, this.Height));
                e.Graphics.FillRectangle(br, new Rectangle(x1, 0, x2 - x1, y1));
                e.Graphics.FillRectangle(br, new Rectangle(x1, y2, x2 - x1, this.Height - y2));
            }
            using (Pen pen = new Pen(Color.Red, 1))
            {
                e.Graphics.DrawRectangle(pen, rcSelect);
            }


            //Resize Controls

            //Top left
            topLeft = new Rectangle(rcSelect.X - 3, rcSelect.Y - 3, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, topLeft);
            }

            //Top middle
            topMiddle = new Rectangle((rcSelect.X - 3) + (rcSelect.Width) / 2, rcSelect.Y - 3, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, topMiddle);
            }

            //Top right
            topRight = new Rectangle((rcSelect.X - 3) + rcSelect.Width, rcSelect.Y - 3, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, topRight);
            }


            //Bottom left
            bottomLeft = new Rectangle(rcSelect.X - 3, (rcSelect.Y - 3) + rcSelect.Height, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, bottomLeft);
            }

            //Bottom middle
            bottomMiddle = new Rectangle((rcSelect.X - 3) + (rcSelect.Width) / 2, (rcSelect.Y - 3) + rcSelect.Height, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, bottomMiddle);
            }

            //Bottom right
            bottomRight = new Rectangle((rcSelect.X - 3) + rcSelect.Width, (rcSelect.Y - 3) + rcSelect.Height, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, bottomRight);
            }

            //Left middle
            leftMiddle = new Rectangle(rcSelect.X - 3, (rcSelect.Y - 3) + (rcSelect.Height) / 2, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, leftMiddle);
            }

            //Right middle
            rightMiddle = new Rectangle((rcSelect.X - 3) + rcSelect.Width, (rcSelect.Y - 3) + (rcSelect.Height) / 2, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, rightMiddle);
            }
        }
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            // Allow canceling the snip with the Escape key
            if (keyData == Keys.Escape) this.DialogResult = DialogResult.Cancel;
            return base.ProcessCmdKey(ref msg, keyData);
        }
    }
}

Edit: Project file: https://www.dropbox.com/s/k1afggj6inye4kp/Free%20Snipipng%20Tool-project.rar?dl=0 编辑:项目文件: https//www.dropbox.com/s/k1afggj6inye4kp/Free%20Snipipng%20Tool-project.rar?dl=0

I will cite the official Windows documentation . 我将引用正式的Windows文档 Although the doc is about Native API, Winforms uses the same underlying technology: 虽然该文档是关于Native API的,但Winforms使用相同的底层技术:

An application invalidates a portion of a window and sets the update region by using the InvalidateRect or InvalidateRgn function. 应用程序使窗口的一部分无效,并使用InvalidateRect或InvalidateRgn函数设置更新区域。 These functions add the specified rectangle or region to the update region, combining the rectangle or region with anything the system or the application may have previously added to the update region. 这些函数将指定的矩形或区域添加到更新区域,将矩形或区域与系统或应用程序之前可能已添加到更新区域的任何内容组合在一起。

The InvalidateRect and InvalidateRgn functions do not generate WM_PAINT messages. InvalidateRect和InvalidateRgn函数不生成WM_PAINT消息。 Instead, the system accumulates the changes made by these functions and its own changes. 相反,系统会累积这些函数所做的更改及其自身的更改。 By accumulating changes, a window processes all changes at once instead of updating bits and pieces one step at a time. 通过累积更改,窗口一次处理所有更改,而不是一次更新一个步骤。

Calling .NET Refresh() is equivalent of calling InvalidateAll() + Update() . 调用.NET Refresh()等同于调用InvalidateAll() + Update() InvalidateAll marks the whole screen as invalid, and Update() forces the process of redrawing what's invalid, so the whole screen. InvalidateAll将整个屏幕标记为无效,而Update()强制重绘无效的过程,因此整个屏幕都是如此。 You can optimize your program if you just invalidate "manually" what you know has changed. 如果您只是“手动”使您知道已更改的内容无效,则可以优化程序。

That's what I did in my modified sample. 这就是我在修改过的样本中所做的。 Instead of calling Refresh(), I added a new oldRcRect variable to be able to invalidate old state and new state, and a RefreshOnMove() method like this (and I replaced most Refresh calls by a RefreshOnMove call) : 我没有调用Refresh(),而是添加了一个新的oldRcRect变量,以便能够使旧状态和新状态无效,以及像这样的RefreshOnMove()方法(我通过RefreshOnMove调用替换了大多数Refresh调用):

    private void RefreshOnMove()
    {
        // invalidate the old rect (+ size of red box)
        var rc = oldRcSelect;
        rc.Inflate(3, 3);
        Invalidate(rc);

        // invalidate the new rect (+ size of red box)
        // note you can almost omit this second one, but if you move the mouse really fast, you'll see some red box not fully displayed
        // but the benefit is small, something like a 3 x width/height rectangle
        rc = rcSelect;
        rc.Inflate(3, 3);
        Invalidate(rc);

        // each time you call invalidate, you just accumulate a change
        // to the change region, nothing actually changes on the screen

        // now, ask Windows to process the combination of changes
        Update();
    }

PS: about my comment about inner region, I just mean it may be possible to avoid invalidating the content of the white box each time as well, but it's more complex. PS:关于我对内部区域的评论,我只是说每次都可以避免使白盒的内容无效,但它更复杂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM