| // Copyright 2018 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #[cfg(feature = "sse")] |
| #[link(name = "accumulate")] |
| extern "C" { |
| fn accumulate_sse(src: *const f32, dst: *mut u8, n: u32); |
| } |
| |
| #[cfg(feature = "sse")] |
| pub fn accumulate(src: &[f32]) -> Vec<u8> { |
| // SIMD instructions force us to align data since we iterate each 4 elements |
| // So: |
| // n (0) => 0 |
| // n (1 or 2 or 3 or 4) => 4, |
| // n (5) => 8 |
| // and so on |
| let len = src.len(); |
| let n = (len + 3) & !3; // align data |
| let mut dst: Vec<u8> = Vec::with_capacity(n); |
| unsafe { |
| accumulate_sse(src.as_ptr(), dst.as_mut_ptr(), n as u32); |
| dst.set_len(len); // we must return vec of the same length as src.len() |
| } |
| dst |
| } |
| |
| #[cfg(not(feature = "sse"))] |
| pub fn accumulate(src: &[f32]) -> Vec<u8> { |
| let mut acc = 0.0; |
| src.iter() |
| .map(|c| { |
| // This would translate really well to SIMD |
| acc += c; |
| let y = acc.abs(); |
| let y = if y < 1.0 { y } else { 1.0 }; |
| (255.0 * y) as u8 |
| }) |
| .collect() |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| // The most simple and straightforward implementation of |
| // accumulate fn |
| fn accumulate_simple_impl(src: &[f32]) -> Vec<u8> { |
| let mut acc = 0.0; |
| src.iter() |
| .map(|c| { |
| acc += c; |
| let y = acc.abs(); |
| let y = if y < 1.0 { y } else { 1.0 }; |
| (255.0 * y) as u8 |
| }) |
| .collect() |
| } |
| fn test_accumulate(src: Vec<f32>) { |
| assert_eq!(accumulate_simple_impl(&src), accumulate(&src)); |
| } |
| |
| #[test] |
| fn max_255_from_1_0() { |
| // 1.0 * 255.0 = 255.0 (max value) |
| test_accumulate(vec![1.0]); |
| } |
| #[test] |
| fn max_255_from_0_5() { |
| // 0.5 * 2 = 1.0 |
| // 1.0 * 255.0 = 255.0 (max value) |
| test_accumulate(vec![0.5; 2]); |
| } |
| #[test] |
| fn max_255_from_0_25() { |
| // 0.25 * 4 = 1.0 |
| // 1.0 * 255.0 = 255.0 (max value) |
| test_accumulate(vec![0.25; 4]); |
| } |
| #[test] |
| fn max_255_from_0_125() { |
| // 0.125 * 8 = 1.0 |
| // 1.0 * 255.0 = 255.0 (max value) |
| test_accumulate(vec![0.125; 8]); |
| } |
| #[test] |
| fn max_255_from_0_0625() { |
| // 0.0625 * 16 = 1.0 |
| // 1.0 * 255.0 = 255.0 (max value) |
| test_accumulate(vec![0.0625; 16]); |
| } |
| #[test] |
| fn max_255_from_0_03125() { |
| // 0.03125 * 32 = 1.0 |
| // 1.0 * 255.0 = 255.0 (max value) |
| test_accumulate(vec![0.03125; 32]); |
| } |
| #[test] |
| fn max_255_from_0_015625() { |
| // 0.015625 * 64 = 1.0 |
| // 1.0 * 255.0 = 255.0 (max value) |
| test_accumulate(vec![0.015625; 64]); |
| } |
| #[test] |
| fn max_255_from_0_0078125() { |
| // 0.0078125 * 128 = 1.0 |
| // 1.0 * 255.0 = 255.0 (max value) |
| test_accumulate(vec![0.0078125; 128]); |
| } |
| |
| #[test] |
| fn simple_0() { |
| test_accumulate(vec![]); |
| } |
| #[test] |
| fn simple_1() { |
| test_accumulate(vec![0.1]); |
| } |
| #[test] |
| fn simple_2() { |
| test_accumulate(vec![0.1, 0.2]); |
| } |
| #[test] |
| fn simple_3() { |
| test_accumulate(vec![0.1, 0.2, 0.3]); |
| } |
| #[test] |
| fn simple_4() { |
| test_accumulate(vec![0.1, 0.2, 0.3, 0.4]); |
| } |
| #[test] |
| fn simple_5() { |
| test_accumulate(vec![0.1, 0.2, 0.3, 0.4, 0.5]); |
| } |
| #[test] |
| fn simple_6() { |
| test_accumulate(vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6]); |
| } |
| #[test] |
| fn simple_7() { |
| test_accumulate(vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7]); |
| } |
| } |