From c236373c272a79a103c0480b4536f01dc3ed4db6 Mon Sep 17 00:00:00 2001 From: CampioneBase <1764544059@qq.com> Date: Wed, 2 Apr 2025 20:52:49 +0800 Subject: [PATCH] =?UTF-8?q?meme=E6=B7=BB=E5=8A=A0=20=E8=87=AA=E8=B5=B0?= =?UTF-8?q?=E6=A3=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memes/auto_chess_gen/__init__.py | 322 ++++++++++++++++++ .../images/class_little_sprite.png | Bin 0 -> 5488 bytes 2 files changed, 322 insertions(+) create mode 100644 meme_generator/memes/auto_chess_gen/__init__.py create mode 100644 meme_generator/memes/auto_chess_gen/images/class_little_sprite.png diff --git a/meme_generator/memes/auto_chess_gen/__init__.py b/meme_generator/memes/auto_chess_gen/__init__.py new file mode 100644 index 00000000..23a025d8 --- /dev/null +++ b/meme_generator/memes/auto_chess_gen/__init__.py @@ -0,0 +1,322 @@ +from datetime import datetime +from pathlib import Path +from typing import Literal +from PIL import Image +from numpy import random + +from pydantic import Field +from arclet.alconna import store_value +from pil_utils import BuildImage, Text2Image +from pil_utils.gradient import LinearGradient, ColorStop + +from meme_generator import MemeArgsModel, MemeArgsType, add_meme, ParserArg, ParserOption + +# 图片文件夹 +IMG_DIR = Path(__file__).parent / "images"; +# 位置参数说明 +POSITION_HELP_TEXT:str = "标签位置: left | right"; +# 稀有度参数说明 +RANK_HELP_TEXT:str = "稀有度:0 ~ 4,默认为4"; +# 图标参数说明 +ICONS_HELP_TEXT:str = "图标选择: 字符串。 生成第几个标签就读取第几位字符,且只有为 1 ~ 8 时才会指定,否则随机生成"; +# 稀有度文字参数说明 +RANK_TEXT_HELP_TEXT:str = "稀有度文字:字符串。用于右上角表示, 第一个占用符会填充稀有度数字"; +# 文字大小参数说明 +TEXT_SIZE_HELP_TEXT:str = "文字大小。默认 48"; +# 稀有度颜色 +RANK_COLOR: list[tuple[int, int, int, int]] = [ + (200, 200, 200, 255), # rank 0 (normal) + (119, 190, 106, 255), # rank 1 (good) + (30, 144, 255, 255), # rank 2 (rare) + (147, 112, 209, 255), # rank 3 (epic) + (255, 140, 0 , 255) # rank 4 (legend) +]; + +class Model(MemeArgsModel): + # 位置 + position: Literal["left", "right"] = Field(default = "left", description = POSITION_HELP_TEXT); + # 稀有度 + rank: int = Field(default = 4, description = RANK_HELP_TEXT); + # 图标 + icons: str = Field(default = "000", description = ICONS_HELP_TEXT); + # 稀有度文字 + text: str = Field(default = "Cost {}", description = RANK_TEXT_HELP_TEXT); + # 文字大小 + size: int = Field(default = 48, description = TEXT_SIZE_HELP_TEXT); + +chess_args = MemeArgsType( + args_model = Model, + args_examples = + [Model(position="left", rank = i, icons = "000") for i in range(len(RANK_COLOR))] + # type: ignore + [Model(position="right", rank = j, icons = "000") for j in range(len(RANK_COLOR))], # type: ignore + parser_options = [ + ParserOption( + names = ["-p", "--position"], + args = [ParserArg(name = "position", value = "str")], + help_text = POSITION_HELP_TEXT + ), + ParserOption( + names = ["-r", "--rank"], + args = [ParserArg(name = "rank", value = "int")], + help_text = RANK_HELP_TEXT + ), + ParserOption( + names = ["-i", "--icon"], + args = [ParserArg(name = "icons", value = "str")], + help_text = ICONS_HELP_TEXT + ), + ParserOption( + names = ["-t", "--text"], + args = [ParserArg(name = "text", value = "str")], + help_text = RANK_TEXT_HELP_TEXT + ), + ParserOption( + names = ["-s", "--size"], + args = [ParserArg(name = "size", value = "int")], + help_text = TEXT_SIZE_HELP_TEXT + ), + ParserOption( + names = ["--left", "左侧", "左"], + dest = "position", + action = store_value("left") + ), + ParserOption( + names = ["--right", "右侧", "右"], + dest = "position", + action = store_value("right") + ), + ParserOption( + names = ["--normal", "普通"], + dest = "rank", + action = store_value(0) + ), + ParserOption( + names = ["--good", "良好"], + dest = "rank", + action = store_value(1) + ), + ParserOption( + names = ["--rare", "稀有"], + dest = "rank", + action = store_value(2) + ), + ParserOption( + names = ["--epic", "史诗"], + dest = "rank", + action = store_value(3) + ), + ParserOption( + names = ["--legend", "传说"], + dest = "rank", + action = store_value(4) + ), + ], +) + +def auto_chess_gen(images: list[BuildImage], texts:list[str], args: Model): + ''' + Meme表情: 自走棋生成器 (autochess) + + images{1}: 底片 + + texts{1~4}: 其中第一段文字作为名称,后面最多三段文字作为标签贴在一侧 + ''' + ## ======= settings ======= ## + # 图片尺寸 + meme_width:int = 600; + meme_height:int = 480; + # 边缘尺寸 + side_weight:int = 16; + # 描边颜色 + border_color = (20, 20, 20, 127); + # 侧边距 + padding_side:int = 12 + side_weight; + # 上边距 + padding_top:int = 18 + side_weight; + # 间距 + text_interval:int = 16; + # 字体 + text_size:int = int(args.size); #36; + # 字体颜色 + text_color = (230, 230, 230, 255); + # 渐变灰度 + gray1:int = 96; + gray2:int = 160; + # 背景灰度 + grayBG:int = 192; + ## ======================== ## + if len(texts) == 1: texts = texts[0].split(","); # 适配 FastAPI + # 标签方向 + is_left:bool = args.position == "left"; + # 棋子名称 + name_text:str = texts.pop(0); + nameImg = Text2Image.from_text( + name_text, + text_size, + fill = text_color, + font_style = "bold", + stroke_width = 1, + stroke_fill = border_color, + );#.to_image(); + # 稀有度 + rank:int = int(args.rank); + rank_text:str = args.text.format(rank + 1); + rankImg = Text2Image.from_text( + rank_text, + text_size, + fill = text_color, + font_style = "bold", + stroke_width = 1, + stroke_fill = border_color, + );#.to_image(); + # 核心图片 + mainImg:BuildImage = images[0].convert("RGBA").resize((meme_width, meme_height), keep_ratio = True); + # tag 背景 + tagBG:BuildImage = BuildImage.new( + "RGBA", (meme_width, meme_height), (0, 0, 0, 0) + ).gradient_color( + LinearGradient( + (0, meme_height >> 1, meme_width >> 1, meme_height >> 1), + [ + ColorStop(1, (0, 0, 0, 0)), + ColorStop(0, (0, 0, 0, grayBG)) + ] + ) + if is_left else + LinearGradient( + (meme_width >> 1, meme_height >> 1, meme_width, meme_height >> 1), + [ + ColorStop(0, (0, 0, 0, 0)), + ColorStop(1, (0, 0, 0, grayBG)) + ] + ) + ); + # 内框点位 + border_pxy:list[tuple[float, float]] = [ + # 左上 + (side_weight, side_weight), + # 花费处边框 + (meme_width - side_weight - rankImg.longest_line - rankImg.height, side_weight), + (meme_width - (side_weight << 1) - rankImg.longest_line, side_weight + rankImg.height), + (meme_width - side_weight, side_weight + rankImg.height), + # 右下 + (meme_width - side_weight, meme_height - side_weight), + # 名字处边框 + (side_weight + nameImg.longest_line + nameImg.height, meme_height - side_weight), + ((side_weight << 1) + nameImg.longest_line, meme_height - side_weight - nameImg.height), + (side_weight, meme_height - side_weight - nameImg.height), + ]; + # 边框蒙版 + maskImg = BuildImage.new("RGBA", (meme_width, meme_height), (0, 0, 0, 255)).draw_polygon(border_pxy, fill = (0, 0, 0, 0)); + # 边框渐变色 + colorImp = BuildImage.new( + "RGBA", (meme_width, meme_height), (0, 0, 0, 0) + ).gradient_color( # 灰度 + LinearGradient( + (0, meme_height >> 1, meme_width, meme_height >> 1), + [ + ColorStop(0, (gray1, gray1, gray1, 255)), + ColorStop(1, (gray2, gray2, gray2, 255)) + ] + ) + ).color_mask(RANK_COLOR[rank if rank in range(len(RANK_COLOR)) else len(RANK_COLOR) - 1]); # 滤镜颜色 + # 边框图片 + borderImg = Image.composite(colorImp.image, Image.new("RGBA", colorImp.size), maskImg.image); + + # 合体! + mainImg.paste(tagBG, alpha = True); + mainImg.paste(borderImg, alpha = True); + #mainImg.paste(nameImg, ((side_weight << 1), meme_height - (side_weight >> 1) - nameImg.height), alpha = True); + #mainImg.paste(rankImg, (meme_width - rankImg.longest_line - (side_weight << 1), (side_weight >> 1)), alpha = True); + mainImg.draw_text( + ((side_weight << 1), meme_height - (side_weight >> 1) - nameImg.height), + name_text, + font_size = text_size, + fill = text_color, + font_style = "bold", + stroke_fill = border_color, + ); + mainImg.draw_text( + (meme_width - rankImg.longest_line - (side_weight << 1), (side_weight >> 1)), + rank_text, + font_size = text_size, + fill = text_color, + font_style = "bold", + stroke_fill = border_color, + ); + # 读取图标精灵 + class_little_sprite:BuildImage = BuildImage.open(IMG_DIR / "class_little_sprite.png"); + # 读取图标的字符串标识 + icon_types:list[str] = list(args.icons); + # 绘制标签 + it = iter(texts); + while True: + try: + text = next(it); + # 绘制坐标 + x:int = padding_side; + y:int = padding_top + int(nameImg.height); + # 绘制图标 + icon_index:int; + # 获取文字图片 + tagImg = Text2Image.from_text( + text, + text_size, + fill = text_color, + font_style = "bold", + stroke_width = 1, + stroke_fill = border_color, + );#.to_image(); + try: + icon_index = int(icon_types.pop(0)); + except: # 包括字符串无法转换成数字 或 字符串列表不够作为标识 + icon_index = 0; + if icon_index not in range(1,9): + icon_index = random.randint(1,9); + unit_size:int = class_little_sprite.height; + # 图标 + iconImp:BuildImage = class_little_sprite.crop( + ((icon_index - 1) * unit_size, 0, icon_index * unit_size, unit_size) + ).circle_corner(4); + dy:int = (int(tagImg.height) - iconImp.height) >> 1; # 图标高度修正 + mainImg.paste( + iconImp, + ( + x if is_left else meme_width - x - iconImp.width, + meme_height - y - iconImp.height - dy if is_left else y + dy + ), + alpha = True + ); + x += iconImp.width + 6; + # 绘制文字 + mainImg.draw_text( + ( + x if is_left else meme_width - x - tagImg.longest_line, + meme_height - y - tagImg.height if is_left else y + ), + text, + font_size = text_size, + fill = text_color, + font_style = "bold", + stroke_fill = border_color, + ); + # 迭代坐标 + padding_top += int(tagImg.height) + text_interval; + except StopIteration: # 迭代结束 + break; + + return mainImg.save_png(); + +add_meme( + "auto_chess_gen", + auto_chess_gen, + min_images = 1, + max_images = 1, + min_texts = 1, + max_texts = 9, + args_type = chess_args, + default_texts = ["Lunastic", "嘿,Meme Generator", "你动不动BUG日子结束了", "把表情交出来"], + keywords = ["自走棋", "autochess"], + date_created = datetime(2025, 3, 29), + date_modified = datetime(2025, 3, 30), +) \ No newline at end of file diff --git a/meme_generator/memes/auto_chess_gen/images/class_little_sprite.png b/meme_generator/memes/auto_chess_gen/images/class_little_sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..4e4308740dde017eef89c3342fe9d1addece7faa GIT binary patch literal 5488 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 mC@U+&v$FDkdno_J0QesgkKBp*r?Jlf0000