summaryrefslogtreecommitdiff
path: root/silx/gui/plot/backends/glutils/GLPlotImage.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/backends/glutils/GLPlotImage.py')
-rw-r--r--silx/gui/plot/backends/glutils/GLPlotImage.py122
1 files changed, 81 insertions, 41 deletions
diff --git a/silx/gui/plot/backends/glutils/GLPlotImage.py b/silx/gui/plot/backends/glutils/GLPlotImage.py
index 5d79023..e985a3d 100644
--- a/silx/gui/plot/backends/glutils/GLPlotImage.py
+++ b/silx/gui/plot/backends/glutils/GLPlotImage.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2014-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2014-2020 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -56,7 +56,7 @@ class _GLPlotData2D(object):
sx, sy = self.scale
col = int((x - ox) / sx)
row = int((y - oy) / sy)
- return ((row, col),)
+ return (row,), (col,)
else:
return None
@@ -141,10 +141,8 @@ class GLPlotColormap(_GLPlotData2D):
""",
'fragTransform': """
uniform bvec2 isLog;
- uniform struct {
- vec2 oneOverRange;
- vec2 originOverRange;
- } bounds;
+ uniform vec2 bounds_oneOverRange;
+ uniform vec2 bounds_originOverRange;
vec2 textureCoords(void) {
vec2 pos = coords;
@@ -154,7 +152,7 @@ class GLPlotColormap(_GLPlotData2D):
if (isLog.y) {
pos.y = pow(10., coords.y);
}
- return pos * bounds.oneOverRange - bounds.originOverRange;
+ return pos * bounds_oneOverRange - bounds_originOverRange;
// TODO texture coords in range different from [0, 1]
}
"""},
@@ -163,12 +161,11 @@ class GLPlotColormap(_GLPlotData2D):
#version 120
uniform sampler2D data;
- uniform struct {
- sampler2D texture;
- bool isLog;
- float min;
- float oneOverRange;
- } cmap;
+ uniform sampler2D cmap_texture;
+ uniform int cmap_normalization;
+ uniform float cmap_parameter;
+ uniform float cmap_min;
+ uniform float cmap_oneOverRange;
uniform float alpha;
varying vec2 coords;
@@ -179,19 +176,33 @@ class GLPlotColormap(_GLPlotData2D):
void main(void) {
float value = texture2D(data, textureCoords()).r;
- if (cmap.isLog) {
+ if (cmap_normalization == 1) { /*Logarithm mapping*/
if (value > 0.) {
- value = clamp(cmap.oneOverRange *
- (oneOverLog10 * log(value) - cmap.min),
+ value = clamp(cmap_oneOverRange *
+ (oneOverLog10 * log(value) - cmap_min),
0., 1.);
} else {
value = 0.;
}
- } else { /*Linear mapping*/
- value = clamp(cmap.oneOverRange * (value - cmap.min), 0., 1.);
+ } else if (cmap_normalization == 2) { /*Square root mapping*/
+ if (value >= 0.) {
+ value = clamp(cmap_oneOverRange * (sqrt(value) - cmap_min),
+ 0., 1.);
+ } else {
+ value = 0.;
+ }
+ } else if (cmap_normalization == 3) { /*Gamma correction mapping*/
+ value = pow(
+ clamp(cmap_oneOverRange * (value - cmap_min), 0., 1.),
+ cmap_parameter);
+ } else if (cmap_normalization == 4) { /* arcsinh mapping */
+ /* asinh = log(x + sqrt(x*x + 1) for compatibility with GLSL 1.20 */
+ value = clamp(cmap_oneOverRange * (log(value + sqrt(value*value + 1.0)) - cmap_min), 0., 1.);
+ } else { /*Linear mapping and fallback*/
+ value = clamp(cmap_oneOverRange * (value - cmap_min), 0., 1.);
}
- gl_FragColor = texture2D(cmap.texture, vec2(value, 0.5));
+ gl_FragColor = texture2D(cmap_texture, vec2(value, 0.5));
gl_FragColor.a *= alpha;
}
"""
@@ -217,8 +228,10 @@ class GLPlotColormap(_GLPlotData2D):
_SHADERS['log']['fragTransform'],
attrib0='position')
+ SUPPORTED_NORMALIZATIONS = 'linear', 'log', 'sqrt', 'gamma', 'arcsinh'
+
def __init__(self, data, origin, scale,
- colormap, cmapIsLog=False, cmapRange=None,
+ colormap, normalization='linear', gamma=0., cmapRange=None,
alpha=1.0):
"""Create a 2D colormap
@@ -231,7 +244,9 @@ class GLPlotColormap(_GLPlotData2D):
:type scale: 2-tuple of floats.
:param str colormap: Name of the colormap to use
TODO: Accept a 1D scalar array as the colormap
- :param bool cmapIsLog: If True, uses log10 of the data value
+ :param str normalization: The colormap normalization.
+ One of: 'linear', 'log', 'sqrt', 'gamma'
+ ;param float gamma: The gamma parameter (for 'gamma' normalization)
:param cmapRange: The range of colormap or None for autoscale colormap
For logarithmic colormap, the range is in the untransformed data
TODO: check consistency with matplotlib
@@ -239,10 +254,12 @@ class GLPlotColormap(_GLPlotData2D):
:param float alpha: Opacity from 0 (transparent) to 1 (opaque)
"""
assert data.dtype in self._INTERNAL_FORMATS
+ assert normalization in self.SUPPORTED_NORMALIZATIONS
super(GLPlotColormap, self).__init__(data, origin, scale)
self.colormap = numpy.array(colormap, copy=False)
- self.cmapIsLog = cmapIsLog
+ self.normalization = normalization
+ self.gamma = gamma
self._cmapRange = (1., 10.) # Colormap range
self.cmapRange = cmapRange # Update _cmapRange
self._alpha = numpy.clip(alpha, 0., 1.)
@@ -263,8 +280,10 @@ class GLPlotColormap(_GLPlotData2D):
@property
def cmapRange(self):
- if self.cmapIsLog:
+ if self.normalization == 'log':
assert self._cmapRange[0] > 0. and self._cmapRange[1] > 0.
+ elif self.normalization == 'sqrt':
+ assert self._cmapRange[0] >= 0. and self._cmapRange[1] > 0.
return self._cmapRange
@cmapRange.setter
@@ -319,6 +338,7 @@ class GLPlotColormap(_GLPlotData2D):
def _setCMap(self, prog):
dataMin, dataMax = self.cmapRange # If log, it is stricly positive
+ param = 0.
if self.data.dtype in (numpy.uint16, numpy.uint8):
# Using unsigned int as normalized integer in OpenGL
@@ -326,19 +346,35 @@ class GLPlotColormap(_GLPlotData2D):
maxInt = float(numpy.iinfo(self.data.dtype).max)
dataMin, dataMax = dataMin / maxInt, dataMax / maxInt
- if self.cmapIsLog:
+ if self.normalization == 'log':
dataMin = math.log10(dataMin)
dataMax = math.log10(dataMax)
-
- gl.glUniform1i(prog.uniforms['cmap.texture'],
+ normID = 1
+ elif self.normalization == 'sqrt':
+ dataMin = math.sqrt(dataMin)
+ dataMax = math.sqrt(dataMax)
+ normID = 2
+ elif self.normalization == 'gamma':
+ # Keep dataMin, dataMax as is
+ param = self.gamma
+ normID = 3
+ elif self.normalization == 'arcsinh':
+ dataMin = numpy.arcsinh(dataMin)
+ dataMax = numpy.arcsinh(dataMax)
+ normID = 4
+ else: # Linear and fallback
+ normID = 0
+
+ gl.glUniform1i(prog.uniforms['cmap_texture'],
self._cmap_texture.texUnit)
- gl.glUniform1i(prog.uniforms['cmap.isLog'], self.cmapIsLog)
- gl.glUniform1f(prog.uniforms['cmap.min'], dataMin)
+ gl.glUniform1i(prog.uniforms['cmap_normalization'], normID)
+ gl.glUniform1f(prog.uniforms['cmap_parameter'], param)
+ gl.glUniform1f(prog.uniforms['cmap_min'], dataMin)
if dataMax > dataMin:
oneOverRange = 1. / (dataMax - dataMin)
else:
oneOverRange = 0. # Fall-back
- gl.glUniform1f(prog.uniforms['cmap.oneOverRange'], oneOverRange)
+ gl.glUniform1f(prog.uniforms['cmap_oneOverRange'], oneOverRange)
self._cmap_texture.bind()
@@ -393,9 +429,9 @@ class GLPlotColormap(_GLPlotData2D):
xOneOverRange = 1. / (ex - ox)
yOneOverRange = 1. / (ey - oy)
- gl.glUniform2f(prog.uniforms['bounds.originOverRange'],
+ gl.glUniform2f(prog.uniforms['bounds_originOverRange'],
ox * xOneOverRange, oy * yOneOverRange)
- gl.glUniform2f(prog.uniforms['bounds.oneOverRange'],
+ gl.glUniform2f(prog.uniforms['bounds_oneOverRange'],
xOneOverRange, yOneOverRange)
gl.glUniform1f(prog.uniforms['alpha'], self.alpha)
@@ -500,10 +536,8 @@ class GLPlotRGBAImage(_GLPlotData2D):
uniform sampler2D tex;
uniform bvec2 isLog;
- uniform struct {
- vec2 oneOverRange;
- vec2 originOverRange;
- } bounds;
+ uniform vec2 bounds_oneOverRange;
+ uniform vec2 bounds_originOverRange;
uniform float alpha;
varying vec2 coords;
@@ -516,7 +550,7 @@ class GLPlotRGBAImage(_GLPlotData2D):
if (isLog.y) {
pos.y = pow(10., coords.y);
}
- return pos * bounds.oneOverRange - bounds.originOverRange;
+ return pos * bounds_oneOverRange - bounds_originOverRange;
// TODO texture coords in range different from [0, 1]
}
@@ -530,7 +564,8 @@ class GLPlotRGBAImage(_GLPlotData2D):
_DATA_TEX_UNIT = 0
_SUPPORTED_DTYPES = (numpy.dtype(numpy.float32),
- numpy.dtype(numpy.uint8))
+ numpy.dtype(numpy.uint8),
+ numpy.dtype(numpy.uint16))
_linearProgram = Program(_SHADERS['linear']['vertex'],
_SHADERS['linear']['fragment'],
@@ -582,9 +617,14 @@ class GLPlotRGBAImage(_GLPlotData2D):
def prepare(self):
if self._texture is None:
- format_ = gl.GL_RGBA if self.data.shape[2] == 4 else gl.GL_RGB
+ formatName = 'GL_RGBA' if self.data.shape[2] == 4 else 'GL_RGB'
+ format_ = getattr(gl, formatName)
- self._texture = Image(format_,
+ if self.data.dtype == numpy.uint16:
+ formatName += '16' # Use sized internal format for uint16
+ internalFormat = getattr(gl, formatName)
+
+ self._texture = Image(internalFormat,
self.data,
format_=format_,
texUnit=self._DATA_TEX_UNIT)
@@ -639,9 +679,9 @@ class GLPlotRGBAImage(_GLPlotData2D):
xOneOverRange = 1. / (ex - ox)
yOneOverRange = 1. / (ey - oy)
- gl.glUniform2f(prog.uniforms['bounds.originOverRange'],
+ gl.glUniform2f(prog.uniforms['bounds_originOverRange'],
ox * xOneOverRange, oy * yOneOverRange)
- gl.glUniform2f(prog.uniforms['bounds.oneOverRange'],
+ gl.glUniform2f(prog.uniforms['bounds_oneOverRange'],
xOneOverRange, yOneOverRange)
try: