""" 图片处理工具模块 提供图片适配模式处理、像素与英寸转换等功能。 """ from PIL import Image, ImageOps from typing import Tuple, Optional def inches_to_pixels(inches: float, dpi: int = 96) -> float: """ 将英寸转换为像素 Args: inches: 英寸值 dpi: DPI(每英寸像素数),默认 96 Returns: 像素值 """ return inches * dpi def pixels_to_inches(pixels: float, dpi: int = 96) -> float: """ 将像素转换为英寸 Args: pixels: 像素值 dpi: DPI(每英寸像素数),默认 96 Returns: 英寸值 """ return pixels / dpi def create_canvas_with_background(size: Tuple[int, int], background_color: str) -> Image.Image: """ 创建带背景色的画布 Args: size: 画布尺寸 (width, height) background_color: 背景颜色(#RRGGBB 或 #RGB 格式) Returns: PIL Image 对象 """ # 验证颜色格式 import re if not isinstance(background_color, str): raise ValueError(f"无效的颜色格式: {background_color}") pattern = r'^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$' if not re.match(pattern, background_color): raise ValueError(f"无效的颜色格式: {background_color}") # 创建 RGB 模式的画布 canvas = Image.new('RGB', size, background_color) return canvas def apply_fit_mode( img: Image.Image, box_size: Tuple[int, int], fit: Optional[str] = None, background: Optional[str] = None ) -> Image.Image: """ 应用图片适配模式 Args: img: PIL Image 对象 box_size: 目标尺寸 (width, height) 像素 fit: 适配模式(stretch, contain, cover, center),默认 stretch background: 背景颜色(#RRGGBB 或 #RGB 格式),仅对 contain 和 center 模式有效 Returns: 处理后的 PIL Image 对象 """ if fit is None or fit == 'stretch': # stretch 模式:直接拉伸到目标尺寸 return img.resize(box_size, Image.Resampling.LANCZOS) elif fit == 'contain': # contain 模式:保持宽高比,完整显示在 box 内 # 如果图片比 box 小,保持原始尺寸;如果比 box 大,等比缩小 img_width, img_height = img.size box_width, box_height = box_size # 检查图片是否需要缩小 if img_width <= box_width and img_height <= box_height: # 图片比 box 小,保持原始尺寸 result = img else: # 图片比 box 大,使用 contain 缩小 result = ImageOps.contain(img, box_size, Image.Resampling.LANCZOS) # 如果指定了背景色,创建画布并居中粘贴 if background: canvas = create_canvas_with_background(box_size, background) # 计算居中位置 offset_x = (box_size[0] - result.width) // 2 offset_y = (box_size[1] - result.height) // 2 canvas.paste(result, (offset_x, offset_y)) return canvas return result elif fit == 'cover': # cover 模式:保持宽高比,填满 box,裁剪超出部分 result = ImageOps.cover(img, box_size, Image.Resampling.LANCZOS) # ImageOps.cover 可能返回比 box_size 大的图片,需要裁剪到精确尺寸 if result.size != box_size: # 从中心裁剪到目标尺寸 left = (result.width - box_size[0]) // 2 top = (result.height - box_size[1]) // 2 result = result.crop((left, top, left + box_size[0], top + box_size[1])) return result elif fit == 'center': # center 模式:不缩放,居中显示,超出部分裁剪 img_width, img_height = img.size box_width, box_height = box_size # 如果图片比 box 大,需要裁剪 if img_width > box_width or img_height > box_height: # 计算裁剪区域(从中心裁剪) left = max(0, (img_width - box_width) // 2) top = max(0, (img_height - box_height) // 2) right = min(img_width, left + box_width) bottom = min(img_height, top + box_height) result = img.crop((left, top, right, bottom)) else: result = img # 如果指定了背景色,创建画布并居中粘贴 if background: canvas = create_canvas_with_background(box_size, background) # 计算居中位置 offset_x = (box_size[0] - result.width) // 2 offset_y = (box_size[1] - result.height) // 2 canvas.paste(result, (offset_x, offset_y)) return canvas return result else: raise ValueError(f"不支持的 fit 模式: {fit}")