I am using ExtTextOut wingdi function to draw text. Also, I am using SetWorldTransform for scaling text but now I want to rotate text(at all angles) too. How can I change/pass parameters to it so it will scale as well as rotate too?
Below is the sample code block, this code works perfect but now I want to introduce rotating text as well without impacting scaling.
var inputString = "Sample text";
var fontSize = 31f;
var font = new Font("Avenir Black", fontSize, new FontStyle());
var startX1 = 50;
var startY1 = 50;
LOGFONT lf = new LOGFONT();
font.ToLogFont(lf, e.Graphics);
IntPtr hPrinterDC = e.Graphics.GetHdc();
StringBuilder sbText = new StringBuilder(inputString);
if (hPrinterDC != IntPtr.Zero)
{
int nPrintHorzRes = GetDeviceCaps(hPrinterDC, HORZRES);
int nPrintVertRes = GetDeviceCaps(hPrinterDC, VERTRES);
int nPhysWidth = GetDeviceCaps(hPrinterDC, PHYSICALWIDTH);
int nPhysHeight = GetDeviceCaps(hPrinterDC, PHYSICALHEIGHT);
int nPhysOffsetX = GetDeviceCaps(hPrinterDC, PHYSICALOFFSETX);
int nPhysOffsetY = GetDeviceCaps(hPrinterDC, PHYSICALOFFSETY);
IntPtr hDCScreen = GetDC(IntPtr.Zero);
float nLogPixelsXScreen = GetDeviceCaps(hDCScreen, LOGPIXELSX);
float nLogPixelsYScreen = GetDeviceCaps(hDCScreen, LOGPIXELSY);
ReleaseDC(hDCScreen, IntPtr.Zero);
float nLogPixelsXPrinter = GetDeviceCaps(hPrinterDC, LOGPIXELSX);
float nLogPixelsYPrinter = GetDeviceCaps(hPrinterDC, LOGPIXELSY);
float nScaleX = Math.Max(nLogPixelsXScreen, nLogPixelsXPrinter) / Math.Min(nLogPixelsXScreen, nLogPixelsXPrinter);
float nScaleY = Math.Max(nLogPixelsYScreen, nLogPixelsYPrinter) / Math.Min(nLogPixelsYScreen, nLogPixelsYPrinter);
System.Drawing.Drawing2D.Matrix transform = new System.Drawing.Drawing2D.Matrix();
transform.Scale(nScaleX, nScaleY);
XFORM renderTransform = new XFORM();
var elements = transform.Elements;
var m11 = elements[0];
var m12 = elements[1];
var m21 = elements[2];
var m22 = elements[3];
var dx = elements[4];
var dy = elements[5];
renderTransform.eM11 = (float)m11;
renderTransform.eM12 = (float)m12;
renderTransform.eM21 = (float)m21;
renderTransform.eM22 = (float)m22;
int nOffsetX = 0;
int nOffsetY = 0;
renderTransform.eDx = (float)transform.OffsetX + nOffsetX;
renderTransform.eDy = (float)transform.OffsetY + nOffsetY;
SetGraphicsMode(hPrinterDC, GM_ADVANCED);
SetMapMode(hPrinterDC, MM_TEXT);
bool bRet = SetWorldTransform(hPrinterDC, ref renderTransform);
var startX2 = startX1;
var startY2 = startY1;
RECT rc = new RECT(startX2, startY2, nPhysWidth, nPhysHeight);
SetViewportOrgEx(hPrinterDC, -nPhysOffsetX, -nPhysOffsetY, IntPtr.Zero);
lf.lfHeight = (int)(lf.lfHeight / nScaleY);
IntPtr hFontNew = CreateFontIndirect(lf);
IntPtr hFontOld = SelectObject(hPrinterDC, hFontNew);
SetBkMode(hPrinterDC, TRANSPARENT);
SetTextColor(hPrinterDC, ColorTranslator.ToWin32(Color.Blue));
ExtTextOut(hPrinterDC, rc.left, rc.top, ETO_OPAQUE, IntPtr.Zero, sbText, (uint)sbText.Length, IntPtr.Zero);
rc.top += -lf.lfHeight;
SetTextColor(hPrinterDC, ColorTranslator.ToWin32(Color.Green));
ExtTextOut(hPrinterDC, rc.left, rc.top, ETO_OPAQUE, IntPtr.Zero, sbText, (uint)sbText.Length, IntPtr.Zero);
var nExtra = -10.0f / nScaleX;
SetTextCharacterExtra(hPrinterDC, (int)nExtra);
rc.top += -lf.lfHeight;
SetTextColor(hPrinterDC, ColorTranslator.ToWin32(Color.Red));
ExtTextOut(hPrinterDC, rc.left, rc.top, ETO_OPAQUE, IntPtr.Zero, sbText, (uint)sbText.Length, IntPtr.Zero);
SelectObject(hPrinterDC, hFontOld);
DeleteObject(hFontNew);
}
Here are pinvokes:
[DllImport("Gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetWorldTransform(IntPtr hdc, ref XFORM lpxf);
[StructLayout(LayoutKind.Sequential)]
public struct XFORM
{
public float eM11;
public float eM12;
public float eM21;
public float eM22;
public float eDx;
public float eDy;
}
[DllImport("Gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern int SetGraphicsMode(IntPtr hdc, int iMode);
public const int GM_COMPATIBLE = 1;
public const int GM_ADVANCED = 2;
public const int GM_LAST = 2;
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
public const int TRANSPARENT = 1;
public const int OPAQUE = 2;
public const int BKMODE_LAST = 2;
[DllImport("Gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern int SetBkMode(IntPtr hdc, int mode);
Related
This question already has an answer here:
Using WriteConsoleOutput to write Unicode with c#
(1 answer)
Closed 2 years ago.
Having looked at this question and modifying the code to print characters like '☺', '☻' or '█', I can't see how to get these characters to actually draw properly with WriteConsoleOutput. (There is no problem when using a stream or Console.Write but I can't individually control the character colour with these solutions so they're unsuitable.)
It seems there was a solution here for C++ but the "L" macro they talk about down the bottom does not apply to C#.
Finally, having checked the suggestions here, I can't see anything wrong with the code I ended up with:
class Program
{
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] uint fileAccess,
[MarshalAs(UnmanagedType.U4)] uint fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] int flags,
IntPtr template
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputW(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion
);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleOutputCP(uint wCodePageID);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCP(uint wCodePageID);
[StructLayout(LayoutKind.Sequential)]
public struct Coord
{
public short X;
public short Y;
public Coord(short X, short Y)
{
this.X = X;
this.Y = Y;
}
};
[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)] public char UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}
[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
[FieldOffset(0)] public CharUnion Char;
[FieldOffset(2)] public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SmallRect
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
static void Main(string[] args)
{
//SetConsoleOutputCP(65001);
//SetConsoleCP(65001);
SafeFileHandle fileHandle = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
Console.TreatControlCAsInput = true;
//Console.OutputEncoding = Encoding.UTF8;
Console.OutputEncoding = System.Text.Encoding.Unicode;
Console.CursorVisible = false;
short screenWidth = 40;
short screenHeight = 20;
Console.SetWindowSize(screenWidth + 1, screenHeight + 1);
Console.SetBufferSize(screenWidth + 1, screenHeight + 1);
bool running = true;
while (running)
{
CharInfo[] buf = new CharInfo[screenWidth * screenHeight];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = screenWidth, Bottom = screenHeight };
for (int i = 0; i < buf.Length; ++i)
{
buf[i].Attributes = 6;
buf[i].Char.UnicodeChar = '☺';
}
bool b = WriteConsoleOutputW(
fileHandle,
buf,
new Coord() { X = screenWidth, Y = screenHeight },
new Coord() { X = 0, Y = 0 },
ref rect
);
Console.SetCursorPosition(0, 0);
}
}
}
I seem to have ruled out all combinations of CharSet, A/W DLLImport suffixes, code pages, Console properties & source-file encoding.
(Surely it can't be impossible to fast-print coloured smiley-face characters to a C# console. Somebody tell me I've missed something silly here; I've spent about 2 hours Googling & trying to emulate something Castle Adventure managed in 1984 with ease.)
How can I get the aforementioned '☺' to print anything other than a '?' or a ':'?
This seems to be a duplicate of Using WriteConsoleOutput to write Unicode with c#
The solution to that was to add the CharSet = CharSet.Unicode attribute to both the CharInfo and CharUnion structs.
Although am getting the values of cursorX, cursorY there is no movement of my mouse cursor. Am I missing anything?
Joint hand = helper.handRight;
CameraSpacePoint position = hand.Position;
DepthSpacePoint handPt = _sensor.CoordinateMapper.MapCameraPointToDepthSpace(position);
Point relativePoint = new Point(
handPt.X * (1920 / _sensor.DepthFrameSource.FrameDescription.Width),
handPt.Y * (1080 / _sensor.DepthFrameSource.FrameDescription.Height) );
cursorX = (int)relativePoint.X;
cursorY = (int)relativePoint.Y;
NativeMethods.SendMouseInput(cursorX, cursorY,
(int)SystemParameters.PrimaryScreenWidth,
(int)SystemParameters.PrimaryScreenHeight, leftClick);
public static class NativeMethods
{
public const int InputMouse = 0;
public const int MouseEventMove = 0x01;
public const int MouseEventLeftDown = 0x02;
public const int MouseEventLeftUp = 0x04;
public const int MouseEventRightDown = 0x08;
public const int MouseEventRightUp = 0x10;
public const int MouseEventMiddleDown = 0x0020;
public const int MouseEventMiddleUp = 0x0040;
public const int MouseEventAbsolute = 0x8000;
public const int MouseEventWheelScroll = 0x0800;
public const int MouseEventWheelHScroll = 0x1000;
private static bool lastLeftDown = false;
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint numInputs, Input[] inputs, int size);
public static void SendMouseInput(int positionX, int positionY, int maxX, int maxY, bool leftDown)
{
if (positionX > int.MaxValue)
throw new ArgumentOutOfRangeException("positionX");
if (positionY > int.MaxValue)
throw new ArgumentOutOfRangeException("positionY");
Input[] i = new Input[4];
// move the mouse to the position specified
i[0] = new Input();
i[0].Type = InputMouse;
i[0].mouseInput.X = (positionX * 65535) / maxX;
i[0].mouseInput.Y = (positionY * 65535) / maxY;
i[0].mouseInput.Flags = MouseEventAbsolute | MouseEventMove;
// determine if we need to send a mouse down or mouse up event
if (!lastLeftDown && leftDown)
{
i[1] = new Input();
i[1].Type = InputMouse;
i[1].mouseInput.Flags = MouseEventLeftDown;
lastLeftDown = true;
}
else if (lastLeftDown && !leftDown)
{
i[1] = new Input();
i[1].Type = InputMouse;
i[1].mouseInput.Flags = MouseEventLeftUp;
lastLeftDown = false;
}
// send it off
uint result = SendInput(2, i, Marshal.SizeOf(i[0]));
if (result == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
I want to compress textures to pvrtc in managed code so I've found a source which does what I want but I get a System.AccessViolationException. This is the code I use so far:
[DllImport("x86/pvrtc.dll", CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CompressTexture(byte[] data, int height, int width, int mipLevels, bool preMultiplied, bool pvrtc4bppCompression, ref IntPtr dataSizes);
public static byte[] Compress(byte[] data, int height, int width, int mipLevels, bool preMultiplied, bool pvrtc4bppCompression)
{
IntPtr dataSizesPtr = Marshal.AllocCoTaskMem(data.Length + 1);
var texDataPtr = CompressTexture(data, height, width, mipLevels, preMultiplied, pvrtc4bppCompression, ref dataSizesPtr);
//Store the size of each mipLevel
var dataSizesArray = new int[mipLevels];
//Copies data from an unmanaged memory pointer to a managed 8-bit unsigned integer array.
Marshal.Copy(dataSizesPtr, dataSizesArray, 0, dataSizesArray.Length);
var levelSize = 0;
byte[] levelData = new byte[0];
for (int x = 0; x < mipLevels; x++)
{
levelSize = dataSizesArray[x];
levelData = new byte[levelSize];
Marshal.Copy(texDataPtr, levelData, 0, levelSize);
texDataPtr = IntPtr.Add(texDataPtr, levelSize);
}
return levelData;
}
Any help is very much appreciated.
I've just recently started playing with the InjectTouchInput for Windows 8 Consumer Preview. I've gone round and round in circles trying to get the darn thing to work but just can't seem to get it to actually interact. I'm working in c# and at the moment am only creating a metro interface with x and y coordinates in two text boxes and buttons which call the functions below to touch on the screen at those coordinates. Is this the right way to go about doing this?
protected unsafe class TouchDriver
{
public struct POINTER_TOUCH_INFO {
public POINTER_INFO pointerInfo; // An embedded POINTER_INFO header structure.
public TOUCH_FLAGS touchFlags; // Currently none.
public Rect rcContact; // Pointer contact area in pixel screen coordinates. By default, if the device does not report a contact area, this field defaults to a 0-by-0 rectangle centered around the pointer location.
public UInt32 orientation; // A pointer orientation, with a value between 0 and 359, where 0 indicates a touch pointer aligned with the x-axis and pointing from left to right; increasing values indicate degrees of rotation in the clockwise direction.
// This field defaults to 0 if the device does not report orientation.
public UInt32 pressure; // Pointer pressure normalized in a range of 0 to 256.
// This field defaults to 128 if the device does not report pressure.
// Question: Can this go from 0 to 1024 to match pen pressure?
}
public enum TOUCH_FLAGS
{
TOUCH_FLAGS_NONE = 0x00000000
}
public POINTER_TOUCH_INFO create_pointer_touch_info(POINTER_INFO pointerInfo, TOUCH_FLAGS touchFlags, RECT rcContact, UInt32 orientation, UInt32 pressure)
{
POINTER_TOUCH_INFO mi = new POINTER_TOUCH_INFO();
mi.pointerInfo = pointerInfo;
mi.touchFlags = touchFlags;
mi.rcContact = rcContact;
mi.orientation = orientation;
mi.pressure = pressure;
return mi;
}
public enum POINTER_INPUT_TYPE
{
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004
}
public struct POINTER_INFO
{
public POINTER_INPUT_TYPE pointerType;
public UInt32 pointerId;
public UInt32 frameId;
public HANDLE sourceDevice;
public HWND hwndTarget;
public Point ptPixelLocation;
public Point ptHimetricLocation;
public Point ptPixelLocationPredicted;
public Point ptHimetricLocationPredicted;
public POINTER_FLAGS pointerFlags;
public DWORD dwTime;
public UInt32 historyCount;
// public UInt32 inputData;
public DWORD dwKeyStates;
public ULONGLONG Reserved;
}
public POINTER_INFO create_pointer_info(
POINTER_INPUT_TYPE pointerType,
UInt32 pointerId,
UInt32 frameId,
HANDLE sourceDevice,
HWND hwndTarget,
Point ptPixelLocation,
Point ptHimetricLocation,
Point ptPixelLocationPredicted,
Point ptHimetricLocationPredicted,
POINTER_FLAGS pointerFlags,
DWORD dwTime,
UInt32 historyCount,
// UInt32 inputData,
DWORD dwKeyStates,
ULONGLONG Reserved)
{
POINTER_INFO mi = new POINTER_INFO();
mi.pointerType = pointerType;
mi.pointerId = pointerId;
mi.frameId = frameId;
mi.sourceDevice = sourceDevice;
mi.hwndTarget = hwndTarget;
mi.ptPixelLocation = ptPixelLocation;
mi.ptHimetricLocation = ptHimetricLocation;
mi.ptPixelLocationPredicted = ptPixelLocationPredicted;
mi.ptHimetricLocationPredicted = ptHimetricLocationPredicted;
mi.pointerFlags = pointerFlags;
mi.dwTime = dwTime;
mi.historyCount = historyCount;
// mi.inputData = inputData;
mi.dwKeyStates = dwKeyStates;
mi.Reserved = Reserved;
return mi;
}
public enum POINTER_FLAGS
{
POINTER_FLAG_NONE = 0x00000000,
POINTER_FLAG_NEW = 0x00000001,
POINTER_FLAG_INRANGE = 0x00000002,
POINTER_FLAG_INCONTACT = 0x00000004,
POINTER_FLAG_FIRSTBUTTON = 0x00000010,
POINTER_FLAG_SECONDBUTTON = 0x00000020,
POINTER_FLAG_THIRDBUTTON = 0x00000040,
POINTER_FLAG_OTHERBUTTON = 0x00000080,
POINTER_FLAG_PRIMARY = 0x00000100,
POINTER_FLAG_CONFIDENCE = 0x00000200,
POINTER_FLAG_CANCELLED = 0x00000400,
POINTER_FLAG_DOWN = 0x00010000,
POINTER_FLAG_UPDATE = 0x00020000,
POINTER_FLAG_UP = 0x00040000,
POINTER_FLAG_WHEEL = 0x00080000,
POINTER_FLAG_HWHEEL = 0x00100000
}
[System.Runtime.InteropServices.DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
private static extern Boolean InjectTouchInput(UInt32 count, POINTER_TOUCH_INFO* pntTchInfo);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern Boolean InitializeTouchInjection(UInt32 maxCount, DWORD dwMode);
private const UInt32 MAX_TOUCH_COUNT = 256; // Can be as high as 256
private const UInt32 TOUCH_FEEDBACK_DEFAULT = 0x1;
private const UInt32 TOUCH_FEEDBACK_INDIRECT = 0x2;
private const UInt32 TOUCH_FEEDBACK_NONE = 0x3;
public unsafe static void MouseTouch(int x, int y)
{
bool ret = false;
ret = InitializeTouchInjection(1, TOUCH_FEEDBACK_DEFAULT);
if (!ret)
{
throw new NotSupportedException();
}
Point point = new Point(x,y);
POINTER_INFO ptrInfo = new POINTER_INFO();
POINTER_TOUCH_INFO* ptrTchInfo;
ptrInfo.pointerType = POINTER_INPUT_TYPE.PT_TOUCH;
ptrInfo.pointerId = 1;
ptrInfo.ptPixelLocation = point;
ptrInfo.pointerFlags = POINTER_FLAGS.POINTER_FLAG_PRIMARY;
POINTER_TOUCH_INFO ptrTchInfobase = new POINTER_TOUCH_INFO();
ptrTchInfo = &ptrTchInfobase;
ptrTchInfo->pointerInfo = ptrInfo;
ptrTchInfo->touchFlags = TOUCH_FLAGS.TOUCH_FLAGS_NONE;
ptrTchInfo->rcContact.X = x - 2;
ptrTchInfo->rcContact.Y = y - 2;
ptrTchInfo->rcContact.Width = 4;
ptrTchInfo->rcContact.Height = 4;
ptrTchInfo->pressure = 128;
ptrTchInfo->orientation = 0;
ret = InjectTouchInput(1, ptrTchInfo);
if (!ret)
{
throw new NotImplementedException();
}
}
}
Almost all of that I've tried to lift from the InjectTouchInput API I found online. I can InitializeTouchInject fine, its the Inject bit thats returning false and I have no idea why.
I went ahead and create some custom functions in c++ based on the sample Microsoft provided and then went and imported that into C# using alot of the same definitions as before but without farting about with all the type checking, pointers and arguments that were causing headaches before.
The DLL file that I've used is TouchInjectionDriver.dll and can be found here:
http://www.mediafire.com/file/do2h6m04omjweb3/TouchInjectionDriver.zip
Below is the C# code I used to implement it.
public enum TOUCH_MASK : uint
{
TOUCH_MASK_NONE = 0x00000000,
TOUCH_MASK_CONTACTAREA = 0x00000001,
TOUCH_MASK_ORIENTATION = 0x00000002,
TOUCH_MASK_PRESSURE = 0x00000004
}
public enum POINTER_INPUT_TYPE : uint
{
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004
}
public enum POINTER_FLAGS : uint
{
POINTER_FLAG_NONE = 0x00000000,
POINTER_FLAG_NEW = 0x00000001,
POINTER_FLAG_INRANGE = 0x00000002,
POINTER_FLAG_INCONTACT = 0x00000004,
POINTER_FLAG_FIRSTBUTTON = 0x00000010,
POINTER_FLAG_SECONDBUTTON = 0x00000020,
POINTER_FLAG_THIRDBUTTON = 0x00000040,
POINTER_FLAG_OTHERBUTTON = 0x00000080,
POINTER_FLAG_PRIMARY = 0x00000100,
POINTER_FLAG_CONFIDENCE = 0x00000200,
POINTER_FLAG_CANCELLED = 0x00000400,
POINTER_FLAG_DOWN = 0x00010000,
POINTER_FLAG_UPDATE = 0x00020000,
POINTER_FLAG_UP = 0x00040000,
POINTER_FLAG_WHEEL = 0x00080000,
POINTER_FLAG_HWHEEL = 0x00100000
}
public enum TOUCH_FEEDBACK : uint
{
TOUCH_FEEDBACK_DEFAULT = 0x1,
TOUCH_FEEDBACK_INDIRECT = 0x2,
TOUCH_FEEDBACK_NONE = 0x3
}
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool InjectTouch(int x, int y, POINTER_INPUT_TYPE pt_input, int pressure, int orientation, int id, int rcContactTop, int rcContactBottom, int rcContactLeft, int rcContactRight, POINTER_FLAGS pointerFlags, TOUCH_MASK touchMask);
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void setTouchFeedback(TOUCH_FEEDBACK fb);
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void setDefaultRectSize(int size);
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void setDefaultPressure(int pres);
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void setDefaultOrientation(int or);
[DllImport("User32.dll")]
static extern Boolean MessageBeep(UInt32 beepType);
public static void mouseclick(int x, int y)
{
bool ret;
setTouchFeedback(TOUCH_FEEDBACK.TOUCH_FEEDBACK_INDIRECT);
ret = InjectTouch(x, y, POINTER_INPUT_TYPE.PT_TOUCH, 3200, 0, 0, x - 4, x + 4, y - 4, y + 4,POINTER_FLAGS.POINTER_FLAG_DOWN|POINTER_FLAGS.POINTER_FLAG_INCONTACT|POINTER_FLAGS.POINTER_FLAG_INRANGE,TOUCH_MASK.TOUCH_MASK_CONTACTAREA|TOUCH_MASK.TOUCH_MASK_ORIENTATION|TOUCH_MASK.TOUCH_MASK_PRESSURE);
if (ret)
{
ret = InjectTouch(x, y, POINTER_INPUT_TYPE.PT_TOUCH, 3200, 0, 0, x - 4, x + 4, y - 4, y + 4, POINTER_FLAGS.POINTER_FLAG_UP, TOUCH_MASK.TOUCH_MASK_CONTACTAREA | TOUCH_MASK.TOUCH_MASK_ORIENTATION | TOUCH_MASK.TOUCH_MASK_PRESSURE);
}
else
{
MessageBeep(0);
}
}
I can connect and get images from my device with twaindotnet. But I want to handle the images as Image class. When I try something like this:
...
ArrayList pics = tw.TransferPictures();
EndingScan();
tw.CloseSrc();
if(pics.Count > 0) {
IntPtr img = (IntPtr) pics[ 0 ];
PicForm newpic = new PicForm( img );
Image r = Image.FromHbitmap(img, this.Handle);
picturebox.Image = r;
}
...
I'm getting an error as "Error:Generic Error Occured in GDI+" on the line ,
Image r = Image.FromHbitmap(img, this.Handle);
So where am I wrong? How can I get as an Image the image?
I too found out that calling Image.FromHbitmap is not enough.
I had a look in the TwainDotNet library you mentioned and there I found the BitmapRenderer class.
Pulling out just the relevant bit it is easy enough to use that to create a simple static method that you can pass in the IntPtr you get from TWAIN (in your case your img variable) and convert it to a Bitmap. You, therefore, call it like so:
Image r = TwainBitmapConvertor.ToBitmap(img);
and here is the code (it only works on x86 and needs tidying but it does the job):
public static class TwainBitmapConvertor
{
[StructLayout(LayoutKind.Sequential, Pack = 2)]
private class BitmapInfoHeader
{
public int Size;
public int Width;
public int Height;
public short Planes;
public short BitCount;
public int Compression;
public int SizeImage;
public int XPelsPerMeter;
public int YPelsPerMeter;
public int ClrUsed;
public int ClrImportant;
}
[DllImport("gdi32.dll", ExactSpelling = true)]
private static extern int SetDIBitsToDevice(IntPtr hdc,
int xdst, int ydst, int width, int height, int xsrc,
int ysrc, int start, int lines, IntPtr bitsptr,
IntPtr bmiptr, int color);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalLock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern bool GlobalUnlock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalFree(IntPtr handle);
public static Bitmap ToBitmap(IntPtr dibHandle)
{
var bitmapPointer = GlobalLock(dibHandle);
var bitmapInfo = new BitmapInfoHeader();
Marshal.PtrToStructure(bitmapPointer, bitmapInfo);
var rectangle = new Rectangle();
rectangle.X = rectangle.Y = 0;
rectangle.Width = bitmapInfo.Width;
rectangle.Height = bitmapInfo.Height;
if (bitmapInfo.SizeImage == 0)
{
bitmapInfo.SizeImage =
((((bitmapInfo.Width * bitmapInfo.BitCount) + 31) & ~31) >> 3)
* bitmapInfo.Height;
}
// The following code only works on x86
Debug.Assert(Marshal.SizeOf(typeof(IntPtr)) == 4);
int pixelInfoPointer = bitmapInfo.ClrUsed;
if ((pixelInfoPointer == 0) && (bitmapInfo.BitCount <= 8))
{
pixelInfoPointer = 1 << bitmapInfo.BitCount;
}
pixelInfoPointer = (pixelInfoPointer * 4) + bitmapInfo.Size
+ bitmapPointer.ToInt32();
IntPtr pixelInfoIntPointer = new IntPtr(pixelInfoPointer);
Bitmap bitmap = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
IntPtr hdc = graphics.GetHdc();
try
{
SetDIBitsToDevice(hdc,
0, 0, rectangle.Width, rectangle.Height, 0, 0, 0,
rectangle.Height, pixelInfoIntPointer, bitmapPointer, 0);
}
finally
{
graphics.ReleaseHdc(hdc);
}
}
bitmap.SetResolution(PpmToDpi(bitmapInfo.XPelsPerMeter),
PpmToDpi(bitmapInfo.YPelsPerMeter));
GlobalUnlock(dibHandle);
GlobalFree(dibHandle);
return bitmap;
}
private static float PpmToDpi(double pixelsPerMeter)
{
double pixelsPerMillimeter = (double)pixelsPerMeter / 1000.0;
double dotsPerInch = pixelsPerMillimeter * 25.4;
return (float)Math.Round(dotsPerInch, 2);
}
}
Assuming that tw.TransferPictures() returns an array of bitmap handles, then change Image r = ... to:
Image r = Image.FromHbitmap(img);
The second argument to FromHbitmap is a handle to a GDI palette, which I doubt you have.