HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/resolve/Developer/Fusion Fuse/Example 6 Text.fuse
--[[-- 
A simple  Text Annotation Fuse, which prints a single window of text onto the input image. 

Unlike Text+, the resolution comes from the input image, and there is no need for 
an additional Merge. This keeps the composition simple. Useful for slates and templates.

Written by : eyeon Software (sdk@eyeonline.com)
Written on : Feb 22nd, 2008
Modified   : May 14th - made Text input work with simple expressions
Requires   : Fusion 5.2 or later.
Modified May 2020 by SR

--]]--

--******************************************************************************************************
-- This Registers the Fuse into Fusion, gives its Name, and Menu location. Location of Help. Can Fuse be edited.
FuRegisterClass("TextAnnotate", CT_Tool, {
	REGS_Name          = "Ex6_Text",
	REGS_Category      = "Fuses\\Examples",
	REGS_OpIconString  = "E6Tx",
	REGS_OpDescription = "Example, using Text and Strings",
	REGS_HelpTopic     = "Example Location of Help", --This can be a URL
	REGS_URL           = "www.blackmagicdesign.com",
	--REGS_IconID = "Icons.Tools.Icons.Example",-- This can be Inline as an array of Values
	REG_OpNoMask      = false,
	REG_NoBlendCtrls  = false,
	REG_NoObjMatCtrls = false,
	REG_NoMotionBlurCtrls = false,
	REG_Fuse_NoEdit   = false,
	REG_Fuse_NoReload = false,
	REG_Version       = 1, 
}) -- End of RegisterClass

	
function Create()
	InText = self:AddInput("Styled Text", "StyledText", {
		LINKID_DataType    = "Text",
		INPID_InputControl = "TextEditControl",
		TEC_Lines          = 3, --How many lines high is the Input.
	})
	
	InFont = self:AddInput("Font", "Font", {
		LINKID_DataType     = "Text",
		INPID_InputControl  = "FontFileControl",
		IC_ControlGroup     = 2,
		IC_ControlID        = 0,
		INP_Level           = 1,
		INP_DoNotifyChanged = true,
	})
		
	InFontStyle = self:AddInput("Style", "Style", {
		LINKID_DataType     = "Text",
		INPID_InputControl  = "FontFileControl",
		IC_ControlGroup     = 2,
		IC_ControlID        = 1,
		INP_Level           = 1,
		INP_DoNotifyChanged = true,
	})

		
	InPosition = self:AddInput("Position", "Position", {
		LINKID_DataType      = "Point",
		INPID_InputControl   = "OffsetControl",
		INPID_PreviewControl = "CrosshairControl",
		})

	InSize = self:AddInput("Size", "Size", {
		LINKID_DataType    = "Number",
		INPID_InputControl = "SliderControl",
		INP_MinScale       = 0.0,
		INP_MaxScale       = 0.5,
		INP_Default        = 0.08,
		})


	
	InJustify = self:AddInput("Justification", "Justification", {
		LINKID_DataType    = "Number",
		INPID_InputControl = "MultiButtonControl",
		INP_Default        = 0.0,
		{ MBTNC_AddButton  = "Left",   MBTNCD_ButtonWidth = 1/3, },
		{ MBTNC_AddButton  = "Center", MBTNCD_ButtonWidth = 1/3, },
		{ MBTNC_AddButton  = "Right",  MBTNCD_ButtonWidth = 1/3, },
		INP_Integer = true,
		})
	
	InR = self:AddInput("Red", "Red", {
		LINKID_DataType    = "Number",
		INPID_InputControl = "ColorControl",
		INP_MinScale       = 0.0,
		INP_MaxScale       = 1.0,
		INP_Default        = 1.0,
		ICS_Name           = "Color",
		CLRC_ShowWheel     = false,
		IC_ControlGroup    = 1,
		IC_ControlID       = 0,  -- Red ID
		})
		
	InG = self:AddInput("Green", "Green", {
		LINKID_DataType    = "Number",
		INPID_InputControl = "ColorControl",
		INP_MinScale       = 0.0,
		INP_MaxScale       = 1.0,
		INP_Default        = 1.0,
		IC_ControlGroup    = 1,
		IC_ControlID       = 1,  -- Green ID
		})
		
	InB = self:AddInput("Blue", "Blue", {
		LINKID_DataType    = "Number",
		INPID_InputControl = "ColorControl",
		INP_MinScale       = 0.0,
		INP_MaxScale       = 1.0,
		INP_Default        = 1.0,
		IC_ControlGroup    = 1,
		IC_ControlID       = 2,  -- Blue ID
		})
		
	InA = self:AddInput("Alpha", "Alpha", {
		LINKID_DataType    = "Number",
		INPID_InputControl = "ColorControl",
		INP_MinScale       = 0.0,
		INP_MaxScale       = 1.0,
		INP_Default        = 1.0,
		IC_ControlGroup    = 1,
		IC_ControlID       = 3,  -- Alpha ID
		})
		
	InImage = self:AddInput("Input", "Input", {
		LINKID_DataType = "Image",
		LINK_Main       = 1,
		})

	OutImage = self:AddOutput("Output", "Output", {
		LINKID_DataType = "Image",
		LINK_Main       = 1,
		})	
end

function drawstring(img, font, style, size, justify, quality, x, y, colour, text)
	local ic = ImageChannel(img, quality) 
	local fs = FillStyle()
	local cs = ChannelStyle()
	
	cs.Color = colour
	ic:SetStyleFill(fs)
	
	-- get the fonts metrics (see http://freetype.sourceforge.net/freetype2/docs/glyphs/index.html for a great guide)
	local font = TextStyleFont(font, style)
	local tfm = TextStyleFontMetrics(font)
	
	-- This is the distance between this line and the next one. 
	local line_height = (tfm.TextAscent + tfm.TextDescent + tfm.TextExternalLeading) * 10 * size 
	local x_move = 0
	
	local mat = Matrix4()
			
	mat:Scale(1.0/tfm.Scale, 1.0/tfm.Scale, 1.0)
	mat:Scale(size, size, 1)
		
	-- set the initial baseline position of the text cursor
	local sh, ch, prevch

	local shape = Shape()
	mat:Move(x, y, 0)
	
	-- split the text into separate lines
	for line in string.gmatch(text, "%C+") do
		
		-- First pass, work out what the total width of this line is going to be
		local line_width = 0
		for i = 1, #line do
			ch = line:sub(i,i):byte()
			
			-- is this ignoring kerning?
			line_width = line_width + tfm:CharacterWidth(ch)*10*size
		end
		
		-- Now work out our initial cursor position, based on the justification
		-- 0 = left justify, 
		-- 1 = centered
		-- 2 = right justify
		if justify == 0 then
			--mat:Move(0, 0, 0)
		elseif justify == 1 then
			mat:Move(-line_width/2, 0, 0)
		elseif justify == 2 then 
			mat:Move(-line_width, 0, 0)
		end
	
		-- Second pass, now we assemble the actual shape
		for i = 1, #line do
			prevch = ch
			
			-- get the character, or glyph
			ch = line:sub(i,i):byte()
			
			-- first we want to know what the width of the character is,
			-- so we know where to start drawing this next character
			-- not really sure why we multiply this by 10, we just do :-)
			local cw = tfm:CharacterWidth(ch) * 10 * size
			
			-- if there is a previous character, we need to get the kerning 
			-- between the current character and the last one. 
			if prevch then
				x_offset = tfm:CharacterKerning(prevch, ch)*10*size
				x_move = x_move + x_offset
				mat:Move(x_offset, 0, 0)
			end
			
			-- move the cursor to the center of the character
			mat:Move(cw/2, 0, 0)
			
			-- I think this renders the shape we are interested in
			sh = tfm:GetCharacterShape(ch, false)
			if sh then
				sh = sh:TransformOfShape(mat)
				
				-- move the text cursor to the end of the glyph.
				mat:Move(cw/2, 0, 0)
				x_move = x_move + cw
				
				shape:AddShape(sh)
			end
		end
		
		-- line end, move the cursor back to the start
		if justify == 0 then
			mat:Move(-x_move, -line_height, 0)
		elseif justify == 1 then
			mat:Move(-x_move/2, -line_height, 0)
		elseif justify == 2 then 
			mat:Move(0, -line_height, 0)
		end
		
		x_move = 0
	end
	
	ic:ShapeFill(shape)
	ic:PutToImage("CM_Merge", cs)
	
end
 
function Process(req) 	
	local img = InImage:GetValue(req)
	local font = InFont:GetValue(req).Value
	local style = InFontStyle:GetValue(req).Value
	local out = img:CopyOf()
	
	local text      = InText:GetValue(req).Value
	local size      = InSize:GetValue(req).Value
	local center    = InPosition:GetValue(req)
	local justify	= InJustify:GetValue(req).Value
	local r         = InR:GetValue(req).Value
	local g         = InG:GetValue(req).Value
	local b         = InB:GetValue(req).Value
	local a         = InA:GetValue(req).Value
	
	local cx = center.X
	local cy = center.Y * (out.Height * out.YScale) / (out.Width * out.XScale)
	local quality = 32
	
	-- if the FontManager list is empty, scan the font list
	-- If the UI has never been shown, as would always be the case on a render node, 
	-- nothing will scan the font list for available fonts. So we check for that here, 
	-- and force a scan if needed.
	if not next( FontManager:GetFontList() ) then
		FontManager:ScanDir()
	end
	
	if req:IsQuick() then 
		quality = 1
	end
	-- the drawstring function is doing all the heavy lifting 
	drawstring(out, font, style, size, justify, quality, cx, cy, Pixel{R=r,G=g,B=b,A=a}, text)
	
	OutImage:Set(req, out)
end

function NotifyChanged(inp, param, time)
	-- when the tools FontFileControl is first created, the FontManager has not yet 
	-- provided a FontList, so we can't set a default value. Instead we do it here.
	
	if inp == InFont then
		local f = param.Value
		
		if f == nil or string.len(f) == 0 then
			InFont:SetSource(Text("Open Sans"), time)
		end
	elseif inp == InFontStyle then
		local f = param.Value
		
		if f == nil or string.len(f) == 0 then
			InFontStyle:SetSource(Text("Regular"), time)
		end
	end
	
end