File: PsychHelperCreateGenericLuminanceToRGBA16MaxLUT.m

package info (click to toggle)
psychtoolbox-3 3.0.19.14.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 86,796 kB
  • sloc: ansic: 176,245; cpp: 20,103; objc: 5,393; sh: 2,753; python: 1,397; php: 384; makefile: 193; java: 113
file content (165 lines) | stat: -rw-r--r-- 7,167 bytes parent folder | download | duplicates (4)
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;