GetDIBits zwraca "czarną" tablicę

0

Mam kodzik:

#include <iostream>
#include <windows.h>
#include <stdio.h>

using namespace std;

int main()
{
    static BITMAPINFO bh;
    unsigned char* bits = new unsigned char[1080*1920*3];

    HDC hdc = GetDC(HWND_DESKTOP);
    HBITMAP hbm = CreateCompatibleBitmap( hdc, 1920, 1080);

    bh.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bh.bmiHeader.biWidth  = 1920;
    bh.bmiHeader.biHeight = 1080;
    bh.bmiHeader.biPlanes = 1;
    bh.bmiHeader.biBitCount = 24;
    bh.bmiHeader.biCompression = BI_RGB;
    bh.bmiHeader.biSizeImage = 0;

    GetDIBits(hdc,hbm,0,1080,bits,(BITMAPINFO*)&bh,DIB_RGB_COLORS);

    FILE *f = fopen("C:\dump.raw", "wb");
    fwrite(bits, 1, 1920*1080*3, f);
    fclose(f);
    return 0;
}

Który powinien raczej działać, ale do pliku mi zwraca całą czarną tablicę (obraz jest cał czarny). Jakieś pomysły? Szukam i szukam, pytałem paru osób, ale nie wiem o co może chodzić :(

0
  1. Dlaczego zakładasz, że głębia koloru w planie takim jaki ma ekran nie może przekroczyć 24-bit? Dzisiaj standardem jest 32-bit, a takie coś Ci się nie zmieści jeżeli arbitralnie dasz za mało miejsca.
  2. Skoro dajesz systemowi wolną rękę jakiego planu użyje, to czemu próbujesz arbitralnie wypełniać BITMAPINFO danymi wyssanymi z palca?
1

Tworzysz uchwyt do bitmapy, której tak naprawdę nigdzie nie ma. Każda bitmapa musi mieć jakiś kontekst, musi być przypisana do jakiegoś urządzenia.
Stwórz kompatybilne memory device (CreateCompatibleDC), przypisz kompatybilną bitmapę do tego urządzenia (SelectObject), skopiuj tam zawartość ekranu (BitBlt) i dopiero z niej pobierz piksele (GetDIBits). Zwolnij bitmapę (DeleteObject) i urządzenie (DeleteDC).

0

Spróbowałem coś takiego, ale nadal nic:

#include <iostream>
#include <windows.h>
#include <stdio.h>

using namespace std;

int main()
{
    static BITMAPINFO bh;
    unsigned char* bits = new unsigned char[1080*1920*3];

    HDC hdc = GetDC(HWND_DESKTOP);
    HDC hdcc = CreateCompatibleDC(hdc);
    HBITMAP hbm = CreateCompatibleBitmap( hdc, 1920, 1080);
    SelectObject(hdcc, hbm);
    BitBlt(hdc, 0, 0, 1920, 1080, hdcc, 0, 0, SRCCOPY);

    bh.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bh.bmiHeader.biWidth  = 1920;
    bh.bmiHeader.biHeight = 1080;
    bh.bmiHeader.biPlanes = 1;
    bh.bmiHeader.biBitCount = 24;
    bh.bmiHeader.biCompression = BI_RGB;
    bh.bmiHeader.biSizeImage = 0;

    GetDIBits(hdc,hbm,0,1080,bits,(BITMAPINFO*)&bh,DIB_RGB_COLORS);

    DeleteObject(hbm);
    ReleaseDC(hdc);
    DeleteDC(hdcc);

    FILE *f = fopen("C:\dump.raw", "wb");
    fwrite(bits, 1, 1920*1080*3, f);
    fclose(f);
    return 0;
}
1

Odwrotnie w BitBlt: najpierw podaje się docelowy kontekst, a później źródłowy.

Ten kod w ogóle się skompilował? ReleaseDC przyjmuje dwa parametry, w tym uchwyt do okna, którego nie masz ;). W ogóle wywołanie tej funkcji nie jest potrzebne.

0

Wielkie dzięki! Po zmianie obraz jest taki: http://puu.sh/1k7jh czyli odwrócony i nie w rgb tylko bgr dało by się to jakoś w miarę szybko "zamienić" na rgb i odwrócić (chyba, że to wpisywanie do pliku mi coś miesza)?

0

Zmieniłem bh.bmiHeader.biHeight = 1080; na bh.bmiHeader.biHeight = -1080; (liczba przeciwna) i obraz już nie jest odwrócony :) Teraz jeszcze tylko pozostała zamiana na rgb. Jakieś porady?

1

Jeden problem już rozwiązałeś - domyślnie GetDiBits zwraca bitmapę bottom-up. Żeby zamienić ją na top-down trzeba podać ujemną wartość biHeight. Drugi problem jest już właściwie z samym wyświetlaniem. Nasza bitmapa zapisana jest poprawnie, jako RGB. Jednak bitmapy zapisywane do plików (pliki BMP) mają kolory zapisane odwrotnie, czyli w przypadku bitmapy 24-bitowej: BGR888.

for(int i = 0; i < height; i++)
{
    for(int j = 0; j < width * 3; j += 3)
    {
        const size_t offset = i * width * 3 + j;
            
        unsigned char red = bits[offset];
        bits[offset] = bits[offset + 2];
        bits[offset + 2] = red;
    }
}

Mało optymalny, ale łatwy do zrozumienia kod. Trzeba natomiast zwrócić uwagę na jedną bardzo ważną rzecz - wiersze zawsze są uzupełniane do wielokrotności 4 bajtów. Tutaj problemu nie ma, bo (1920 * 3) dzieli się przez 4, ale w innych przypadkach trzeba mieć to na uwadze.

0

Wielkie dzięki za masakryczną pomoc! :)
Temat uważam za rozwiązany !

1 użytkowników online, w tym zalogowanych: 0, gości: 1