-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Might even be possible to port Filmic Chroma to it. It'll need to FFI directly still but one less build to maintain.
- Loading branch information
Showing
6 changed files
with
323 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
#!/usr/bin/env python3 | ||
# -*- coding: utf-8 -*- | ||
|
||
""" | ||
Gegl Operation references: | ||
http://www.gegl.org/operations/ | ||
https://gitlab.gnome.org/GNOME/gegl/-/tree/master/operations | ||
If on Linux: | ||
$ gegl --list-all | ||
$ gegl --info "operation-name" | ||
Also build the .gir files using g-ir-doc-tool for additional insight. | ||
If the docs don't have a description on something like class methods, | ||
run python's help() on it to view it in the terminal. | ||
""" | ||
|
||
# Uncomment as needed. | ||
# I don't actually know if it's faster to not import some of the gi repo stuff | ||
# since it probably gets imported later anyway ...right? | ||
import gi | ||
gi.require_version('Gimp', '3.0') | ||
from gi.repository import Gimp | ||
gi.require_version('Gegl', '0.4') | ||
from gi.repository import Gegl | ||
# from gi.repository import GObject | ||
# from gi.repository import GLib | ||
# from gi.repository import Gio | ||
import sys | ||
import os.path | ||
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + '/../') | ||
from bsz_gimp_lib import PlugIn, ParamCombo, ParamString | ||
|
||
import ctypes | ||
from sys import platform | ||
EXTENSIONS = {"win32": ".dll", "linux": ".so"} | ||
PM2 = ctypes.CDLL( | ||
os.path.dirname(os.path.realpath(__file__)) + | ||
"/pixel_math_2" + EXTENSIONS.get(platform) | ||
).pixel_math_2 | ||
PM2.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint] | ||
|
||
|
||
FORMATS = { | ||
"RGBA": "RGBA double", | ||
"HSLA": "HSLA double", | ||
"LABA": "CIE Lab alpha double", | ||
"LCHA": "CIE LCH(ab) alpha double", | ||
} | ||
|
||
|
||
# Main function. | ||
def pixel_math(image, drawable, babl_format, code): | ||
# {{{ | ||
# Fairly certain mask_intersect() is the current selection mask | ||
intersect, x, y, width, height = drawable.mask_intersect() | ||
if intersect: | ||
# start Gegl | ||
Gegl.init(None) | ||
# fetch main buffer | ||
buff = drawable.get_buffer() | ||
|
||
# fetch shadow aka "temp" buffer | ||
shadow = drawable.get_shadow_buffer() | ||
|
||
# create working rectangle area using mask intersect. | ||
rect = Gegl.Rectangle.new(x, y, width, height) | ||
|
||
if babl_format == "RGBA double": | ||
channels = "rgba" | ||
elif babl_format == "HSLA double": | ||
channels = "hsla" | ||
elif babl_format == "CIE Lab alpha double": | ||
channels = "laba" | ||
elif babl_format == "CIE LCH(ab) alpha double": | ||
channels = "lcha" | ||
else: | ||
raise ValueError("Invalid/unsupported BABL format") | ||
|
||
code = code.casefold() | ||
|
||
for num, c in enumerate(channels): | ||
code = code.replace(f"{c} ", f"c{num+1} ") | ||
code = code.replace(f"{c}\n", f"c{num+1}\n") | ||
# since everything is whitespace separated in rust | ||
# you can just search for whitespace to avoid | ||
# double-replacing 'c' | ||
|
||
pixels = buff.get(rect, 1.0, babl_format, | ||
Gegl.AbyssPolicy.CLAMP) | ||
|
||
PM2(code.encode('UTF-8'), pixels, len(pixels)) | ||
|
||
shadow.set(rect, babl_format, bytes(pixels)) | ||
|
||
# Flush shadow buffer and combine it with main drawable | ||
shadow.flush() | ||
drawable.merge_shadow(True) | ||
|
||
# Update everything. | ||
drawable.update(x, y, width, height) | ||
Gimp.displays_flush() | ||
# }}} | ||
|
||
|
||
# create the plugin from bsz_gimp_lib | ||
plugin = PlugIn( | ||
"Pixel Math 2", # name | ||
pixel_math, # function | ||
ParamCombo('Format', FORMATS, "HSLA double", "Pixel format"), | ||
|
||
ParamString("Operations", | ||
"v1 = L\n" | ||
"L = 1\n" | ||
"L - v1", | ||
"See description for code documentation", | ||
ui_multiline=True, | ||
ui_min_width=600, ui_min_height=200), | ||
|
||
description="""\ | ||
Pixel math. Code format is {channel} {operator} {value} | ||
So c1 + 0.5 will add 0.5 to the first channel. | ||
Available channels: | ||
c1, c2, c3, c4 (always alpha). These are mapped to RGBA, HSLA, etc. | ||
You may also use the channel letters themselves, such as | ||
r, g, b, a | ||
Supported operators are | ||
= or == : set/assign | ||
+ or += : add | ||
- or -= : subtract | ||
* or *= : multiply | ||
/ or /= : divide | ||
In addition to channels, you can store temporary values in variables. | ||
Available variables: | ||
v1, v2, v3...v9 | ||
""", | ||
images="RGB*, GRAY*", | ||
) | ||
|
||
# register the plugin's Procedure class with gimp | ||
Gimp.main(plugin.Procedure.__gtype__, sys.argv) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
cd "${0%/*}" | ||
rustc --crate-type cdylib -O pixel-math-2.rs | ||
mv libpixel_math_2.so pixel_math_2.so | ||
strip pixel_math_2.so | ||
rustc --crate-type cdylib -O --target x86_64-pc-windows-gnu pixel-math-2.rs | ||
rm libpixel_math_2.dll.a | ||
strip pixel_math_2.dll |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
use std::convert::TryInto; | ||
use std::os::raw; | ||
|
||
|
||
#[derive(Clone, Copy)] | ||
enum Op { | ||
Add, | ||
Sub, | ||
Mul, | ||
Div, | ||
Set, | ||
} | ||
|
||
|
||
#[derive(Clone, Copy)] | ||
enum Obj { | ||
Chan(usize), | ||
Var(usize), | ||
Num(f64), | ||
} | ||
|
||
|
||
#[derive(Clone, Copy)] | ||
struct Operation { | ||
target: Obj, | ||
operation: Op, | ||
value: Obj, | ||
} | ||
|
||
|
||
fn parse_ops(ops: &str) -> Vec<Operation> { | ||
let mut line = 0; | ||
let mut result = Vec::<Operation>::new(); | ||
for op in ops.trim().to_ascii_lowercase().split('\n') { | ||
line += 1; | ||
let items = op.split_ascii_whitespace().collect::<Vec<&str>>(); | ||
if items.len() != 3 { | ||
println!("Invalid number of args on operation line {}", line); | ||
continue | ||
} | ||
|
||
result.push( Operation{ | ||
target: match items[0] { | ||
// don't hate I made these with a vim macro | ||
"c1" => Obj::Chan(0), | ||
"c2" => Obj::Chan(1), | ||
"c3" => Obj::Chan(2), | ||
"c4" => Obj::Chan(3), | ||
"v1" => Obj::Var(0), | ||
"v2" => Obj::Var(1), | ||
"v3" => Obj::Var(2), | ||
"v4" => Obj::Var(3), | ||
"v5" => Obj::Var(4), | ||
"v6" => Obj::Var(5), | ||
"v7" => Obj::Var(6), | ||
"v8" => Obj::Var(7), | ||
"v9" => Obj::Var(8), | ||
_ => { | ||
println!("Invalid target on operation line {}", line); | ||
continue | ||
}, | ||
}, | ||
|
||
operation: match items[1] { | ||
"+=" | "+" => Op::Add, | ||
"-=" | "-" => Op::Sub, | ||
"*=" | "*" => Op::Mul, | ||
"/=" | "/" => Op::Div, | ||
"==" | "=" => Op::Set, | ||
_ => { | ||
println!("Invalid math operator on operation line {}", line); | ||
continue | ||
}, | ||
}, | ||
|
||
value: match items[2] { | ||
"c1" => Obj::Chan(0), | ||
"c2" => Obj::Chan(1), | ||
"c3" => Obj::Chan(2), | ||
"c4" => Obj::Chan(3), | ||
"v1" => Obj::Var(0), | ||
"v2" => Obj::Var(1), | ||
"v3" => Obj::Var(2), | ||
"v4" => Obj::Var(3), | ||
"v5" => Obj::Var(4), | ||
"v6" => Obj::Var(5), | ||
"v7" => Obj::Var(6), | ||
"v8" => Obj::Var(7), | ||
"v9" => Obj::Var(8), | ||
s => { | ||
match s.parse::<f64>() { | ||
Ok(n) => Obj::Num(n), | ||
Err(_) => { | ||
println!("Invalid value on operation line {}", line); | ||
continue | ||
} | ||
} | ||
}, | ||
} | ||
}); | ||
|
||
} | ||
|
||
result | ||
} | ||
|
||
|
||
#[no_mangle] | ||
pub extern "C" fn pixel_math_2( | ||
code: *const raw::c_char, | ||
pixels: *mut raw::c_char, | ||
len: usize) { | ||
|
||
let code = unsafe { | ||
assert!(!code.is_null()); | ||
std::ffi::CStr::from_ptr(code).to_str().expect("Invalid code string") | ||
}; | ||
let pixels = unsafe { | ||
assert!(!pixels.is_null()); | ||
std::slice::from_raw_parts_mut(pixels.cast::<u8>(), len) | ||
}; | ||
|
||
let ops = parse_ops(code); | ||
|
||
for chunk in pixels.chunks_exact_mut(32) { | ||
let mut c: [f64; 4] = [ | ||
f64::from_le_bytes(chunk[0..8].try_into().expect("bytefail c1")), | ||
f64::from_le_bytes(chunk[8..16].try_into().expect("bytefail c2")), | ||
f64::from_le_bytes(chunk[16..24].try_into().expect("bytefail c3")), | ||
f64::from_le_bytes(chunk[24..32].try_into().expect("bytefail c4")), | ||
]; | ||
let mut v: [f64; 9] = [0.0; 9]; | ||
|
||
for op in ops.iter() { | ||
let val: f64 = match op.value { | ||
Obj::Chan(i) => c[i], | ||
Obj::Var(i) => v[i], | ||
Obj::Num(n) => n, | ||
}; | ||
let tar: &mut f64 = match op.target { | ||
Obj::Chan(i) => &mut c[i], | ||
Obj::Var(i) => &mut v[i], | ||
Obj::Num(_) => panic!("This shouldn't be reachable"), | ||
}; | ||
match op.operation { | ||
Op::Add => *tar += val, | ||
Op::Sub => *tar -= val, | ||
Op::Mul => *tar *= val, | ||
Op::Div => *tar /= val, | ||
Op::Set => *tar = val, | ||
}; | ||
} | ||
|
||
let c1b = c[0].to_le_bytes(); | ||
let c2b = c[1].to_le_bytes(); | ||
let c3b = c[2].to_le_bytes(); | ||
let c4b = c[3].to_le_bytes(); | ||
for x in 0..8 {chunk[x] = c1b[x]}; | ||
for x in 0..8 {chunk[x+8] = c2b[x]}; | ||
for x in 0..8 {chunk[x+16] = c3b[x]}; | ||
for x in 0..8 {chunk[x+24] = c4b[x]}; | ||
}; | ||
} |