how get pixels colors from HDC?

how get pixels colors from HDC?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void GetPixelsFromHDC(HDC Destination, int *pixels)
{
    //Get the HBITMAP from HDC:
    HBITMAP hBitmap = (HBITMAP)GetCurrentObject(Destination, OBJ_BITMAP);
    if(hBitmap==NULL) MessageBox(NULL,"error", "HBITMAP", MB_OK);
    
    //Get the DIBSECTION  information from HBITMAP:
    DIBSECTION dibs;
    if(::GetObject(hBitmap, sizeof(DIBSECTION), &dibs)==0)  MessageBox(NULL,"error", "DIBSECTION", MB_OK);
    
    //Get BITMAPINFO information from HBITMAP:
    BITMAPINFO bitinfo;
    if(::GetObject(hBitmap, sizeof(BITMAPINFO), &bitinfo)==0)  MessageBox(NULL,"error", "BITMAPINFO", MB_OK);
    
    //Get the pixels from 'GetDIBitd()':
    pixels = (unsigned int*)malloc(dibs.dsBmih.biWidth*dibs.dsBmih.biHeight*sizeof(unsigned int));
    if(::GetDIBits(Destination, hBitmap,0,0,&pixels,&bitinfo,dibs.dsBmih.biClrUsed)==0)  MessageBox(NULL,to_string(GetLastError()).c_str(), "GetDIBits", MB_OK);
    //The 'GetLastError()' give me zero... but the 'GetDIBits()' give me zero... from MSDN:
    //"If the function fails, the return value is zero."
    //https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getdibits 
}

how these functions works:
1 - Get HBITMAP from HDC using the 'GetCurrentObject()';
2 - Get DIBSECTION from HBITMAP using 'GetObject()';
3 - Get BITMAPINFO from HBITMAP using 'GetObject()';
4 - Get the pixel array size using the DIBSECTION;
5 - Get the 'GetDIBits()' using the others variables steps.
The 'GetDIBits()' is give me zero and the the 'GetLastError()' give me zero too.. what i'm doing wrong for get an error?

PS: Moderator, please why, when i enter on page, i get like logined... after create the topic, it will ask me for login? if i'm login, why ask me for login?
by these error, i recreate these topic :(
Last edited on
The fourth parameter tells the function how many scanlines to copy, and you're passing zero. This is equivalent to doing memcpy(dst, src, 0).
how can i get the scanlines?
It's the height of the bitmap, which you're already using in the malloc() call.
i tryied that without sucess :(
1
2
3
4
5
6
7
8
9
10
11
12
13
void GetPixelsFromHDC(HDC Destination, int *pixels)
{

    HBITMAP hBitmap = reinterpret_cast<HBITMAP>(GetCurrentObject(Destination, OBJ_BITMAP));
    DIBSECTION dibs;
    ::GetObject(hBitmap, sizeof(DIBSECTION), &dibs);
    BITMAPINFO bitinfo;
    ::GetObject(hBitmap, sizeof(BITMAPINFO), &bitinfo);
    pixels = (unsigned int*)malloc(dibs.dsBmih.biWidth*dibs.dsBmih.biHeight*sizeof(unsigned int));
    ::GetDIBits(Destination, hBitmap,0,dibs.dsBmih.biHeight,&pixels,&bitinfo,dibs.dsBmih.biClrUsed);
   //Just for test it:
    for(int i=0; i<100; i++)  pixels[i]=RGB(255,0,0);
}

on execution i have an error: "Process returned -1073740940 (0xC0000374) execution time : 0.094 s
Press any key to continue."
the problem is on pixels array size or GetDIBits().
wow... see these line:
cout << "\t" << dibs.dsBmih.biWidth;
maybe, because i didn't created the HBITMAP on form(console window)?
sometimes i get a big negative number... why?
There are several problems with the GetDIBits(...) function. See:

https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getdibits

Especially this:
The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function.


The next problem is the last parameter where you use dibs.dsBmih.biClrUsed. This could be DIB_PAL_COLORS but you want DIB_RGB_COLORS. So use the latter.

The next important thing is this:
The scan lines must be aligned on a DWORD except for RLE compressed bitmaps.
This would be the case when using the COLORREF type (instead of int).

Consider this:
If lpvBits is NULL and the bit count member of BITMAPINFO is initialized to zero, GetDIBits fills in a BITMAPINFOHEADER structure or BITMAPCOREHEADER without the color table. This technique can be used to query bitmap attributes.
So when you call this twice. Without and with the lpvBits set your relatively safe.

The function return the scan lines. Check that value to determine whether it was successful.
Note that it only copies the bits of the source bitmap.
so the best is use the GetBitmapDIBS() and after change the pointer values, we use the SetBitmapDIBS()... right?
so the best is use the GetBitmapDIBS() and after change the pointer values, we use the SetBitmapDIBS()... right?
No, for 2[3] reasons:

1. The probably have the same restrictions like GetDIBits(...)
2. You still don't know what the nature of these bits is
[3. They are deprecated]

I would suggest CreateDIBSection(...):
1
2
3
4
5
6
7
8
9
10
        BITMAPINFO bmi;
        memset(&bmi, 0, sizeof(bmi));
        bmi.bmiHeader.biSize = sizeof(bmi);
        bmi.bmiHeader.biWidth = x; // X-size
        bmi.bmiHeader.biHeight = y; // Y-size
        bmi.bmiHeader.biPlanes = 1;
        bmi.bmiHeader.biBitCount = 32;
        bmi.bmiHeader.biCompression = BI_RGB;

        hb = CreateDIBSection(HDC{}, &bmi, DIB_RGB_COLORS, &dibs, 0, 0);
This will provide full control of the data.

You can use BitBlt(...)/Memory DC to copy from another bitmap.
Last edited on
"I would suggest CreateDIBSection(...)"
in these case the 'dibs.dsBmih.biWidth' and 'dibs.dsBmih.biHeight' is\are the right size?

"You can use BitBlt(...)/Memory DC to copy from another bitmap."
i'm sorry, but not in these case... i'm drawing the image pixel to pixel for i draw it using 3D rotation
in these case the 'dibs.dsBmih.biWidth' and 'dibs.dsBmih.biHeight' is\are the right size?

Sorry it is actually
hb = CreateDIBSection(HDC{}, &bmi, DIB_RGB_COLORS, &pixels, 0, 0);

Yes you can directly access the pixels.

i'm sorry, but not in these case... i'm drawing the image pixel to pixel for i draw it using 3D rotation
You can change the pixels a much as you want. It will not appear on the screen unless you BitBlt(...) it (when you're finished drawing) to the window/screen DC. That would be the same for your first approach.
i did these change but nothing is changed :(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void GetPixelsFromHDC(HDC Destination, int *pixels)
{
    //Get HBITMAP, BITMAP and BITMAPINFO data:
    HBITMAP hBitmap= (HBITMAP)GetCurrentObject (Destination,OBJ_BITMAP);

    BITMAP structBitmapHeader;
    memset( &structBitmapHeader, 0, sizeof(BITMAP) );
    GetObject(hBitmap, sizeof(BITMAP), &structBitmapHeader);

    BITMAPINFO bmi;
    //The BITMAPINFO  must have zero on all members:
    memset(&bmi, 0, sizeof(bmi));
   //And the size must be added:
    bmi.bmiHeader.biSize = sizeof(bmi);
    bmi.bmiHeader.biWidth = structBitmapHeader.bmWidth;
    bmi.bmiHeader.biHeight = structBitmapHeader.bmHeight;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;

    //Creating the pixel pointer:
    CreateDIBSection(Destination, &bmi, DIB_RGB_COLORS, &pixels, 0, 0);

    //Change the pixels values just for test:
    for(int i=0; i<100; i++)  pixels[i]=RGB(0,255,0);
}
CreateDIBSection(...) creates an entirely new bitmap. It returns a HBITMAP which can be selected to the destination hdc. See:

https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-selectobject

When you do so you should see your changes. Note that pixels is uninitialized when created hence it has a random content. You may use memset(...) to change that.

I don't know whether it is allowed to access the pixels after you selected the bitmap though...
:(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void GetPixelsFromHDC(HDC Destination, int *pixels)
{
    //Get HBITMAP, BITMAP and BITMAPINFO data:
    HBITMAP hBitmap= (HBITMAP)GetCurrentObject (Destination,OBJ_BITMAP);
    BITMAP structBitmapHeader;
    memset( &structBitmapHeader, 0, sizeof(BITMAP) );
    GetObject(hBitmap, sizeof(BITMAP), &structBitmapHeader);
    //cout << "Width: " << structBitmapHeader.bmWidth << "\tHeight: " << structBitmapHeader.bmHeight << "\n";
    BITMAPINFO bmi;
    memset(&bmi, 0, sizeof(bmi));
    bmi.bmiHeader.biSize = sizeof(bmi);
    bmi.bmiHeader.biWidth = structBitmapHeader.bmWidth;
    bmi.bmiHeader.biHeight = structBitmapHeader.bmHeight;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;

    //Creating the pixels pointer:
    pixels = (unsigned int*)malloc(bmi.bmiHeader.biWidth*bmi.bmiHeader.biHeight*sizeof(unsigned int));
    HBITMAP hb =CreateDIBSection(Destination, &bmi, DIB_RGB_COLORS, &pixels, 0, 0);
    if(hb==NULL) MessageBox(NULL, "error", "error", MB_OK);
    HGDIOBJ  r= SelectObject(Destination,hb);
    if(r==NULL) MessageBox(NULL, to_string(GetLastError()).c_str(), "error no bitmap selected", MB_OK);
    //Change the pixels values just for test:
    for(int i=0; i<100; i++)  pixels[i]=RGB(0,255,0);
}

the 'r' is NULL, but the 'GetLastError()' is zero.. so what is wrong?
Last edited on
the window or console window have a HBITMAP object or just HDC?
maybe i need create a HDC and HBITMAP memory and then draw it on window HDC.
the 'r' is NULL, but the 'GetLastError()' is zero.. so what is wrong?
SelectObject(...) returns the previous selected. There is none. When GetLastError() returns 0 it is supposed to be ok.

On the console I have no idea how that would work...

So does it work?
"So does it work?"
The answer is no.
The best is create a memory HDC and HBITMAP for the entire result.
Then i use BitBlt() for get the result. All Windows have a HDC.
I need another correction: after use GetBitmapBits(), i must use SetBitmapsBits() for see the results?
the answer is YES.
thanks for all
Topic archived. No new replies allowed.