SFML community forums

Help => Graphics => Topic started by: Raincode on November 27, 2016, 03:03:04 pm

Title: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
Post by: Raincode on November 27, 2016, 03:03:04 pm
Hello,

I need a quick hint on how to properly load e.g. an sf::Texture from memory with the loadFromMemory function. Specifically, I am not sure what precisely the function expects (some data and its size - ok - but what data?). The docs state "load from a file in memory" - does this mean I must construct a .bmp file in memory and pass the pointer to the first byte of the entire file + the size of the file to loadFromMemory?

The error:
Failed to load image from memory. Reason: Image not of any known type, or corrupt

How I obtain my HBITMAP:
OpenClipboard(nullptr);
if (!IsClipboardFormatAvailable(CF_BITMAP))
{
    std::cerr << "No Bitmap in clipboard\n";
    return -1;
}
HBITMAP bmp = (HBITMAP)GetClipboardData(CF_BITMAP);
CloseClipboard();

Here is my code, as you can see I cluelessly use GetDIBits, but I don't know how to load the sf::Image properly.

bool SFMLLoadHBitmapAsImage(HBITMAP bitmap, sf::Image image)
{
    HDC deviceContext = GetDC(nullptr);
    if (!deviceContext)
    {
        return false;
    }
   
    //Create BITMAPINFO variable, set size
    BITMAPINFO bmpInfo{};
    bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);

    //Get the BITMAPINFO structure from the bitmap
    // I believe this populates the BITMAPINFOHEADER portion of BITMAPINFO
    if (!GetDIBits(deviceContext, bitmap, 0, 0, nullptr, &bmpInfo, DIB_RGB_COLORS))
    {
        return false;
    }

    BYTE *pixels = new BYTE[bmpInfo.bmiHeader.biSizeImage];
    bmpInfo.bmiHeader.biCompression = BI_RGB;

    if (!GetDIBits(deviceContext, bitmap, 0, bmpInfo.bmiHeader.biHeight, (LPVOID)pixels, &bmpInfo, DIB_RGB_COLORS))
    {
        return false;
    }

    if (!image->loadFromMemory(?, ?)) // <-- Not sure what goes here
    {
        return false;
    }

    delete[] pixels;
    ReleaseDC(nullptr, deviceContext);
    return true;
}

I pretty much am trying to update the code from here https://www.codeproject.com/tips/527353/load-an-hbitmap-into-sfml-sf-image-container (https://www.codeproject.com/tips/527353/load-an-hbitmap-into-sfml-sf-image-container) for SFML2.

This feels like an intuitive solution, but it gives said error:
if (!image->loadFromMemory(pixels, bmpInfo.bmiHeader.biSizeImage))
{
    return false;
}
My guess is this is missing something, since the function isn't called loadFromPixels anymore.

I have about 20+ Tabs of msdn open and am actively trying to understand this hot mess of DDBs and DIBs, until then maybe someone shall ease my pain and point me in the right direction. If I find the solution beforehand I'll make sure to update my post.

Kind Regards,
Raincode
Title: Re: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
Post by: Laurent on November 27, 2016, 06:27:32 pm
If you have a pixel array rather than a file in memory, the solution is rather to use Texture::create + Texture::update. You're right that loadFromMemory takes a true image file as input (the same as you would have on disk, but in memory instead).
Title: Re: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
Post by: Raincode on November 27, 2016, 06:47:57 pm
Hi Laurent,

I just tried simply passing the pixel array to update and the result looks pretty good.

I think I have to switch the R and B portions, since windows does this the other way around.

However, everything is still upside down and inside out. Not sure what went wrong there.

Anyways thanks for your reply, I think I can work with that.
Title: Re: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
Post by: alimao on July 18, 2022, 11:04:55 pm
i know its a bit late and you probably dont remember but i am trying to do a similar thing and do you remember what size did you pass to the function?
Title: Re: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
Post by: liulun on June 29, 2023, 07:27:50 am
Here is what I did.

sf::Texture texture;
void shotScreen() {
    int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
    int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
    int w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    int h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
    HDC hScreen = GetDC(NULL);
    HDC hDC = CreateCompatibleDC(hScreen);
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
    DeleteObject(SelectObject(hDC, hBitmap));
    BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x, y, SRCCOPY);
    unsigned int dataSize = ((w * 32 + 31) / 32) * 4 * h;
    BITMAPINFO Info = { sizeof(BITMAPINFOHEADER), static_cast<long>(w), static_cast<long>(0 - h), 1, 32, BI_RGB, dataSize, 0, 0, 0, 0 };
    std::uint8_t* pixels = new std::uint8_t[dataSize];
    int r = GetDIBits(hDC, hBitmap, 0, h, (LPVOID)pixels, &Info, DIB_RGB_COLORS);
    for (int x = 0; x < dataSize; x += 4)
    {
        auto r = pixels[x + 2];
        auto b = pixels[x];
        pixels[x] = r;
        pixels[x + 2] = b;
    }
    auto flag = texture.create(sf::Vector2u(w, h));
    texture.update(pixels);
    delete[] pixels;
    DeleteDC(hDC);
    ReleaseDC(NULL, hScreen);
    DeleteObject(hBitmap);
}