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