gtest.rs (32936B)
1 #[cfg(all(test, feature = "c_bindings"))] 2 #[allow(clippy::all)] 3 mod gtest { 4 use crate::{ 5 c_bindings::*, iccread::*, transform::DataType::*, transform::*, 6 transform_util::lut_inverse_interp16, Intent::Perceptual, 7 }; 8 use libc::c_void; 9 #[cfg(target_arch = "arm")] 10 use std::arch::is_arm_feature_detected; 11 #[cfg(target_arch = "aarch64")] 12 use std::arch::is_aarch64_feature_detected; 13 use std::ptr::null_mut; 14 15 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] 16 use crate::transform_neon::{ 17 qcms_transform_data_bgra_out_lut_neon, qcms_transform_data_rgb_out_lut_neon, 18 qcms_transform_data_rgba_out_lut_neon, 19 }; 20 21 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 22 use crate::{ 23 transform_avx::{ 24 qcms_transform_data_bgra_out_lut_avx, qcms_transform_data_rgb_out_lut_avx, 25 qcms_transform_data_rgba_out_lut_avx, 26 }, 27 transform_sse2::{ 28 qcms_transform_data_bgra_out_lut_sse2, qcms_transform_data_rgb_out_lut_sse2, 29 qcms_transform_data_rgba_out_lut_sse2, 30 }, 31 }; 32 33 #[test] 34 fn test_lut_inverse_crash() { 35 let lutTable1: [u16; 128] = [ 36 0x0000, 0x0000, 0x0000, 0x8000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 37 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 38 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 39 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 40 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 41 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 42 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 43 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 44 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 45 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 46 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 47 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 48 ]; 49 let lutTable2: [u16; 128] = [ 50 0xFFF0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 51 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 52 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 53 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 54 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 55 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 56 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 57 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 58 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 59 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 60 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 61 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 62 ]; 63 64 // Crash/Assert test 65 66 lut_inverse_interp16(5, &lutTable1); 67 lut_inverse_interp16(5, &lutTable2); 68 } 69 70 #[test] 71 fn test_lut_inverse() { 72 // mimic sRGB_v4_ICC mBA Output 73 // 74 // XXXX 75 // X 76 // X 77 // XXXX 78 let mut value: u16; 79 let mut lutTable: [u16; 256] = [0; 256]; 80 81 for i in 0..20 { 82 lutTable[i] = 0; 83 } 84 85 for i in 20..200 { 86 lutTable[i] = ((i - 20) * 0xFFFF / (200 - 20)) as u16; 87 } 88 89 for i in 200..lutTable.len() { 90 lutTable[i] = 0xFFFF; 91 } 92 93 for i in 0..65535 { 94 lut_inverse_interp16(i, &lutTable); 95 } 96 97 // Lookup the interesting points 98 99 value = lut_inverse_interp16(0, &lutTable); 100 assert!(value <= 20 * 256); 101 102 value = lut_inverse_interp16(1, &lutTable); 103 assert!(value > 20 * 256); 104 105 value = lut_inverse_interp16(65535, &lutTable); 106 assert!(value < 201 * 256); 107 } 108 109 // this test takes to long to run on miri 110 #[cfg(not(miri))] 111 #[test] 112 fn test_lut_inverse_non_monotonic() { 113 // Make sure we behave sanely for non monotic functions 114 // X X X 115 // X X X 116 // X X X 117 let mut lutTable: [u16; 256] = [0; 256]; 118 119 for i in 0..100 { 120 lutTable[i] = ((i - 0) * 0xFFFF / (100 - 0)) as u16; 121 } 122 123 for i in 100..200 { 124 lutTable[i] = ((i - 100) * 0xFFFF / (200 - 100)) as u16; 125 } 126 127 for i in 200..256 { 128 lutTable[i] = ((i - 200) * 0xFFFF / (256 - 200)) as u16; 129 } 130 131 for i in 0..65535 { 132 lut_inverse_interp16(i, &lutTable); 133 } 134 135 // Make sure we don't crash, hang or let sanitizers do their magic 136 } 137 /* qcms_data_create_rgb_with_gamma is broken 138 #[test] 139 fn profile_from_gamma() { 140 141 let white_point = qcms_CIE_xyY { x: 0.64, y: 0.33, Y: 1.}; 142 let primaries = qcms_CIE_xyYTRIPLE { 143 red: qcms_CIE_xyY { x: 0.64, y: 0.33, Y: 1.}, 144 green: qcms_CIE_xyY { x: 0.21, y: 0.71, Y: 1.}, 145 blue: qcms_CIE_xyY { x: 0.15, y: 0.06, Y: 1.} 146 }; 147 let mut mem: *mut libc::c_void = std::ptr::null_mut(); 148 let mut size: size_t = 0; 149 unsafe { qcms_data_create_rgb_with_gamma(white_point, primaries, 2.2, &mut mem, &mut size); } 150 assert!(size != 0) 151 } 152 */ 153 154 #[test] 155 fn alignment() { 156 assert_eq!(std::mem::align_of::<qcms_transform>(), 16); 157 } 158 159 #[test] 160 fn basic() { 161 let sRGB_profile = crate::c_bindings::qcms_profile_sRGB(); 162 163 let Rec709Primaries = qcms_CIE_xyYTRIPLE { 164 red: qcms_CIE_xyY { 165 x: 0.6400, 166 y: 0.3300, 167 Y: 1.0, 168 }, 169 green: qcms_CIE_xyY { 170 x: 0.3000, 171 y: 0.6000, 172 Y: 1.0, 173 }, 174 blue: qcms_CIE_xyY { 175 x: 0.1500, 176 y: 0.0600, 177 Y: 1.0, 178 }, 179 }; 180 let D65 = qcms_white_point_sRGB(); 181 let other = unsafe { qcms_profile_create_rgb_with_gamma(D65, Rec709Primaries, 2.2) }; 182 unsafe { qcms_profile_precache_output_transform(&mut *other) }; 183 184 let transform = unsafe { 185 qcms_transform_create(&mut *sRGB_profile, RGB8, &mut *other, RGB8, Perceptual) 186 }; 187 let mut data: [u8; 120] = [0; 120]; 188 189 unsafe { 190 qcms_transform_data( 191 &*transform, 192 data.as_ptr() as *const libc::c_void, 193 data.as_mut_ptr() as *mut libc::c_void, 194 data.len() / 3, 195 ) 196 }; 197 198 unsafe { 199 qcms_transform_release(transform); 200 qcms_profile_release(sRGB_profile); 201 qcms_profile_release(other); 202 } 203 } 204 205 #[test] 206 fn gray_alpha() { 207 let sRGB_profile = qcms_profile_sRGB(); 208 let other = unsafe { qcms_profile_create_gray_with_gamma(2.2) }; 209 unsafe { qcms_profile_precache_output_transform(&mut *other) }; 210 211 let transform = unsafe { 212 qcms_transform_create(&mut *other, GrayA8, &mut *sRGB_profile, RGBA8, Perceptual) 213 }; 214 assert!(!transform.is_null()); 215 216 let in_data: [u8; 4] = [0, 255, 255, 0]; 217 let mut out_data: [u8; 2 * 4] = [0; 8]; 218 unsafe { 219 qcms_transform_data( 220 &*transform, 221 in_data.as_ptr() as *const libc::c_void, 222 out_data.as_mut_ptr() as *mut libc::c_void, 223 in_data.len() / 2, 224 ) 225 }; 226 227 assert_eq!(out_data, [0, 0, 0, 255, 255, 255, 255, 0]); 228 unsafe { 229 qcms_transform_release(transform); 230 qcms_profile_release(sRGB_profile); 231 qcms_profile_release(other); 232 } 233 } 234 #[test] 235 fn samples() { 236 use libc::c_void; 237 use std::io::Read; 238 239 let mut d = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); 240 qcms_enable_iccv4(); 241 d.push("fuzz"); 242 d.push("samples"); 243 let samples = [ 244 "0220-ca351238d719fd07ef8607d326b398fe.icc", 245 "0372-973178997787ee780b4b58ee47cad683.icc", 246 "0744-0a5faafe175e682b10c590b03d3f093b.icc", 247 "0316-eb3f97ab646cd7b66bee80bdfe6098ac.icc", 248 "0732-80707d91aea0f8e64ef0286cc7720e99.icc", 249 "1809-2bd4b77651214ca6110fdbee2502671e.icc", 250 ]; 251 for s in samples.iter() { 252 let mut p = d.clone(); 253 p.push(s); 254 let mut file = std::fs::File::open(p.clone()).unwrap(); 255 let mut data = Vec::new(); 256 file.read_to_end(&mut data).unwrap(); 257 let profile = 258 unsafe { qcms_profile_from_memory(data.as_ptr() as *const c_void, data.len()) }; 259 assert_ne!(profile, std::ptr::null_mut()); 260 unsafe { qcms_profile_release(profile) }; 261 } 262 } 263 264 #[test] 265 fn v4() { 266 use libc::c_void; 267 use std::io::Read; 268 269 let mut p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); 270 qcms_enable_iccv4(); 271 p.push("profiles"); 272 // this profile was made by taking the lookup table profile from 273 // http://displaycal.net/icc-color-management-test/ and removing 274 // the unneeed tables using lcms 275 p.push("displaycal-lut-stripped.icc"); 276 277 let mut file = std::fs::File::open(p).unwrap(); 278 let mut data = Vec::new(); 279 file.read_to_end(&mut data).unwrap(); 280 let profile = 281 unsafe { qcms_profile_from_memory(data.as_ptr() as *const c_void, data.len()) }; 282 assert_ne!(profile, std::ptr::null_mut()); 283 284 let srgb_profile = qcms_profile_sRGB(); 285 assert_ne!(srgb_profile, std::ptr::null_mut()); 286 287 unsafe { qcms_profile_precache_output_transform(&mut *srgb_profile) }; 288 289 let intent = unsafe { qcms_profile_get_rendering_intent(&*profile) }; 290 let transform = 291 unsafe { qcms_transform_create(&*profile, RGB8, &*srgb_profile, RGB8, intent) }; 292 293 assert_ne!(transform, std::ptr::null_mut()); 294 295 const SRC_SIZE: usize = 4; 296 let src: [u8; SRC_SIZE * 3] = [ 297 246, 246, 246, // gray 298 255, 0, 0, // red 299 0, 255, 255, // cyan 300 255, 255, 0, // yellow 301 ]; 302 let mut dst: [u8; SRC_SIZE * 3] = [0; SRC_SIZE * 3]; 303 304 // the reference values here should be adjusted if the accuracy 305 // of the transformation changes 306 let reference = [ 307 246, 246, 246, // gray 308 255, 0, 0, // red 309 248, 14, 22, // red 310 0, 0, 255, // blue 311 ]; 312 313 unsafe { 314 qcms_transform_data( 315 &*transform, 316 src.as_ptr() as *const libc::c_void, 317 dst.as_mut_ptr() as *mut libc::c_void, 318 SRC_SIZE, 319 ); 320 } 321 322 assert_eq!(reference, dst); 323 unsafe { qcms_transform_release(transform) } 324 unsafe { qcms_profile_release(profile) } 325 unsafe { qcms_profile_release(srgb_profile) } 326 } 327 328 fn CmpRgbChannel(reference: &[u8], test: &[u8], index: usize) -> bool { 329 (reference[index] as i32 - test[index] as i32).abs() <= 1 330 } 331 332 fn CmpRgbBufferImpl( 333 refBuffer: &[u8], 334 testBuffer: &[u8], 335 pixels: usize, 336 kSwapRB: bool, 337 hasAlpha: bool, 338 ) -> bool { 339 let pixelSize = if hasAlpha { 4 } else { 3 }; 340 if refBuffer[..pixels * pixelSize] == testBuffer[..pixels * pixelSize] { 341 return true; 342 } 343 344 let kRIndex = if kSwapRB { 2 } else { 0 }; 345 let kGIndex = 1; 346 let kBIndex = if kSwapRB { 0 } else { 2 }; 347 let kAIndex = 3; 348 349 let mut remaining = pixels; 350 let mut reference = &refBuffer[..]; 351 let mut test = &testBuffer[..]; 352 while remaining > 0 { 353 if !CmpRgbChannel(reference, test, kRIndex) 354 || !CmpRgbChannel(reference, test, kGIndex) 355 || !CmpRgbChannel(reference, test, kBIndex) 356 || (hasAlpha && reference[kAIndex] != test[kAIndex]) 357 { 358 assert_eq!(test[kRIndex], reference[kRIndex]); 359 assert_eq!(test[kGIndex], reference[kGIndex]); 360 assert_eq!(test[kBIndex], reference[kBIndex]); 361 if hasAlpha { 362 assert_eq!(test[kAIndex], reference[kAIndex]); 363 } 364 return false; 365 } 366 remaining -= 1; 367 reference = &reference[pixelSize..]; 368 test = &test[pixelSize..]; 369 } 370 371 true 372 } 373 374 fn GetRgbInputBufferImpl(kSwapRB: bool, kHasAlpha: bool) -> (usize, Vec<u8>) { 375 let colorSamples = [0, 5, 16, 43, 101, 127, 182, 255]; 376 let colorSampleMax = colorSamples.len(); 377 let pixelSize = if kHasAlpha { 4 } else { 3 }; 378 let pixelCount = colorSampleMax * colorSampleMax * 256 * 3; 379 380 let mut outBuffer = vec![0; pixelCount * pixelSize]; 381 382 let kRIndex = if kSwapRB { 2 } else { 0 }; 383 let kGIndex = 1; 384 let kBIndex = if kSwapRB { 0 } else { 2 }; 385 let kAIndex = 3; 386 387 // Sample every red pixel value with a subset of green and blue. 388 // we use a u16 for r to avoid https://github.com/rust-lang/rust/issues/78283 389 let mut color: &mut [u8] = &mut outBuffer[..]; 390 for r in 0..=255u16 { 391 for &g in colorSamples.iter() { 392 for &b in colorSamples.iter() { 393 color[kRIndex] = r as u8; 394 color[kGIndex] = g; 395 color[kBIndex] = b; 396 if kHasAlpha { 397 color[kAIndex] = 0x80; 398 } 399 color = &mut color[pixelSize..]; 400 } 401 } 402 } 403 404 // Sample every green pixel value with a subset of red and blue. 405 let mut color = &mut outBuffer[..]; 406 for &r in colorSamples.iter() { 407 for g in 0..=255u16 { 408 for &b in colorSamples.iter() { 409 color[kRIndex] = r; 410 color[kGIndex] = g as u8; 411 color[kBIndex] = b; 412 if kHasAlpha { 413 color[kAIndex] = 0x80; 414 } 415 color = &mut color[pixelSize..]; 416 } 417 } 418 } 419 420 // Sample every blue pixel value with a subset of red and green. 421 let mut color = &mut outBuffer[..]; 422 for &r in colorSamples.iter() { 423 for &g in colorSamples.iter() { 424 for b in 0..=255u16 { 425 color[kRIndex] = r; 426 color[kGIndex] = g; 427 color[kBIndex] = b as u8; 428 if kHasAlpha { 429 color[kAIndex] = 0x80; 430 } 431 color = &mut color[pixelSize..]; 432 } 433 } 434 } 435 436 (pixelCount, outBuffer) 437 } 438 439 fn GetRgbInputBuffer() -> (usize, Vec<u8>) { 440 GetRgbInputBufferImpl(false, false) 441 } 442 443 fn GetRgbaInputBuffer() -> (usize, Vec<u8>) { 444 GetRgbInputBufferImpl(false, true) 445 } 446 447 fn GetBgraInputBuffer() -> (usize, Vec<u8>) { 448 GetRgbInputBufferImpl(true, true) 449 } 450 451 fn CmpRgbBuffer(refBuffer: &[u8], testBuffer: &[u8], pixels: usize) -> bool { 452 CmpRgbBufferImpl(refBuffer, testBuffer, pixels, false, false) 453 } 454 455 fn CmpRgbaBuffer(refBuffer: &[u8], testBuffer: &[u8], pixels: usize) -> bool { 456 CmpRgbBufferImpl(refBuffer, testBuffer, pixels, false, true) 457 } 458 459 fn CmpBgraBuffer(refBuffer: &[u8], testBuffer: &[u8], pixels: usize) -> bool { 460 CmpRgbBufferImpl(refBuffer, testBuffer, pixels, true, true) 461 } 462 463 fn ClearRgbBuffer(buffer: &mut [u8], pixels: usize) { 464 for i in 0..pixels * 3 { 465 buffer[i] = 0; 466 } 467 } 468 469 fn ClearRgbaBuffer(buffer: &mut [u8], pixels: usize) { 470 for i in 0..pixels * 4 { 471 buffer[i] = 0; 472 } 473 } 474 475 fn GetRgbOutputBuffer(pixels: usize) -> Vec<u8> { 476 vec![0; pixels * 3] 477 } 478 479 fn GetRgbaOutputBuffer(pixels: usize) -> Vec<u8> { 480 vec![0; pixels * 4] 481 } 482 483 struct QcmsProfileTest { 484 in_profile: *mut Profile, 485 out_profile: *mut Profile, 486 transform: *mut qcms_transform, 487 488 input: Vec<u8>, 489 output: Vec<u8>, 490 reference: Vec<u8>, 491 492 pixels: usize, 493 storage_type: DataType, 494 precache: bool, 495 } 496 497 impl QcmsProfileTest { 498 fn new() -> QcmsProfileTest { 499 QcmsProfileTest { 500 in_profile: null_mut(), 501 out_profile: null_mut(), 502 transform: null_mut(), 503 input: Vec::new(), 504 output: Vec::new(), 505 reference: Vec::new(), 506 507 pixels: 0, 508 storage_type: RGB8, 509 precache: false, 510 } 511 } 512 513 fn SetUp(&mut self) { 514 qcms_enable_iccv4(); 515 } 516 517 unsafe fn TearDown(&mut self) { 518 if self.in_profile != null_mut() { 519 qcms_profile_release(self.in_profile) 520 } 521 522 if self.out_profile != null_mut() { 523 qcms_profile_release(self.out_profile) 524 } 525 526 if self.transform != null_mut() { 527 qcms_transform_release(self.transform) 528 } 529 } 530 531 unsafe fn SetTransform(&mut self, transform: *mut qcms_transform) -> bool { 532 if self.transform != null_mut() { 533 qcms_transform_release(self.transform) 534 } 535 self.transform = transform; 536 self.transform != null_mut() 537 } 538 539 unsafe fn SetTransformForType(&mut self, ty: DataType) -> bool { 540 self.SetTransform(qcms_transform_create( 541 &*self.in_profile, 542 ty, 543 &*self.out_profile, 544 ty, 545 Perceptual, 546 )) 547 } 548 549 unsafe fn SetBuffers(&mut self, ty: DataType) -> bool { 550 match ty { 551 RGB8 => { 552 let (pixels, input) = GetRgbInputBuffer(); 553 self.input = input; 554 self.pixels = pixels; 555 self.reference = GetRgbOutputBuffer(self.pixels); 556 self.output = GetRgbOutputBuffer(self.pixels) 557 } 558 RGBA8 => { 559 let (pixels, input) = GetBgraInputBuffer(); 560 self.input = input; 561 self.pixels = pixels; 562 self.reference = GetRgbaOutputBuffer(self.pixels); 563 self.output = GetRgbaOutputBuffer(self.pixels); 564 } 565 BGRA8 => { 566 let (pixels, input) = GetRgbaInputBuffer(); 567 self.input = input; 568 self.pixels = pixels; 569 self.reference = GetRgbaOutputBuffer(self.pixels); 570 self.output = GetRgbaOutputBuffer(self.pixels); 571 } 572 _ => unreachable!("Unknown type!"), 573 } 574 self.storage_type = ty; 575 self.pixels > 0 576 } 577 578 unsafe fn ClearOutputBuffer(&mut self) { 579 match self.storage_type { 580 RGB8 => ClearRgbBuffer(&mut self.output, self.pixels), 581 RGBA8 | BGRA8 => ClearRgbaBuffer(&mut self.output, self.pixels), 582 _ => unreachable!("Unknown type!"), 583 } 584 } 585 586 unsafe fn ProduceRef(&mut self, trans_fn: transform_fn_t) { 587 trans_fn.unwrap()( 588 &*self.transform, 589 self.input.as_mut_ptr(), 590 self.reference.as_mut_ptr(), 591 self.pixels, 592 ) 593 } 594 595 fn CopyInputToRef(&mut self) { 596 let pixelSize = match self.storage_type { 597 RGB8 => 3, 598 RGBA8 | BGRA8 => 4, 599 _ => unreachable!("Unknown type!"), 600 }; 601 self.reference 602 .copy_from_slice(&self.input[..self.pixels * pixelSize]) 603 } 604 605 unsafe fn ProduceOutput(&mut self, trans_fn: transform_fn_t) { 606 self.ClearOutputBuffer(); 607 trans_fn.unwrap()( 608 &*self.transform, 609 self.input.as_mut_ptr(), 610 self.output.as_mut_ptr(), 611 self.pixels, 612 ) 613 } 614 615 unsafe fn VerifyOutput(&self, buf: &[u8]) -> bool { 616 match self.storage_type { 617 RGB8 => CmpRgbBuffer(buf, &self.output, self.pixels), 618 RGBA8 => CmpRgbaBuffer(buf, &self.output, self.pixels), 619 BGRA8 => CmpBgraBuffer(buf, &self.output, self.pixels), 620 _ => unreachable!("Unknown type!"), 621 } 622 } 623 624 unsafe fn ProduceVerifyOutput(&mut self, trans_fn: transform_fn_t) -> bool { 625 self.ProduceOutput(trans_fn); 626 self.VerifyOutput(&self.reference) 627 } 628 629 unsafe fn PrecacheOutput(&mut self) { 630 qcms_profile_precache_output_transform(&mut *self.out_profile); 631 self.precache = true; 632 } 633 unsafe fn TransformPrecache(&mut self) { 634 assert_eq!(self.precache, false); 635 assert!(self.SetBuffers(RGB8)); 636 assert!(self.SetTransformForType(RGB8)); 637 self.ProduceRef(Some(qcms_transform_data_rgb_out_lut)); 638 639 self.PrecacheOutput(); 640 assert!(self.SetTransformForType(RGB8)); 641 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_rgb_out_lut_precache))) 642 } 643 644 unsafe fn TransformPrecachePlatformExt(&mut self) { 645 self.PrecacheOutput(); 646 647 // Verify RGB transforms. 648 assert!(self.SetBuffers(RGB8)); 649 assert!(self.SetTransformForType(RGB8)); 650 self.ProduceRef(Some(qcms_transform_data_rgb_out_lut_precache)); 651 652 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 653 { 654 if is_x86_feature_detected!("sse2") { 655 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_rgb_out_lut_sse2))); 656 } 657 if is_x86_feature_detected!("avx") { 658 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_rgb_out_lut_avx))) 659 } 660 } 661 662 #[cfg(target_arch = "arm")] 663 { 664 if is_arm_feature_detected!("neon") { 665 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_rgb_out_lut_neon))) 666 } 667 } 668 669 #[cfg(target_arch = "aarch64")] 670 { 671 if is_aarch64_feature_detected!("neon") { 672 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_rgb_out_lut_neon))) 673 } 674 } 675 676 // Verify RGBA transform. 677 assert!(self.SetBuffers(RGBA8)); 678 assert!(self.SetTransformForType(RGBA8)); 679 self.ProduceRef(Some(qcms_transform_data_rgba_out_lut_precache)); 680 681 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 682 { 683 if is_x86_feature_detected!("sse2") { 684 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_rgba_out_lut_sse2))); 685 } 686 if is_x86_feature_detected!("avx") { 687 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_rgba_out_lut_avx))) 688 } 689 } 690 691 #[cfg(target_arch = "arm")] 692 { 693 if is_arm_feature_detected!("neon") { 694 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_rgba_out_lut_neon))) 695 } 696 } 697 698 #[cfg(target_arch = "aarch64")] 699 { 700 if is_aarch64_feature_detected!("neon") { 701 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_rgba_out_lut_neon))) 702 } 703 } 704 705 // Verify BGRA transform. 706 assert!(self.SetBuffers(BGRA8)); 707 assert!(self.SetTransformForType(BGRA8)); 708 self.ProduceRef(Some(qcms_transform_data_bgra_out_lut_precache)); 709 710 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 711 { 712 if is_x86_feature_detected!("sse2") { 713 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_bgra_out_lut_sse2))); 714 } 715 if is_x86_feature_detected!("avx") { 716 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_bgra_out_lut_avx))) 717 } 718 } 719 720 #[cfg(target_arch = "arm")] 721 { 722 if is_arm_feature_detected!("neon") { 723 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_bgra_out_lut_neon))) 724 } 725 } 726 727 #[cfg(target_arch = "aarch64")] 728 { 729 if is_aarch64_feature_detected!("neon") { 730 assert!(self.ProduceVerifyOutput(Some(qcms_transform_data_bgra_out_lut_neon))) 731 } 732 } 733 } 734 } 735 736 #[test] 737 fn sRGB_to_sRGB_precache() { 738 unsafe { 739 let mut pt = QcmsProfileTest::new(); 740 pt.SetUp(); 741 pt.in_profile = qcms_profile_sRGB(); 742 pt.out_profile = qcms_profile_sRGB(); 743 pt.TransformPrecache(); 744 pt.TearDown(); 745 } 746 } 747 748 #[test] 749 fn sRGB_to_sRGB_transform_identity() { 750 unsafe { 751 let mut pt = QcmsProfileTest::new(); 752 pt.SetUp(); 753 pt.in_profile = qcms_profile_sRGB(); 754 pt.out_profile = qcms_profile_sRGB(); 755 pt.PrecacheOutput(); 756 pt.SetBuffers(RGB8); 757 pt.SetTransformForType(RGB8); 758 qcms_transform_data( 759 &*pt.transform, 760 pt.input.as_mut_ptr() as *mut c_void, 761 pt.output.as_mut_ptr() as *mut c_void, 762 pt.pixels, 763 ); 764 assert!(pt.VerifyOutput(&pt.input)); 765 pt.TearDown(); 766 } 767 } 768 769 fn profile_from_path(file: &str) -> *mut Profile { 770 use std::io::Read; 771 let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); 772 path.push("profiles"); 773 path.push(file); 774 let mut file = std::fs::File::open(path).unwrap(); 775 let mut data = Vec::new(); 776 file.read_to_end(&mut data).unwrap(); 777 let profile = 778 unsafe { qcms_profile_from_memory(data.as_ptr() as *const c_void, data.len()) }; 779 assert_ne!(profile, std::ptr::null_mut()); 780 profile 781 } 782 783 #[test] 784 fn sRGB_to_ThinkpadW540() { 785 unsafe { 786 let mut pt = QcmsProfileTest::new(); 787 pt.SetUp(); 788 pt.in_profile = qcms_profile_sRGB(); 789 pt.out_profile = profile_from_path("lcms_thinkpad_w540.icc"); 790 pt.TransformPrecachePlatformExt(); 791 pt.TearDown(); 792 } 793 } 794 795 #[test] 796 fn sRGB_to_SamsungSyncmaster() { 797 unsafe { 798 let mut pt = QcmsProfileTest::new(); 799 pt.SetUp(); 800 pt.in_profile = qcms_profile_sRGB(); 801 pt.out_profile = profile_from_path("lcms_samsung_syncmaster.icc"); 802 pt.TransformPrecachePlatformExt(); 803 pt.TearDown(); 804 } 805 } 806 807 #[test] 808 fn v4_output() { 809 qcms_enable_iccv4(); 810 let input = qcms_profile_sRGB(); 811 // B2A0-ident.icc was created from the profile in bug 1679621 812 // manually edited using iccToXML/iccFromXML 813 let output = profile_from_path("B2A0-ident.icc"); 814 815 let transform = unsafe { qcms_transform_create(&*input, RGB8, &*output, RGB8, Perceptual) }; 816 let src = [0u8, 60, 195]; 817 let mut dst = [0u8, 0, 0]; 818 unsafe { 819 qcms_transform_data( 820 &*transform, 821 src.as_ptr() as *const libc::c_void, 822 dst.as_mut_ptr() as *mut libc::c_void, 823 1, 824 ); 825 } 826 assert_eq!(dst, [15, 16, 122]); 827 unsafe { 828 qcms_transform_release(transform); 829 qcms_profile_release(input); 830 qcms_profile_release(output); 831 } 832 } 833 834 #[test] 835 fn gray_smoke_test() { 836 let input = crate::Profile::new_gray_with_gamma(2.2); 837 let output = crate::Profile::new_sRGB(); 838 let xfm = 839 transform_create(&input, GrayA8, &output, RGBA8, crate::Intent::default()).unwrap(); 840 let src = [20u8, 20u8]; 841 let mut dst = [0u8, 0, 0, 0]; 842 unsafe { 843 qcms_transform_data( 844 &xfm, 845 src.as_ptr() as *const libc::c_void, 846 dst.as_mut_ptr() as *mut libc::c_void, 847 src.len() / GrayA8.bytes_per_pixel(), 848 ); 849 } 850 } 851 852 #[test] 853 fn data_create_rgb_with_gamma() { 854 let Rec709Primaries = qcms_CIE_xyYTRIPLE { 855 red: { 856 qcms_CIE_xyY { 857 x: 0.6400, 858 y: 0.3300, 859 Y: 1.0, 860 } 861 }, 862 green: { 863 qcms_CIE_xyY { 864 x: 0.3000, 865 y: 0.6000, 866 Y: 1.0, 867 } 868 }, 869 blue: { 870 qcms_CIE_xyY { 871 x: 0.1500, 872 y: 0.0600, 873 Y: 1.0, 874 } 875 }, 876 }; 877 let D65 = qcms_white_point_sRGB(); 878 let mut mem = std::ptr::null_mut(); 879 let mut size = 0; 880 unsafe { 881 qcms_data_create_rgb_with_gamma(D65, Rec709Primaries, 2.2, &mut mem, &mut size); 882 } 883 assert_ne!(size, 0); 884 unsafe { libc::free(mem) }; 885 } 886 } 887 888 #[cfg(test)] 889 mod test { 890 use crate::{Profile, Transform}; 891 #[test] 892 fn identity() { 893 let p1 = Profile::new_sRGB(); 894 let p2 = Profile::new_sRGB(); 895 let xfm = 896 Transform::new(&p1, &p2, crate::DataType::RGB8, crate::Intent::default()).unwrap(); 897 let mut data = [4, 30, 80]; 898 xfm.apply(&mut data); 899 assert_eq!(data, [4, 30, 80]); 900 } 901 #[test] 902 fn D50() { 903 let p1 = Profile::new_sRGB(); 904 let p2 = Profile::new_XYZD50(); 905 let xfm = 906 Transform::new(&p1, &p2, crate::DataType::RGB8, crate::Intent::default()).unwrap(); 907 let mut data = [4, 30, 80]; 908 xfm.apply(&mut data); 909 assert_eq!(data, [4, 4, 15]); 910 } 911 912 fn profile_from_path(file: &str) -> Box<Profile> { 913 use std::io::Read; 914 let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); 915 path.push("profiles"); 916 path.push(file); 917 let mut file = std::fs::File::open(path).unwrap(); 918 let mut data = Vec::new(); 919 file.read_to_end(&mut data).unwrap(); 920 Profile::new_from_slice(&data, false).unwrap() 921 } 922 923 #[test] 924 fn parametric_threshold() { 925 let src = profile_from_path("parametric-thresh.icc"); 926 let dst = crate::Profile::new_sRGB(); 927 let xfm = 928 Transform::new(&src, &dst, crate::DataType::RGB8, crate::Intent::default()).unwrap(); 929 let mut data = [4, 30, 80]; 930 xfm.apply(&mut data); 931 assert_eq!(data, [188, 188, 189]); 932 } 933 934 #[test] 935 fn cmyk() { 936 let input = profile_from_path("ps_cmyk_min.icc"); 937 let output = Profile::new_sRGB(); 938 let xfm = crate::Transform::new_to( 939 &input, 940 &output, 941 crate::DataType::CMYK, 942 crate::DataType::RGB8, 943 crate::Intent::default(), 944 ) 945 .unwrap(); 946 let src = [4, 30, 80, 10]; 947 let mut dst = [0, 0, 0]; 948 xfm.convert(&src, &mut dst); 949 assert_eq!(dst, [252, 237, 211]); 950 } 951 952 #[test] 953 fn sRGB_parametric() { 954 let src = Profile::new_sRGB(); 955 let dst = Profile::new_sRGB_parametric(); 956 let xfm = 957 Transform::new(&src, &dst, crate::DataType::RGB8, crate::Intent::default()).unwrap(); 958 let mut data = [4, 30, 80]; 959 xfm.apply(&mut data); 960 assert_eq!(data, [4, 30, 80]); 961 } 962 }