четверг, 7 февраля 2013 г.

Исходники класса по захвату экран. Часть 2

Вот обещал выложить исходники по захвату экрана, к ранее опубликованной статье, как говорится суть.

Предыдущая статья.

Для начала работы всего этого безобразия нужно импортировать Вин АПИ функции. Вот они.

#region
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

#endregion

namespace Utils.OCR
{
/// <summary>
/// Оболочка для Win32 функций
/// </summary>
public class Win32
{
public const int SRCCOPY = 0x00CC0020;
public const int R2_NOT = 6;
public const int R2_NOP = 11;
private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x0002;
private const UInt32 MOUSEEVENTF_LEFTUP = 0x0004;

[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);

[DllImport("User32.dll")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);

[DllImport("Gdi32.dll")]
public static extern int BitBlt(
IntPtr hdcDest,
int nXDest,
int nYDest,
int nWidth,
int nHeight,
IntPtr hdcSrc,
int nXSrc,
int nYSrc,
int dwRop
);

[DllImport("Gdi32.dll")]
public static extern int StretchBlt(
IntPtr hdcDest,
int nXOriginDest,
int nYOriginDest,
int nWidthDest,
int nHeightDest,
IntPtr hdcSrc,
int nXOriginSrc,
int nYOriginSrc,
int nWidthSrc,
int nHeightSrc,
int dwRop
);

[DllImport("Gdi32.dll")]
public static extern int SetROP2(IntPtr hDC, int fnDrawMode);

[DllImport("Gdi32.dll")]
private static extern int MoveToEx(IntPtr hDC, int x, int y, IntPtr lpPoint);

public static int MoveTo(IntPtr hDC, int x, int y)
{
return MoveToEx(hDC, x, y, IntPtr.Zero);
}

[DllImport("Gdi32.dll")]
public static extern int LineTo(IntPtr hDC, int nXEnd, int nYEnd);

[DllImport("Gdi32.dll")]
private static extern int GetPixel(
IntPtr hdc,
int nXPos,
int nYPos
);

public static Color GetPixelColor(IntPtr hDC, int x, int y)
{
long colorRef = GetPixel(hDC, x, y);
return Color.FromArgb((byte) colorRef, (byte) (colorRef >> 8), (byte) (colorRef >> 16));
}

[DllImport("Gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hGdiObj);

[DllImport("Gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

[DllImport("Gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight);

[DllImport("Gdi32.dll")]
public static extern int DeleteObject(IntPtr hObject);

// Get a handle to an application window.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);

// Activate an application window.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern void mouse_event(UInt32 dwFlags, UInt32 dx, UInt32 dy, UInt32 dwData, IntPtr dwExtraInfo);

public static void SendClick(System.Drawing.Point location)
{
Cursor.Position = location;
Thread.Sleep(100);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, new IntPtr());
Thread.Sleep(100);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, new IntPtr());
}

public enum Bool
{
False = 0,
True
};


[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public Int32 x;
public Int32 y;

public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }
}


[StructLayout(LayoutKind.Sequential)]
public struct Size
{
public Int32 cx;
public Int32 cy;

public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }
}


[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ARGB
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}


[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}


public const Int32 ULW_COLORKEY = 0x00000001;
public const Int32 ULW_ALPHA = 0x00000002;
public const Int32 ULW_OPAQUE = 0x00000004;

public const byte AC_SRC_OVER = 0x00;
public const byte AC_SRC_ALPHA = 0x01;


[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);



[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteDC(IntPtr hdc);


}
}



Их несколько больше чем нужно но не чего страшного, можете потом оставить только нужные вам.


Собственно исходник.


#region
using System;
using System.Drawing;
using System.Windows.Forms;
#endregion

namespace Utils.OCR
{
/// <summary>
/// Осуществляет всю работу по захвату изображения
/// </summary>
internal class ScreenCapturer
{
private readonly IntPtr hWndDesktop;
private Control control;
private int desktopBottom;
private int desktopRight;
private bool isFormDeactivated;
private bool needGrab;
private Rectangle oldRectangle;
private int zoomRate = 1;

public ScreenCapturer(Control control)
{
this.control = control;
hWndDesktop = IntPtr.Zero;
desktopRight = Screen.PrimaryScreen.Bounds.Right - 1;
desktopBottom = Screen.PrimaryScreen.Bounds.Bottom - 1;
}

public int ZoomRate
{
set { zoomRate = value; }
}

public Rectangle CaptureRectangle
{
get { return oldRectangle; }
}

public Point DesktopDimension
{
set
{
desktopRight = value.X - 1;
desktopBottom = value.Y - 1;
}
}

public void BeginCapture(Point cursorPosition)
{
needGrab = true;
Rectangle rectangle = GetZoomedRectangle(cursorPosition.X, cursorPosition.Y);
DrawRectangle(rectangle);
oldRectangle = rectangle;
}

public Rectangle StopCapture()
{
if (needGrab || isFormDeactivated)
{
if (isFormDeactivated)
isFormDeactivated = false;

DrawRectangle(oldRectangle);
}
needGrab = false;
return oldRectangle;
}

public void CopyImage(Point cursorPosition)
{
if (!needGrab)
return;

Rectangle rectangle = GetZoomedRectangle(cursorPosition.X, cursorPosition.Y);
DrawRectangle(oldRectangle);
CopyDesktopRectangle(rectangle.Left, rectangle.Top, null);
DrawRectangle(rectangle);
oldRectangle = rectangle;
}

public void PaintControl(Graphics graphics)
{
CopyDesktopRectangle(oldRectangle.Left, oldRectangle.Top, graphics);
}

//Рисует на Destkop'e прямоугольник, для выделения копируемой оьласти
private void DrawRectangle(Rectangle rectangle)
{
IntPtr hDCDesktop = Win32.GetDC(hWndDesktop);
Win32.SetROP2(hDCDesktop, Win32.R2_NOT);

Win32.MoveTo(hDCDesktop, rectangle.Left, rectangle.Top);
Win32.LineTo(hDCDesktop, rectangle.Right, rectangle.Top);
Win32.LineTo(hDCDesktop, rectangle.Right, rectangle.Bottom);
Win32.LineTo(hDCDesktop, rectangle.Left, rectangle.Bottom);
Win32.LineTo(hDCDesktop, rectangle.Left, rectangle.Top);

Win32.SetROP2(hDCDesktop, Win32.R2_NOP);
Win32.ReleaseDC(hWndDesktop, hDCDesktop);
}

/// <summary>
/// Возвращает Rectangle с координатами рисования прямоугольника копируемой области
/// </summary>
private Rectangle GetZoomedRectangle(int x, int y)
{
Point point = control.PointToScreen(new Point(x, y));
x = point.X;
y = point.Y;

int halfWidth = control.Width/zoomRate/2;
int halfHeight = control.Height/zoomRate/2;

int left = x - halfWidth;
int top = y - halfHeight;

if (left < 0)
left = 0;

if (top < 0)
top = 0;

int right = left + control.Width/zoomRate - 1;
int bottom = top + control.Height/zoomRate - 1;

if (right > desktopRight)
{
right = desktopRight;
left = right - control.Width/zoomRate + 1;
}

if (bottom > desktopBottom)
{
bottom = desktopBottom;
top = bottom - control.Height/zoomRate + 1;
}

return Rectangle.FromLTRB(left, top, right, bottom);
}

/// <summary>
/// Копирует прямоугольную область с Desktop'a в Panel
/// </summary>
/// <param name="left"> X координата левого верхнего угла Desktop'a </param>
/// <param name="top"> Y координата левого верхнего угла Desktop'a </param>
/// <param name="createDesktopDC"> Нужно ли получать HDC Desktop'a? </param>
private void CopyDesktopRectangle(int left, int top, Graphics graphics)
{
if (graphics == null)
graphics = control.CreateGraphics();

IntPtr hDCPanel = graphics.GetHdc();
IntPtr hDCDesktop = Win32.GetDC(hWndDesktop);

Win32.StretchBlt(hDCPanel, 0, 0, control.Width, control.Height,
hDCDesktop, left, top, control.Width/zoomRate, control.Height/zoomRate,
Win32.SRCCOPY);

graphics.ReleaseHdc(hDCPanel);
Win32.ReleaseDC(hWndDesktop, hDCDesktop);
}
}
}


Собственно этим кодом я не пользуюсь. Так что приведу свой способ.
Также импортируем ВИН 32 АПИ для роботы с содержимым экрана. Собственно буквально пару функций.
далее

using System;
using System.Collections.Generic;
using System.Drawing;

namespace Utils.OCR
{
// собственно код извлекающий картинку.
[Serializable]
public class SCP
{

public static SCP Recognizer= new SCP();
public static IntPtr window;
// область захвата
public Rectangle WorcRect = new Rectangle() ;
// указатель на окно которое мы захватываем
public IntPtr grappointer;

public SCP()
{
// создаем все что нужно для захвата графики согласо установленых настроек
RecrateGraphics();
}
// открытие графического контекста, для операцыии с екраном.
public void OpenContext()
{
// устанавливаем контекст для захвата всего что есть на екране включая рабочий стол и окна. Есле нужно захватить какоето
// конкретное окно подставляем его индентификатор вместо IntPtr.Zero.
Win32.SetForegroundWindow(IntPtr.Zero);
grappointer = Win32.GetDC(IntPtr.Zero);
}

// метод получение значения цвета в заданой точке.
private Color getPixelColor(int X, int Y)
{
return Win32.GetPixelColor(grappointer, X, Y);
}

//Закрыть контекст после окончания операцыи захвата
private void CloseContext()
{
Win32.ReleaseDC(IntPtr.Zero, grappointer);
}

[NonSerialized]
public Bitmap CapturetBitmap;



// собственно функцыя захвата может находится в цыкле и переодически обновлять изображение
public void UpdatesScanBitmap()
{
if(WorcRect == Rectangle.Empty || lockUpdate ) return;
OpenContext();
// перебор попиксельно области захвата
for (int i =WorcRect .Left; i < WorcRect .Right; i++)
{
for (int j = WorcRect .Top; j < WorcRect .Bottom; j++)
{
Color curent = getPixelColor(i, j);
CapturetBitmap.SetPixel(i, j, curent);
}
}
CloseContext();
}

// создание изображений
private void RecrateGraphics()
{
CapturetBitmap = new Bitmap(WorcRect .Width, WorcRect .Height);
}

// установить зону захвата, используютса абсолютные позицыи точек четырехугольника на екране.
// ето требуетса когда вы устанавливаете зону захвата с помощбю мышы.
public void MuveWorckRect(int X, int Y, int X1, int Y1)
{
WorcRect = new Rectangle(X,Y,X1-X,Y1-Y);
}
// флаг обновления содержымого
[NonSerialized]
bool lockUpdate=false;

}
}



Из за чего такой странный подход? Мне нужно было анализировать каждый пиксел, поэтому захватывать картинку и в последствии опять таки анализировать эти пиксели опять из картинки для меня не имело смысла.


Данный код работает отлично на вин ХР, на семерке и висте только в режиме совместимости(Сильно тормозит и выдает не верные цвета). В режиме совместимости нужно вроде как отключать полупрозрачные окна. Но связи с тем что использовал я данный сабж для перевода цвета в биты то для корректной работы вам может и не придётся выключать визуальное оформление. Но включать совместимость с ХР обязательно. Иначе можете забыть о захвате изображения в реальном времени, с помощью данного кода.

Комментариев нет:

Отправить комментарий