// Graphics Importer and Exporter Samples
// This example uses graphics importers to draw an image
// then demonstrates adding a filter effect to the image and
// using a Graphics Exporter component to export the image as
// a PNG image.
// Originally written by Sam Bushell for QuickTime "Live" '99
// WWDC 2000 Introduction to QuickTime

#include "MacShell.h"

//----------
static pascal Boolean theRequestSettingsFilter( DialogRef inDialog, EventRecord *ioEvent, short *outItemHit, void *inDataPtr );
static pascal Boolean theRequestSettingsFilter( DialogRef inDialog, EventRecord *ioEvent, short *outItemHit, void *inDataPtr )
{
#pragma unused (outItemHit)

	WindowRef		eventWindow;
	Boolean			result = false;
	PixMapHandle	hPixMap = GetGWorldPixMap(static_cast<GWorldPtr>(inDataPtr)); // static_cast for GCC

	if (ioEvent->what == updateEvt) {
		eventWindow = (WindowRef)(ioEvent->message);
		if (eventWindow != GetDialogWindow( inDialog )) {
			// blit the image to the screen	
			CopyBits(GetPortBitMapForCopyBits(static_cast<CGrafPtr>(inDataPtr)),
					 GetPortBitMapForCopyBits(GetWindowPort(eventWindow)),
			 		 &(*hPixMap)->bounds,
			 		 &(*hPixMap)->bounds,
					 ditherCopy,
			 		 NULL);
			 result = true;
		}
	}

	return result;
}

void FilterExport( void )
{
	OSErr err = noErr;
	Handle hOpenTypeList = NewHandle(0);
	long  numTypes = 0;
	FSSpec	theFSSpec;
	Boolean isSelected, isReplacing;
	GraphicsImportComponent importer = 0;
	Rect naturalBounds, windowBounds;
	WindowPtr window = NULL;
	GWorldPtr sourceGWorld = NULL;
	ImageDescriptionHandle offscreenDesc = NULL;
	GWorldPtr destGWorld = NULL;
	MatrixRecord matrix;
	ImageSequence imageSequence = 0;
	QTAtomContainer videoFilter = 0;
	QTAtomContainer videoFilterList = 0;
	ImageDescriptionHandle effectDesc = NULL;
	ImageSequenceDataSource effectDataSource = 0;
	QTParameterDialog dialog = NULL;
	QTParamPreviewRecord previewParameter = {0, NULL};	// sourceID, picHandle
	long aLong;
	QTAtom effectTypeAtom;
	OSType effectType;
	GraphicsExportComponent exporter = 0;
	
	BuildGraphicsImporterValidFileTypes( hOpenTypeList, &numTypes );
	HLock( hOpenTypeList );

	err = GetOneFileWithPreview(numTypes, (OSTypePtr)*hOpenTypeList, &theFSSpec, NULL);
	DisposeHandle( hOpenTypeList );
	if ( err ) return;
	
	// locate and open a graphics importer component
	err = GetGraphicsImporterForFile( &theFSSpec, &importer );

	// get the native size of the image associated with the importer
	err = GraphicsImportGetNaturalBounds( importer, &naturalBounds );
	windowBounds = naturalBounds;
	OffsetRect( &windowBounds, 10, 45 );
	window = NewCWindow( NULL, &windowBounds, "\pFilter Export ", true, documentProc, (WindowPtr)-1, true, 0);
	SetPortWindowPort( window );
	
	// draw the image into an offscreen GWorld
	// create the offscreen
	err = QTNewGWorld( &sourceGWorld,			// returned GWorld
					   k32ARGBPixelFormat,		// pixel format 
					   &naturalBounds,			// bounds 
					   NULL,					// cTable 	
					   NULL, 					// GDevice
					   kICMTempThenAppMemory );	// flags
	LockPixels( GetGWorldPixMap( sourceGWorld ) );
	
	// set the graphics port for drawing
	err = GraphicsImportSetGWorld( importer, sourceGWorld, NULL );
	
	// draw the image to the offscreen
	err = GraphicsImportDraw( importer );

	// blit the offscreen to the window
	CopyBits(GetPortBitMapForCopyBits(sourceGWorld),
			 GetPortBitMapForCopyBits(GetWindowPort(window)),
			 &naturalBounds,
			 &naturalBounds,
			 ditherCopy,
			 NULL);
	
	pause();

	// allocate a second offscreen GWorld to draw the filtered image into
	err = QTNewGWorld( &destGWorld, k32ARGBPixelFormat, &naturalBounds, NULL, NULL, kICMTempThenAppMemory );
	LockPixels( GetGWorldPixMap( destGWorld ) );

	// ask for a list of all the single source effects (ie, filters)
	// this function returns a QTAtomContainer holding a list of the currently installed effects components
	err = QTGetEffectsList( &videoFilterList,	// QTAtomContainer holding a list
							1,					// min number of sources that an effect must have to be added to the list
							1,					// max number of sources that an effect can have to be added to the list
							0 );				// getOptions 0 for all effects except 'none' effect

	// prompt for the user to select a filter
	// create a new atom container to build the effect
	err = QTNewAtomContainer( &videoFilter );
	
	// create the effect dialog box; choose an effect from the list of effects passed to the function
	// standard dialog includes a preview of the effect
	err = QTCreateStandardParameterDialog( videoFilterList,	// list of effects
										   videoFilter,	    // on return, this atom container holds an effect description for the effect selected 
			                               pdOptionsCollectOneValue | // options to control the behavior of the dialog
			                               pdOptionsModalDialogBox,
			                               &dialog );		// returns a reference to the dialog box
	
	// set up a preview for the source
	// creates a new QuickDraw picture handle containing the image currently in use by the graphics import component
	err = GraphicsImportGetAsPicture( importer, &previewParameter.sourcePicture );
	previewParameter.sourceID = 1;
	
	// change some of the default behaviors of the standard parameter dialog box.
	QTStandardParameterDialogDoAction( dialog,						// dialog box reference
									   pdActionSetPreviewPicture,	// action to perform
									   &previewParameter );			// the (optional) parameter to the action
	
	// show the dialog and run it							   	
	// passing a NULL event here causes QuickTime to take over until the dialog is dismissed
	err = QTIsStandardParameterDialogEvent( NULL, dialog );
	QTDismissStandardParameterDialog( dialog );

	// codecParameterDialogConfirm means OK was hit, userCanceledErr means Cancel was hit.
	if( codecParameterDialogConfirm == err )
		err = noErr;
	else goto bail;

	// parse the effect sample to find out what kind of effect it was
	effectTypeAtom = QTFindChildByIndex( videoFilter,			// atom container
										kParentAtomIsContainer,	// parent atom specifier
										kParameterWhatName,		// type of atom to be retrieved
										1,						// index
										NULL );					// atomID; NULL to ignore
	
	// copy the effect type from the effectType atom -- ie. Blur effect is type 'blur'									
	err = QTCopyAtomDataToPtr( videoFilter,			// atom container containing the leaf atom
							   effectTypeAtom,		// leaf atom who's data will be copied
							   false,				// copy fewer bytes than the number of bytes specified by the size parameter?
							   sizeof(effectType), 	// size
							   &effectType,			// target buffer pointer
							   NULL );				// returned actual size; NULL to ignore
	
	// cross platform friendly						   
	effectType = EndianU32_BtoN(effectType);

	// add a source label to the effect sample
	aLong = EndianU32_BtoN('srca');
	err = QTInsertChild( videoFilter,				// specifies the atom container that contains the parent atom
						 kParentAtomIsContainer,	// specifies the parent atom within the atom container
						 kEffectSourceName,			// type of the new atom to be inserted
						 1,							// id of new atom
						 0, 						// index -- 0 will insert at the end of the list
						 sizeof(aLong),				// data size
						 &aLong,					// the data
						 NULL );					// ptr to newly created atom -- NULL to ignore
	HLockHi(videoFilter);

	// make an image description for the effect sample and set its size
	err = MakeImageDescriptionForEffect( effectType,		// four-character code identifying the type of effect to make an image description for
										 &effectDesc );		// returned handle of correctly filled-out image description for the selected effect type
	(*effectDesc)->width = naturalBounds.right;
	(*effectDesc)->height = naturalBounds.bottom;

	// build a decompression sequence for the effect
	SetIdentityMatrix( &matrix );
	err = DecompressSequenceBeginS( &imageSequence,				// sequence id
									effectDesc,					// description
									*videoFilter,				// data
									GetHandleSize(videoFilter), // size of the data buffer
									destGWorld,					// graphics port for the destination image
									NULL,						// graphics device handle
									NULL,						// rect 
									&matrix,					// matrix to use					
									ditherCopy,					// graphics mode
									NULL,						// mask
									0,							// flags
									codecNormalQuality,			// accuracy setting
									NULL );						// codec

	// the offscreen gworld is a data source for the video effect
	err = MakeImageDescriptionForPixMap( GetGWorldPixMap(sourceGWorld), &offscreenDesc );

	// create a new data source for the sequence
	err = CDSequenceNewDataSource( imageSequence,			// sequence identifier returned by the DecompressSequenceBeginS()
								   &effectDataSource,		// returned data source identifier
								   'srca',					// four-character code describing how the input will be used
								   1,						// source input number
								   (Handle)offscreenDesc,	// input data description (ImageDescriptionHandle)
								   NULL,					// transfer procedure
								   0 );						// refCon
	
	// sets data in a new frame to a specific data source
	err = CDSequenceSetSourceData( effectDataSource,		// data source identifier 
								   GetPixBaseAddr(GetGWorldPixMap(sourceGWorld)),	// pointer to data
								   (**offscreenDesc).dataSize );					// data size
	
	// render the effect to the second offscreen
	err = DecompressSequenceFrameS( imageSequence,				// sequence identifier
									*videoFilter,				// source data  
									GetHandleSize(videoFilter),	// data size
									0,							// in flags
									NULL,						// out flags
									NULL );						// async completion function; NULL for synchronous
	
	// blit the now filtered image to the screen		
	CopyBits(GetPortBitMapForCopyBits(destGWorld),
			 GetPortBitMapForCopyBits(GetWindowPort(window)),
			 &naturalBounds,
			 &naturalBounds,
			 ditherCopy,
			 NULL);

	pause();

	// write the filtered image out as a PNG file.
	err = PutFile( "\pSave the filtered image:", "\pfiltered.png", &theFSSpec, &isSelected, &isReplacing);
	if (err) goto bail;

	// find and open the PNG Graphics Exporter component
	err = OpenADefaultComponent( GraphicsExporterComponentType,	// component type
								 kQTFileTypePNG,				// component subType
								 &exporter );					// returned component instance
	
	// set the input for the graphics exporter
	err = GraphicsExportSetInputGWorld( exporter,		// component instance
										destGWorld );	// input source
	
	// set the destination for the export								
	err = GraphicsExportSetOutputFile( exporter,
									   &theFSSpec );	// export destination
	
	// the depth to use for the exported image
	err = GraphicsExportSetDepth( exporter,
								  k24RGBPixelFormat );	// depth
	
	// do the export
	err = GraphicsExportDoExport( exporter,
								  NULL );		// actual size written; NULL to ignore
	
	// blit the image to the screen	
	CopyBits(GetPortBitMapForCopyBits(destGWorld),
			 GetPortBitMapForCopyBits(GetWindowPort(window)),
			 &naturalBounds,
			 &naturalBounds,
			 ditherCopy,
			 NULL);
	
	CloseComponent ( exporter );
	
	pause();
	
	// write out the image again as a .jpg using the settings dialog
	OpenADefaultComponent( GraphicsExporterComponentType, kQTFileTypeJPEG, &exporter );
	
	// does the export component supports the exporter setting dialog?
	if ( CallComponentCanDo( exporter , kGraphicsExportRequestSettingsSelect )) {
	
		// set the input for the graphics exporter
		err = GraphicsExportSetInputGWorld( exporter,		// component instance
											destGWorld );	// input source
		
		// get the settings using the settings dialog
		err = GraphicsExportRequestSettings( exporter,
									   		 NewModalFilterYDUPP( theRequestSettingsFilter ),
									   		 destGWorld );
									   		 
		err = PutFile( "\pSave the filtered image:", "\pfiltered.jpg", &theFSSpec, &isSelected, &isReplacing);
		if (err) goto bail;		

		// set the destination for the export								
		err = GraphicsExportSetOutputFile( exporter,
									   	   &theFSSpec );	// export destination		
		// do the export
		err = GraphicsExportDoExport( exporter,
								  	  NULL );				// actual size written; NULL to ignore
	}
	
	// blit the image to the screen	
	CopyBits(GetPortBitMapForCopyBits(destGWorld),
			 GetPortBitMapForCopyBits(GetWindowPort(window)),
			 &naturalBounds,
			 &naturalBounds,
			 ditherCopy,
			 NULL);	
bail:
	// clean up
	if ( imageSequence ) 	CDSequenceEnd( imageSequence );
	if ( videoFilter ) 		QTDisposeAtomContainer( videoFilter );
	if ( videoFilterList ) 	QTDisposeAtomContainer( videoFilterList );
	if ( offscreenDesc)		DisposeHandle( (Handle)offscreenDesc );
	if ( effectDesc )		DisposeHandle( (Handle)effectDesc );
	if ( importer )			CloseComponent( importer );
	if ( exporter )			CloseComponent( exporter );
	if ( sourceGWorld )		DisposeGWorld( sourceGWorld );
	if ( destGWorld )		DisposeGWorld( destGWorld );
	if ( previewParameter.sourcePicture ) KillPicture( previewParameter.sourcePicture );
}
