SFML community forums

Help => Graphics => Topic started by: Mörkö on January 14, 2015, 05:34:57 pm

Title: sf::Texture load from memory
Post by: Mörkö on January 14, 2015, 05:34:57 pm
Hi. I'm having trouble figuring out how sf::Texture::loadFromMemory works. I would be grateful if you could demonstrate a complete minimal working example how to use it.

If you don't want to generate your own image, then you may take one of these:

(click to show/hide)

(click to show/hide)

Thanks.
Title: Re: sf::Texture load from memory
Post by: Laurent on January 14, 2015, 07:33:08 pm
This function is simple, it acts the same as loadFromFile except that the file contents are in memory rather than on the disk.

Sorry I haven't got the time to write a minimal example for it, maybe later.
Title: Re: sf::Texture load from memory
Post by: Jesper Juhl on January 14, 2015, 09:44:51 pm
Here's a quick and dirty example that I just quickly threw together:
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <iostream>
#include <fstream>
#include <cstdlib>

int main()
{
    sf::ContextSettings glsettings;
    glsettings.antialiasingLevel = 2;
    sf::RenderWindow window{sf::VideoMode{300, 80}, "loadFromMemory Example", sf::Style::Titlebar | sf::Style::Close, glsettings};

    const auto desktop = sf::VideoMode::getDesktopMode();
    window.setPosition({static_cast<int>(desktop.width / 2 - window.getSize().x / 2),
                       static_cast<int>(desktop.height / 4 - window.getSize().y / 4)});

    window.setMouseCursorVisible(false);
    window.setVerticalSyncEnabled(true);
    window.setKeyRepeatEnabled(false);

    std::ifstream texture_file{"example-texture.png", std::ifstream::binary};
    std::vector<char> buffer;
    if (texture_file) {
        // get length of file:
        texture_file.seekg(0, texture_file.end);
        const auto length = texture_file.tellg();
        if (!length) {
            std::cerr << "Cannot load zero byte texture file" << std::endl;
            return EXIT_FAILURE;
        }
        buffer.resize(length); // reserve space

        texture_file.seekg(0, texture_file.beg);

        auto start = &*buffer.begin();
        texture_file.read(start, length);
        texture_file.close();
    } else {
        std::cerr << "Could not open texture file" << std::endl;
        return EXIT_FAILURE;
    }

    sf::Texture texture;
    if (!texture.loadFromMemory(&buffer[0], buffer.size())) {
        std::cerr << "Texture load failed" << std::endl;
        return EXIT_FAILURE;
    }

    sf::Sprite sprite;
    sprite.setTexture(texture);

    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        window.clear(sf::Color::Black);
        window.draw(sprite);
        window.display();
    }

    return EXIT_SUCCESS;
}
 

The "example-texture.png" image I used is here:
(http://i.imgur.com/MKeYIIS.png)
And here's a screenshot of the result:
(http://i.imgur.com/Opn07bD.png)
Title: Re: sf::Texture load from memory
Post by: Mörkö on January 14, 2015, 11:43:11 pm
Yeah, I guess that will do it, but it's not quite what I was after  ;D

I should have mentioned my goal is to embed some images in the executable, so obviously loading the data from a file defeats the purpose. Sorry my bad.

How would you do it with C formatted data?
Title: Re: sf::Texture load from memory
Post by: Jesper Juhl on January 14, 2015, 11:49:52 pm
Whether or not you load a file from disk to a memory buffer and then loadFromMemory() from that buffer or have a memory buffer already embedded in the executable (like an array or similar) makes no difference what-so-ever. As long as you can pass loadFromMemory() the address of the start of your memory buffer as well as the size, it doesn't care how that data got to be in memory - it'll just load it.
So, if your data is in an array named "foo" embedded in the executable, then just do
Code: [Select]
loadFromMemory(foo, sizeof(foo / sizeof(foo[0])); or similar.
Title: Re: sf::Texture load from memory
Post by: Hapax on January 15, 2015, 01:13:16 am
The memory that loadFromMemory loads from should still be in a recognised file format (http://www.sfml-dev.org/documentation/2.0/classsf_1_1Image.php#aaa6c7afa5851a51cec6ab438faa7354c).
If you require to load a texture from raw pixel data, you would use sf::Image directly and create one from the data (http://www.sfml-dev.org/documentation/2.0/classsf_1_1Image.php#a1c2b960ea12bdbb29e80934ce5268ebf). Then, you load the sf::Texture from the sf::Image (http://www.sfml-dev.org/documentation/2.0/classsf_1_1Texture.php#abec4567ad9856a3596dc74803f26fba2).
Title: Re: sf::Texture load from memory
Post by: grok on January 15, 2015, 06:21:59 am
Mörkö,
am I right you want to embed your encrypted images into the executable and then load them normally using loadFromMemory into sf::Texture?
if yes, then it is possible, but requires a few more steps to be performed:
1) load your image in the memory
2) perform some encryption
3) save that chunk of memory into somewhere (probably on disk)
4) copy that chunk of bytes from disk to some static array in your program
5) when your program runs you decrypt that array
6) load image normally from the decrypted chunk of memory you got from 5).

Or did I miss your point?  8)
Title: Re: sf::Texture load from memory
Post by: Laurent on January 15, 2015, 07:55:43 am
static const char embeddedImageData[] =
{
    /* content generated by one of the hundreds
        of programs that do it, or by your own small utility
       (doesn't take more than 10 lines of C++ code) */

};

texture.loadFromMemory(embeddedImageData, sizeof(embeddedImageData));

Example of code that generate such an array:

#include <fstream>
#include <iomanip>

int main(int argc, char** argv)
{
    std::ifstream in("image.png", std::ios_base::binary);
    std::ofstream out("header.hpp");

    out << "const char imageData[] =" << std::endl;
    out << "{" << std::endl;
    do
    {
        out << "    ";
        for (int i = 0; (i < 20) && in; ++i)
            out << "0x" << std::hex << std::setw(2) << std::setfill('0') << in.get() << std::dec << ", ";
        out << std::endl;
    }
    while (in);
    out << "};" << std::endl;

    return 0;
 

Note that it can be more concise if you don't care about the pretty formatting.

Quote
loadFromMemory(foo, sizeof(foo / sizeof(foo[0]));
This one is wrong, we really want the size in bytes, not the number of elements.
Title: Re: sf::Texture load from memory
Post by: Jesper Juhl on January 15, 2015, 07:59:36 am
Of course. Stupid late at night mistake.
Title: Re: sf::Texture load from memory
Post by: Jesper Juhl on January 15, 2015, 09:08:16 am
am I right you want to embed your encrypted images into the executable and then load them normally using loadFromMemory into sf::Texture?
if yes, then it is possible, but requires a few more steps to be performed:
You are overcomplicating things.

First of all, the easiest way to get a C array from an image is to just use the 'convert' tool from ImageMagick (http://www.imagemagick.org/) or one of a ton of other similar tools.

And there is certainly no need to use any encryption as you wrote.
Title: Re: sf::Texture load from memory
Post by: Mörkö on January 15, 2015, 05:24:40 pm
Thanks all, and esp Jesper and Laurent for the examples, it's clear now, and I got it to work.

Quote from: grok
am I right you want to embed your encrypted images into the executable and then load them normally using loadFromMemory into sf::Texture?

Nope, like Jesper Juhl said, embedding is enough.
Title: Re: sf::Texture load from memory
Post by: eXpl0it3r on January 20, 2015, 04:02:14 pm
Just in case someone runs into this topic again and doesn't fully understand how it got resolved.

GIMP exports files in the pixel format, meaning that you get an array of char with each char representing a pixel component and each 4 chars representing a full pixel in the RGBA format. If you want to use this kind of format, you can call the sf::Image::create(width, height, pixels) function (http://www.sfml-dev.org/documentation/2.2/classsf_1_1Image.php#a1c2b960ea12bdbb29e80934ce5268ebf). This is also optimal for direct usage with the sf::Window::setIcon() function.

However if you want to use the loadFromMemory() function, you actually need to have the full binary data of the image in memory. It needs to be in a supported format like PNG, JPEG or similar. So basically a file from your disk, but in memory.
Title: Re: sf::Texture load from memory
Post by: Austin J on January 22, 2015, 06:18:52 am
Yeah, the only time I've seen the call for loadFromMemory() is if you loaded the file with another lib, then passed it on, which does arise for some people.
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 25, 2015, 10:52:48 pm
Is this what I would use if I wanted to load a texture from a resource file that had been compiled into the exe?
Title: Re: sf::Texture load from memory
Post by: Ixrec on January 25, 2015, 11:06:08 pm
Yes.
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 25, 2015, 11:06:55 pm
So how would I reference the image in the resource file? I should say I have very little experience with resource files so I don't know how to write them or reference the images within them, though I have been using SFML for quite a while.
Title: Re: sf::Texture load from memory
Post by: Ixrec on January 25, 2015, 11:15:40 pm
To my knowledge resource files are a Windows-specific thing; at least I was never able to find any evidence that Mac and/or Linux support anything similar.  So you'll have to look at Windows documentation and tools to figure that out. SFML definitely doesn't support it directly.  Of course, that would make your code no longer cross-platform.
Title: Re: sf::Texture load from memory
Post by: eXpl0it3r on January 25, 2015, 11:41:19 pm
Yes, resource files are Windows specific and I'm not sure how easily you can pass them on to SFML classes.

What we've here been talking about however, is converting an image into C++ code data, which you directly link/include into your application. This also works for any C++ compiler on any platform.

The main confusion on this thread was about the format you'd convert the image into. If you want to use the loadFromMemory function, your data must be a conversion from the binary file. If you want to use the create function, your data must be in the RGBA format.

In my small application (http://en.sfml-dev.org/forums/index.php?topic=17188.msg124699#msg124699) I've used GIMP to generate the header file and then created an image out of it.

Incomplete example:

logo.hpp
#pragma once

#include <SFML/Config.hpp>

static const struct {
        sf::Uint32      width;
        sf::Uint32      height;
        sf::Uint32      bytes_per_pixel; // 2:RGB16, 3:RGB, 4:RGBA
        sf::Uint8       data[250 * 43 * 4 + 1];
} IMG_LOGO = {
    250, 43, 4,
    "\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31"
    "\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33"
    "\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377"
    "\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31"
    "\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33\32\31\377\33"
        // etc.

Application.cpp
#include "logo.hpp"

Application::Application()
{
        sf::Image img;
        img.create(IMG_LOGO.width, IMG_LOGO.height, IMG_LOGO.data);
        m_tex_logo.loadFromImage(img);
        // etc.
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 26, 2015, 09:29:25 pm
Thanks, that's been working really well with my 4x4, 8x8 and 16x16 images. However I now need to include my 500x100 images into my code and when I export them from Gimp, the code is literally 10800 lines long, per image, and c++ doesn't allow strings that long. Is there any way around that?
Title: Re: sf::Texture load from memory
Post by: Jesper Juhl on January 26, 2015, 09:31:48 pm
Sure. Split it into multiple parts in the source, combine into one in memory and load. If you insist on building them into the executable. But, to be honest, I don't really see the point. Why not keep the resources in a seperate resource file?
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 26, 2015, 09:36:22 pm
I'm already doing that, I have several images that are about 500 x 1000 and I was already splitting each into 10. I read on stack overflow that you can convert it into a hex array, but I don't know how to do that or how to use it with sfml. Is this where I'd use Laurent's earlier post about a converter? I must confess when I first read it I didn't understand it and I hadn't been back through this discussion.
Title: Re: sf::Texture load from memory
Post by: Laurent on January 26, 2015, 10:54:22 pm
Quote
Is this where I'd use Laurent's earlier post about a converter?
Yes, using a byte array instead of a string should remove any compiler limitation.
Title: Re: sf::Texture load from memory
Post by: M74 on January 26, 2015, 11:55:46 pm
Here`s how i load Textures which are stored as Resources in Visual Studio 2013 on Windows:

1. Add the images to resource.rc file:
IDR_TEXTURE0    RCDATA  ".\\..\\Graphics\\Texture0.png"
IDR_TEXTURE1    RCDATA  ".\\..\\Graphics\\Texture1.png"
IDR_TEXTURE2    RCDATA  ".\\..\\Graphics\\Texture2.png"
IDR_TEXTURE3    RCDATA  ".\\..\\Graphics\\Texture3.png"
 

2. Add them to resource.h file:
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif

#define IDI_ICON1 101
#define IDI_TEXTURE0 102
#define IDI_TEXTURE1 103
#define IDI_TEXTURE2 104
#define IDI_TEXTURE3 105
#define IDI_FONT0 106
#define IDI_FONT1 107
 

3. Load them in code:
        const int TEXTURES_COUNT = 4;
        sf::Texture textures[TEXTURES_COUNT];
        sf::Sprite sprites[TEXTURES_COUNT];
        for (int i = 0; i < TEXTURES_COUNT; i++)
        {
                std::wstring numString = L"IDR_TEXTURE" + std::to_wstring(i);
                HRSRC rsrcData = FindResource(NULL, numString.c_str(), RT_RCDATA);
                LPVOID firstByte = LockResource(LoadResource(NULL, rsrcData));
                textures[i].loadFromMemory(firstByte, SizeofResource(NULL, rsrcData));
                sprites[i].setTexture(textures[i]);
                textures[i].setSmooth(true);
        }
 
Title: Re: sf::Texture load from memory
Post by: Laurent on January 27, 2015, 07:39:49 am
What's the advantage of using a platform-specific solution when there's a simple and portable way to embed resources in the executable?
Title: Re: sf::Texture load from memory
Post by: Jesper Juhl on January 27, 2015, 07:56:18 am
I was not advocating Windows/VS resource files. Just that using a seperate  data file (in whatever format) is usually more managable than adding resources to the executable.
Title: Re: sf::Texture load from memory
Post by: Laurent on January 27, 2015, 08:44:41 am
Win32 resource files are compiled into the executable ;)
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 27, 2015, 05:38:12 pm
Just been using the converter to convert the images into const char arrays and I'm getting lots of warnings of narrowing conversion from 'int to 'const char' is ill-formed in c++ 11. Is there any way around that? I've tried converting the array into int but then img.create doesn't work.
Title: Re: sf::Texture load from memory
Post by: eXpl0it3r on January 27, 2015, 05:56:02 pm
Where do these warnings get raised and what is "lots of"?
Have you taken a look at the header file generated?

Laurent's code add an extra "0xffffffff" at the end of the array (not sure why). I think you can safely remove that.
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 27, 2015, 05:59:37 pm
Just tried removing the 0xfffffff and it hasn't helped, yes I've looked at the header because I've taken the code out of it and put in in my own header alongside some other image code. Lots of warnings is 750. The compiler log says that the warnings are all on the line of the curly brace that closes the array.

EDIT: I've realised that I was using img.create rather than loadFromMemory and changing that has got rid of the errors. I'm not sure why that gets rid of errors in the header file containing the image data, not the one with the img.loadFromMemory but it seems to have done.

EDIT2: No, it turns out my compiler was just not warning me about things it had warned me about before. When I added another image with exactly the same syntax as before it came out with another 1000 warnings.
Title: Re: sf::Texture load from memory
Post by: Hapax on January 27, 2015, 06:38:33 pm
image.create() wants pixel data whereas image.loadFromMemory() requires the data to still be in a recognisable file format.
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 27, 2015, 06:48:46 pm
What about the -Wnarrowing warnings? Is that just something that I need to put up with an wait a few minutes for it to compile every time I add a new image, or is there something I can do about it?
Title: Re: sf::Texture load from memory
Post by: eXpl0it3r on January 27, 2015, 09:10:19 pm
We don't know where and why it happens, as such we can't give you any answer. I just know, if you do it correctly, it does work without warnings.
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 27, 2015, 09:14:42 pm
Sorry for not being specific. My code goes like this:
static const char image[]
{
    "0x83, 0x91, 0x23, ...
(rest of image here)
     ... , 0xffffff"
};
My compiler then comes up with approximately one thousand Wnarrowing warnings of converting from int to const char* all said to be on the line of the closing curly brace, in the first column.
Title: Re: sf::Texture load from memory
Post by: Hapax on January 27, 2015, 09:39:33 pm
You probably don't want the two double-quotes (").
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 27, 2015, 10:11:48 pm
Ah sorry, I need to remember never to quote my code if it isn't right in front of me but I didn't have access to it at the time and I'd been looking at it for so long I thought I could remember it. Those double-quotes are from the method I was using for storing small images (gimp exported c source code) and they were at the start and end of every line. I got rid of them when I started using const char arrays instead. Other than the quotes, the code I posted is what I've got at the moment apart from the fact that they aren't static (I've just checked). I've disabled all warnings on my compiler and now and it isn't coming up with the -Wnarrowing, but warnings are useful for other things so I would prefer to fix it rather than leave it with the compiler just ignoring it and other potential problems. Sorry for being such a persistent problem, I just want to get this to work.
Title: Re: sf::Texture load from memory
Post by: eXpl0it3r on January 27, 2015, 11:15:50 pm
Provide a complete and minimal example. Without it we can just make random guesses.
Title: Re: sf::Texture load from memory
Post by: Hapax on January 27, 2015, 11:26:06 pm
Does it work if you change char to unsigned char?

Also, googling that error suggests that making sure your compiler is up-to-date may solve it.


Apart from those, what eXpl0it3r said.
Title: Re: sf::Texture load from memory
Post by: Jesper Juhl on January 28, 2015, 12:05:31 am
I've disabled all warnings on my compiler and now and it isn't coming up with the -Wnarrowing
That certainly is not a fix for anything.
Compiler warnings are there to help you. Just disabling them changes nothing.
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 28, 2015, 02:23:04 pm
eXpl0it3r:
The image code (ButtonImages.hpp)
(click to show/hide)

The function that applies the image (Images.hpp)
(click to show/hide)

The use of the function:
(click to show/hide)

Hapax: I've tried converting it to unsigned char and that stops most of the Wnarrowing, though it does still display it once per array, but also comes up with large values being truncated (Woverflow)

Jester Juhl: I only turned warnings off because I actually couldn't compile it before because it took far too long to go through all the warnings
Title: Re: sf::Texture load from memory
Post by: Laurent on January 28, 2015, 02:35:06 pm
Anything greater than 0x80 doesn't fit into a signed char, so indeed the type of the array has to be unsigned char[] (or uint8_t[]) if the generator outputs such constants.

As already said, you must also remove the ending 0xffffffff, this should get rid of the truncation warning.
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 28, 2015, 02:53:46 pm
IT WORKS!!!!!  :)  :)  :)
I turned them all into unsigned chars, I got rid of the 0xfffffff and now it works!!!!
Title: Re: sf::Texture load from memory
Post by: eXpl0it3r on January 28, 2015, 03:23:42 pm
Right, I used sf::Uint8 which is why I never got the errors. Didn't think of that.
Also apparently this is only an "issue" since C++0x/C++11. :)
Title: Re: sf::Texture load from memory
Post by: shadowmouse on January 28, 2015, 03:41:00 pm
So it might be better in c++14? That would be nice, though as far as I know I can only use the version of MinGW that codeblocks was compiled with so I'll have to wait until it is remade.
Title: Re: sf::Texture load from memory
Post by: eXpl0it3r on January 28, 2015, 04:04:18 pm
No, that was meant as, it's only an "issue" start with C++0x/C++11 because of the initializationlists. ;)
Title: Re: sf::Texture load from memory
Post by: lelion on May 21, 2019, 01:34:00 am
static const unsigned char embeddedImageData[] =
{
    /* content generated by one of the hundreds
        of programs that do it, or by your own small utility
       (doesn't take more than 10 lines of C++ code) */

};

texture.loadFromMemory(embeddedImageData, sizeof(embeddedImageData));

Example of code that generate such an array:

#include <fstream>
#include <iomanip>

int main(int argc, char** argv)
{
    std::ifstream in("image.png", std::ios_base::binary);
    std::ofstream out("header.hpp");

    out << "static const unsigned char embeddedImageData[] =" << std::endl;
    out << "{" << std::endl;
    do
    {
        out << "    ";
        for (int i = 0; (i < 20) && in; ++i)
            out << "0x" << std::hex << std::setw(2) << std::setfill('0') << in.get() << std::dec << ", ";
        out << std::endl;
    }
    while (in);
    out << "};" << std::endl;

    return 0;
}
 

Note that it can be more concise if you don't care about the pretty formatting.
(delete last 0xffffffff)

Merci beaucoup Laurent pour ce bou de code,  ca marche a merveille.