I need to create a bitmap from an array of pixels for a raycaster I'm working on in Direct2D. However, I'm having trouble understanding how to use the CreateBitmap function. Specifically, I'm not sure what the srcData parameter is supposed to be. I'm pretty sure/hoping it's a pointer to an array of pixels, but I'm not sure how to set up that array. What kind of array is it supposed to be? What data type? Etc.
Here's what I've tried:
int width = 400, height = 400;
D2D1::ColorF * arr = (D2D1::ColorF*)calloc(width * height * 4, sizeof(D2D1::ColorF));
for (int i = 0; i < width * height * 4; i++) { arr[i] = D2D1::ColorF(0.0f, 1.0f, 0.0f); }
// Create the bitmap and draw it on the screen
ID2D1Bitmap * bmp;
HRESULT hr;
hr = renderTarget->CreateBitmap(
D2D1::SizeU(width, height),
arr,
width * sizeof(int) * 4,
D2D1::BitmapProperties(),
&bmp);
if (hr != S_OK) { return; } // I've tested and found that hr does not equal S_OK
// Draw the bitmap...
What should the second and third lines look like? Is there anything else I'm doing incorrectly?
Syntax:
HRESULT CreateBitmap(
D2D1_SIZE_U size,
const void *srcData,
UINT32 pitch,
const D2D1_BITMAP_PROPERTIES & bitmapProperties,
ID2D1Bitmap **bitmap
);
Your code:
hr = renderTarget->CreateBitmap(
D2D1::SizeU(width, height),
arr, // <<--- Wrong, see (a) below
width * sizeof(int) * 4, // <<--- Close but wrong, see (b) below
D2D1::BitmapProperties(), // <<--- Wrong, see (c) below
&bmp);
(a) - you are supposed to provide an array of pixel data here, where the format depends on format of the bitmap. Note that this is optional an d you can create a bitmap without initialization. The pixels are not D2D1::ColorF exactly. They could be 4 byte RGBA data if you request respective bitmap format, see (c) below.
(b) - this is distance between rows in bytes, if your pixels are supposed to be 32-bit values you would normally want Width * 4 here
(c) - this requests DXGI_FORMAT_UNKNOWN D2D1_ALPHA_MODE_UNKNOWN and results in bitmap creation error. You need a real format here such as DXGI_FORMAT_B8G8R8A8_UNORM (see Pixel Formats and also Supported Pixel Formats and Alpha Modes)
The first link above shows how exactly bytes in memory map to pixel colors, and you are supposed to prepare your data respectively.
UPD
With DXGI_FORMAT_B8G8R8A8_UNORM your initialization structure is this:
UINT8* Data = malloc(Height * Width * 4);
for(UINT Y = 0; Y < Height; Y++)
for(UINT X = 0; X < Width; X++)
{
UINT8* PixelData = Data + ((Y * Width) + X) * 4;
PixelData[0] = unsigned integer blue in range 0..255;
PixelData[1] = unsigned integer red in range 0..255;
PixelData[2] = unsigned integer green in range 0..255;
PixelData[3] = 255;
}
Related
to circumvent some (a lot) of problems with the Actionscript Camera API on Windows 8 Systems,
I decided to create a native extension to deal with the camera.
Right now, the camera part and all the glue to communicate with the AIR Runtime is actually working, so clicking on a button in AIR will open a new Windows window that will return a System::Drawing::Bitmap.
My task would be now to
a) Create a FREBitmapData object and
b) Fill in the BitmapData from the Windows Bitmap.
Should be easy, I thought, many days ago... As I'm not really familiar with C++ I didn't get this to work at all.
Here's what I tried so far:
bmp = form1->bitmap; // bmp is a handle to the System::Drawing::Bitmap returned from the external window
// Lock the bitmap's bits.
Rectangle rect = Rectangle(0, 0, bmp->Width, bmp->Height);
System::Drawing::Imaging::BitmapData^ bmpData = bmp->LockBits(rect, System::Drawing::Imaging::ImageLockMode::ReadWrite, bmp->PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData->Scan0;
// Declare an array to hold the bytes of the bitmap.
// This code is specific to a bitmap with 24 bits per pixels.
int inputLength = Math::Abs(bmpData->Stride) * bmp->Height;
array<Byte>^ input = gcnew array<Byte>(inputLength);
// Copy the RGB values into the array.
System::Runtime::InteropServices::Marshal::Copy(ptr, input, 0, inputLength);
// Unlock the bits.
bmp->UnlockBits(bmpData);
// Create a FREByteArray to hold the data.
// Don't know, if this is necessary
FREObject* outputObject;
FREByteArray* outputBytes = new FREByteArray;
outputBytes->length = inputLength;
outputBytes->bytes = (uint8_t *) &input;
FREAcquireByteArray(outputObject, outputBytes);
// now copy it over
memcpy(outputBytes->bytes, &input, inputLength);
FREReleaseByteArray(outputObject);
// we create a new instance of BitmapData here,
// as we cannot simply pass it over in the args,
// because we don't know it's correct size at extension creation
FREObject* width;
FRENewObjectFromUint32(bmp->Width, width);
FREObject* height;
FRENewObjectFromUint32(bmp->Height, height);
FREObject* transparent;
FRENewObjectFromBool(uint32_t(0), transparent);
FREObject* fillColor;
FRENewObjectFromUint32(uint32_t(0xFFFFFF), fillColor);
FREObject obs[4] = { width, height, transparent, fillColor };
// we create some Actionscript Intsances here, we want to send back
FREObject* asBmpObj;
FRENewObject("BitmapData", 4, obs, asBmpObj, NULL);
// Now we have our AS bitmap data, copy bytes over
FREBitmapData* asData;
FREAcquireBitmapData(asBmpObj, asData);
// Now what? asData->bits32 won't accept array<Bytes> or any other value I've tried.
return asBmpObj;
The basic idea was:
a) find out the size and bit-depth of the original Win Bitmap (size is determinded by cam resolution picked in the Camera window)
b) write it's bytes to an array. Convert to 32 bits as necessary. (Still missing any idea.)
c) create AS Bitmap of the same size. Bit-depth must always be 32.
d) copy over array to AS Bitmap.
But I just can't achieve this.
Any advice? Thank you!
I don't think the following straight copy will work.
// Copy the RGB values into the array.
System::Runtime::InteropServices::Marshal::Copy(ptr, input, 0, inputLength);
You have to convert pixel by pixel. I don't know how to convert it to FREBitmapData. Here are the examples you can following on msdn
I finally figured it out:
the code below doesn't deal with the 24to32 bit conversion though, but it actually works in my application quite well, so I thought, i might share it:
FREObject launch(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
{
System::Drawing::Bitmap^ windowsBitmap;
SKILLCamControl::CamControlForm^ form1;
form1 = gcnew SKILLCamControl::CamControlForm();
DialogResult dr;
// Show testDialog as a modal dialog and determine if DialogResult = OK.
dr = form1->ShowDialog();
if (dr == DialogResult::OK) {
windowsBitmap = form1->bitmap;
int bmpW = windowsBitmap->Width;
int bmpH = windowsBitmap->Height;
// we create a new instance of BitmapData here,
// as we cannot simply pass it over in the args,
// because we don't know it's correct size at extension creation
FREObject width;
FRENewObjectFromUint32( uint32_t(bmpW), &width);
FREObject height;
FRENewObjectFromUint32( uint32_t(bmpH), &height);
FREObject transparent;
FRENewObjectFromBool( uint32_t(0), &transparent);
FREObject fillColor;
FRENewObjectFromUint32( uint32_t(0xFF0000), &fillColor);
FREObject obs[4] = { width, height, transparent, fillColor };
FREObject freBitmap;
FRENewObject((uint8_t *)"flash.display.BitmapData", 4, obs, &freBitmap , NULL);
FREBitmapData2 freBitmapData;
FREAcquireBitmapData2(freBitmap, &freBitmapData);
// is inverted?
if (&freBitmapData.isInvertedY != (uint32_t*)(0) ) windowsBitmap->RotateFlip(RotateFlipType::RotateNoneFlipY);
int pixelSize = 4;
//Rect rect( 0, 0, freBitmap.width, freBitmap.height );
System::Drawing::Rectangle rect(0, 0, bmpW, bmpH);
BitmapData^ windowsBitmapData = windowsBitmap->LockBits(rect, ImageLockMode::ReadOnly, PixelFormat::Format32bppArgb);
for (int y = 0; y < bmpH ; y++)
{
//get pixels from each bitmap
byte* oRow = (byte*)windowsBitmapData->Scan0.ToInt32() + (y * windowsBitmapData->Stride);
byte* nRow = (byte*)freBitmapData.bits32 + (y * freBitmapData.lineStride32 * 4);
for (int x = 0; x < bmpW ; x++)
{
// set pixels
nRow[x * pixelSize] = oRow[x * pixelSize]; //B
nRow[x * pixelSize + 1] = oRow[x * pixelSize + 1]; //G
nRow[x * pixelSize + 2] = oRow[x * pixelSize + 2]; //R
}
}
// Free resources
FREReleaseBitmapData(freBitmap);
FREInvalidateBitmapDataRect(freBitmap, 0, 0, bmpW, bmpH);
windowsBitmap->UnlockBits(windowsBitmapData);
delete windowsBitmapData;
delete windowsBitmap;
return freBitmap;
}
else if (dr == DialogResult::Cancel)
{
return NULL;
}
return NULL;
}
I dont use C++ myself so this is not a full answer but just something to consider...
Bitmap data is universal raw pixel data. It should be passable within different software. Unless you are actually creating .BMP files with header etc??
...that will return a System::Drawing::Bitmap does this mean you have a bitmap's data held by C++ (as raw uncompressed RGBA pixels)? If so then just either put that inside a byteArray and send to AS3 or a if you can get that bitmap copied to the Windows clipboard then use AS3 to read from clipboard into a new AS3 Bitmap.
these might help you:
AS3: Copy image from clipboard
AS3: Serialize Bitmaps : Scroll down to the section ByteArray to BitmapData (for this to work you must first store the C++ bitmap bytes as a file call it what you want, example tempIMG.dat or myPIc.bin or whatever since file extension does not really matter just that you need a loadable URL).
I am trying to make a bitmap from scratch. I have a BYTE array (with known size) of RGB values and I would like to generate an HBITMAP.
For further clarification, the array of bytes I am working with is purely RGB values.
I have made sure that all variables are set and proper, and I believe that the issue has to do with lpvBits. I have been doing as much research for this in the past few days I have been unable to find anything that makes sense to me.
For testing purposes the width = 6 and height = 1
Code:
HBITMAP RayTracing::getBitmap(void){
BYTE * bytes = getPixels();
void * lpvBits = (void *)bytes;
HBITMAP hBMP = CreateBitmap(width, height, 1, 24, lpvBits);
return hBMP;
}
BYTE * RayTracing::getPixels(void){
Vec3 * vecs = display.getPixels();
BYTE * bytes;
bytes = new BYTE[(3 * width * height)];
for (unsigned int i = 0; i < (width * height); i++){
*bytes = static_cast<BYTE>(vecs->x);
bytes++;
*bytes = static_cast<BYTE>(vecs->y);
bytes++;
*bytes = static_cast<BYTE>(vecs->z);
bytes++;
vecs++;
}
return bytes;
}
You need to properly dword-align your array so each line is an even multiple of 4 bytes, and then skip those bytes when filling the array:
HBITMAP RayTracing::getBitmap(void)
{
BYTE * bytes = getPixels();
HBITMAP hBMP = CreateBitmap(width, height, 1, 24, bytes);
delete[] bytes;
return hBMP;
}
BYTE * RayTracing::getPixels(void)
{
Vec3 * vecs = display.getPixels(); // <-- don't forget to free if needed
int linesize = ((3 * width) + 3) & ~3; // <- 24bit pixels, width number of pixels, rounded to nearest dword boundary
BYTE * bytes = new BYTE[linesize * height];
for (unsigned int y = 0; y < height; y++)
{
BYTE *line = &bytes[linesize*y];
Vec3 *vec = &vecs[width*y];
for (unsigned int x = 0; x < width; x++)
{
*line++ = static_cast<BYTE>(vec->x);
*line++ = static_cast<BYTE>(vec->y);
*line++ = static_cast<BYTE>(vec->z);
++vec;
}
}
return bytes;
}
The third parameter of CreateBitmap should be 3, not 1. There are three color planes: Red, Green, and Blue.
Also, if you set the height to anything greater than one, you'll need to pad each row of pixels with zeroes to make the width a multiple of 4. So for a 6x2 image, after saving the 6*3 bytes for the first row, you'd need to save two zero bytes to make the row 20 bytes long.
So I'm trying to create some simple terrain using a bmp and I don't know how to access the pixel color to get it's RGB value to use as the height. I understand the concept, just not how to put it into practice. This is the code I have so far. Any help is very much appreciated!
CUSTOMVERTEX vecArray[256][256];
m_pSurface = nullptr;
D3DXIMAGE_INFO imageInfo;
ZeroMemory(&imageInfo, sizeof(D3DXIMAGE_INFO));
HRESULT hr = D3DXGetImageInfoFromFile(L"heightmap.bmp", &imageInfo);
_pDevice->CreateOffscreenPlainSurface(imageInfo.Width, imageInfo.Height, D3DFMT_X8R8G8B8, D3DPOOL_SCRATCH, &m_pSurface, 0);
hr = D3DXLoadSurfaceFromFile(m_pSurface, 0, 0, L"heightmap.bmp", 0, D3DX_FILTER_NONE, 0, &imageInfo);
D3DLOCKED_RECT lockRect;
ZeroMemory(&lockRect, sizeof(D3DLOCKED_RECT));
m_pSurface->LockRect(&lockRect, 0, D3DLOCK_READONLY);
int iNumPixels = imageInfo.Width * imageInfo.Height;
int iPixelsWidth = imageInfo.Width;
int iPixelsHeight = imageInfo.Height;
for (int i = 0; i < iPixelsWidth; ++i) // HORIZONTAL ROWS
{
for (int j = 0; j < iPixelsHeight; ++j) // VERTICAL ROWS
{
vecArray[i][j].x = (float)i;
vecArray[i][j].y = (float)j;
vecArray[i][j].z = ???? // Get Height from bmp
vecArray[i][j].color = D3DCOLOR_XRGB(255, 255, 255);
}
}
m_pSurface->UnlockRect();
The struct is define as:
struct CUSTOMVERTEX
{
FLOAT x, y, z; // The untransformed, 3D position for the vertex
DWORD color;
};
Once you have the pixels mapped into a buffer, interpolate the color value as an integer (range 0 to 255) to your height range. If you are using a 3-channel image (rgb) you will need to first average the channels to one value (ie. (r + g + b) / 3). That will give you a single value for the pixel.
See "linear interpolation" to convert from pixel range to height range. If you're asking how to access the pixel values themselves, that depends on the method used to load the bitmap.
I'm trying to get the Kinect depth camera pixels to overlay onto the RGB camera. I am using the C++ Kinect 1.0 SDK with an Xbox Kinect, OpenCV and trying to use the new "NuiImageGetColorPixelCoordinateFrameFromDepthPixelFrameAtResolution" method.
I have watched the image render itself in slow motion and looks as if pixels are being drawn multiple times in the one frame. It first draws itself from the top and left borders, then it gets to a point (you can see a 45 degree angle in there) where it starts drawing weird.
I have been trying to base my code off of the C# code written by Adam Smith at the MSDN forums but no dice. I have stripped out the overlay stuff and just want to draw the depth normalized depth pixels where it "should" be in the RGB image.
The image on the left is what I'm getting when trying to fit the depth image to RGB space, and the image on the right is the "raw" depth image as I like to see it. I was hoping this my method would create a similar image to the one on the right with slight distortions.
This is the code and object definitions that I have at the moment:
// From initialization
INuiSensor *m_pNuiInstance;
NUI_IMAGE_RESOLUTION m_nuiResolution = NUI_IMAGE_RESOLUTION_640x480;
HANDLE m_pDepthStreamHandle;
IplImage *m_pIplDepthFrame;
IplImage *m_pIplFittedDepthFrame;
m_pIplDepthFrame = cvCreateImage(cvSize(640, 480), 8, 1);
m_pIplFittedDepthFrame = cvCreateImage(cvSize(640, 480), 8, 1);
// Method
IplImage *Kinect::GetRGBFittedDepthFrame() {
static long *pMappedBits = NULL;
if (!pMappedBits) {
pMappedBits = new long[640*480*2];
}
NUI_IMAGE_FRAME pNuiFrame;
NUI_LOCKED_RECT lockedRect;
HRESULT hr = m_pNuiInstance->NuiImageStreamGetNextFrame(m_pDepthStreamHandle, 0, &pNuiFrame);
if (FAILED(hr)) {
// return the older frame
return m_pIplFittedDepthFrame;
}
bool hasPlayerData = HasSkeletalEngine(m_pNuiInstance);
INuiFrameTexture *pTexture = pNuiFrame.pFrameTexture;
pTexture->LockRect(0, &lockedRect, NULL, 0);
if (lockedRect.Pitch != 0) {
cvZero(m_pIplFittedDepthFrame);
hr = m_pNuiInstance->NuiImageGetColorPixelCoordinateFrameFromDepthPixelFrameAtResolution(
m_nuiResolution,
NUI_IMAGE_RESOLUTION_640x480,
640 * 480, /* size is previous */ (unsigned short*) lockedRect.pBits,
(640 * 480) * 2, /* size is previous */ pMappedBits);
if (FAILED(hr)) {
return m_pIplFittedDepthFrame;
}
for (int i = 0; i < lockedRect.size; i++) {
unsigned char* pBuf = (unsigned char*) lockedRect.pBits + i;
unsigned short* pBufS = (unsigned short*) pBuf;
unsigned short depth = hasPlayerData ? ((*pBufS) & 0xfff8) >> 3 : ((*pBufS) & 0xffff);
unsigned char intensity = depth > 0 ? 255 - (unsigned char) (256 * depth / 0x0fff) : 0;
long
x = pMappedBits[i], // tried with *(pMappedBits + (i * 2)),
y = pMappedBits[i + 1]; // tried with *(pMappedBits + (i * 2) + 1);
if (x >= 0 && x < m_pIplFittedDepthFrame->width && y >= 0 && y < m_pIplFittedDepthFrame->height) {
m_pIplFittedDepthFrame->imageData[x + y * m_pIplFittedDepthFrame->widthStep] = intensity;
}
}
}
pTexture->UnlockRect(0);
m_pNuiInstance->NuiImageStreamReleaseFrame(m_pDepthStreamHandle, &pNuiFrame);
return(m_pIplFittedDepthFrame);
}
Thanks
I have found that the problem was that the loop,
for (int i = 0; i < lockedRect.size; i++) {
// code
}
was iterating on a per-byte basis, not on a per-short (2 bytes) basis. Since lockedRect.size returns the number of bytes the fix was simply changing the increment to i += 2, even better would be changing it to sizeof(short), like so,
for (int i = 0; i < lockedRect.size; i += sizeof(short)) {
// code
}
I need to create a CImage from a byte array (actually, its an array of unsigned char, but I can cast to whatever form is necessary). The byte array is in the form "RGBRGBRGB...". The new image needs to contain a copy of the image bytes, rather than using the memory of the byte array itself.
I have tried many different ways of achieving this -- including going through various HBITMAP creation functions, trying to use BitBlt -- and nothing so far has worked.
To test whether the function works, it should pass this test:
BYTE* imgBits;
int width;
int height;
int Bpp; // BYTES per pixel (e.g. 3)
getImage(&imgBits, &width, &height, &Bpp); // get the image bits
// This is the magic function I need!!!
CImage img = createCImage(imgBits, width, height, Bpp);
// Test the image
BYTE* data = img.GetBits(); // data should now have the same data as imgBits
All implementations of createCImage() so far have ended up with data pointing to an empty (zero filled) array.
CImage supports DIBs quite neatly and has a SetPixel() method so you could presumably do something like this (uncompiled, untested code ahead!):
CImage img;
img.Create(width, height, 24 /* bpp */, 0 /* No alpha channel */);
int nPixel = 0;
for(int row = 0; row < height; row++)
{
for(int col = 0; col < width; col++)
{
BYTE r = imgBits[nPixel++];
BYTE g = imgBits[nPixel++];
BYTE b = imgBits[nPixel++];
img.SetPixel(row, col, RGB(r, g, b));
}
}
Maybe not the most efficient method but I should think it is the simplest approach.
Use memcpy to copy the data, then SetDIBits or SetDIBitsToDevice depending on what you need to do. Take care though, the scanlines of the raw image data are aligned on 4-byte boundaries (IIRC, it's been a few years since I did this) so the data you get back from GetDIBits will never be exactly the same as the original data (well it might, depending on the image size).
So most likely you will need to memcpy scanline by scanline.
Thanks everyone, I managed to solve it in the end with your help. It mainly involved #tinman and #Roel's suggestion to use SetDIBitsToDevice(), but it involved a bit of extra bit-twiddling and memory management, so I thought I'd share my end-point here.
In the code below, I assume that width, height and Bpp (Bytes per pixel) are set, and that data is a pointer to the array of RGB pixel values.
// Create the header info
bmInfohdr.biSize = sizeof(BITMAPINFOHEADER);
bmInfohdr.biWidth = width;
bmInfohdr.biHeight = -height;
bmInfohdr.biPlanes = 1;
bmInfohdr.biBitCount = Bpp*8;
bmInfohdr.biCompression = BI_RGB;
bmInfohdr.biSizeImage = width*height*Bpp;
bmInfohdr.biXPelsPerMeter = 0;
bmInfohdr.biYPelsPerMeter = 0;
bmInfohdr.biClrUsed = 0;
bmInfohdr.biClrImportant = 0;
BITMAPINFO bmInfo;
bmInfo.bmiHeader = bmInfohdr;
bmInfo.bmiColors[0].rgbBlue=255;
// Allocate some memory and some pointers
unsigned char * p24Img = new unsigned char[width*height*3];
BYTE *pTemp,*ptr;
pTemp=(BYTE*)data;
ptr=p24Img;
// Convert image from RGB to BGR
for (DWORD index = 0; index < width*height ; index++)
{
unsigned char r = *(pTemp++);
unsigned char g = *(pTemp++);
unsigned char b = *(pTemp++);
*(ptr++) = b;
*(ptr++) = g;
*(ptr++) = r;
}
// Create the CImage
CImage im;
im.Create(width, height, 24, NULL);
HDC dc = im.GetDC();
SetDIBitsToDevice(dc, 0,0,width,height,0,0, 0, height, p24Img, &bmInfo, DIB_RGB_COLORS);
im.ReleaseDC();
delete[] p24Img;
Here is a simpler solution. You can use GetPixelAddress(...) instead of all this BITMAPHEADERINFO and SedDIBitsToDevice. Another problem I have solved was with 8-bit images, which need to have the color table defined.
CImage outImage;
outImage.Create(width, height, channelCount * 8);
int lineSize = width * channelCount;
if (channelCount == 1)
{
// Define the color table
RGBQUAD* tab = new RGBQUAD[256];
for (int i = 0; i < 256; ++i)
{
tab[i].rgbRed = i;
tab[i].rgbGreen = i;
tab[i].rgbBlue = i;
tab[i].rgbReserved = 0;
}
outImage.SetColorTable(0, 256, tab);
delete[] tab;
}
// Copy pixel values
// Warining: does not convert from RGB to BGR
for ( int i = 0; i < height; i++ )
{
void* dst = outImage.GetPixelAddress(0, i);
const void* src = /* put the pointer to the i'th source row here */;
memcpy(dst, src, lineSize);
}