Draw rectangle with native method fills with white background - c#

I am using the native methods to draw the rectangle in a form. when I using the graphics.DrawRectangle method the rectangle will draw properly. But when using the native method like below code, it always fills the white background inside the rectangle.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.BackColor = Color.Yellow;
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rect = new Rectangle(20,20,100,30);
e.Graphics.DrawRectangle(new Pen(Color.Red, 1), rect);
IntPtr hdc = e.Graphics.GetHdc();
DrawRectangle(hdc, Pens.Red, new Rectangle(25, 60, 100, 30));
e.Graphics.ReleaseHdc();
base.OnPaint(e);
}
public void DrawRectangle(IntPtr hdc, Pen pen, Rectangle rect)
{
IntPtr hpen = IntPtr.Zero;
try
{
hpen = CreatePen((int)pen.DashStyle, (int)pen.Width, (int)new RGB(pen.Color).ToInt32());
SelectObject(hdc, hpen);
RectangleCE(hdc, rect.Left, rect.Top, rect.Right, rect.Bottom);
}
finally
{
if (hpen != IntPtr.Zero)
DeleteObject(hpen);
}
}
[DllImport("gdi32")]
public static extern IntPtr CreatePen(int penStyle, int width, int color);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);
[DllImport("gdi32.dll", EntryPoint = "Rectangle", SetLastError = true)]
public static extern uint RectangleCE(IntPtr hdc, int leftRect, int topRect, int rightRect, int bottomRect);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int DeleteObject(IntPtr hObject);
/// <summary>
/// Selects a red, green, blue (RGB) color based on the arguments supplied and the color capabilities of the output device
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct RGB
{
/// <summary>
/// The reserved fields.
/// </summary>
private byte r, g, b, reserved;
/// <summary>
/// Initializes a new instance of the <see cref="RGB"/> struct.
/// </summary>
/// <param name="colorIn">The color value.</param>
public RGB(Color colorIn)
{
r = colorIn.R;
g = colorIn.G;
b = colorIn.B;
reserved = 0;
}
/// <summary>
/// Convert the RGB color value to integer value.
/// </summary>
/// <returns>Returns the converted value.</returns>
public int ToInt32()
{
var colors = new byte[4];
colors[0] = r;
colors[1] = g;
colors[2] = b;
colors[3] = reserved;
return BitConverter.ToInt32(colors, 0);
}
}
}
Please find the attached image, in that the form was filled by yellow color. The first one rectangle is drawing using the graphics.DrawRectangle method and the second one is using the above native method.
Please anyone suggest how to draw the rectangle without fill white background using the above native method?

Rectangle function will draws a rectangle with the current pen and also fills the interior with current brush.
https://msdn.microsoft.com/en-us/library/aa269219(v=vs.60).aspx
You can change the current brush using SelectObject like below. But setting Color.Transparent will be considered as White Color.
var yColor = (uint) new RGB(Color.Yellow).ToInt32();
SelectObject(hdc, CreateSolidBrush(yColor));
hpen = CreatePen((int)pen.DashStyle, (int)pen.Width, (uint)new RGB(pen.Color).ToInt32());
SelectObject(hdc, hpen);
RectangleCE(hdc, rect.Left, rect.Top, rect.Right, rect.Bottom);
If you don’t want to fill the interior, then you have to use the FrameRect function. But using this function, thickness of line always be 1. We can’t adjust it.
https://msdn.microsoft.com/en-us/library/dd144838(v=vs.85).aspx
var rect2 = new RECT(rect);
FrameRect(hdc, ref rect2, CreateSolidBrush((uint)new RGB(Color.Red).ToInt32()));

Related

How can I Use Win32 and GDI calls to write text characters to a bitmap

I'm working on a c# (.NET Framework) program that uses SharpGL. That library has methods to draw text to the OpenGL context using the wglUseFontBitmaps call from Win32; however, that method uses display lists that were deprecated in v3.0. I'd therefore like to find a way to use VBOs and VAOs to draw texts. However, the wglUseFontBitmaps method produces reasonably legible text even at font sizes of 10 or 12 (which I need).
I have tried to match that outcome using several approaches including .NET's GlyphTypeface.GetGlyphOutlines and SharpFont (which wraps FreeType). With both of those I tried rendering larger size fonts (without antialiasing) and letting OpenGL scale them to smaller sizes. I still can't get reliably good looking results that match wglUseFontBitmaps.
So, My current attempt is to use the Win32 GDI APIs to write the text, assuming it might produce similar results to wglUseFontBitmaps; however, I can't get the first step to work -- just writing a character into a bitmap.
Below I'm posting an entire c# program file. It can be complied as a .NET Framework Console application, but you must add a reference to System.Drawing and you must turn on "Allow unsafe code" in the Project's preferences under the Build tab.
Currently, it creates very odd bitmap files (and, by the way, it will write the test file called "TMP.BMP" into your desktop folder).
Here's the code -- a bit long but includes all you need to run the test:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace CharToBitmapConsoleTest
{
class Program
{
static void Main(string[] args)
{
var tester = new CharToBitmapTester();
tester.RunTests();
}
// Calls the TestWithCharacter method a few times for testing
public class CharToBitmapTester
{
public void RunTests()
{
var fontFamilyName = "Calibri";
var fontHeight = 14;
TestWithCharacter((int)'%', fontFamilyName, fontHeight);
TestWithCharacter((int)'#', fontFamilyName, fontHeight);
TestWithCharacter((int)'X', fontFamilyName, fontHeight);
TestWithCharacter((int)'H', fontFamilyName, fontHeight);
}
/// <summary>
/// Attempts to do every step needed to write a characte (corersponding to the given
/// unicode index) into a bitmap using the given font family name and font height.
/// The test returns true if any bits were written to memory as a result of the
/// attempt. The test also writes a bitmap file (TMP.BMP) to the Users's desktop.
/// </summary>
/// <param name="unicodeIndex"></param>
/// <param name="fontFamilyName"></param>
/// <param name="fontHeight"></param>
/// <returns></returns>
public bool TestWithCharacter(int unicodeIndex, string fontFamilyName, int fontHeight)
{
//var hDC = gl.RenderContextProvider.DeviceContextHandle;
// Get the desktop DC.
IntPtr desktopDC = WinGdi32.GetDC(IntPtr.Zero);
// Create our DC as a compatible DC for the desktop.
var hDC = WinGdi32.CreateCompatibleDC(desktopDC);
// Create the font handle (IntPtr) for the WinGDI font object
var hFont = WinGdi32.CreateFont(fontHeight, 0, 0, 0, WinGdi32.FW_DONTCARE, 0, 0, 0, WinGdi32.DEFAULT_CHARSET,
WinGdi32.OUT_OUTLINE_PRECIS, WinGdi32.CLIP_DEFAULT_PRECIS, WinGdi32.CLEARTYPE_QUALITY, WinGdi32.VARIABLE_PITCH, fontFamilyName);
// Select the font object into the Device Context
// GDI actions will use hFont as the current font object
WinGdi32.SelectObject(hDC, hFont);
// Get the true widths for the glyph placement of all the characters
var charWidthInfoArray = new WinGdi32.ABCFLOAT[256];
WinGdi32.GetCharABCWidthsFloat(hDC, 0, 255, charWidthInfoArray);
char character = (char)unicodeIndex;
string characterAsString = character.ToString();
var characterWidthInfo = charWidthInfoArray[unicodeIndex];
var characterFullWidth = characterWidthInfo.abcfA + characterWidthInfo.abcfB + characterWidthInfo.abcfC;
var glyphUnitWidth = (int)Math.Ceiling(characterWidthInfo.abcfB);
var glyphUnitHeight = (int)fontHeight;
//*************************************************************************************
// Create a DIBSection
//
// Start with the BITMAPINFO
var bitCount = 24;// 32;
var info = new WinGdi32.BITMAPINFO();
// Set the data.
info.biSize = Marshal.SizeOf(info);
info.biBitCount = (short)bitCount;
info.biPlanes = 1;
info.biWidth = glyphUnitWidth;
info.biHeight = glyphUnitHeight;
IntPtr bits;
// Create the bitmap.
var hBitmap = WinGdi32.CreateDIBSection(hDC, ref info, WinGdi32.DIB_RGB_COLORS, out bits, IntPtr.Zero, 0);
WinGdi32.SelectObject(hDC, hBitmap);
// Set the pixel format.
var pixelFormat = new WinGdi32.PIXELFORMATDESCRIPTOR();
pixelFormat.Init();
pixelFormat.nVersion = 1;
pixelFormat.dwFlags = (WinGdi32.PFD_DRAW_TO_BITMAP | WinGdi32.PFD_SUPPORT_OPENGL | WinGdi32.PFD_SUPPORT_GDI);
pixelFormat.iPixelType = WinGdi32.PFD_TYPE_RGBA;
pixelFormat.cColorBits = (byte)bitCount;
pixelFormat.cDepthBits = (byte)bitCount;
pixelFormat.iLayerType = WinGdi32.PFD_MAIN_PLANE;
// Try to match a pixel format and note failure if we get an error
int iPixelformat;
if ((iPixelformat = WinGdi32.ChoosePixelFormat(hDC, pixelFormat)) == 0)
return false;
// Sets pixel format and test for errors
if (WinGdi32.SetPixelFormat(hDC, iPixelformat, pixelFormat) == 0)
{
// Falure -- clear error and retur nfalse
int _ = Marshal.GetLastWin32Error();
return false;
}
// Done Creating a DIBSection
// If I understand correctly, the hDC now has the DIBSction as the current object and
// calls related to drawing should go to it (and, I belive, fill our "bits" buffer)
//*************************************************************************************
// Set a location to output the text -- not really sure what to use here but going with 0, 0
int x = 0;
int y = 9;
// Could play around with foreground and background colors...
//var prevFgColorRef = WinGdi32.SetTextColor(hDC, ColorTranslator.ToWin32(System.Drawing.Color.White));
//var prevBkColorRef = WinGdi32.SetBkColor(hDC, ColorTranslator.ToWin32(System.Drawing.Color.Black));
// NOTE: we've already set hFont as the current font and hBitmap as the current bitmap...
// Output the text -- this should go to the current bitmap and fill the bits buffer, right?
var textOutWorked = WinGdi32.TextOut(hDC, x, y, characterAsString.ToString(), 1);
if (textOutWorked)
{
System.Diagnostics.Debug.WriteLine("TextOut finished without complaint");
}
else
{
System.Diagnostics.Debug.WriteLine("TextOut says it did NOT work");
return false;
}
var dibSectionSize = glyphUnitWidth * glyphUnitHeight * bitCount;
var testArray = new byte[dibSectionSize];
Marshal.Copy(bits, testArray, 0, dibSectionSize);
var bitsWithData = 0;
foreach (var b in testArray)
{
if (b != 0)
{
bitsWithData++;
}
}
System.Diagnostics.Debug.WriteLine(bitsWithData > 0 ?
$"Test Wrote something to the bits! Font {fontFamilyName}; Character: {characterAsString}!" :
$"Test did NOT write to the bits! Font {fontFamilyName}; Character: {characterAsString}!");
var stride = bitCount * glyphUnitWidth;
using (Bitmap bitmap = new Bitmap(glyphUnitWidth, glyphUnitHeight, stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, bits))
{
bitmap.Save(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TMP.BMP"));
}
return bitsWithData > 0;
}
}
public static class WinGdi32
{
public const string Gdi32 = "gdi32.dll";
public const string User32 = "user32.dll";
/// <summary>
/// The TextOut function writes a character string at the specified location, using the currently selected font, background color, and text color
/// </summary>
/// <param name="hDC">A handle to the device context.</param>
/// <param name="x">The x-coordinate, in logical coordinates, of the reference point that the system uses to align the string.</param>
/// <param name="y">The y-coordinate, in logical coordinates, of the reference point that the system uses to align the string.</param>
/// <param name="str">The string to be drawn. The string does not need to be zero-terminated, because cchString specifies the length of the string.</param>
/// <param name="c">The length of the string in characters.</param>
/// <returns></returns>
[DllImport(Gdi32, SetLastError = true)]
public static extern bool TextOut(IntPtr hDC, int x, int y, [MarshalAs(UnmanagedType.LPStr)] string str, int c);
/// <summary>
/// The GetCharABCWidthsFloat function retrieves the widths, in logical units, of consecutive characters in a specified range from the current font.
/// </summary>
/// <param name="hDC">Handle to the device context.</param>
/// <param name="iFirstChar">Specifies the code point of the first character in the group of consecutive characters where the ABC widths are seeked.</param>
/// <param name="iLastChar">Specifies the code point of the last character in the group of consecutive characters where the ABC widths are seeked. This range is inclusive. An error is returned if the specified last character precedes the specified first character</param>
/// <param name="ABCF">An array of ABCFLOAT structures that receives the character widths, in logical units</param>
/// <returns></returns>
[DllImport(Gdi32, SetLastError = true)]
public static extern bool GetCharABCWidthsFloat(IntPtr hDC, uint iFirstChar, uint iLastChar, [Out, MarshalAs(UnmanagedType.LPArray)] ABCFLOAT[] ABCF);
[DllImport(Gdi32, SetLastError = true)]
public static extern IntPtr SetTextColor(IntPtr hDC, int crColor);
[DllImport(Gdi32, SetLastError = true)]
public static extern IntPtr SetBkColor(IntPtr hDC, int crColor);
[DllImport(Gdi32, SetLastError = true)]
public unsafe static extern int ChoosePixelFormat(IntPtr hDC, [In, MarshalAs(UnmanagedType.LPStruct)] PIXELFORMATDESCRIPTOR ppfd);
[DllImport(Gdi32, SetLastError = true)]
public static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BITMAPINFO pbmi, uint pila, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
[DllImport(Gdi32, SetLastError = true)]
public unsafe static extern int SetPixelFormat(IntPtr hDC, int iPixelFormat, [In, MarshalAs(UnmanagedType.LPStruct)] PIXELFORMATDESCRIPTOR ppfd);
[DllImport(User32, SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport(Gdi32, SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport(Gdi32, SetLastError = true)]
public static extern IntPtr CreateFont(int nHeight, int nWidth, int nEscapement,
int nOrientation, uint fnWeight, uint fdwItalic, uint fdwUnderline, uint fdwStrikeOut,
uint fdwCharSet, uint fdwOutputPrecision, uint fdwClipPrecision, uint fdwQuality,
uint fdwPitchAndFamily, string lpszFace);
[DllImport(Gdi32, SetLastError = true)]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
/// <summary>
/// The SIZE structure specifies the width and height of a rectangle.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class SIZE
{
/// <summary>
/// Specifies the rectangle's width. The units depend on which function uses this.
/// </summary>
public long cx;
/// <summary>
/// Specifies the rectangle's height. The units depend on which function uses this.
/// </summary>
public long cy;
}
/// <summary>
/// The ABCFLOAT structure contains the A, B, and C widths of a font character.
/// </summary>
public struct ABCFLOAT
{
/// <summary>
/// The A spacing of the character. The A spacing is the distance to add to the current position before drawing the character glyph.
/// </summary>
public float abcfA;
/// <summary>
/// The B spacing of the character. The B spacing is the width of the drawn portion of the character glyph.
/// </summary>
public float abcfB;
/// <summary>
/// The C spacing of the character. The C spacing is the distance to add to the current position to provide white space to the right of the character glyph.
/// </summary>
public float abcfC;
}
[StructLayout(LayoutKind.Sequential)]
public 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;
public void Init()
{
biSize = Marshal.SizeOf(this);
}
}
[StructLayout(LayoutKind.Explicit)]
public class PIXELFORMATDESCRIPTOR
{
[FieldOffset(0)]
public UInt16 nSize;
[FieldOffset(2)]
public UInt16 nVersion;
[FieldOffset(4)]
public UInt32 dwFlags;
[FieldOffset(8)]
public Byte iPixelType;
[FieldOffset(9)]
public Byte cColorBits;
[FieldOffset(10)]
public Byte cRedBits;
[FieldOffset(11)]
public Byte cRedShift;
[FieldOffset(12)]
public Byte cGreenBits;
[FieldOffset(13)]
public Byte cGreenShift;
[FieldOffset(14)]
public Byte cBlueBits;
[FieldOffset(15)]
public Byte cBlueShift;
[FieldOffset(16)]
public Byte cAlphaBits;
[FieldOffset(17)]
public Byte cAlphaShift;
[FieldOffset(18)]
public Byte cAccumBits;
[FieldOffset(19)]
public Byte cAccumRedBits;
[FieldOffset(20)]
public Byte cAccumGreenBits;
[FieldOffset(21)]
public Byte cAccumBlueBits;
[FieldOffset(22)]
public Byte cAccumAlphaBits;
[FieldOffset(23)]
public Byte cDepthBits;
[FieldOffset(24)]
public Byte cStencilBits;
[FieldOffset(25)]
public Byte cAuxBuffers;
[FieldOffset(26)]
public SByte iLayerType;
[FieldOffset(27)]
public Byte bReserved;
[FieldOffset(28)]
public UInt32 dwLayerMask;
[FieldOffset(32)]
public UInt32 dwVisibleMask;
[FieldOffset(36)]
public UInt32 dwDamageMask;
public void Init()
{
nSize = (ushort)Marshal.SizeOf(this);
}
}
public const uint FW_DONTCARE = 0;
public const uint ANSI_CHARSET = 0;
public const uint DEFAULT_CHARSET = 1;
public const uint SYMBOL_CHARSET = 2;
public const uint OUT_OUTLINE_PRECIS = 8;
public const uint CLIP_DEFAULT_PRECIS = 0;
public const uint CLEARTYPE_QUALITY = 5;
public const uint FIXED_PITCH = 1;
public const uint VARIABLE_PITCH = 2;
public const uint DIB_RGB_COLORS = 0;
public const uint PFD_DRAW_TO_BITMAP = 8;
public const uint PFD_SUPPORT_GDI = 16;
public const uint PFD_SUPPORT_OPENGL = 32;
public const byte PFD_TYPE_RGBA = 0;
public const sbyte PFD_MAIN_PLANE = 0;
}
}
}
If anyone can tell me how to get the code to actually write individual characters to a bitmap, I should be able to take it from there and use it in my OpenGL project.
Thank you!
Just to show, this would be a sample using GDI+:
var text = "Hello world!";
var font = new Font("Calibri", 8);
var bitmap = new Bitmap(200, 100);
var targetRectangle = new RectangleF(0, 0, 200, 120);
var sf = new StringFormat(StringFormat.GenericDefault);
sf.SetMeasurableCharacterRanges(
Enumerable.Range(0, text.Length)
.Select(i => new CharacterRange(i, 1)).ToArray());
using (var gr = Graphics.FromImage(bitmap))
{
gr.Clear(Color.Black);
gr.DrawString(text, font, Brushes.White, targetRectangle, sf);
var ranges = gr.MeasureCharacterRanges(text, font, targetRectangle, sf);
gr.DrawRectangles(Pens.Red, ranges.Select(i => i.GetBounds(gr)).ToArray());
}
bitmap.Dump();
This produces a bitmap containing the text "Hello world!" along with the area of each of the characters (though those are not actually the bounds of the characters - careful with accents). This allows you to create an atlas of characters for rendering easily (you'd want more spacing between the characters to deal with the overlap, of course).
This produces legible text down to about 6 emsize (8 is much better, though), but that does require sub-pixel rendering, which means this is only suitable when you don't need to scale the font (e.g. for UI). There's many ways to tweak this - the defaults are a compromise between rendering speed and quality. For example, using gr.PixelOffsetMode = PixelOffsetMode.Half; will give you much sharper and nicer text (especially for such a tiny font size in a font not optimised for small size or minimal aliasing) by aligning the characters better (at the cost of being less "pixel perfect"). By the time you get to emsize around 12, the artefacts tend to be mostly invisible even with a bit of scaling (provided you configured ClearType properly). Of course, the results depend on the geometry of the pixels of your display - they're not portable. Both GDI and GDI+ are primarily meant to be used for presentation, not to produce device independent graphics. You'd probably want to use specialised software for that.
You can also remove all hinting using gr.TextRenderingHint = TextRenderingHint.SingleBitPerPixel;. This will tend to produce poor text rendering with modern fonts; it really needs a font optimized for rendering without sub-pixels. The main advantage is that it will look the same regardless of your hardware - but it tends to look very bad.

SetTextCharacterExtra function is not working with DrawText function for specific fonts/font size

I am printing texts using wingdi functions with setting character spacing by SetTextCharacterExtra function. I am using TextRenderer.DrawText method to draw texts which uses wingdi functions internally. My problem is, character spacing is not working for some texts - I noticed that it does not work for bigger font sizes. I have also tried DrawText function directly instead of TextRenderer.DrawText but still no luck.
This is the output where character spacing is not applied to Special text. I have tried with different spacing(-2, -3, -4, -5) but no luck. I have changed font type as well but still the same output. If you notice character spacing is applied properly for other texts like Huggies Infant Boy 96pk, and Was $32.00, and others.
Does anyone know what is going on here?
UPDATE:
Here is the sample code to reproduce this issue:
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DrawText(IntPtr hdc, string text, int length, ref RECT rect, int formats);
[DllImport("Gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool ExtTextOut(IntPtr hdc, int x, int y, uint options, IntPtr lprect, StringBuilder lpString, uint c, IntPtr lpDx);
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
internal static extern int SetTextCharacterExtra(IntPtr hdc, int nCharExtra);
public const int ETO_OPAQUE = 0x0002;
static void PrintByTextRenderer()
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler(delegate (Object sender, PrintPageEventArgs e)
{
var str = "Drawing Text with Avenir Black";
var font = new Font("Avenir Black", 140, new FontStyle()); // Calibri
var rect = new Rectangle(200, 20, 4000, 400);
IntPtr hPrinterDC = e.Graphics.GetHdc();
if (hPrinterDC != IntPtr.Zero)
{
var nCharExtra = -5;
SetTextCharacterExtra(hPrinterDC, nCharExtra);
RECT rc = new RECT(rect.X, rect.Y, rect.Right, rect.Bottom);
//// METHOD 1 - winapi fn : DrawText : Intercharacter Spacing not working
//var flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.WordBreak | TextFormatFlags.NoClipping;
//DrawText(hPrinterDC, str, str.Length, ref rc, (int)flags);
//// METHOD 2 - winapi fn : ExtTextOut : Intercharacter Spacing is working properly
//ExtTextOut(hPrinterDC, rc.left, rc.top, ETO_OPAQUE, IntPtr.Zero, new StringBuilder(str), (uint)str.Length, IntPtr.Zero);
e.Graphics.ReleaseHdc(hPrinterDC);
//// METHOD 3 - TextRenderer.DrawText : Intercharacter Spacing not working
TextRenderer.DrawText(e.Graphics, str, font, rect, Color.Black);
}
});
pd.Print();
}

An image who is sometimes rendered, sometimes not

I'm creating a console app game and I would like to display some images on the screen. So, I found some tricks on google and I created that script to display an image :
[DllImport("user32.dll")] public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)] public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow", SetLastError = true)] private static extern IntPtr GetConsoleHandle();
private static void ShowImage(string filePath, int posX, int posY)
{
Image img = Image.FromFile(filePath);
var form = new Form
{
FormBorderStyle = FormBorderStyle.None
};
var parent = GetConsoleHandle();
var child = form.Handle;
SetParent(child, parent);
MoveWindow(child, 50, 50, img.Width, img.Height, true);
form.Paint += delegate (object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle rc = new Rectangle(new Point(0, 0), img.Size);
g.DrawImage(img, form.ClientRectangle, rc, GraphicsUnit.Pixel);
};
Application.Run(form);
}
My problem is that, sometimes, I don't know why, my image doesn't appear ! And it's random, not 1 time on 2 or something like that !
Note : I use this code to display a png image
So, if someone know were is the problem, I'm ready to take notes :)

Easiest way to select screen region

I'm writing a simple program in C# + WPF that records/serializes user mouse and keyboard activities and then he can playback it.
Some of its features require user to specify coordinates of a small area in the screen. Typing coordinates of top left and bottom right corners to textboxes is not very comfortable so I want to select it in similar way like Windows Snipping Tool, ZScreen/ShareX etc. do that.
Something like that, maybe simplified
What is the easiest way to implement that?
I've googled a lot but didn't find anything simple and clear. http://cropper.codeplex.com/ source code looks like too complicated and I don't need image, only coordinates relative to screen.
You can try with this static class CaptureScreen and Capture method
static public class CaptureScreen
{
public static BitmapSource Capture(Rect area)
{
IntPtr screenDC = GetDC(IntPtr.Zero);
IntPtr memDC = CreateCompatibleDC(screenDC);
IntPtr hBitmap = CreateCompatibleBitmap(screenDC, (int)SystemParameters.VirtualScreenWidth, (int)SystemParameters.VirtualScreenHeight);
SelectObject(memDC, hBitmap); // Select bitmap from compatible bitmap to memDC
// TODO: BitBlt may fail horribly
BitBlt(memDC, 0, 0, (int)area.Width, (int)area.Height, screenDC, (int)area.X, (int)area.Y, TernaryRasterOperations.SRCCOPY);
BitmapSource bsource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap);
ReleaseDC(IntPtr.Zero, screenDC);
ReleaseDC(IntPtr.Zero, memDC);
return bsource;
}
#region WINAPI DLL Imports
[DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateBitmap(int nWidth, int nHeight, uint cPlanes, uint cBitsPerPel, IntPtr lpvBits);
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
private enum TernaryRasterOperations : uint
{
/// <summary>dest = source</summary>
SRCCOPY = 0x00CC0020,
/// <summary>dest = source OR dest</summary>
SRCPAINT = 0x00EE0086,
/// <summary>dest = source AND dest</summary>
SRCAND = 0x008800C6,
/// <summary>dest = source XOR dest</summary>
SRCINVERT = 0x00660046,
/// <summary>dest = source AND (NOT dest)</summary>
SRCERASE = 0x00440328,
/// <summary>dest = (NOT source)</summary>
NOTSRCCOPY = 0x00330008,
/// <summary>dest = (NOT src) AND (NOT dest)</summary>
NOTSRCERASE = 0x001100A6,
/// <summary>dest = (source AND pattern)</summary>
MERGECOPY = 0x00C000CA,
/// <summary>dest = (NOT source) OR dest</summary>
MERGEPAINT = 0x00BB0226,
/// <summary>dest = pattern</summary>
PATCOPY = 0x00F00021,
/// <summary>dest = DPSnoo</summary>
PATPAINT = 0x00FB0A09,
/// <summary>dest = pattern XOR dest</summary>
PATINVERT = 0x005A0049,
/// <summary>dest = (NOT dest)</summary>
DSTINVERT = 0x00550009,
/// <summary>dest = BLACK</summary>
BLACKNESS = 0x00000042,
/// <summary>dest = WHITE</summary>
WHITENESS = 0x00FF0062
}
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
#endregion
}

C# Move bitmap in picturebox

I create image with LockBits from array in cycle and scale to PictureBox.Width * n and Height:
using (var bmp = new Bitmap(len, _height, PixelFormat.Format24bppRgb))
{
var data = bmp.LockBits(new Rectangle(0, 0, len, _height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
var bytes = data.Stride * data.Height;
var rgb = new byte[bytes];
var ptr = data.Scan0;
Marshal.Copy(data.Scan0, rgb, 0, bytes);
// …fill array „rgb“
Marshal.Copy(rgb, 0, ptr, bytes);
bmp.UnlockBits(data);
g = _pictureBox.CreateGraphics();
g.InterpolationMode = InterpolationMode.Default;
g.DrawImage(bmp, _pictureBox.Width - len * _scaleWidth, 0, len * _scaleWidth, _pictureBox.Height);
}
In the next iterateration:
Graphics g;
using (var bmp = new Bitmap(_pictureBox.Image))
{
g = _pictureBox.CreateGraphics();
g.InterpolationMode = InterpolationMode.Default;
g.DrawImage(_pictureBox.Image, new RectangleF(0, 0, _pictureBox.Width - len * _scaleWidth, _pictureBox.Height), new RectangleF(len * _scaleWidth, 0, _pictureBox.Width * _scaleWidth - len, _height), GraphicsUnit.Pixel);
}
g.Dispose();
In short: I cut and copy part of the image that would shift the picture, but do not get anything. Is it possible because of the scale in the previous step?
Maybe I'm wrong. Advise the algorithm for shifting and adding a new Bitmap into an end.
I advice you to use Control.CreateGraphics() method at Form instance, and write on a Form directly, because PaintBox control is quite slow.
Try using my helper function, this will allow you to paste a portion of your bitmap using StretchBlt (stretching) or BitBlt (no stretching) using Win32 Interop:
Sample usage:
Graphics graphics = ...;
graphics.GdiDrawImage
(
image,
new Rectangle(
(int)rectangle.Left,
(int)rectangle.Top,
(int)rectangle.Width,
(int)rectangle.Height
),
0, 0, image.Width, image.Height
);
Source code:
public static class GraphicsHelper
{
public static void GdiDrawImage(this Graphics graphics, Bitmap image, Rectangle rectangleDst, int nXSrc, int nYSrc, int nWidth, int nHeight)
{
IntPtr hdc = graphics.GetHdc();
IntPtr memdc = GdiInterop.CreateCompatibleDC(hdc);
IntPtr bmp = image.GetHbitmap();
GdiInterop.SelectObject(memdc, bmp);
GdiInterop.SetStretchBltMode(hdc, 0x04);
GdiInterop.StretchBlt(hdc, rectangleDst.Left, rectangleDst.Top, rectangleDst.Width, rectangleDst.Height, memdc, nXSrc, nYSrc, nWidth, nHeight, GdiInterop.TernaryRasterOperations.SRCCOPY);
//GdiInterop.BitBlt(..) put it here, if you did not mention stretching the source image
GdiInterop.DeleteObject(bmp);
GdiInterop.DeleteDC(memdc);
graphics.ReleaseHdc(hdc);
}
}
public class GdiInterop
{
/// <summary>
/// Enumeration for the raster operations used in BitBlt.
/// In C++ these are actually #define. But to use these
/// constants with C#, a new enumeration _type is defined.
/// </summary>
public enum TernaryRasterOperations
{
SRCCOPY = 0x00CC0020, // dest = source
SRCPAINT = 0x00EE0086, // dest = source OR dest
SRCAND = 0x008800C6, // dest = source AND dest
SRCINVERT = 0x00660046, // dest = source XOR dest
SRCERASE = 0x00440328, // dest = source AND (NOT dest)
NOTSRCCOPY = 0x00330008, // dest = (NOT source)
NOTSRCERASE = 0x001100A6, // dest = (NOT src) AND (NOT dest)
MERGECOPY = 0x00C000CA, // dest = (source AND pattern)
MERGEPAINT = 0x00BB0226, // dest = (NOT source) OR dest
PATCOPY = 0x00F00021, // dest = pattern
PATPAINT = 0x00FB0A09, // dest = DPSnoo
PATINVERT = 0x005A0049, // dest = pattern XOR dest
DSTINVERT = 0x00550009, // dest = (NOT dest)
BLACKNESS = 0x00000042, // dest = BLACK
WHITENESS = 0x00FF0062, // dest = WHITE
};
/// <summary>
/// Enumeration to be used for those Win32 function
/// that return BOOL
/// </summary>
public enum Bool
{
False = 0,
True
};
/// <summary>
/// Sets the background color.
/// </summary>
/// <param name="hdc">The HDC.</param>
/// <param name="crColor">Color of the cr.</param>
/// <returns></returns>
[DllImport("gdi32.dll")]
public static extern int SetBkColor(IntPtr hdc, int crColor);
/// <summary>
/// CreateCompatibleDC
/// </summary>
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
/// <summary>
/// DeleteDC
/// </summary>
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteDC(IntPtr hdc);
/// <summary>
/// SelectObject
/// </summary>
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
/// <summary>
/// DeleteObject
/// </summary>
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteObject(IntPtr hObject);
/// <summary>
/// CreateCompatibleBitmap
/// </summary>
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hObject, int width, int height);
/// <summary>
/// BitBlt
/// </summary>
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjSource, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
/// <summary>
/// StretchBlt
/// </summary>
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool StretchBlt(IntPtr hObject, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, IntPtr hObjSource, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, TernaryRasterOperations dwRop);
/// <summary>
/// SetStretchBltMode
/// </summary>
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool SetStretchBltMode(IntPtr hObject, int nStretchMode);
}
Notice: This is no full answer to the question, but I want to provide some useful information to the existing answer.
In contrast to the answer of Artur Mustafin, I suggest not to use the Windows GDI directly, but to use the .net methods of the Graphics class. In my tests they have performed a lot better than the GDI functions.
The GDI code is taken from the answer of Artur Mustafin.
The test was displaying an image of 1Mpix (1k x 1k) Bitmap or 100Mpix (10k x 10k) Bitmap in 1k x 1k Picturebox by drawing in the Paint event.
The tests were done in Visual Studio 2015.3 on Windows 7 Pro x64 SP1, Debug mode, on an Intel Core i7-3630QM CPU # 2.4 GHz, 16 GB RAM.
Results:
1000x Graphics.DrawImage() unscaled of 100M Bitmap: 36.8s (x86), 24.2s (x64).
1000x Graphics.DrawImage() unscaled of 1M Bitmap: 5.2s (x86), 3.8s (x64).
100x Graphics.DrawImage() scaled of 100M Bitmap: 62.8s (x86), 39.0s (x64).
1000x Graphics.DrawImage() scaled of 1M Bitmap: 5.2s (x86), 3.8s (x64).
100x GdiDrawImage() StretchBlockTransfer of 100M Bitmap: OutOfMem#x86, 88.5s (x64).
1000x GdiDrawImage() StretchBlockTransfer of 1M Bitmap: 12.9s (x86), 11.5s (x64).
100x GdiDrawImage() BitBlockTransfer of 100M Bitmap: OutOfMem#x86, 49.7s (x64).
1000x GdiDrawImage() BitBlockTransfer of 1M Bitmap: 7.2s (x86), 5.8s (x64).
Test code:
public partial class FormPictureboxPaint : Form
{
private Bitmap m_oBitmap;
public FormPictureboxPaint ()
{
InitializeComponent ();
string sFile = Application.StartupPath + #"\..\..\..\bitmap.png"; // The bitmap file contains an image with 10k x 10k pixels.
m_oBitmap = new Bitmap (sFile);
if (false) // CHANGE TO TRUE IF TESTING WITH 1k x 1k BITMAPS
{
var oBitmap = new Bitmap (m_oBitmap, new Size (1000, 1000));
m_oBitmap.Dispose ();
m_oBitmap = null;
GC.Collect ();
GC.WaitForFullGCComplete ();
GC.WaitForPendingFinalizers ();
m_oBitmap = oBitmap;
}
}
private void pictureBox1_Paint (object sender, PaintEventArgs e)
{
var oGraphics = e.Graphics;
DateTime dtNow = DateTime.Now;
// UNCOMMENT THE FOLLOWING LINES FOR TESTS WITH DrawImage
// COMMENT THE FOLLOWING LINES FOR TESTS WITH GDI
//for (int ixCnt = 0; ixCnt < 1000; ixCnt++)
// PictureboxPaint01 (oGraphics);
// COMMENT THE FOLLOWING LINES FOR TESTS WITH DrawImage
// UNCOMMENT THE FOLLOWING LINES FOR TESTS WITH GDI
for (int ixCnt = 0; ixCnt < 100; ixCnt++)
PictureboxPaint02 (oGraphics);
TimeSpan ts = (DateTime.Now - dtNow);
}
private void PictureboxPaint01 (Graphics i_oGraphics)
{
//_oGraphics.DrawImage (m_oBitmap, new Point ());
i_oGraphics.DrawImage (m_oBitmap, new Rectangle (0, 0, 1000, 1000));
}
private void PictureboxPaint02 (Graphics i_oGraphics)
{
// from https://stackoverflow.com/a/7481071
i_oGraphics.GdiDrawImage
(
m_oBitmap,
new Rectangle (
(int)pictureBox1.Left,
(int)pictureBox1.Top,
(int)pictureBox1.Width,
(int)pictureBox1.Height
),
0, 0, m_oBitmap.Width, m_oBitmap.Height
);
}
}
public static class GraphicsHelper
{
public static void GdiDrawImage (this Graphics graphics, Bitmap image, Rectangle rectangleDst, int nXSrc, int nYSrc, int nWidth, int nHeight)
{
IntPtr hdc = graphics.GetHdc ();
IntPtr memdc = GdiInterop.CreateCompatibleDC (hdc);
IntPtr bmp = image.GetHbitmap ();
GdiInterop.SelectObject (memdc, bmp);
GdiInterop.SetStretchBltMode (hdc, 0x04);
GdiInterop.StretchBlt (hdc, rectangleDst.Left, rectangleDst.Top, rectangleDst.Width, rectangleDst.Height, memdc, nXSrc, nYSrc, nWidth, nHeight, GdiInterop.TernaryRasterOperations.SRCCOPY);
//GdiInterop.BitBlt (hdc, rectangleDst.Left, rectangleDst.Top, rectangleDst.Width, rectangleDst.Height, memdc, nXSrc, nYSrc, GdiInterop.TernaryRasterOperations.SRCCOPY); //put it here, if you did not mention stretching the source image
GdiInterop.DeleteObject (bmp);
GdiInterop.DeleteDC (memdc);
graphics.ReleaseHdc (hdc);
}
}
public class GdiInterop
{
public enum TernaryRasterOperations
{
SRCCOPY = 0x00CC0020, // dest = source
};
public enum Bool
{
False = 0,
True
};
[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);
[DllImport ("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool BitBlt (IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjSource, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
[DllImport ("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool StretchBlt (IntPtr hObject, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, IntPtr hObjSource, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, TernaryRasterOperations dwRop);
[DllImport ("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool SetStretchBltMode (IntPtr hObject, int nStretchMode);
}

Categories