1
1
use wasm_bindgen:: prelude:: * ;
2
- use visioncortex:: PathSimplifyMode ;
3
- use visioncortex:: color_clusters:: { IncrementalBuilder , Clusters , Runner , RunnerConfig , HIERARCHICAL_MAX } ;
2
+ use visioncortex:: { Color , ColorImage , PathSimplifyMode } ;
3
+ use visioncortex:: color_clusters:: { Clusters , Runner , RunnerConfig , HIERARCHICAL_MAX , IncrementalBuilder , KeyingAction } ;
4
4
5
5
use crate :: canvas:: * ;
6
6
use crate :: svg:: * ;
7
7
8
8
use serde:: Deserialize ;
9
9
use super :: util;
10
10
11
+ const KEYING_THRESHOLD : f32 = 0.2 ;
12
+
11
13
#[ derive( Debug , Deserialize ) ]
12
14
pub struct ColorImageConverterParams {
13
15
pub canvas_id : String ,
@@ -67,7 +69,26 @@ impl ColorImageConverter {
67
69
pub fn init ( & mut self ) {
68
70
let width = self . canvas . width ( ) as u32 ;
69
71
let height = self . canvas . height ( ) as u32 ;
70
- let image = self . canvas . get_image_data_as_color_image ( 0 , 0 , width, height) ;
72
+ let mut image = self . canvas . get_image_data_as_color_image ( 0 , 0 , width, height) ;
73
+
74
+ let key_color = if Self :: should_key_image ( & image) {
75
+ if let Ok ( key_color) = Self :: find_unused_color_in_image ( & image) {
76
+ for y in 0 ..height as usize {
77
+ for x in 0 ..width as usize {
78
+ if image. get_pixel ( x, y) . a == 0 {
79
+ image. set_pixel ( x, y, & key_color) ;
80
+ }
81
+ }
82
+ }
83
+ key_color
84
+ } else {
85
+ Color :: default ( )
86
+ }
87
+ } else {
88
+ // The default color is all zeroes, which is treated by visioncortex as a special value meaning no keying will be applied.
89
+ Color :: default ( )
90
+ } ;
91
+
71
92
let runner = Runner :: new ( RunnerConfig {
72
93
diagonal : self . params . layer_difference == 0 ,
73
94
hierarchical : HIERARCHICAL_MAX ,
@@ -78,6 +99,12 @@ impl ColorImageConverter {
78
99
is_same_color_b : 1 ,
79
100
deepen_diff : self . params . layer_difference ,
80
101
hollow_neighbours : 1 ,
102
+ key_color,
103
+ keying_action : if self . params . hierarchical == "cutout" {
104
+ KeyingAction :: Keep
105
+ } else {
106
+ KeyingAction :: Discard
107
+ } ,
81
108
} , image) ;
82
109
self . stage = Stage :: Clustering ( runner. start ( ) ) ;
83
110
}
@@ -108,6 +135,8 @@ impl ColorImageConverter {
108
135
is_same_color_b : 1 ,
109
136
deepen_diff : 0 ,
110
137
hollow_neighbours : 0 ,
138
+ key_color : Default :: default ( ) ,
139
+ keying_action : KeyingAction :: Discard ,
111
140
} , image) ;
112
141
self . stage = Stage :: Reclustering ( runner. start ( ) ) ;
113
142
} ,
@@ -167,4 +196,56 @@ impl ColorImageConverter {
167
196
} ) as i32
168
197
}
169
198
199
+ fn color_exists_in_image ( img : & ColorImage , color : Color ) -> bool {
200
+ for y in 0 ..img. height {
201
+ for x in 0 ..img. width {
202
+ let pixel_color = img. get_pixel ( x, y) ;
203
+ if pixel_color. r == color. r && pixel_color. g == color. g && pixel_color. b == color. b {
204
+ return true
205
+ }
206
+ }
207
+ }
208
+ false
209
+ }
210
+
211
+ fn find_unused_color_in_image ( img : & ColorImage ) -> Result < Color , String > {
212
+ let special_colors = IntoIterator :: into_iter ( [
213
+ Color :: new ( 255 , 0 , 0 ) ,
214
+ Color :: new ( 0 , 255 , 0 ) ,
215
+ Color :: new ( 0 , 0 , 255 ) ,
216
+ Color :: new ( 255 , 255 , 0 ) ,
217
+ Color :: new ( 0 , 255 , 255 ) ,
218
+ Color :: new ( 255 , 0 , 255 ) ,
219
+ Color :: new ( 128 , 128 , 128 ) ,
220
+ ] ) ;
221
+ for color in special_colors {
222
+ if !Self :: color_exists_in_image ( img, color) {
223
+ return Ok ( color) ;
224
+ }
225
+ }
226
+ Err ( String :: from ( "unable to find unused color in image to use as key" ) )
227
+ }
228
+
229
+ fn should_key_image ( img : & ColorImage ) -> bool {
230
+ if img. width == 0 || img. height == 0 {
231
+ return false ;
232
+ }
233
+
234
+ // Check for transparency at several scanlines
235
+ let threshold = ( ( img. width * 2 ) as f32 * KEYING_THRESHOLD ) as usize ;
236
+ let mut num_transparent_boundary_pixels = 0 ;
237
+ let y_positions = [ 0 , img. height / 4 , img. height / 2 , 3 * img. height / 4 , img. height - 1 ] ;
238
+ for y in y_positions {
239
+ for x in 0 ..img. width {
240
+ if img. get_pixel ( x, y) . a == 0 {
241
+ num_transparent_boundary_pixels += 1 ;
242
+ }
243
+ if num_transparent_boundary_pixels >= threshold {
244
+ return true ;
245
+ }
246
+ }
247
+ }
248
+
249
+ false
250
+ }
170
251
}
0 commit comments