Cleanup: simplify lasso reallocation
[blender-staging.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
49 #include "WM_api.h"
50 #include "WM_types.h"
51
52 #include "wm.h"
53 #include "wm_subwindow.h"
54 #include "wm_draw.h"
55 #include "GPU_basic_shader.h"
56
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         gesture->userdata_free = true;   /* Free if userdata is set. */
75         
76         wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy);
77         
78         if (ELEM(type, WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, WM_GESTURE_TWEAK,
79                   WM_GESTURE_CIRCLE, WM_GESTURE_STRAIGHTLINE))
80         {
81                 rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new");
82                 
83                 gesture->customdata = rect;
84                 rect->xmin = event->x - sx;
85                 rect->ymin = event->y - sy;
86                 if (type == WM_GESTURE_CIRCLE) {
87 #ifdef GESTURE_MEMORY
88                         rect->xmax = circle_select_size;
89 #else
90                         rect->xmax = 25;    // XXX temp
91 #endif
92                 }
93                 else {
94                         rect->xmax = event->x - sx;
95                         rect->ymax = event->y - sy;
96                 }
97         }
98         else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) {
99                 short *lasso;
100                 gesture->points_alloc = 1024;
101                 gesture->customdata = lasso = MEM_mallocN(sizeof(short[2]) * gesture->points_alloc, "lasso points");
102                 lasso[0] = event->x - sx;
103                 lasso[1] = event->y - sy;
104                 gesture->points = 1;
105         }
106         
107         return gesture;
108 }
109
110 void WM_gesture_end(bContext *C, wmGesture *gesture)
111 {
112         wmWindow *win = CTX_wm_window(C);
113         
114         if (win->tweak == gesture)
115                 win->tweak = NULL;
116         BLI_remlink(&win->gesture, gesture);
117         MEM_freeN(gesture->customdata);
118         if (gesture->userdata && gesture->userdata_free) {
119                 MEM_freeN(gesture->userdata);
120         }
121         MEM_freeN(gesture);
122 }
123
124 void WM_gestures_remove(bContext *C)
125 {
126         wmWindow *win = CTX_wm_window(C);
127         
128         while (win->gesture.first)
129                 WM_gesture_end(C, win->gesture.first);
130 }
131
132
133 /* tweak and line gestures */
134 int wm_gesture_evaluate(wmGesture *gesture)
135 {
136         if (gesture->type == WM_GESTURE_TWEAK) {
137                 rcti *rect = gesture->customdata;
138                 int dx = BLI_rcti_size_x(rect);
139                 int dy = BLI_rcti_size_y(rect);
140                 if (abs(dx) + abs(dy) > U.tweak_threshold) {
141                         int theta = round_fl_to_int(4.0f * atan2f((float)dy, (float)dx) / (float)M_PI);
142                         int val = EVT_GESTURE_W;
143
144                         if (theta == 0) val = EVT_GESTURE_E;
145                         else if (theta == 1) val = EVT_GESTURE_NE;
146                         else if (theta == 2) val = EVT_GESTURE_N;
147                         else if (theta == 3) val = EVT_GESTURE_NW;
148                         else if (theta == -1) val = EVT_GESTURE_SE;
149                         else if (theta == -2) val = EVT_GESTURE_S;
150                         else if (theta == -3) val = EVT_GESTURE_SW;
151                         
152 #if 0
153                         /* debug */
154                         if (val == 1) printf("tweak north\n");
155                         if (val == 2) printf("tweak north-east\n");
156                         if (val == 3) printf("tweak east\n");
157                         if (val == 4) printf("tweak south-east\n");
158                         if (val == 5) printf("tweak south\n");
159                         if (val == 6) printf("tweak south-west\n");
160                         if (val == 7) printf("tweak west\n");
161                         if (val == 8) printf("tweak north-west\n");
162 #endif
163                         return val;
164                 }
165         }
166         return 0;
167 }
168
169
170 /* ******************* gesture draw ******************* */
171
172 static void wm_gesture_draw_rect(wmGesture *gt)
173 {
174         rcti *rect = (rcti *)gt->customdata;
175         
176         glEnable(GL_BLEND);
177         glColor4f(1.0, 1.0, 1.0, 0.05);
178         glBegin(GL_QUADS);
179         glVertex2s(rect->xmax, rect->ymin);
180         glVertex2s(rect->xmax, rect->ymax);
181         glVertex2s(rect->xmin, rect->ymax);
182         glVertex2s(rect->xmin, rect->ymin);
183         glEnd();
184         glDisable(GL_BLEND);
185         
186         GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR);
187         glColor3ub(96, 96, 96);
188         GPU_basic_shader_line_stipple(1, 0xCCCC);
189         sdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
190         glColor3ub(255, 255, 255);
191         GPU_basic_shader_line_stipple(1, 0x3333);
192         sdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
193         GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
194 }
195
196 static void wm_gesture_draw_line(wmGesture *gt)
197 {
198         rcti *rect = (rcti *)gt->customdata;
199         
200         GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE);
201         glColor3ub(96, 96, 96);
202         GPU_basic_shader_line_stipple(1, 0xAAAA);
203         sdrawline(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
204         glColor3ub(255, 255, 255);
205         GPU_basic_shader_line_stipple(1, 0x5555);
206         sdrawline(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
207
208         GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
209         
210 }
211
212 static void wm_gesture_draw_circle(wmGesture *gt)
213 {
214         rcti *rect = (rcti *)gt->customdata;
215
216         glTranslatef((float)rect->xmin, (float)rect->ymin, 0.0f);
217
218         glEnable(GL_BLEND);
219         glColor4f(1.0, 1.0, 1.0, 0.05);
220         glutil_draw_filled_arc(0.0, M_PI * 2.0, rect->xmax, 40);
221         glDisable(GL_BLEND);
222         
223         // for USE_GLSL works bad because of no relation between lines
224         GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR);
225         glColor3ub(96, 96, 96);
226         GPU_basic_shader_line_stipple(1, 0xAAAA);
227         glutil_draw_lined_arc(0.0, M_PI * 2.0, rect->xmax, 40);
228         glColor3ub(255, 255, 255);
229         GPU_basic_shader_line_stipple(1, 0x5555);
230         glutil_draw_lined_arc(0.0, M_PI * 2.0, rect->xmax, 40);
231         
232         GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
233         glTranslatef(-rect->xmin, -rect->ymin, 0.0f);
234         
235 }
236
237 struct LassoFillData {
238         unsigned char *px;
239         int width;
240 };
241
242 static void draw_filled_lasso_px_cb(int x, int x_end, int y, void *user_data)
243 {
244         struct LassoFillData *data = user_data;
245         unsigned char *col = &(data->px[(y * data->width) + x]);
246         memset(col, 0x10, x_end - x);
247 }
248
249 static void draw_filled_lasso(wmWindow *win, wmGesture *gt)
250 {
251         const short *lasso = (short *)gt->customdata;
252         const int tot = gt->points;
253         int (*moves)[2] = MEM_mallocN(sizeof(*moves) * (tot + 1), __func__);
254         int i;
255         rcti rect;
256         rcti rect_win;
257
258         for (i = 0; i < tot; i++, lasso += 2) {
259                 moves[i][0] = lasso[0];
260                 moves[i][1] = lasso[1];
261         }
262
263         BLI_lasso_boundbox(&rect, (const int (*)[2])moves, tot);
264
265         wm_subwindow_rect_get(win, gt->swinid, &rect_win);
266         BLI_rcti_translate(&rect, rect_win.xmin, rect_win.ymin);
267         BLI_rcti_isect(&rect_win, &rect, &rect);
268         BLI_rcti_translate(&rect, -rect_win.xmin, -rect_win.ymin);
269
270         /* highly unlikely this will fail, but could crash if (tot == 0) */
271         if (BLI_rcti_is_empty(&rect) == false) {
272                 const int w = BLI_rcti_size_x(&rect);
273                 const int h = BLI_rcti_size_y(&rect);
274                 unsigned char *pixel_buf = MEM_callocN(sizeof(*pixel_buf) * w * h, __func__);
275                 struct LassoFillData lasso_fill_data = {pixel_buf, w};
276
277                 BLI_bitmap_draw_2d_poly_v2i_n(
278                        rect.xmin, rect.ymin, rect.xmax, rect.ymax,
279                        (const int (*)[2])moves, tot,
280                        draw_filled_lasso_px_cb, &lasso_fill_data);
281
282                 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
283
284                 glColor4f(1, 1, 1, 1);
285                 glPixelTransferf(GL_RED_BIAS, 1);
286                 glPixelTransferf(GL_GREEN_BIAS, 1);
287                 glPixelTransferf(GL_BLUE_BIAS, 1);
288
289                 GPU_basic_shader_bind(GPU_SHADER_TEXTURE_2D | GPU_SHADER_USE_COLOR);
290
291                 glEnable(GL_BLEND);
292                 glaDrawPixelsTex(rect.xmin, rect.ymin, w, h, GL_ALPHA, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf);
293                 glDisable(GL_BLEND);
294
295                 GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
296
297                 glPixelTransferf(GL_RED_BIAS, 0);
298                 glPixelTransferf(GL_GREEN_BIAS, 0);
299                 glPixelTransferf(GL_BLUE_BIAS, 0);
300
301                 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
302
303                 MEM_freeN(pixel_buf);
304         }
305
306         MEM_freeN(moves);
307 }
308
309
310 static void wm_gesture_draw_lasso(wmWindow *win, wmGesture *gt, bool filled)
311 {
312         const short *lasso = (short *)gt->customdata;
313         int i;
314
315         if (filled) {
316                 draw_filled_lasso(win, gt);
317         }
318
319         // for USE_GLSL can't check this yet
320         GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR);
321         glColor3ub(96, 96, 96);
322         GPU_basic_shader_line_stipple(1, 0xAAAA);
323         glBegin(GL_LINE_STRIP);
324         for (i = 0; i < gt->points; i++, lasso += 2)
325                 glVertex2sv(lasso);
326         if (gt->type == WM_GESTURE_LASSO)
327                 glVertex2sv((short *)gt->customdata);
328         glEnd();
329         
330         glColor3ub(255, 255, 255);
331         GPU_basic_shader_line_stipple(1, 0x5555);
332         glBegin(GL_LINE_STRIP);
333         lasso = (short *)gt->customdata;
334         for (i = 0; i < gt->points; i++, lasso += 2)
335                 glVertex2sv(lasso);
336         if (gt->type == WM_GESTURE_LASSO)
337                 glVertex2sv((short *)gt->customdata);
338         glEnd();
339         
340         GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
341         
342 }
343
344 static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt)
345 {
346         rcti *rect = (rcti *)gt->customdata;
347         const int winsize_x = WM_window_pixels_x(win);
348         const int winsize_y = WM_window_pixels_y(win);
349
350         GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR);
351         glColor3ub(96, 96, 96);
352         GPU_basic_shader_line_stipple(1, 0xCCCC);
353         sdrawline(rect->xmin - winsize_x, rect->ymin, rect->xmin + winsize_x, rect->ymin);
354         sdrawline(rect->xmin, rect->ymin - winsize_y, rect->xmin, rect->ymin + winsize_y);
355         
356         glColor3ub(255, 255, 255);
357         GPU_basic_shader_line_stipple(1, 0x3333);
358         sdrawline(rect->xmin - winsize_x, rect->ymin, rect->xmin + winsize_x, rect->ymin);
359         sdrawline(rect->xmin, rect->ymin - winsize_y, rect->xmin, rect->ymin + winsize_y);
360         GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
361 }
362
363 /* called in wm_draw.c */
364 void wm_gesture_draw(wmWindow *win)
365 {
366         wmGesture *gt = (wmGesture *)win->gesture.first;
367         
368         GPU_basic_shader_line_width(1);
369         for (; gt; gt = gt->next) {
370                 /* all in subwindow space */
371                 wmSubWindowSet(win, gt->swinid);
372                 
373                 if (gt->type == WM_GESTURE_RECT)
374                         wm_gesture_draw_rect(gt);
375 //              else if (gt->type == WM_GESTURE_TWEAK)
376 //                      wm_gesture_draw_line(gt);
377                 else if (gt->type == WM_GESTURE_CIRCLE)
378                         wm_gesture_draw_circle(gt);
379                 else if (gt->type == WM_GESTURE_CROSS_RECT) {
380                         if (gt->is_active) {
381                                 wm_gesture_draw_rect(gt);
382                         }
383                         else {
384                                 wm_gesture_draw_cross(win, gt);
385                         }
386                 }
387                 else if (gt->type == WM_GESTURE_LINES)
388                         wm_gesture_draw_lasso(win, gt, false);
389                 else if (gt->type == WM_GESTURE_LASSO)
390                         wm_gesture_draw_lasso(win, gt, true);
391                 else if (gt->type == WM_GESTURE_STRAIGHTLINE)
392                         wm_gesture_draw_line(gt);
393         }
394 }
395
396 void wm_gesture_tag_redraw(bContext *C)
397 {
398         wmWindow *win = CTX_wm_window(C);
399         bScreen *screen = CTX_wm_screen(C);
400         ARegion *ar = CTX_wm_region(C);
401         
402         if (screen)
403                 screen->do_draw_gesture = true;
404
405         wm_tag_redraw_overlay(win, ar);
406 }