diff --git a/.gitignore b/.gitignore
index 14803ad980956b04712d46c855bc6e3c3454ee29..8098709b36f8f384501bb23e156756dfaf4d9eab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,7 +8,7 @@
 .project
 .settings
 /target
-
+classes/
 # IntelliJ
 .idea/
 *.iml
diff --git a/pom.xml b/pom.xml
index ce3fdd41dccd46e2b398c2e0220c433fe7883eac..3bcf22161bb8852cd056600496f0306a6ebff91e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -201,5 +201,11 @@
 			<artifactId>junit</artifactId>
 			<scope>test</scope>
 		</dependency>
-	</dependencies>
+        <dependency>
+            <groupId>org.azgra</groupId>
+            <artifactId>DataCompressor</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
 </project>
diff --git a/src/main/java/bdv/BigDataViewer.java b/src/main/java/bdv/BigDataViewer.java
index 266aa8d8d2ec0f26c04c913fa7daba7e3f9a61c7..9f2390ba1f9e6d395970af9e094bbf07c15180f2 100644
--- a/src/main/java/bdv/BigDataViewer.java
+++ b/src/main/java/bdv/BigDataViewer.java
@@ -43,6 +43,7 @@ import javax.swing.JMenuBar;
 import javax.swing.JMenuItem;
 import javax.swing.filechooser.FileFilter;
 
+import azgracompress.cache.ICacheFile;
 import net.imglib2.Volatile;
 import net.imglib2.converter.Converter;
 import net.imglib2.display.ColorConverter;
@@ -96,628 +97,589 @@ import mpicbg.spim.data.generic.sequence.BasicViewSetup;
 import mpicbg.spim.data.sequence.Angle;
 import mpicbg.spim.data.sequence.Channel;
 
-public class BigDataViewer
-{
-	protected final ViewerFrame viewerFrame;
+public class BigDataViewer {
+    protected final ViewerFrame viewerFrame;
 
-	protected final ViewerPanel viewer;
+    protected final ViewerPanel viewer;
 
-	protected final SetupAssignments setupAssignments;
-
-	protected final ManualTransformation manualTransformation;
-
-	protected final Bookmarks bookmarks;
-
-	protected final BrightnessDialog brightnessDialog;
-
-	protected final CropDialog cropDialog;
-
-	protected final RecordMovieDialog movieDialog;
-
-	protected final RecordMaxProjectionDialog movieMaxProjectDialog;
-
-	protected final VisibilityAndGroupingDialog activeSourcesDialog;
-
-	protected final HelpDialog helpDialog;
-
-	protected final ManualTransformationEditor manualTransformationEditor;
-
-	protected final BookmarksEditor bookmarkEditor;
-
-	protected final JFileChooser fileChooser;
-
-	protected File proposedSettingsFile;
-
-	public void toggleManualTransformation()
-	{
-		manualTransformationEditor.toggle();
-	}
-
-	public void initSetBookmark()
-	{
-		bookmarkEditor.initSetBookmark();
-	}
-
-	public void initGoToBookmark()
-	{
-		bookmarkEditor.initGoToBookmark();
-	}
-
-	public void initGoToBookmarkRotation()
-	{
-		bookmarkEditor.initGoToBookmarkRotation();
-	}
-
-	private static String createSetupName( final BasicViewSetup setup )
-	{
-		if ( setup.hasName() )
-			return setup.getName();
-
-		String name = "";
-
-		final Angle angle = setup.getAttribute( Angle.class );
-		if ( angle != null )
-			name += ( name.isEmpty() ? "" : " " ) + "a " + angle.getName();
-
-		final Channel channel = setup.getAttribute( Channel.class );
-		if ( channel != null )
-			name += ( name.isEmpty() ? "" : " " ) + "c " + channel.getName();
-
-		return name;
-	}
-
-	/**
-	 * Create standard converter from the given {@code type} to ARGB:
-	 * <ul>
-	 * <li>For {@code RealType}s a {@link RealARGBColorConverter} is
-	 * returned.</li>
-	 * <li>For {@code ARGBType}s a {@link ScaledARGBConverter.ARGB} is
-	 * returned.</li>
-	 * <li>For {@code VolatileARGBType}s a
-	 * {@link ScaledARGBConverter.VolatileARGB} is returned.</li>
-	 * </ul>
-	 */
-	@SuppressWarnings( "unchecked" )
-	public static < T extends NumericType< T > > Converter< T, ARGBType > createConverterToARGB( final T type )
-	{
-		if ( type instanceof RealType )
-		{
-			final RealType< ? > t = ( RealType< ? > ) type;
-			final double typeMin = Math.max( 0, Math.min( t.getMinValue(), 65535 ) );
-			final double typeMax = Math.max( 0, Math.min( t.getMaxValue(), 65535 ) );
-			return ( Converter< T, ARGBType > ) RealARGBColorConverter.create( t, typeMin, typeMax );
-		}
-		else if ( type instanceof ARGBType )
-			return ( Converter< T, ARGBType > ) new ScaledARGBConverter.ARGB( 0, 255 );
-		else if ( type instanceof VolatileARGBType )
-			return ( Converter< T, ARGBType > ) new ScaledARGBConverter.VolatileARGB( 0, 255 );
-		else
-			throw new IllegalArgumentException( "ImgLoader of type " + type.getClass() + " not supported." );
-	}
-
-	/**
-	 * Create a {@code ConverterSetup} for the given {@code SourceAndConverter}.
-	 * {@link SourceAndConverter#asVolatile() Nested volatile}
-	 * {@code SourceAndConverter} are added to the {@code ConverterSetup} if
-	 * present. If {@code SourceAndConverter} does not comprise a
-	 * {@code ColorConverter}, returns {@code null}.
-	 *
-	 * @param soc
-	 *            {@code SourceAndConverter} for which to create a
-	 *            {@code ConverterSetup}
-	 * @param setupId
-	 *            setupId of the created {@code ConverterSetup}
-	 * @return a new {@code ConverterSetup} or {@code null}
-	 */
-	public static ConverterSetup createConverterSetup( final SourceAndConverter< ? > soc, final int setupId )
-	{
-		final List< ColorConverter > converters = new ArrayList<>();
-
-		final Converter< ?, ARGBType > c = soc.getConverter();
-		if ( c instanceof ColorConverter )
-			converters.add( ( ColorConverter ) c );
-
-		final SourceAndConverter< ? extends Volatile< ? > > vsoc = soc.asVolatile();
-		if ( vsoc != null )
-		{
-			final Converter< ?, ARGBType > vc = vsoc.getConverter();
-			if ( vc instanceof ColorConverter )
-				converters.add( ( ColorConverter ) vc );
-		}
-
-		if ( converters.isEmpty() )
-			return null;
-		else
-			return new RealARGBColorConverterSetup( setupId, converters );
-	}
-
-	/**
-	 * Decorate source with an extra transformation, that can be edited manually
-	 * in this viewer. {@link SourceAndConverter#asVolatile() Nested volatile}
-	 * {@code SourceAndConverter} are wrapped as well, if present.
-	 */
-	public static < T, V extends Volatile< T > > SourceAndConverter< T > wrapWithTransformedSource( final SourceAndConverter< T > soc )
-	{
-		if ( soc.asVolatile() == null )
-			return new SourceAndConverter<>( new TransformedSource<>( soc.getSpimSource() ), soc.getConverter() );
-
-		@SuppressWarnings( "unchecked" )
-		final SourceAndConverter< V > vsoc = ( SourceAndConverter< V > ) soc.asVolatile();
-		final TransformedSource< T > ts = new TransformedSource<>( soc.getSpimSource() );
-		final TransformedSource< V > vts = new TransformedSource<>( vsoc.getSpimSource(), ts );
-		return new SourceAndConverter<>( ts, soc.getConverter(), new SourceAndConverter<>( vts, vsoc.getConverter() ) );
-	}
-
-	private static < T extends NumericType< T >, V extends Volatile< T > & NumericType< V > > void initSetupNumericType(
-			final AbstractSpimData< ? > spimData,
-			final BasicViewSetup setup,
-			final List< ConverterSetup > converterSetups,
-			final List< SourceAndConverter< ? > > sources )
-	{
-		final int setupId = setup.getId();
-		final ViewerImgLoader imgLoader = ( ViewerImgLoader ) spimData.getSequenceDescription().getImgLoader();
-		@SuppressWarnings( "unchecked" )
-		final ViewerSetupImgLoader< T, V > setupImgLoader = ( ViewerSetupImgLoader< T, V > ) imgLoader.getSetupImgLoader( setupId );
-		final T type = setupImgLoader.getImageType();
-		final V volatileType = setupImgLoader.getVolatileImageType();
-
-		if ( ! ( type instanceof NumericType ) )
-			throw new IllegalArgumentException( "ImgLoader of type " + type.getClass() + " not supported." );
-
-		final String setupName = createSetupName( setup );
-
-		SourceAndConverter< V > vsoc = null;
-		if ( volatileType != null )
-		{
-			final VolatileSpimSource< V > vs = new VolatileSpimSource<>( spimData, setupId, setupName );
-			vsoc = new SourceAndConverter<>( vs, createConverterToARGB( volatileType ) );
-		}
-
-		final SpimSource< T > s = new SpimSource<>( spimData, setupId, setupName );
-		final SourceAndConverter< T > soc = new SourceAndConverter<>( s, createConverterToARGB( type ), vsoc );
-		final SourceAndConverter< T > tsoc = wrapWithTransformedSource( soc );
-		sources.add( tsoc );
-
-		final ConverterSetup converterSetup = createConverterSetup( tsoc, setupId );
-		if ( converterSetup != null )
-			converterSetups.add( converterSetup );
-	}
-
-	public static void initSetups(
-			final AbstractSpimData< ? > spimData,
-			final List< ConverterSetup > converterSetups,
-			final List< SourceAndConverter< ? > > sources )
-	{
-		for ( final BasicViewSetup setup : spimData.getSequenceDescription().getViewSetupsOrdered() )
-			initSetupNumericType( spimData, setup, converterSetups, sources );
-	}
-
-	/**
-	 *
-	 * @param converterSetups
-	 *            list of {@link ConverterSetup} that control min/max and color
-	 *            of sources.
-	 * @param sources
-	 *            list of pairs of source of some type and converter from that
-	 *            type to ARGB.
-	 * @param spimData
-	 *            may be null. The {@link AbstractSpimData} of the dataset (if
-	 *            there is one). If it exists, it is used to set up a "Crop"
-	 *            dialog.
-	 * @param numTimepoints
-	 *            the number of timepoints in the dataset.
-	 * @param cache
-	 *            handle to cache. This is used to control io timing.
-	 * @param windowTitle
-	 *            title of the viewer window.
-	 * @param progressWriter
-	 *            a {@link ProgressWriter} to which BDV may report progress
-	 *            (currently only used in the "Record Movie" dialog).
-	 * @param options
-	 *            optional parameters.
-	 */
-	public BigDataViewer(
-			final ArrayList< ConverterSetup > converterSetups,
-			final ArrayList< SourceAndConverter< ? > > sources,
-			final AbstractSpimData< ? > spimData,
-			final int numTimepoints,
-			final CacheControl cache,
-			final String windowTitle,
-			final ProgressWriter progressWriter,
-			final ViewerOptions options )
-	{
-		final InputTriggerConfig inputTriggerConfig = getInputTriggerConfig( options );
-		if ( options.values.getTransformEventHandlerFactory() instanceof BehaviourTransformEventHandlerFactory )
-			( ( BehaviourTransformEventHandlerFactory< ? > ) options.values.getTransformEventHandlerFactory() ).setConfig( inputTriggerConfig );
-
-		viewerFrame = new ViewerFrame( sources, numTimepoints, cache, options );
-		if ( windowTitle != null )
-			viewerFrame.setTitle( windowTitle );
-		viewer = viewerFrame.getViewerPanel();
-
-		for ( final ConverterSetup cs : converterSetups )
-			cs.setViewer( viewer );
-
-		manualTransformation = new ManualTransformation( viewer );
-		manualTransformationEditor = new ManualTransformationEditor( viewer, viewerFrame.getKeybindings() );
-
-		bookmarks = new Bookmarks();
-		bookmarkEditor = new BookmarksEditor( viewer, viewerFrame.getKeybindings(), bookmarks );
-
-		setupAssignments = new SetupAssignments( converterSetups, 0, 65535 );
-		if ( setupAssignments.getMinMaxGroups().size() > 0 )
-		{
-			final MinMaxGroup group = setupAssignments.getMinMaxGroups().get( 0 );
-			for ( final ConverterSetup setup : setupAssignments.getConverterSetups() )
-				setupAssignments.moveSetupToGroup( setup, group );
-		}
-
-		brightnessDialog = new BrightnessDialog( viewerFrame, setupAssignments );
-
-		if (spimData != null )
-			viewer.getSourceInfoOverlayRenderer().setTimePointsOrdered( spimData.getSequenceDescription().getTimePoints().getTimePointsOrdered() );
-
-		cropDialog = ( spimData == null ) ? null : new CropDialog( viewerFrame, viewer, spimData.getSequenceDescription() );
-
-		movieDialog = new RecordMovieDialog( viewerFrame, viewer, progressWriter );
-		// this is just to get updates of window size:
-		viewer.getDisplay().addOverlayRenderer( movieDialog );
-
-		movieMaxProjectDialog = new RecordMaxProjectionDialog( viewerFrame, viewer, progressWriter );
-		// this is just to get updates of window size:
-		viewer.getDisplay().addOverlayRenderer( movieMaxProjectDialog );
-
-		activeSourcesDialog = new VisibilityAndGroupingDialog( viewerFrame, viewer.getVisibilityAndGrouping() );
-
-		helpDialog = new HelpDialog( viewerFrame );
-
-		fileChooser = new JFileChooser();
-		fileChooser.setFileFilter( new FileFilter()
-		{
-			@Override
-			public String getDescription()
-			{
-				return "xml files";
-			}
-
-			@Override
-			public boolean accept( final File f )
-			{
-				if ( f.isDirectory() )
-					return true;
-				if ( f.isFile() )
-				{
-					final String s = f.getName();
-					final int i = s.lastIndexOf( '.' );
-					if ( i > 0 && i < s.length() - 1 )
-					{
-						final String ext = s.substring( i + 1 ).toLowerCase();
-						return ext.equals( "xml" );
-					}
-				}
-				return false;
-			}
-		} );
-
-		NavigationActions.installActionBindings( viewerFrame.getKeybindings(), viewer, inputTriggerConfig );
-		BigDataViewerActions.installActionBindings( viewerFrame.getKeybindings(), this, inputTriggerConfig );
-
-		final JMenuBar menubar = new JMenuBar();
-		JMenu menu = new JMenu( "File" );
-		menubar.add( menu );
-
-		final ActionMap actionMap = viewerFrame.getKeybindings().getConcatenatedActionMap();
-		final JMenuItem miLoadSettings = new JMenuItem( actionMap.get( BigDataViewerActions.LOAD_SETTINGS ) );
-		miLoadSettings.setText( "Load settings" );
-		menu.add( miLoadSettings );
-
-		final JMenuItem miSaveSettings = new JMenuItem( actionMap.get( BigDataViewerActions.SAVE_SETTINGS ) );
-		miSaveSettings.setText( "Save settings" );
-		menu.add( miSaveSettings );
-
-		menu = new JMenu( "Settings" );
-		menubar.add( menu );
-
-		final JMenuItem miBrightness = new JMenuItem( actionMap.get( BigDataViewerActions.BRIGHTNESS_SETTINGS ) );
-		miBrightness.setText( "Brightness & Color" );
-		menu.add( miBrightness );
-
-		final JMenuItem miVisibility = new JMenuItem( actionMap.get( BigDataViewerActions.VISIBILITY_AND_GROUPING ) );
-		miVisibility.setText( "Visibility & Grouping" );
-		menu.add( miVisibility );
-
-		menu = new JMenu( "Tools" );
-		menubar.add( menu );
-
-		if ( cropDialog != null )
-		{
-			final JMenuItem miCrop = new JMenuItem( actionMap.get( BigDataViewerActions.CROP ) );
-			miCrop.setText( "Crop" );
-			menu.add( miCrop );
-		}
-
-		final JMenuItem miMovie = new JMenuItem( actionMap.get( BigDataViewerActions.RECORD_MOVIE ) );
-		miMovie.setText( "Record Movie" );
-		menu.add( miMovie );
-
-		final JMenuItem miMaxProjectMovie = new JMenuItem( actionMap.get( BigDataViewerActions.RECORD_MAX_PROJECTION_MOVIE ) );
-		miMaxProjectMovie.setText( "Record Max-Projection Movie" );
-		menu.add( miMaxProjectMovie );
-
-		final JMenuItem miManualTransform = new JMenuItem( actionMap.get( BigDataViewerActions.MANUAL_TRANSFORM ) );
-		miManualTransform.setText( "Manual Transform" );
-		menu.add( miManualTransform );
-
-		menu = new JMenu( "Help" );
-		menubar.add( menu );
-
-		final JMenuItem miHelp = new JMenuItem( actionMap.get( BigDataViewerActions.SHOW_HELP ) );
-		miHelp.setText( "Show Help" );
-		menu.add( miHelp );
-
-		viewerFrame.setJMenuBar( menubar );
-	}
-
-	public static BigDataViewer open( final AbstractSpimData< ? > spimData, final String windowTitle, final ProgressWriter progressWriter, final ViewerOptions options )
-	{
-		if ( WrapBasicImgLoader.wrapImgLoaderIfNecessary( spimData ) )
-		{
-			System.err.println( "WARNING:\nOpening <SpimData> dataset that is not suited for interactive browsing.\nConsider resaving as HDF5 for better performance." );
-		}
-
-		final ArrayList< ConverterSetup > converterSetups = new ArrayList<>();
-		final ArrayList< SourceAndConverter< ? > > sources = new ArrayList<>();
-		initSetups( spimData, converterSetups, sources );
-
-		final AbstractSequenceDescription< ?, ?, ? > seq = spimData.getSequenceDescription();
-		final int numTimepoints = seq.getTimePoints().size();
-		final CacheControl cache = ( ( ViewerImgLoader ) seq.getImgLoader() ).getCacheControl();
-
-		final BigDataViewer bdv = new BigDataViewer( converterSetups, sources, spimData, numTimepoints, cache, windowTitle, progressWriter, options );
-
-		WrapBasicImgLoader.removeWrapperIfPresent( spimData );
-
-		bdv.viewerFrame.setVisible( true );
-		InitializeViewerState.initTransform( bdv.viewer );
-		return bdv;
-	}
-
-	public static BigDataViewer open( final String xmlFilename, final String windowTitle, final ProgressWriter progressWriter, final ViewerOptions options ) throws SpimDataException
-	{
-		final SpimDataMinimal spimData = new XmlIoSpimDataMinimal().load( xmlFilename );
-		final BigDataViewer bdv = open( spimData, windowTitle, progressWriter, options );
-		if ( !bdv.tryLoadSettings( xmlFilename ) )
-			InitializeViewerState.initBrightness( 0.001, 0.999, bdv.viewer, bdv.setupAssignments );
-		return bdv;
-	}
-
-	public static BigDataViewer open(
-			final ArrayList< ConverterSetup > converterSetups,
-			final ArrayList< SourceAndConverter< ? > > sources,
-			final int numTimepoints,
-			final CacheControl cache,
-			final String windowTitle,
-			final ProgressWriter progressWriter,
-			final ViewerOptions options )
-	{
-		final BigDataViewer bdv = new BigDataViewer( converterSetups, sources, null, numTimepoints, cache, windowTitle, progressWriter, options );
-		bdv.viewerFrame.setVisible( true );
-		InitializeViewerState.initTransform( bdv.viewer );
-		return bdv;
-	}
-
-	public ViewerPanel getViewer()
-	{
-		return viewer;
-	}
-
-	public ViewerFrame getViewerFrame()
-	{
-		return viewerFrame;
-	}
-
-	public SetupAssignments getSetupAssignments()
-	{
-		return setupAssignments;
-	}
-
-	public ManualTransformationEditor getManualTransformEditor()
-	{
-		return manualTransformationEditor;
-	}
-
-	public boolean tryLoadSettings( final String xmlFilename )
-	{
-		proposedSettingsFile = null;
-		if( xmlFilename.startsWith( "http://" ) )
-		{
-			// load settings.xml from the BigDataServer
-			final String settings = xmlFilename + "settings";
-			{
-				try
-				{
-					loadSettings( settings );
-					return true;
-				}
-				catch ( final FileNotFoundException e )
-				{}
-				catch ( final Exception e )
-				{
-					e.printStackTrace();
-				}
-			}
-		}
-		else if ( xmlFilename.endsWith( ".xml" ) )
-		{
-			final String settings = xmlFilename.substring( 0, xmlFilename.length() - ".xml".length() ) + ".settings" + ".xml";
-			proposedSettingsFile = new File( settings );
-			if ( proposedSettingsFile.isFile() )
-			{
-				try
-				{
-					loadSettings( settings );
-					return true;
-				}
-				catch ( final Exception e )
-				{
-					e.printStackTrace();
-				}
-			}
-		}
-		return false;
-	}
-
-	public void saveSettings()
-	{
-		fileChooser.setSelectedFile( proposedSettingsFile );
-		final int returnVal = fileChooser.showSaveDialog( null );
-		if ( returnVal == JFileChooser.APPROVE_OPTION )
-		{
-			proposedSettingsFile = fileChooser.getSelectedFile();
-			try
-			{
-				saveSettings( proposedSettingsFile.getCanonicalPath() );
-			}
-			catch ( final IOException e )
-			{
-				e.printStackTrace();
-			}
-		}
-	}
-
-	public void saveSettings( final String xmlFilename ) throws IOException
-	{
-		final Element root = new Element( "Settings" );
-		root.addContent( viewer.stateToXml() );
-		root.addContent( setupAssignments.toXml() );
-		root.addContent( manualTransformation.toXml() );
-		root.addContent( bookmarks.toXml() );
-		final Document doc = new Document( root );
-		final XMLOutputter xout = new XMLOutputter( Format.getPrettyFormat() );
-		xout.output( doc, new FileWriter( xmlFilename ) );
-	}
-
-	/**
-	 * If {@code options} doesn't define a {@link InputTriggerConfig}, try to
-	 * load it from files in this order:
-	 * <ol>
-	 * <li>"bdvkeyconfig.yaml" in the current directory.
-	 * <li>".bdv/bdvkeyconfig.yaml" in the user's home directory.
-	 * <li>legacy "bigdataviewer.keys.properties" in current directory (will be
-	 * also written to "bdvkeyconfig.yaml").
-	 * </ol>
-	 *
-	 * @param options
-	 * @return
-	 */
-	public static InputTriggerConfig getInputTriggerConfig( final ViewerOptions options )
-	{
-		InputTriggerConfig conf = options.values.getInputTriggerConfig();
-
-		// try "bdvkeyconfig.yaml" in current directory
-		if ( conf == null && new File( "bdvkeyconfig.yaml" ).isFile() )
-		{
-			try
-			{
-				conf = new InputTriggerConfig( YamlConfigIO.read( "bdvkeyconfig.yaml" ) );
-			}
-			catch ( final IOException e )
-			{}
-		}
-
-		// try "~/.bdv/bdvkeyconfig.yaml"
-		if ( conf == null )
-		{
-			final String fn = System.getProperty( "user.home" ) + "/.bdv/bdvkeyconfig.yaml";
-			if ( new File( fn ).isFile() )
-			{
-				try
-				{
-					conf = new InputTriggerConfig( YamlConfigIO.read( fn ) );
-				}
-				catch ( final IOException e )
-				{}
-			}
-		}
-
-		if ( conf == null )
-		{
-			conf = new InputTriggerConfig();
-		}
-
-		return conf;
-	}
-
-	public void loadSettings()
-	{
-		fileChooser.setSelectedFile( proposedSettingsFile );
-		final int returnVal = fileChooser.showOpenDialog( null );
-		if ( returnVal == JFileChooser.APPROVE_OPTION )
-		{
-			proposedSettingsFile = fileChooser.getSelectedFile();
-			try
-			{
-				loadSettings( proposedSettingsFile.getCanonicalPath() );
-			}
-			catch ( final Exception e )
-			{
-				e.printStackTrace();
-			}
-		}
-	}
-
-	public void loadSettings( final String xmlFilename ) throws IOException, JDOMException
-	{
-		final SAXBuilder sax = new SAXBuilder();
-		final Document doc = sax.build( xmlFilename );
-		final Element root = doc.getRootElement();
-		viewer.stateFromXml( root );
-		setupAssignments.restoreFromXml( root );
-		manualTransformation.restoreFromXml( root );
-		bookmarks.restoreFromXml( root );
-		activeSourcesDialog.update();
-		viewer.requestRepaint();
-	}
-
-	public static void main( final String[] args )
-	{
-//		final String fn = "http://tomancak-mac-17.mpi-cbg.de:8080/openspim/";
-//		final String fn = "/Users/Pietzsch/Desktop/openspim/datasetHDF.xml";
-//		final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml";
-//		final String fn = "/Users/Pietzsch/Desktop/spimrec2/dataset.xml";
-//		final String fn = "/Users/pietzsch/Desktop/HisYFP-SPIM/dataset.xml";
-//		final String fn = "/Users/Pietzsch/Desktop/bdv example/drosophila 2.xml";
-//		final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml";
-//		final String fn = "/Users/Pietzsch/Desktop/data/catmaid.xml";
-//		final String fn = "src/main/resources/openconnectome-bock11-neariso.xml";
-//		final String fn = "/home/saalfeld/catmaid.xml";
-//		final String fn = "/home/saalfeld/catmaid-fafb00-v9.xml";
-//		final String fn = "/home/saalfeld/catmaid-fafb00-sample_A_cutout_3k.xml";
-//		final String fn = "/home/saalfeld/catmaid-thorsten.xml";
-//		final String fn = "/home/saalfeld/knossos-example.xml";
-//		final String fn = "/Users/Pietzsch/Desktop/data/catmaid-confocal.xml";
-//		final String fn = "/Users/pietzsch/desktop/data/BDV130418A325/BDV130418A325_NoTempReg.xml";
-//		final String fn = "/Users/pietzsch/Desktop/data/valia2/valia.xml";
-//		final String fn = "/Users/pietzsch/workspace/data/fast fly/111010_weber/combined.xml";
-//		final String fn = "/Users/pietzsch/workspace/data/mette/mette.xml";
-//		final String fn = "/Users/tobias/Desktop/openspim.xml";
-//		final String fn = "/Users/pietzsch/Desktop/data/fibsem.xml";
-//		final String fn = "/Users/pietzsch/Desktop/data/fibsem-remote.xml";
-//		final String fn = "/Users/pietzsch/Desktop/url-valia.xml";
-//		final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml";
-		final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml";
-//		final String fn = "/Volumes/projects/tomancak_lightsheet/Mette/ZeissZ1SPIM/Maritigrella/021013_McH2BsGFP_CAAX-mCherry/11-use/hdf5/021013_McH2BsGFP_CAAX-mCherry-11-use.xml";
-		try
-		{
-			System.setProperty( "apple.laf.useScreenMenuBar", "true" );
-
-			final BigDataViewer bdv = open( fn, new File( fn ).getName(), new ProgressWriterConsole(), ViewerOptions.options() );
-
-//			DumpInputConfig.writeToYaml( System.getProperty( "user.home" ) + "/.bdv/bdvkeyconfig.yaml", bdv.getViewerFrame() );
-		}
-		catch ( final Exception e )
-		{
-			e.printStackTrace();
-		}
-	}
+    protected final SetupAssignments setupAssignments;
+
+    protected final ManualTransformation manualTransformation;
+
+    protected final Bookmarks bookmarks;
+
+    protected final BrightnessDialog brightnessDialog;
+
+    protected final CropDialog cropDialog;
+
+    protected final RecordMovieDialog movieDialog;
+
+    protected final RecordMaxProjectionDialog movieMaxProjectDialog;
+
+    protected final VisibilityAndGroupingDialog activeSourcesDialog;
+
+    protected final HelpDialog helpDialog;
+
+    protected final ManualTransformationEditor manualTransformationEditor;
+
+    protected final BookmarksEditor bookmarkEditor;
+
+    protected final JFileChooser fileChooser;
+
+    protected File proposedSettingsFile;
+
+    public void toggleManualTransformation() {
+        manualTransformationEditor.toggle();
+    }
+
+    public void initSetBookmark() {
+        bookmarkEditor.initSetBookmark();
+    }
+
+    public void initGoToBookmark() {
+        bookmarkEditor.initGoToBookmark();
+    }
+
+    public void initGoToBookmarkRotation() {
+        bookmarkEditor.initGoToBookmarkRotation();
+    }
+
+    private static String createSetupName(final BasicViewSetup setup) {
+        if (setup.hasName())
+            return setup.getName();
+
+        String name = "";
+
+        final Angle angle = setup.getAttribute(Angle.class);
+        if (angle != null)
+            name += (name.isEmpty() ? "" : " ") + "a " + angle.getName();
+
+        final Channel channel = setup.getAttribute(Channel.class);
+        if (channel != null)
+            name += (name.isEmpty() ? "" : " ") + "c " + channel.getName();
+
+        return name;
+    }
+
+    /**
+     * Create standard converter from the given {@code type} to ARGB:
+     * <ul>
+     * <li>For {@code RealType}s a {@link RealARGBColorConverter} is
+     * returned.</li>
+     * <li>For {@code ARGBType}s a {@link ScaledARGBConverter.ARGB} is
+     * returned.</li>
+     * <li>For {@code VolatileARGBType}s a
+     * {@link ScaledARGBConverter.VolatileARGB} is returned.</li>
+     * </ul>
+     */
+    @SuppressWarnings("unchecked")
+    public static <T extends NumericType<T>> Converter<T, ARGBType> createConverterToARGB(final T type) {
+        if (type instanceof RealType) {
+            final RealType<?> t = (RealType<?>) type;
+            final double typeMin = Math.max(0, Math.min(t.getMinValue(), 65535));
+            final double typeMax = Math.max(0, Math.min(t.getMaxValue(), 65535));
+            return (Converter<T, ARGBType>) RealARGBColorConverter.create(t, typeMin, typeMax);
+        } else if (type instanceof ARGBType)
+            return (Converter<T, ARGBType>) new ScaledARGBConverter.ARGB(0, 255);
+        else if (type instanceof VolatileARGBType)
+            return (Converter<T, ARGBType>) new ScaledARGBConverter.VolatileARGB(0, 255);
+        else
+            throw new IllegalArgumentException("ImgLoader of type " + type.getClass() + " not supported.");
+    }
+
+    /**
+     * Create a {@code ConverterSetup} for the given {@code SourceAndConverter}.
+     * {@link SourceAndConverter#asVolatile() Nested volatile}
+     * {@code SourceAndConverter} are added to the {@code ConverterSetup} if
+     * present. If {@code SourceAndConverter} does not comprise a
+     * {@code ColorConverter}, returns {@code null}.
+     *
+     * @param soc     {@code SourceAndConverter} for which to create a
+     *                {@code ConverterSetup}
+     * @param setupId setupId of the created {@code ConverterSetup}
+     * @return a new {@code ConverterSetup} or {@code null}
+     */
+    public static ConverterSetup createConverterSetup(final SourceAndConverter<?> soc, final int setupId) {
+        final List<ColorConverter> converters = new ArrayList<>();
+
+        final Converter<?, ARGBType> c = soc.getConverter();
+        if (c instanceof ColorConverter)
+            converters.add((ColorConverter) c);
+
+        final SourceAndConverter<? extends Volatile<?>> vsoc = soc.asVolatile();
+        if (vsoc != null) {
+            final Converter<?, ARGBType> vc = vsoc.getConverter();
+            if (vc instanceof ColorConverter)
+                converters.add((ColorConverter) vc);
+        }
+
+        if (converters.isEmpty())
+            return null;
+        else
+            return new RealARGBColorConverterSetup(setupId, converters);
+    }
+
+    /**
+     * Decorate source with an extra transformation, that can be edited manually
+     * in this viewer. {@link SourceAndConverter#asVolatile() Nested volatile}
+     * {@code SourceAndConverter} are wrapped as well, if present.
+     */
+    public static <T, V extends Volatile<T>> SourceAndConverter<T> wrapWithTransformedSource(final SourceAndConverter<T> soc) {
+        if (soc.asVolatile() == null)
+            return new SourceAndConverter<>(new TransformedSource<>(soc.getSpimSource()), soc.getConverter());
+
+        @SuppressWarnings("unchecked") final SourceAndConverter<V> vsoc = (SourceAndConverter<V>) soc.asVolatile();
+        final TransformedSource<T> ts = new TransformedSource<>(soc.getSpimSource());
+        final TransformedSource<V> vts = new TransformedSource<>(vsoc.getSpimSource(), ts);
+        return new SourceAndConverter<>(ts, soc.getConverter(), new SourceAndConverter<>(vts, vsoc.getConverter()));
+    }
+
+    private static <T extends NumericType<T>, V extends Volatile<T> & NumericType<V>> void initSetupNumericType(
+            final AbstractSpimData<?> spimData,
+            final BasicViewSetup setup,
+            final List<ConverterSetup> converterSetups,
+            final List<SourceAndConverter<?>> sources) {
+        final int setupId = setup.getId();
+        final ViewerImgLoader imgLoader = (ViewerImgLoader) spimData.getSequenceDescription().getImgLoader();
+        @SuppressWarnings("unchecked") final ViewerSetupImgLoader<T, V> setupImgLoader =
+                (ViewerSetupImgLoader<T, V>) imgLoader.getSetupImgLoader(
+                setupId);
+        final T type = setupImgLoader.getImageType();
+        final V volatileType = setupImgLoader.getVolatileImageType();
+
+        if (!(type instanceof NumericType))
+            throw new IllegalArgumentException("ImgLoader of type " + type.getClass() + " not supported.");
+
+        final String setupName = createSetupName(setup);
+
+        SourceAndConverter<V> vsoc = null;
+        if (volatileType != null) {
+            final VolatileSpimSource<V> vs = new VolatileSpimSource<>(spimData, setupId, setupName);
+            vsoc = new SourceAndConverter<>(vs, createConverterToARGB(volatileType));
+        }
+
+        final SpimSource<T> s = new SpimSource<>(spimData, setupId, setupName);
+        final SourceAndConverter<T> soc = new SourceAndConverter<>(s, createConverterToARGB(type), vsoc);
+        final SourceAndConverter<T> tsoc = wrapWithTransformedSource(soc);
+        sources.add(tsoc);
+
+        final ConverterSetup converterSetup = createConverterSetup(tsoc, setupId);
+        if (converterSetup != null)
+            converterSetups.add(converterSetup);
+    }
+
+    public static void initSetups(
+            final AbstractSpimData<?> spimData,
+            final List<ConverterSetup> converterSetups,
+            final List<SourceAndConverter<?>> sources) {
+        for (final BasicViewSetup setup : spimData.getSequenceDescription().getViewSetupsOrdered())
+            initSetupNumericType(spimData, setup, converterSetups, sources);
+    }
+
+    /**
+     * @param converterSetups list of {@link ConverterSetup} that control min/max and color
+     *                        of sources.
+     * @param sources         list of pairs of source of some type and converter from that
+     *                        type to ARGB.
+     * @param spimData        may be null. The {@link AbstractSpimData} of the dataset (if
+     *                        there is one). If it exists, it is used to set up a "Crop"
+     *                        dialog.
+     * @param numTimepoints   the number of timepoints in the dataset.
+     * @param cache           handle to cache. This is used to control io timing.
+     * @param windowTitle     title of the viewer window.
+     * @param progressWriter  a {@link ProgressWriter} to which BDV may report progress
+     *                        (currently only used in the "Record Movie" dialog).
+     * @param options         optional parameters.
+     */
+    public BigDataViewer(
+            final ArrayList<ConverterSetup> converterSetups,
+            final ArrayList<SourceAndConverter<?>> sources,
+            final AbstractSpimData<?> spimData,
+            final int numTimepoints,
+            final CacheControl cache,
+            final String windowTitle,
+            final ProgressWriter progressWriter,
+            final ViewerOptions options) {
+        final InputTriggerConfig inputTriggerConfig = getInputTriggerConfig(options);
+        if (options.values.getTransformEventHandlerFactory() instanceof BehaviourTransformEventHandlerFactory)
+            ((BehaviourTransformEventHandlerFactory<?>) options.values.getTransformEventHandlerFactory()).setConfig(
+                    inputTriggerConfig);
+
+        viewerFrame = new ViewerFrame(sources, numTimepoints, cache, options);
+        if (windowTitle != null)
+            viewerFrame.setTitle(windowTitle);
+        viewer = viewerFrame.getViewerPanel();
+
+        for (final ConverterSetup cs : converterSetups)
+            cs.setViewer(viewer);
+
+        manualTransformation = new ManualTransformation(viewer);
+        manualTransformationEditor = new ManualTransformationEditor(viewer, viewerFrame.getKeybindings());
+
+        bookmarks = new Bookmarks();
+        bookmarkEditor = new BookmarksEditor(viewer, viewerFrame.getKeybindings(), bookmarks);
+
+        setupAssignments = new SetupAssignments(converterSetups, 0, 65535);
+        if (setupAssignments.getMinMaxGroups().size() > 0) {
+            final MinMaxGroup group = setupAssignments.getMinMaxGroups().get(0);
+            for (final ConverterSetup setup : setupAssignments.getConverterSetups())
+                setupAssignments.moveSetupToGroup(setup, group);
+        }
+
+        brightnessDialog = new BrightnessDialog(viewerFrame, setupAssignments);
+
+        if (spimData != null)
+            viewer.getSourceInfoOverlayRenderer().setTimePointsOrdered(spimData.getSequenceDescription().getTimePoints().getTimePointsOrdered());
+
+        cropDialog = (spimData == null) ? null : new CropDialog(viewerFrame, viewer, spimData.getSequenceDescription());
+
+        movieDialog = new RecordMovieDialog(viewerFrame, viewer, progressWriter);
+        // this is just to get updates of window size:
+        viewer.getDisplay().addOverlayRenderer(movieDialog);
+
+        movieMaxProjectDialog = new RecordMaxProjectionDialog(viewerFrame, viewer, progressWriter);
+        // this is just to get updates of window size:
+        viewer.getDisplay().addOverlayRenderer(movieMaxProjectDialog);
+
+        activeSourcesDialog = new VisibilityAndGroupingDialog(viewerFrame, viewer.getVisibilityAndGrouping());
+
+        helpDialog = new HelpDialog(viewerFrame);
+
+        fileChooser = new JFileChooser();
+        fileChooser.setFileFilter(new FileFilter() {
+            @Override
+            public String getDescription() {
+                return "xml files";
+            }
+
+            @Override
+            public boolean accept(final File f) {
+                if (f.isDirectory())
+                    return true;
+                if (f.isFile()) {
+                    final String s = f.getName();
+                    final int i = s.lastIndexOf('.');
+                    if (i > 0 && i < s.length() - 1) {
+                        final String ext = s.substring(i + 1).toLowerCase();
+                        return ext.equals("xml");
+                    }
+                }
+                return false;
+            }
+        });
+
+        NavigationActions.installActionBindings(viewerFrame.getKeybindings(), viewer, inputTriggerConfig);
+        BigDataViewerActions.installActionBindings(viewerFrame.getKeybindings(), this, inputTriggerConfig);
+
+        final JMenuBar menubar = new JMenuBar();
+        JMenu menu = new JMenu("File");
+        menubar.add(menu);
+
+        final ActionMap actionMap = viewerFrame.getKeybindings().getConcatenatedActionMap();
+        final JMenuItem miLoadSettings = new JMenuItem(actionMap.get(BigDataViewerActions.LOAD_SETTINGS));
+        miLoadSettings.setText("Load settings");
+        menu.add(miLoadSettings);
+
+        final JMenuItem miSaveSettings = new JMenuItem(actionMap.get(BigDataViewerActions.SAVE_SETTINGS));
+        miSaveSettings.setText("Save settings");
+        menu.add(miSaveSettings);
+
+        menu = new JMenu("Settings");
+        menubar.add(menu);
+
+        final JMenuItem miBrightness = new JMenuItem(actionMap.get(BigDataViewerActions.BRIGHTNESS_SETTINGS));
+        miBrightness.setText("Brightness & Color");
+        menu.add(miBrightness);
+
+        final JMenuItem miVisibility = new JMenuItem(actionMap.get(BigDataViewerActions.VISIBILITY_AND_GROUPING));
+        miVisibility.setText("Visibility & Grouping");
+        menu.add(miVisibility);
+
+        menu = new JMenu("Tools");
+        menubar.add(menu);
+
+        if (cropDialog != null) {
+            final JMenuItem miCrop = new JMenuItem(actionMap.get(BigDataViewerActions.CROP));
+            miCrop.setText("Crop");
+            menu.add(miCrop);
+        }
+
+        final JMenuItem miMovie = new JMenuItem(actionMap.get(BigDataViewerActions.RECORD_MOVIE));
+        miMovie.setText("Record Movie");
+        menu.add(miMovie);
+
+        final JMenuItem miMaxProjectMovie =
+                new JMenuItem(actionMap.get(BigDataViewerActions.RECORD_MAX_PROJECTION_MOVIE));
+        miMaxProjectMovie.setText("Record Max-Projection Movie");
+        menu.add(miMaxProjectMovie);
+
+        final JMenuItem miManualTransform = new JMenuItem(actionMap.get(BigDataViewerActions.MANUAL_TRANSFORM));
+        miManualTransform.setText("Manual Transform");
+        menu.add(miManualTransform);
+
+        menu = new JMenu("Help");
+        menubar.add(menu);
+
+        final JMenuItem miHelp = new JMenuItem(actionMap.get(BigDataViewerActions.SHOW_HELP));
+        miHelp.setText("Show Help");
+        menu.add(miHelp);
+
+        viewerFrame.setJMenuBar(menubar);
+    }
+
+    public static BigDataViewer open(final AbstractSpimData<?> spimData,
+                                     final String windowTitle,
+                                     final ProgressWriter progressWriter,
+                                     final ViewerOptions options) {
+        if (WrapBasicImgLoader.wrapImgLoaderIfNecessary(spimData)) {
+            System.err.println(
+                    "WARNING:\nOpening <SpimData> dataset that is not suited for interactive browsing.\nConsider " +
+                            "resaving as HDF5 for better performance.");
+        }
+
+        final ArrayList<ConverterSetup> converterSetups = new ArrayList<>();
+        final ArrayList<SourceAndConverter<?>> sources = new ArrayList<>();
+        initSetups(spimData, converterSetups, sources);
+
+        final AbstractSequenceDescription<?, ?, ?> seq = spimData.getSequenceDescription();
+        final int numTimepoints = seq.getTimePoints().size();
+        final CacheControl cache = ((ViewerImgLoader) seq.getImgLoader()).getCacheControl();
+
+        final BigDataViewer bdv = new BigDataViewer(converterSetups,
+                                                    sources,
+                                                    spimData,
+                                                    numTimepoints,
+                                                    cache,
+                                                    windowTitle,
+                                                    progressWriter,
+                                                    options);
+
+        WrapBasicImgLoader.removeWrapperIfPresent(spimData);
+
+        bdv.viewerFrame.setVisible(true);
+        InitializeViewerState.initTransform(bdv.viewer);
+        return bdv;
+    }
+
+    public static BigDataViewer open(final String xmlFilename,
+                                     final String windowTitle,
+                                     final ProgressWriter progressWriter,
+                                     final ViewerOptions options,
+                                     final boolean allowCompression) throws SpimDataException {
+
+        final XmlIoSpimDataMinimal xmlIoSpimDataMinimal = new XmlIoSpimDataMinimal();
+        xmlIoSpimDataMinimal.setAllowCompression(allowCompression);
+        final SpimDataMinimal spimData = xmlIoSpimDataMinimal.load(xmlFilename);
+        final BigDataViewer bdv = open(spimData, windowTitle, progressWriter, options);
+        if (!bdv.tryLoadSettings(xmlFilename))
+            InitializeViewerState.initBrightness(0.001, 0.999, bdv.viewer, bdv.setupAssignments);
+        return bdv;
+    }
+
+    public static BigDataViewer open(
+            final ArrayList<ConverterSetup> converterSetups,
+            final ArrayList<SourceAndConverter<?>> sources,
+            final int numTimepoints,
+            final CacheControl cache,
+            final String windowTitle,
+            final ProgressWriter progressWriter,
+            final ViewerOptions options) {
+        final BigDataViewer bdv = new BigDataViewer(converterSetups,
+                                                    sources,
+                                                    null,
+                                                    numTimepoints,
+                                                    cache,
+                                                    windowTitle,
+                                                    progressWriter,
+                                                    options);
+        bdv.viewerFrame.setVisible(true);
+        InitializeViewerState.initTransform(bdv.viewer);
+        return bdv;
+    }
+
+    public ViewerPanel getViewer() {
+        return viewer;
+    }
+
+    public ViewerFrame getViewerFrame() {
+        return viewerFrame;
+    }
+
+    public SetupAssignments getSetupAssignments() {
+        return setupAssignments;
+    }
+
+    public ManualTransformationEditor getManualTransformEditor() {
+        return manualTransformationEditor;
+    }
+
+    public boolean tryLoadSettings(final String xmlFilename) {
+        proposedSettingsFile = null;
+        if (xmlFilename.startsWith("http://")) {
+            // load settings.xml from the BigDataServer
+            final String settings = xmlFilename + "settings";
+            {
+                try {
+                    loadSettings(settings);
+                    return true;
+                } catch (final FileNotFoundException e) {
+                } catch (final Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        } else if (xmlFilename.endsWith(".xml")) {
+            final String settings = xmlFilename.substring(0,
+                                                          xmlFilename.length() - ".xml".length()) + ".settings" +
+                    ".xml";
+            proposedSettingsFile = new File(settings);
+            if (proposedSettingsFile.isFile()) {
+                try {
+                    loadSettings(settings);
+                    return true;
+                } catch (final Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return false;
+    }
+
+    public void saveSettings() {
+        fileChooser.setSelectedFile(proposedSettingsFile);
+        final int returnVal = fileChooser.showSaveDialog(null);
+        if (returnVal == JFileChooser.APPROVE_OPTION) {
+            proposedSettingsFile = fileChooser.getSelectedFile();
+            try {
+                saveSettings(proposedSettingsFile.getCanonicalPath());
+            } catch (final IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public void saveSettings(final String xmlFilename) throws IOException {
+        final Element root = new Element("Settings");
+        root.addContent(viewer.stateToXml());
+        root.addContent(setupAssignments.toXml());
+        root.addContent(manualTransformation.toXml());
+        root.addContent(bookmarks.toXml());
+        final Document doc = new Document(root);
+        final XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
+        xout.output(doc, new FileWriter(xmlFilename));
+    }
+
+    /**
+     * If {@code options} doesn't define a {@link InputTriggerConfig}, try to
+     * load it from files in this order:
+     * <ol>
+     * <li>"bdvkeyconfig.yaml" in the current directory.
+     * <li>".bdv/bdvkeyconfig.yaml" in the user's home directory.
+     * <li>legacy "bigdataviewer.keys.properties" in current directory (will be
+     * also written to "bdvkeyconfig.yaml").
+     * </ol>
+     *
+     * @param options
+     * @return
+     */
+    public static InputTriggerConfig getInputTriggerConfig(final ViewerOptions options) {
+        InputTriggerConfig conf = options.values.getInputTriggerConfig();
+
+        // try "bdvkeyconfig.yaml" in current directory
+        if (conf == null && new File("bdvkeyconfig.yaml").isFile()) {
+            try {
+                conf = new InputTriggerConfig(YamlConfigIO.read("bdvkeyconfig.yaml"));
+            } catch (final IOException e) {
+            }
+        }
+
+        // try "~/.bdv/bdvkeyconfig.yaml"
+        if (conf == null) {
+            final String fn = System.getProperty("user.home") + "/.bdv/bdvkeyconfig.yaml";
+            if (new File(fn).isFile()) {
+                try {
+                    conf = new InputTriggerConfig(YamlConfigIO.read(fn));
+                } catch (final IOException e) {
+                }
+            }
+        }
+
+        if (conf == null) {
+            conf = new InputTriggerConfig();
+        }
+
+        return conf;
+    }
+
+    public void loadSettings() {
+        fileChooser.setSelectedFile(proposedSettingsFile);
+        final int returnVal = fileChooser.showOpenDialog(null);
+        if (returnVal == JFileChooser.APPROVE_OPTION) {
+            proposedSettingsFile = fileChooser.getSelectedFile();
+            try {
+                loadSettings(proposedSettingsFile.getCanonicalPath());
+            } catch (final Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public void loadSettings(final String xmlFilename) throws IOException, JDOMException {
+        final SAXBuilder sax = new SAXBuilder();
+        final Document doc = sax.build(xmlFilename);
+        final Element root = doc.getRootElement();
+        viewer.stateFromXml(root);
+        setupAssignments.restoreFromXml(root);
+        manualTransformation.restoreFromXml(root);
+        bookmarks.restoreFromXml(root);
+        activeSourcesDialog.update();
+        viewer.requestRepaint();
+    }
+
+    public static void main(final String[] args) {
+        //		final String fn = "http://tomancak-mac-17.mpi-cbg.de:8080/openspim/";
+        //		final String fn = "/Users/Pietzsch/Desktop/openspim/datasetHDF.xml";
+        //		final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml";
+        //		final String fn = "/Users/Pietzsch/Desktop/spimrec2/dataset.xml";
+        //		final String fn = "/Users/pietzsch/Desktop/HisYFP-SPIM/dataset.xml";
+        //		final String fn = "/Users/Pietzsch/Desktop/bdv example/drosophila 2.xml";
+        //		final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml";
+        //		final String fn = "/Users/Pietzsch/Desktop/data/catmaid.xml";
+        //		final String fn = "src/main/resources/openconnectome-bock11-neariso.xml";
+        //		final String fn = "/home/saalfeld/catmaid.xml";
+        //		final String fn = "/home/saalfeld/catmaid-fafb00-v9.xml";
+        //		final String fn = "/home/saalfeld/catmaid-fafb00-sample_A_cutout_3k.xml";
+        //		final String fn = "/home/saalfeld/catmaid-thorsten.xml";
+        //		final String fn = "/home/saalfeld/knossos-example.xml";
+        //		final String fn = "/Users/Pietzsch/Desktop/data/catmaid-confocal.xml";
+        //		final String fn = "/Users/pietzsch/desktop/data/BDV130418A325/BDV130418A325_NoTempReg.xml";
+        //		final String fn = "/Users/pietzsch/Desktop/data/valia2/valia.xml";
+        //		final String fn = "/Users/pietzsch/workspace/data/fast fly/111010_weber/combined.xml";
+        //		final String fn = "/Users/pietzsch/workspace/data/mette/mette.xml";
+        //		final String fn = "/Users/tobias/Desktop/openspim.xml";
+        //		final String fn = "/Users/pietzsch/Desktop/data/fibsem.xml";
+        //		final String fn = "/Users/pietzsch/Desktop/data/fibsem-remote.xml";
+        //		final String fn = "/Users/pietzsch/Desktop/url-valia.xml";
+        //		final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml";
+        //		final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml";
+        //		final String fn = "/Volumes/projects/tomancak_lightsheet/Mette/ZeissZ1SPIM/Maritigrella/021013_McH2BsGFP_CAAX-mCherry/11-use/hdf5/021013_McH2BsGFP_CAAX-mCherry-11-use.xml";
+        if (args.length < 1) {
+            System.err.println("Provide path.");
+            return;
+        }
+        final String fn = args[0];
+        final boolean allowCompression = (args.length > 1) && (args[1].equals("-qcmp"));
+        try {
+            System.setProperty("apple.laf.useScreenMenuBar", "true");
+
+            final BigDataViewer bdv = open(fn,
+                                           "Server test",
+                                           new ProgressWriterConsole(),
+                                           ViewerOptions.options(),
+                                           allowCompression);
+
+            //			DumpInputConfig.writeToYaml( System.getProperty( "user.home" ) + "/.bdv/bdvkeyconfig.yaml", bdv.getViewerFrame() );
+        } catch (final Exception e) {
+            e.printStackTrace();
+        }
+    }
 }
diff --git a/src/main/java/bdv/img/remote/RemoteImageLoader.java b/src/main/java/bdv/img/remote/RemoteImageLoader.java
index b485a68a4bed4b180909602d39fa5fe7d82de5db..2d5247a2995966d20952922aeada3d75252c9188 100644
--- a/src/main/java/bdv/img/remote/RemoteImageLoader.java
+++ b/src/main/java/bdv/img/remote/RemoteImageLoader.java
@@ -7,13 +7,13 @@
  * %%
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -29,13 +29,8 @@
  */
 package bdv.img.remote;
 
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.HashMap;
-
-import com.google.gson.GsonBuilder;
-
+import azgracompress.cache.ICacheFile;
+import azgracompress.cache.QuantizationCacheManager;
 import bdv.AbstractViewerSetupImgLoader;
 import bdv.ViewerImgLoader;
 import bdv.img.cache.VolatileCachedCellImg;
@@ -44,6 +39,7 @@ import bdv.img.hdf5.DimsAndExistence;
 import bdv.img.hdf5.MipmapInfo;
 import bdv.img.hdf5.ViewLevelId;
 import bdv.util.ConstantRandomAccessible;
+import com.google.gson.GsonBuilder;
 import mpicbg.spim.data.generic.sequence.ImgLoaderHint;
 import net.imglib2.FinalInterval;
 import net.imglib2.RandomAccessibleInterval;
@@ -57,211 +53,213 @@ import net.imglib2.type.volatiles.VolatileUnsignedShortType;
 import net.imglib2.util.IntervalIndexer;
 import net.imglib2.view.Views;
 
-public class RemoteImageLoader implements ViewerImgLoader
-{
-	protected String baseUrl;
-
-	protected RemoteImageLoaderMetaData metadata;
-
-	protected HashMap< ViewLevelId, int[] > cellsDimensions;
-
-	protected VolatileGlobalCellCache cache;
-
-	protected RemoteVolatileShortArrayLoader shortLoader;
-
-	/**
-	 * TODO
-	 */
-	protected final HashMap< Integer, SetupImgLoader > setupImgLoaders;
-
-	public RemoteImageLoader( final String baseUrl ) throws IOException
-	{
-		this( baseUrl, true );
-	}
-
-	public RemoteImageLoader( final String baseUrl, final boolean doOpen ) throws IOException
-	{
-		this.baseUrl = baseUrl;
-		setupImgLoaders = new HashMap<>();
-		if ( doOpen )
-			open();
-	}
-
-	@Override
-	public SetupImgLoader getSetupImgLoader( final int setupId )
-	{
-		tryopen();
-		return setupImgLoaders.get( setupId );
-	}
-
-	private boolean isOpen = false;
-
-	private void open() throws IOException
-	{
-		if ( ! isOpen )
-		{
-			synchronized ( this )
-			{
-				if ( isOpen )
-					return;
-				isOpen = true;
-
-				final URL url = new URL( baseUrl + "?p=init" );
-				final GsonBuilder gsonBuilder = new GsonBuilder();
-				gsonBuilder.registerTypeAdapter( AffineTransform3D.class, new AffineTransform3DJsonSerializer() );
-				metadata = gsonBuilder.create().fromJson(
-						new InputStreamReader( url.openStream() ),
-						RemoteImageLoaderMetaData.class );
-				shortLoader = new RemoteVolatileShortArrayLoader( this );
-				cache = new VolatileGlobalCellCache( metadata.maxNumLevels, 10 );
-				cellsDimensions = metadata.createCellsDimensions();
-				for ( final int setupId : metadata.perSetupMipmapInfo.keySet() )
-					setupImgLoaders.put( setupId, new SetupImgLoader( setupId ) );
-			}
-		}
-	}
-
-	private void tryopen()
-	{
-		try
-		{
-			open();
-		}
-		catch ( final IOException e )
-		{
-			throw new RuntimeException( e );
-		}
-	}
-
-	@Override
-	public VolatileGlobalCellCache getCacheControl()
-	{
-		tryopen();
-		return cache;
-	}
-
-	public MipmapInfo getMipmapInfo( final int setupId )
-	{
-		tryopen();
-		return metadata.perSetupMipmapInfo.get( setupId );
-	}
-
-	/**
-	 * Checks whether the given image data is present on the server.
-	 *
-	 * @return true, if the given image data is present.
-	 */
-	public boolean existsImageData( final ViewLevelId id )
-	{
-		return getDimsAndExistence( id ).exists();
-	}
-
-	/**
-	 * For images that are missing in the hdf5, a constant image is created. If
-	 * the dimension of the missing image is known (see
-	 * {@link #getDimsAndExistence(ViewLevelId)}) then use that. Otherwise
-	 * create a 1x1x1 image.
-	 */
-	protected < T > RandomAccessibleInterval< T > getMissingDataImage( final ViewLevelId id, final T constant )
-	{
-		final long[] d = getDimsAndExistence( id ).getDimensions();
-		return Views.interval( new ConstantRandomAccessible<>( constant, 3 ), new FinalInterval( d ) );
-	}
-
-	public DimsAndExistence getDimsAndExistence( final ViewLevelId id )
-	{
-		tryopen();
-		return metadata.dimsAndExistence.get( id );
-	}
-
-	int getCellIndex( final int timepoint, final int setup, final int level, final long[] globalPosition )
-	{
-		final int[] cellDims = cellsDimensions.get( new ViewLevelId( timepoint, setup, level ) );
-		final int[] cellSize = getMipmapInfo( setup ).getSubdivisions()[ level ];
-		final int[] cellPos = new int[] {
-				( int ) globalPosition[ 0 ] / cellSize[ 0 ],
-				( int ) globalPosition[ 1 ] / cellSize[ 1 ],
-				( int ) globalPosition[ 2 ] / cellSize[ 2 ] };
-		return IntervalIndexer.positionToIndex( cellPos, cellDims );
-	}
-
-	/**
-	 * Create a {@link VolatileCachedCellImg} backed by the cache. The
-	 * {@code type} should be either {@link UnsignedShortType} and
-	 * {@link VolatileUnsignedShortType}.
-	 */
-	protected < T extends NativeType< T > > RandomAccessibleInterval< T > prepareCachedImage(
-			final ViewLevelId id,
-			final LoadingStrategy loadingStrategy,
-			final T type )
-	{
-		tryopen();
-		if ( cache == null )
-			throw new RuntimeException( "no connection open" );
-
-//		final ViewLevelId id = new ViewLevelId( timepointId, setupId, level );
-		if ( ! existsImageData( id ) )
-		{
-			System.err.println(	String.format(
-					"image data for timepoint %d setup %d level %d could not be found.",
-					id.getTimePointId(), id.getViewSetupId(), id.getLevel() ) );
-			return getMissingDataImage( id, type );
-		}
-
-		final int timepointId = id.getTimePointId();
-		final int setupId = id.getViewSetupId();
-		final int level = id.getLevel();
-		final MipmapInfo mipmapInfo = metadata.perSetupMipmapInfo.get( setupId );
-
-		final long[] dimensions = metadata.dimsAndExistence.get( id ).getDimensions();
-		final int[] cellDimensions = mipmapInfo.getSubdivisions()[ level ];
-		final CellGrid grid = new CellGrid( dimensions, cellDimensions );
-
-		final int priority = mipmapInfo.getMaxLevel() - level;
-		final CacheHints cacheHints = new CacheHints( loadingStrategy, priority, false );
-		return cache.createImg( grid, timepointId, setupId, level, cacheHints, shortLoader, type );
-	}
-
-	public class SetupImgLoader extends AbstractViewerSetupImgLoader< UnsignedShortType, VolatileUnsignedShortType >
-	{
-		private final int setupId;
-
-		protected SetupImgLoader( final int setupId )
-		{
-			super( new UnsignedShortType(), new VolatileUnsignedShortType() );
-			this.setupId = setupId;
-		}
-
-		@Override
-		public RandomAccessibleInterval< UnsignedShortType > getImage( final int timepointId, final int level, final ImgLoaderHint... hints )
-		{
-			final ViewLevelId id = new ViewLevelId( timepointId, setupId, level );
-			return prepareCachedImage( id, LoadingStrategy.BLOCKING, type );
-		}
-
-		@Override
-		public RandomAccessibleInterval< VolatileUnsignedShortType > getVolatileImage( final int timepointId, final int level, final ImgLoaderHint... hints )
-		{
-			final ViewLevelId id = new ViewLevelId( timepointId, setupId, level );
-			return prepareCachedImage( id, LoadingStrategy.BUDGETED, volatileType );
-		}
-
-		@Override
-		public double[][] getMipmapResolutions()
-		{
-			return getMipmapInfo( setupId ).getResolutions();
-		}
-
-		@Override
-		public AffineTransform3D[] getMipmapTransforms()
-		{
-			return getMipmapInfo( setupId ).getTransforms();
-		}
-
-		@Override
-		public int numMipmapLevels()
-		{
-			return getMipmapInfo( setupId ).getNumLevels();
-		}
-	}
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.HashMap;
+
+public class RemoteImageLoader implements ViewerImgLoader {
+    protected String baseUrl;
+
+    protected RemoteImageLoaderMetaData metadata;
+
+    protected HashMap<ViewLevelId, int[]> cellsDimensions;
+
+    protected VolatileGlobalCellCache cache;
+
+    protected RemoteVolatileShortArrayLoader shortLoader;
+
+    private boolean allowCompression;
+    /**
+     * TODO
+     */
+    protected final HashMap<Integer, SetupImgLoader> setupImgLoaders;
+
+    public RemoteImageLoader(final String baseUrl) throws IOException {
+        this(baseUrl, true);
+    }
+
+    public RemoteImageLoader(final String baseUrl,
+                             final boolean doOpen) throws IOException {
+        this.baseUrl = baseUrl;
+        setupImgLoaders = new HashMap<>();
+        if (doOpen)
+            open();
+    }
+
+    @Override
+    public SetupImgLoader getSetupImgLoader(final int setupId) {
+        tryopen();
+        return setupImgLoaders.get(setupId);
+    }
+
+    private boolean isOpen = false;
+
+    private void open() throws IOException {
+        if (!isOpen) {
+            synchronized (this) {
+                if (isOpen)
+                    return;
+                isOpen = true;
+
+                if (allowCompression) {
+                    receiveCompressionInfo();
+                }
+
+                final URL url = new URL(baseUrl + "?p=init");
+                final GsonBuilder gsonBuilder = new GsonBuilder();
+                gsonBuilder.registerTypeAdapter(AffineTransform3D.class, new AffineTransform3DJsonSerializer());
+                metadata = gsonBuilder.create().fromJson(
+                        new InputStreamReader(url.openStream()),
+                        RemoteImageLoaderMetaData.class);
+                shortLoader = new RemoteVolatileShortArrayLoader(this);
+                cache = new VolatileGlobalCellCache(metadata.maxNumLevels, 10);
+                cellsDimensions = metadata.createCellsDimensions();
+                for (final int setupId : metadata.perSetupMipmapInfo.keySet())
+                    setupImgLoaders.put(setupId, new SetupImgLoader(setupId));
+            }
+        }
+    }
+
+    public boolean shouldAllowCompression() {
+        return allowCompression;
+    }
+
+    public void setAllowCompression(final boolean compressionEnabled) {
+        this.allowCompression = compressionEnabled;
+    }
+
+    private void receiveCompressionInfo() throws IOException {
+        final URL url = new URL(baseUrl + "?p=init_qcmp");
+        final ICacheFile cachedCodebook = QuantizationCacheManager.readCacheFile(url.openStream());
+        System.out.println("\u001b[33mRemoteImageLoader::receiveCompressionInfo() - received cache file. '" + cachedCodebook.toString() + "'\u001b[0m");
+    }
+
+
+    private void tryopen() {
+        try {
+            open();
+        } catch (final IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public VolatileGlobalCellCache getCacheControl() {
+        tryopen();
+        return cache;
+    }
+
+    public MipmapInfo getMipmapInfo(final int setupId) {
+        tryopen();
+        return metadata.perSetupMipmapInfo.get(setupId);
+    }
+
+    /**
+     * Checks whether the given image data is present on the server.
+     *
+     * @return true, if the given image data is present.
+     */
+    public boolean existsImageData(final ViewLevelId id) {
+        return getDimsAndExistence(id).exists();
+    }
+
+    /**
+     * For images that are missing in the hdf5, a constant image is created. If
+     * the dimension of the missing image is known (see
+     * {@link #getDimsAndExistence(ViewLevelId)}) then use that. Otherwise
+     * create a 1x1x1 image.
+     */
+    protected <T> RandomAccessibleInterval<T> getMissingDataImage(final ViewLevelId id, final T constant) {
+        final long[] d = getDimsAndExistence(id).getDimensions();
+        return Views.interval(new ConstantRandomAccessible<>(constant, 3), new FinalInterval(d));
+    }
+
+    public DimsAndExistence getDimsAndExistence(final ViewLevelId id) {
+        tryopen();
+        return metadata.dimsAndExistence.get(id);
+    }
+
+    int getCellIndex(final int timepoint, final int setup, final int level, final long[] globalPosition) {
+        final int[] cellDims = cellsDimensions.get(new ViewLevelId(timepoint, setup, level));
+        final int[] cellSize = getMipmapInfo(setup).getSubdivisions()[level];
+        final int[] cellPos = new int[]{
+                (int) globalPosition[0] / cellSize[0],
+                (int) globalPosition[1] / cellSize[1],
+                (int) globalPosition[2] / cellSize[2]};
+        return IntervalIndexer.positionToIndex(cellPos, cellDims);
+    }
+
+    /**
+     * Create a {@link VolatileCachedCellImg} backed by the cache. The
+     * {@code type} should be either {@link UnsignedShortType} and
+     * {@link VolatileUnsignedShortType}.
+     */
+    protected <T extends NativeType<T>> RandomAccessibleInterval<T> prepareCachedImage(
+            final ViewLevelId id,
+            final LoadingStrategy loadingStrategy,
+            final T type) {
+        tryopen();
+        if (cache == null)
+            throw new RuntimeException("no connection open");
+
+        //		final ViewLevelId id = new ViewLevelId( timepointId, setupId, level );
+        if (!existsImageData(id)) {
+            System.err.println(String.format(
+                    "image data for timepoint %d setup %d level %d could not be found.",
+                    id.getTimePointId(), id.getViewSetupId(), id.getLevel()));
+            return getMissingDataImage(id, type);
+        }
+
+        final int timepointId = id.getTimePointId();
+        final int setupId = id.getViewSetupId();
+        final int level = id.getLevel();
+        final MipmapInfo mipmapInfo = metadata.perSetupMipmapInfo.get(setupId);
+
+        final long[] dimensions = metadata.dimsAndExistence.get(id).getDimensions();
+        final int[] cellDimensions = mipmapInfo.getSubdivisions()[level];
+        final CellGrid grid = new CellGrid(dimensions, cellDimensions);
+
+        final int priority = mipmapInfo.getMaxLevel() - level;
+        final CacheHints cacheHints = new CacheHints(loadingStrategy, priority, false);
+        return cache.createImg(grid, timepointId, setupId, level, cacheHints, shortLoader, type);
+    }
+
+    public class SetupImgLoader extends AbstractViewerSetupImgLoader<UnsignedShortType, VolatileUnsignedShortType> {
+        private final int setupId;
+
+        protected SetupImgLoader(final int setupId) {
+            super(new UnsignedShortType(), new VolatileUnsignedShortType());
+            this.setupId = setupId;
+        }
+
+        @Override
+        public RandomAccessibleInterval<UnsignedShortType> getImage(final int timepointId, final int level, final ImgLoaderHint... hints) {
+            final ViewLevelId id = new ViewLevelId(timepointId, setupId, level);
+            return prepareCachedImage(id, LoadingStrategy.BLOCKING, type);
+        }
+
+        @Override
+        public RandomAccessibleInterval<VolatileUnsignedShortType> getVolatileImage(final int timepointId,
+                                                                                    final int level,
+                                                                                    final ImgLoaderHint... hints) {
+            final ViewLevelId id = new ViewLevelId(timepointId, setupId, level);
+            return prepareCachedImage(id, LoadingStrategy.BUDGETED, volatileType);
+        }
+
+        @Override
+        public double[][] getMipmapResolutions() {
+            return getMipmapInfo(setupId).getResolutions();
+        }
+
+        @Override
+        public AffineTransform3D[] getMipmapTransforms() {
+            return getMipmapInfo(setupId).getTransforms();
+        }
+
+        @Override
+        public int numMipmapLevels() {
+            return getMipmapInfo(setupId).getNumLevels();
+        }
+    }
 }
diff --git a/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java b/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java
index e6da148df6fa8b212dbffed20e0edc525fc26a43..ea2582129362c2f21434d88805fc8e97b6539167 100644
--- a/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java
+++ b/src/main/java/bdv/img/remote/RemoteVolatileShortArrayLoader.java
@@ -7,13 +7,13 @@
  * %%
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -53,24 +53,31 @@ public class RemoteVolatileShortArrayLoader implements CacheArrayLoader< Volatil
 		final short[] data = new short[ dimensions[ 0 ] * dimensions[ 1 ] * dimensions[ 2 ] ];
 		try
 		{
-			final URL url = new URL( String.format( "%s?p=cell/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
-					imgLoader.baseUrl,
-					index,
-					timepoint,
-					setup,
-					level,
-					dimensions[ 0 ],
-					dimensions[ 1 ],
-					dimensions[ 2 ],
-					min[ 0 ],
-					min[ 1 ],
-					min[ 2 ] ) );
+			final URL url = new URL(String.format("%s?p=cell/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
+												  imgLoader.baseUrl,
+												  index,
+												  timepoint,
+												  setup,
+												  level,
+												  dimensions[0],
+												  dimensions[1],
+												  dimensions[2],
+												  min[0],
+												  min[1],
+												  min[2]));
 			final InputStream s = url.openStream();
-			final byte[] buf = new byte[ data.length * 2 ];
-			for ( int i = 0, l = s.read( buf, 0, buf.length ); l > 0; i += l, l = s.read( buf, i, buf.length - i ) );
-			for ( int i = 0, j = 0; i < data.length; ++i, j += 2 )
-				data[ i ] = ( short ) ( ( ( buf[ j ] & 0xff ) << 8 ) | ( buf[ j + 1 ] & 0xff ) );
-			s.close();
+//            System.out.println("Request URL=" + url);
+            final byte[] buf = new byte[data.length * 2];
+
+            // NOTE(Moravec): Decompression place!
+			for (int i = 0, l = s.read(buf, 0, buf.length);
+				 l > 0;
+				 i += l, l = s.read(buf, i, buf.length - i))
+			;
+
+            for (int i = 0, j = 0; i < data.length; ++i, j += 2)
+                data[i] = (short) (((buf[j] & 0xff) << 8) | (buf[j + 1] & 0xff));
+            s.close();
 		}
 		catch ( final MalformedURLException e )
 		{
diff --git a/src/main/java/bdv/img/remote/XmlIoRemoteImageLoader.java b/src/main/java/bdv/img/remote/XmlIoRemoteImageLoader.java
index 3cf39d3afda23219f8d7bd47fbba570d83f3fe63..6b9b8a03b3687b30b4f0345429dfe455298770e0 100644
--- a/src/main/java/bdv/img/remote/XmlIoRemoteImageLoader.java
+++ b/src/main/java/bdv/img/remote/XmlIoRemoteImageLoader.java
@@ -7,13 +7,13 @@
  * %%
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -41,31 +41,36 @@ import mpicbg.spim.data.generic.sequence.XmlIoBasicImgLoader;
 
 import org.jdom2.Element;
 
-@ImgLoaderIo( format = "bdv.remote", type = RemoteImageLoader.class )
-public class XmlIoRemoteImageLoader implements XmlIoBasicImgLoader< RemoteImageLoader >
-{
+@ImgLoaderIo(format = "bdv.remote", type = RemoteImageLoader.class)
+public class XmlIoRemoteImageLoader implements XmlIoBasicImgLoader<RemoteImageLoader> {
 
-	@Override
-	public Element toXml( final RemoteImageLoader imgLoader, final File basePath )
-	{
-		final Element elem = new Element( "ImageLoader" );
-		elem.setAttribute( IMGLOADER_FORMAT_ATTRIBUTE_NAME, "bdv.remote" );
-		elem.addContent( XmlHelpers.textElement( "baseUrl", imgLoader.baseUrl ) );
-		return elem;
-	}
+    private boolean allowCompression = false;
 
-	@Override
-	public RemoteImageLoader fromXml( final Element elem, final File basePath, final AbstractSequenceDescription< ?, ?, ? > sequenceDescription )
-	{
-		final String baseUrl = elem.getChildText( "baseUrl" );
-		try
-		{
-			return new RemoteImageLoader( baseUrl );
-		}
-		catch ( final IOException e )
-		{
-			throw new RuntimeException( e );
-		}
-	}
+    @Override
+    public Element toXml(final RemoteImageLoader imgLoader, final File basePath) {
+        final Element elem = new Element("ImageLoader");
+        elem.setAttribute(IMGLOADER_FORMAT_ATTRIBUTE_NAME, "bdv.remote");
+        elem.addContent(XmlHelpers.textElement("baseUrl", imgLoader.baseUrl));
+        return elem;
+    }
 
+    @Override
+    public RemoteImageLoader fromXml(final Element elem,
+                                     final File basePath,
+                                     final AbstractSequenceDescription<?, ?, ?> sequenceDescription) {
+        final String baseUrl = elem.getChildText("baseUrl");
+        try {
+            return new RemoteImageLoader(baseUrl, allowCompression);
+        } catch (final IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public boolean shouldAllowCompression() {
+        return allowCompression;
+    }
+
+    public void setAllowCompression(boolean allowCompression) {
+        this.allowCompression = allowCompression;
+    }
 }
diff --git a/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java b/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java
index c36211563a3c68d88448d3bf1837fa0f2fb06c30..8737c5210f73452ebbc869418cc3cd878868edb0 100644
--- a/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java
+++ b/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java
@@ -7,13 +7,13 @@
  * %%
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -33,6 +33,7 @@ import static mpicbg.spim.data.XmlKeys.SPIMDATA_TAG;
 
 import java.io.File;
 
+import bdv.img.remote.RemoteImageLoader;
 import mpicbg.spim.data.SpimDataException;
 import mpicbg.spim.data.SpimDataIOException;
 import mpicbg.spim.data.generic.XmlIoAbstractSpimData;
@@ -51,6 +52,8 @@ import bdv.spimdata.legacy.XmlIoSpimDataMinimalLegacy;
 
 public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData< SequenceDescriptionMinimal, SpimDataMinimal >
 {
+    private boolean allowCompression = false;
+
 	public XmlIoSpimDataMinimal()
 	{
 		super( SpimDataMinimal.class,
@@ -62,6 +65,10 @@ public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData< SequenceDescrip
 				new XmlIoViewRegistrations() );
 	}
 
+    public void setAllowCompression(final boolean allowCompression) {
+        this.allowCompression = allowCompression;
+    }
+
 	@Override
 	public SpimDataMinimal load( final String xmlFilename ) throws SpimDataException
 		{
@@ -83,6 +90,11 @@ public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData< SequenceDescrip
 			if ( root.getName() != SPIMDATA_TAG )
 				throw new RuntimeException( "expected <" + SPIMDATA_TAG + "> root element. wrong file?" );
 
-			return fromXml( root, new File( xmlFilename ) );
+            SpimDataMinimal spimDataMinimal = fromXml(root, new File(xmlFilename));
+            if (spimDataMinimal.getSequenceDescription().getImgLoader() instanceof RemoteImageLoader) {
+                final RemoteImageLoader remoteImageLoader = (RemoteImageLoader) spimDataMinimal.getSequenceDescription().getImgLoader();
+                remoteImageLoader.setAllowCompression(allowCompression);
+            }
+			return spimDataMinimal;
 		}
 }
diff --git a/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java b/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java
index 7f0ffefcea08cc066abd4488e48f7cb5a4641c88..a59c57b3d11f2b6225474398994294219a909c36 100644
--- a/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java
+++ b/src/main/java/bdv/spimdata/legacy/XmlIoSpimDataMinimalLegacy.java
@@ -165,6 +165,7 @@ public class XmlIoSpimDataMinimalLegacy
 	{
 		final Element elem = sequenceDescriptionElem.getChild( "ImageLoader" );
 		final String classn = elem.getAttributeValue( "class" );
+
 		if ( classn.equals( "viewer.hdf5.Hdf5ImageLoader" ) || classn.equals( "bdv.img.hdf5.Hdf5ImageLoader" ) )
 		{
 			final String path = loadPath( elem, "hdf5", basePath ).toString();