tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }