Skip to content

Commit

Permalink
initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
tukinami committed Dec 9, 2022
0 parents commit b97d85e
Show file tree
Hide file tree
Showing 20 changed files with 1,273 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
target = "i686-pc-windows-msvc"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
/Cargo.lock
29 changes: 29 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "saori-resized-png"
version = "1.0.0"
authors = ["tukinami seika <10forchette@gmail.com>"]
license = "MIT"
readme = "README.md"
edition = "2021"
description = "SAORI to resize image and save as png"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
image = "0.24.5"
fast_image_resize = "2.3.0"

[target.'cfg(windows)'.dependencies]
winapi = {version = "0.3.9", features = ["winbase", "libloaderapi"]}
encoding_rs = "0.8.31"

[lib]
name = "resizedpng"
path = "src/lib.rs"
crate-type = ["rlib", "cdylib"]

[profile.release]
strip = true
opt-level = "z"
lto = true
codegen-units = 1
94 changes: 94 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Resized Png

[GitHub repository](https://github.com/tukinami/saori-resized-png)

## これは何?

デスクトップマスコット、「伺か」で使用できるSAORIの一種です。

機能としては、指定した画像ファイルを拡大または縮小し、pngとして出力します。

「伺か」「SAORI」等の用語については詳しく説明いたしませんのでご了承下さい。

## 使い方

SAORI自体の使い方は、使用するSHIORIなどによって異なりますので、ご自身でお調べ下さい。

ここではこのSAORIの使い方について説明いたします。

Argument0に、使用する機能名を指定して使用します。
指定できる機能は`GetImageType``ToResizedPng`です。

### `GetImageType`

+ Argument1: 判別するファイルのパス

+ Result: 画像形式を表す文字列

指定されたファイルの画像形式を返します。
画像でない、または対応していない画像は`UNKNOWN`が返ります。

対応している形式は以下(色深度などによっては、対応していない場合があります):

+ `AVIF`
+ `BMP`
+ `DDS`
+ `FARBFELD`
+ `GIF`
+ `HDR`
+ `ICO`
+ `JPEG`
+ `OPENEXR`
+ `PNG`
+ `PNM`
+ `TGA`
+ `TIFF`
+ `WEBP`

### `ToResizedPng`

+ Argument1: 入力するファイルのパス
+ Argument2: 出力するファイルのパス
+ Argument3: 出力する画像の横幅の数値
+ Argument4: 出力する画像の縦幅の数値

+ Result: エラーコードの数値(下記参照)

入力された画像を拡大または縮小して、pngとして出力します。
何か問題があった場合は、Resultに`0`以外が入ります。

横幅と縦幅は、負の数を指定すると、もう片方の拡大縮小率に基づいて自動で値が決まります
(両方負の数にすると、何もせずに終了します)。
また、`0`を指定すると入力された画像の値を使用します。

#### エラーコード

0. 正常終了
1. 対応していない形式だった
2. ファイルが見つからなかった
3. 入出力に問題があった
4. 画像のデコードに問題があった
5. 画像のエンコードに問題があった
6. 画像のパラメータに問題があった
7. 画像の大きさが限界値を越えていた
8. 画像サイズが小さすぎた

## 使用ライブラリ

いずれも敬称略。ありがとうございます。

+ [winapi\_rs](https://github.com/retep998/winapi-rs) / Peter Atashian
+ [encoding\_rs](https://github.com/hsivonen/encoding_rs) / Henri Sivonen
+ [image](https://github.com/image-rs/image) / The image-rs Developers
+ [fast\_image\_resize](https://github.com/cykooz/fast_image_resize) / Kirill Kuzminykh


## ライセンス

MITにて配布いたします。

## 作成者

月波 清火 (tukinami seika) <10forchette@gmail.com>

[GitHub](https://github.com/tukinami/saori-resized-png)
59 changes: 59 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use fast_image_resize as fir;

#[derive(Debug)]
pub(crate) enum ResizedPngError {
Unsupported,
NotFound,
IoError,
DecodingError,
EncodingError,
ParameterError,
LimitsError,
InputSizeError,
}

impl ResizedPngError {
pub(crate) fn to_code(&self) -> u32 {
match self {
Self::Unsupported => 1,
Self::NotFound => 2,
Self::IoError => 3,
Self::DecodingError => 4,
Self::EncodingError => 5,
Self::ParameterError => 6,
Self::LimitsError => 7,
Self::InputSizeError => 8,
}
}
}

impl From<std::io::Error> for ResizedPngError {
fn from(e: std::io::Error) -> Self {
match e.kind() {
std::io::ErrorKind::NotFound => Self::NotFound,
_ => Self::IoError,
}
}
}

impl From<image::error::ImageError> for ResizedPngError {
fn from(e: image::error::ImageError) -> Self {
match e {
image::ImageError::Decoding(_) => Self::DecodingError,
image::ImageError::Encoding(_) => Self::EncodingError,
image::ImageError::Parameter(_) => Self::ParameterError,
image::ImageError::Limits(_) => Self::LimitsError,
image::ImageError::Unsupported(_) => Self::Unsupported,
image::ImageError::IoError(e) => e.into(),
}
}
}

impl From<fir::ImageBufferError> for ResizedPngError {
fn from(e: fir::ImageBufferError) -> Self {
match e {
fir::ImageBufferError::InvalidBufferSize => Self::InputSizeError,
fir::ImageBufferError::InvalidBufferAlignment => Self::DecodingError,
}
}
}
129 changes: 129 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
mod error;
mod procedure;
mod request;
mod resized_png;
mod response;

use winapi::ctypes::c_long;
use winapi::shared::minwindef::{BOOL, DWORD, HGLOBAL, HINSTANCE, LPVOID, MAX_PATH, TRUE};
use winapi::um::libloaderapi::GetModuleFileNameW;
use winapi::um::winbase::{GlobalAlloc, GlobalFree, GMEM_FIXED};
use winapi::um::winnt::{
DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH,
};

use std::slice;

use crate::request::{SaoriCommand, SaoriRequest};
use crate::response::SaoriResponse;

static mut DLL_PATH: String = String::new();

#[no_mangle]
pub extern "system" fn DllMain(
h_module: HINSTANCE,
ul_reason_for_call: DWORD,
_l_reserved: LPVOID,
) -> BOOL {
match ul_reason_for_call {
DLL_PROCESS_ATTACH => {
register_dll_path(h_module);
}
DLL_PROCESS_DETACH => {}
DLL_THREAD_ATTACH => {}
DLL_THREAD_DETACH => {
unload();
}
_ => {}
}
return TRUE;
}

fn register_dll_path(h_module: HINSTANCE) {
let mut buf: [u16; MAX_PATH + 1] = [0; MAX_PATH + 1];
unsafe {
GetModuleFileNameW(h_module, buf.as_mut_ptr(), MAX_PATH as u32);
}

let p = buf.partition_point(|v| *v != 0);

unsafe {
DLL_PATH = String::from_utf16_lossy(&buf[..p]);
}
}

#[no_mangle]
pub extern "cdecl" fn load(h: HGLOBAL, _len: c_long) -> BOOL {
unsafe { GlobalFree(h) };

unsafe { procedure::load(&DLL_PATH) };

return TRUE;
}

#[no_mangle]
pub extern "cdecl" fn unload() -> BOOL {
unsafe { procedure::unload(&DLL_PATH) };
return TRUE;
}

#[no_mangle]
pub extern "cdecl" fn request(h: HGLOBAL, len: *mut c_long) -> HGLOBAL {
// リクエストの取得
let s = unsafe { hglobal_to_vec_u8(h, *len) };
unsafe { GlobalFree(h) };

let request = SaoriRequest::from_u8(&s);

// 返答の組み立て
let mut response = match &request {
Ok(r) => SaoriResponse::from_request(r),
Err(_e) => SaoriResponse::new_bad_request(),
};

if let Ok(r) = request {
match r.command() {
SaoriCommand::GetVersion => {
unsafe { procedure::get_version(&DLL_PATH, &r, &mut response) };
}
SaoriCommand::Execute => {
unsafe { procedure::execute(&DLL_PATH, &r, &mut response) };
}
}
}

let response_bytes = response.to_encoded_bytes().unwrap_or(Vec::new());

let response = slice_u8_to_hglobal(len, &response_bytes);

return response;
}

fn slice_u8_to_hglobal(h_len: *mut c_long, data: &[u8]) -> HGLOBAL {
let data_len = data.len();

let h = unsafe { GlobalAlloc(GMEM_FIXED, data_len) };

unsafe { *h_len = data_len as c_long };

let h_slice = unsafe { slice::from_raw_parts_mut(h as *mut u8, data_len) };

for (index, value) in data.iter().enumerate() {
h_slice[index] = *value;
}

return h;
}

fn hglobal_to_vec_u8(h: HGLOBAL, len: c_long) -> Vec<u8> {
let mut s = vec![0; len as usize + 1];

let slice = unsafe { slice::from_raw_parts(h as *const u8, len as usize) };

for (index, value) in slice.iter().enumerate() {
s[index] = *value;
}
s[len as usize] = b'\0';

return s;
}
68 changes: 68 additions & 0 deletions src/procedure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::path::PathBuf;

use crate::request::*;
use crate::resized_png::{get_image_type, to_resized_png};
use crate::response::*;

/// load時に呼ばれる関数
pub fn load(_path: &str) {}

/// unload時に呼ばれる関数
pub fn unload(_path: &str) {}

/// request GET Version時に呼ばれる関数
pub fn get_version(_path: &str, _request: &SaoriRequest, response: &mut SaoriResponse) {
response.set_result(String::from(env!("CARGO_PKG_VERSION")));
}

/// request EXECUTE時に呼ばれる関数
/// メインの処理はここに記述する
pub fn execute(path: &str, request: &SaoriRequest, response: &mut SaoriResponse) {
let args = request.argument();
let mut path = PathBuf::from(path);
if !path.is_dir() {
path.pop();
}

if let Some(func) = args.get(0) {
match func.as_str() {
"GetImageType" => {
if let Some(input_path_str) = args.get(1) {
let input_path = path.join(input_path_str);

let v = get_image_type(&input_path);

response.set_result(v.to_string());
}
}
"ToResizedPng" => {
if let (
Some(input_path_str),
Some(output_path_str),
Some(width_str),
Some(height_str),
) = (args.get(1), args.get(2), args.get(3), args.get(4))
{
let Ok(width_command) = width_str.parse::<i64>() else { return };
let Ok(height_command) = height_str.parse::<i64>() else { return };

let input_path = path.clone().join(input_path_str);
let output_path = path.join(output_path_str);

let v = match to_resized_png(
&input_path,
&output_path,
width_command,
height_command,
) {
Ok(()) => 0,
Err(e) => e.to_code(),
};

response.set_result(format!("{}", v));
}
}
_ => {}
}
}
}
Loading

0 comments on commit b97d85e

Please sign in to comment.