Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to analyze the full-featured screenshot examples of C++

2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

How to analyze C++ to achieve full-featured screenshot examples, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain in detail for you, people with this need can come to learn. I hope you can get something.

Screen capture has become a necessary module of all IM instant messaging software, and it is also one of the most frequently used functions in daily office. Today, from the perspective of C++ development, let's take a look at how the main function points of the screenshot are realized. Here, we will share with you many implementation details of the screenshot.

Development tools: Visual Studio 2010

Development language: C++

UI framework: MFC (it can also be based on the open source duilib framework, which actually calls Windows API in duilib)

1. Overview

To use screenshots, it is actually very easy to install a chat software or office software, such as QQ, WeCom, nails, flying books and so on. But it is not so easy to develop screenshot modules like these. In fact, the technology to achieve screen capture is not complex, mainly in the handling of various details.

Some people may say, I do not need to develop these features, I can search some open source code, or I can search the Internet for a bunch of articles about screenshots or download resources, and I should be able to find useful code or resources. What I want to say is that you can give it a try, many of them are just scratching the surface, almost none of them have achieved the complete screenshot function, and none of them can be used in the actual project. Simply write a few words of code, play is OK, from the real commercial to the project, the difference is too far! Real project-level code, to consider a variety of scenarios and details, to consider performance and stability, is tempered after many rounds of testing, can not be casually written out!

Combined with the actual project experience of developing screen screenshots, this paper will introduce in detail the implementation details and methods of the main function points of screen screenshots, so as to provide you with a reference and reference.

2. The main function points of screenshots

A screenshot with complete function should contain the above function points, such as desktop graying, window automatic lasso, area magnification, rectangle and other graphic elements, input text, etc.

3. The idea of realizing the subject of screen screenshot

On the Internet, it is difficult to find a detailed description of the full function of screen screenshots, so what is the idea of the main body of screen screenshots? Let's briefly describe it. The effect of a set of screenshots we have implemented is as follows (download the source code of C++ at the end of the article):

Below, based on the screenshot we have implemented, we will introduce in detail some of the main function points and implementation ideas of the screenshot.

3.1. The screenshot window is set to the top of the full screen.

We need to create a screenshot of the main window, open the screenshot and put the screenshot of the main window full screen, covering the entire screen, and set the window TopMost top attribute. Then our subsequent operations are drawn on this full-screen top window, that is, all the contents seen in the screenshot window (such as desktop ashing, window lasso, area magnification, various elements, etc.) are drawn!

3.2. Desktop ashing

When opening the screenshot, first save the image on the current desktop to the bitmap object, and save two bitmaps, one is the brightly colored desktop image, and the other is the grayed desktop image. First, the grayed bitmap is drawn on the screenshot dialog box to achieve the gray mask. Then, according to the area selected by the user pulling the mouse, the bright color image of the corresponding region is extracted from the bright color bitmap and drawn on the dialog box, and the effect of region selection can be achieved.

3.3, window automatic lasso

When starting the screenshot, you need to traverse all the open windows in the current system, as well as the child windows in these windows, and record the coordinate locations of these windows and save them in memory. When the mouse moves, see which uppermost window the mouse moves to, then draw the boundary of the lasso in the area of the window, and "light" the window area. Lighting up is actually very simple, according to the coordinates of the window to the memory saved in the bright color bitmap to dig out the corresponding area, draw to the window, and then draw the lasso boundary line on the window boundary.

3.4. Region magnification

In fact, to achieve this function is not difficult, you can carefully observe the following mainstream IM software display details, you can find ideas and answers! Region magnification is to enlarge the area around the location where the mouse is moved in real time, the enlarged area is a small rectangular area centered on the mouse punctuation, and then magnify the area 4 times to draw the magnified effect to the screenshot dialog box.

3.5. Selection of interception area

You can use the rubber band type CRectTracker provided in the Microsoft MFC library to achieve region selection. The rubber band corresponds to a selection border, and the rubber band frame of the selection area is drawn by pulling the mouse. The rubber band frame supports dragging to change the size of the rubber band frame. According to the selected area of the eraser, pick out the bright color selection area from the bright color bitmap saved in memory and draw it to the screenshot window.

3.5. Screenshot toolbar

The screenshot tool bar is generally made into a window close to the screenshot selection area, and the window contains a row of function buttons, including rectangle tool, ellipse tool, straight line tool with arrow, curve tool, Undo tool, close screenshot, and finish screenshot. After selecting the rectangle tool, the Ellipse tool, the Line with Arrow tool and the Curve tool, the mouse draws the corresponding types of entities on the screenshot window. The Undo button retraces the last sketched element.

3.6, drawing of rectangle and other elements

We need to design the C++ class corresponding to the element type, which inherits from a base class of CSharp. The base class stores the line color, start and end coordinates of the current drawing elements, and a pure virtual interface Draw for drawing the content of the elements. The specific Draw operations are all implemented in the specific elements. This actually uses the concept of polymorphism in C++.

For rectangles, ellipses and straight lines with arrowheads, we only need to record the start and end coordinates of the elements. For curves, which are composed of multiple straight line segments, we need to record multiple points in the drawing process. When the user presses the left button, starts to draw the element, records the starting coordinates of the element at this time, screenshots the drawing of the current element when the left button pops up, records the end coordinates of the element, and then creates the corresponding type of primitive object, saves the start and end coordinates to the object, and then saves these primitive objects to the list of elements. When the window needs to be refreshed, call the Draw interface of these images in the list to draw all the elements onto the screenshot window.

4. Implementation details of desktop ashing

When the screenshot is opened, the desktop image is saved to the bright bitmap object, at the same time, the image is grayed out, and the processed image is saved to the dark bitmap object. The code to save the desktop image is as follows:

/ / copy the desktop. LpRect represents the selected area. Whether the bSave tag saves the image content to the clipboard HBITMAP CScreenCatchDlg::CopyScreenToBitmap (LPRECT lpRect) {/ / make sure that the selected area is not an empty rectangle if (IsRectEmpty (lpRect)) {return NULL;} CString strLog / / create a device description table for the screen HDC hScrDC =: CreateDC (_ T ("DISPLAY"), NULL, NULL, NULL); if (hScrDC = = NULL) {strLog.Format (_ T ("[CCatchScreenDlg::CopyScreenToBitmap] failed to create DISPLAY, GetLastError:% d"), GetLastError (); WriteScreenCatchLog (strLog); return NULL } / / create a compatible memory device description table for the screen device description table HDC hMemDC =: CreateCompatibleDC (hScrDC); if (hMemDC = = NULL) {strLog.Format (_ T ("[CCatchScreenDlg::CopyScreenToBitmap] failed to create a hScrDC compatible hMemDC, GetLastError:% d"), GetLastError (); WriteScreenCatchLog (strLog);: DeleteDC (hScrDC); return NULL } int nX = 0; int nY = 0; int nX2 = 0; int nY2 = 0; int nWidth = 0; int nHeight = 0; / guarantee that left is less than right,top < bottom LONG lTemp = 0; if (lpRect- > left > lpRect- > right) {lTemp = lpRect- > left; lpRect- > left = lpRect- > right; lpRect- > right = lTemp } if (lpRect- > top > lpRect- > bottom) {lTemp = lpRect- > top; lpRect- > top = lpRect- > bottom; lpRect- > bottom = lTemp;} / / get the coordinates of the selected area nX = lpRect- > left; nY = lpRect- > top; nX2 = lpRect- > right; nY2 = lpRect- > bottom; / / ensure that the selected area is visible if (nX

< 0 ) { nX = 0; } if ( nY < 0 ) { nY = 0; } if ( nX2 >

M_xScreen) {nX2 = nY2 screen;} if (nY2 > m_yScreen) {nY2 = nY2 screen;} nWidth = nX2-screen = nY2-screen / / create a bitmap compatible with the screen device description table HBITMAP hBitmap =: CreateCompatibleBitmap (hScrDC, nWidth, nHeight) If (hBitmap = = NULL) {strLog.Format (_ T ("[CCatchScreenDlg::CopyScreenToBitmap] failed to create Bitmap compatible with hScrDC, GetLastError:% d"), GetLastError (); WriteScreenCatchLog (strLog);: DeleteDC (hScrDC);: DeleteDC (hMemDC); return NULL } / / Select the new bitmap into the memory device description table: SelectObject (hMemDC, hBitmap); BOOL bRet =: BitBlt (hMemDC, 0,0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY | CAPTUREBLT) / / CAPTUREBLT-this parameter ensures that the transparent window if (! bRet) {strLog.Format (_ T ("[CCatchScreenDlg::CopyScreenToBitmap] failed to copy hScrDC to hMemDC, GetLastError:% d"), GetLastError ()); WriteScreenCatchLog (strLog);: DeleteDC (hScrDC);: DeleteDC (hMemDC);: DeleteObject (hBitmap); return NULL } if (hScrDC! = NULL) {:: DeleteDC (hScrDC);} if (hMemDC! = NULL) {:: DeleteDC (hMemDC);} return hBitmap; / / hBitmap resource cannot be released because it is used outside the function.

How to gray the desktop image? In fact, it is very simple. Just read out the RGB of each pixel value in the saved desktop bitmap, multiply the R, G, B values in each pixel by a coefficient, and then set these values back to the bitmap. The related code is as follows:

Void CScreenCatchDlg::GrayLightBmp () {CString strLog; CDC * pDC = GetDC (); ASSERT (pDC); if (pDC = = NULL) {strLog.Format (_ T ("[CCatchScreenDlg::DoGrayLightBmp] GetDC failed, GetLastError:% d"), GetLastError (); WriteScreenCatchLog (strLog); return;} CBitmap cbmp; cbmp.Attach (m_hGreyBitmap) / / use m_hDarkBitmap BITMAP bmp; cbmp.GetBitmap (& bmp) that temporarily stores bright bitmaps; cbmp.Detach (); / / objects and handles need to be separated, and m_hDarkBitmap bitmap resources need to be saved in memory. If not, when objects die, m_hDarkBitmap bitmap resources will be automatically released UINT * pData = new UINT [bmp.bmWidth * bmp.bmHeight] If (pData = = NULL) {int nSize = bmp.bmWidth * bmp.bmHeight; strLog.Format (_ T ("[CCatchScreenDlg::DoGrayLightBmp] pData failed to request% s bytes of memory via new, direct return"), nSize); WriteScreenCatchLog (strLog); ReleaseDC (pDC); return;} BITMAPINFO bmpInfo; bmpInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = bmp.bmWidth BmpInfo.bmiHeader.biHeight =-bmp.bmHeight; bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biCompression = BI_RGB; bmpInfo.bmiHeader.biBitCount = 32; int nRet = GetDIBits (pDC- > m_hDC, m_hGreyBitmap, 0, bmp.bmHeight, pData, & bmpInfo, DIB_RGB_COLORS) If (0 = = nRet) {strLog.Format (_ T ("[CCatchScreenDlg::DoGrayLightBmp] GetDIBits failure nRet = = 0, GetLastError:% d"), GetLastError (); WriteScreenCatchLog (strLog);} / / multiplies the RGB values of all pixels in the image by 0.4, that is, the image ashing UINT color, r, g, b; for (int I = 0; I) is realized.

< bmp.bmWidth * bmp.bmHeight; i++ ) { color = pData[i]; b = ( color >

24) * 0.4; g = (color > 24) * 0.4; r = (color > 24) * 0.4; pData [I] = RGB (r, g, b);} / / if the function succeeds, the return value is the number of copied scan lines; if the function fails, the return value is 0. NRet = SetDIBits (pDC- > m_hDC, m_hGreyBitmap, 0, bmp.bmHeight, pData, & bmpInfo, DIB_RGB_COLORS); if (0 = = nRet) {strLog.Format (_ T ("[CCatchScreenDlg::DoGrayLightBmp] SetDIBits failure nRet = = 0, GetLastError:% d"), GetLastError (); WriteScreenCatchLog (strLog);} delete [] pData; pData = NULL; ReleaseDC (pDC);}

Keep two bitmaps in memory, one is a brightly colored desktop image, and the other is a grayed desktop image. First, the grayed bitmap is drawn on the screenshot dialog box to achieve the gray mask. Then, according to the area selected by the user pulling the mouse, the bright color image of the corresponding region is extracted from the bright color bitmap and drawn on the dialog box, and the effect of region selection can be achieved.

5. Realization of window automatic lasso

When starting the screenshot, you need to traverse all the open windows in the current system, as well as the child windows in these windows, and record the coordinate locations of these windows and save them in memory. First, call the system API function EnumWindows to enumerate all the open windows in the system:

/ / use EnumWindows to enumerate all large windows opened by the current system: EnumWindows (EnumWindowsProc, NULL); BOOL CEnumWindows::EnumWindowsProc (HWND hWnd, LPARAM lParam) {TCHAR achWndName [Max _ PATH+1] = {0} If (:: IsWindow (hWnd) & &:: IsWindowVisible (hWnd) & &!:: IsIconic (hWnd)) {/ / Save all valid windows EnumedWindowInfo tWndInfo; tWndInfo.m_hWnd = hWnd;:: GetWindowText (hWnd, achWndName, sizeof (achWndName) / sizeof (TCHAR)); tWndInfo.m_strWndName = achWndName Filter out / / if (! _ tcscmp (tWndInfo.m_strWndName, _ T ("Program Manager") / / {/ / return TRUE; / /}:: GetWindowRect (hWnd, & (tWndInfo.m_rcWnd)) M_listWindows.push_back (tWndInfo);} return TRUE;}

Then traverse these windows, use recursive calls to find out the child windows of these main windows, and record the information of these child windows.

When the mouse moves, according to the location coordinates of the mouse, traverse in the window information list to see which uppermost window the mouse moves to, then draw the boundary of the lasso in the area of the window, and "light" the window area. Lighting up is actually very simple, according to the coordinates of the window to the memory saved in the bright color bitmap to dig out the corresponding area, draw to the window, and then draw the lasso boundary line on the window boundary.

6. Realization of region magnification

To achieve this is not difficult, you can carefully observe the following mainstream IM software display details, you can find ideas and methods! Region magnification is a real-time magnification of the area around the location where the mouse is moved, which is a small rectangular area centered on mouse punctuation.

After determining the coordinates of the area to be magnified, pick out the area to be magnified from the bright color bitmap of the desktop saved in memory, and then call StretchBlt to draw the enlarged image to the screenshot window. The related code is as follows:

/ / draw the automatic lasso window void CScreenCatchDlg::DrawAutoLassoWndArea (CDC* pMemDC, CDC* pLightDC) {if (pMemDC = = NULL | | pLightDC = = NULL) {return;} if (m_rcTargetWnd.IsRectEmpty ()) {return;} / / first extract the target window from the bright color image CRect rcArea = m_rcTargetWnd BOOL bRet = pMemDC- > BitBlt (rcArea.left,rcArea.top, rcArea.Width (), rcArea.Height (), pLightDC, rcArea.left,rcArea.top, SRCCOPY); if (! bRet) {WriteScreenCatchLog (_ T ("[CCatchScreenDlg::DrawAutoLassoWndPic] pMemDC- > BitBlt (rcArea.left,rcArea.top... Failed);} rcArea.left = (rcArea.left-4m_yScreen)? (m_yScreen-4): rcArea.bottom; / / then draw the automatic lasso boundary line CPen pen (PS_SOLID, 1, RGB (0174,255)) around the target window; CPen* pOldPen = pMemDC- > SelectObject (& pen); CBrush* pOldBrush = (CBrush*) pMemDC- > SelectStockObject (NULL_BRUSH) / call SelectStockObject using NULL_BRUSH to achieve the effect of transparent brush rcArea.InflateRect (1,1); pMemDC- > Rectangle (& rcArea); rcArea.InflateRect (1,1); pMemDC- > Rectangle (& rcArea); rcArea.InflateRect (1,1); pMemDC- > Rectangle (& rcArea); rcArea.InflateRect (1,1); pMemDC- > Rectangle (& rcArea) / / rcArea.DeflateRect (1,1); / / rcArea.DeflateRect (1,1); / / pMemDC- > Rectangle (& rcArea); / / rcArea.DeflateRect (1,1); / / pMemDC- > Rectangle (& rcArea); / / rcArea.DeflateRect (1,1); / / pMemDC- > Rectangle (& rcArea); pMemDC- > SelectObject (pOldBrush); pMemDC- > SelectObject (pOldPen) } 7. Selection of interception area

The rubber band CRectTracker in Microsoft's MFC library is a good thing. It draws a rectangular boundary line with a border line, and there are eight points on the border line that you can click and drag with the mouse to resize the rectangular boundary line. We can use this rubber band class to select the screenshot area.

The implementation of rubber band CRectTracker is a bit complex and ingenious. We take the code of this class from the MFC library and make some simple and flexible modifications to it, which can be used in the screenshot module. Add some message notifications and additional processing mechanisms. The class we picked out is named CCatchTracker, and its header file is as follows:

/ / CCatchTracker-simple rectangular tracking rectangle w/resize handles / / CCatchTracker class comes from the MFC source file COPY, modifies according to its own needs, makes some changes to the message mechanism / /, and adds part of the interface # ifndef CATCH_SCREEN_TRACKER_H#define CATCH_SCREEN_TRACKER_H#define CX_BORDER 1#define CY_BORDER 1#define WM_UPDATE_TOOLBAR_POS (WM_USER+700) / / update screenshot toolbar location message Send the message # define CRIT_RECTTRACKER 5void AFXAPI AfxLockGlobals (int nLockType) to the interface when the intercept area changes Void AFXAPI AfxUnlockGlobals (int nLockType); void AFXAPI AfxDeleteObject (HGDIOBJ* pObject); enum TrackerHit {hitNothing =-1, hitTopLeft = 0, hitTopRight = 1, hitBottomRight = 2, hitBottomLeft = 3, hitTop = 4, hitRight = 5, hitBottom = 6, hitLeft = 7, hitMiddle = 8}; class CCatchTracker {public:// Constructors CCatchTracker (); CCatchTracker (LPCRECT lpSrcRect, UINT nStyle) / / StyleFlags enum StyleFlags {solidLine = 1, dottedLine = 2, hatchedBorder = 4, resizeInside = 8, resizeOutside = 16, hatchInside = 32, resizeMiddle = 80 / / set Intermediate} / / Hit-Test codes / / enum TrackerHit / / {/ / hitNothing =-1, / / hitTopLeft = 0, hitTopRight = 1, hitBottomRight = 2, hitBottomLeft = 3, / / hitTop = 4, hitRight = 5, hitBottom = 6, hitLeft = 7, hitMiddle = 8 /}; / / Operations void Draw (CDC* pDC) const; void GetTrueRect (LPRECT lpTrueRect) const BOOL SetCursor (CWnd* pWnd, UINT nHitTest) const; BOOL Track (CWnd* pWnd, CPoint point, BOOL bAllowInvert = TRUE, CWnd* pWndClipTo = NULL); BOOL TrackRubberBand (CWnd* pWnd, CPoint point, BOOL bAllowInvert = TRUE); int HitTest (CPoint point) const; int NormalizeHit (int nHandle) const; / / Overridables virtual void DrawTrackerRect (LPCRECT lpRect, CWnd* pWndClipTo, CDC* pDC, CWnd* pWnd) Virtual void AdjustRect (int nHandle, LPRECT lpRect); virtual void OnChangedRect (const CRect& rectOld); virtual UINT GetHandleMask () const; / / Implementationpublic: virtual ~ CCatchTracker (); public: / / set the adjustment cursor void SetResizeCursor (UINT nID_N_S,UINT nID_W_E,UINT nID_NW_SE, UINT nID_NE_SW,UINT nIDMiddle) / / create a brush and call void CreatePen () internally; / / set the rectangle color void SetRectColor (COLORREF rectColor); / / set whether the rectangle tracker can be moved, and cannot move the void SetMovable (BOOL bMoveable) when you click the button in the screenshot sidebar; BOOL GetMovable () {return masked bMovable;} / implementation helpers int HitTestHandles (CPoint point) const; void GetHandleRect (int nHandle, CRect* pHandleRect) const; void GetModifyPointers (int nHandle, int**ppx, int**ppy, int* px, int*py); virtual int GetHandleSize (LPCRECT lpRect = NULL) const; BOOL TrackHandle (int nHandle, CWnd* pWnd, CPoint point, CWnd* pWndClipTo); void Construct (); void SetMsgHwnd (HWND hwnd) Public: / / Attributes UINT masks; / / current state CRect masks; / / current position (always in pixels) CSize massively sizeMins; / / minimum X and Y size during track operation int mechnHandleSize; / / size of resize handles (default from WIN.INI) BOOL masks bAllowInvert; / / flag passed to Track or TrackRubberBand CRect masks Last; CSize m_sizeLast BOOL masked bErase; / / TRUE if DrawTrackerRect is called for erasing BOOL masked bFinalErase; / / TRUE if DragTrackerRect called for final erase COLORREF masked colors; / / current rectangle color HWND MsgWnd; / / window handle BOOL m_bMovable that sends messages to the interface. / / Mark whether the rectangular tracker can be moved, which cannot be moved when you click the button in the screenshot sidebar; # endif

According to the selected area of the eraser, pick out the bright color selection area from the bright color bitmap saved in memory and draw it to the screenshot window. The screenshot bar is close to the rubber band selection area and is located below the rubber band selection area. when the size of the rubber band area changes, the screenshot bar window should be notified to start with the screenshot area, so that the screenshot bar follows the rubber band selection area. So we throw the following notification message in the rubber band class:

Switch (msg.message) {/ / handle movement/accept messages case WM_LBUTTONUP: case WM_MOUSEMOVE: rectOld = m_rect / / handle resize cases (and part of move) if (px! = NULL) * px = (int) (short) LOWORD (msg.lParam)-xDiff; if (py! = NULL) * py = (int) (short) HIWORD (msg.lParam)-yDiff / / handle move case if (nHandle = = hitMiddle) {m_rect.right = m_rect.left + nWidth; m_rect.bottom = m_rect.top + nHeight } / / send the coordinates of the upper left corner and the lower right corner of the rectangular region to the interface, on the one hand, you need to use it when moving the rectangle. / / on the one hand, you need to use if (IsWindow (m_hMsgWnd)) / / to verify whether it is a valid window handle {BOOL bLBtnUp = FALSE when updating the location of the screenshot toolbar in the interface. If (msg.message = = WM_LBUTTONUP) {bLBtnUp = TRUE;}:: SendMessage (m_hMsgWnd, WM_UPDATE_TOOLBAR_POS, (WPARAM) & m_rect, (LPARAM) bLBtnUp) } 8. Drawing of rectangle and other elements

To support the drawing of rectangle, ellipse, straight line with arrowhead and curve in the screenshot, we designed C++ classes corresponding to the entity type. These classes inherit from a base class of CSharp. The base class stores the line color, start and end coordinates of the current drawing entity, and a pure virtual interface Draw for drawing the content of the entity:

/ / shape base class class CShape {public: CShape (); virtual ~ CShape (); virtual void Draw (CDC* pDC) = 0; protected: CPoint mcolors startPt; / / start CPoint mcolors; / / end COLORREF mcolors; / / current use color}

The specific Draw operations are implemented in the specific elements. This actually uses the concept of polymorphism in C++.

Taking a rectangular entity as an example, the header file of the rectangular class CRectangle is as follows:

/ / rectangular class CRectangle: public CShape {public: CRectangle (CPoint startPt, CPoint endPt); ~ CRectangle (); void Draw (CDC* pDC);}

The code for the cpp source file is as follows:

CRectangle::CRectangle (CPoint startPt, CPoint endPt) {m_startPt = startPt; m_endPt = endPt;} CRectangle::~CRectangle () {} void CRectangle::Draw (CDC* pDC) {if (pDC = = NULL) {return;} Pen pen (Color (255,0,0); pen.SetLineCap (LineCapRound, LineCapRound, DashCapRound) Graphics graphics (pDC- > GetSafeHdc ()); / / graphics.SetSmoothingMode (SmoothingModeAntiAlias); / / graphics.DrawRectangle (& pen, m_startPt.x, m_startPt.y, m_endPt.x-m_startPt.x, m_endPt.y-m_startPt.y); CRect rcTemp (m_startPt.x, m_startPt.y, m_endPt.x, m_endPt.y); rcTemp.NormalizeRect () Status stRet = graphics.DrawRectangle (& pen, rcTemp.left, rcTemp.top, rcTemp.Width (), rcTemp.Height ();}

For rectangles, ellipses and straight lines with arrowheads, we only need to record the start and end coordinates of the elements. For curves, which are composed of multiple straight line segments, we need to record multiple points in the drawing process. When the user presses the left button, starts to draw the element, records the starting coordinates of the element at this time, screenshots the drawing of the current element when the left button pops up, records the end coordinates of the element, and then creates the corresponding type of primitive object, saves the start and end coordinates to the object, and then saves these primitive objects to the list of elements. When the window needs to be refreshed, call the Draw interface of these images in the list to draw all the elements onto the screenshot window.

At the beginning, we use GDI function to draw entities, such as API function Reactangle (drawing rectangle) and Ellipse (drawing ellipse) in GDI, but when drawing straight lines and curves with arrows, the result drawn by GDI function has obvious aliasing, the effect is not good. So later, we changed all the drawing of elements to use the GDI+ library, and the Graphics class in GDI+ can set the anti-aliasing mode when drawing elements:

Case emBtnEllipse: / / draw ellipses {/ / for anti-aliasing, all use GDI+ to draw primitives (GDI has obvious aliasing when drawing lines and curves) Pen pen (Color (255,0,0), WIDTH_DRAW_PEN); Graphics graphics (m_tmpDrawDC.GetSafeHdc ()) Graphics.SetSmoothingMode (SmoothingModeAntiAlias) Graphics.DrawEllipse (& pen, macaque drawStartPt.xgram) point.x-m_drawStartPt.x/*+m_rectTracker.m_rect.left*/, point.x-m_drawStartPt.x/*+m_rectTracker.m_rect.left*/ Point.y-m_drawStartPt.y/*+m_rectTracker.m_rect.top*/) } break;9, drawing mechanism of screenshot window

All the contents displayed in the full-screen top screenshot main window are drawn by us in the screenshot window, such as the automatic lasso effect of the window, the area magnification effect, the rubber band selection box of the screenshot area, the drawing of various elements, and so on.

To take over the drawing of all content on the screenshot window, we need to intercept the WM_ERASEBKGND and WM_PAINT messages of the screenshot window. First of all, after receiving the WM_ERASEBKGND message, return TRUE directly. You don't need the system to draw the background for us:

BOOL CScreenCatchDlg::OnEraseBkgnd (CDC* pDC) {return TRUE;}

When you receive a WM_PAINT message, use double-buffered drawing to draw what you want to draw on the screenshot window. The idea of the so-called double-buffered drawing is to first draw all the contents that need to be drawn to the memory DC, which may take time, and then draw the contents of the memory DC to the DC. Double-buffer drawing is an effective method to solve the problem of window flickering when drawing.

When dealing with WM_PAINT messages, you need to call BeginPaint and EndPaint to empty the invalid area of the window after drawing the window, and remember to call these two functions. If you do not call these two interfaces, it will cause the window to have invalid areas all the time, so that the system always detects the invalid areas of the window and keeps generating WM_PAINT messages, so the program has been busy with WM_PAINT messages, resulting in low-priority WM_TIMER messages being flooded and discarded, and the interface will cause serious flicker problems due to continuous drawing. In our OnPaint function, we use the CPaintDC class, which encapsulates calls to BeginPaint and EndPaint:

CPaintDC::CPaintDC (CWnd* pWnd) {ASSERT_VALID (pWnd); ASSERT (:: IsWindow (pWnd- > m_hWnd)); if (! Attach (:: BeginPaint (m_hWnd = pWnd- > m_hWnd, & m_ps)) AfxThrowResourceException ();} CPaintDC::~CPaintDC () {ASSERT (m_hDC! = NULL); ASSERT (:: IsWindow (m_hWnd)) :: EndPaint (m_hWnd, & m_ps); Detach ();}

Sometimes we want to refresh the window immediately after certain operations. We can call InvalidateRect and UpdateWindow,InvalidateRect together to invalidate the window. UpdateWindow allows the system to generate WM_PAINT messages immediately and deliver the WM_PAINT to the window process (instead of putting WM_PAINT in the message queue for processing), so that the window can be refreshed immediately. Calling UpdateWindow is equivalent to forcing the window to refresh immediately.

As for the relationship between WM_PAINT, BeginPaint, InvalidateRect, and UpdateWindow, you can refer to a previous topic article I wrote specifically: https://blog.csdn.net/chenlycly/article/details/120931704, which describes the relationship between these objects in detail.

10. Detailed design of screenshot exit type

There are a variety of scenarios that exit screenshots, and different exit scenarios may require different subsequent processing, so we have defined a variety of types when exiting screenshots:

Enum EmQuitType {emQuitInvalid =-1, / / invalid exit type emESCQuit = 0, / / press ESC to exit emRClickQuit, / / right-click to exit emLDClickQuit, / / left-click double-click to exit emSendtoBlogQuit, / / send to Weibo to exit emSaveQuit / / exit emCancelQuit after saving screenshot, / / cancel screenshot and exit emCompleteQuit, / / exit emMemoryLackQuit after screenshot completion, / / exit gdi operation caused by insufficient memory exit emCutRectEmptyQuit / / exit empty capture area}

1) press the ESC key to exit, right-click to exit, save the picture to exit, click the cancel button to exit, and the interception area is empty to exit.

When exiting screenshots in these scenarios, the screenshot module does not need any processing, but simply exits screenshots.

2) double-click the screenshot area to exit the screenshot, and click the finish button to exit the screenshot

In these scenarios, before exiting the screenshot, the picture bitmap of the captured area is saved to the clipboard and the screenshot is saved to the disk file. After exiting the screenshot, if it is triggered by the screenshot entry in the chat box, the captured image needs to be automatically inserted into the chat box.

3) failed to exit screenshot of insufficient memory

This scenario is due to the failure of the GDI function call due to insufficient system memory, and the external need to pop up a prompt that "screenshot failed, may be caused by insufficient system memory, quit some programs and try again".

Therefore, we design the corresponding exit type according to these exit scenarios, set the exit type when exiting the screenshot, and provide the API GetQuitType for the exit type when obtaining the exit screenshot, so that after exiting the screenshot, the external call GetQuitType gets the type of exit of the current screenshot to see if further processing is needed.

11. Replace CreateCompatibleBitmap with CreateDIBSection when creating a bitmap

At first, we called CreateCompatibleBitmap when we created the bitmap in the code, but this interface often returns failures when there is not enough memory in the system, which is often encountered in daily testing. The error code obtained through GetLastError after a failed CreateCompatibleBitmap call is 8:

The error code is described above, which means that there is not much available memory space in the current system. When you call CreateCompatibleBitmap to create a bitmap, you need to apply for a certain amount of memory space. If there is not enough memory space, the function will return a failure.

After consulting relevant materials later, Mr. Yuan Feng mentioned in his book "Windows graphic programming" that the text map created by CreateCompatibleBitmap is a DDB bitmap, which depends on the device-related bitmap, and is allocated from the kernel address space, while the kernel memory resources are relatively limited. It is recommended to use CreateDIBSection to create bitmaps. The details in the book are as follows:

The bitmaps created by CreateDIBSection are DIB bitmaps, which are device-independent bitmaps that do not depend on the device, and are allocated from the virtual memory in the user-state address space. With few restrictions, they are generally successful. So later we encapsulated an interface to create a bitmap, as follows:

/ / create a device-independent bitmap to solve the problem of HBITMAP CreateDIBBitmap (const int nWidth, const int nHeight) {BITMAPINFO bmi;:: ZeroMemory (& bmi, sizeof (bmi)); bmi.bmiHeader.biSize = sizeof (bmi.bmiHeader); bmi.bmiHeader.biWidth = nWidth; bmi.bmiHeader.biHeight = nHeight; bmi.bmiHeader.biPlanes = 1 Bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = nWidth * nHeight * 4 incinerator 4 void* pvBits = NULL; return:: CreateDIBSection (NULL, & bmi, DIB_RGB_COLORS, & pvBits, NULL, 0);} 12, finally

This article describes some implementation ideas and details in the screen screenshot, but the details in the actual implementation are much more than those mentioned above!

Here, we provide a project-grade, high-quality and complete screenshot of C++ implementation source code download link: ScreenCatch.zip

In the source code, we encapsulate the screenshot module into a dll and provide a project TestScreenCatch that calls the API dll (both the project and the screenshot dll provide the complete source code of C++). The code to call the API for screenshot dll is as follows:

Void CTestScreenCatchDlg::OnBnClickedBtnStartCapture () {CString strPath = GetModuleFullPath (); / / the mode box of the screenshot will pop up in the interface. The interface will not return to the module box of the interface until the screenshot dialog box is closed, so that the whole thread will not be blocked. The message loop will be taken over inside the mode box and the message DoScreenCatch ((LPCTSTR) strPath) will be distributed; EmQuitType emQuitType = GetQuitType () If (emQuitType = = emLDClickQuit | | emQuitType = = emCompleteQuit) {if (IsPicFileSaved ()) {TCHAR achPciPath [Max _ PATH] = {0}; GetPicFileSavedPath (achPciPath, sizeof (achPciPath) / sizeof (TCHAR)); CString strTip StrTip.Format (_ T ("Screenshot to path:% s"), achPciPath); AfxMessageBox (strTip);}} else if (emQuitType = = emMemoryLackQuit) {AfxMessageBox (_ T ("Screenshot failed, may be caused by insufficient memory, quit some programs and try again!") );}} is it helpful for you to read the above content? If you want to know more about the relevant knowledge or read more related articles, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report