diff --git a/src/main/java/azgracompress/ViewerCompressionOptions.java b/src/main/java/azgracompress/ViewerCompressionOptions.java
deleted file mode 100644
index c74cd3c181a707bb92f4ae775a54c1a846495163..0000000000000000000000000000000000000000
--- a/src/main/java/azgracompress/ViewerCompressionOptions.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package azgracompress;
-
-public class ViewerCompressionOptions {
-    private boolean enabled = false;
-    private int compressFromMipmapLevel = 0;
-
-    public boolean isEnabled() {
-        return enabled;
-    }
-
-    public void setEnabled(final boolean enable) {
-        this.enabled = enable;
-    }
-
-    public int getCompressFromMipmapLevel() {
-        return compressFromMipmapLevel;
-    }
-
-    public void setCompressFromMipmapLevel(final int compressFrom) {
-        this.compressFromMipmapLevel = compressFrom;
-    }
-}
diff --git a/src/main/java/bdv/BigDataViewer.java b/src/main/java/bdv/BigDataViewer.java
index 2756ebff4497eb0a76a7500a94c50651bc83e13d..df3644ad2d832778de00296be7ff84795f47f53f 100644
--- a/src/main/java/bdv/BigDataViewer.java
+++ b/src/main/java/bdv/BigDataViewer.java
@@ -28,7 +28,6 @@
  */
 package bdv;
 
-import azgracompress.ViewerCompressionOptions;
 import azgracompress.utilities.ColorConsole;
 import bdv.cache.CacheControl;
 import bdv.export.ProgressWriter;
@@ -93,619 +92,576 @@ public class BigDataViewer {
 
     protected final RecordMovieDialog movieDialog;
 
-	protected final BrightnessDialog brightnessDialog;
-
-	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 );
-
-		viewerFrame = new ViewerFrame( sources, numTimepoints, cache, options.inputTriggerConfig( inputTriggerConfig ) );
-		if ( windowTitle != null )
-			viewerFrame.setTitle( windowTitle );
-		viewer = viewerFrame.getViewerPanel();
-
-//		final ConverterSetup.SetupChangeListener requestRepaint = s -> viewer.requestRepaint();
-//		for ( final ConverterSetup cs : converterSetups )
-//			cs.setupChangeListeners().add( requestRepaint );
-
-		manualTransformation = new ManualTransformation( viewer );
-		manualTransformationEditor = new ManualTransformationEditor( viewer, viewerFrame.getKeybindings() );
-
-		bookmarks = new Bookmarks();
-		bookmarkEditor = new BookmarksEditor( viewer, viewerFrame.getKeybindings(), bookmarks );
-
-		final ConverterSetups setups = viewerFrame.getConverterSetups();
-		if ( converterSetups.size() != sources.size() )
-			System.err.println( "WARNING! Constructing BigDataViewer, with converterSetups.size() that is not the same as sources.size()." );
-		final int numSetups = Math.min( converterSetups.size(), sources.size() );
-		for ( int i = 0; i < numSetups; ++i )
-		{
-			final SourceAndConverter< ? > source = sources.get( i );
-			final ConverterSetup setup = converterSetups.get( i );
-			if ( setup != null )
-				setups.put( source, setup );
-		}
-
-		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().overlays().add( movieDialog );
-
-		movieMaxProjectDialog = new RecordMaxProjectionDialog( viewerFrame, viewer, progressWriter );
-		// this is just to get updates of window size:
-		viewer.getDisplay().overlays().add( movieMaxProjectDialog );
-
-		activeSourcesDialog = new VisibilityAndGroupingDialog( viewerFrame, viewer.state() );
-
-		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;
-	}
+    protected final BrightnessDialog brightnessDialog;
 
-    public static BigDataViewer open(final String xmlFilename,
+    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;
+
+    private boolean enableCompression;
+
+    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);
+
+        viewerFrame = new ViewerFrame(sources, numTimepoints, cache, options.inputTriggerConfig(inputTriggerConfig));
+        if (windowTitle != null)
+            viewerFrame.setTitle(windowTitle);
+        viewer = viewerFrame.getViewerPanel();
+
+        //		final ConverterSetup.SetupChangeListener requestRepaint = s -> viewer.requestRepaint();
+        //		for ( final ConverterSetup cs : converterSetups )
+        //			cs.setupChangeListeners().add( requestRepaint );
+
+        manualTransformation = new ManualTransformation(viewer);
+        manualTransformationEditor = new ManualTransformationEditor(viewer, viewerFrame.getKeybindings());
+
+        bookmarks = new Bookmarks();
+        bookmarkEditor = new BookmarksEditor(viewer, viewerFrame.getKeybindings(), bookmarks);
+
+        final ConverterSetups setups = viewerFrame.getConverterSetups();
+        if (converterSetups.size() != sources.size())
+            System.err.println("WARNING! Constructing BigDataViewer, with converterSetups.size() that is not the same as sources.size().");
+        final int numSetups = Math.min(converterSetups.size(), sources.size());
+        for (int i = 0; i < numSetups; ++i) {
+            final SourceAndConverter<?> source = sources.get(i);
+            final ConverterSetup setup = converterSetups.get(i);
+            if (setup != null)
+                setups.put(source, setup);
+        }
+
+        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().overlays().add(movieDialog);
+
+        movieMaxProjectDialog = new RecordMaxProjectionDialog(viewerFrame, viewer, progressWriter);
+        // this is just to get updates of window size:
+        viewer.getDisplay().overlays().add(movieMaxProjectDialog);
+
+        activeSourcesDialog = new VisibilityAndGroupingDialog(viewerFrame, viewer.state());
+
+        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);
+
+        enableCompression = options.values.getCompressionOptions().isEnabled();
+        if (options.values.getCompressionOptions().isEnabled()) {
+            menu = new JMenu("QCMP Compression");
+            menubar.add(menu);
+
+            final JCheckBoxMenuItem enableCompressionCheckBox = new JCheckBoxMenuItem("Enable compression", true);
+            enableCompressionCheckBox.addActionListener(e -> {
+                enableDisableCompression(enableCompressionCheckBox.isSelected());
+            });
+
+            menu.add(enableCompressionCheckBox);
+        }
+
+
+        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,
-                                     final ViewerCompressionOptions ops) throws SpimDataException {
+                                     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;
+    }
+
+    private synchronized void enableDisableCompression(final boolean enabled) {
+        if (this.enableCompression == enabled)
+            return;
+        this.enableCompression = enabled;
+
+
+    }
 
+    public static BigDataViewer open(final String xmlFilename,
+                                     final String windowTitle,
+                                     final ProgressWriter progressWriter,
+                                     final ViewerOptions options) throws SpimDataException {
         final XmlIoSpimDataMinimal xmlIoSpimDataMinimal = new XmlIoSpimDataMinimal();
-        xmlIoSpimDataMinimal.setViewerCompressionOptions(ops);
+        xmlIoSpimDataMinimal.setViewerCompressionOptions(options.values.getCompressionOptions());
+
         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.viewerFrame );
+            InitializeViewerState.initBrightness(0.001, 0.999, bdv.viewerFrame);
         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.viewerFrame );
-		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 ConverterSetups getConverterSetups()
-	{
-		return viewerFrame.getConverterSetups();
-	}
-
-	/**
-	 * @deprecated Instead {@code getViewer().state()} returns the {@link ViewerState} that can be modified directly.
-	 */
-	@Deprecated
-	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 void expandAndFocusCardPanel()
-	{
-		viewerFrame.getSplitPanel().setCollapsed( false );
-		viewerFrame.getSplitPanel().getRightComponent().requestFocusInWindow();
-	}
-
-	public void collapseCardPanel()
-	{
-		viewerFrame.getSplitPanel().setCollapsed( true );
-		viewer.requestFocusInWindow();
-	}
+    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 ConverterSetups getConverterSetups() {
+        return viewerFrame.getConverterSetups();
+    }
+
+    /**
+     * @deprecated Instead {@code getViewer().state()} returns the {@link ViewerState} that can be modified directly.
+     */
+    @Deprecated
+    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 void expandAndFocusCardPanel() {
+        viewerFrame.getSplitPanel().setCollapsed(false);
+        viewerFrame.getSplitPanel().getRightComponent().requestFocusInWindow();
+    }
+
+    public void collapseCardPanel() {
+        viewerFrame.getSplitPanel().setCollapsed(true);
+        viewer.requestFocusInWindow();
+    }
 
     public static void main(final String[] args) {
         if (args.length < 1) {
@@ -714,29 +670,22 @@ public class BigDataViewer {
         }
         final String fn = args[0];
 
-        final ViewerCompressionOptions ops = new ViewerCompressionOptions();
+        final ViewerOptions viewerOptions = ViewerOptions.options();
         if (args.length > 1) {
             for (int i = 1; i < args.length; i++) {
 
                 switch (args[i]) {
                     case "-qcmp":
-                        ops.setEnabled(true);
-                        break;
-                    case "-compress-from":
-                        if (args.length <= i + 1) {
-                            System.err.println("Missing -compress-from integer parameter.");
-                            return;
-                        }
-                        ops.setCompressFromMipmapLevel(Integer.parseInt(args[++i]));
+                        viewerOptions.values.getCompressionOptions().setEnabled(true);
                         break;
                 }
 
             }
         }
 
-        ColorConsole.printf(ColorConsole.Color.Green, "Compression:\t" + (ops.isEnabled() ? "ON" : "OFF"));
-        if (ops.isEnabled())
-            ColorConsole.printf(ColorConsole.Color.Green, "CompressFrom:\t" + ops.getCompressFromMipmapLevel());
+        ColorConsole.printf(ColorConsole.Color.Green, "Compression:\t" +
+                (viewerOptions.values.getCompressionOptions().isEnabled() ? "ON" : "OFF"));
+
 
         try {
             System.setProperty("apple.laf.useScreenMenuBar", "true");
@@ -744,8 +693,7 @@ public class BigDataViewer {
             final BigDataViewer bdv = open(fn,
                                            "Server test",
                                            new ProgressWriterConsole(),
-                                           ViewerOptions.options(),
-                                           ops);
+                                           viewerOptions);
 
         } 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 01835d7790c86af1b55466e22a035fd41c4964cc..74178125e057eb7c88ec721b2cb5209057873afc 100644
--- a/src/main/java/bdv/img/remote/RemoteImageLoader.java
+++ b/src/main/java/bdv/img/remote/RemoteImageLoader.java
@@ -28,7 +28,6 @@
  */
 package bdv.img.remote;
 
-import azgracompress.ViewerCompressionOptions;
 import azgracompress.cache.ICacheFile;
 import azgracompress.cache.QuantizationCacheManager;
 import azgracompress.compression.ImageDecompressor;
@@ -41,6 +40,7 @@ import bdv.img.hdf5.DimsAndExistence;
 import bdv.img.hdf5.MipmapInfo;
 import bdv.img.hdf5.ViewLevelId;
 import bdv.util.ConstantRandomAccessible;
+import bdv.viewer.ViewerOptions;
 import com.google.gson.GsonBuilder;
 import mpicbg.spim.data.generic.sequence.ImgLoaderHint;
 import net.imglib2.FinalInterval;
@@ -77,7 +77,7 @@ public class RemoteImageLoader implements ViewerImgLoader {
     /**
      * Flag whether we allow the server to send us compressed data.
      */
-    private ViewerCompressionOptions viewerCompressionOptions;
+    private ViewerOptions.CompressionOptions viewerCompressionOptions;
 
 
     /**
@@ -131,7 +131,7 @@ public class RemoteImageLoader implements ViewerImgLoader {
         }
     }
 
-    public void setViewerCompressionOptions(final ViewerCompressionOptions ops) {
+    public void setViewerCompressionOptions(final ViewerOptions.CompressionOptions ops) {
         this.viewerCompressionOptions = ops;
     }
 
@@ -146,7 +146,9 @@ public class RemoteImageLoader implements ViewerImgLoader {
             return;
         }
         final ArrayList<ICacheFile> cacheFiles = new ArrayList<>();
+        int compressFromMipmapLevel = 0;
         try (final DataInputStream dis = new DataInputStream(connection.getInputStream())) {
+            compressFromMipmapLevel = dis.readByte();
             final int codebookCount = dis.readByte();
             for (int cbIndex = 0; cbIndex < codebookCount; cbIndex++) {
                 final ICacheFile readCacheFile = QuantizationCacheManager.readCacheFile(dis);
@@ -160,13 +162,15 @@ public class RemoteImageLoader implements ViewerImgLoader {
             }
         }
         ColorConsole.fprintf(ColorConsole.Target.stdout, ColorConsole.Color.Yellow, "Received %d cache files.", cacheFiles.size());
+        ColorConsole.fprintf(ColorConsole.Target.stdout, ColorConsole.Color.Yellow,
+                             "Decompressing from mipmap level %d.", compressFromMipmapLevel);
 
 
         final ImageDecompressor[] decompressors = new ImageDecompressor[cacheFiles.size()];
         for (int i = 0; i < cacheFiles.size(); i++) {
             decompressors[i] = new ImageDecompressor(cacheFiles.get(i));
         }
-        shortLoader.setDataDecompressors(decompressors, metadata.maxNumLevels, viewerCompressionOptions.getCompressFromMipmapLevel());
+        shortLoader.setDataDecompressors(decompressors, metadata.maxNumLevels, compressFromMipmapLevel);
     }
 
 
diff --git a/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java b/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java
index 2d5daf3cb1bde2824b930c8d4f23f52438f5b5d9..a2baab0cac95f8e82215d1e8b322790cf7e4c9e1 100644
--- a/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java
+++ b/src/main/java/bdv/spimdata/XmlIoSpimDataMinimal.java
@@ -28,9 +28,9 @@
  */
 package bdv.spimdata;
 
-import azgracompress.ViewerCompressionOptions;
 import bdv.img.remote.RemoteImageLoader;
 import bdv.spimdata.legacy.XmlIoSpimDataMinimalLegacy;
+import bdv.viewer.ViewerOptions;
 import mpicbg.spim.data.SpimDataException;
 import mpicbg.spim.data.SpimDataIOException;
 import mpicbg.spim.data.generic.XmlIoAbstractSpimData;
@@ -49,7 +49,7 @@ import java.io.File;
 import static mpicbg.spim.data.XmlKeys.SPIMDATA_TAG;
 
 public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData<SequenceDescriptionMinimal, SpimDataMinimal> {
-    private ViewerCompressionOptions compressionOptions;
+    private ViewerOptions.CompressionOptions compressionOptions;
 
     public XmlIoSpimDataMinimal() {
         super(SpimDataMinimal.class,
@@ -61,7 +61,7 @@ public class XmlIoSpimDataMinimal extends XmlIoAbstractSpimData<SequenceDescript
               new XmlIoViewRegistrations());
     }
 
-    public void setViewerCompressionOptions(final ViewerCompressionOptions ops) {
+    public void setViewerCompressionOptions(final ViewerOptions.CompressionOptions ops) {
         this.compressionOptions = ops;
     }
 
diff --git a/src/main/java/bdv/viewer/ViewerOptions.java b/src/main/java/bdv/viewer/ViewerOptions.java
index b977c4585e8fdfadafe7b32ffc2efb6bb0a283f4..9561384d069cdcdd6ff0fb5265a08435ba3c6899 100644
--- a/src/main/java/bdv/viewer/ViewerOptions.java
+++ b/src/main/java/bdv/viewer/ViewerOptions.java
@@ -6,13 +6,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,285 +29,274 @@
 package bdv.viewer;
 
 import bdv.TransformEventHandler3D;
-import java.awt.event.KeyListener;
-
-import org.scijava.ui.behaviour.KeyPressedManager;
-import org.scijava.ui.behaviour.io.InputTriggerConfig;
-
+import bdv.TransformEventHandlerFactory;
 import bdv.viewer.animate.MessageOverlayAnimator;
 import bdv.viewer.render.AccumulateProjector;
 import bdv.viewer.render.AccumulateProjectorARGB;
 import bdv.viewer.render.AccumulateProjectorFactory;
 import bdv.viewer.render.MultiResolutionRenderer;
-import net.imglib2.realtransform.AffineTransform3D;
 import net.imglib2.type.numeric.ARGBType;
-import bdv.TransformEventHandlerFactory;
+import org.scijava.ui.behaviour.KeyPressedManager;
+import org.scijava.ui.behaviour.io.InputTriggerConfig;
+
+import java.awt.event.KeyListener;
 
 /**
  * Optional parameters for {@link ViewerPanel}.
  *
  * @author Tobias Pietzsch
  */
-public class ViewerOptions
-{
-	public final Values values = new Values();
-
-	/**
-	 * Create default {@link ViewerOptions}.
-	 * @return default {@link ViewerOptions}.
-	 */
-	public static ViewerOptions options()
-	{
-		return new ViewerOptions();
-	}
-
-	/**
-	 * Set width of {@link ViewerPanel} canvas.
-	 */
-	public ViewerOptions width( final int w )
-	{
-		values.width = w;
-		return this;
-	}
-
-	/**
-	 * Set height of {@link ViewerPanel} canvas.
-	 */
-	public ViewerOptions height( final int h )
-	{
-		values.height = h;
-		return this;
-	}
-
-	/**
-	 * Set the number and scale factors for scaled screen images.
-	 *
-	 * @param s
-	 *            Scale factors from the viewer canvas to screen images of
-	 *            different resolutions. A scale factor of 1 means 1 pixel in
-	 *            the screen image is displayed as 1 pixel on the canvas, a
-	 *            scale factor of 0.5 means 1 pixel in the screen image is
-	 *            displayed as 2 pixel on the canvas, etc.
-	 * @see MultiResolutionRenderer
-	 */
-	public ViewerOptions screenScales( final double[] s )
-	{
-		values.screenScales = s;
-		return this;
-	}
-
-	/**
-	 * Set target rendering time in nanoseconds.
-	 *
-	 * @param t
-	 *            Target rendering time in nanoseconds. The rendering time for
-	 *            the coarsest rendered scale should be below this threshold.
-	 * @see MultiResolutionRenderer
-	 */
-	public ViewerOptions targetRenderNanos( final long t )
-	{
-		values.targetRenderNanos = t;
-		return this;
-	}
-
-	/**
-	 * Set how many threads to use for rendering.
-	 *
-	 * @param n
-	 *            How many threads to use for rendering.
-	 * @see MultiResolutionRenderer
-	 */
-	public ViewerOptions numRenderingThreads( final int n )
-	{
-		values.numRenderingThreads = n;
-		return this;
-	}
-
-	/**
-	 * Set how many source groups there are initially.
-	 *
-	 * @param n
-	 *            How many source groups to create initially.
-	 */
-	public ViewerOptions numSourceGroups( final int n )
-	{
-		values.numSourceGroups = n;
-		return this;
-	}
-
-	/**
-	 * Set whether volatile versions of sources should be used if available.
-	 *
-	 * @param v
-	 *            whether volatile versions of sources should be used if
-	 *            available.
-	 * @see MultiResolutionRenderer
-	 */
-	public ViewerOptions useVolatileIfAvailable( final boolean v )
-	{
-		values.useVolatileIfAvailable = v;
-		return this;
-	}
-
-	public ViewerOptions msgOverlay( final MessageOverlayAnimator o )
-	{
-		values.msgOverlay = o;
-		return this;
-	}
-
-	public ViewerOptions transformEventHandlerFactory( final TransformEventHandlerFactory f )
-	{
-		values.transformEventHandlerFactory = f;
-		return this;
-	}
-
-	/**
-	 * Set the factory for creating {@link AccumulateProjector}. This can be
-	 * used to customize how sources are combined.
-	 *
-	 * @param f
-	 *            factory for creating {@link AccumulateProjector}.
-	 * @see MultiResolutionRenderer
-	 */
-	public ViewerOptions accumulateProjectorFactory( final AccumulateProjectorFactory< ARGBType > f )
-	{
-		values.accumulateProjectorFactory = f;
-		return this;
-	}
-
-	/**
-	 * Set the {@link InputTriggerConfig} from which keyboard and mouse action mapping is loaded.
-	 *
-	 * @param c the {@link InputTriggerConfig} from which keyboard and mouse action mapping is loaded
-	 */
-	public ViewerOptions inputTriggerConfig( final InputTriggerConfig c )
-	{
-		values.inputTriggerConfig = c;
-		return this;
-	}
-
-	/**
-	 * Set the {@link KeyPressedManager} to share
-	 * {@link KeyListener#keyPressed(java.awt.event.KeyEvent)} events with other
-	 * ui-behaviour windows.
-	 * <p>
-	 * The goal is to make keyboard click/drag behaviours work like mouse
-	 * click/drag: When a behaviour is initiated with a key press, the window
-	 * under the mouse receives focus and the behaviour is handled there.
-	 * </p>
-	 *
-	 * @param manager
-	 * @return
-	 */
-	public ViewerOptions shareKeyPressedEvents( final KeyPressedManager manager )
-	{
-		values.keyPressedManager = manager;
-		return this;
-	}
-
-	/**
-	 * Read-only {@link ViewerOptions} values.
-	 */
-	public static class Values
-	{
-		private int width = 800;
-
-		private int height = 600;
-
-		private double[] screenScales = new double[] { 1, 0.75, 0.5, 0.25, 0.125 };
-
-		private long targetRenderNanos = 30 * 1000000l;
-
-		private int numRenderingThreads = 3;
-
-		private int numSourceGroups = 10;
-
-		private boolean useVolatileIfAvailable = true;
-
-		private MessageOverlayAnimator msgOverlay = new MessageOverlayAnimator( 800 );
-
-		private TransformEventHandlerFactory transformEventHandlerFactory = TransformEventHandler3D::new;
-
-		private AccumulateProjectorFactory< ARGBType > accumulateProjectorFactory = AccumulateProjectorARGB.factory;
-
-		private InputTriggerConfig inputTriggerConfig = null;
-
-		private KeyPressedManager keyPressedManager = null;
-
-		public ViewerOptions optionsFromValues()
-		{
-			return new ViewerOptions().
-				width( width ).
-				height( height ).
-				screenScales( screenScales ).
-				targetRenderNanos( targetRenderNanos ).
-				numRenderingThreads( numRenderingThreads ).
-				numSourceGroups( numSourceGroups ).
-				useVolatileIfAvailable( useVolatileIfAvailable ).
-				msgOverlay( msgOverlay ).
-				transformEventHandlerFactory( transformEventHandlerFactory ).
-				accumulateProjectorFactory( accumulateProjectorFactory ).
-				inputTriggerConfig( inputTriggerConfig );
-		}
-
-		public int getWidth()
-		{
-			return width;
-		}
-
-		public int getHeight()
-		{
-			return height;
-		}
-
-		public double[] getScreenScales()
-		{
-			return screenScales;
-		}
-
-		public long getTargetRenderNanos()
-		{
-			return targetRenderNanos;
-		}
-
-		public int getNumRenderingThreads()
-		{
-			return numRenderingThreads;
-		}
-
-		public int getNumSourceGroups()
-		{
-			return numSourceGroups;
-		}
-
-		public boolean isUseVolatileIfAvailable()
-		{
-			return useVolatileIfAvailable;
-		}
-
-		public MessageOverlayAnimator getMsgOverlay()
-		{
-			return msgOverlay;
-		}
-
-		public TransformEventHandlerFactory getTransformEventHandlerFactory()
-		{
-			return transformEventHandlerFactory;
-		}
-
-		public AccumulateProjectorFactory< ARGBType > getAccumulateProjectorFactory()
-		{
-			return accumulateProjectorFactory;
-		}
-
-		public InputTriggerConfig getInputTriggerConfig()
-		{
-			return inputTriggerConfig;
-		}
-
-		public KeyPressedManager getKeyPressedManager()
-		{
-			return keyPressedManager;
-		}
-	}
+public class ViewerOptions {
+    public static class CompressionOptions {
+        private boolean enabled = false;
+
+        public boolean isEnabled() {
+            return enabled;
+        }
+
+        public void setEnabled(final boolean enable) {
+            this.enabled = enable;
+        }
+    }
+
+    public final Values values = new Values();
+
+    /**
+     * Create default {@link ViewerOptions}.
+     *
+     * @return default {@link ViewerOptions}.
+     */
+    public static ViewerOptions options() {
+        return new ViewerOptions();
+    }
+
+    /**
+     * Set width of {@link ViewerPanel} canvas.
+     */
+    public ViewerOptions width(final int w) {
+        values.width = w;
+        return this;
+    }
+
+    public ViewerOptions compressionOptions(final CompressionOptions compressionOptions) {
+        values.compressionOptions = compressionOptions;
+        return this;
+    }
+
+    /**
+     * Set height of {@link ViewerPanel} canvas.
+     */
+    public ViewerOptions height(final int h) {
+        values.height = h;
+        return this;
+    }
+
+    /**
+     * Set the number and scale factors for scaled screen images.
+     *
+     * @param s Scale factors from the viewer canvas to screen images of
+     *          different resolutions. A scale factor of 1 means 1 pixel in
+     *          the screen image is displayed as 1 pixel on the canvas, a
+     *          scale factor of 0.5 means 1 pixel in the screen image is
+     *          displayed as 2 pixel on the canvas, etc.
+     * @see MultiResolutionRenderer
+     */
+    public ViewerOptions screenScales(final double[] s) {
+        values.screenScales = s;
+        return this;
+    }
+
+    /**
+     * Set target rendering time in nanoseconds.
+     *
+     * @param t Target rendering time in nanoseconds. The rendering time for
+     *          the coarsest rendered scale should be below this threshold.
+     * @see MultiResolutionRenderer
+     */
+    public ViewerOptions targetRenderNanos(final long t) {
+        values.targetRenderNanos = t;
+        return this;
+    }
+
+    /**
+     * Set how many threads to use for rendering.
+     *
+     * @param n How many threads to use for rendering.
+     * @see MultiResolutionRenderer
+     */
+    public ViewerOptions numRenderingThreads(final int n) {
+        values.numRenderingThreads = n;
+        return this;
+    }
+
+    /**
+     * Set how many source groups there are initially.
+     *
+     * @param n How many source groups to create initially.
+     */
+    public ViewerOptions numSourceGroups(final int n) {
+        values.numSourceGroups = n;
+        return this;
+    }
+
+    /**
+     * Set whether volatile versions of sources should be used if available.
+     *
+     * @param v whether volatile versions of sources should be used if
+     *          available.
+     * @see MultiResolutionRenderer
+     */
+    public ViewerOptions useVolatileIfAvailable(final boolean v) {
+        values.useVolatileIfAvailable = v;
+        return this;
+    }
+
+    public ViewerOptions msgOverlay(final MessageOverlayAnimator o) {
+        values.msgOverlay = o;
+        return this;
+    }
+
+    public ViewerOptions transformEventHandlerFactory(final TransformEventHandlerFactory f) {
+        values.transformEventHandlerFactory = f;
+        return this;
+    }
+
+    /**
+     * Set the factory for creating {@link AccumulateProjector}. This can be
+     * used to customize how sources are combined.
+     *
+     * @param f factory for creating {@link AccumulateProjector}.
+     * @see MultiResolutionRenderer
+     */
+    public ViewerOptions accumulateProjectorFactory(final AccumulateProjectorFactory<ARGBType> f) {
+        values.accumulateProjectorFactory = f;
+        return this;
+    }
+
+    /**
+     * Set the {@link InputTriggerConfig} from which keyboard and mouse action mapping is loaded.
+     *
+     * @param c the {@link InputTriggerConfig} from which keyboard and mouse action mapping is loaded
+     */
+    public ViewerOptions inputTriggerConfig(final InputTriggerConfig c) {
+        values.inputTriggerConfig = c;
+        return this;
+    }
+
+    /**
+     * Set the {@link KeyPressedManager} to share
+     * {@link KeyListener#keyPressed(java.awt.event.KeyEvent)} events with other
+     * ui-behaviour windows.
+     * <p>
+     * The goal is to make keyboard click/drag behaviours work like mouse
+     * click/drag: When a behaviour is initiated with a key press, the window
+     * under the mouse receives focus and the behaviour is handled there.
+     * </p>
+     *
+     * @param manager
+     * @return
+     */
+    public ViewerOptions shareKeyPressedEvents(final KeyPressedManager manager) {
+        values.keyPressedManager = manager;
+        return this;
+    }
+
+    /**
+     * Read-only {@link ViewerOptions} values.
+     */
+    public static class Values {
+        private int width = 800;
+
+        private int height = 600;
+
+        private double[] screenScales = new double[]{1, 0.75, 0.5, 0.25, 0.125};
+
+        private long targetRenderNanos = 30 * 1000000l;
+
+        private int numRenderingThreads = 3;
+
+        private int numSourceGroups = 10;
+
+        private boolean useVolatileIfAvailable = true;
+
+        private MessageOverlayAnimator msgOverlay = new MessageOverlayAnimator(800);
+
+        private TransformEventHandlerFactory transformEventHandlerFactory = TransformEventHandler3D::new;
+
+        private AccumulateProjectorFactory<ARGBType> accumulateProjectorFactory = AccumulateProjectorARGB.factory;
+
+        private InputTriggerConfig inputTriggerConfig = null;
+
+        private KeyPressedManager keyPressedManager = null;
+
+        private CompressionOptions compressionOptions = new CompressionOptions();
+
+        public ViewerOptions optionsFromValues() {
+            return new ViewerOptions().
+                    width(width).
+                    height(height).
+                    screenScales(screenScales).
+                    targetRenderNanos(targetRenderNanos).
+                    numRenderingThreads(numRenderingThreads).
+                    numSourceGroups(numSourceGroups).
+                    useVolatileIfAvailable(useVolatileIfAvailable).
+                    msgOverlay(msgOverlay).
+                    transformEventHandlerFactory(transformEventHandlerFactory).
+                    accumulateProjectorFactory(accumulateProjectorFactory).
+                    inputTriggerConfig(inputTriggerConfig).
+                    compressionOptions(compressionOptions);
+        }
+
+        public int getWidth() {
+            return width;
+        }
+
+        public int getHeight() {
+            return height;
+        }
+
+        public double[] getScreenScales() {
+            return screenScales;
+        }
+
+        public long getTargetRenderNanos() {
+            return targetRenderNanos;
+        }
+
+        public int getNumRenderingThreads() {
+            return numRenderingThreads;
+        }
+
+        public int getNumSourceGroups() {
+            return numSourceGroups;
+        }
+
+        public boolean isUseVolatileIfAvailable() {
+            return useVolatileIfAvailable;
+        }
+
+        public MessageOverlayAnimator getMsgOverlay() {
+            return msgOverlay;
+        }
+
+        public TransformEventHandlerFactory getTransformEventHandlerFactory() {
+            return transformEventHandlerFactory;
+        }
+
+        public AccumulateProjectorFactory<ARGBType> getAccumulateProjectorFactory() {
+            return accumulateProjectorFactory;
+        }
+
+        public InputTriggerConfig getInputTriggerConfig() {
+            return inputTriggerConfig;
+        }
+
+        public KeyPressedManager getKeyPressedManager() {
+            return keyPressedManager;
+        }
+
+        public CompressionOptions getCompressionOptions() {
+            return compressionOptions;
+        }
+    }
 }