
/*
**
**  $VER: classbase.c 1.2 (21.9.97)
**  gifanim.datatype 1.2
**
**  Library routines for a DataTypes class
**
**  Written 1997 by Roland 'Gizzy' Mainz
**  Original example source from David N. Junod
**
*/


/* main includes */
#include "classbase.h"


/****** gifanim.datatype/MAIN ************************************************
*
*    INTRODUCTION
*        Datatypes class for GIF animations. Based on "giftopnm" by David
*        Koblas and the CBM datatypes example source/ documents written by
*        David Junod.
*
*    REQUIREMENTS
*        * WARNING: This datatype was designed for animation.datatype V41.
*        * Usage with animation.datatype causes many problems as noted
*        * in the BUGS section below. See there for a workaround.
*
*        - You need at least Kick/WB 3.0.
*          | Many people wrote me that they cannot find an
*          | "animation.datatype" class.
*          | Only the 3.1 release contains it. (Subclasses of)
*          | animation.datatype can run under 3.0.
*
*        - "datatypes/animation.datatype", >= V39.
*          "animation.datatype 40.6 (28.09.93)" requires itself some
*          libraries/boopsi classes:
*        - "realtime.library", >= V39              - for timing
*        - "gadgets/tapedeck.gadget" (any version) - for the controls
*
*           If you want to attach samples, you need "sound.datatype" >= V39
*           and your prefereed subclass (8svx.datatype for IFF 8SVX samples
*           etc.).
*
*    USAGE
*        If the datatypes descriptor file was activated, any attempt to load
*        a GIF anim stream using GMultiView, MultiView, AmigaGuide or
*        SwitchWindow will load and play the animation. If the source was a
*        file, gifanim.datatype loads frames dynamically from disk.
*
*        If you want to save the current animation in gifanim.datatype's 
*        local format, use MultiView's "Project/Save As..." menu (or 
*        GMultiView's "Project/Save As Raw...").
*        gifanim.datatype saves the current animation, stating with the
*        current frame as GIF animation.
*
*        If you want to attach samples to the animation, you must edit the
*        prefs file (ENV:Classes/DataTypes/gifanim.prefs) and add the
*        following line:
*        VERBOSE SAMPLE="ram:have_a_nice_day.8svx"
*        Which loads and attaches the sample "ram:have_a_nice_day.8svx" to 
*        the animation. See gifanim.datatype.doc/preferences for a complete
*        description of the prefs file.
*
*    INSTALLATION
*        After unpacking this archive:
*        Because this version does not include an Installer script, you have
*        to do the installation manually through the shell:
*
*          - Unpack this archive and copy the "gifanim.datatype" to
*            SYS:Classes/DataTypes:
*
*        Copy CLONE FROM "gifanim.datatype" TO
*         "SYS:Classes/DataTypes/gifanim.datatype"
*
*          - Then copy the datatypes descriptor into the DEVS:DataTypes
*            directory.
*            If the descriptor already exists, you should not replace it,
*            otherwise you may loose "toolnodes" and other settings stored in
*            the existing descriptor.
*
*     Copy CLONE FROM "GIFANIM(%|.info)" TO DEVS:Datatypes/
*
*    SOURCE
*        Source is included as an example how to write an
*        animation.datatype subclass which deals with things chunky bitmaps.
*
*    LEGAL
*      - Compuserves banner:
*
*      "The Graphics Interchange Format(c) is the Copyright property of
*      CompuServe Incorporated. GIF(sm) is a Service Mark property of
*      CompuServe Incorporated."
*
*      - "giftopnm" legal info:
*      +-------------------------------------------------------------------+
*      | Copyright 1990, 1991, 1993, David Koblas.  (koblas@netcom.com)    |
*      |   Permission to use, copy, modify, and distribute this software   |
*      |   and its documentation for any purpose and without fee is hereby |
*      |   granted, provided that the above copyright notice appear in all |
*      |   copies and that both that copyright notice and this permission  |
*      |   notice appear in supporting documentation.  This software is    |
*      |   provided "as is" without express or implied warranty.           |
*      +-------------------------------------------------------------------+
*
*
*    AUTHOR
*        If you want to blame me, report any bugs, or wants a new version
*        send your letter to:
*                        Roland Mainz
*                        Hohenstaufenstraße 8
*                        52388 Nörvenich
*                        GERMANY
*
*        Phone: (+49)(0)2426/901568
*        Fax:   (+49)(0)2426/901569
*
*        EMAIL is also available (if you want to send me attachments
*        larger than 1MB (up to 5MB, more with my permission):
*
*        GISBURN@w-specht.rhein-ruhr.de
*
*        Up to November 1997 I'm reachable using this email address, too:
*        Reinhold.A.Mainz@KBV.DE
*
*        | Please put your name and address in your mails !
*        | German mailers should add their phone numbers.
*        | See BUGS section above when submitting bug reports.
*
*        Sorry, but I can only look once a week for mails.
*        If you don't hear something from me within three weeks, please
*        send your mail again (but watch about new releases) (problems with
*        this email port are caused by reconfigurations, hackers, network
*        problems etc.).
*
*        The  entire  "gifanim.datatype"  package  may  be  noncommercially
*        redistributed, provided  that  the package  is always  distributed
*        in it's complete  form (including it's documentation). A small
*        copy fee  for media costs is okay but any kind of commercial
*        distribution is strictly forbidden without my permission !
*        Comments and suggestions how to improve this program are
*        generally appreciated!
*
*        Thanks to David Junod, who wrote the animation.datatype and lots of
*        the datatypes example code, David Koblas for his "giftopnm"
*        and other people for their compression formats, Peter McGavin for
*        his C2P function, Matt Dillon for his DICE, Olaf 'Olsen' Barthel
*        for his help, ideas and some text clips from his documentations.
*
******************************************************************************
*
*/



/****** gifanim.datatype/--datasheed-- ***************************************
*
*   NAME
*       gifanim.datatype -- data type for GIF Animations
*
*   SUPERCLASS
*       animation.datatype
*
*   DESCRIPTION
*       The anim datatype, a sub-class of the animation.datatype, is used to
*       load and play GIF animations.
*       It supports GIF 87a and GIF 89a compressed animations.
*       Using the prefs-file, any sound can be attached to the animation.
*
*   METHODS
*       OM_NEW -- Create a new animation object from a description file. The
*           source may only be a file.
*
*       OM_DISPOSE -- Dispose instance and contents (frames, colormaps, sounds
*           etc.), then pass msg to superclass
*
*       OM_UPDATE -- Perform an ICM_CHECKLOOP check, and if succesfull, the
*           method will be executed like OM_SET downstairs.
*
*       OM_SET -- Pass msg to superclass and call GM_RENDER if retval from
*           superclass was != 0UL.
*
*       DTM_WRITE -- Save object's contents in local (GIF Animation) or
*           superclass (IFF ILBM) format.
*
*       ADTM_LOADFRAME -- Fill in struct adtFrame with requested information
*           from internal FrameNode list like bitmap, colormap and sample. If
*           the bitmap information is not loaded yet, it will be loaded from
*           disk.
*
*       ADTM_UNLOADFRAME -- Free resources obtained by ADTM_UNLOADFRAME.
*
*       All other methods are passed unchanged to superclass.
*
*   ATTRIBUTES
*       Following attributes are set by the object and are READ-ONLY for
*       applications:
*       ADTA_Width              -- set by GIF screen
*       ADTA_Height             -- set by GIF screen
*       ADTA_Depth              -- set by GIF screen
*       ADTA_Frames             -- number of frames in animation
*       ADTA_FramesPerSecond    -- fixed to 100 fps
*       ADTA_KeyFrame           -- Key frame of animation
*       DTA_ObjName             -- file name
*       DTA_ObjAnnotation       -- set by extension blocks (gif comment
*                                  extention)
*
*   BUGS
*       * The whole code looks like a big hack; sorry, but this datatype was
*         put together within 2 hours...
*         ...V1.2 only fixes some bugs and adds a few features.
*
*       - Does not support transparency yet.
*
*       - Very slow playback.
*         Reasons:
*         - Multiple memory allocations, Seek's and Read's in one
*           ADTM_LOADFRAME
*         - Unbufferd loading
*         - C2P conversion (fast, but not the fastest possible)
*         - crap design
*         - Slow gif decoder. Rewriting the beast in ASM maybe speed up
*           things, but I don't know much about mc68k assember.
*           Anybody out there who wants to do the job ?
*         ....
*
*         To get rid of this problem, the LOADALL switch is set for now.
*         On >= mc68040/25MHz, the LOADALL switch in unneccesary, use
*         NOLOADALL in this case...
*
*       - Some anims get a trashed background, maybe due a bug in the GIF89a
*         frame disposal support code.
*
*       - Remapping of the first frame does not work proerly (e.g. selection of
*         first colormap is broken).
*
*       - The width is padded to 16 pixels. This may cause an ugly border on
*         the left-hand side.
*
*       - This datatype was written for animation.datatype V41. Using this
*         datatype with animation.datatype V40.6 but may not work with all
*         animations and requires the FPS=5 statement in the prefs file.
*
*       - In large videos, the frames at the end will be played slower than
*         those at the beginning of the file. This is the result of the
*         sequential search internally used (only serious with more than 25000
*         frames (mc6030/50mhz)).
*         May or may not be fixed.
*
*   TODO
*       - Fixing the bugs above.
*
*       - Write the "--input_format--"-Autodoc section.
*
*       - Support for chunkypixel bitmaps like CyberGFX or P96 ones.
*         This would speed up things significatly because the C2P
*         conversion can be skipped in this case.
*
*   HISTORY
*       V1.1
*         Released to the Waldspecht-BBS for testing.
*
*       V1.2
*         - Found the longstanding bug that animation.datatype V40.7
*           didn't free some frames. Reason is that ADTM_LOADFRAME
*           may be used like "realloc". Now alf_UserData is checked;
*           any given frame will be freed (ADTM_UNLOADFRAME).
*           Fixed.
*
*         - Minor houskeeping changes; removed some unneccesary code.
*
*         - Now uses buffered reading (FRead instead of Read and SetVBuf).
*           Maybe this speeds up loading a little bit.
*
*         - Fixed the bug in NOLOADALL loading mode (dynamic loading of
*           frames from disk) that some "disposal" modes (previous image)
*           didn't work properly under some conditions.
*           Fixed.
*
*         - Fixed the bug that animations with a static background were
*           not correctly handled in LOADALL mode (I simply forgot
*           to write the code...).
*           Thanks to Francis Labrie (fb691875@er.uquam.ca) for reporting
*           the bug.
*           Fixed.
*
*         - Fixed the bug that ADTM_LOADFRAME returns evertimes
*           ERROR_NO_FREE_STORE on failure instead of returning the real
*           cause.
*           Also fixed the error handling code in ADTM_LOADFRAME.
*           Fixed.
*
*         - Implemented a delta mode for WPA8. If possible (e.g. if the
*           current frame uses the previous one as it's background), only
*           the changed areas are processed by the C2P code.
*
*         - Now supports the GIF comment extension. The character set is
*           converted automatically, chars > 128 are replaced by '_'
*           except the german 'ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü' and 'ß'.
*           Multiple comment chunks are merged together.
*           The comment will be stored in DTA_ObjAnnotation.
*
*         - Cut some unneccesary VERBOSE output.
*
*   NOTES
*       This datatype first scans the whole animation to get index
*       information (if the LOADALL switch was set in the prefs-file,
*       the entire animation will be loaded), colormaps will be loaded
*       immediately.
*
*   SEE ALSO
*       animation.datatype,
*       anim.datatype,
*       mpegsystem.datatype, mpegvideo.datatype,
*       picmovie.datatype,
*       cdxl.datatype, avi.datatype, quicktime.datatype,
*       moviesetter.datatype,
*       film.datatype,
*       directory.datatype,
*       markabletextdtclass
*
*******************************************************************************
*
*/


/****** gifanim.datatype/--input_format-- ************************************
*
*    NAME
*        GIF ANIM -- GIF Animation format
*
*    DESCRIPTION
*        <Not written yet, sorry>
*
*    SEE ALSO
*
*******************************************************************************
*
*/



/*****************************************************************************/

DISPATCHERFLAGS
struct IClass *ObtainGIFAnimEngine( REGA6 struct ClassBase *cb )
{
    return( (cb -> cb_Lib . cl_Class) );
}

/*****************************************************************************/

DISPATCHERFLAGS
struct Library *LibInit( REGD0 struct ClassBase *cb, REGA0 BPTR seglist, REGA6 struct ExecBase *sysbase )
{
    cb -> cb_SegList = seglist;
    cb -> cb_SysBase = sysbase;

    InitSemaphore( (&(cb -> cb_Lock)) );

    if( (cb -> cb_SysBase -> LibNode . lib_Version) >= 39UL )
    {
      /* Obtain ROM libs */
      if( cb -> cb_UtilityBase = OpenLibrary( "utility.library", 39UL ) )
      {
        if( cb -> cb_DOSBase = OpenLibrary( "dos.library", 39UL ) )
        {
          if( cb -> cb_GfxBase = OpenLibrary( "graphics.library",  39UL ) )
          {
            if( cb -> cb_IntuitionBase = OpenLibrary( "intuition.library", 39UL ) )
            {
              return( (&(cb -> cb_Lib . cl_Lib)) );

#ifdef COMMENTED_OUT
              CloseLibrary( (cb -> cb_IntuitionBase) );
#endif /* COMMENTED_OUT */
            }

            CloseLibrary( (cb -> cb_GfxBase) );
          }

          CloseLibrary( (cb -> cb_DOSBase) );
        }

        CloseLibrary( (cb -> cb_UtilityBase) );
      }
    }

    return( NULL );
}

/*****************************************************************************/

DISPATCHERFLAGS
LONG LibOpen( REGA6 struct ClassBase *cb )
{
    LONG retval = (LONG)cb;
    BOOL success = TRUE;

    ObtainSemaphore( (&(cb -> cb_Lock)) );

    /* Use an internal use counter */
    cb -> cb_Lib . cl_Lib . lib_OpenCnt++;
    cb -> cb_Lib . cl_Lib . lib_Flags &= ~LIBF_DELEXP;

    if( (cb -> cb_Lib . cl_Lib . lib_OpenCnt) == 1U )
    {
      if( (cb -> cb_Lib . cl_Class) == NULL )
      {
        success = FALSE;

        if( cb -> cb_DataTypesBase = OpenLibrary( "datatypes.library", 39UL ) )
        {
          /* Should require animation.datatype V41 */
          if( cb -> cb_SuperClassBase = OpenLibrary( "datatypes/animation.datatype", 39UL ) )
          {
            if( cb -> cb_Lib . cl_Class = initClass( cb ) )
            {
              success = TRUE;
            }
          }
        }
      }
    }

    if( !success )
    {
      CloseLibrary( (cb -> cb_SuperClassBase) );
      CloseLibrary( (cb -> cb_DataTypesBase) );

      cb -> cb_DataTypesBase = cb -> cb_SuperClassBase = NULL;

      (cb -> cb_Lib . cl_Lib . lib_OpenCnt)--;

      retval = 0L;
    }

    ReleaseSemaphore( (&(cb -> cb_Lock)) );

    return( retval );
}

/*****************************************************************************/

DISPATCHERFLAGS
LONG LibClose( REGA6 struct ClassBase *cb )
{
    LONG retval = 0L;

    ObtainSemaphore( (&(cb -> cb_Lock)) );

    if( cb -> cb_Lib . cl_Lib . lib_OpenCnt )
    {
      (cb -> cb_Lib . cl_Lib . lib_OpenCnt)--;
    }

    if( ((cb -> cb_Lib . cl_Lib . lib_OpenCnt) == 0U) && (cb -> cb_Lib . cl_Class) )
    {
      if( FreeClass( (cb -> cb_Lib . cl_Class) ) )
      {
        cb -> cb_Lib . cl_Class = NULL;

        CloseLibrary( (cb -> cb_SuperClassBase) );
        CloseLibrary( (cb -> cb_DataTypesBase) );
      }
      else
      {
        cb -> cb_Lib . cl_Lib . lib_Flags |= LIBF_DELEXP;
      }
    }

    ReleaseSemaphore( (&(cb -> cb_Lock)) );

    if( (cb -> cb_Lib . cl_Lib . lib_Flags) & LIBF_DELEXP )
    {
      retval = LibExpunge( cb );
    }

    return( retval );
}

/*****************************************************************************/

DISPATCHERFLAGS
LONG LibExpunge( REGA6 struct ClassBase *cb )
{
    BPTR seg;

    if( cb -> cb_Lib . cl_Lib . lib_OpenCnt )
    {
      cb -> cb_Lib . cl_Lib . lib_Flags |= LIBF_DELEXP;

      seg = NULL;
    }
    else
    {
      Remove( (&(cb -> cb_Lib . cl_Lib . lib_Node)) );

      seg = cb -> cb_SegList;

      CloseLibrary( (cb -> cb_IntuitionBase) );
      CloseLibrary( (cb -> cb_GfxBase) );
      CloseLibrary( (cb -> cb_DOSBase) );
      CloseLibrary( (cb -> cb_UtilityBase) );

      FreeMem( (APTR)((ULONG)(cb) - (ULONG)(cb -> cb_Lib . cl_Lib . lib_NegSize)), (ULONG)((cb -> cb_Lib . cl_Lib . lib_NegSize) + (cb -> cb_Lib . cl_Lib . lib_PosSize)) );
    }

    return( (LONG)seg );
}


