1use crate::{
7 error::{Result, VCError},
8 share::Share,
9 utils::{convert_to_binary, resize_to_match},
10};
11use image::{DynamicImage, GenericImageView, ImageBuffer, Luma, Rgb};
12use qrcode::{EcLevel, QrCode};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum QrErrorCorrection {
17 Low, Medium, Quartile, High, }
22
23impl From<QrErrorCorrection> for EcLevel {
24 fn from(level: QrErrorCorrection) -> Self {
25 match level {
26 QrErrorCorrection::Low => EcLevel::L,
27 QrErrorCorrection::Medium => EcLevel::M,
28 QrErrorCorrection::Quartile => EcLevel::Q,
29 QrErrorCorrection::High => EcLevel::H,
30 }
31 }
32}
33
34#[derive(Debug, Clone)]
36pub struct QrVcConfig {
37 pub error_correction: QrErrorCorrection,
39 pub module_size: u32,
41 pub border: u32,
43 pub use_error_correction_embedding: bool,
45 pub data_capacity_ratio: f32,
47}
48
49impl Default for QrVcConfig {
50 fn default() -> Self {
51 Self {
52 error_correction: QrErrorCorrection::Medium,
53 module_size: 4,
54 border: 4,
55 use_error_correction_embedding: true,
56 data_capacity_ratio: 0.8,
57 }
58 }
59}
60
61#[derive(Debug, Clone)]
63pub struct QrShare {
64 pub share: Share,
66 pub qr_data: String,
68 pub qr_image: DynamicImage,
70 pub metadata: QrEmbeddingMetadata,
72}
73
74#[derive(Debug, Clone)]
76pub struct QrEmbeddingMetadata {
77 pub original_qr_size: (u32, u32),
79 pub share_size: (u32, u32),
81 pub embedded_bits: usize,
83 pub qr_version: String,
85 pub capacity_used: f32,
87}
88
89pub struct QrVisualCryptography {
91 config: QrVcConfig,
92}
93
94impl QrVisualCryptography {
95 pub fn new(config: QrVcConfig) -> Self {
97 Self { config }
98 }
99
100 pub fn generate_qr_shares(&self, shares: &[Share], qr_data: &[String]) -> Result<Vec<QrShare>> {
102 if shares.len() != qr_data.len() {
103 return Err(VCError::InvalidConfiguration(
104 "Number of shares must match number of QR data strings".to_string(),
105 ));
106 }
107
108 let mut qr_shares = Vec::new();
109
110 for (share, data) in shares.iter().zip(qr_data.iter()) {
111 let qr_share = self.embed_share_in_qr(share, data)?;
112 qr_shares.push(qr_share);
113 }
114
115 Ok(qr_shares)
116 }
117
118 fn embed_share_in_qr(&self, share: &Share, qr_data: &str) -> Result<QrShare> {
120 let qr_code =
122 QrCode::with_error_correction_level(qr_data, self.config.error_correction.into())
123 .map_err(|e| VCError::QrCodeError(format!("Failed to generate QR code: {}", e)))?;
124
125 let qr_image = self.qr_to_image(&qr_code);
127 let (qr_width, qr_height) = qr_image.dimensions();
128
129 let resized_share = resize_to_match(&share.image, qr_width, qr_height);
131 let share_binary = convert_to_binary(&resized_share);
132
133 let embedded_image = if self.config.use_error_correction_embedding {
135 self.embed_using_error_correction(&qr_image, &share_binary, &qr_code)?
136 } else {
137 self.embed_using_module_replacement(&qr_image, &share_binary)?
138 };
139
140 let metadata = QrEmbeddingMetadata {
141 original_qr_size: (qr_width, qr_height),
142 share_size: share.dimensions(),
143 embedded_bits: self.count_embedded_bits(&share_binary),
144 qr_version: format!("{:?}", qr_code.version()),
145 capacity_used: self.calculate_capacity_usage(&qr_code, &share_binary),
146 };
147
148 Ok(QrShare {
149 share: share.clone(),
150 qr_data: qr_data.to_string(),
151 qr_image: embedded_image,
152 metadata,
153 })
154 }
155
156 fn embed_using_error_correction(
158 &self,
159 qr_image: &DynamicImage,
160 share_binary: &ImageBuffer<Luma<u8>, Vec<u8>>,
161 qr_code: &QrCode,
162 ) -> Result<DynamicImage> {
163 let qr_gray = qr_image.to_luma8();
164 let (width, height) = qr_gray.dimensions();
165 let mut result = qr_gray.clone();
166
167 let modules = qr_code.to_vec();
169 let module_count = qr_code.width();
170 let scale_x = width as f32 / module_count as f32;
171 let scale_y = height as f32 / module_count as f32;
172
173 let error_correction_modules =
175 self.identify_error_correction_modules(&modules, module_count);
176
177 for y in 0..height {
179 for x in 0..width {
180 let module_x = (x as f32 / scale_x) as usize;
181 let module_y = (y as f32 / scale_y) as usize;
182
183 if module_x < module_count && module_y < module_count {
184 let module_idx = module_y * module_count + module_x;
185
186 if error_correction_modules.contains(&module_idx) {
187 let share_pixel = share_binary
189 .get_pixel(x % share_binary.width(), y % share_binary.height())[0];
190
191 let qr_pixel = qr_gray.get_pixel(x, y)[0];
193 let blended = self.blend_pixels(qr_pixel, share_pixel);
194 result.put_pixel(x, y, Luma([blended]));
195 }
196 }
197 }
198 }
199
200 Ok(DynamicImage::ImageLuma8(result))
201 }
202
203 fn embed_using_module_replacement(
205 &self,
206 qr_image: &DynamicImage,
207 share_binary: &ImageBuffer<Luma<u8>, Vec<u8>>,
208 ) -> Result<DynamicImage> {
209 let qr_gray = qr_image.to_luma8();
210 let (width, height) = qr_gray.dimensions();
211 let mut result = qr_gray.clone();
212
213 for y in 0..height {
215 for x in 0..width {
216 let qr_pixel = qr_gray.get_pixel(x, y)[0];
217 let share_pixel =
218 share_binary.get_pixel(x % share_binary.width(), y % share_binary.height())[0];
219
220 let alpha = self.config.data_capacity_ratio;
222 let blended = ((1.0 - alpha) * qr_pixel as f32 + alpha * share_pixel as f32) as u8;
223 result.put_pixel(x, y, Luma([blended]));
224 }
225 }
226
227 Ok(DynamicImage::ImageLuma8(result))
228 }
229
230 pub fn extract_shares_from_qr(&self, qr_shares: &[QrShare]) -> Result<Vec<Share>> {
232 let mut extracted_shares = Vec::new();
233
234 for qr_share in qr_shares {
235 let share = self.extract_share_from_qr_image(&qr_share.qr_image, &qr_share.metadata)?;
236 extracted_shares.push(share);
237 }
238
239 Ok(extracted_shares)
240 }
241
242 fn extract_share_from_qr_image(
244 &self,
245 qr_image: &DynamicImage,
246 metadata: &QrEmbeddingMetadata,
247 ) -> Result<Share> {
248 let qr_gray = qr_image.to_luma8();
249 let (qr_width, qr_height) = qr_gray.dimensions();
250
251 let mut extracted_data = ImageBuffer::new(metadata.share_size.0, metadata.share_size.1);
253
254 for y in 0..metadata.share_size.1 {
255 for x in 0..metadata.share_size.0 {
256 let qr_x = (x * qr_width) / metadata.share_size.0;
258 let qr_y = (y * qr_height) / metadata.share_size.1;
259
260 let pixel = qr_gray.get_pixel(qr_x.min(qr_width - 1), qr_y.min(qr_height - 1))[0];
261
262 let extracted_pixel = self.extract_pixel(pixel);
264 extracted_data.put_pixel(x, y, Luma([extracted_pixel]));
265 }
266 }
267
268 Ok(Share::new(
269 DynamicImage::ImageLuma8(extracted_data),
270 1, 1, metadata.share_size.0,
273 metadata.share_size.1,
274 1,
275 true, ))
277 }
278
279 pub fn decode_qr_data(&self, qr_share: &QrShare) -> Result<String> {
281 Ok(qr_share.qr_data.clone())
284 }
285
286 fn qr_to_image(&self, qr_code: &QrCode) -> DynamicImage {
288 let modules = qr_code.to_vec();
289 let module_count = qr_code.width();
290
291 let image_size = (module_count as u32 + 2 * self.config.border) * self.config.module_size;
292 let mut img = ImageBuffer::new(image_size, image_size);
293
294 for pixel in img.pixels_mut() {
296 *pixel = Luma([255u8]);
297 }
298
299 for y in 0..module_count {
301 for x in 0..module_count {
302 if modules[y * module_count + x] {
303 let start_x = (x as u32 + self.config.border) * self.config.module_size;
305 let start_y = (y as u32 + self.config.border) * self.config.module_size;
306
307 for dy in 0..self.config.module_size {
308 for dx in 0..self.config.module_size {
309 img.put_pixel(start_x + dx, start_y + dy, Luma([0u8]));
310 }
311 }
312 }
313 }
314 }
315
316 DynamicImage::ImageLuma8(img)
317 }
318
319 fn identify_error_correction_modules(
321 &self,
322 modules: &[bool],
323 module_count: usize,
324 ) -> Vec<usize> {
325 let mut error_correction_modules = Vec::new();
326
327 for y in 0..module_count {
334 for x in 0..module_count {
335 if !self.is_function_module(x, y, module_count) {
336 error_correction_modules.push(y * module_count + x);
337 }
338 }
339 }
340
341 error_correction_modules
342 }
343
344 fn is_function_module(&self, x: usize, y: usize, size: usize) -> bool {
346 if (x < 9 && y < 9) || (x >= size - 8 && y < 9) || (x < 9 && y >= size - 8) {
348 return true;
349 }
350
351 if (x == 6 && y >= 8 && y < size - 8) || (y == 6 && x >= 8 && x < size - 8) {
353 return true;
354 }
355
356 false
360 }
361
362 fn blend_pixels(&self, qr_pixel: u8, share_pixel: u8) -> u8 {
364 let alpha = self.config.data_capacity_ratio;
366
367 if self.config.use_error_correction_embedding {
368 if qr_pixel == 0 && share_pixel == 0 {
370 0 } else if qr_pixel == 255 && share_pixel == 255 {
372 255 } else {
374 ((1.0 - alpha) * qr_pixel as f32 + alpha * share_pixel as f32) as u8
376 }
377 } else {
378 ((1.0 - alpha) * qr_pixel as f32 + alpha * share_pixel as f32) as u8
380 }
381 }
382
383 fn extract_pixel(&self, blended_pixel: u8) -> u8 {
385 if blended_pixel > 127 {
388 255
389 } else {
390 0
391 }
392 }
393
394 fn count_embedded_bits(&self, share_binary: &ImageBuffer<Luma<u8>, Vec<u8>>) -> usize {
396 share_binary.pixels().filter(|pixel| pixel[0] == 0).count()
397 }
398
399 fn calculate_capacity_usage(
401 &self,
402 qr_code: &QrCode,
403 share_binary: &ImageBuffer<Luma<u8>, Vec<u8>>,
404 ) -> f32 {
405 let total_modules = (qr_code.width() * qr_code.width()) as f32;
406 let embedded_bits = self.count_embedded_bits(share_binary) as f32;
407 embedded_bits / total_modules
408 }
409}
410
411pub fn create_test_qr_share(data: &str, share_data: Vec<u8>) -> Result<QrShare> {
413 let config = QrVcConfig::default();
414 let qr_processor = QrVisualCryptography::new(config);
415
416 let width = 100;
418 let height = 100;
419 let mut share_img = ImageBuffer::new(width, height);
420
421 for (i, pixel) in share_img.pixels_mut().enumerate() {
422 let value = if i < share_data.len() && share_data[i] == 1 {
423 0
424 } else {
425 255
426 };
427 *pixel = Luma([value]);
428 }
429
430 let share = Share::new(
431 DynamicImage::ImageLuma8(share_img),
432 1,
433 1,
434 width,
435 height,
436 1,
437 true,
438 );
439
440 qr_processor.embed_share_in_qr(&share, data)
441}
442
443#[cfg(test)]
444mod tests {
445 use super::*;
446
447 #[test]
448 fn test_qr_vc_config_default() {
449 let config = QrVcConfig::default();
450 assert_eq!(config.error_correction, QrErrorCorrection::Medium);
451 assert_eq!(config.module_size, 4);
452 }
453
454 #[test]
455 fn test_create_test_qr_share() {
456 let share_data = vec![1, 0, 1, 0, 1];
457 let qr_share = create_test_qr_share("Hello World", share_data);
458 assert!(qr_share.is_ok());
459
460 let qr_share = qr_share.unwrap();
461 assert_eq!(qr_share.qr_data, "Hello World");
462 }
463
464 #[test]
465 fn test_qr_visual_cryptography_creation() {
466 let config = QrVcConfig::default();
467 let qr_vc = QrVisualCryptography::new(config);
468
469 assert_eq!(qr_vc.config.error_correction, QrErrorCorrection::Medium);
471 }
472}