codepet 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codepet",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "在编程软件里养电子宠物 — 支持 Claude Code / Codex / Cursor / VS Code 等 10+ 平台",
5
5
  "main": "core/index.js",
6
6
  "bin": {
@@ -47,6 +47,7 @@
47
47
  "scripts/img2ascii.py",
48
48
  "scripts/img2emoji.py",
49
49
  "scripts/polaroid.py",
50
+ "scripts/polaroid_image.py",
50
51
  "scripts/pet_card.py",
51
52
  "scripts/render_to_image.py",
52
53
  "scripts/show_all.py",
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env python3
2
+ """拍立得相框 — 像素风 PNG 图片版"""
3
+
4
+ import sys, os, json, platform
5
+ from PIL import Image, ImageDraw, ImageFont, ImageFilter
6
+
7
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
8
+ SPRITE_DIR = os.path.join(SCRIPT_DIR, "..", "sprites")
9
+ PET_JSON = os.path.join(os.path.expanduser("~"), ".codepet", "pet.json")
10
+ OUTPUT = os.path.join(os.path.expanduser("~"), ".codepet", "photo.png")
11
+
12
+ SCENE_ZH = {
13
+ 'normal': '日常', 'happy': '开心的瞬间', 'sleep': '睡着了',
14
+ 'eat': '在吃东西', 'pet': '被摸摸的样子', 'worry': '有点紧张',
15
+ }
16
+
17
+ def load_font(size):
18
+ system = platform.system()
19
+ candidates = []
20
+ if system == "Darwin":
21
+ candidates = ["/System/Library/Fonts/PingFang.ttc", "/System/Library/Fonts/Hiragino Sans GB.ttc"]
22
+ elif system == "Windows":
23
+ windir = os.environ.get("WINDIR", "C:\\Windows")
24
+ candidates = [os.path.join(windir, "Fonts", "msyh.ttc"), os.path.join(windir, "Fonts", "simhei.ttf"), os.path.join(windir, "Fonts", "arial.ttf")]
25
+ else:
26
+ candidates = ["/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"]
27
+ for fp in candidates:
28
+ if os.path.exists(fp):
29
+ try:
30
+ return ImageFont.truetype(fp, size)
31
+ except:
32
+ continue
33
+ return ImageFont.load_default()
34
+
35
+ def pixelate(img, pixel_size=8):
36
+ """把图片像素化 — 缩小再放大,产生像素颗粒感"""
37
+ w, h = img.size
38
+ small = img.resize((w // pixel_size, h // pixel_size), Image.NEAREST)
39
+ return small.resize((w, h), Image.NEAREST)
40
+
41
+ def main():
42
+ character = sys.argv[1] if len(sys.argv) > 1 else 'bagayalu'
43
+ scene = sys.argv[2] if len(sys.argv) > 2 else 'normal'
44
+
45
+ img_path = os.path.join(SPRITE_DIR, character, f"{scene}.png")
46
+ if not os.path.exists(img_path):
47
+ img_path = os.path.join(SPRITE_DIR, f"{character}.png")
48
+
49
+ name = character
50
+ try:
51
+ with open(PET_JSON, 'r', encoding='utf-8') as f:
52
+ pet = json.load(f)
53
+ name = pet.get('nickname', pet.get('name', character))
54
+ except:
55
+ pass
56
+
57
+ scene_zh = SCENE_ZH.get(scene, '一个瞬间')
58
+
59
+ # 加载并像素化角色图片
60
+ sprite = Image.open(img_path).convert("RGBA")
61
+
62
+ # 统一缩放到 400px 宽
63
+ target_w = 400
64
+ ratio = target_w / sprite.width
65
+ target_h = int(sprite.height * ratio)
66
+ sprite = sprite.resize((target_w, target_h), Image.LANCZOS)
67
+
68
+ # 像素化处理 — 产生复古像素风
69
+ sprite = pixelate(sprite, pixel_size=6)
70
+
71
+ # 拍立得尺寸
72
+ padding = 50
73
+ bottom_area = 140
74
+ photo_w = sprite.width + padding * 2
75
+ photo_h = sprite.height + padding + bottom_area
76
+
77
+ # 奶白色拍立得底板
78
+ card = Image.new("RGBA", (photo_w, photo_h), (252, 250, 245, 255))
79
+
80
+ # 贴像素化的角色图片
81
+ card.paste(sprite, (padding, padding), sprite)
82
+
83
+ draw = ImageDraw.Draw(card)
84
+
85
+ # 图片区域细边框
86
+ draw.rectangle(
87
+ [padding - 2, padding - 2, padding + sprite.width + 1, padding + sprite.height + 1],
88
+ outline=(220, 218, 210), width=1
89
+ )
90
+
91
+ # 外边框阴影
92
+ draw.rectangle([0, 0, photo_w - 1, photo_h - 1], outline=(210, 208, 200), width=2)
93
+
94
+ # 底部文字
95
+ font_caption = load_font(20)
96
+ font_studio = load_font(15)
97
+
98
+ caption = f"📸 刚才抓拍到了{name}{scene_zh}..."
99
+ studio = "🐋 Ai小蓝鲸照相馆"
100
+
101
+ caption_y = sprite.height + padding + 20
102
+ studio_y = caption_y + 45
103
+
104
+ draw.text((padding, caption_y), caption, fill=(80, 80, 80), font=font_caption)
105
+ draw.text((padding, studio_y), studio, fill=(160, 158, 150), font=font_studio)
106
+
107
+ # 保存
108
+ os.makedirs(os.path.dirname(OUTPUT), exist_ok=True)
109
+ card_rgb = Image.new("RGB", card.size, (252, 250, 245))
110
+ card_rgb.paste(card, mask=card.split()[3])
111
+ card_rgb.save(OUTPUT, "PNG", quality=95)
112
+
113
+ print(OUTPUT)
114
+
115
+ if __name__ == '__main__':
116
+ main()