1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
function luttex = PsychHelperCreateGenericLuminanceToRGBA16MaxLUT(lut, targetBpc, win)
% luttex = PsychHelperCreateGenericLuminanceToRGBA16MaxLUT(lut, targetBpc, win);
%
% Helper function for PsychImaging() - Don't call from usercode!
%
% Used by PsychImaging(....,'EnableGenericHighPrecisionLuminanceOutput', lut);
% Converts a uint16 luminance -> RGB8/10/16 or RGBA8/10/16 LUT as provided in
% the 3- or 4 rows by n columns matrix 'lut' into an equivalent RGBA8/10/16
% lookup table texture, then returns the texture handle to the calling routine.
%
% 'lut' = 3 rows by nslots columns uint8 matrix for LUT's that map the
% input luminance value range [0.0 - 1.0] to the integral 0 - nslots range,
% then lookup the corresponding RGB8/10/16 output pixel in the lut. If 'lut' has
% 4 rows, then the 4th row encodes alpha-channel - This is only useful for
% debugging, as teh alpha channel can't be output to the external device.
%
% Implementation details:
%
% The lut texture is built as a 256x256 2D rectangle texture, allowing for
% luts with up to 2^16 luminance slots that work on all supported graphics
% hardware. One could extend the size of the texture to up to 8192x8192 on
% latest hardware, allowing for up to 2^26 bits, but that is likely not
% that useful, given that internal GPU precision is restricted to 23 bits
% (floating point single precision) and there doesn't exist a single output
% device with more than 16 bits of output resolution anyway.
%
% We assume GL is available:
global GL;
if nargin < 1
error('Called without mandatory "lut" input argument!');
end
if isempty(lut)
error('Called without mandatory "lut" input argument!');
end
[channels, nslots] = size(lut);
if channels < 3 || channels > 4
error('lut must have 3 or 4 rows aka channels!');
end
if nslots < 1 || nslots > 2^16
error('lut must have between 1 and 65536 slots, aka columns!');
end
if ~isa(lut,'uint16')
error('lut must by of uint16 type!');
end
% Pad lut with a fourth row of zeros, if it isn't already a 4 row lut:
if channels~=4
lut = [lut ; uint16(zeros(1, nslots))];
end
nrows = ceil(nslots / 256);
ncols = 256;
% Padding required? Expand lut with 'nexpand' zero-slots aka columns if lut
% size is not a multiple of 'ncols' slots:
if mod(nslots, ncols)~=0
nexpand = ncols - mod(nslots, ncols);
lut = [lut , uint16(zeros(4, nexpand))];
end
% Ok, our 'lut' is 4 rows aka channels, and has a multiple of 'ncols' slots
% to cleanly fill 'nrows' rows of a 'nrows' by 'ncols' matrix. Copy it into
% matrix:
clut = uint16(zeros(4, ncols, nrows));
for i=1:nrows
clut(1,:,i) = lut(1, (((i-1)*ncols + 1):(i*ncols)));
clut(2,:,i) = lut(2, (((i-1)*ncols + 1):(i*ncols)));
clut(3,:,i) = lut(3, (((i-1)*ncols + 1):(i*ncols)));
clut(4,:,i) = lut(4, (((i-1)*ncols + 1):(i*ncols)));
end
% Select format with minimal memory storage requirement for faithful
% representation of targetBpc bits deep RGB color:
if targetBpc <= 8
% Classic 8 bpc:
internalFormat = GL.RGBA;
elseif targetBpc <= 10
% Up to 10 bpc:
internalFormat = GL.RGB10_A2;
else
% Up to 16 bpc:
internalFormat = GL.RGBA16;
end
% Build texture:
% Create and setup texture from 'clut':
luttex = glGenTextures(1);
glBindTexture(GL.TEXTURE_RECTANGLE_EXT, luttex);
% Allocate texture storage:
glTexImage2D(GL.TEXTURE_RECTANGLE_EXT, 0, internalFormat, ncols, nrows, 0, GL.RGBA, GL.UNSIGNED_SHORT, clut);
% Query what we've got:
trueR = glGetTexLevelParameteriv(GL.TEXTURE_RECTANGLE_EXT, 0, GL.TEXTURE_RED_SIZE_EXT);
trueG = glGetTexLevelParameteriv(GL.TEXTURE_RECTANGLE_EXT, 0, GL.TEXTURE_GREEN_SIZE_EXT);
trueB = glGetTexLevelParameteriv(GL.TEXTURE_RECTANGLE_EXT, 0, GL.TEXTURE_BLUE_SIZE_EXT);
if (internalFormat == GL.RGBA) && (trueR < 8 || trueG < 8 || trueB < 8)
warning('PsychHelperCreateGenericLuminanceToRGBA16MaxLUT: Did not even get required 8 bpc LUT depth!');
end
if (internalFormat == GL.RGB10_A2) && (trueR < targetBpc || trueG < targetBpc || trueB < targetBpc)
fprintf('PsychHelperCreateGenericLuminanceToRGBA16MaxLUT: Did not get required %i bpc LUT depth, upgrading to 16 bpc texture.\n', targetBpc);
internalFormat = GL.RGBA16;
glTexImage2D(GL.TEXTURE_RECTANGLE_EXT, 0, internalFormat, ncols, nrows, 0, GL.RGBA, GL.UNSIGNED_SHORT, clut);
% Query what we've got:
trueR = glGetTexLevelParameteriv(GL.TEXTURE_RECTANGLE_EXT, 0, GL.TEXTURE_RED_SIZE_EXT);
trueG = glGetTexLevelParameteriv(GL.TEXTURE_RECTANGLE_EXT, 0, GL.TEXTURE_GREEN_SIZE_EXT);
trueB = glGetTexLevelParameteriv(GL.TEXTURE_RECTANGLE_EXT, 0, GL.TEXTURE_BLUE_SIZE_EXT);
end
if (internalFormat == GL.RGBA16) && (trueR < targetBpc || trueG < targetBpc || trueB < targetBpc)
fprintf('PsychHelperCreateGenericLuminanceToRGBA16MaxLUT: Did not get required %i bpc LUT depth, upgrading to 32 bpc floating point texture.\n', targetBpc);
internalFormat = GL.RGBA32F;
winfo = Screen('GetWindowInfo', win);
if winfo.GLSupportsTexturesUpToBpc < 32
error('PsychHelperCreateGenericLuminanceToRGBA16MaxLUT: Would need to use 32 bit floating point textures, but these are unsupported by the hardware!');
end
glTexImage2D(GL.TEXTURE_RECTANGLE_EXT, 0, internalFormat, ncols, nrows, 0, GL.RGBA, GL.UNSIGNED_SHORT, clut);
% Query what we've got:
trueR = glGetTexLevelParameteriv(GL.TEXTURE_RECTANGLE_EXT, 0, GL.TEXTURE_RED_SIZE_EXT);
trueG = glGetTexLevelParameteriv(GL.TEXTURE_RECTANGLE_EXT, 0, GL.TEXTURE_GREEN_SIZE_EXT);
trueB = glGetTexLevelParameteriv(GL.TEXTURE_RECTANGLE_EXT, 0, GL.TEXTURE_BLUE_SIZE_EXT);
if (trueR < targetBpc || trueG < targetBpc || trueB < targetBpc)
error('PsychHelperCreateGenericLuminanceToRGBA16MaxLUT: Impossible to get LUT textures of sufficient precision from hardware!');
end
end
% Ok, we have a texture of sufficient but minimal bit depths for driving a targetBpc deep framebuffer, suitable for use as a LUT.
switch internalFormat
case GL.RGBA
fprintf('PsychHelperCreateGenericLuminanceToRGBA16MaxLUT: Using RGBA8 LUT with bit depths [%i,%i,%i] for targetBpc %i\n', trueR, trueG, trueB, targetBpc);
case GL.RGB10_A2
fprintf('PsychHelperCreateGenericLuminanceToRGBA16MaxLUT: Using RGB10_A2 LUT with bit depths [%i,%i,%i] for targetBpc %i\n', trueR, trueG, trueB, targetBpc);
case GL.RGBA16
fprintf('PsychHelperCreateGenericLuminanceToRGBA16MaxLUT: Using RGBA16 LUT with bit depths [%i,%i,%i] for targetBpc %i\n', trueR, trueG, trueB, targetBpc);
case GL.RGBA32F
fprintf('PsychHelperCreateGenericLuminanceToRGBA16MaxLUT: Using RGBA32F floating point LUT with bit depths [%i,%i,%i] for targetBpc %i\n', trueR, trueG, trueB, targetBpc);
end
% Make sure we use nearest neighbour sampling:
glTexParameteri(GL.TEXTURE_RECTANGLE_EXT, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
glTexParameteri(GL.TEXTURE_RECTANGLE_EXT, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
% And that we clamp to edge:
glTexParameteri(GL.TEXTURE_RECTANGLE_EXT, GL.TEXTURE_WRAP_S, GL.CLAMP);
glTexParameteri(GL.TEXTURE_RECTANGLE_EXT, GL.TEXTURE_WRAP_T, GL.CLAMP);
glBindTexture(GL.TEXTURE_RECTANGLE_EXT, 0);
% Ready, return luttex:
return;
|