From a58d483e6a01c3f92a14b16a307e7034a2befae4 Mon Sep 17 00:00:00 2001
From: Louis Capitanchik <contact@louiscap.co>
Date: Sun, 23 Jul 2023 18:48:18 +0100
Subject: [PATCH] Simplify image extrusion, support pixel-clone extrusion

---
 CHANGELOG.md            |  7 ++++
 src/commands/extrude.rs | 83 ++++++++++++++++++++++++++++-------------
 src/utils.rs            |  4 ++
 3 files changed, 69 insertions(+), 25 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 348a34d..00207cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+### Added
+- Support for `--extrude` param in `extrude` command
+
+## [0.5.0] - 2023-02-22
+### Added
+- `split` command for taking a sprite sheet and converting it into individual tiles
+
 ### Changed
 - Moved all arguments into subcommands, for a less confusing end user experience
 
diff --git a/src/commands/extrude.rs b/src/commands/extrude.rs
index fdff8c9..1bbff08 100644
--- a/src/commands/extrude.rs
+++ b/src/commands/extrude.rs
@@ -1,8 +1,11 @@
 use crate::utils::{RgbaOutputFormat, SpriteData};
 use clap::Parser;
-use image::{GenericImage, GenericImageView, Pixel, Rgba, RgbaImage};
+use image::flat::View;
+use image::math::Rect;
+use image::{DynamicImage, GenericImage, GenericImageView, Pixel, Rgba, RgbaImage};
 use num_traits::cast::ToPrimitive;
 use serde::{Deserialize, Serialize};
+use std::ops::Deref;
 
 #[inline(always)]
 fn tile_size() -> u32 {
@@ -44,7 +47,10 @@ pub struct Extrude {
 }
 
 impl Extrude {
-	pub fn run(&self, image: &impl GenericImage) -> anyhow::Result<RgbaOutputFormat> {
+	pub fn run(
+		&self,
+		image: &impl GenericImage<Pixel = Rgba<u8>>,
+	) -> anyhow::Result<RgbaOutputFormat> {
 		log::info!(
 			"Image loaded with size {} x {}",
 			image.width(),
@@ -84,32 +90,59 @@ impl Extrude {
 			new_width,
 			new_height
 		);
-		let mut new_image = RgbaImage::new(new_width, new_height);
-		let (new_image_x, new_image_y, new_image_width, new_image_height) = new_image.bounds();
-		for x in new_image_x..new_image_width {
-			for y in new_image_y..new_image_height {
-				new_image.put_pixel(x, y, Rgba::from([0, 0, 0, 0]));
-			}
-		}
-
+		let mut new_image = RgbaImage::from_pixel(new_width, new_height, Rgba::from([0, 0, 0, 0]));
 		for sprite in views.iter() {
 			let (img_x, img_y, width, height) = sprite.data.bounds();
-			for x in 0..width {
-				for y in 0..height {
-					let pix = sprite.data.get_pixel(x, y).to_rgba();
-					let p = Rgba::from([
-						pix.0[0].to_u8().unwrap(),
-						pix.0[1].to_u8().unwrap(),
-						pix.0[2].to_u8().unwrap(),
-						pix.0[3].to_u8().unwrap(),
-					]);
+			let target_x = self.padding + img_x + (sprite.tx * self.space_x);
+			let target_y = self.padding + img_y + (sprite.ty * self.space_y);
+
+			new_image.copy_from(sprite.data.deref(), target_x, target_y)?;
 
-					new_image.put_pixel(
-						self.padding + (sprite.tx * self.space_x) + img_x + x,
-						self.padding + (sprite.ty * self.space_y) + img_y + y,
-						p,
-					);
-				}
+			if self.extrude {
+				// Left Side
+				new_image.copy_within(
+					Rect {
+						x: target_x,
+						y: target_y,
+						width: 1,
+						height,
+					},
+					target_x.saturating_sub(1),
+					target_y,
+				);
+				// Right Side
+				new_image.copy_within(
+					Rect {
+						x: target_x + width - 1,
+						y: target_y,
+						width: 1,
+						height,
+					},
+					target_x + width,
+					target_y,
+				);
+				// Top Side
+				new_image.copy_within(
+					Rect {
+						x: target_x,
+						y: target_y,
+						width,
+						height: 1,
+					},
+					target_x,
+					target_y.saturating_sub(1),
+				);
+				// Bottom Side
+				new_image.copy_within(
+					Rect {
+						x: target_x,
+						y: target_y + height - 1,
+						width,
+						height: 1,
+					},
+					target_x,
+					target_y + height,
+				);
 			}
 		}
 
diff --git a/src/utils.rs b/src/utils.rs
index edef97e..eb21cbe 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -10,10 +10,14 @@ use lab::Lab;
 pub struct SpriteData<'a, T: GenericImage> {
 	pub data: SubImage<&'a T>,
 	#[allow(dead_code)]
+	/// Horizontal pixel coordinate of this sprite within the parent image
 	pub x: u32,
 	#[allow(dead_code)]
+	/// Vertical pixel coordinate of this sprite within the parent image
 	pub y: u32,
+	/// "Tile X" - The horizontal grid position of this Sprite within a Spritesheet
 	pub tx: u32,
+	/// "Tile Y" - The vertical grid position of this Sprite within a Spritesheet
 	pub ty: u32,
 	#[allow(dead_code)]
 	pub width: u32,
-- 
GitLab