Compositor: Add String To Image node

This patch adds a new String To Image node that generates a new image
with a text paragraph. This also adds the Font socket and Input Font
nodes.

Pull Request: https://projects.blender.org/blender/blender/pulls/155864
This commit is contained in:
Omar Emara 2026-04-17 14:05:21 +02:00 • committed by Omar Emara
parent 107c81827d
commit d5fa560cc8
12 changed files with 651 additions and 2 deletions

View file

@ -25,6 +25,7 @@ class NODE_MT_compositor_node_input_base(node_add_menu.NodeMenu):
self.node_operator(layout, "CompositorNodeMovieClip")
if context.space_data.node_tree_sub_type == 'SEQUENCER':
self.node_operator(layout, "CompositorNodeSequencerStripInfo")
self.node_operator(layout, "CompositorNodeStringToImage")
layout.separator()
self.draw_menu(layout, path="Input/Scene")
@ -41,6 +42,7 @@ class NODE_MT_compositor_node_input_constant_base(node_add_menu.NodeMenu):
layout = self.layout
self.node_operator(layout, "FunctionNodeInputBool")
self.node_operator(layout, "CompositorNodeRGB")
self.node_operator(layout, "GeometryNodeInputFont")
self.node_operator(layout, "FunctionNodeInputInt")
self.node_operator(layout, "FunctionNodeInputIntVector")
self.node_operator(layout, "FunctionNodeInputMenu")

View file

@ -122,6 +122,7 @@ set(SRC
cached_resources/intern/morphological_distance_feather_weights.cc
cached_resources/intern/ocio_color_space_conversion_shader.cc
cached_resources/intern/smaa_precomputed_textures.cc
cached_resources/intern/string_image.cc
cached_resources/intern/symmetric_blur_weights.cc
cached_resources/intern/symmetric_separable_blur_weights.cc
cached_resources/intern/van_vliet_gaussian_coefficients.cc
@ -139,6 +140,7 @@ set(SRC
cached_resources/COM_morphological_distance_feather_weights.hh
cached_resources/COM_ocio_color_space_conversion_shader.hh
cached_resources/COM_smaa_precomputed_textures.hh
cached_resources/COM_string_image.hh
cached_resources/COM_symmetric_blur_weights.hh
cached_resources/COM_symmetric_separable_blur_weights.hh
cached_resources/COM_van_vliet_gaussian_coefficients.hh
@ -154,6 +156,7 @@ set(SRC
)
set(LIB
PRIVATE bf::blenfont
PRIVATE bf::blenkernel
PRIVATE bf::blentranslation
PRIVATE bf::functions

View file

@ -16,6 +16,7 @@
#include "COM_morphological_distance_feather_weights.hh"
#include "COM_ocio_color_space_conversion_shader.hh"
#include "COM_smaa_precomputed_textures.hh"
#include "COM_string_image.hh"
#include "COM_symmetric_blur_weights.hh"
#include "COM_symmetric_separable_blur_weights.hh"
#include "COM_van_vliet_gaussian_coefficients.hh"
@ -62,6 +63,7 @@ class StaticCacheManager {
VanVlietGaussianCoefficientsContainer van_vliet_gaussian_coefficients;
FogGlowKernelContainer fog_glow_kernels;
ImageCoordinatesContainer image_coordinates;
StringImageContainer string_images;
public:
/* Reset the cache manager by deleting the cached resources that are no longer needed because

View file

@ -0,0 +1,104 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include "BLI_map.hh"
#include "DNA_node_types.h"
#include "DNA_vfont_types.h"
#include "COM_cached_resource.hh"
#include "COM_result.hh"
namespace blender::compositor {
class Context;
enum class HorizontalAlignment : uint8_t {
Left = 0,
Center = 1,
Right = 2,
};
enum class VerticalAlignment : uint8_t {
Top = 0,
TopBaseline = 1,
Middle = 2,
BottomBaseline = 3,
Bottom = 4,
};
/* ------------------------------------------------------------------------------------------------
* String Image Key.
*/
class StringImageKey {
public:
const std::string string;
const VFont *font;
const float size;
const HorizontalAlignment horizontal_alignment;
const VerticalAlignment vertical_alignment;
const std::optional<int> wrap_width;
StringImageKey(const std::string string,
const VFont *font,
const float size,
const HorizontalAlignment horizontal_alignment,
const VerticalAlignment vertical_alignment,
const std::optional<int> wrap_width);
uint64_t hash() const;
friend bool operator==(const StringImageKey &a, const StringImageKey &b) = default;
};
/* -------------------------------------------------------------------------------------------------
* String Image.
*
* A cached resource that computes and caches a result containing a string with the given
* parameters. */
class StringImage : public CachedResource {
public:
Result result;
StringImage(Context &context,
const std::string string,
const VFont *font,
const float size,
const HorizontalAlignment horizontal_alignment,
const VerticalAlignment vertical_alignment,
const std::optional<int> wrap_width);
~StringImage();
};
/* ------------------------------------------------------------------------------------------------
* String Image Container.
*/
class StringImageContainer : CachedResourceContainer {
private:
Map<StringImageKey, std::unique_ptr<StringImage>> map_;
public:
void reset() override;
/* Check if there is an available StringImage cached resource with the given parameters in the
* container, if one exists, return it, otherwise, return a newly created one and add it to the
* container. In both cases, tag the cached resource as needed to keep it cached for the next
* evaluation. */
Result &get(Context &context,
const std::string string,
const VFont *font,
const float size,
const HorizontalAlignment horizontal_alignment,
const VerticalAlignment vertical_alignment,
const std::optional<int> wrap_width);
};
} // namespace blender::compositor

View file

@ -0,0 +1,284 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <cstdint>
#include <limits>
#include <string>
#include "BLI_assert.h"
#include "BLI_hash.hh"
#include "BLI_memory_utils.hh"
#include "BLI_path_utils.hh"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "DNA_packedFile_types.h"
#include "DNA_vfont_types.h"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_vfont.hh"
#include "BLF_api.hh"
#include "COM_context.hh"
#include "COM_result.hh"
#include "COM_string_image.hh"
#include "COM_utilities.hh"
namespace blender::compositor {
/* --------------------------------------------------------------------
* String Image Key.
*/
StringImageKey::StringImageKey(const std::string string,
const VFont *font,
const float size,
const HorizontalAlignment horizontal_alignment,
const VerticalAlignment vertical_alignment,
const std::optional<int> wrap_width)
: string(string),
font(font),
size(size),
horizontal_alignment(horizontal_alignment),
vertical_alignment(vertical_alignment),
wrap_width(wrap_width)
{
}
uint64_t StringImageKey::hash() const
{
return get_default_hash(
get_default_hash(string, font, size, horizontal_alignment, vertical_alignment),
wrap_width.value_or(-1));
}
/* --------------------------------------------------------------------
* String Image.
*/
/* Loads a BLF font from the given font ID and return its identifier. Unloading the font is the
* responsibility of the caller. */
static int load_font(const VFont *font)
{
if (!font || BKE_vfont_is_builtin(font)) {
return BLF_load_default(true);
}
if (font->packedfile != nullptr) {
char name[MAX_ID_FULL_NAME];
BKE_id_full_name_get(name, &font->id, 0);
return BLF_load_mem_unique(
name, static_cast<const uchar *>(font->packedfile->data), font->packedfile->size);
}
char file_path[FILE_MAX];
STRNCPY(file_path, font->filepath);
BLI_path_abs(file_path, ID_BLEND_PATH_FROM_GLOBAL(&font->id));
return BLF_load_unique(file_path);
}
/* Computes the horizontal position in pixels from which the line with the given width should be
* drawn. The BLF module draws from the left most edge of the line. The start offset is an offset
* that is intrinsic to the used font and thus should be canceled here to draw right from the edge
* of the image. The total width is the total width of the image and is the maximum width of all
* lines. */
static float compute_draw_horizontal_position(const int start_offset,
const int line_width,
const int total_width,
const HorizontalAlignment alignment)
{
switch (alignment) {
case HorizontalAlignment::Left:
return float(-start_offset);
case HorizontalAlignment::Center:
return -start_offset + (total_width - line_width) / 2.0f;
case HorizontalAlignment::Right:
return float(-start_offset + total_width - line_width);
}
BLI_assert_unreachable();
return float(-start_offset);
}
/* Computes the vertical position in pixels from which the line with the given index should be
* drawn. Assuming there are the given number of lines with each having the given height and
* descender. The BLF module draws from the baseline of the line starting from the lower left
* origin of the image, therefore, we need to subtract the negative descender such that the line is
* fully drawn in the image. */
static float compute_draw_vertical_position(const int lines_count,
const int line_index,
const int line_height,
const int descender)
{
return (lines_count - 1 - line_index) * line_height - float(descender);
}
/* Computes the horizontal offset in pixels depending on the alignment. We also restore the start
* offset that was canceled during drawing, see compute_draw_horizontal_position. */
static float compute_horizontal_offset(const int start_offset,
const int total_width,
const HorizontalAlignment alignment)
{
switch (alignment) {
case HorizontalAlignment::Left:
return start_offset + total_width / 2.0f;
case HorizontalAlignment::Center:
return float(start_offset);
case HorizontalAlignment::Right:
return start_offset - total_width / 2.0f;
}
BLI_assert_unreachable();
return float(start_offset);
}
/* Computes the vertical offset in pixels depending on the alignment. Assuming the image has the
* given total height, the lines have the given height and negative descender. */
static float compute_vertical_offset(const int total_height,
const int line_height,
const int descender,
const VerticalAlignment alignment)
{
switch (alignment) {
case VerticalAlignment::Top:
return -total_height / 2.0f;
case VerticalAlignment::TopBaseline:
return -total_height / 2.0f + line_height + descender;
case VerticalAlignment::Middle:
return 0.0f;
case VerticalAlignment::BottomBaseline:
return total_height / 2.0f + descender;
case VerticalAlignment::Bottom:
return total_height / 2.0f;
}
BLI_assert_unreachable();
return float(descender);
}
StringImage::StringImage(Context &context,
const std::string string,
const VFont *font,
const float size,
const HorizontalAlignment horizontal_alignment,
const VerticalAlignment vertical_alignment,
const std::optional<int> wrap_width)
: result(context.create_result(ResultType::Float))
{
if (string.empty() || !font || size <= 0.0f) {
return;
}
const int font_identifier = load_font(font);
if (font_identifier == -1) {
return;
}
BLI_SCOPED_DEFER([&]() { BLF_unload_id(font_identifier); });
BLF_size(font_identifier, size);
Vector<StringRef> lines = BLF_string_wrap(
font_identifier, string, wrap_width.value_or(-1), BLFWrapMode::Typographical);
/* Compute the width of all lines as well as their starting offset. The starting offset will be
* zero in most fonts, but some special fonts might start before or after the zero point. */
int total_width = 0;
Array<int> line_widths(lines.size());
int start_offset = std::numeric_limits<int>::max();
for (const int64_t i : lines.index_range()) {
rcti line_bounding_box;
BLF_boundbox(font_identifier, lines[i].data(), lines[i].size(), &line_bounding_box);
line_widths[i] = BLI_rcti_size_x(&line_bounding_box);
total_width = math::max(total_width, line_widths[i]);
start_offset = math::min(start_offset, line_bounding_box.xmin);
}
const int line_height = BLF_height_max(font_identifier);
const int total_height = line_height * lines.size();
/* Fill the background with alpha since the draws function does not initialize the background. */
this->result.allocate_texture(int2(total_width, total_height), false, ResultStorageType::CPU);
parallel_for(this->result.domain().data_size,
[&](const int2 texel) { this->result.store_pixel(texel, 0.0f); });
BLF_buffer_col(font_identifier, Color(1.0f, 1.0f, 1.0f, 1.0f));
BLF_buffer(font_identifier,
static_cast<float *>(this->result.cpu_data().data()),
nullptr,
total_width,
total_height,
1,
nullptr);
/* Draw each of lines in the appropriate position. */
const int descender = BLF_descender(font_identifier);
for (const int64_t i : lines.index_range()) {
const float vertical_position = compute_draw_vertical_position(
lines.size(), i, line_height, descender);
const float horizontal_position = compute_draw_horizontal_position(
start_offset, line_widths[i], total_width, horizontal_alignment);
BLF_position(font_identifier, horizontal_position, vertical_position, 0.0f);
BLF_draw_buffer(font_identifier, lines[i].data(), lines[i].size());
}
BLF_buffer(font_identifier, nullptr, nullptr, 0, 0, 1, nullptr);
/* Move the image to account for the requested alignment. */
const float horizontal_offset = compute_horizontal_offset(
start_offset, total_width, horizontal_alignment);
const float vertical_offset = compute_vertical_offset(
total_height, line_height, descender, vertical_alignment);
this->result.domain().transformation.location() = float2(horizontal_offset, vertical_offset);
if (context.use_gpu()) {
const Result gpu_result = this->result.upload_to_gpu(false);
this->result.release();
this->result = gpu_result;
}
}
StringImage::~StringImage()
{
this->result.release();
}
/* --------------------------------------------------------------------
* String Image Container.
*/
void StringImageContainer::reset()
{
/* First, delete all resources that are no longer needed. */
map_.remove_if([](auto item) { return !item.value->needed; });
/* Second, reset the needed status of the remaining resources to false to ready them to track
* their needed status for the next evaluation. */
for (auto &value : map_.values()) {
value->needed = false;
}
}
Result &StringImageContainer::get(Context &context,
const std::string string,
const VFont *font,
const float size,
const HorizontalAlignment horizontal_alignment,
const VerticalAlignment vertical_alignment,
const std::optional<int> wrap_width)
{
const StringImageKey key(
string, font, size, horizontal_alignment, vertical_alignment, wrap_width);
auto &string_image = *map_.lookup_or_add_cb(key, [&]() {
return std::make_unique<StringImage>(
context, string, font, size, horizontal_alignment, vertical_alignment, wrap_width);
});
string_image.needed = true;
return string_image.result;
}
} // namespace blender::compositor

View file

@ -24,6 +24,7 @@ void StaticCacheManager::reset()
van_vliet_gaussian_coefficients.reset();
fog_glow_kernels.reset();
image_coordinates.reset();
string_images.reset();
}
void StaticCacheManager::free()

View file

@ -1108,6 +1108,20 @@ enum CMPNodeRelativeToPixelReferenceDimension {
CMP_NODE_RELATIVE_TO_PIXEL_REFERENCE_DIMENSION_DIAGONAL = 5,
};
enum CMPNodeStringToImageHorizontalAlignment {
CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_LEFT = 0,
CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_CENTER = 1,
CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_RIGHT = 2,
};
enum CMPNodeStringToImageVerticalAlignment {
CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_TOP = 0,
CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_TOP_BASELINE = 1,
CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_MIDDLE = 2,
CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_BOTTOM_BASELINE = 3,
CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_BOTTOM = 4,
};
/* Scattering phase functions */
enum {
SHD_PHASE_HENYEY_GREENSTEIN = 0,

View file

@ -10257,6 +10257,7 @@ static void rna_def_nodes(BlenderRNA *brna)
define("CompositorNode", "CompositorNodeSplit");
define("CompositorNode", "CompositorNodeStabilize", def_cmp_stabilize2d);
define("CompositorNode", "CompositorNodeSequencerStripInfo");
define("CompositorNode", "CompositorNodeStringToImage");
define("CompositorNode", "CompositorNodeSwitch");
define("CompositorNode", "CompositorNodeSwitchView");
define("CompositorNode", "CompositorNodeTime", def_time);

View file

@ -100,6 +100,7 @@ set(SRC
nodes/node_composite_set_alpha.cc
nodes/node_composite_split.cc
nodes/node_composite_stabilize_2d.cc
nodes/node_composite_string_to_image.cc
nodes/node_composite_strip_info.cc
nodes/node_composite_switch.cc
nodes/node_composite_switch_view.cc

View file

@ -118,7 +118,8 @@ static bool composite_node_tree_socket_type_valid(bke::bNodeTreeType * /*ntreety
SOCK_MATRIX,
SOCK_MENU,
SOCK_STRING,
SOCK_OBJECT);
SOCK_OBJECT,
SOCK_FONT);
}
/**

View file

@ -0,0 +1,213 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <optional>
#include "DNA_vfont_types.h"
#include "BKE_vfont.hh"
#include "UI_resources.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_string_to_image_cc {
static const EnumPropertyItem rna_node_compositor_string_to_image_horizontal_alignment_items[] = {
{CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_LEFT,
"LEFT",
ICON_ALIGN_LEFT,
"Left",
"Align text to the left"},
{CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_CENTER,
"CENTER",
ICON_ALIGN_CENTER,
"Center",
"Align text to the center"},
{CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_RIGHT,
"RIGHT",
ICON_ALIGN_RIGHT,
"Right",
"Align text to the right"},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem rna_node_compositor_string_to_image_vertical_alignment_items[] = {
{CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_TOP,
"TOP",
ICON_ALIGN_TOP,
"Top",
"Align text to the top"},
{CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_TOP_BASELINE,
"TOP_BASELINE",
ICON_ALIGN_TOP,
"Top Baseline",
"Align text to the top line's baseline"},
{CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_MIDDLE,
"MIDDLE",
ICON_ALIGN_MIDDLE,
"Middle",
"Align text to the middle"},
{CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_BOTTOM_BASELINE,
"BOTTOM_BASELINE",
ICON_ALIGN_BOTTOM,
"Bottom Baseline",
"Align text to the bottom line's baseline"},
{CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_BOTTOM,
"BOTTOM",
ICON_ALIGN_BOTTOM,
"Bottom",
"Align text to the bottom"},
{0, nullptr, 0, nullptr, nullptr},
};
static void node_declare(NodeDeclarationBuilder &b)
{
b.use_custom_socket_order();
b.allow_any_socket_order();
b.add_output<decl::Float>("Image"_ustr)
.structure_type(StructureType::Dynamic)
.description("The image containing the paragraph of text");
b.add_input<decl::String>("String"_ustr).optional_label();
b.add_input<decl::Font>("Font"_ustr)
.default_value_fn(
[](const bNode & /*node*/) { return id_cast<ID *>(BKE_vfont_builtin_ensure()); })
.optional_label();
b.add_input<decl::Float>("Size"_ustr)
.default_value(128.0f)
.subtype(PROP_PIXEL)
.min(0.0f)
.description("The height of each line in pixels");
{
PanelDeclarationBuilder &panel = b.add_panel("Alignment"_ustr).default_closed(true);
panel.add_input<decl::Menu>("Horizontal Alignment"_ustr)
.static_items(rna_node_compositor_string_to_image_horizontal_alignment_items)
.default_value(CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_CENTER)
.optional_label();
panel.add_input<decl::Menu>("Vertical Alignment"_ustr)
.static_items(rna_node_compositor_string_to_image_vertical_alignment_items)
.default_value(CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_MIDDLE)
.optional_label();
}
{
PanelDeclarationBuilder &panel = b.add_panel("Wrap"_ustr).default_closed(true);
panel.add_input<decl::Bool>("Wrap"_ustr)
.default_value(true)
.panel_toggle()
.description("Wrap text into new lines if it exceeds the specified width");
panel.add_input<decl::Int>("Width"_ustr, "Wrap Width"_ustr)
.default_value(1920)
.min(0)
.subtype(PROP_PIXEL)
.description(
"The maximum width of each line in pixels. Lines with larger widths will be wrapped "
"into new lines");
}
}
using namespace blender::compositor;
class StringToImageOperation : public NodeOperation {
public:
using NodeOperation::NodeOperation;
void execute() override
{
const std::string string = this->get_input("String").get_single_value_default<std::string>();
const VFont *font = this->get_input("Font").get_single_value_default<VFont *>();
const float size = this->get_input("Size").get_single_value_default<float>();
const Result &string_image = this->context().cache_manager().string_images.get(
this->context(),
string,
font,
size,
this->get_horizontal_alignment(),
this->get_vertical_alignment(),
this->get_wrap_width());
if (!string_image.is_allocated()) {
this->allocate_default_remaining_outputs();
return;
}
Result &output = this->get_result("Image");
output.wrap_external(string_image);
}
HorizontalAlignment get_horizontal_alignment()
{
const auto horizontal_alignment = CMPNodeStringToImageHorizontalAlignment(
this->get_input("Horizontal Alignment").get_single_value_default<MenuValue>().value);
switch (horizontal_alignment) {
case CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_LEFT:
return HorizontalAlignment::Left;
case CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_CENTER:
return HorizontalAlignment::Center;
case CMP_NODE_STRING_TO_IMAGE_HORIZONTAL_ALIGNMENT_RIGHT:
return HorizontalAlignment::Right;
}
BLI_assert_unreachable();
return HorizontalAlignment::Left;
}
VerticalAlignment get_vertical_alignment()
{
const auto vertical_alignment = CMPNodeStringToImageVerticalAlignment(
this->get_input("Vertical Alignment").get_single_value_default<MenuValue>().value);
switch (vertical_alignment) {
case CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_TOP:
return VerticalAlignment::Top;
case CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_TOP_BASELINE:
return VerticalAlignment::TopBaseline;
case CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_MIDDLE:
return VerticalAlignment::Middle;
case CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_BOTTOM_BASELINE:
return VerticalAlignment::BottomBaseline;
case CMP_NODE_STRING_TO_IMAGE_VERTICAL_ALIGNMENT_BOTTOM:
return VerticalAlignment::Bottom;
}
BLI_assert_unreachable();
return VerticalAlignment::Middle;
}
std::optional<int> get_wrap_width()
{
const bool use_wrap = this->get_input("Wrap").get_single_value_default<bool>();
if (!use_wrap) {
return std::nullopt;
}
return math::max(0, this->get_input("Wrap Width").get_single_value_default<int>());
}
};
static NodeOperation *get_compositor_operation(Context &context, const bNode &node)
{
return new StringToImageOperation(context, node);
}
static void node_register()
{
static bke::bNodeType ntype;
cmp_node_type_base(&ntype, "CompositorNodeStringToImage"_ustr);
ntype.ui_name = "String To Image";
ntype.ui_description = "Generates an image containing the given paragraph of text";
ntype.nclass = NODE_CLASS_INPUT;
ntype.declare = node_declare;
ntype.get_compositor_operation = get_compositor_operation;
bke::node_register_type(ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_composite_string_to_image_cc

View file

@ -9,6 +9,8 @@
#include "UI_interface_c.hh"
#include "UI_interface_layout.hh"
#include "COM_node_operation.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_input_font_cc {
@ -38,17 +40,38 @@ static void node_geo_exec(GeoNodeExecParams params)
params.set_output("Font"_ustr, font);
}
using namespace blender::compositor;
class InputFontOperation : public NodeOperation {
public:
using NodeOperation::NodeOperation;
void execute() override
{
VFont *font = id_cast<VFont *>(this->node().id);
Result &result = this->get_result("Font");
result.allocate_single_value();
result.set_single_value(font);
}
};
static NodeOperation *get_compositor_operation(Context &context, const bNode &node)
{
return new InputFontOperation(context, node);
}
static void node_register()
{
static bke::bNodeType ntype;
geo_node_type_base(&ntype, "GeometryNodeInputFont"_ustr);
geo_cmp_node_type_base(&ntype, "GeometryNodeInputFont"_ustr);
ntype.ui_name = "Font";
ntype.ui_description = "Output a font";
ntype.nclass = NODE_CLASS_INPUT;
ntype.declare = node_declare;
ntype.initfunc = node_init;
ntype.geometry_node_execute = node_geo_exec;
ntype.get_compositor_operation = get_compositor_operation;
bke::node_register_type(ntype);
}
NOD_REGISTER_NODE(node_register)