main

CEV_gif library

Presentation :

This static lib was developed to read gif files with SDL1 / SDL2. Only SDL is needed, this lib can be used without SDL_Image as the file decompression has internally been implemented.
Unlikely SDL_image, it will not only load one pic of a set, but allows to run it with full animation.
But, yeah, you can still load an non-animated gif though it would not be very memory friendly considering the size of the structure used.
But, still, you could just load the file, copy the resulted SDL_Surface into another and then free the animation structure.
It can manage the most common gif files, from the overlay to the redraw method, from the basic pictures to the interlaced ones.
Also, alpha channel is taken into consideration, thus, I strongly recommend to use 32 bits surfaces with alpha chan enabled.
Really, I didn't try to set a 8 bits video surface with this lib.
Well, why would I, we are living the 3rd millennium aren't we ?
Say, so far every file I've tried has been processed successfully, not even a miss !
This lib was tested with a lot of care, though some bug may still appear.
Nice to tell me so I can correct any unwanted behavior.

Important Note :

Documentation here refers to SDL 1.x, if you use SDL 2 version, an SDL_Texture will replace the SDL_Surface...
I'll here make a difference between the logical order and the reading order.
The logical order is the one internally managed by the structure as the gif is supposed to be naturally played, as if opened with a web browser for instance. Say, there are 5 frames in the animation, we will have something like frame[5]. frame[0] will then be the first logical frame and frame[4] the last logical frame.
The reading order is the one depending on which direction you are playing the animation. This lib allows to "play" with how to show animation, thus you can run it backward if you wish. In this case, animation will be played from frame[4] to frame[0] and so on...
The first reading frame will then be frame[4] while the first logical frame remains frame[0]. Remember this nuance as I'll make use of it in the functions explanation, so you don't have any surprise.

SDL 1.x zip file Version.

SDL 2.x zip file Version.

SDL 2.x Example project (Code::Blocks).

Library references :

Here is all you wanna know about how to use this lib.
Basically, most common use will take to manage one structure pointer and two or three functions, though some other functions were added to add some fitness, say if you wanna try and do weird things.

How to install this lib ?

Download lib and sources up there. Folder (zip) contains sources. Include the .c files into your project and include SDL_gif.h.
For the moment I got a little problem with the static lib.a as it doesn't seem to link correctly to SDL.
I'm trying to, but, yet, it is not like this for now. If you ever have an idea, I'd like to hear it ^^.
Anyway, sources will let you have a try.

The control Structure.

Let's go straight to the point : CEV_GifAnim is the basic animation instance you wanna get from a gif file.
Just as you use to have an SDL_surface/texture as a picture, an CEV_GifAnim is an animation, easy so far...
You will then manipulate a pointer to such a structure, thus you will declare a pointer to an CEV_GifAnim.


/* creating a pointer */

CEV_GifAnim *animation = NULL;
				
Not too bad, you get it, but you're still pointing to nothing, let's go and open a gif file, shall we ?

CEV_gifAnimLoad() / CEV_gifAnimLoadRW()

Complete SDL1 prototype are :

  • CEV_GifAnim* CEV_gifAnimLoad(const *char fileName)
  • CEV_GifAnim* CEV_gifAnimLoadRW(SDL_RWops *rwops)

  • Complete SDL2 prototype are :

  • CEV_GifAnim* CEV_gifAnimLoad(const *char fileName, SDL_Renderer *renderer)
  • CEV_GifAnim* CEV_gifAnimLoadRW(SDL_RWops *rwops, SDL_Renderer *renderer)

  • This function will load a gif named "fileName" and allocate sufficient memspace to store an CEV_GifAnim structure.

    If using SDL2 Version, you need to add the renderer pointer you want the texture to be related to.

    See SDL_RWops to use the RW version of this function.
    Note that if you intend to load a gif file within a thread (for SDL2), you will have to lock access to the renderer.
    Ok, let's do it now !

    Basic Code :
    
    /* opening gif file */
    
    CEV_GifAnim *animation = CEV_gifAnimLoad("file.gif", myRenderer);
    
    if (animation != NULL)
    /*File opened, read correctly and animation is ready !!*/
    else
    /*Something went wrong...*/
    				
    return values :
  • on success, a pointer to an CEV_GifAnim structure is returned.
  • a NULL pointer is returned if function has failed, stderr is completed with error information.

  • The main surface/texture (as returned by CEV_gifSurface()) shows the first logical frame if displayed as it is.
    Animation mode is set to GIF_STOP when loaded, you must enable animation by setting the animation mode through CEV_gifLoopMode().

    CEV_gifSurface() / CEV_gifTexture()

    Complete SDL prototype is : SDL_Surface* CEV_gifSurface(CEV_GifAnim* anim)
    Complete SDL2 prototype is : SDL_Texture* CEV_gifTexture(CEV_GifAnim* anim)

    This function returns the main surface/texture to be used for your application.
    anim beeing the pointer to the animation of which you wanna extract the main surface/texture.

    This SDL_Surface*/Texture* will remain constant, whatever happens. Animation is not switching from one frame to another but is actually updating this surface/texture with frames.

    Basic Code :

    
    /*basic SDL1 animation code*/
    
    SDL_Surface* actSurface = CEV_gifSurface(animation);
    
    SDL_FillRect(screen, NULL, 0x0);			
    SDL_BlitSurface(actSurface, NULL, screen, &blitPos);
    SDL_Flip(screen);		
    				
    Thus, though actSurface status is modified by CEV_gifAnimAuto(), you can endlessly blit actSurface.
    Mostly, you can store it anywhere once for all without having to worry about it to change, it won't... unless it's freed...
    Note that if animation is not launched, actSurface will then show the first logical frame of the gif file.

    Having the animation to be played.

    CEV_gifAnimAuto() :

    Complete prototype is : char CEV_gifAnimAuto(CEV_GifAnim* anim)

    This function updates the main surface/texture according to the reading mode parameters and individual frames delay.

    Return value :

  • 1 when the frame has changed.
  • 0 otherwise.

  • Return value can be used if you don't want to blit again and again but only when the animation has been refreshed.
    Basic Code :
    				
    SDL_Surface* actSurface = CEV_gifSurface(animation);
    CEV_gifLoopMode(animation, GIF_REPEAT_FOR);
    
    while(!anyKeyHit())
    {
    	SDL_FillRect(screen, NULL, 0x0);
    	CEV_gifAnimAuto(animation);
    	SDL_BlitSurface(actSurface, NULL, screen, &blitPos);
    	SDL_Flip(screen);
    }
    
    /**or refreshing screen only when really needed : **/
    
    while(!anyKeyHit())
    {	
    	if(CEV_gifAnimAuto(animation))
    	{
    		SDL_FillRect(screen, NULL, 0x0);
    		SDL_BlitSurface(actSurface, NULL, screen, &blitPos);
    		SDL_Flip(screen);
    	}
    }
    			
    If you want an accurate animation, I recommend that you update it as often as possible, that is in your main game loop.
    If a call to CEV_gifAnimAuto() was to be done, let's say, sometimes, the effect would just be the same as CEV_gifFrameNext() but that the actual frame delay would be taken into consideration. Not sure you move to next frame then...
    If you stop calling CEV_gifAnimAuto(), no animation occurs, and is just freezed as it until further update.
    The main surface / texture will keep displaying what it was displaying before you stopped calling CEV_gifAnimAuto().
    note that if you restart the animation by calling the function again, the actual frame delay may be overpassed, causing the animation to instantly skip to the next reading frame.

    CEV_gifFrameNext() :

    Complete prototype is : void CEV_gifFrameNext(CEV_GifAnim *anim)

    This will go to the next reading frame to be displayed accordingly to the mode set with CEV_gifLoopMode().

    If the loop mode is set to GIF_ONCE_XXX, the animation will then stop at the last reading frame. Even if you would call this function 200 hundred times nothing would happen, it still need to be reset.
    This will allow you to have your own management on when to change frame, say on a particular event.
    Call to this function inhibits the frame delay, if every frame delay was set on 500ms, you can play the animation faster by calling CEV_gifFrameNext every 100 ms.
    CEV_gifAnimAuto() should do just as well most of the time.

    Loop control functions

    CEV_gifLoopMode() :

    Complete prototype is : void CEV_gifLoopMode(CEV_GifAnim* anim, unsigned int mode)

    This function is used to take control over the loop behavior.
    "anim" is the CEV_GifAnim which you want to modify.
    "mode" can get any of these values :
    GIF_REPEAT_FOR : gif loops forward, just as it would if opened with any software : repeat forward gif You might just consider it as "normal" behavior.
    GIF_ONCE_FOR : gif will run from first frame to latest then stop, see CEV_gifLoopReset() to restart animation : once forward gif
    GIF_FORTH_BACK : gif will loop forth and back, showing all frames from first to last, then backward to first and so on... : forth and back gif
    GIF_REPEAT_REV : gif loops backward, showing all frames from last logical frame to first, then starts from last again and so on.. : repeat reverse gif
    GIF_ONCE_REV : same as GIF_ONCE_FOR, but reverse, from last logical frame to first : once forward gif

    GIF_STOP : animation is stopped and is reset to display the first logical frame.

    If using one of the GIF_ONCE_XXX mode, you might wanna know whenever the loop has finished, to do so, you might want to have a look at CEV_gifLoopStatus() and CEV_gifLoopReset().

    ATTENTION :
    The play-it-backward can sometimes lead to a weird result as some frames may be set to (partly) overlay the previous one instead of an "erase and replace" behavior.
    This is called the disposal method.
    Playing backward will keep the disposal method unchanged : a frame supposed to overlay the logical previous frame will then overlay the following logical frame instead. Which is not what was supposed to happen when the gif was initially created.
    Obviously, an animation with the overlay method is not meant to be played backward.
    See here an overlay method gif played forward on left, and the same one played backward on right : repeat forward gif repeat forward gif
    But, hey! try it for fun.

    CEV_gifLoopReset() :

    Complete prototype is : void CEV_gifLoopReset(CEV_GifAnim* anim)

    This function simply resets the animation loop that will then start playing again from the first reading frame accordingly to the reading mode.

    Say you played it one shot (GIF_ONCE_XXX), this will allow the loop to restart and be played once again from the first reading frame.

    Resetting an animation set to GIF_FORTH_BACK mode will go back to the first logical frame and set it to start forward again.

    About knowing when to reset an animation stucked using GIF_ONCE_XXX mode, you might want to have a look at CEV_gifLoopStatus().

    CEV_gifReverse() :

    Complete prototype is : void CEV_gifReverse(CEV_GifAnim *anim)

    This will simply revert the actual direction of frame reading.
    Global mode is kept as, only direction is changed : say you set the animation mode to GIF_REPEAT_FOR, a call to CEV_gifReverse() will set it to GIF_REPEAT_REV. In this case, direction is changed from the frame it is displaying at function call.

    If a one shot reading has come to an end (GIF_ONCE_XXX), direction will be changed, but animation will not restart until a call to CEV_gifLoopReset() is done.

    If mode was set to GIF_FORTH_BACK, animation will then instantly change its direction from where it is at function call.

    CEV_gifSpeedSet() :

    Complete prototype is : void CEV_gifSpeedSet(CEV_GifAnim *anim, int frameNum, int delayms)

    Display delay is basically set to the value set by the original gif file, frame by frame.
    Though, you can adjust the delay frame by frame, or globally.

    if a GIF_ALL value is given as frameNum, the display time of all frames will be set at delayms.

    Otherwise, only the frame frameNum will be modified. Nothing is changed if frame index given is higher than the number of frame available minus 1.
    Say 5 frames for this animation, frameNum then can have a value of 0 to 4, 0 being the first logical frame and 4 the last one.

    delayms is in milliseconds. Internally this value is turned into a 16 bits unsigned thus, maximum value available is 65 535.

    CEV_gifMethodSet() :

    Complete prototype is : void CEV_gifMethodSet(CEV_GifAnim* anim, int num, uint8_t method)

    This function allows to set "anim"'s "num"th frame disposal method to "method". If "num" is out of range, parameters stay unchanged.
    If GIF_ALL is passed as num, method will be applied to all frames.
    method can take value METHOD_OVERLAY or METHOD_REDRAW.
    Any value different from these ones will be considered like METHOD_REDRAW which is the default behavior.

    Getting informations about...

    CEV_gifLoopStatus() :

    Complete prototype is : char CEV_gifLoopStatus(CEV_GifAnim* anim)

    This function let you know whether the animation needs a reset or not.
    Mostly, when playing an animation in GIF_ONCE_XXX mode, this will allow to know if it has finished playing or not.
    Return value is 1 while playing, whatever the mode may be (even in GIF_LOOP_STOP mode).
    Return value is 0 once the GIF_ONCE_XXX animation has been executed and is stuck at last reading frame.

    CEV_gifMethod() :

    Complete prototype is : char CEV_gifMethod(CEV_GifAnim* anim, uint8_t num)

    Allows to query the disposal method of a particular frame. Returns disposal method of the "num"th frame of "anim". If "num" is out of range, disposal method of first logical frame is returned.
    This value is supposed to be either METHOD_OVERWRITE or METHOD_REDRAW.
    METHOD_REDRAW is applied if the disposal method would be different from one of these values.

    CEV_gifComment() :

    Complete prototype is : char *CEV_gifComment(CEV_GifAnim *anim)

    returns the gif comment embedded in the gif file if any, otherwise a NULL pointer is returned.

    CEV_gifVersion() :

    Complete prototype is : char *CEV_gifVersion(CEV_GifAnim *anim)

    returns the gif version embedded in the gif file if any, otherwise a pointer to a nul char ('/0') is returned.

    CEV_gifsignature() :

    Complete prototype is : char *CEV_gifSignature(CEV_GifAnim *anim)

    returns the signature embedded in the gif file if any, otherwise a pointer to a nul char ('/0') is returned.

    CEV_gifFrameNum() :

    Complete prototype is : int CEV_gifFrameNum(CEV_GifAnim*anim)

    returns the number of frames available in anim.

    Cleaning it all...

    CEV_gifAnimFree() :

    Complete prototype is : void CEV_gifAnimFree(CEV_GifAnim* anim)

    Well, now you're done and you don't need this animation no more, just free it to clean everything up correctly... if you care.