=====================================
File input/output and the I/O manager
=====================================

Introduction
============

In HOOPS/MVO, the HIOManager manages all file input and output modules. A variety of input and output handlers come prebuilt which allows developers to import and export widely used :doc:`file formats </general/supported_file_formats>` immediately.

To import a file, simply call ``HBaseModel::Read``. This method automatically asks the ``HIOManager`` for the correct HInputHandler. The ``HIOManager`` selects input handlers based primarily on the input file's extension.  While information is loaded into the HOOPS database, you can query the ``HInputHandler`` class for :ref:`progress information <prog_guide/mvo/04_hio_manager:I/O progress and error reporting>`. You can also check for errors and generate a log file.

HOOPS/MVO also provides the ability to export to an extensive number of :doc:`file formats </general/supported_file_formats>`. To export data to a file, use ``HBaseModel::Write``. Like the import process, the export process begins with querying the ``HIOManager`` for the appropriate ``HOutputHandler``. The output handlers, then, write model and/or scene information out to a file. In addition to saving to a file, HOOPS/MVO also allows you to :ref:`print to hardcopy <prog_guide/mvo/04_hio_manager:Printing to hardcopy>`.

The following sample code shows how to create a simple program with HOOPS/MVO to read in an HSF file and write that information into a PLY file::

	HDB * my_db = new HDB();
	my_db->Init();

	HBaseModel * my_model = new HBaseModel(my_db);
	my_model->Init();

	HBaseView * my_view = new HBaseView(my_model, NULL, NULL, NULL, NULL, NULL);
	my_view->Init();

	// write an HSF
	my_model->Read("MyModel.hsf");

	// write a PLY file
	my_model->Write("MyModel.ply");


Importing data
==============

On any given system, you can query the ``HIOManager`` to find what input formats are supported by calling ``HIOManager::GetInputHandlerTypes``. This method returns a semicolon delimited list of all extensions registered with available HInputHandlers. You can parse this list to create a file type list for input file dialogs.  Input handlers not only report the type of file extensions they support but also the type of input style the work with.  You can find the input style a handler supports by calling ``HInputHandler::GetInputStyle``. It will return either ``HInputHandlerStyleModel``, indicating it imports 3D model information, or ``HInputHandlerStyleImage``, specifying that it imports image data.

When importing files, some formats have ``HInputHandlerOptions`` available to determine what and how information is brought into HOOPS. For some file formats, the options may be limited to one or two settings like ``HInputHandlerOptions::m_pImageName`` for image file import. On the other hand, you may have a multitude of options when importing third party format like :doc:`RealDWG </prog_guide/hio/HIO_RealDWGIntegration>`.

The following sample code shows how you can import an image file into HOOPS and use the ``InputHandlerOptions::m_pImageName`` to give it a name::

	HInputHandlerOptions ImageOptions;
	ImageOptions.m_pImageName = "OakTexture";

	my_model->Read("oakply.jpg", my_view, true, &ImageOptions);


Exporting data
==============

To find out what file formats are available for export, you can call ``HIOManager::GetOutputHandlerTypes``. You can use the semicolon delimited list of registered extensions to create a file list for save file dialogs.

HOOPS/MVO makes a distinction between the type of file an output handler can export but also what kind of data it can export. For instance, if you want to export OBJ, a 3D format, the ``HIOManager::GetOutputHandler`` method will return a pointer to an instance of ``HIOUtilityObj``. Using the pointer to ``HIOUtilityObj``, ``HBaseModel::Write`` queries the handler for the output style via ``HOutputHandler::GetOutputStyle`` to determine what type of data the handler can export. In this case, ``HIOUtilityObj`` returns ``HOutputHandlerStyleModel`` since OBJ is a geometry definition file format with little or no support for scene information like camera angle or lighting. Using this information, ``HBaseModel::Write`` calls ``HIOUtilityObj::FileOutputByKey`` passing the correct information. ``HIOUtilityObj::FileOutputByKey`` takes three parameters: a file name, a HOOPS database key and a pointer to an instance of ``HOutputHandlerOptions``  Because ``HBaseModel::Write`` knows the output style is ``HOutputHandlerStyleModel``, it passes the model key for the second parameter. ``HOutputHandlerOptions``, the third parameter, is not a parameter passed into ``HBaseModel::Write``. Instead, ``HBaseModel::Write`` takes in the parameters height and width packaging them into an instance of ``HOutputHandlerOptions``.  Then it calls ``HBaseModel::WriteWithOptions``.


HOutputHandlerOptions
=====================

For more control over the export process, you can use ``HBaseModel::WriteWithOptions`` passing your own instance of ``HOutputHandlerOptions``. This class has a list of options which lets the output handler know in more detail how to export your data. ``HOutputHandlerOptions`` is most useful when exporting to handlers that report a ``HOutputHandlerStyleImage`` or ``HOutputHandlerStyleHardcopy`` output style.  For these handlers, you can set window color, dimensions and whether the view window should match the size of the output using ``HOutputHandlerOptions::WindowColor``, ``HOutputHandlerOptions::WindowHeight``, ``HOutputHandlerOptions::WindowWidth`` and ``HOutputHandlerOptions::UseSubscreen``, respectively. The following sample code shows how to export a model from the HOOPS database to a 640x480 image file::

	#include <hc.h>
	#include <stdio.h>
	#include <stdlib.h>
	#include <HDB.h>
	#include <HBaseView.h>
	#include <HBaseModel.h>
	#include <HIOManager.h>

	int main(int argc, char * argv[])
	{
		HDB gHDB;
		gHDB.Init();

		HBaseModel model;
		model.Init();
		model.Read("Bicycle.hsf");

		HBaseView view(&model, 0, "printf", "null+1");
		view.Init();
		view.FitWorld();

		/* Write a 640x480 image, with Image DPI = 1
		   Note that any pt-sized text depends on the Image DPI, so this example would
		   result in pt-sized text not being visible.  */
		HOutputHandlerOptions options;
		options.WindowWidth(640);
		options.WindowHeight(480);
		options.ImageDpi(1);
		
		model.WriteWithOptions("bicycle1.png", &view, &options);
		
		/* Write a ~640 x ~480 image, but with Image DPI = 100
		   Now, any existing pt-sized text will get drawn at 72 DPI and look correct.  */
		HOutputHandlerOptions options;
		options.WindowWidth(640/72.0);
		options.WindowHeight(480/72.0);
		options.ImageDpi(72); 
		
		model.WriteWithOptions("bicycle2.png", &view, &options);
		
		return EXIT_SUCCESS;
	}


Printing to hardcopy
====================

In addition to supporting many 2D and 3D formats, HOOPS/MVO also offers the ability to print to hardcopy formats such as PostScript and 2D-PDF via prebuilt output handlers. For instance, the output handler associated with PostScript is ``HIOUtilityPostscript``. When the ``HIOUtilityPostscript::GetOutputStyle`` is called, it returns ``HOutputHandlerStyleHardcopy`` indicating that it can export to hardcopy. To begin the printing process, call ``HIOUtilityPostscript::FileOutputByKey``.

``HIOUtilityPostscript::FileOutputByKey`` takes three parameters: the filename, the key to the output data and a pointer to an instance of ``HOutputHandlerOptions``. For printing, the options of note are ``HOutputHandlerOptions::PaperDpi``, ``HOutputHandlerOptions::PaperWidth`` and ``HOutputHandlerOptions::PaperHeight``. You can also determine if you want to include the window color in the printing using the ``HOutputHandlerOptions::UseWindowColor`` option.

The following sample shows how we can modify the previous code sample to print to a postscript file that is 8.5 x 11 inches, with 100 DPI::

	/*initial database*/
    HDB gHDB;
	gHDB.Init();

	/*create a model*/
    HBaseModel model;
    model.Init();
    model.Read("Bicycle.hsf");

	/*create a view*/
    HBaseView view(&model, 0, "printf", "null+1");
    view.Init();
    view.FitWorld();

    HOutputHandlerOptions options;

    /* Write a postscript file that 8.5x11 inches with 100 DPI. */   	
	options.PaperWidth(8.5);
    options.PaperHeight(11);
    options.PaperDpi(100);
    model.WriteWithOptions("bicycle.ps", &view, &options);

The prebuilt output handlers with output style ``HOutputHandlerStyleHardcopy`` use ``HUtility::TwoPassPrint`` to generate the image for the printer driver.  The two pass printing process is effective way to produce high quality images.  First, ``HUtility::TwoPassPrint`` creates a new instance under the printer driver path passed as the first parameter.  Then, in the first pass, ``HUtility::TwoPassPrint`` takes the second parameter, the scene key,and creates an image with only faces data -- excluding vector data like markers, lines and edges.  This image is then set as the window background.  In the second pass, hidden line rendering is turned on, drawing vector information into the scene.  After the second pass is complete, Update is called on the display and the actual hardcopy is created.

.. only:: standard

	HOOPS/MVO has out-of-box ability to export to 2D PDF files. (To export to a 3D PDF, 
	you must use the <a href="http://www.techsoft3d.com/products/hoops-toolkits/hoops-publish/">HOOPS Publish</a> component, and review the <a href="../hio/HIO_Publish.html">HOOPS Publish HIO documentation</a>).  The following sample code shows how to export a model out to a 2D PDF file::

		/* Write a 2D PDF file whose dimensions are 8.5x11 inches and with 100 DPI. */
		HOutputHandlerOptions pdfoptions;
		pdfoptions.PaperWidth(8.5);
		pdfoptions.PaperHeight(11);
		pdfoptions.PaperDpi(100);
		model.WriteWithOptions("bicycle.pdf", &view, &pdfoptions);


Printing to scale
-----------------

It may be desirable to generate a hardcopy that is 'to scale'. For example, if you have a 3D model of a ruler with units of inches, then enabling 'printing to scale' would result in a 1-inch section of the ruler taking up 1-inch on the printed page.  This capability is enabled by setting ``HOutputHandlerOptions::PrintToScale()`` to specify a scaling ratio which indicates how many HOOPS units represent a centimeter. Refer to the  Reference Manual entry for more detailed information.


Image I/O
=========

HOOPS supports both the import and export of all the standard image formats viathe integration of widely used Image SDKs. For image I/O, HOOPS uses ImageMagick, which is distributed under a GPL-style license. Integration with this toolkit is done in the HIOUtilityMagick header and implementation files which are included in the source directory of the HOOPS/MVO project.

Additionally, a pre-built version of the HOOPS/MVO library which statically links the ImageMagick libraries is included in the hoops_mvo directory. Developers who want to use this should link the hoops_mvo_mgk library into their application instead of the standard hoops_mvo library. If you do use the ImageMagick variant of the MVO library then you will automatically have full import/export support of the standard raster formats. 


Using the HIO plug-in architecture
==================================

If you want to import/export data to/from third party formats, HOOPS provides the HIO components for seamless third party integration.  Under the HIO Plug-in architecture, there are HIO components for common formats like :doc:`RealDWG </prog_guide/hio/HIO_RealDWGIntegration>` and :doc:`DGN </prog_guide/hio/HIO_DGNIntegration>` which are loaded dynamically by HOOPS/MVO during start-up. No recompiling is required to use any or all of the HIO components. Just place the HIO components in your application's working directory. HOOPS will automatically load in the necessary libraries and register the appropriate I/O handlers. To learn more about the HIO components and integration with third party libraries, please see the :doc:`HOOPS/HIO Technical Overview </tech_overview/mvo_technical_overview>`.


I/O progress and error reporting
================================

During the data loading process, any class can access the progress information provided by ``HInputHandler`` either via polling or subscribing to the events via a callback. With the polling method, for instance, you can call ``HInputHandler::GetInputPercentProgress`` throughout the loading process to find how much data has been read. You can also find out what the ``HInputHandler`` is currently doing by calling ``HInputHandler::GetInputProgress``. If you want to create a log file, use the ``HInputHandler::GetInputInformation``. This method return the messages posted during the loading process one at a time in order of oldest to newest. When there are no more messages, it will return null.

If you prefer not to poll for information, you can set a callback for the input information, progress and percentage complete. To set a callback from percentage progress, use the method ``HInputHandler::SetInputPercentProgressNoticeCallback`` which expects a pointer to an instance of the ``HIOPercentNoticeCallback`` class. Since ``HIOPercentNoticeCallback`` is an abstract class, you need to create your own subclass and implement the pure virtual method ``HIOPercentNoticeCallback::Notice``. The ``Notice`` method is called when the percent progress is updated in the ``HInputHandler``. The ``HInputHandler::SetInputInformationNoticeCallback`` and ``HInputHandler::SetInputProgressNoticeCallback`` methods allow you to set callbacks for input information and progress, respectively.  They both expect a pointer to an instance of the ``HIONoticeCallback`` class as a parameter.  Since ``HIONoticeCallback`` is also an abstract class, you need to create a subclass and implement the pure virtual method ``HIONoticeCallback::Notice``.

In the sample code below, we see how a callback class is created and then is registered to an input handler to receive progress information::

	class MyNoticeCallback:public HIONoticeCallback, public HIOPercentNoticeCallback 
	{
	public:
			...
			void Notice(char const * msg);
			void Notice(float percent);
			...
	}

	public void MyNoticeCallback::Notice(char const * msg)
	{
		WriteLogFile(msg);
	}

	public void MyNoticeCallback::Notice(float percent)
	{
		char tmp[100];
		sprintf(tmp,"Loading File ... %d%% ",(int)(notice*100));
		SetWindowText(tmp);
	}

	.
	.
	.
	void MyBaseView::ReadHMF(char * pFileName) 
	{
		...
		HInputHandler * hmf_handler = HDB::GetHIOManager()->GetInputHandler("hmf");
		if(hmf_handler) 
		{
			MyNoticeCallback notice();
			hmf_handler->SetInputProgressNoticeCallback(notice);
			hmf_handler->SetInputPercentProgressNoticeCallback(notice);
			hmf_handler->SetInputInformationNoticeCallback(notice);
			HFileInputResult result = hmf_handler->FileInputByKey(pfilename, this->GetSceneKey(), NULL);
			
			//NULL out the progress callbacks when we are done with them.
			hmf_handler->SetInputProgressNoticeCallback(NULL);
			hmf_handler->SetInputPercentProgressNoticeCallback(NULL);
			hmf_handler->SetInputInformationNoticeCallback(NULL);
			...
		}
	}

The progress and error reporting interface in the ``HOutputHandler`` class is analagous to the ``HInputHandler`` interface.


Custom I/O handlers
===================

Developers who want their custom input/output handlers can do so by creating a handler class derived from the ``HInputHandler`` and/or ``HOutputHandler`` classes.  This class must implement the ``HInputHandler::FileInputByKey`` and ``HOutputHandler::FileOutputByKey`` methods. Additionally, it must also implement the ``HOutputHandler::RegisterOutputHandlerTypes`` and/or ``HInputHandler::RegisterInputHandlerTypes`` methods. These methods are used by the ``HIOManager`` to determine which handler should be used to import/export a specific filetype.
 
Finally, the I/O handlers deal with two distinct data types. The developer specifies the datatype it was designed to import/export by implementing the ``HInputHandler::GetInputStyle`` and/or ``HOutputHandler::GetOutputStyle`` methods. If the input handler is of type *model*, then the data will either be read into a segment whose key is supplied by the developer. For output handlers with output style *model*, the model data is written directly out to a file whose handle is supplied by the developer. For output handlers with style *image*, the the contents of the segment tree will be rendered into a HOOPS image segment whose key is provide by the developer and then compressed. For the import case, the raster data will be uncompressed and then placed in the memory referenced by the supplied image key.  


Example:

Say we have a filetype ``myFile`` which we want to support the input/output of within our HOOPS based application. The first thing we would do is derive our class from the ``HInputHandler`` and/or ``HOutputHandler`` method:: 

	class MVO_API HIOUtilityMyFile: public HInputHandler, public HOutputHandler
	{

		public:

			HIOUtilityMyFile()
			{
				SetInputOps(HInputOpFileInputByKey);
				SetOutputOps(HOutputOpFileOutputByKey);
			};

			~HIOUtilityMyFile() {;};

			void RegisterInputHandlerTypes()
			{
				HIORegisterInputType("myFile", this);
			};

			HInputHandlerStyle GetInputStyle() {return HInputHandlerStyleModel;};
			const char * GetInputTypesString() {return "myFile";};

			HFileInputResult FileInputByKey(const char * FileName, HC_KEY key, HInputHandlerOptions * options);

			void RegisterOutputHandlerTypes()
			{
				HIORegisterOutputType("myFile",this);
			};

			const char * GetOutputTypesString() {return "myFile";};

			// You need to set a style to either Model or Image so that we can differentiate 
			// between raster or facet based input.
			HOutputHandlerStyle GetOutputStyle() {return HOutputHandlerStyleModel;};

			HFileOutputResult FileOutputByKey(const char * filename, HC_KEY key, HOutputHandlerOptions * options);

	};

Now, you simply register your new Input and Output handler as the handler for dealing with the *.myFile* extension. Note, we use the ``HIORegisterInputType`` and ``HIORegisterOutput`` type macro which registers the I/O handler with the currently active Input/Output Manager::

	m_pIOUtilityMyFile = new HIOUtilityMyFile();
	HIORegisterInputType("myFile", m_pIOUtilityMyFile);
	HIORegisterOutputType("myFile", m_pIOUtilityMyFile);

Now ``HBaseModel`` will automatically detect and load the correct input handler in its ``Read`` method without you having to make any modification to the ``Read`` method. Here is the code for ``HBaseModel::Read``. Notice how there is no extension specific code::

	HUtility::FindFileNameExtension(FileName, ext);
	HInputHandler * handler = HDB::GetHIOManager()->GetInputHandler(ext);

	// if none found the quit
	if(!handler)
		return InputNotHandled;

	HFileInputResult result;

	if(pHView)
	{
		result = pHView->FileInput(FileName, handler, 0);
	}
	else
	{
		if(handler->GetInputStyle() == HInputHandlerStyleModel)
		{
			result = handler->FileInputByKey(FileName, m_ModelKey, 0);
		}
		else if(handler->GetInputStyle() == HInputHandlerStyleImage)
		{
			HC_KEY image_key;
			result = handler->FileInputToImageKey(FileName, !_key, GetModelKey(), 0);
		}
			else
				result = InputNotHandled;
	}


Implementing progress and error reporting
=========================================

When you create a custom I/O handler, you have the option to provide progress and error reporting during the load and save process for your handler.  

To report progress from an the input handler, call ``HInputHandler::SetStartingInput`` before loading your data.  This method initializes and clears all the members associated with the progress reporting process. Once data loading has started, you can call ``HInputHandler::ReportInputPercentProgress`` passing a float to indicate how much of the loading process has been completed.  If you want to indicate where the input handler is in the loading process, you can call ``HInputHandler::ReportInputProgress`` and pass a string like "Loading Annotations." The ``HInputHandler`` class also has a method for reporting log file information via the ``HInputHandler::ReportInputInformation``. When data loading is complete, call ``HInputHandler::SetFinishedInput`` to indicate the reporting process is done.

For ``HOutputHandler``, the process for implementing progress and log information is analagous to HInputHandler. Before exporting data, call ``HOutputHandler::SetStartingOutput``. During the exporting process, call ``HOutputHandler::ReportOutputPercentProgress`` passing a float to communicate how much of the export process has been performed.  To indicate where the output handler is in the export process, you can call ``HOutputHandler::ReportOutputProgress`` and pass a string like "Writing Metadata."  The ``HOutputHandler`` class also has a method for reporting log file information via the ``HOutputHandler::ReportOutputInformation``. When data export is complete, call ``HOutputHandler::SetFinishedOutput`` to indicate the reporting process has finished.
