Example 1 - Read & display a MNG
The following will describe what an application needs to do to read & display a MNG.
(TODO: intersperse with actual C snippets)
Read & display is the heart of this library. It is also the hardest part. Viewing a single image is pretty easy compared to viewing an animation. Especially if you consider al the nice features MNG provides. But now you don't have to worry about that. libmng does it all for you!
So let's have a look at what would be involved in this process.
First of all we need to define a couple of callback-functions. The ones we absolutely need are openstream, closestream, readdata, processheader, getcanvasline, refresh, gettickcount & settimer. You may also need memalloc & memfree. if you want to supply an application-provided background you'll also need getbkgdline. if you'd like to try recovering from one or more warnings you'll need errorproc. Seems like a lot but they can be quite small.
Note that on the mng_initialize call, you can supply a variable (called user_data) that can be accessed later from the callback-functions. This is especially handy if your app displays multiple images at the same time. In those cases, the callback will need to know for which image it is called, and the user_data variable can serve this purpose. The variable is exactly large enough to hold a memory-pointer, if that's what you need! It's accessible through the mng_get_userdata function.
- openstream & closestream
These are called just before the library starts reading data and after it has reached EOF. This allows you to do some housekeeping with respect to I/O.
This is used to get chunks of data; could be a complete PNG/MNG-chunk or just a couple of bytes; your app should not assume anything and just provide what is requested. Note however that it is also possible to do streaming I/O, where the input-data arrives at unknown intervals (eg. internet). This will be discussed in a separate example, when I have some time left.
This is called as soon as the first chunk is read, decoded and recognized. At this point you can access the image characteristics. Now would be a good time to initialize the drawing canvas where you want the image-data to be transfered to. Also make sure to tell the library what format you need, by calling mng_set_canvasstyle!
Note: Windows DIB's are in bgr order!
This is called whenever the library needs to update your canvas with one or more pixels. Everything is handled as rows of pixels, so provide a pointer to the right canvasline starting at position 0.
The library will call this if it feels there's a need to update the canvas onto your screen. If your canvas draws directly to screen, this can be a dummy function, but if you build the canvas in memory, this is when you need to paint it onto the screen. It happens during interframe delays (for animations) or with "largish" images (> 250 lines; this may change...) to give a progressive display.
This callback should return a number of milliseconds. It's purpose is to get an idea how many milliseconds have elapsed since the animation started. So you could simply return that, if you know how to. You can also use an OS-supplied counter if one exists. Eg. on Windows simply call GetTickCount and return that value.
This is called to indicate the library likes to take a little break and have a KitKat. Ok. Seriously. It's called to signify a delay is required by the animation, before the next frame gets rendered. Within this callback you should set a timer, using whatever means you have, to wait the given number of milliseconds. The callback should return to the library, which in turn will return all control to the app. When the timer reaches the number of msecs, the app must call mng_display_resume to properly continue the animation!
Your app should allocate a block of memory of the given size, AND, initialize it to all zeroes!
Your app should take the proper steps to free a previously allocated block of memory.
This is called to get access to the background-canvas. It works exactly like getcanvasline.
This is called when the library encounters a problem that may be circumvented after some (user-)interaction. The most common use is when the library reads a header which identifies an image of "immense" proportions. Well, larger than the maximum dimensions (set initially to 10000x10000 pixels; which can be changed by the app through mng_set_maxcanvassize).
Now with all that properly defined we can go about and tell the library what is where and what to do with it (this screams for a corny remark, but I'll restrain myself) :
Always, always, always the first call! It will return a handle which is used with all the other functions. If you didn't compile the library with MNG_INTERNAL_MEMMNGMT, you'll need to supply the memory-management callbacks with this call! You may also wish to supply the user_data variable to link your own data-structure with this particular handle, in case your app deals with multiple images at the same time. Each libmng handle can only service one (1) image at a time!
You've already defined them. Now you need to tell the library where to find them!
Tell the library to get at it. This is all you need to do to read & display a PNG, JNG or MNG image. Note that this call will return after the first time the timer gets set (only with MNG). At that point you definitely do not want to call mng_cleanup. The animation is still active remember!
As explained in the settimer callback, this needs to be called right after the timer has elapsed. The function will also return every time the timer gets set again.
This should be called to cleanup when you're done displaying, or when the library returns an errorcode (oh yeah, that can happen!). If you want to kill an animation before it's finished (note, some of them may never finish!), you'll need to call mng_display_freeze first. Don't call this function when the timer is set, or even when the file or stream reached EOF. Even then the animation may still be active!
That's the basics. The following aren't a necessity, but may be nice features to add:
Call this during a timer-break, if you want the lib to freeze the animation in mid-play. Perhaps when the application-user makes a request to do so. Just call mng_display_resume if you want to continue again from the exact point where you left off.
Call this after freezing an animation to return it to just before the first frame. Eg. when the apps background changes or simply because the user requested it.
Use these to advance or backtrack an animation to a specific frame, layer or timeslot. The animation must be frozen first!
In the event the library encounters a problem (out-of-memory situations, invalid chunk-data, etc.), it will return an errorcode. Call this function to get more details about how, where and when it occured.
If you wish to display a different image and discard the current one, you'll want to call this function. It saves you from calling mng_cleanup and then mng_initialize again.