I am fairly new to using p/invoke calls and am wondering if someone can guide me on how to retrieve the raw pixel data (unsigned char*) from an hbitmap.
This is my scenario:
I am loading a .NET Bitmap object on the C# side and sending it's IntPtr to my unmanaged c++ method. Once I receive the hbitmap ptr on the C++ side, I would like to access the Bitmaps' pixel data. I already made a method that accepts an unsigned char* which represents the raw pixel data from c# however I found extracting the byte[] from the c# is fairly slow. This is why I want to send in the Bitmap ptr instead of converting the Bitmap into a byte[] and sending that to my C++ method.
C# code for getting Bitmap IntPtr
Bitmap srcBitmap = new Bitmap(m_testImage);
IntPtr hbitmap = srcBitmap.GetHbitmap();
C# code for importing c++ method
[SuppressUnmanagedCodeSecurityAttribute()]
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int ResizeImage(IntPtr srcImg);
C++ method that will receive the Hbitmap handler
int Resize::ResizeImage(unsigned char* srcImg){
//access srcImgs raw pixel data (preferably in unsigned char* format)
//do work with that
return status;
}
Questions:
1) Since I am sending in an IntPrt, can my C++ method parameter be an unsigned char* ?
2) If not, how can I access the bitmap's raw data from c++?
The GetHbitmap method does not retrieve pixel data. It yields a GDI bitmap handle, of type HBITMAP. Your unmanaged code would receive that as a parameter of type HBITMAP. You can obtain the pixel data from that using GDI calls. But it is not, in itself, the raw pixels.
In fact, I'm pretty sure you are attacking this problem the wrong way. You are probably heading this way because GetPixel and SetPixel are slow. This quite true. Indeed, their GDI equivalents are too. What you need to do is to use LockBits. This will allow you to operate on the entire pixel data in C# in an efficient way. A good description of the subject can be found here: https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx. Note that, for efficiency, this is one type of C# code where unsafe code and pointers is often the best solution.
If, for whatever reason, you still wish to operate on the pixel data using C++ code, then you can still use LockBits as the simplest way to get a pointer to the pixel data. It's certainly much easier than the unmanaged GDI equivalents.
First, an HBITMAP shouldn't be a unsigned char*. If you are passing an HBITMAP to C++ then the parameter should be an HBITMAP:
int Resize::ResizeImage(HBITMAP hBmp)
Next to convert from HBITMAP to pixels:
std::vector<unsigned char> ToPixels(HBITMAP BitmapHandle, int &width, int &height)
{
BITMAP Bmp = {0};
BITMAPINFO Info = {0};
std::vector<unsigned char> Pixels = std::vector<unsigned char>();
HDC DC = CreateCompatibleDC(NULL);
std::memset(&Info, 0, sizeof(BITMAPINFO)); //not necessary really..
HBITMAP OldBitmap = (HBITMAP)SelectObject(DC, BitmapHandle);
GetObject(BitmapHandle, sizeof(Bmp), &Bmp);
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biWidth = width = Bmp.bmWidth;
Info.bmiHeader.biHeight = height = Bmp.bmHeight;
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = Bmp.bmBitsPixel;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biSizeImage = ((width * Bmp.bmBitsPixel + 31) / 32) * 4 * height;
Pixels.resize(Info.bmiHeader.biSizeImage);
GetDIBits(DC, BitmapHandle, 0, height, &Pixels[0], &Info, DIB_RGB_COLORS);
SelectObject(DC, OldBitmap);
height = std::abs(height);
DeleteDC(DC);
return Pixels;
}
Apparently sending in the Pointer from Scan0 is equivalent to what I was searching for. I am able to manipulate the data as expected by sending in an IntPtr retrieved from the bitmapData.Scan0 method.
Bitmap srcBitmap = new Bitmap(m_testImage);
Rectangle rect = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData bmpData = srcBitmap.LockBits(rect, ImageLockMode.ReadWrite, srcBitmap.PixelFormat);
//Get ptr to pixel data of image
IntPtr ptr = bmpData.Scan0;
//Call c++ method
int status = myDll.ResizeImage(ptr);
srcBitmap.UnlockBits(bmpData);
To further help clarify, the only code I changed from my initial post was the first block of code. All the rest remained the same. (C++ method still accepts unsigned char * as a param)
Related
I'm implementing a Custom Credential Provider in C#. I'm using a C++ project as example. This piece of C++ code provides an image to Windows. The way I see it phbmp is a pointer to the image-bitmap. The code either updates the pointer so it points to a new bitmap (read from Resource) or it loads the bitmap to the address pointed by phbmp. I'm not sure if the pointer itself is changed or not.
// Get the image to show in the user tile
HRESULT CSampleCredential::GetBitmapValue(DWORD dwFieldID, _Outptr_result_nullonfailure_ HBITMAP *phbmp)
{
HRESULT hr;
*phbmp = nullptr;
if ((SFI_TILEIMAGE == dwFieldID))
{
HBITMAP hbmp = LoadBitmap(HINST_THISDLL, MAKEINTRESOURCE(IDB_TILE_IMAGE));
if (hbmp != nullptr)
{
hr = S_OK;
*phbmp = hbmp;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
Below is the C# equivalent I'm implementing:
public int GetBitmapValue(uint dwFieldID, IntPtr phbmp)
{
if (dwFieldID == 2)
{
Bitmap image = Resource1.TileImage;
ImageConverter imageConverter = new ImageConverter();
byte[] bytes = (byte[])imageConverter.ConvertTo(image, typeof(byte[]));
Marshal.Copy(bytes, 0, phbmp, bytes.Length);
return HResultValues.S_OK;
}
return HResultValues.E_INVALIDARG;
}
What I'm trying to do:
Load the image from resource (this works, it has the correct length)
Convert the Bitmap to an array of bytes
Copy these bytes to the address pointed by phbmp
This crashes, I assume because of memory-allocation.
The parameters in this method are defined by an interface (in CredentialProvider.Interop.dll, which is provided by Microsoft - I think). So I'm pretty sure it's correct and phbmp is not an out-parameter.
Because it is not an out-parameter I can not change phbmp to let it point to my bitmap, right? I have assigned phbmp to Bitmap.GetHbitmap() and that doesn't crash but it isn't working either. I assume that the change to phbmp is only local in this method.
I can understand that it is not possible to alloc memory to a predefined address. It's the other way around: you alloc memory and get an pointer to it. But then this change is local again. How does this work?
Although some people agreed that IntPtr should be an out-parameter (see comments in https://syfuhs.net/2017/10/15/creating-custom-windows-credential-providers-in-net/) the answer was actually:
var bmp = new Bitmap(imageStream);
Marshal.WriteIntPtr(phbmp, bmp.GetHbitmap());
I have some C++/CLI code that creates a simple CImg image and draws a circle on it. I want to pass it to C#, but I'm not sure how. I thought of using a byte array to pass it to C#, but I can't get the length of the array, which is needed for any conversion from byte* to byte[] or for passing into the unmanaged memory stream. I've tried using strlen, but that just returns 0.
Here is my C++ code:
unsigned char* calculateFrame::ReadImage() {
CImg<unsigned char> testImage(1920, 1080, 1, 3, 0);
const unsigned char white[3] = { 255,255,255 };
testImage.draw_circle(256, 256, 200, white, 1.0f, ~0U);
testImage.draw_point(500, 500, white, 255);
unsigned char* charArray = (unsigned char*)testImage;
return charArray;
}
C# code:
Bitmap testBmp;
using(var test = new FrameCalculator.calculateFrame())
{
Console.WriteLine(test.calculateOneFrame(3));
unsafe
{
byte* imageArray = test.ReadImage();
using(var ms = new UnmanagedMemoryStream(imageArray , /* length of byte* (unknown) */))
{
testBmp = new Bitmap(ms);
}
}
}
If you have any tricks to get around unsafe code without sacrificing performance, that would be nice, but I'm not opposed to using unsafe code if it's necessary.
I ended up deciding that in the future, I would need a frame buffer, which necessitated writing the frames to the disk, so that they weren't lost in a restart.
Anyways, my solution was to write the image to disk as a .BMP and access it using Image.FromFile in C#. This isn't a great way to do this in most cases, because it adds a lot of overhead, but it made sense in my program.
On Windows Phone 8, I wish to take a camera shot in native code, but I'm blocked on the final stage not being able to extract information from IOutputStream.
in C# we code:
MemoryStream image = new MemoryStream();
MemoryStream imagePreview = new MemoryStream();
cameraCaptureSequence.Frames[0].CaptureStream = image.AsOutputStream();
cameraCaptureSequence.Frames[0].ThumbnailStream = imagePreview.AsOutputStream();
await cameraCaptureSequence.StartCaptureAsync();
from now image stream has information of captured image and I can render it.
In C++ / Cx I need to do the same thing but more until to catch the byte* of captured image, here my code:
Windows::Phone::Media::Capture::CameraCaptureSequence^ cameraCaptureSequence;
IBuffer^ image;
return concurrency::create_async([this]()
{
cameraCaptureSequence->Frames->GetAt(0)->CaptureStream = reinterpret_cast<IOutputStream^>(image);
create_task( m_camera->PrepareCaptureSequenceAsync(cameraCaptureSequence) ).wait();
create_task( cameraCaptureSequence->StartCaptureAsync() ).then([this]()
{
}
}
Starting from the most basic thing I wish to understand how to "save" into an IBuffer^ the result of captured image stream, better how to get the internal byte* buffer.
Thanks
You can access the pixel data from a captured image in Native code through the ICameraCaptureFrameNative. The object that implements the interface is obtained through COM. Once you have obtained the object, use MapBuffer() to access the BYTE * array.
Note that the pixel data obtained this way is in NV12 format, not a JPEG or RGB as one would expect.
#include <Windows.Phone.Media.Capture.Native.h>
CameraCaptureFrame^ frame = m_cameraCaptureSequence->Frames->GetAt(0);
pNativeFrame = NULL;
HRESULT hr = reinterpret_cast<IUnknown*>(frame)->QueryInterface(__uuidof(ICameraCaptureFrameNative ), (void**) &pNativeFrame);
create_task( m_camera->PrepareCaptureSequenceAsync(m_cameraCaptureSequence) ).wait();
create_task( m_cameraCaptureSequence->StartCaptureAsync() ).then([this]()
{
DWORD bufferSize =0;
BYTE * pBuffer = NULL;
pNativeFrame->MapBuffer(&bufferSize, &pBuffer); // Pixels are in pBuffer.
// Unmap() the buffer before capturing another image.
ICameraCaptureFrameNative doesn't give access to a texture containing the preview?
If you want acces data from a IBuffer look here : http://msdn.microsoft.com/en-us/library/windows/apps/dn182761.aspx
For your case, i thinks you need a class which implement IOutputStream. Maybe InMemoryRandomAccessStream ?
I am trying to write images acquired from a webcam to a file via FileStream in C#. They are 16-bit monochrome so I cannot just write out the Bitmap object. I am using Marshal.Copy() in order to work around this as follows:
unsafe private void RecordingFrame()
{
Bitmap bm16;
BitmapData bmd;
Emgu.CV.Image<Gray, UInt16> currentFrame;
const int ORIGIN_X = 0;
const int ORIGIN_Y = 0;
// get image here and put it in bm16...
bmd = bm16.LockBits(new Rectangle(ORIGIN_X, ORIGIN_Y, bm16.Width, bm16.Height),
ImageLockMode.ReadOnly, bm16.PixelFormat);
var length = bmd.Stride * bmd.Height;
byte[] bytes = new byte[length];
Marshal.Copy(bmd.Scan0, bytes, 0, length);
fsVideoWriter.Write(bytes, 0, length);
bm16.UnlockBits(bmd);
}
Is this the best way to accomplish this? I wanted to simply pass the BitmapData's Scan0 member as a pointer to FileStream but I couldn't figure out how to do this so I copied the data into a byte buffer. This reduces performance slightly so if I can improve it to achieve a higher frame rate I'd like to do so.
You could create an UnmanagedMemoryStream from bmd.Scan0 and then call CopyTo(fsVideoWriter). But I'm not sure if this would be any faster than what you have now.
It is possible to get a pointer from a managed array
byte [] buffer = new byte[length + byteAlignment];
GCHandle bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr ptr = bufferHandle.AddrOfPinnedObject();
is there any way to do the opposite. getting a byte array from a pinned object without copying?
Sure, that's what Marshal.Copy is for - there is no way (well, no way without copying of some variety) to otherwise get memory between the managed and unmanaged states...well, that's not 100% true, but I'm assuming you don't want to rely solely on Win32/C and p/invoke to copy memory around.
Marshal.Copy use would look like:
IntPtr addressOfThing = ....;
byte[] buffer = new byte[...];
Marshal.Copy(addressOfThing, buffer, 0, bufferSize);