From 947df0c4cabdf52d271d6cee929284ccf82db6dd Mon Sep 17 00:00:00 2001 From: tibs Date: Mon, 14 Feb 2022 14:39:31 -0500 Subject: [PATCH 1/4] Add cube render overlay --- src/lib.rs | 4 ++-- src/skin.rs | 25 ++++++++++++++++++++----- worker/request.ts | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7ee76bc..c207d5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ impl RenderType { .resize(size, size, image::imageops::FilterType::Nearest), RenderType::Helm => img.get_part(Layer::Both, BodyPart::Head, options.model) .resize(size, size, image::imageops::FilterType::Nearest), - RenderType::Cube => img.render_cube(true, size), + RenderType::Cube => img.render_cube(size, options), RenderType::Body => img.render_body(options) .resize(size, size * 2, image::imageops::FilterType::Nearest), RenderType::Cape => img.get_cape() @@ -75,7 +75,7 @@ pub fn get_rendered_image(skin_image: Uint8Array, size: u32, what: String, armor Ok(skin_img) => { let skin = MinecraftSkin::new(skin_img); let options = match slim { - true => RenderOptions { armored, model: SkinModel::Slim }, + true => RenderOptions { armored, model: SkinModel::Slim }, false => RenderOptions { armored, model: SkinModel::Regular } }; let rendered = render_type.unwrap().render(&skin, size, options); diff --git a/src/skin.rs b/src/skin.rs index 713140e..07c61c6 100644 --- a/src/skin.rs +++ b/src/skin.rs @@ -163,13 +163,10 @@ impl MinecraftSkin { DynamicImage::ImageRgba8(image) } - pub(crate) fn render_cube(&self, overlay: bool, width: u32) -> DynamicImage { + pub(crate) fn render_cube(&self, width: u32, options: RenderOptions) -> DynamicImage { let scale = (width as f32) / 20.0 as f32; let height = (18.5 * scale).ceil() as u32; - let layer_type = match overlay { - true => Layer::Both, - false => Layer::Bottom - }; + let mut render = RgbaImage::new(width, height); let z_offset = scale * 3.0; @@ -179,6 +176,10 @@ impl MinecraftSkin { let head_orig_right = self.0.crop_imm(0, 8, 8, 8); let head_orig_front = self.0.crop_imm(8, 8, 8, 8); + let head_orig_top_overlay = self.0.crop_imm(40, 0, 8, 8); + let head_orig_right_overlay = self.0.crop_imm(32, 8, 8, 8); + let head_orig_front_overlay = self.0.crop_imm(40, 8, 8, 8); + // The warp_into function clears every part of the output image that is not part of the pre-image. // As a workaround, we ask warp_into to draw into a scratch image, overlay the final image with the // scratch image, and let the scratch be overwritten. @@ -211,6 +212,20 @@ impl MinecraftSkin { warp_into(&head_orig_right.into_rgba8(), &head_right_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); imageops::overlay(&mut render, &scratch, 0, 0); + if options.armored { + // head top overlay + warp_into(&head_orig_top_overlay.into_rgba8(), &head_top_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); + imageops::overlay(&mut render, &scratch, 0, 0); + + // head front overlay + warp_into(&head_orig_front_overlay.into_rgba8(), &head_front_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); + imageops::overlay(&mut render, &scratch, 0, 0); + + // head right overlay + warp_into(&head_orig_right_overlay.into_rgba8(), &head_right_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); + imageops::overlay(&mut render, &scratch, 0, 0); + } + DynamicImage::ImageRgba8(render) } } diff --git a/worker/request.ts b/worker/request.ts index a8da1be..3dfda47 100644 --- a/worker/request.ts +++ b/worker/request.ts @@ -58,7 +58,7 @@ export function interpretRequest(request: Request): CraftheadRequest | null { let armored = false let sliceAmt = 1 - if (url.pathname.includes("armor/body")) { + if (url.pathname.includes("/armor/cube/") || url.pathname.includes("/armor/body/")) { armored = true sliceAmt = 2 } From 0570f1d3d2a4fae4025037517ff0edbdf9cb4a75 Mon Sep 17 00:00:00 2001 From: tibs Date: Tue, 15 Feb 2022 12:07:11 -0500 Subject: [PATCH 2/4] Center head render in image --- src/skin.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/skin.rs b/src/skin.rs index 07c61c6..049394d 100644 --- a/src/skin.rs +++ b/src/skin.rs @@ -39,8 +39,8 @@ pub(crate) enum BodyPart { LegRight, } -const skew_a: f32 = 26.0 / 45.0; // 0.57777777 -const skew_b: f32 = skew_a * 2.0; // 1.15555555 +const SKEW_A: f32 = 26.0 / 45.0; // 0.57777777 +const SKEW_B: f32 = SKEW_A * 2.0; // 1.15555555 impl MinecraftSkin { pub fn new(skin: DynamicImage) -> MinecraftSkin { @@ -163,11 +163,13 @@ impl MinecraftSkin { DynamicImage::ImageRgba8(image) } - pub(crate) fn render_cube(&self, width: u32, options: RenderOptions) -> DynamicImage { - let scale = (width as f32) / 20.0 as f32; - let height = (18.5 * scale).ceil() as u32; + pub(crate) fn render_cube(&self, size: u32, options: RenderOptions) -> DynamicImage { + let scale = (size as f32) / 20.0 as f32; - let mut render = RgbaImage::new(width, height); + let x_render_offset = scale.ceil() as u32; + let z_render_offset = x_render_offset / 2; + + let mut render = RgbaImage::new(size, size); let z_offset = scale * 3.0; let x_offset = scale * 2.0; @@ -183,47 +185,47 @@ impl MinecraftSkin { // The warp_into function clears every part of the output image that is not part of the pre-image. // As a workaround, we ask warp_into to draw into a scratch image, overlay the final image with the // scratch image, and let the scratch be overwritten. - let mut scratch = RgbaImage::new(width, height); + let mut scratch = RgbaImage::new(size, size); // head top let head_top_skew = Projection::from_matrix([ 1.0, 1.0, 0.0, - -skew_a, skew_a, 0.0, + -SKEW_A, SKEW_A, 0.0, 0.0, 0.0, 1.0, ]).unwrap() * Projection::translate(-0.5 - z_offset, x_offset + z_offset - 0.5) * Projection::scale(scale, scale + (1.0 / 8.0)); warp_into(&head_orig_top.into_rgba8(), &head_top_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); - imageops::overlay(&mut render, &scratch, 0, 0); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); // head front let head_front_skew = Projection::from_matrix([ 1.0, 0.0, 0.0, - -skew_a, skew_b, skew_a, + -SKEW_A, SKEW_B, SKEW_A, 0.0, 0.0, 1.0, ]).unwrap() * Projection::translate(x_offset + 7.5 * scale - 0.5, (x_offset + 8.0 * scale) + z_offset - 0.5) * Projection::scale(scale, scale); warp_into(&head_orig_front.into_rgba8(), &head_front_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); - imageops::overlay(&mut render, &scratch, 0, 0); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); // head right let head_right_skew = Projection::from_matrix([ 1.0, 0.0, 0.0, - skew_a, skew_b, 0.0, + SKEW_A, SKEW_B, 0.0, 0.0, 0.0, 1.0, ]).unwrap() * Projection::translate(x_offset - (scale / 2.0), z_offset + scale) * Projection::scale(scale + (0.5 / 8.0), scale + (1.0 / 8.0)); warp_into(&head_orig_right.into_rgba8(), &head_right_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); - imageops::overlay(&mut render, &scratch, 0, 0); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); if options.armored { // head top overlay warp_into(&head_orig_top_overlay.into_rgba8(), &head_top_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); - imageops::overlay(&mut render, &scratch, 0, 0); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); // head front overlay warp_into(&head_orig_front_overlay.into_rgba8(), &head_front_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); - imageops::overlay(&mut render, &scratch, 0, 0); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); // head right overlay warp_into(&head_orig_right_overlay.into_rgba8(), &head_right_skew, Interpolation::Nearest, Rgba([0, 0, 0, 0]), &mut scratch); - imageops::overlay(&mut render, &scratch, 0, 0); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); } DynamicImage::ImageRgba8(render) From 10fdc383b99f1a2d053adc920fcc418692e2fd3b Mon Sep 17 00:00:00 2001 From: tibs Date: Tue, 15 Feb 2022 12:08:01 -0500 Subject: [PATCH 3/4] Add depth shading --- src/skin.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/skin.rs b/src/skin.rs index 049394d..f353bd0 100644 --- a/src/skin.rs +++ b/src/skin.rs @@ -182,6 +182,10 @@ impl MinecraftSkin { let head_orig_right_overlay = self.0.crop_imm(32, 8, 8, 8); let head_orig_front_overlay = self.0.crop_imm(40, 8, 8, 8); + // Shade right texture darker to show depth + let head_orig_right = head_orig_right.brighten(-4); + let head_orig_right_overlay = head_orig_right_overlay.brighten(-4); + // The warp_into function clears every part of the output image that is not part of the pre-image. // As a workaround, we ask warp_into to draw into a scratch image, overlay the final image with the // scratch image, and let the scratch be overwritten. From c972ddc300ce29ce66a17a8c9154966f5caaa641 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 22 Dec 2024 17:37:09 +0000 Subject: [PATCH 4/4] chore: update for new repo changes --- src/rust/lib.rs | 2 +- src/rust/skin.rs | 68 +++++++++++++++++++++++++++++++++++-------- src/worker/request.ts | 2 +- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/rust/lib.rs b/src/rust/lib.rs index 9975c3f..bc28499 100644 --- a/src/rust/lib.rs +++ b/src/rust/lib.rs @@ -34,7 +34,7 @@ impl RenderType { RenderType::Helm => img .get_part(Layer::Both, BodyPart::Head, options.model) .resize(size, size, image::imageops::FilterType::Nearest), - RenderType::Cube => img.render_cube(true, size), + RenderType::Cube => img.render_cube(size, options), RenderType::Body => img.render_body(options).resize( size, size * 2, diff --git a/src/rust/skin.rs b/src/rust/skin.rs index 4685686..cd95653 100644 --- a/src/rust/skin.rs +++ b/src/rust/skin.rs @@ -175,14 +175,13 @@ impl MinecraftSkin { DynamicImage::ImageRgba8(image) } - pub(crate) fn render_cube(&self, overlay: bool, width: u32) -> DynamicImage { - let scale = (width as f32) / 20.0 as f32; - let height = (18.5 * scale).ceil() as u32; - let _layer_type = match overlay { - true => Layer::Both, - false => Layer::Bottom, - }; - let mut render = RgbaImage::new(width, height); + pub(crate) fn render_cube(&self, size: u32, options: RenderOptions) -> DynamicImage { + let scale = (size as f32) / 20.0 as f32; + + let x_render_offset = scale.ceil() as i64; + let z_render_offset = x_render_offset / 2; + + let mut render = RgbaImage::new(size, size); let z_offset = scale * 3.0; let x_offset = scale * 2.0; @@ -191,10 +190,18 @@ impl MinecraftSkin { let head_orig_right = self.0.crop_imm(0, 8, 8, 8); let head_orig_front = self.0.crop_imm(8, 8, 8, 8); + let head_orig_top_overlay = self.0.crop_imm(40, 0, 8, 8); + let head_orig_right_overlay = self.0.crop_imm(32, 8, 8, 8); + let head_orig_front_overlay = self.0.crop_imm(40, 8, 8, 8); + + // Shade right texture darker to show depth + let head_orig_right = head_orig_right.brighten(-4); + let head_orig_right_overlay = head_orig_right_overlay.brighten(-4); + // The warp_into function clears every part of the output image that is not part of the pre-image. // As a workaround, we ask warp_into to draw into a scratch image, overlay the final image with the // scratch image, and let the scratch be overwritten. - let mut scratch = RgbaImage::new(width, height); + let mut scratch = RgbaImage::new(size, size); // head top let head_top_skew = @@ -208,7 +215,12 @@ impl MinecraftSkin { Rgba([0, 0, 0, 0]), &mut scratch, ); - imageops::overlay(&mut render, &scratch, 0, 0); + imageops::overlay( + &mut render, + &scratch, + x_render_offset.into(), + z_render_offset.into(), + ); // head front let head_front_skew = @@ -224,7 +236,7 @@ impl MinecraftSkin { Rgba([0, 0, 0, 0]), &mut scratch, ); - imageops::overlay(&mut render, &scratch, 0, 0); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); // head right let head_right_skew = @@ -238,7 +250,39 @@ impl MinecraftSkin { Rgba([0, 0, 0, 0]), &mut scratch, ); - imageops::overlay(&mut render, &scratch, 0, 0); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); + + if options.armored { + // head top overlay + warp_into( + &head_orig_top_overlay.into_rgba8(), + &head_top_skew, + Interpolation::Nearest, + Rgba([0, 0, 0, 0]), + &mut scratch, + ); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); + + // head front overlay + warp_into( + &head_orig_front_overlay.into_rgba8(), + &head_front_skew, + Interpolation::Nearest, + Rgba([0, 0, 0, 0]), + &mut scratch, + ); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); + + // head right overlay + warp_into( + &head_orig_right_overlay.into_rgba8(), + &head_right_skew, + Interpolation::Nearest, + Rgba([0, 0, 0, 0]), + &mut scratch, + ); + imageops::overlay(&mut render, &scratch, x_render_offset, z_render_offset); + } DynamicImage::ImageRgba8(render) } diff --git a/src/worker/request.ts b/src/worker/request.ts index 2a55b3c..e466dbb 100644 --- a/src/worker/request.ts +++ b/src/worker/request.ts @@ -95,7 +95,7 @@ export function interpretRequest(request: Request): CraftheadRequest | null { let armored = false; let sliceAmt = 1; - if (url.pathname.includes('armor/body') || url.pathname.includes('armor/bust')) { + if (url.pathname.includes('/armor/cube/') || url.pathname.includes('/armor/body/') || url.pathname.includes('/armor/bust/')) { armored = true; sliceAmt = 2; }