doxygen: prevent GPL license block from being parsed as doxygen comment.
[blender.git] / source / blender / editors / screen / glutil.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * Contributor(s): Blender Foundation
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #include <stdio.h>
29 #include <string.h>
30
31 #include "MEM_guardedalloc.h"
32
33 #include "DNA_vec_types.h"
34
35 #include "BLI_utildefines.h"
36
37 #include "BKE_colortools.h"
38
39 #include "BLI_math.h"
40 #include "BLI_threads.h"
41
42 #include "BIF_gl.h"
43 #include "BIF_glutil.h"
44
45 #ifndef GL_CLAMP_TO_EDGE
46 #define GL_CLAMP_TO_EDGE                        0x812F
47 #endif
48
49
50 /* ******************************************** */
51
52 /* defined in BIF_gl.h */
53 GLubyte stipple_halftone[128] = {
54         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
55         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
56         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
57         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
58         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
59         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
60         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
61         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
62         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
63         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
64         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
65         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
66         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
67         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 
68         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
69         0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55};
70
71
72 /*  repeat this pattern
73         X000X000
74         00000000
75         00X000X0
76         00000000 */
77
78
79 GLubyte stipple_quarttone[128] = { 
80         136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0,
81         136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0,
82         136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0,
83         136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0,
84         136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0,
85         136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0,
86         136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0,
87         136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0};
88
89
90 void fdrawbezier(float vec[4][3])
91 {
92         float dist;
93         float curve_res = 24, spline_step = 0.0f;
94         
95         dist= 0.5f*ABS(vec[0][0] - vec[3][0]);
96         
97         /* check direction later, for top sockets */
98         vec[1][0]= vec[0][0]+dist;
99         vec[1][1]= vec[0][1];
100         
101         vec[2][0]= vec[3][0]-dist;
102         vec[2][1]= vec[3][1];
103         /* we can reuse the dist variable here to increment the GL curve eval amount*/
104         dist = 1.0f/curve_res;
105         
106         cpack(0x0);
107         glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, vec[0]);
108         glBegin(GL_LINE_STRIP);
109         while (spline_step < 1.000001f) {
110                 /*if(do_shaded)
111                         UI_ThemeColorBlend(th_col1, th_col2, spline_step);*/
112                 glEvalCoord1f(spline_step);
113                 spline_step += dist;
114         }
115         glEnd();
116 }
117
118 void fdrawline(float x1, float y1, float x2, float y2)
119 {
120         float v[2];
121         
122         glBegin(GL_LINE_STRIP);
123         v[0] = x1; v[1] = y1;
124         glVertex2fv(v);
125         v[0] = x2; v[1] = y2;
126         glVertex2fv(v);
127         glEnd();
128 }
129
130 void fdrawbox(float x1, float y1, float x2, float y2)
131 {
132         float v[2];
133         
134         glBegin(GL_LINE_STRIP);
135         
136         v[0] = x1; v[1] = y1;
137         glVertex2fv(v);
138         v[0] = x1; v[1] = y2;
139         glVertex2fv(v);
140         v[0] = x2; v[1] = y2;
141         glVertex2fv(v);
142         v[0] = x2; v[1] = y1;
143         glVertex2fv(v);
144         v[0] = x1; v[1] = y1;
145         glVertex2fv(v);
146         
147         glEnd();
148 }
149
150 void sdrawline(short x1, short y1, short x2, short y2)
151 {
152         short v[2];
153         
154         glBegin(GL_LINE_STRIP);
155         v[0] = x1; v[1] = y1;
156         glVertex2sv(v);
157         v[0] = x2; v[1] = y2;
158         glVertex2sv(v);
159         glEnd();
160 }
161
162 /*
163
164         x1,y2
165         |  \
166         |   \
167         |    \
168         x1,y1-- x2,y1
169
170 */
171
172 static void sdrawtripoints(short x1, short y1, short x2, short y2)
173 {
174         short v[2];
175         v[0]= x1; v[1]= y1;
176         glVertex2sv(v);
177         v[0]= x1; v[1]= y2;
178         glVertex2sv(v);
179         v[0]= x2; v[1]= y1;
180         glVertex2sv(v);
181 }
182
183 void sdrawtri(short x1, short y1, short x2, short y2)
184 {
185         glBegin(GL_LINE_STRIP);
186         sdrawtripoints(x1, y1, x2, y2);
187         glEnd();
188 }
189
190 void sdrawtrifill(short x1, short y1, short x2, short y2)
191 {
192         glBegin(GL_TRIANGLES);
193         sdrawtripoints(x1, y1, x2, y2);
194         glEnd();
195 }
196
197 void sdrawbox(short x1, short y1, short x2, short y2)
198 {
199         short v[2];
200         
201         glBegin(GL_LINE_STRIP);
202         
203         v[0] = x1; v[1] = y1;
204         glVertex2sv(v);
205         v[0] = x1; v[1] = y2;
206         glVertex2sv(v);
207         v[0] = x2; v[1] = y2;
208         glVertex2sv(v);
209         v[0] = x2; v[1] = y1;
210         glVertex2sv(v);
211         v[0] = x1; v[1] = y1;
212         glVertex2sv(v);
213         
214         glEnd();
215 }
216
217
218 /* ******************************************** */
219
220 void setlinestyle(int nr)
221 {
222         if(nr==0) {
223                 glDisable(GL_LINE_STIPPLE);
224         }
225         else {
226                 
227                 glEnable(GL_LINE_STIPPLE);
228                 glLineStipple(nr, 0xAAAA);
229         }
230 }
231
232         /* Invert line handling */
233         
234 #define glToggle(mode, onoff)   (((onoff)?glEnable:glDisable)(mode))
235
236 void set_inverted_drawing(int enable) 
237 {
238         glLogicOp(enable?GL_INVERT:GL_COPY);
239
240         /* Use GL_BLEND_EQUATION_EXT on sgi (if we have it),
241          * apparently GL_COLOR_LOGIC_OP doesn't work on O2?
242          * Is this an sgi bug or our bug?
243          */
244 #if defined(__sgi) && defined(GL_BLEND_EQUATION_EXT)
245         glBlendEquationEXT(enable?GL_LOGIC_OP:GL_FUNC_ADD_EXT);
246         glToggle(GL_BLEND, enable);
247 #else
248         glToggle(GL_COLOR_LOGIC_OP, enable);
249 #endif
250
251         glToggle(GL_DITHER, !enable);
252 }
253
254 void sdrawXORline(int x0, int y0, int x1, int y1)
255 {
256         if(x0==x1 && y0==y1) return;
257
258         set_inverted_drawing(1);
259         
260         glBegin(GL_LINES);
261         glVertex2i(x0, y0);
262         glVertex2i(x1, y1);
263         glEnd();
264         
265         set_inverted_drawing(0);
266 }
267
268 void sdrawXORline4(int nr, int x0, int y0, int x1, int y1)
269 {
270         static short old[4][2][2];
271         static char flags[4]= {0, 0, 0, 0};
272         
273                 /* with builtin memory, max 4 lines */
274
275         set_inverted_drawing(1);
276                 
277         glBegin(GL_LINES);
278         if(nr== -1) { /* flush */
279                 for (nr=0; nr<4; nr++) {
280                         if (flags[nr]) {
281                                 glVertex2sv(old[nr][0]);
282                                 glVertex2sv(old[nr][1]);
283                                 flags[nr]= 0;
284                         }
285                 }
286         } else {
287                 if(nr>=0 && nr<4) {
288                         if(flags[nr]) {
289                                 glVertex2sv(old[nr][0]);
290                                 glVertex2sv(old[nr][1]);
291                         }
292
293                         old[nr][0][0]= x0;
294                         old[nr][0][1]= y0;
295                         old[nr][1][0]= x1;
296                         old[nr][1][1]= y1;
297                         
298                         flags[nr]= 1;
299                 }
300                 
301                 glVertex2i(x0, y0);
302                 glVertex2i(x1, y1);
303         }
304         glEnd();
305         
306         set_inverted_drawing(0);
307 }
308
309 void fdrawXORellipse(float xofs, float yofs, float hw, float hh)
310 {
311         if(hw==0) return;
312
313         set_inverted_drawing(1);
314
315         glPushMatrix();
316         glTranslatef(xofs, yofs, 0.0);
317         glScalef(1,hh/hw,1);
318         glutil_draw_lined_arc(0.0, M_PI*2.0, hw, 20);
319         glPopMatrix();
320
321         set_inverted_drawing(0);
322 }
323 void fdrawXORcirc(float xofs, float yofs, float rad)
324 {
325         set_inverted_drawing(1);
326
327         glPushMatrix();
328         glTranslatef(xofs, yofs, 0.0);
329         glutil_draw_lined_arc(0.0, M_PI*2.0, rad, 20);
330         glPopMatrix();
331
332         set_inverted_drawing(0);
333 }
334
335 void glutil_draw_filled_arc(float start, float angle, float radius, int nsegments) {
336         int i;
337         
338         glBegin(GL_TRIANGLE_FAN);
339         glVertex2f(0.0, 0.0);
340         for (i=0; i<nsegments; i++) {
341                 float t= (float) i/(nsegments-1);
342                 float cur= start + t*angle;
343                 
344                 glVertex2f(cos(cur)*radius, sin(cur)*radius);
345         }
346         glEnd();
347 }
348
349 void glutil_draw_lined_arc(float start, float angle, float radius, int nsegments) {
350         int i;
351         
352         glBegin(GL_LINE_STRIP);
353         for (i=0; i<nsegments; i++) {
354                 float t= (float) i/(nsegments-1);
355                 float cur= start + t*angle;
356                 
357                 glVertex2f(cos(cur)*radius, sin(cur)*radius);
358         }
359         glEnd();
360 }
361
362 int glaGetOneInteger(int param)
363 {
364         GLint i;
365         glGetIntegerv(param, &i);
366         return i;
367 }
368
369 float glaGetOneFloat(int param)
370 {
371         GLfloat v;
372         glGetFloatv(param, &v);
373         return v;
374 }
375
376 void glaRasterPosSafe2f(float x, float y, float known_good_x, float known_good_y)
377 {
378         GLubyte dummy= 0;
379
380                 /* As long as known good coordinates are correct
381                  * this is guarenteed to generate an ok raster
382                  * position (ignoring potential (real) overflow
383                  * issues).
384                  */
385         glRasterPos2f(known_good_x, known_good_y);
386
387                 /* Now shift the raster position to where we wanted
388                  * it in the first place using the glBitmap trick.
389                  */
390         glBitmap(0, 0, 0, 0, x - known_good_x, y - known_good_y, &dummy);
391 }
392
393 static int get_cached_work_texture(int *w_r, int *h_r)
394 {
395         static GLint texid= -1;
396         static int tex_w= 256;
397         static int tex_h= 256;
398
399         if (texid==-1) {
400                 GLint ltexid= glaGetOneInteger(GL_TEXTURE_2D);
401                 unsigned char *tbuf;
402
403                 glGenTextures(1, (GLuint *)&texid);
404
405                 glBindTexture(GL_TEXTURE_2D, texid);
406
407                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
408                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
409
410                 tbuf= MEM_callocN(tex_w*tex_h*4, "tbuf");
411                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tbuf);
412                 MEM_freeN(tbuf);
413
414                 glBindTexture(GL_TEXTURE_2D, ltexid);
415         }
416
417         *w_r= tex_w;
418         *h_r= tex_h;
419         return texid;
420 }
421
422 void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, void *rect, float scaleX, float scaleY)
423 {
424         unsigned char *uc_rect= (unsigned char*) rect;
425         float *f_rect= (float *)rect;
426         float xzoom= glaGetOneFloat(GL_ZOOM_X), yzoom= glaGetOneFloat(GL_ZOOM_Y);
427         int ltexid= glaGetOneInteger(GL_TEXTURE_2D);
428         int lrowlength= glaGetOneInteger(GL_UNPACK_ROW_LENGTH);
429         int subpart_x, subpart_y, tex_w, tex_h;
430         int seamless, offset_x, offset_y, nsubparts_x, nsubparts_y;
431         int texid= get_cached_work_texture(&tex_w, &tex_h);
432         
433         /* Specify the color outside this function, and tex will modulate it.
434          * This is useful for changing alpha without using glPixelTransferf()
435          */
436         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
437         glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w);
438         glBindTexture(GL_TEXTURE_2D, texid);
439
440         /* don't want nasty border artifacts */
441         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
442         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
443
444 #ifdef __APPLE__
445         /* workaround for os x 10.5/10.6 driver bug: http://lists.apple.com/archives/Mac-opengl/2008/Jul/msg00117.html */
446         glPixelZoom(1.f, 1.f);
447 #endif
448         
449         /* setup seamless 2=on, 0=off */
450         seamless= ((tex_w<img_w || tex_h<img_h) && tex_w>2 && tex_h>2)? 2: 0;
451         
452         offset_x= tex_w - seamless;
453         offset_y= tex_h - seamless;
454         
455         nsubparts_x= (img_w + (offset_x - 1))/(offset_x);
456         nsubparts_y= (img_h + (offset_y - 1))/(offset_y);
457
458         for (subpart_y=0; subpart_y<nsubparts_y; subpart_y++) {
459                 for (subpart_x=0; subpart_x<nsubparts_x; subpart_x++) {
460                         int remainder_x= img_w-subpart_x*offset_x;
461                         int remainder_y= img_h-subpart_y*offset_y;
462                         int subpart_w= (remainder_x<tex_w)? remainder_x: tex_w;
463                         int subpart_h= (remainder_y<tex_h)? remainder_y: tex_h;
464                         int offset_left= (seamless && subpart_x!=0)? 1: 0;
465                         int offset_bot= (seamless && subpart_y!=0)? 1: 0;
466                         int offset_right= (seamless && remainder_x>tex_w)? 1: 0;
467                         int offset_top= (seamless && remainder_y>tex_h)? 1: 0;
468                         float rast_x= x+subpart_x*offset_x*xzoom;
469                         float rast_y= y+subpart_y*offset_y*yzoom;
470                         
471                         /* check if we already got these because we always get 2 more when doing seamless*/
472                         if(subpart_w<=seamless || subpart_h<=seamless)
473                                 continue;
474                         
475                         if(format==GL_FLOAT) {
476                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, GL_RGBA, GL_FLOAT, &f_rect[subpart_y*offset_y*img_w*4 + subpart_x*offset_x*4]);
477                                 
478                                 /* add an extra border of pixels so linear looks ok at edges of full image. */
479                                 if(subpart_w<tex_w)
480                                         glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, GL_RGBA, GL_FLOAT, &f_rect[subpart_y*offset_y*img_w*4 + (subpart_x*offset_x+subpart_w-1)*4]);
481                                 if(subpart_h<tex_h)
482                                         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, GL_RGBA, GL_FLOAT, &f_rect[(subpart_y*offset_y+subpart_h-1)*img_w*4 + subpart_x*offset_x*4]);
483                                 if(subpart_w<tex_w && subpart_h<tex_h)
484                                         glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, GL_RGBA, GL_FLOAT, &f_rect[(subpart_y*offset_y+subpart_h-1)*img_w*4 + (subpart_x*offset_x+subpart_w-1)*4]);
485                         }
486                         else {
487                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, GL_RGBA, GL_UNSIGNED_BYTE, &uc_rect[subpart_y*offset_y*img_w*4 + subpart_x*offset_x*4]);
488                                 
489                                 if(subpart_w<tex_w)
490                                         glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, GL_RGBA, GL_UNSIGNED_BYTE, &uc_rect[subpart_y*offset_y*img_w*4 + (subpart_x*offset_x+subpart_w-1)*4]);
491                                 if(subpart_h<tex_h)
492                                         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, GL_RGBA, GL_UNSIGNED_BYTE, &uc_rect[(subpart_y*offset_y+subpart_h-1)*img_w*4 + subpart_x*offset_x*4]);
493                                 if(subpart_w<tex_w && subpart_h<tex_h)
494                                         glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &uc_rect[(subpart_y*offset_y+subpart_h-1)*img_w*4 + (subpart_x*offset_x+subpart_w-1)*4]);
495                         }
496
497                         glEnable(GL_TEXTURE_2D);
498                         glBegin(GL_QUADS);
499                         glTexCoord2f((float)(0 + offset_left)/tex_w, (float)(0 + offset_bot)/tex_h);
500                         glVertex2f(rast_x + (float)offset_left*xzoom, rast_y + (float)offset_bot*xzoom);
501
502                         glTexCoord2f((float)(subpart_w - offset_right)/tex_w, (float)(0 + offset_bot)/tex_h);
503                         glVertex2f(rast_x + (float)(subpart_w - offset_right)*xzoom*scaleX, rast_y + (float)offset_bot*xzoom);
504
505                         glTexCoord2f((float)(subpart_w - offset_right)/tex_w, (float)(subpart_h - offset_top)/tex_h);
506                         glVertex2f(rast_x + (float)(subpart_w - offset_right)*xzoom*scaleX, rast_y + (float)(subpart_h - offset_top)*yzoom*scaleY);
507
508                         glTexCoord2f((float)(0 + offset_left)/tex_w, (float)(subpart_h - offset_top)/tex_h);
509                         glVertex2f(rast_x + (float)offset_left*xzoom, rast_y + (float)(subpart_h - offset_top)*yzoom*scaleY);
510                         glEnd();
511                         glDisable(GL_TEXTURE_2D);
512                 }
513         }
514
515         glBindTexture(GL_TEXTURE_2D, ltexid);
516         glPixelStorei(GL_UNPACK_ROW_LENGTH, lrowlength);
517         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
518         
519 #ifdef __APPLE__
520         /* workaround for os x 10.5/10.6 driver bug (above) */
521         glPixelZoom(xzoom, yzoom);
522 #endif
523 }
524
525 void glaDrawPixelsTex(float x, float y, int img_w, int img_h, int format, void *rect)
526 {
527         glaDrawPixelsTexScaled(x, y, img_w, img_h, format, rect, 1.0f, 1.0f);
528 }
529
530 /* row_w is unused but kept for completeness */
531 void glaDrawPixelsSafe_to32(float fx, float fy, int img_w, int img_h, int UNUSED(row_w), float *rectf, int do_gamma_correct)
532 {
533         unsigned char *rect32;
534         
535         /* copy imgw-imgh to a temporal 32 bits rect */
536         if(img_w<1 || img_h<1) return;
537         
538         rect32= MEM_mallocN(img_w*img_h*sizeof(int), "temp 32 bits");
539         
540         if (do_gamma_correct) {
541                 floatbuf_to_srgb_byte(rectf, rect32, 0, img_w, 0, img_h, img_w);
542         } else {
543                 floatbuf_to_byte(rectf, rect32, 0, img_w, 0, img_h, img_w);
544          }
545         
546         glaDrawPixelsSafe(fx, fy, img_w, img_h, img_w, GL_RGBA, GL_UNSIGNED_BYTE, rect32);
547
548         MEM_freeN(rect32);
549 }
550
551 void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int format, int type, void *rect)
552 {
553         float xzoom= glaGetOneFloat(GL_ZOOM_X);
554         float yzoom= glaGetOneFloat(GL_ZOOM_Y);
555                 
556                 /* The pixel space coordinate of the intersection of
557                  * the [zoomed] image with the origin.
558                  */
559         float ix= -x/xzoom;
560         float iy= -y/yzoom;
561         
562                 /* The maximum pixel amounts the image can be cropped
563                  * at the lower left without exceeding the origin.
564                  */
565         int off_x= floor(MAX2(ix, 0));
566         int off_y= floor(MAX2(iy, 0));
567                 
568                 /* The zoomed space coordinate of the raster position 
569                  * (starting at the lower left most unclipped pixel).
570                  */
571         float rast_x= x + off_x*xzoom;
572         float rast_y= y + off_y*yzoom;
573
574         GLfloat scissor[4];
575         int draw_w, draw_h;
576
577                 /* Determine the smallest number of pixels we need to draw
578                  * before the image would go off the upper right corner.
579                  * 
580                  * It may seem this is just an optimization but some graphics 
581                  * cards (ATI) freak out if there is a large zoom factor and
582                  * a large number of pixels off the screen (probably at some
583                  * level the number of image pixels to draw is getting multiplied
584                  * by the zoom and then clamped). Making sure we draw the
585                  * fewest pixels possible keeps everyone mostly happy (still
586                  * fails if we zoom in on one really huge pixel so that it
587                  * covers the entire screen).
588                  */
589         glGetFloatv(GL_SCISSOR_BOX, scissor);
590         draw_w = MIN2(img_w-off_x, ceil((scissor[2]-rast_x)/xzoom));
591         draw_h = MIN2(img_h-off_y, ceil((scissor[3]-rast_y)/yzoom));
592
593         if (draw_w>0 && draw_h>0) {
594                 int old_row_length = glaGetOneInteger(GL_UNPACK_ROW_LENGTH);
595
596                         /* Don't use safe RasterPos (slower) if we can avoid it. */
597                 if (rast_x>=0 && rast_y>=0) {
598                         glRasterPos2f(rast_x, rast_y);
599                 } else {
600                         glaRasterPosSafe2f(rast_x, rast_y, 0, 0);
601                 }
602
603                 glPixelStorei(GL_UNPACK_ROW_LENGTH, row_w);
604                 if(format==GL_LUMINANCE || format==GL_RED) {
605                         if(type==GL_FLOAT) {
606                                 float *f_rect= (float *)rect;
607                                 glDrawPixels(draw_w, draw_h, format, type, f_rect + (off_y*row_w + off_x));
608                         }
609                         else if(type==GL_INT || type==GL_UNSIGNED_INT) {
610                                 int *i_rect= (int *)rect;
611                                 glDrawPixels(draw_w, draw_h, format, type, i_rect + (off_y*row_w + off_x));
612                         }
613                 }
614                 else { /* RGBA */
615                         if(type==GL_FLOAT) {
616                                 float *f_rect= (float *)rect;
617                                 glDrawPixels(draw_w, draw_h, format, type, f_rect + (off_y*row_w + off_x)*4);
618                         }
619                         else if(type==GL_UNSIGNED_BYTE) {
620                                 unsigned char *uc_rect= (unsigned char *) rect;
621                                 glDrawPixels(draw_w, draw_h, format, type, uc_rect + (off_y*row_w + off_x)*4);
622                         }
623                 }
624                 
625                 glPixelStorei(GL_UNPACK_ROW_LENGTH,  old_row_length);
626         }
627 }
628
629 /* 2D Drawing Assistance */
630
631 void glaDefine2DArea(rcti *screen_rect)
632 {
633         int sc_w= screen_rect->xmax - screen_rect->xmin + 1;
634         int sc_h= screen_rect->ymax - screen_rect->ymin + 1;
635
636         glViewport(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h);
637         glScissor(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h);
638
639                 /* The 0.375 magic number is to shift the matrix so that
640                  * both raster and vertex integer coordinates fall at pixel
641                  * centers properly. For a longer discussion see the OpenGL
642                  * Programming Guide, Appendix H, Correctness Tips.
643                  */
644
645         glMatrixMode(GL_PROJECTION);
646         glLoadIdentity();
647         glOrtho(0.0, sc_w, 0.0, sc_h, -1, 1);
648         glTranslatef(0.375, 0.375, 0.0);
649
650         glMatrixMode(GL_MODELVIEW);
651         glLoadIdentity();
652 }
653
654 struct gla2DDrawInfo {
655         int orig_vp[4], orig_sc[4];
656         float orig_projmat[16], orig_viewmat[16];
657
658         rcti screen_rect;
659         rctf world_rect;
660
661         float wo_to_sc[2];
662 };
663
664 void gla2DGetMap(gla2DDrawInfo *di, rctf *rect) 
665 {
666         *rect= di->world_rect;
667 }
668
669 void gla2DSetMap(gla2DDrawInfo *di, rctf *rect) 
670 {
671         int sc_w, sc_h;
672         float wo_w, wo_h;
673
674         di->world_rect= *rect;
675         
676         sc_w= (di->screen_rect.xmax-di->screen_rect.xmin);
677         sc_h= (di->screen_rect.ymax-di->screen_rect.ymin);
678         wo_w= (di->world_rect.xmax-di->world_rect.xmin);
679         wo_h= (di->world_rect.ymax-di->world_rect.ymin);
680         
681         di->wo_to_sc[0]= sc_w/wo_w;
682         di->wo_to_sc[1]= sc_h/wo_h;
683 }
684
685
686 gla2DDrawInfo *glaBegin2DDraw(rcti *screen_rect, rctf *world_rect) 
687 {
688         gla2DDrawInfo *di= MEM_mallocN(sizeof(*di), "gla2DDrawInfo");
689         int sc_w, sc_h;
690         float wo_w, wo_h;
691
692         glGetIntegerv(GL_VIEWPORT, (GLint *)di->orig_vp);
693         glGetIntegerv(GL_SCISSOR_BOX, (GLint *)di->orig_sc);
694         glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat *)di->orig_projmat);
695         glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)di->orig_viewmat);
696
697         di->screen_rect= *screen_rect;
698         if (world_rect) {
699                 di->world_rect= *world_rect;
700         } else {
701                 di->world_rect.xmin= di->screen_rect.xmin;
702                 di->world_rect.ymin= di->screen_rect.ymin;
703                 di->world_rect.xmax= di->screen_rect.xmax;
704                 di->world_rect.ymax= di->screen_rect.ymax;
705         }
706
707         sc_w= (di->screen_rect.xmax-di->screen_rect.xmin);
708         sc_h= (di->screen_rect.ymax-di->screen_rect.ymin);
709         wo_w= (di->world_rect.xmax-di->world_rect.xmin);
710         wo_h= (di->world_rect.ymax-di->world_rect.ymin);
711
712         di->wo_to_sc[0]= sc_w/wo_w;
713         di->wo_to_sc[1]= sc_h/wo_h;
714
715         glaDefine2DArea(&di->screen_rect);
716
717         return di;
718 }
719
720 void gla2DDrawTranslatePt(gla2DDrawInfo *di, float wo_x, float wo_y, int *sc_x_r, int *sc_y_r)
721 {
722         *sc_x_r= (wo_x - di->world_rect.xmin)*di->wo_to_sc[0];
723         *sc_y_r= (wo_y - di->world_rect.ymin)*di->wo_to_sc[1];
724 }
725 void gla2DDrawTranslatePtv(gla2DDrawInfo *di, float world[2], int screen_r[2])
726 {
727         screen_r[0]= (world[0] - di->world_rect.xmin)*di->wo_to_sc[0];
728         screen_r[1]= (world[1] - di->world_rect.ymin)*di->wo_to_sc[1];
729 }
730
731 void glaEnd2DDraw(gla2DDrawInfo *di)
732 {
733         glViewport(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]);
734         glScissor(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]);
735         glMatrixMode(GL_PROJECTION);
736         glLoadMatrixf(di->orig_projmat);
737         glMatrixMode(GL_MODELVIEW);
738         glLoadMatrixf(di->orig_viewmat);
739
740         MEM_freeN(di);
741 }
742
743 /* **************** glPoint hack ************************ */
744
745 static int curmode=0;
746 static int pointhack=0;
747 static GLubyte Squaredot[16] = { 0xff,0xff,0xff,0xff,
748                                                                  0xff,0xff,0xff,0xff,
749                                                                  0xff,0xff,0xff,0xff, 
750                                                                  0xff,0xff,0xff,0xff};
751
752 void bglBegin(int mode)
753 {
754         curmode= mode;
755         
756         if(mode==GL_POINTS) {
757                 float value[4];
758                 glGetFloatv(GL_POINT_SIZE_RANGE, value);
759                 if(value[1]<2.0) {
760                         glGetFloatv(GL_POINT_SIZE, value);
761                         pointhack= floor(value[0]+0.5);
762                         if(pointhack>4) pointhack= 4;
763                 }
764                 else glBegin(mode);
765         }
766 }
767
768 int bglPointHack(void) {
769         float value[4];
770         int pointhack_px;
771         glGetFloatv(GL_POINT_SIZE_RANGE, value);
772         if(value[1]<2.0) {
773                 glGetFloatv(GL_POINT_SIZE, value);
774                 pointhack_px= floor(value[0]+0.5);
775                 if(pointhack_px>4) pointhack_px= 4;
776                 return pointhack_px;
777         }
778         return 0;
779 }
780
781 void bglVertex3fv(float *vec)
782 {
783         switch(curmode) {
784         case GL_POINTS:
785                 if(pointhack) {
786                         glRasterPos3fv(vec);
787                         glBitmap(pointhack, pointhack, (float)pointhack/2.0, (float)pointhack/2.0, 0.0, 0.0, Squaredot);
788                 }
789                 else glVertex3fv(vec);
790                 break;
791         }
792 }
793
794 void bglVertex3f(float x, float y, float z)
795 {
796         switch(curmode) {
797         case GL_POINTS:
798                 if(pointhack) {
799                         glRasterPos3f(x, y, z);
800                         glBitmap(pointhack, pointhack, (float)pointhack/2.0, (float)pointhack/2.0, 0.0, 0.0, Squaredot);
801                 }
802                 else glVertex3f(x, y, z);
803                 break;
804         }
805 }
806
807 void bglVertex2fv(float *vec)
808 {
809         switch(curmode) {
810         case GL_POINTS:
811                 if(pointhack) {
812                         glRasterPos2fv(vec);
813                         glBitmap(pointhack, pointhack, (float)pointhack/2, pointhack/2, 0.0, 0.0, Squaredot);
814                 }
815                 else glVertex2fv(vec);
816                 break;
817         }
818 }
819
820
821 void bglEnd(void)
822 {
823         if(pointhack) pointhack= 0;
824         else glEnd();
825         
826 }
827
828 /* Uses current OpenGL state to get view matrices for gluProject/gluUnProject */
829 void bgl_get_mats(bglMats *mats)
830 {
831         const double badvalue= 1.0e-6;
832
833         glGetDoublev(GL_MODELVIEW_MATRIX, mats->modelview);
834         glGetDoublev(GL_PROJECTION_MATRIX, mats->projection);
835         glGetIntegerv(GL_VIEWPORT, (GLint *)mats->viewport);
836         
837         /* Very strange code here - it seems that certain bad values in the
838            modelview matrix can cause gluUnProject to give bad results. */
839         if(mats->modelview[0] < badvalue &&
840            mats->modelview[0] > -badvalue)
841                 mats->modelview[0]= 0;
842         if(mats->modelview[5] < badvalue &&
843            mats->modelview[5] > -badvalue)
844                 mats->modelview[5]= 0;
845         
846         /* Set up viewport so that gluUnProject will give correct values */
847         mats->viewport[0] = 0;
848         mats->viewport[1] = 0;
849 }
850
851 /* *************** glPolygonOffset hack ************* */
852
853 /* dist is only for ortho now... */
854 void bglPolygonOffset(float viewdist, float dist) 
855 {
856         static float winmat[16], offset=0.0;    
857         
858         if(dist!=0.0) {
859                 float offs;
860                 
861                 // glEnable(GL_POLYGON_OFFSET_FILL);
862                 // glPolygonOffset(-1.0, -1.0);
863
864                 /* hack below is to mimic polygon offset */
865                 glMatrixMode(GL_PROJECTION);
866                 glGetFloatv(GL_PROJECTION_MATRIX, (float *)winmat);
867                 
868                 /* dist is from camera to center point */
869                 
870                 if(winmat[15]>0.5) offs= 0.00001*dist*viewdist;  // ortho tweaking
871                 else offs= 0.0005*dist;  // should be clipping value or so...
872                 
873                 winmat[14]-= offs;
874                 offset+= offs;
875                 
876                 glLoadMatrixf(winmat);
877                 glMatrixMode(GL_MODELVIEW);
878         }
879         else {
880
881                 glMatrixMode(GL_PROJECTION);
882                 winmat[14]+= offset;
883                 offset= 0.0;
884                 glLoadMatrixf(winmat);
885                 glMatrixMode(GL_MODELVIEW);
886         }
887 }
888
889 void bglFlush(void) 
890 {
891         glFlush();
892 #ifdef __APPLE__
893 //      if(GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_MAC, GPU_DRIVER_OFFICIAL))
894 // XXX          myswapbuffers(); //hack to get mac intel graphics to show frontbuffer
895 #endif
896 }
897