~ubuntu-branches/ubuntu/trusty/psychtoolbox-3/trusty-proposed

« back to all changes in this revision

Viewing changes to Psychtoolbox/PsychGLImageProcessing/CreateResolutionPyramid.m

  • Committer: Package Import Robot
  • Author(s): Yaroslav Halchenko
  • Date: 2013-11-19 23:34:50 UTC
  • mfrom: (3.1.4 experimental)
  • Revision ID: package-import@ubuntu.com-20131119233450-f7nf92vb8qavjmk8
Tags: 3.0.11.20131017.dfsg1-3
Upload to unsable since fresh glew has arrived to sid!

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
function CreateResolutionPyramid(tex, glsl, usebilinear)
 
2
% Build a mip-map image resolution pyramid for given texture.
 
3
%
 
4
% CreateResolutionPyramid(tex [, glsl=0][, usebilinear=0])
 
5
%
 
6
% 'tex' must be the Screen() texture or offscreen window handle of a
 
7
% texture or offscreen window of type GL_TEXTURE_2D, ie., created with
 
8
% 'specialFlags' setting 1. The function will create a OpenGL mip-map
 
9
% resolution pyramid by successive downsampling of 'tex' image content down
 
10
% to a image of 1x1 pixels. If 'glsl' is provided as handle to a shader, it
 
11
% will use that shader to do the downsampling, otherwise it will use
 
12
% bilinear filtering for successive pyramid levels. If 'usebilinear' is set
 
13
% to 1 it will feed the shader with linear interpolated samples, otherwise
 
14
% with nearest neighbour samples.
 
15
%
 
16
% You will likely also want to set 'specialFlags' flag 8 or 16 to prevent
 
17
% Screen('DrawTexture') from overwriting the image pyramid with its own
 
18
% auto-generated one.
 
19
%
 
20
% This needs a modern GLSL shading capable graphics card to work.
 
21
%
 
22
 
 
23
% History:
 
24
% 27.08.2012  mk   Written.
 
25
%
 
26
 
 
27
% Needed for GL constant definitions:
 
28
global GL;
 
29
 
 
30
% Make sure MOGL is initialized:
 
31
if isempty(GL)
 
32
    InitializeMatlabOpenGL([],[],1);
 
33
end
 
34
 
 
35
if nargin < 1 || isempty(tex)
 
36
    error('Must provide a valid Screen() texture or offscreen window handle for building of resolution pyramid!');
 
37
end
 
38
 
 
39
if nargin < 2 || isempty(glsl)
 
40
    glsl = 0;
 
41
end
 
42
 
 
43
if nargin < 3 || isempty(usebilinear)
 
44
    usebilinear = 0;
 
45
end
 
46
 
 
47
% Only supported for offscreen targets, not onscreen windows, proxies etc.:
 
48
if Screen('WindowKind', tex) ~= -1
 
49
    error('Tried to built a resolution image pyramid for something else than a texture or offscreen window! Unsupported.');
 
50
end
 
51
 
 
52
% This, as a well defined side effect, will bind the tex'tures associated
 
53
% FBO, or create one if it doesn't have one yet, as well as normalize the
 
54
% textures format and orientation, just as we need it:
 
55
Screen('GetWindowInfo', tex);
 
56
 
 
57
% Get width and height:
 
58
[w, h] = Screen('WindowSize', tex);
 
59
 
 
60
% Get low-level texture handle and type:
 
61
[gltexid, gltextarget] = Screen('GetOpenGLTexture', tex, tex);
 
62
 
 
63
% Make sure it is a mip-map capable type:
 
64
if gltextarget ~= GL.TEXTURE_2D
 
65
    error('Tried to built a resolution image pyramid for something else than a GL_TEXTURE_2D texture! Unsupported.');
 
66
end
 
67
 
 
68
% Time for a state backup:
 
69
glPushAttrib(GL.ALL_ATTRIB_BITS);
 
70
 
 
71
% Ok, the currently bound FBO is the FBO to use for drawing into this
 
72
% texture. And we got the gltexid of the corresponding texture.
 
73
% We also know that all texture parameters are set in a way to make it
 
74
% available as a rendertarget, e.g., the texture minification/magnification
 
75
% filters, wrap modes etc. are set to safe values, e.g., nearest neighbour
 
76
% sampling, clamp to edge etc.
 
77
 
 
78
% Bind the texture:
 
79
glBindTexture(gltextarget, gltexid);
 
80
 
 
81
% Enable sampling:
 
82
glDisable(GL.TEXTURE_RECTANGLE_EXT);
 
83
glEnable(gltextarget);
 
84
 
 
85
% Compute to what miplevel we need to downsample to cover all bases:
 
86
maxlod = floor(log2(max(w, h)));
 
87
 
 
88
% Query size of mip-level 'maxlod':
 
89
tw = glGetTexLevelParameteriv(gltextarget, maxlod, GL.TEXTURE_WIDTH);
 
90
th = glGetTexLevelParameteriv(gltextarget, maxlod, GL.TEXTURE_HEIGHT);
 
91
 
 
92
% If no such mip-level exists?
 
93
if tw == 0 || th == 0
 
94
    % No such mip-level: Generate initial mip-chain for this texture. We
 
95
    % allocate mip-levels of proper format and size, but empty, ie.,
 
96
    % without actually computing any content here:
 
97
    internalFormat = glGetTexLevelParameteriv(gltextarget, 0, GL.TEXTURE_INTERNAL_FORMAT);
 
98
 
 
99
    for miplevel = 1:maxlod
 
100
        % Need to case tw, th to double(), use max() operators and whatnot
 
101
        % to get proper size specs -- Matlab arithmetic on the returned
 
102
        % int32's from query behaves rather strange otherwise:
 
103
        tw = double(glGetTexLevelParameteriv(gltextarget, miplevel - 1, GL.TEXTURE_WIDTH));
 
104
        th = double(glGetTexLevelParameteriv(gltextarget, miplevel - 1, GL.TEXTURE_HEIGHT));
 
105
        glTexImage2D(gltextarget, miplevel, internalFormat, max(floor(tw/2), 1), max(floor(th/2), 1), 0, GL.RGBA, GL.UNSIGNED_BYTE, 0);
 
106
    end
 
107
end
 
108
 
 
109
% Bind downsampling shader and set it up to read from texture unit 0:
 
110
glUseProgram(glsl);
 
111
 
 
112
% If we've actually bound the fixed-function pipeline, we skip this step:
 
113
if glsl ~= 0
 
114
    glUniform1i(glGetUniformLocation(glsl, 'Image'), 0);
 
115
    
 
116
    % Query locations of uniforms for passing src / dst dimensions:
 
117
    srcSize = glGetUniformLocation(glsl, 'srcSize');
 
118
    dstSize = glGetUniformLocation(glsl, 'dstSize');
 
119
end
 
120
 
 
121
% Just to be extra safe:
 
122
glTexParameteri(gltextarget, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
 
123
glTexParameteri(gltextarget, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
 
124
 
 
125
if glsl ~= 0 && ~usebilinear
 
126
    % Use nearest neighbour sampling for real downsampling shaders, so they
 
127
    % have full control over actual sampling:
 
128
    glTexParameteri(gltextarget, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
 
129
    glTexParameteri(gltextarget, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
 
130
else
 
131
    % For fixed function, use bilinear filtering. This should pretty much
 
132
    % emulate a 2x2 texels box-filter:
 
133
    glTexParameteri(gltextarget, GL.TEXTURE_MIN_FILTER, GL.LINEAR);
 
134
    glTexParameteri(gltextarget, GL.TEXTURE_MAG_FILTER, GL.LINEAR);
 
135
end
 
136
 
 
137
% Base level 0 is always enabled, as we never write to it:
 
138
glTexParameteri(gltextarget, GL.TEXTURE_BASE_LEVEL, 0);
 
139
 
 
140
% Iterate over all mip-levels 'miplevel' from 1 to maxlod, and source each
 
141
% 'miplevel-1' to compute 'miplevel':
 
142
for miplevel = 1:maxlod
 
143
    % Query size of target mip level:
 
144
    tw = glGetTexLevelParameteriv(gltextarget, miplevel, GL.TEXTURE_WIDTH);
 
145
    th = glGetTexLevelParameteriv(gltextarget, miplevel, GL.TEXTURE_HEIGHT);
 
146
 
 
147
    % If size is smaller than 1 texel in any dimension, exit the downsampling loop:
 
148
    if tw < 1 || th < 1
 
149
        break;
 
150
    end
 
151
    
 
152
    % Set mip-level upper range limit for sampling from texture from to "miplevel-1":
 
153
    glTexParameteri(gltextarget, GL.TEXTURE_MAX_LEVEL, miplevel-1);
 
154
    
 
155
    % Select the target mip-level for drawing into (== render-to-texture
 
156
    % via FBO color attachment) to 'miplevel':
 
157
    glFramebufferTexture2DEXT(GL.FRAMEBUFFER_EXT, GL.COLOR_ATTACHMENT0_EXT, gltextarget, gltexid, miplevel);
 
158
 
 
159
    % Setup drawing transforms, projection and viewport:
 
160
    glMatrixMode(GL.PROJECTION);
 
161
    glLoadIdentity;
 
162
    gluOrtho2D(0, tw, 0 , th);
 
163
    glMatrixMode(GL.MODELVIEW);
 
164
    glLoadIdentity;
 
165
    glViewport(0, 0, tw, th);
 
166
    
 
167
    % Ok, we should be setup properly now. Any shader sampling from the
 
168
    % texture can only do nearest neighbour sampling from level
 
169
    % 'miplevel-1', and all writes go to 'miplevel'.
 
170
    
 
171
    % Communicate sizes of source and destination surface to possible
 
172
    % shaders:
 
173
    if glsl
 
174
        glUniform2f(srcSize, glGetTexLevelParameteriv(gltextarget, miplevel-1, GL.TEXTURE_WIDTH), glGetTexLevelParameteriv(gltextarget, miplevel-1, GL.TEXTURE_HEIGHT));
 
175
        glUniform2f(dstSize, tw, th);
 
176
    end
 
177
    
 
178
    % Blit a "fullscreen quad" - or more specifically a full-texture quad
 
179
    % to drive the downsampling shader:
 
180
    glColor4f(1,1,1,1);
 
181
    glBegin(GL.QUADS)
 
182
    glTexCoord2f(0,0);
 
183
    glVertex2f(0,0);
 
184
    glTexCoord2f(0,1);
 
185
    glVertex2f(0,th);
 
186
    glTexCoord2f(1,1);
 
187
    glVertex2f(tw,th);
 
188
    glTexCoord2f(1,0);
 
189
    glVertex2f(tw,0);
 
190
    glEnd;
 
191
    % Rinse, wash, repeat at next miplevel...
 
192
end
 
193
 
 
194
% Reset mip-level range to normal:
 
195
glTexParameteri(gltextarget, GL.TEXTURE_BASE_LEVEL, 0);
 
196
glTexParameteri(gltextarget, GL.TEXTURE_MAX_LEVEL, 1000);
 
197
    
 
198
% Disable and unbind texture:
 
199
glDisable(gltextarget);
 
200
glBindTexture(gltextarget, 0);
 
201
 
 
202
% Restore framebuffer attachment for texture to render into miplevel zero:
 
203
glFramebufferTexture2DEXT(GL.FRAMEBUFFER_EXT, GL.COLOR_ATTACHMENT0_EXT, gltextarget, gltexid, 0);
 
204
 
 
205
% Unbind shader:
 
206
glUseProgram(0);
 
207
 
 
208
% Final state: Texture unbound from sampler, but bound as current
 
209
% framebuffer/rendertarget. Regular PTB code will take care of this.
 
210
 
 
211
% Restore pre-op state:
 
212
glPopAttrib;
 
213
 
 
214
% Done.
 
215
return;