From 164fab665b4361a1c39d096fda1fccd9b8f4f995 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Fri, 27 Oct 2023 22:48:18 +0200 Subject: [PATCH] Extract and show sophie item images --- data-prepper/src/ryza3/extract_images/mod.rs | 2 ++ data-prepper/src/sophie/extract_images/mod.rs | 4 ++- data-prepper/src/utils/images/mod.rs | 14 ++++++++- data-prepper/src/utils/images/rgba8_image.rs | 11 +++++++ frontend/src/data/sophie_data.ts | 5 +++ frontend/src/routes/sophie/items/detail.tsx | 5 ++- frontend/src/routes/sophie/items/list.tsx | 15 +++++++++ .../sophie/utility_components/links.tsx | 31 +++++++++++++++++++ 8 files changed, 84 insertions(+), 3 deletions(-) diff --git a/data-prepper/src/ryza3/extract_images/mod.rs b/data-prepper/src/ryza3/extract_images/mod.rs index 922d281..af82857 100644 --- a/data-prepper/src/ryza3/extract_images/mod.rs +++ b/data-prepper/src/ryza3/extract_images/mod.rs @@ -25,6 +25,7 @@ pub fn extract_images( subdirectory: "enemies", sprite_dimensions: (512, 512), texture_atlas_dimensions: (64, 64), + ..Default::default() }; extract_sprites_with_texture_atlas(args, pak_index, output_directory, options) .context("extract monster portraits")?; @@ -37,6 +38,7 @@ pub fn extract_images( subdirectory: "items", sprite_dimensions: (512, 512), texture_atlas_dimensions: (64, 64), + ..Default::default() }; extract_sprites_with_texture_atlas(args, pak_index, output_directory, options) .context("extract item icons")?; diff --git a/data-prepper/src/sophie/extract_images/mod.rs b/data-prepper/src/sophie/extract_images/mod.rs index 46f47b3..1b09370 100644 --- a/data-prepper/src/sophie/extract_images/mod.rs +++ b/data-prepper/src/sophie/extract_images/mod.rs @@ -19,7 +19,9 @@ pub fn extract_images( pattern: r"\Data\Win32\ui_JP\a17_item_l_*.g1t", subdirectory: "items", sprite_dimensions: (512, 512), - texture_atlas_dimensions: (64, 64), + sprite_trimmed_dimensions: Some((288, 288)), + // TODO: do we want 64 instead? we can only do integer scaling for now + texture_atlas_dimensions: (72, 72), }; extract_sprites_with_texture_atlas(args, pak_index, output_directory, options) .context("extract item icons")?; diff --git a/data-prepper/src/utils/images/mod.rs b/data-prepper/src/utils/images/mod.rs index 03ec8a8..fc75da9 100644 --- a/data-prepper/src/utils/images/mod.rs +++ b/data-prepper/src/utils/images/mod.rs @@ -11,6 +11,7 @@ use crate::extract_images::Args; pub mod rgba8_image; pub mod texture_atlas; +#[derive(Default)] pub struct ExtractSpritesOptions { /// The pattern used to find file names pub pattern: &'static str, @@ -19,6 +20,8 @@ pub struct ExtractSpritesOptions { pub subdirectory: &'static str, /// The size of each individual input image. pub sprite_dimensions: (u32, u32), + /// The size of the image after optional trimming. If not present, no trimming is done. + pub sprite_trimmed_dimensions: Option<(u32, u32)>, /// The size of each item in the texture atlas pub texture_atlas_dimensions: (u32, u32), } @@ -47,7 +50,9 @@ pub fn extract_sprites_with_texture_atlas( // create texture atlas let mut texture_atlas = UniformTextureAtlas::new_with_scaling( - options.sprite_dimensions, + options + .sprite_trimmed_dimensions + .unwrap_or(options.sprite_dimensions), options.texture_atlas_dimensions, entries.len(), ) @@ -78,6 +83,13 @@ pub fn extract_sprites_with_texture_atlas( let image = Rgba8Image::new(texture.width, image_bytes).context("image buffer to image")?; debug_assert_eq!(image.height(), texture.height); + // cut image if needed + let image = if let Some(trimmed_dimensions) = options.sprite_trimmed_dimensions { + image.trim_to(trimmed_dimensions.0, trimmed_dimensions.1)? + } else { + image + }; + debug!(?entry, "adding image to texture atlas"); texture_atlas .add_image(&image, num.to_string()) diff --git a/data-prepper/src/utils/images/rgba8_image.rs b/data-prepper/src/utils/images/rgba8_image.rs index 979c541..cb1263e 100644 --- a/data-prepper/src/utils/images/rgba8_image.rs +++ b/data-prepper/src/utils/images/rgba8_image.rs @@ -119,6 +119,17 @@ impl Rgba8Image { new_image } + pub fn trim_to(&self, new_width: u32, new_height: u32) -> anyhow::Result { + if new_width > self.width() { + bail!("new width must be less than or equal to current width"); + } + if new_height > self.height() { + bail!("new height must be less than or equal to current height"); + } + + self.copy_chunk(0, 0, new_width, new_height) + } + /// Copies a chunk of the image into a new image. pub fn copy_chunk(&self, x: u32, y: u32, width: u32, height: u32) -> anyhow::Result { if width == 0 { diff --git a/frontend/src/data/sophie_data.ts b/frontend/src/data/sophie_data.ts index e6188dd..6e6e5a4 100644 --- a/frontend/src/data/sophie_data.ts +++ b/frontend/src/data/sophie_data.ts @@ -1,9 +1,11 @@ +import type TextureAtlasTypes from "@/data/types/texture_atlas.d.ts"; import type SophieTypes from "@/data/types/sophie.d.ts"; import { createContext } from "react"; export const SophieContext = createContext(null as unknown as SophieData); export type SophieData = { + items_texture_atlas: TextureAtlasTypes.UniformTextureAtlasInfo, items: SophieTypes.Item[], present_info: SophieTypes.PresentInfo, rumors: SophieTypes.Rumor[], @@ -14,11 +16,13 @@ export async function getSophieData(): Promise { const url_base = `${import.meta.env.VITE_DATA_URL}/sophie`; const [ + items_texture_atlas, items, present_info, rumors, dolls, ] = await Promise.all([ + fetch(`${url_base}/texture-atlasses/items.json`).then(res => res.json()), fetch(`${url_base}/items.json`).then(res => res.json()), fetch(`${url_base}/presents.json`).then(res => res.json()), fetch(`${url_base}/rumors.json`).then(res => res.json()), @@ -26,6 +30,7 @@ export async function getSophieData(): Promise { ]); return { + items_texture_atlas, items, present_info, rumors, diff --git a/frontend/src/routes/sophie/items/detail.tsx b/frontend/src/routes/sophie/items/detail.tsx index bf8f082..97a3add 100644 --- a/frontend/src/routes/sophie/items/detail.tsx +++ b/frontend/src/routes/sophie/items/detail.tsx @@ -3,7 +3,7 @@ import types from "@/data/types/sophie"; import { useContext } from "react"; import { useParams } from "react-router-dom"; import { CategoryLink } from "../utility_components/links"; -import { itemDisplayName } from "../sophie_data_util"; +import { getImageLink, itemDisplayName } from "../sophie_data_util"; export default function ItemDetail(): JSX.Element { const sophieData = useContext(SophieContext); @@ -26,6 +26,9 @@ export default function ItemDetail(): JSX.Element { return ( <>

{itemDisplayName(item)}

+ {item.img_no !== null && item.image_no >= 0 && ( + + )} ); diff --git a/frontend/src/routes/sophie/items/list.tsx b/frontend/src/routes/sophie/items/list.tsx index 7bbefbd..4f650d8 100644 --- a/frontend/src/routes/sophie/items/list.tsx +++ b/frontend/src/routes/sophie/items/list.tsx @@ -4,6 +4,7 @@ import types from "@/data/types/sophie"; import { ColumnDef, createColumnHelper } from "@tanstack/react-table"; import { useContext } from "react"; import { ItemLink } from "../utility_components/links"; +import { TextureAtlasImage } from "@/routes/sophie/utility_components/links"; export default function ItemList(): JSX.Element { const sophieData = useContext(SophieContext); @@ -24,6 +25,20 @@ export default function ItemList(): JSX.Element { function getColumnDefs(sophieData: SophieData): ColumnDef[] { const columnHelper = createColumnHelper<(typeof sophieData.items)[0]>(); return [ + columnHelper.accessor("image_no", { + header: "Image", + cell: (i) => { + return ( + + + + ); + }, + }), columnHelper.accessor("name", { header: "Name", cell: (i) => , diff --git a/frontend/src/routes/sophie/utility_components/links.tsx b/frontend/src/routes/sophie/utility_components/links.tsx index 6a8a671..1c8c414 100644 --- a/frontend/src/routes/sophie/utility_components/links.tsx +++ b/frontend/src/routes/sophie/utility_components/links.tsx @@ -1,4 +1,5 @@ import types from "@/data/types/sophie"; +import texture_atlas from "@/data/types/texture_atlas"; import { Link } from "react-router-dom"; import { findItemByTag, @@ -60,3 +61,33 @@ export function CategoryLink({ ); } + +export function TextureAtlasImage({ + texture_atlas, + texture_atlas_name, + name, // TODO: accept dimensions as parameter too +}: { + texture_atlas: texture_atlas.UniformTextureAtlasInfo; + texture_atlas_name: string; + name: string; +}) { + const index = texture_atlas.stored_images.indexOf(name); + const x_index = index % texture_atlas.columns; + const y_index = Math.floor(index / texture_atlas.columns); + + return ( + + ); +}