@pixagram/renderart 0.4.5 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +171 -67
- package/dist/crt-gpu.d.ts +30 -0
- package/dist/crt-gpu.d.ts.map +1 -0
- package/dist/crt-gpu.js +282 -0
- package/dist/crt-gpu.js.map +1 -0
- package/dist/hex-gpu.d.ts +35 -0
- package/dist/hex-gpu.d.ts.map +1 -0
- package/dist/hex-gpu.js +382 -0
- package/dist/hex-gpu.js.map +1 -0
- package/dist/index.d.ts +24 -300
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -963
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +84 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/wasm-wrapper.d.ts +71 -0
- package/dist/wasm-wrapper.d.ts.map +1 -0
- package/dist/wasm-wrapper.js +76 -0
- package/dist/wasm-wrapper.js.map +1 -0
- package/dist/xbrz-gpu.d.ts +34 -0
- package/dist/xbrz-gpu.d.ts.map +1 -0
- package/dist/xbrz-gpu.js +640 -0
- package/dist/xbrz-gpu.js.map +1 -0
- package/package.json +47 -35
- package/src/crt-gpu.ts +313 -0
- package/src/hex-gpu.ts +426 -0
- package/src/index.ts +47 -0
- package/src/types.ts +90 -0
- package/src/wasm/crt.rs +181 -0
- package/src/wasm/hex.rs +324 -0
- package/src/wasm/lib.rs +285 -0
- package/src/wasm/xbrz.rs +262 -0
- package/src/wasm-wrapper.ts +195 -0
- package/src/xbrz-gpu.ts +671 -0
- package/dist/index.d.mts +0 -305
- package/dist/index.mjs +0 -948
- package/dist/index.mjs.map +0 -1
- package/pkg/LICENSE +0 -21
- package/pkg/README.md +0 -117
- package/pkg/renderart_wasm.d.ts +0 -52
- package/pkg/renderart_wasm.js +0 -5
- package/pkg/renderart_wasm_bg.js +0 -283
- package/pkg/renderart_wasm_bg.wasm +0 -0
- package/pkg/renderart_wasm_bg.wasm.d.ts +0 -24
package/src/wasm/lib.rs
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
//! RenderArt WASM Module
|
|
2
|
+
//!
|
|
3
|
+
//! High-performance pixel art rendering engines for WebAssembly.
|
|
4
|
+
|
|
5
|
+
use wasm_bindgen::prelude::*;
|
|
6
|
+
|
|
7
|
+
mod crt;
|
|
8
|
+
mod hex;
|
|
9
|
+
mod xbrz;
|
|
10
|
+
|
|
11
|
+
// Static output buffers to avoid deallocation issues
|
|
12
|
+
static mut CRT_BUFFER: Vec<u8> = Vec::new();
|
|
13
|
+
static mut HEX_BUFFER: Vec<u8> = Vec::new();
|
|
14
|
+
static mut XBRZ_BUFFER: Vec<u8> = Vec::new();
|
|
15
|
+
|
|
16
|
+
/// Result of an upscale operation
|
|
17
|
+
#[wasm_bindgen]
|
|
18
|
+
pub struct UpscaleResult {
|
|
19
|
+
pub ptr: u32,
|
|
20
|
+
pub len: u32,
|
|
21
|
+
pub width: u32,
|
|
22
|
+
pub height: u32,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Get WASM memory for reading output buffers
|
|
26
|
+
#[wasm_bindgen]
|
|
27
|
+
pub fn get_memory() -> JsValue {
|
|
28
|
+
wasm_bindgen::memory()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// CRT Functions
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
/// CRT upscale with default config
|
|
36
|
+
#[wasm_bindgen]
|
|
37
|
+
pub fn crt_upscale(data: &[u8], width: u32, height: u32, scale: u32) -> UpscaleResult {
|
|
38
|
+
crt_upscale_config(
|
|
39
|
+
data, width, height, scale,
|
|
40
|
+
0.015, 0.02, // warp_x, warp_y
|
|
41
|
+
-4.0, 0.5, 0.3, // scan_hardness, scan_opacity, mask_opacity
|
|
42
|
+
true, true, true // enable_warp, enable_scanlines, enable_mask
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// CRT upscale with full config
|
|
47
|
+
#[wasm_bindgen]
|
|
48
|
+
pub fn crt_upscale_config(
|
|
49
|
+
data: &[u8],
|
|
50
|
+
width: u32,
|
|
51
|
+
height: u32,
|
|
52
|
+
scale: u32,
|
|
53
|
+
warp_x: f32,
|
|
54
|
+
warp_y: f32,
|
|
55
|
+
scan_hardness: f32,
|
|
56
|
+
scan_opacity: f32,
|
|
57
|
+
mask_opacity: f32,
|
|
58
|
+
enable_warp: bool,
|
|
59
|
+
enable_scanlines: bool,
|
|
60
|
+
enable_mask: bool,
|
|
61
|
+
) -> UpscaleResult {
|
|
62
|
+
let config = crt::CrtConfig {
|
|
63
|
+
warp_x,
|
|
64
|
+
warp_y,
|
|
65
|
+
scan_hardness,
|
|
66
|
+
scan_opacity,
|
|
67
|
+
mask_opacity,
|
|
68
|
+
enable_warp,
|
|
69
|
+
enable_scanlines,
|
|
70
|
+
enable_mask,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
let output = crt::crt_upscale(data, width as usize, height as usize, scale as usize, &config);
|
|
74
|
+
let out_width = width * scale;
|
|
75
|
+
let out_height = height * scale;
|
|
76
|
+
|
|
77
|
+
unsafe {
|
|
78
|
+
CRT_BUFFER = output;
|
|
79
|
+
UpscaleResult {
|
|
80
|
+
ptr: CRT_BUFFER.as_ptr() as u32,
|
|
81
|
+
len: CRT_BUFFER.len() as u32,
|
|
82
|
+
width: out_width,
|
|
83
|
+
height: out_height,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// HEX Functions
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
/// HEX upscale with default config
|
|
93
|
+
#[wasm_bindgen]
|
|
94
|
+
pub fn hex_upscale(data: &[u8], width: u32, height: u32, scale: u32) -> UpscaleResult {
|
|
95
|
+
hex_upscale_config(
|
|
96
|
+
data, width, height, scale,
|
|
97
|
+
0, // orientation (flat-top)
|
|
98
|
+
false, // draw_borders
|
|
99
|
+
0x282828FF, // border_color
|
|
100
|
+
1, // border_thickness
|
|
101
|
+
0x00000000 // background_color
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// HEX upscale with full config
|
|
106
|
+
#[wasm_bindgen]
|
|
107
|
+
pub fn hex_upscale_config(
|
|
108
|
+
data: &[u8],
|
|
109
|
+
width: u32,
|
|
110
|
+
height: u32,
|
|
111
|
+
scale: u32,
|
|
112
|
+
orientation: u32,
|
|
113
|
+
draw_borders: bool,
|
|
114
|
+
border_color: u32,
|
|
115
|
+
border_thickness: u32,
|
|
116
|
+
background_color: u32,
|
|
117
|
+
) -> UpscaleResult {
|
|
118
|
+
let config = hex::HexConfig {
|
|
119
|
+
orientation: if orientation == 0 {
|
|
120
|
+
hex::HexOrientation::FlatTop
|
|
121
|
+
} else {
|
|
122
|
+
hex::HexOrientation::PointyTop
|
|
123
|
+
},
|
|
124
|
+
draw_borders,
|
|
125
|
+
border_color,
|
|
126
|
+
border_thickness: border_thickness as usize,
|
|
127
|
+
background_color,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
let (out_width, out_height) = hex::get_output_dimensions(
|
|
131
|
+
width as usize,
|
|
132
|
+
height as usize,
|
|
133
|
+
scale as usize,
|
|
134
|
+
&config.orientation
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
let output = hex::hex_upscale(data, width as usize, height as usize, scale as usize, &config);
|
|
138
|
+
|
|
139
|
+
unsafe {
|
|
140
|
+
HEX_BUFFER = output;
|
|
141
|
+
UpscaleResult {
|
|
142
|
+
ptr: HEX_BUFFER.as_ptr() as u32,
|
|
143
|
+
len: HEX_BUFFER.len() as u32,
|
|
144
|
+
width: out_width as u32,
|
|
145
|
+
height: out_height as u32,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// Get HEX output dimensions
|
|
151
|
+
#[wasm_bindgen]
|
|
152
|
+
pub fn hex_get_dimensions(width: u32, height: u32, scale: u32, orientation: u32) -> Vec<u32> {
|
|
153
|
+
let orient = if orientation == 0 {
|
|
154
|
+
hex::HexOrientation::FlatTop
|
|
155
|
+
} else {
|
|
156
|
+
hex::HexOrientation::PointyTop
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
let (out_w, out_h) = hex::get_output_dimensions(
|
|
160
|
+
width as usize,
|
|
161
|
+
height as usize,
|
|
162
|
+
scale as usize,
|
|
163
|
+
&orient
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
vec![out_w as u32, out_h as u32]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ============================================================================
|
|
170
|
+
// XBRZ Functions
|
|
171
|
+
// ============================================================================
|
|
172
|
+
|
|
173
|
+
/// XBRZ upscale with default config
|
|
174
|
+
#[wasm_bindgen]
|
|
175
|
+
pub fn xbrz_upscale(data: &[u8], width: u32, height: u32, scale: u32) -> UpscaleResult {
|
|
176
|
+
xbrz_upscale_config(
|
|
177
|
+
data, width, height, scale,
|
|
178
|
+
1.0, // luminance_weight
|
|
179
|
+
30, // equal_color_tolerance
|
|
180
|
+
4.4, // dominant_direction_threshold
|
|
181
|
+
2.2 // steep_direction_threshold
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// XBRZ upscale with full config
|
|
186
|
+
#[wasm_bindgen]
|
|
187
|
+
pub fn xbrz_upscale_config(
|
|
188
|
+
data: &[u8],
|
|
189
|
+
width: u32,
|
|
190
|
+
height: u32,
|
|
191
|
+
scale: u32,
|
|
192
|
+
luminance_weight: f32,
|
|
193
|
+
equal_color_tolerance: u32,
|
|
194
|
+
dominant_direction_threshold: f32,
|
|
195
|
+
steep_direction_threshold: f32,
|
|
196
|
+
) -> UpscaleResult {
|
|
197
|
+
let config = xbrz::XbrzConfig {
|
|
198
|
+
luminance_weight,
|
|
199
|
+
equal_color_tolerance,
|
|
200
|
+
dominant_direction_threshold,
|
|
201
|
+
steep_direction_threshold,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
let clamped_scale = scale.clamp(2, 6) as usize;
|
|
205
|
+
let output = xbrz::xbrz_upscale(data, width as usize, height as usize, clamped_scale, &config);
|
|
206
|
+
|
|
207
|
+
let out_width = width * clamped_scale as u32;
|
|
208
|
+
let out_height = height * clamped_scale as u32;
|
|
209
|
+
|
|
210
|
+
unsafe {
|
|
211
|
+
XBRZ_BUFFER = output;
|
|
212
|
+
UpscaleResult {
|
|
213
|
+
ptr: XBRZ_BUFFER.as_ptr() as u32,
|
|
214
|
+
len: XBRZ_BUFFER.len() as u32,
|
|
215
|
+
width: out_width,
|
|
216
|
+
height: out_height,
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// Tests
|
|
223
|
+
// ============================================================================
|
|
224
|
+
|
|
225
|
+
#[cfg(test)]
|
|
226
|
+
mod tests {
|
|
227
|
+
use super::*;
|
|
228
|
+
|
|
229
|
+
fn create_test_image(w: usize, h: usize) -> Vec<u8> {
|
|
230
|
+
let mut data = vec![0u8; w * h * 4];
|
|
231
|
+
for y in 0..h {
|
|
232
|
+
for x in 0..w {
|
|
233
|
+
let i = (y * w + x) * 4;
|
|
234
|
+
data[i] = (x * 255 / w) as u8; // R
|
|
235
|
+
data[i + 1] = (y * 255 / h) as u8; // G
|
|
236
|
+
data[i + 2] = 128; // B
|
|
237
|
+
data[i + 3] = 255; // A
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
data
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#[test]
|
|
244
|
+
fn test_crt_basic() {
|
|
245
|
+
let img = create_test_image(4, 4);
|
|
246
|
+
let result = crt_upscale(&img, 4, 4, 2);
|
|
247
|
+
assert_eq!(result.width, 8);
|
|
248
|
+
assert_eq!(result.height, 8);
|
|
249
|
+
assert_eq!(result.len, 8 * 8 * 4);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
#[test]
|
|
253
|
+
fn test_hex_dimensions() {
|
|
254
|
+
let dims = hex_get_dimensions(4, 4, 16, 0);
|
|
255
|
+
assert!(dims[0] > 0);
|
|
256
|
+
assert!(dims[1] > 0);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
#[test]
|
|
260
|
+
fn test_hex_render() {
|
|
261
|
+
let img = create_test_image(4, 4);
|
|
262
|
+
let result = hex_upscale(&img, 4, 4, 8);
|
|
263
|
+
assert!(result.width > 0);
|
|
264
|
+
assert!(result.height > 0);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
#[test]
|
|
268
|
+
fn test_xbrz_basic() {
|
|
269
|
+
let img = create_test_image(4, 4);
|
|
270
|
+
let result = xbrz_upscale(&img, 4, 4, 2);
|
|
271
|
+
assert_eq!(result.width, 8);
|
|
272
|
+
assert_eq!(result.height, 8);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
#[test]
|
|
276
|
+
fn test_xbrz_scale_factors() {
|
|
277
|
+
let img = create_test_image(4, 4);
|
|
278
|
+
|
|
279
|
+
for scale in 2..=6 {
|
|
280
|
+
let result = xbrz_upscale(&img, 4, 4, scale);
|
|
281
|
+
assert_eq!(result.width, 4 * scale);
|
|
282
|
+
assert_eq!(result.height, 4 * scale);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
package/src/wasm/xbrz.rs
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
//! xBRZ Pixel Art Scaling Engine
|
|
2
|
+
|
|
3
|
+
/// XBRZ configuration
|
|
4
|
+
#[derive(Clone, Copy)]
|
|
5
|
+
pub struct XbrzConfig {
|
|
6
|
+
pub luminance_weight: f32,
|
|
7
|
+
pub equal_color_tolerance: u32,
|
|
8
|
+
pub dominant_direction_threshold: f32,
|
|
9
|
+
pub steep_direction_threshold: f32,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
impl Default for XbrzConfig {
|
|
13
|
+
fn default() -> Self {
|
|
14
|
+
Self {
|
|
15
|
+
luminance_weight: 1.0,
|
|
16
|
+
equal_color_tolerance: 30,
|
|
17
|
+
dominant_direction_threshold: 4.4,
|
|
18
|
+
steep_direction_threshold: 2.2,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[derive(Clone, Copy, Default)]
|
|
24
|
+
struct Color {
|
|
25
|
+
r: u8,
|
|
26
|
+
g: u8,
|
|
27
|
+
b: u8,
|
|
28
|
+
a: u8,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
impl Color {
|
|
32
|
+
#[inline(always)]
|
|
33
|
+
fn from_rgba(data: &[u8], idx: usize) -> Self {
|
|
34
|
+
Self {
|
|
35
|
+
r: data[idx],
|
|
36
|
+
g: data[idx + 1],
|
|
37
|
+
b: data[idx + 2],
|
|
38
|
+
a: data[idx + 3],
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#[inline(always)]
|
|
43
|
+
fn write_to(&self, data: &mut [u8], idx: usize) {
|
|
44
|
+
data[idx] = self.r;
|
|
45
|
+
data[idx + 1] = self.g;
|
|
46
|
+
data[idx + 2] = self.b;
|
|
47
|
+
data[idx + 3] = self.a;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[inline(always)]
|
|
51
|
+
fn luminance(&self) -> u32 {
|
|
52
|
+
(self.r as u32 * 299 + self.g as u32 * 587 + self.b as u32 * 114) / 1000
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#[inline(always)]
|
|
56
|
+
fn blend(c1: Color, c2: Color, ratio: u32) -> Color {
|
|
57
|
+
let inv_ratio = 256 - ratio;
|
|
58
|
+
Color {
|
|
59
|
+
r: ((c1.r as u32 * inv_ratio + c2.r as u32 * ratio) >> 8) as u8,
|
|
60
|
+
g: ((c1.g as u32 * inv_ratio + c2.g as u32 * ratio) >> 8) as u8,
|
|
61
|
+
b: ((c1.b as u32 * inv_ratio + c2.b as u32 * ratio) >> 8) as u8,
|
|
62
|
+
a: ((c1.a as u32 * inv_ratio + c2.a as u32 * ratio) >> 8) as u8,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#[inline(always)]
|
|
68
|
+
fn color_distance(c1: Color, c2: Color, lum_weight: f32) -> f32 {
|
|
69
|
+
let dr = (c1.r as i32 - c2.r as i32).abs() as f32;
|
|
70
|
+
let dg = (c1.g as i32 - c2.g as i32).abs() as f32;
|
|
71
|
+
let db = (c1.b as i32 - c2.b as i32).abs() as f32;
|
|
72
|
+
let da = (c1.a as i32 - c2.a as i32).abs() as f32;
|
|
73
|
+
|
|
74
|
+
let lum1 = c1.luminance() as f32;
|
|
75
|
+
let lum2 = c2.luminance() as f32;
|
|
76
|
+
let dl = (lum1 - lum2).abs();
|
|
77
|
+
|
|
78
|
+
(dr * 0.299 + dg * 0.587 + db * 0.114) * (1.0 - lum_weight) + dl * lum_weight + da * 0.5
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#[inline(always)]
|
|
82
|
+
fn colors_equal(c1: Color, c2: Color, tolerance: u32) -> bool {
|
|
83
|
+
let dr = (c1.r as i32 - c2.r as i32).abs() as u32;
|
|
84
|
+
let dg = (c1.g as i32 - c2.g as i32).abs() as u32;
|
|
85
|
+
let db = (c1.b as i32 - c2.b as i32).abs() as u32;
|
|
86
|
+
let da = (c1.a as i32 - c2.a as i32).abs() as u32;
|
|
87
|
+
|
|
88
|
+
dr + dg + db + da <= tolerance * 4
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// Core xBRZ rendering function
|
|
92
|
+
pub fn xbrz_upscale(
|
|
93
|
+
input: &[u8],
|
|
94
|
+
src_w: usize,
|
|
95
|
+
src_h: usize,
|
|
96
|
+
scale: usize,
|
|
97
|
+
config: &XbrzConfig,
|
|
98
|
+
) -> Vec<u8> {
|
|
99
|
+
let scale = scale.clamp(2, 6);
|
|
100
|
+
let out_w = src_w * scale;
|
|
101
|
+
let out_h = src_h * scale;
|
|
102
|
+
let mut output = vec![0u8; out_w * out_h * 4];
|
|
103
|
+
|
|
104
|
+
for sy in 0..src_h {
|
|
105
|
+
for sx in 0..src_w {
|
|
106
|
+
let kernel = get_kernel(input, src_w, src_h, sx as i32, sy as i32);
|
|
107
|
+
let scaled = scale_pixel(&kernel, scale, config);
|
|
108
|
+
|
|
109
|
+
for by in 0..scale {
|
|
110
|
+
for bx in 0..scale {
|
|
111
|
+
let ox = sx * scale + bx;
|
|
112
|
+
let oy = sy * scale + by;
|
|
113
|
+
let out_idx = (oy * out_w + ox) * 4;
|
|
114
|
+
scaled[by * scale + bx].write_to(&mut output, out_idx);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
output
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
fn get_kernel(input: &[u8], w: usize, h: usize, x: i32, y: i32) -> [Color; 9] {
|
|
124
|
+
let mut kernel = [Color::default(); 9];
|
|
125
|
+
|
|
126
|
+
for dy in -1..=1i32 {
|
|
127
|
+
for dx in -1..=1i32 {
|
|
128
|
+
let sx = (x + dx).clamp(0, w as i32 - 1) as usize;
|
|
129
|
+
let sy = (y + dy).clamp(0, h as i32 - 1) as usize;
|
|
130
|
+
let idx = (sy * w + sx) * 4;
|
|
131
|
+
let ki = ((dy + 1) * 3 + (dx + 1)) as usize;
|
|
132
|
+
kernel[ki] = Color::from_rgba(input, idx);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
kernel
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
fn scale_pixel(kernel: &[Color; 9], scale: usize, config: &XbrzConfig) -> Vec<Color> {
|
|
140
|
+
let center = kernel[4];
|
|
141
|
+
let mut result = vec![center; scale * scale];
|
|
142
|
+
|
|
143
|
+
process_corner(
|
|
144
|
+
&mut result, scale, 0, 0,
|
|
145
|
+
kernel[0], kernel[1], kernel[3], center,
|
|
146
|
+
config,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
process_corner(
|
|
150
|
+
&mut result, scale, scale as i32 - 1, 0,
|
|
151
|
+
kernel[2], kernel[1], kernel[5], center,
|
|
152
|
+
config,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
process_corner(
|
|
156
|
+
&mut result, scale, 0, scale as i32 - 1,
|
|
157
|
+
kernel[6], kernel[7], kernel[3], center,
|
|
158
|
+
config,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
process_corner(
|
|
162
|
+
&mut result, scale, scale as i32 - 1, scale as i32 - 1,
|
|
163
|
+
kernel[8], kernel[7], kernel[5], center,
|
|
164
|
+
config,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
result
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#[allow(clippy::too_many_arguments)]
|
|
171
|
+
fn process_corner(
|
|
172
|
+
result: &mut [Color],
|
|
173
|
+
scale: usize,
|
|
174
|
+
cx: i32, cy: i32,
|
|
175
|
+
corner: Color,
|
|
176
|
+
edge1: Color,
|
|
177
|
+
edge2: Color,
|
|
178
|
+
center: Color,
|
|
179
|
+
config: &XbrzConfig,
|
|
180
|
+
) {
|
|
181
|
+
let lum_w = config.luminance_weight;
|
|
182
|
+
let eq_tol = config.equal_color_tolerance;
|
|
183
|
+
|
|
184
|
+
let e1_eq_c = colors_equal(edge1, center, eq_tol);
|
|
185
|
+
let e2_eq_c = colors_equal(edge2, center, eq_tol);
|
|
186
|
+
let corner_eq_e1 = colors_equal(corner, edge1, eq_tol);
|
|
187
|
+
let corner_eq_e2 = colors_equal(corner, edge2, eq_tol);
|
|
188
|
+
|
|
189
|
+
let dist_e1_e2 = color_distance(edge1, edge2, lum_w);
|
|
190
|
+
let dist_e1_c = color_distance(edge1, center, lum_w);
|
|
191
|
+
let dist_e2_c = color_distance(edge2, center, lum_w);
|
|
192
|
+
|
|
193
|
+
if !e1_eq_c && !e2_eq_c {
|
|
194
|
+
let weight = determine_blend_weight(
|
|
195
|
+
dist_e1_c, dist_e2_c, dist_e1_e2,
|
|
196
|
+
corner_eq_e1, corner_eq_e2,
|
|
197
|
+
config,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if weight > 0.0 {
|
|
201
|
+
let blend_color = if dist_e1_c < dist_e2_c { edge1 } else { edge2 };
|
|
202
|
+
let ratio = (weight * 256.0) as u32;
|
|
203
|
+
|
|
204
|
+
let idx = (cy * scale as i32 + cx) as usize;
|
|
205
|
+
result[idx] = Color::blend(center, blend_color, ratio.min(192));
|
|
206
|
+
|
|
207
|
+
if scale >= 3 {
|
|
208
|
+
blend_adjacent(result, scale, cx, cy, blend_color, weight * 0.5);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
fn determine_blend_weight(
|
|
215
|
+
dist1: f32,
|
|
216
|
+
dist2: f32,
|
|
217
|
+
dist_between: f32,
|
|
218
|
+
_eq1: bool,
|
|
219
|
+
_eq2: bool,
|
|
220
|
+
config: &XbrzConfig,
|
|
221
|
+
) -> f32 {
|
|
222
|
+
let ratio = if dist1 < 0.001 || dist2 < 0.001 {
|
|
223
|
+
0.0
|
|
224
|
+
} else if dist1 < dist2 {
|
|
225
|
+
dist2 / dist1
|
|
226
|
+
} else {
|
|
227
|
+
dist1 / dist2
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
if ratio > config.dominant_direction_threshold {
|
|
231
|
+
0.75
|
|
232
|
+
} else if ratio > config.steep_direction_threshold {
|
|
233
|
+
0.5
|
|
234
|
+
} else if dist_between > (dist1 + dist2) * 0.5 {
|
|
235
|
+
0.25
|
|
236
|
+
} else {
|
|
237
|
+
0.0
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
fn blend_adjacent(
|
|
242
|
+
result: &mut [Color],
|
|
243
|
+
scale: usize,
|
|
244
|
+
cx: i32, cy: i32,
|
|
245
|
+
blend: Color,
|
|
246
|
+
weight: f32,
|
|
247
|
+
) {
|
|
248
|
+
let s = scale as i32;
|
|
249
|
+
let ratio = (weight * 128.0) as u32;
|
|
250
|
+
|
|
251
|
+
let hx = if cx == 0 { 1 } else { cx - 1 };
|
|
252
|
+
if hx >= 0 && hx < s {
|
|
253
|
+
let idx = (cy * s + hx) as usize;
|
|
254
|
+
result[idx] = Color::blend(result[idx], blend, ratio);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
let vy = if cy == 0 { 1 } else { cy - 1 };
|
|
258
|
+
if vy >= 0 && vy < s {
|
|
259
|
+
let idx = (vy * s + cx) as usize;
|
|
260
|
+
result[idx] = Color::blend(result[idx], blend, ratio);
|
|
261
|
+
}
|
|
262
|
+
}
|