1 // This is core/vgui/vgui_section_render.cxx
2 //:
3 // \file
4 // \author fsm
5
6 #include <cmath>
7 #include "vgui_section_render.h"
8
9 #ifdef _MSC_VER
10 # include "vcl_msvc_warnings.h"
11 #endif
12 #include <cassert>
13 #include <climits> // for UCHAR_MAX
14 #include <vgui/internals/vgui_rasterpos.h>
15 #include <vgui/internals/vgui_accelerate.h>
16 #include "vgui/vgui_utils.h"
17
18 static inline float
fsm_max(float x,float y)19 fsm_max(float x, float y)
20 {
21 return x > y ? x : y;
22 }
23 static inline float
fsm_min(float x,float y)24 fsm_min(float x, float y)
25 {
26 return x < y ? x : y;
27 }
28
29 // Set to 1 for verbose debugging.
30 #if 0
31 # include <cstdio>
32 # define fsm_debug std::printf
33 #else
34 static inline void
fsm_debug(char const *,...)35 fsm_debug(char const *, ...)
36 {}
37 #endif
38 static bool
clamped_viewport(float x0,float y0,float x1,float y1,unsigned & i0,unsigned & j0,unsigned & ni,unsigned & nj,float & zoomx,float & zoomy)39 clamped_viewport(float x0,
40 float y0,
41 float x1,
42 float y1,
43 unsigned & i0,
44 unsigned & j0,
45 unsigned & ni,
46 unsigned & nj,
47 float & zoomx,
48 float & zoomy)
49 {
50 // Get current matrix state, in fortran order.
51 double Pt[4][4], Mt[4][4];
52 glGetDoublev(GL_PROJECTION_MATRIX, &Pt[0][0]);
53 glGetDoublev(GL_MODELVIEW_MATRIX, &Mt[0][0]);
54
55 // Get total world-to-device transformation. It should be of the form :
56 // * 0 0 *
57 // 0 * 0 *
58 // 0 0 * *
59 // 0 0 0 *
60 // with the diagonal entries non-zero. If not of this form, return false.
61 //
62 double T[4][4];
63 for (unsigned i = 0; i < 4; ++i)
64 {
65 for (unsigned j = 0; j < 4; ++j)
66 {
67 T[i][j] = 0;
68 for (unsigned k = 0; k < 4; ++k)
69 T[i][j] += Pt[k][i] * Mt[j][k]; // Pt[k][i] = P[i][k] etc
70 }
71 }
72 if (!T[0][0] || T[0][1] || T[0][2] || T[1][0] || !T[1][1] || T[1][2] || T[2][0] || T[2][1] || !T[2][2] || T[3][0] ||
73 T[3][1] || T[3][2] || !T[3][3])
74 return false; // cannot do
75 // From image to device coordinates, the projection is :
76 // [ T00 0 T03 ] [ a u ]
77 // [ 0 T11 T13 ] ~ [ b v ]
78 // [ 0 0 T33 ] [ 1 ]
79 float a = float(T[0][0] / T[3][3]), b = float(T[1][1] / T[3][3]);
80 float u = float(T[0][3] / T[3][3]), v = float(T[1][3] / T[3][3]);
81
82 // Get size of viewport. We need this to determine how much to scale pixels by.
83 GLint vp[4]; // x,y, w,h
84 vgui_utils::get_glViewport(vp);
85 // int vp_x = vp[0];
86 // int vp_y = vp[1];
87 int vp_w = vp[2];
88 int vp_h = vp[3];
89 if (vp_w <= 0 || vp_h <= 0)
90 return true;
91
92
93 // From device to viewport coordinates, the transformation is :
94 // [ vp_w 0 vp_x ] [ 1/2 0 1/2 ]
95 // [ 0 vp_h vp_y ] [ 0 1/2 1/2 ]
96 // [ 0 0 1 ] [ 0 0 1 ]
97 // where vp_x, vp_y, vp_w, vp_h are the start, width and height of the viewport.
98
99 // Compute pixel zoom, as passed to vgui_utils::set_glPixelZoom().
100 zoomx = a * vp_w / 2;
101 zoomy = b * vp_h / 2;
102
103 // Clip the given region [x0, x1] x [y0, y1] to the viewport. In
104 // device coordinates, the viewport is [-1, +1] x [-1, +1] so it's
105 // easiest to start from that. This clipping is especially important
106 // for non-local displays, where the display clipping happens on the
107 // display server.
108 //
109 if (a > 0)
110 {
111 // [ (-1-u)/a, (+1-u)/a ]
112 x0 = fsm_max(x0, (-1 - u) / a);
113 x1 = fsm_min(x1, (+1 - u) / a);
114 }
115 else
116 {
117 // [ (+1-u)/a, (-1-u)/a ]
118 x0 = fsm_max(x0, (+1 - u) / a);
119 x1 = fsm_min(x1, (-1 - u) / a);
120 }
121 if (b > 0)
122 {
123 // [ (-1-v)/b, (+1-v)/b ]
124 y0 = fsm_max(y0, (-1 - v) / b);
125 y1 = fsm_min(y1, (+1 - v) / b);
126 }
127 else
128 {
129 // [ (+1-v)/b, (-1-v)/b ]
130 y0 = fsm_max(y0, (+1 - v) / b);
131 y1 = fsm_min(y1, (-1 - v) / b);
132 }
133 if (x0 > x1 || y0 > y1)
134 {
135 fsm_debug("nothing to render\n");
136 return true; // that's easy.
137 }
138
139 // Before dumping the image, we have to set a valid raster
140 // position. However, to get a smooth panning effect, we want to
141 // render the (potentially) partial pixels at the borders, which
142 // means we must get the raster position to an "invalid" position
143 // outside the viewport. vgui_rasterpos wraps the appropriate
144 // trickery.
145
146 int i_x0 = int(std::floor(x0)), i_y0 = int(std::floor(y0));
147 int i_x1 = int(std::ceil(x1)), i_y1 = int(std::ceil(y1));
148 // Set the raster position
149 vgui_rasterpos2i(i_x0, i_y0);
150 // return the view parameters
151 i0 = static_cast<unsigned>(i_x0);
152 ni = static_cast<unsigned>(i_x1 - i_x0);
153 j0 = static_cast<unsigned>(i_y0);
154 nj = static_cast<unsigned>(i_y1 - i_y0);
155 return true;
156 }
157
158 bool
pixel_view(unsigned & i0,unsigned & ni,unsigned & j0,unsigned & nj,float & zoomx,float & zoomy)159 pixel_view(unsigned & i0, unsigned & ni, unsigned & j0, unsigned & nj, float & zoomx, float & zoomy)
160 {
161 float x0 = i0, x1 = ni, y0 = j0, y1 = nj;
162 return clamped_viewport(x0, y0, x1, y1, i0, j0, ni, nj, zoomx, zoomy);
163 }
164
165 static void
GL_setup(GLenum format,GLenum type,bool hardware_map,GLint & alignment,GLint & row_length,GLint & skip_pixels,GLint & skip_rows,GLint & table_size,GLboolean & map_color,vbl_array_1d<float> * fLmap,vbl_array_1d<float> * fRmap,vbl_array_1d<float> * fGmap,vbl_array_1d<float> * fBmap,vbl_array_1d<float> * fAmap)166 GL_setup(GLenum format,
167 GLenum type,
168 bool hardware_map,
169 GLint & alignment,
170 GLint & row_length,
171 GLint & skip_pixels,
172 GLint & skip_rows,
173 GLint & table_size,
174 GLboolean & map_color,
175 vbl_array_1d<float> * fLmap,
176 vbl_array_1d<float> * fRmap,
177 vbl_array_1d<float> * fGmap,
178 vbl_array_1d<float> * fBmap,
179 vbl_array_1d<float> * fAmap)
180 {
181 glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
182 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length);
183 glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skip_pixels);
184 glGetIntegerv(GL_UNPACK_SKIP_ROWS, &skip_rows);
185 glGetIntegerv(GL_MAX_PIXEL_MAP_TABLE, &table_size);
186 glGetBooleanv(GL_MAP_COLOR, &map_color);
187 // If hardware mapping is requested, set up the maps
188 // only support color mapping for byte component type
189 if (hardware_map && type == GL_UNSIGNED_BYTE && table_size >= (UCHAR_MAX + 1))
190 {
191 if (format == GL_LUMINANCE)
192 {
193 glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
194 float * tfLmap = fLmap->begin();
195 glPixelMapfv(GL_PIXEL_MAP_R_TO_R, UCHAR_MAX, tfLmap);
196 glPixelMapfv(GL_PIXEL_MAP_G_TO_G, UCHAR_MAX, tfLmap);
197 glPixelMapfv(GL_PIXEL_MAP_B_TO_B, UCHAR_MAX, tfLmap);
198 }
199 else if (format == GL_RGB)
200 {
201 glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
202 float * tfRmap = fRmap->begin();
203 float * tfGmap = fGmap->begin();
204 float * tfBmap = fBmap->begin();
205 glPixelMapfv(GL_PIXEL_MAP_R_TO_R, UCHAR_MAX, tfRmap);
206 glPixelMapfv(GL_PIXEL_MAP_G_TO_G, UCHAR_MAX, tfGmap);
207 glPixelMapfv(GL_PIXEL_MAP_B_TO_B, UCHAR_MAX, tfBmap);
208 }
209 else if (format == GL_RGBA)
210 {
211 glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
212 float * tfRmap = fRmap->begin();
213 float * tfGmap = fGmap->begin();
214 float * tfBmap = fBmap->begin();
215 float * tfAmap = fAmap->begin();
216 glPixelMapfv(GL_PIXEL_MAP_R_TO_R, UCHAR_MAX, tfRmap);
217 glPixelMapfv(GL_PIXEL_MAP_G_TO_G, UCHAR_MAX, tfGmap);
218 glPixelMapfv(GL_PIXEL_MAP_B_TO_B, UCHAR_MAX, tfBmap);
219 glPixelMapfv(GL_PIXEL_MAP_A_TO_A, UCHAR_MAX, tfAmap);
220 }
221 }
222 if (hardware_map && format == GL_LUMINANCE && type == GL_UNSIGNED_SHORT && table_size >= USHRT_MAX)
223 {
224 glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
225 float * tfLmap = fLmap->begin();
226 glPixelMapfv(GL_PIXEL_MAP_R_TO_R, USHRT_MAX, tfLmap);
227 glPixelMapfv(GL_PIXEL_MAP_G_TO_G, USHRT_MAX, tfLmap);
228 glPixelMapfv(GL_PIXEL_MAP_B_TO_B, USHRT_MAX, tfLmap);
229 }
230 }
231
232 static void
GL_restore(GLboolean & map_color,GLint alignment,GLint row_length,GLint skip_pixels,GLint skip_rows)233 GL_restore(GLboolean & map_color, GLint alignment, GLint row_length, GLint skip_pixels, GLint skip_rows)
234 {
235 // Restore previous values.
236 glPixelTransferi(GL_MAP_COLOR, map_color);
237 glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
238 glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
239 glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_pixels);
240 glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_rows);
241 }
242
243 bool
vgui_section_render(void const * pixels,unsigned w,unsigned h,float x0,float y0,float x1,float y1,GLenum format,GLenum type,bool hardware_map,vbl_array_1d<float> * fLmap,vbl_array_1d<float> * fRmap,vbl_array_1d<float> * fGmap,vbl_array_1d<float> * fBmap,vbl_array_1d<float> * fAmap)244 vgui_section_render(void const * pixels,
245 unsigned w,
246 unsigned h, // Size of image.
247 float x0,
248 float y0, // Region of image
249 float x1,
250 float y1, // to render.
251 GLenum format,
252 GLenum type,
253 bool hardware_map,
254 vbl_array_1d<float> * fLmap,
255 vbl_array_1d<float> * fRmap,
256 vbl_array_1d<float> * fGmap,
257 vbl_array_1d<float> * fBmap,
258 vbl_array_1d<float> * fAmap)
259 {
260 assert(h > 0); // eliminates warning of unused h
261 assert(pixels);
262 assert(x0 <= x1);
263 assert(y0 <= y1);
264
265 assert(!hardware_map || format != GL_LUMINANCE || fLmap);
266 assert(!hardware_map || format != GL_RGB || (fRmap && fGmap && fBmap));
267 assert(!hardware_map || format != GL_RGBA || (fRmap && fGmap && fBmap && fAmap));
268 float zoomx = 1.0f, zoomy = 1.0f;
269 unsigned i0 = 0, j0 = 0, ni = 0, nj = 0;
270 if (!clamped_viewport(x0, y0, x1, y1, i0, j0, ni, nj, zoomx, zoomy))
271 return false;
272 int i_x0 = i0, i_y0 = j0, i_x1 = i0 + ni, i_y1 = j0 + nj;
273 // Store old transfer characteristics for restoring it in a bit.
274 GLint alignment, row_length, skip_pixels, skip_rows, table_size;
275 GLboolean map_color;
276 GL_setup(format,
277 type,
278 hardware_map,
279 alignment,
280 row_length,
281 skip_pixels,
282 skip_rows,
283 table_size,
284 map_color,
285 fLmap,
286 fRmap,
287 fGmap,
288 fBmap,
289 fAmap);
290 // Set pixel transfer characteristics.
291 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // use byte alignment for now.
292 glPixelStorei(GL_UNPACK_ROW_LENGTH, w); // size of image rows.
293 glPixelStorei(GL_UNPACK_SKIP_PIXELS, i_x0); // number of pixels to skip on the left.
294 glPixelStorei(GL_UNPACK_SKIP_ROWS, i_y0); // number of pixels to skip at the bottom.
295
296 vgui_utils::set_glPixelZoom(zoomx, zoomy);
297 vgui_accelerate::instance()->vgui_glDrawPixels(i_x1 - i_x0, // Size of pixel rectangle
298 i_y1 - i_y0, // to be written to frame buffer.
299 format,
300 type,
301 pixels);
302 GL_restore(map_color, alignment, row_length, skip_pixels, skip_rows);
303 return true; // could do
304 }
305
306 bool
vgui_view_render(void const * pixels,unsigned w,unsigned h,float zoomx,float zoomy,GLenum format,GLenum type,bool hardware_map,vbl_array_1d<float> * fLmap,vbl_array_1d<float> * fRmap,vbl_array_1d<float> * fGmap,vbl_array_1d<float> * fBmap,vbl_array_1d<float> * fAmap)307 vgui_view_render(void const * pixels,
308 unsigned w,
309 unsigned h, // Size of view
310 float zoomx,
311 float zoomy,
312 GLenum format,
313 GLenum type,
314 bool hardware_map,
315 vbl_array_1d<float> * fLmap,
316 vbl_array_1d<float> * fRmap,
317 vbl_array_1d<float> * fGmap,
318 vbl_array_1d<float> * fBmap,
319 vbl_array_1d<float> * fAmap)
320 {
321 assert(pixels);
322 assert(!hardware_map || format != GL_LUMINANCE || fLmap);
323 assert(!hardware_map || format != GL_RGB || (fRmap && fGmap && fBmap));
324 assert(!hardware_map || format != GL_RGBA || (fRmap && fGmap && fBmap && fAmap));
325
326 // Store old transfer characteristics for restoring it in a bit.
327 GLint alignment, row_length, table_size, skip_pixels, skip_rows;
328 GLboolean map_color;
329 GL_setup(format,
330 type,
331 hardware_map,
332 alignment,
333 row_length,
334 skip_pixels,
335 skip_rows,
336 table_size,
337 map_color,
338 fLmap,
339 fRmap,
340 fGmap,
341 fBmap,
342 fAmap);
343 // Set pixel transfer characteristics.
344 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // use byte alignment for now.
345 glPixelStorei(GL_UNPACK_ROW_LENGTH, w); // size of image rows.
346 vgui_utils::set_glPixelZoom(zoomx + 0.001f, zoomy + 0.001f); // something weird happens
347 // for identity zoom
348 vgui_accelerate::instance()->vgui_glDrawPixels(w, // Size of pixel rectangle
349 h, // to be written to frame buffer.
350 format,
351 type,
352 pixels);
353
354 GL_restore(map_color, alignment, row_length, skip_pixels, skip_rows);
355 return true; // could do
356 }
357
358 //--------------------------------------------------------------------------------
359