diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 3f5fb3ff837..a2ccf4effac 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -175,6 +175,15 @@ ImBuf *BKE_image_acquire_multilayer_view_ibuf(const RenderData &render_data, void BKE_image_release_ibuf(struct Image *ima, struct ImBuf *ibuf, void *lock); +/** + * Return image buffer of preview for given image + * r_width & r_height are optional and return the _original size_ of the image. + */ +struct ImBuf *BKE_image_preview(struct Image *ima, + short max_size, + short *r_width, + short *r_height); + struct ImagePool *BKE_image_pool_new(void); void BKE_image_pool_free(struct ImagePool *pool); struct ImBuf *BKE_image_pool_acquire_ibuf(struct Image *ima, diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index c81f642f671..6aa7144d262 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -4902,6 +4902,31 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) return ibuf != nullptr; } +ImBuf *BKE_image_preview(Image *ima, const short max_size, short *r_width, short *r_height) +{ + void *lock; + ImBuf *image_ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); + if (image_ibuf == nullptr) { + return nullptr; + } + + ImBuf *preview = IMB_dupImBuf(image_ibuf); + float scale = float(max_size) / float(std::max(image_ibuf->x, image_ibuf->y)); + if (r_width) { + *r_width = image_ibuf->x; + } + if (r_height) { + *r_height = image_ibuf->y; + } + BKE_image_release_ibuf(ima, image_ibuf, lock); + + /* Resize. */ + IMB_scaleImBuf(preview, scale * image_ibuf->x, scale * image_ibuf->y); + IMB_rect_from_float(preview); + + return preview; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 51bdc8c6175..ce490725150 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -3322,15 +3322,6 @@ ARegion *UI_tooltip_create_from_button_or_extra_icon( ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz); void UI_tooltip_free(bContext *C, bScreen *screen, ARegion *region); -struct uiSearchItemTooltipData { - /** A description for the item, e.g. what happens when selecting it. */ - char description[UI_MAX_DRAW_STR]; - /* The full name of the item, without prefixes or suffixes (e.g. hint with UI_SEP_CHARP). */ - const char *name; - /** Additional info about the item (e.g. library name of a linked data-block). */ - char hint[UI_MAX_DRAW_STR]; -}; - /** * Create a tooltip from search-item tooltip data \a item_tooltip data. * To be called from a callback set with #UI_but_func_search_set_tooltip(). @@ -3338,11 +3329,10 @@ struct uiSearchItemTooltipData { * \param item_rect: Rectangle of the search item in search region space (#ui_searchbox_butrect()) * which is passed to the tooltip callback. */ -ARegion *UI_tooltip_create_from_search_item_generic( - bContext *C, - const ARegion *searchbox_region, - const rcti *item_rect, - const uiSearchItemTooltipData *item_tooltip_data); +ARegion *UI_tooltip_create_from_search_item_generic(bContext *C, + const ARegion *searchbox_region, + const rcti *item_rect, + ID *id); /* How long before a tool-tip shows. */ #define UI_TOOLTIP_DELAY 0.5 diff --git a/source/blender/editors/interface/regions/interface_region_tooltip.cc b/source/blender/editors/interface/regions/interface_region_tooltip.cc index c9c18174d48..3d643397a39 100644 --- a/source/blender/editors/interface/regions/interface_region_tooltip.cc +++ b/source/blender/editors/interface/regions/interface_region_tooltip.cc @@ -31,28 +31,35 @@ #include "BLI_listbase.h" #include "BLI_math_color.h" #include "BLI_math_vector.h" +#include "BLI_path_util.h" #include "BLI_rect.h" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BKE_context.hh" +#include "BKE_idtype.hh" +#include "BKE_image.h" #include "BKE_paint.hh" #include "BKE_screen.hh" #include "BIF_glutil.hh" +#include "DNA_vfont_types.h" + #include "GPU_immediate.hh" #include "GPU_immediate_util.hh" #include "GPU_state.hh" #include "IMB_imbuf.hh" #include "IMB_imbuf_types.hh" +#include "IMB_thumbs.hh" #include "WM_api.hh" #include "WM_types.hh" #include "RNA_access.hh" #include "RNA_path.hh" +#include "RNA_prototypes.h" #include "UI_interface.hh" @@ -1589,23 +1596,193 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) return ui_tooltip_create_with_data(C, data, init_position, nullptr, aspect); } -static uiTooltipData *ui_tooltip_data_from_search_item_tooltip_data( - const uiSearchItemTooltipData *item_tooltip_data) +static void ui_tooltip_from_image(Image &ima, uiTooltipData &data) +{ + if (ima.filepath[0]) { + char root[FILE_MAX]; + BLI_path_split_dir_part(ima.filepath, root, FILE_MAX); + UI_tooltip_text_field_add(&data, root, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL); + } + + std::string image_type; + switch (ima.source) { + case IMA_SRC_FILE: + image_type = TIP_("Single Image"); + break; + case IMA_SRC_SEQUENCE: + image_type = TIP_("Image Sequence"); + break; + case IMA_SRC_MOVIE: + image_type = TIP_("Movie"); + break; + case IMA_SRC_GENERATED: + image_type = TIP_("Generated"); + break; + case IMA_SRC_VIEWER: + image_type = TIP_("Viewer"); + break; + case IMA_SRC_TILED: + image_type = TIP_("UDIM Tiles"); + break; + } + UI_tooltip_text_field_add(&data, image_type, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL); + + short w; + short h; + ImBuf *ibuf = BKE_image_preview(&ima, 200.0f * UI_SCALE_FAC, &w, &h); + + if (ibuf) { + UI_tooltip_text_field_add( + &data, fmt::format("{} \u00D7 {}", w, h), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL); + } + + if (BKE_image_has_anim(&ima)) { + ImBufAnim *anim = static_cast(ima.anims.first); + if (anim) { + int duration = IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN); + UI_tooltip_text_field_add( + &data, fmt::format("Frames: {}", duration), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL); + } + } + + UI_tooltip_text_field_add( + &data, ima.colorspace_settings.name, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL); + + UI_tooltip_text_field_add( + &data, fmt::format(TIP_("Users: {}"), ima.id.us), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL); + + if (ibuf) { + uiTooltipImage image_data; + image_data.width = int(ibuf->x); + image_data.height = int(ibuf->y); + image_data.ibuf = ibuf; + image_data.border = true; + image_data.background = uiTooltipImageBackground::Checkerboard_Themed; + image_data.premultiplied = true; + UI_tooltip_text_field_add(&data, {}, {}, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + UI_tooltip_text_field_add(&data, {}, {}, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + UI_tooltip_image_field_add(&data, image_data); + } +} + +static void ui_tooltip_from_clip(MovieClip &clip, uiTooltipData &data) +{ + if (clip.filepath[0]) { + char root[FILE_MAX]; + BLI_path_split_dir_part(clip.filepath, root, FILE_MAX); + UI_tooltip_text_field_add(&data, root, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL); + } + + std::string image_type; + switch (clip.source) { + case IMA_SRC_SEQUENCE: + image_type = TIP_("Image Sequence"); + break; + case IMA_SRC_MOVIE: + image_type = TIP_("Movie"); + break; + } + UI_tooltip_text_field_add(&data, image_type, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL); + + if (clip.anim) { + ImBufAnim *anim = clip.anim; + + UI_tooltip_text_field_add(&data, + fmt::format("{} \u00D7 {}", + IMB_anim_get_image_width(anim), + IMB_anim_get_image_height(anim)), + {}, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + + UI_tooltip_text_field_add( + &data, + fmt::format("Frames: {}", IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN)), + {}, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); + + ImBuf *ibuf = IMB_anim_previewframe(anim); + + if (ibuf) { + /* Resize. */ + float scale = float(200.0f * UI_SCALE_FAC) / float(std::max(ibuf->x, ibuf->y)); + IMB_scaleImBuf(ibuf, scale * ibuf->x, scale * ibuf->y); + IMB_rect_from_float(ibuf); + + uiTooltipImage image_data; + image_data.width = int(ibuf->x); + image_data.height = int(ibuf->y); + image_data.ibuf = ibuf; + image_data.border = true; + image_data.background = uiTooltipImageBackground::Checkerboard_Themed; + image_data.premultiplied = true; + UI_tooltip_text_field_add(&data, {}, {}, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + UI_tooltip_text_field_add(&data, {}, {}, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL); + UI_tooltip_image_field_add(&data, image_data); + IMB_freeImBuf(ibuf); + } + } +} + +static void ui_tooltip_from_vfont(VFont &font, uiTooltipData &data) +{ + if (!font.filepath[0]) { + /* Let's not bother with packed files _for now_.*/ + return; + } + + float color[4]; + const uiWidgetColors *theme = ui_tooltip_get_theme(); + rgba_uchar_to_float(color, theme->text); + ImBuf *ibuf = IMB_font_preview(font.filepath, 200 * UI_SCALE_FAC, color); + if (ibuf) { + uiTooltipImage image_data; + image_data.width = ibuf->x; + image_data.height = ibuf->y; + image_data.ibuf = ibuf; + image_data.border = false; + image_data.background = uiTooltipImageBackground::None; + image_data.premultiplied = false; + image_data.text_color = true; + UI_tooltip_image_field_add(&data, image_data); + IMB_freeImBuf(ibuf); + } +} + +static uiTooltipData *ui_tooltip_data_from_search_item_tooltip_data(ID *id) { uiTooltipData *data = MEM_new(__func__); + const ID_Type type_id = GS(id->name); - if (item_tooltip_data->description[0]) { - UI_tooltip_text_field_add( - data, item_tooltip_data->description, {}, UI_TIP_STYLE_HEADER, UI_TIP_LC_NORMAL, true); + UI_tooltip_text_field_add(data, id->name + 2, {}, UI_TIP_STYLE_HEADER, UI_TIP_LC_MAIN); + + if (type_id == ID_IM) { + ui_tooltip_from_image(*reinterpret_cast(id), *data); + } + else if (type_id == ID_MC) { + ui_tooltip_from_clip(*reinterpret_cast(id), *data); + } + else if (type_id == ID_VF) { + ui_tooltip_from_vfont(*reinterpret_cast(id), *data); + } + else { + UI_tooltip_text_field_add(data, + fmt::format(TIP_("Choose {} data-block to be assigned to this user"), + BKE_idtype_idcode_to_name(GS(id->name))), + {}, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); } - if (item_tooltip_data->name && item_tooltip_data->name[0]) { + /** Additional info about the item (e.g. library name of a linked data-block). */ + if (ID_IS_LINKED(id)) { UI_tooltip_text_field_add( - data, item_tooltip_data->name, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_VALUE, true); - } - if (item_tooltip_data->hint[0]) { - UI_tooltip_text_field_add( - data, item_tooltip_data->hint, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL, true); + data, + fmt::format(TIP_("Source library: {}\n{}"), id->lib->id.name + 2, id->lib->filepath), + {}, + UI_TIP_STYLE_NORMAL, + UI_TIP_LC_NORMAL); } if (data->fields.is_empty()) { @@ -1615,13 +1792,12 @@ static uiTooltipData *ui_tooltip_data_from_search_item_tooltip_data( return data; } -ARegion *UI_tooltip_create_from_search_item_generic( - bContext *C, - const ARegion *searchbox_region, - const rcti *item_rect, - const uiSearchItemTooltipData *item_tooltip_data) +ARegion *UI_tooltip_create_from_search_item_generic(bContext *C, + const ARegion *searchbox_region, + const rcti *item_rect, + ID *id) { - uiTooltipData *data = ui_tooltip_data_from_search_item_tooltip_data(item_tooltip_data); + uiTooltipData *data = ui_tooltip_data_from_search_item_tooltip_data(id); if (data == nullptr) { return nullptr; } diff --git a/source/blender/editors/interface/templates/interface_templates.cc b/source/blender/editors/interface/templates/interface_templates.cc index c5c842b5ccd..1e9053fdf41 100644 --- a/source/blender/editors/interface/templates/interface_templates.cc +++ b/source/blender/editors/interface/templates/interface_templates.cc @@ -498,26 +498,10 @@ static void id_search_cb_objects_from_scene(const bContext *C, } static ARegion *template_ID_search_menu_item_tooltip( - bContext *C, ARegion *region, const rcti *item_rect, void *arg, void *active) + bContext *C, ARegion *region, const rcti *item_rect, void * /*arg*/, void *active) { - TemplateID *template_ui = static_cast(arg); ID *active_id = static_cast(active); - StructRNA *type = RNA_property_pointer_type(&template_ui->ptr, template_ui->prop); - - uiSearchItemTooltipData tooltip_data = {{0}}; - - tooltip_data.name = active_id->name + 2; - SNPRINTF(tooltip_data.description, - TIP_("Choose %s data-block to be assigned to this user"), - RNA_struct_ui_name(type)); - if (ID_IS_LINKED(active_id)) { - SNPRINTF(tooltip_data.hint, - TIP_("Source library: %s\n%s"), - active_id->lib->id.name + 2, - active_id->lib->filepath); - } - - return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data); + return UI_tooltip_create_from_search_item_generic(C, region, item_rect, active_id); } /* ID Search browse menu, open */ diff --git a/source/blender/imbuf/IMB_thumbs.hh b/source/blender/imbuf/IMB_thumbs.hh index 18712104d64..527658e85e7 100644 --- a/source/blender/imbuf/IMB_thumbs.hh +++ b/source/blender/imbuf/IMB_thumbs.hh @@ -91,6 +91,8 @@ ImBuf *IMB_thumb_load_blend(const char *blen_path, const char *blen_group, const ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned int y); bool IMB_thumb_load_font_get_hash(char *r_hash); +ImBuf *IMB_font_preview(const char *filename, unsigned int width, float color[4]); + /* Threading */ void IMB_thumb_locks_acquire(); diff --git a/source/blender/imbuf/intern/thumbs_font.cc b/source/blender/imbuf/intern/thumbs_font.cc index f9e40735912..cbf56e3fcdb 100644 --- a/source/blender/imbuf/intern/thumbs_font.cc +++ b/source/blender/imbuf/intern/thumbs_font.cc @@ -45,3 +45,40 @@ bool IMB_thumb_load_font_get_hash(char *r_hash) return true; } + +ImBuf *IMB_font_preview(const char *filename, unsigned int width, float color[4]) +{ + int font_id = (filename[0] != '<') ? BLF_load(filename) : 0; + const char sample[] = "ABCDEFGH\nabcdefg123"; + + BLF_buffer_col(font_id, color); + + BLF_size(font_id, 50.0f); + BLF_enable(font_id, BLF_WORD_WRAP); + float name_w; + float name_h; + BLF_width_and_height(font_id, sample, sizeof(sample), &name_w, &name_h); + float scale = float(width) / name_w; + BLF_size(font_id, scale * 50.0f); + name_w *= scale; + name_h *= scale; + + int height = int(name_h * 1.3f); + ImBuf *ibuf = IMB_allocImBuf(width, height, 32, IB_rect); + /* fill with white and zero alpha */ + const float col[4] = {1.0f, 1.0f, 1.0f, 0.0f}; + IMB_rectfill(ibuf, col); + + BLF_buffer(font_id, ibuf->float_buffer.data, ibuf->byte_buffer.data, width, height, nullptr); + + BLF_position(font_id, 0.0f, name_h * 0.8f, 0.0f); + BLF_draw_buffer(font_id, sample, 1024); + + BLF_buffer(font_id, nullptr, nullptr, 0, 0, nullptr); + + if (font_id != 0) { + BLF_unload_id(font_id); + } + + return ibuf; +}