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