/usr/share/psychtoolbox-3/PsychGLImageProcessing/CreateResolutionPyramid.m is in psychtoolbox-3-common 3.0.11.20131230.dfsg1-1build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | function CreateResolutionPyramid(tex, glsl, usebilinear)
% Build a mip-map image resolution pyramid for given texture.
%
% CreateResolutionPyramid(tex [, glsl=0][, usebilinear=0])
%
% 'tex' must be the Screen() texture or offscreen window handle of a
% texture or offscreen window of type GL_TEXTURE_2D, ie., created with
% 'specialFlags' setting 1. The function will create a OpenGL mip-map
% resolution pyramid by successive downsampling of 'tex' image content down
% to a image of 1x1 pixels. If 'glsl' is provided as handle to a shader, it
% will use that shader to do the downsampling, otherwise it will use
% bilinear filtering for successive pyramid levels. If 'usebilinear' is set
% to 1 it will feed the shader with linear interpolated samples, otherwise
% with nearest neighbour samples.
%
% You will likely also want to set 'specialFlags' flag 8 or 16 to prevent
% Screen('DrawTexture') from overwriting the image pyramid with its own
% auto-generated one.
%
% This needs a modern GLSL shading capable graphics card to work.
%
% History:
% 27.08.2012 mk Written.
%
% Needed for GL constant definitions:
global GL;
% Make sure MOGL is initialized:
if isempty(GL)
InitializeMatlabOpenGL([],[],1);
end
if nargin < 1 || isempty(tex)
error('Must provide a valid Screen() texture or offscreen window handle for building of resolution pyramid!');
end
if nargin < 2 || isempty(glsl)
glsl = 0;
end
if nargin < 3 || isempty(usebilinear)
usebilinear = 0;
end
% Only supported for offscreen targets, not onscreen windows, proxies etc.:
if Screen('WindowKind', tex) ~= -1
error('Tried to built a resolution image pyramid for something else than a texture or offscreen window! Unsupported.');
end
% This, as a well defined side effect, will bind the tex'tures associated
% FBO, or create one if it doesn't have one yet, as well as normalize the
% textures format and orientation, just as we need it:
Screen('GetWindowInfo', tex);
% Get width and height:
[w, h] = Screen('WindowSize', tex);
% Get low-level texture handle and type:
[gltexid, gltextarget] = Screen('GetOpenGLTexture', tex, tex);
% Make sure it is a mip-map capable type:
if gltextarget ~= GL.TEXTURE_2D
error('Tried to built a resolution image pyramid for something else than a GL_TEXTURE_2D texture! Unsupported.');
end
% Time for a state backup:
glPushAttrib(GL.ALL_ATTRIB_BITS);
% Ok, the currently bound FBO is the FBO to use for drawing into this
% texture. And we got the gltexid of the corresponding texture.
% We also know that all texture parameters are set in a way to make it
% available as a rendertarget, e.g., the texture minification/magnification
% filters, wrap modes etc. are set to safe values, e.g., nearest neighbour
% sampling, clamp to edge etc.
% Bind the texture:
glBindTexture(gltextarget, gltexid);
% Enable sampling:
glDisable(GL.TEXTURE_RECTANGLE_EXT);
glEnable(gltextarget);
% Compute to what miplevel we need to downsample to cover all bases:
maxlod = floor(log2(max(w, h)));
% Query size of mip-level 'maxlod':
tw = glGetTexLevelParameteriv(gltextarget, maxlod, GL.TEXTURE_WIDTH);
th = glGetTexLevelParameteriv(gltextarget, maxlod, GL.TEXTURE_HEIGHT);
% If no such mip-level exists?
if tw == 0 || th == 0
% No such mip-level: Generate initial mip-chain for this texture. We
% allocate mip-levels of proper format and size, but empty, ie.,
% without actually computing any content here:
internalFormat = glGetTexLevelParameteriv(gltextarget, 0, GL.TEXTURE_INTERNAL_FORMAT);
for miplevel = 1:maxlod
% Need to case tw, th to double(), use max() operators and whatnot
% to get proper size specs -- Matlab arithmetic on the returned
% int32's from query behaves rather strange otherwise:
tw = double(glGetTexLevelParameteriv(gltextarget, miplevel - 1, GL.TEXTURE_WIDTH));
th = double(glGetTexLevelParameteriv(gltextarget, miplevel - 1, GL.TEXTURE_HEIGHT));
glTexImage2D(gltextarget, miplevel, internalFormat, max(floor(tw/2), 1), max(floor(th/2), 1), 0, GL.RGBA, GL.UNSIGNED_BYTE, 0);
end
end
% Bind downsampling shader and set it up to read from texture unit 0:
glUseProgram(glsl);
% If we've actually bound the fixed-function pipeline, we skip this step:
if glsl ~= 0
glUniform1i(glGetUniformLocation(glsl, 'Image'), 0);
% Query locations of uniforms for passing src / dst dimensions:
srcSize = glGetUniformLocation(glsl, 'srcSize');
dstSize = glGetUniformLocation(glsl, 'dstSize');
end
% Just to be extra safe:
glTexParameteri(gltextarget, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
glTexParameteri(gltextarget, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
if glsl ~= 0 && ~usebilinear
% Use nearest neighbour sampling for real downsampling shaders, so they
% have full control over actual sampling:
glTexParameteri(gltextarget, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
glTexParameteri(gltextarget, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
else
% For fixed function, use bilinear filtering. This should pretty much
% emulate a 2x2 texels box-filter:
glTexParameteri(gltextarget, GL.TEXTURE_MIN_FILTER, GL.LINEAR);
glTexParameteri(gltextarget, GL.TEXTURE_MAG_FILTER, GL.LINEAR);
end
% Base level 0 is always enabled, as we never write to it:
glTexParameteri(gltextarget, GL.TEXTURE_BASE_LEVEL, 0);
% Iterate over all mip-levels 'miplevel' from 1 to maxlod, and source each
% 'miplevel-1' to compute 'miplevel':
for miplevel = 1:maxlod
% Query size of target mip level:
tw = glGetTexLevelParameteriv(gltextarget, miplevel, GL.TEXTURE_WIDTH);
th = glGetTexLevelParameteriv(gltextarget, miplevel, GL.TEXTURE_HEIGHT);
% If size is smaller than 1 texel in any dimension, exit the downsampling loop:
if tw < 1 || th < 1
break;
end
% Set mip-level upper range limit for sampling from texture from to "miplevel-1":
glTexParameteri(gltextarget, GL.TEXTURE_MAX_LEVEL, miplevel-1);
% Select the target mip-level for drawing into (== render-to-texture
% via FBO color attachment) to 'miplevel':
glFramebufferTexture2DEXT(GL.FRAMEBUFFER_EXT, GL.COLOR_ATTACHMENT0_EXT, gltextarget, gltexid, miplevel);
% Setup drawing transforms, projection and viewport:
glMatrixMode(GL.PROJECTION);
glLoadIdentity;
gluOrtho2D(0, tw, 0 , th);
glMatrixMode(GL.MODELVIEW);
glLoadIdentity;
glViewport(0, 0, tw, th);
% Ok, we should be setup properly now. Any shader sampling from the
% texture can only do nearest neighbour sampling from level
% 'miplevel-1', and all writes go to 'miplevel'.
% Communicate sizes of source and destination surface to possible
% shaders:
if glsl
glUniform2f(srcSize, glGetTexLevelParameteriv(gltextarget, miplevel-1, GL.TEXTURE_WIDTH), glGetTexLevelParameteriv(gltextarget, miplevel-1, GL.TEXTURE_HEIGHT));
glUniform2f(dstSize, tw, th);
end
% Blit a "fullscreen quad" - or more specifically a full-texture quad
% to drive the downsampling shader:
glColor4f(1,1,1,1);
glBegin(GL.QUADS)
glTexCoord2f(0,0);
glVertex2f(0,0);
glTexCoord2f(0,1);
glVertex2f(0,th);
glTexCoord2f(1,1);
glVertex2f(tw,th);
glTexCoord2f(1,0);
glVertex2f(tw,0);
glEnd;
% Rinse, wash, repeat at next miplevel...
end
% Reset mip-level range to normal:
glTexParameteri(gltextarget, GL.TEXTURE_BASE_LEVEL, 0);
glTexParameteri(gltextarget, GL.TEXTURE_MAX_LEVEL, 1000);
% Disable and unbind texture:
glDisable(gltextarget);
glBindTexture(gltextarget, 0);
% Restore framebuffer attachment for texture to render into miplevel zero:
glFramebufferTexture2DEXT(GL.FRAMEBUFFER_EXT, GL.COLOR_ATTACHMENT0_EXT, gltextarget, gltexid, 0);
% Unbind shader:
glUseProgram(0);
% Final state: Texture unbound from sampler, but bound as current
% framebuffer/rendertarget. Regular PTB code will take care of this.
% Restore pre-op state:
glPopAttrib;
% Done.
return;
|