CopyFromScreen and GetPixel performance issues [duplicate] - c#

Okay, I am looking for a function or something that will read the color of a certain pixel on my monitor, and when that color is detected, another function will be enabled. I figure using RGB. All help appreciated. Thank You.

This is the most efficient: It grabs a pixel at the location of the cursor, and doesn't rely on only having one monitor.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
namespace FormTest
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public Form1()
{
InitializeComponent();
}
private void MouseMoveTimer_Tick(object sender, EventArgs e)
{
Point cursor = new Point();
GetCursorPos(ref cursor);
var c = GetColorAt(cursor);
this.BackColor = c;
if (c.R == c.G && c.G < 64 && c.B > 128)
{
MessageBox.Show("Blue");
}
}
Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
public Color GetColorAt(Point location)
{
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
}
return screenPixel.GetPixel(0, 0);
}
}
}
Now, obviously, you don't have to use the cursor's current location, but this is the general idea.
EDIT:
Given the above GetColorAt function you can poll a certain pixel on the screen in a safe, performance friendly way like this:
private void PollPixel(Point location, Color color)
{
while(true)
{
var c = GetColorAt(location);
if (c.R == color.R && c.G == color.G && c.B == color.B)
{
DoAction();
return;
}
// By calling Thread.Sleep() without a parameter, we are signaling to the
// operating system that we only want to sleep long enough for other
// applications. As soon as the other apps yield their CPU time, we will
// regain control.
Thread.Sleep()
}
}
You can wrap that in a Thread if you want, or execute it from a Console application. "Whatever suits your fancy," I guess.

Most answers here use the very same source of that pixel (desktop dc).
The key function is GetPixel.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr window);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern uint GetPixel(IntPtr dc, int x, int y);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr window, IntPtr dc);
public static Color GetColorAt(int x, int y)
{
IntPtr desk = GetDesktopWindow();
IntPtr dc = GetWindowDC(desk);
int a = (int) GetPixel(dc, x, y);
ReleaseDC(desk, dc);
return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff);
}
I think this is the cleanest and quickest way.
Note:
If you have modified the default text size among the Display Settings on Windows to increase readability on a high resolution display, the coordinate parameters of GetPixel() need to be adjusted the same way. For example, if the cursor location is (x,y) with 150% of text size on Windows 7, you need to call GetPixel(x*1.5, y*1.5) to get the color of the pixel under the cursor.

This function is shorter and can achieve the same result using System.Drawing, without Pinvoke.
Color GetColorAt(int x, int y)
{
Bitmap bmp = new Bitmap(1, 1);
Rectangle bounds = new Rectangle(x, y, 1, 1);
using (Graphics g = Graphics.FromImage(bmp))
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
return bmp.GetPixel(0, 0);
}

Please check this two different functions I have used in one of my previous projects :
1) This function takes snapshot of Desktop
private void CaptureScreenAndSave(string strSavePath)
{
//SetTitle("Capturing Screen...");
Bitmap bmpScreenshot;
Graphics gfxScreenshot;
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
MemoryStream msIn = new MemoryStream();
bmpScreenshot.Save(msIn, System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[0], null);
msIn.Close();
byte[] buf = msIn.ToArray();
MemoryStream msOut = new MemoryStream();
msOut.Write(buf, 0, buf.Length);
msOut.Position = 0;
Bitmap bmpOut = new Bitmap(msOut);
try
{
bmpOut.Save(strSavePath, System.Drawing.Imaging.ImageFormat.Bmp);
//SetTitle("Capturing Screen Image Saved...");
}
catch (Exception exp)
{
}
finally
{
msOut.Close();
}
}
2) This function takes an image in input and calculates RGB average of pixel range given.
double GetRGBAverageForPixelRange( int istartRange, int iEndRange, Bitmap oBitmap )
{
double dRetnVal = 0 ;
Color oTempColor ;
int i, j ;
for( int iCounter = istartRange ; iCounter < iEndRange ; iCounter++ )
{
i = (iCounter % (oBitmap.Width));
j = ( iCounter / ( oBitmap.Width ) ) ;
if (i >= 0 && j >= 0 && i < oBitmap.Width && j < oBitmap.Height )
{
oTempColor = oBitmap.GetPixel(i, j);
dRetnVal = dRetnVal + oTempColor.ToArgb();
}
}
return dRetnVal ;
}
This two functions together might solve your problem. Happy Coding :)
EDIT : Please note that GetPixel is very slow function. I will think twice befor using it.

As far as I know the easiest way to do this is to:
take a screenshot
look at the bitmap and get the pixel color
Edit
There is probably no way to "wait" until the pixel changes to a certain color. Your program will probably have to just loop and check it every so often until it sees the color.
For example:
while(!IsPixelColor(x, y, color))
{
//probably best to add a sleep here so your program doesn't use too much CPU
}
DoAction();
EDIT 2
Here is some sample code you can modify. This code just changes the color of a label based on the current color in a given pixel. This code avoids the handle leak mentioned.
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.Threading;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
Thread t;
int x, y;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
x = 20;
y = 50;
t = new Thread(update);
t.Start();
}
private void update()
{
Bitmap screenCopy = new Bitmap(1, 1);
using (Graphics gdest = Graphics.FromImage(screenCopy))
{
while (true)
{
//g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(256, 256));
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, x, y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
Color c = Color.FromArgb(screenCopy.GetPixel(0, 0).ToArgb());
label1.ForeColor = c;
}
}
}
}
}

This line uses About 10 ms.
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);

Related

Add a UAC shield to a button and retain its background image?

Using C# and .Net 4.0 in a winforms application: Is it possible to add the UAC shield to a button and retain the buttons background image? How?
This is what I'm using at the moment, but it removes the image...
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
public static void UACToButton(Button button, bool add)
{
const Int32 BCM_SETSHIELD = 0x160C;
if (add)
{
// The button must have the flat style
button.FlatStyle = FlatStyle.System;
if (button.Text == "")
// and it must have text to display the shield
button.Text = " ";
SendMessage(button.Handle, BCM_SETSHIELD, 0, 1);
}
else
SendMessage(button.Handle, BCM_SETSHIELD, 0, 0);
}
Thanks!
As Elmue mentioned, SystemIcons.Shield is the easiest way to access the UAC Shield icon. Here's the method that I use to add it on top of other images:
public static Image AddUACShieldToImage(Image image)
{
var shield = SystemIcons.Shield.ToBitmap();
shield.MakeTransparent();
var g = Graphics.FromImage(image);
g.CompositingMode = CompositingMode.SourceOver;
g.DrawImage(shield, new Rectangle(image.Width / 2, image.Height / 2, image.Width / 2, image.Height / 2));
return image;
}
Perhaps this will help: http://blog.csharphelper.com/2011/03/03/add-uac-shields-to-buttons-menu-items-and-picture-boxes-in-c.aspx
It takes the UAC shield and returns a bitmap that can be placed on anything that supports bitmaps.
EDIT:
Here's some rough sample code that could work:
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;
namespace UAC_Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
// Make the button display the UAC shield.
public static void AddShieldToButton(Button btn)
{
const Int32 BCM_SETSHIELD = 0x160C;
// Give the button the flat style and make it display the UAC shield.
btn.FlatStyle = System.Windows.Forms.FlatStyle.System;
SendMessage(btn.Handle, BCM_SETSHIELD, 0, 1);
}
// Return a bitmap containing the UAC shield.
private static Bitmap shield_bm = null;
public static Bitmap GetUacShieldImage()
{
if (shield_bm != null) return shield_bm;
const int WID = 50;
const int HGT = 50;
const int MARGIN = 4;
// Make the button. For some reason, it must
// have text or the UAC shield won't appear.
Button btn = new Button();
btn.Text = " ";
btn.Size = new Size(WID, HGT);
AddShieldToButton(btn);
// Draw the button onto a bitmap.
Bitmap bm = new Bitmap(WID, HGT);
btn.Refresh();
btn.DrawToBitmap(bm, new Rectangle(0, 0, WID, HGT));
// Find the part containing the shield.
int min_x = WID, max_x = 0, min_y = HGT, max_y = 0;
// Fill on the left.
for (int y = MARGIN; y < HGT - MARGIN; y++)
{
// Get the leftmost pixel's color.
Color target_color = bm.GetPixel(MARGIN, y);
// Fill in with this color as long as we see the target.
for (int x = MARGIN; x < WID - MARGIN; x++)
{
// See if this pixel is part of the shield.
if (bm.GetPixel(x, y).Equals(target_color))
{
// It's not part of the shield.
// Clear the pixel.
bm.SetPixel(x, y, Color.Transparent);
}
else
{
// It's part of the shield.
if (min_y > y) min_y = y;
if (min_x > x) min_x = x;
if (max_y < y) max_y = y;
if (max_x < x) max_x = x;
}
}
}
// Clip out the shield part.
int shield_wid = max_x - min_x + 1;
int shield_hgt = max_y - min_y + 1;
shield_bm = new Bitmap(shield_wid, shield_hgt);
Graphics shield_gr = Graphics.FromImage(shield_bm);
shield_gr.DrawImage(bm, 0, 0,
new Rectangle(min_x, min_y, shield_wid, shield_hgt),
GraphicsUnit.Pixel);
// Return the shield.
return shield_bm;
}
private void Form1_Load(object sender, EventArgs e)
{
Graphics gfx = Graphics.FromImage(button1.BackgroundImage);
Bitmap shield = GetUacShieldImage();
gfx.DrawImage(shield, button1.Width / 2, button1.Height / 2);
}
}
}
This is drawing the UAC shield over the background image. You can adjust the image location on top of the button. It doesn't look as pretty as the System UAC shield, but it does show the UAC shield icon on top of your background image.

Get image as Image Class with twain in c#

I can connect and get images from my device with twaindotnet. But I want to handle the images as Image class. When I try something like this:
...
ArrayList pics = tw.TransferPictures();
EndingScan();
tw.CloseSrc();
if(pics.Count > 0) {
IntPtr img = (IntPtr) pics[ 0 ];
PicForm newpic = new PicForm( img );
Image r = Image.FromHbitmap(img, this.Handle);
picturebox.Image = r;
}
...
I'm getting an error as "Error:Generic Error Occured in GDI+" on the line ,
Image r = Image.FromHbitmap(img, this.Handle);
So where am I wrong? How can I get as an Image the image?
I too found out that calling Image.FromHbitmap is not enough.
I had a look in the TwainDotNet library you mentioned and there I found the BitmapRenderer class.
Pulling out just the relevant bit it is easy enough to use that to create a simple static method that you can pass in the IntPtr you get from TWAIN (in your case your img variable) and convert it to a Bitmap. You, therefore, call it like so:
Image r = TwainBitmapConvertor.ToBitmap(img);
and here is the code (it only works on x86 and needs tidying but it does the job):
public static class TwainBitmapConvertor
{
[StructLayout(LayoutKind.Sequential, Pack = 2)]
private class BitmapInfoHeader
{
public int Size;
public int Width;
public int Height;
public short Planes;
public short BitCount;
public int Compression;
public int SizeImage;
public int XPelsPerMeter;
public int YPelsPerMeter;
public int ClrUsed;
public int ClrImportant;
}
[DllImport("gdi32.dll", ExactSpelling = true)]
private static extern int SetDIBitsToDevice(IntPtr hdc,
int xdst, int ydst, int width, int height, int xsrc,
int ysrc, int start, int lines, IntPtr bitsptr,
IntPtr bmiptr, int color);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalLock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern bool GlobalUnlock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalFree(IntPtr handle);
public static Bitmap ToBitmap(IntPtr dibHandle)
{
var bitmapPointer = GlobalLock(dibHandle);
var bitmapInfo = new BitmapInfoHeader();
Marshal.PtrToStructure(bitmapPointer, bitmapInfo);
var rectangle = new Rectangle();
rectangle.X = rectangle.Y = 0;
rectangle.Width = bitmapInfo.Width;
rectangle.Height = bitmapInfo.Height;
if (bitmapInfo.SizeImage == 0)
{
bitmapInfo.SizeImage =
((((bitmapInfo.Width * bitmapInfo.BitCount) + 31) & ~31) >> 3)
* bitmapInfo.Height;
}
// The following code only works on x86
Debug.Assert(Marshal.SizeOf(typeof(IntPtr)) == 4);
int pixelInfoPointer = bitmapInfo.ClrUsed;
if ((pixelInfoPointer == 0) && (bitmapInfo.BitCount <= 8))
{
pixelInfoPointer = 1 << bitmapInfo.BitCount;
}
pixelInfoPointer = (pixelInfoPointer * 4) + bitmapInfo.Size
+ bitmapPointer.ToInt32();
IntPtr pixelInfoIntPointer = new IntPtr(pixelInfoPointer);
Bitmap bitmap = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
IntPtr hdc = graphics.GetHdc();
try
{
SetDIBitsToDevice(hdc,
0, 0, rectangle.Width, rectangle.Height, 0, 0, 0,
rectangle.Height, pixelInfoIntPointer, bitmapPointer, 0);
}
finally
{
graphics.ReleaseHdc(hdc);
}
}
bitmap.SetResolution(PpmToDpi(bitmapInfo.XPelsPerMeter),
PpmToDpi(bitmapInfo.YPelsPerMeter));
GlobalUnlock(dibHandle);
GlobalFree(dibHandle);
return bitmap;
}
private static float PpmToDpi(double pixelsPerMeter)
{
double pixelsPerMillimeter = (double)pixelsPerMeter / 1000.0;
double dotsPerInch = pixelsPerMillimeter * 25.4;
return (float)Math.Round(dotsPerInch, 2);
}
}
Assuming that tw.TransferPictures() returns an array of bitmap handles, then change Image r = ... to:
Image r = Image.FromHbitmap(img);
The second argument to FromHbitmap is a handle to a GDI palette, which I doubt you have.

How to get the screenshot of the form

Any method to output the screenshot of an active form?
Even simpler answer supported by .NET:
Control.DrawToBitmap.
Use the Control.DrawToBitmap() method. For example:
private void timer1_Tick(object sender, EventArgs e) {
var frm = Form.ActiveForm;
using (var bmp = new Bitmap(frm.Width, frm.Height)) {
frm.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
bmp.Save(#"c:\temp\screenshot.png");
}
}
Try this:
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;
namespace ScreenshotCapturer
{
public partial class Form1 : Form
{
private static Bitmap bmpScreenshot;
private static Graphics gfxScreenshot;
public Form1()
{
InitializeComponent();
}
private void btnCapture_Click(object sender, EventArgs e)
{
// If the user has choosed a path where to save the screenshot
if (saveScreenshot.ShowDialog() == DialogResult.OK)
{
// Hide the form so that it does not appear in the screenshot
this.Hide();
// Set the bitmap object to the size of the screen
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
// Create a graphics object from the bitmap
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
// Take the screenshot from the upper left corner to the right bottom corner
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
// Save the screenshot to the specified path that the user has chosen
bmpScreenshot.Save(saveScreenshot.FileName, ImageFormat.Png);
// Show the form again
this.Show();
}
}
}
}
Here's an extension method you can use:
#region Interop
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr hdc, PRF_FLAGS drawingOptions);
const uint WM_PRINT = 0x317;
[Flags]
enum PRF_FLAGS : uint
{
CHECKVISIBLE = 0x01,
CHILDREN = 0x02,
CLIENT = 0x04,
ERASEBKGND = 0x08,
NONCLIENT = 0x10,
OWNED = 0x20
}
#endregion
public static Image CaptureImage(this Control control)
{
Image img = new Bitmap(control.Width, control.Height);
using (Graphics g = Graphics.FromImage(img))
{
SendMessage(
control.Handle,
WM_PRINT,
g.GetHdc(),
PRF_FLAGS.CLIENT | PRF_FLAGS.NONCLIENT | PRF_FLAGS.ERASEBKGND);
}
return img;
}
Form inherits from Control, so you can use it on a form too.
Because Control.DrawToBitmap has some limitations (most notably drawing child controls in reverse order) I have made this alternative. The variable _control is the WinForms Control/Form that you want to copy.
using (Bitmap bitmap = new Bitmap(width, height)) {
using (Graphics gb = Graphics.FromImage(bitmap))
using (Graphics gc = Graphics.FromHwnd(_control.Handle)) {
IntPtr hdcDest = IntPtr.Zero;
IntPtr hdcSrc = IntPtr.Zero;
try {
hdcDest = gb.GetHdc();
hdcSrc = gc.GetHdc();
BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, SRC_COPY);
} finally {
if (hdcDest != IntPtr.Zero) gb.ReleaseHdc(hdcDest);
if (hdcSrc != IntPtr.Zero) gc.ReleaseHdc(hdcSrc);
}
}
bitmap.Save(...);
}
[DllImport("gdi32.dll", EntryPoint = "BitBlt")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(
[In()] System.IntPtr hdc, int x, int y, int cx, int cy,
[In()] System.IntPtr hdcSrc, int x1, int y1, uint rop);
private const int SRC_COPY = 0xCC0020;
this.Opacity = 0;
Rectangle bounds = Screen.GetBounds(Point.Empty);
// create the bitmap to copy the screen shot to
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
// now copy the screen image to the graphics device from the bitmap
using (Graphics gr = Graphics.FromImage(bitmap))
{
gr.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
}
this.Opacity = 100;
Now just
gr.Save(#"c\pic.jpg",ImageFormat.jpg);
This will save a screenshot of your active window to your C: drive.
The SendKeys.Send("") is used to send keypresses to the computer. In this case % is used to press Alt and {PRTSC} obviously to press the Print Screen button on your keyboard.
Hope this helps someone!
private void printScreenButton_Click(object sender, EventArgs e)
{
SendKeys.Send("%{PRTSC}");
Image img = Clipboard.GetImage();
img.Save(#"C:\\testprintscreen.jpg");
}
here is:
public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter
[DllImport("gdi32.dll")]
public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest,
int nWidth, int nHeight, IntPtr hObjectSource,
int nXSrc, int nYSrc, int dwRop);
[DllImport("gdi32.dll")]
public static extern bool StretchBlt(
IntPtr hdcDest, // handle to destination DC
int nXOriginDest, // x-coord of destination upper-left corner
int nYOriginDest, // y-coord of destination upper-left corner
int nWidthDest, // width of destination rectangle
int nHeightDest, // height of destination rectangle
IntPtr hdcSrc, // handle to source DC
int nXOriginSrc, // x-coord of source upper-left corner
int nYOriginSrc, // y-coord of source upper-left corner
int nWidthSrc, // width of source rectangle
int nHeightSrc, // height of source rectangle
int dwRop // raster operation code
);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth,
int nHeight);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
private void run_test()
{
Rectangle rc = new Rectangle();
Image img = ScreenToImage(ref rc, false);
img.Save(#"C:\Users\ssaamm\Desktop\Capt44ure.JPG", System.Drawing.Imaging.ImageFormat.Jpeg);
img.Dispose();
}
private Image ScreenToImage(ref Rectangle rcDest, bool IsPapaer)
{
IntPtr handle = this.Handle;
//this.Handle
// get te hDC of the target window
IntPtr hdcSrc = GetWindowDC(handle);
// get the size
RECT windowRect = new RECT();
GetWindowRect(handle, ref windowRect);
int nWidth = windowRect.right - windowRect.left;
int nHeight = windowRect.bottom - windowRect.top;
if (IsPapaer)
{
float fRate = (float)rcDest.Width / nWidth;
//float fHeight = nHeight * fRate;
//rcDest.Height = (int)(nHeight * fRate);
//rcDest.Width = (int)(rcDest.Width);// * fRate);
rcDest.X = 0;
rcDest.Y = 0;
rcDest.Height = (int)(nHeight * fRate);
//rcDest.Width = (int)(nWidth * fRate);
}
else
{
rcDest.X = 0;
rcDest.Y = 0;
rcDest.Height = nHeight;
rcDest.Width = nWidth;
}
// create a device context we can copy to
IntPtr hdcDest = CreateCompatibleDC(hdcSrc);
// create a bitmap we can copy it to,
// using GetDeviceCaps to get the width/height
IntPtr hBitmap = CreateCompatibleBitmap(hdcSrc, rcDest.Width, rcDest.Height);
// select the bitmap object
IntPtr hOld = SelectObject(hdcDest, hBitmap);
// bitblt over
StretchBlt(hdcDest, rcDest.X, rcDest.Y, rcDest.Width, rcDest.Height, hdcSrc, 0, 0, nWidth, nHeight, SRCCOPY);
// restore selection
SelectObject(hdcDest, hOld);
// clean up
DeleteDC(hdcDest);
ReleaseDC(handle, hdcSrc);
// get a .NET image object for it
Image img = Image.FromHbitmap(hBitmap);
// free up the Bitmap object
DeleteObject(hBitmap);
return img;
}

how to draw constantly changing graphics

Have not done this before (except in java, look how Steve McLeod fixed it), so obviously I suck at it. Here 64 pixels around current mouse position get drawn little bigger on a form. Problem is, that it's 'kind of' to slow, and I have no idea where to start fixing.
Besides that, I made a timer thread, that constantly calls update graphics when it's finished and a little fps like text, to show really how fast things are drawn.
Image example: (Image is from letter 'a' in "IntelliTrace" in Microsoft VS2010)
Source example:
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;
namespace Zoom
{
public partial class Form1 : Form
{
static class dllRef
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out Point lpPoint);
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("user32.dll")]
static extern Int32 ReleaseDC(IntPtr hwnd, IntPtr hdc);
[DllImport("gdi32.dll")]
static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);
// from http://www.pinvoke.net/default.aspx/gdi32/GetPixel.html
static public System.Drawing.Color getPixelColor(int x, int y) {
IntPtr hdc = GetDC(IntPtr.Zero);
uint pixel = GetPixel(hdc, x, y);
ReleaseDC(IntPtr.Zero, hdc);
Color color = Color.FromArgb((int)(pixel & 0x000000FF),
(int)(pixel & 0x0000FF00) >> 8,
(int)(pixel & 0x00FF0000) >> 16);
return color;
}
static public System.Drawing.Point getMousePosition() {
Point p = new Point();
GetCursorPos(out p);
return p;
}
}
public Form1() {
InitializeComponent();
this.Size = new Size(400,400);
this.Text="Image zoom";
this.Location = new Point(640, 0);
this.image = new Bitmap(320, 320);
this.timeRef = DateTime.Now;
this.BackColor = Color.White;
Timer t = new Timer();
t.Interval = 25;
t.Tick += new EventHandler(Timer_Tick);
t.Start();
}
public void Timer_Tick(object sender, EventArgs eArgs) {
this.Form1_Paint(this, new PaintEventArgs(this.CreateGraphics(), new Rectangle(0, 0, this.Width, this.Height)));
}
private bool isdone = true;
private int iter = 0;
private Bitmap image;
private DateTime timeRef;
private void Form1_Paint(object sender, PaintEventArgs e) {
if (isdone) {
isdone = false;
int step = 40;
Point p = dllRef.getMousePosition();
Pen myPen = new Pen(Color.Gray, 1);
SolidBrush myBrush = null;
Bitmap image2 = new Bitmap(320, 340);
Graphics gc = Graphics.FromImage(image2);
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
myBrush = new SolidBrush(dllRef.getPixelColor(p.X - 4 + x, p.Y - 4 + y));
gc.FillEllipse(myBrush, x * step, y * step, step - 3, step - 3);
gc.DrawEllipse(myPen, x * step, y * step, step - 3, step - 3);
}
}
StringBuilder sb = new StringBuilder();
sb.Append(iter)
.Append(" frames in ")
.Append(String.Format("{0:0.###}", ((DateTime.Now-this.timeRef).TotalMilliseconds)/1000))
.Append("s.");
gc.FillRectangle(new SolidBrush(this.BackColor), new Rectangle( 0, 320, 320, 40));
gc.DrawString(sb.ToString(),new Font("Arial", 12),new SolidBrush(Color.Black), 10, 320);
gc.Dispose();
isdone = true;
iter++;
image = image2;
}
e.Graphics.DrawImage(image, 35f, 15f);
}
}
}
After changes i made, this one is ~98% faster:
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;
namespace Zoom
{
public partial class Form1 : Form
{
static class dllRef
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out Point lpPoint);
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("user32.dll")]
static extern Int32 ReleaseDC(IntPtr hwnd, IntPtr hdc);
[DllImport("gdi32.dll")]
static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);
// from http://www.pinvoke.net/default.aspx/gdi32/GetPixel.html
static public System.Drawing.Color getPixelColor(int x, int y) {
IntPtr hdc = GetDC(IntPtr.Zero);
uint pixel = GetPixel(hdc, x, y);
ReleaseDC(IntPtr.Zero, hdc);
Color color = Color.FromArgb((int)(pixel & 0x000000FF),
(int)(pixel & 0x0000FF00) >> 8,
(int)(pixel & 0x00FF0000) >> 16);
return color;
}
static public System.Drawing.Point getMousePosition() {
Point p = new Point();
GetCursorPos(out p);
return p;
}
}
public Form1() {
InitializeComponent();
this.Size = new Size(400,400);
this.Text="Image zoom";
this.Location = new Point(640, 0);
this.image = new Bitmap(320, 340);
this.timeRef = DateTime.Now;
this.BackColor = Color.White;
Timer t = new Timer();
t.Interval = 25;
t.Tick += new EventHandler(Timer_Tick);
t.Start();
}
public void Timer_Tick(object sender, EventArgs eArgs) {
this.Form1_Paint(this, new PaintEventArgs(this.CreateGraphics(), new Rectangle(0, 0, this.Width, this.Height)));
}
private bool isdone = true;
private int iter = 0;
private Bitmap image;
private DateTime timeRef;
private void Form1_Paint(object sender, PaintEventArgs e) {
if (isdone) {
isdone = false;
int step = 40;
Point p = dllRef.getMousePosition();
SolidBrush myBrush = null;
Bitmap hc = new Bitmap(8, 8);
using (Pen myPen = new Pen(Color.Gray, 1))
using (Graphics gc = Graphics.FromImage(image))
using (Graphics gf = Graphics.FromImage(hc))
{
gf.CopyFromScreen(p.X - 4, p.Y - 4, 0, 0, new Size(8, 8),
CopyPixelOperation.SourceCopy);
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
myBrush = new SolidBrush(hc.GetPixel(x, y));
gc.FillEllipse(myBrush, x * step, y * step, step - 3, step - 3);
gc.DrawEllipse(myPen, x * step, y * step, step - 3, step - 3);
}
}
double ts = ((DateTime.Now - this.timeRef).TotalMilliseconds) / 1000;
StringBuilder sb = new StringBuilder();
sb.Append(++iter).Append(" frames in ").Append(String.Format("{0:0.###}", ts)).Append("s.");
gc.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 320, 320, 40));
gc.DrawString(sb.ToString(), new Font("Arial", 12), new SolidBrush(Color.Black), 10, 320);
}
isdone = true;
}
e.Graphics.DrawImage(image, 35f, 15f);
}
}
}
One thing that should speed things up is if you do the GetDC just once and get all of the pixels you need, then call ReleaseDC. So rather than:
for each pixel
GetDC
Read Pixel
ReleaseDC
You have:
GetDC
for each pixel
read pixel and store value
ReleaseDC
Then process the stored pixels.
That said, you're probably better off not using GetPixel at all, as I seem to remember it being terribly inefficient. I suspect you'd have better performance just grabbing the entire screen into a bitmap and getting the pixels from there. Perhaps the answer to this question will help you: Capture the Screen into a Bitmap

How to read the Color of a Screen Pixel

Okay, I am looking for a function or something that will read the color of a certain pixel on my monitor, and when that color is detected, another function will be enabled. I figure using RGB. All help appreciated. Thank You.
This is the most efficient: It grabs a pixel at the location of the cursor, and doesn't rely on only having one monitor.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
namespace FormTest
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public Form1()
{
InitializeComponent();
}
private void MouseMoveTimer_Tick(object sender, EventArgs e)
{
Point cursor = new Point();
GetCursorPos(ref cursor);
var c = GetColorAt(cursor);
this.BackColor = c;
if (c.R == c.G && c.G < 64 && c.B > 128)
{
MessageBox.Show("Blue");
}
}
Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
public Color GetColorAt(Point location)
{
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
}
return screenPixel.GetPixel(0, 0);
}
}
}
Now, obviously, you don't have to use the cursor's current location, but this is the general idea.
EDIT:
Given the above GetColorAt function you can poll a certain pixel on the screen in a safe, performance friendly way like this:
private void PollPixel(Point location, Color color)
{
while(true)
{
var c = GetColorAt(location);
if (c.R == color.R && c.G == color.G && c.B == color.B)
{
DoAction();
return;
}
// By calling Thread.Sleep() without a parameter, we are signaling to the
// operating system that we only want to sleep long enough for other
// applications. As soon as the other apps yield their CPU time, we will
// regain control.
Thread.Sleep()
}
}
You can wrap that in a Thread if you want, or execute it from a Console application. "Whatever suits your fancy," I guess.
Most answers here use the very same source of that pixel (desktop dc).
The key function is GetPixel.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr window);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern uint GetPixel(IntPtr dc, int x, int y);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr window, IntPtr dc);
public static Color GetColorAt(int x, int y)
{
IntPtr desk = GetDesktopWindow();
IntPtr dc = GetWindowDC(desk);
int a = (int) GetPixel(dc, x, y);
ReleaseDC(desk, dc);
return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff);
}
I think this is the cleanest and quickest way.
Note:
If you have modified the default text size among the Display Settings on Windows to increase readability on a high resolution display, the coordinate parameters of GetPixel() need to be adjusted the same way. For example, if the cursor location is (x,y) with 150% of text size on Windows 7, you need to call GetPixel(x*1.5, y*1.5) to get the color of the pixel under the cursor.
This function is shorter and can achieve the same result using System.Drawing, without Pinvoke.
Color GetColorAt(int x, int y)
{
Bitmap bmp = new Bitmap(1, 1);
Rectangle bounds = new Rectangle(x, y, 1, 1);
using (Graphics g = Graphics.FromImage(bmp))
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
return bmp.GetPixel(0, 0);
}
Please check this two different functions I have used in one of my previous projects :
1) This function takes snapshot of Desktop
private void CaptureScreenAndSave(string strSavePath)
{
//SetTitle("Capturing Screen...");
Bitmap bmpScreenshot;
Graphics gfxScreenshot;
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
MemoryStream msIn = new MemoryStream();
bmpScreenshot.Save(msIn, System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[0], null);
msIn.Close();
byte[] buf = msIn.ToArray();
MemoryStream msOut = new MemoryStream();
msOut.Write(buf, 0, buf.Length);
msOut.Position = 0;
Bitmap bmpOut = new Bitmap(msOut);
try
{
bmpOut.Save(strSavePath, System.Drawing.Imaging.ImageFormat.Bmp);
//SetTitle("Capturing Screen Image Saved...");
}
catch (Exception exp)
{
}
finally
{
msOut.Close();
}
}
2) This function takes an image in input and calculates RGB average of pixel range given.
double GetRGBAverageForPixelRange( int istartRange, int iEndRange, Bitmap oBitmap )
{
double dRetnVal = 0 ;
Color oTempColor ;
int i, j ;
for( int iCounter = istartRange ; iCounter < iEndRange ; iCounter++ )
{
i = (iCounter % (oBitmap.Width));
j = ( iCounter / ( oBitmap.Width ) ) ;
if (i >= 0 && j >= 0 && i < oBitmap.Width && j < oBitmap.Height )
{
oTempColor = oBitmap.GetPixel(i, j);
dRetnVal = dRetnVal + oTempColor.ToArgb();
}
}
return dRetnVal ;
}
This two functions together might solve your problem. Happy Coding :)
EDIT : Please note that GetPixel is very slow function. I will think twice befor using it.
As far as I know the easiest way to do this is to:
take a screenshot
look at the bitmap and get the pixel color
Edit
There is probably no way to "wait" until the pixel changes to a certain color. Your program will probably have to just loop and check it every so often until it sees the color.
For example:
while(!IsPixelColor(x, y, color))
{
//probably best to add a sleep here so your program doesn't use too much CPU
}
DoAction();
EDIT 2
Here is some sample code you can modify. This code just changes the color of a label based on the current color in a given pixel. This code avoids the handle leak mentioned.
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.Threading;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
Thread t;
int x, y;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
x = 20;
y = 50;
t = new Thread(update);
t.Start();
}
private void update()
{
Bitmap screenCopy = new Bitmap(1, 1);
using (Graphics gdest = Graphics.FromImage(screenCopy))
{
while (true)
{
//g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(256, 256));
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, x, y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
Color c = Color.FromArgb(screenCopy.GetPixel(0, 0).ToArgb());
label1.ForeColor = c;
}
}
}
}
}
This line uses About 10 ms.
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);

Categories