Outline a path with GDI+ in .Net - c#

How does one outline a graphicspath using GDI+? For example, I add two intersecting rectangles to a GraphicsPath. I want to draw the outline of this resulting graphicspath only.
Note that I don't want to fill the area, I just want to draw the outline.
Example:

There is no managed way to do the outline. However, GDI+ does have an function called GdipWindingModeOutline that can do exactly this.
Here is the MSDN reference
This code does the trick:
// Declaration required for interop
[DllImport(#"gdiplus.dll")]
public static extern int GdipWindingModeOutline( HandleRef path, IntPtr matrix, float flatness );
void someControl_Paint(object sender, PaintEventArgs e)
{
// Create a path and add some rectangles to it
GraphicsPath path = new GraphicsPath();
path.AddRectangles(rectangles.ToArray());
// Create a handle that the unmanaged code requires. nativePath private unfortunately
HandleRef handle = new HandleRef(path, (IntPtr)path.GetType().GetField("nativePath", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(path));
// Change path so it only contains the outline
GdipWindingModeOutline(handle, IntPtr.Zero, 0.25F);
using (Pen outlinePen = new Pen(Color.FromArgb(255, Color.Red), 2))
{
g.DrawPath(outlinePen, path);
}
}

I cannot try it myself, but I think you should create a Region object with your shapes' intersections and then use the FrameRgn API.
Here is its pinvoke signature:
[DllImport("gdi32.dll")]
static extern bool FrameRgn(
IntPtr hdc,
IntPtr hrgn,
IntPtr hbr,
int nWidth,
int nHeight);
P.S: please post your solution if this works, I'm curious :)

Related

Get/set pixel from Graphics object GDI+

I'm trying to find an alternative solution to getting/setting pixels at certain position in a Graphics object.
Right now I'm using GDI functions:
[DllImport("gdi32.dll")]
public static extern int GetPixel(System.IntPtr hdc, int nXPos, int nYPos);
[DllImport("gdi32.dll")]
public static extern uint SetPixel(IntPtr hdc, int X, int Y, int crColor);
I couldn't find any for GDI+. GetPixel/SetPixel seems to be on Bitmap object only.
The alternatives from gdi32.dll work well when the Graphics is backed by the screen but when using Graphics with a bitmap it doesn't work anymore (GetPixel returns black, since this works on another bitmap not the actual one): http://support.microsoft.com/kb/311221
Some sample code:
private void ChangeImage(Graphics g)
{
IntPtr gDC = IntPtr.Zero;
try
{
gDC = g.GetHdc();
// get the pixel color
Color pixel = ColorTranslator.FromWin32(GetPixel(gDC, x, y));
//change pixel object and persist
SetPixel(gDC, x, y, ColorTranslator.ToWin32(pixel));
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
finally
{
if (gDC != IntPtr.Zero)
g.ReleaseHdc(gDC);
}
}
Is there any way to parse the the Graphics object per pixel basis?
The Graphics object represents a surface where the user is free to add any objects including hand drawing. On top of this I need to apply filters (like blur or pixelation) on some screen parts so I need to access what has been painted already in the graphics.
I've also tried to find a way to persist the current graphics to a Bitmap and use GetPixel from the bitmap object but I've also couldn't find a way to save Graphics content.
Thx
No, it's not possible to read pixels from a Graphics object directly. You would need access to the underlying HDC or Bitmap object to do this.
Dim bmap As New Bitmap(550, 100)
Dim g As Graphics = Graphics.FromImage(bmap)
' Dont use the Graphics interface, use the BitMap interface
DIM col as color = bmap.GetPixel(x,y)

Getting the color of a pixel drawn by DWM in C#

I want to get Screenshots of a possible hidden Window of another application that is using drawing via direct3d or opengl. I tryed a lot of ways to receive this windows content but only got black or transparent pictures. The closest i got was by using a DWM sample here
http://bartdesmet.net/blogs/bart/archive/2006/10/05/4495.aspx
this paints the window onto my c# form but i cant get the pixelcolors. If ill do a form.drawtobitmap the pixels drawn by dwm are missing.
So is their any way to use DWM to recive the capture into a image
or to get the image drawn onto my form?
To answer your question:
You can use GetPixel() Win32 function. But it's overkill in this situation.
Pinvoke GetPixel
MSDN GetPixel
The right way, is to get the device context and bit blit the content.
EDIT:
I've thrown together some code, by using PrintWindow. Seems to work quite well, even with media players. Note that GetWindowRect returns invalid rectangle for minimized Windows. But it's a decent start.
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
internal Rect(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hwnd, out Rect lpRect);
public void DumpWindow(IntPtr hwndSource, string filename)
{
Rect rc;
GetWindowRect(hwndSource, out rc);
var bmp = new Bitmap(rc.Right - rc.Left, rc.Bottom - rc.Top, PixelFormat.Format32bppArgb);
using (Graphics gBmp = Graphics.FromImage(bmp))
{
IntPtr hdcBmp = gBmp.GetHdc();
PrintWindow(hwndSource, hdcBmp, 0);
gBmp.ReleaseHdc(hdcBmp);
}
bmp.Save(filename);
}
Edit2:
And if you add a second button to DWM demo form, insert this:
private void button1_Click(object sender, EventArgs e)
{
var w = (Window)lstWindows.SelectedItem;
DumpWindow(w.Handle, "test.bmp");
Process.Start("test.bmp");
}
It still shows an empty image?

Print and Export to USB (File Format: XML/CSV/Excel) Functionality in Smart device[Symbo Motoroal MC75(Windows Mobile 6.1)] application?

I have a form which contains combo boxes, textboxes and a data grid with many rows. I want to take print out (with generated barcode [application generating barcode as image]) and also want to export the data in that page as CSV/XML/Excel format to USB or Phone's Physical Directory. Please guide me how to it. This is my first Windows Mobile app. I am not so wise in Windows Mobile. Please help me find a better solution as a code or link or just direct me.
To create the Print Out, you will have to write to your PrintDocument using GDI. There is nothing really built in. You could possibly do a screenshot (code below).
Exporting data to CSV is best done on your own as well. Just Create/Open a file stream and write whatever you want to it.
Screenshot: Requires PInvoke to BitBlt and GetDC
const int SRCCOPY = 0x00CC0020;
[DllImport("coredll.dll")]
private static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("coredll.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);
public Bitmap ScreenCapture(string fileName) {
Bitmap bitmap = new Bitmap(this.Width, this.Height);
using (Graphics gScr = Graphics.FromHdc(GetDC(IntPtr.Zero))) { // A Zero Pointer will Get the screen context
using (Graphics gBmp = Graphics.FromImage(bitmap)) { // Get the bitmap graphics
BitBlt(gBmp.GetHdc(), 0, 0, this.Width, this.Height, gScr.GetHdc(), this.Left, this.Top, SRCCOPY); // Blit the image data
}
}
bitmap.Save(fileName, ImageFormat.Png); //Saves the image
return bitmap;
}
[Update]:
If you want the image saved to a particular location, send the full path with the filename (i.e. \\Windows\Temp\screenShot.png).
If you want to exclude the controls, reduce the this.Width, this.Height, this.Left and this.Right until you have the size that fits the region that works.
Last, if you want the Bitmap to use in memory, simply save it and use it as necessary. Example:
panel1.Image = ScreenCapture("image.png");
panel1.BringToFront();
Hope that helps.

How to get mouse position related to desktop in WPF?

Problem
When you search for such question using google you get a lot of hits but all solutions assume you have at least one window.
But my question is just like I phrased it -- not assumptions at all. I can have a window, but I could have zero windows (because I didn't even show one or I just closed the last one). So in short the solution cannot rely on any widget or window -- the only thing is known, is there is a desktop (and app running, but it does not have any windows).
So the question is -- how to get the mouse position?
Background
I would like to show windows centered to mouse position. There is no such mode in WPF (there are only center to owner, or center to screen) so I have to do it manually. The missing piece is mouse position.
Edits
Thank you all, so now I have the first part of the solution -- raw position. Now there is a problem how to convert the data for WPF. I found such topic:
WPF Pixels to desktop pixels
but again, it assumes having some window.
Then I googled more and I found solution:
http://jerryclin.wordpress.com/2007/11/13/creating-non-rectangular-windows-with-interop/
the code includes class for scaling up/down coordinates relying only on info about desktop. So joining those two pieces, I finally get the solution :-). Thanks again.
Getting the Screen Coordinates:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
private void WritePoint(object sender, RoutedEventArgs e)
{
POINT p;
if (GetCursorPos(out p))
{
System.Console.WriteLine(Convert.ToString(p.X) + ";" + Convert.ToString(p.Y));
}
}
Converting Pixels to WPF Units:
[DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
private Point ConvertPixelsToUnits(int x, int y)
{
// get the system DPI
IntPtr dDC = GetDC(IntPtr.Zero); // Get desktop DC
int dpi = GetDeviceCaps(dDC, 88);
bool rv = ReleaseDC(IntPtr.Zero, dDC);
// WPF's physical unit size is calculated by taking the
// "Device-Independant Unit Size" (always 1/96)
// and scaling it by the system DPI
double physicalUnitSize = (1d / 96d) * (double)dpi;
Point wpfUnits = new Point(physicalUnitSize * (double)x,
physicalUnitSize * (double)y);
return wpfUnits;
}
Putting both together:
private void WriteMouseCoordinatesInWPFUnits()
{
POINT p;
if (GetCursorPos(out p))
{
Point wpfPoint = ConvertPixelsToUnits(p.X, p.Y);
System.Console.WriteLine(Convert.ToString(wpfPoint.X) + ";" + Convert.ToString(wpfPoint.Y));
}
}
Two options:
Use System.Windows.Forms.Control.MousePosition, or p/invoke
[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
public static extern bool GetCursorPos([In, Out] NativeMethods.POINT pt);
The first option already does the p/invoke for you. I'm not entirely sure it requires you have some UI splashed up, but I don't think so. Yes, its winforms and not wpf, but it really doesn't have anything to do with where its located at.
If you want to skip any dependencies on system.windows.forms.dll then check out more information about the second on pinvoke.net.
I stumbled over that thread while looking for a solution for the same problem. In the meantime, I found PointToScreen, which does not require any P/Invoke. The method is available on any Visual starting .NET 3.0 (and thus UIElement, Control, etc.) and an implementation would look like this:
protected void OnMouseLeave(object Sender, MouseEventArgs e) {
var relativePosition = e.GetPosition(this);
var screenPosition = this.PointToScreen(relativePosition);
}

How to draw directly on the Windows desktop, C#?

This question has been asked for other languages, and even for those other languages, I have found their answers lacking in how to exactly do it, cleanly (no messed up screen repaints, etc..).
Is it possible to draw onto the Windows Desktop from C#? I am looking for an example if possible.
Try the following:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
class Program {
[DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
static extern int ReleaseDC(IntPtr hwnd, IntPtr dc);
static void Main(string[] args) {
IntPtr desktop = GetDC(IntPtr.Zero);
using (Graphics g = Graphics.FromHdc(desktop)) {
g.FillRectangle(Brushes.Red, 0, 0, 100, 100);
}
ReleaseDC(IntPtr.Zero, desktop);
}
}
You can try:
Graphics.FromHwnd(IntPtr.Zero)
You can see a real-world code example within https://uiautomationverify.codeplex.com/SourceControl/latest#UIAVerify/Tools/visualuiverify/utils/screenrectangle.cs
This draws a rectangle that will appear on the screen until the user chooses to remove it at an arbitrary position (wont be repainted over). It uses a windows form thats hidden/ appears as a popup.
This is the code behind the UIAVerify.exe tool in the current Windows SDK.
If you want to use the above, copy the following files into your project:
utils\screenboundingrectangle.cs
utils\screenrectangle.cs
win32\*
Might need to update namespaces accordingly + add references to System.Drawing + System.Windows.Forms
Then you can draw a rectangle with the following code:
namespace Something
{
public class Highlighter
{
ScreenBoundingRectangle _rectangle = new ScreenBoundingRectangle();
public void DrawRectangle(Rectangle rect)
{
_rectangle.Color = System.Drawing.Color.Red;
_rectangle.Opacity = 0.8;
_rectangle.Location = rect;
this._rectangle.Visible = true;
}
}
}
and
var rect = Rectangle.FromLTRB(100, 100, 100, 100);
var hi = new Highlighter();
hi.DrawRectangle(rect);

Categories