Make image drawing code working with core profile
[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         VertexFormat *vert_format = immVertexFormat();
143         unsigned int pos = VertexFormat_add_attrib(vert_format, "pos", COMP_F32, 2, KEEP_FLOAT);
144         unsigned int texco = VertexFormat_add_attrib(vert_format, "texCoord", COMP_F32, 2, KEEP_FLOAT);
145         state->pos = pos;
146         state->texco = texco;
147 }
148
149 /* To be used before calling immDrawPixelsTex
150  * Default shader is GPU_SHADER_2D_IMAGE_COLOR
151  * You can still set uniforms with :
152  * GPU_shader_uniform_int(shader, GPU_shader_get_uniform(shader, "name"), 0);
153  * */
154 IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin)
155 {
156         IMMDrawPixelsTexState state;
157         immDrawPixelsTexSetupAttributes(&state);
158
159         GPUShader *shader = GPU_shader_get_builtin_shader(builtin);
160         /* Shader will be unbind by immUnbindProgram in immDrawPixelsTexScaled_clipping */
161         immBindBuiltinProgram(builtin);
162         GPU_shader_uniform_int(shader, GPU_shader_get_uniform(shader, "image"), 0);
163
164         state.shader = shader;
165
166         return state;
167 }
168
169 /* Use the currently bound shader.
170  *
171  * Use immDrawPixelsTexSetup to bind the shader you
172  * want before calling immDrawPixelsTex.
173  *
174  * If using a special shader double check it uses the same
175  * attributes "pos" "texCoord" and uniform "image".
176  *
177  * If color is NULL then use white by default
178  *
179  * Be also aware that this function unbinds the shader when
180  * it's finished.
181  * */
182 void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
183                                      float x, float y, int img_w, int img_h,
184                                      int format, int type, int zoomfilter, void *rect,
185                                      float scaleX, float scaleY,
186                                      float clip_min_x, float clip_min_y,
187                                      float clip_max_x, float clip_max_y,
188                                      float xzoom, float yzoom, float color[4])
189 {
190         unsigned char *uc_rect = (unsigned char *) rect;
191         const float *f_rect = (float *)rect;
192         int subpart_x, subpart_y, tex_w, tex_h;
193         int seamless, offset_x, offset_y, nsubparts_x, nsubparts_y;
194         int texid = get_cached_work_texture(&tex_w, &tex_h);
195         int components;
196         const bool use_clipping = ((clip_min_x < clip_max_x) && (clip_min_y < clip_max_y));
197         float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
198
199         GLint unpack_row_length;
200         glGetIntegerv(GL_UNPACK_ROW_LENGTH, &unpack_row_length);
201
202         glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w);
203         glBindTexture(GL_TEXTURE_2D, texid);
204
205         /* don't want nasty border artifacts */
206         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
207         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
208         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, zoomfilter);
209
210         /* setup seamless 2=on, 0=off */
211         seamless = ((tex_w < img_w || tex_h < img_h) && tex_w > 2 && tex_h > 2) ? 2 : 0;
212
213         offset_x = tex_w - seamless;
214         offset_y = tex_h - seamless;
215
216         nsubparts_x = (img_w + (offset_x - 1)) / (offset_x);
217         nsubparts_y = (img_h + (offset_y - 1)) / (offset_y);
218
219         if (format == GL_RGBA)
220                 components = 4;
221         else if (format == GL_RGB)
222                 components = 3;
223         else if (format == GL_RED)
224                 components = 1;
225         else {
226                 BLI_assert(!"Incompatible format passed to glaDrawPixelsTexScaled");
227                 return;
228         }
229
230         if (type == GL_FLOAT) {
231                 /* need to set internal format to higher range float */
232
233                 /* NOTE: this could fail on some drivers, like mesa,
234                  *       but currently this code is only used by color
235                  *       management stuff which already checks on whether
236                  *       it's possible to use GL_RGBA16F_ARB
237                  */
238
239                 /* TODO viewport : remove extension */
240                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, tex_w, tex_h, 0, format, GL_FLOAT, NULL);
241         }
242         else {
243                 /* switch to 8bit RGBA for byte buffer */
244                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, format, GL_UNSIGNED_BYTE, NULL);
245         }
246
247         unsigned int pos = 0, texco = 1;
248
249         /* optional */
250         /* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since
251          * it does not need color.
252          */
253         if (state->shader != NULL && GPU_shader_get_uniform(state->shader, "color") != -1) {
254                 immUniform4fv("color", (color) ? color : white);
255         }
256
257         for (subpart_y = 0; subpart_y < nsubparts_y; subpart_y++) {
258                 for (subpart_x = 0; subpart_x < nsubparts_x; subpart_x++) {
259                         int remainder_x = img_w - subpart_x * offset_x;
260                         int remainder_y = img_h - subpart_y * offset_y;
261                         int subpart_w = (remainder_x < tex_w) ? remainder_x : tex_w;
262                         int subpart_h = (remainder_y < tex_h) ? remainder_y : tex_h;
263                         int offset_left = (seamless && subpart_x != 0) ? 1 : 0;
264                         int offset_bot = (seamless && subpart_y != 0) ? 1 : 0;
265                         int offset_right = (seamless && remainder_x > tex_w) ? 1 : 0;
266                         int offset_top = (seamless && remainder_y > tex_h) ? 1 : 0;
267                         float rast_x = x + subpart_x * offset_x * xzoom;
268                         float rast_y = y + subpart_y * offset_y * yzoom;
269                         /* check if we already got these because we always get 2 more when doing seamless */
270                         if (subpart_w <= seamless || subpart_h <= seamless)
271                                 continue;
272
273                         if (use_clipping) {
274                                 if (rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX < clip_min_x ||
275                                     rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY < clip_min_y)
276                                 {
277                                         continue;
278                                 }
279                                 if (rast_x + (float)offset_left * xzoom > clip_max_x ||
280                                     rast_y + (float)offset_bot * yzoom > clip_max_y)
281                                 {
282                                         continue;
283                                 }
284                         }
285
286                         if (type == GL_FLOAT) {
287                                 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]);
288
289                                 /* add an extra border of pixels so linear looks ok at edges of full image */
290                                 if (subpart_w < tex_w)
291                                         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]);
292                                 if (subpart_h < tex_h)
293                                         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]);
294                                 if (subpart_w < tex_w && subpart_h < tex_h)
295                                         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]);
296                         }
297                         else {
298                                 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]);
299
300                                 if (subpart_w < tex_w)
301                                         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]);
302                                 if (subpart_h < tex_h)
303                                         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]);
304                                 if (subpart_w < tex_w && subpart_h < tex_h)
305                                         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]);
306                         }
307
308                         immBegin(PRIM_TRIANGLE_FAN, 4);
309                         immAttrib2f(texco, (float)(0 + offset_left) / tex_w, (float)(0 + offset_bot) / tex_h);
310                         immVertex2f(pos, rast_x + (float)offset_left * xzoom, rast_y + (float)offset_bot * yzoom);
311
312                         immAttrib2f(texco, (float)(subpart_w - offset_right) / tex_w, (float)(0 + offset_bot) / tex_h);
313                         immVertex2f(pos, rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX, rast_y + (float)offset_bot * yzoom);
314
315                         immAttrib2f(texco, (float)(subpart_w - offset_right) / tex_w, (float)(subpart_h - offset_top) / tex_h);
316                         immVertex2f(pos, rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX, rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY);
317
318                         immAttrib2f(texco, (float)(0 + offset_left) / tex_w, (float)(subpart_h - offset_top) / tex_h);
319                         immVertex2f(pos, rast_x + (float)offset_left * xzoom, rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY);
320                         immEnd();
321                 }
322         }
323
324         immUnbindProgram();
325
326         glBindTexture(GL_TEXTURE_2D, 0);
327         glPixelStorei(GL_UNPACK_ROW_LENGTH, unpack_row_length);
328 }
329
330 void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state,
331                             float x, float y, int img_w, int img_h,
332                             int format, int type, int zoomfilter, void *rect,
333                             float scaleX, float scaleY, float xzoom, float yzoom, float color[4])
334 {
335         immDrawPixelsTexScaled_clipping(state, x, y, img_w, img_h, format, type, zoomfilter, rect,
336                                         scaleX, scaleY, 0.0f, 0.0f, 0.0f, 0.0f, xzoom, yzoom, color);
337 }
338
339 void immDrawPixelsTex(IMMDrawPixelsTexState *state,
340                       float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect,
341                       float xzoom, float yzoom, float color[4])
342 {
343         immDrawPixelsTexScaled_clipping(state, x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f,
344                                         0.0f, 0.0f, 0.0f, 0.0f, xzoom, yzoom, color);
345 }
346
347 void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state,
348                                float x, float y, int img_w, int img_h,
349                                int format, int type, int zoomfilter, void *rect,
350                                float clip_min_x, float clip_min_y, float clip_max_x, float clip_max_y,
351                                float xzoom, float yzoom, float color[4])
352 {
353         immDrawPixelsTexScaled_clipping(state, x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f,
354                                         clip_min_x, clip_min_y, clip_max_x, clip_max_y, xzoom, yzoom, color);
355 }
356
357 /* 2D Drawing Assistance */
358
359 void glaDefine2DArea(rcti *screen_rect)
360 {
361         const int sc_w = BLI_rcti_size_x(screen_rect) + 1;
362         const int sc_h = BLI_rcti_size_y(screen_rect) + 1;
363
364         glViewport(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h);
365         glScissor(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h);
366
367         /* The GLA_PIXEL_OFS magic number is to shift the matrix so that
368          * both raster and vertex integer coordinates fall at pixel
369          * centers properly. For a longer discussion see the OpenGL
370          * Programming Guide, Appendix H, Correctness Tips.
371          */
372
373 #if 1 /* new style */
374         gpuOrtho2D(GLA_PIXEL_OFS, sc_w + GLA_PIXEL_OFS, GLA_PIXEL_OFS, sc_h + GLA_PIXEL_OFS);
375         gpuLoadIdentity();
376 #else /* original */
377         glMatrixMode(GL_PROJECTION);
378         gpuLoadIdentity();
379         glOrtho(0.0, sc_w, 0.0, sc_h, -1, 1);
380         gpuTranslate2f(GLA_PIXEL_OFS, GLA_PIXEL_OFS);
381
382         glMatrixMode(GL_MODELVIEW);
383         gpuLoadIdentity();
384 #endif
385 }
386
387 /* TODO(merwin): put the following 2D code to use, or build new 2D code inspired & informd by it */
388
389 #if 0 /* UNUSED */
390
391 struct gla2DDrawInfo {
392         int orig_vp[4], orig_sc[4];
393         float orig_projmat[16], orig_viewmat[16];
394
395         rcti screen_rect;
396         rctf world_rect;
397
398         float wo_to_sc[2];
399 };
400
401 void gla2DGetMap(gla2DDrawInfo *di, rctf *rect) 
402 {
403         *rect = di->world_rect;
404 }
405
406 void gla2DSetMap(gla2DDrawInfo *di, rctf *rect) 
407 {
408         int sc_w, sc_h;
409         float wo_w, wo_h;
410
411         di->world_rect = *rect;
412         
413         sc_w = BLI_rcti_size_x(&di->screen_rect);
414         sc_h = BLI_rcti_size_y(&di->screen_rect);
415         wo_w = BLI_rcti_size_x(&di->world_rect);
416         wo_h = BLI_rcti_size_y(&di->world_rect);
417         
418         di->wo_to_sc[0] = sc_w / wo_w;
419         di->wo_to_sc[1] = sc_h / wo_h;
420 }
421
422 /** Save the current OpenGL state and initialize OpenGL for 2D
423  * rendering. glaEnd2DDraw should be called on the returned structure
424  * to free it and to return OpenGL to its previous state. The
425  * scissor rectangle is set to match the viewport.
426  *
427  * See glaDefine2DArea for an explanation of why this function uses integers.
428  *
429  * \param screen_rect The screen rectangle to be used for 2D drawing.
430  * \param world_rect The world rectangle that the 2D area represented
431  * by \a screen_rect is supposed to represent. If NULL it is assumed the
432  * world has a 1 to 1 mapping to the screen.
433  */
434 gla2DDrawInfo *glaBegin2DDraw(rcti *screen_rect, rctf *world_rect) 
435 {
436         gla2DDrawInfo *di = MEM_mallocN(sizeof(*di), "gla2DDrawInfo");
437         int sc_w, sc_h;
438         float wo_w, wo_h;
439
440         glGetIntegerv(GL_VIEWPORT, (GLint *)di->orig_vp);
441         glGetIntegerv(GL_SCISSOR_BOX, (GLint *)di->orig_sc);
442         gpuGetProjectionMatrix3D(di->orig_projmat);
443         gpuGetModelViewMatrix3D(di->orig_viewmat);
444
445         di->screen_rect = *screen_rect;
446         if (world_rect) {
447                 di->world_rect = *world_rect;
448         }
449         else {
450                 di->world_rect.xmin = di->screen_rect.xmin;
451                 di->world_rect.ymin = di->screen_rect.ymin;
452                 di->world_rect.xmax = di->screen_rect.xmax;
453                 di->world_rect.ymax = di->screen_rect.ymax;
454         }
455
456         sc_w = BLI_rcti_size_x(&di->screen_rect);
457         sc_h = BLI_rcti_size_y(&di->screen_rect);
458         wo_w = BLI_rcti_size_x(&di->world_rect);
459         wo_h = BLI_rcti_size_y(&di->world_rect);
460
461         di->wo_to_sc[0] = sc_w / wo_w;
462         di->wo_to_sc[1] = sc_h / wo_h;
463
464         glaDefine2DArea(&di->screen_rect);
465
466         return di;
467 }
468
469 /**
470  * Translate the (\a wo_x, \a wo_y) point from world coordinates into screen space.
471  */
472 void gla2DDrawTranslatePt(gla2DDrawInfo *di, float wo_x, float wo_y, int *r_sc_x, int *r_sc_y)
473 {
474         *r_sc_x = (wo_x - di->world_rect.xmin) * di->wo_to_sc[0];
475         *r_sc_y = (wo_y - di->world_rect.ymin) * di->wo_to_sc[1];
476 }
477
478 /**
479  * Translate the \a world point from world coordinates into screen space.
480  */
481 void gla2DDrawTranslatePtv(gla2DDrawInfo *di, float world[2], int r_screen[2])
482 {
483         screen_r[0] = (world[0] - di->world_rect.xmin) * di->wo_to_sc[0];
484         screen_r[1] = (world[1] - di->world_rect.ymin) * di->wo_to_sc[1];
485 }
486
487 /**
488  * Restores the previous OpenGL state and frees the auxiliary gla data.
489  */
490 void glaEnd2DDraw(gla2DDrawInfo *di)
491 {
492         glViewport(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]);
493         glScissor(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]);
494         gpuLoadProjectionMatrix3D(di->orig_projmat);
495         gpuLoadMatrix3D(di->orig_viewmat);
496
497         MEM_freeN(di);
498 }
499
500 #endif /* UNUSED */
501
502
503 /* *************** glPolygonOffset hack ************* */
504
505 /**
506  * \note \a viewdist is only for ortho at the moment.
507  */
508 void bglPolygonOffset(float viewdist, float dist)
509 {
510         static float winmat[16], offset = 0.0f;
511         
512         if (dist != 0.0f) {
513                 float offs;
514                 
515                 // glEnable(GL_POLYGON_OFFSET_FILL);
516                 // glPolygonOffset(-1.0, -1.0);
517
518                 /* hack below is to mimic polygon offset */
519                 gpuGetProjectionMatrix3D((float (*)[4])winmat);
520                 
521                 /* dist is from camera to center point */
522                 
523                 if (winmat[15] > 0.5f) {
524 #if 1
525                         offs = 0.00001f * dist * viewdist;  // ortho tweaking
526 #else
527                         static float depth_fac = 0.0f;
528                         if (depth_fac == 0.0f) {
529                                 int depthbits;
530                                 glGetIntegerv(GL_DEPTH_BITS, &depthbits);
531                                 depth_fac = 1.0f / (float)((1 << depthbits) - 1);
532                         }
533                         offs = (-1.0 / winmat[10]) * dist * depth_fac;
534
535                         UNUSED_VARS(viewdist);
536 #endif
537                 }
538                 else {
539                         /* This adjustment effectively results in reducing the Z value by 0.25%.
540                          *
541                          * winmat[14] actually evaluates to `-2 * far * near / (far - near)`,
542                          * is very close to -0.2 with default clip range, and is used as the coefficient multiplied by `w / z`,
543                          * thus controlling the z dependent part of the depth value.
544                          */
545                         offs = winmat[14] * -0.0025f * dist;
546                 }
547                 
548                 winmat[14] -= offs;
549                 offset += offs;
550         }
551         else {
552                 winmat[14] += offset;
553                 offset = 0.0;
554         }
555
556         gpuLoadProjectionMatrix3D((const float (*)[4])winmat);
557 }
558
559 /* **** Color management helper functions for GLSL display/transform ***** */
560
561 /* Draw given image buffer on a screen using GLSL for display transform */
562 void glaDrawImBuf_glsl_clipping(ImBuf *ibuf, float x, float y, int zoomfilter,
563                                 ColorManagedViewSettings *view_settings,
564                                 ColorManagedDisplaySettings *display_settings,
565                                 float clip_min_x, float clip_min_y,
566                                 float clip_max_x, float clip_max_y,
567                                 float zoom_x, float zoom_y)
568 {
569         bool force_fallback = false;
570         bool need_fallback = true;
571
572         /* Early out */
573         if (ibuf->rect == NULL && ibuf->rect_float == NULL)
574                 return;
575
576         /* Single channel images could not be transformed using GLSL yet */
577         force_fallback |= ibuf->channels == 1;
578
579         /* If user decided not to use GLSL, fallback to glaDrawPixelsAuto */
580         force_fallback |= (U.image_draw_method != IMAGE_DRAW_METHOD_GLSL);
581
582         /* Try to draw buffer using GLSL display transform */
583         if (force_fallback == false) {
584                 int ok;
585
586                 IMMDrawPixelsTexState state = {0};
587                 immDrawPixelsTexSetupAttributes(&state);
588
589                 if (ibuf->rect_float) {
590                         if (ibuf->float_colorspace) {
591                                 ok = IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings,
592                                                                                     ibuf->float_colorspace,
593                                                                                     ibuf->dither, true);
594                         }
595                         else {
596                                 ok = IMB_colormanagement_setup_glsl_draw(view_settings, display_settings,
597                                                                          ibuf->dither, true);
598                         }
599                 }
600                 else {
601                         ok = IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings,
602                                                                             ibuf->rect_colorspace,
603                                                                             ibuf->dither, false);
604                 }
605
606                 if (ok) {
607                         if (ibuf->rect_float) {
608                                 int format = 0;
609
610                                 if (ibuf->channels == 3)
611                                         format = GL_RGB;
612                                 else if (ibuf->channels == 4)
613                                         format = GL_RGBA;
614                                 else
615                                         BLI_assert(!"Incompatible number of channels for GLSL display");
616
617                                 if (format != 0) {
618                                         immDrawPixelsTex_clipping(&state,
619                                                                   x, y, ibuf->x, ibuf->y, format, GL_FLOAT,
620                                                                   zoomfilter, ibuf->rect_float,
621                                                                   clip_min_x, clip_min_y, clip_max_x, clip_max_y,
622                                                                   zoom_x, zoom_y, NULL);
623                                 }
624                         }
625                         else if (ibuf->rect) {
626                                 /* ibuf->rect is always RGBA */
627                                 immDrawPixelsTex_clipping(&state,
628                                                           x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE,
629                                                           zoomfilter, ibuf->rect,
630                                                           clip_min_x, clip_min_y, clip_max_x, clip_max_y,
631                                                           zoom_x, zoom_y, NULL);
632                         }
633
634                         IMB_colormanagement_finish_glsl_draw();
635
636                         need_fallback = false;
637                 }
638         }
639
640         /* In case GLSL failed or not usable, fallback to glaDrawPixelsAuto */
641         if (need_fallback) {
642                 unsigned char *display_buffer;
643                 void *cache_handle;
644
645                 display_buffer = IMB_display_buffer_acquire(ibuf, view_settings, display_settings, &cache_handle);
646
647                 if (display_buffer) {
648                         IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
649                         immDrawPixelsTex_clipping(&state,
650                                                   x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE,
651                                                   zoomfilter, display_buffer,
652                                                   clip_min_x, clip_min_y, clip_max_x, clip_max_y,
653                                                   zoom_x, zoom_y, NULL);
654                 }
655
656                 IMB_display_buffer_release(cache_handle);
657         }
658 }
659
660 void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter,
661                        ColorManagedViewSettings *view_settings,
662                        ColorManagedDisplaySettings *display_settings,
663                        float zoom_x, float zoom_y)
664 {
665         glaDrawImBuf_glsl_clipping(ibuf, x, y, zoomfilter, view_settings, display_settings,
666                                    0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y);
667 }
668
669 void glaDrawImBuf_glsl_ctx_clipping(const bContext *C,
670                                     ImBuf *ibuf,
671                                     float x, float y,
672                                     int zoomfilter,
673                                     float clip_min_x, float clip_min_y,
674                                     float clip_max_x, float clip_max_y,
675                                     float zoom_x, float zoom_y)
676 {
677         ColorManagedViewSettings *view_settings;
678         ColorManagedDisplaySettings *display_settings;
679
680         IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
681
682         glaDrawImBuf_glsl_clipping(ibuf, x, y, zoomfilter, view_settings, display_settings,
683                                    clip_min_x, clip_min_y, clip_max_x, clip_max_y,
684                                    zoom_x, zoom_y);
685 }
686
687 void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter,
688                            float zoom_x, float zoom_y)
689 {
690         glaDrawImBuf_glsl_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y);
691 }
692
693 void cpack(unsigned int x)
694 {
695         /* DEPRECATED: use imm_cpack */
696         glColor3ub(( (x)        & 0xFF),
697                    (((x) >>  8) & 0xFF),
698                    (((x) >> 16) & 0xFF));
699 }
700
701 /* don't move to GPU_immediate_util.h because this uses user-prefs
702  * and isn't very low level */
703 void immDrawBorderCorners(unsigned int pos, const rcti *border, float zoomx, float zoomy)
704 {
705         float delta_x = 4.0f * UI_DPI_FAC / zoomx;
706         float delta_y = 4.0f * UI_DPI_FAC / zoomy;
707
708         delta_x = min_ff(delta_x, border->xmax - border->xmin);
709         delta_y = min_ff(delta_y, border->ymax - border->ymin);
710
711         /* left bottom corner */
712         immBegin(PRIM_LINE_STRIP, 3);
713         immVertex2f(pos, border->xmin, border->ymin + delta_y);
714         immVertex2f(pos, border->xmin, border->ymin);
715         immVertex2f(pos, border->xmin + delta_x, border->ymin);
716         immEnd();
717
718         /* left top corner */
719         immBegin(PRIM_LINE_STRIP, 3);
720         immVertex2f(pos, border->xmin, border->ymax - delta_y);
721         immVertex2f(pos, border->xmin, border->ymax);
722         immVertex2f(pos, border->xmin + delta_x, border->ymax);
723         immEnd();
724
725         /* right bottom corner */
726         immBegin(PRIM_LINE_STRIP, 3);
727         immVertex2f(pos, border->xmax - delta_x, border->ymin);
728         immVertex2f(pos, border->xmax, border->ymin);
729         immVertex2f(pos, border->xmax, border->ymin + delta_y);
730         immEnd();
731
732         /* right top corner */
733         immBegin(PRIM_LINE_STRIP, 3);
734         immVertex2f(pos, border->xmax - delta_x, border->ymax);
735         immVertex2f(pos, border->xmax, border->ymax);
736         immVertex2f(pos, border->xmax, border->ymax - delta_y);
737         immEnd();
738 }