mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
Expand the pixel-art skill from 2 presets (arcade, snes) to 14 presets with hardware-accurate palettes (NES, Game Boy, PICO-8, C64, Apple II, MS Paint, CRT mono), plus a procedural video overlay pipeline. Ported from Synero/pixel-art-studio (MIT). Full attribution in ATTRIBUTION.md. What's in: - scripts/palettes.py — 28 named RGB palettes (hardware + artistic) - scripts/pixel_art.py — 14 presets, named palette support, CLI - scripts/pixel_art_video.py — 12 animation scenes (stars, rain, fireflies, snow, embers, lightning, etc.) → MP4/GIF via ffmpeg - references/palettes.md — palette catalog - SKILL.md — clarify-tool workflow (offer style, then optional scene) What's out (intentional): - Wu's quantizer (PIL's built-in quantize suffices) - Sobel edge-aware downsample (scipy dep not worth it) - Atkinson/Bayer dither (would need numpy reimpl) - Pollinations text-to-image (Hermes uses image_generate instead) Video pipeline uses subprocess.run with check=True (replaces os.system) and tempfile.TemporaryDirectory (replaces manual cleanup).
167 lines
7.3 KiB
Python
167 lines
7.3 KiB
Python
"""Named RGB palettes for pixel_art() and pixel_art_video().
|
|
|
|
Palette RGB values sourced from pixel-art-studio (MIT License)
|
|
https://github.com/Synero/pixel-art-studio — see ATTRIBUTION.md.
|
|
"""
|
|
|
|
PALETTES = {
|
|
# ── Hardware palettes ───────────────────────────────────────────────
|
|
"NES": [
|
|
(0, 0, 0), (124, 124, 124), (0, 0, 252), (0, 0, 188), (68, 40, 188),
|
|
(148, 0, 132), (168, 0, 32), (168, 16, 0), (136, 20, 0), (0, 116, 0),
|
|
(0, 148, 0), (0, 120, 0), (0, 88, 0), (0, 64, 88), (188, 188, 188),
|
|
(0, 120, 248), (0, 88, 248), (104, 68, 252), (216, 0, 204), (228, 0, 88),
|
|
(248, 56, 0), (228, 92, 16), (172, 124, 0), (0, 184, 0), (0, 168, 0),
|
|
(0, 168, 68), (0, 136, 136), (248, 248, 248), (60, 188, 252),
|
|
(104, 136, 252), (152, 120, 248), (248, 120, 248), (248, 88, 152),
|
|
(248, 120, 88), (252, 160, 68), (248, 184, 0), (184, 248, 24),
|
|
(88, 216, 84), (88, 248, 152), (0, 232, 216), (120, 120, 120),
|
|
(252, 252, 252), (164, 228, 252), (184, 184, 248), (216, 184, 248),
|
|
(248, 184, 248), (248, 164, 192), (240, 208, 176), (252, 224, 168),
|
|
(248, 216, 120), (216, 248, 120), (184, 248, 184), (184, 248, 216),
|
|
(0, 252, 252), (216, 216, 216),
|
|
],
|
|
"C64": [
|
|
(0, 0, 0), (255, 255, 255), (161, 77, 67), (106, 191, 199),
|
|
(161, 87, 164), (92, 172, 95), (64, 64, 223), (191, 206, 137),
|
|
(161, 104, 60), (108, 80, 21), (203, 126, 117), (98, 98, 98),
|
|
(137, 137, 137), (154, 226, 155), (124, 124, 255), (173, 173, 173),
|
|
],
|
|
"COMMODORE_64": [
|
|
(0, 0, 0), (255, 255, 255), (161, 77, 67), (106, 192, 200),
|
|
(161, 87, 165), (92, 172, 95), (64, 68, 227), (203, 214, 137),
|
|
(163, 104, 58), (110, 84, 11), (204, 127, 118), (99, 99, 99),
|
|
(139, 139, 139), (154, 227, 157), (139, 127, 205), (175, 175, 175),
|
|
],
|
|
"ZX_SPECTRUM": [
|
|
(0, 0, 0), (0, 39, 251), (252, 48, 22), (255, 63, 252),
|
|
(0, 249, 44), (0, 252, 254), (255, 253, 51), (255, 255, 255),
|
|
],
|
|
"APPLE_II_LO": [
|
|
(0, 0, 0), (133, 59, 81), (80, 71, 137), (234, 93, 240),
|
|
(0, 104, 82), (146, 146, 146), (0, 168, 241), (202, 195, 248),
|
|
(81, 92, 15), (235, 127, 35), (146, 146, 146), (246, 185, 202),
|
|
(0, 202, 41), (203, 211, 155), (155, 220, 203), (255, 255, 255),
|
|
],
|
|
"APPLE_II_HI": [
|
|
(0, 0, 0), (255, 0, 255), (0, 255, 0), (255, 255, 255),
|
|
(0, 175, 255), (255, 80, 0),
|
|
],
|
|
"GAMEBOY_ORIGINAL": [
|
|
(0, 63, 0), (46, 115, 32), (140, 191, 10), (160, 207, 10),
|
|
],
|
|
"GAMEBOY_POCKET": [
|
|
(0, 0, 0), (85, 85, 85), (170, 170, 170), (255, 255, 255),
|
|
],
|
|
"GAMEBOY_VIRTUALBOY": [
|
|
(239, 0, 0), (164, 0, 0), (85, 0, 0), (0, 0, 0),
|
|
],
|
|
"PICO_8": [
|
|
(0, 0, 0), (29, 43, 83), (126, 37, 83), (0, 135, 81), (171, 82, 54),
|
|
(95, 87, 79), (194, 195, 199), (255, 241, 232), (255, 0, 77),
|
|
(255, 163, 0), (255, 236, 39), (0, 228, 54), (41, 173, 255),
|
|
(131, 118, 156), (255, 119, 168), (255, 204, 170),
|
|
],
|
|
"TELETEXT": [
|
|
(0, 0, 0), (255, 0, 0), (0, 128, 0), (255, 255, 0),
|
|
(0, 0, 255), (255, 0, 255), (0, 255, 255), (255, 255, 255),
|
|
],
|
|
"CGA_MODE4_PAL1": [
|
|
(0, 0, 0), (255, 255, 255), (0, 255, 255), (255, 0, 255),
|
|
],
|
|
"MSX": [
|
|
(0, 0, 0), (62, 184, 73), (116, 208, 125), (89, 85, 224),
|
|
(128, 118, 241), (185, 94, 81), (101, 219, 239), (219, 101, 89),
|
|
(255, 137, 125), (204, 195, 94), (222, 208, 135), (58, 162, 65),
|
|
(183, 102, 181), (204, 204, 204), (255, 255, 255),
|
|
],
|
|
"MICROSOFT_WINDOWS_16": [
|
|
(0, 0, 0), (128, 0, 0), (0, 128, 0), (128, 128, 0), (0, 0, 128),
|
|
(128, 0, 128), (0, 128, 128), (192, 192, 192), (128, 128, 128),
|
|
(255, 0, 0), (0, 255, 0), (255, 255, 0), (0, 0, 255),
|
|
(255, 0, 255), (0, 255, 255), (255, 255, 255),
|
|
],
|
|
"MICROSOFT_WINDOWS_PAINT": [
|
|
(0, 0, 0), (255, 255, 255), (123, 123, 123), (189, 189, 189),
|
|
(123, 12, 2), (255, 37, 0), (123, 123, 2), (255, 251, 2),
|
|
(0, 123, 2), (2, 249, 2), (0, 123, 122), (2, 253, 254),
|
|
(2, 19, 122), (5, 50, 255), (123, 25, 122), (255, 64, 254),
|
|
(122, 57, 2), (255, 122, 57), (123, 123, 56), (255, 252, 122),
|
|
(2, 57, 57), (5, 250, 123), (0, 123, 255), (255, 44, 123),
|
|
],
|
|
"MONO_BW": [(0, 0, 0), (255, 255, 255)],
|
|
"MONO_AMBER": [(40, 40, 40), (255, 176, 0)],
|
|
"MONO_GREEN": [(40, 40, 40), (51, 255, 51)],
|
|
|
|
# ── Artistic palettes ───────────────────────────────────────────────
|
|
"PASTEL_DREAM": [
|
|
(255, 218, 233), (255, 229, 204), (255, 255, 204), (204, 255, 229),
|
|
(204, 229, 255), (229, 204, 255), (255, 204, 229), (204, 255, 255),
|
|
(255, 245, 220), (230, 230, 250),
|
|
],
|
|
"NEON_CYBER": [
|
|
(0, 0, 0), (255, 0, 128), (0, 255, 255), (255, 0, 255),
|
|
(0, 255, 128), (255, 255, 0), (128, 0, 255), (255, 128, 0),
|
|
(0, 128, 255), (255, 255, 255),
|
|
],
|
|
"RETRO_WARM": [
|
|
(62, 39, 35), (139, 69, 19), (210, 105, 30), (244, 164, 96),
|
|
(255, 218, 185), (255, 245, 238), (178, 34, 34), (205, 92, 92),
|
|
(255, 99, 71), (255, 160, 122),
|
|
],
|
|
"OCEAN_DEEP": [
|
|
(0, 25, 51), (0, 51, 102), (0, 76, 153), (0, 102, 178),
|
|
(0, 128, 204), (51, 153, 204), (102, 178, 204), (153, 204, 229),
|
|
(204, 229, 255), (229, 245, 255),
|
|
],
|
|
"FOREST_MOSS": [
|
|
(34, 51, 34), (51, 76, 51), (68, 102, 51), (85, 128, 68),
|
|
(102, 153, 85), (136, 170, 102), (170, 196, 136), (204, 221, 170),
|
|
(238, 238, 204), (245, 245, 220),
|
|
],
|
|
"SUNSET_FIRE": [
|
|
(51, 0, 0), (102, 0, 0), (153, 0, 0), (204, 0, 0), (255, 0, 0),
|
|
(255, 51, 0), (255, 102, 0), (255, 153, 0), (255, 204, 0),
|
|
(255, 255, 51),
|
|
],
|
|
"ARCTIC_ICE": [
|
|
(0, 0, 51), (0, 0, 102), (0, 51, 153), (0, 102, 153),
|
|
(51, 153, 204), (102, 204, 255), (153, 229, 255), (204, 242, 255),
|
|
(229, 247, 255), (255, 255, 255),
|
|
],
|
|
"VINTAGE_ROSE": [
|
|
(103, 58, 63), (137, 72, 81), (170, 91, 102), (196, 113, 122),
|
|
(219, 139, 147), (232, 168, 175), (240, 196, 199), (245, 215, 217),
|
|
(249, 232, 233), (255, 245, 245),
|
|
],
|
|
"EARTH_CLAY": [
|
|
(62, 39, 35), (89, 56, 47), (116, 73, 59), (143, 90, 71),
|
|
(170, 107, 83), (197, 124, 95), (210, 155, 126), (222, 186, 160),
|
|
(235, 217, 196), (248, 248, 232),
|
|
],
|
|
"ELECTRIC_VIOLET": [
|
|
(26, 0, 51), (51, 0, 102), (76, 0, 153), (102, 0, 204),
|
|
(128, 0, 255), (153, 51, 255), (178, 102, 255), (204, 153, 255),
|
|
(229, 204, 255), (245, 229, 255),
|
|
],
|
|
}
|
|
|
|
|
|
def build_palette_image(palette_name):
|
|
"""Build a 1x1 PIL 'P'-mode image with the named palette for Image.quantize(palette=...)."""
|
|
from PIL import Image
|
|
|
|
if palette_name not in PALETTES:
|
|
raise ValueError(
|
|
f"Unknown palette {palette_name!r}. "
|
|
f"Choose from: {sorted(PALETTES)}"
|
|
)
|
|
flat = []
|
|
for (r, g, b) in PALETTES[palette_name]:
|
|
flat.extend([r, g, b])
|
|
# Pad to 768 bytes (256 colors) as PIL requires
|
|
while len(flat) < 768:
|
|
flat.append(0)
|
|
pal_img = Image.new("P", (1, 1))
|
|
pal_img.putpalette(flat)
|
|
return pal_img
|