Created
March 11, 2025 05:52
-
-
Save minyoung/22d3348ca3a99c9ab8c8a26ad4675eb2 to your computer and use it in GitHub Desktop.
Migrate old darkest dungeon 1 skins to new atlas offsets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"size": [645, 548], | |
"transforms": [ | |
{ | |
"source": [2, 13, 92, 163], | |
"rotate": -90, | |
"target": [487, 333] | |
}, | |
{ | |
"source": [133, 48, 213, 173], | |
"rotate": -90, | |
"target": [478, 466] | |
}, | |
{ | |
"source": [217, 22, 308, 181], | |
"rotate": -90, | |
"target": [257, 389, 416, 480] | |
}, | |
{ | |
"source": [311, 25, 447, 167], | |
"target": [112, 326, 248, 468] | |
}, | |
{ | |
"source": [450, 5, 516, 53], | |
"target": [183, 44] | |
}, | |
{ | |
"source": [467, 56, 668, 173], | |
"rotate": -90, | |
"target": [118, 111, 235, 312] | |
}, | |
{ | |
"source": [523, 16, 592, 53], | |
"target": [28, 11, 97, 48] | |
}, | |
{ | |
"source": [595, 14, 618, 53], | |
"target": [2, 9, 25, 48] | |
}, | |
{ | |
"source": [671, 3, 749, 58], | |
"rotate": 90, | |
"target": [574, 31, 629, 109] | |
}, | |
{ | |
"source": [671, 61, 770, 179], | |
"rotate": -90, | |
"target": [257, 298, 375, 397] | |
}, | |
{ | |
"source": [759, 5, 807, 55], | |
"target": [577, 112, 625, 162] | |
}, | |
{ | |
"source": [773, 63, 871, 179], | |
"rotate": -90, | |
"target": [257, 200, 373, 298] | |
}, | |
{ | |
"source": [818, 3, 907, 60], | |
"rotate": -90, | |
"target": [112, 2, 169, 91] | |
}, | |
{ | |
"source": [879, 70, 981, 177], | |
"target": [471, 205, 573, 312] | |
}, | |
{ | |
"source": [910, 3, 972, 67], | |
"rotate": -90, | |
"target": [576, 168, 640, 230] | |
}, | |
{ | |
"source": [975, 5, 1331, 67], | |
"target": [112, 483, 468, 545] | |
}, | |
{ | |
"source": [990, 72, 1312, 179], | |
"rotate": -90, | |
"target": [2, 54, 109, 376] | |
}, | |
{ | |
"source": [1336, 7, 1402, 69], | |
"rotate": 90, | |
"target": [581, 246, 643, 312] | |
}, | |
{ | |
"source": [1420, 2, 1493, 69], | |
"target": [394, 413, 467, 480] | |
}, | |
{ | |
"source": [1421, 102, 1477, 150], | |
"rotate": -90, | |
"target": [31, 489, 79, 545] | |
}, | |
{ | |
"source": [1480, 74, 1583, 179], | |
"rotate": -90, | |
"target": [236, 95, 341, 198] | |
}, | |
{ | |
"source": [1498, 14, 1568, 66], | |
"rotate": -90, | |
"target": [509, 132, 561, 202] | |
}, | |
{ | |
"source": [1583, 5, 1656, 71], | |
"target": [392, 344, 465, 410] | |
}, | |
{ | |
"source": [1587, 83, 1666, 179], | |
"rotate": 90, | |
"target": [402, 22, 498, 101] | |
}, | |
{ | |
"source": [1659, 2, 1736, 72], | |
"rotate": 90, | |
"target": [376, 200, 446, 277] | |
}, | |
{ | |
"source": [1669, 102, 1728, 179], | |
"rotate": -90, | |
"target": [378, 280, 455, 339] | |
}, | |
{ | |
"source": [1733, 94, 1784, 168], | |
"target": [258, 7, 309, 81] | |
}, | |
{ | |
"source": [1739, 20, 1818, 91], | |
"target": [312, 21, 391, 92] | |
}, | |
{ | |
"source": [1787, 102, 1865, 179], | |
"target": [422, 120, 500, 197] | |
}, | |
{ | |
"source": [1821, 25, 1876, 83], | |
"target": [501, 43, 556, 101] | |
}, | |
{ | |
"source": [1872, 121, 1917, 166], | |
"rotate": -90, | |
"target": [352, 129] | |
}, | |
{ | |
"source": [1895, 43, 1966, 103], | |
"rotate": -90, | |
"target": [526, 316] | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import json | |
import sys | |
from PIL import Image | |
def dissect(filename, export_sprites=True): | |
image = Image.open(filename) | |
size = image.size | |
print('Image size:', size) | |
seen = set() | |
def find_pixel(resume): | |
x, y = resume | |
while x < size[0]: | |
while y < size[1]: | |
coord = (x, y) | |
if coord in seen: | |
y += 1 | |
continue | |
if image.getpixel(coord): | |
return coord | |
y += 1 | |
y = 0 | |
x += 1 | |
return None | |
sprite_start = (0, 0) | |
sprites = [] | |
while True: | |
sprite_start = find_pixel(sprite_start) | |
if sprite_start is None: | |
break | |
sprite_bbox = [None, None, None, None] | |
def check_coord(coord): | |
if coord in seen: | |
return None | |
for i in range(2): | |
if coord[i] < 0: | |
return None | |
if coord[i] >= size[i]: | |
return None | |
seen.add(coord) | |
pixel = image.getpixel(coord) | |
if not pixel or pixel[3] == 0: | |
return None | |
for i in range(2): | |
if sprite_bbox[i] is None or sprite_bbox[i] > coord[i]: | |
sprite_bbox[i] = coord[i] | |
if sprite_bbox[2+i] is None or sprite_bbox[2+i] < coord[i]: | |
sprite_bbox[2+i] = coord[i] | |
return coord | |
seen.add(sprite_start) | |
queue = [sprite_start] | |
while queue: | |
coord = queue.pop() | |
for offset in [[-1, 0], [1, 0], [0, -1], [0, 1]]: | |
checked = check_coord((coord[0] + offset[0], coord[1] + offset[1])) | |
if checked: | |
queue.append(checked) | |
if sprite_bbox[0] is None: | |
continue | |
width = sprite_bbox[2] - sprite_bbox[0] | |
height = sprite_bbox[3] - sprite_bbox[1] | |
if width < 5 or height < 5: | |
continue | |
# print("sprite found:", sprite_bbox) | |
if export_sprites: | |
sprite = image.crop(sprite_bbox) | |
sprite.save('sprite-{}-{}-{}-{}.png'.format(*sprite_bbox)) | |
sprites.append(sprite_bbox) | |
print("{") | |
print(' "size": [{}, {}],'.format(*size)) | |
print(' "transforms": [') | |
for sprite in sprites: | |
print(' {') | |
# print(' "source": [],') | |
print(' "source": [{}, {}, {}, {}],'.format(*sprite)) | |
print(' "rotate": [],') | |
print(' "target": []') | |
# print(' "target": [{}, {}, {}, {}]'.format(*sprite)) | |
print(' },') | |
print(' ]') | |
print('}') | |
# TODO? inline image edit | |
def reconstruct(input_image_filename, output_image_filename, rules_filename): | |
rules = {} | |
with open(rules_filename) as f: | |
rules = json.load(f) | |
image = Image.open(input_image_filename) | |
# output_image = Image.new('RGBA', rules['size'], None) | |
output_image = Image.new(image.mode, rules['size'], None) | |
for rule in rules['transforms']: | |
sprite = image.crop(rule['source']) | |
if not rule['target']: | |
continue | |
if 'rotate' in rule and rule['rotate']: | |
if rule['rotate'] == 90: | |
sprite = sprite.transpose(Image.ROTATE_90) | |
elif rule['rotate'] == -90: | |
sprite = sprite.transpose(Image.ROTATE_270) | |
else: | |
sprite = sprite.rotate(rule['rotate'], expand=True) | |
# sprite = sprite.crop(sprite.getbbox()) | |
print('Pasting:', rule, sprite.size) | |
overlay = Image.new(image.mode, rules['size'], None) | |
overlay.paste(sprite, rule['target']) | |
output_image = Image.alpha_composite(output_image, overlay) | |
# output_image.paste(sprite, rule['target']) | |
output_image.save(output_image_filename) | |
# dissect(sys.argv[1]) | |
reconstruct(sys.argv[1], sys.argv[2], sys.argv[3]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"size": [1239, 271], | |
"transforms": [ | |
{ | |
"source": [962, 52, 1054, 217], | |
"target": [251, 98] | |
}, | |
{ | |
"source": [3, 4, 216, 219], | |
"rotate": -90, | |
"target": [81, 59] | |
}, | |
{ | |
"source": [715, 47, 958, 182], | |
"rotate": -90, | |
"target": [38, 20] | |
}, | |
{ | |
"source": [906, 11, 950, 44], | |
"target": [17, 200] | |
}, | |
{ | |
"source": [953, 16, 1013, 44], | |
"target": [2, 230] | |
}, | |
{ | |
"source": [1057, 3, 1135, 51], | |
"target": [1119, 155] | |
}, | |
{ | |
"source": [1315, 89, 1369, 138], | |
"rotate": -90, | |
"target": [1186, 147] | |
}, | |
{ | |
"source": [1383, 101, 1483, 152], | |
"rotate": 90, | |
"target": [888, 103] | |
}, | |
{ | |
"source": [1584, 96, 1634, 152], | |
"rotate": 90, | |
"target": [876, 153] | |
}, | |
{ | |
"source": [302, 21, 479, 209], | |
"target": [434, 80, 611, 268] | |
}, | |
{ | |
"source": [484, 33, 608, 208], | |
"target": [645, 93, 769, 268] | |
}, | |
{ | |
"source": [215, 6, 251, 32], | |
"target": [346, 57, 382, 83] | |
}, | |
{ | |
"source": [230, 63, 301, 217], | |
"target": [361, 114, 432, 268] | |
}, | |
{ | |
"source": [623, 2, 654, 40], | |
"rotate": -90, | |
"target": [1198, 110, 1236, 141] | |
}, | |
{ | |
"source": [862, 5, 903, 44], | |
"target": [1193, 71, 1234, 110] | |
}, | |
{ | |
"source": [624, 43, 712, 217], | |
"target": [785, 94, 873, 268] | |
}, | |
{ | |
"source": [657, 4, 754, 40], | |
"rotate": 90, | |
"target": [931, 3, 967, 100] | |
}, | |
{ | |
"source": [758, 8, 855, 44], | |
"rotate": 90, | |
"target": [944, 106, 980, 203] | |
}, | |
{ | |
"source": [1057, 54, 1183, 214], | |
"rotate": 90, | |
"target": [1031, 9, 1191, 135] | |
}, | |
{ | |
"source": [1185, 3, 1268, 69], | |
"rotate": 90, | |
"target": [642, 9, 708, 92] | |
}, | |
{ | |
"source": [1192, 76, 1239, 220], | |
"rotate": -90, | |
"target": [345, 7, 489, 54] | |
}, | |
{ | |
"source": [1242, 87, 1306, 223], | |
"rotate": -90, | |
"target": [983, 138, 1119, 202] | |
}, | |
{ | |
"source": [1278, 2, 1345, 81], | |
"target": [776, 9, 843, 88] | |
}, | |
{ | |
"source": [1310, 155, 1666, 217], | |
"target": [876, 206, 1232, 268] | |
}, | |
{ | |
"source": [1356, 26, 1435, 84], | |
"rotate": -90, | |
"target": [712, 9, 770, 88] | |
}, | |
{ | |
"source": [1443, 11, 1518, 90], | |
"target": [852, 12, 927, 91] | |
}, | |
{ | |
"source": [1489, 96, 1584, 155], | |
"rotate": 90, | |
"target": [971, 13, 1030, 108] | |
}, | |
{ | |
"source": [1521, 7, 1592, 91], | |
"target": [494, 2, 565, 86] | |
}, | |
{ | |
"source": [1596, 13, 1678, 82], | |
"rotate": 90, | |
"target": [569, 5, 638, 87] | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"size": [1544, 247], | |
"transforms": [ | |
{ | |
"source": [2, 23, 201, 183], | |
"rotate": -90, | |
"target": [262, 45, 422, 244] | |
}, | |
{ | |
"source": [2, 186, 174, 277], | |
"target": [425, 153, 597, 244] | |
}, | |
{ | |
"source": [206, 6, 358, 182], | |
"rotate": 90, | |
"target": [784, 8, 960, 160] | |
}, | |
{ | |
"source": [228, 198, 334, 251], | |
"target": [649, 165, 755, 218] | |
}, | |
{ | |
"source": [337, 215, 693, 277], | |
"target": [425, 88, 781, 150] | |
}, | |
{ | |
"source": [362, 17, 518, 81], | |
"target": [760, 180, 916, 244] | |
}, | |
{ | |
"source": [362, 84, 519, 212], | |
"rotate": 90, | |
"target": [964, 11, 1092, 168] | |
}, | |
{ | |
"source": [522, 12, 608, 93], | |
"target": [61, 164, 147, 245] | |
}, | |
{ | |
"source": [525, 96, 688, 208], | |
"rotate": -90, | |
"target": [1099, 18, 1211, 181] | |
}, | |
{ | |
"source": [611, 23, 674, 93], | |
"target": [1402, 10, 1465, 80] | |
}, | |
{ | |
"source": [677, 24, 744, 89], | |
"rotate": -90, | |
"target": [1468, 13, 1533, 80] | |
}, | |
{ | |
"source": [696, 180, 874, 277], | |
"rotate": -90, | |
"target": [1214, 3, 1311, 181] | |
}, | |
{ | |
"source": [699, 92, 799, 173], | |
"rotate": -90, | |
"target": [1318, 49, 1399, 149] | |
}, | |
{ | |
"source": [747, 42, 798, 89], | |
"rotate": 90, | |
"target": [472, 2, 519, 53] | |
}, | |
{ | |
"source": [801, 12, 848, 41], | |
"target": [425, 56, 472, 85] | |
}, | |
{ | |
"source": [801, 44, 873, 74], | |
"target": [262, 12, 334, 42] | |
}, | |
{ | |
"source": [802, 77, 875, 176], | |
"rotate": 90, | |
"target": [919, 171, 1018, 244] | |
}, | |
{ | |
"source": [851, 2, 893, 26], | |
"rotate": 90, | |
"target": [1257, 202, 1281, 244] | |
}, | |
{ | |
"source": [874, 183, 988, 276], | |
"target": [1312, 152, 1426, 245] | |
}, | |
{ | |
"source": [878, 109, 988, 182], | |
"target": [574, 12, 684, 85] | |
}, | |
{ | |
"source": [894, 47, 972, 106], | |
"target": [1418, 90, 1496, 149] | |
}, | |
{ | |
"source": [898, 12, 924, 37], | |
"rotate": 90, | |
"target": [1284, 218, 1309, 244] | |
}, | |
{ | |
"source": [991, 37, 1085, 119], | |
"target": [687, 3, 781, 85] | |
}, | |
{ | |
"source": [991, 123, 1103, 180], | |
"target": [1142, 185, 1254, 242] | |
}, | |
{ | |
"source": [996, 185, 1104, 277], | |
"target": [1433, 152, 1541, 244] | |
}, | |
{ | |
"source": [1088, 29, 1147, 62], | |
"target": [339, 6, 398, 39] | |
}, | |
{ | |
"source": [1088, 68, 1137, 119], | |
"target": [522, 8, 571, 59] | |
}, | |
{ | |
"source": [1106, 122, 1165, 157], | |
"target": [401, 7, 460, 42] | |
}, | |
{ | |
"source": [1107, 160, 1167, 277], | |
"rotate": -90, | |
"target": [1022, 184, 1139, 244] | |
}, | |
{ | |
"source": [1140, 75, 1163, 119], | |
"rotate": 90, | |
"target": [478, 62, 522, 85] | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment