c# gdi32 bitblt seems to do nothing - c#

I'm trying to use BitBlt to get regions of a graphics and store them in bitmaps.
But here, I do something easier to understand my problem:
Bitmap sourceBitmap = new Bitmap(64, 64, PixelFormat.Format32bppRgb);
Graphics sourceGraphics = Graphics.FromImage(sourceBitmap);
Bitmap destBitmap = new Bitmap(64, 64, PixelFormat.Format32bppRgb);
Graphics destGraphics = Graphics.FromImage(destBitmap);
sourceGraphics.FillRectangle(new SolidBrush(Color.Red), new Rectangle(0, 0, 30, 30));
sourceGraphics.FillRectangle(new SolidBrush(Color.Green), new Rectangle(30, 30, 30, 30));
destGraphics.FillRectangle(new SolidBrush(Color.Blue), new Rectangle(0, 0, 30, 30));
destGraphics.FillRectangle(new SolidBrush(Color.Yellow), new Rectangle(30, 30, 30, 30));
IntPtr destDC = destGraphics.GetHdc();
IntPtr destHB = destBitmap.GetHbitmap();
IntPtr old = SelectObject(destDC, destHB);
IntPtr sourceDC = sourceGraphics.GetHdc();
IntPtr sourceHB = sourceBitmap.GetHbitmap();
old = SelectObject(sourceDC, sourceHB);
int success = BitBlt(
destDC, 0, 0, 64, 64, sourceDC, 0, 0, 0x00CC0020
);
Why after the BitBlt my destBitmap contains blue/yellow rectangles (initial bitmap in destination) instead of the red/green rectangles which should have been blitted from the source bitmap ?
Imports are done like this :
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern int BitBlt(
IntPtr hdcDest, // handle to destination DC (device context)
int nXDest, // x-coord of destination upper-left corner
int nYDest, // y-coord of destination upper-left corner
int nWidth, // width of destination rectangle
int nHeight, // height of destination rectangle
IntPtr hdcSrc, // handle to source DC
int nXSrc, // x-coordinate of source upper-left corner
int nYSrc, // y-coordinate of source upper-left corner
System.Int32 dwRop // raster operation code
);
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
public static extern void DeleteObject(IntPtr obj);
And finally cleaning code, and bitmap streaming to see bitmap content :
DeleteObject(destHB);
DeleteObject(sourceHB);
destGraphics.ReleaseHdc();
sourceGraphics.ReleaseHdc();
string path = "c:/tmp/dest.png";
destBitmap.Save(path);

Found a combination that works...but I don't understand why (definitely not a GDI expert):
I've added in calls to create compatible DCs with CreateCompatibleDC().
But note that in the actual call to BitBlt(), I'm still using the original DC "destDC" for the destination DC (not the new "destCDC"), but the new compatible DC "sourceCDC" for the source DC. No other combo seemed to work. I still, however, had to create a compatible DC for the destination even though I wasn't using it in the BitBlt() call:
private const int SRCCOPY = 0xCC0020;
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern int BitBlt(
IntPtr hdcDest, // handle to destination DC (device context)
int nXDest, // x-coord of destination upper-left corner
int nYDest, // y-coord of destination upper-left corner
int nWidth, // width of destination rectangle
int nHeight, // height of destination rectangle
IntPtr hdcSrc, // handle to source DC
int nXSrc, // x-coordinate of source upper-left corner
int nYSrc, // y-coordinate of source upper-left corner
int dwRop // raster operation code
);
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
public static extern void DeleteObject(IntPtr obj);
private void button1_Click(object sender, EventArgs e)
{
Bitmap sourceBitmap = new Bitmap(64, 64, PixelFormat.Format32bppRgb);
Graphics sourceGraphics = Graphics.FromImage(sourceBitmap);
Bitmap destBitmap = new Bitmap(64, 64, PixelFormat.Format32bppRgb);
Graphics destGraphics = Graphics.FromImage(destBitmap);
sourceGraphics.FillRectangle(new SolidBrush(Color.Red), new Rectangle(0, 0, 30, 30));
sourceGraphics.FillRectangle(new SolidBrush(Color.Green), new Rectangle(30, 30, 30, 30));
destGraphics.FillRectangle(new SolidBrush(Color.Blue), new Rectangle(0, 0, 30, 30));
destGraphics.FillRectangle(new SolidBrush(Color.Yellow), new Rectangle(30, 30, 30, 30));
IntPtr destDC = destGraphics.GetHdc();
IntPtr destCDC = CreateCompatibleDC(destDC);
IntPtr destHB = destBitmap.GetHbitmap();
IntPtr oldDest = SelectObject(destCDC, destHB);
IntPtr sourceDC = sourceGraphics.GetHdc();
IntPtr sourceCDC = CreateCompatibleDC(sourceDC);
IntPtr sourceHB = sourceBitmap.GetHbitmap();
IntPtr oldSource = SelectObject(sourceCDC, sourceHB);
int success = BitBlt(
destDC, 0, 0, 64, 64, sourceCDC, 0, 0, SRCCOPY
);
SelectObject(destCDC, oldDest);
SelectObject(sourceCDC, oldSource);
DeleteObject(destCDC);
DeleteObject(sourceCDC);
DeleteObject(destHB);
DeleteObject(sourceHB);
destGraphics.ReleaseHdc();
sourceGraphics.ReleaseHdc();
pictureBox1.Image = sourceBitmap;
pictureBox2.Image = destBitmap;
}
Anyone have any insight as to why this combo works?...

Related

PrintScreen and BitBlt causing screen flicker

I'm trying to take a screenshot of another window specified by a valid hwnd. The procedure works, however the window flickers when the screenshot is taken.
The first function uses PrintScreen, the second BitBlt. Each is called from a routine that takes a screenshot every 10 seconds.
Is there a way to avoid a flicker when using the PrintScreen or BitBlt functions?
Any help would be appreciated.
public Bitmap GetScreenshot(IntPtr ihandle)
{
IntPtr hwnd = ihandle;//handle here
RECT rc;
FocusWindow(ihandle);
GetWindowRect(hwnd, out rc);
Bitmap bmp = new Bitmap(rc.Right - rc.Left, rc.Bottom - rc.Top, PixelFormat.Format32bppArgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap;
try
{
hdcBitmap = gfxBmp.GetHdc();
}
catch
{
return null;
}
bool succeeded = PrintWindow(hwnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
if (!succeeded)
{
gfxBmp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(Point.Empty, bmp.Size));
}
IntPtr hRgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hwnd, hRgn);
Region region = Region.FromHrgn(hRgn);//err here once
if (!region.IsEmpty(gfxBmp))
{
gfxBmp.ExcludeClip(region);
gfxBmp.Clear(Color.Transparent);
}
gfxBmp.Dispose();
return bmp;
}
public Bitmap CaptureWindowImage(IntPtr hWnd) //, System.Drawing.Rectangle wndRect)
{
IntPtr hWndDc = GetDC(hWnd);
IntPtr hMemDc = CreateCompatibleDC(hWndDc);
RECT rc;
GetWindowRect(hWnd, out rc);
IntPtr hBitmap = CreateCompatibleBitmap(hWndDc, rc.Width, rc.Height);
SelectObject(hMemDc, hBitmap);
BitBlt(hMemDc, 0, 0, rc.Width, rc.Height, hWndDc, 0, 0, TernaryRasterOperations.SRCCOPY);
Bitmap bitmap = Bitmap.FromHbitmap(hBitmap);
DeleteObject(hBitmap);
ReleaseDC(hWnd, hWndDc);
ReleaseDC(IntPtr.Zero, hMemDc);
DeleteDC(hMemDc);
return bitmap;
}

Creating Smooth Rounded Corners in WinForm Applications

I want to create a button and a container with rounded corners.
I'm using the Region to paint the corners, code attached below.
However the corners doesn't seem smooth, is there any way to fix this, any help would be appreciated.
Image attached below as im not allowed to upload images yet.
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern IntPtr CreateRoundRectRgn(
int nLeftRect,
int nTopRect,
int nRightRect,
int nBottomRect,
int nWidthEllipse,
int nHeightEllipse
);
public Login()
{
InitializeComponent();
this.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 30, 30));
this.logo.Image = Properties.Resources.logo;
this.btn_login.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, this.btn_login.Width, this.btn_login.Height, 10, 10));
}
Once the function is not implemented using the normal WinForm function.
Therefore we must implement it using win32Api.
The code is created referring to this and this.
First, you have to draw a round rectangle.
public static GraphicsPath RoundedRect(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
public static void FillRoundedRectangle(Graphics graphics, Brush brush, Rectangle bounds, int cornerRadius)
{
if (graphics == null)
throw new ArgumentNullException("graphics");
if (brush == null)
throw new ArgumentNullException("brush");
using (GraphicsPath path = RoundedRect(bounds, cornerRadius))
{
graphics.FillPath(brush, path);
}
}
And let's add it to the drawing call.
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = e.Graphics;
Rectangle gradientRectangle = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Brush b = new LinearGradientBrush(gradientRectangle, Color.DarkSlateBlue, Color.MediumPurple, 0.0f);
graphics.SmoothingMode = SmoothingMode.HighQuality;
FillRoundedRectangle(graphics, b, gradientRectangle, 35);
}
Then we can draw the same form as the picture above.
Second, draw a form using Per Pixel Alpha Blend.
public void SetBitmap(Bitmap bitmap)
{
SetBitmap(bitmap, 255);
}
public void SetBitmap(Bitmap bitmap, byte opacity)
{
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");
IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
try
{
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
oldBitmap = Win32.SelectObject(memDc, hBitmap);
Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
Win32.Point pointSource = new Win32.Point(0, 0);
Win32.Point topPos = new Win32.Point(Left, Top);
Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
blend.BlendOp = Win32.AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = opacity;
blend.AlphaFormat = Win32.AC_SRC_ALPHA;
Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
Win32.SelectObject(memDc, oldBitmap);
Win32.DeleteObject(hBitmap);
}
Win32.DeleteDC(memDc);
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00080000;
return cp;
}
}
Finally, call SetBitmap when loading a form.
private void Form1_Load(object sender, EventArgs e)
{
Bitmap myBitmap = new Bitmap(this.Width, this.Height);
Graphics graphics = Graphics.FromImage(myBitmap);
Rectangle gradientRectangle = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Brush b = new LinearGradientBrush(gradientRectangle, Color.DarkSlateBlue, Color.MediumPurple, 0.0f);
graphics.SmoothingMode = SmoothingMode.HighQuality;
FillRoundedRectangle(graphics, b, gradientRectangle, 35);
SetBitmap(myBitmap);
}
When you finish the above tasks, you can finally get Smooth Round Corners in WinForm Applications.
Full code of Form
public class RoundedForm : Form
{
private Timer drawTimer = new Timer();
public NanoRoundedForm()
{
this.FormBorderStyle = FormBorderStyle.None;
}
protected override void OnLoad(EventArgs e)
{
if (!DesignMode)
{
drawTimer.Interval = 1000 / 60;
drawTimer.Tick += DrawForm;
drawTimer.Start();
}
base.OnLoad(e);
}
private void DrawForm(object pSender, EventArgs pE)
{
using (Bitmap backImage = new Bitmap(this.Width, this.Height))
{
using (Graphics graphics = Graphics.FromImage(backImage))
{
Rectangle gradientRectangle = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
using (Brush b = new LinearGradientBrush(gradientRectangle, Color.DarkSlateBlue, Color.MediumPurple, 0.0f))
{
graphics.SmoothingMode = SmoothingMode.HighQuality;
RoundedRectangle.FillRoundedRectangle(graphics, b, gradientRectangle, 35);
foreach (Control ctrl in this.Controls)
{
using (Bitmap bmp = new Bitmap(ctrl.Width, ctrl.Height))
{
Rectangle rect = new Rectangle(0, 0, ctrl.Width, ctrl.Height);
ctrl.DrawToBitmap(bmp, rect);
graphics.DrawImage(bmp, ctrl.Location);
}
}
PerPixelAlphaBlend.SetBitmap(backImage, Left, Top, Handle);
}
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (DesignMode)
{
Graphics graphics = e.Graphics;
Rectangle gradientRectangle = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
Brush b = new LinearGradientBrush(gradientRectangle, Color.DarkSlateBlue, Color.MediumPurple, 0.0f);
graphics.SmoothingMode = SmoothingMode.HighQuality;
RoundedRectangle.FillRoundedRectangle(graphics, b, gradientRectangle, 35);
}
base.OnPaint(e);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
if (!DesignMode)
{
cp.ExStyle |= 0x00080000;
}
return cp;
}
}
}
public static class RoundedRectangle
{
public static GraphicsPath RoundedRect(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
public static void FillRoundedRectangle(Graphics graphics, Brush brush, Rectangle bounds, int cornerRadius)
{
if (graphics == null)
throw new ArgumentNullException("graphics");
if (brush == null)
throw new ArgumentNullException("brush");
using (GraphicsPath path = RoundedRect(bounds, cornerRadius))
{
graphics.FillPath(brush, path);
}
}
}
internal static class PerPixelAlphaBlend
{
public static void SetBitmap(Bitmap bitmap, int left, int top, IntPtr handle)
{
SetBitmap(bitmap, 255, left, top, handle);
}
public static void SetBitmap(Bitmap bitmap, byte opacity, int left, int top, IntPtr handle)
{
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");
IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
try
{
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
oldBitmap = Win32.SelectObject(memDc, hBitmap);
Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
Win32.Point pointSource = new Win32.Point(0, 0);
Win32.Point topPos = new Win32.Point(left, top);
Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
blend.BlendOp = Win32.AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = opacity;
blend.AlphaFormat = Win32.AC_SRC_ALPHA;
Win32.UpdateLayeredWindow(handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
Win32.SelectObject(memDc, oldBitmap);
Win32.DeleteObject(hBitmap);
}
Win32.DeleteDC(memDc);
}
}
}
internal class Win32
{
public enum Bool
{
False = 0,
True
};
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public Int32 x;
public Int32 y;
public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }
}
[StructLayout(LayoutKind.Sequential)]
public struct Size
{
public Int32 cx;
public Int32 cy;
public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ARGB
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
public const Int32 ULW_COLORKEY = 0x00000001;
public const Int32 ULW_ALPHA = 0x00000002;
public const Int32 ULW_OPAQUE = 0x00000004;
public const byte AC_SRC_OVER = 0x00;
public const byte AC_SRC_ALPHA = 0x01;
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", ExactSpelling = true)]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteObject(IntPtr hObject);
}

How can i get pixels from a specific area of the screen in C# very fast?

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.

Drawing text to a Bitmap with TextRenderer

I am trying to draw some text using TextRenderer (since this is favorable to using Graphics.DrawString) to a Bitmap, however it is having some very undesirable effects.
Example Code
using (Bitmap buffer = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (Graphics graphics = Graphics.FromImage(buffer))
{
// Produces the result below
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
// Produces clean text, but I'd really like ClearType!
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
TextRenderer.DrawText(graphics, "Hello World", this.Font, this.ClientRectangle, Color.Black);
}
e.Graphics.DrawImageUnscaled(buffer, this.ClientRectangle);
}
Result
I'm not sure exactly how to fix this...help!
I do NOT want to use Graphics.DrawString as I want correct GDI (as opposed to GDI+) rendering.
NOTE: I've just realized, I've left a gaping hole in this question. Some people have pointed out that rendering ClearType text is working fine on their machines...
I'm trying to render the text to a transparent (Color.Transparent) bitmap. If I do it with a solid color, everything works fine! (but it is imperative that I render to a transparent Bitmap).
Specify a BackColor in your call to DrawText():
TextRenderer.DrawText(graphics, "Hello World", this.Font, this.ClientRectangle, Color.Black, this.BackColor);
You can try setting TextRenderingHint for your Image Graphics:
using (Graphics graphics = Graphics.FromImage(buffer))
{
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
TextRenderer.DrawText(graphics, "Hello World", this.Font, this.ClientRectangle, Color.Black);
}
The issue is that TextRenderer uses GDI rendering that uses ClearType to render text, clear-type uses special anti-aliasing algorithm to smooth the text, unfortunately it doesn't work when you try to draw on bitmap device.
To make it work you have to use a trick, draw into in-memory and then copy to bitmap:
Create in-memory bitmap buffer that is compatible with display device context (IntPtr.Zero handle)
Fill the buffer background with solid color or image
Render the text into the memory bitmap
Copy from in-memory bitmap to image device context (BitBlt)
See this blog for details: GDI text rendering to image.
Sample code, sorry its a bit long:
public static class Test
{
public static Image Render()
{
// create the final image to render into
var image = new Bitmap(190, 30, PixelFormat.Format32bppArgb);
// create memory buffer from desktop handle that supports alpha channel
IntPtr dib;
var memoryHdc = CreateMemoryHdc(IntPtr.Zero, image.Width, image.Height, out dib);
try
{
// create memory buffer graphics to use for HTML rendering
using (var memoryGraphics = Graphics.FromHdc(memoryHdc))
{
// must not be transparent background
memoryGraphics.Clear(Color.White);
// execute GDI text rendering
TextRenderer.DrawText(memoryGraphics, "Test string 1", new Font("Arial", 12), new Point(5, 5), Color.Red, Color.Wheat);
TextRenderer.DrawText(memoryGraphics, "Test string 2", new Font("Arial", 12), new Point(100, 5), Color.Red);
}
// copy from memory buffer to image
using (var imageGraphics = Graphics.FromImage(image))
{
var imgHdc = imageGraphics.GetHdc();
BitBlt(imgHdc, 0, 0, image.Width, image.Height, memoryHdc, 0, 0, 0x00CC0020);
imageGraphics.ReleaseHdc(imgHdc);
}
}
finally
{
// release memory buffer
DeleteObject(dib);
DeleteDC(memoryHdc);
}
return image;
}
private static IntPtr CreateMemoryHdc(IntPtr hdc, int width, int height, out IntPtr dib)
{
// Create a memory DC so we can work off-screen
IntPtr memoryHdc = CreateCompatibleDC(hdc);
SetBkMode(memoryHdc, 1);
// Create a device-independent bitmap and select it into our DC
var info = new BitMapInfo();
info.biSize = Marshal.SizeOf(info);
info.biWidth = width;
info.biHeight = -height;
info.biPlanes = 1;
info.biBitCount = 32;
info.biCompression = 0; // BI_RGB
IntPtr ppvBits;
dib = CreateDIBSection(hdc, ref info, 0, out ppvBits, IntPtr.Zero, 0);
SelectObject(memoryHdc, dib);
return memoryHdc;
}
[DllImport("gdi32.dll")]
public static extern int SetBkMode(IntPtr hdc, int mode);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BitMapInfo pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
[DllImport("gdi32.dll")]
public static extern int SelectObject(IntPtr hdc, IntPtr hgdiObj);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool DeleteDC(IntPtr hdc);
[StructLayout(LayoutKind.Sequential)]
internal struct BitMapInfo
{
public int biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
public byte bmiColors_rgbBlue;
public byte bmiColors_rgbGreen;
public byte bmiColors_rgbRed;
public byte bmiColors_rgbReserved;
}
}

VisualStyleRenderer to bitmap

I need to draw a different progress bar through VisualStyleRenderer. Everything works fine if I use Graphics of OnPaint method. But since I want to save it in hard drive, I need to render progressbar in Bitmap object and then save it.
Here is example code
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawImage(RenderProgressbarImage(), new Point(5, 5));
//following code works good
progressRenderer.SetParameters("PROGRESS", 11, 2);
progressRenderer.DrawBackground(e.Graphics, new Rectangle(125, 5, 100, 13));
}
VisualStyleRenderer progressRenderer = new VisualStyleRenderer(VisualStyleElement.ProgressBar.Bar.Normal);
Bitmap RenderProgressbarImage()
{
Bitmap bmp = new Bitmap(100, 13);
using (Graphics g = Graphics.FromImage((Image)bmp))
{
progressRenderer.SetParameters("PROGRESS", 11, 2);
progressRenderer.DrawBackground(g, new Rectangle(0, 0, bmp.Width, bmp.Height));
}
return bmp;
}
But if I draw it in Bitmap, it have black corners instead of transparent. However if it uses Graphics of OnPaint, everything draws good.
Using Bitmap, you will a rectangular object using GDI+ the way you are doing it.
Creating an Image with Rounded Corners might help you with creating a rounded bitmap image as you'd like.
Edit - Modified RenderProgressbarImage to take a Graphics object as an input
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawImage(RenderProgressbarImage(e.Graphics), new Point(5, 5));
//Test to Check for Output
RenderProgressbarImage(e.Graphics).Save(#"C:\Bitmap.bmp");;
//following code works good
progressRenderer.SetParameters("PROGRESS", 11, 2);
progressRenderer.DrawBackground(e.Graphics, new Rectangle(125, 5, 100, 13));
}
Bitmap RenderProgressbarImage(Graphics g)
{
Bitmap bmp = new Bitmap(100, 13, g);
progressRenderer.SetParameters("PROGRESS", 11, 2);
progressRenderer.DrawBackground(g, new Rectangle(0, 0, bmp.Width, bmp.Height));
return bmp;
}
Edit2: Modified to simplify solution per OP's comment below
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Bitmap bmp = new Bitmap(100, 13, e.Graphics);
bmp.Save(<SomefilePath.png>);
//following code works good
progressRenderer.SetParameters("PROGRESS", 11, 2);
progressRenderer.DrawBackground(e.Graphics, new Rectangle(125, 5, 100, 13));
}
A note on this: doing a save of the Bitmap in the OnPaint event will be a definite performance hit on rendering. Perhaps just update a Bitmap variable in your class and save the Bitmap periodically from a different Thread/ some Timer/etc.; it all depends on your needs.
I know it's old but I faced the same problem and after a lot of research I found a solution, I hope it's help someone.
// Created by: Motaz Alnuweiri
// Reference:
// URL1: https://www.autoitscript.com/forum/topic/181956-drawthemebackground-bitmap-alpha/
// URL2: https://gist.github.com/wavescholar/11297223#file-gdi-bitmap-conversion-L71
// URL3: https://www.experts-exchange.com/questions/20872978/BITMAPINFOHEADER-from-NET-Bitmap.html
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
public class Helper
{
#region Win32 Native APIs
internal class NativeMethods
{
// CreateDIBSection funcation iUsage value
internal const int DIB_RGB_COLORS = 0x00;
internal const int DIB_PAL_COLORS = 0x01;
internal const int DIB_PAL_INDICES = 0x02;
[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
internal static extern bool DeleteObject(IntPtr hObject);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern int InvalidateRect(IntPtr hwnd, IntPtr rect, int bErase);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
internal static extern int DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint iUsage,
out IntPtr bits, IntPtr hSection, uint dwOffset);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
[StructLayout(LayoutKind.Sequential)]
internal struct BITMAPINFO
{
public Int32 biSize;
public Int32 biWidth;
public Int32 biHeight;
public Int16 biPlanes;
public Int16 biBitCount;
public Int32 biCompression;
public Int32 biSizeImage;
public Int32 biXPelsPerMeter;
public Int32 biYPelsPerMeter;
public Int32 biClrUsed;
public Int32 biClrImportant;
}
}
#endregion
public static Image VisualStyleRendererToImage(VisualStyleElement element, Rectangle bounds)
{
if (ToolStripManager.VisualStylesEnabled && VisualStyleRenderer.IsElementDefined(element))
{
VisualStyleRenderer renderer = new VisualStyleRenderer(element);
using (Bitmap bit = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb))
{
NativeMethods.BITMAPINFO bmi = new NativeMethods.BITMAPINFO();
bmi.biWidth = bit.Width;
bmi.biHeight = bit.Height;
bmi.biPlanes = 1;
bmi.biBitCount = 32;
bmi.biXPelsPerMeter = (int)bit.HorizontalResolution;
bmi.biYPelsPerMeter = (int)bit.VerticalResolution;
bmi.biSize = Marshal.SizeOf(typeof(NativeMethods.BITMAPINFO));
IntPtr bits;
IntPtr bmp = NativeMethods.CreateDIBSection(IntPtr.Zero, ref bmi,
NativeMethods.DIB_RGB_COLORS, out bits, IntPtr.Zero, 0);
IntPtr dc = NativeMethods.GetDC(IntPtr.Zero);
IntPtr hdc = NativeMethods.CreateCompatibleDC(dc);
NativeMethods.SelectObject(hdc, bmp);
using (Graphics g = Graphics.FromHdc(hdc))
{
renderer.DrawBackground(g, bounds);
}
Bitmap image = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppPArgb);
using (Bitmap tempImage = new Bitmap(bounds.Width, bounds.Height, bounds.Width * 4,
PixelFormat.Format32bppPArgb, bits))
{
BitmapData tempBitmapData = tempImage.LockBits(bounds, ImageLockMode.ReadOnly,
PixelFormat.Format32bppPArgb);
BitmapData bitmapData = image.LockBits(bounds, ImageLockMode.WriteOnly,
PixelFormat.Format32bppPArgb);
NativeMethods.CopyMemory(bitmapData.Scan0, tempBitmapData.Scan0,
(uint)tempBitmapData.Stride * (uint)tempBitmapData.Height);
tempImage.UnlockBits(tempBitmapData);
image.UnlockBits(bitmapData);
}
NativeMethods.DeleteObject(bmp);
NativeMethods.DeleteDC(hdc);
NativeMethods.ReleaseDC(IntPtr.Zero, dc);
return image;
}
}
else
{
return new Bitmap(bounds.Width, bounds.Height);
}
}
}
Reference:
URL1: https://www.autoitscript.com/forum/topic/181956-drawthemebackground-bitmap-alpha/
URL2: https://gist.github.com/wavescholar/11297223#file-gdi-bitmap-conversion-L71
URL3: https://www.experts-exchange.com/questions/20872978/BITMAPINFOHEADER-from-NET-Bitmap.html

Categories

Resources