diff --git a/fsl/data/strings.py b/fsl/data/strings.py
index 128c87bbfa563c011ebefd0494c894323dcf3cbe..3c91eafb0630fca4c36679f600acca55104583fc 100644
--- a/fsl/data/strings.py
+++ b/fsl/data/strings.py
@@ -202,10 +202,9 @@ properties = TypeDict({
     
     'Profile.mode' : 'Profile',
 
-    'CanvasPanel.syncLocation'       : 'Sync location',
-    'CanvasPanel.syncOverlayOrder'   : 'Sync overlay order',
-    'CanvasPanel.syncVolume'         : 'Sync volume',
-    'CanvasPanel.profile'            : 'Mode',
+    'CanvasPanel.syncLocation'     : 'Sync location',
+    'CanvasPanel.syncOverlayOrder' : 'Sync overlay order',
+    'CanvasPanel.profile'          : 'Mode',
 
     'SceneOpts.showCursor'         : 'Show location cursor',
     'SceneOpts.showColourBar'      : 'Show colour bar',
diff --git a/fsl/fslview/displaycontext/volumeopts.py b/fsl/fslview/displaycontext/volumeopts.py
index 9a90a753632ead44e9aff573fceeab5c38736f1a..a96729b82165c9dfd5d72032d84ba13c9ff48abc 100644
--- a/fsl/fslview/displaycontext/volumeopts.py
+++ b/fsl/fslview/displaycontext/volumeopts.py
@@ -29,12 +29,12 @@ class ImageOpts(fsldisplay.DisplayOpts):
     """
 
     
-    resolution = props.Real(maxval=10, default=1, clamped=True)
-    """Data resolution in world space. The minimum value is set in __init__.""" 
+    volume = props.Int(minval=0, maxval=0, default=0, clamped=True)
+    """If the data is 4D , the current volume to display."""    
 
     
-    volume = props.Int(minval=0, maxval=0, default=0, clamped=True)
-    """If the data is 4D , the current volume to display."""
+    resolution = props.Real(maxval=10, default=1, clamped=True)
+    """Data resolution in world space. The minimum value is set in __init__.""" 
 
 
     transform = props.Choice(
@@ -60,11 +60,6 @@ class ImageOpts(fsldisplay.DisplayOpts):
  
     def __init__(self, *args, **kwargs):
 
-        nounbind = kwargs.get('nounbind', [])
-        nounbind.extend(('transform', 'resolution', 'volume'))
-        
-        kwargs['nounbind'] = nounbind
-        
         fsldisplay.DisplayOpts.__init__(self, *args, **kwargs)
 
         overlay = self.overlay
@@ -239,31 +234,6 @@ class VolumeOpts(ImageOpts):
     invert = props.Boolean(default=False)
     """Invert the colour map."""
 
-    
-    _tooltips = {
-        'name'          : 'The name of this image',
-        'enabled'       : 'Enable/disable this image',
-        'alpha'         : 'Opacity, between 0.0 (transparent) '
-                          'and 100.0 (opaque)',
-        'displayRange'  : 'Minimum/maximum display values',
-        'clipLow'       : 'Do not show image values which are '
-                          'lower than the display range',
-        'clipHigh'      : 'Do not show image values which are '
-                          'higher than the display range', 
-        'interpolation' : 'Interpolate between voxel values at '
-                          'each displayed real world location',
-        'resolution'    : 'Data resolution in voxels',
-        'volume'        : 'Volume number (for 4D images)',
-        'transform'     : 'The transformation matrix which specifies the '
-                          'conversion from voxel coordinates to a real '
-                          'world location',
-        'imageType'     : 'the type of data contained in the image',
-        'cmap'          : 'Colour map'}
-
-    
-    _propHelp = _tooltips
-
-
 
     def __init__(self,
                  overlay,
@@ -310,9 +280,6 @@ class VolumeOpts(ImageOpts):
         
         self.setConstraint('displayRange', 'minDistance', dMinDistance)
 
-        nounbind = kwargs.get('nounbind', [])
-        nounbind.extend(('interpolation')) 
-
         ImageOpts.__init__(self,
                            overlay,
                            display,
diff --git a/fsl/fslview/gl/gllabel.py b/fsl/fslview/gl/gllabel.py
index 05ff11afa3f4d5912eff9fa1ac5034f68e5871f3..08e2341288c04d1bcc51d641f086cdb729f60bae 100644
--- a/fsl/fslview/gl/gllabel.py
+++ b/fsl/fslview/gl/gllabel.py
@@ -28,8 +28,7 @@ class GLLabel(globject.GLImageObject):
             imageTexName,
             textures.ImageTexture,
             imageTexName,
-            image,
-            display)
+            image)
         
         fslgl.gllabel_funcs.init(self)
 
diff --git a/fsl/fslview/gl/glvector.py b/fsl/fslview/gl/glvector.py
index 79b0b49c2aea227add3e83c0e036a41074c2e8fe..cde79f0a459dc74ae25eddeb4ba95c651e817d9a 100644
--- a/fsl/fslview/gl/glvector.py
+++ b/fsl/fslview/gl/glvector.py
@@ -136,7 +136,6 @@ class GLVector(globject.GLImageObject):
             textures.ImageTexture,
             texName,
             self.image,
-            display=self.display,
             nvals=3,
             normalise=True,
             prefilter=realPrefilter) 
@@ -220,7 +219,6 @@ class GLVector(globject.GLImageObject):
             textures.ImageTexture,
             texName,
             modImage,
-            display=modDisplay,
             normalise=norm)
 
 
diff --git a/fsl/fslview/gl/glvolume.py b/fsl/fslview/gl/glvolume.py
index 7dd1a2aa7bc1b0d5e3b717a9ea5cf48f6395afe7..64572f20e09eb7f9eb72eb45ca00b4436b1ba55f 100644
--- a/fsl/fslview/gl/glvolume.py
+++ b/fsl/fslview/gl/glvolume.py
@@ -85,70 +85,17 @@ class GLVolume(globject.GLImageObject):
         self.addDisplayListeners()
 
         # Create an image texture and a colour map texture
-        texName = '{}_{}'.format(type(self).__name__, id(self.image))
+        self.texName = '{}_{}'.format(type(self).__name__, id(self.image))
 
-        # The image texture may be used elsewhere,
-        # so we'll use the resource management
-        # module rather than creating one directly
-        self.imageTexture = glresources.get(
-            texName, 
-            textures.ImageTexture,
-            texName,
-            self.image,
-            self.display)
-
-        self.colourTexture = textures.ColourMapTexture(texName)
-        
+        self.imageTexture  = None
+        self.colourTexture = textures.ColourMapTexture(self.texName)
+ 
+        self.refreshImageTexture()
         self.refreshColourTexture()
         
         fslgl.glvolume_funcs.init(self)
 
 
-    def preDraw(self):
-        """Sets up the GL state to draw a slice from this :class:`GLVolume`
-        instance.
-        """
-        
-        # Set up the image and colour textures
-        self.imageTexture .bindTexture(gl.GL_TEXTURE0)
-        self.colourTexture.bindTexture(gl.GL_TEXTURE1)
-
-        fslgl.glvolume_funcs.preDraw(self)
-
-        
-    def draw(self, zpos, xform=None):
-        """Draws a 2D slice of the image at the given real world Z location.
-        This is performed via a call to the OpenGL version-dependent `draw`
-        function, contained in one of the :mod:`~fsl.fslview.gl.gl14` or
-        :mod:`~fsl.fslview.gl.gl21` packages.
-
-        If `xform` is not None, it is applied as an affine transformation to
-        the vertex coordinates of the rendered image data.
-
-        Note: Calls to this method must be preceded by a call to
-        :meth:`preDraw`, and followed by a call to :meth:`postDraw`.
-        """
-        
-        fslgl.glvolume_funcs.draw(self, zpos, xform)
-
-        
-    def drawAll(self, zposes, xforms):
-        """Calls the module-specific ``drawAll`` function. """
-        
-        fslgl.glvolume_funcs.drawAll(self, zposes, xforms)
-
-        
-    def postDraw(self):
-        """Clears the GL state after drawing from this :class:`GLVolume`
-        instance.
-        """
-
-        self.imageTexture .unbindTexture()
-        self.colourTexture.unbindTexture()
-        
-        fslgl.glvolume_funcs.postDraw(self) 
-
-
     def destroy(self):
         """This should be called when this :class:`GLVolume` object is no
         longer needed. It performs any needed clean up of OpenGL data (e.g.
@@ -165,6 +112,32 @@ class GLVolume(globject.GLImageObject):
         self.removeDisplayListeners()
         fslgl.glvolume_funcs.destroy(self)
 
+
+    def refreshImageTexture(self):
+
+        opts    = self.displayOpts
+        texName = self.texName
+
+        unsynced = (not opts.isSyncedToParent('volume')        or
+                    not opts.isSyncedToParent('transform')     or
+                    not opts.isSyncedToParent('resolution')    or
+                    not opts.isSyncedToParent('interpolation'))
+
+        if unsynced:
+            texName = '{}_unsync_{}'.format(texName, id(opts))
+
+        if self.imageTexture is not None:
+            glresources.delete(self.imageTexture.getTextureName())
+
+        # The image texture may be used elsewhere,
+        # so we'll use the resource management
+        # module rather than creating one directly
+        self.imageTexture = glresources.get(
+            texName, 
+            textures.ImageTexture,
+            texName,
+            self.image)        
+
     
     def refreshColourTexture(self):
         """Configures the colour texture used to colour image voxels."""
@@ -209,31 +182,44 @@ class GLVolume(globject.GLImageObject):
             fslgl.glvolume_funcs.updateShaderState(self)
             self.onUpdate()
 
-        def interpUpdate(*a):
-            
-            if opts.interpolation == 'none': interp = gl.GL_NEAREST
-            else:                            interp = gl.GL_LINEAR
-            
-            self.imageTexture.setInterpolation(interp)
+        def shaderCompile(*a):
+            fslgl.glvolume_funcs.compileShaders(   self)
             fslgl.glvolume_funcs.updateShaderState(self)
             self.onUpdate()
 
-        def shaderCompile(*a):
-            fslgl.glvolume_funcs.compileShaders(   self)
+        def imageRefresh(*a):
+            self.refreshImageTexture()
+            self.onUpdate()
+
+        def imageUpdate(*a):
+            volume     = opts.volume
+            resolution = opts.resolution
+
+            if opts.interpolation == 'none': interp = gl.GL_NEAREST
+            else:                            interp = gl.GL_LINEAR
+            
+            self.imageTexture.set(volume=volume,
+                                  interp=interp,
+                                  resolution=resolution)
+            
             fslgl.glvolume_funcs.updateShaderState(self)
             self.onUpdate()
 
         def update(*a):
             self.onUpdate()
         
-        display.addListener('softwareMode',  lName, shaderCompile)
-        display.addListener('alpha',         lName, colourUpdate)
-        opts   .addListener('interpolation', lName, interpUpdate)
-        opts   .addListener('resolution',    lName, update)
-        opts   .addListener('displayRange',  lName, colourUpdate)
-        opts   .addListener('clippingRange', lName, shaderUpdate)
-        opts   .addListener('cmap',          lName, colourUpdate)
-        opts   .addListener('invert',        lName, colourUpdate)
+        display.addListener(          'softwareMode',  lName, shaderCompile)
+        display.addListener(          'alpha',         lName, colourUpdate)
+        opts   .addListener(          'displayRange',  lName, colourUpdate)
+        opts   .addListener(          'clippingRange', lName, shaderUpdate)
+        opts   .addListener(          'cmap',          lName, colourUpdate)
+        opts   .addListener(          'invert',        lName, colourUpdate)
+        opts   .addListener(          'volume',        lName, imageUpdate)
+        opts   .addListener(          'resolution',    lName, imageUpdate)
+        opts   .addListener(          'interpolation', lName, imageUpdate)
+        opts   .addSyncChangeListener('volume',        lName, imageRefresh)
+        opts   .addSyncChangeListener('resolution',    lName, imageRefresh)
+        opts   .addSyncChangeListener('interpolation', lName, imageRefresh)
 
 
     def removeDisplayListeners(self):
@@ -246,11 +232,60 @@ class GLVolume(globject.GLImageObject):
 
         lName = self.name
         
-        display.removeListener('softwareMode',  lName)
-        display.removeListener('alpha',         lName)
-        opts   .removeListener('interpolation', lName)
-        opts   .removeListener('resolution',    lName)
-        opts   .removeListener('displayRange',  lName)
-        opts   .removeListener('clippingRange', lName)
-        opts   .removeListener('cmap',          lName)
-        opts   .removeListener('invert',        lName)
+        display.removeListener(          'softwareMode',  lName)
+        display.removeListener(          'alpha',         lName)
+        opts   .removeListener(          'displayRange',  lName)
+        opts   .removeListener(          'clippingRange', lName)
+        opts   .removeListener(          'cmap',          lName)
+        opts   .removeListener(          'invert',        lName)
+        opts   .removeListener(          'volume',        lName)
+        opts   .removeListener(          'resolution',    lName)
+        opts   .removeListener(          'interpolation', lName)
+        opts   .removeSyncChangeListener('volume',        lName)
+        opts   .removeSyncChangeListener('resolution',    lName)
+        opts   .removeSyncChangeListener('interpolation', lName)
+
+        
+    def preDraw(self):
+        """Sets up the GL state to draw a slice from this :class:`GLVolume`
+        instance.
+        """
+        
+        # Set up the image and colour textures
+        self.imageTexture .bindTexture(gl.GL_TEXTURE0)
+        self.colourTexture.bindTexture(gl.GL_TEXTURE1)
+
+        fslgl.glvolume_funcs.preDraw(self)
+
+        
+    def draw(self, zpos, xform=None):
+        """Draws a 2D slice of the image at the given real world Z location.
+        This is performed via a call to the OpenGL version-dependent `draw`
+        function, contained in one of the :mod:`~fsl.fslview.gl.gl14` or
+        :mod:`~fsl.fslview.gl.gl21` packages.
+
+        If `xform` is not None, it is applied as an affine transformation to
+        the vertex coordinates of the rendered image data.
+
+        Note: Calls to this method must be preceded by a call to
+        :meth:`preDraw`, and followed by a call to :meth:`postDraw`.
+        """
+        
+        fslgl.glvolume_funcs.draw(self, zpos, xform)
+
+        
+    def drawAll(self, zposes, xforms):
+        """Calls the module-specific ``drawAll`` function. """
+        
+        fslgl.glvolume_funcs.drawAll(self, zposes, xforms)
+
+        
+    def postDraw(self):
+        """Clears the GL state after drawing from this :class:`GLVolume`
+        instance.
+        """
+
+        self.imageTexture .unbindTexture()
+        self.colourTexture.unbindTexture()
+        
+        fslgl.glvolume_funcs.postDraw(self) 
diff --git a/fsl/fslview/gl/textures/imagetexture.py b/fsl/fslview/gl/textures/imagetexture.py
index 943a2bc737a96389c53a7dc126b16c1b2455a939..922f65f97922267fafa697f2262908f5246b3187 100644
--- a/fsl/fslview/gl/textures/imagetexture.py
+++ b/fsl/fslview/gl/textures/imagetexture.py
@@ -24,14 +24,6 @@ class ImageTexture(texture.Texture):
     """This class contains the logic required to create and manage a 3D
     texture which represents a :class:`~fsl.data.image.Image` instance.
 
-    On creation, an ``ImageTexture`` instance registers some listeners on the
-    properties of the :class:`~fsl.data.image.Image` instance (and the
-    :class:`~fsl.fslview.displaycontext.Display` instance if one is provided),
-    so that it may re-generate the texture data when these properties
-    change. For example, if the
-    :attr:`~fsl.fslview.displaycontext.Display.resolution` property changes,
-    the image data is re-sampled accordingly.
-
     Once created, the following attributes are available on an
     :class:`ImageTexture` object:
 
@@ -46,7 +38,6 @@ class ImageTexture(texture.Texture):
     def __init__(self,
                  name,                 
                  image,
-                 display=None,
                  nvals=1,
                  normalise=False,
                  prefilter=None,
@@ -56,10 +47,6 @@ class ImageTexture(texture.Texture):
         :arg name:      A name for the texture.
         
         :arg image:     The :class:`~fsl.data.image.Image` instance.
-        
-        :arg display:   A :class:`~fsl.fslview.displaycontext.Display`
-                        instance which defines how the image is to be
-                        displayed, or ``None`` if this image has no display.
           
         :arg nvals:     Number of values per voxel. For example. a normal MRI
                         or fMRI image contains only one value for each voxel.
@@ -83,97 +70,91 @@ class ImageTexture(texture.Texture):
                                'size {} requested for '
                                'image shape {}'.format(nvals, image.shape))
 
-        self.image     = image
-        self.display   = display
-        self.nvals     = nvals
-        self.prefilter = prefilter
+        self.image        = image
+        self.nvals        = nvals
+        self.__interp     = None
+        self.__prefilter  = None
+        self.__resolution = None
+        self.__volume     = None
+        self.__normalise  = None
 
-        dtype = image.data.dtype
+        self.__name = '{}_{}'.format(type(self).__name__, id(self))
+        self.image.addListener('data',
+                               self.__name,
+                               lambda *a: self.__imageDataChanged())
 
-        # If the normalise flag is true, or the data is
-        # of a type which cannot be stored natively as
-        # an OpenGL texture, the data is cast to a
-        # standard type, and normalised - see
-        # _determineTextureType  and _prepareTextureData
-        self.normalise = normalise or dtype not in (np.uint8,
-                                                    np.int8,
-                                                    np.uint16,
-                                                    np.int16)
+        self.__imageDataChanged(False)
 
-        texFmt, intFmt, texDtype, voxValXform = self._determineTextureType()
-
-        self.texFmt         = texFmt
-        self.texIntFmt      = intFmt
-        self.texDtype       = texDtype
-        self.voxValXform    = voxValXform
-        self.invVoxValXform = transform.invert(voxValXform)
-
-        self._addListeners()
-
-        self.setInterpolation(interp)
-        self.refresh()
+        self.set(interp=interp,
+                 prefilter=prefilter,
+                 resolution=None,
+                 volume=None,
+                 normalise=normalise)
 
 
     def destroy(self):
-        """Deletes the texture identifier, and removes any property
-        listeners which were registered on the ``.Image`` and ``.Display``
-        instances.
-        """
+        """Deletes the texture identifier """
 
         texture.Texture.destroy(self)
-        self._removeListeners()
+        self.image.removeListener('data', self.__name)
 
-        
-    def setPrefilter(self, prefilter):
-        """Updates the method used to pre-filter the data, and refreshes the
-        texture.
-
-        See :meth:`__init__`.
-        """
-        
-        changed = self.prefilter is not prefilter
-        self.prefilter = prefilter
-
-        if changed:
-            self.refresh()
 
-    
-    def _addListeners(self):
-        """Adds listeners to some properties of the ``Image`` and ``Display``
-        instances, so this ``ImageTexture`` can re-generate texture data
-        when necessary.
-        """
+    def __imageDataChanged(self, refresh=True):
 
-        image   = self.image
-        display = self.display
-        name    = '{}_{}'.format(type(self).__name__, id(self))
+        data  = self.image.data
 
-        image.addListener('data', name, self.refresh)
+        if self.__prefilter is not None:
+            data = self.__prefilter(data)
+        
+        self.__dataMin  = float(data.min())
+        self.__dataMax  = float(data.max())         
 
-        if display is not None:
-            opts    = display.getDisplayOpts()
-                    
-            opts.addListener('volume',     name, self.refresh)
-            opts.addListener('resolution', name, self.refresh)
+        if refresh:
+            self.refresh()
 
         
-    def _removeListeners(self):
-        """Called by the :meth:`destroy` method. Removes the property
-        listeners which were configured by the :meth:`_addListeners`
-        method.
-        """
+    def set(self, **kwargs):
+        interp     = kwargs.get('interp',     self.__interp)
+        prefilter  = kwargs.get('prefilter',  self.__prefilter)
+        resolution = kwargs.get('resolution', self.__resolution)
+        volume     = kwargs.get('volume',     self.__volume)
+        normalise  = kwargs.get('normalise',  self.__normalise)
+
+        changed = (interp     != self.__interp     or
+                   prefilter  != self.__prefilter  or
+                   resolution != self.__resolution or
+                   volume     != self.__volume     or
+                   normalise  != self.__normalise)
+
+        if not changed:
+            return
+
+        if prefilter != self.__prefilter:
+            self.__imageDataChanged()
+
+        self.__interp     = interp
+        self.__prefilter  = prefilter
+        self.__resolution = resolution
+        self.__volume     = volume
+            
+        # If the data is of a type which cannot be
+        # stored natively as an OpenGL texture, the
+        # data is cast to a standard type, and
+        # normalised - see _determineTextureType
+        # and _prepareTextureData
+        dtype = self.image.data.dtype
+        self.__normalise = normalise or dtype not in (np.uint8,
+                                                      np.int8,
+                                                      np.uint16,
+                                                      np.int16)
+        self.refresh()
 
-        image   = self.image
-        display = self.display
-        name    = '{}_{}'.format(type(self).__name__, id(self))
-        
-        image.removeListener('data', name)
 
-        if display is not None:
-            opts = display.getDisplayOpts()
- 
-            opts   .removeListener('volume',        name)
-            opts   .removeListener('resolution',    name)
+    def setInterp(    self, interp):     self.set(interp=interp)
+    def setPrefilter( self, prefilter):  self.set(prefilter=prefilter)
+    def setResolution(self, resolution): self.set(resolution=resolution)
+    def setVolume(    self, volume):     self.set(volume=volume)
+    def setNormalise( self, normalise):  self.set(normalise=normalise)
 
 
     def _determineTextureType(self):
@@ -205,28 +186,34 @@ class ImageTexture(texture.Texture):
            to the appropriate range, and calculate a transformation matrix
            to transform back to the data range.
 
-        This method returns a tuple containing the following:
+        This method sets the following attributes on thius ``ImageTexture``
+        instance:
 
-          - The texture format (e.g. ``GL_RGB``, ``GL_LUMINANCE``)
+          - ``texFmt``:         The texture format (e.g. ``GL_RGB``,
+                                ``GL_LUMINANCE``)
 
-          - The internal texture format used by OpenGL for storage (e.g.
-            ``GL_RGB16``, ``GL_LUMINANCE8``).
+          - ``texIntFmt``:      The internal texture format used by OpenGL for
+                                storage (e.g. ``GL_RGB16``, ``GL_LUMINANCE8``).
 
-          - The raw type of the texture data (e.g. ``GL_UNSIGNED_SHORT``)
+          - ``texDtype``:       The raw type of the texture data (e.g.
+                                ``GL_UNSIGNED_SHORT``)
 
-          - An affine transformation matrix which encodes an offset and a
-            scale, which may be used to transform the texture data from
-            the range [0.0, 1.0] to its original range.
+          - ``voxValXform``:    An affine transformation matrix which encodes 
+                                an offset and a scale, which may be used to 
+                                transform the texture data from the range 
+                                [0.0, 1.0] to its original range.
+
+          - ``invVoxValXform``: Inverse of ``voxValXform``.
         """        
 
         data  = self.image.data
 
-        if self.prefilter is not None:
-            data = self.prefilter(data)
+        if self.__prefilter is not None:
+            data = self.__prefilter(data)
         
         dtype = data.dtype
-        dmin  = float(data.min())
-        dmax  = float(data.max()) 
+        dmin  = self.__dataMin
+        dmax  = self.__dataMax
 
         # Signed data types are a pain in the arse.
         #
@@ -235,7 +222,7 @@ class ImageTexture(texture.Texture):
         # for signed types.
 
         # Texture data type
-        if   self.normalise:     texDtype = gl.GL_UNSIGNED_BYTE
+        if   self.__normalise:   texDtype = gl.GL_UNSIGNED_BYTE
         elif dtype == np.uint8:  texDtype = gl.GL_UNSIGNED_BYTE
         elif dtype == np.int8:   texDtype = gl.GL_UNSIGNED_BYTE
         elif dtype == np.uint16: texDtype = gl.GL_UNSIGNED_SHORT
@@ -254,28 +241,28 @@ class ImageTexture(texture.Texture):
         # Internal texture format
         if self.nvals == 1:
 
-            if   self.normalise:     intFmt = gl.GL_LUMINANCE8
+            if   self.__normalise:   intFmt = gl.GL_LUMINANCE8
             elif dtype == np.uint8:  intFmt = gl.GL_LUMINANCE8
             elif dtype == np.int8:   intFmt = gl.GL_LUMINANCE8
             elif dtype == np.uint16: intFmt = gl.GL_LUMINANCE16
             elif dtype == np.int16:  intFmt = gl.GL_LUMINANCE16
 
         elif self.nvals == 2:
-            if   self.normalise:     intFmt = gl.GL_LUMINANCE8_ALPHA8
+            if   self.__normalise:   intFmt = gl.GL_LUMINANCE8_ALPHA8
             elif dtype == np.uint8:  intFmt = gl.GL_LUMINANCE8_ALPHA8
             elif dtype == np.int8:   intFmt = gl.GL_LUMINANCE8_ALPHA8
             elif dtype == np.uint16: intFmt = gl.GL_LUMINANCE16_ALPHA16
             elif dtype == np.int16:  intFmt = gl.GL_LUMINANCE16_ALPHA16
 
         elif self.nvals == 3:
-            if   self.normalise:     intFmt = gl.GL_RGB8
+            if   self.__normalise:   intFmt = gl.GL_RGB8
             elif dtype == np.uint8:  intFmt = gl.GL_RGB8
             elif dtype == np.int8:   intFmt = gl.GL_RGB8
             elif dtype == np.uint16: intFmt = gl.GL_RGB16
             elif dtype == np.int16:  intFmt = gl.GL_RGB16
             
         elif self.nvals == 4:
-            if   self.normalise:     intFmt = gl.GL_RGBA8
+            if   self.__normalise:   intFmt = gl.GL_RGBA8
             elif dtype == np.uint8:  intFmt = gl.GL_RGBA8
             elif dtype == np.int8:   intFmt = gl.GL_RGBA8
             elif dtype == np.uint16: intFmt = gl.GL_RGBA16
@@ -284,13 +271,13 @@ class ImageTexture(texture.Texture):
         # Offsets/scales which can be used to transform from
         # the texture data (which may be offset or normalised)
         # back to the original voxel data
-        if   self.normalise:     offset =  dmin
+        if   self.__normalise:   offset =  dmin
         elif dtype == np.uint8:  offset =  0
         elif dtype == np.int8:   offset = -128
         elif dtype == np.uint16: offset =  0
         elif dtype == np.int16:  offset = -32768
 
-        if   self.normalise:     scale = dmax - dmin
+        if   self.__normalise:   scale = dmax - dmin
         elif dtype == np.uint8:  scale = 255
         elif dtype == np.int8:   scale = 255
         elif dtype == np.uint16: scale = 65535
@@ -337,11 +324,15 @@ class ImageTexture(texture.Texture):
                           sTexDtype,
                           sTexFmt,
                           sIntFmt,
-                          self.normalise,
+                          self.__normalise,
                           scale,
                           offset))
 
-        return texFmt, intFmt, texDtype, voxValXform
+        self.texFmt         = texFmt
+        self.texIntFmt      = intFmt
+        self.texDtype       = texDtype
+        self.voxValXform    = voxValXform
+        self.invVoxValXform = transform.invert(voxValXform)
 
 
     def _prepareTextureData(self):
@@ -363,31 +354,28 @@ class ImageTexture(texture.Texture):
             be used as-is).
         """
 
-        image   = self.image
-        display = self.display
+        image = self.image
+        data  = image.data
+        dtype = data.dtype
 
-        dtype = image.data.dtype
+        volume     = self.__volume
+        resolution = self.__resolution
+        prefilter  = self.__prefilter
+        normalise  = self.__normalise
 
-        if display is None:
-            data = image.data
-            
-        else:
-            opts = display.getDisplayOpts()
+        if volume is None:
+            volume = 0
+
+        if image.is4DImage() and self.nvals == 1:
+            data = data[..., volume]
+
+        if resolution is not None:
+            data = glroutines.subsample(data, resolution, image.pixdim)[0]
             
-            if self.nvals == 1 and image.is4DImage():
-                data = glroutines.subsample(image.data,
-                                            opts.resolution,
-                                            image.pixdim, 
-                                            opts.volume)[0]
-            else:
-                data = glroutines.subsample(image.data,
-                                            opts.resolution,
-                                            image.pixdim)[0]
-
-        if self.prefilter is not None:
-            data = self.prefilter(data)
+        if prefilter is not None:
+            data = prefilter(data)
         
-        if self.normalise:
+        if normalise:
             dmin = float(data.min())
             dmax = float(data.max())
             if dmax != dmin:
@@ -402,23 +390,13 @@ class ImageTexture(texture.Texture):
 
         return data
 
-
-    def setInterpolation(self, interp):
-        """Sets the texture interpolation method."""
-
-        self.bindTexture()
-        
-        gl.glTexParameteri(gl.GL_TEXTURE_3D, gl.GL_TEXTURE_MAG_FILTER, interp)
-        gl.glTexParameteri(gl.GL_TEXTURE_3D, gl.GL_TEXTURE_MIN_FILTER, interp)
-        
-        self.unbindTexture()
-
         
     def refresh(self, *a):
         """(Re-)generates the OpenGL image texture used to store the image
         data.
         """
 
+        self._determineTextureType()
         data = self._prepareTextureData()
 
         # It is assumed that, for textures with more than one
@@ -446,6 +424,14 @@ class ImageTexture(texture.Texture):
 
         self.bindTexture()
 
+        # set interpolation routine
+        gl.glTexParameteri(gl.GL_TEXTURE_3D,
+                           gl.GL_TEXTURE_MAG_FILTER,
+                           self.__interp)
+        gl.glTexParameteri(gl.GL_TEXTURE_3D,
+                           gl.GL_TEXTURE_MIN_FILTER,
+                           self.__interp)
+
         # Clamp texture borders to the edge
         # values - it is the responsibility
         # of the rendering logic to not draw
diff --git a/fsl/fslview/layouts.py b/fsl/fslview/layouts.py
index bfdc2f170e25c42366e54c092cb87e9205ae8cd8..d293161643ab068378f57dcd27b33326b90bc0d1 100644
--- a/fsl/fslview/layouts.py
+++ b/fsl/fslview/layouts.py
@@ -203,9 +203,9 @@ VolumeOptsLayout = props.VGroup(
 
 
 MaskOptsLayout = props.VGroup(
-    (widget(VolumeOpts, 'resolution',    showLimits=False),
-     widget(VolumeOpts, 'transform'),
-     widget(VolumeOpts, 'volume',        showLimits=False),
+    (widget(MaskOpts, 'resolution',    showLimits=False),
+     widget(MaskOpts, 'transform'),
+     widget(MaskOpts, 'volume',        showLimits=False),
      widget(MaskOpts, 'colour'),
      widget(MaskOpts, 'invert'),
      widget(MaskOpts, 'threshold', showLimits=False)))
@@ -214,7 +214,6 @@ MaskOptsLayout = props.VGroup(
 RGBVectorOptsLayout = props.VGroup((
     widget(RGBVectorOpts, 'resolution',    showLimits=False),
     widget(RGBVectorOpts, 'transform'),
-    widget(RGBVectorOpts, 'volume',        showLimits=False),
     widget(RGBVectorOpts, 'interpolation'),
     props.HGroup((
         widget(RGBVectorOpts, 'xColour'),
@@ -232,7 +231,6 @@ RGBVectorOptsLayout = props.VGroup((
 LineVectorOptsLayout = props.VGroup((
     widget(LineVectorOpts, 'resolution',    showLimits=False),
     widget(LineVectorOpts, 'transform'),
-    widget(LineVectorOpts, 'volume',        showLimits=False),
     props.HGroup((
         widget(LineVectorOpts, 'xColour'),
         widget(LineVectorOpts, 'yColour'),
diff --git a/fsl/fslview/views/canvaspanel.py b/fsl/fslview/views/canvaspanel.py
index c9e44f0e90a1acd17738ffcdd1a06cdacd7c3a45..4344dbde0747ce5651431e5b38e525f9b8718157 100644
--- a/fsl/fslview/views/canvaspanel.py
+++ b/fsl/fslview/views/canvaspanel.py
@@ -164,13 +164,8 @@ class CanvasPanel(viewpanel.ViewPanel):
     """
     """
 
-    syncLocation     = displayctx.DisplayContext.getSyncProperty('location')
-    # syncVolume       = displayctx.DisplayContext.getSyncProperty('volume')
-
-    # TODO
-    syncVolume       = props.Boolean(default=True)
-    syncOverlayOrder = displayctx.DisplayContext.getSyncProperty(
-        'overlayOrder')
+    syncLocation     = props.Boolean(default=True)
+    syncOverlayOrder = props.Boolean(default=True)
 
     def __init__(self,
                  parent,
@@ -212,10 +207,6 @@ class CanvasPanel(viewpanel.ViewPanel):
             self.bindProps('syncOverlayOrder',
                            displayCtx,
                            displayCtx.getSyncPropertyName('overlayOrder'))
-
-            log.warn('Skipping parent syncVolume binding, '
-                     'as it is not working yet.')
-
             
         # If the displayCtx instance does not
         # have a parent, this means that it is
@@ -223,7 +214,6 @@ class CanvasPanel(viewpanel.ViewPanel):
         else:
             self.disableProperty('syncLocation')
             self.disableProperty('syncOverlayOrder')
-            self.disableProperty('syncVolume')
 
         self.__canvasContainer = wx.Panel(self)
         self.__canvasPanel     = wx.Panel(self.__canvasContainer)