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