Extending Import & Export

A Tutorial for Expanding Import[] and Export[]

Dale Horton, P.J. Hinton, John Fultz
Wolfram Research, Inc.

Abstract

Mathematica supports the ability to expand the Import and Export commands to add your own data formats.  This presentation will show how this works and demonstrate best practices for authoring converters in Mathematica, C/C++, and Java.  It will also touch on some of the new Import/Export technologies coming in future releases of Mathematica.

Table of contents

Extending Import/Export with RegisterConverter	
J/Link converters
        Part 1						
        Part 2						
Implementing New Formats in C/C++			
        Format correspondence				
        Template writing					
        Algorithm Implementation			
Special Considerations — Bitmap Export		
Front End As Rendering Engine			
ExportPacket						
ExportPacket Options					
Extending the User Interface				
Extended Samples

Extending Import/Export with RegisterConverter

Formats are added to Import and Export with Experimental`RegisterConverter . It allows you to specify what function is called when Importing or Exporting a certain format.

<< Experimental`

RegisterConverter[Import | Export, "format", function]

Option				Form					Default			Purpose
"Extensions"			{"*.ext" ... }				{ }			Recognized extensions
"Source"			"binary.exe"|"package`"|None		None			What to load during first use
Path				{"dir" ... }				Automatic		Where to find Source
"FunctionChannels"		{FileNames|Streams..}			{FileNames}		Channels supported by function
"TextFormat"			True|False				True			ASCII or Binary format

When you register a converter with a certain format, many processes occur automatically. For example, if you register an Import converter, ImportString is automatically registered as well.

Implementing new formats in Mathematica

First you write the low-level function that does the Import or Export.

MyImport[file_,  
        opts___] := 
Apply[{#5,  #1,  #3} &,  
        ReadList[
          file,  {Number, Character,  Number, Character,  
            Number}], {1}]

Then you install that function into Import or Export.

RegisterConverter[Import, "Date", MyImport]

ImportString["5/15/2001 \n 10/31/2003",  "Date"]

If your function relies on a package, you can load the package automatically by setting the "Source".

RegisterConverter[Import, "Date", 
      MyPackage`MyImport,  "Source" -> "MyPackage`"]

Implementing new formats in J/Link (part1)

There are two ways to write a J/Link application. The first method is to call a Java class, either one you've downloaded or written. Here I took a JPEGImport class, compiled it into a jar file, then put it on the  CLASSPATH. See the J/Link documentation for details.

\(ImportJPEG2[s_String, \ 
        opts___] := 
Module[{data}, 
JLink`InstallJava[]; 
JLink`LoadClass["JPEGImport"]; 
data = JPEGImport`importJPEG[s]; 
Graphics[
          Raster[Reverse[data]/255. , ColorFunction -> RGBColor], \ 
          AspectRatio -> Automatic]
]\)

Implementing new formats in J/Link (part1)

\(RegisterConverter[Import, \ "JPEG2", \ 
      ImportJPEG2, \ 
"Source" -> "JLink`", \ "TextFormat" -> False]\)

Export["tst.jpg",  
        Plot3D[Sin[x y], {x, 0, π},  {y, 0, π},  
          DisplayFunction -> Identity],  "JPEG"];

Show[Import["tst.jpg",  "JPEG2"],  
        ImageSize -> 190];

Implementing new formats in J/Link (part2)

The other method is to call each of the individual Java methods from Mathematica.

\(ImportJPEG3[s_String, \ opts___] := 
JLink`JavaBlock[
        Module[{data, \ w, \ h}, 
JLink`InstallJava[]; 
          JLink`LoadClass["com.sun.image.codec.jpeg.JPEGCodec"]; 
stream\  = \ 
            JLink`JavaNew["java.io.FileInputStream", \ s]; 
          decoder\  = \ JPEGCodec`createJPEGDecoder[stream]; 
          img = decoder@decodeAsBufferedImage[]; {w, h}\  = \ 
            Map[img, \ {getWidth[], getHeight[]}]; 
          pixels\  = \ 
            JLink`MakeJavaObject[Table[0, {w*h}]]; 
pg = 
            JLink`JavaNew["java.awt.image.PixelGrabber", img, 0, 0, w, h, 
              pixels, 0, w]; 
pg@
            grabPixels[]; 
data\  = \ 
            JLink`JavaObjectToExpression[
              pixels]; 
data\  = \ 
            Partition[IntegerDigits[Mod[data, \ 2^32], \ 2^8, \ 3], \ 
              w]; 
Graphics[
            Raster[Reverse[data]/255. , ColorFunction -> RGBColor], \ 
            AspectRatio -> Automatic]
]]\)

Implementing new formats in J/Link (part2)

\(RegisterConverter[Import, \ "JPEG3", \ 
      ImportJPEG3, \ 
"Source" -> "JLink`", \ "TextFormat" -> False]\)

Show[Import["tst.jpg",  "JPEG3"],  
        ImageSize -> 190];

Implementing new formats in C/C++

Fundamentals

Support for calling external C/C++ functions from Import and Export is built into the format registration API that Dale mentioned earlier.

Bridging between Mathematica and C/C++ takes advantage of the kernel's Install function.  Those unfamiliar with this interface should refer to the introductory material on writing installable MathLink programs, which may be found in The Mathematica Book (Fourth Edition), Section 2.12.3 ff.

The steps to defining an Import or Export operation in C/C++ are:

Step 1: Format Correspondence

Spend time trying to envision precisely how a Mathematica expression might be represented in the target format and vice versa.

Some rules of thumb to keep in mind.

Step 2: Template Writing

Handler Function Syntax

As we saw earlier, Experimental`RegisterConverter is used to specify low-level handler that should be called for a given format target.

Handler functions must follow specific calling conventions.

lowLevelExport[filename_String, expression_, options___?OptionQ]

lowLevelImport[filename_String, options___?OptionQ]

Step 2: Template Writing (cont'd)

Design Considerations

Some things to keep in mind when defining a handler function through a MathLink template.

Step 2: Template Writing (cont'd)

Template Example

The following example defines a template for a hypothetical Export operation.

:Evaluate: Begin["System`ConvertersDump`"]

:Begin:
:Function: genericExport
:Pattern: ExportGeneric[fname_String, expr_, opts___?OptionQ]
:Arguments: {fname, expr, {opts}}
:ArgumentTypes: {Manual}
:ReturnType: Manual
:End:

:Evaluate: End[]

The Mathematica handler function is called System`ConvertersDump`ExportGeneric.  It calls a C/C++ function named genericExport(void).

Note the use of :Evaluate: to put the ExportGeneric in the System`ConvertersDump` context.

Options are wrapped in a list to make getting the number of options easier on the C/C++ side.

Step 3: Algorithm Implementation

Export Operations

Export operations should expect to receive the following pieces of information from the kernel on stdlink.

If the procedure completes successfully, then the program should put the supplied file name on the link that points to the kernel.

\(Export["D:\Users\paulh\testme.jpg", \ 
Graphics[Line[{{0, 0}, {1, 1}}]]]\)

Step 3: Algorithm Implementation (cont'd)

Import Operations

Import operations should expect to receive the following pieces of information from the kernel on stdlink.

If the procedure completes successfully, then the program should put the resulting Mathematica expression on the link that points to the kernel.

Import["D:\\Users\\paulh\\testme.jpg"]

Step 3: Algorithm Implementation (cont'd)

Errors and Failures

Errors should be directed to the kernel via an EvaluatePacket placed on the link pointing to the kernel.  Writing the message to stderr may result in unpredictable results.

Incomplete operations should be indicated by placing $Failed on the link that points back to the kernel.

/* EvaluatePacket[Message[Import::"fmterr", "JPEG"] */
MLPutFunction(kerlink, "EvaluatePacket", 1);
MLPutFunction(kerlink, "Message", 2);
MLPutFunction(kerlink, "MessageName", 2);
MLPutSymbol(kerlink, "Import");
MLPutString(kerlink, "fmterr");
MLPutString(kerlink, "JPEG");

Import["D:\\Users\\paulh\\empty.jpg"]

Format Registration

To make the kernel aware of the new function, we use Experimental`RegisterConverter`.  Here is an example call for our Generic format that we implemented through a template earlier.

Experimental`RegisterConverter[
	Export, 
	"Generic"
	System`ConvertersDump`ExportGeneric,
	Path -> 
		ToFileName[
			{$UserAddOnsDirectory, "SystemFiles", "Converters", "Binaries"}, 
			$SystemID
		],
	"Source" -> "Generic.exe",
	"TextFormat" -> False
]

Some things worth noting about this example:

Special Considerations for Bitmap Export

Bitmap Export is Complicated

The kernel does not have the ability to render bitmaps from Mathematica graphics objects.  The front end has an undocumented packet interface for obtaining bitmaps of cell expressions, but preparing the input for this interface can be very tedious.  The underlying process used in Mathematica 5.0 and later is described in the figure below

Special Considerations for Bitmap Export

Bitmap Export Wrappers

Consequently, we have defined two special handler functions for bitmap operations. They act as wrappers to your handler code and hide away all of the gory details of communicating with the front end.  We are also working on a supplemental library which will simplify the task of managing the listening links.

This function provides data for converters that work with true-color rasters.

System`ConvertersDump`exportToBitmapConverter[
	format_String, function_Symbol, filename_String, expression_, options___?OptionQ
]

This function provides data for converters that work with colormapped rasters.

System`ConvertersDump`exportToColormappedBitmapConverter[
	format_String, function_Symbol, filename_String, expression_, opts___?OptionQ
]

In both cases, the pattern for function should be that of the lowLevelExport example cited earlier.  The expression will be guaranteed to be a RasterDataPacket structure.

Special Considerations for Bitmap Export

RasterDataPacket Expression

The RasterDataPacket symbol resides in the System`ConvertersDump` context.  It contains a rendered bitmap that may be suitable for input to a bitmap library function.

The three-argument form denotes a non-colormap raster.  The dimensions of the raster argument imply the type of raster stored.

Non-Colormap Raster

System`ConvertersDump`RasterDataPacket[
	Integer,
	maximum_Integer, (* Usually 1 for bi-level, 255 for everything else *)
	raster:(
		(* bi-level or grayscale *) { {_Integer..}.. } | 
		(* RGB color *) 			{ { {_Integer, _Integer, _Integer}.. }..} |
		(* CMYK color *)			{ { {_Integer, _Integer, _Integer, _Integer}.. } ..} )
]

Special Considerations for Bitmap Export

Colormap Raster

The four-argument form denotes a colormap raster, with the colormap making up the additional argument.

System`ConvertersDump`RasterDataPacket[
	Integer,
	maximum_Integer, (* Usually 255 *)
	raster:{ {_Integer.. }.. } ,
	colormap:{ {_Integer, _Integer, _Integer}.. } (* RGB color *)
]

Special Considerations for Bitmap Export

Format Registration

The example below demonstrates the format registration command for PNG export operations.

Experimental`RegisterConverter[
	Export, 
	{{"PNG"}, {"image/png", "image/x-png"}}, 
	System`ConvertersDump`exportToBitmapConverter[
		"PNG", System`ConvertersDump`ExportPNG, ##
	]&,
	"TextFormat" -> False,
	"Extensions" -> {"*.png"}, "Source" ->"PNG.exe"
]

Note the use of SlotSequence in the wrapper because multiple arguments are to be placed there.

The front end as a rendering engine

The front end is a most versatile rendering system.  It renders:
• PostScript graphics
• Typesetting data
• Notebooks, cells, and any other notebook construct
• Even platform-dependent formats like Windows metafile and Macintosh PICT.

It can render output in PostScript, InputForm, and various graphical formats.  These facilities are used presently by:
• Export
• Basic graphics facilities in the kernel
• webMathematica

among others, and are available for your use if you know how to get to them.

The rendering keystone: ExportPacket

ExportPacket[data,  format,  options]
ExportPacket[data,  format,  filename,  options]

data  =  _Cell | {_Cell,  ... } || _Notebook | 
   boxes,

\(format\  = \ "PostScript" | "InputForm" | "GIF" | "MGF" | "PPM" | "PGM" | "Metafile" | "PICT"\)

filename  =  _String  |  _FrontEnd`FileName

An example with raw BoxData

mybox = BoxData[SqrtBox["2"]];
DisplayForm[mybox[[1]]]

\(\(LinkWrite[$ParentLink, 
\ 
        ExportPacket[
mybox, 
"GIF"]
];\)\)
gifOutput  =  LinkRead[$ParentLink]

Show[ImportString[gifOutput[[1]], "GIF"]];

An example with a Cell expression

mycell = 
        Cell[BoxData[SqrtBox["2"]], "Input",  
          Magnification -> 3];
CellPrint[mycell];

\(\(LinkWrite[$ParentLink, 
\ 
        ExportPacket[
mycell, 
"GIF"]
];\)\)
gifOutput  =  LinkRead[$ParentLink]

Show[ImportString[gifOutput[[1]], "GIF"]];

An example with a Notebook expression

mynotebook = 
        Notebook[{Cell[BoxData[SqrtBox["2"]], "Input"]},  
          StyleDefinitions -> "PrimaryColor.nb"];
NotebookPut[mynotebook];

LinkWrite[$ParentLink, 
 
        ExportPacket[
mynotebook, 
"GIF\
"]
];
gifOutput  =  LinkRead[$ParentLink]

Show[ImportString[gifOutput[[1]], "GIF"]];

ExportPacket options

Options and their possible values:

Options only relevant for GIF

ExportPacket Bounds options

mycell = 
        Cell[BoxData[SqrtBox["x"]], CellLabel -> "In[5]:=", 
          Background -> RGBColor[1, 0, 0]];
ShowMeTheBounds[x_] := 
      Module[{returnValue}, 
LinkWrite[$ParentLink,  
          ExportPacket[mycell,  "GIF", 
            Bounds -> x ]]; 
returnValue  =  
          LinkRead[$ParentLink]; 
Show[
          ImportString[returnValue[[1]],  "GIF"]]]

ShowMeTheBounds[CellBoundingBox];

ShowMeTheBounds[CellElementsBoundingBox];

ShowMeTheBounds[CellContentsBoundingBox];

ShowMeTheBounds[ShrinkWrapBoundingBox];

ExportPacket Verbose options

Verbose gives us all of the bounding boxes, whereas non-verbose just gives us the bounding box we actually used.

LinkWrite[$ParentLink,  
        ExportPacket[BoxData[SqrtBox["2"]],  "GIF", 
          Verbose -> True]];
LinkRead[$ParentLink]

If you want to compute a bounding box without getting a graphic back, you can use GetBoundingBoxSizePacket with and without the verbose arguments.

LinkWrite[$ParentLink,  
      GetBoundingBoxSizePacket[BoxData[SqrtBox["2"]], 
        Verbose -> True]]; 
LinkRead[$ParentLink]

Adding an interface for your new command

Mathematica allows dynamic addition of menu commands.  All you need to know is where you want to place the menu and what you want the menu item to do.  Menu items look like much like button functions.

\(ButtonBox["Make me a red notebook", \ 
ButtonFunction :> \((FrontEndExecute[
                FrontEnd`NotebookPut[
                  Notebook[{}, 
                    Background -> 
                      RGBColor[1, 0, 
                        0]]]] &)\), 
Active -> True] // 
      DisplayForm\)

LinkWriteHeld[$ParentLink, 
Hold@
        AddMenuCommands["New", 
{Item["Make me a \
green notebook",  
FrontEndExecute[
                FrontEnd`NotebookPut[
                  Notebook[{}, 
                    Background -> RGBColor[0, 1, 0]]]]]}]]

Adding an interface for your new command

First, how do we get a filename?

Experimental`FileBrowse[True | False,  
      SymbolicFileName -> False | True]

First argument: True if you want a saving dialog, False if you don't

Example: Saving a notebook, receiving a raw filename

Experimental`FileBrowse[True]

Experimental`FileBrowse[False,  
      SymbolicFileName -> True]

Be prepared to deal with a return value of $Failed, which means the dialog was cancelled.

Adding an interface for your new command

Let's define a function which saves our selection as a JPEG.

ExportMyJPEGFile[] := 
      Module[{contents  =  NotebookRead[InputNotebook[]], cellexpr, 
          pathname}, 
cellexpr  =  
          Switch[contents, 
{}, 
            Null, 
_BoxData | _String,   Cell[
              contents], 
{___, _String, ___},  
            Cell[TextData[contents]], 
_,  
            contents]; 
If[
          cellexpr  =!=  Null, 
pathname  =  
            Experimental`FileBrowse[]; 
If[
            pathname  =!=  $Failed, 
 
            Export[pathname,  cellexpr,  "JPEG"]]
]; 
        0
]

Adding an interface for your new command

Now we can simply assign the function.  Keep in mind that, just as with ButtonFunctions, menu items must have their evaluators set to Automatic to use the default kernel.

\(ButtonBox["Save Selection As JPEG", \ 
        ButtonFunction :> \((KernelExecute[
                ToExpression["ExportMyJPEGFile[]"]] &)\), 
Active -> 
          True, 
ButtonEvaluator -> Automatic] // 
      DisplayForm\)

\(LinkWriteHeld[$ParentLink, 
Hold@
        AddMenuCommands["SaveRename", 
{Item["Save Selection As JPEG", 
KernelExecute[
                ToExpression["ExportMyJPEGFile[]"]], 
MenuEvaluator -> Automatic]}]]\)

Sample Skeletal Converter

In the examples below, note that all C functions having names of the form ImpExp* are implemented forthcoming utility library that is designed to help speed the development of converters.

Generic Bitmap Export Function

void genericBitmapExport(void)
{
	char *fname;
	const unsigned char *bytefname;
	long len, mlerr;
	MLINK opts;
	long rasterheight, rasterwidth;
	RasterDataPacket *rdp;
	/* Place any additional variable declarations here. */
	
	/* Pull arguments from link. */
	/* Sequence of items on link guaranteed by template pattern. */

	/* Target File Name */
	MLGetByteString(stdlink, &bytefname, &len, 0L);
	fname = (char *) malloc(len * sizeof(char) + 1);
	memcpy((void *) fname, (void *) bytefname, len);
	*(fname + len) = '\0';
	MLDisownByteString(stdlink, bytefname, len);

	/* Raster Image Data */
	ImpExpGetRasterDataPacket(stdlink, &rdp);

	/* Generic Expression */
	expr = MLLoopbackOpen(stdenv, &mlerr);
	MLTransferExpression(expr, stdlink);

	/* Optional Arguments */
	/* Put options onto loopback link for easy storage. */
	opts = MLLoopbackOpen(stdenv, &mlerr);
	MLTransferExpression(opts, stdlink);
	/* Process options with ImpExpGetOptionValueByName(). */

	/* Get dimensions and raster type. */
	rasterheight = rdp->rasterdims[0];
	rasterwidth = rdp->rasterdims[1];
	/* Identify type of incoming RasterDataPacket[]. */
	switch (rdp->rastertype)
	{
		case  WRI_IMPEXP_CD_GRAYLEVEL :
			/* Add side effects of 8-bit grayscale graphic here. */
			break;
		case  WRI_IMPEXP_CD_RGBCOLOR :
			/* Add side effects of 24-bit true color graphic here. */
			break;
		case  WRI_IMPEXP_CD_CMYKCOLOR :
			/* Add side effects of 32-bit CMYK color graphic here . */
			break;
	}

	/* Open output stream. */
	/* Perform format specific conversions. */
	/* Write output to stream. */
	/* Close output stream. */
	/* If error occurs, use ImpExpSendMessageToKernel(). */
	
	/* Send file name back to kernel. */
	ImpExpSendByteStringToKernel((unsigned char *) fname, strlen(fname));

	/* Free data allocated by reading arguments from link. */
	/* File name */
	free(fname);
	/* Expression */
	ImpExpDisownRasterDataPacket(rdp);	
	/* Options */
	MLClose(opts);

	return;
}

Generic Import Function

void genericImport(void)
{
	char *fname;
	const unsigned char *bytefname;
	long len, mlerr;
	MLINK expr, opts;
	RasterDataPacket rdp;

	/* Initialize RasterDataPacket array members to NULL. */
	rdp.rasterarray = NULL;
	rdp.cmaparray = NULL;

	/* Pull arguments from link. */
	/* Sequence of items on link guaranteed by template pattern. */

	/* Target File Name */
	MLGetByteString(stdlink, &bytefname, &len, 0L);
	fname = (char *) malloc(len * sizeof(char) + 1);
	memcpy((void *) fname, (void *) bytefname, len);
	*(fname + len) = '\0';
	MLDisownByteString(stdlink, bytefname, len);

	/* Optional Arguments */
	opts = MLLoopbackOpen(stdenv, &mlerr);
	MLTransferExpression(opts, stdlink);
	/* Process options with ImpExpGetOptionValueByName(). */

	/* Open new input stream. */
	/* Read data from file. */
	/* Do format specific conversions. */
	/* If error occurs, use ImpExpSendMessageToKernel(). */
	
	/* Put result on loopback link. */
	expr = MLLoopbackOpen(stdenv, &mlerr);
	ImpExpPutRasterDataPacket(expr, &rdp);
	ImpExpSendExpressionToKernel(expr);

	/* Free data allocated from arguments. */
	free(fname);
	if (rdp.rasterarray != NULL)
		free(rdp.rasterarray);
	if (rdp.cmaparray != NULL)
		free(rdp.cmaparray);
	MLClose(opts);
	MLClose(expr);

	return;
}

Boilerplate Code

void ConverterLinkCreate(void)
{
	ImpExpConverterLinkCreate(stdlink);	
	return;
}

void ConverterLinkRead (void)
{
	ImpExpConverterLinkRead(stdlink);
	return;
}

void ConverterLinkClose(void)
{
	ImpExpConverterLinkClose(stdlink);
	return;
}

void ConverterGetFunctionOrdinal(void)
{
	ImpExpGetFunctionOrdinal(stdlink);
	return;
}

#if WINDOWS_MATHLINK

#if __BORLANDC__
#pragma argsused
#endif

int PASCAL WinMain( HINSTANCE hinstCurrent, HINSTANCE hinstPrevious, 
	LPSTR lpszCmdLine, int nCmdShow)
{
	char  buff[512];
	char FAR * buff_start = buff;
	int argc;
	char FAR * argv[32];
	char FAR * FAR * argv_end = argv + 32;

	hinstPrevious = hinstPrevious; /* suppress warning */

	MLScanString( argv, &argv_end, &lpszCmdLine, &buff_start);
	argc = argv_end - argv;

	ImpExpInitialize(argc, argv, &stdenv, &stdyielder);
	
	return MLMain(argc, argv);
}

#else

int main(int argc, char* argv[])
{
	
	ImpExpInitialize(argc, argv, &stdenv, &stdyielder);

	return MLMain(argc, argv);
}

#endif

MathLink Template

:Evaluate: Begin["System`ConvertersDump`"]

:Begin:
:Function: genericExport
:Pattern: ExportGeneric[fname_String, expr_, opts___?OptionQ]
:Arguments: {fname, expr, {opts}}
:ArgumentTypes: {Manual}
:ReturnType: Manual
:End:

:Begin:
:Function: genericImport
:Pattern: ImportGeneric[fname_String, opts___?OptionQ]
:Arguments: {fname, {opts}}
:ArgumentTypes: {Manual}
:ReturnType: Manual
:End:

:Begin:
:Function: ConverterLinkCreate
:Pattern: ConverterLinkCreate["Generic", opts___?OptionQ]
:Arguments: {{opts}}
:ArgumentTypes: {Manual}
:ReturnType: Manual
:End:

:Begin:
:Function: ConverterLinkRead
:Pattern: ConverterLinkRead["Generic"]
:Arguments: {}
:ArgumentTypes: {Manual}
:ReturnType: Manual
:End:

:Begin:
:Function: ConverterLinkClose
:Pattern: ConverterLinkClose["Generic"]
:Arguments: {}
:ArgumentTypes: {Manual}
:ReturnType: Manual
:End:

:Begin:
:Function: ConverterGetFunctionOrdinal
:Pattern: ConverterGetFunctionOrdinal["Generic", symname_Symbol]
:Arguments: {symname}
:ArgumentTypes: {Manual}
:ReturnType: Manual
:End:

:Evaluate: End[]

Sample Format Registration Commands

Example 1 – Non-bitmap Export

Experimental`RegisterConverter[
	Export, 
	"FITS", 
	"FITS.exe",
	System`ConvertersDump`ExportFITS, 
	"TextFormat" -> False, "Unevaluated" -> False, 
	"Extensions" -> {"*.fit", "*.fits"}, "Source" -> "FITS.exe"
]

Example 2 – Non-bitmap Import

Experimental`RegisterConverter[
	Import,
	{{"SDTS"}, {"DDF"}}, 
	System`ConvertersDump`ImportSDTS,
	"TextFormat" -> False,
	"Extensions" -> {"*.ddf"}, "Source" -> "SDTS.exe"
]

Example 3 – True Color Bitmap Export

Experimental`RegisterConverter[
	Export, 
	{{"PNG"}, {"image/png", "image/x-png"}}, 
	System`ConvertersDump`exportToBitmapConverter[
		"PNG", System`ConvertersDump`ExportPNG, ##
	]&,
	"TextFormat" -> False, "Unevaluated" -> False,
	"Extensions" -> {"*.png"}, "Source" ->"PNG.exe"
]

Example 4 – Colormap Bitmap Export

Experimental`RegisterConverter[
	Export,
	{{"GIF"},{"image/gif"}}, 
	System`ConvertersDump`exportToColormappedBitmapConverter[
		"GIF", System`ConvertersDump`ExportGIF, ##
	]&,
	"TextFormat" -> False, "Unevaluated" -> False,
	"Extensions -> {"*.gif"}, Source -> "GIF.exe"
]

Example 5 – Bitmap Import

Experimental`RegisterConverter[
	Import, 
	{{"PNG"}, {"image/png", "image/x-png"}},
	System`ConvertersDump`ImportPNG,
	"TextFormat" -> False, 
	"Extensions" -> {"*.png"}, "Source" -> "PNG.exe"
]

New Framework Benchmarks

test graphic: Plot3D[Sin[x] Cos[y], {x, 0, 2\[Pi]}, {y, 0, 2\[Pi]}];

target format: JPEG

timing function:

SetAttributes[stopwatch, HoldFirst]

stopwatch[expr_]:=
  Module[
    {start},
    start = AbsoluteTime[];
    expr;
    AbsoluteTime[] - start
    ]

test hardware: Dell Inspiron 5000 / Pentium III 650 MHz, 512 MB RAM

Windows XP Professional w/ Service Pack 1

  bitmap           4.2 front end    5.0 front end    5.0 front end
  size             old framework    old framework    new framework
  -------
  288x233          4.00000          5.4778768        1.9928656
  (10 iterations)

  576x466          4.00000          6.0787408        1.8526640
  (3 iterations)

  864x699          3.00000          5.8383952        2.5236288

  1152x932         12.00000         14.5409088       9.2432912
  ----------------------------------------------------------------

Note: With new framework on Windows, the front end -> converter link 
uses "SharedMemory" device.

Red Hat Linux 7.2

  bitmap           4.2 front end    5.0 front end    5.0 front end
  size             old framework    old framework    new framework
  ----------------------------------------------------------------
  288x233          4.00000          7.718629         3.026156
  (10 iterations)

  576x466          5.00000          8.236139         2.936865
  (3 iterations)

  864x699          5.00000          6.798433         2.766127

  1152x932         10.00000         13.625942        6.644974
  ----------------------------------------------------------------

Note: With new framework on Linux, the front end -> converter link 
uses "TCPIP" device.