How to Render a Bitmap from BITMAPINFOHEADER and BYTE using Direct2D - c++

I am trying to create a C++ application which actually captures the bitmap from magnifier and render it using Direct 2d.
I am currently having the code to save the bitmap from magnifier to a file. but what I need is to do is to draw that bitmap to my window using direct 2d instead of saving it to file.
The magnifier returns image as struct in the form of MAGIMAGEHEADER and I was able to obtain BITMAPINFOHEADER and byte from it. I need to render it to a window using direct 2D.
Here is the code used to obtain the BITMAPINFOHEADER and bytes from Magnifier API
BOOL MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader,RECT unclipped, RECT clipped, HRGN dirty)
{
// Setup the bitmap info header
bmif.biSize = sizeof(BITMAPINFOHEADER);
bmif.biHeight = srcheader.height;
bmif.biWidth = srcheader.width;
bmif.biSizeImage = srcheader.cbSize;
bmif.biPlanes = 1;
bmif.biBitCount = (WORD)(bmif.biSizeImage / bmif.biHeight / bmif.biWidth * 8);
bmif.biCompression = BI_RGB;
// Prepare the buffer
if (pData != NULL)
{
delete pData;
pData = NULL;
}
pData = (BYTE*)malloc(bmif.biSizeImage);
memcpy(pData, srcdata, bmif.biSizeImage);
// The data bit is in top->bottom order, so we convert it to bottom->top order
LONG lineSize = bmif.biWidth * bmif.biBitCount / 8;
BYTE* pLineData = new BYTE[lineSize];
BYTE* pStart;
BYTE* pEnd;
LONG lineStart = 0;
LONG lineEnd = bmif.biHeight - 1;
while (lineStart < lineEnd)
{
// Get the address of the swap line
pStart = pData + (lineStart * lineSize);
pEnd = pData + (lineEnd * lineSize);
// Swap the top with the bottom
memcpy(pLineData, pStart, lineSize);
memcpy(pStart, pEnd, lineSize);
memcpy(pEnd, pLineData, lineSize);
// Adjust the line index
lineStart++;
lineEnd--;
}
delete pLineData;
// Set the flag to say that the callback function is finished
bCallbacked = TRUE;
return TRUE;
}
Here the variable bmif is BITMAPINFOHEADER and pData is the Bytes
Is there any way to achieve this?

if you have the HBITMAP handle, you can do this:
The the size of your image using: ::GetObject(hBmp, sizeof(BITMAP), &bmpSizeInfo);
fill a BITMAPINFO like this:
memset(&bmpData, 0, sizeof(BITMAPINFO));
bmpData.bmiHeader.biSize = sizeof(bmpData.bmiHeader);
bmpData.bmiHeader.biHeight = -bmpSizeInfo.bmHeight;
bmpData.bmiHeader.biWidth = bmpSizeInfo.bmWidth;
bmpData.bmiHeader.biPlanes = bmpSizeInfo.bmPlanes;
bmpData.bmiHeader.biBitCount = bmpSizeInfo.bmBitsPixel;
create enough heap memory to hold the data for your bitmap:
pBuff = new char[bmpSizeInfo.bmWidth * bmpSizeInfo.bmHeight * 4];
Get the bitmap data like this:
::GetDIBits(hDc, hBmp, 0, bmpSizeInfo.bmHeight, (void*)pBuff, &bmpData, DIB_RGB_COLORS);
Create a D2D1_BITMAP_PROPERTIES and fill it like this:
bmpPorp.dpiX = 0.0f;
bmpPorp.dpiY = 0.0f;
bmpPorp.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
bmpPorp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
Using your render target turn the data into ID2D1Bitmap
pRT->CreateBitmap(bmpSize, pBuff, 4 * bmpSizeInfo.bmWidth, bmpPorp, &pBmpFromH);

Here is how you can use mag's bitmap with Direct2D. You don't need BITMAPINFOHEADER since mag format is the same as DXGI_FORMAT_B8G8R8A8_UNORM:
BOOL MagImageScaling(HWND hwnd, void* srcdata, MAGIMAGEHEADER srcheader, void* destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty)
{
// note: all this (dc, surface, targte) can be created only once as long as the D3D device isn't reset
ComPtr<ID2D1DeviceContext> dc;
HR(d2Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, dc.GetAddressOf()));
ComPtr<IDXGISurface2> surface;
HR(swapChain->GetBuffer(0, IID_PPV_ARGS(&surface)));
ComPtr<ID2D1Bitmap1> target;
HR(dc->CreateBitmapFromDxgiSurface(surface.Get(), NULL, target.GetAddressOf()));
dc->SetTarget(target.Get());
D2D1_BITMAP_PROPERTIES properties = {};
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
// note: this is ok as srcheader.format (GUID_WICPixelFormat32bppRGBA) is compatible
properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
D2D1_SIZE_U size = {};
size.width = srcheader.width;
size.height = srcheader.height;
ComPtr<ID2D1Bitmap> bitmap;
HR(dc->CreateBitmap(size, properties, bitmap.GetAddressOf()));
HR(bitmap->CopyFromMemory(NULL, srcdata, srcheader.stride));
dc->BeginDraw();
// note: we don't call this because we draw on the whole render target
//dc->Clear();
dc->DrawBitmap(bitmap.Get());
HR(dc->EndDraw());
HR(swapChain->Present(1, 0));
return TRUE;
}

Related

Copy HICON / HCURSOR in to Byte Array

Is any way can we convert HICON or HCURSOR in to Byte array, I googled in all the way I didnt found a single generic solution, below I tried to convert HICON color and mask BITMAP to byte array and sending this through socket and creating my icon using CreateIconIndirect API but instead of doing all this stuff if I can able to send a HICON directly that will be good.
int ProcessMouse()
{
BYTE m_hbmMaskBits[70000];
BYTE m_hbmColorBits[70000];
CURSORINFO CursorInfo;
CursorInfo.cbSize = sizeof(CursorInfo);
GetCursorInfo(&CursorInfo);
ICONINFO iconInfo;
if (!GetIconInfo(CursorInfo.hCursor, &iconInfo))
{
MessageBox(NULL, _T("CreateCursor Failed"),_T("message"),MB_OK|MB_SYSTEMMODAL);
}
bool isColorShape = (iconInfo.hbmColor != NULL);
bool isMaskShape = (iconInfo.hbmMask != NULL);
LONG cbSize = 0; int nWidth = 0; int nHeight = 0; int actualHeight = 0; int bmPlanes = 0;
int bmBitsPixel = 0; int xHotspot = 0; int yHotspot = 0; int widthBytes = 0;
// Return width,height,actualheight,bmplanes,bmbitspixel,hotsopt of cursor.
if(!CopyIconInfo( CursorInfo.hCursor,
nWidth,
nHeight,
actualHeight,
bmPlanes,
bmBitsPixel,
xHotspot,
yHotspot,
widthBytes ))
{
return 0;
}
std::vector<BYTE> bColor;
std::vector<BYTE> bMask;
int sz_hbmColor = 0;
int sz_hbmMask = 0;
_tempWidth = nWidth;
_tempHeight = nHeight;
//If HCURSOR have both color and mask go with regular approach.
if(isColorShape)
{
//Convert iconInfo.hbmColor HBITMAP to Byte array.
bColor = HBIMAPtoBYTE(iconInfo.hbmColor,sz_hbmColor);
//Convert iconInfo.hbmMask HBITMAP to Byte array.
bMask = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmMask);
}
// If HCURSOR have only mask data go with new approach(split mask bitmap to color and mask).
else if(isMaskShape)
{
std::vector<BYTE> bSrcBitmap;
int sz_hbmBitmap = 0;
//Convert iconInfo.hbmMask HBITMAP to Byte array.
bSrcBitmap = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmBitmap);
sz_hbmColor = sz_hbmBitmap/2;
sz_hbmMask = sz_hbmBitmap/2;
bMask.resize(bMask.size() + sz_hbmBitmap/2);
memcpy(&bMask[bSrcBitmap.size() - sz_hbmBitmap], &bSrcBitmap[0], sz_hbmBitmap/2 * sizeof(BYTE));
bColor.resize(bColor.size() + sz_hbmBitmap/2);
memcpy(&bColor[bSrcBitmap.size() - sz_hbmBitmap], &bSrcBitmap[sz_hbmBitmap/2], sz_hbmBitmap/2 * sizeof(BYTE));
//Clear at end.
bSrcBitmap.clear();
}
try{
err = memcpy_s((m_hbmMaskBits), sz_hbmMask, &(bMask[0]), sz_hbmMask );
err = memcpy_s((m_hbmColorBits),sz_hbmColor,&(bColor[0]),sz_hbmColor);
//Clear at end.
bMask.clear();
bColor.clear();
return 1;
}catch(...) {
if(err) {
MessageBox(NULL, _T("memcopy failed at mask or color copy"),_T("message"),MB_OK|MB_SYSTEMMODAL);
}
}
}
I tried in below way but it doesn't support for few monochrome cursors.
PICTDESC pd = {sizeof(pd), PICTYPE_ICON};
pd.icon.hicon = CursorInfo.hCursor;
CComPtr<IPicture> pPict = NULL;
CComPtr<IStream> pStrm = NULL;
BOOL res = FALSE;
res = SUCCEEDED( ::CreateStreamOnHGlobal(NULL, TRUE, &pStrm) );
res = SUCCEEDED( ::OleCreatePictureIndirect(&pd, IID_IPicture, TRUE, (void**)&pPict) );
res = SUCCEEDED( pPict->SaveAsFile( pStrm, TRUE, &cbSize ) );
if( res )
{
// rewind stream to the beginning
LARGE_INTEGER li = {0};
pStrm->Seek(li, STREAM_SEEK_SET, NULL);
// write to file
DWORD dwWritten = 0, dwRead = 0, dwDone = 0;
while( dwDone < cbSize )
{
if( SUCCEEDED(pStrm->Read(bCursorBuff, sizeof(bCursorBuff), &dwRead)) )
{
dwDone += dwRead;
}
}
_ASSERTE(dwDone == cbSize);
}
//End of Cursor image
pStrm.Release();
pPict.Release();
HICON and HCURSOR are system handles, so they work only on the current machine.
Over network only the actual data can be sent (bitmap bytes). Then that machine can create its own handles for it.
Using the HBITMAP bytes is the correct approach. You can find some details here:
How to convert HICON to HBITMAP in VC++?
You can get the raw HBITMAP bits using GetDIBits(). More information: C++/Win32: How to get the alpha channel from an HBITMAP?
Below Code works only for color cursor for monochrome cursor use to
convert 16bpp bitmap to 32bpp bitmap and use same code its works.
bool saveToMemory(HICON hIcon, BYTE* buffer, DWORD& nSize)
{
if (hIcon == 0)
return FALSE;
int * pImageOffset;
int nNumIcons = 1;
nSize = 0;
// copy iconheader first of all
ICONHEADER iconheader;
// Setup the icon header
iconheader.idReserved = 0; // Must be 0
iconheader.idType = 1; // Type 1 = ICON (type 2 = CURSOR)
iconheader.idCount = nNumIcons; // number of ICONDIRs
// save to memory
memcpy(buffer, &iconheader, sizeof(iconheader));
nSize += sizeof(iconheader); // update
//
// Leave space for the IconDir entries
//
nSize += sizeof(ICONDIR);
pImageOffset = (int *)malloc(nNumIcons * sizeof(int));
ICONINFO iconInfo;
BITMAP bmpColor, bmpMask;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
// record the file-offset of the icon image for when we write the icon directories
pImageOffset[0] = nSize;
// bitmapinfoheader + colortable
//WriteIconImageHeader(hFile, &bmpColor, &bmpMask);
BITMAPINFOHEADER biHeader;
UINT nImageBytes;
// calculate how much space the COLOR and MASK bitmaps take
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
// write the ICONIMAGE to disk (first the BITMAPINFOHEADER)
ZeroMemory(&biHeader, sizeof(biHeader));
// Fill in only those fields that are necessary
biHeader.biSize = sizeof(biHeader);
biHeader.biWidth = bmpColor.bmWidth;
biHeader.biHeight = bmpColor.bmHeight * 2; // height of color+mono
biHeader.biPlanes = bmpColor.bmPlanes;
biHeader.biBitCount = bmpColor.bmBitsPixel;
biHeader.biSizeImage = nImageBytes;
// write the BITMAPINFOHEADER
//WriteFile(hFile, &biHeader, sizeof(biHeader), &nWritten, 0);
memcpy(&buffer[nSize], &biHeader, sizeof(biHeader));
nSize += sizeof(biHeader);
// save color and mask bitmaps
saveIconData(buffer, nSize, iconInfo.hbmColor);
saveIconData(buffer, nSize, iconInfo.hbmMask);
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
//
// Lastly, save the icon directories.
//
DWORD size = saveIconDirectoryEntry(buffer, sizeof(ICONHEADER), pImageOffset[0], hIcon);
free(pImageOffset);
return TRUE;
}
//
// Return the number of BYTES the bitmap will take ON DISK
//
static UINT NumBitmapBytes(BITMAP *pBitmap)
{
int nWidthBytes = pBitmap->bmWidthBytes;
// bitmap scanlines MUST be a multiple of 4 bytes when stored
// inside a bitmap resource, so round up if necessary
if (nWidthBytes & 3)
nWidthBytes = (nWidthBytes + 4) & ~3;
return nWidthBytes * pBitmap->bmHeight;
}
// same as WriteIconData but save to memory
static UINT saveIconData(BYTE* buffer, DWORD& nSize, HBITMAP hBitmap)
{
BITMAP bmp;
int i;
BYTE * pIconData;
UINT nBitmapBytes;
DWORD nWritten = 0;
GetObject(hBitmap, sizeof(BITMAP), &bmp);
nBitmapBytes = NumBitmapBytes(&bmp);
pIconData = (BYTE *)malloc(nBitmapBytes);
GetBitmapBits(hBitmap, nBitmapBytes, pIconData);
// bitmaps are stored inverted (vertically) when on disk..
// so write out each line in turn, starting at the bottom + working
// towards the top of the bitmap. Also, the bitmaps are stored in packed
// in memory - scanlines are NOT 32bit aligned, just 1-after-the-other
for (i = bmp.bmHeight - 1; i >= 0; i--)
{
// Write the bitmap scanline
// save to memory
memcpy(&buffer[nSize], pIconData + (i * bmp.bmWidthBytes), bmp.bmWidthBytes);
nSize += bmp.bmWidthBytes;
nWritten += bmp.bmWidthBytes;
}
free(pIconData);
return nWritten;
}
//
// same as WriteIconDirectoryEntry but save to memory
//
static UINT saveIconDirectoryEntry(BYTE* buffer, DWORD pos, int imageOffset, HICON hIcon)
{
ICONINFO iconInfo;
ICONDIR iconDir;
BITMAP bmpColor;
BITMAP bmpMask;
DWORD nWritten = 0;
UINT nColorCount;
UINT nImageBytes;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
if (bmpColor.bmBitsPixel >= 8)
nColorCount = 0;
else
nColorCount = 1 << (bmpColor.bmBitsPixel * bmpColor.bmPlanes);
// Create the ICONDIR structure
iconDir.bWidth = (BYTE)bmpColor.bmWidth;
iconDir.bHeight = (BYTE)bmpColor.bmHeight;
iconDir.bColorCount = nColorCount;
iconDir.bReserved = 0;
iconDir.wPlanes = bmpColor.bmPlanes;
iconDir.wBitCount = bmpColor.bmBitsPixel;
iconDir.dwBytesInRes = sizeof(BITMAPINFOHEADER) + nImageBytes;
iconDir.dwImageOffset = imageOffset;
// save to memory
memcpy(&buffer[pos], &iconDir, sizeof(iconDir));
nWritten += sizeof(iconDir);
// Free resources
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
return nWritten;
}
I was able to do so by calling GetDIBits() twice, once to get the actual details of the cursor images and another time to get the pixels.
You can apply this code for the color and mask, just be aware that it only returns 32x32px cursors, also only the first frame, even if the size is configured for something else.
var windowDeviceContext = User32.GetWindowDC(IntPtr.Zero);
//Initialize the bitmap header and calculate its size.
var maskHeader = new BitmapInfoHeader();
maskHeader.Size = (uint) Marshal.SizeOf(maskHeader);
//Gets the image details.
Gdi32.GetDIBits(windowDeviceContext, iconInfo.Mask, 0, 0, null, ref maskHeader, DibColorModes.RgbColors);
//If there's any data, get it.
if (maskHeader.Height != 0)
{
//To prevent the cursor image from being inverted.
maskHeader.Height *= -1;
var maskBuffer = new byte[maskHeader.SizeImage];
Gdi32.GetDIBits(windowDeviceContext, iconInfo.Mask, 0, (uint) maskHeader.Height, maskBuffer, ref maskHeader, DibColorModes.RgbColors);
}
It's C#, but easily converted to your language of choice.

DirectX Partial Screen Capture

I am trying to create a program that will capture a full screen directx application, look for a specific set of pixels on the screen and if it finds it then draw an image on the screen.
I have been able to set up the application to capture the screen the directx libraries using the code the answer for this question Capture screen using DirectX
In this example the code saves to the harddrive using the IWIC libraries. I would rather manipulate the pixels instead of saving it.
After I have captured the screen and have a LPBYTE of the entire screen pixels I am unsure how to crop it to the region I want and then being able to manipulate the pixel array. Is it just a multi dimensional byte array?
The way I think I should do it is
Capture screen to IWIC bitmap (done).
Convert IWIC bitmap to ID2D1 bitmap using ID2D1RenderTarget::CreateBitmapFromWicBitmap
Create new ID2D1::Bitmap to store partial image.
Copy region of the ID2D1 bitmap to a new bitmap using ID2D1::CopyFromBitmap.
Render back onto screen using ID2D1 .
Any help on any of this would be so much appreciated.
Here is a modified version of the original code that only captures a portion of the screen into a buffer, and also gives back the stride. Then it browses all the pixels, dumps their colors as a sample usage of the returned buffer.
In this sample, the buffer is allocated by the function, so you must free it once you've used it:
// sample usage
int main()
{
LONG left = 10;
LONG top = 10;
LONG width = 100;
LONG height = 100;
LPBYTE buffer;
UINT stride;
RECT rc = { left, top, left + width, top + height };
Direct3D9TakeScreenshot(D3DADAPTER_DEFAULT, &buffer, &stride, &rc);
// In 32bppPBGRA format, each pixel is represented by 4 bytes
// with one byte each for blue, green, red, and the alpha channel, in that order.
// But don't forget this is all modulo endianness ...
// So, on Intel architecture, if we read a pixel from memory
// as a DWORD, it's reversed (ARGB). The macros below handle that.
// browse every pixel by line
for (int h = 0; h < height; h++)
{
LPDWORD pixels = (LPDWORD)(buffer + h * stride);
for (int w = 0; w < width; w++)
{
DWORD pixel = pixels[w];
wprintf(L"#%02X#%02X#%02X#%02X\n", GetBGRAPixelAlpha(pixel), GetBGRAPixelRed(pixel), GetBGRAPixelGreen(pixel), GetBGRAPixelBlue(pixel));
}
}
// get pixel at 50, 50 in the buffer, as #ARGB
DWORD pixel = GetBGRAPixel(buffer, stride, 50, 50);
wprintf(L"#%02X#%02X#%02X#%02X\n", GetBGRAPixelAlpha(pixel), GetBGRAPixelRed(pixel), GetBGRAPixelGreen(pixel), GetBGRAPixelBlue(pixel));
SavePixelsToFile32bppPBGRA(width, height, stride, buffer, L"test.png", GUID_ContainerFormatPng);
LocalFree(buffer);
return 0;;
}
#define GetBGRAPixelBlue(p) (LOBYTE(p))
#define GetBGRAPixelGreen(p) (HIBYTE(p))
#define GetBGRAPixelRed(p) (LOBYTE(HIWORD(p)))
#define GetBGRAPixelAlpha(p) (HIBYTE(HIWORD(p)))
#define GetBGRAPixel(b,s,x,y) (((LPDWORD)(((LPBYTE)b) + y * s))[x])
int main()
HRESULT Direct3D9TakeScreenshot(UINT adapter, LPBYTE *pBuffer, UINT *pStride, const RECT *pInputRc = nullptr)
{
if (!pBuffer || !pStride) return E_INVALIDARG;
HRESULT hr = S_OK;
IDirect3D9 *d3d = nullptr;
IDirect3DDevice9 *device = nullptr;
IDirect3DSurface9 *surface = nullptr;
D3DPRESENT_PARAMETERS parameters = { 0 };
D3DDISPLAYMODE mode;
D3DLOCKED_RECT rc;
*pBuffer = NULL;
*pStride = 0;
// init D3D and get screen size
d3d = Direct3DCreate9(D3D_SDK_VERSION);
HRCHECK(d3d->GetAdapterDisplayMode(adapter, &mode));
LONG width = pInputRc ? (pInputRc->right - pInputRc->left) : mode.Width;
LONG height = pInputRc ? (pInputRc->bottom - pInputRc->top) : mode.Height;
parameters.Windowed = TRUE;
parameters.BackBufferCount = 1;
parameters.BackBufferHeight = height;
parameters.BackBufferWidth = width;
parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
parameters.hDeviceWindow = NULL;
// create device & capture surface (note it needs desktop size, not our capture size)
HRCHECK(d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &parameters, &device));
HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr));
// get pitch/stride to compute the required buffer size
HRCHECK(surface->LockRect(&rc, pInputRc, 0));
*pStride = rc.Pitch;
HRCHECK(surface->UnlockRect());
// allocate buffer
*pBuffer = (LPBYTE)LocalAlloc(0, *pStride * height);
if (!*pBuffer)
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
// get the data
HRCHECK(device->GetFrontBufferData(0, surface));
// copy it into our buffer
HRCHECK(surface->LockRect(&rc, pInputRc, 0));
CopyMemory(*pBuffer, rc.pBits, rc.Pitch * height);
HRCHECK(surface->UnlockRect());
cleanup:
if (FAILED(hr))
{
if (*pBuffer)
{
LocalFree(*pBuffer);
*pBuffer = NULL;
}
*pStride = 0;
}
RELEASE(surface);
RELEASE(device);
RELEASE(d3d);
return hr;
}

How to display jpg image by ImageMagicK in MFC?

I have got a hard time to display jpg file at VIEW class by ImageMagicK in MFC.
The following code are my code. the problem is looking as following
image
void CTestview::DoDisplayImage(void)
{
CDC *pDC = GetDC();
if (fileposition != NULL)
AfxMessageBox(fileposition);
char m_szAppPath[255];
//m_szAppPath = (char) * fileposition;
(void)MagickCore::SetClientPath(fileposition);
InitializeMagick(fileposition);
MagickCore::RegisterStaticModules();
// Parse command line for standard shell commands, DDE, file open
Image m_Image;
m_Image.read(fileposition);
/*
char m_szAppPath[255];
(void) MagickCore::SetClientPath(m_szAppPath);
InitializeMagick(m_szAppPath);
MagickCore::RegisterStaticModules();
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
Image master;
master.read("D:\\work\\mfc_test5\\q1.jpg");
*/
if (pDC != NULL && m_Image.isValid())
{
CRect rectClient;
GetClientRect(rectClient);
// Clear the background
pDC->FillSolidRect(rectClient, pDC->GetBkColor());
// Set up the Windows bitmap header
BITMAPINFOHEADER bmi;
bmi.biSize = sizeof(BITMAPINFOHEADER); // Size of structure
bmi.biWidth = m_Image.columns(); // Bitmaps width in pixels
bmi.biHeight = (-1)*m_Image.rows(); // Bitmaps height n pixels
bmi.biPlanes = 1; // Number of planes in the image
bmi.biBitCount = 32; // The number of bits per pixel
bmi.biCompression = BI_RGB; // The type of compression used
bmi.biSizeImage = 0; // The size of the image in bytes
bmi.biXPelsPerMeter = 0; // Horizontal resolution
bmi.biYPelsPerMeter = 0; // Veritical resolution
bmi.biClrUsed = 0; // Number of colors actually used
bmi.biClrImportant = 0; // Colors most important
// Extract the pixels from Magick++ image object and convert to a DIB section
PixelPacket *pPixels = m_Image.getPixels(0, 0, m_Image.columns(), m_Image.rows());
RGBQUAD *prgbaDIB = 0;
HBITMAP hBitmap = CreateDIBSection
(
pDC->m_hDC, // handle to device context
(BITMAPINFO *)&bmi, // pointer to structure containing bitmap size, format, and color data
DIB_RGB_COLORS, // color data type indicator: RGB values or palette indices
(void**)&prgbaDIB, // pointer to variable to receive a pointer to the bitmap's bit values
NULL, // optional handle to a file mapping object
0 // offset to the bitmap bit values within the file mapping object
);
if (!hBitmap)
return;
unsigned long nPixels = m_Image.columns() * m_Image.rows();
RGBQUAD *pDestPixel = prgbaDIB;
// Transfer pixels, scaling to Quantum
/*
for( unsigned long nPixelCount = nPixels; nPixelCount ; nPixelCount-- )
{
pDestPixel->rgbRed = MagickCore::GetPixelRed(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbGreen = MagickCore::GetPixelGreen(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbBlue = MagickCore::GetPixelBlue(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbReserved = 0;
++pDestPixel;
pPixels+=MagickCore::GetPixelChannels(m_Image.constImage());
}
*/
for (int i = 0; i < 256; i++)
{
pPixels->blue = (BYTE)i;
pPixels->green = (BYTE)i;
pPixels->red = (BYTE)i;
pPixels->opacity = 0;
pPixels++;
}
// Now copy the bitmap to device.
HDC hMemDC = CreateCompatibleDC(pDC->m_hDC);
SelectObject(hMemDC, hBitmap);
BitBlt(pDC->m_hDC, 0, 0, m_Image.columns(), m_Image.rows(), hMemDC, 0, 0, SRCCOPY);
DeleteObject(hMemDC);
}
}
Especially, at the following code does not sure.
What should I do for solving this problem?
for( int i = 0 ; i < 256 ; i++ )
{
pPixels->blue = (BYTE)i;
pPixels->green = (BYTE)i;
pPixels->red = (BYTE)i;
pPixels->opacity = 0;
pPixels++;
}
Would you please help me how do I do for solving this problem?
As you can see, the output image has black.

Monochrome Bitmap C++ GDIPlus

I'm currently having issues displaying a monochrome bitmap to my form. I belive that somehow I am setting up my bitmap incorrectly, but I can't see where that would be at the moment. All I have to do to "fix" this issue is set the biBitCount to 24, but then when I call DWORD d = GetCharacterPlacementA latter on I'm seeing corrupted memory latter on. So I'm trying to keep my original desire for a monochrome bitmap and not leak memory.
Currently I'm storing my bitmap information in the standard variables:
BITMAPINFO bi24BitInfo;
HDC hMemoryDC;
HBITMAP hMemoryBitmap;
HGDIOBJ hDefaultBitmap;
HBITMAP hGdiBitmap;
This is how I am setting up the bitmap:
hMemoryDC = CreateCompatibleDC(NULL);
bi24BitInfo.bmiHeader.biSize = sizeof(bi24BitInfo.bmiHeader); // size of this struct
bi24BitInfo.bmiHeader.biWidth = 600;//sizeRect.cx; // width of window
bi24BitInfo.bmiHeader.biHeight = 600;//sizeRect.cy; // height of window
bi24BitInfo.bmiHeader.biPlanes = 1;
bi24BitInfo.bmiHeader.biBitCount = 1; // monochrome // rgb 8 bytes for each component(3)
bi24BitInfo.bmiHeader.biCompression = BI_RGB; // Means its uncompressed. Has nothing to do with color.
bi24BitInfo.bmiHeader.biSizeImage = 0;
bi24BitInfo.bmiHeader.biXPelsPerMeter = pelsPerMeter;
bi24BitInfo.bmiHeader.biYPelsPerMeter = pelsPerMeter;
bi24BitInfo.bmiHeader.biClrUsed = 2;
bi24BitInfo.bmiHeader.biClrImportant = 0;
bi24BitInfo.bmiColors[0].rgbBlue = 0;
bi24BitInfo.bmiColors[0].rgbRed = 0;
bi24BitInfo.bmiColors[0].rgbGreen = 0;
bi24BitInfo.bmiColors[0].rgbReserved = 0;
bi24BitInfo.bmiColors[1].rgbBlue = (BYTE)0xFF;
bi24BitInfo.bmiColors[1].rgbRed = (BYTE)0xFF;
bi24BitInfo.bmiColors[1].rgbGreen = (BYTE)0xFF;
bi24BitInfo.bmiColors[1].rgbReserved = 0;
// Create the memory bitmap
if (hMemoryBitmap != NULL)
{
DeleteObject(hMemoryBitmap);
}
hMemoryBitmap = CreateCompatibleBitmap(hMemoryDC, 600, 600);
hDefaultBitmap = SelectObject(hMemoryDC, hMemoryBitmap);
HGDIOBJ hOldFont = SelectObject(hMemoryDC, GetStockObject(NULL_BRUSH));
// Do not fill background
int nOldBkMode = GetBkMode(hMemoryDC);
SetBkMode(hMemoryDC, TRANSPARENT);
int nRet(0);
if (hDIB != NULL)
{
GlobalFree(hDIB);
}
DWORD dwBmpSize = ((bi24BitInfo.bmiHeader.biWidth * bi24BitInfo.bmiHeader.biBitCount + 31) / 32) * 4 * bi24BitInfo.bmiHeader.biHeight;
//DWORD dwBmpSize = ((bi24BitInfo.bmiHeader.biWidth * bi24BitInfo.bmiHeader.biHeight)/8);
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that call HeapAlloc using a handle
// to the process's default heap. Therefore, GlobalAlloc and LocalAlloc have greater overhead than HeapAlloc.
hDIB = GlobalAlloc(GHND, dwBmpSize);
DWORD D = GetLastError();
pBytes = (BYTE*)GlobalLock(hDIB);
D = GetLastError();
nRet = GetDIBits(hMemoryDC, hMemoryBitmap, 0, (UINT)bi24BitInfo.bmiHeader.biHeight, pBytes, (BITMAPINFO*)&bi24BitInfo, DIB_RGB_COLORS);
D = GetLastError();
if (pBitmap != NULL)
{
delete pBitmap;
pBitmap = NULL;
}
pBitmap = new Gdiplus::Bitmap(&bi24BitInfo, pBytes);
D = GetLastError();
GlobalUnlock(hDIB);
D = GetLastError();
Then after all of this I'm getting an error in the following:
pGraphics = Graphics::FromImage(pBitmap);
// set a graphics property
s = pGraphics->SetTextRenderingHint(TextRenderingHintAntiAlias);
After this pGraphics->LastResult is now set to "OutOfMemory" and s is set to the enum "InvalidParameter" from: http://msdn.microsoft.com/en-us/library/ms534175(v=VS.85).aspx
Any help on this would be appriciated.
BITMAPINFO only contains room for one palette entry; when you write to bi24BitInfo.bmiColors[1] you're clobbering the memory that follows, likely the hMemoryDC. Fix this by creating another structure that contains BITMAPINFO at the front and an array for the palette following.
Edit: It appears you aren't able to create a Graphics object from a monochrome bitmap. From the Graphics::FromImage documentation:
This method also fails if the image
has one of the following pixel
formats:
PixelFormatUndefined
PixelFormatDontCare
PixelFormat1bppIndexed
PixelFormat4bppIndexed
PixelFormat8bppIndexed
PixelFormat16bppGrayScale
PixelFormat16bppARGB1555

Drawing on 8bpp grayscale bitmap (unmanaged C++)

I've been attempting to draw on an 8bpp grayscale bitmap without success. Here are some of my attempts. Maybe someone can point out what I'm doing wrong.
===================================================
Attempt 1: Create, select, and draw:
In constructor:
CBitmap bm;
bm.CreateBitmap (200, 200, 1, 8, NULL);
In OnDraw:
CDC *mdc=new CDC ();
HGDIOBJ tmp = mdc->SelectObject(bm);
Result: tmp is NULL, indicating failure.
===================================================
Attempt 2: CreateDIBSection
In constructor:
HBITMAP hbm;
BITMAPINFOHEADER bih;
BITMAPINFO bi;
HANDLE hb;
CDC* myDc = new CDC ();
HDC hdc = myDc->GetSafeHdc ();
void* bits;
RGBQUAD rq [256];
initBi ();
hbm = CreateDIBSection (hdc, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
...
void CEightBitDrawingView::initBi()
{
bih.biSize = sizeof (BITMAPINFOHEADER);
bih.biWidth = 200;
bih.biHeight = -200;
bih.biPlanes = 1;
bih.biBitCount = 8;
bih.biCompression = BI_RGB;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 14173;
bih.biYPelsPerMeter = 14173;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
memset ((void *) rq, 0, 256 * sizeof (RGBQUAD));
bi.bmiHeader = bih;
bi.bmiColors = rq;
}
Result: This doesn't even compile because the BITMAPINFO bmiColors member is defined as:
RGBQUAD bmiColors[1];
so won't accept more than one RGB color. In fact, nothing I assign to this member compiles! (Could they possibly make it any more complex!?)
Any suggestions would be appreciated! Thanks!
===================================================
Here. Code that demonstrates how to - in a not managed world - allocate a dynamically sized structure on the stack, fill it in and pass it to CreateDIBSection.
#include <malloc.h>
HBITMAP CreateGreyscaleBitmap(int cx, int cy)
{
BITMAPINFO* pbmi = (BITMAPINFO*)alloca( sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
pbmi->bmiHeader.biSize = sizeof (pbmi->bmiHeader);
pbmi->bmiHeader.biWidth = cx;
pbmi->bmiHeader.biHeight = cy;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 8;
pbmi->bmiHeader.biCompression = BI_RGB;
pbmi->bmiHeader.biSizeImage = 0;
pbmi->bmiHeader.biXPelsPerMeter = 14173;
pbmi->bmiHeader.biYPelsPerMeter = 14173;
pbmi->bmiHeader.biClrUsed = 0;
pbmi->bmiHeader.biClrImportant = 0;
for(int i=0; i<256; i++)
{
pbmi->bmiColors[i].rgbRed = i;
pbmi->bmiColors[i].rgbGreen = i;
pbmi->bmiColors[i].rgbBlue = i;
pbmi->bmiColors[i].rgbReserved = 0;
}
PVOID pv;
return CreateDIBSection(NULL,pbmi,DIB_RGB_COLORS,&pv,NULL,0);
}
In both your examples, you created a new CDC with the following line:
CDC* pDC = new CDC();
But there's something missing: This will just create a new CDC object, but without a valid HDC handle attached to it. You need to call CDC::CreateCompatibleDC first, otherwise trying to select any object into this DC will fail.
Regarding the bmiColors: This member is defined as 1 sized array because the data behind it depends on the color depth and type of bitmap. This is documented in the MSDN. For example, if you had a 128x128 pixels 8Bit Bitmap, you would have to allocate the following amount of mem:
128 * 128 * sizeof(WORD) + sizeof(BITMAPINFOHEADER)
I finally resorted to using a .NET graphics tool (Aurigma) to create an 8bpp bitmap, and passed its handle to the unmanaged C++.
Then in C++:
HDC memDc = CreateCompatibleDC (NULL);
HGDIOBJ Obmp = ::SelectObject(memDc, varLayer); // Handle to 8-bit bitmap.
I was able to select the bitmap into a CDC and draw on it. Not 100% unmanaged, but this allowed me to do the drawing in unmanaged code, which gives acceptable speed.
Your bitmap needs to be compatible (same color-depth) as the display context you're going to render it on. Also, 8-bits/pixel bitmaps aren't necessarily grayscale - that's a function of what palette you're using.