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) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * Contributor(s): Blender Foundation
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/editors/screen/glutil.c
34 #include "MEM_guardedalloc.h"
36 #include "DNA_userdef_types.h"
37 #include "DNA_vec_types.h"
40 #include "BLI_utildefines.h"
43 #include "BKE_context.h"
46 #include "BIF_glutil.h"
48 #include "IMB_colormanagement.h"
49 #include "IMB_imbuf_types.h"
51 #include "GPU_immediate.h"
52 #include "GPU_matrix.h"
53 #include "GPU_state.h"
55 #include "UI_interface.h"
57 /* ******************************************** */
59 void setlinestyle(int nr)
62 GPU_line_stipple(false);
66 GPU_line_stipple(true);
67 if (U.pixelsize > 1.0f)
68 glLineStipple(nr, 0xCCCC);
70 glLineStipple(nr, 0xAAAA);
74 /* Invert line handling */
76 #define GL_TOGGLE(mode, onoff) (((onoff) ? glEnable : glDisable)(mode))
78 void set_inverted_drawing(int enable)
80 glLogicOp(enable ? GL_INVERT : GL_COPY);
81 GL_TOGGLE(GL_COLOR_LOGIC_OP, enable);
82 GL_TOGGLE(GL_DITHER, !enable);
85 float glaGetOneFloat(int param)
88 glGetFloatv(param, &v);
92 int glaGetOneInt(int param)
95 glGetIntegerv(param, &v);
99 void glaRasterPosSafe2f(float x, float y, float known_good_x, float known_good_y)
103 /* As long as known good coordinates are correct
104 * this is guaranteed to generate an ok raster
105 * position (ignoring potential (real) overflow
108 glRasterPos2f(known_good_x, known_good_y);
110 /* Now shift the raster position to where we wanted
111 * it in the first place using the glBitmap trick.
113 glBitmap(0, 0, 0, 0, x - known_good_x, y - known_good_y, &dummy);
116 static int get_cached_work_texture(int *r_w, int *r_h)
118 static GLint texid = -1;
119 static int tex_w = 256;
120 static int tex_h = 256;
123 glGenTextures(1, (GLuint *)&texid);
125 glBindTexture(GL_TEXTURE_2D, texid);
127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
128 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
130 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
132 glBindTexture(GL_TEXTURE_2D, 0);
140 static void immDrawPixelsTexSetupAttributes(IMMDrawPixelsTexState *state)
142 GPUVertFormat *vert_format = immVertexFormat();
143 state->pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
144 state->texco = GPU_vertformat_attr_add(vert_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
147 /* To be used before calling immDrawPixelsTex
148 * Default shader is GPU_SHADER_2D_IMAGE_COLOR
149 * You can still set uniforms with :
150 * GPU_shader_uniform_int(shader, GPU_shader_get_uniform(shader, "name"), 0);
152 IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin)
154 IMMDrawPixelsTexState state;
155 immDrawPixelsTexSetupAttributes(&state);
157 state.shader = GPU_shader_get_builtin_shader(builtin);
159 /* Shader will be unbind by immUnbindProgram in immDrawPixelsTexScaled_clipping */
160 immBindBuiltinProgram(builtin);
161 immUniform1i("image", 0);
162 state.do_shader_unbind = true;
167 /* Use the currently bound shader.
169 * Use immDrawPixelsTexSetup to bind the shader you
170 * want before calling immDrawPixelsTex.
172 * If using a special shader double check it uses the same
173 * attributes "pos" "texCoord" and uniform "image".
175 * If color is NULL then use white by default
177 * Be also aware that this function unbinds the shader when
180 void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
181 float x, float y, int img_w, int img_h,
182 int format, int type, int zoomfilter, void *rect,
183 float scaleX, float scaleY,
184 float clip_min_x, float clip_min_y,
185 float clip_max_x, float clip_max_y,
186 float xzoom, float yzoom, float color[4])
188 unsigned char *uc_rect = (unsigned char *) rect;
189 const float *f_rect = (float *)rect;
190 int subpart_x, subpart_y, tex_w, tex_h;
191 int seamless, offset_x, offset_y, nsubparts_x, nsubparts_y;
192 int texid = get_cached_work_texture(&tex_w, &tex_h);
194 const bool use_clipping = ((clip_min_x < clip_max_x) && (clip_min_y < clip_max_y));
195 float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
197 GLint unpack_row_length;
198 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &unpack_row_length);
200 glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w);
201 glActiveTexture(GL_TEXTURE0);
202 glBindTexture(GL_TEXTURE_2D, texid);
204 /* don't want nasty border artifacts */
205 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
207 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, zoomfilter);
209 /* setup seamless 2=on, 0=off */
210 seamless = ((tex_w < img_w || tex_h < img_h) && tex_w > 2 && tex_h > 2) ? 2 : 0;
212 offset_x = tex_w - seamless;
213 offset_y = tex_h - seamless;
215 nsubparts_x = (img_w + (offset_x - 1)) / (offset_x);
216 nsubparts_y = (img_h + (offset_y - 1)) / (offset_y);
218 if (format == GL_RGBA)
220 else if (format == GL_RGB)
222 else if (format == GL_RED)
225 BLI_assert(!"Incompatible format passed to glaDrawPixelsTexScaled");
229 if (type == GL_FLOAT) {
230 /* need to set internal format to higher range float */
231 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, tex_w, tex_h, 0, format, GL_FLOAT, NULL);
234 /* switch to 8bit RGBA for byte buffer */
235 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, format, GL_UNSIGNED_BYTE, NULL);
238 unsigned int pos = state->pos, texco = state->texco;
241 /* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since
242 * it does not need color.
244 if (state->shader != NULL && GPU_shader_get_uniform(state->shader, "color") != -1) {
245 immUniformColor4fv((color) ? color : white);
248 for (subpart_y = 0; subpart_y < nsubparts_y; subpart_y++) {
249 for (subpart_x = 0; subpart_x < nsubparts_x; subpart_x++) {
250 int remainder_x = img_w - subpart_x * offset_x;
251 int remainder_y = img_h - subpart_y * offset_y;
252 int subpart_w = (remainder_x < tex_w) ? remainder_x : tex_w;
253 int subpart_h = (remainder_y < tex_h) ? remainder_y : tex_h;
254 int offset_left = (seamless && subpart_x != 0) ? 1 : 0;
255 int offset_bot = (seamless && subpart_y != 0) ? 1 : 0;
256 int offset_right = (seamless && remainder_x > tex_w) ? 1 : 0;
257 int offset_top = (seamless && remainder_y > tex_h) ? 1 : 0;
258 float rast_x = x + subpart_x * offset_x * xzoom;
259 float rast_y = y + subpart_y * offset_y * yzoom;
260 /* check if we already got these because we always get 2 more when doing seamless */
261 if (subpart_w <= seamless || subpart_h <= seamless)
265 if (rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX < clip_min_x ||
266 rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY < clip_min_y)
270 if (rast_x + (float)offset_left * xzoom > clip_max_x ||
271 rast_y + (float)offset_bot * yzoom > clip_max_y)
277 if (type == GL_FLOAT) {
278 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_FLOAT, &f_rect[((size_t)subpart_y) * offset_y * img_w * components + subpart_x * offset_x * components]);
280 /* add an extra border of pixels so linear looks ok at edges of full image */
281 if (subpart_w < tex_w)
282 glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_FLOAT, &f_rect[((size_t)subpart_y) * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
283 if (subpart_h < tex_h)
284 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_FLOAT, &f_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]);
285 if (subpart_w < tex_w && subpart_h < tex_h)
286 glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_FLOAT, &f_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
289 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[((size_t)subpart_y) * offset_y * img_w * components + subpart_x * offset_x * components]);
291 if (subpart_w < tex_w)
292 glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[((size_t)subpart_y) * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
293 if (subpart_h < tex_h)
294 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]);
295 if (subpart_w < tex_w && subpart_h < tex_h)
296 glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
299 immBegin(GPU_PRIM_TRI_FAN, 4);
300 immAttr2f(texco, (float)(0 + offset_left) / tex_w, (float)(0 + offset_bot) / tex_h);
301 immVertex2f(pos, rast_x + (float)offset_left * xzoom, rast_y + (float)offset_bot * yzoom);
303 immAttr2f(texco, (float)(subpart_w - offset_right) / tex_w, (float)(0 + offset_bot) / tex_h);
304 immVertex2f(pos, rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX, rast_y + (float)offset_bot * yzoom);
306 immAttr2f(texco, (float)(subpart_w - offset_right) / tex_w, (float)(subpart_h - offset_top) / tex_h);
307 immVertex2f(pos, rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX, rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY);
309 immAttr2f(texco, (float)(0 + offset_left) / tex_w, (float)(subpart_h - offset_top) / tex_h);
310 immVertex2f(pos, rast_x + (float)offset_left * xzoom, rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY);
315 if (state->do_shader_unbind) {
319 glBindTexture(GL_TEXTURE_2D, 0);
320 glPixelStorei(GL_UNPACK_ROW_LENGTH, unpack_row_length);
323 void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state,
324 float x, float y, int img_w, int img_h,
325 int format, int type, int zoomfilter, void *rect,
326 float scaleX, float scaleY, float xzoom, float yzoom, float color[4])
328 immDrawPixelsTexScaled_clipping(state, x, y, img_w, img_h, format, type, zoomfilter, rect,
329 scaleX, scaleY, 0.0f, 0.0f, 0.0f, 0.0f, xzoom, yzoom, color);
332 void immDrawPixelsTex(IMMDrawPixelsTexState *state,
333 float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect,
334 float xzoom, float yzoom, float color[4])
336 immDrawPixelsTexScaled_clipping(state, x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f,
337 0.0f, 0.0f, 0.0f, 0.0f, xzoom, yzoom, color);
340 void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state,
341 float x, float y, int img_w, int img_h,
342 int format, int type, int zoomfilter, void *rect,
343 float clip_min_x, float clip_min_y, float clip_max_x, float clip_max_y,
344 float xzoom, float yzoom, float color[4])
346 immDrawPixelsTexScaled_clipping(state, x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f,
347 clip_min_x, clip_min_y, clip_max_x, clip_max_y, xzoom, yzoom, color);
350 /* *************** glPolygonOffset hack ************* */
353 * \note \a viewdist is only for ortho at the moment.
355 void bglPolygonOffset(float viewdist, float dist)
357 static float winmat[16], offset = 0.0f;
362 // glEnable(GL_POLYGON_OFFSET_FILL);
363 // glPolygonOffset(-1.0, -1.0);
365 /* hack below is to mimic polygon offset */
366 GPU_matrix_projection_get(winmat);
368 /* dist is from camera to center point */
370 if (winmat[15] > 0.5f) {
372 offs = 0.00001f * dist * viewdist; // ortho tweaking
374 static float depth_fac = 0.0f;
375 if (depth_fac == 0.0f) {
377 glGetIntegerv(GL_DEPTH_BITS, &depthbits);
378 depth_fac = 1.0f / (float)((1 << depthbits) - 1);
380 offs = (-1.0 / winmat[10]) * dist * depth_fac;
382 UNUSED_VARS(viewdist);
386 /* This adjustment effectively results in reducing the Z value by 0.25%.
388 * winmat[14] actually evaluates to `-2 * far * near / (far - near)`,
389 * is very close to -0.2 with default clip range, and is used as the coefficient multiplied by `w / z`,
390 * thus controlling the z dependent part of the depth value.
392 offs = winmat[14] * -0.0025f * dist;
399 winmat[14] += offset;
403 GPU_matrix_projection_set(winmat);
406 /* **** Color management helper functions for GLSL display/transform ***** */
408 /* Draw given image buffer on a screen using GLSL for display transform */
409 void glaDrawImBuf_glsl_clipping(ImBuf *ibuf, float x, float y, int zoomfilter,
410 ColorManagedViewSettings *view_settings,
411 ColorManagedDisplaySettings *display_settings,
412 float clip_min_x, float clip_min_y,
413 float clip_max_x, float clip_max_y,
414 float zoom_x, float zoom_y)
416 bool force_fallback = false;
417 bool need_fallback = true;
420 if (ibuf->rect == NULL && ibuf->rect_float == NULL)
423 /* Single channel images could not be transformed using GLSL yet */
424 force_fallback |= ibuf->channels == 1;
426 /* If user decided not to use GLSL, fallback to glaDrawPixelsAuto */
427 force_fallback |= (U.image_draw_method != IMAGE_DRAW_METHOD_GLSL);
429 /* Try to draw buffer using GLSL display transform */
430 if (force_fallback == false) {
433 IMMDrawPixelsTexState state = {0};
434 /* We want GLSL state to be fully handled by OCIO. */
435 state.do_shader_unbind = false;
436 immDrawPixelsTexSetupAttributes(&state);
438 if (ibuf->rect_float) {
439 if (ibuf->float_colorspace) {
440 ok = IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings,
441 ibuf->float_colorspace,
445 ok = IMB_colormanagement_setup_glsl_draw(view_settings, display_settings,
450 ok = IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings,
451 ibuf->rect_colorspace,
452 ibuf->dither, false);
456 if (ibuf->rect_float) {
459 if (ibuf->channels == 3)
461 else if (ibuf->channels == 4)
464 BLI_assert(!"Incompatible number of channels for GLSL display");
467 immDrawPixelsTex_clipping(&state,
468 x, y, ibuf->x, ibuf->y, format, GL_FLOAT,
469 zoomfilter, ibuf->rect_float,
470 clip_min_x, clip_min_y, clip_max_x, clip_max_y,
471 zoom_x, zoom_y, NULL);
474 else if (ibuf->rect) {
475 /* ibuf->rect is always RGBA */
476 immDrawPixelsTex_clipping(&state,
477 x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE,
478 zoomfilter, ibuf->rect,
479 clip_min_x, clip_min_y, clip_max_x, clip_max_y,
480 zoom_x, zoom_y, NULL);
483 IMB_colormanagement_finish_glsl_draw();
485 need_fallback = false;
489 /* In case GLSL failed or not usable, fallback to glaDrawPixelsAuto */
491 unsigned char *display_buffer;
494 display_buffer = IMB_display_buffer_acquire(ibuf, view_settings, display_settings, &cache_handle);
496 if (display_buffer) {
497 IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
498 immDrawPixelsTex_clipping(&state,
499 x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE,
500 zoomfilter, display_buffer,
501 clip_min_x, clip_min_y, clip_max_x, clip_max_y,
502 zoom_x, zoom_y, NULL);
505 IMB_display_buffer_release(cache_handle);
509 void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter,
510 ColorManagedViewSettings *view_settings,
511 ColorManagedDisplaySettings *display_settings,
512 float zoom_x, float zoom_y)
514 glaDrawImBuf_glsl_clipping(ibuf, x, y, zoomfilter, view_settings, display_settings,
515 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y);
518 void glaDrawImBuf_glsl_ctx_clipping(const bContext *C,
522 float clip_min_x, float clip_min_y,
523 float clip_max_x, float clip_max_y,
524 float zoom_x, float zoom_y)
526 ColorManagedViewSettings *view_settings;
527 ColorManagedDisplaySettings *display_settings;
529 IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
531 glaDrawImBuf_glsl_clipping(ibuf, x, y, zoomfilter, view_settings, display_settings,
532 clip_min_x, clip_min_y, clip_max_x, clip_max_y,
536 void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter,
537 float zoom_x, float zoom_y)
539 glaDrawImBuf_glsl_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y);
542 /* don't move to GPU_immediate_util.h because this uses user-prefs
543 * and isn't very low level */
544 void immDrawBorderCorners(unsigned int pos, const rcti *border, float zoomx, float zoomy)
546 float delta_x = 4.0f * UI_DPI_FAC / zoomx;
547 float delta_y = 4.0f * UI_DPI_FAC / zoomy;
549 delta_x = min_ff(delta_x, border->xmax - border->xmin);
550 delta_y = min_ff(delta_y, border->ymax - border->ymin);
552 /* left bottom corner */
553 immBegin(GPU_PRIM_LINE_STRIP, 3);
554 immVertex2f(pos, border->xmin, border->ymin + delta_y);
555 immVertex2f(pos, border->xmin, border->ymin);
556 immVertex2f(pos, border->xmin + delta_x, border->ymin);
559 /* left top corner */
560 immBegin(GPU_PRIM_LINE_STRIP, 3);
561 immVertex2f(pos, border->xmin, border->ymax - delta_y);
562 immVertex2f(pos, border->xmin, border->ymax);
563 immVertex2f(pos, border->xmin + delta_x, border->ymax);
566 /* right bottom corner */
567 immBegin(GPU_PRIM_LINE_STRIP, 3);
568 immVertex2f(pos, border->xmax - delta_x, border->ymin);
569 immVertex2f(pos, border->xmax, border->ymin);
570 immVertex2f(pos, border->xmax, border->ymin + delta_y);
573 /* right top corner */
574 immBegin(GPU_PRIM_LINE_STRIP, 3);
575 immVertex2f(pos, border->xmax - delta_x, border->ymax);
576 immVertex2f(pos, border->xmax, border->ymax);
577 immVertex2f(pos, border->xmax, border->ymax - delta_y);