I've noticed that on the OS X port the function al_load_bitmap_f() is not completely implemented. It currently reads in the entire file, making it not work with my custom formats. Often I will have a PNG or set of PNGs embedded in a larger file and with the current native image loader all data past the first embedded image is skipped, causing my loaders to break.
Are there any plans to fix this? If not it would be good to mention somewhere that this functionality is broken in the native image loader so someone what wants to use it on OS X will know to turn WANT_NATIVE_IMAGE_LOADER off.
I'm not sure I understand what the problem is and so I'm not sure what to suggest (but if you're putting multiple resources in a single file, the proper way to deal with that is to write an I/O interface for it, but the easiest thing to do is go through PhysFS).
However, if there's a problem and you have a way to fix it, we gladly accept patches.
I think we were supposed to add subfile or "chunk" support at some point to deal with that sort of use case. I thought Matthew or Peter had worked on it at some point.
Ah, ok. Probably not for the OS X native loader then. Not sure how to implement this there, but yes, this should be done then.
Here's what one of my custom format loaders looks like:
This works perfectly with the Allegro image loaders but fails on the OS X native loader (haven't tried the Windows native loader). I already looked at the code for the native loader and I can't see any obvious way to fix it. It would require knowing the size of the embedded image file ahead of time and there's no way of knowing that without adding some extra code to read to the end of the image. I didn't see anything in the CGImage API that would be useful for this.
I already looked at the code for the native loader and I can't see any obvious way to fix it. It would require knowing the size of the embedded image file ahead of time and there's no way of knowing that without adding some extra code to read to the end of the image. I didn't see anything in the CGImage API that would be useful for this.
That's what I was afraid of.
Is it possible to figure out after the fact and seek to that location in the file?
You will need to record the size of the PNG file within the larger file. Then you may read the number of bytes into a buffer, wrap it with [al_open_memfile] and load the image from the memfile.
The planned chunk functionality would be slightly more efficient because you wouldn't need to read the data into a temporary buffer. No one's working on it right now.
This works perfectly with the Allegro image loaders
It is not guaranteed to work, so there's nothing to fix on OS X.
I wrote slice code that should be sufficient for what you are doing. If I can find it, I'll post it later. It looks something like:
ALLEGRO_FILE *slice = al_open_slice(fp, size, flags); ap->bitmap[i] = al_load_bitmap_f(slice, ".png"); al_fclose(slice);
The size is still sometimes necessary because some loaders might do a SEEK_END, and we cannot control what the OS loaders do.
That's what I was looking into but I can't see any way to know how much data the CGImage function actually used to load the image file.
There might be a way to get the size after the fact and seek to where we need to be. I don't know if it would work but maybe this would help:
NSImage *image = NSImageFromAllegroBitmap(bmp); NSArray *reps = [image representations]; NSData *nsdata = [NSBitmapImageRep representationOfImageRepsInArray: reps usingType: type properties: nil]; size_t size = (size_t)[nsdata length];
This is how image saving works so maybe we could do something like this:
This might work depending on how NSImage handles representations. If it spits back the exact data it read when you retrieve the representation it should work.
You will need to record the size of the PNG file within the larger file.
Edit: I can't know the size of the PNG before recording it. I would have to write a temporary file, get the size, then rewrite the data into my own file.
It is not guaranteed to work, so there's nothing to fix on OS X.
Since the function exists and doesn't work then it needs to be fixed. There is no note of "not gauranteed to work" in the manual. If the al_load_*_f() functions are meant to be used with memfiles then it should be noted somewhere.
al_load_*_f are allowed to read as much data as they like (this is a documentation bug). Some file formats simply don't have a well defined end. TGA is one. I don't remember if PNG is one.
I can't know the size of the PNG before recording it. I would have to write a temporary file, get the size, then rewrite the data into my own file.
That's what the packfiles in A4 does. Other possibilities: write to a memory buffer first. Or reserve a few bytes, write the PNG, then seek back to the old location and fill in the length then.
Since the function exists and doesn't work then it needs to be fixed. There is no note of "not gauranteed to work" in the manual. If the al_load_*_f() functions are meant to be used with memfiles then it should be noted somewhere.
They aren't meant to work only with memfiles, but they do require a full random access I/O interface that represents a single file.
The native loaders (that we didn't write and have no control over) might do something like seek(SEEK_SET, 0) or seek(SEEK_END, 16). If you supply a pointer to a something that represents more than a single file, there's no way for Allegro to guarantee that it works.
I don't think the proper solution is to try to hack Allegro to work around those limitations in every single loader and saver (images, sounds, fonts, etc). Instead, file slices could be used over top any underlying interface.
I proposed (and partially implemented) something like:
ALLEGRO_FILE *slice = al_fopen_slice(fp, 0, "rw+"); al_save_bitmap_f(slice, ".png", bmp); al_fclose(slice);
In this case, the slice is opened with an initial length of 0 for read (r) and write (w) and is expandable (+).
A slice is always opened up at the current position of the file. (Opening two slices on the same file at the same time is undefined.) When closing the slice, the original file is moved to the end of the slice.
I was also considering a "memory buffer" (m) option that would automatically store everything in memory so that it could be used for write only streams. For example:
ALLEGRO_FILE *ftp_file = open_ftp_file("ftp://allegro.cc/foo.png"); ALLEGRO_FILE *slice = al_fopen_slice(ftp_file, 0, "mrw+"); al_save_bitmap_f(slice, ".png", bmp); al_fclose(slice); // memory buffer is flushed
The native loaders (that we didn't write and have no control over) might do something like seek(SEEK_SET, 0) or seek(SEEK_END, 16). If you supply a pointer to a something that represents more than a single file, there's no way for Allegro to guarantee that it works.
The docs should mention that the stream passed to the al_load_*_f() functions should represent a single file or else the behavior is undefined. Maybe even suggest using a memfile (or slices if those are implemented) so people can understand how to use them. I know I am not the only one who assumes you can just pass an open ALLEGRO_FILE stream. I can see the writer of the OS X loader was assuming this, too, based on the comments in the code.