IMG_LoadTexture_RW seems to load too much data

I created a custom font editor in Lazarus. Fonts are small binary files that contain some basic information (predefined header), a PNG image with an atlas, and a list of structures with character data, exactly in that order. Unfortunately, the game code can’t load the font data correctly, because the IMG_LoadTexture_RW function reads too much data, pushing the stream position to the very end, so the list of character data cannot be loaded.

I checked exactly how much data the font editor saves by displaying the position in the file in several moments — just before saving the atlas, right after saving the atlas and right after saving the character data. The positions look like this:

  • 24 — before saving atlas,
  • 219 — after saving atlas,
  • 253 — after saving characters data.

I did the same in the game code, displaying the position of the stream in the console (with the SDL_RWtell function) just before and after reading the atlas. The case is as follows:

  • 24 — before loading atlas,
  • 253 — after loading atlas.

The atlas is read correctly, the IMG_LoadTexture_RW function returns the correct texture pointer, but instead of stopping reading PNG at position 219, this function moves the position of the stream to byte 253, i.e. to the very end.

Does anyone have an idea why this is happening? Anyone ever had a similar problem?

I need to do something about it because currently I don’t have the ability to create binary files and save PNG images in them. I’ll check what happens if I reverse the order, i.e. save the PNG file at the very end. However, this still doesn’t change the fact that the IMG_LoadTexture_RW function works wrong in my case.

Here is the font file to test, if you want to try — 0.bin (253 Bytes)

I checked how it looks with a different order of data in the file. If I save the character data first and then the PNG (at the end of the file), this problem does not occur. In this case, the font editor writes PNG from byte 58 to byte 253, and the game code correctly reads PNG from byte position 58 until the end of the file, which is byte 253.

So if the PNG is somewhere in the middle of the stream, the IMG_LoadTexture_RW function reads too much data, so the next data cannot be read properly, because of the invalid stream position.

Here is the font file with the PNG stored at the very end of the file — 0.bin (253 Bytes)

I’m not at all surprised. Since it doesn’t know the type of image data it’s going to read, it makes sense to me that it should read the entire stream into memory and only then determine the type (and hence size) of the data. I suppose it could then re-seek the stream according to what it discovers, but I wouldn’t necessarily expect that.

Does it make any difference if you use IMG_LoadTextureTyped_RW() (probably not, but at least it does then know in advance the expected image type)?

From what I’ve seen in the sources, this function first checks what type of image is in the stream (by reading the stream and then restoring its previous position), and then calls the appropriate loader that loads this data (for a specific type of image). And if so, the PNG loader should be called, which should stop after reading the IEND chunk.

I will try the IMG_LoadTextureTyped_RW function and I will let you know. However, I still think that the IMG_LoadTexture_RW function works incorrectly, because it ignores the existence of the IEND chunk and reads on for some unknown reason.

I checked the IMG_LoadTextureTyped_RW and the situation is the same — it moves the pointer to the end of the file stream, the same as IMG_LoadTexture_RW.

Currently the only option to read PNG from the RW stream, as a workaround, is to manualy find the IEND chunk, copy all image data to the separate memory stream, use the IMG_LoadTexture(Typed)_RW function to load image and then, move the pointer in the original stream to the appropriate position.

I tried using STBIMG_Load_RW() but that seems to read exactly 112 bytes beyond the end of the PNG data (STBIMG_LoadRW() returns a surface, not a texture, but you can always use SDL_CreateTextureFromSurface()).

As a test I concatenated two identical PNG files, each 4,235 bytes long (so the total file length was 8,470 bytes). After calling STBIMG_Load_RW() the stream pointer was at 4,347, 112 bytes beyond the end of the first PNG.

I then repeated the test with a different PNG (length 5,376). This time the pointer ended up at 5,488, again 112 bytes past where I would have expected!

I have some vague recollection that recent versions of SDL2_image may also be using stb_image.h for PNG decoding, so maybe that’s where the problem really lies.

1 Like

Thanks for testing and confirming.

It seems that this pointer “over-shift” has a fixed size (always the same), so it may be a problem reading the IEND chunk. Well, I have no choice but to report it as a bug, which I just did.