Make image drawing code working with core profile
[blender.git] / source / blender / windowmanager / intern / wm_gesture.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) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/windowmanager/intern/wm_gesture.c
28  *  \ingroup wm
29  *
30  * Gestures (cursor motions) creating, evaluating and drawing, shared between operators.
31  */
32
33 #include "DNA_screen_types.h"
34 #include "DNA_vec_types.h"
35 #include "DNA_userdef_types.h"
36 #include "DNA_windowmanager_types.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_bitmap_draw_2d.h"
41 #include "BLI_blenlib.h"
42 #include "BLI_math.h"
43 #include "BLI_utildefines.h"
44 #include "BLI_lasso.h"
45
46 #include "BKE_context.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "wm.h"
52 #include "wm_subwindow.h"
53 #include "wm_draw.h"
54
55 #include "GPU_immediate.h"
56 #include "GPU_immediate_util.h"
57
58 #include "BIF_glutil.h"
59
60
61 /* context checked on having screen, window and area */
62 wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type)
63 {
64         wmGesture *gesture = MEM_callocN(sizeof(wmGesture), "new gesture");
65         wmWindow *window = CTX_wm_window(C);
66         ARegion *ar = CTX_wm_region(C);
67         int sx, sy;
68         
69         BLI_addtail(&window->gesture, gesture);
70         
71         gesture->type = type;
72         gesture->event_type = event->type;
73         gesture->swinid = ar->swinid;    /* means only in area-region context! */
74         
75         wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy);
76         
77         if (ELEM(type, WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, WM_GESTURE_TWEAK,
78                   WM_GESTURE_CIRCLE, WM_GESTURE_STRAIGHTLINE))
79         {
80                 rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new");
81                 
82                 gesture->customdata = rect;
83                 rect->xmin = event->x - sx;
84                 rect->ymin = event->y - sy;
85                 if (type == WM_GESTURE_CIRCLE) {
86 #ifdef GESTURE_MEMORY
87                         rect->xmax = circle_select_size;
88 #else
89                         rect->xmax = 25;    // XXX temp
90 #endif
91                 }
92                 else {
93                         rect->xmax = event->x - sx;
94                         rect->ymax = event->y - sy;
95                 }
96         }
97         else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) {
98                 short *lasso;
99                 gesture->customdata = lasso = MEM_callocN(2 * sizeof(short) * WM_LASSO_MIN_POINTS, "lasso points");
100                 lasso[0] = event->x - sx;
101                 lasso[1] = event->y - sy;
102                 gesture->points = 1;
103                 gesture->size = WM_LASSO_MIN_POINTS;
104         }
105         
106         return gesture;
107 }
108
109 void WM_gesture_end(bContext *C, wmGesture *gesture)
110 {
111         wmWindow *win = CTX_wm_window(C);
112         
113         if (win->tweak == gesture)
114                 win->tweak = NULL;
115         BLI_remlink(&win->gesture, gesture);
116         MEM_freeN(gesture->customdata);
117         if (gesture->userdata) {
118                 MEM_freeN(gesture->userdata);
119         }
120         MEM_freeN(gesture);
121 }
122
123 void WM_gestures_remove(bContext *C)
124 {
125         wmWindow *win = CTX_wm_window(C);
126         
127         while (win->gesture.first)
128                 WM_gesture_end(C, win->gesture.first);
129 }
130
131
132 /* tweak and line gestures */
133 int wm_gesture_evaluate(wmGesture *gesture)
134 {
135         if (gesture->type == WM_GESTURE_TWEAK) {
136                 rcti *rect = gesture->customdata;
137                 int dx = BLI_rcti_size_x(rect);
138                 int dy = BLI_rcti_size_y(rect);
139                 if (abs(dx) + abs(dy) > U.tweak_threshold) {
140                         int theta = iroundf(4.0f * atan2f((float)dy, (float)dx) / (float)M_PI);
141                         int val = EVT_GESTURE_W;
142
143                         if (theta == 0) val = EVT_GESTURE_E;
144                         else if (theta == 1) val = EVT_GESTURE_NE;
145                         else if (theta == 2) val = EVT_GESTURE_N;
146                         else if (theta == 3) val = EVT_GESTURE_NW;
147                         else if (theta == -1) val = EVT_GESTURE_SE;
148                         else if (theta == -2) val = EVT_GESTURE_S;
149                         else if (theta == -3) val = EVT_GESTURE_SW;
150                         
151 #if 0
152                         /* debug */
153                         if (val == 1) printf("tweak north\n");
154                         if (val == 2) printf("tweak north-east\n");
155                         if (val == 3) printf("tweak east\n");
156                         if (val == 4) printf("tweak south-east\n");
157                         if (val == 5) printf("tweak south\n");
158                         if (val == 6) printf("tweak south-west\n");
159                         if (val == 7) printf("tweak west\n");
160                         if (val == 8) printf("tweak north-west\n");
161 #endif
162                         return val;
163                 }
164         }
165         return 0;
166 }
167
168
169 /* ******************* gesture draw ******************* */
170
171 static void wm_gesture_draw_line(wmGesture *gt)
172 {
173         rcti *rect = (rcti *)gt->customdata;
174         
175         VertexFormat *format = immVertexFormat();
176         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
177         unsigned int line_origin = VertexFormat_add_attrib(format, "line_origin", COMP_F32, 2, KEEP_FLOAT);
178
179         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_COLOR);
180
181         immUniform4f("color1", 0.4f, 0.4f, 0.4f, 1.0f);
182         immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
183         immUniform1f("dash_width", 8.0f);
184         immUniform1f("dash_width_on", 4.0f);
185
186         float xmin = (float)rect->xmin;
187         float ymin = (float)rect->ymin;
188
189         immBegin(PRIM_LINES, 2);
190
191         immAttrib2f(line_origin, xmin, ymin);
192         immVertex2f(pos, xmin, ymin);
193         immAttrib2f(line_origin, xmin, ymin);
194         immVertex2f(pos, (float)rect->xmax, (float)rect->ymax);
195
196         immEnd();
197
198         immUnbindProgram();
199 }
200
201 static void imm_draw_line_box_dashed(unsigned pos, unsigned line_origin, float x1, float y1, float x2, float y2)
202 {
203         immBegin(PRIM_LINES, 8);
204         immAttrib2f(line_origin, x1, y1);
205         immVertex2f(pos, x1, y1);
206         immVertex2f(pos, x1, y2);
207         immAttrib2f(line_origin, x1, y2);
208         immVertex2f(pos, x1, y2);
209         immVertex2f(pos, x2, y2);
210         immAttrib2f(line_origin, x2, y1);
211         immVertex2f(pos, x2, y2);
212         immVertex2f(pos, x2, y1);
213         immAttrib2f(line_origin, x1, y1);
214         immVertex2f(pos, x2, y1);
215         immVertex2f(pos, x1, y1);
216         immEnd();
217 }
218
219 static void wm_gesture_draw_rect(wmGesture *gt)
220 {
221         rcti *rect = (rcti *)gt->customdata;
222
223         VertexFormat *format = immVertexFormat();
224         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_I32, 2, CONVERT_INT_TO_FLOAT);
225
226         glEnable(GL_BLEND);
227
228         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
229         immUniform4f("color", 1.0f, 1.0f, 1.0f, 0.05f);
230
231         immRecti(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
232
233         immUnbindProgram();
234
235         glDisable(GL_BLEND);
236
237         format = immVertexFormat();
238         pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
239         unsigned line_origin = VertexFormat_add_attrib(format, "line_origin", COMP_F32, 2, KEEP_FLOAT);
240
241         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_COLOR);
242
243         immUniform4f("color1", 0.4f, 0.4f, 0.4f, 1.0f);
244         immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
245         immUniform1f("dash_width", 8.0f);
246         immUniform1f("dash_width_on", 4.0f);
247         
248         imm_draw_line_box_dashed(pos, line_origin,
249                 (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, (float)rect->ymax);
250
251         immUnbindProgram();
252
253         // wm_gesture_draw_line(gt); // draws a diagonal line in the lined box to test wm_gesture_draw_line
254 }
255
256 static void imm_draw_lined_dashed_circle(unsigned pos, unsigned line_origin, float x, float y, float rad, int nsegments)
257 {
258         float xpos, ypos;
259
260         xpos = x + rad;
261         ypos = y;
262
263         immBegin(PRIM_LINES, nsegments * 2);
264
265         for (int i = 1; i <= nsegments; ++i) {
266                 float angle = 2 * M_PI * ((float)i / (float)nsegments);
267
268                 immAttrib2f(line_origin, xpos, ypos);
269                 immVertex2f(pos, xpos, ypos);
270
271                 xpos = x + rad * cosf(angle);
272                 ypos = y + rad * sinf(angle);
273
274                 immVertex2f(pos, xpos, ypos);
275         }
276
277         immEnd();
278 }
279
280 static void wm_gesture_draw_circle(wmGesture *gt)
281 {
282         rcti *rect = (rcti *)gt->customdata;
283
284         glEnable(GL_BLEND);
285
286         VertexFormat *format = immVertexFormat();
287         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
288
289         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
290
291         immUniformColor4f(1.0, 1.0, 1.0, 0.05);
292         imm_draw_circle_fill(pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40);
293
294         immUnbindProgram();
295
296         glDisable(GL_BLEND);
297         
298         format = immVertexFormat();
299         pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
300         unsigned int line_origin = VertexFormat_add_attrib(format, "line_origin", COMP_F32, 2, KEEP_FLOAT);
301
302         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_COLOR);
303
304         immUniform4f("color1", 0.4f, 0.4f, 0.4f, 1.0f);
305         immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
306         immUniform1f("dash_width", 4.0f);
307         immUniform1f("dash_width_on", 2.0f);
308         
309         imm_draw_lined_dashed_circle(pos, line_origin,
310                 (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40);
311
312         immUnbindProgram();
313 }
314
315 struct LassoFillData {
316         unsigned char *px;
317         int width;
318 };
319
320 static void draw_filled_lasso_px_cb(int x, int x_end, int y, void *user_data)
321 {
322         struct LassoFillData *data = user_data;
323         unsigned char *col = &(data->px[(y * data->width) + x]);
324         memset(col, 0x10, x_end - x);
325 }
326
327 static void draw_filled_lasso(wmWindow *win, wmGesture *gt)
328 {
329         const short *lasso = (short *)gt->customdata;
330         const int tot = gt->points;
331         int (*moves)[2] = MEM_mallocN(sizeof(*moves) * (tot + 1), __func__);
332         int i;
333         rcti rect;
334         rcti rect_win;
335         float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
336
337         for (i = 0; i < tot; i++, lasso += 2) {
338                 moves[i][0] = lasso[0];
339                 moves[i][1] = lasso[1];
340         }
341
342         BLI_lasso_boundbox(&rect, (const int (*)[2])moves, tot);
343
344         wm_subwindow_rect_get(win, gt->swinid, &rect_win);
345         BLI_rcti_translate(&rect, rect_win.xmin, rect_win.ymin);
346         BLI_rcti_isect(&rect_win, &rect, &rect);
347         BLI_rcti_translate(&rect, -rect_win.xmin, -rect_win.ymin);
348
349         /* highly unlikely this will fail, but could crash if (tot == 0) */
350         if (BLI_rcti_is_empty(&rect) == false) {
351                 const int w = BLI_rcti_size_x(&rect);
352                 const int h = BLI_rcti_size_y(&rect);
353                 unsigned char *pixel_buf = MEM_callocN(sizeof(*pixel_buf) * w * h, __func__);
354                 struct LassoFillData lasso_fill_data = {pixel_buf, w};
355
356                 BLI_bitmap_draw_2d_poly_v2i_n(
357                        rect.xmin, rect.ymin, rect.xmax, rect.ymax,
358                        (const int (*)[2])moves, tot,
359                        draw_filled_lasso_px_cb, &lasso_fill_data);
360
361                 /* Additive Blending */
362                 glEnable(GL_BLEND);
363                 glBlendFunc(GL_ONE, GL_ONE);
364
365                 GLint unpack_alignment;
366                 glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment);
367
368                 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
369
370                 IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
371                 GPU_shader_bind(state.shader);
372                 GPU_shader_uniform_vector(state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
373
374                 immDrawPixelsTex(&state, rect.xmin, rect.ymin, w, h, GL_RED, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf, 1.0f, 1.0f, NULL);
375
376                 GPU_shader_unbind();
377
378                 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
379
380                 MEM_freeN(pixel_buf);
381
382                 glDisable(GL_BLEND);
383         }
384
385         MEM_freeN(moves);
386 }
387
388
389 static void wm_gesture_draw_lasso(wmWindow *win, wmGesture *gt, bool filled)
390 {
391         const short *lasso = (short *)gt->customdata;
392         int i, numverts;
393         float x, y;
394
395         if (filled) {
396                 draw_filled_lasso(win, gt);
397         }
398
399         numverts = gt->points;
400         if (gt->type == WM_GESTURE_LASSO) {
401                 numverts++;
402         }
403
404         /* Nothing to drawe, do early output. */
405         if (numverts < 2) {
406                 return;
407         }
408
409         VertexFormat *format = immVertexFormat();
410         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
411         unsigned int line_origin = VertexFormat_add_attrib(format, "line_origin", COMP_F32, 2, KEEP_FLOAT);
412
413         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_COLOR);
414
415         immUniform4f("color1", 0.4f, 0.4f, 0.4f, 1.0f);
416         immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
417         immUniform1f("dash_width", 2.0f);
418         immUniform1f("dash_width_on", 1.0f);
419
420         immBegin(PRIM_LINE_STRIP, numverts);
421
422         for (i = 0; i < gt->points; i++, lasso += 2) {
423
424                 /* get line_origin coordinates only from the first vertex of each line */
425                 if (!(i % 2)) {
426                         x = (float)lasso[0];
427                         y = (float)lasso[1];
428                 }
429
430                 immAttrib2f(line_origin, x, y);
431                 immVertex2f(pos, (float)lasso[0], (float)lasso[1]);
432         }
433
434         if (gt->type == WM_GESTURE_LASSO) {
435                 immAttrib2f(line_origin, x, y);
436                 lasso = (short *)gt->customdata;
437                 immVertex2f(pos, (float)lasso[0], (float)lasso[1]);
438         }
439
440         immEnd();
441
442         immUnbindProgram();
443 }
444
445 static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt)
446 {
447         rcti *rect = (rcti *)gt->customdata;
448         const int winsize_x = WM_window_pixels_x(win);
449         const int winsize_y = WM_window_pixels_y(win);
450
451         float x1, x2, y1, y2;
452
453         VertexFormat *format = immVertexFormat();
454         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
455         unsigned int line_origin = VertexFormat_add_attrib(format, "line_origin", COMP_F32, 2, KEEP_FLOAT);
456
457         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_COLOR);
458
459         immUniform4f("color1", 0.4f, 0.4f, 0.4f, 1.0f);
460         immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
461         immUniform1f("dash_width", 8.0f);
462         immUniform1f("dash_width_on", 4.0f);
463
464         immBegin(PRIM_LINES, 4);
465
466         x1 = (float)(rect->xmin - winsize_x);
467         y1 = (float)rect->ymin;
468         x2 = (float)(rect->xmin + winsize_x);
469         y2 = y1;
470
471         immAttrib2f(line_origin, x1, y1);
472         immVertex2f(pos, x1, y1);
473         immAttrib2f(line_origin, x1, y1);
474         immVertex2f(pos, x2, y2);
475
476         x1 = (float)rect->xmin;
477         y1 = (float)(rect->ymin - winsize_y);
478         x2 = x1;
479         y2 = (float)(rect->ymin + winsize_y);
480
481         immAttrib2f(line_origin, x1, y1);
482         immVertex2f(pos, x1, y1);
483         immAttrib2f(line_origin, x1, y1);
484         immVertex2f(pos, x2, y2);
485
486         immEnd();
487
488         immUnbindProgram();
489 }
490
491 /* called in wm_draw.c */
492 void wm_gesture_draw(wmWindow *win)
493 {
494         wmGesture *gt = (wmGesture *)win->gesture.first;
495
496         glLineWidth(1.0f);
497         for (; gt; gt = gt->next) {
498                 /* all in subwindow space */
499                 wmSubWindowSet(win, gt->swinid);
500                 
501                 if (gt->type == WM_GESTURE_RECT)
502                         wm_gesture_draw_rect(gt);
503 //              else if (gt->type == WM_GESTURE_TWEAK)
504 //                      wm_gesture_draw_line(gt);
505                 else if (gt->type == WM_GESTURE_CIRCLE)
506                         wm_gesture_draw_circle(gt);
507                 else if (gt->type == WM_GESTURE_CROSS_RECT) {
508                         if (gt->mode == 1)
509                                 wm_gesture_draw_rect(gt);
510                         else
511                                 wm_gesture_draw_cross(win, gt);
512                 }
513                 else if (gt->type == WM_GESTURE_LINES)
514                         wm_gesture_draw_lasso(win, gt, false);
515                 else if (gt->type == WM_GESTURE_LASSO)
516                         wm_gesture_draw_lasso(win, gt, true);
517                 else if (gt->type == WM_GESTURE_STRAIGHTLINE)
518                         wm_gesture_draw_line(gt);
519         }
520 }
521
522 void wm_gesture_tag_redraw(bContext *C)
523 {
524         wmWindow *win = CTX_wm_window(C);
525         bScreen *screen = CTX_wm_screen(C);
526         ARegion *ar = CTX_wm_region(C);
527         
528         if (screen)
529                 screen->do_draw_gesture = true;
530
531         wm_tag_redraw_overlay(win, ar);
532 }