1 // This is core/vgui/vgui_utils.cxx
2 #include <cstdlib>
3 #include <iostream>
4 #include "vgui_utils.h"
5 //:
6 // \file
7 // \author fsm
8 // \date   Oct 99
9 // \brief  See vgui_utils.h for a description of this file.
10 
11 #ifdef _MSC_VER
12 #  include "vcl_msvc_warnings.h"
13 #endif
14 #include <cassert>
15 
16 #include "vil1/vil1_rgba.h"
17 #include "vil1/vil1_save.h"
18 
19 #include "vgui/vgui_gl.h"
20 #include "vgui/vgui_glu.h"
21 #include "vgui/vgui_utils.h"
22 
23 #include "vil/vil_rgba.h"
24 #include "vil/vil_resample_nearest.h"
25 #include "vil/vil_resample_bilin.h"
26 
27 //------------------------------------------------------------------------------
28 // copy the buffer into a memory image
29 vil1_memory_image_of<vil1_rgb<GLubyte>>
get_image(double scale)30 vgui_utils::get_image(double scale)
31 {
32   get_gl_scale_default(scale);
33 
34   // We should grab the pixels off the front buffer, since that is what is visible.
35   GLint cur_read_buffer;
36   glGetIntegerv(GL_READ_BUFFER, &cur_read_buffer);
37   glReadBuffer(GL_FRONT);
38 
39   // get viewport size
40   GLint vp[4]; // x,y,w,h
41   // Needs to be physical  for ReadPixels
42   glGetIntegerv(GL_VIEWPORT, vp);
43   unsigned x = vp[0];
44   unsigned y = vp[1];
45   unsigned w = vp[2];
46   unsigned h = vp[3];
47 
48   // It's easier to get the buffer in vil1_rgba format and then convert to
49   // RGB, because that avoids alignment problems with glReadPixels.
50   vil1_rgba<GLubyte> * pixels = new vil1_rgba<GLubyte>[w * h];
51 
52   // glReadPixels is not affected by Zoom
53   // glPixelZoom(1, 1);
54   glPixelTransferi(GL_MAP_COLOR, 0);
55   glPixelTransferi(GL_RED_SCALE, 1);
56   glPixelTransferi(GL_RED_BIAS, 0);
57   glPixelTransferi(GL_GREEN_SCALE, 1);
58   glPixelTransferi(GL_GREEN_BIAS, 0);
59   glPixelTransferi(GL_BLUE_SCALE, 1);
60   glPixelTransferi(GL_BLUE_BIAS, 0);
61 
62   //
63   glPixelStorei(GL_PACK_ALIGNMENT, 1);   // byte alignment.
64   glPixelStorei(GL_PACK_ROW_LENGTH, 0);  // use default value (the arg to pixel routine).
65   glPixelStorei(GL_PACK_SKIP_PIXELS, 0); //
66   glPixelStorei(GL_PACK_SKIP_ROWS, 0);   //
67 
68   //
69   glReadPixels(x,
70                y, //
71                w,
72                h,                //
73                GL_RGBA,          // format
74                GL_UNSIGNED_BYTE, // type
75                pixels);
76 
77   glReadBuffer(cur_read_buffer);
78 
79   // glReadPixels() reads the pixels from the bottom of the viewport up.
80   // Copy them into a vil1_memory_image_of in the other order :
81   vil1_memory_image_of<vil1_rgb<GLubyte>> colour_buffer(w, h);
82   for (unsigned yy = 0; yy < h; ++yy)
83     for (unsigned xx = 0; xx < w; ++xx)
84     {
85       colour_buffer(xx, h - 1 - yy).r = pixels[xx + w * yy].r;
86       colour_buffer(xx, h - 1 - yy).g = pixels[xx + w * yy].g;
87       colour_buffer(xx, h - 1 - yy).b = pixels[xx + w * yy].b;
88     }
89 
90   //
91   delete[] pixels;
92 
93   if (scale == 1)
94     return colour_buffer;
95 
96   int width_logical = w / scale;
97   int height_logical = h / scale;
98   vil1_memory_image_of<vil1_rgb<GLubyte>> logical_image(width_logical, height_logical);
99 
100   // Convert to views for vil_resample_bilin
101   vil_image_view<GLubyte> logical_view(
102     (GLubyte *)logical_image.begin(), width_logical, height_logical, 3, 3, 3 * width_logical, 1);
103   vil_image_view<GLubyte> physical_view((GLubyte *)colour_buffer.begin(), w, h, 3, 3, 3 * w, 1);
104 
105   // Resample
106   vil_resample_bilin<GLubyte, GLubyte>(physical_view, logical_view, width_logical, height_logical);
107 
108   // Return
109   return logical_image;
110 }
111 
112 // return a memory image corresponding to the GL buffer
113 vil1_memory_image_of<vil1_rgb<unsigned char>>
colour_buffer_to_image()114 vgui_utils::colour_buffer_to_image()
115 {
116   vil1_memory_image_of<vil1_rgb<GLubyte>> colour_buffer = vgui_utils::get_image();
117   vil1_memory_image_of<vil1_rgb<unsigned char>> temp(colour_buffer);
118   return temp;
119 }
120 
121 // write the GL buffer to a file
122 void
dump_colour_buffer(char const * file)123 vgui_utils::dump_colour_buffer(char const * file)
124 {
125   vil1_memory_image_of<vil1_rgb<GLubyte>> colour_buffer = vgui_utils::get_image();
126   vil1_save(colour_buffer, file);
127 }
128 
129 //------------------------------------------------------------------------------
130 // copy the buffer into a vil image view
131 vil_image_view<GLubyte>
get_view(double scale)132 vgui_utils::get_view(double scale)
133 {
134   get_gl_scale_default(scale);
135 
136   // get viewport size
137   GLint vp[4]; // x,y,w,h
138   // Needs to be physical  for ReadPixels
139   glGetIntegerv(GL_VIEWPORT, vp);
140   unsigned x = vp[0];
141   unsigned y = vp[1];
142   unsigned w = vp[2];
143   unsigned h = vp[3];
144 
145   // It's easier to get the buffer in vil_rgba format and then convert to
146   // RGB, because that avoids alignment problems with glReadPixels.
147   vil_rgba<GLubyte> * pixels = new vil_rgba<GLubyte>[w * h];
148 
149   // glReadPixels is not affected by Zoom
150   // glPixelZoom(1, 1);
151   glPixelTransferi(GL_MAP_COLOR, 0);
152   glPixelTransferi(GL_RED_SCALE, 1);
153   glPixelTransferi(GL_RED_BIAS, 0);
154   glPixelTransferi(GL_GREEN_SCALE, 1);
155   glPixelTransferi(GL_GREEN_BIAS, 0);
156   glPixelTransferi(GL_BLUE_SCALE, 1);
157   glPixelTransferi(GL_BLUE_BIAS, 0);
158 
159   //
160   glPixelStorei(GL_PACK_ALIGNMENT, 1);   // byte alignment.
161   glPixelStorei(GL_PACK_ROW_LENGTH, 0);  // use default value (the arg to pixel routine).
162   glPixelStorei(GL_PACK_SKIP_PIXELS, 0); //
163   glPixelStorei(GL_PACK_SKIP_ROWS, 0);   //
164 
165   //
166   glReadPixels(x,
167                y, //
168                w,
169                h,                //
170                GL_RGBA,          // format
171                GL_UNSIGNED_BYTE, // type
172                pixels);
173 
174   // glReadPixels() reads the pixels from the bottom of the viewport up.
175   // Copy them into a vil_image_view in the other order :
176   vil_image_view<GLubyte> view(w, h, 1, 3);
177   for (unsigned yy = 0; yy < h; ++yy)
178     for (unsigned xx = 0; xx < w; ++xx)
179     {
180       view(xx, h - 1 - yy, 0) = pixels[xx + w * yy].r;
181       view(xx, h - 1 - yy, 1) = pixels[xx + w * yy].g;
182       view(xx, h - 1 - yy, 2) = pixels[xx + w * yy].b;
183     }
184 
185   //
186   delete[] pixels;
187 
188   if (scale == 1)
189     return view;
190 
191   int width_logical = w / scale;
192   int height_logical = h / scale;
193   vil_image_view<GLubyte> logical_view(width_logical, height_logical, 1, 3);
194 
195   // Don't use bicub here, it's ugly and fully of tons of artifacts. Bilin is ok
196   vil_resample_bilin<GLubyte, GLubyte>(view, logical_view, width_logical, height_logical);
197   return logical_view;
198 }
199 
200 
201 //: Get an image view corresponding to the OpenGL area
202 vil_image_view<vxl_byte>
colour_buffer_to_view()203 vgui_utils::colour_buffer_to_view()
204 {
205   vil_image_view<GLubyte> buffer = vgui_utils::get_view();
206   vil_image_view<vxl_byte> temp(buffer);
207   return temp;
208 }
209 
210 
211 //------------------------------------------------------------------------------
212 
213 // Copies the contents of the current read colour buffer into the current draw
214 // colour buffer.
215 void
do_copy(double scale)216 vgui_utils::do_copy(double scale)
217 {
218   // void glCopyPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum type )
219 
220   GLint vp[4]; // x,y,w,h
221 
222   get_gl_scale_default(scale);
223 
224   // This has to be done in physical pixels
225   glGetIntegerv(GL_VIEWPORT, vp);
226 
227   // save matrices and set new :
228   glMatrixMode(GL_MODELVIEW);
229   glPushMatrix();
230   glLoadIdentity();
231 
232   glMatrixMode(GL_PROJECTION);
233   glPushMatrix();
234   glLoadIdentity();
235 
236   // Transformations are always in logical pixels...
237   // This doesn't matter, as long as the origin is 0,0? The only point of this
238   // is to get the RasterPos at 0,0, so the width and height don't actually
239   // matter.
240   glOrtho(0, vp[2] / scale, 0, vp[3] / scale, -1, +1); // near, far
241 
242   // set raster position to the bottom left-hand corner.
243   glRasterPos2i(0, 0);
244 
245   // restore old matrices.
246   glMatrixMode(GL_MODELVIEW);
247   glPopMatrix();
248 
249   glMatrixMode(GL_PROJECTION);
250   glPopMatrix();
251 
252   // copy pixels :
253   glPixelZoom(1, 1); // CopyPixels needs to be physical pixels
254   glPixelTransferi(GL_MAP_COLOR, 0);
255   glPixelTransferi(GL_RED_SCALE, 1);
256   glPixelTransferi(GL_RED_BIAS, 0);
257   glPixelTransferi(GL_GREEN_SCALE, 1);
258   glPixelTransferi(GL_GREEN_BIAS, 0);
259   glPixelTransferi(GL_BLUE_SCALE, 1);
260   glPixelTransferi(GL_BLUE_BIAS, 0);
261   glPixelTransferi(GL_ALPHA_SCALE, 1);
262   glPixelTransferi(GL_ALPHA_BIAS, 0);
263   glDisable(GL_DITHER);
264   glCopyPixels(0,
265                0, // window coordinates of lower left corner
266                vp[2],
267                vp[3],     // width and height of region to be copied in physical pixels
268                GL_COLOR); // copy colour values.
269 }
270 
271 void
copy_front_to_back()272 vgui_utils::copy_front_to_back()
273 {
274   GLint old_read, old_draw;
275   glGetIntegerv(GL_READ_BUFFER, &old_read);
276   glGetIntegerv(GL_DRAW_BUFFER, &old_draw);
277 
278   glReadBuffer(GL_FRONT);
279   glDrawBuffer(GL_BACK);
280   vgui_utils::do_copy();
281 
282   glReadBuffer(GLenum(old_read));
283   glDrawBuffer(GLenum(old_draw));
284 }
285 
286 void
copy_back_to_front()287 vgui_utils::copy_back_to_front()
288 {
289   GLint old_read, old_draw;
290   glGetIntegerv(GL_READ_BUFFER, &old_read);
291   glGetIntegerv(GL_DRAW_BUFFER, &old_draw);
292 
293   glReadBuffer(GL_BACK);
294   glDrawBuffer(GL_FRONT);
295   vgui_utils::do_copy();
296 
297   glReadBuffer(GLenum(old_read));
298   glDrawBuffer(GLenum(old_draw));
299 }
300 
301 //------------------------------------------------------------------------------
302 
303 static GLint gl_old_buffer = -1;
304 
305 void
begin_sw_overlay()306 vgui_utils::begin_sw_overlay()
307 {
308   glGetIntegerv(GL_DRAW_BUFFER, &gl_old_buffer);
309   if (gl_old_buffer != GL_NONE)
310     glDrawBuffer(GL_FRONT);
311 }
312 
313 void
end_sw_overlay()314 vgui_utils::end_sw_overlay()
315 {
316   if (gl_old_buffer == -1)
317   {
318     std::cerr << "WARNING :  end_sw_overlay called before begin_sw_overlay\n";
319     return;
320   }
321 
322   glFlush();
323   // revert to rendering into the back buffer :
324   glDrawBuffer((GLenum)gl_old_buffer);
325 
326   gl_old_buffer = -1;
327 }
328 
329 
330 static bool in_pick_mode = false;
331 
332 GLuint *
enter_pick_mode(float x,float y,float w,float h)333 vgui_utils::enter_pick_mode(float x, float y, float w, float h)
334 {
335   assert(!in_pick_mode);
336   in_pick_mode = true;
337 
338   if (h == 0)
339     h = w;
340 
341   static unsigned const HIT_BUFFER_SIZE = 4096;
342   static GLuint buffer[HIT_BUFFER_SIZE];
343 
344   // define hit buffer
345   glSelectBuffer(HIT_BUFFER_SIZE, buffer);
346 
347   // get viewport
348   GLint viewport[4];
349   vgui_utils::get_glViewport(viewport);
350 
351   // enter selection mode
352   glRenderMode(GL_SELECT);
353 
354   //
355   glInitNames();
356 
357   // save old projection matrix and define viewing volume for selection :
358   glMatrixMode(GL_PROJECTION);
359   glPushMatrix();
360 
361   float P[16]; // get current projection matrix
362   glGetFloatv(GL_PROJECTION_MATRIX, P);
363 
364   glLoadIdentity();                    // make a pick matrix
365   gluPickMatrix(x, y, w, h, viewport); // thank heavens for viewport coordinates.
366 
367   glMultMatrixf(P); // right multiply the old matrix onto it
368 
369   return buffer;
370 }
371 
372 // return number of hits.
373 unsigned
leave_pick_mode()374 vgui_utils::leave_pick_mode()
375 {
376   assert(in_pick_mode);
377   in_pick_mode = false;
378 
379   // restore viewing volume and render mode
380   glMatrixMode(GL_PROJECTION);
381   glPopMatrix();
382   return glRenderMode(GL_RENDER);
383 }
384 
385 void
process_hits(int num_hits,GLuint * ptr,std::vector<std::vector<unsigned>> & hits)386 vgui_utils::process_hits(int num_hits, GLuint * ptr, std::vector<std::vector<unsigned>> & hits)
387 {
388 #ifdef DEBUG
389   std::cerr << "hits = " << num_hits << std::endl;
390 #endif
391   // for each hit
392   for (int i = 0; i < num_hits; i++)
393   {
394     GLuint num_names = *ptr;
395 #ifdef DEBUG
396     std::cerr << "number of names for hit[" << i << "] = " << num_names << std::endl;
397 #endif
398     ptr++;
399 #ifdef DEBUG
400     std::cerr << " z1 is " << *ptr;
401 #endif
402     ptr++;
403 #ifdef DEBUG
404     std::cerr << "; z2 is " << *ptr << std::endl;
405 #endif
406     ptr++;
407 
408     std::vector<unsigned> names;
409 #ifdef DEBUG
410     std::cerr << " the name is ";
411 #endif
412     // for each name
413     for (unsigned int j = 0; j < num_names; j++)
414     {
415       names.push_back(*ptr);
416 #ifdef DEBUG
417       std::cerr << *ptr << ' ';
418 #endif
419       ptr++;
420     }
421 #ifdef DEBUG
422     std::cerr << std::endl << "names.size() " << names.size() << std::endl;
423 #endif
424     hits.push_back(names);
425 
426 #ifdef DEBUG
427     std::cerr << std::endl;
428 #endif
429   }
430 #ifdef DEBUG
431   std::cerr << "hits.size() " << hits.size() << std::endl;
432 #endif
433 }
434 
435 
436 int
bits_per_pixel(GLenum format,GLenum type)437 vgui_utils::bits_per_pixel(GLenum format, GLenum type)
438 {
439 #define M(f, t, size)                                                                                                  \
440   if (format == f && type == t)                                                                                        \
441     return size;
442   M(GL_RGB, GL_UNSIGNED_BYTE, 24);
443   M(GL_BGR, GL_UNSIGNED_BYTE, 24);
444   M(GL_RGBA, GL_UNSIGNED_BYTE, 32);
445 #if defined(GL_UNSIGNED_SHORT_5_6_5)
446   M(GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16);
447 #endif
448 #if defined(GL_UNSIGNED_SHORT_5_5_5_1)
449   M(GL_RGB, GL_UNSIGNED_SHORT_5_5_5_1, 16);
450 #endif
451 #if defined(GL_BGRA)
452   M(GL_BGRA, GL_UNSIGNED_BYTE, 32);
453 #endif
454 #if defined(GL_EXT_abgr) || defined(GL_ABGR_EXT)
455   M(GL_ABGR_EXT, GL_UNSIGNED_BYTE, 32);
456 #endif
457 #undef M
458 
459   std::cerr << "vgui_utils::bits_per_pixel: UNKNOWN COMBO, format = " << format << ", type = " << type << std::endl;
460   std::abort();
461   return 0;
462 }
463 
464 /**
465  * Sets the viewport and corrects for physical to logical scaling, so that the
466  * values are always in units of logical pixels, cross platform.
467  *
468  * @param x GLint left in logical pixels
469  * @param y GLint bottom in logical pixels
470  * @param width GLint width in logical pixels
471  * @param height GLint height in logical pixels
472  * @param scale The scale factor used. Defaults to 0, meaning auto determine
473  *              using vgui_adaptor::current. If this is not correct, the scale
474  *              factor can manually be set
475  */
476 void
set_glViewport(GLint x,GLint y,GLsizei width,GLsizei height,double scale)477 vgui_utils::set_glViewport(GLint x, GLint y, GLsizei width, GLsizei height, double scale)
478 {
479   get_gl_scale_default(scale);
480 
481   glViewport(x * scale, y * scale, width * scale, height * scale);
482 }
483 
484 /**
485  * Sets the scissors box and corrects for physical to logical scaling, so that
486  * the values are always in units of logical pixels, cross platform.
487  *
488  * @param x GLint left in logical pixels
489  * @param y GLint bottom in logical pixels
490  * @param width GLint width in logical pixels
491  * @param height GLint height in logical pixels
492  * @param scale The scale factor used. Defaults to 0, meaning auto determine
493  *              using vgui_adaptor::current. If this is not correct, the scale
494  *              factor can manually be set
495  */
496 void
set_glScissor(GLint x,GLint y,GLsizei width,GLsizei height,double scale)497 vgui_utils::set_glScissor(GLint x, GLint y, GLsizei width, GLsizei height, double scale)
498 {
499   get_gl_scale_default(scale);
500 
501   glScissor(x * scale, y * scale, width * scale, height * scale);
502 }
503 
504 /**
505  * Sets the line width and corrects for physical to logical scaling, so that the
506  * values are always in units of logical pixels, cross platform.
507  *
508  * @param width GLfloat width of lines drawn in logical pixels
509  * @param scale The scale factor used. Defaults to 0, meaning auto determine
510  *              using vgui_adaptor::current. If this is not correct, the scale
511  *              factor can manually be set
512  */
513 void
set_glLineWidth(GLfloat width,double scale)514 vgui_utils::set_glLineWidth(GLfloat width, double scale)
515 {
516   get_gl_scale_default(scale);
517   glLineWidth(scale * width);
518 }
519 
520 /**
521  * Sets the point size and corrects for physical to logical scaling, so that the
522  * values are always in units of logical pixels, cross platform.
523  *
524  * @param size GLfloat width of dots drawn in logical pixels
525  * @param scale The scale factor used. Defaults to 0, meaning auto determine
526  *              using vgui_adaptor::current. If this is not correct, the scale
527  *              factor can manually be set
528  */
529 void
set_glPointSize(GLfloat size,double scale)530 vgui_utils::set_glPointSize(GLfloat size, double scale)
531 {
532   get_gl_scale_default(scale);
533   glPointSize(scale * size);
534 }
535 
536 /**
537  * Sets the zoom factors and corrects for physical to logical scaling, so that
538  * the values are always in units of logical pixels, cross platform. It is less
539  * obvious why this needs a conversion. But adding the multiplier here, as long
540  * as set_glPixelZoom is called, the amount of zoom that is needed to fill the
541  * screen is added to the intended zoom, corrects the projection matrix, which
542  * compensated for the larger viewport used, and functions like glDrawPixels,
543  * glCopyPixels, etc... act in a DPI independent manner now. This means if you
544  * want the zoom to be "1" logically, you still have to call set_glPixelZoom(1),
545  * or else this correction will not be added.
546  *
547  * @param xfactor GLfloat horizontal zoom in logical pixels
548  * @param yfactor GLfloat vertical zoom in logical pixels
549  * @param scale The scale factor used. Defaults to 0, meaning auto determine
550  *              using vgui_adaptor::current. If this is not correct, the scale
551  *              factor can manually be set
552  */
553 void
set_glPixelZoom(GLfloat xfactor,GLfloat yfactor,double scale)554 vgui_utils::set_glPixelZoom(GLfloat xfactor, GLfloat yfactor, double scale)
555 {
556   get_gl_scale_default(scale);
557   glPixelZoom(scale * xfactor, scale * yfactor);
558 }
559 
560 /**
561  * Draws a bitmaps corrects for physical to logical scaling, so that the values
562  * are always in units of logical pixels, cross platform. This is more complex
563  * than the other gl functions, as the bitmap is a raster in physical pixels,
564  * and actually needs to be resampled when scaled. The glRasterPos is in local
565  * (object) 3D coordinates, so it doesn't need to be scaled.
566  *
567  * @param width GLsizei width displayed of the bitmap (may be less than actual
568  *              bitmap width which must me divisible by 8).
569  * @param height GLsizei height of the bitmap.
570  * @param xorig GLfloat offset of bitmap in window coordinates (logical)
571  * @param yorig GLfloat offset of bitmap in window coordinates (logical)
572  * @param xmove GLfloat offset of raster position after drawing in window
573  *              coordinates (logical)
574  * @param ymove GLfloat offset of raster position after drawing in window
575  *              coordinates (logical)
576  * @param bitmap GLubytes* the bitmap. This function supports a binary image
577  *               bitmap using 1/2/4/8 packed bits.
578  * @param scale The scale factor used. Defaults to 0, meaning auto determine
579  *              using vgui_adaptor::current. If this is not correct, the scale
580  *              factor can manually be set
581  */
582 void
draw_glBitmap(GLsizei width,GLsizei height,GLfloat xorig,GLfloat yorig,GLfloat xmove,GLfloat ymove,const GLubyte * bitmap,double scale)583 vgui_utils::draw_glBitmap(GLsizei width,
584                           GLsizei height,
585                           GLfloat xorig,
586                           GLfloat yorig,
587                           GLfloat xmove,
588                           GLfloat ymove,
589                           const GLubyte * bitmap,
590                           double scale)
591 {
592   GLint unpack_size;
593   glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_size);
594 
595   // The real with is always divisible by 8, this is just how glBitmap works.
596   int remainder = width % 8;
597   if (remainder)
598   {
599     width += 8 - remainder;
600   }
601 
602   get_gl_scale_default(scale);
603 
604   if (scale == 1)
605   {
606     glBitmap(width, height, xorig, yorig, xmove, ymove, bitmap);
607   }
608   else
609   {
610     vil_image_view<bool> original_bitmap(width, height, 1);
611 
612     int index = 0;
613     unsigned char mask0 = (0xff << (8 - unpack_size)) & 0xff;
614     unsigned char mask = mask0;
615 
616     // Unpack into vil_image_view
617     for (int r = 0; r < height; r++)
618     {
619       for (int c = 0; c < width; c++)
620       {
621         original_bitmap(c, r, 0) = bitmap[index] & mask;
622         // Increment the mask
623         mask >>= unpack_size;
624         // If the mask is blank
625         if (!mask)
626         {
627           // reset the bitmask
628           mask = mask0;
629           // Go onto the next byte
630           ++index;
631         }
632       }
633     }
634 
635     // Rescale mask
636     int width2 = width * scale;
637     int height2 = height * scale;
638     vil_image_view<bool> scaled_bitmap(width2, height2, 1);
639     vil_resample_nearest<bool, bool>(original_bitmap, scaled_bitmap, width2, height2);
640 
641     // Make sure new width is divisible by 8 (support for fractional scales)
642     remainder = width2 % 8;
643     if (remainder)
644     {
645       width2 += 8 - remainder;
646     }
647 
648     // Make new GLubyte array
649     std::vector<GLubyte> raster_scaled(width2 * height2, 0);
650 
651     // repack into gl array.
652     int stride = width2 * unpack_size / 8;
653     for (int r = 0, index = 0; r < height2; r++, index = stride * r)
654     {
655       for (int c = 0; c < scaled_bitmap.ni(); c++)
656       {
657         if (scaled_bitmap(c, r, 0))
658         {
659           // sets all the bits (0xff) possible given the unpack size
660           raster_scaled[index] = raster_scaled[index] | (0xff & mask);
661         }
662 
663         // Increment the mask
664         mask >>= unpack_size;
665         // If the mask is blank
666         if (!mask)
667         {
668           // reset the bitmask
669           mask = mask0;
670           // Go onto the next byte
671           ++index;
672         }
673       }
674     }
675     glBitmap(width2, height2, xorig * scale, yorig * scale, xmove * scale, ymove * scale, raster_scaled.data());
676   }
677 }
678