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.
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.
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.
16 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17 * All rights reserved.
27 #include "DNA_userdef_types.h"
28 #include "DNA_vec_types.h"
31 #include "BLI_utildefines.h"
33 #include "BKE_context.h"
35 #include "BIF_glutil.h"
37 #include "IMB_colormanagement.h"
38 #include "IMB_imbuf_types.h"
40 #include "GPU_immediate.h"
41 #include "GPU_matrix.h"
42 #include "GPU_texture.h"
45 # include "GPU_state.h"
48 #include "UI_interface.h"
50 /* ******************************************** */
52 static void immDrawPixelsTexSetupAttributes(IMMDrawPixelsTexState *state)
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);
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);`
66 IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin)
68 IMMDrawPixelsTexState state;
69 immDrawPixelsTexSetupAttributes(&state);
71 state.shader = GPU_shader_get_builtin_shader(builtin);
73 /* Shader will be unbind by immUnbindProgram in immDrawPixelsTexScaled_clipping */
74 immBindBuiltinProgram(builtin);
75 immUniform1i("image", 0);
76 state.do_shader_unbind = true;
82 * Use the currently bound shader.
84 * Use #immDrawPixelsTexSetup to bind the shader you
85 * want before calling #immDrawPixelsTex.
87 * If using a special shader double check it uses the same
88 * attributes "pos" "texCoord" and uniform "image".
90 * If color is NULL then use white by default
92 * Be also aware that this function unbinds the shader when
95 void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
100 eGPUTextureFormat gpu_format,
111 const float color[4])
113 int subpart_x, subpart_y, tex_w = 256, tex_h = 256;
114 int seamless, offset_x, offset_y, nsubparts_x, nsubparts_y;
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};
119 if (ELEM(gpu_format, GPU_RGBA8, GPU_RGBA16F)) {
122 else if (ELEM(gpu_format, GPU_RGB16F)) {
125 else if (ELEM(gpu_format, GPU_R8, GPU_R16F)) {
129 BLI_assert(!"Incompatible format passed to immDrawPixels");
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));
137 GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", tex_w, tex_h, 1, gpu_format, NULL);
139 GPU_texture_filter_mode(tex, use_filter);
140 GPU_texture_wrap_mode(tex, false, true);
142 GPU_texture_bind(tex, 0);
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;
147 offset_x = tex_w - seamless;
148 offset_y = tex_h - seamless;
150 nsubparts_x = (img_w + (offset_x - 1)) / (offset_x);
151 nsubparts_y = (img_h + (offset_y - 1)) / (offset_y);
154 /* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since
155 * it does not need color.
157 if (state->shader != NULL && GPU_shader_get_uniform(state->shader, "color") != -1) {
158 immUniformColor4fv((color) ? color : white);
161 GPU_unpack_row_length_set(img_w);
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) {
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;
186 if (rast_x + right * xzoom * scaleX < clip_min_x ||
187 rast_y + top * yzoom * scaleY < clip_min_y) {
190 if (rast_x + left * xzoom > clip_max_x || rast_y + bottom * yzoom > clip_max_y) {
196 int src_y = subpart_y * offset_y;
197 int src_x = subpart_x * offset_x;
199 #define DATA(_y, _x) ((char *)rect + stride * ((size_t)(_y)*img_w + (_x)))
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);
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);
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);
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);
228 uint pos = state->pos, texco = state->texco;
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);
234 immAttr2f(texco, right / (float)tex_w, bottom / (float)tex_h);
235 immVertex2f(pos, rast_x + right * xzoom * scaleX, rast_y + offset_bot * yzoom);
237 immAttr2f(texco, right / (float)tex_w, top / (float)tex_h);
238 immVertex2f(pos, rast_x + right * xzoom * scaleX, rast_y + top * yzoom * scaleY);
240 immAttr2f(texco, left / (float)tex_w, top / (float)tex_h);
241 immVertex2f(pos, rast_x + offset_left * xzoom, rast_y + top * yzoom * scaleY);
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. */
254 if (state->do_shader_unbind) {
258 GPU_texture_unbind(tex);
259 GPU_texture_free(tex);
261 /* Restore default. */
262 GPU_unpack_row_length_set(0);
265 void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state,
270 eGPUTextureFormat gpu_format,
277 const float color[4])
279 immDrawPixelsTexScaled_clipping(state,
298 void immDrawPixelsTex(IMMDrawPixelsTexState *state,
303 eGPUTextureFormat gpu_format,
308 const float color[4])
310 immDrawPixelsTexScaled_clipping(state,
329 void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state,
334 eGPUTextureFormat gpu_format,
343 const float color[4])
345 immDrawPixelsTexScaled_clipping(state,
364 /* **** Color management helper functions for GLSL display/transform ***** */
366 /* Draw given image buffer on a screen using GLSL for display transform */
367 void ED_draw_imbuf_clipping(ImBuf *ibuf,
371 ColorManagedViewSettings *view_settings,
372 ColorManagedDisplaySettings *display_settings,
380 bool force_fallback = false;
381 bool need_fallback = true;
384 if (ibuf->rect == NULL && ibuf->rect_float == NULL) {
388 /* Single channel images could not be transformed using GLSL yet */
389 force_fallback |= ibuf->channels == 1;
391 /* If user decided not to use GLSL, fallback to glaDrawPixelsAuto */
392 force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL);
394 /* Try to draw buffer using GLSL display transform */
395 if (force_fallback == false) {
398 IMMDrawPixelsTexState state = {0};
399 /* We want GLSL state to be fully handled by OCIO. */
400 state.do_shader_unbind = false;
401 immDrawPixelsTexSetupAttributes(&state);
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);
409 ok = IMB_colormanagement_setup_glsl_draw(
410 view_settings, display_settings, ibuf->dither, true);
414 ok = IMB_colormanagement_setup_glsl_draw_from_space(
415 view_settings, display_settings, ibuf->rect_colorspace, ibuf->dither, false, false);
419 if (ibuf->rect_float) {
420 eGPUTextureFormat format = 0;
422 if (ibuf->channels == 3) {
425 else if (ibuf->channels == 4) {
426 format = GPU_RGBA16F;
429 BLI_assert(!"Incompatible number of channels for GLSL display");
433 immDrawPixelsTex_clipping(&state,
450 else if (ibuf->rect) {
451 /* ibuf->rect is always RGBA */
452 immDrawPixelsTex_clipping(&state,
469 IMB_colormanagement_finish_glsl_draw();
471 need_fallback = false;
475 /* In case GLSL failed or not usable, fallback to glaDrawPixelsAuto */
477 uchar *display_buffer;
480 display_buffer = IMB_display_buffer_acquire(
481 ibuf, view_settings, display_settings, &cache_handle);
483 if (display_buffer) {
484 IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
485 immDrawPixelsTex_clipping(&state,
502 IMB_display_buffer_release(cache_handle);
506 void ED_draw_imbuf(ImBuf *ibuf,
510 ColorManagedViewSettings *view_settings,
511 ColorManagedDisplaySettings *display_settings,
515 ED_draw_imbuf_clipping(ibuf,
529 void ED_draw_imbuf_ctx_clipping(const bContext *C,
541 ColorManagedViewSettings *view_settings;
542 ColorManagedDisplaySettings *display_settings;
544 IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
546 ED_draw_imbuf_clipping(ibuf,
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)
563 ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, use_filter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y);
566 int ED_draw_imbuf_method(ImBuf *ibuf)
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;
575 return (size > threshold) ? IMAGE_DRAW_METHOD_2DTEXTURE : IMAGE_DRAW_METHOD_GLSL;
577 return U.image_draw_method;
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)
584 float delta_x = 4.0f * UI_DPI_FAC / zoomx;
585 float delta_y = 4.0f * UI_DPI_FAC / zoomy;
587 delta_x = min_ff(delta_x, border->xmax - border->xmin);
588 delta_y = min_ff(delta_y, border->ymax - border->ymin);
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);
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);
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);
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);