Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory  (Read 4047 times)

0 Members and 1 Guest are viewing this topic.

Raincode

  • Full Member
  • ***
  • Posts: 118
    • View Profile
HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
« 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 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
« Last Edit: November 27, 2016, 03:47:15 pm by Raincode »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
« Reply #1 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).
Laurent Gomila - SFML developer

Raincode

  • Full Member
  • ***
  • Posts: 118
    • View Profile
Re: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
« Reply #2 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.

alimao

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
« Reply #3 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?

liulun

  • Newbie
  • *
  • Posts: 1
    • View Profile
Re: HBITMAP --> sf::Texture/sf::Image using ::loadFromMemory
« Reply #4 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);
}
 

 

anything