🐛 修复制图wrong mode报错

This commit is contained in:
CMHopeSunshine 2023-07-21 13:45:04 +08:00
parent 9ba266b038
commit 783928fafa

View File

@ -1,38 +1,38 @@
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from typing import Tuple, Union, Literal, List, Optional, Dict from typing import Tuple, Union, Literal, List, Optional, Dict, Any
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont, ImageOps from PIL import Image, ImageDraw, ImageFont, ImageOps
from PIL.ImageFont import FreeTypeFont
from nonebot.utils import run_sync from nonebot.utils import run_sync
from LittlePaimon.config import config from LittlePaimon.config import config
from .path import FONTS_PATH from .path import FONTS_PATH
from .requests import aiorequests from .requests import aiorequests
plt.switch_backend('agg')
class PMImage: class PMImage:
def __init__(self, def __init__(self,
image: Union[Image.Image, Path] = None, image: Union[Image.Image, Path, None] = None,
*, *,
size: Tuple[int, int] = (200, 200), size: Tuple[int, int] = (200, 200),
color: Union[str, Tuple[int, int, int, int]] = (255, 255, 255), color: Union[str, Tuple[int, int, int, int], Tuple[int, int, int]] = (255, 255, 255, 255),
mode: str = 'RGBA' mode: Literal["1", "CMYK", "F", "HSV", "I", "L", "LAB", "P", "RGB", "RGBA", "RGBX", "YCbCr"] = 'RGBA'
): ):
""" """
初始化图像优先读取image参数如无则新建图像 初始化图像优先读取image参数如无则新建图像
:param image: PIL对象或图像路径
:param size: 图像大小 :param image: PIL对象或图像路径
:param color: 图像颜色 :param size: 图像大小
:param mode: 图像模式 :param color: 图像颜色
:param mode: 图像模式
""" """
if image: if image:
self.image = Image.open(image) if isinstance(image, Path) else image.copy() self.image = Image.open(image) if isinstance(image, Path) else image.copy()
else: else:
if mode == 'RGB': if mode == 'RGB' and isinstance(color, tuple):
color = (color[0], color[1], color[2]) color = (color[0], color[1], color[2])
self.image = Image.new(mode, size, color) self.image = Image.new(mode, size, color)
self.draw = ImageDraw.Draw(self.image) self.draw = ImageDraw.Draw(self.image)
@ -61,12 +61,13 @@ class PMImage:
self.image = Image.new(size=size, color=color, mode=mode) self.image = Image.new(size=size, color=color, mode=mode)
def convert(self, mode: str): def convert(self, mode: str):
self.image.convert(mode) self.image = self.image.convert(mode)
def save(self, path: Union[str, Path], **kwargs): def save(self, path: Union[str, Path], **kwargs):
""" """
保存图像 保存图像
:param path: 保存路径
:param path: 保存路径
""" """
self.image.save(path, **kwargs) self.image.save(path, **kwargs)
@ -81,7 +82,8 @@ class PMImage:
def copy(self) -> "PMImage": def copy(self) -> "PMImage":
""" """
返回一个本对象的复制 返回一个本对象的复制
:return: PMImage
:return: PMImage
""" """
return PMImage(self.image.copy()) return PMImage(self.image.copy())
@ -95,7 +97,7 @@ class PMImage:
text: str, text: str,
width: Tuple[int, int], width: Tuple[int, int],
height: Tuple[int, int], height: Tuple[int, int],
font: ImageFont.ImageFont = None) -> int: font: ImageFont.ImageFont) -> int:
text_height = self.draw.textsize(text, font=font)[1] text_height = self.draw.textsize(text, font=font)[1]
width_now = width[0] width_now = width[0]
height_now = height[0] height_now = height[0]
@ -159,6 +161,7 @@ class PMImage:
image = image.image image = image.image
if alpha: if alpha:
image = image.convert('RGBA') image = image.convert('RGBA')
self.convert('RGBA')
self.image.alpha_composite(image, pos) self.image.alpha_composite(image, pos)
else: else:
self.image.paste(image, pos) self.image.paste(image, pos)
@ -169,7 +172,7 @@ class PMImage:
text: str, text: str,
width: Union[float, Tuple[float, float]], width: Union[float, Tuple[float, float]],
height: Union[float, Tuple[float, float]], height: Union[float, Tuple[float, float]],
font: ImageFont.ImageFont = None, font: ImageFont.ImageFont,
color: Union[str, Tuple[int, int, int, int]] = 'white', color: Union[str, Tuple[int, int, int, int]] = 'white',
align: Literal['left', 'center', 'right'] = 'left' align: Literal['left', 'center', 'right'] = 'left'
): ):
@ -210,7 +213,7 @@ class PMImage:
text: str, text: str,
width: Tuple[int, int], width: Tuple[int, int],
height: Tuple[int, int], height: Tuple[int, int],
font: ImageFont.ImageFont = None, font: ImageFont.ImageFont,
color: Union[str, Tuple[int, int, int, int]] = 'white'): color: Union[str, Tuple[int, int, int, int]] = 'white'):
text_height = self.draw.textsize(text, font=font)[1] text_height = self.draw.textsize(text, font=font)[1]
width_now = width[0] width_now = width[0]
@ -322,11 +325,12 @@ class PMImage:
angles: List[Literal['ul', 'ur', 'll', 'lr']] = None): angles: List[Literal['ul', 'ur', 'll', 'lr']] = None):
""" """
选择最多4个角绘制圆角矩形 选择最多4个角绘制圆角矩形
:param pos: 左上角起点坐标
:param size: 矩形大小 :param pos: 左上角起点坐标
:param radius: 半径 :param size: 矩形大小
:param color: 颜色 :param radius: 半径
:param angles: 角列表 :param color: 颜色
:param angles: 角列表
""" """
self.draw.rectangle((pos[0] + radius / 2, pos[1], pos[0] + size[0] - (radius / 2), pos[1] + size[1]), self.draw.rectangle((pos[0] + radius / 2, pos[1], pos[0] + size[0] - (radius / 2), pos[1] + size[1]),
fill=color) fill=color)
@ -371,13 +375,14 @@ class PMImage:
): ):
""" """
画百分比圆环 画百分比圆环
:param size: 圆环大小
:param pos: 圆环位置 :param size: 圆环大小
:param width: 圆环宽度 :param pos: 圆环位置
:param percent: 百分比 :param width: 圆环宽度
:param colors: 颜色 :param percent: 百分比
:param startangle: 角度 :param colors: 颜色
:param transparent: 是否透明 :param startangle: 角度
:param transparent: 是否透明
""" """
if isinstance(percent, float): if isinstance(percent, float):
if percent < 0: if percent < 0:
@ -432,7 +437,7 @@ class PMImage:
antialias = 4 antialias = 4
ellipse_box = [0, 0, r2 - 2, r2 - 2] ellipse_box = [0, 0, r2 - 2, r2 - 2]
mask = Image.new( mask = Image.new(
size=[int(dim * antialias) for dim in self.image.size], size=(int(self.image.size[0] * antialias), int(self.image.size[1] * antialias)),
mode="L", mode="L",
color="black") color="black")
draw = ImageDraw.Draw(mask) draw = ImageDraw.Draw(mask)
@ -499,12 +504,13 @@ class FontManager:
self.fonts = fonts self.fonts = fonts
self.fonts_cache = {} self.fonts_cache = {}
def get(self, font_name: str = 'hywh.ttf', size: int = 25, variation: str = None) -> ImageFont.ImageFont: def get(self, font_name: str = 'hywh.ttf', size: int = 25, variation: Optional[str] = None) -> FreeTypeFont:
""" """
获取字体如果已在缓存中则直接返回 获取字体如果已在缓存中则直接返回
:param font_name: 字体名称
:param size: 字体大小 :param font_name: 字体名称
:param variation: 字体变体 :param size: 字体大小
:param variation: 字体变体
""" """
if 'ttf' not in font_name and 'ttc' not in font_name and 'otf' not in font_name: if 'ttf' not in font_name and 'ttc' not in font_name and 'otf' not in font_name:
font_name += '.ttf' font_name += '.ttf'
@ -524,7 +530,7 @@ class FontManager:
font_manager = FontManager() font_manager = FontManager()
cache_image: Dict[str, any] = {} cache_image: Dict[str, Any] = {}
headers = { headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'} 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'}
@ -538,12 +544,14 @@ async def load_image(
) -> Image.Image: ) -> Image.Image:
""" """
读取图像并预处理 读取图像并预处理
:param path: 图片路径
:param size: 预处理尺寸 :param path: 图片路径
:param crop: 预处理裁剪大小 :param size: 预处理尺寸
:param mode: 预处理图像模式 :param crop: 预处理裁剪大小
:return: 图像对象 :param mode: 预处理图像模式
:return: 图像对象
""" """
path = Path(path)
if config.img_use_cache and str(path) in cache_image: if config.img_use_cache and str(path) in cache_image:
img = cache_image[str(path)] img = cache_image[str(path)]
else: else:
@ -552,7 +560,7 @@ async def load_image(
elif path.name.startswith(('UI_', 'Skill_')): elif path.name.startswith(('UI_', 'Skill_')):
img = await aiorequests.download_icon(path.name, headers=headers, save_path=path, follow_redirects=True) img = await aiorequests.download_icon(path.name, headers=headers, save_path=path, follow_redirects=True)
if img is None or isinstance(img, str): if img is None or isinstance(img, str):
return Image.new('RGBA', size=size or (50, 50), color=(0, 0, 0, 0)) return Image.new('RGBA', size=size if isinstance(size, tuple) else (50, 50), color=(0, 0, 0, 0))
else: else:
raise FileNotFoundError(f'{path} not found') raise FileNotFoundError(f'{path} not found')
if config.img_use_cache: if config.img_use_cache: