diff --git a/intrinsiccv/include/intrinsiccv.h b/intrinsiccv/include/intrinsiccv.h index 10b29c61bdf27ba84251f72ee667578a709e1b3d..a2d378a97724adb2c66faaed64f0a702e517947a 100644 --- a/intrinsiccv/include/intrinsiccv.h +++ b/intrinsiccv/include/intrinsiccv.h @@ -113,6 +113,30 @@ INTRINSICCV_BINARY_OP(saturating_absdiff_u16, uint16_t); INTRINSICCV_BINARY_OP(saturating_absdiff_s16, int16_t); INTRINSICCV_BINARY_OP(saturating_absdiff_s32, int32_t); +/// Multiplies the values of the corresponding elements in `src_a` and `src_b`, +/// and puts the result into `dst`. +/// +/// The multiplication is saturated, i.e. the result is the largest number of +/// the type of the element if the multiplication result would overflow. Source +/// data length (in bytes) is `stride` * `height`. Width and height are the +/// same for the two sources. +/// +/// @param src_a Pointer to the first source data. Must be non-null. +/// @param src_b Pointer to the second source data. Must be non-null. +/// @param src_a_stride Distance in bytes from the start of one row to the +/// start of the next row for the first source data. +/// Must not be less than width * sizeof(type). +/// @param src_b_stride Distance in bytes from the start of one row to the +/// start of the next row for the second source data. +/// Must not be less than width * sizeof(type). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes from the start of one row to the +/// start of the next row for the destination data. +/// Must not be less than width * sizeof(type). +/// @param width How many elements are in a row for the output. +/// @param height How many rows are in the output. +/// @param scale Currently unused parameter. +/// INTRINSICCV_BINARY_OP(saturating_multiply_u8, uint8_t, double); INTRINSICCV_BINARY_OP(saturating_multiply_s8, int8_t, double); INTRINSICCV_BINARY_OP(saturating_multiply_u16, uint16_t, double); diff --git a/test/api/test_saturating_multiply.cpp b/test/api/test_saturating_multiply.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bbb031be24b69f621bea7bb89919d8d17437b359 --- /dev/null +++ b/test/api/test_saturating_multiply.cpp @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include + +#include "framework/operation.h" + +#define INTRINSICCV_SATURATING_MULTIPLY(type, suffix) \ + INTRINSICCV_API(saturating_multiply, \ + intrinsiccv_saturating_multiply_##suffix, type) + +INTRINSICCV_SATURATING_MULTIPLY(uint8_t, u8); +INTRINSICCV_SATURATING_MULTIPLY(int8_t, s8); +INTRINSICCV_SATURATING_MULTIPLY(uint16_t, u16); +INTRINSICCV_SATURATING_MULTIPLY(int16_t, s16); +INTRINSICCV_SATURATING_MULTIPLY(int32_t, s32); + +template +class SaturatingMultiplyTest final : public BinaryOperationTest { + /// Expose constructor of base class. + using BinaryOperationTest::BinaryOperationTest; + + protected: + using Elements = typename BinaryOperationTest::Elements; + using BinaryOperationTest::min; + using BinaryOperationTest::max; + + /// Calls the API-under-test in the appropriate way. + void call_api() override { + saturating_multiply()( + this->inputs_[0].data(), this->inputs_[0].stride(), + this->inputs_[1].data(), this->inputs_[1].stride(), + this->actual_[0].data(), this->actual_[0].stride(), this->width(), + this->height(), this->scale()); + } + double scale() { + return 1.0; // This is the only parameter IntrinsicCV supports. + } + /// Returns different test data for signed and unsigned element types. + const std::vector& test_elements() override { + if constexpr (std::is_unsigned_v) { + static const std::vector kTestElements = { + // clang-format off + { 0, 0, 0}, + { 1, 1, 1}, + { 2, 2, 4}, + { 6, 8, 48}, + { max(), 1, max()}, + { max(), 2, max()}, + { max(), max(), max()}, + // clang-format on + }; + + return kTestElements; + } else { + static const std::vector kTestElements = { + // clang-format off + { min(), min(), max()}, + { min(), max(), min()}, + { min(), -1, max()}, + {min() + 2, -1,max() - 1}, + { 6, -8, -48}, + { -2, 2, -4}, + { -1, -1, 1}, + { 0, 0, 0}, + { 1, 1, 1}, + { 2, 2, 4}, + { 6, 8, 48}, + {max() - 2, -1,min() + 3}, + { max(), 1, max()}, + { max(), 2, max()}, + { max(), max(), max()}, + // clang-format on + }; + + return kTestElements; + } + } +}; // end of class SaturatingMultiplyTest + +template +class SaturatingMultiply : public testing::Test {}; + +using ElementTypes = + ::testing::Types; +TYPED_TEST_SUITE(SaturatingMultiply, ElementTypes); + +/// Tests \ref intrinsiccv_saturating_multiply_ API. +TYPED_TEST(SaturatingMultiply, API) { + // Test without padding. + SaturatingMultiplyTest{}.test(); + // Test with padding. + SaturatingMultiplyTest{} + .with_padding(test::Options::vector_length()) + .test(); +}