mirror of
https://github.com/UiO-Gaming/mustafa.git
synced 2026-11-05 13:56:48 +01:00
draw a solid crab rave line and space the texts evenly from it
The line was a row of underscores, which left gaps between them. Draw it as a solid bar instead, and place the top text's baseline and the top of the bottom text's capital letters equally far from it, whatever size the texts end up being. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b67381976c
commit
d0bf523448
1 file changed
+25
-14
+25
-14
@@ -11,6 +11,7 @@ import numpy as np
|
||||
import yt_dlp
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
from moviepy import ColorClip
|
||||
from moviepy import CompositeVideoClip
|
||||
from moviepy import ImageClip
|
||||
from moviepy import VideoFileClip
|
||||
@@ -385,7 +386,9 @@ class Meme(commands.Cog):
|
||||
self.bot.logger.warning(f"Failed to remove temporary file {temp_file}")
|
||||
|
||||
@staticmethod
|
||||
def text_clip(text: str, font_size: int, stroke_width: int = 0, max_width: int | None = None) -> ImageClip:
|
||||
def text_clip(
|
||||
text: str, font_size: int, stroke_width: int = 0, max_width: int | None = None
|
||||
) -> tuple[ImageClip, int, int]:
|
||||
"""
|
||||
Render white text with a black outline as a transparent clip.
|
||||
moviepy's own TextClip cuts off everything below the tallest letters with Pillow 11.2 and newer,
|
||||
@@ -401,6 +404,8 @@ class Meme(commands.Cog):
|
||||
Returns
|
||||
----------
|
||||
(ImageClip): The text with a transparent background
|
||||
(int): How far down the clip the text's baseline is
|
||||
(int): The height of capital letters above the baseline, at the size the text ended up being
|
||||
"""
|
||||
|
||||
font = misc_utils.load_font(misc_utils.UNICODE_FONT, font_size)
|
||||
@@ -414,10 +419,12 @@ class Meme(commands.Cog):
|
||||
left, _, right, _ = font.getbbox(text, stroke_width=stroke_width, anchor="ls")
|
||||
|
||||
ascent, descent = font.getmetrics()
|
||||
baseline = ascent + stroke_width
|
||||
cap_height = -font.getbbox("H", anchor="ls")[1]
|
||||
|
||||
image = Image.new("RGBA", (right - left, ascent + descent + 2 * stroke_width), (0, 0, 0, 0))
|
||||
ImageDraw.Draw(image).text(
|
||||
(-left, ascent + stroke_width),
|
||||
(-left, baseline),
|
||||
text,
|
||||
font=font,
|
||||
fill="white",
|
||||
@@ -425,7 +432,7 @@ class Meme(commands.Cog):
|
||||
stroke_width=stroke_width,
|
||||
stroke_fill="black",
|
||||
)
|
||||
return ImageClip(np.array(image))
|
||||
return ImageClip(np.array(image)), baseline, cap_height
|
||||
|
||||
@staticmethod
|
||||
def make_crab(top_text: str, bottom_text: str, user_id: int):
|
||||
@@ -442,22 +449,26 @@ class Meme(commands.Cog):
|
||||
|
||||
clip = VideoFileClip("./src/assets/crab.mp4")
|
||||
|
||||
# Leave 5% of the video's width free on each side
|
||||
# A solid line between the texts, which sit equally far from it: the top text's baseline above it,
|
||||
# and the top of the bottom text's capital letters below it
|
||||
line_top, line_thickness, line_width = 381, 6, 708
|
||||
gap = 21
|
||||
line = ColorClip(size=(line_width, line_thickness), color=(255, 255, 255)).with_position(("center", line_top))
|
||||
|
||||
# Leave 5% of the video's width free on each side. Long texts are shrunk to fit
|
||||
max_width = int(clip.w * 0.9)
|
||||
top_part = Meme.text_clip(top_text, 60, stroke_width=2, max_width=max_width)
|
||||
# Long texts are shrunk to fit, so keep the top text on the baseline full-size text would have,
|
||||
# right above the line, instead of letting it float up. Ascent scales with the font size
|
||||
ascent, descent = misc_utils.load_font(misc_utils.UNICODE_FONT, 60).getmetrics()
|
||||
top_ascent = round((top_part.h - 2 * 2) * ascent / (ascent + descent))
|
||||
top_part = top_part.with_position(("center", 300 + ascent - top_ascent))
|
||||
middle_part = Meme.text_clip("_" * 26, 48).with_position(("center", "center"))
|
||||
bottom_part = Meme.text_clip(bottom_text, 60, stroke_width=2, max_width=max_width).with_position(
|
||||
("center", 400)
|
||||
top_part, top_baseline, _ = Meme.text_clip(top_text, 60, stroke_width=2, max_width=max_width)
|
||||
top_part = top_part.with_position(("center", line_top - gap - top_baseline))
|
||||
bottom_part, bottom_baseline, bottom_cap_height = Meme.text_clip(
|
||||
bottom_text, 60, stroke_width=2, max_width=max_width
|
||||
)
|
||||
bottom_part = bottom_part.with_position(
|
||||
("center", line_top + line_thickness + gap - (bottom_baseline - bottom_cap_height))
|
||||
)
|
||||
|
||||
parts = [
|
||||
part.with_start(11.0).with_duration(26.0).with_effects([vfx.CrossFadeIn(1)])
|
||||
for part in (top_part, middle_part, bottom_part)
|
||||
for part in (top_part, line, bottom_part)
|
||||
]
|
||||
|
||||
video = CompositeVideoClip([clip, *parts]).with_duration(26.0)
|
||||
|
||||
Reference in new issue
Block a user