Skip to content

Commit

Permalink
WIP: drawpile-cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
askmeaboutlo0m committed Sep 3, 2023
1 parent c1c76f9 commit eae9d0c
Show file tree
Hide file tree
Showing 13 changed files with 833 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"src/drawdance/rust",
"src/drawdance/libmsg",
"src/tools/dprectool",
"src/tools/drawpile-cmd",
]

[workspace.package]
Expand Down
22 changes: 22 additions & 0 deletions src/drawdance/rust/engine/acl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::{DP_AclState, DP_acl_state_free, DP_acl_state_new};

pub struct AclState {
acls: *mut DP_AclState,
}

impl AclState {
pub fn new() -> Self {
let acls = unsafe { DP_acl_state_new() };
AclState { acls }
}

pub fn as_ptr(&mut self) -> *mut DP_AclState {
self.acls
}
}

impl Drop for AclState {
fn drop(&mut self) {
unsafe { DP_acl_state_free(self.acls) }
}
}
22 changes: 22 additions & 0 deletions src/drawdance/rust/engine/draw_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::{DP_DrawContext, DP_draw_context_free, DP_draw_context_new};

pub struct DrawContext {
dc: *mut DP_DrawContext,
}

impl DrawContext {
pub fn new() -> Self {
let dc = unsafe { DP_draw_context_new() };
DrawContext { dc }
}

pub fn as_ptr(&mut self) -> *mut DP_DrawContext {
self.dc
}
}

impl Drop for DrawContext {
fn drop(&mut self) {
unsafe { DP_draw_context_free(self.dc) }
}
}
226 changes: 226 additions & 0 deletions src/drawdance/rust/engine/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
use core::slice;
use std::{
ffi::{c_int, CString, NulError},
io::{self},
mem::size_of,
num::TryFromIntError,
ptr::{self, copy_nonoverlapping},
};

use crate::{
dp_error, DP_Image, DP_Output, DP_Pixel8, DP_Quad, DP_file_output_new_from_path, DP_image_free,
DP_image_height, DP_image_new, DP_image_pixels, DP_image_transform_pixels, DP_image_width,
DP_image_write_jpeg, DP_image_write_png, DP_output_free, DP_MSG_TRANSFORM_REGION_MODE_BILINEAR,
};

use super::DrawContext;

pub struct Image {
image: *mut DP_Image,
}

#[derive(Debug)]
pub struct ImageError {
pub message: String,
}

impl ImageError {
fn from_dp_error() -> Self {
Self {
message: dp_error(),
}
}
}

impl From<&str> for ImageError {
fn from(value: &str) -> Self {
Self {
message: value.to_owned(),
}
}
}

impl From<TryFromIntError> for ImageError {
fn from(value: TryFromIntError) -> Self {
Self {
message: value.to_string(),
}
}
}

impl From<NulError> for ImageError {
fn from(value: NulError) -> Self {
Self {
message: value.to_string(),
}
}
}

impl Image {
pub fn new(width: usize, height: usize) -> Result<Self, ImageError> {
if width > 0 && height > 0 {
let w = c_int::try_from(width)?;
let h = c_int::try_from(height)?;
let image = unsafe { DP_image_new(w, h) };
Ok(Self { image })
} else {
Err(ImageError::from("Empty image"))
}
}

pub fn new_from_pixels(
width: usize,
height: usize,
pixels: &[u32],
) -> Result<Self, ImageError> {
let count = width * height;
if pixels.len() >= count {
let img = Self::new(width, height)?;
unsafe {
copy_nonoverlapping(
pixels.as_ptr(),
DP_image_pixels(img.image).cast::<u32>(),
count,
);
}
Ok(img)
} else {
Err(ImageError::from("Not enough pixels"))
}
}

pub fn new_from_pixels_scaled(
width: usize,
height: usize,
pixels: &[u32],
scale_width: usize,
scale_height: usize,
dc: &mut DrawContext,
) -> Result<Self, ImageError> {
if width == 0 || height == 0 {
return Err(ImageError::from("Empty source image"));
}

if scale_width == 0 || scale_height == 0 {
return Err(ImageError::from("Empty target image"));
}

let count = width * height;
if pixels.len() < count {
return Err(ImageError::from("Not enough pixels"));
}

let right = c_int::try_from(scale_width - 1)?;
let bottom = c_int::try_from(scale_height - 1)?;
let dst_quad = DP_Quad {
x1: 0,
y1: 0,
x2: right,
y2: 0,
x3: right,
y3: bottom,
x4: 0,
y4: bottom,
};

let image = unsafe {
DP_image_transform_pixels(
c_int::try_from(width)?,
c_int::try_from(height)?,
pixels.as_ptr().cast(),
dc.as_ptr(),
&dst_quad,
DP_MSG_TRANSFORM_REGION_MODE_BILINEAR as i32,
ptr::null_mut(),
ptr::null_mut(),
)
};

if image.is_null() {
Err(ImageError::from_dp_error())
} else {
Ok(Image { image })
}
}

pub fn width(&self) -> usize {
unsafe { DP_image_width(self.image) as usize }
}

pub fn height(&self) -> usize {
unsafe { DP_image_height(self.image) as usize }
}

pub fn dump(&self, writer: &mut dyn io::Write) -> io::Result<()> {
let pixels = unsafe { DP_image_pixels(self.image) };
let size = self.width() * self.height() * size_of::<u32>();
writer.write_all(unsafe { slice::from_raw_parts(pixels.cast::<u8>(), size) })
}

pub fn write_png(&self, path: &str) -> Result<(), ImageError> {
self.write(path, DP_image_write_png)
}

pub fn write_jpeg(&self, path: &str) -> Result<(), ImageError> {
self.write(path, DP_image_write_jpeg)
}

fn write(
&self,
path: &str,
func: unsafe extern "C" fn(*mut DP_Image, *mut DP_Output) -> bool,
) -> Result<(), ImageError> {
let cpath = CString::new(path)?;
let output = unsafe { DP_file_output_new_from_path(cpath.as_ptr()) };
if output.is_null() {
return Err(ImageError::from_dp_error());
}
let result = match unsafe { func(self.image, output) } {
true => Ok(()),
false => Err(ImageError::from_dp_error()),
};
unsafe { DP_output_free(output) };
result
}

pub fn fade_to_white(&mut self, src: &Image, opacity: u8) -> Result<(), ImageError> {
let w = self.width();
let h = self.height();
if w != src.width() || h != src.height() {
return Err(ImageError::from("Mismatched dimensions"));
}

let count = w * h;
let src = unsafe { slice::from_raw_parts(DP_image_pixels(src.image), count) };
let dst = unsafe { slice::from_raw_parts_mut(DP_image_pixels(self.image), count) };
let opa = opacity as u32;
for i in 0..count {
dst[i] = Self::blend_white(&src[i], opa);
}

Ok(())
}

fn blend_white(src: &DP_Pixel8, opa: u32) -> DP_Pixel8 {
let opa1 = 255u32 - opa;
let color = unsafe { src.color };
let r = Self::mul(0xffu32, opa) + Self::mul(color & 0xffu32, opa1);
let g = Self::mul(0xffu32, opa) + Self::mul((color >> 8u32) & 0xffu32, opa1);
let b = Self::mul(0xffu32, opa) + Self::mul((color >> 16u32) & 0xffu32, opa1);
let a = color & 0xff000000u32;
DP_Pixel8 {
color: (r as u32) | ((g as u32) << 8u32) | ((b as u32) << 16u32) | a,
}
}

fn mul(a: u32, b: u32) -> u8 {
let c = a * b + 0x80u32;
(((c >> 8u32) + c) >> 8u32) as u8
}
}

impl Drop for Image {
fn drop(&mut self) {
unsafe { DP_image_free(self.image) }
}
}
8 changes: 8 additions & 0 deletions src/drawdance/rust/engine/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-or-later

mod acl;
mod draw_context;
mod image;
mod paint_engine;
mod player;
mod recorder;

pub use acl::AclState;
pub use draw_context::DrawContext;
pub use image::{Image, ImageError};
pub use paint_engine::{PaintEngine, PaintEngineError};
pub use player::{Player, PlayerError};
pub use recorder::{Recorder, RecorderError};
Loading

0 comments on commit eae9d0c

Please sign in to comment.