Merge remote-tracking branch 'origin/blender-v2.92-release'
[blender.git] / source / blender / editors / screen / glutil.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edscr
22  */
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "DNA_userdef_types.h"
28 #include "DNA_vec_types.h"
29
30 #include "BLI_math.h"
31 #include "BLI_utildefines.h"
32
33 #include "BKE_context.h"
34
35 #include "BIF_glutil.h"
36
37 #include "IMB_colormanagement.h"
38 #include "IMB_imbuf_types.h"
39
40 #include "GPU_immediate.h"
41 #include "GPU_matrix.h"
42 #include "GPU_texture.h"
43
44 #ifdef __APPLE__
45 #  include "GPU_state.h"
46 #endif
47
48 #include "UI_interface.h"
49
50 /* ******************************************** */
51
52 static void immDrawPixelsTexSetupAttributes(IMMDrawPixelsTexState *state)
53 {
54   GPUVertFormat *vert_format = immVertexFormat();
55   state->pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
56   state->texco = GPU_vertformat_attr_add(
57       vert_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
58 }
59
60 /**
61  * To be used before calling #immDrawPixelsTex
62  * Default shader is #GPU_SHADER_2D_IMAGE_COLOR
63  * You can still set uniforms with:
64  * `GPU_shader_uniform_int(shader, GPU_shader_get_uniform(shader, "name"), 0);`
65  */
66 IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin)
67 {
68   IMMDrawPixelsTexState state;
69   immDrawPixelsTexSetupAttributes(&state);
70
71   state.shader = GPU_shader_get_builtin_shader(builtin);
72
73   /* Shader will be unbind by immUnbindProgram in immDrawPixelsTexScaled_clipping */
74   immBindBuiltinProgram(builtin);
75   immUniform1i("image", 0);
76   state.do_shader_unbind = true;
77
78   return state;
79 }
80
81 /**
82  * Use the currently bound shader.
83  *
84  * Use #immDrawPixelsTexSetup to bind the shader you
85  * want before calling #immDrawPixelsTex.
86  *
87  * If using a special shader double check it uses the same
88  * attributes "pos" "texCoord" and uniform "image".
89  *
90  * If color is NULL then use white by default
91  *
92  * Be also aware that this function unbinds the shader when
93  * it's finished.
94  */
95 void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
96                                      float x,
97                                      float y,
98                                      int img_w,
99                                      int img_h,
100                                      eGPUTextureFormat gpu_format,
101                                      bool use_filter,
102                                      void *rect,
103                                      float scaleX,
104                                      float scaleY,
105                                      float clip_min_x,
106                                      float clip_min_y,
107                                      float clip_max_x,
108                                      float clip_max_y,
109                                      float xzoom,
110                                      float yzoom,
111                                      const float color[4])
112 {
113   int subpart_x, subpart_y, tex_w = 256, tex_h = 256;
114   int seamless, offset_x, offset_y, nsubparts_x, nsubparts_y;
115   int components;
116   const bool use_clipping = ((clip_min_x < clip_max_x) && (clip_min_y < clip_max_y));
117   const float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
118
119   if (ELEM(gpu_format, GPU_RGBA8, GPU_RGBA16F)) {
120     components = 4;
121   }
122   else if (ELEM(gpu_format, GPU_RGB16F)) {
123     components = 3;
124   }
125   else if (ELEM(gpu_format, GPU_R8, GPU_R16F)) {
126     components = 1;
127   }
128   else {
129     BLI_assert(!"Incompatible format passed to immDrawPixels");
130     return;
131   }
132
133   const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F);
134   eGPUDataFormat gpu_data = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE;
135   size_t stride = components * ((use_float_data) ? sizeof(float) : sizeof(uchar));
136
137   GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", tex_w, tex_h, 1, gpu_format, NULL);
138
139   GPU_texture_filter_mode(tex, use_filter);
140   GPU_texture_wrap_mode(tex, false, true);
141
142   GPU_texture_bind(tex, 0);
143
144   /* setup seamless 2=on, 0=off */
145   seamless = ((tex_w < img_w || tex_h < img_h) && tex_w > 2 && tex_h > 2) ? 2 : 0;
146
147   offset_x = tex_w - seamless;
148   offset_y = tex_h - seamless;
149
150   nsubparts_x = (img_w + (offset_x - 1)) / (offset_x);
151   nsubparts_y = (img_h + (offset_y - 1)) / (offset_y);
152
153   /* optional */
154   /* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since
155    * it does not need color.
156    */
157   if (state->shader != NULL && GPU_shader_get_uniform(state->shader, "color") != -1) {
158     immUniformColor4fv((color) ? color : white);
159   }
160
161   GPU_unpack_row_length_set(img_w);
162
163   for (subpart_y = 0; subpart_y < nsubparts_y; subpart_y++) {
164     for (subpart_x = 0; subpart_x < nsubparts_x; subpart_x++) {
165       int remainder_x = img_w - subpart_x * offset_x;
166       int remainder_y = img_h - subpart_y * offset_y;
167       int subpart_w = (remainder_x < tex_w) ? remainder_x : tex_w;
168       int subpart_h = (remainder_y < tex_h) ? remainder_y : tex_h;
169       int offset_left = (seamless && subpart_x != 0) ? 1 : 0;
170       int offset_bot = (seamless && subpart_y != 0) ? 1 : 0;
171       int offset_right = (seamless && remainder_x > tex_w) ? 1 : 0;
172       int offset_top = (seamless && remainder_y > tex_h) ? 1 : 0;
173       float rast_x = x + subpart_x * offset_x * xzoom;
174       float rast_y = y + subpart_y * offset_y * yzoom;
175       /* check if we already got these because we always get 2 more when doing seamless */
176       if (subpart_w <= seamless || subpart_h <= seamless) {
177         continue;
178       }
179
180       int right = subpart_w - offset_right;
181       int top = subpart_h - offset_top;
182       int bottom = 0 + offset_bot;
183       int left = 0 + offset_left;
184
185       if (use_clipping) {
186         if (rast_x + right * xzoom * scaleX < clip_min_x ||
187             rast_y + top * yzoom * scaleY < clip_min_y) {
188           continue;
189         }
190         if (rast_x + left * xzoom > clip_max_x || rast_y + bottom * yzoom > clip_max_y) {
191           continue;
192         }
193       }
194
195       {
196         int src_y = subpart_y * offset_y;
197         int src_x = subpart_x * offset_x;
198
199 #define DATA(_y, _x) ((char *)rect + stride * ((size_t)(_y)*img_w + (_x)))
200         {
201           void *data = DATA(src_y, src_x);
202           GPU_texture_update_sub(tex, gpu_data, data, 0, 0, 0, subpart_w, subpart_h, 0);
203         }
204         /* Add an extra border of pixels so linear interpolation looks ok
205          * at edges of full image. */
206         if (subpart_w < tex_w) {
207           void *data = DATA(src_y, src_x + subpart_w - 1);
208           const int offset[2] = {subpart_w, 0};
209           const int extent[2] = {1, subpart_h};
210           GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0);
211         }
212         if (subpart_h < tex_h) {
213           void *data = DATA(src_y + subpart_h - 1, src_x);
214           const int offset[2] = {0, subpart_h};
215           const int extent[2] = {subpart_w, 1};
216           GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0);
217         }
218
219         if (subpart_w < tex_w && subpart_h < tex_h) {
220           void *data = DATA(src_y + subpart_h - 1, src_x + subpart_w - 1);
221           const int offset[2] = {subpart_w, subpart_h};
222           const int extent[2] = {1, 1};
223           GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0);
224         }
225 #undef DATA
226       }
227
228       uint pos = state->pos, texco = state->texco;
229
230       immBegin(GPU_PRIM_TRI_FAN, 4);
231       immAttr2f(texco, left / (float)tex_w, bottom / (float)tex_h);
232       immVertex2f(pos, rast_x + offset_left * xzoom, rast_y + offset_bot * yzoom);
233
234       immAttr2f(texco, right / (float)tex_w, bottom / (float)tex_h);
235       immVertex2f(pos, rast_x + right * xzoom * scaleX, rast_y + offset_bot * yzoom);
236
237       immAttr2f(texco, right / (float)tex_w, top / (float)tex_h);
238       immVertex2f(pos, rast_x + right * xzoom * scaleX, rast_y + top * yzoom * scaleY);
239
240       immAttr2f(texco, left / (float)tex_w, top / (float)tex_h);
241       immVertex2f(pos, rast_x + offset_left * xzoom, rast_y + top * yzoom * scaleY);
242       immEnd();
243
244       /* NOTE: Weirdly enough this is only required on macOS. Without this there is some sort of
245        * bleeding of data is happening from tiles which are drawn later on.
246        * This doesn't seem to be too slow,
247        * but still would be nice to have fast and nice solution. */
248 #ifdef __APPLE__
249       GPU_flush();
250 #endif
251     }
252   }
253
254   if (state->do_shader_unbind) {
255     immUnbindProgram();
256   }
257
258   GPU_texture_unbind(tex);
259   GPU_texture_free(tex);
260
261   /* Restore default. */
262   GPU_unpack_row_length_set(0);
263 }
264
265 void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state,
266                             float x,
267                             float y,
268                             int img_w,
269                             int img_h,
270                             eGPUTextureFormat gpu_format,
271                             bool use_filter,
272                             void *rect,
273                             float scaleX,
274                             float scaleY,
275                             float xzoom,
276                             float yzoom,
277                             const float color[4])
278 {
279   immDrawPixelsTexScaled_clipping(state,
280                                   x,
281                                   y,
282                                   img_w,
283                                   img_h,
284                                   gpu_format,
285                                   use_filter,
286                                   rect,
287                                   scaleX,
288                                   scaleY,
289                                   0.0f,
290                                   0.0f,
291                                   0.0f,
292                                   0.0f,
293                                   xzoom,
294                                   yzoom,
295                                   color);
296 }
297
298 void immDrawPixelsTex(IMMDrawPixelsTexState *state,
299                       float x,
300                       float y,
301                       int img_w,
302                       int img_h,
303                       eGPUTextureFormat gpu_format,
304                       bool use_filter,
305                       void *rect,
306                       float xzoom,
307                       float yzoom,
308                       const float color[4])
309 {
310   immDrawPixelsTexScaled_clipping(state,
311                                   x,
312                                   y,
313                                   img_w,
314                                   img_h,
315                                   gpu_format,
316                                   use_filter,
317                                   rect,
318                                   1.0f,
319                                   1.0f,
320                                   0.0f,
321                                   0.0f,
322                                   0.0f,
323                                   0.0f,
324                                   xzoom,
325                                   yzoom,
326                                   color);
327 }
328
329 void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state,
330                                float x,
331                                float y,
332                                int img_w,
333                                int img_h,
334                                eGPUTextureFormat gpu_format,
335                                bool use_filter,
336                                void *rect,
337                                float clip_min_x,
338                                float clip_min_y,
339                                float clip_max_x,
340                                float clip_max_y,
341                                float xzoom,
342                                float yzoom,
343                                const float color[4])
344 {
345   immDrawPixelsTexScaled_clipping(state,
346                                   x,
347                                   y,
348                                   img_w,
349                                   img_h,
350                                   gpu_format,
351                                   use_filter,
352                                   rect,
353                                   1.0f,
354                                   1.0f,
355                                   clip_min_x,
356                                   clip_min_y,
357                                   clip_max_x,
358                                   clip_max_y,
359                                   xzoom,
360                                   yzoom,
361                                   color);
362 }
363
364 /* **** Color management helper functions for GLSL display/transform ***** */
365
366 /* Draw given image buffer on a screen using GLSL for display transform */
367 void ED_draw_imbuf_clipping(ImBuf *ibuf,
368                             float x,
369                             float y,
370                             bool use_filter,
371                             ColorManagedViewSettings *view_settings,
372                             ColorManagedDisplaySettings *display_settings,
373                             float clip_min_x,
374                             float clip_min_y,
375                             float clip_max_x,
376                             float clip_max_y,
377                             float zoom_x,
378                             float zoom_y)
379 {
380   bool force_fallback = false;
381   bool need_fallback = true;
382
383   /* Early out */
384   if (ibuf->rect == NULL && ibuf->rect_float == NULL) {
385     return;
386   }
387
388   /* Single channel images could not be transformed using GLSL yet */
389   force_fallback |= ibuf->channels == 1;
390
391   /* If user decided not to use GLSL, fallback to glaDrawPixelsAuto */
392   force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL);
393
394   /* Try to draw buffer using GLSL display transform */
395   if (force_fallback == false) {
396     int ok;
397
398     IMMDrawPixelsTexState state = {0};
399     /* We want GLSL state to be fully handled by OCIO. */
400     state.do_shader_unbind = false;
401     immDrawPixelsTexSetupAttributes(&state);
402
403     if (ibuf->rect_float) {
404       if (ibuf->float_colorspace) {
405         ok = IMB_colormanagement_setup_glsl_draw_from_space(
406             view_settings, display_settings, ibuf->float_colorspace, ibuf->dither, true, false);
407       }
408       else {
409         ok = IMB_colormanagement_setup_glsl_draw(
410             view_settings, display_settings, ibuf->dither, true);
411       }
412     }
413     else {
414       ok = IMB_colormanagement_setup_glsl_draw_from_space(
415           view_settings, display_settings, ibuf->rect_colorspace, ibuf->dither, false, false);
416     }
417
418     if (ok) {
419       if (ibuf->rect_float) {
420         eGPUTextureFormat format = 0;
421
422         if (ibuf->channels == 3) {
423           format = GPU_RGB16F;
424         }
425         else if (ibuf->channels == 4) {
426           format = GPU_RGBA16F;
427         }
428         else {
429           BLI_assert(!"Incompatible number of channels for GLSL display");
430         }
431
432         if (format != 0) {
433           immDrawPixelsTex_clipping(&state,
434                                     x,
435                                     y,
436                                     ibuf->x,
437                                     ibuf->y,
438                                     format,
439                                     use_filter,
440                                     ibuf->rect_float,
441                                     clip_min_x,
442                                     clip_min_y,
443                                     clip_max_x,
444                                     clip_max_y,
445                                     zoom_x,
446                                     zoom_y,
447                                     NULL);
448         }
449       }
450       else if (ibuf->rect) {
451         /* ibuf->rect is always RGBA */
452         immDrawPixelsTex_clipping(&state,
453                                   x,
454                                   y,
455                                   ibuf->x,
456                                   ibuf->y,
457                                   GPU_RGBA8,
458                                   use_filter,
459                                   ibuf->rect,
460                                   clip_min_x,
461                                   clip_min_y,
462                                   clip_max_x,
463                                   clip_max_y,
464                                   zoom_x,
465                                   zoom_y,
466                                   NULL);
467       }
468
469       IMB_colormanagement_finish_glsl_draw();
470
471       need_fallback = false;
472     }
473   }
474
475   /* In case GLSL failed or not usable, fallback to glaDrawPixelsAuto */
476   if (need_fallback) {
477     uchar *display_buffer;
478     void *cache_handle;
479
480     display_buffer = IMB_display_buffer_acquire(
481         ibuf, view_settings, display_settings, &cache_handle);
482
483     if (display_buffer) {
484       IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
485       immDrawPixelsTex_clipping(&state,
486                                 x,
487                                 y,
488                                 ibuf->x,
489                                 ibuf->y,
490                                 GPU_RGBA8,
491                                 use_filter,
492                                 display_buffer,
493                                 clip_min_x,
494                                 clip_min_y,
495                                 clip_max_x,
496                                 clip_max_y,
497                                 zoom_x,
498                                 zoom_y,
499                                 NULL);
500     }
501
502     IMB_display_buffer_release(cache_handle);
503   }
504 }
505
506 void ED_draw_imbuf(ImBuf *ibuf,
507                    float x,
508                    float y,
509                    bool use_filter,
510                    ColorManagedViewSettings *view_settings,
511                    ColorManagedDisplaySettings *display_settings,
512                    float zoom_x,
513                    float zoom_y)
514 {
515   ED_draw_imbuf_clipping(ibuf,
516                          x,
517                          y,
518                          use_filter,
519                          view_settings,
520                          display_settings,
521                          0.0f,
522                          0.0f,
523                          0.0f,
524                          0.0f,
525                          zoom_x,
526                          zoom_y);
527 }
528
529 void ED_draw_imbuf_ctx_clipping(const bContext *C,
530                                 ImBuf *ibuf,
531                                 float x,
532                                 float y,
533                                 bool use_filter,
534                                 float clip_min_x,
535                                 float clip_min_y,
536                                 float clip_max_x,
537                                 float clip_max_y,
538                                 float zoom_x,
539                                 float zoom_y)
540 {
541   ColorManagedViewSettings *view_settings;
542   ColorManagedDisplaySettings *display_settings;
543
544   IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
545
546   ED_draw_imbuf_clipping(ibuf,
547                          x,
548                          y,
549                          use_filter,
550                          view_settings,
551                          display_settings,
552                          clip_min_x,
553                          clip_min_y,
554                          clip_max_x,
555                          clip_max_y,
556                          zoom_x,
557                          zoom_y);
558 }
559
560 void ED_draw_imbuf_ctx(
561     const bContext *C, ImBuf *ibuf, float x, float y, bool use_filter, float zoom_x, float zoom_y)
562 {
563   ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, use_filter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y);
564 }
565
566 int ED_draw_imbuf_method(ImBuf *ibuf)
567 {
568   if (U.image_draw_method == IMAGE_DRAW_METHOD_AUTO) {
569     /* Use faster GLSL when CPU to GPU transfer is unlikely to be a bottleneck,
570      * otherwise do color management on CPU side. */
571     const size_t threshold = sizeof(float[4]) * 2048 * 2048;
572     const size_t data_size = (ibuf->rect_float) ? sizeof(float) : sizeof(uchar);
573     const size_t size = ibuf->x * ibuf->y * ibuf->channels * data_size;
574
575     return (size > threshold) ? IMAGE_DRAW_METHOD_2DTEXTURE : IMAGE_DRAW_METHOD_GLSL;
576   }
577   return U.image_draw_method;
578 }
579
580 /* don't move to GPU_immediate_util.h because this uses user-prefs
581  * and isn't very low level */
582 void immDrawBorderCorners(uint pos, const rcti *border, float zoomx, float zoomy)
583 {
584   float delta_x = 4.0f * UI_DPI_FAC / zoomx;
585   float delta_y = 4.0f * UI_DPI_FAC / zoomy;
586
587   delta_x = min_ff(delta_x, border->xmax - border->xmin);
588   delta_y = min_ff(delta_y, border->ymax - border->ymin);
589
590   /* left bottom corner */
591   immBegin(GPU_PRIM_LINE_STRIP, 3);
592   immVertex2f(pos, border->xmin, border->ymin + delta_y);
593   immVertex2f(pos, border->xmin, border->ymin);
594   immVertex2f(pos, border->xmin + delta_x, border->ymin);
595   immEnd();
596
597   /* left top corner */
598   immBegin(GPU_PRIM_LINE_STRIP, 3);
599   immVertex2f(pos, border->xmin, border->ymax - delta_y);
600   immVertex2f(pos, border->xmin, border->ymax);
601   immVertex2f(pos, border->xmin + delta_x, border->ymax);
602   immEnd();
603
604   /* right bottom corner */
605   immBegin(GPU_PRIM_LINE_STRIP, 3);
606   immVertex2f(pos, border->xmax - delta_x, border->ymin);
607   immVertex2f(pos, border->xmax, border->ymin);
608   immVertex2f(pos, border->xmax, border->ymin + delta_y);
609   immEnd();
610
611   /* right top corner */
612   immBegin(GPU_PRIM_LINE_STRIP, 3);
613   immVertex2f(pos, border->xmax - delta_x, border->ymax);
614   immVertex2f(pos, border->xmax, border->ymax);
615   immVertex2f(pos, border->xmax, border->ymax - delta_y);
616   immEnd();
617 }