Merge branch 'master' into blender2.8
[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
43 #include "BKE_context.h"
44
45 #include "BIF_gl.h"
46 #include "BIF_glutil.h"
47
48 #include "IMB_colormanagement.h"
49 #include "IMB_imbuf_types.h"
50
51 #include "GPU_basic_shader.h"
52 #include "GPU_immediate.h"
53 #include "GPU_matrix.h"
54
55 #include "UI_interface.h"
56
57 /* ******************************************** */
58
59 void setlinestyle(int nr)
60 {
61         if (nr == 0) {
62                 glDisable(GL_LINE_STIPPLE);
63         }
64         else {
65
66                 glEnable(GL_LINE_STIPPLE);
67                 if (U.pixelsize > 1.0f)
68                         glLineStipple(nr, 0xCCCC);
69                 else
70                         glLineStipple(nr, 0xAAAA);
71         }
72 }
73
74 /* Invert line handling */
75
76 #define GL_TOGGLE(mode, onoff)  (((onoff) ? glEnable : glDisable)(mode))
77
78 void set_inverted_drawing(int enable)
79 {
80         glLogicOp(enable ? GL_INVERT : GL_COPY);
81         GL_TOGGLE(GL_COLOR_LOGIC_OP, enable);
82         GL_TOGGLE(GL_DITHER, !enable);
83 }
84
85 float glaGetOneFloat(int param)
86 {
87         GLfloat v;
88         glGetFloatv(param, &v);
89         return v;
90 }
91
92 int glaGetOneInt(int param)
93 {
94         GLint v;
95         glGetIntegerv(param, &v);
96         return v;
97 }
98
99 void glaRasterPosSafe2f(float x, float y, float known_good_x, float known_good_y)
100 {
101         GLubyte dummy = 0;
102
103         /* As long as known good coordinates are correct
104          * this is guaranteed to generate an ok raster
105          * position (ignoring potential (real) overflow
106          * issues).
107          */
108         glRasterPos2f(known_good_x, known_good_y);
109
110         /* Now shift the raster position to where we wanted
111          * it in the first place using the glBitmap trick.
112          */
113         glBitmap(0, 0, 0, 0, x - known_good_x, y - known_good_y, &dummy);
114 }
115
116 static int get_cached_work_texture(int *r_w, int *r_h)
117 {
118         static GLint texid = -1;
119         static int tex_w = 256;
120         static int tex_h = 256;
121
122         if (texid == -1) {
123                 glGenTextures(1, (GLuint *)&texid);
124
125                 glBindTexture(GL_TEXTURE_2D, texid);
126
127                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
128                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
129
130                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
131
132                 glBindTexture(GL_TEXTURE_2D, 0);
133         }
134
135         *r_w = tex_w;
136         *r_h = tex_h;
137         return texid;
138 }
139
140 static void immDrawPixelsTexSetupAttributes(IMMDrawPixelsTexState *state)
141 {
142         Gwn_VertFormat *vert_format = immVertexFormat();
143         state->pos = GWN_vertformat_attr_add(vert_format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
144         state->texco = GWN_vertformat_attr_add(vert_format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
145 }
146
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);
151  * */
152 IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin)
153 {
154         IMMDrawPixelsTexState state;
155         immDrawPixelsTexSetupAttributes(&state);
156
157         state.shader = GPU_shader_get_builtin_shader(builtin);
158
159         /* Shader will be unbind by immUnbindProgram in immDrawPixelsTexScaled_clipping */
160         immBindBuiltinProgram(builtin);
161         immUniform1i("image", 0);
162         state.do_shader_unbind = true;
163
164         return state;
165 }
166
167 /* Use the currently bound shader.
168  *
169  * Use immDrawPixelsTexSetup to bind the shader you
170  * want before calling immDrawPixelsTex.
171  *
172  * If using a special shader double check it uses the same
173  * attributes "pos" "texCoord" and uniform "image".
174  *
175  * If color is NULL then use white by default
176  *
177  * Be also aware that this function unbinds the shader when
178  * it's finished.
179  * */
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])
187 {
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);
193         int components;
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};
196
197         GLint unpack_row_length;
198         glGetIntegerv(GL_UNPACK_ROW_LENGTH, &unpack_row_length);
199
200         glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w);
201         glActiveTexture(GL_TEXTURE0);
202         glBindTexture(GL_TEXTURE_2D, texid);
203
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);
208
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;
211
212         offset_x = tex_w - seamless;
213         offset_y = tex_h - seamless;
214
215         nsubparts_x = (img_w + (offset_x - 1)) / (offset_x);
216         nsubparts_y = (img_h + (offset_y - 1)) / (offset_y);
217
218         if (format == GL_RGBA)
219                 components = 4;
220         else if (format == GL_RGB)
221                 components = 3;
222         else if (format == GL_RED)
223                 components = 1;
224         else {
225                 BLI_assert(!"Incompatible format passed to glaDrawPixelsTexScaled");
226                 return;
227         }
228
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);
232         }
233         else {
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);
236         }
237
238         unsigned int pos = state->pos, texco = state->texco;
239
240         /* optional */
241         /* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since
242          * it does not need color.
243          */
244         if (state->shader != NULL && GPU_shader_get_uniform(state->shader, "color") != -1) {
245                 immUniformColor4fv((color) ? color : white);
246         }
247
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)
262                                 continue;
263
264                         if (use_clipping) {
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)
267                                 {
268                                         continue;
269                                 }
270                                 if (rast_x + (float)offset_left * xzoom > clip_max_x ||
271                                     rast_y + (float)offset_bot * yzoom > clip_max_y)
272                                 {
273                                         continue;
274                                 }
275                         }
276
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]);
279
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]);
287                         }
288                         else {
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]);
290
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]);
297                         }
298
299                         immBegin(GWN_PRIM_TRI_FAN, 4);
300                         immAttrib2f(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);
302
303                         immAttrib2f(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);
305
306                         immAttrib2f(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);
308
309                         immAttrib2f(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);
311                         immEnd();
312                 }
313         }
314
315         if (state->do_shader_unbind) {
316                 immUnbindProgram();
317         }
318
319         glBindTexture(GL_TEXTURE_2D, 0);
320         glPixelStorei(GL_UNPACK_ROW_LENGTH, unpack_row_length);
321 }
322
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])
327 {
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);
330 }
331
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])
335 {
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);
338 }
339
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])
345 {
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);
348 }
349
350 /* *************** glPolygonOffset hack ************* */
351
352 /**
353  * \note \a viewdist is only for ortho at the moment.
354  */
355 void bglPolygonOffset(float viewdist, float dist)
356 {
357         static float winmat[16], offset = 0.0f;
358
359         if (dist != 0.0f) {
360                 float offs;
361
362                 // glEnable(GL_POLYGON_OFFSET_FILL);
363                 // glPolygonOffset(-1.0, -1.0);
364
365                 /* hack below is to mimic polygon offset */
366                 gpuGetProjectionMatrix(winmat);
367
368                 /* dist is from camera to center point */
369
370                 if (winmat[15] > 0.5f) {
371 #if 1
372                         offs = 0.00001f * dist * viewdist;  // ortho tweaking
373 #else
374                         static float depth_fac = 0.0f;
375                         if (depth_fac == 0.0f) {
376                                 int depthbits;
377                                 glGetIntegerv(GL_DEPTH_BITS, &depthbits);
378                                 depth_fac = 1.0f / (float)((1 << depthbits) - 1);
379                         }
380                         offs = (-1.0 / winmat[10]) * dist * depth_fac;
381
382                         UNUSED_VARS(viewdist);
383 #endif
384                 }
385                 else {
386                         /* This adjustment effectively results in reducing the Z value by 0.25%.
387                          *
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.
391                          */
392                         offs = winmat[14] * -0.0025f * dist;
393                 }
394
395                 winmat[14] -= offs;
396                 offset += offs;
397         }
398         else {
399                 winmat[14] += offset;
400                 offset = 0.0;
401         }
402
403         gpuLoadProjectionMatrix(winmat);
404 }
405
406 /* **** Color management helper functions for GLSL display/transform ***** */
407
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)
415 {
416         bool force_fallback = false;
417         bool need_fallback = true;
418
419         /* Early out */
420         if (ibuf->rect == NULL && ibuf->rect_float == NULL)
421                 return;
422
423         /* Single channel images could not be transformed using GLSL yet */
424         force_fallback |= ibuf->channels == 1;
425
426         /* If user decided not to use GLSL, fallback to glaDrawPixelsAuto */
427         force_fallback |= (U.image_draw_method != IMAGE_DRAW_METHOD_GLSL);
428
429         /* Try to draw buffer using GLSL display transform */
430         if (force_fallback == false) {
431                 int ok;
432
433                 IMMDrawPixelsTexState state = {0};
434                 /* We want GLSL state to be fully handled by OCIO. */
435                 state.do_shader_unbind = false;
436                 immDrawPixelsTexSetupAttributes(&state);
437
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,
442                                                                                     ibuf->dither, true);
443                         }
444                         else {
445                                 ok = IMB_colormanagement_setup_glsl_draw(view_settings, display_settings,
446                                                                          ibuf->dither, true);
447                         }
448                 }
449                 else {
450                         ok = IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings,
451                                                                             ibuf->rect_colorspace,
452                                                                             ibuf->dither, false);
453                 }
454
455                 if (ok) {
456                         if (ibuf->rect_float) {
457                                 int format = 0;
458
459                                 if (ibuf->channels == 3)
460                                         format = GL_RGB;
461                                 else if (ibuf->channels == 4)
462                                         format = GL_RGBA;
463                                 else
464                                         BLI_assert(!"Incompatible number of channels for GLSL display");
465
466                                 if (format != 0) {
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);
472                                 }
473                         }
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);
481                         }
482
483                         IMB_colormanagement_finish_glsl_draw();
484
485                         need_fallback = false;
486                 }
487         }
488
489         /* In case GLSL failed or not usable, fallback to glaDrawPixelsAuto */
490         if (need_fallback) {
491                 unsigned char *display_buffer;
492                 void *cache_handle;
493
494                 display_buffer = IMB_display_buffer_acquire(ibuf, view_settings, display_settings, &cache_handle);
495
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);
503                 }
504
505                 IMB_display_buffer_release(cache_handle);
506         }
507 }
508
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)
513 {
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);
516 }
517
518 void glaDrawImBuf_glsl_ctx_clipping(const bContext *C,
519                                     ImBuf *ibuf,
520                                     float x, float y,
521                                     int zoomfilter,
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)
525 {
526         ColorManagedViewSettings *view_settings;
527         ColorManagedDisplaySettings *display_settings;
528
529         IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
530
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,
533                                    zoom_x, zoom_y);
534 }
535
536 void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter,
537                            float zoom_x, float zoom_y)
538 {
539         glaDrawImBuf_glsl_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y);
540 }
541
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)
545 {
546         float delta_x = 4.0f * UI_DPI_FAC / zoomx;
547         float delta_y = 4.0f * UI_DPI_FAC / zoomy;
548
549         delta_x = min_ff(delta_x, border->xmax - border->xmin);
550         delta_y = min_ff(delta_y, border->ymax - border->ymin);
551
552         /* left bottom corner */
553         immBegin(GWN_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);
557         immEnd();
558
559         /* left top corner */
560         immBegin(GWN_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);
564         immEnd();
565
566         /* right bottom corner */
567         immBegin(GWN_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);
571         immEnd();
572
573         /* right top corner */
574         immBegin(GWN_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);
578         immEnd();
579 }