Compositor: UI: Visualize render size and domain size in image editor

Show render region as a bounding box with a passepartout option.
Additionally, a text info is shown at the upper left corner, similar to
the info text in 3d view, showing the render size and current image
size.

Devtalk thread:
https://devtalk.blender.org/t/compositor-ui-improvements/34186?u=izo

Pull Request: https://projects.blender.org/blender/blender/pulls/120471
This commit is contained in:
Habib Gahbiche
2025-05-21 15:57:31 +02:00
parent 9a9fec7f03
commit f6048a5e7b
7 changed files with 236 additions and 2 deletions

View File

@@ -1588,7 +1588,7 @@ class IMAGE_PT_overlay(Panel):
bl_space_type = 'IMAGE_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Overlays"
bl_ui_units_x = 13
bl_ui_units_x = 14
def draw(self, context):
pass
@@ -1735,6 +1735,37 @@ class IMAGE_PT_overlay_image(Panel):
layout.prop(uvedit, "show_metadata")
class IMAGE_PT_overlay_render_guides(Panel):
bl_space_type = 'IMAGE_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Guides"
bl_parent_id = "IMAGE_PT_overlay"
@classmethod
def poll(cls, context):
sima = context.space_data
return ((sima.mode == 'MASK' or sima.mode == 'VIEW') and
(sima.image and sima.image.source == 'VIEWER' and
sima.image.type == 'COMPOSITING'))
def draw(self, context):
layout = self.layout
sima = context.space_data
overlay = sima.overlay
layout.active = overlay.show_overlays
row = layout.row(align=True)
layout.prop(overlay, "show_text_info")
row = layout.row(align=True)
row.prop(overlay, "show_render_size")
subrow = row.row()
subrow.active = overlay.show_render_size
subrow.prop(overlay, "passepartout_alpha", text="Passepartout")
# Grease Pencil properties
class IMAGE_PT_annotation(AnnotationDataPanel, Panel):
bl_space_type = 'IMAGE_EDITOR'
@@ -1830,6 +1861,7 @@ classes = (
IMAGE_PT_overlay_uv_edit_geometry,
IMAGE_PT_overlay_uv_display,
IMAGE_PT_overlay_image,
IMAGE_PT_overlay_render_guides,
IMAGE_AST_brush_paint,
)

View File

@@ -77,6 +77,18 @@ void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *region, void *arg_
void ED_region_image_metadata_draw(
int x, int y, const ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy);
void ED_region_image_overlay_info_text_draw(const int render_size_x,
const int render_size_y,
const int viewer_size_x,
const int viewer_size_y,
const int draw_offset_x,
const int draw_offset_y);
void ED_region_image_render_region_draw(
int x, int y, const rcti *frame, float zoomx, float zoomy, float passepartout_alpha);
/* Slider */
struct tSlider;

View File

@@ -25,6 +25,7 @@
#include "BKE_layer.hh"
#include "BKE_lib_query.hh"
#include "BKE_lib_remap.hh"
#include "BKE_scene.hh"
#include "BKE_screen.hh"
#include "RNA_access.hh"
@@ -105,6 +106,7 @@ static SpaceLink *image_create(const ScrArea * /*area*/, const Scene * /*scene*/
simage->uv_face_opacity = 1.0f;
simage->stretch_opacity = 1.0f;
simage->overlay.flag = SI_OVERLAY_SHOW_OVERLAYS | SI_OVERLAY_SHOW_GRID_BACKGROUND;
simage->overlay.passepartout_alpha = 0.5f;
BKE_imageuser_default(&simage->iuser);
simage->iuser.flag = IMA_SHOW_STEREO | IMA_ANIM_ALWAYS;
@@ -634,7 +636,19 @@ static void image_main_region_draw(const bContext *C, ARegion *region)
Scene *scene = CTX_data_scene(C);
View2D *v2d = &region->v2d;
Image *image = ED_space_image(sima);
/* Typically a render result or viewer image from the compositor. */
const bool show_viewer = (image && image->source == IMA_SRC_VIEWER);
const bool show_compositor_viewer = show_viewer && image->type == IMA_TYPE_COMPOSITE;
/* Text info and render region are only relevant for the compositor. */
const bool show_text_info = (sima->overlay.flag & SI_OVERLAY_SHOW_OVERLAYS &&
sima->overlay.flag & SI_OVERLAY_DRAW_TEXT_INFO &&
(sima->mode == SI_MODE_MASK || sima->mode == SI_MODE_VIEW)) &&
show_compositor_viewer;
const bool show_render_region = (sima->overlay.flag & SI_OVERLAY_SHOW_OVERLAYS &&
sima->overlay.flag & SI_OVERLAY_DRAW_RENDER_REGION &&
(sima->mode == SI_MODE_MASK || sima->mode == SI_MODE_VIEW)) &&
show_compositor_viewer;
/* XXX not supported yet, disabling for now */
scene->r.scemode &= ~R_COMP_CROP;
@@ -657,6 +671,28 @@ static void image_main_region_draw(const bContext *C, ARegion *region)
BLI_thread_unlock(LOCK_DRAW_IMAGE);
}
if (show_render_region) {
int render_size_x, render_size_y;
BKE_render_resolution(&scene->r, true, &render_size_x, &render_size_y);
float zoomx, zoomy;
ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
int width, height;
ED_space_image_get_size(sima, &width, &height);
int center_x = width / 2;
int center_y = height / 2;
int x, y;
rcti render_region;
BLI_rcti_init(
&render_region, center_x, render_size_x + center_x, center_y, render_size_y + center_y);
UI_view2d_view_to_region(&region->v2d, 0.0f, 0.0f, &x, &y);
ED_region_image_render_region_draw(
x, y, &render_region, zoomx, zoomy, sima->overlay.passepartout_alpha);
}
draw_image_main_helpers(C, region);
/* Draw Meta data of the image isn't added to the DrawManager as it is
@@ -678,6 +714,23 @@ static void image_main_region_draw(const bContext *C, ARegion *region)
ED_space_image_release_buffer(sima, ibuf, lock);
}
if (show_text_info) {
int render_size_x, render_size_y;
BKE_render_resolution(&scene->r, true, &render_size_x, &render_size_y);
/* Use same positioning convention as in 3D View. */
const rcti *rect = ED_region_visible_rect(region);
int xoffset = rect->xmin + (0.5f * U.widget_unit);
int yoffset = rect->ymax - (0.1f * U.widget_unit);
int viewer_size_x, viewer_size_y;
ED_space_image_get_size(sima, &viewer_size_x, &viewer_size_y);
ED_region_image_overlay_info_text_draw(
render_size_x, render_size_y, viewer_size_x, viewer_size_y, xoffset, yoffset);
}
/* sample line */
UI_view2d_view_ortho(v2d);
draw_image_sample_line(sima);

View File

@@ -909,6 +909,124 @@ static float metadata_box_height_get(const ImBuf *ibuf, int fontid, const bool i
return 0;
}
static void text_info_row(const char *text,
const int text_len,
int col1,
int col2,
int row,
const int size_x,
const int size_y)
{
const int font_id = BLF_default();
float text_color[4];
UI_GetThemeColor4fv(TH_TEXT_HI, text_color);
BLF_color4fv(font_id, text_color);
/* Ensure text is visible against bright background. */
const float shadow_color[4] = {0.0f, 0.0f, 0.0f, 0.8f};
BLF_enable(font_id, BLF_SHADOW);
BLF_shadow_offset(font_id, 0, 0);
BLF_shadow(font_id, FontShadowType::Outline, shadow_color);
BLF_position(font_id, col1, row, 0.0f);
BLF_draw(font_id, IFACE_(text), text_len);
BLF_position(font_id, col2, row, 0.0f);
char draw_text[MAX_NAME];
SNPRINTF(draw_text, "%d x %d", size_x, size_y);
BLF_draw(font_id, draw_text, sizeof(draw_text));
BLF_disable(font_id, BLF_SHADOW);
}
void ED_region_image_overlay_info_text_draw(const int render_size_x,
const int render_size_y,
const int viewer_size_x,
const int viewer_size_y,
const int draw_offset_x,
const int draw_offset_y)
{
BLF_set_default();
const int font_id = BLF_default();
int overlay_lineheight = (UI_style_get()->widget.points * UI_SCALE_FAC * 1.6f);
const char render_size_name[MAX_NAME] = "Render Size";
const char viewer_size_name[MAX_NAME] = "Image Size";
const int render_size_width = BLF_width(font_id, render_size_name, sizeof(render_size_name));
const int viewer_size_width = BLF_width(font_id, viewer_size_name, sizeof(viewer_size_name));
int longest_label = max_ii(render_size_width, viewer_size_width);
int col1 = draw_offset_x;
int col2 = draw_offset_x + longest_label + (0.5 * U.widget_unit);
text_info_row(render_size_name,
sizeof(render_size_name),
col1,
col2,
draw_offset_y - overlay_lineheight,
render_size_x,
render_size_y);
text_info_row(viewer_size_name,
sizeof(viewer_size_name),
col1,
col2,
draw_offset_y - overlay_lineheight * 2,
viewer_size_x,
viewer_size_y);
}
void ED_region_image_render_region_draw(
int x, int y, const rcti *frame, float zoomx, float zoomy, float passepartout_alpha)
{
GPU_matrix_push();
/* Offset and zoom using GPU viewport. */
const auto frame_width = BLI_rcti_size_x(frame);
const auto frame_height = BLI_rcti_size_y(frame);
GPU_matrix_translate_2f(x, y);
GPU_matrix_scale_2f(zoomx, zoomy);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
const float x1 = frame->xmin - frame_width / 2;
const float x2 = frame->xmax - frame_width / 2;
const float y1 = frame->ymin - frame_height / 2;
const float y2 = frame->ymax - frame_height / 2;
/* Darken the area outside the frame. */
if (passepartout_alpha > 0) {
/* Using a sufficiently large number instead of numeric_limits::infinity(), to avoid comparison
* issues and different behavior around large numbers on different platforms. */
constexpr float inf = 10e5;
immUniformColor4f(0.0f, 0.0f, 0.0f, passepartout_alpha);
immRectf(pos, -inf, y2, inf, inf);
immRectf(pos, -inf, y1, inf, -inf);
immRectf(pos, -inf, y1, x1, y2);
immRectf(pos, x2, y1, inf, y2);
}
float wire_color[3];
UI_GetThemeColor3fv(TH_WIRE_EDIT, wire_color);
immUniformColor4f(wire_color[0], wire_color[1], wire_color[2], 1);
/* The bounding box must be drawn last to ensure it remains visible
* when passepartout_alpha > 0. */
imm_draw_box_wire_2d(pos, x1, y1, x2, y2);
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
GPU_matrix_pop();
}
void ED_region_image_metadata_draw(
int x, int y, const ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy)
{

View File

@@ -770,6 +770,8 @@ typedef enum eSpaceImage_Flag {
typedef enum eSpaceImageOverlay_Flag {
SI_OVERLAY_SHOW_OVERLAYS = (1 << 0),
SI_OVERLAY_SHOW_GRID_BACKGROUND = (1 << 1),
SI_OVERLAY_DRAW_RENDER_REGION = (1 << 2),
SI_OVERLAY_DRAW_TEXT_INFO = (1 << 3),
} eSpaceImageOverlay_Flag;
/** #SpaceImage.gizmo_flag */

View File

@@ -622,7 +622,7 @@ typedef struct FileDirEntryArr {
typedef struct SpaceImageOverlay {
int flag;
char _pad[4];
float passepartout_alpha;
} SpaceImageOverlay;
typedef struct SpaceImage {

View File

@@ -5854,6 +5854,23 @@ static void rna_def_space_image_overlay(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, nullptr, "overlay.flag", SI_OVERLAY_SHOW_GRID_BACKGROUND);
RNA_def_property_ui_text(prop, "Display Background", "Show the grid background and borders");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, nullptr);
prop = RNA_def_property(srna, "show_render_size", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "overlay.flag", SI_OVERLAY_DRAW_RENDER_REGION);
RNA_def_property_ui_text(prop, "Render Region", "Display the region of the final render");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, nullptr);
prop = RNA_def_property(srna, "show_text_info", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "overlay.flag", SI_OVERLAY_DRAW_TEXT_INFO);
RNA_def_property_ui_text(prop, "Text Info", "Display overlay text");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, nullptr);
prop = RNA_def_property(srna, "passepartout_alpha", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "overlay.passepartout_alpha");
RNA_def_property_float_default(prop, 0.5f);
RNA_def_property_ui_text(
prop, "Passepartout Alpha", "Opacity of the darkened overlay outside the render region");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, nullptr);
}
static void rna_def_space_image(BlenderRNA *brna)