home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of Mecomp Multimedia 1
/
Mecomp-CD.iso
/
amiga
/
datatypes
/
film_datatype
/
dispatch.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-04-18
|
84KB
|
2,509 lines
/*
**
** $VER: dispatch.c 1.5 (19.4.97)
** film.datatype 1.5
**
** Dispatch routine for a DataTypes class
**
** Written 1996/97 by Roland 'Gizzy' Mainz
** Original example source from David N. Junod
**
*/
/* main includes */
#include "classbase.h"
/* ansi includes */
#include <limits.h>
/* film.datatype class instance data */
struct FilmInstData
{
/* Misc */
struct SignalSemaphore fid_SigSem; /* Instance data lock */
UWORD fid_Pad0;
APTR fid_Pool; /* Memory pool (for frame(-nodes) etc.) */
struct BitMapHeader *fid_BMH; /* Shortcut to PDTA_BitMapHeader */
struct BitMap *fid_KeyBitMap; /* Key BitMap */
struct MinList fid_FrameList; /* List of frames */
STRPTR fid_ProjectName; /* Shortcut to DTA_Name */
BPTR fid_VerboseOutput; /* Verbose output */
/* Prefs */
ULONG fid_ModeID;
BOOL fid_LoadAll; /* Load all frames of the animation */
BOOL fid_NoRemap; /* Set ADTA_Remap to FALSE if possible */
UWORD fid_FPS; /* fps of stream (maybe modified by prefs) */
UWORD fid_Volume; /* Sample volume */
ULONG fid_Period; /* Sample period */
/* Disk-loading section */
BPTR fid_FH;
LONG fid_CurrFilePos;
};
/* node which holds information about a single animation/movie frame */
struct FrameNode
{
struct MinNode fn_Node;
/* Misc */
WORD fn_UseCount;
UWORD fn_Pad0;
/* Timing section */
ULONG fn_TimeStamp;
ULONG fn_Frame;
/* Bitmap section */
struct BitMap *fn_BitMap;
struct ColorMap *fn_CMap;
/* BitMap loading section */
LONG fn_BMOffset; /* File offset (0 is begin of file) */
ULONG fn_BMSize; /* Chunk size */
/* Sample section */
BYTE *fn_Sample;
ULONG fn_SampleLength;
};
/*****************************************************************************/
/* local prototypes */
static STRPTR GetPrefsVar( struct ClassBase *, STRPTR );
static BOOL matchstr( struct ClassBase *, STRPTR, STRPTR );
static void ReadENVPrefs( struct ClassBase *, struct FilmInstData * );
static LONG LoadFrames( struct ClassBase *, Object * );
static struct FrameNode *AllocFrameNode( struct ClassBase *, APTR );
static struct FrameNode *FindFrameNode( struct MinList *, ULONG );
static void FreeFrameNodeResources( struct ClassBase *, struct MinList * );
static void CopyBitMap( struct ClassBase *, struct BitMap *, struct BitMap * );
static struct BitMap *AllocBitMapPooled( struct ClassBase *, ULONG, ULONG, ULONG, APTR );
static BOOL CMAP2Object( struct ClassBase *, Object *, UBYTE *, ULONG );
static struct ColorMap *CMAP2ColorMap( struct ClassBase *, struct FilmInstData *, UBYTE *, ULONG );
static struct ColorMap *CopyColorMap( struct ClassBase *, struct ColorMap * );
static APTR AllocVecPooled( struct ClassBase *, APTR, ULONG );
static void FreeVecPooled( struct ClassBase *, APTR, APTR );
static void verbose_printf( struct ClassBase *, struct FilmInstData *, STRPTR, ... );
static void OpenLogfile( struct ClassBase *, struct FilmInstData * );
static void mysprintf( struct ClassBase *, STRPTR, STRPTR, ... );
static ULONG SaveIFFFilm( struct ClassBase *, struct IClass *, Object *, struct dtWrite * );
static struct IFFHandle *CreateDOSIFFHandle( struct ClassBase *, BPTR );
static LONG StartIFFFilm( struct ClassBase *, struct FilmInstData *, struct IFFHandle *, struct BitMapHeader *, ULONG, ULONG *, ULONG, struct VoiceHeader * );
static void EndIFFFilm( struct ClassBase *, struct FilmInstData *, struct IFFHandle * );
static LONG WriteIFFFilmCell( struct ClassBase *, struct IFFHandle *, struct BitMapHeader *, ULONG *, ULONG, struct BitMap *, BYTE *, ULONG );
static LONG PutILBMCMAP( struct ClassBase *, struct IFFHandle *, ULONG *, ULONG );
static LONG PutILBMBody( struct ClassBase *, struct IFFHandle *, struct BitMap *, struct BitMapHeader * );
static LONG Put8SVXBody( struct ClassBase *, struct IFFHandle *, BYTE *, ULONG );
/*****************************************************************************/
/* Create "film.datatype" BOOPSI class */
struct IClass *initClass( struct ClassBase *cb )
{
struct IClass *cl;
/* Create our class... */
if( cl = MakeClass( FILMDTCLASS, ANIMATIONDTCLASS, NULL, (ULONG)sizeof( struct FilmInstData ), 0UL ) )
{
cl -> cl_Dispatcher . h_Entry = (HOOKFUNC)Dispatch;
cl -> cl_UserData = (ULONG)cb;
AddClass( cl );
}
return( cl );
}
/*****************************************************************************/
/* IFF errors to DOS errors */
const
LONG ifferr2doserr[] =
{
0L, /* End of file (not an error). */
0L, /* End of context (not an error). */
DTERROR_INVALID_DATA, /* No lexical scope. */
ERROR_NO_FREE_STORE, /* Insufficient memory. */
ERROR_SEEK_ERROR, /* Stream read error. */
ERROR_SEEK_ERROR, /* Stream write error. */
ERROR_SEEK_ERROR, /* Stream seek error. */
DTERROR_INVALID_DATA, /* File is corrupt. */
DTERROR_INVALID_DATA, /* IFF syntax error. */
ERROR_OBJECT_WRONG_TYPE, /* Not an IFF file. */
ERROR_REQUIRED_ARG_MISSING, /* Required call-back hook missing. */
0xDEADDEAD /* Return to client. You should never see this ! */
};
/*****************************************************************************/
struct MyStackSwapStruct
{
struct StackSwapStruct stk;
struct IClass *cl;
Object *o;
Msg msg;
};
/*****************************************************************************/
DISPATCHERFLAGS
ULONG Dispatch( REGA0 struct IClass *cl, REGA2 Object *o, REGA1 Msg msg )
{
struct ClassBase *cb = (struct ClassBase *)(cl -> cl_UserData);
ULONG retval;
struct MyStackSwapStruct mystk;
UBYTE *lower,
*upper,
*sp;
#define DTSTACKSIZE (16384UL)
while( !(lower = (UBYTE *)AllocMem( DTSTACKSIZE, MEMF_PUBLIC )) );
sp = upper = lower + DTSTACKSIZE;
mystk . stk . stk_Lower = lower;
mystk . stk . stk_Upper = (ULONG)upper;
mystk . stk . stk_Pointer = sp;
mystk . cl = cl;
mystk . o = o;
mystk . msg = msg;
retval = SwapMe( (&mystk) );
FreeMem( lower, DTSTACKSIZE );
return( retval );
}
DISPATCHERFLAGS
ULONG SwapMe( REGA0 struct MyStackSwapStruct *mystk )
{
register ULONG retval;
#define cb ((struct ClassBase *)(mystk -> cl -> cl_UserData))
StackSwap( (&(mystk -> stk)) );
retval = MyDispatch( mystk );
StackSwap( (&(mystk -> stk)) );
#undef cb
return( retval );
}
/* class dispatcher */
DISPATCHERFLAGS
ULONG MyDispatch( REGA0 struct MyStackSwapStruct *mystk )
{
struct IClass *cl = mystk -> cl;
Object *o = mystk -> o;
Msg msg = mystk -> msg;
struct ClassBase *cb = (struct ClassBase *)(cl -> cl_UserData);
struct FilmInstData *fid;
ULONG retval = 0UL;
switch( msg -> MethodID )
{
/****** film.datatype/OM_NEW *************************************************
*
* NAME
* OM_NEW -- Create a film.datatype object.
*
* FUNCTION
* The OM_NEW method is used to create an instance of the film.datatype
* class. This method is passed to the superclass first. After this,
* film.datatype parses the description file and makes a scan through
* the data to get index information. The samples are loaded directly
* (due their small size), frame bitmaps are loaded if the input
* stream isn't seekable (e.g. IFF handle/clipboard).
*
* ATTRIBUTES
* The following attributes can be specified at creation time.
*
* DTA_SourceType (ULONG) -- Determinates the type of DTA_Handle
* attribute. DTST_FILE, DTST_CLIPBOARD and DTST_RAM are supported.
* If any other type was set in a given DTA_SourceType,
* OM_NEW will be rejected.
* Defaults to DTST_FILE.
*
* DTA_Handle -- For both supported DTST_FILE and DTST_CLIPBOARD, a
* (struct IFFHandle *) is expected.
* (DTST_FILE expects a IFF Stream handle because this is a IFF
* type DataType (DTF_IFF)).
* A DTST_RAM (create empty object) source type requires a NULL
* handle.
*
* BUGS
* Before V1.3, this autodoc described that a DTA_Handle with a
* DTA_SourceType of DTST_FILE takes a DOS filehandle as an argument.
* This was WRONG ! Because this is a DTF_IFF type datatype, DTST_FILE
* takes also an IFF Stream handle as an argument.
*
* RESULT
* If the object was created a pointer to the object is returned,
* otherwise NULL is returned.
*
******************************************************************************
*
*/
case OM_NEW:
{
struct TagItem *ti;
/* We only support DTST_FILE, DTST_CLIPBOARD or DTST_RAM as source type */
if( ti = FindTagItem( DTA_SourceType, (((struct opSet *)msg) -> ops_AttrList) ) )
{
if( ((ti -> ti_Data) != DTST_FILE) &&
((ti -> ti_Data) != DTST_CLIPBOARD) &&
((ti -> ti_Data) != DTST_RAM) )
{
SetIoErr( ERROR_OBJECT_WRONG_TYPE );
break;
}
}
if( retval = DoSuperMethodA( cl, o, msg ) )
{
LONG error;
/* Load frames... */
if( error = LoadFrames( cb, (Object *)retval ) )
{
/* Something went fatally wrong, dispose object */
CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
retval = 0UL;
}
SetIoErr( error );
}
}
break;
/****** film.datatype/OM_DISPOSE *********************************************
*
* NAME
* OM_DISPOSE -- Delete a film.datatype object.
*
* FUNCTION
* The OM_DISPOSE method is used to delete an instance of the
* film.datatype class. This method is pass to the superclass when
* it has completed.
* This method frees all frame nodes and their contents (bitmaps,
* sounds etc.).
*
* RESULT
* The object is deleted. 0UL is returned.
*
******************************************************************************
*
*/
case OM_DISPOSE:
{
/* Get a pointer to our object data */
fid = (struct FilmInstData *)INST_DATA( cl, o );
/* Free colormaps etc. */
FreeFrameNodeResources( cb, (&(fid -> fid_FrameList)) );
/* Free our key bitmap */
FreeBitMap( (fid -> fid_KeyBitMap) );
/* Delete the frame pool */
DeletePool( (fid -> fid_Pool) );
if( fid -> fid_FH )
{
Close( (fid -> fid_FH) );
}
/* Close verbose output file */
if( fid -> fid_VerboseOutput )
{
Close( (fid -> fid_VerboseOutput) );
}
/* Dispose object */
DoSuperMethodA( cl, o, msg );
}
break;
case OM_UPDATE:
{
if( DoMethod( o, ICM_CHECKLOOP ) )
{
break;
}
}
case OM_SET:
{
/* Pass the attributes to the animation class and force a refresh if we need it */
if( retval = DoSuperMethodA( cl, o, msg ) )
{
/* Top instance ? */
if( OCLASS( o ) == cl )
{
struct RastPort *rp;
/* Get a pointer to the rastport */
if( rp = ObtainGIRPort( (((struct opSet *)msg) -> ops_GInfo) ) )
{
struct gpRender gpr;
/* Force a redraw */
gpr . MethodID = GM_RENDER;
gpr . gpr_GInfo = ((struct opSet *)msg) -> ops_GInfo;
gpr . gpr_RPort = rp;
gpr . gpr_Redraw = GREDRAW_UPDATE;
DoMethodA( o, (Msg)(&gpr) );
/* Release the temporary rastport */
ReleaseGIRPort( rp );
retval = 0UL;
}
}
}
}
break;
/****** film.datatype/GM_LAYOUT *****************************************
*
* NAME
* GM_LAYOUT -- Layout animation object
* DTM_PROCLAYOUT -- Layout animation object on application's task
*
* FUNCTION
* This method is passed to the superclass first.
* Then it collects information from superclass, set up
* struct DTSpecialInfo and send an OM_NOTIFY containing the following
* attributes:
* DTA_VisibleVert, (DTA_Domain -> Height)
* DTA_TotalVert, (same as ADTA_Height)
* DTA_NominalVert, (same as ADTA_Height)
* DTA_VertUnit, 1UL,
* DTA_VisibleHoriz, (DTA_Domain -> Width),
* DTA_TotalHoriz, (same as ADTA_Width),
* DTA_NominalHoriz, (same as ADTA_Width),
* DTA_HorizUnit, 1UL,
* GA_ID, (GadgetID),
* DTA_Title, (same as DTA_Name),
* DTA_Busy, FALSE,
* DTA_Sync, TRUE
*
* RESULT
* The value from the superclass is returned.
*
*******************************************************************************
*
*/
case GM_LAYOUT:
case DTM_PROCLAYOUT:
{
/* Attributes obtained from super-class */
struct IBox *domain;
STRPTR title;
ULONG animwidth,
animheight;
/* Let the super-class partake */
retval = DoSuperMethodA( cl, o, msg );
/* Get all the attributes that we are going to need for a successful layout */
if( GetDTAttrs( o,
DTA_Domain, (&domain),
DTA_Name, (&title),
ADTA_Width, (&animwidth),
ADTA_Height, (&animheight),
TAG_DONE ) == 4UL )
{
struct DTSpecialInfo *si = (struct DTSpecialInfo *)(G( o ) -> SpecialInfo);
struct TagItem NotifyTags[ 13 ];
struct opUpdate opu;
/* Lock the global object data so that nobody else can manipulate it
* (when modifying DTSpecialInfo and building the notify attributes)
*/
ObtainSemaphore( (&(si -> si_Lock)) );
/* Compute the lines and columns type information */
si -> si_VertUnit = 1L;
si -> si_VisVert = (LONG)(domain -> Height);
si -> si_TotVert = (LONG)animheight;
si -> si_HorizUnit = 1L;
si -> si_VisHoriz = (LONG)(domain -> Width);
si -> si_TotHoriz = (LONG)animwidth;
/* Build notify attrs */
NotifyTags[ 0 ] . ti_Tag = DTA_VisibleVert;
NotifyTags[ 0 ] . ti_Data = (si -> si_VisVert);
NotifyTags[ 1 ] . ti_Tag = DTA_TotalVert;
NotifyTags[ 1 ] . ti_Data = (si -> si_TotVert);
NotifyTags[ 2 ] . ti_Tag = DTA_NominalVert;
NotifyTags[ 2 ] . ti_Data = animheight;
NotifyTags[ 3 ] . ti_Tag = DTA_VertUnit;
NotifyTags[ 3 ] . ti_Data = (si -> si_VertUnit);
NotifyTags[ 4 ] . ti_Tag = DTA_VisibleHoriz;
NotifyTags[ 4 ] . ti_Data = (si -> si_VisHoriz);
NotifyTags[ 5 ] . ti_Tag = DTA_TotalHoriz;
NotifyTags[ 5 ] . ti_Data = (si -> si_TotHoriz);
NotifyTags[ 6 ] . ti_Tag = DTA_NominalHoriz;
NotifyTags[ 6 ] . ti_Data = animwidth;
NotifyTags[ 7 ] . ti_Tag = DTA_HorizUnit;
NotifyTags[ 7 ] . ti_Data = (ULONG)(si -> si_HorizUnit);
NotifyTags[ 8 ] . ti_Tag = GA_ID;
NotifyTags[ 8 ] . ti_Data = (ULONG)(G( o ) -> GadgetID);
NotifyTags[ 9 ] . ti_Tag = DTA_Title;
NotifyTags[ 9 ] . ti_Data = (ULONG)title;
NotifyTags[ 10 ] . ti_Tag = DTA_Busy;
NotifyTags[ 10 ] . ti_Data = (ULONG)FALSE;
NotifyTags[ 11 ] . ti_Tag = DTA_Sync;
NotifyTags[ 11 ] . ti_Data = (ULONG)TRUE;
NotifyTags[ 12 ] . ti_Tag = TAG_DONE;
NotifyTags[ 12 ] . ti_Data = 0UL;
/* Release the global data lock */
ReleaseSemaphore( (&(si -> si_Lock)) );
/* Tell the world of our newest attributes */
opu . MethodID = OM_NOTIFY;
opu . opu_AttrList = NotifyTags;
opu . opu_GInfo = ((struct gpLayout *)msg) -> gpl_GInfo;
opu . opu_Flags = 0UL;
DoMethodA( o, (Msg)(&opu) );
}
}
break;
/****** film.datatype/DTM_FRAMEBOX ***************************************
*
* NAME
* DTM_FRAMEBOX -- Set up object for a specified environment
*
* FUNCTION
* The DTM_FRAMEBOX method is used to set up for a specified
* environment. This method is passed to the superclass after
* film.datatype instance finished it's work.
*
* Only if FRAMEF_SPECIFY is set in (dtf -> dtf_FrameFlags), the
* film.datatype instance will modify the ADTA_Remap to FALSE if
* the NOREMAP switch in the prefs file was set and the animation
* depth matches the given (frameinfo -> fri_Screen -> BitMap . Depth)
* depth.
*
* RESULT
* The value from the superclass is returned.
*
* SEE ALSO
* 3.1_Examples2:DataTypes/Src/ClipView/clipview.c
*
******************************************************************************
*
*/
case DTM_FRAMEBOX:
{
struct dtFrameBox *dtf;
fid = (struct FilmInstData *)INST_DATA( cl, o );
dtf = (struct dtFrameBox *)msg;
/* Make do with the dimensions of FrameBox provided ? */
if( (dtf -> dtf_FrameFlags) & FRAMEF_SPECIFY )
{
/* NOREMAP switch set ? */
if( fid -> fid_NoRemap )
{
ULONG depth;
/* Get animation depth */
if( GetAttr( ADTA_Depth, o, (&depth) ) == 1UL )
{
/* Match ? */
if( depth == (dtf -> dtf_ContentsInfo -> fri_Dimensions . Depth) )
{
SetAttrs( o, ADTA_Remap, FALSE, TAG_DONE );
}
}
}
}
retval = DoSuperMethodA( cl, o, msg );
}
break;
/****** film.datatype/DTM_WRITE **********************************************
*
* NAME
* DTM_WRITE -- Save data
*
* FUNCTION
* This method saves the object's contents to disk.
*
* If dtw_Mode is DTWM_IFF, the method is passed unchanged to the
* superclass, animation.datatype, which writes a single IFF ILBM
* picture.
*
* If dtw_mode is DTWM_RAW, the object saved an IFF FILM stream to
* the filehandle given, starting with the current frame until
* the end is reached.
* The sequence saved can be controlled by the ADTA_Frame, ADTA_Frames
* and ADTA_FrameIncrement attributes (see TAGS section below).
*
* TAGS
* When writing the local ("raw") format, IFF FILM, the following
* attributes are recognized:
*
* ADTA_Frame (ULONG) - start frame, saving starts here.
* Defaults to the current frame displayed.
*
* ADTA_Frames (ULONG) - the number of frames to be saved,
* Defaults to (max_num_of_frames - curr_frame).
*
* ADTA_FrameIncrement (ULONG) - frame increment when saving.
* Defaults to 1, which means: "jump to next frame".
*
* BUGS
* Any other ADTA_FrameIncrement value than 1 causes that
* the sound of the skipped frames got lost. A recommened way would be
* to append their sound to the next frame(s).
*
* RESULT
* Returns 0 for failure (IoErr() returns result2), non-zero
* for success.
*
******************************************************************************
*
*/
case DTM_WRITE:
{
struct dtWrite *dtw;
dtw = (struct dtWrite *)msg;
/* Local data format not supported yet... */
if( (dtw -> dtw_Mode) == DTWM_RAW )
{
retval = SaveIFFFilm( cb, cl, o, dtw );
}
else
{
/* Pass msg to superclass (which writes a single frame as an IFF ILBM picture)... */
retval = DoSuperMethodA( cl, o, msg );
}
}
break;
/****** film.datatype/ADTM_LOADFRAME *****************************************
*
* NAME
* ADTM_LOADFRAME -- Load frame
*
* FUNCTION
* The ADTM_LOADFRAME method is used to obtain the bitmap and timing
* data of the animation.
* The given timestamp will be used to find a matching timestamp
* in the internal FrameNode list. If it was found, the corresponding
* timing, bitmap, and sound data are stored into the struct
* adtFrame. If the bitmap wasn't loaded, this method attempts to
* load it from disk.
*
* RESULT
* Returns always non-zero,
* the bitmap ptr if a bitmap was found,
* 1UL otherwise.
*
******************************************************************************
*
*/
case ADTM_LOADFRAME:
{
struct FrameNode *fn;
struct adtFrame *alf;
fid = (struct FilmInstData *)INST_DATA( cl, o );
alf = (struct adtFrame *)msg;
ObtainSemaphore( (&(fid -> fid_SigSem)) );
/* Find frame by timestamp */
if( fn = FindFrameNode( (&(fid -> fid_FrameList)), (alf -> alf_TimeStamp) ) )
{
/* Load bitmaps only if we don't cache the whole anim and
* if we have a filehandle to load from (an empty object created using DTST_RAM)...
*/
if( (fid -> fid_LoadAll) == FALSE && (fid -> fid_FH) )
{
/* If no bitmap is loaded, load it... */
if( (fn -> fn_BitMap) == NULL )
{
if( fn -> fn_BitMap = AllocBitMapPooled( cb, (ULONG)(fid -> fid_BMH -> bmh_Width), (ULONG)(fid -> fid_BMH -> bmh_Height), (ULONG)(fid -> fid_BMH -> bmh_Depth), (fid -> fid_Pool) ) )
{
if( Seek( (fid -> fid_FH), (((fn -> fn_BMOffset) + 8UL) - (fid -> fid_CurrFilePos)), OFFSET_CURRENT ) != (-1L) )
{
/* Load ILBM BODY directly into bitmap, assuming an interleaved bitmap */
if( Read( (fid -> fid_FH), (fn -> fn_BitMap -> Planes[ 0 ]), (fn -> fn_BMSize) ) == (fn -> fn_BMSize) )
{
/* Bump file pos */
fid -> fid_CurrFilePos = ((fn -> fn_BMOffset) + 8UL) + (fn -> fn_BMSize);
/* Success ! */
retval = 1UL;
}
}
if( retval == 0UL )
{
verbose_printf( cb, fid, "ADTM_LOADFRAME: read error %ld\n", IoErr() );
/* Error, rewind stream */
Seek( (fid -> fid_FH), 0L, OFFSET_BEGINNING );
fid -> fid_CurrFilePos = 0L;
}
}
}
}
/* Store frame/context information */
alf -> alf_Frame = fn -> fn_Frame;
alf -> alf_UserData = (APTR)fn; /* Links back to this FrameNode (used by ADTM_UNLOADFRAME) */
/* Store bitmap information */
alf -> alf_BitMap = fn -> fn_BitMap;
/* Is there a sample to play ? */
if( fn -> fn_Sample )
{
/* Store sound information */
alf -> alf_Sample = fn -> fn_Sample;
alf -> alf_SampleLength = fn -> fn_SampleLength;
alf -> alf_Period = fid -> fid_Period;
}
else
{
/* No sound */
alf -> alf_Sample = NULL;
alf -> alf_SampleLength = 0UL;
alf -> alf_Period = 0UL;
}
/* Frame "in use" */
fn -> fn_UseCount++;
/* Return bitmap ptr of possible, 1UL otherwise */
retval = (alf -> alf_BitMap)?((ULONG)(alf -> alf_BitMap)):(1UL);
}
else
{
/* no matching frame found */
retval = 1UL;
}
ReleaseSemaphore( (&(fid -> fid_SigSem)) );
}
break;
/****** film.datatype/ADTM_UNLOADFRAME ***************************************
*
* NAME
* ADTM_UNLOADFRAME -- Load frame contents
*
* FUNCTION
* The ADTM_UNLOADFRAME method is used to release the contents of a
* animation frame.
*
* This method frees the bitmap data found in adtFrame.
*
* RESULT
* Returns always 0UL.
*
******************************************************************************
*
*/
case ADTM_UNLOADFRAME:
{
struct FrameNode *fn;
struct adtFrame *alf;
fid = (struct FilmInstData *)INST_DATA( cl, o );
alf = (struct adtFrame *)msg;
/* Free bitmaps only if we don't cache the whole anim */
if( (fid -> fid_LoadAll) == FALSE )
{
ObtainSemaphore( (&(fid -> fid_SigSem)) );
if( fn = (struct FrameNode *)(alf -> alf_UserData) )
{
if( (fn -> fn_UseCount) > 0 )
{
fn -> fn_UseCount--;
if( ((fn -> fn_UseCount) == 0) && (fn -> fn_BitMap) )
{
FreeVecPooled( cb, (fid -> fid_Pool), (fn -> fn_BitMap) );
fn -> fn_BitMap = NULL;
}
}
}
ReleaseSemaphore( (&(fid -> fid_SigSem)) );
}
}
break;
/* Let the superclass handle everything else */
default:
{
retval = DoSuperMethodA( cl, o, msg );
}
break;
}
return( retval );
}
/****** film.datatype/preferences ********************************************
*
* NAME
* preferences
*
* DESCRIPTION
* The "ENV:Classes/DataTypes/film.prefs" file contains global
* settings for the datatype.
* The preferences file is an ASCII file containing one line where the
* preferences can be set.
* It can be superset by a local variable with the same name.
*
* Each line can contain settings, special settings for some projects
* can be set using the MATCHPROJECT option.
* Lines beginning with a '#' or ';' chars are treated as comments.
* Lines are limitted to 256 chars.
*
* TEMPLATE
* MATCHPROJECT/K,MODEID/K/N,NOREMAP/S,FPS/K/N,VOLUME/K/N,LOADALL/S,
* VERBOSE/S
*
* MATCHPROJECT -- The settings in this line belongs only to this
* project(s), e.g. if the case-insensitive pattern does not match,
* this line is ignored.
* The maximum length of the pattern is 256 chars.
* Defaults to #?, which matches any project.
*
* MODEID -- Select screen mode id of datatype (will be stored in
* ADTA_ModeID). Note that the DOS ReadArgs function used for parsing
* fetches a SIGNED long. The bit 31 will be represented by minus
* '-'. (example: "MODEID=266240" sets the mode to the A2024 screen
* mode id)
* Defaults to 0, which means: Use the best screenmode available
* for the given width, height and depth.
*
* FPS -- frames per second
* Defaults to 0, which means: overtake fps rate from video stream.
* Setting the FPS value also affects an attched sound. The period
* of the sample (e.g. the playback speed) will everytimes as long
* as the frame is displayed.
*
* VOLUME -- Volume of the sound when playing.
* Defaults to 64, which is the maximum. A value greater than 64 will
* be set to 64.
*
* LOADALL -- Load all frames into memory.
*
* VERBOSE -- Print out verbose information about the movie like
* width, height, depth, number of frames, FPS-Rate etc.
*
* NOTE
* An invalid prefs file line will be ignored.
*
* BUGS
* - Low memory may cause that the prefs file won't be parsed.
*
* - Lines are limitted to 256 chars
*
******************************************************************************
*
*/
static
STRPTR GetPrefsVar( struct ClassBase *cb, STRPTR name )
{
STRPTR buff;
const ULONG buffsize = 16UL;
if( buff = (STRPTR)AllocVec( (buffsize + 2UL), (MEMF_PUBLIC | MEMF_CLEAR) ) )
{
if( GetVar( name, buff, buffsize, GVF_BINARY_VAR ) != (-1L) )
{
ULONG varsize = IoErr();
varsize += 2UL;
if( varsize > buffsize )
{
FreeVec( buff );
if( buff = (STRPTR)AllocVec( (varsize + 2UL), (MEMF_PUBLIC | MEMF_CLEAR) ) )
{
if( GetVar( name, buff, varsize, GVF_BINARY_VAR ) != (-1L) )
{
return( buff );
}
}
}
else
{
return( buff );
}
}
FreeVec( buff );
}
return( NULL );
}
static
BOOL matchstr( struct ClassBase *cb, STRPTR pat, STRPTR s )
{
TEXT buff[ 516 ];
if( pat && s )
{
if( ParsePatternNoCase( pat, buff, (ULONG)(sizeof( buff ) - 1) ) != (-1L) )
{
if( MatchPatternNoCase( buff, s ) )
{
return( TRUE );
}
}
}
return( FALSE );
}
static
void ReadENVPrefs( struct ClassBase *cb, struct FilmInstData *fid )
{
struct RDArgs envvarrda =
{
NULL,
256L,
0L,
0L,
NULL,
0L,
NULL,
RDAF_NOPROMPT
};
struct
{
STRPTR matchproject;
long *modeid;
long *noremap;
long *fps;
long *volume;
long *loadall;
long *verbose;
} animargs;
TEXT varbuff[ 258 ];
STRPTR var;
if( var = GetPrefsVar( cb, "Classes/DataTypes/film.prefs" ) )
{
STRPTR prefsline = var,
nextprefsline;
ULONG linecount = 1UL;
/* Be sure that "var" contains at least one break-char */
strcat( var, "\n" );
while( nextprefsline = strpbrk( prefsline, "\n" ) )
{
stccpy( varbuff, prefsline, MIN( (sizeof( varbuff ) - 2UL), (((ULONG)(nextprefsline - prefsline)) + 1UL) ) );
/* be sure that this line isn't a comment line or an empty line */
if( (varbuff[ 0 ] != '#') && (varbuff[ 0 ] != ';') && (varbuff[ 0 ] != '\n') && (strlen( varbuff ) > 2UL) )
{
/* Prepare ReadArgs processing */
strcat( varbuff, "\n" ); /* Add NEWLINE-char */
envvarrda . RDA_Source . CS_Buffer = varbuff; /* Buffer */
envvarrda . RDA_Source . CS_Length = strlen( varbuff ) + 1UL; /* Set up input buffer length */
envvarrda . RDA_Source . CS_CurChr = 0L;
envvarrda . RDA_Buffer = NULL;
envvarrda . RDA_BufSiz = 0L;
memset( (void *)(&animargs), 0, sizeof( animargs ) ); /* Clear result array */
if( ReadArgs( "MATCHPROJECT/K,"
"MODEID/K/N,"
"NOREMAP/S,"
"FPS/K/N,"
"VOLUME/K/N,"
"LOADALL/S,"
"VERBOSE/S", (LONG *)(&animargs), (&envvarrda) ) )
{
BOOL noignore = TRUE;
if( (animargs . matchproject) && (fid -> fid_ProjectName) )
{
noignore = matchstr( cb, (animargs . matchproject), (fid -> fid_ProjectName) );
}
if( noignore )
{
if( animargs . verbose )
{
OpenLogfile( cb, fid );
}
if( animargs . modeid )
{
fid -> fid_ModeID = *(animargs . modeid);
}
if( animargs . noremap )
{
fid -> fid_NoRemap = TRUE;
}
if( animargs . fps )
{
fid -> fid_FPS = *(animargs . fps);
}
if( animargs . volume )
{
fid -> fid_Volume = *(animargs . volume);
if( (fid -> fid_Volume) > 64UL )
{
fid -> fid_Volume = 64UL;
}
}
else
{
fid -> fid_Volume = 64UL;
}
if( animargs . loadall )
{
fid -> fid_LoadAll = TRUE;
}
}
else
{
verbose_printf( cb, fid, "prefs line %lu ignored\n", linecount );
}
FreeArgs( (&envvarrda) );
}
else
{
LONG ioerr = IoErr();
TEXT errbuff[ 256 ];
Fault( ioerr, "Classes/DataTypes/film.prefs", errbuff, sizeof( errbuff ) );
verbose_printf( cb, fid, "preferences \"%s\" line %lu\n", errbuff, linecount );
}
}
prefsline = ++nextprefsline;
linecount++;
}
FreeVec( var );
}
}
static
LONG LoadFrames( struct ClassBase *cb, Object *o )
{
struct FilmInstData *fid = (struct FilmInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
LONG error = 0L;
InitSemaphore( (&(fid -> fid_SigSem)) );
NewList( (struct List *)(&(fid -> fid_FrameList)) );
/* Create a memory pool for frame nodes */
if( fid -> fid_Pool = CreatePool( MEMF_PUBLIC, 16384UL, 16384UL ) )
{
APTR fh; /* IFF stream handle */
ULONG sourcetype; /* type of stream (either DTST_FILE, DTST_CLIPBOARD or DTST_RAM */
ULONG pos = 0UL; /* current file pos in IFF stream */
struct BitMapHeader *bmh; /* obj's bitmapheader */
ULONG modeid = 0UL; /* anim view mode */
ULONG animwidth = 0UL, /* anim width */
animheight = 0UL, /* anim height */
animdepth = 0UL; /* anim depth */
ULONG timestamp = 0UL; /* timestamp */
ULONG sps = 0UL; /* samples per second */
ULONG spf = 0UL; /* samples per frame */
/* Prefs defaults */
fid -> fid_Volume = 64U;
/* Read prefs */
ReadENVPrefs( cb, fid );
/* Get file handle, handle type and BitMapHeader */
if( GetDTAttrs( o, DTA_SourceType, (&sourcetype),
DTA_Handle, (&fh),
DTA_Name, (&(fid -> fid_ProjectName)),
PDTA_BitMapHeader, (&bmh),
TAG_DONE ) == 4UL )
{
struct IFFHandle *iff = NULL;
ULONG numcmaps = 0UL; /* number of loaded colormaps */
fid -> fid_BMH = bmh; /* Store BitMapHeader */
switch( sourcetype )
{
case DTST_CLIPBOARD:
{
fid -> fid_LoadAll = TRUE; /* Cache all frames because we cannot load randomly from stream */
iff = (struct IFFHandle *)fh;
}
break;
case DTST_FILE:
{
BPTR iff_file_fh;
iff = (struct IFFHandle *)fh;
/* Attempt to open file from given stream (allows usage of virtual fs when using datatypes.library V45) */
iff_file_fh = (BPTR)(iff -> iff_Stream); /* see iffparse.library/InitIFFasDOS autodoc */
if( iff_file_fh )
{
BPTR lock;
if( lock = DupLockFromFH( iff_file_fh ) )
{
/* Set up a filehandle for disk-based loading (random loading) */
if( !(fid -> fid_FH = (LONG)OpenFromLock( lock )) )
{
/* failure */
UnLock( lock );
}
}
}
/* OpenFromLock failed ? - Then open by name :-( */
if( (fid -> fid_FH) == NULL )
{
/* Set up a filehandle for disk-based loading (random loading) */
if( !(fid -> fid_FH = (LONG)Open( (fid -> fid_ProjectName), MODE_OLDFILE )) )
{
/* Can't open file */
error = IoErr();
}
}
}
break;
case DTST_RAM:
{
/* No nothing... */
}
break;
default:
{
/* unsupported source type */
error = ERROR_NOT_IMPLEMENTED;
}
break;
}
/* Any error ? */
if( error == 0L )
{
/* Any handle to proccess ? */
if( iff )
{
struct StoredProperty *bmhdprop = NULL, /* ILBM BMHD (struct BitMapHeader) */
*camgprop = NULL, /* ILBM CAMG (amiga view mode id) */
*vhdrprop = NULL, /* 8SVX VHDR (struct VoiceHeader) */
*chanprop = NULL; /* 8SVX CHAN (left/right/stereo channel) */
ULONG chan_channel = CHAN_RIGHT; /* or CHAN_LEFT - whatever you want, but only ONE channel */
#define NUM_PROPCHUNKS (4L)
const
LONG propchunks[ (NUM_PROPCHUNKS * 2) ] =
{
ID_ILBM, ID_BMHD,
ID_ILBM, ID_CAMG,
ID_8SVX, ID_VHDR,
ID_8SVX, ID_CHAN
};
if( !(error = PropChunks( iff, (LONG *)propchunks, NUM_PROPCHUNKS )) )
{
#define NUM_STOPCHUNKS (4L)
const
LONG stopchunks[ (NUM_STOPCHUNKS * 2) ] =
{
ID_CELL, ID_CAT,
ID_ILBM, ID_BODY,
ID_ILBM, ID_CMAP,
ID_8SVX, ID_BODY
};
if( !(error = StopChunks( iff, (LONG *)stopchunks, NUM_STOPCHUNKS )) )
{
struct FrameNode *fn = NULL;
/* Scan IFF stream until an error or an EOF occurs */
for( ;; )
{
struct ContextNode *cn;
if( error = ParseIFF( iff, IFFPARSE_SCAN ) )
{
/* EOF (End Of File) is no error here... */
if( error == IFFERR_EOF )
{
error = 0L;
}
break;
}
/* Get file position */
if( cn = CurrentChunk( iff ) )
{
pos = 0UL;
while( cn = ParentChunk( cn ) )
{
pos += cn -> cn_Scan;
}
}
/* bmhd header loaded ? */
if( bmhdprop == NULL )
{
if( bmhdprop = FindProp( iff, ID_ILBM, ID_BMHD ) )
{
*bmh = *((struct BitMapHeader *)(bmhdprop -> sp_Data));
animwidth = bmh -> bmh_Width;
animheight = bmh -> bmh_Height;
animdepth = bmh -> bmh_Depth;
/* ILBM BODY __must__ not be compressed for IFF FILM */
if( (bmh -> bmh_Compression) != cmpNone )
{
error = DTERROR_INVALID_DATA;
}
}
}
/* camg loaded ? */
if( camgprop == NULL )
{
if( camgprop = FindProp( iff, ID_ILBM, ID_CAMG ) )
{
modeid = *(ULONG *)(camgprop -> sp_Data);
}
}
/* vhdr loaded ? */
if( vhdrprop == NULL )
{
if( vhdrprop = FindProp( iff, ID_8SVX, ID_VHDR ) )
{
struct VoiceHeader *vhdr = (struct VoiceHeader *)(vhdrprop -> sp_Data);
if( (vhdr -> vh_SamplesPerSec) == 0UL )
{
/* vh_SamplesPerSec must not be 0 */
error = DTERROR_INVALID_DATA;
break;
}
sps = vhdr -> vh_SamplesPerSec;
}
}
/* chan loaded ? */
if( chanprop == NULL )
{
if( chanprop = FindProp( iff, ID_8SVX, ID_CHAN ) )
{
chan_channel = *(ULONG *)(chanprop -> sp_Data);
if( (chan_channel != CHAN_RIGHT) &&
(chan_channel != CHAN_LEFT) &&
(chan_channel != CHAN_STEREO) )
{
/* unsupported 8SVX/CHAN channel ID, use the default */
chan_channel = CHAN_RIGHT; /* or CHAN_LEFT, see upstairs */
}
}
}
if( cn = CurrentChunk( iff ) )
{
switch( (cn -> cn_Type) )
{
/* New IFF FILM CELL found */
case ID_CELL:
{
if( (cn -> cn_ID) == ID_CAT )
{
/* Move previous cell into frame list */
if( fn )
{
AddTail( (struct List *)(&(fid -> fid_FrameList)), (struct Node *)(&(fn -> fn_Node)) );
}
/* Create an prepare a new frame node */
if( fn = AllocFrameNode( cb, (fid -> fid_Pool) ) )
{
fn -> fn_TimeStamp = timestamp++;
fn -> fn_Frame = fn -> fn_TimeStamp;
}
else
{
/* can't alloc frame node */
error = ERROR_NO_FREE_STORE;
}
}
}
break;
case ID_ILBM:
{
switch( cn -> cn_ID )
{
case ID_CMAP:
{
UBYTE *buff;
/* Allocate buffer */
if( buff = (UBYTE *)AllocVecPooled( cb, (fid -> fid_Pool), ((cn -> cn_Size) + 16UL) ) )
{
/* Load CMAP data */
error = ReadChunkBytes( iff, buff, (cn -> cn_Size) );
/* All read ? */
if( error == (cn -> cn_Size) )
{
error = 0L; /* Success ! */
if( timestamp == 0UL )
{
if( !CMAP2Object( cb, o, buff, (cn -> cn_Size) ) )
{
/* can't alloc object's color table */
error = ERROR_NO_FREE_STORE;
}
}
else
{
if( fh )
{
verbose_printf( cb, fid, "CMAP found, changing colormap\n" );
}
}
/* Any failure ? */
if( error == 0L )
{
if( fn )
{
if( fn -> fn_CMap = CMAP2ColorMap( cb, fid, buff, (cn -> cn_Size) ) )
{
numcmaps++;
}
else
{
/* no colormap */
error = ERROR_NO_FREE_STORE;
}
}
}
}
FreeVecPooled( cb, (fid -> fid_Pool), buff );
}
else
{
/* no load buff */
error = ERROR_NO_FREE_STORE;
}
}
break;
case ID_BODY:
{
if( fn )
{
/* Store position of BODY (pos points to the BODY ID) */
fn -> fn_BMOffset = pos;
fn -> fn_BMSize = cn -> cn_Size;
if( (fn -> fn_BitMap) == NULL )
{
/* Preload frames only if requested or if this is the key frame (first frame of anim) */
if( (fid -> fid_LoadAll) || IsListEmpty( (struct List *)(&(fid -> fid_FrameList)) ) )
{
if( animwidth && animheight && animdepth )
{
if( fn -> fn_BitMap = AllocBitMapPooled( cb, animwidth, animheight, animdepth, (fid -> fid_Pool) ) )
{
(void)ReadChunkBytes( iff, (fn -> fn_BitMap -> Planes[ 0 ]), (cn -> cn_Size) );
}
else
{
/* no bitmap */
error = ERROR_NO_FREE_STORE;
}
}
else
{
/* no dimensions for bitmap (possibly a missing bmhd) */
error = DTERROR_NOT_ENOUGH_DATA;
}
}
}
else
{
/* bitmap already loaded */
error = DTERROR_INVALID_DATA;
}
}
}
break;
}
}
break;
case ID_8SVX:
{
if( (cn -> cn_ID) == ID_BODY )
{
if( fn )
{
BYTE *buff;
ULONG bufflength = cn -> cn_Size;
/* Calc samples per frame (if not set previously)
* If sample data represents mono audio,
* spf = length of sample data
* If sample data represents stereo audio (2 channels),
* spf = (length of sample data) / 2
*/
if( spf == 0UL )
{
spf = ((chan_channel != CHAN_STEREO)?(cn -> cn_Size):((cn -> cn_Size) / 2UL));
}
if( buff = (BYTE *)AllocPooled( (fid -> fid_Pool), bufflength ) )
{
(void)ReadChunkBytes( iff, buff, bufflength );
if( chan_channel != CHAN_STEREO )
{
fn -> fn_Sample = buff;
fn -> fn_SampleLength = bufflength;
}
else
{
BYTE *sample;
ULONG samplelength = bufflength / 2UL;
if( sample = (BYTE *)AllocPooled( (fid -> fid_Pool), (samplelength + 4UL) ) )
{
UBYTE *left = buff,
*right = buff + samplelength,
*dest = sample;
ULONG i = samplelength;
/* Merge the two stereo channels together (quality loss) */
while( i-- )
{
*dest++ = (BYTE)((((WORD)(*left++)) + ((WORD)(*right++))) / 2);
}
fn -> fn_Sample = sample;
fn -> fn_SampleLength = samplelength;
}
else
{
/* no sample mem (stereo) */
error = ERROR_NO_FREE_STORE;
}
FreePooled( (fid -> fid_Pool), buff, bufflength );
}
}
else
{
/* no sample mem mono (or stereo temp buffer) */
error = ERROR_NO_FREE_STORE;
}
}
else
{
/* no fn while reading a 8SVX BODY */
error = ERROR_NO_FREE_STORE;
}
}
}
break;
}
}
/* on error: leave for-loop */
if( error )
{
break;
}
}
if( fn )
{
/* Move last cell into frame list */
AddTail( (struct List *)(&(fid -> fid_FrameList)), (struct Node *)(&(fn -> fn_Node)) );
}
}
}
/* bmh, vh and all other required information available ? */
if( (bmhdprop == NULL) || (vhdrprop == NULL) || (sps == 0UL) || (spf == 0UL) )
{
error = DTERROR_INVALID_DATA;
}
/* Process colormaps */
if( timestamp && (error == 0L) && numcmaps )
{
if( numcmaps == 1UL )
{
/* We only have a global colormap and no colormap changes,
* delete first colormap (a colormap in the first frames indicates following
* colormap/palette changes)
*/
struct FrameNode *firstnode = (struct FrameNode *)(fid -> fid_FrameList . mlh_Head);
if( firstnode -> fn_CMap )
{
FreeColorMap( (firstnode -> fn_CMap) );
firstnode -> fn_CMap = NULL;
}
}
else
{
/* All frames must have a colormap, therefore we replicate the colormap
* from the previous colormap if one is missing
*/
struct FrameNode *worknode,
*nextnode;
struct ColorMap *currcm = NULL;
worknode = (struct FrameNode *)(fid -> fid_FrameList . mlh_Head);
while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
{
if( worknode -> fn_CMap )
{
/* Current node contains colormap, this are the colors for the following frames... */
currcm = worknode -> fn_CMap;
}
else
{
if( currcm )
{
/* Copy colormap from previous one... */
if( !(worknode -> fn_CMap = CopyColorMap( cb, currcm )) )
{
/* Can't copy/alloc colormap */
error = ERROR_NO_FREE_STORE;
}
}
else
{
verbose_printf( cb, fid, "scan/load: no colormap, can't copy it\n" );
}
}
worknode = nextnode;
}
}
}
/* Any error ? */
if( error == 0L )
{
struct FrameNode *firstfn; /* short cut to the first FrameNode */
/* Set up fid_FirstFrameNode */
if( firstfn = (struct FrameNode *)(fid -> fid_FrameList . mlh_Head) )
{
/* Alloc interleaved bitmap as key bitmap */
if( (fid -> fid_KeyBitMap) = AllocBitMap( animwidth, animheight, animdepth, (BMF_CLEAR | BMF_INTERLEAVED), NULL ) )
{
/* Be sure that this is an interleaved bitmap */
if( GetBitMapAttr( (fid -> fid_KeyBitMap), BMA_FLAGS ) & BMF_INTERLEAVED )
{
ULONG clock = ((SysBase -> ex_EClockFrequency) * 5UL); /* amiga clock */
ULONG fps; /* frames per second */
ULONG period;
/* Calc sample period */
period = clock / sps;
/* Calc samples per second */
sps = clock / period;
/* Calc FPS */
fps = sps / spf; /* (samples per second) / (samples per frame) = (frames per second) */
fps++;
/* Other FPS set by prefs ? */
if( fid -> fid_FPS )
{
period = (period * fps) / (fid -> fid_FPS);
}
else
{
fid -> fid_FPS = fps;
}
fid -> fid_Period = period; /* Store period */
/* Assert: Be sure our FPS and period values are valid ! */
if( ((fid -> fid_FPS) == 0UL) || (period == 0UL) )
{
/* invalid fps or period */
error = DTERROR_INVALID_DATA;
}
else
{
if( firstfn -> fn_BitMap )
{
/* Copy first frame into key bitmap */
CopyBitMap( cb, (fid -> fid_KeyBitMap), (firstfn -> fn_BitMap) );
}
if( fid -> fid_ModeID )
{
modeid = fid -> fid_ModeID;
}
else
{
/* No mode id ? */
if( modeid == 0UL )
{
/* Get best modeid for this dimensions */
modeid = BestModeID( BIDTAG_NominalWidth, animwidth,
BIDTAG_NominalHeight, animheight,
BIDTAG_Depth, animdepth,
BIDTAG_DIPFMustNotHave, (DIPF_IS_DUALPF | DIPF_IS_PF2PRI),
TAG_DONE );
}
}
verbose_printf( cb, fid, "width %lu height %lu depth %lu frames %lu fps %lu volume %lu\n",
animwidth,
animheight,
animdepth,
timestamp,
(fid -> fid_FPS),
(ULONG)(fid -> fid_Volume) );
/* Set misc attributes */
SetDTAttrs( o, NULL, NULL,
ADTA_ModeID, modeid,
ADTA_Width, animwidth,
ADTA_Height, animheight,
ADTA_Depth, animdepth,
ADTA_Frames, timestamp,
ADTA_FramesPerSecond, (fid -> fid_FPS),
ADTA_KeyFrame, (fid -> fid_KeyBitMap),
ADTA_Sample, (firstfn -> fn_Sample),
ADTA_SampleLength, (firstfn -> fn_SampleLength),
ADTA_Period, (fid -> fid_Period),
ADTA_Volume, (fid -> fid_Volume),
ADTA_Cycles, 1UL,
DTA_NominalHoriz, animwidth,
DTA_NominalVert, animheight,
TAG_DONE );
}
}
else
{
/* can't alloc interleaved key bitmap
* (not enought memory to allocate continous memory, AllocBitMap allocated a non-interleaved one)
*/
error = ERROR_NO_FREE_STORE;
}
}
else
{
/* can't alloc key bitmap */
error = ERROR_NO_FREE_STORE;
}
}
else
{
/* not enougth frames (at least one required) */
error = DTERROR_NOT_ENOUGH_DATA;
}
}
}
else
{
/* No iff handle ? - Be sure we got a DTST_RAM sourcetype */
if( sourcetype != DTST_RAM )
{
/* No handle ! */
error = ERROR_REQUIRED_ARG_MISSING;
}
}
}
}
else
{
/* can't get required attributes from superclass */
error = ERROR_OBJECT_WRONG_TYPE;
}
}
else
{
/* no memory pool */
error = ERROR_NO_FREE_STORE;
}
/* Error codes below 0 are related to the IFFParse.library functions */
if( error < 0L )
{
/* convert IFFParse error to DOS error */
error = ifferr2doserr[ (-error - 1) ];
}
return( error );
}
static
struct FrameNode *AllocFrameNode( struct ClassBase *cb, APTR pool )
{
struct FrameNode *fn;
if( fn = (struct FrameNode *)AllocPooled( pool, (ULONG)sizeof( struct FrameNode ) ) )
{
memset( fn, 0, sizeof( struct FrameNode ) );
}
return( fn );
}
static
struct FrameNode *FindFrameNode( struct MinList *fnl, ULONG timestamp )
{
if( fnl )
{
struct FrameNode *worknode,
*nextnode;
worknode = (struct FrameNode *)(fnl -> mlh_Head);
while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
{
if( (worknode -> fn_TimeStamp) == timestamp )
{
return( worknode );
}
worknode = nextnode;
}
}
return( NULL );
}
static
void FreeFrameNodeResources( struct ClassBase *cb, struct MinList *fnl )
{
if( fnl )
{
struct FrameNode *worknode,
*nextnode;
worknode = (struct FrameNode *)(fnl -> mlh_Head);
while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
{
if( worknode -> fn_CMap )
{
FreeColorMap( (worknode -> fn_CMap) );
worknode -> fn_CMap = NULL;
}
worknode = nextnode;
}
}
}
/* Copy interleaved source to an interleaved destination */
static
void CopyBitMap( struct ClassBase *cb, struct BitMap *dest, struct BitMap *src )
{
if( dest && src )
{
ULONG planesize = (ULONG)(dest -> BytesPerRow) * (ULONG)(dest -> Rows);
CopyMem( (src -> Planes[ 0 ]), (dest -> Planes[ 0 ]), planesize );
}
}
/* This function assumes (0UL < depth) && (depth <= 8UL) */
static
struct BitMap *AllocBitMapPooled( struct ClassBase *cb, ULONG width, ULONG height, ULONG depth, APTR pool )
{
struct BitMap *bm;
ULONG planesize,
size;
planesize = (ULONG)RASSIZE( width, height );
size = ((ULONG)sizeof( struct BitMap )) + (planesize * depth) + 64UL;
if( bm = (struct BitMap *)AllocVecPooled( cb, pool, size ) )
{
UWORD pl;
PLANEPTR plane;
ULONG BytesPerRow;
InitBitMap( bm, depth, width, height );
/* Modify bitmap's BytesPerRow to be an interleaved bitmap (BytesPerRow = BytesPerRow * Depth,
* plane ptr's follows with old BytesPerRow offset)
*/
BytesPerRow = bm -> BytesPerRow;
bm -> BytesPerRow *= bm -> Depth;
plane = (PLANEPTR)(bm + 1); /* First plane follows struct BitMap */
/* Set up plane data */
pl = 0U;
/* Set up plane ptrs */
while( pl < depth )
{
bm -> Planes[ pl ] = plane;
plane = (PLANEPTR)(((UBYTE *)plane) + BytesPerRow);
pl++;
}
/* Clear the remaining plane ptrs */
while( pl < 8U )
{
bm -> Planes[ pl ] = NULL;
pl++;
}
}
return( bm );
}
static
BOOL CMAP2Object( struct ClassBase *cb, Object *o, UBYTE *rgb, ULONG rgbsize )
{
struct ColorRegister *acm;
ULONG *acregs;
ULONG nc;
/* file has this many colors (e.g. each color has one byte per R,B,G-gun) */
nc = rgbsize / 3UL;
SetDTAttrs( o, NULL, NULL, ADTA_NumColors, nc, TAG_DONE );
/* Get color context */
if( GetDTAttrs( o,
ADTA_ColorRegisters, (&acm),
ADTA_CRegs, (&acregs),
ADTA_NumColors, (&nc),
TAG_DONE ) == 3UL )
{
/* All valid ? */
if( acm && acregs && nc )
{
ULONG i;
for( i = 0UL ; i < nc ; i++, acm++ )
{
acm -> red = *rgb++;
acm -> green = *rgb++;
acm -> blue = *rgb++;
/* Replicate the color information.
* This surrounds an OS bug which uses the low-oreder bytes of the 32-bit colors
* instead of the high order ones
*/
acregs[ ((i * 3) + 0) ] = ((ULONG)(acm -> red)) * 0x01010101UL;
acregs[ ((i * 3) + 1) ] = ((ULONG)(acm -> green)) * 0x01010101UL;
acregs[ ((i * 3) + 2) ] = ((ULONG)(acm -> blue)) * 0x01010101UL;
}
return( TRUE );
}
}
return( FALSE );
}
static
struct ColorMap *CMAP2ColorMap( struct ClassBase *cb, struct FilmInstData *fid, UBYTE *rgb, ULONG rgbsize )
{
struct ColorMap *cm;
ULONG a_nc = (1UL << (ULONG)(fid -> fid_BMH -> bmh_Depth)); /* Number of colors in animation */
ULONG rgb_nc = rgbsize / 3UL; /* Number of colors in CMAP */
/* Get a colormap which hold all colors */
if( cm = GetColorMap( (long)MAX( a_nc, rgb_nc ) ) )
{
ULONG i,
r, g, b;
for( i = 0UL ; i < rgb_nc ; i++ )
{
r = *rgb++;
g = *rgb++;
b = *rgb++;
/* Replicate color information (see CMAP2Object for details) and store them into colormap */
SetRGB32CM( cm, i, (r * 0x01010101UL), (g * 0x01010101UL), (b * 0x01010101UL) );
}
/* BUG: the remaining entries should be filled with colors from the last colormap */
for( ; i < a_nc ; i++ )
{
SetRGB32CM( cm, i, 0UL, 0UL, 0UL ); /* fill remaining entries with black */
}
}
return( cm );
}
static
struct ColorMap *CopyColorMap( struct ClassBase *cb, struct ColorMap *src )
{
struct ColorMap *dest = NULL;
if( src )
{
ULONG *ctable;
if( ctable = (ULONG *)AllocVec( ((ULONG)(src -> Count) * sizeof( ULONG ) * 3UL), MEMF_PUBLIC ) )
{
if( dest = GetColorMap( (long)(src -> Count) ) )
{
ULONG i;
GetRGB32( src, 0UL, (ULONG)(src -> Count), ctable );
for( i = 0UL ; i < (src -> Count) ; i++ )
{
SetRGB32CM( dest, i, ctable[ ((i * 3) + 0) ], ctable[ ((i * 3) + 1) ], ctable[ ((i * 3) + 2) ] );
}
}
FreeVec( ctable );
}
}
return( dest );
}
static
APTR AllocVecPooled( struct ClassBase *cb, APTR pool, ULONG memsize )
{
ULONG *memory = NULL;
if( pool && memsize )
{
memsize += (ULONG)sizeof( ULONG );
if( memory = (ULONG *)AllocPooled( pool, memsize ) )
{
(*memory) = memsize;
memory++;
}
}
return( (APTR)memory );
}
static
void FreeVecPooled( struct ClassBase *cb, APTR pool, APTR mem )
{
if( pool && mem )
{
ULONG *memory;
memory = (ULONG *)mem;
memory--;
FreePooled( pool, memory, (*memory) );
}
}
static
void verbose_printf( struct ClassBase *cb, struct FilmInstData *fid, STRPTR format, ... )
{
if( fid -> fid_VerboseOutput )
{
VFPrintf( (fid -> fid_VerboseOutput), format, (APTR)((&format) + 1) );
}
}
static
void OpenLogfile( struct ClassBase *cb, struct FilmInstData *fid )
{
if( (fid -> fid_VerboseOutput) == NULL )
{
STRPTR confile;
if( confile = (STRPTR)AllocVec( (((fid -> fid_ProjectName)?(strlen((fid -> fid_ProjectName) )):(0UL)) + 100UL), MEMF_PUBLIC ) )
{
mysprintf( cb, confile, "CON:////Film DataType %s/auto/wait/close/inactive",
((fid -> fid_ProjectName)?(FilePart( (fid -> fid_ProjectName) )):(NULL)) );
fid -> fid_VerboseOutput = Open( confile, MODE_READWRITE );
FreeVec( confile );
}
}
}
static
void mysprintf( struct ClassBase *cb, STRPTR buffer, STRPTR fmt, ... )
{
APTR args;
args = (APTR)((&fmt) + 1);
RawDoFmt( fmt, args, (void (*))"\x16\xc0\x4e\x75", buffer );
}
static
ULONG SaveIFFFilm( struct ClassBase *cb, struct IClass *cl, Object *o, struct dtWrite *dtw )
{
ULONG retval = 0UL;
LONG error = 0L;
/* A NULL file handle is a nop (GMultiView uses this to test if a datatype supports RAW writing) */
if( dtw -> dtw_FileHandle )
{
struct FilmInstData *fid = (struct FilmInstData *)INST_DATA( cl, o );
struct BitMapHeader *bmh;
ULONG modeid;
ULONG *cregs;
ULONG numcolors;
ULONG samplelength,
period;
ULONG startframe = 0UL,
numframes = 0UL,
framestep = 1UL;
if( GetDTAttrs( o, PDTA_BitMapHeader, (&bmh),
ADTA_ModeID, (&modeid),
ADTA_CRegs, (&cregs),
ADTA_NumColors, (&numcolors),
ADTA_SampleLength, (&samplelength),
ADTA_Period, (&period),
ADTA_Frame, (&startframe),
ADTA_Frames, (&numframes),
TAG_DONE ) == 8UL )
{
struct TagItem *tstate,
*ti;
numframes -= startframe;
tstate = dtw -> dtw_AttrList;
while( ti = NextTagItem( (&tstate) ) )
{
switch( ti -> ti_Tag )
{
case ADTA_Frame: startframe = ti -> ti_Data; break;
case ADTA_Frames: numframes = ti -> ti_Data; break;
case ADTA_FrameIncrement: framestep = ti -> ti_Data; break;
}
}
if( framestep == 0UL ) framestep = 1UL;
if( samplelength && period )
{
struct IFFHandle *iff;
struct VoiceHeader vh;
/* Set up voice header */
vh . vh_OneShotHiSamples = samplelength;
vh . vh_RepeatHiSamples = 0UL;
vh . vh_SamplesPerHiCycle = 0UL;
vh . vh_SamplesPerSec = ((SysBase -> ex_EClockFrequency) * 5UL) / period;
vh . vh_Octaves = 1UL;
vh . vh_Compression = CMP_NONE;
vh . vh_Volume = 0x10000UL; /* maximum volume */
verbose_printf( cb, fid, "saving iff film %lu %lu %lu\n", startframe, numframes, framestep );
if( iff = CreateDOSIFFHandle( cb, (dtw -> dtw_FileHandle) ) )
{
if( !(error = StartIFFFilm( cb, fid, iff, bmh, modeid, cregs, numcolors, (&vh) )) )
{
struct adtFrame alf;
ULONG timestamp,
*cmap_cregs = NULL;
/* Start scan through animation */
for( timestamp = startframe ; numframes > 0UL ; timestamp += framestep, numframes-- )
{
/* On error break */
if( error )
{
break;
}
/* Check for CTRL_C signal... */
if( SetSignal( 0UL, 0UL ) & SIGBREAKF_CTRL_C )
{
error = ERROR_BREAK;
break;
}
/* reset method msg */
memset( (void *)(&alf), 0, sizeof( struct adtFrame ) );
/* load frame */
alf . MethodID = ADTM_LOADFRAME;
alf . alf_TimeStamp = timestamp;
alf . alf_Frame = timestamp;
if( DoMethodA( o, (Msg)(&alf) ) == 0UL )
{
error = IoErr();
break;
}
/* print frame contents */
verbose_printf( cb, fid, "frame: timestamp %lu frame %lu duration %lu bitmap %lx cmap %lx sample %lx len %lu period %lu\n",
timestamp,
(alf . alf_Frame),
(alf . alf_Duration),
(alf . alf_BitMap),
(alf . alf_CMap),
(alf . alf_Sample),
(alf . alf_SampleLength),
(alf . alf_Period) );
if( alf . alf_CMap )
{
if( cmap_cregs = (ULONG *)AllocVec( (((sizeof( ULONG ) * 3UL) + 1UL) * numcolors), MEMF_PUBLIC ) )
{
GetRGB32( (alf . alf_CMap), 0UL, (numcolors - 1UL), cmap_cregs );
}
else
{
verbose_printf( cb, fid, "can't alloc dynamic palette buffer\n" );
error = ERROR_NO_FREE_STORE;
}
}
if( (alf . alf_BitMap) && (alf . alf_Sample) )
{
if( error = WriteIFFFilmCell( cb, iff, bmh, cmap_cregs, numcolors, (alf . alf_BitMap), (alf . alf_Sample), (alf . alf_SampleLength) ) )
{
verbose_printf( cb, fid, "error while writing IFF FILM, aborted\n" );
}
}
if( cmap_cregs )
{
FreeVec( cmap_cregs );
cmap_cregs = NULL;
}
alf . MethodID = ADTM_UNLOADFRAME;
DoMethodA( o, (Msg)(&alf) );
}
EndIFFFilm( cb, fid, iff );
if( error == 0L )
{
retval = 1UL; /* success ! */
}
}
FreeIFF( iff );
}
}
else
{
verbose_printf( cb, fid, "no sound, can't save iff film\n" );
}
}
else
{
verbose_printf( cb, fid, "not enougth attributes\n" );
}
}
/* Error codes below 0 are related to the IFFParse.library functions */
if( error < 0L )
{
/* convert IFFParse error to DOS error */
error = ifferr2doserr[ (-error - 1) ];
}
SetIoErr( error );
return( retval );
}
static
struct IFFHandle *CreateDOSIFFHandle( struct ClassBase *cb, BPTR fh )
{
struct IFFHandle *iff;
if( iff = AllocIFF() )
{
iff -> iff_Stream = (ULONG)fh;
InitIFFasDOS( iff );
}
return( iff );
}
static
LONG StartIFFFilm( struct ClassBase *cb, struct FilmInstData *fid, struct IFFHandle *iff, struct BitMapHeader *bmh, ULONG modeid, ULONG *cregs, ULONG numcolors, struct VoiceHeader *vh )
{
LONG error;
if( !(error = OpenIFF( iff, IFFF_WRITE )) )
{
for( ;; ) /* not a loop, used as a jump table */
{
if( error = PushChunk( iff, ID_FILM, ID_LIST, IFFSIZE_UNKNOWN ) )
break;
/* write ILBM props */
{
if( error = PushChunk( iff, ID_ILBM, ID_PROP, IFFSIZE_UNKNOWN ) )
break;
/* write ILBM BMHD (BitMapHeader) */
{
if( error = PushChunk( iff, 0UL, ID_BMHD, IFFSIZE_UNKNOWN ) )
break;
if( WriteChunkBytes( iff, (APTR)bmh, sizeof( struct BitMapHeader ) ) != sizeof( struct BitMapHeader ) )
{
error = IFFERR_WRITE;
break;
}
if( error = PopChunk( iff ) )
break;
}
/* write ILBM CMAP (global color map) */
if( error = PutILBMCMAP( cb, iff, cregs, numcolors ) )
break;
/* write ILBM CAMG (amiga view mode) */
{
if( error = PushChunk( iff, 0UL, ID_CAMG, IFFSIZE_UNKNOWN ) )
break;
if( WriteChunkBytes( iff, (APTR)(&modeid), sizeof( ULONG ) ) != sizeof( ULONG ) )
{
error = IFFERR_WRITE;
break;
}
if( error = PopChunk( iff ) )
break;
}
if( error = PopChunk( iff ) )
break;
}
/* write 8SVX props */
{
if( error = PushChunk( iff, ID_8SVX, ID_PROP, IFFSIZE_UNKNOWN ) )
break;
{
/* write 8SVX VHDR (VoiceHeader) */
{
if( error = PushChunk( iff, 0UL, ID_VHDR, IFFSIZE_UNKNOWN ) )
break;
if( WriteChunkBytes( iff, (APTR)vh, sizeof( struct VoiceHeader ) ) != sizeof( struct VoiceHeader ) )
{
error = IFFERR_WRITE;
break;
}
if( error = PopChunk( iff ) )
break;
}
}
/* a 8SVX CHAN chunk is missing here (animation.datatype does currently not support stereo sound)
* subclasses like "film.datatype" may create "interleaved samples" to support this, maybe we've to
* add a de-interleave-code here...
*/
if( error = PopChunk( iff ) )
break;
}
break; /* end of jump table */
}
/* All headers written successfully ? */
if( error == 0L )
{
/* Success ! */
return( 0L );
}
CloseIFF( iff );
}
return( error );
}
static
void EndIFFFilm( struct ClassBase *cb, struct FilmInstData *fid, struct IFFHandle *iff )
{
if( iff )
{
LONG error;
if( error = PopChunk( iff ) )
{
verbose_printf( cb, fid, "error while popping IFF FILM LIST %ld\n", error );
}
CloseIFF( iff );
verbose_printf( cb, fid, "IFF FILM sucessfully created\n" );
}
}
static
LONG WriteIFFFilmCell( struct ClassBase *cb, struct IFFHandle *iff, struct BitMapHeader *bmh, ULONG *cregs, ULONG numcolors, struct BitMap *bm, BYTE *sample, ULONG samplelength )
{
LONG error;
for( ;; ) /* not a loop, used as a jump-table */
{
if( error = PushChunk( iff, ID_CELL, ID_CAT, IFFSIZE_UNKNOWN ) )
break;
/* write FORM ILBM */
{
if( error = PushChunk( iff, ID_ILBM, ID_FORM, IFFSIZE_UNKNOWN ) )
break;
/* Palette change */
if( cregs && numcolors )
{
if( error = PutILBMCMAP( cb, iff, cregs, numcolors ) )
break;
}
/* Write ILBM BODY */
if( error = PutILBMBody( cb, iff, bm, bmh ) )
break;
if( error = PopChunk( iff ) )
break;
}
/* write FORM 8SVX */
{
if( error = PushChunk( iff, ID_8SVX, ID_FORM, IFFSIZE_UNKNOWN ) )
break;
/* Write 8SVX BODY */
if( error = Put8SVXBody( cb, iff, sample, samplelength ) )
break;
if( error = PopChunk( iff ) )
break;
}
if( error = PopChunk( iff ) )
break;
break; /* end of jump-table */
}
return( error );
}
static
LONG Put8SVXBody( struct ClassBase *cb, struct IFFHandle *iff, BYTE *sample, ULONG samplelength )
{
LONG error;
/* Write out a BODY chunk header */
if( error = PushChunk( iff, NULL, ID_BODY, IFFSIZE_UNKNOWN ) )
return( error );
if( WriteChunkBytes( iff, sample, samplelength ) != samplelength )
return( IFFERR_WRITE );
/* Finish the chunk */
error = PopChunk( iff );
return( error );
}
/* write ILBM CMAP */
static
LONG PutILBMCMAP( struct ClassBase *cb, struct IFFHandle *iff, ULONG *cregs, ULONG numcolors )
{
long error;
ULONG i;
struct ColorRegister cm;
if( error = PushChunk( iff, 0UL, ID_CMAP, IFFSIZE_UNKNOWN ) )
return( error );
for( i = 0UL ; i < numcolors ; i++ )
{
/* reduce colors from 32 bits to cmap's 8 bit-per-gun */
cm . red = (UBYTE)(cregs[ ((i * 3) + 0) ] >> 24UL);
cm . green = (UBYTE)(cregs[ ((i * 3) + 1) ] >> 24UL);
cm . blue = (UBYTE)(cregs[ ((i * 3) + 2) ] >> 24UL);
/* Write R, B, G bytes */
if( WriteChunkBytes( iff, (APTR)(&cm), 3UL ) != 3UL )
{
return( IFFERR_WRITE );
}
}
return( PopChunk( iff ) );
}
/*****************************************************************************/
/* from IFF example code ("iffp/ilbm.h") */
#define RowBytes( w ) ((((w) + 15) >> 4) << 1)
#define RowBits( w ) ((((w) + 15) >> 4) << 4)
static
LONG PutILBMBody( struct ClassBase *cb, struct IFFHandle *iff, struct BitMap *bitmap, struct BitMapHeader *bmh )
{
LONG error;
LONG rowBytes = bitmap -> BytesPerRow; /* for source modulo only */
LONG FileRowBytes = RowBytes( (bmh -> bmh_Width) ); /* width to write in bytes */
ULONG planeCnt = bmh -> bmh_Depth; /* number of bit planes including mask */
ULONG iPlane,
iRow;
BYTE *planes[ 24 ]; /* array of ptrs to planes & mask */
/* Copy the ptrs to bit & mask planes into local array "planes" */
for( iPlane = 0 ; iPlane < planeCnt; iPlane++ )
planes[ iPlane ] = (BYTE *)(bitmap -> Planes[ iPlane ]);
/* Write out a BODY chunk header */
if( error = PushChunk( iff, 0L, ID_BODY, IFFSIZE_UNKNOWN ) )
return( error );
/* Write out the BODY contents */
for( iRow = bmh -> bmh_Height ; iRow > 0 ; iRow-- )
{
for( iPlane = 0 ; iPlane < planeCnt ; iPlane++ )
{
/* Write next row.*/
if( WriteChunkBytes( iff, planes[ iPlane ], FileRowBytes ) != FileRowBytes )
return( IFFERR_WRITE );
planes[ iPlane ] += rowBytes; /* Possibly skipping unused bytes */
}
}
/* Finish the chunk */
error = PopChunk( iff );
return( error );
}