Get/set pixel from Graphics object GDI+ - c#

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)

Related

Pick up and read the code in RGB hum mouseDown event in C #

I want every time I click somewhere ( either in the form or a picture, des that has color) that has color, read and "capture" / " store " the RGB value of that location.
And as the first time I've been trying to do something , I even lost in what to use or not to use and really I'm on the right track :
Current state:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Point p = e.Location;
label1.Text = Convert.ToString(p);
label1.Refresh();
int x, y;
System.Drawing.Color cor = new Bitmap(" ").GetPixel(x, y);
MessageBox.Show(Convert.ToString(cor.R) + Convert.ToString(cor.G) + Convert.ToString(cor.B));
}
After searching for something that could be necessary, falls into the " GetPixel " but do not know if it really served to what I want , because until then as I said, the first time I try something related .
Two problems :
Do not know how I will use the "x " and " y" to get the position , although I can get the two only with the " Point p = e.Location ; " but I can not use it with the GetPixel .
How will not be necessarily every time I open an image to check and will not be the same or may be I just want to know the color within a form.
I shall be grateful if someone could help me find the right one , do not want the answer.
Accoring to the example code of "GetPixel" you can just use the
Cursor.Position.X Cursor.Position.Y here (see second line of full example below)
so just use:
GetPixel(hdc, p.X, p.Y);
full example:
static private void myControl_MouseMove(object sender,System.Windows.Forms.MouseEventArgs e)
{
IntPtr hdc = GetDC(IntPtr.Zero);
uint pixel = GetPixel(hdc, Cursor.Position.X, Cursor.Position.Y);
ReleaseDC(IntPtr.Zero,hdc);
Color color = Color.FromArgb((int)pixel);
Console.WriteLine("Color is {0}",color);
}
The rest of the functions are just imported gdi/user32 libraries
[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);
Should work like a charm.
Further explanation:
The GetDC function retrieves a handle to a device context (DC) for the client area of a specified window or for the entire screen. You can also use the returned handle in subsequent GDI functions to draw in the DC.
Release DC releases the handle.
The color has to be converted from Argb to Rgp
The GetPixel function detailed information here retrieves the red, green, blue color value of the pixel at the specified coordinates with
A handle to the device context.
The x-coordinate, in logical units, of the pixel to be examined.
The y-coordinate, in logical units, of the pixel to be examined.
Quite complicated for each of these scenarios will have to be handled differently. For the form, does it have a constant background colour or is it variable?
But for the picture box you would need to assign a picturebox mouse click event to it, much in a similar way to how you've done it for the form. Now once you've done that you need to work out where on the picture box it was clicked. You've already done this i.e. e.Location. From there it is simply getting the pixel at that location:
Color color = pictureBoxControl.Image.GetPixel(e.Location.X, e.Location.Y);
Note that depending on your image you might also have to check the position in the PictureBox that was clicked is on the picture i.e. was it on the picture or the margin. If it's in the margin your color is simply the BackgroundImage or BackgroundColor property. This can then be applied to other controls and also the form.
Also GetPixel is really slow and you may want to have a look at Bitmap.LockBits.
Firstly you need to create a screenshot of your window:
private System.Drawing.Bitmap TakeScreenshot()
{
int StartX, StartY;
int Width, Height;
StartX = 0;
StartY = 0;
Width = Convert.ToInt32(this.Width);
Height = Convert.ToInt32(this.Height);
System.Drawing.Bitmap Screenshot = new System.Drawing.Bitmap(Width, Height);
System.Drawing.Graphics G = System.Drawing.Graphics.FromImage(Screenshot);
G.CopyFromScreen(StartX, StartY, 0, 0, new System.Drawing.Size(Width, Height));
G.Dispose();
return Screenshot;
}
Then you need your mouse position:
Point current = e.Location;
And your color is:
Bitmap colorpicker = TakeScreenshot();
Point current = e.Location;
hoverColor = colorpicker.GetPixel(current.X,current.Y);
colorpicker.Dispose();
The created bitmap includes all pixel and their color of your window and with the current mouse position you have the x-coordinate and the y-coordinate you choose the pixel you want to get from the bitmap and by calling the method GetPixel you get the color of your pixel.

Converting Print page Graphics to Bitmap C#

I have an application where the user can print a document of selected items in the form of an invoice. Everything works well however on the PrintPage event of the PrintDocument I want to capture the document or he graphics, turn it into a bitmap so I can save to it a .bmp for later use / viewing. (Note: There are multiple pages in this document) I have it set up like this:
PrintDocument doc = new PrintDocument();
doc.PrintPage += new PrintPageEventHandler(doc_PrintPage);
doc.Print();
Then on the PrintPage event:
private void doc_PrintPage(object sender, PrintPageEventArgs ev)
{
// Use ev.Graphics to create the document
// I create the document here
// After I have drawn all the graphics I want to get it and turn it into a bitmap and save it.
}
I have cut out all the ev.Graphics code just because it is a lot of lines. Is there a way to turn the Graphics into a Bitmap without changing any of the code that draws graphics onto the PrintDocument? Or do something similar to that, maybe copying the document and converting it into a bitmap?
You should actually draw the page into the bitmap, and then use ev.Graphics to draw that bitmap on the page.
private void doc_PrintPage(object sender, PrintPageEventArgs ev)
{
var bitmap = new Bitmap((int)graphics.ClipBounds.Width,
(int)graphics.ClipBounds.Height);
using (var g = Graphics.FromImage(bitmap))
{
// Draw all the graphics using into g (into the bitmap)
g.DrawLine(Pens.Black, 0, 0, 100, 100);
}
// And maybe some control drawing if you want...?
this.label1.DrawToBitmap(bitmap, this.label1.Bounds);
ev.Graphics.DrawImage(bitmap, 0, 0);
}
Actually, Yorye Nathan's answer on Jun 3'12 at 7:33 is correct and it was the starting point that helped me. However, I was not able to get it to work as-is, so I made some corrections to make it work in my application. The correction is to get the Printer's page size from the PrintPgeEventArgs.Graphics Device Context, and include the PrintPage Graphics as a third parameter in the new Bitmap(...) construction.
private void doc_PrintPage(object sender, PrintPageEventArgs ppea)
{
// Retrieve the physical bitmap boundaries from the PrintPage Graphics Device Context
IntPtr hdc = ppea.Graphics.GetHdc();
Int32 PhysicalWidth = GetDeviceCaps(hdc, (Int32)PHYSICALWIDTH);
Int32 PhysicalHeight = GetDeviceCaps(hdc, (Int32)PHYSICALHEIGHT);
ppea.Graphics.ReleaseHdc(hdc);
// Create a bitmap with PrintPage Graphic's size and resolution
Bitmap myBitmap = new Bitmap(PhysicalWidth, PhysicalHeight, ppea.Graphics);
// Get the new work Graphics to use to draw the bitmap
Graphics myGraphics = Graphics.FromImage(myBitmap);
// Draw everything on myGraphics to build the bitmap
// Transfer the bitmap to the PrintPage Graphics
ppea.Graphics.DrawImage(myBitmap, 0, 0);
// Cleanup
myBitmap.Dispose();
}
////////
// Win32 API GetDeviceCaps() function needed to get the DC Physical Width and Height
const int PHYSICALWIDTH = 110; // Physical Width in device units
const int PHYSICALHEIGHT = 111; // Physical Height in device units
// This function returns the device capability value specified
// by the requested index value.
[DllImport("GDI32.DLL", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Int32 GetDeviceCaps(IntPtr hdc, Int32 nIndex);
Thank you again Yorye Nathan for providing the original answer.
//AJ

StretchBlt occasionally inverts image when it shouldn't

I’m having a problem using StretchBlt (or BitBlt) to copy an image from a PictureBox to a Bitmap for use in creating an AVI file.
The AVI file is an unimportant aspect I believe - I can just display the Bitmap on another PictureBox and see the problem.
The problem is that occasionally (not often) a single image is flipped (mirrored across the X axis, never the Y axis).
I’m not sure if this is a known problem with StretchBlt I haven’t yet found mention of or if I am doing something wrong.
Note this is NOT due to the intended functionality with StretchBlt of "If the signs of source and destination height or width are different then it creates a mirror image".
UPDATE: I changed things to force the source/destination to be the same size, and am using BitBlt with the same behavior.
I’ve included some code (c#), hopefully all of the important parts.
Stepping through the code I can see this happen for a single image that has exactly the same information being passed to StretchBlt (other than the hdc to copy to) as the previous image and the next image (and next, next) all of which are fine.
It doesn't happen often, and I dont see any reason when it does. Or a way to detect it happened (so I can flip it back).
I have a work around that doesn't use StretchBlt, but it is much slower and really degrades performance.
Another possibly useful bit: this flipped image is rare in normal usage (less than 1 in 100 frames). But when run in the IDE, stepping through image by image, it happens very regularly (maybe 1 in 10).
Any ideas what could be causing this or what I could be doing wrong? Or other FAST methods to copy the Bitmap including re-sizing.
NOTE: The bitmaps do vary in size (can't use BitBlt), but not by a lot.
Thank you!
Kate
// --- Import statement
[DllImport("GDI32.DLL", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern bool StretchBlt(
IntPtr hdcDest, int nXDest, int nYDest, int nDestWidth, int nDestHeight,
IntPtr hdcSrc, int nXSrc, int nYSrc, int nSrcWidth, int nSrcHeight, Int32 dwRop );
// --- A small class for creating/storing Bitmap and Graphics objects, which get reused
public class CAviImageInfo
{
private Bitmap mAviBitmap = null;
private Graphics mAviGraphics = null;
public CAviImageInfo(int width, int height )
{
mAviBitmap = new Bitmap(width, height);
mAviGraphics = Graphics.FromImage(mAviBitmap);
}
~CAviImageInfo()
{
mAviGraphics.Dispose();
mAviGraphics = null;
}
public Bitmap AviBitmap
{ get { return mAviBitmap; } }
public Graphics AviGraphics
{ get { return mAviGraphics; } }
}
// --- Two PictureBoxs placed on form at design time (one is just to watch for these mirrored images):
PictureBox mMainPictureBox; // --- Displays the images to be copied
PictureBox DebugPictureBox;
// --- The following done one time:
Graphics mMainPictureBoxGraphics = mMainPictureBox.CreateGraphics();
IntPtr mMainPictureBoxHdc = mMainPictureBoxGraphics.GetHdc();
// --- Method that does the copying. Called each time image on panel is updated.
Public void UpdateAviRecording()
{
// --- Gets unused Bitmap and Graphics objects (these are reused)
CAviImageInfo aviImageInfo = GetUnusedAviImageInfo();
IntPtr destinationHdc = aviImageInfo.AviGraphics.GetHdc();
StretchBlt(
destinationHdc,
0, 0, aviImageInfo.AviBitmap.Width, aviImageInfo.AviBitmap.Height,
mMainPictureBoxHdc,
0, 0, mMainPictureBox.Width, mMainPictureBox.Height, SRCCOPY);
// --- Show the copied Bitmap on the debug PictureBox
// --- (normally would pass it to be written to avi file)
DebugPictureBox.Image = aviImageInfo.AviBitmap;
DebugPictureBox.Refresh();
aviImageInfo.AviGraphics.ReleaseHdc(destinationHdc);
}
this is just a suggestion. I don't know if it will work..
Set the width and height using Math.Abs() of the image in the call to StretchBlt.
That might prevent possible memory corruption and inversion of signs (as GSerg mentioned).
..
StretchBlt(
destinationHdc,
0, 0, Math.Abs(aviImageInfo.AviBitmap.Width),
Math.Abs(aviImageInfo.AviBitmap.Height),
mMainPictureBoxHdc,
0, 0, Math.Abs(mMainPictureBox.Width),
Math.Abs(mMainPictureBox.Height), SRCCOPY);

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.

Outline a path with GDI+ in .Net

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 :)

Categories