2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2012 Blender Foundation.
19 * All rights reserved.
22 * Contributor(s): Blender Foundation,
26 * ***** END GPL LICENSE BLOCK *****
29 /** \file blender/editors/mask/mask_draw.c
33 #include "MEM_guardedalloc.h"
35 #include "BLI_utildefines.h"
39 #include "BLI_listbase.h"
41 #include "BKE_context.h"
44 #include "DNA_mask_types.h"
45 #include "DNA_screen_types.h"
46 #include "DNA_object_types.h" /* SELECT */
47 #include "DNA_space_types.h"
50 #include "ED_mask.h" /* own include */
51 #include "ED_space_api.h"
53 #include "BIF_glutil.h"
55 #include "GPU_immediate.h"
57 #include "GPU_shader.h"
58 #include "GPU_matrix.h"
59 #include "GPU_state.h"
61 #include "UI_interface.h"
62 #include "UI_resources.h"
63 #include "UI_view2d.h"
65 #include "mask_intern.h" /* own include */
67 static void mask_spline_color_get(MaskLayer *masklay, MaskSpline *spline, const bool is_sel,
68 unsigned char r_rgb[4])
71 if (masklay->act_spline == spline) {
72 r_rgb[0] = r_rgb[1] = r_rgb[2] = 255;
76 r_rgb[1] = r_rgb[2] = 0;
81 r_rgb[1] = r_rgb[2] = 0;
87 static void mask_spline_feather_color_get(MaskLayer *UNUSED(masklay), MaskSpline *UNUSED(spline), const bool is_sel,
88 unsigned char r_rgb[4])
92 r_rgb[0] = r_rgb[2] = 0;
96 r_rgb[0] = r_rgb[2] = 0;
102 static void mask_point_undistort_pos(SpaceClip *sc, float r_co[2], const float co[2])
104 BKE_mask_coord_to_movieclip(sc->clip, &sc->user, r_co, co);
105 ED_clip_point_undistorted_pos(sc, r_co, r_co);
106 BKE_mask_coord_from_movieclip(sc->clip, &sc->user, r_co, r_co);
109 static void draw_single_handle(const MaskLayer *mask_layer, const MaskSplinePoint *point,
110 const eMaskWhichHandle which_handle, const int draw_type,
111 const float handle_size,
112 const float point_pos[2], const float handle_pos[2])
114 const BezTriple *bezt = &point->bezt;
117 if (which_handle == MASK_WHICH_HANDLE_STICK || which_handle == MASK_WHICH_HANDLE_LEFT) {
118 handle_type = bezt->h1;
121 handle_type = bezt->h2;
124 if (handle_type == HD_VECT) {
128 GPUVertFormat *format = immVertexFormat();
129 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
130 const unsigned char rgb_gray[4] = {0x60, 0x60, 0x60, 0xff};
132 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
133 immUniformColor3ubv(rgb_gray);
135 /* this could be split into its own loop */
136 if (draw_type == MASK_DT_OUTLINE) {
137 GPU_line_width(3.0f);
138 immBegin(GPU_PRIM_LINES, 2);
139 immVertex2fv(pos, point_pos);
140 immVertex2fv(pos, handle_pos);
144 switch (handle_type) {
146 immUniformThemeColor(TH_HANDLE_FREE);
149 immUniformThemeColor(TH_HANDLE_AUTO);
152 case HD_ALIGN_DOUBLESIDE:
153 immUniformThemeColor(TH_HANDLE_ALIGN);
157 GPU_line_width(1.0f);
158 immBegin(GPU_PRIM_LINES, 2);
159 immVertex2fv(pos, point_pos);
160 immVertex2fv(pos, handle_pos);
164 /* draw handle points */
165 immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA);
166 immUniform1f("size", handle_size);
167 immUniform1f("outlineWidth", 1.5f);
169 float point_color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; /* active color by default */
170 if (MASKPOINT_ISSEL_HANDLE(point, which_handle)) {
171 if (point != mask_layer->act_point) {
172 UI_GetThemeColor3fv(TH_HANDLE_VERTEX_SELECT, point_color);
176 UI_GetThemeColor3fv(TH_HANDLE_VERTEX, point_color);
179 immUniform4fv("outlineColor", point_color);
180 immUniformColor3fvAlpha(point_color, 0.25f);
182 immBegin(GPU_PRIM_POINTS, 1);
183 immVertex2fv(pos, handle_pos);
189 /* return non-zero if spline is selected */
190 static void draw_spline_points(const bContext *C, MaskLayer *masklay, MaskSpline *spline,
191 const char draw_flag, const char draw_type)
193 const bool is_spline_sel = (spline->flag & SELECT) && (masklay->restrictflag & MASK_RESTRICT_SELECT) == 0;
194 const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0;
196 unsigned char rgb_spline[4];
197 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
198 SpaceClip *sc = CTX_wm_space_clip(C);
199 bool undistort = false;
201 int tot_feather_point;
202 float (*feather_points)[2], (*fp)[2];
203 float min[2], max[2];
205 if (!spline->tot_point)
209 undistort = sc->clip && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT);
211 /* TODO, add this to sequence editor */
212 float handle_size = 2.0f * UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE) * U.pixelsize;
214 mask_spline_color_get(masklay, spline, is_spline_sel, rgb_spline);
216 GPUVertFormat *format = immVertexFormat();
217 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
219 immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
220 immUniform1f("size", 0.7f * handle_size);
223 feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
224 for (int i = 0; i < spline->tot_point; i++) {
226 /* watch it! this is intentionally not the deform array, only check for sel */
227 MaskSplinePoint *point = &spline->points[i];
229 for (int j = 0; j <= point->tot_uw; j++) {
230 float feather_point[2];
233 copy_v2_v2(feather_point, *fp);
236 mask_point_undistort_pos(sc, feather_point, feather_point);
239 sel = MASKPOINT_ISSEL_ANY(point);
242 sel = (point->uw[j - 1].flag & SELECT) != 0;
246 if (point == masklay->act_point)
247 immUniformColor3f(1.0f, 1.0f, 1.0f);
249 immUniformThemeColor(TH_HANDLE_VERTEX_SELECT);
252 immUniformThemeColor(TH_HANDLE_VERTEX);
255 immBegin(GPU_PRIM_POINTS, 1);
256 immVertex2fv(pos, feather_point);
262 MEM_freeN(feather_points);
267 GPU_line_smooth(true);
271 INIT_MINMAX2(min, max);
272 for (int i = 0; i < spline->tot_point; i++) {
274 /* watch it! this is intentionally not the deform array, only check for sel */
275 MaskSplinePoint *point = &spline->points[i];
276 MaskSplinePoint *point_deform = &points_array[i];
277 BezTriple *bezt = &point_deform->bezt;
281 copy_v2_v2(vert, bezt->vec[1]);
284 mask_point_undistort_pos(sc, vert, vert);
287 /* draw handle segment */
288 if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) {
290 BKE_mask_point_handle(point_deform, MASK_WHICH_HANDLE_STICK, handle);
292 mask_point_undistort_pos(sc, handle, handle);
294 draw_single_handle(masklay, point, MASK_WHICH_HANDLE_STICK,
295 draw_type, handle_size, vert, handle);
298 float handle_left[2], handle_right[2];
299 BKE_mask_point_handle(point_deform, MASK_WHICH_HANDLE_LEFT, handle_left);
300 BKE_mask_point_handle(point_deform, MASK_WHICH_HANDLE_RIGHT, handle_right);
302 mask_point_undistort_pos(sc, handle_left, handle_left);
303 mask_point_undistort_pos(sc, handle_left, handle_left);
305 draw_single_handle(masklay, point, MASK_WHICH_HANDLE_LEFT,
306 draw_type, handle_size, vert, handle_left);
307 draw_single_handle(masklay, point, MASK_WHICH_HANDLE_RIGHT,
308 draw_type, handle_size, vert, handle_right);
311 /* bind program in loop so it does not interfere with draw_single_handle */
312 immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
315 if (MASKPOINT_ISSEL_KNOT(point)) {
316 if (point == masklay->act_point)
317 immUniformColor3f(1.0f, 1.0f, 1.0f);
319 immUniformThemeColor(TH_HANDLE_VERTEX_SELECT);
322 immUniformThemeColor(TH_HANDLE_VERTEX);
324 immBegin(GPU_PRIM_POINTS, 1);
325 immVertex2fv(pos, vert);
330 minmax_v2v2_v2(min, max, vert);
334 GPU_line_smooth(false);
338 float x = (min[0] + max[0]) * 0.5f;
339 float y = (min[1] + max[1]) * 0.5f;
341 immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA);
342 immUniform1f("outlineWidth", 1.5f);
344 if (masklay->act_spline == spline) {
345 immUniformColor3f(1.0f, 1.0f, 1.0f);
348 immUniformColor3f(1.0f, 1.0f, 0.0f);
351 immUniform4f("outlineColor", 0.0f, 0.0f, 0.0f, 1.0f);
352 immUniform1f("size", 12.0f);
354 immBegin(GPU_PRIM_POINTS, 1);
355 immVertex2f(pos, x, y);
362 static void mask_color_active_tint(unsigned char r_rgb[4], const unsigned char rgb[4], const bool is_active)
365 r_rgb[0] = (unsigned char)((((int)(rgb[0])) + 128) / 2);
366 r_rgb[1] = (unsigned char)((((int)(rgb[1])) + 128) / 2);
367 r_rgb[2] = (unsigned char)((((int)(rgb[2])) + 128) / 2);
371 *(unsigned int *)r_rgb = *(const unsigned int *)rgb;
375 static void mask_draw_array(unsigned int pos, GPUPrimType prim_type, const float (*points)[2], unsigned int vertex_len)
377 immBegin(prim_type, vertex_len);
378 for (unsigned int i = 0; i < vertex_len; ++i) {
379 immVertex2fv(pos, points[i]);
384 static void mask_draw_curve_type(const bContext *C, MaskSpline *spline, float (*orig_points)[2], int tot_point,
385 const bool is_feather, const bool is_active,
386 const unsigned char rgb_spline[4], const char draw_type)
388 const GPUPrimType draw_method = (spline->flag & MASK_SPLINE_CYCLIC) ? GPU_PRIM_LINE_LOOP : GPU_PRIM_LINE_STRIP;
389 const unsigned char rgb_black[4] = {0x00, 0x00, 0x00, 0xff};
390 unsigned char rgb_tmp[4];
391 SpaceClip *sc = CTX_wm_space_clip(C);
392 float (*points)[2] = orig_points;
395 const bool undistort = sc->clip && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT);
398 points = MEM_callocN(2 * tot_point * sizeof(float), "undistorthed mask curve");
400 for (int i = 0; i < tot_point; i++) {
401 mask_point_undistort_pos(sc, points[i], orig_points[i]);
406 GPUVertFormat *format = immVertexFormat();
407 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
411 case MASK_DT_OUTLINE:
412 /* TODO(merwin): use fancy line shader here
413 * probably better with geometry shader (after core profile switch)
415 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
417 GPU_line_width(3.0f);
419 mask_color_active_tint(rgb_tmp, rgb_black, is_active);
420 immUniformColor4ubv(rgb_tmp);
421 mask_draw_array(pos, draw_method, points, tot_point);
423 GPU_line_width(1.0f);
425 mask_color_active_tint(rgb_tmp, rgb_spline, is_active);
426 immUniformColor4ubv(rgb_tmp);
427 mask_draw_array(pos, draw_method, points, tot_point);
434 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
435 GPU_line_width(1.0f);
437 if (draw_type == MASK_DT_BLACK) { rgb_tmp[0] = rgb_tmp[1] = rgb_tmp[2] = 0; }
438 else { rgb_tmp[0] = rgb_tmp[1] = rgb_tmp[2] = 255; }
439 /* alpha values seem too low but gl draws many points that compensate for it */
440 if (is_feather) { rgb_tmp[3] = 64; }
441 else { rgb_tmp[3] = 128; }
444 rgb_tmp[0] = (unsigned char)(((short)rgb_tmp[0] + (short)rgb_spline[0]) / 2);
445 rgb_tmp[1] = (unsigned char)(((short)rgb_tmp[1] + (short)rgb_spline[1]) / 2);
446 rgb_tmp[2] = (unsigned char)(((short)rgb_tmp[2] + (short)rgb_spline[2]) / 2);
449 mask_color_active_tint(rgb_tmp, rgb_tmp, is_active);
450 immUniformColor4ubv(rgb_tmp);
451 mask_draw_array(pos, draw_method, points, tot_point);
460 mask_color_active_tint(rgb_tmp, rgb_spline, is_active);
461 rgba_uchar_to_float(colors, rgb_tmp);
462 mask_color_active_tint(rgb_tmp, rgb_black, is_active);
463 rgba_uchar_to_float(colors + 4, rgb_tmp);
465 immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
467 float viewport_size[4];
468 GPU_viewport_size_get_f(viewport_size);
469 immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
471 immUniform1i("colors_len", 2); /* "advanced" mode */
472 immUniformArray4fv("colors", colors, 2);
473 immUniform1f("dash_width", 4.0f);
474 GPU_line_width(1.0f);
476 mask_draw_array(pos, draw_method, points, tot_point);
486 if (points != orig_points)
490 static void draw_spline_curve(const bContext *C, MaskLayer *masklay, MaskSpline *spline,
491 const char draw_flag, const char draw_type,
492 const bool is_active,
493 const int width, const int height)
495 const unsigned int resol = max_ii(BKE_mask_spline_feather_resolution(spline, width, height),
496 BKE_mask_spline_resolution(spline, width, height));
498 unsigned char rgb_tmp[4];
500 const bool is_spline_sel = (spline->flag & SELECT) && (masklay->restrictflag & MASK_RESTRICT_SELECT) == 0;
501 const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0;
502 const bool is_fill = (spline->flag & MASK_SPLINE_NOFILL) == 0;
504 unsigned int tot_diff_point;
505 float (*diff_points)[2];
507 unsigned int tot_feather_point;
508 float (*feather_points)[2];
510 diff_points = BKE_mask_spline_differentiate_with_resolution(spline, &tot_diff_point, resol);
516 GPU_line_smooth(true);
519 feather_points = BKE_mask_spline_feather_differentiated_points_with_resolution(spline, &tot_feather_point, resol, (is_fill != false));
522 mask_spline_feather_color_get(masklay, spline, is_spline_sel, rgb_tmp);
523 mask_draw_curve_type(C, spline, feather_points, tot_feather_point,
528 const float *fp = &diff_points[0][0];
529 float *fp_feather = &feather_points[0][0];
531 BLI_assert(tot_diff_point == tot_feather_point);
533 for (int i = 0; i < tot_diff_point; i++, fp += 2, fp_feather += 2) {
535 sub_v2_v2v2(tvec, fp, fp_feather);
536 add_v2_v2v2(fp_feather, fp, tvec);
540 mask_draw_curve_type(C, spline, feather_points, tot_feather_point,
545 MEM_freeN(feather_points);
547 /* draw main curve */
548 mask_spline_color_get(masklay, spline, is_spline_sel, rgb_tmp);
549 mask_draw_curve_type(C, spline, diff_points, tot_diff_point,
552 MEM_freeN(diff_points);
555 GPU_line_smooth(false);
559 static void draw_masklays(const bContext *C, Mask *mask, const char draw_flag, const char draw_type,
560 const int width, const int height)
563 GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
564 GPU_enable_program_point_size();
569 for (masklay = mask->masklayers.first, i = 0; masklay; masklay = masklay->next, i++) {
571 const bool is_active = (i == mask->masklay_act);
573 if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
577 for (spline = masklay->splines.first; spline; spline = spline->next) {
579 /* draw curve itself first... */
580 draw_spline_curve(C, masklay, spline, draw_flag, draw_type, is_active, width, height);
582 if (!(masklay->restrictflag & MASK_RESTRICT_SELECT)) {
583 /* ...and then handles over the curve so they're nicely visible */
584 draw_spline_points(C, masklay, spline, draw_flag, draw_type);
587 /* show undeform for testing */
589 void *back = spline->points_deform;
591 spline->points_deform = NULL;
592 draw_spline_curve(C, masklay, spline, draw_flag, draw_type, is_active, width, height);
593 draw_spline_points(C, masklay, spline, draw_flag, draw_type);
594 spline->points_deform = back;
599 GPU_disable_program_point_size();
603 void ED_mask_draw(const bContext *C,
604 const char draw_flag, const char draw_type)
606 ScrArea *sa = CTX_wm_area(C);
607 Mask *mask = CTX_data_edit_mask(C);
613 ED_mask_get_size(sa, &width, &height);
615 draw_masklays(C, mask, draw_flag, draw_type, width, height);
618 static float *mask_rasterize(Mask *mask, const int width, const int height)
620 MaskRasterHandle *handle;
621 float *buffer = MEM_mallocN(sizeof(float) * height * width, "rasterized mask buffer");
623 /* Initialize rasterization handle. */
624 handle = BKE_maskrasterize_handle_new();
625 BKE_maskrasterize_handle_init(handle, mask, width, height, true, true, true);
627 BKE_maskrasterize_buffer(handle, width, height, buffer);
630 BKE_maskrasterize_handle_free(handle);
635 /* sets up the opengl context.
636 * width, height are to match the values from ED_mask_get_size() */
637 void ED_mask_draw_region(
638 Mask *mask, ARegion *ar,
639 const char draw_flag, const char draw_type, const char overlay_mode,
640 /* convert directly into aspect corrected vars */
641 const int width_i, const int height_i,
642 const float aspx, const float aspy,
643 const bool do_scale_applied, const bool do_draw_cb,
644 /* optional - only used by clip */
646 /* optional - only used when do_post_draw is set or called from clip editor */
649 struct View2D *v2d = &ar->v2d;
651 /* aspect always scales vertically in movie and image spaces */
652 const float width = width_i, height = (float)height_i * (aspy / aspx);
662 /* find window pixel coordinates of origin */
663 UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &x, &y);
666 /* w = BLI_rctf_size_x(&v2d->tot); */
667 /* h = BLI_rctf_size_y(&v2d->tot); */
670 zoomx = (float)(BLI_rcti_size_x(&ar->winrct) + 1) / BLI_rctf_size_x(&ar->v2d.cur);
671 zoomy = (float)(BLI_rcti_size_y(&ar->winrct) + 1) / BLI_rctf_size_y(&ar->v2d.cur);
673 if (do_scale_applied) {
678 x += v2d->tot.xmin * zoomx;
679 y += v2d->tot.ymin * zoomy;
681 /* frame the image */
682 maxdim = max_ff(width, height);
683 if (width == height) {
686 else if (width < height) {
687 xofs = ((height - width) / -2.0f) * zoomx;
690 else { /* (width > height) */
692 yofs = ((width - height) / -2.0f) * zoomy;
695 if (draw_flag & MASK_DRAWFLAG_OVERLAY) {
696 float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
697 float *buffer = mask_rasterize(mask, width, height);
699 if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) {
700 /* More blending types could be supported in the future. */
702 GPU_blend_set_func(GPU_DST_COLOR, GPU_ZERO);
706 GPU_matrix_translate_2f(x, y);
707 GPU_matrix_scale_2f(zoomx, zoomy);
709 GPU_matrix_mul(stabmat);
711 IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
712 GPU_shader_uniform_vector(state.shader, GPU_shader_get_uniform_ensure(state.shader, "shuffle"), 4, 1, red);
713 immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GL_RED, GL_FLOAT, GL_NEAREST, buffer, 1.0f, 1.0f, NULL);
717 if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) {
724 /* apply transformation so mask editing tools will assume drawing from the
725 * origin in normalized space */
727 GPU_matrix_translate_2f(x + xofs, y + yofs);
728 GPU_matrix_scale_2f(zoomx, zoomy);
730 GPU_matrix_mul(stabmat);
732 GPU_matrix_scale_2f(maxdim, maxdim);
735 ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
739 draw_masklays(C, mask, draw_flag, draw_type, width, height);
742 ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
748 void ED_mask_draw_frames(Mask *mask, ARegion *ar, const int cfra, const int sfra, const int efra)
750 const float framelen = ar->winx / (float)(efra - sfra + 1);
752 MaskLayer *masklay = BKE_mask_layer_active(mask);
755 unsigned int num_lines = BLI_listbase_count(&masklay->splines_shapes);
758 uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
760 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
761 immUniformColor4ub(255, 175, 0, 255);
763 immBegin(GPU_PRIM_LINES, 2 * num_lines);
765 for (MaskLayerShape *masklay_shape = masklay->splines_shapes.first;
767 masklay_shape = masklay_shape->next)
769 int frame = masklay_shape->frame;
771 /* draw_keyframe(i, CFRA, sfra, framelen, 1); */
772 int height = (frame == cfra) ? 22 : 10;
773 int x = (frame - sfra) * framelen;
774 immVertex2i(pos, x, 0);
775 immVertex2i(pos, x, height);