diff --git a/plugins/filters/pixelizefilter/CMakeLists.txt b/plugins/filters/pixelizefilter/CMakeLists.txt index a75e23eeca..fdb85ab402 100644 --- a/plugins/filters/pixelizefilter/CMakeLists.txt +++ b/plugins/filters/pixelizefilter/CMakeLists.txt @@ -1,4 +1,34 @@ +set(RUST_CARGO_EXECUTABLE "D:\\dev\\toolchain\\rust\\cargo\\bin\\cargo.exe") + +# FIXME: Specify target only for Windows (mingw) +# FIXME: Detect bitness +set(RUST_TARGET "x86_64-pc-windows-gnu") +# FIXME: ... +list(APPEND RUST_NATIVE_STATIC_LIBS userenv) + +set(RUST_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/rust-target") +# Note: `rustc` does *not* output libxxx.a on Windows even with the GNU target. +# FIXME: look for proper file on *nix +set(krita_filter_pixelize_rs_OUTLIB "${RUST_TARGET_DIR}/x86_64-pc-windows-gnu/debug/krita_filter_pixelize_rs.lib") + +# Dummy target to cause `cargo` to always run +add_custom_command(OUTPUT always_build COMMAND ${CMAKE_COMMAND} -E echo_append) + +# FIXME: Add `--release` for release build +add_custom_command( + OUTPUT "${krita_filter_pixelize_rs_OUTLIB}" + COMMAND "${RUST_CARGO_EXECUTABLE}" ARGS build --target x86_64-pc-windows-gnu --target-dir "${RUST_TARGET_DIR}" + DEPENDS always_build + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/rust" + USES_TERMINAL +) + +add_custom_target(krita_filter_pixelize_rs DEPENDS "${krita_filter_pixelize_rs_OUTLIB}") + + set(kritapixelizefilter_SOURCES kis_pixelize_filter_plugin.cpp kis_pixelize_filter.cpp ) add_library(kritapixelizefilter MODULE ${kritapixelizefilter_SOURCES}) -target_link_libraries(kritapixelizefilter kritaui) +target_compile_definitions(kritapixelizefilter PRIVATE -DUSE_RUST) +add_dependencies(kritapixelizefilter krita_filter_pixelize_rs) +target_link_libraries(kritapixelizefilter PRIVATE kritaui "${krita_filter_pixelize_rs_OUTLIB}" ${RUST_NATIVE_STATIC_LIBS}) install(TARGETS kritapixelizefilter DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/filters/pixelizefilter/kis_pixelize_filter.cpp b/plugins/filters/pixelizefilter/kis_pixelize_filter.cpp index 704fab4ef1..06291202bb 100644 --- a/plugins/filters/pixelizefilter/kis_pixelize_filter.cpp +++ b/plugins/filters/pixelizefilter/kis_pixelize_filter.cpp @@ -54,6 +54,37 @@ #include "kis_algebra_2d.h" #include "kis_lod_transform.h" +#ifdef USE_RUST +#include "krita_filter_pixelize_rs.hpp" +extern "C" { + +bool kisSequentialConstIteratorNextPixelCallback(KisSequentialConstIterator *it) +{ + return it->nextPixel(); +} + +const quint8 *kisSequentialConstIteratorOldRawDataCallback(const KisSequentialConstIterator *it) +{ + return it->oldRawData(); +} + +bool kisSequentialIteratorNextPixelCallback(KisSequentialIterator *it) +{ + return it->nextPixel(); +} + +quint8 *kisSequentialIteratorRawDataCallback(KisSequentialIterator *it) +{ + return it->rawData(); +} + +void koMixColorsOpMixColors(const KoMixColorsOp *mixOp, const quint8 *colors, quint32 nColors, quint8 *dst) +{ + mixOp->mixColors(colors, nColors, dst); +} + +} // extern "C" +#endif KisPixelizeFilter::KisPixelizeFilter() : KisFilter(id(), FiltersCategoryArtisticId, i18n("&Pixelize...")) { @@ -105,10 +136,16 @@ void KisPixelizeFilter::processImpl(KisPaintDeviceSP device, //read KisSequentialConstIterator srcIt(device, pixelRect); + // write only colors in applyRect + const QRect writeRect = pixelRect & applyRect; + KisSequentialIterator dstIt(device, writeRect); memset(buffer.data(), 0, bufferSize); quint8 *bufferPtr = buffer.data(); +#ifdef USE_RUST + krita_filter_pixelize_rs_process_block(&srcIt, &dstIt, pixelSize, pixelWidth, pixelHeight, mixOp, bufferPtr, numColors, pixelColor.data()); +#else while (srcIt.nextPixel()) { memcpy(bufferPtr, srcIt.oldRawData(), pixelSize); bufferPtr += pixelSize; @@ -117,13 +154,10 @@ void KisPixelizeFilter::processImpl(KisPaintDeviceSP device, // mix all the colors mixOp->mixColors(buffer.data(), numColors, pixelColor.data()); - // write only colors in applyRect - const QRect writeRect = pixelRect & applyRect; - - KisSequentialIterator dstIt(device, writeRect); while (dstIt.nextPixel()) { memcpy(dstIt.rawData(), pixelColor.data(), pixelSize); } +#endif } progressUpdater->setValue(i); } diff --git a/plugins/filters/pixelizefilter/krita_filter_pixelize_rs.hpp b/plugins/filters/pixelizefilter/krita_filter_pixelize_rs.hpp new file mode 100644 index 0000000000..52972702ac --- /dev/null +++ b/plugins/filters/pixelizefilter/krita_filter_pixelize_rs.hpp @@ -0,0 +1,18 @@ +#ifndef KRITA_FILTER_PIXELIZE_RS_HPP +#define KRITA_FILTER_PIXELIZE_RS_HPP + +extern "C" { + +void krita_filter_pixelize_rs_process_block(KisSequentialConstIterator *src_it, + KisSequentialIterator *dst_it, + qint32 pixel_size, + qint32 pixelize_width, + qint32 pixelize_height, + const KoMixColorsOp *ko_mix_colors_op, + quint8 *working_buffer, + quint32 num_colors, + quint8 *pixel_color_data); + +} // extern "C" + +#endif diff --git a/plugins/filters/pixelizefilter/rust/.gitignore b/plugins/filters/pixelizefilter/rust/.gitignore new file mode 100644 index 0000000000..2f7896d1d1 --- /dev/null +++ b/plugins/filters/pixelizefilter/rust/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/plugins/filters/pixelizefilter/rust/Cargo.lock b/plugins/filters/pixelizefilter/rust/Cargo.lock new file mode 100644 index 0000000000..779a2ad1c3 --- /dev/null +++ b/plugins/filters/pixelizefilter/rust/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "krita-filter-pixelize-rs" +version = "0.1.0" + diff --git a/plugins/filters/pixelizefilter/rust/Cargo.toml b/plugins/filters/pixelizefilter/rust/Cargo.toml new file mode 100644 index 0000000000..7b541c3a1b --- /dev/null +++ b/plugins/filters/pixelizefilter/rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "krita-filter-pixelize-rs" +version = "0.1.0" +authors = ["Alvin Wong "] +edition = "2018" +publish = false + +[dependencies] + +[lib] +crate-type = ["staticlib"] diff --git a/plugins/filters/pixelizefilter/rust/src/krita_ffi.rs b/plugins/filters/pixelizefilter/rust/src/krita_ffi.rs new file mode 100644 index 0000000000..40343a8121 --- /dev/null +++ b/plugins/filters/pixelizefilter/rust/src/krita_ffi.rs @@ -0,0 +1,63 @@ +extern "C" { + fn kisSequentialConstIteratorNextPixelCallback(it: *mut KisSequentialConstIterator) -> bool; + fn kisSequentialConstIteratorOldRawDataCallback( + it: *const KisSequentialConstIterator, + ) -> *const u8; + fn kisSequentialIteratorNextPixelCallback(it: *mut KisSequentialIterator) -> bool; + fn kisSequentialIteratorRawDataCallback(it: *mut KisSequentialIterator) -> *mut u8; + fn koMixColorsOpMixColors( + ko_mix_colors_op: *const KoMixColorsOp, + colors: *const u8, + n_colors: u32, + dst: *mut KoColorData, + ); +} + +#[repr(C)] +pub struct KisSequentialConstIterator { + _private: [u8; 0], +} + +impl KisSequentialConstIterator { + pub fn next_pixel(&mut self) -> bool { + unsafe { kisSequentialConstIteratorNextPixelCallback(self) } + } + + pub unsafe fn old_raw_data_ptr(&self) -> *const u8 { + let pixel_ptr = kisSequentialConstIteratorOldRawDataCallback(self); + // slice::from_raw_parts(pixel_ptr, pixel_size as usize) + pixel_ptr + } +} + +#[repr(C)] +pub struct KisSequentialIterator { + _private: [u8; 0], +} + +impl KisSequentialIterator { + pub fn next_pixel(&mut self) -> bool { + unsafe { kisSequentialIteratorNextPixelCallback(self) } + } + + pub unsafe fn raw_data_ptr(&mut self) -> *mut u8 { + let pixel_ptr = kisSequentialIteratorRawDataCallback(self); + pixel_ptr + } +} + +#[repr(C)] +pub struct KoColorData { + _private: [u8; 0], +} + +#[repr(C)] +pub struct KoMixColorsOp { + _private: [u8; 0], +} + +impl KoMixColorsOp { + pub unsafe fn mix_colors(&self, colors: *const u8, n_colors: u32, dst: *mut KoColorData) { + koMixColorsOpMixColors(self, colors, n_colors, dst); + } +} diff --git a/plugins/filters/pixelizefilter/rust/src/lib.rs b/plugins/filters/pixelizefilter/rust/src/lib.rs new file mode 100644 index 0000000000..9949a81d11 --- /dev/null +++ b/plugins/filters/pixelizefilter/rust/src/lib.rs @@ -0,0 +1,80 @@ +mod krita_ffi; + +use crate::krita_ffi::KisSequentialConstIterator; +use crate::krita_ffi::KisSequentialIterator; +use crate::krita_ffi::KoColorData; +use crate::krita_ffi::KoMixColorsOp; + +/// Invokes a closure and aborts if an unwinding panic occurs. +/// +/// This must be used in any exported functions callable from C/C++ code, as +/// unwinding from Rust code into external caller is undefined behaviour. One +/// must not assume an panic will not happen in Rust code. +/// +/// An exception is when the specific function provides an alternative +/// mechanism to inform the caller of a panic and it can be certain that the +/// program will be able to properly recover from a panic condition, which in +/// such cases it is still an absolute requirement to wrap the code in +/// `std::panic::catch_unwind` and manually handle the error. +fn catch_unwind_abort R + std::panic::UnwindSafe, R>(f: F) -> R { + std::panic::catch_unwind(f).unwrap_or_else(|_| { + std::process::abort(); + }) +} + +#[no_mangle] +pub extern "C" fn krita_filter_pixelize_rs_process_block( + src_it: *mut KisSequentialConstIterator, + dst_it: *mut KisSequentialIterator, + pixel_size: i32, + _pixelize_width: i32, + _pixelize_height: i32, + ko_mix_colors_op: *const KoMixColorsOp, + working_buffer: *mut u8, + num_colors: u32, + pixel_color_data: *mut KoColorData, +) { + catch_unwind_abort(|| { + let src_it = unsafe { &mut *src_it }; + let dst_it = unsafe { &mut *dst_it }; + let mix_op = unsafe { &*ko_mix_colors_op }; + + let mut buffer_ptr = working_buffer; + while src_it.next_pixel() { + unsafe { + std::ptr::copy_nonoverlapping( + src_it.old_raw_data_ptr(), + buffer_ptr, + pixel_size as usize, + ); + buffer_ptr = buffer_ptr.offset(pixel_size as isize); + } + } + + // mix all the colors + unsafe { + mix_op.mix_colors(working_buffer, num_colors, pixel_color_data); + } + + while dst_it.next_pixel() { + unsafe { + std::ptr::copy_nonoverlapping( + pixel_color_data as *const u8, + dst_it.raw_data_ptr(), + pixel_size as usize, + ); + } + } + }) +} + +#[cfg(test)] +mod tests { + // /// This test cannot be made to pass unless [rust-lang/rust#32512][1] is + // /// fixed. + // /// [1]: https://github.com/rust-lang/rust/issues/32512 + // #[test] + // fn test_should_abort() { + // super::catch_unwind_abort(|| panic!("test panic")); + // } +}