Using Directshow.NET I have developed an application which will record the desktop screen, to get mouse pointer we need to paint mouse pointer by our own. So I added SampleGrabber adn in BufferCB I have written below code:
public const Int32 CURSOR_SHOWING = 0x00000001;
[StructLayout(LayoutKind.Sequential)]
public struct ICONINFO
{
public bool fIcon;
public Int32 xHotspot;
public Int32 yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public Int32 x;
public Int32 y;
}
[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public POINT ptScreenPos;
}
[DllImport("user32.dll")]
public static extern bool GetCursorInfo(out CURSORINFO pci);
[DllImport("user32.dll")]
public static extern IntPtr CopyIcon(IntPtr hIcon);
[DllImport("user32.dll")]
public static extern bool DrawIcon(IntPtr hdc, int x, int y, IntPtr hIcon);
[DllImport("user32.dll")]
public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
Graphics g;
Bitmap v;
v = new Bitmap(m_videoWidth, m_videoHeight, m_stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, pBuffer);
g = Graphics.FromImage(v);
CURSORINFO cursorInfo;
cursorInfo.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out cursorInfo))
{
if (cursorInfo.flags == CURSOR_SHOWING)
{
var iconPointer = CopyIcon(cursorInfo.hCursor);
ICONINFO iconInfo;
int iconX, iconY;
if (GetIconInfo(iconPointer, out iconInfo))
{
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
DrawIcon(g.GetHdc(), iconX, iconY, cursorInfo.hCursor);
g.ReleaseHdc();
g.Dispose();
v.Dispose();
}
}
}
return 0;
}
This code is painting the mouse cursor but cursor is flipped on Y axis.
It's may be because in BufferCB if we convert pBuffer in Bitmap then that frame is flipped on Y axis. To solve this I flipped current frame on Y axis by adding v.RotateFlip(RotateFlipType.RotateNoneFlipY); inside BufferCB after this change mouse pointer is not visible in desktop recording video.
How I can flip mouse pointer?
Update #1
I converted icon pointer to Icon then into Bitmap using Icon.ToBitmap() and then flipped on Y axis, here is code (Thanks to #Roman R.):
...
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
Icon ic = Icon.FromHandle(iconPointer);
Bitmap icon = ic.ToBitmap();
icon.RotateFlip(RotateFlipType.RotateNoneFlipY);
g.DrawImage(icon, iconX, iconY);
g.Dispose();
v.Dispose();
icon.Dispose();
ic.Dispose();
...
Only one issue I am facing in above modification sometimes I get ArgumentException at line Bitmap icon = ic.ToBitmap();
System.ArgumentException occurred
HResult=-2147024809
Message=Parameter is not valid.
Source=System.Drawing
StackTrace:
at System.Drawing.Bitmap.FromHicon(IntPtr hicon)
InnerException:
I disposed all the bitmaps used, still i get this exception.
The problem you have here is your BufferCB implementation. You create a temporary Graphics/Bitmap object, so that it could be updated with the cursor overlay. You have this bitmap upside down and normal draw of the icon applies it flipped.
You need to take into account that normal order of lines of 24-bit RGB buffer you get with BufferCB callback is bottom-to-top; order of lines Bitmap constructor expects is the opposite. You need to transfer lines into Bitmap in reversed order, then get them back respectively. I am not sure if negative stride works out there well, presumably it will not. Or simply overlay the cursor pre-flipped, to compensate for flipped background and overlay coordinates too.
You have another option of painting correct mouse pointer. Just take a mouse pointer's PNG image and in BufferCB:
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
Graphics g;
Bitmap v;
v = new Bitmap(m_videoWidth, m_videoHeight, m_stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, pBuffer);
g = Graphics.FromImage(v);
CURSORINFO cursorInfo;
cursorInfo.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out cursorInfo))
{
if (cursorInfo.flags == CURSOR_SHOWING)
{
var iconPointer = CopyIcon(cursorInfo.hCursor);
ICONINFO iconInfo;
int iconX, iconY;
if (GetIconInfo(iconPointer, out iconInfo))
{
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
//DRAW STATIC POINTER IMAGE
Bitmap pointerImage = new Bitmap('pointer.png');
g.DrawImage(pointerImage,iconX,iconY);
g.Dispose();
v.Dispose();
}
}
}
return 0;
}
Related
So right now I am taking a screenshot of an application by doing this:
using (var bitmap = new Bitmap(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height))
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(new Point(Screen.PrimaryScreen.WorkingArea.Left, Screen.PrimaryScreen.WorkingArea.Top), new Point(0, 0), Screen.PrimaryScreen.WorkingArea.Size);
bitmap.Save(#"C:\Users\Neoray\Desktop\Test.png", ImageFormat.Bmp);
}
This works fine enough but the problem is that I need to get rid of the Title Bar that is in there (doesn't matter if its afterwards by altering the image or when taking the screenshot itself).
Hope someone can help me with this.
As noted in the comments, this method will return the ClientArea of a Process Window, excluding the Caption (Title Bar) only when the Window actually has a Caption. Not all applications do (in WinForms sense).
Also, no guarantee whatsoever that it can work with a maximized Window which is rendered using DirectX APIs.
In this case, it would be necessary to implement a completely different method.
Anyway, it should work correctly with the sample image you're showing.
The overloaded main method accepts both a Process Name and Process ID.
Parameters:
string processName or int processId:
Specify the process friendly Name (the one shown in Task Manager, it's not case sensitive) or the Process Id (also available in Task Manager).
It can be easily modified to also accept the Window Title.
bool clientAreaOnly:
Switch. Lets you specify whether you want a screenshot of the ClientArea section only or the whole Windows bounds.
float dpi:
You can define the Dpi resolution of the final Bitmap.
Sample usage:
Takes a screenshot of the Main Window of a process with Name [Some Process Name]
Instructs to only consider the ClientArea (the Window Caption and Borders will be excluded)
Specifies that the Bitmap resolution should be 120Dpi
Saves a Bitmap in PNG format, named ScreenShot.png, in [Some Path]
using (var bitmap = ProcessWindowScreenshot("[Some Process Name]", true, 120)) {
bitmap.Save(#"[Some Path]\ScreenShot.png", ImageFormat.Png);
}
Sample procedure:
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.InteropServices;
public Bitmap ProcessWindowScreenshot(string processName, bool clientAreaOnly, float dpi) {
using (var process = Process.GetProcessesByName(processName)
.FirstOrDefault(p => p.MainWindowHandle != IntPtr.Zero)) {
if (process == null) return null;
return GetProcessWindowScreenshot(GetProcessWindowRect(process.Id, clientAreaOnly), dpi);
}
}
public Bitmap ProcessWindowScreenshot(int processId, bool clientAreaOnly, float dpi) {
return GetProcessWindowScreenshot(GetProcessWindowRect(processId, clientAreaOnly), dpi);
}
private Bitmap GetProcessWindowScreenshot(Rectangle window, float dpi)
{
var bitmap = new Bitmap(window.Width, window.Height, PixelFormat.Format32bppArgb);
bitmap.SetResolution(dpi, dpi);
using (var g = Graphics.FromImage(bitmap))
{
g.InterpolationMode = InterpolationMode.High;
g.CopyFromScreen(window.Location, Point.Empty, window.Size, CopyPixelOperation.SourceCopy);
return bitmap;
};
}
private Rectangle GetProcessWindowRect(int processId, bool isClientArea)
{
IntPtr hWnd = Process.GetProcessById(processId).MainWindowHandle;
if (isClientArea) {
return GetWindowClientRectangle(hWnd);
}
else {
return GetWindowRectangle(hWnd);
}
}
Windows API declarations and helpers methods:
[UIPermission(SecurityAction.Demand, Action = SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)]
public static Rectangle GetWindowRectangle(IntPtr handle) {
NativeMethods.GetWindowRect(handle, out RECT rect);
return rect.ToRectangle();
}
[UIPermission(SecurityAction.Demand, Action = SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)]
public static Rectangle GetWindowClientRectangle(IntPtr handle) {
NativeMethods.GetClientRect(handle, out RECT rect);
NativeMethods.ClientToScreen(handle, out POINT point);
return rect.ToRectangleOffset(point);
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
public Rectangle ToRectangleOffset(POINT p) => Rectangle.FromLTRB(p.x, p.y, Right + p.x, Bottom + p.y);
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
internal static class NativeMethods
{
[DllImport("User32.dll", SetLastError = true)]
internal static extern bool ClientToScreen(IntPtr hWnd, out POINT point);
[DllImport("User32.dll", SetLastError = true)]
internal static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("User32.dll", SetLastError = true)]
internal static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
}
I'm trying to build a map editor like this one: http://blog.rpgmakerweb.com/wp-content/uploads/2012/04/T2EditorLabelled.png in WPF.
How should it work:
By selecting a specific tile on the "tile list" - B section - is possible to draw that tile on the canvas - section A.
In the end the final result is a full game level drawn on the canvas.
First approach:
In my first approach each tile is drawn by creating a new image control and adding it to the canvas (a WPF canvas control).
Steps:
Select the tile from the tileset
Catch the click event on the canvas
Create a new image, cropping the tile from the tileset
Adding the image in the right spot on the canvas (as a child)
This method is quite naive and it imply two big issues:
All the pixels are already buffered on the tileset that contains all the tiles, but each time I draw a tile on the canvas a new image is create and by doing so I am obliged to replicate part of the tileset pixel data as a source for the new
Too many controls on the canvas:
A map game can reach the size of 1000 tiles x 1000 tiles, WPF start to have sensible decay of performance on a 100x100 tile map.
So create a control image for each tile is an unfeasible solution.
Second approach:
My second approach contemplate the use of a single big WriteableBitmap as canvas background.
As in the previous approach the tile is selected on the tileset and the draw event is a click on the canvas.
In this case though no new image is created ex-novo but the background WriteableBitmap is modified accordingly.
So in the number of controls is reduced drastically since all the draw mechanism is performed on the WriteableBitmap.
The main problem with this approach is that if I want to create a large map with 1k x 1k tiles with a tile of 32x32, the size of the background image would be astronomical.
I wonder if there is a way to develop a good solution of this problem in WPF.
How would you address this development problem?
There a variety of different ways you could approach this problem to increase performance.
In terms of the rendering of images, WPF by default isn't amazing, so you could;
Use GDI's BitBlt to quickly render images to a WinForms control which can be hosted. The benefit of this is that GDI is software and hence does not require a graphics card or anything. (WPF fast method to draw image in UI)
You can use D3DImage as an image source. This would mean that you can use the D3DImage as the canvas to which to draw. Doing this would mean you would have to render all the tiles to the D3DImage image source using Direct3D, which would be much faster as it is hardware accelerated. (https://www.codeproject.com/Articles/28526/Introduction-to-D3DImage)
You may be able to host XNA through a WinForms control and rendering using that, I have no experience with this, so I test to any performance. (WPF vs XNA to render thousands of sprites)
Personally, for rendering, I would use the GDI method as it is software based, relatively easy to set up and I have had experience with it, and seen it's performance.
Additionally, when rendering the tiles to a control, you can use the scrollbar positions and control size to determine the area of your map which is actually visible. From this, you can simply select those few tiles and only render them, saving a lot of time.
Furthermore, when you manage it yourself, you can simply load the different sprites into memory and then use that same memory to draw it in different locations onto a buffer image. This would decrease that memory issue you mentioned.
Below is my example code for the GDI method, I render 2500 32x32 pixel sprites (all of which are the same green color, however you'd set this memory to an actual sprite - src memory). The sprites are bitblit to a buffer image (srcb memory) which is then bitblit to the window, in your case, you'd bitblit the buffer image to a winforms canvas or something. With this, I get between 30 and 40 fps on my base model Surface Pro 3. This should be enough performance for a level editor's rendering. Please note this code is very crude and just roughly outlines the process, it can almost certainly be improved upon.
//
// GDI DLL IMPORT
//
[DllImport("gdi32.dll", SetLastError = true)]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteDC(IntPtr hDC);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern bool BitBlt(IntPtr hDC, int x, int y, int width, int height, IntPtr hDCSource, int sourceX, int sourceY, uint type);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern bool FillRgn(IntPtr hdc, IntPtr hrgn, IntPtr hbr);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr CreateSolidBrush(uint crColor);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
public const uint SRCCOPY = 0x00CC0020; // dest = source
public const uint SRCPAINT = 0x00EE0086; // dest = source OR dest
public const uint SRCAND = 0x008800C6; // dest = source AND dest
public const uint SRCINVERT = 0x00660046; // dest = source XOR dest
public const uint SRCERASE = 0x00440328; // dest = source AND (NOT dest )
public const uint NOTSRCCOPY = 0x00330008; // dest = (NOT source)
public const uint NOTSRCERASE = 0x001100A6; // dest = (NOT src) AND (NOT dest)
public const uint MERGECOPY = 0x00C000CA; // dest = (source AND pattern)
public const uint MERGEPAINT = 0x00BB0226; // dest = (NOT source) OR dest
public const uint PATCOPY = 0x00F00021; // dest = pattern
public const uint PATPAINT = 0x00FB0A09; // dest = DPSnoo
public const uint PATINVERT = 0x005A0049; // dest = pattern XOR dest
public const uint DSTINVERT = 0x00550009; // dest = (NOT dest)
public const uint BLACKNESS = 0x00000042; // dest = BLACK
public const uint WHITENESS = 0x00FF0062; // dest = WHITE
//
// END DLL IMPORT
//
//GDI Graphics
private Graphics g;
//Colors
private const int BACKGROUND_COLOR = 0xffffff;
private const int GRAPH_COLOR_ONE = 0x00FF00;
//Pointers
IntPtr hdc;
IntPtr srcb;
IntPtr dchb;
IntPtr origb;
IntPtr src;
IntPtr dch;
IntPtr orig;
//Brushes
IntPtr brush_one;
IntPtr brush_back;
public Form1()
{
InitializeComponent();
//Create a graphics engine from the window
g = Graphics.FromHwnd(this.Handle);
//Get the handle of the Window's graphics and then create a compatible source handle
hdc = g.GetHdc();
srcb = CreateCompatibleDC(hdc);
src = CreateCompatibleDC(hdc);
//Get the handle of a new compatible bitmap object and map it using the source handle to produce a handle to the actual source
dchb = CreateCompatibleBitmap(hdc, ClientRectangle.Width, ClientRectangle.Height);
origb = SelectObject(srcb, dchb);
//Get the handle of a new compatible bitmap object and map it using the source handle to produce a handle to the actual source
dch = CreateCompatibleBitmap(hdc, 32, 32);
orig = SelectObject(src, dch);
//Create the burshes
brush_one = CreateSolidBrush(GRAPH_COLOR_ONE);
brush_back = CreateSolidBrush(BACKGROUND_COLOR);
//Create Image
FillRectangle(brush_one, src, 0, 0, 32, 32);
//Fill Background
FillRectangle(brush_back, hdc, 0, 0, ClientRectangle.Width, ClientRectangle.Height);
this.Show();
Render();
}
private void Render()
{
Stopwatch s = new Stopwatch();
s.Start();
int frames = 0;
while(frames <= 30)
{
frames++;
FillRectangle(brush_back, srcb, 0, 0, ClientRectangle.Width, ClientRectangle.Height);
for (int i = 0; i < 50; i++)
for (int j = 0; j < 50; j++)
BlitBitmap(i * 5, j * 5, 32, 32, srcb, src);
BlitBitmap(0, 0, ClientRectangle.Width, ClientRectangle.Height, hdc, srcb);
}
s.Stop();
float fps = (float)frames / ((float)s.ElapsedMilliseconds / 1000.0f);
MessageBox.Show(Math.Round(fps, 2).ToString(), "FPS");
}
private void FillRectangle(IntPtr b, IntPtr hdc, int x, int y, int w, int h)
{
//Create the region
IntPtr r = CreateRectRgn(x, y, x + w, y + h);
//Fill the region using the specified brush
FillRgn(hdc, r, b);
//Delete the region object
DeleteObject(r);
}
private void BlitBitmap(int x, int y, int w, int h, IntPtr to, IntPtr from)
{
//Blit the bits of the actual source object to the window, using its handle
BitBlt(to, x, y, w, h, from, 0, 0, SRCCOPY);
}
I want to calculate average pixels color from a specific area of the screen. I'm making a LED backlight for my TV so it needs to be very fast. At least 30fps. Bitmap.GetPixel() is too slow for it. I found an OpenGL method Gl.ReadPixels() but i don't know how it works. It recieving int[] as a data to return. But after calling this method i only get an array full of 0. How can i use this method? Or maybe there is some other way for this task?
By far the fastest way to process images in C# is through pinvoke using Win32 Gdi32 and User32 functionality.
Here is some C# code which captures the current desktop. Optionally you can pass the filepath of a 'mask' image which can be used to isolate a region(s) of the desktop capture. In the mask, texel which is black (with alpha 0) will be discarded from the screen grab. It's quite rudimental since it only supports 32-bit images and requires the mask and screen capture to be the same size. You welcome to improve on it in your own way, or you can see how image processing is done on a texel by texel basis by hooking directly into the Win32 functions with pinvoke (see constructor).
public class ScreenCapture
{
private System.Drawing.Bitmap capture;
/// <summary>
/// Constructor
/// </summary>
/// <param name="maskFile">Optional mask file, use "" for no mask</param>
public ScreenCapture( string maskFile )
{
// capture the screen
capture = CaptureWindow(User32.GetDesktopWindow());
// if there is a mask file, load it and apply it to the capture
if (maskFile != "")
{
string maskfilename = maskFile + ".png";
System.Drawing.Bitmap maskImage = System.Drawing.Image.FromFile(maskfilename) as Bitmap;
// ensure mask is same dim as capture...
if ((capture.Width != maskImage.Width) || (capture.Height != maskImage.Height))
throw new System.Exception("Mask image is required to be " + capture.Width + " x " + capture.Height + " RGBA for this screen capture");
Rectangle rectCapture = new Rectangle(0, 0, capture.Width, capture.Height);
Rectangle rectMask = new Rectangle(0, 0, maskImage.Width, maskImage.Height);
System.Drawing.Imaging.BitmapData dataCapture = capture.LockBits(rectCapture, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
System.Drawing.Imaging.BitmapData dataMask = maskImage.LockBits(rectCapture, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
IntPtr ptrCapture = dataCapture.Scan0;
IntPtr ptrMask = dataMask.Scan0;
int size = Math.Abs(dataCapture.Stride) * capture.Height;
byte[] bitsCapture = new byte[size];
byte[] bitsMask = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(ptrCapture, bitsCapture, 0, size);
System.Runtime.InteropServices.Marshal.Copy(ptrMask, bitsMask, 0, size);
// check each pixel of the image... if the mask is 0 for each channel, set the capture pixel to 0.
for (int n = 0; n < size; n += 4)
{
bool clearPixel = true;
for ( int c = 0; c < 4; ++c )
{
// if any pixel of the mask is not black, do not clear the capture pixel
if (bitsMask[n + c] != 0)
{
clearPixel = false;
break;
}
}
if ( clearPixel )
{
bitsCapture[n] = 0;
bitsCapture[n + 1] = 0;
bitsCapture[n + 2] = 0;
bitsCapture[n + 3] = 0;
}
}
System.Runtime.InteropServices.Marshal.Copy(bitsCapture, 0, ptrCapture, size);
System.Runtime.InteropServices.Marshal.Copy(bitsMask, 0, ptrMask, size);
capture_.UnlockBits(dataCapture);
maskImage.UnlockBits(dataMask);
}
}
/// <summary>
/// Creates an Image object containing a screen shot of a specific window
/// </summary>
/// <param name="handle">The handle to the window.
/// (In windows forms, this is obtained by the Handle property)</param>
/// <returns>Screen grab image</returns>
private System.Drawing.Bitmap CaptureWindow(IntPtr handle)
{
// get te hDC of the target window
IntPtr hdcSrc = User32.GetWindowDC(handle);
// get the size
User32.RECT windowRect = new User32.RECT();
User32.GetWindowRect(handle, ref windowRect);
int width = windowRect.right - windowRect.left;
int height = windowRect.bottom - windowRect.top;
// create a device context we can copy to
IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
// create a bitmap we can copy it to,
// using GetDeviceCaps to get the width/height
IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
// select the bitmap object
IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
// bitblt over
GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
// restore selection
GDI32.SelectObject(hdcDest, hOld);
// clean up
GDI32.DeleteDC(hdcDest);
User32.ReleaseDC(handle, hdcSrc);
// get a .NET image object for it
System.Drawing.Bitmap img = System.Drawing.Image.FromHbitmap(hBitmap);
// free up the Bitmap object
GDI32.DeleteObject(hBitmap);
return img;
}
/// <summary>
/// Save this capture as a Png image.
/// </summary>
/// <param name="filename">File path and name not including extension</param>
public void SaveCapture( string filename )
{
capture.Save(filename + ".png", System.Drawing.Imaging.ImageFormat.Png);
}
/// <summary>
/// Helper class containing Gdi32 API functions
/// </summary>
private class GDI32
{
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 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);
}
/// <summary>
/// Helper class containing User32 API functions
/// </summary>
private class User32
{
[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 GetActiveWindow();
[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);
}
}
It throws exceptions, so be sure to catch them. To use...
ScreenCapture grab = new ScreenCapture( "screenGrabMask.png" );
grab.SaveCapture( "screenGrab.png" );
Take a look at Bitmap.LockBits
https://msdn.microsoft.com/de-de/library/5ey6h79d(v=vs.110).aspx
to provide some basic idea
private unsafe void demo(Bitmap bm)
{
System.Drawing.Imaging.BitmapData D = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
int stride = Math.Abs(D.Stride);
byte* pImg = (byte*)D.Scan0.ToPointer();
for(int y = 0; y < bm.Height; y++)
{
byte* pRow = (byte*)(pImg + y * stride);
for(int x = 0; x < bm.Width; x++)
{
uint b = *pRow++;
uint g = *pRow++;
uint r = *pRow++;
pRow++; // skip alpha
// do something with r g b
}
}
bm.UnlockBits(D);
}
Allow unsafe code (be careful) to avoid extra memory copies.
I have a cursor image .cur with maximum Width and Height of 250 pixels which i fully need.
I already managed to the replace the mouse pointer image with this cur image instead when holding right click.
The problem is that when using I'm doing that the pointer is associated with the top left corner of the image so when I go beyond for example the bounds of the canvas the cur image disappear and I go back to the normal pointer image.
I want this cur image to be centered on the mouse pointer location, not on it's top left corner. How can I do that?
private void canvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Cursor cPro = new Cursor(#"C:\Users\Faris\Desktop\C# Testing Projects\cPro.cur");
globalValues.cursorSave = canvas.Cursor;
canvas.Cursor = cPro;
}
private void canvas_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
canvas.Cursor = globalValues.cursorSave;
}
You have two options:
In Visual Studio, open the cursor file or resource in the image editor and select the Hotspot Tool from the toolbar. Then click on the new hotspot and save the file.
Create a cursor pragmatically using a bitmap and specify the hot spot yourself.
The code below is from here:
namespace CursorTest
{
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
public class CursorTest : Form
{
public CursorTest()
{
this.Text = "Cursor Test";
Bitmap bitmap = new Bitmap(140, 25);
Graphics g = Graphics.FromImage(bitmap);
using (Font f = new Font(FontFamily.GenericSansSerif, 10))
g.DrawString("{ } Switch On The Code", f, Brushes.Green, 0, 0);
this.Cursor = CreateCursor(bitmap, 3, 3);
bitmap.Dispose();
}
[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
return new Cursor(CreateIconIndirect(ref tmp));
}
}
}
I have one Windows Form with
this.BackColor = Color.Red
and
this.TransparencyKey = Color.Red
and there is one PictureBox (png image with transparency corners) on this form,
PictureBox.SizeMode = Normal.
Then I set SizeMode of PictureBox to StretchImage and get other result:you can see it here
(sorry, I can put one hyperlink only)
You can see red points/dots, but it is not Color.Red because it is transparency key of form.
I tried to implement transparent form, transparent control to remove these "red" points.
Anyways, I would like to ask about my last point - I tried to override "OnPaintBackground" method and when I implemented some like code below:
e.Graphics.FillRectangle(Brushes.Red, ClientRectangle);
TextureBrush brush = ImageHelper.ScaleImage(BackgroundImage, ClientRectangle.Width, ClientRectangle.Height);
e.Graphics.FillRectangle(brush, ClientRectangle);
I saved scaled BitMap to file before putting it to TextureBrush - this png scaled image doesn't contain "red" points, but they were painted on form.
Does somebody have any idea why it happens and tell me some way to solve it.
Best regards.
This happens because GDI+, which is drawing the image, doesn't know that red is becoming transparent.
Therefore, it blends the borders of the image with the red background, creating reddish (but not completely red) pixels which do not become transparent.
To solve this, you'll need to make a layered window.
EDIT:
Use the following native methods:
static class NativeMethods {
public const int LayeredWindow = 0x80000;//WS_EX_LAYERED
#region Drawing
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UpdateLayeredWindow(IntPtr handle, IntPtr screenDc, ref Point windowLocation, ref Size windowSize, IntPtr imageDc, ref Point dcLocation, int colorKey, ref BlendFunction blendInfo, UlwType type);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("User32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(IntPtr hObject);
#endregion
}
struct BlendFunction {
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
enum UlwType : int {
None = 0,
ColorKey = 0x00000001,
Alpha = 0x00000002,
Opaque = 0x00000004
}
Override the form's CreateParams:
protected override CreateParams CreateParams {
get {
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= NativeMethods.LayeredWindow;
return createParams;
}
}
Call the following function in OnShown:
static Point Zero;
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")]
void UpdateWindow() {
IntPtr screenDC = NativeMethods.GetDC(IntPtr.Zero);
IntPtr imageDC = NativeMethods.CreateCompatibleDC(screenDC);
IntPtr gdiBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
try {
gdiBitmap = image.GetHbitmap(Color.FromArgb(0)); //Get a GDI handle to the image.
oldBitmap = NativeMethods.SelectObject(imageDC, gdiBitmap); //Select the image into the DC, and cache the old bitmap.
Size size = image.Size; //Get the size and location of the form, as integers.
Point location = this.Location;
BlendFunction alphaInfo = new BlendFunction { SourceConstantAlpha = 255, AlphaFormat = 1 }; //This struct provides information about the opacity of the form.
NativeMethods.UpdateLayeredWindow(Handle, screenDC, ref location, ref size, imageDC, ref Zero, 0, ref alphaInfo, UlwType.Alpha);
} finally {
NativeMethods.ReleaseDC(IntPtr.Zero, screenDC); //Release the Screen's DC.
if (gdiBitmap != IntPtr.Zero) { //If we got a GDI bitmap,
NativeMethods.SelectObject(imageDC, oldBitmap); //Select the old bitmap into the DC
NativeMethods.DeleteObject(gdiBitmap); //Delete the GDI bitmap,
}
NativeMethods.DeleteDC(imageDC); //And delete the DC.
}
Invalidate();
}