From eb19acafc7291e67375a029eb66c1bc062e4c097 Mon Sep 17 00:00:00 2001 From: Metal Gear Sloth Date: Wed, 19 Aug 2020 22:06:24 +1000 Subject: [PATCH] RSI state differ Useful for cutting up unlit layers for textures. --- rsi/__init__.py | 2 +- rsi/__main__.py | 18 +++++++++++++++++- rsi/state.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/rsi/__init__.py b/rsi/__init__.py index 64d375c..b5eb0bf 100644 --- a/rsi/__init__.py +++ b/rsi/__init__.py @@ -1,2 +1,2 @@ from .rsi import Rsi -from .state import State +from .state import State, rsi_state_diff diff --git a/rsi/__main__.py b/rsi/__main__.py index 22662ea..a08cef6 100644 --- a/rsi/__main__.py +++ b/rsi/__main__.py @@ -1,7 +1,7 @@ import argparse from pathlib import Path from typing import Optional -from rsi import Rsi +from rsi import Rsi, rsi_state_diff def main() -> int: @@ -24,6 +24,11 @@ def main() -> int: _new_rsi.add_argument("-l", "--license", action="store", help="The license of this RSI file, as valid SPDX License Identifier (Google it).", nargs="?") _new_rsi.add_argument("--dont-make-parent-dirs", action="store_true", help="Do not create parent directories if they do not exist, instead throw an error.", dest="no_parents") + _image_diff = subparser.add_parser("diff", help="Will output the image differences between 2 rsi state images.") + _image_diff.add_argument("source", help="The image to compare against the target. Supports multiple frames.", type=Path) + _image_diff.add_argument("target", help="The base image to be compared against. Single frame only.", type=Path) + _image_diff.add_argument("output", help="Filepath to output the result to.", type=Path) + args = parser.parse_args() if args.command == "from_dmi": @@ -33,6 +38,10 @@ def main() -> int: if args.command == "new": return new_rsi(args.rsi, args.dimensions, args.copyright, args.license, not args.no_parents) + if args.command == "diff": + image_diff(args.source, args.target, args.output) + return 0 + print("No command specified!") return 1 @@ -73,3 +82,10 @@ def new_rsi(loc: Path, rsi.write(loc, make_parents) return 0 + + +def image_diff(source: Path, + target: Path, + output: Path) -> None: + + rsi_state_diff(source, target, output) diff --git a/rsi/state.py b/rsi/state.py index 84cf22b..4796306 100644 --- a/rsi/state.py +++ b/rsi/state.py @@ -1,5 +1,7 @@ from typing import List, Tuple, Dict, Any from PIL import Image +from pathlib import Path + class State(object): def __init__(self, @@ -13,3 +15,50 @@ def __init__(self, self.delays = [[] for i in range(self.directions)] # type: List[List[float]] self.icons = [[] for i in range(self.directions)] # type: List[List[Image.Image]] + + +def rsi_state_diff(source: Path, target: Path, output: Path) -> None: + """ + Gets the pixels in the source image that aren't in the target image and outputs to the output image. + This does NOT get the color difference between the 2 pixels. + Can also account for multiple frames. + This whole thing is very .rsi specialized so it's not recommended to use this for other types of image operations. + :param output: + :param target: + :param source: + :return: + """ + + source_image: Image = Image.open(source).convert("RGB") + target_image: Image = Image.open(target).convert("RGB") + + if source_image.size < target_image.size: + raise Exception("Source image must be larger than or equal to target size") + + # I must be dumb but PIL doesn't seem to have a native tool for getting image diffs + # there is an "ImageChops.difference" but that gets the actual color difference rather than different pixels + diff = Image.new(mode="RGBA", size=source_image.size) + frame_size = target_image.size + + # source can have or 1 more frames but target must have 1 frame only + for column in range(int(source_image.size[0] / frame_size[0])): + for row in range(int(source_image.size[1] / frame_size[1])): + for x in range(frame_size[0]): + for y in range(frame_size[1]): + offset_x = x + column * frame_size[0] + offset_y = y + row * frame_size[1] + source_color = source_image.getpixel((offset_x, offset_y)) + + # if source is already transparent then just skip + if source_color == (0, 0, 0): + continue + + target_color = target_image.getpixel((x, y)) + + if source_color == target_color: + diff.putpixel((offset_x, offset_y), (0, 0, 0, 0)) + continue + + diff.putpixel((offset_x, offset_y), source_color) + + diff.save(output)