-
I have some file where several texture images are packed. I wanted to do so
result = texture.loadFromStream(input);
But it fails, log message is "Failed to load image from stream. Reason : Image not of any known type, or corrupt".
So i changed to
void* data = malloc(size);
input.read(data, size);
texture.loadFromMemory(data, size);
free(data);
and it works fine, but there reduntant memory allocation operations. Any thoughts how to do it better?
-
I think you have to give us some more info you that magic "input" object. Does it support streaming? Does it stream things correctly? etc
-
Well, yes it's sf::InputStream based object. It support memory, file and zip archive. And this stream is well tested many times. I do not think it's fault of "input" stream. =)
-
What you could test is, loading a different image, writing the stream to a log and check whether everything is in order or not, take a look at SFML's implementation and file history to see if nothing has been missed.
Can you tell us what you're streaming? And at best make a minimal example, otherwise we can essentially just keep guessing... ;)
-
Minimal example in topic... =) Else it will be FULL example. Several images are packed in file one by one. These images can be RAW, PNG or JPEG. To read compressed data used loadFromStream - does not work, and loadFromMemory - perfectly works, where input stream is the same for each method. So i want to know, why loadFromStream can't load from stream. =)
ps Do you understand me? =(
-
So you have a perfectly working solution and only because you guess a single allocation would slow down your program you want to add a 10 times more complicated solution?
Please, consider profiling, if it really bothers you.
Then dont forget to also try out alternatives like mmap/CreateFileMapping.
-
Minimal example in topic... =) Else it will be FULL example.
It's always meant as minimal and complete. The thing is a few lines of code can sometimes be enough, but most of the time it's not. Since InputStream should work and it doesn't for you and you can't figure out why, we'd have to reproduce the issue and without the code we can't test it, thus leaving your issue unresolved. ;)
Several images are packed in file one by one. These images can be RAW, PNG or JPEG. To read compressed data used loadFromStream - does not work, and loadFromMemory - perfectly works, where input stream is the same for each method. So i want to know, why loadFromStream can't load from stream. =)
Well what image are you loading when it fails? Does it fail with all? Did you already output the data via stream and check for corruption? Just because it works with read doesn't automatically mean that streaming will work, both use different approaches, so you should look at the stream on it's own to see what's going wrong there.
-
I think loadFromStream tries to load image from BEGIN of the input stream. If it correct behavior, so no any questions from me. But i think that loadFromStream have to read image from CURRENT position and read size of image header. :-\
-
Yes, loadFromStream reads from the begining of the stream, not from the current position. Your stream should really provide an interface to the file that you want to load, and nothing else. A stream instance is not meant to be used for loading several resources.
So you would not do this
MyStream stream("all_my_textures.pak");
texture1.loadFromStream(stream);
texture2.loadFromStream(stream);
...
But rather
MyStream stream1("all_my_textures.pak", 1); // second argument refers to the texture, it can be an index of a relative path
texture1.loadFromStream(stream);
MyStream stream2("all_my_textures.pak", 2);
texture2.loadFromStream(stream);
Or, if you don't want to open the package everytime
MyPackage pack("all_my_textures.pak");
MyStream stream1(pack, 1);
texture1.loadFromStream(stream1);
MyStream stream2(pack, 2);
texture2.loadFromStream(stream2);
-
That's what I wanted to hear. =) Thanks to all!
-
So, result of discussion is something like that:
(i removed some code for simplicity)
class SubStream: public sf::InputStream
{
public:
explicit SubStream(sf::InputStream* stream, size_t begin, size_t size);
virtual ~SubStream();
virtual sf::Int64 read(void* data, sf::Int64 size) override;
virtual sf::Int64 seek(sf::Int64 position) override;
virtual sf::Int64 tell() override;
virtual sf::Int64 getSize() override;
protected:
sf::InputStream* mStream = nullptr;
size_t mBegin = 0;
size_t mSize = 0;
size_t mOffset = 0;
};
SubStream::SubStream(sf::InputStream* stream, size_t begin, size_t size):
mStream(stream),
mBegin(begin),
mSize(size),
mOffset(0)
{
mIsOk = mStream != nullptr && mSize > 0;
}
SubStream::~SubStream()
{
}
/* virtual */ sf::Int64 SubStream::read(void* data, sf::Int64 size)
{
if (mIsOk)
{
sf::Int64 readed = mStream->read(data, size);
if (readed != -1)
{
mOffset += readed;
return readed;
}
}
return -1;
}
/* virtual */ sf::Int64 SubStream::seek(sf::Int64 position)
{
if ( (size_t) position < mSize)
{
mOffset = (size_t) position;
mStream->seek(mBegin + mOffset);
return (sf::Int64) mOffset;
}
return -1;
}
/* virtual */ sf::Int64 SubStream::tell()
{
return (sf::Int64) mOffset;
}
/* virtual */ sf::Int64 SubStream::getSize()
{
return (sf::Int64) mSize;
}
It works very well, and a little bit faster than use of redundant malloc/free .