Capture mouse cursor and apply transparency masking in Win32 - c++

Im trying to capture mouse cursor using windows API GetCursorInfo and taking in to CURSORINFO structure after I reading ICONINFO using GetIconInfo so I will get hbmMask and hbmColor bitmap
The hbmMask bitmap is first applied with an AND raster operation, then the hbmColor bitmap is applied with an XOR raster operation. This results in an opaque cursor and a transparent background but this is not happening in my POC complete code in below example.
After doing AND raster on mask data and XOR on color data the final result will be cursor and covered with white colored rectangle 32*32.
void save_as_bitmap(unsigned char *bitmap_data, int rowPitch, int width, int height, char *filename)
{
// A file is created, this is where we will save the screen capture.
FILE *f;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
//Make the size negative if the image is upside down.
bi.biHeight = -height;
//There is only one plane in RGB color space where as 3 planes in YUV.
bi.biPlanes = 1;
//In windows RGB, 8 bit - depth for each of R, G, B and alpha.
bi.biBitCount = 32;
//We are not compressing the image.
bi.biCompression = BI_RGB;
// The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps.
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
// rowPitch = the size of the row in bytes.
DWORD dwSizeofImage = rowPitch * height;
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwSizeofImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
// TODO: Handle getting current directory
fopen_s(&f, filename, "wb");
DWORD dwBytesWritten = 0;
dwBytesWritten += fwrite(&bmfHeader, sizeof(BITMAPFILEHEADER), 1, f);
dwBytesWritten += fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, f);
dwBytesWritten += fwrite(bitmap_data, 1, dwSizeofImage, f);
fclose(f);
}
//ST484 : HBIMAPtoBYTE : Convert BITMAP to BYTE array.
std::vector<BYTE> HBIMAPtoBYTE( HBITMAP hBitmap,
int &hBitmapSize,
bool &bResult,
int &nWidth,
int &nHeight )
{
bResult = true;
BITMAP bmp;
if (!GetObject(hBitmap, sizeof(BITMAP), (LPVOID)&bmp))
{
DeleteObject(hBitmap);
bResult = false;
}
int rpcbiPlanes = 32;
BITMAPINFO info;
memset(&info, 0, sizeof(BITMAPINFO));
info.bmiHeader.biSize = sizeof(info.bmiHeader);
info.bmiHeader.biWidth = bmp.bmWidth;
info.bmiHeader.biHeight = -bmp.bmHeight;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = rpcbiPlanes;
info.bmiHeader.biCompression= BI_RGB;
size_t pixelSize = info.bmiHeader.biBitCount / 8;
size_t scanlineSize = (pixelSize * info.bmiHeader.biWidth + 3) & ~3;
size_t bitmapSize = bmp.bmHeight * scanlineSize;
hBitmapSize = bitmapSize;
nWidth = bmp.bmWidth;
nHeight = bmp.bmHeight;
std::vector<BYTE> pixels(bitmapSize);
HDC hdc = ::GetDC(NULL);
if(!GetDIBits(hdc, hBitmap, 0, bmp.bmHeight, &pixels[0], &info, DIB_RGB_COLORS))
{
hBitmapSize = 0;
bResult = false;
}
return pixels;
}
// getHCursor : Capture cursor.
CURSORINFO getHCursor()
{
CURSORINFO cursorInfo;
cursorInfo.cbSize = sizeof(CURSORINFO);
if (GetCursorInfo(&cursorInfo) == 0)
{
MessageBox(NULL, _T("Exception : GetCursorInfo creation failed"),_T("message"),MB_OK|MB_SYSTEMMODAL);
cursorInfo.hCursor = NULL;
return cursorInfo;
}
return cursorInfo;
}
//Main Call
int _tmain(int argc, _TCHAR* argv[])
{
int CountP = 0;
while (true)
{
CURSORINFO CursorInfo = getHCursor();
if (CursorInfo.hCursor == NULL)
{
::Sleep(MinSleep);
continue;
}
ICONINFO iconInfo;
if (!GetIconInfo(CursorInfo.hCursor, &iconInfo))
{
MessageBox(NULL, _T("Exception : GetIconInfo creation failed"),_T("message"),MB_OK|MB_SYSTEMMODAL);
::Sleep(MinSleep);
}
std::vector<BYTE> bColorBitmap;
std::vector<BYTE> bMaskBitmap;
std::vector<BYTE> bDestBitmap;
int sz_hbmColor = 0;
int sz_hbmMask = 0;
int sz_hbDest = 0;
int nWidth = 0;
int nHeight = 0;
bool hbmColor_result = false;
bool hbmMask_result = false;
bool hbmDest_result = false;
int rpcbiPlanes = 32;
bool isColorShape = (iconInfo.hbmColor != NULL);
// read mask and color in to byte vector.
bColorBitmap = HBIMAPtoBYTE(iconInfo.hbmColor,sz_hbmColor,hbmColor_result,nWidth,nHeight);
bMaskBitmap = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmMask,hbmMask_result,nWidth,nHeight);
//Create Dummy bitmap using width and height filled with black color.
HBITMAP desBitmap = CreateBitmap(nWidth,nHeight,1,rpcbiPlanes,NULL);
if(desBitmap != NULL)
{
// read dummy bitmap in to byte vector.
bDestBitmap = HBIMAPtoBYTE(desBitmap,sz_hbDest,hbmDest_result,nWidth,nHeight);
}
//the mask bitmap is first applied with an AND raster operation.
for(int i = 0; i < nHeight ; i++)
{
for(int j = 0; j < nWidth; j++)
{
bDestBitmap[i*4*nWidth + j*4 ] &= bMaskBitmap[i*4*nWidth + j*4 ];
bDestBitmap[i*4*nWidth + j*4 + 1] &= bMaskBitmap[i*4*nWidth + j*4 + 1];
bDestBitmap[i*4*nWidth + j*4 + 2] &= bMaskBitmap[i*4*nWidth + j*4 + 2];
bDestBitmap[i*4*nWidth + j*4 + 3] &= bMaskBitmap[i*4*nWidth + j*4 + 3];
}
}
//then the color bitmap is applied with an XOR raster operation.
for(int i = 0; i < nHeight ; i++)
{
for(int j = 0; j < nWidth; j++)
{
bDestBitmap[i*4*nWidth + j*4 ] ^= bColorBitmap[i*4*nWidth + j*4 ];
bDestBitmap[i*4*nWidth + j*4 + 1] ^= bColorBitmap[i*4*nWidth + j*4 + 1];
bDestBitmap[i*4*nWidth + j*4 + 2] ^= bColorBitmap[i*4*nWidth + j*4 + 2];
bDestBitmap[i*4*nWidth + j*4 + 3] ^= bColorBitmap[i*4*nWidth + j*4 + 3];
}
}
//Save Color bitmap.
sprintf_s(file_name,"C:\\Test\\captured\\Cursor_%d.bmp", CountP);
save_as_bitmap(&(bDestBitmap[0]), nWidth*4, nWidth, nHeight, file_name);
CountP++;
Sleep(MaxSleep);
}
return 0;
}
After saving cursor getting image like this

Finally I found excellent result using windows API.
Include comctl32.lib in to your project
HIMAGELIST iCursorList=ImageList_Create(nWidth,nHeight,ILC_COLOR32|ILC_MASK,8,8);
ImageList_AddMasked(iCursorList,iconInfo.hbmColor,000000);
DeleteObject(iconInfo.hbmColor);
TRANSPARENT_HICON = ImageList_GetIcon(iCursorList,0,ILD_TRANSPARENT);
1 ) Create ImageList_Create using width, height, ILC_COLOR32|ILC_MASK
2 ) Add bitmap in to ImageList_AddMasked and apply which color you want to make transparent, 000000 are black color in my code.
3 ) Get transparent icon from ImageList_GetIcon.

Related

Win32 GetPixel() returns wrong values whereas SetPixel() works fine, even when DPI awareness is turned on

I have been struggling with this issue for the entire day and I am still not making any progress.
CONTEXT
I am trying to reproduce a virtual piano keyboard (driven with MIDI files) on LEDs attached to a physical keyboard so that my wife can learn how to play piano quicker (Christmas gift). Here is a picture of the program:
WHAT THE CODE DOES
(Or is supposed to do) I enter the coordinates of the keyboard as measured in Paint.NET after taking a screenshot of the program full-screen. The user is asked to put the software full screen. Then I calculate the coordinates of the middle of all the keys, and get the colour of the key when it is not activated. Finally I check that all white keys have the same colour, and that the black keys have the same colour (to make sure the keyboard is correctly displayed).
Later, I'll loop forever, getting the colour of each key and lighting up the corresponding LED on the physical keyboard if the colour of that key has changed from the "rest" state.
PROBLEM
I know I pick the colours at the right location, because SetPixel() displays the red pixels spot on in the middle of all keys (as seen in code). However, GetPixel still returns wrong values often times (1/4th of the time for the white keys, always for the black keys). What's happening ?
WHAT I TRIED
A added a manifest file to the project which successfully modified the size of the desktop to the correct value of 1920x1080 - but even though the RGB values changed a bit, they are still off
- I added a captureAndSave() function in case the computer "sees" a different thing than me when I do a screen capture (to obtain all the input coordinates), or than whatever does SetPixel() because this works - but the image is indeed identical, so I don't know what is happening.
- Many debugging runs...
CODE
(skip to VirtualKeyboard::init())
#include <iostream>
#include <windows.h>
using namespace std;
const unsigned int N_WHITE_KEYS = 49;
const unsigned int N_PIANO_KEYS = N_WHITE_KEYS+35;
const unsigned int N_ABSENT_BLK_KEYS = N_WHITE_KEYS*2-N_PIANO_KEYS-1;
const unsigned int ABSENT_BLK_KEYS[N_ABSENT_BLK_KEYS] = {3,7,10,14,17,21,24,28,31,35,38,42,45}; //TODO: find a formula rather than this list
const unsigned int VKxLeft = 19,
VKyTop = 150,
VKxRight = 1731,
VKyBtm = 324;
//TEMP
#include <stdio.h>
bool captureAndSave(const HWND& hWnd, int nBitCount, const char* szFilePath)
{
if(!szFilePath || !strlen(szFilePath))
{
cout << "bad function arguments\n";
return false;
}
//calculate the number of color indexes in the color table
int nColorTableEntries = -1;
switch(nBitCount)
{
case 1:
nColorTableEntries = 2;
break;
case 4:
nColorTableEntries = 16;
break;
case 8:
nColorTableEntries = 256;
break;
case 16:
case 24:
case 32:
nColorTableEntries = 0;
break;
default:
nColorTableEntries = -1;
break;
}
if(nColorTableEntries == -1)
{
cout << "bad bits-per-pixel argument\n";
return false;
}
HDC hDC = GetDC(hWnd);
HDC hMemDC = CreateCompatibleDC(hDC);
int nWidth = 0;
int nHeight = 0;
if(hWnd != HWND_DESKTOP)
{
RECT rect;
GetClientRect(hWnd, &rect);
nWidth = rect.right - rect.left;
nHeight = rect.bottom - rect.top;
}
else
{
nWidth = ::GetSystemMetrics(SM_CXSCREEN);
nHeight = ::GetSystemMetrics(SM_CYSCREEN);
}
HBITMAP hBMP = CreateCompatibleBitmap(hDC, nWidth, nHeight);
SelectObject(hMemDC, hBMP);
BitBlt(hMemDC, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);
int nStructLength = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries;
LPBITMAPINFOHEADER lpBitmapInfoHeader = (LPBITMAPINFOHEADER)new char[nStructLength];
::ZeroMemory(lpBitmapInfoHeader, nStructLength);
lpBitmapInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
lpBitmapInfoHeader->biWidth = nWidth;
lpBitmapInfoHeader->biHeight = nHeight;
lpBitmapInfoHeader->biPlanes = 1;
lpBitmapInfoHeader->biBitCount = nBitCount;
lpBitmapInfoHeader->biCompression = BI_RGB;
lpBitmapInfoHeader->biXPelsPerMeter = 0;
lpBitmapInfoHeader->biYPelsPerMeter = 0;
lpBitmapInfoHeader->biClrUsed = nColorTableEntries;
lpBitmapInfoHeader->biClrImportant = nColorTableEntries;
DWORD dwBytes = ((DWORD) nWidth * nBitCount) / 32;
if(((DWORD) nWidth * nBitCount) % 32) {
dwBytes++;
}
dwBytes *= 4;
DWORD dwSizeImage = dwBytes * nHeight;
lpBitmapInfoHeader->biSizeImage = dwSizeImage;
LPBYTE lpDibBits = 0;
HBITMAP hBitmap = ::CreateDIBSection(hMemDC, (LPBITMAPINFO)lpBitmapInfoHeader, DIB_RGB_COLORS, (void**)&lpDibBits, NULL, 0);
SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hDC);
BITMAPFILEHEADER bmfh;
bmfh.bfType = 0x4d42; // 'BM'
int nHeaderSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries;
bmfh.bfSize = 0;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries;
FILE *pFile = 0;
pFile = fopen(szFilePath, "wb");
if(!pFile)
{
::DeleteObject(hBMP);
::DeleteObject(hBitmap);
delete[]lpBitmapInfoHeader;
cout << "can not open file\n";
return false;
}
DWORD nColorTableSize = 0;
if (nBitCount != 24)
nColorTableSize = (1 << nBitCount) * sizeof(RGBQUAD);
else
nColorTableSize = 0;
fwrite(&bmfh, sizeof(BITMAPFILEHEADER), 1, pFile);
fwrite(lpBitmapInfoHeader, nHeaderSize,1,pFile);
if(nBitCount < 16)
{
int nBytesWritten = 0;
RGBQUAD *rgbTable = new RGBQUAD[nColorTableEntries * sizeof(RGBQUAD)];
//fill RGBQUAD table and write it in file
for(int i = 0; i < nColorTableEntries; ++i)
{
rgbTable[i].rgbRed = rgbTable[i].rgbGreen = rgbTable[i].rgbBlue = i;
rgbTable[i].rgbReserved = 0;
fwrite(&rgbTable[i], sizeof(RGBQUAD), 1, pFile);
}
delete[]rgbTable;
/*
RGBQUAD rgb;
for (DWORD i = 0; i < nColorTableEntries ; i++)
{
rgb.rgbBlue = rgb.rgbGreen = rgb.rgbRed = (BYTE)(i*(255/(nColorTableEntries-1)));
nBytesWritten = fwrite(&rgb, 1, sizeof(rgb), pFile);
if (nBytesWritten != sizeof(rgb))
{
printf("error while writing rgb header\n");
fclose(pFile);
::DeleteObject(hBMP);
::DeleteObject(hBitmap);
delete[]lpBitmapInfoHeader;
return false;
}
}
*/
}
fwrite(lpDibBits, dwSizeImage, 1, pFile);
fclose(pFile);
::DeleteObject(hBMP);
::DeleteObject(hBitmap);
delete[]lpBitmapInfoHeader;
}
//End TEMP
inline bool SameColours(COLORREF const &a, COLORREF const &b) {
bool ret = true;
ret &= GetRValue(a) == GetRValue(b);
ret &= GetGValue(a) == GetGValue(b);
ret &= GetBValue(a) == GetBValue(b);
return ret;
}
class VirtualKeyboard
{
COLORREF keyColUnpressed[N_PIANO_KEYS];
unsigned int keyX[N_PIANO_KEYS]; //White then black, from left to right
unsigned int keyY[N_PIANO_KEYS]; //White then black, from left to right
public:
bool init(unsigned int xLeft, unsigned int yTop, unsigned int xRight, unsigned yBtm) {
bool ret = true;
//Calculate parameters of the virtual keyboard
const float whtKeyHeight = (yBtm-yTop);
const float whtKeyWidth = float(xRight-xLeft)/(N_WHITE_KEYS);
//Calculate coordinates of the white keys
for(unsigned int i = 0; i < N_WHITE_KEYS ; ++i) {
keyX[i]=xLeft+(i+1.f/2)*float(whtKeyWidth);
keyY[i]=yTop+3.f/4*float(whtKeyHeight);
}
//Calculate coordinates of the black keys
unsigned int iBlkKey = 0;
for(unsigned int i = 0 ; i < N_WHITE_KEYS-1 ; ++i) {
//Determine if there is a black key
bool skip = false;
//Some black keys are absent from the offset white keys pattern - skip if applicable
for(unsigned int j = 0 ; j < N_ABSENT_BLK_KEYS ; ++j) {
if(i+1 == ABSENT_BLK_KEYS[j]) {
skip = true;
break;
}
}
//If that key exists, add it to the list
if(!skip) {
keyX[iBlkKey+N_WHITE_KEYS]=xLeft+whtKeyWidth*(i+1);
keyY[iBlkKey+N_WHITE_KEYS]=yTop+1.f/4*float(whtKeyHeight);
iBlkKey++;
}
}
//Capture the screen
HDC hdcScreen = ::GetDC(GetDesktopWindow());
captureAndSave(GetDesktopWindow(),32,"./capture.bmp");
//And fill in the colors "at rest" for all the keys
for(unsigned int i = 0 ; i < N_PIANO_KEYS ; ++i) {
keyColUnpressed[i] = ::GetPixel(hdcScreen, keyX[i], keyY[i]);
unsigned int r = GetRValue(keyColUnpressed[i]);
unsigned int g = GetGValue(keyColUnpressed[i]);
unsigned int b = GetBValue(keyColUnpressed[i]);
::SetPixel(hdcScreen, keyX[i], keyY[i], RGB(255,0,0)); //DEBUG: Breakpoint on this line, the RGB values are wrong for some keys (e.g. i=8 and all blacks)
Sleep(100);
}
ReleaseDC(GetDesktopWindow(),hdcScreen);
//Sanity check : all white keys should have the same colour, and all black keys their own colour as well
for(unsigned int i = 1 ; i < N_PIANO_KEYS ; ++i) {
if(i != 1 && i != N_WHITE_KEYS) {
if(
!SameColours(
keyColUnpressed[i],(i < N_WHITE_KEYS ? keyColUnpressed[0]
: keyColUnpressed[N_WHITE_KEYS])
)
)
{
ret = false;
break;
}
}
}
return 0;
}
};
int main()
{
VirtualKeyboard vk;
cout << "You have 3 seconds to minimize this window and maximise MidiSheetMusic" << endl;
Sleep(3000);
cout << "Result of init() : " << vk.init(VKxLeft, VKyTop, VKxRight, VKyBtm) << endl;
while(1)
{
//Get all keys pixels and reproduce on the LEDs on the physical keyboard
Sleep(10); //Runs while not Ctrl+c
}
return 0;
}
Thank you very much in advance.

Bitmap to char * win32 problems

Im trying to take a screenshot, converting it to a char * and sending it via winsocks.
I use bitmaps because it appears to be the easiest way.
Heres what I got so far:
HDC handle_ScreenDC = GetDC( NULL );
HDC handle_MemoryDC = CreateCompatibleDC( handle_ScreenDC );
BITMAP bitmap;
int x = GetDeviceCaps( handle_ScreenDC, HORZRES );
int y = GetDeviceCaps( handle_ScreenDC, VERTRES );
HBITMAP handle_Bitmap = CreateCompatibleBitmap( handle_ScreenDC, x, y );
SelectObject( handle_MemoryDC, handle_Bitmap );
BitBlt( handle_MemoryDC, 0, 0, x, y, handle_ScreenDC, 0, 0, SRCCOPY );
GetObject( handle_Bitmap, sizeof( BITMAP ), &bitmap );
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof( BITMAPINFOHEADER );
bi.biWidth = bitmap.bmWidth;
bi.biHeight = bitmap.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ( ( bitmap.bmWidth * bi.biBitCount + 5 ) / 32 ) * 4 * bitmap.bmHeight;
HANDLE hDIB = GlobalAlloc( GHND, dwBmpSize );
char* bufptr = ( char * ) GlobalLock( hDIB );
GetDIBits( handle_ScreenDC, handle_Bitmap, 0, ( UINT ) bitmap.bmHeight, bufptr, ( BITMAPINFO * ) &bi, DIB_RGB_COLORS );
return bufptr;
Now, I get a valid screenshot if I write hBitmap to a file ( which I do not want to do ).
However, when I try to convert the Bitmap to a char *, I always get the following data:
\x1\x1\x1ÿ\x1\x1\x1ÿ\x1\x1\x1ÿ\x1\x1\x1ÿ\x1\x1\x1ÿ\x1\x1\x1ÿ\x1\x1\x1ÿ\
Can anyone help me out?
Thanks in advance.
Not sure why you want to convert it to a string.. You could also do what you're currently doing which is to send over the header of the bitmap first. Followed by the pixels. I don't see the need to convert it to a string.
Here is a very basic example of converting the bitmap to a string (for the sake of answering the question).
class Image
{
private:
std::vector<std::uint8_t> Pixels;
std::uint32_t width, height;
std::uint16_t BitsPerPixel;
public:
explicit Image(HDC DC, int X, int Y, int Width, int Height);
std::uint32_t get_width() {return width;}
std::uint32_t get_height() {return height;}
std::uint32_t get_bits_per_pixel() {return BitsPerPixel;}
std::string to_string();
};
Image::Image(HDC DC, int X, int Y, int Width, int Height) : Pixels(), width(Width), height(Height), BitsPerPixel(32)
{
BITMAP Bmp = {0};
HBITMAP hBmp = reinterpret_cast<HBITMAP>(GetCurrentObject(DC, OBJ_BITMAP));
if (GetObject(hBmp, sizeof(BITMAP), &Bmp) == 0)
throw std::runtime_error("BITMAP DC NOT FOUND.");
RECT area = {X, Y, X + Width, Y + Height};
HWND Window = WindowFromDC(DC);
GetClientRect(Window, &area);
HDC MemDC = GetDC(nullptr);
HDC SDC = CreateCompatibleDC(MemDC);
HBITMAP hSBmp = CreateCompatibleBitmap(MemDC, width, height);
DeleteObject(SelectObject(SDC, hSBmp));
BitBlt(SDC, 0, 0, width, height, DC, X, Y, SRCCOPY);
unsigned int data_size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
this->Pixels.resize(data_size);
BITMAPINFO Info = {sizeof(BITMAPINFOHEADER), static_cast<long>(width), static_cast<long>(height), 1, BitsPerPixel, BI_RGB, data_size, 0, 0, 0, 0};
GetDIBits(SDC, hSBmp, 0, height, &Pixels[0], &Info, DIB_RGB_COLORS);
DeleteDC(SDC);
DeleteObject(hSBmp);
ReleaseDC(nullptr, MemDC);
}
std::string Image::to_string()
{
auto base64encode = [](std::vector<uint8_t> &in, std::string &out) -> void {
int i, j;
char *ptr;
uint32_t c[4];
static char indexes[] = {0, 2, 1};
static const char *Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
out.resize(4 * ((in.size() + 2) / 3));
ptr = &out[0];
for (i = 0, j = 0; i < in.size();)
{
c[0] = i < in.size() ? in[i++] : 0;
c[1] = i < in.size() ? in[i++] : 0;
c[2] = i < in.size() ? in[i++] : 0;
c[3] = (c[0] << 0x10) + (c[1] << 0x08) + c[2];
ptr[j++] = Base64Chars[(c[3] >> 3 * 6) & 0x3F];
ptr[j++] = Base64Chars[(c[3] >> 2 * 6) & 0x3F];
ptr[j++] = Base64Chars[(c[3] >> 1 * 6) & 0x3F];
ptr[j++] = Base64Chars[(c[3] >> 0 * 6) & 0x3F];
}
for (i = 0; i < indexes[in.size() % 3]; ++i)
ptr[out.size() - 1 - i] = '=';
};
std::string result;
base64encode(Pixels, result);
return result;
}
int main()
{
//..
Image img(screen_dc, 0, 0, screen_width, screen_height);
std::string res = img.to_string();
std::size_t size = res.size();
//..
socket.write(&screen_width, sizeof(screen_width));
socket.write(&screen_height, sizeof(screen_height);
socket.write(&size, sizeof(size));
socket.write(&res[0], res.size());
}

Create monochrome BMP from bitset

I could need some help to figure out how to feed the proc below. I need to write a monochrome BMP file. The code below (its from: How to Save monochrome Image as bmp in windows C++ ?) looks like to be able to do this. I'm now stuck on how to convert a std::bitset or preferably boost::dynamic_bitset into this byte* format. All of my attempts so far failed, I wasn't able to write something like an 8x8 checker pattern into the BMP. The proc creates the BMP and it is readable by Photoshop, but the content is a mess. So any suggestions how to solve this are appreciated!
Save1BppImage(byte* ImageData, const char* filename, long w, long h){
int bitmap_dx = w; // Width of image
int bitmap_dy = h; // Height of Image
// create file
std::ofstream file(filename, std::ios::binary | std::ios::trunc);
if(!file) return;
// save bitmap file headers
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER * infoHeader;
infoHeader = (BITMAPINFOHEADER*) malloc(sizeof(BITMAPINFOHEADER) );
RGBQUAD bl = {0,0,0,0}; //black color
RGBQUAD wh = {0xff,0xff,0xff,0xff}; // white color
fileHeader.bfType = 0x4d42;
fileHeader.bfSize = 0;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + (sizeof(BITMAPINFOHEADER));
infoHeader->biSize = (sizeof(BITMAPINFOHEADER) );
infoHeader->biWidth = bitmap_dx;
infoHeader->biHeight = bitmap_dy;
infoHeader->biPlanes = 1;
infoHeader->biBitCount = 1;
infoHeader->biCompression = BI_RGB; //no compression needed
infoHeader->biSizeImage = 0;
infoHeader->biXPelsPerMeter = 0;
infoHeader->biYPelsPerMeter = 0;
infoHeader->biClrUsed = 2;
infoHeader->biClrImportant = 2;
file.write((char*)&fileHeader, sizeof(fileHeader)); //write bitmapfileheader
file.write((char*)infoHeader, (sizeof(BITMAPINFOHEADER) )); //write bitmapinfoheader
file.write((char*)&bl,sizeof(bl)); //write RGBQUAD for black
file.write((char*)&wh,sizeof(wh)); //write RGBQUAD for white
int bytes = (w/8) * h ; //for example for 32X64 image = (32/8)bytes X 64 = 256;
file.write((const char*)ImageData, bytes);
file.close();
}
-edit-
an naive approach of mine was something like this
byte test[64];
for(unsigned int i=0; i<64; ++i)
if(i % 2)
test[i] = 0;
else
test[i] = 1;
Save1BppImage(test, "C:/bitmap.bmp", 8, 8);
The code you have is very close. Here are a few thoughts about where it might be off.
The bfOffBits value must include the size of the palette.
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + (sizeof(BITMAPINFOHEADER)) + 2*sizeof(RGBQUAD);
Some software may interpret 0 as white and 1 as black, regardless of what the palette says. Even though the file format allows you to go either way, you're better off specifying the palette in that order and inverting your bits if necessary.
Each row of a bitmap will start on a 4-byte boundary. If your bitmap width isn't a multiple of 32, you're going to need some padding between each row.
BMP files are ordered from the bottom row to the top row, which is backwards from the way most people organize their arrays.
The last two recommendations are combined to look something like this:
int bytes_in = (w + 7) / 8;
int bytes_out = ((w + 31) / 32) * 4;
const char * zeros[4] = {0, 0, 0, 0};
for (int y = h - 1; y >= 0; --y)
{
file.write(((const char *)ImageData) + (y * bytes_in), bytes_in);
if (bytes_out != bytes_in)
file.write(zeros, bytes_out - bytes_in);
}
Just for the archive, below the working version. It takes a boost bitset as input pixel storage.
void bitsetToBmp(boost::dynamic_bitset<unsigned char> bitset, const char* filename, int width, int height){
//write the bitset to file as 1-bit deep bmp
//bit order 0...n equals image pixels top left...bottom right, row by row
//the bitset must be at least the size of width*height, this is not checked
std::ofstream file(filename, std::ios::binary | std::ios::trunc);
if(!file) return;
// save bitmap file headers
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER * infoHeader;
infoHeader = (BITMAPINFOHEADER*) malloc(sizeof(BITMAPINFOHEADER) );
RGBQUAD bl = {0,0,0,0}; //black color
RGBQUAD wh = {0xff,0xff,0xff,0xff}; // white color
fileHeader.bfType = 0x4d42;
fileHeader.bfSize = 0;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + (sizeof(BITMAPINFOHEADER)) + 2*sizeof(RGBQUAD);
infoHeader->biSize = (sizeof(BITMAPINFOHEADER) );
infoHeader->biWidth = width;
infoHeader->biHeight = height;
infoHeader->biPlanes = 1;
infoHeader->biBitCount = 1;
infoHeader->biCompression = BI_RGB; //no compression needed
infoHeader->biSizeImage = 0;
infoHeader->biXPelsPerMeter = 0;
infoHeader->biYPelsPerMeter = 0;
infoHeader->biClrUsed = 2;
infoHeader->biClrImportant = 2;
file.write((char*)&fileHeader, sizeof(fileHeader)); //write bitmapfileheader
file.write((char*)infoHeader, (sizeof(BITMAPINFOHEADER) )); //write bitmapinfoheader
file.write((char*)&bl,sizeof(bl)); //write RGBQUAD for black
file.write((char*)&wh,sizeof(wh)); //write RGBQUAD for white
// convert the bits into bytes and write the file
int offset, numBytes = ((width + 31) / 32) * 4;
byte* bytes = (byte*) malloc(numBytes * sizeof(byte));
for(int y=height - 1; y>=0; --y){
offset = y * width;
memset(bytes, 0, (numBytes * sizeof(byte)));
for(int x=0; x<width; ++x)
if(bitset[offset++]){
bytes[x / 8] |= 1 << (7 - x % 8);
};
file.write((const char *)bytes, numBytes);
};
free(bytes);
file.close();
}
I wonder if theres a simpler/faster way to put the bits into a file? The whole bitset could instead be overhanded as array of rows to skip the subset extraction.
I have something very similiar...
This approach DOES NOT treat the padding of the BMP format. So You can only make bitmaps with width multiple of 4.
This is NOT a monochromatic bitmap. It's a RGB format, but you can tune it easily.
This is NOT an exactly answer to you, but for sure may be useful for you.
Enjoy it.
void createBitmap( byte * imageData, const char * filename, int width, int height )
{
BITMAPFILEHEADER bitmapFileHeader;
memset( &bitmapFileHeader, 0, sizeof( bitmapFileHeader ) );
bitmapFileHeader.bfType = ( 'B' | 'M' << 8 );
bitmapFileHeader.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER );
bitmapFileHeader.bfSize = bitmapFileHeader.bfOffBits + width * height * 3;
BITMAPINFOHEADER bitmapInfoHeader;
memset( &bitmapInfoHeader, 0, sizeof( bitmapInfoHeader ) );
bitmapInfoHeader.biSize = sizeof( BITMAPINFOHEADER );
bitmapInfoHeader.biWidth = width;
bitmapInfoHeader.biHeight = height;
bitmapInfoHeader.biPlanes = 1;
bitmapInfoHeader.biBitCount = 24;
std::ofstream file( filename, std::fstream::binary );
file.write( reinterpret_cast< char * >( &bitmapFileHeader ), sizeof( bitmapFileHeader ) );
file.write( reinterpret_cast< char * >( &bitmapInfoHeader ), sizeof( bitmapInfoHeader ) );
// the pixels!
file.write( imageData, width * height * 3 );
file.close();
}
int main( int argc, const char * argv[] )
{
int width = 12; // multiple of 4
int height = 12;
byte imageData[ width * height * 3 ];
// fill imageData the way you want, this is just a sample
// on how to set the pixel at any specific (X,Y) position
for ( int y = 0; y < height; ++y )
{
for ( int x = 0; x < width; ++x )
{
int pos = 3 * ( y * width + x );
byte pixelColor = ( x == 2 && y == 2 ) ? 0x00 : 0xff;
imageData[ pos ] = pixelColor;
imageData[ pos + 1 ] = pixelColor;
imageData[ pos + 2 ] = pixelColor;
}
}
createBitmap( imageData, "bitmap.bmp", width, height );
return 0;
}
In this sample we want a white bitmap with a single black pixel at position X = 2, Y = 2.
The BMP format constiders that Y grows up from bottom to top.
If you have a bitmap width a pixel per bit (the real monochromatic bitmap) you can test the bits and fill the imageData. To test a bit in a byte do like myByte >> position & 1 where position is the bit you wanna test from 0 to 7.

How to get one picture from YUV frame in C++

In C++ i have an
LPBYTE pYUV;
It's contains a YUV data. I used this code to save it to an image file named Original.bmp
BITMAPINFOHEADER* bi = new BITMAPINFOHEADER;
bi->biSize = sizeof(BITMAPINFOHEADER);
bi->biWidth = pVSI->nWidth;
bi->biHeight = pVSI->nHeight;
bi->biPlanes = 1;
bi->biBitCount = 24;
bi->biCompression = 0;
bi->biSizeImage = 0;
bi->biXPelsPerMeter = 2835;
bi->biYPelsPerMeter = 2835;
bi->biClrUsed = 0;
bi->biClrImportant = 0;
BITMAPINFO* bmi = (BITMAPINFO*)bi;
HDC dc = ::GetDC(NULL);
HBITMAP bmp = CreateDIBitmap(dc, bi, CBM_INIT, pYUV, bmi, DIB_RGB_COLORS);
::ReleaseDC(NULL, dc);
CBitmapEx bitmap;
bitmap.Load(bmp);
bitmap.Save(_T("Original.bmp"));
When i open the Original.bmp, it's look like this
It's have too many same picture in the image.How do i process the LPBYTE pYUV to make one picture in the Original.bmp file ?
ps: I can do this by draw it to DirectX by this code.
But i don't know how to write it to image file
LPBYTE apDst[3] = { 0 };
apDst[0] = (LPBYTE)pSurface; // Y
apDst[1] = (LPBYTE)pSurface + nPitch*nH; // U
apDst[2] = (LPBYTE)pSurface + nPitch*nH * 5 / 4; //V
LPBYTE apSrc[3] = { 0 };
apSrc[0] = pYUV; // Y
apSrc[1] = pYUV + nW*nH * 5 / 4; // V
apSrc[2] = pYUV + nW*nH; // U
int anPch[3] = { 0 };
anPch[0] = nW; // Y
anPch[1] = nW / 2; // V
anPch[2] = nW / 2; // U
LPBYTE pDst, pSrc;
int i, k;
int w, h, p;
int div[3] = { 1, 2, 2 };
for (k = 0; k < 3; k++)
{
pSrc = apSrc[k];
pDst = apDst[k];
w = nW / div[k]; // w means the width of one row
h = nH / div[k]; // h means the rows
p = nPitch / div[k];
for (i = 0; i < h; i++)
{
memcpy(pDst, pSrc, w);
pDst += p;
pSrc += anPch[k];
}
}
and It work like this
Thanks in advance.

CreateDIBSection graphical artifacts or how to access bitmap bits correctly

UPDATE:
Thanks to Denis Anisimov I solved my problem by replacing:
for (int i = 0; i < Width * Height; i++)
{
pRgb[0] = RValue;
pRgb[1] = GValue;
pRgb[2] = BValue;
pRgb += 3;
}
with:
int BytesPerLine = Width * 3;
if (BytesPerLine % 4 != 0)
BytesPerLine += 4 - BytesPerLine % 4;
PBYTE Line = NULL;
for (int y = 0; y < Height; y++)
{
Line = pRgb;
for (int x = 0; x < Width; x++)
{
Line[0] = RValue;
Line[1] = GValue;
Line[2] = BValue;
Line += 3;
}
pRgb += BytesPerLine;
}
When creating device independent bitmap I sometimes get strange line artifacts. It only occurs when I change window, in which I draw, width. During resize I delete old bitmap and create new using custom function. Because I have direct access to bitmap's bits, I change their colors directly, but that is where problem occurs (I think), because FillRect works perfectly. I may be not accessing bits pointer correctly. Here are pictures when it works and other with artifacts:
This code shows how I draw in window:
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
Painter Paint;
if (temp != NULL)
{
DeleteObject(temp);
temp = NULL;
}
temp = Paint.Create24BitBitmap(NULL, ps.rcPaint.right - ps.rcPaint.left - 5, ps.rcPaint.bottom - ps.rcPaint.top - 5, 20, 70, 0);
HDC MemoryDC = CreateCompatibleDC(ps.hdc);
SelectObject(MemoryDC, temp);
BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left - 5, ps.rcPaint.bottom - ps.rcPaint.top - 5, MemoryDC, 0, 0, SRCCOPY);
DeleteDC(MemoryDC);
EndPaint(hWnd, &ps);
}
break;
case WM_NCPAINT:
{
RedrawWindow(hWnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE | RDW_INTERNALPAINT);
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
break;
And this one shows how I create bitmap:
HBITMAP Create24BitBitmap(HDC *hDC, int Width, int Height, BYTE RValue, BYTE GValue, BYTE BValue)
{
Width = ((Width < 1)?1:Width);
Height = ((Height < 1)?1:Height);
RValue = ((RValue > 254)?255:((RValue < 0)?0:RValue));
GValue = ((GValue > 254)?255:((GValue < 0)?0:GValue));
BValue = ((BValue > 254)?255:((BValue < 0)?0:BValue));
HDC MemoryDC = NULL;
if (hDC != NULL)
MemoryDC = CreateCompatibleDC(*hDC);
else
MemoryDC = CreateCompatibleDC(NULL);
BITMAPINFO BitmapInfo;
ZeroMemory(&BitmapInfo,sizeof(BITMAPINFO));
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = Width;
BitmapInfo.bmiHeader.biHeight = Height;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 24;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
PBYTE pRgb = NULL;
HBITMAP hBitmap = CreateDIBSection(MemoryDC, &BitmapInfo, DIB_RGB_COLORS, (void**)&pRgb, NULL, NULL);
DeleteDC(MemoryDC);
if (hBitmap == NULL)
{
MessageBox(NULL, TEXT("\"Create24BitBitmap\" function failed."), TEXT("ERROR!"), MB_ICONERROR);
exit(EXIT_FAILURE);
}
for (int i = 0; i < Width * Height; i++)
{
pRgb[0] = RValue;
pRgb[1] = GValue;
pRgb[2] = BValue;
pRgb += 3;
}
return hBitmap;
}
So the question would be how to correctly access bitmap bits? Or are there other things that may cause this artifact?
Here you can find compiled program:
http://www.putlocker.com/file/D555A03E41032615
This code is wrong:
for (int i = 0; i < Width * Height; i++)
{
pRgb[0] = RValue;
pRgb[1] = GValue;
pRgb[2] = BValue;
pRgb += 3;
}
Start of every line of bitmap must be aligned to 4-byte boundaries.
Update:
You should use something like this:
BytesPerLine := Width * 3;
if BytesPerLine mod 4 <> 0 then
Inc(BytesPerLine, 4 - BytesPerLine mod 4);
for y := 0 to Height - 1 do
begin
Line := pRgb;
for x := 0 to Width - 1 do
begin
Line[0] := RValue;
Line[1] := GValue;
Line[2] := BValue;
Inc(Line, 3);
end;
Inc(pRgb, BytesPerLine);
end;