1
0
mirror of https://github.com/Klagarge/PokeHES.git synced 2024-11-30 04:33:27 +00:00
PokeHES/resources/map/texture_splitter_v2.py
2022-06-12 18:47:19 +02:00

256 lines
8.2 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
from PIL import Image
import numpy as np
import pygame
W, H = 32, 32
def pilToPygame(pilImage):
return pygame.image.fromstring(
pilImage.tobytes(), pilImage.size, pilImage.mode).convert_alpha()
def rect(p1, p2):
x = min(p1[0], p2[0])
y = min(p1[1], p2[1])
w = max(p1[0], p2[0])-x + 1
h = max(p1[1], p2[1])-y + 1
return x,y,w,h
class Rect:
def __init__(self, ox1, oy1, ox2, oy2):
self.ox1, self.oy1 = min(ox1, ox2), min(oy1, oy2)
self.ox2, self.oy2 = max(ox1, ox2), max(oy1, oy2)
self.x, self.y, self.w, self.h = None, None, self.ox2-self.ox1+1, self.oy2-self.oy1+1
self.hover = False
def render(self, w, sx, sy, mouse, original=False):
self.hover = False
x, y = self.x, self.y
if original:
x, y = self.ox1, self.oy1
color = (255,200,200)
if x*W <= mouse[0] < x*W+self.w*W \
and y*H <= mouse[1] < y*H+self.h*H:
color = (0,255,0)
self.hover = True
pygame.draw.rect(w, color, [x*W-sx, y*H-sy, self.w*W, self.h*H], 2)
@property
def area(self):
return self.w*self.h
class Editor:
WIDTH = 600
HEIGHT = 600
SCROLL_SPEED = 10
FPS = 30
def __init__(self, path):
self.running = False
if not os.path.exists(path):
print(f"File '{path}' not found")
return
self.path = path
self.dir_ = os.path.dirname(path)
self.base = os.path.basename(path)
self.base, self.ext = os.path.splitext(self.base)
try:
img = Image.open(path)
except:
print(f"Could not load '{path}'. Maybe its not an image")
return
self.WIDTH = min(img.width, self.WIDTH)
self.HEIGHT = min(img.height, self.HEIGHT)
pygame.init()
self.win = pygame.display.set_mode([self.WIDTH, self.HEIGHT], pygame.SRCALPHA)
self.over = pygame.Surface([self.WIDTH, self.HEIGHT], pygame.SRCALPHA)
self.clock = pygame.time.Clock()
pygame.display.set_caption(f"Texture Splitter - {self.base}{self.ext}")
self.img = pilToPygame(img)
self.a = np.array(img)
self.w, self.h = self.a.shape[1]//W, self.a.shape[0]//H
self.w2, self.h2 = 0, 0
img.close()
if self.a.shape[1] % 32 or self.a.shape[0] % 32:
print(f"The texture's shape is not a multiple of 32x32 ({self.a.shape[1]}x{self.a.shape[0]}).")
self.running = True
self.rects = []
self.sel = (False, None)
self.scroll_x, self.scroll_y = 0, 0
self.moving = False
def mainloop(self):
while self.running:
self.handle_events()
self.render()
self.clock.tick(self.FPS)
def render(self):
mouse = pygame.mouse.get_pos()
mouse = (mouse[0]+self.scroll_x, mouse[1]+self.scroll_y)
self.win.fill(0)
self.over.fill((0,0,0,0))
self.win.blit(self.img, (-self.scroll_x, -self.scroll_y))
for r in self.rects:
r.render(self.win, self.scroll_x, self.scroll_y, mouse, True)
pygame.draw.rect(self.over, (255,255,255,50), [self.mx*W-self.scroll_x, self.my*H-self.scroll_y, W, H])
if self.sel[0]:
x,y,w,h = rect(self.sel[1], (self.mx, self.my))
pygame.draw.rect(self.over, (255,255,255), [x*W-self.scroll_x, y*H-self.scroll_y, w*W, h*H], 1)
self.win.blit(self.over, (0, 0))
pygame.display.flip()
def handle_events(self):
events = pygame.event.get()
mouse = pygame.mouse.get_pos()
self.mx, self.my = (mouse[0]+self.scroll_x)//W, (mouse[1]+self.scroll_y)//H
for event in events:
if event.type == pygame.QUIT \
or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
self.running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if not self.sel[0]:
self.sel = (True, (self.mx, self.my))
elif event.button in [4, 5]:
speed = self.SCROLL_SPEED if event.button == 5 else -self.SCROLL_SPEED
if pygame.key.get_pressed()[pygame.K_LSHIFT]:
self.scroll_x += speed
else:
self.scroll_y += speed
self.clamp_scroll()
elif event.button == 3:
self.rects = list(filter(lambda r: not r.hover, self.rects))
elif event.button == 2:
self.moving = True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
if self.sel[0]:
self.rects.append(Rect(*self.sel[1], self.mx, self.my))
self.sel = (False, None)
self.organize()
elif event.button == 2:
self.moving = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_s and event.mod & pygame.KMOD_CTRL:
self.save()
elif event.type == pygame.MOUSEMOTION:
if self.moving:
self.scroll_x -= event.rel[0]
self.scroll_y -= event.rel[1]
self.clamp_scroll()
def clamp_scroll(self):
self.scroll_x = min(self.a.shape[1]-self.WIDTH, self.scroll_x)
self.scroll_x = max(0, self.scroll_x)
self.scroll_y = min(self.a.shape[0]-self.HEIGHT, self.scroll_y)
self.scroll_y = max(0, self.scroll_y)
def organize(self):
for rect in self.rects:
rect.x = None
rect.y = None
self.rects = sorted(self.rects, key=lambda r: r.area, reverse=True)
w, h = 0, 0
a = np.zeros([h, w])
for r in self.rects:
placed = False
best = (None, None)
for y in range(h):
for x in range(w):
if a[y:y+r.h, x:x+r.w].any():
continue
d_area = max(x+r.w, w)*max(y+r.h, h)-w*h
if d_area > 0:
if best[0] is None or d_area < best[0]:
best = (d_area, (x,y))
continue
placed = True
best = (0, (x,y))
break
if placed:
break
if best[0] is None:
if h+r.h > w+r.w:
p = (w, 0)
else:
p = (0, h)
best = (r.w*r.h, p)
if best[0] > 0:
a2 = np.zeros([max(h,best[1][1]+r.h), max(w,best[1][0]+r.w)])
a2[:h, :w] = a
a = a2
h, w = a.shape
r.x, r.y = best[1]
a[r.y:r.y+r.h, r.x:r.x+r.w] = 1
self.w2, self.h2 = w, h
def save(self):
img2 = np.zeros([self.h2*H, self.w2*W, *self.a.shape[2:]], dtype=self.a.dtype)
for r in self.rects:
img2[r.y*H:(r.y+r.h)*H, r.x*W:(r.x+r.w)*W] = \
self.a[r.oy1*H:(r.oy1+r.h)*H, r.ox1*W:(r.ox1+r.w)*W]
path = os.path.join(self.dir_, f"{self.base}_modified{self.ext}")
try:
img2 = Image.fromarray(img2)
img2.save(path)
except:
print("Could not save modified texture")
else:
print(f"Succesfully saved modified texture as {path}")
if __name__ == "__main__":
path = input("Path to tileset texture: ")
editor = Editor(path)
editor.mainloop()