GDI Screenshot, results varying on different computer - c++

I try to take a screenshot with GDI, then i use it in FFmpeg.
The screenshot works well and FFmpeg handle it without any problem.
But, on some computer, the image is not very what i want like you can see below.
Here is the code i use to init my bitmap :
//--
mImageBuffer = new unsigned char[mWxHxS];
memset(mImageBuffer, 0, mWxHxS);
//--
hScreenDC = GetDC(0);
hMemoryDC = CreateCompatibleDC(hScreenDC);
//--
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biWidth = mWidth;
bi.bmiHeader.biHeight = mHeight;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biClrUsed = 24;
bi.bmiHeader.biClrImportant = 256;
hBitmap = CreateDIBSection(hMemoryDC, &bi, DIB_RGB_COLORS, &mImageBuffer, 0, 0);
SelectObject(hMemoryDC, hBitmap);
And here for each screenshot :
if(BitBlt(
hMemoryDC,
0,
0,
mWidth,
mHeight,
hScreenDC,
mPx,
mPy,
SRCCOPY | CAPTUREBLT
))
I do not have any error running my application but this ugly image and only on some computer.
I don't know what is the difference causing that on these computer (All are Win7, Aero actived...).
I do not understand because my code follow all example i found...
Please help me !

You are creating a Device-Independent-Bitmap (CreateDIBSection) then using a Device Dependent Context (CreateCompatibleDC) to work with it. I believe you need to create a device dependent bitmap to be compatible with BitBlt, or use StretchDIBits to support device-independent image data. The reason this works on some computers and not others is because the video driver determines the format of a device-dependent image, which may or may not be the same as the Windows definition of a device-independent image.
Here is an example of capturing an image (yes, its unnecessarily long, but still seems to contain good info): https://msdn.microsoft.com/en-us/library/windows/desktop/dd183402(v=vs.85).aspx
And here is documentation on StretchDIBits, in case you require a DIB: https://msdn.microsoft.com/en-us/library/windows/desktop/dd145121(v=vs.85).aspx

So i finaly find the solution :
It seems that the BitBlt and StretchBlt doesnt really handle correctly the transfer between 32 to 24 bits on some computers...
Now, i use only 32bits with GDI and let FFmpeg with libswscale convert my RGBA image to a YUV format.
My changes :
mWidth = GetDeviceCaps(hScreenDC, HORZRES);
mHeight = GetDeviceCaps(hScreenDC, VERTRES);
mWxHxS = mWidth*mHeight*4;
bi.bmiHeader.biBitCount = 32;
hBitmap = CreateCompatibleBitmap(hScreenDC, mWidth, mHeight);
if(BitBlt(
hMemoryDC,
0,
0,
mWidth,
mHeight,
hScreenDC,
mPx,
mPy,
SRCCOPY | CAPTUREBLT
) && GetDIBits(hScreenDC, hBitmap, 0, mHeight, mImageBuffer, &bi, DIB_RGB_COLORS))
{
return true;
}
Thanks trying to help me !

Related

Using StretchDIBits to copy PNG (with alpha)

Could someone help me?
How I can copy png to virtual HDC.
Transparent pixels are always black.
I tried but alpha is always black.
Seems like BITMAPINFOHEADER does not support alpha.
Thank you very much.
and sorry for my English. Peace.
(Note I need to use GDI only)
// fill background
HBRUSH hbrush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue()));
::FillRect(m_virtualHDC, &wRect, hbrush);
DeleteObject(hbrush);
// load image
QImage pix;
pix.load("D:\\1.png");
// draw image with alpha
BITMAPINFO bmInfo;
ZeroMemory(&bmInfo, sizeof(BITMAPINFO));
BITMAPINFOHEADER& BMPInfoHeader = bmInfo.bmiHeader;
BMPInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
BMPInfoHeader.biWidth = pix.width();
BMPInfoHeader.biHeight = pix.height();
BMPInfoHeader.biPlanes = 1;
BMPInfoHeader.biBitCount = 32;
BMPInfoHeader.biCompression = BI_RGB;
BMPInfoHeader.biSizeImage = pix.byteCount();
BMPInfoHeader.biXPelsPerMeter = 0;
BMPInfoHeader.biYPelsPerMeter = 0;
BMPInfoHeader.biClrUsed = 0;
SetStretchBltMode(m_virtualHDC, HALFTONE);
StretchDIBits(m_virtualHDC,
targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
pix.bits(), &bmInfo, DIB_RGB_COLORS, SRCCOPY);
GDI in general does not support an alpha channel.
You need to use an API that has dedicated alpha support, e. g. AlphaBlend() or GDI+.
Most of these APIs have very poor resampling quality. I suggest to use a third-party library like stb_image_resize to resample in memory and use APIs like AlphaBlend() only to display the final result to the screen, without doing further scaling.

How to draw text with transparency using GDI?

My goal is to dynamically put some arbitrary text into an HICON image (at runtime.) I'm using the following code:
//Error checks are omitted for brevity
//First create font
LOGFONT lf = {0};
lf.lfHeight = -58;
lf.lfWeight = FW_NORMAL;
lf.lfOutPrecision = OUT_TT_PRECIS; //Use TrueType fonts for anti-alliasing
lf.lfQuality = CLEARTYPE_QUALITY;
lstrcpy(lf.lfFaceName, L"Segoe UI");
HFONT hFont = ::CreateFontIndirect(&lf);
//HICON hIcon = original icon to use as a source
//I'm using a large 256x256 pixel icon
hIcon = (HICON)::LoadImage(theApp.m_hInstance, MAKEINTRESOURCE(IDI_ICON_GREEN_DIAMOND), IMAGE_ICON, 256, 256, LR_DEFAULTCOLOR);
ICONINFO ii = {0};
::GetIconInfo(hIcon, &ii);
BITMAP bm = {0};
::GetObject(ii.hbmColor, sizeof(bm), &bm);
SIZE szBmp = {bm.bmWidth, bm.bmHeight};
HDC hDc = ::GetDC(hWnd);
HDC hMemDC = ::CreateCompatibleDC(hDc);
HGDIOBJ hOldBmp = ::SelectObject(hMemDC, ii.hbmColor);
HGDIOBJ hOldFont = ::SelectObject(hMemDC, hFont);
::SetBkMode(hMemDC, TRANSPARENT);
::SetTextColor(hMemDC, RGB(255, 0, 0)); //Red text
//Draw text
//NOTE that DrawText API behaves in a similar way
::TextOut(hMemDC, 0, 0, L"Hello", 5);
::SelectObject(hMemDC, hOldFont);
::SelectObject(hMemDC, hOldBmp);
//We need a simple mask bitmap for the icon
HBITMAP hBmpMsk = ::CreateBitmap(szBmp.cx, szBmp.cy, 1, 1, NULL);
ICONINFO ii2 = {0};
ii2.fIcon = TRUE;
ii2.hbmColor = ii.hbmColor;
ii2.hbmMask = hBmpMsk;
//Create updated icon
HICON hIcon2 = ::CreateIconIndirect(&ii2);
//Cleanup
::DeleteObject(hBmpMsk);
::DeleteDC(hMemDC);
::ReleaseDC(hWnd, hDc);
::DeleteObject(ii.hbmColor);
::DeleteObject(ii.hbmMask);
::DeleteObject(hFont);
and then I can display the icon in my window from OnPaint() handler (so that I can see how it turns out) as such:
::DrawIconEx(dc.GetSafeHdc(), 0, 0,
hIcon2,
256, 256, NULL,
::GetSysColorBrush(COLOR_BTNFACE),
DI_NORMAL);
So here's what I get:
To see what's going on pixel-wise in my hIcon2 I called GetDIBits on its ii.hbmColor from the code above. The resulting pixel array where my word "Hello" was supposed to be shown looked like this:
The pixels are encoded as BGRA in that memory dump, so the 4th byte in each DWORD stands for transparency: 0=transparent, FF=opaque. But in this case TextOut doesn't fill out transparency, or leaves it as 0, which is interpreted as "fully transparent." Instead it seems to pre-multiply it into the RGB colors themselves.
Note that if I keep looking further down the same bitmap, where the green diamond begins, the image pixels seem to have transparency bytes set correctly:
Any idea how to draw text so that the API could set those transparency bytes?
EDIT: As was suggested below I tried the following GDI+ method:
HGDIOBJ hOldBmp = ::SelectObject(hMemDC, ii.hbmColor);
Graphics grpx(hMemDC);
RectF rcfTxt(0.0f, 0.0f, (REAL)szBmp.cx, (REAL)szBmp.cy);
Font gdiFont(L"Segoe UI", 58.0f, FontStyleRegular, UnitPixel);
SolidBrush gdiBrush(Color(255, 0, 0));
StringFormat gdiSF;
gdiSF.SetAlignment(StringAlignmentNear);
gdiSF.SetFormatFlags(StringFormatFlagsNoWrap);
gdiSF.SetHotkeyPrefix(HotkeyPrefixNone);
//The reason I was using GDI was because I was setting
//spacing between letters using SetTextCharacterExtra()
//Unfortunately with GDI+ this does not work!
HDC hTmpDC = grpx.GetHDC();
::SetTextCharacterExtra(hTmpDC, -4); //This doesn't do anything!
grpx.ReleaseHDC(hTmpDC);
grpx.DrawString(L"Hello", 5, &gdiFont, rcfTxt, &gdiSF, &gdiBrush);
::SelectObject(hMemDC, hOldBmp);
and besides not being able to set character spacing (which I could with GDI using SetTextCharacterExtra) here's what I got (slightly enlarged for visibility):
So clearly still an issue with transparency.
Taken from an old post by Microsoft MVP Mike D Sutton here.
When you create a DC it initially has default 'stock' objects selected
into it, including the stock 1*1*1 Bitmap. Since there is a Bitmap
already selected into the DC when you call DrawText() it will still
try and render to it even though pretty much everything (apart from
one pixel) will be clipped.
What you need to do is to create a Bitmap,
either DDB or DIBSection, and select that into your DC before drawing
to it.
First though you need to find the size of your Bitmap since you
want it large enough to display your text in, so for that you use the
DrawText() call again on the initial DC but include the DT_CALCRECT
flag. What this does is rather than drawing anything it simply
measures how large the text is and dumps that into the RECT you pass
the call. From here you can go ahead and create your DIBSection using
those dimensions and select it into your DC. Finally perform your
existing DrawText ()call (you may also want to use SetBkMode/Color())
which will render the text to the DIBSection from which you can get at
the data.
This seems to work pretty well here:
HBITMAP CreateAlphaTextBitmap(LPCSTR inText, HFONT inFont, COLORREF inColour) {
int TextLength = (int)strlen(inText);
if (TextLength <= 0) return NULL;
// Create DC and select font into it
HDC hTextDC = CreateCompatibleDC(NULL);
HFONT hOldFont = (HFONT)SelectObject(hTextDC, inFont);
HBITMAP hMyDIB = NULL;
// Get text area
RECT TextArea = {0, 0, 0, 0};
DrawText(hTextDC, inText, TextLength, &TextArea, DT_CALCRECT);
if ((TextArea.right > TextArea.left) && (TextArea.bottom > TextArea.top)) {
BITMAPINFOHEADER BMIH;
memset(&BMIH, 0x0, sizeof(BITMAPINFOHEADER));
void *pvBits = NULL;
// Specify DIB setup
BMIH.biSize = sizeof(BMIH);
BMIH.biWidth = TextArea.right - TextArea.left;
BMIH.biHeight = TextArea.bottom - TextArea.top;
BMIH.biPlanes = 1;
BMIH.biBitCount = 32;
BMIH.biCompression = BI_RGB;
// Create and select DIB into DC
hMyDIB = CreateDIBSection(hTextDC, (LPBITMAPINFO)&BMIH, 0, (LPVOID*)&pvBits, NULL, 0);
HBITMAP hOldBMP = (HBITMAP)SelectObject(hTextDC, hMyDIB);
if (hOldBMP != NULL) {
// Set up DC properties
SetTextColor(hTextDC, 0x00FFFFFF);
SetBkColor(hTextDC, 0x00000000);
SetBkMode(hTextDC, OPAQUE);
// Draw text to buffer
DrawText(hTextDC, inText, TextLength, &TextArea, DT_NOCLIP);
BYTE* DataPtr = (BYTE*)pvBits;
BYTE FillR = GetRValue(inColour);
BYTE FillG = GetGValue(inColour);
BYTE FillB = GetBValue(inColour);
BYTE ThisA;
for (int LoopY = 0; LoopY < BMIH.biHeight; LoopY++) {
for (int LoopX = 0; LoopX < BMIH.biWidth; LoopX++) {
ThisA = *DataPtr; // Move alpha and pre-multiply with RGB
*DataPtr++ = (FillB * ThisA) >> 8;
*DataPtr++ = (FillG * ThisA) >> 8;
*DataPtr++ = (FillR * ThisA) >> 8;
*DataPtr++ = ThisA; // Set Alpha
}
}
// De-select bitmap
SelectObject(hTextDC, hOldBMP);
}
}
// De-select font and destroy temp DC
SelectObject(hTextDC, hOldFont);
DeleteDC(hTextDC);
// Return DIBSection
return hMyDIB;
}
If you need an example of how to call it then try something like this
(inDC is the DC to render to):
void TestAlphaText(HDC inDC, int inX, int inY) {
const char *DemoText = "Hello World!\0";
RECT TextArea = {0, 0, 0, 0};
HFONT TempFont = CreateFont(50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Arial\0");
HBITMAP MyBMP = CreateAlphaTextBitmap(DemoText, TempFont, 0xFF);
DeleteObject(TempFont);
if (MyBMP) { // Create temporary DC and select new Bitmap into it
HDC hTempDC = CreateCompatibleDC(inDC);
HBITMAP hOldBMP = (HBITMAP)SelectObject(hTempDC, MyBMP);
if (hOldBMP) {
BITMAP BMInf; // Get Bitmap image size
GetObject(MyBMP, sizeof(BITMAP), &BMInf);
// Fill blend function and blend new text to window
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0x80;
bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(inDC, inX, inY, BMInf.bmWidth, BMInf.bmHeight,
hTempDC, 0, 0, BMInf.bmWidth, BMInf.bmHeight, bf);
// Clean up
SelectObject(hTempDC, hOldBMP);
DeleteObject(MyBMP);
DeleteDC(hTempDC);
}
}
}
All credit to answer and code go to original posters on that forum, I've simply reposted it so that this answer will be valid if the links die.
This reply is coming almost 3 years after the question was posted, but people still consult these things long into the future. So I'll explain what's happening.
DrawText (and other GDI text functions) will work on a transparent bitmap. The text is not coming out black even though it displays that way. The alpha channel is set to 0 on all pixels the text draws to, overriding whatever alpha you had set previously. If you set an alpha value in SetTextColor the text will render all black. If you're feeling ambitious you can run through pixel by pixel and target anything not your fill color (which requires a single fill color) but the problem then becomes one of the nature of ClearType being overridden and all alphas are set to whatever you set them to. The text ends up looking very funky. If you use a constant alpha for your background fill you can simply do a blanket run across the entire bitmap's bits after the text is drawn and reset all the alpha values. Since you have to read a byte to determine if it's background or not, you might as well just set every pixel's alpha to whatever the standard alpha is for that image and bypass the slow compares. This works reasonably well and I've found it to be very acceptable. In this day and age, MS should have taken care of this long ago but it's not to be.
https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-antialiasing-with-text-use
Gdiplus::Bitmap bmp( your_Width, your_Height, PixelFormat64bppARGB);
//PixelFormat64bppARGB ARGB needed
FontFamily fontFamily(L"Arial");
Font font(&fontFamily, 29, FontStyleRegular, UnitPoint);
Gdiplus::RectF rectF(00.0f, 10.0f, your_Width, your_Height);
StringFormat stringFormat;
SolidBrush solidBrush(Color(63, 0, 0, 255));
stringFormat.SetAlignment(StringAlignmentCenter);
//solidBrush Color(63, 0, 0, 255) ARGB neede
graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
graphics.DrawString("your_text", -1, &font, rectF, &stringFormat, &solidBrush);
//TextRenderingHintAntiAlias this needed

Get bitmap of browser window

Does anyone know how to get a the bitmap of a Mac and Windwos browser window? I want to capture images from Chrome or FireFox as they are rendered and pass them into a C++ based plug-in.
I've looked into using a Chrome extension, but it's not practical.
Willing to do a Mac and Windows native app.
With the windows API you can grab screenshots fairly easy.
// get the device context of the screen
HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int width = GetDeviceCaps(hScreenDC, HORZRES);
int height = GetDeviceCaps(hScreenDC, VERTRES);
// maybe worth checking these are positive values
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
// get a new bitmap
HBITMAP hOldBitmap = SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = SelectObject(hMemoryDC, hOldBitmap);
// clean up
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
// now your image is held in hBitmap. You can save it or do whatever with it
Im not sure about the exact area of the browser tho, or how to solve it for Mac. If nothing else, its a start.

Capturing camera(USB) via D2XX library or OPENCV

I want to write an application(in c++) in order to capture images from a camera which is using in a acquisition system. The camera is connected to a box (acquisition system) and I've found that the chip that is used is FTDI. The chip is located in the box between camera and PC. The camera is connected to this box. A USB cable is connected to a PC and the box. Some other tools are connected to the box which are not important.
Moreover, there is a simple commercial application which is written by MFC and I want to do exactly the same. In the folder of the application there are D2XX driver files(ftd2xx.h, etc) and an information file(*.inf) of the camera.
Also, the camera is not recording video but taking photo in short intervals(<0.1s) and the interval is determined by the acquisition system not the commercial application(the acquisition system detect when camera has to take photo)
Here is my question:
Since the information file of the USB device is provided, could I just utilize the Open-CV lib to capture the camera OR do I have to only use D2XX library?
If I have to use D2XX library in order to read the data, how could I convert the raw data to Image format (in Qt)?
I can't simply write application and test on the device over and over to find the solution since the device is located far from my location and for every test I have to travel this distance. So, I want to make sure that my application will work.
A company from China made the device for us and they won't support it any more :(
The camera uses a custom communications protocol, it doesn't implement an imaging device class. OpenCV won't see it, neither will any other multimedia library. No matter what, you need to implement that protocol. You can then expose it to OpenCV if you wish.
To convert into image, try this:
Mat hwnd2mat(HWND hwnd){
HDC hwindowDC, hwindowCompatibleDC;
int height, width, srcheight, srcwidth;
HBITMAP hbwindow; // <-- The image represented by hBitmap
cv::Mat src; // <-- The image represented by mat
BITMAPINFOHEADER bi;
// Initialize DCs
hwindowDC = GetDC(hwnd); // Get DC of the target capture..
hwindowCompatibleDC = CreateCompatibleDC(hwindowDC); // Create compatible DC
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
RECT windowsize; // get the height and width of the screen
GetClientRect(hwnd, &windowsize);
srcheight = windowsize.bottom;
srcwidth = windowsize.right;
height = windowsize.bottom *2/ 2; //change this to whatever size you want to resize to
width = windowsize.right *2/ 2;
src.create(height, width, CV_8UC4);
// create a bitmap
hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height; //this is the line that makes it draw upside down or not
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;
// use the previously created device context with the bitmap
SelectObject(hwindowCompatibleDC, hbwindow);
// copy from the window device context to the bitmap device context
StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, 0, 0, srcwidth, srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS); //copy from hwindowCompatibleDC to hbwindow
// avoid memory leak
DeleteObject(hbwindow); DeleteDC(hwindowCompatibleDC); ReleaseDC(hwnd, hwindowDC);
return src;
}

Completely transparent image

Does anyone know how I can create a Completely transparent image or given am image (HBITMAP) how can I wipe it Completely so that all pixels in it are 100% transparent?
Thank you.
The bitmap needs to be 32-bit so it has an alpha channel that you can set opacity values with. If your image is not 32-bit, you will have to create a new 32-bit bitmap and copy the original pixels into it.
I also found the solution without the use of GDI+.
BITMAPV5HEADER bi = {sizeof(BITMAPV5HEADER), 320, 240, 1, 32, BI_BITFIELDS, 0, 0, 0, 0, 0, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000};
HDC hDc = ::CreateCompatibleDC(0);
if(NULL != hDc)
{
RGBQUAD* pArgb;
HBITMAP tBmp = ::CreateDIBSection(hDc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&pArgb, NULL, 0);
if(NULL != tBmp)
{
int x, y;
DWORD pBits;
pBits = (DWORD*)pArgb;
for(y = 0; y < bi.bV5Height; y++)
{
for(x = 0; x < bi.bV5Width; x++)
{
*pBits = 0;
pBits++;
}
}
}
::DeleteObject(hDc);
}
tBmp will be a 320 x 240 bitmap that is completely transparent
You can use a monochrome bitmap as a mask to create transparent images from colour ones. This is quite complex. See this example (in VB but uses Win32 APIs directly)
Alternatively, the TransparentBlt function might make what you're trying to do unnecessary.
I found the answer in GDI+. The best way to do this is to attach the device context of the image that you are working with with GDI+ graphic object and then call its clear method.