I've been working on the same program for 6+ hours now. I've gotten to the point where I'm so tired that I don't even remember why half of the things are the way they are. The script
looked like it was working with two inputs, but that just turned out to be a cruel illusion as it promtly shat itself and died when I tried with five inputs. As it is now, it won't even run and I'm too tired and demoralized to know for sure why.
import sys, argparse
from texmerge_classes import output_image as output
from PIL import Image
args = sys.argv[1:]
parser = argparse.ArgumentParser(description="Combine multiple textures for use in 3D games or sprite sheets.")
parser.add_argument("input", nargs="+", help="Input image files")
parser.add_argument("-output", default='./output', help="Output file. Default is ./output")
parser.add_argument("-filetype", nargs=1, default='.png',
choices=['.png', '.jpg', '.jpeg'], help="Format of output file. Default is PNG")
args = parser.parse_args(args)
inputs = args.input
inputImages = []
outputDir = args.output
finalType = args.filetype
for i in range(0, len(inputs)):
inputImages.append(Image.open(inputs[i]))
outputImage = output.OutputImage((Image.new('RGBA', size=(0, 0))), inputImages)
outputImage.merge_images()
outputImage.image.save(outputDir + finalType)
for region in outputImage.used_regions:
print("Region used: " + str((region.x, region.y, region.width, region.height)))
for region in outputImage.empty_regions:
print("Empty region: " + str((region.x, region.y, region.width, region.height)))
"""
Old way of doing it - make way for output_image!
outputImage = Image.new('RGBA', size=(1, 1))
for img in inputImages:
print("Image size is " + str(img.size))
old = outputImage.copy()
if img.width > img.height:
# if it's short, add it to the bottom
yPosition = outputImage.height
xPosition = 0
if outputImage.width < img.width:
newWidth = outputImage.width + (img.width - outputImage.width)
else:
newWidth = outputImage.width
newSize = (newWidth, outputImage.height + img.height)
outputImage = Image.new('RGBA', size=newSize)
outputImage.paste(old, box=(0, 0))
outputImage.paste(img, box=(xPosition, yPosition))
else:
# if it's tall, add it to the side
yPosition = 0
xPosition = outputImage.width
if outputImage.height < img.height:
newHeight = outputImage.height + (img.height - outputImage.height)
else:
newHeight = outputImage.height
newSize = (outputImage.width + img.width, newHeight)
outputImage = Image.new('RGBA', size=newSize)
outputImage.paste(old, box=(0, 0))
outputImage.paste(img, box=(xPosition, yPosition))
print("Image size is " + str(img.size))
xPosition = outputImage.width
old = outputImage.copy()
outputImage = Image.new('RGBA', size=(outputImage.width + img.width, outputImage.height + img.height))
outputImage.paste(old, box=(0, 0, old.width, old.height))
print("Current output size: " + str(outputImage.size))
outputImage.paste(img, box=(xPosition, 0, xPosition+img.width, img.height))
print("Pasted image to " + str((xPosition, 0, xPosition+img.width, img.height)))
"""
"""
output_image
Representation of the final output image and related methods.
"""
import sys
from PIL import Image
from math import ceil, floor
class Region:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def contains_point(self, x, y):
if x in range(self.x, self.x + self.width) and y in range(self.y, self.y + self.height):
#print("Point " + str((x, y)) + " is in " + str((self.x, self.y, self.width, self.height)))
return True
else:
#print("Point " + str((x, y)) + " is not in " + str((self.x, self.y, self.width, self.height)))
return False
def area(self):
area = ((self.width - self.x) * (self.height - self.y))
return area
class OutputImage:
def __init__(self, image=None, inputs=None, tiles_width=0, tiles_height=0):
self.image = image
self.inputs = inputs
self.tiles_width = tiles_width
self.tiles_height = tiles_height
self.empty_regions = []
self.used_regions = []
self.cursor_position = [0, 0]
self.empty = True
def calculate_tile_dimensions(self):
if isinstance(len(self.inputs)/2, float):
self.tiles_width = ceil(len(self.inputs)/2)
self.tiles_height = floor(len(self.inputs)/2)
else:
self.tiles_width, self.tiles_height = len(self.inputs)/2
def merge_images(self):
for image in self.inputs:
self.add_image(image)
def add_image(self, img):
if self.empty:
print("Adding first image")
self.resize_output_image((img.width, img.height))
self.image.paste(img, box=(0, 0))
self.used_regions.append(Region(0, 0, img.width, img.height))
self.empty = False
return
else:
if len(self.empty_regions) > 0:
for region in self.empty_regions:
if region.width >= img.width and region.height >= img.height: # if there's an empty area big enough
print("Found empty area: Adding image")
self.image.paste(img, box=(region.x, region.y))
print("----- Pasted at " + str(region.x) + ", " + str(region.y) + " -----")
self.used_regions.append(Region(region.x, region.y,
region.x + img.width, region.y + img.height))
self.calculate_empty_regions()
break
elif region.height < img.height:
print("Cannot add image: No empty area tall enough, Expanding image")
self.resize_output_image((self.image.width, self.image.height + img.height))
self.calculate_empty_regions()
self.add_image(img)
break
else: # if there's no empty area large enough, we need to expand
print("Cannot add image: No empty area wide enough, Expanding image")
self.resize_output_image((img.width, self.image.height))
self.calculate_empty_regions()
self.add_image(img) # try again...
break
else: # if there's no empty area at all, we need to expand
print("Cannot add image: No empty area at all, Expanding image height-first")
self.resize_output_image((self.image.width, self.image.height + img.height))
self.calculate_empty_regions()
self.add_image(img) # try again...
def resize_output_image(self, new_size):
old_image = self.image.copy()
self.image = Image.new('RGBA', new_size)
self.image.paste(old_image, box=(0, 0))
print("Image size is now " + str((self.image.width, self.image.height)))
def calculate_empty_regions(self):
# n used regions results in n-1 unused regions
self.empty_regions.clear()
print("Calculating empty regions")
for x in range(0, len(self.used_regions)):
startpoint = self.find_first_empty_point()
if startpoint[0] is not None:
startx = startpoint[0]
starty = startpoint[1]
endpoint = self.find_last_empty_point()
endx = endpoint[0]
endy = endpoint[1]
print("Found empty region " + str((startx, starty, endx, endy)))
self.empty_regions.append(Region(startx, starty, endx, endy))
else:
print("calculate_empty_regions: Did not find empty point, returning")
return
def find_first_empty_point(self):
print("Finding first empty point")
for x in range(0, self.image.width):
for y in range(0, self.image.height):
#print("Checking point " + str((x, y)))
region_checks = [True]
for region in self.used_regions:
if region.contains_point(x, y):
region_checks.append(False)
else:
region_checks.append(True)
#print("Region checks " + str(region_checks))
#print(str(all(region_checks)))
if all(region_checks) is True:
region_checks = [True]
for region in self.empty_regions:
if region.contains_point(x, y):
region_checks.append(False)
break
else:
region_checks.append(True)
break
if all(region_checks) is True:
print("Found last empty point " + str((x, y)))
return [x, y]
else:
return [None, None]
else:
pass
def find_last_empty_point(self):
for x in range(self.image.width, 0, -1):
for y in range(self.image.height, 0, -1):
region_checks = [True]
for region in self.used_regions:
if region.contains_point(x, y):
region_checks.append(False)
else:
region_checks.append(True)
#print("Region checks " + str(region_checks))
#print(str(all(region_checks)))
if all(region_checks) is True:
region_checks = [True]
for region in self.empty_regions:
if region.contains_point(x, y):
region_checks.append(False)
break
else:
region_checks.append(True)
break
if all(region_checks) is True:
print("Found last empty point " + str((x, y)))
return [x, y]
else:
return [None, None]
else:
pass
Don't ask me how it works, because I'm so tired that I don't even remember right now.
The most infuriating thing is, I'm 90% sure I would have had it if I'd went to bed less than an hour ago and worked on it after I'd gotten some sleep. I'm sure that a lot of easy-to-spot mistakes have added up into a huge unfixable mess, because I thought this was going to be
so easy