/*! \page basic_import Making an Image Import Plugin

\subsection import_overview Overview

This tutorial will guide you to write a simple image importer for Moppi Demopaja.
The image format this plugin imports is the PCX file format. 


\subsection import_first First step

First we need to write the few functions which Demopaja loads from the DLL when the DLL is loaded.
See the \ref new_project for how to set up a new project.


\subsection import_desc Writing a plugin class descriptor

The next step is to write a class descriptor. We will implement a class descriptor for a import plugin. At this point we also need the unique class ID for our class. This class is generated with the class ID generator which comes with the SDK.

The class descriptor of the PCX importer looks like this:

\code
	// The Class ID
	const ClassIdC	CLASS_PCX_IMPORT( 0x74C4BF04, 0xCA444A3D );


	//  PCX importer class descriptor.
	class PCXImportDescC : public ClassDescC
	{
	public:
		PCXImportDescC();
		virtual ~PCXImportDescC();
		virtual void*			create();
		virtual int32			get_classtype() const;
		virtual SuperClassIdC	get_super_class_id() const;
		virtual ClassIdC		get_class_id() const;
		virtual const char*		get_name() const;
		virtual const char*		get_desc() const;
		virtual const char*		get_author_name() const;
		virtual const char*		get_copyright_message() const;
		virtual const char*		get_url() const;
		virtual const char*		get_help_filename() const;
		virtual uint32			get_required_device_driver_count() const;
		virtual const ClassIdC&	get_required_device_driver( PajaTypes::uint32 ui32Idx );
		virtual uint32			get_ext_count() const;
		virtual const char*		get_ext( uint32 ui32Index ) const;
	};

	PCXImportDescC::PCXImportDescC()
	{
		// empty
	}

	PCXImportDescC::~PCXImportDescC()
	{
		// empty
	}
\endcode

The create method is used to create a new instance of the PCX importer class. In the PCX importer class there is a static member method which is used to create a new instance. 

\code
	void*
	PCXImportDescC::create()
	{
		return PCXImportC::create_new();
	}
\endcode

Returns the plugin class type. CLASS_TYPE_FILEIMPORT is returned for importer plugins.

\code
	int32
	PCXImportDescC::get_classtype() const
	{
		return CLASS_TYPE_FILEIMPORT;
	}
\endcode

Returns the super class ID. Since the importer class is derived from the ImageImporterI we return the superclass ID of the ImportableImageI interface.

\code
	SuperClassIdC
	PCXImportDescC::get_super_class_id() const
	{
		return SUPERCLASS_IMAGE;
	}
\endcode

Returns the class ID of the importer class. This class ID has to be unique for every plugin class that is implemented.

\code
	ClassIdC
	PCXImportDescC::get_class_id() const
	{
		return CLASS_PCX_IMPORT;
	}
\endcode

Returns the name of the plugin class. This name should be short but desciptive. This name is shown to the user of the Demopaja.

\code
	const char*
	PCXImportDescC::get_name() const
	{
		return "PCX Image";
	}
\endcode

Returns a short description of the plugin class.

\code
	const char*
	PCXImportDescC::get_desc() const
	{
		return "Importer for PCX images";
	}
\endcode

These methods returns some information about the author of the plugin class, such name, copyright notice and an URL where the plugin/author can be found.

\code
	const char*
	PCXImportDescC::get_author_name() const
	{
		return "Mikko \"memon\" Mononen";
	}

	const char*
	PCXImportDescC::get_copyright_message() const
	{
		return "Copyright (c) 2000 Moppi Productions";
	}

	const char*
	PCXImportDescC::get_url() const
	{
		return "http://www.moppi.inside.org/demopaja/";
	}
\endcode

Returns the file name of the help file. Help files are in HTML format. This plugin has the HTML file stored in
the resources of the DLL. See \ref html_help for more information how to deal with help files.

\code
const char*
PCXImportDescC::get_help_filename() const
{
	return "res://pcxhelp.html";
}
\endcode

These tow methods are used to checks if this plugin class can be created and used within the current device context. Every device the plugin class depends on should be returned here.

\code
	uint32
	PCXImportDescC::get_required_device_driver_count() const
	{
		return 1;
	}
\endcode

\code
	const ClassIdC&
	PCXImportDescC::get_required_device_driver( uint32 ui32Idx )
	{
		return PajaSystem::CLASS_OPENGL_DEVICEDRIVER;
	}
\endcode

Returns the number of file extensions this plugin class uses. For effect plugin classes zero should be returned, but since we are implementing an importer plugin we have to define at least one extension. Here we just return number of the extensions we have defined.

\code
	uint32
	PCXImportDescC::get_ext_count() const
	{
		return 1;
	}
\endcode

Returns an extension string at specified index. We have only one extension and it is "pcx".

\code
	const char*
	PCXImportDescC::get_ext( uint32 ui32Index ) const
	{
		if( ui32Index == 0 )
			return "pcx";
		return 0;
	}
\endcode

Now we should declare one global variable (those people who fear global variables propably can find a different way to do this) which type is the class we just defined. It is important that this variable is global (or at least static) since a pointer to it is returned to the Demopaja via the get_classdesc() function.

\code
PCXImportDescC		g_rPCXImportDesc;
\endcode

\subsection import_final The Import Class

The final phase is to write the importer class itself. All importer plugin classes has to be derived from the ImporterI super class. In the Demopaja SDK there are set of interfaces which all are derived from the generic ImportableI interface.  These interfaces has a set of methods which are common to all importers which are derived from it. For example image is such a generic thing that it can have an interface. The advantage of this is that all the effect plugins needs to only know how to use for example the ImportableImageI interface and they can use all the image file formats that are implemented by the importer plugins.

Here is the class definition of the PCX importer plugin class:

\code
	class PCXImportC : public Import::ImportableImageI
	{
	public:
		static PCXImportC*				create_new();
		virtual Edit::DataBlockI*		create();
		virtual Edit::DataBlockI*		create( Edit::EditableI* pOriginal );
		virtual void					copy( Edit::DataBlockI* pBlock );
		virtual void					restore( Edit::EditableI* pEditable );

		virtual const char*				get_filename();

		virtual bool
		load_file( const char* szName, Import::ImportInterfaceC* pInterface );
		virtual void
		initialize( PajaTypes::uint32 ui32Reason, PajaSystem::DeviceContextC* pContext, PajaSystem::TimeContextC* pTimeContext );

		virtual PluginClass::ClassIdC	get_class_id();
		virtual const char*				get_class_name();

		// The importable image interface.
		virtual PajaTypes::int32		get_width();
		virtual PajaTypes::int32		get_height();
		virtual PajaTypes::int32		get_pitch();
		virtual PajaTypes::int32		get_bpp();
		virtual PajaTypes::uint8*		get_data();

		virtual void
		bind_texture( PajaSystem::DeviceInterfaceI* pInterface,
				PajaTypes::uint32 ui32Properties );

		virtual const char*				get_info();
		virtual PluginClass::ClassIdC	get_default_effect();

		virtual PajaTypes::int32
		get_duration( PajaSystem::TimeContextC* pTimeContext );

		virtual PajaTypes::float32
		get_start_label( PajaSystem::TimeContextC* pTimeContext );
	
		virtual PajaTypes::float32
		get_end_label( PajaSystem::TimeContextC* pTimeContext );
	
		virtual PajaTypes::uint32		save( FileIO::SaveC* pSave );
		virtual PajaTypes::uint32		load( FileIO::LoadC* pLoad );
	
	protected:
		PCXImportC();
		PCXImportC( Edit::EditableI* pOriginal );
		virtual ~PCXImportC();
	
	private:
	
		bool	read_encoded_block( PajaTypes::uint32 ui32Size,
						PajaTypes::uint8* pBuffer, FILE* pStream );
	
		PajaTypes::uint32		m_ui32TextureId;
		PajaTypes::uint8*		m_pData;
		PajaTypes::int32		m_i32Width, m_i32Height;
		PajaTypes::int32		m_i32Bpp;
		std::string			m_sFileName;
	};
\endcode

Here is the implementation:

The default constructor. Set everything to the default values.

\code	
	PCXImportC::PCXImportC() :
		m_pData( 0 ),
		m_i32Width( 0 ),
		m_i32Height( 0 ),
		m_ui32TextureId( 0 )
	{
		// empty
	}
\endcode

The constructor which is used when a clone of this file is made. The most important thing is to call the base class constructor, then set the data to the default values.

\code
	PCXImportC::PCXImportC( EditableI* pOriginal ) :
		ImportableImageI( pOriginal ),
		m_pData( 0 ),
		m_i32Width( 0 ),
		m_i32Height( 0 ),
		m_ui32TextureId( 0 )
	{
		// empty
	}
\endcode

The destructor. Since the data is not duplicated when a clone object is created (the importer class in this case) we don't release the data if the object is a clone. The clone can be detected by the get_original() method of the EditableI interface. If it returns a valid pointer (anything else than NULL ) then the object is a clone and no data should be released.

\code
	PCXImportC::~PCXImportC()
	{
		// Return if this is a clone.
		if( get_original() )
			return;
	
		// Delete creted texture.
		if( m_ui32TextureId )
			glDeleteTextures( 1, &m_ui32TextureId );
	
		delete m_pData;
	}
\endcode

Static member which is used to create a new instance of a plugin class. It is only a recommendation to use this method name. It could be really anything. There just have to be a way to create a new instance of the plugin class.

\code
	PCXImportC*
	PCXImportC::create_new()
	{
		return new PCXImportC;
	}
\endcode

This method is analogous to the default constructor.

\code
	DataBlockI*
	PCXImportC::create()
	{
		return new PCXImportC;
	}
\endcode

This method is analogous to the clone constructor.

\code
	DataBlockI*
	PCXImportC::create( EditableI* pOriginal )
	{
	 	return new PCXImportC( pOriginal );
	}
\endcode

This method is used to copy data from a specified datablock (the datablock is guaranteed to be of the same class as the class which copy() method is called). For importables the copy method is called only when an importable
is reloaded. So, if the plugin has an import dialog where user can choose how the data is interpreted, these settings should copied here.

\code
	void
	PCXImportC::copy( DataBlockI* pBlock )
	{
		// empty
	}
\endcode

This method is used to make shallow copy from a specified editable (the editable is guaranteed to be of the same class as the class which copy() method is called). Only the variables should be copied, no the data they point to. This method is used first to store the data to a clone object and then later on to restore the data from the clone, hence the name.

\code
	void
	PCXImportC::restore( EditableI* pEditable )
	{
		PCXImportC*	pFile = (PCXImportC*)pEditable;
	
		m_ui32TextureId = pFile->m_ui32TextureId;
		m_pData = pFile->m_pData;
		m_i32Width = pFile->m_i32Width;
		m_i32Height = pFile->m_i32Height;
		m_i32Bpp = pFile->m_i32Bpp;
		m_sFileName = pFile->m_sFileName;
	}
\endcode

Returns the name of the file attached to this importable. This name is shown in the GUI (first the path is stripped off) and is also used to reload the file. So be sure to store the original file name.

\code
	const char*
	PCXImportC::get_filename()
	{
		return m_sFileName.c_str();
	}
\endcode

This method is used by the PCX loader. It reads a RLE encoded block from the file.

\code
	bool
	PCXImportC::read_encoded_block( uint32 ui32Size, uint8* pBuffer, FILE* pStream )
	{
		uint32	ui32Count;
		uint32	ui32Val;
		uint8*	pDest = pBuffer;
	
		for( uint32 i = 0; i < ui32Size; ) {
	
			ui32Val = fgetc( pStream );
			ui32Count = 1;
	
			if( feof( pStream ) )
				return true;

			if( (ui32Val & 0xC0) == 0xC0 ) {
				ui32Count = ui32Val & 0x3F;
				ui32Val = fgetc( pStream );
				if( feof( pStream ) )
					return false;
			}
	
			for( uint32 j = 0; j < ui32Count; j++ )
				*pDest++ = (uint8)ui32Val;
	
			i += ui32Count;
		}
	
		return true;
	}
\endcode

Loads the file. It is convenient to store the file name in this method. The import interface is not used in this importer. Most the code is PCX file format specific.

\code
	bool
	PCXImportC::load_file( const char* szName, ImportInterfaceC* pInterface )
	{
		FILE*		pStream;
		PCXHeaderS	rHeader;
	
		if( (pStream = fopen( szName, "rb" )) == 0 ) {
			return false;
		}
	
		// Read header
		fread( &rHeader, 1, sizeof( rHeader ), pStream );
	
		// We only support BPP images
		if( rHeader.m_ui8BPP != 8 )
			return false;
	
		// Store width and height
		m_i32Width = rHeader.m_ui16BytesPerLine;
		m_i32Height = (rHeader.m_ui16YMax - rHeader.m_ui16YMin) + 1;

		if( rHeader.m_ui8NPlanes == 1 ) {
			// Read 8-bit image
			uint32	ui32Size = m_i32Width * m_i32Height;
			uint8*	pBuffer = new uint8[ui32Size];
	
			// Encode
			if( !read_encoded_block( ui32Size, pBuffer, pStream ) )
				return false;
	
			if( rHeader.m_ui16PalInfo == 2 ) {
				// Greyscale
				m_pData = pBuffer;
				m_i32Bpp = 8;
			}
			else {
				// Read palette.
				uint8	ui8Palette[256 * 3];
				fseek( pStream, SEEK_END, -768 );
				fread( ui8Palette, 256 * 3, 1, pStream );
	
				m_pData = new uint8[m_i32Width * m_i32Height * 3];
				uint8*	pDest = m_pData;
				uint8*	pSrc = pBuffer;
	
				// Convert paletted to RGB
				for( uint32 i = 0; i < m_i32Height; i++ ) {
					for( uint32 j = 0; j < m_i32Width; j++ ) {
						*pDest++ = ui8Palette[(*pSrc) * 3 + 2];
						*pDest++ = ui8Palette[(*pSrc) * 3 + 1];
						*pDest++ = ui8Palette[(*pSrc) * 3 + 0];
						pSrc++;
					}
				}
	
				m_i32Bpp = 24;
				delete [] pBuffer;
			}
	
		}
		else if( rHeader.m_ui8NPlanes == 3 ) {
			// Read 24-bit image.
			uint32	ui32Size = m_i32Width * m_i32Height * 3;
			uint8*	pBuffer = new uint8[ui32Size];
	
			// Encode
			if( !read_encoded_block( ui32Size, pBuffer, pStream ) )
				return false;
	
			m_pData = new uint8[ui32Size];
			uint8*	pDest = m_pData;
	
			// Convert to interleaved RGB
			for( uint32 i = 0; i < m_i32Height; i++ ) {
				for( uint32 j = 0; j < m_i32Width; j++ ) {
					*pDest++ =
					pBuffer[i * m_i32Width * 3 + j];
					*pDest++ =
					pBuffer[i * m_i32Width * 3 + j + m_i32Width];
					*pDest++ =
					pBuffer[i * m_i32Width * 3 + j + m_i32Width * 2];
				}
			}
	
			delete [] pBuffer;
	
			m_i32Bpp = 24;
		}

		fclose( pStream );

		// Store file name.
		m_sFileName = szName;

		return true;
	}
\endcode


Handles the intialisation messages.

\code
	void
	PCXImportC::initialize( uint32 ui32Reason, DeviceContextC* pContext, TimeContextC* pTimeContext )
	{
		if( ui32Reason == INIT_DEVICE_CHANGED ) {
	
			OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
			if( !pDevice )
				return;
	
			if( pDevice->get_state() == DEVICE_STATE_SHUTTINGDOWN ) {
				// Delete textures
				glDeleteTextures( 1, &m_ui32TextureId );
				m_ui32TextureId = 0;
			}	
		}
	}
\endcode

Returns the class ID. This method should return the same information as the method of same name in the class descriptor.

\code
	ClassIdC
	PCXImportC::get_class_id()
	{
		return CLASS_PCX_IMPORT;
	}
\endcode

Returns the name of the class. This method should return the same information as the method of same name in the class descriptor.

\code
	const char*
	PCXImportC::get_class_name()
	{
		return "PCX Image";
	}
\endcode

Returns the width of the image in pixels. This method is from the ImportableImageI interface.

\code
	int32
	PCXImportC::get_width()
	{
		return m_i32Width;
	}
\endcode

Returns the height of the image in pixels. This method is from the ImportableImageI interface.

\code
	int32
	PCXImportC::get_height()
	{
		return m_i32Height;
	}
\endcode

Returns the pitch of the image in bytes. In some cases it is necessary to store the image in the memory so that the length of each row in the data is not same as the width of the image. This method is from the ImportableImageI interface.

\code
	int32
	PCXImportC::get_pitch()
	{
		return m_i32Width * m_i32BPP / 8;
	}
\endcode

Returns the Bits Per Pixel (BPP) of the image. This method is from the ImportableImageI interface.

\code
	int32
	PCXImportC::get_bpp()
	{
		return m_i32Bpp;
	}
\endcode

Returns pointer to the data of the image. This method is from the ImportableImageI interface.

\code
	uint8*
	PCXImportC::get_data()
	{
		return m_pData;
	}
\endcode

Sets the image as current texture on specified device. This method enables the texture only be stored in the graphic card once. In this case this is done via the texture objects in OpenGL. This method is from the ImportableImageI interface.

\code
	void
	PCXImportC::bind_texture( DeviceInterfaceI* pInterface, uint32 ui32Properties )
	{
		if( !pInterface || pInterface->get_class_id() != CLASS_OPENGL_DEVICEDRIVER )
			return;
	
		if( !m_ui32TextureId ) {
	
			glGenTextures( 1, &m_ui32TextureId );
			glBindTexture( GL_TEXTURE_2D, m_ui32TextureId );
			glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
	
			if( m_i32Bpp == 8 ) {
				glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA , m_i32Width,
				m_i32Height, 0, GL_ALPHA , GL_UNSIGNED_BYTE, m_pData );
			}
			else if( m_i32Bpp == 24 ) {
				glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, m_i32Width,
				m_i32Height, 0, GL_RGB, GL_UNSIGNED_BYTE, m_pData );
			}
			else if( m_i32Bpp ==32 ) {
				glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, m_i32Width,
				m_i32Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pData );
			}
		}
		else
			glBindTexture( GL_TEXTURE_2D, m_ui32TextureId );

		if( ui32Properties & IMAGE_LINEAR ) {
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		}
		else {
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
		}
	
		if( ui32Properties & IMAGE_CLAMP ) {
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
		}
		else {
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
		}
	}
\endcode

Returns short info about the importable. In this case the importable is an image, and the image dimension and image type is returned.

\code
	const char*
	PCXImportC::get_info()
	{
		static char	szInfo[256];
		_snprintf( szInfo, 255, "%s %d x %d x %dbit",
			m_i32Bpp == 8 ? "GREY" : "RGB", m_i32Width, m_i32Height, m_i32Bpp );
		return szInfo;
	}
\endcode

Returns the default effect which is able to show this data. This method is used when a new effect is created by drag'n'drop operation.

\code
	ClassIdC
	PCXImportC::get_default_effect()
	{
		return CLASS_SIMPLEIMAGE_EFFECT;
	}
\endcode

Returns the duration of the data. Since the data in this case is a still image return -1, which indicates, that there is no duration.

\code
	int32
	PCXImportC::get_duration( TimeContextC* pTimeContext )
	{
		return -1;
	}
\endcode

Return dummy for labels. The labels are only used if the importable has animation. The start label is the first frame number, and the end label is the last frame number.

\code
	float32
	PCXImportC::get_start_label( TimeContextC* pTimeContext )
	{
		return 0;
	}

	float32
	PCXImportC::get_end_label( TimeContextC* pTimeContext )
	{
		return 0;
	}

\endcode

Save the importable to a Demopaja stream. The data is written in two chunks. The first chunk contains the name of the file and the second cintains the actual data.

\code
	enum PCXImportChunksE {
		CHUNK_PCXIMPORT_BASE =	0x1000,
		CHUNK_PCXIMPORT_DATA =	0x2000,
	};

	const uint32	PCXIMPORT_VERSION = 1;
	
	
	uint32
	PCXImportC::save( SaveC* pSave )
	{
		uint32		ui32Error = IO_OK;
		uint8		ui8Tmp;
		std::string	sStr;
	
		// file base
		pSave->begin_chunk( CHUNK_PCXIMPORT_BASE, PCXIMPORT_VERSION );
			sStr = m_sFileName;
			if( sStr.size() > 255 )
					sStr.resize( 255 );
			ui32Error = pSave->write_str( sStr.c_str() );
		pSave->end_chunk();
	
		// file data
		pSave->begin_chunk( CHUNK_PCXIMPORT_DATA, PCXIMPORT_VERSION );
			ui32Error = pSave->write( &m_i32Width, sizeof( m_i32Width ) );
			ui32Error = pSave->write( &m_i32Height, sizeof( m_i32Height ) );
			ui8Tmp = (uint8)m_i32Bpp;
			ui32Error = pSave->write( &ui8Tmp, sizeof( ui8Tmp ) );
			ui32Error = pSave->write( m_pData,
					m_i32Width * m_i32Height * (m_i32Bpp / 8) );
		pSave->end_chunk();
	
		return ui32Error;
	}
\endcode

Loads the importable from a Demopaja stream.

\code
	uint32
	PCXImportC::load( LoadC* pLoad )
	{
		uint32	ui32Error = IO_OK;
		char	szStr[256];
		uint8	ui8Tmp;

		while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

			switch( pLoad->get_chunk_id() ) {
			case CHUNK_PCXIMPORT_BASE:
				if( pLoad->get_chunk_version() == PCXIMPORT_VERSION ) {
					ui32Error = pLoad->read_str( szStr );
					m_sFileName = szStr;
				}
				break;

			case CHUNK_PCXIMPORT_DATA:
				if( pLoad->get_chunk_version() == PCXIMPORT_VERSION ) {
					// delete old data if any
					delete m_pData;
					// load new data
					ui32Error = pLoad->read( &m_i32Width,
						sizeof( m_i32Width ) );
					ui32Error = pLoad->read( &m_i32Height,
						sizeof( m_i32Height ) );
					ui32Error = pLoad->read( &ui8Tmp,
						sizeof( ui8Tmp ) );
					m_i32Bpp = ui8Tmp;
					m_pData = new uint8[m_i32Width * m_i32Height *
						(m_i32Bpp / 8)];
					ui32Error = pLoad->read( m_pData,
						m_i32Width * m_i32Height * (m_i32Bpp / 8) );
				}
				break;
	
			default:
				assert( 0 );
			}
	
			pLoad->close_chunk();
	
			if( ui32Error != IO_OK && ui32Error != IO_END ) {
				return ui32Error;
			}
		}
	
		return ui32Error;
	}
\endcode

Now we have created an importer plugin which imports PCX images to Demopaja for further use. The source for this plugin can be found in the \c Examples\ImagePlugin directory. That plugin also contains a simple effect which shows the data.

*/