Skip to content

Commit

Permalink
Merge pull request #1920 from djhoese/feature-image-texture-format
Browse files Browse the repository at this point in the history
Add 'texture_format' kwarg to ImageVisual for floating point textures
  • Loading branch information
djhoese committed Mar 15, 2021
2 parents 76b5e47 + f952129 commit 04d0505
Show file tree
Hide file tree
Showing 10 changed files with 669 additions and 198 deletions.
2 changes: 1 addition & 1 deletion examples/basics/plotting/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def gen_image(width, height):
grid = np.meshgrid(x_vals, y_vals)
v_fn = np.vectorize(exp_z_inv)

return v_fn(*grid).astype(np.float)
return v_fn(*grid).astype(np.float32)

fig = vp.Fig(size=(800, 600), show=False)
plot = fig[0, 0]
Expand Down
3 changes: 2 additions & 1 deletion examples/basics/scene/isocurve_updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@
# Create random image
img_data1 = np.empty((200, 100, 3), dtype=np.ubyte)
noise = np.random.normal(size=(200, 100), loc=50, scale=150)
noise = gaussian_filter(noise, (4, 4, 0))
noise = gaussian_filter(noise, (4, 4, 0)).astype(np.float32)
img_data1[:] = noise[..., np.newaxis]

# create 2d array with some function
x, y = np.mgrid[0:2*np.pi:201j, 0:2*np.pi:101j]
myfunc = np.cos(2*x[:-1, :-1]) + np.sin(2*y[:-1, :-1])
myfunc = myfunc.astype(np.float32)

# add image to viewbox1
image1 = scene.visuals.Image(noise, parent=vb1.scene, cmap='cubehelix')
Expand Down
2 changes: 1 addition & 1 deletion examples/basics/visuals/colorbar_visual.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def get_mandlebrot_escape_values(width, height):
grid = np.meshgrid(x_vals, y_vals)

v_get_num_escape_turns = np.vectorize(get_num_escape_turns)
return v_get_num_escape_turns(*grid).astype(np.float)
return v_get_num_escape_turns(*grid).astype(np.float32)


def get_vertical_bar(pos, size):
Expand Down
11 changes: 10 additions & 1 deletion vispy/gloo/glir.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,16 @@
gl.Enum('GL_RGBA8', 32856),
gl.Enum('GL_RGBA16', 32859),
gl.Enum('GL_RGBA16F', 34842),
gl.Enum('GL_RGBA32F', 34836)
gl.Enum('GL_RGBA32F', 34836),
# extended formats (not currently supported)
# gl.Enum('GL_R32I', 33333),
# gl.Enum('GL_RG32I', 33339),
# gl.Enum('GL_RGB32I', 36227),
# gl.Enum('GL_RGBA32I', 36226),
# gl.Enum('GL_R32UI', 33334),
# gl.Enum('GL_RG32UI', 33340),
# gl.Enum('GL_RGB32UI', 36209),
# gl.Enum('GL_RGBA32UI', 36208),
]
_internalformats = dict([(enum.name, enum) for enum in _internalformats])

Expand Down
62 changes: 37 additions & 25 deletions vispy/gloo/texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,14 @@ class BaseTexture(GLObject):
'rgba': 4
}

# NOTE: non-normalized formats ending with 'i' and 'ui' are currently
# disabled as they don't work with the current VisPy implementation.
# Attempting to use them along with the additional enums defined in
# vispy/gloo/glir.py produces an invalid operation from OpenGL.
_inv_internalformats = dict([
(base + suffix, channels)
for base, channels in [('r', 1), ('rg', 2), ('rgb', 3), ('rgba', 4)]
for suffix in ['8', '16', '16f', '32f']
for suffix in ['8', '16', '16f', '32f'] # , '8i', '8ui', '32i', '32ui']
] + [
('luminance', 1),
('alpha', 1),
Expand Down Expand Up @@ -147,10 +151,14 @@ def shape(self):

@property
def format(self):
""" The texture format (color channels).
"""
"""The texture format (color channels)."""
return self._format

@property
def internalformat(self):
"""The texture internalformat."""
return self._internalformat

@property
def wrapping(self):
""" Texture wrapping mode """
Expand Down Expand Up @@ -222,51 +230,55 @@ def resize(self, shape, format=None, internalformat=None):
"""
return self._resize(shape, format, internalformat)

def _resize(self, shape, format=None, internalformat=None):
"""Internal method for resize.
"""
shape = self._normalize_shape(shape)

# Check
if not self._resizable:
raise RuntimeError("Texture is not resizable")

def _check_format_change(self, format, num_channels):
# Determine format
if format is None:
format = self._formats[shape[-1]]
format = self._formats[num_channels]
# Keep current format if channels match
if self._format and \
self._inv_formats[self._format] == self._inv_formats[format]:
self._inv_formats[self._format] == self._inv_formats[format]:
format = self._format
else:
format = check_enum(format)

if format not in self._inv_formats:
raise ValueError('Invalid texture format: %r.' % format)
elif num_channels != self._inv_formats[format]:
raise ValueError('Format does not match with given shape. '
'(format expects %d elements, data has %d)' %
(self._inv_formats[format], num_channels))
return format

def _check_internalformat_change(self, internalformat, num_channels):
if internalformat is None:
# Keep current internalformat if channels match
if self._internalformat and \
self._inv_internalformats[self._internalformat] == shape[-1]:
self._inv_internalformats[self._internalformat] == num_channels:
internalformat = self._internalformat
else:

internalformat = check_enum(internalformat)

# Check
if format not in self._inv_formats:
raise ValueError('Invalid texture format: %r.' % format)
elif shape[-1] != self._inv_formats[format]:
raise ValueError('Format does not match with given shape. '
'(format expects %d elements, data has %d)' %
(self._inv_formats[format], shape[-1]))

if internalformat is None:
pass
elif internalformat not in self._inv_internalformats:
raise ValueError(
'Invalid texture internalformat: %r. Allowed formats: %r'
% (internalformat, self._inv_internalformats)
)
elif shape[-1] != self._inv_internalformats[internalformat]:
elif num_channels != self._inv_internalformats[internalformat]:
raise ValueError('Internalformat does not match with given shape.')
return internalformat

def _resize(self, shape, format=None, internalformat=None):
"""Internal method for resize."""
shape = self._normalize_shape(shape)

# Check
if not self._resizable:
raise RuntimeError("Texture is not resizable")

format = self._check_format_change(format, shape[-1])
internalformat = self._check_internalformat_change(internalformat, shape[-1])

# Store and send GLIR command
self._shape = shape
Expand Down
5 changes: 2 additions & 3 deletions vispy/gloo/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ def check_identifier(name):


def check_enum(enum, name=None, valid=None):
""" Get lowercase string representation of enum.
"""
"""Get lowercase string representation of enum."""
name = name or 'enum'
# Try to convert
res = None
Expand All @@ -85,7 +84,7 @@ def check_enum(enum, name=None, valid=None):
res = enum.lower()
# Check
if res is None:
raise ValueError('Could not determine string represenatation for'
raise ValueError('Could not determine string representation for'
'enum %r' % enum)
elif valid and res not in valid:
raise ValueError('Value of %s must be one of %r, not %r' %
Expand Down
4 changes: 4 additions & 0 deletions vispy/testing/_runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,10 @@ def test(label='full', extra_arg_string='', coverage=False):
if label not in known_types + backend_names:
raise ValueError('label must be one of %s, or a backend name %s, '
'not \'%s\'' % (known_types, backend_names, label))
# remove troublesome backends
# see https://github.com/vispy/vispy/issues/2009
backend_names.remove('tkinter')

# figure out what we actually need to run
runs = []
if label in ('full', 'unit'):
Expand Down

0 comments on commit 04d0505

Please sign in to comment.