### Godot version

v4.2.rc1.official [ad72de508]

### System information

Godot v4.2.rc1 - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1660 Ti (NVIDIA; 31.0.15.3742) - Intel(R) Core(TM) i7-10700F CPU @ 2.90GHz (16 Threads)

### Issue description

While trying to use a custom effect script as a workaround for the [rainbow bbcode effect](https://github.com/godotengine/godot/issues/85031) at the RichTextLabel I found a bug regarding the `CharFXTransform.relative_index`.
The variable is explained as follows in the [documentation](https://docs.godotengine.org/en/4.0/classes/class_charfxtransform.html#class-charfxtransform-property-relative-index): 
```
The character offset of the glyph, relative to the current RichTextEffect custom block.
```
However, when using the script below, the character index shifts one to the left each time instead of being at the same position for each new effect block.
![grafik](https://github.com/godotengine/godot/assets/71942164/7b1b0557-d515-4d56-a25e-d2a2810b2966)


### Steps to reproduce

Add this script as Custom Effect to the RichTextLabel (or see the MRP):
```@tool
extends RichTextEffect
class_name test

var bbcode = "test"

func _process_custom_fx(char_fx):
	if char_fx.relative_index == 5:
		char_fx.color = Color.BLACK
	pass
```
Add this Text to a Rich TextLabel:
```
[test]0123456789[/test]
[test]0123456789[/test]
[test]0123456789[/test]
[test]0123456789[/test]
[test]0123456789[/test]
[test]0123456789[/test]
``` 

### Minimal reproduction project

[relative_index_issue.zip](https://github.com/godotengine/godot/files/13462368/relative_index_issue.zip)

}
					fx_offset += item_tornado->prev_off;
				} else if (item_fx->type == ITEM_RAINBOW) {
					ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
					font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + gloff.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
				} else if (item_fx->type == ITEM_PULSE) {
					ItemPulse *item_pulse = static_cast<ItemPulse *>(item_fx);
					const float sined_time = (Math::ease(Math::pingpong(item_pulse->elapsed_time, 1.0 / item_pulse->frequency) * item_pulse->frequency, item_pulse->ease));
					font_color = font_color.lerp(font_color * item_pulse->color, sined_time);
				}
			}
			if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
				fx_offset = fx_offset.round();
			}
			Vector2 char_off = char_xform.get_origin();
			
			const Color modulated_outline_color = font_outline_color * Color(1, 1, 1, font_color.a);
			const Color modulated_shadow_color = font_shadow_color * Color(1, 1, 1, font_color.a);
			for (int j = 0; j < glyphs[i].repeat; j++) {
				if (txt_visible) {
					bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
					if (!skip && frid != RID()) {
						if (modulated_shadow_color.a > 0) {
							Transform2D char_reverse_xform;
							char_reverse_xform.set_origin(-char_off - p_shadow_ofs);
							Transform2D char_final_xform = char_xform * char_reverse_xform;
							char_final_xform.columns[2] += p_shadow_ofs;
							draw_set_transform_matrix(char_final_xform);
							TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off + p_shadow_ofs, gl, modulated_shadow_color);
							if (p_shadow_outline_size > 0) {