diff --git a/assets/img/meme/anan_base.png b/assets/img/meme/anan_base.png new file mode 100644 index 0000000..13c60bf Binary files /dev/null and b/assets/img/meme/anan_base.png differ diff --git a/assets/img/meme/anan_top.png b/assets/img/meme/anan_top.png new file mode 100644 index 0000000..5da1d36 Binary files /dev/null and b/assets/img/meme/anan_top.png differ diff --git a/konabot/plugins/memepack/__init__.py b/konabot/plugins/memepack/__init__.py index d95d70e..d211223 100644 --- a/konabot/plugins/memepack/__init__.py +++ b/konabot/plugins/memepack/__init__.py @@ -19,6 +19,7 @@ from konabot.common.nb.extract_image import PIL_Image, extract_image_from_messag from konabot.plugins.memepack.drawing.display import ( draw_cao_display, draw_snaur_display, + draw_anan_display, ) from konabot.plugins.memepack.drawing.saying import ( draw_cute_ten, @@ -244,3 +245,36 @@ async def _( img_processed.save(img_data, "PNG") await snaur_display_cmd.send(await UniMessage().image(raw=img_data).export()) +anan_display_cmd = on_alconna( + Alconna( + "安安展示", + Args["image", Image], + ), +) +@anan_display_cmd.handle() +async def _(msg: UniMsg, evt: Event, bot: Bot): + flag = False + for text in cast(Iterable[Text], msg.get(Text)): + if text.text.strip() == "安安展示": + flag = True + elif text.text.strip() == "": + continue + else: + return + if not flag: + return + match await extract_image_from_message(evt.get_message(), evt, bot): + case Success(img): + img_handled = await draw_anan_display(img) + img_bytes = BytesIO() + img_handled.save(img_bytes, format="PNG") + await anan_display_cmd.send(await UniMessage().image(raw=img_bytes).export()) + case Failure(err): + await anan_display_cmd.send( + await UniMessage() + .at(user_id=evt.get_user_id()) + .text(" ") + .text(err) + .export() + ) + diff --git a/konabot/plugins/memepack/drawing/display.py b/konabot/plugins/memepack/drawing/display.py index 21825ea..2997934 100644 --- a/konabot/plugins/memepack/drawing/display.py +++ b/konabot/plugins/memepack/drawing/display.py @@ -27,6 +27,15 @@ SNAUR_QUAD_POINTS = np.float32(cast(Any, [ [106, 1280], ])) +anan_image_base = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "anan_base.png") +anan_image_top = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "anan_top.png") +ANAN_QUAD_POINTS = np.float32(cast(Any, [ + [150, 250], + [650, 250], + [630, 750], + [170, 750], +])) + def _draw_cao_display(image: PIL.Image.Image): src = np.array(image.convert("RGB")) h, w = src.shape[:2] @@ -139,3 +148,50 @@ async def draw_snaur_display( opacity, saturation, ) + +def _draw_anan_display(image: PIL.Image.Image) -> PIL.Image.Image: + src = np.array(image.convert("RGB")) + h, w = src.shape[:2] + + src_points = np.float32(cast(Any, [ + [0, 0], + [w, 0], + [w, h], + [0, h] + ])) + dst_points = ANAN_QUAD_POINTS + M = cv2.getPerspectiveTransform(cast(Any, src_points), cast(Any, dst_points)) + output_w, output_h = anan_image_top.size + src_rgb = cv2.cvtColor(src, cv2.COLOR_RGBA2RGB) if src.shape[2] == 4 else src + warped_rgb = cv2.warpPerspective( + src_rgb, + M, + (output_w, output_h), + flags=cv2.INTER_LINEAR, + borderMode=cv2.BORDER_CONSTANT, + borderValue=(0, 0, 0) + ) + + mask = np.zeros((h, w), dtype=np.uint8) + mask[:, :] = 255 + warped_mask = cv2.warpPerspective( + mask, + M, + (output_w, output_h), + flags=cv2.INTER_LINEAR, + borderMode=cv2.BORDER_CONSTANT, + borderValue=0 + ) + + warped_rgba = cv2.cvtColor(warped_rgb, cv2.COLOR_RGB2RGBA) + warped_rgba[:, :, 3] = warped_mask + + warped_pil = PIL.Image.fromarray(warped_rgba, 'RGBA') + + result = PIL.Image.alpha_composite(anan_image_base, warped_pil) + result = PIL.Image.alpha_composite(result, anan_image_top) + return result + + +async def draw_anan_display(image: PIL.Image.Image) -> PIL.Image.Image: + return await asyncio.to_thread(_draw_anan_display, image) \ No newline at end of file