libcamera: controls: Support array controls in ControlValue
Add array controls support to the ControlValue class. The polymorphic class can now store more than a single element and supports access and creation through the use of Span<>. Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
parent
cd0f7929ec
commit
1fa4b43402
2 changed files with 222 additions and 42 deletions
|
@ -9,6 +9,7 @@
|
||||||
#define __LIBCAMERA_CONTROLS_H__
|
#define __LIBCAMERA_CONTROLS_H__
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
@ -51,6 +52,10 @@ struct control_type<int64_t> {
|
||||||
static constexpr ControlType value = ControlTypeInteger64;
|
static constexpr ControlType value = ControlTypeInteger64;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T, std::size_t N>
|
||||||
|
struct control_type<Span<T, N>> : public control_type<std::remove_cv_t<T>> {
|
||||||
|
};
|
||||||
|
|
||||||
} /* namespace details */
|
} /* namespace details */
|
||||||
|
|
||||||
class ControlValue
|
class ControlValue
|
||||||
|
@ -58,15 +63,35 @@ class ControlValue
|
||||||
public:
|
public:
|
||||||
ControlValue();
|
ControlValue();
|
||||||
|
|
||||||
template<typename T>
|
#ifndef __DOXYGEN__
|
||||||
ControlValue(T value)
|
template<typename T, typename std::enable_if_t<!details::is_span<T>::value, std::nullptr_t> = nullptr>
|
||||||
: type_(details::control_type<std::remove_cv_t<T>>::value)
|
ControlValue(const T &value)
|
||||||
|
: type_(ControlTypeNone), numElements_(0)
|
||||||
{
|
{
|
||||||
*reinterpret_cast<T *>(&bool_) = value;
|
set(details::control_type<std::remove_cv_t<T>>::value, false,
|
||||||
|
&value, 1, sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if_t<details::is_span<T>::value, std::nullptr_t> = nullptr>
|
||||||
|
#else
|
||||||
|
template<typename T>
|
||||||
|
#endif
|
||||||
|
ControlValue(const T &value)
|
||||||
|
: type_(ControlTypeNone), numElements_(0)
|
||||||
|
{
|
||||||
|
set(details::control_type<std::remove_cv_t<T>>::value, true,
|
||||||
|
value.data(), value.size(), sizeof(typename T::value_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
~ControlValue();
|
||||||
|
|
||||||
|
ControlValue(const ControlValue &other);
|
||||||
|
ControlValue &operator=(const ControlValue &other);
|
||||||
|
|
||||||
ControlType type() const { return type_; }
|
ControlType type() const { return type_; }
|
||||||
bool isNone() const { return type_ == ControlTypeNone; }
|
bool isNone() const { return type_ == ControlTypeNone; }
|
||||||
|
bool isArray() const { return isArray_; }
|
||||||
|
std::size_t numElements() const { return numElements_; }
|
||||||
Span<const uint8_t> data() const;
|
Span<const uint8_t> data() const;
|
||||||
|
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
|
@ -77,31 +102,61 @@ public:
|
||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
#ifndef __DOXYGEN__
|
||||||
|
template<typename T, typename std::enable_if_t<!details::is_span<T>::value, std::nullptr_t> = nullptr>
|
||||||
T get() const
|
T get() const
|
||||||
{
|
{
|
||||||
assert(type_ == details::control_type<std::remove_cv_t<T>>::value);
|
assert(type_ == details::control_type<std::remove_cv_t<T>>::value);
|
||||||
|
assert(!isArray_);
|
||||||
|
|
||||||
return *reinterpret_cast<const T *>(&bool_);
|
return *reinterpret_cast<const T *>(data().data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if_t<details::is_span<T>::value, std::nullptr_t> = nullptr>
|
||||||
|
#else
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
#endif
|
||||||
|
T get() const
|
||||||
|
{
|
||||||
|
assert(type_ == details::control_type<std::remove_cv_t<T>>::value);
|
||||||
|
assert(isArray_);
|
||||||
|
|
||||||
|
using V = typename T::value_type;
|
||||||
|
const V *value = reinterpret_cast<const V *>(data().data());
|
||||||
|
return { value, numElements_ };
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __DOXYGEN__
|
||||||
|
template<typename T, typename std::enable_if_t<!details::is_span<T>::value, std::nullptr_t> = nullptr>
|
||||||
void set(const T &value)
|
void set(const T &value)
|
||||||
{
|
{
|
||||||
type_ = details::control_type<std::remove_cv_t<T>>::value;
|
set(details::control_type<std::remove_cv_t<T>>::value, false,
|
||||||
*reinterpret_cast<T *>(&bool_) = value;
|
reinterpret_cast<const void *>(&value), 1, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if_t<details::is_span<T>::value, std::nullptr_t> = nullptr>
|
||||||
|
#else
|
||||||
|
template<typename T>
|
||||||
|
#endif
|
||||||
|
void set(const T &value)
|
||||||
|
{
|
||||||
|
set(details::control_type<std::remove_cv_t<T>>::value, true,
|
||||||
|
value.data(), value.size(), sizeof(typename T::value_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ControlType type_;
|
ControlType type_ : 8;
|
||||||
|
bool isArray_ : 1;
|
||||||
|
std::size_t numElements_ : 16;
|
||||||
|
uint64_t storage_;
|
||||||
|
|
||||||
union {
|
void release();
|
||||||
bool bool_;
|
void set(ControlType type, bool isArray, const void *data,
|
||||||
int32_t integer32_;
|
std::size_t numElements, std::size_t elementSize);
|
||||||
int64_t integer64_;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(ControlValue) == 16, "Invalid size of ControlValue class");
|
||||||
|
|
||||||
class ControlId
|
class ControlId
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "control_validator.h"
|
#include "control_validator.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
@ -50,7 +51,7 @@ LOG_DEFINE_CATEGORY(Controls)
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
static constexpr size_t ControlValueSize[] = {
|
static constexpr size_t ControlValueSize[] = {
|
||||||
[ControlTypeNone] = 1,
|
[ControlTypeNone] = 0,
|
||||||
[ControlTypeBool] = sizeof(bool),
|
[ControlTypeBool] = sizeof(bool),
|
||||||
[ControlTypeInteger32] = sizeof(int32_t),
|
[ControlTypeInteger32] = sizeof(int32_t),
|
||||||
[ControlTypeInteger64] = sizeof(int64_t),
|
[ControlTypeInteger64] = sizeof(int64_t),
|
||||||
|
@ -80,16 +81,60 @@ static constexpr size_t ControlValueSize[] = {
|
||||||
* \brief Construct an empty ControlValue.
|
* \brief Construct an empty ControlValue.
|
||||||
*/
|
*/
|
||||||
ControlValue::ControlValue()
|
ControlValue::ControlValue()
|
||||||
: type_(ControlTypeNone)
|
: type_(ControlTypeNone), isArray_(false), numElements_(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn template<typename T> T ControlValue::ControlValue(T value)
|
* \fn template<typename T> T ControlValue::ControlValue(const T &value)
|
||||||
* \brief Construct a ControlValue of type T
|
* \brief Construct a ControlValue of type T
|
||||||
* \param[in] value Initial value
|
* \param[in] value Initial value
|
||||||
|
*
|
||||||
|
* This function constructs a new instance of ControlValue and stores the \a
|
||||||
|
* value inside it. If the type \a T is equivalent to Span<R>, the instance
|
||||||
|
* stores an array of values of type \a R. Otherwise the instance stores a
|
||||||
|
* single value of type \a T. The numElements() and type() are updated to
|
||||||
|
* reflect the stored value.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void ControlValue::release()
|
||||||
|
{
|
||||||
|
std::size_t size = numElements_ * ControlValueSize[type_];
|
||||||
|
|
||||||
|
if (size > sizeof(storage_)) {
|
||||||
|
delete[] *reinterpret_cast<char **>(&storage_);
|
||||||
|
storage_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlValue::~ControlValue()
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Construct a ControlValue with the content of \a other
|
||||||
|
* \param[in] other The ControlValue to copy content from
|
||||||
|
*/
|
||||||
|
ControlValue::ControlValue(const ControlValue &other)
|
||||||
|
: type_(ControlTypeNone), numElements_(0)
|
||||||
|
{
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Replace the content of the ControlValue with a copy of the content
|
||||||
|
* of \a other
|
||||||
|
* \param[in] other The ControlValue to copy content from
|
||||||
|
* \return The ControlValue with its content replaced with the one of \a other
|
||||||
|
*/
|
||||||
|
ControlValue &ControlValue::operator=(const ControlValue &other)
|
||||||
|
{
|
||||||
|
set(other.type_, other.isArray_, other.data().data(),
|
||||||
|
other.numElements_, ControlValueSize[other.type_]);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn ControlValue::type()
|
* \fn ControlValue::type()
|
||||||
* \brief Retrieve the data type of the value
|
* \brief Retrieve the data type of the value
|
||||||
|
@ -102,16 +147,33 @@ ControlValue::ControlValue()
|
||||||
* \return True if the value type is ControlTypeNone, false otherwise
|
* \return True if the value type is ControlTypeNone, false otherwise
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn ControlValue::isArray()
|
||||||
|
* \brief Determine if the value stores an array
|
||||||
|
* \return True if the value stores an array, false otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn ControlValue::numElements()
|
||||||
|
* \brief Retrieve the number of elements stored in the ControlValue
|
||||||
|
*
|
||||||
|
* For instances storing an array, this function returns the number of elements
|
||||||
|
* in the array. Otherwise, it returns 1.
|
||||||
|
*
|
||||||
|
* \return The number of elements stored in the ControlValue
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Retrieve the raw data of a control value
|
* \brief Retrieve the raw data of a control value
|
||||||
* \return The raw data of the control value as a span of uint8_t
|
* \return The raw data of the control value as a span of uint8_t
|
||||||
*/
|
*/
|
||||||
Span<const uint8_t> ControlValue::data() const
|
Span<const uint8_t> ControlValue::data() const
|
||||||
{
|
{
|
||||||
return {
|
std::size_t size = numElements_ * ControlValueSize[type_];
|
||||||
reinterpret_cast<const uint8_t *>(&bool_),
|
const uint8_t *data = size > sizeof(storage_)
|
||||||
ControlValueSize[type_]
|
? *reinterpret_cast<const uint8_t * const *>(&storage_)
|
||||||
};
|
: reinterpret_cast<const uint8_t *>(&storage_);
|
||||||
|
return { data, size };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,18 +182,43 @@ Span<const uint8_t> ControlValue::data() const
|
||||||
*/
|
*/
|
||||||
std::string ControlValue::toString() const
|
std::string ControlValue::toString() const
|
||||||
{
|
{
|
||||||
switch (type_) {
|
if (type_ == ControlTypeNone)
|
||||||
case ControlTypeNone:
|
return "<ValueType Error>";
|
||||||
return "<None>";
|
|
||||||
case ControlTypeBool:
|
const uint8_t *data = ControlValue::data().data();
|
||||||
return bool_ ? "True" : "False";
|
std::string str(isArray_ ? "[ " : "");
|
||||||
case ControlTypeInteger32:
|
|
||||||
return std::to_string(integer32_);
|
for (unsigned int i = 0; i < numElements_; ++i) {
|
||||||
case ControlTypeInteger64:
|
switch (type_) {
|
||||||
return std::to_string(integer64_);
|
case ControlTypeBool: {
|
||||||
|
const bool *value = reinterpret_cast<const bool *>(data);
|
||||||
|
str += *value ? "True" : "False";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ControlTypeInteger32: {
|
||||||
|
const int32_t *value = reinterpret_cast<const int32_t *>(data);
|
||||||
|
str += std::to_string(*value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ControlTypeInteger64: {
|
||||||
|
const int64_t *value = reinterpret_cast<const int64_t *>(data);
|
||||||
|
str += std::to_string(*value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ControlTypeNone:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 1 != numElements_)
|
||||||
|
str += ", ";
|
||||||
|
|
||||||
|
data += ControlValueSize[type_];
|
||||||
}
|
}
|
||||||
|
|
||||||
return "<ValueType Error>";
|
if (isArray_)
|
||||||
|
str += " ]";
|
||||||
|
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,16 +230,13 @@ bool ControlValue::operator==(const ControlValue &other) const
|
||||||
if (type_ != other.type_)
|
if (type_ != other.type_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
switch (type_) {
|
if (numElements_ != other.numElements())
|
||||||
case ControlTypeBool:
|
|
||||||
return bool_ == other.bool_;
|
|
||||||
case ControlTypeInteger32:
|
|
||||||
return integer32_ == other.integer32_;
|
|
||||||
case ControlTypeInteger64:
|
|
||||||
return integer64_ == other.integer64_;
|
|
||||||
default:
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
if (isArray_ != other.isArray_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return memcmp(data().data(), other.data().data(), data().size()) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,8 +249,16 @@ bool ControlValue::operator==(const ControlValue &other) const
|
||||||
* \fn template<typename T> T ControlValue::get() const
|
* \fn template<typename T> T ControlValue::get() const
|
||||||
* \brief Get the control value
|
* \brief Get the control value
|
||||||
*
|
*
|
||||||
* The control value type shall match the type T, otherwise the behaviour is
|
* This function returns the contained value as an instance of \a T. If the
|
||||||
* undefined.
|
* ControlValue instance stores a single value, the type \a T shall match the
|
||||||
|
* stored value type(). If the instance stores an array of values, the type
|
||||||
|
* \a T should be equal to Span<const R>, and the type \a R shall match the
|
||||||
|
* stored value type(). The behaviour is undefined otherwise.
|
||||||
|
*
|
||||||
|
* Note that a ControlValue instance that stores a non-array value is not
|
||||||
|
* equivalent to an instance that stores an array value containing a single
|
||||||
|
* element. The latter shall be accessed through a Span<const R> type, while
|
||||||
|
* the former shall be accessed through a type \a T corresponding to type().
|
||||||
*
|
*
|
||||||
* \return The control value
|
* \return The control value
|
||||||
*/
|
*/
|
||||||
|
@ -175,8 +267,41 @@ bool ControlValue::operator==(const ControlValue &other) const
|
||||||
* \fn template<typename T> void ControlValue::set(const T &value)
|
* \fn template<typename T> void ControlValue::set(const T &value)
|
||||||
* \brief Set the control value to \a value
|
* \brief Set the control value to \a value
|
||||||
* \param[in] value The control value
|
* \param[in] value The control value
|
||||||
|
*
|
||||||
|
* This function stores the \a value in the instance. If the type \a T is
|
||||||
|
* equivalent to Span<R>, the instance stores an array of values of type \a R.
|
||||||
|
* Otherwise the instance stores a single value of type \a T. The numElements()
|
||||||
|
* and type() are updated to reflect the stored value.
|
||||||
|
*
|
||||||
|
* The entire content of \a value is copied to the instance, no reference to \a
|
||||||
|
* value or to the data it references is retained. This may be an expensive
|
||||||
|
* operation for Span<> values that refer to large arrays.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void ControlValue::set(ControlType type, bool isArray, const void *data,
|
||||||
|
std::size_t numElements, std::size_t elementSize)
|
||||||
|
{
|
||||||
|
ASSERT(elementSize == ControlValueSize[type]);
|
||||||
|
|
||||||
|
release();
|
||||||
|
|
||||||
|
type_ = type;
|
||||||
|
numElements_ = numElements;
|
||||||
|
isArray_ = isArray;
|
||||||
|
|
||||||
|
std::size_t size = elementSize * numElements;
|
||||||
|
void *storage;
|
||||||
|
|
||||||
|
if (size > sizeof(storage_)) {
|
||||||
|
storage = reinterpret_cast<void *>(new char[size]);
|
||||||
|
*reinterpret_cast<void **>(&storage_) = storage;
|
||||||
|
} else {
|
||||||
|
storage = reinterpret_cast<void *>(&storage_);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(storage, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class ControlId
|
* \class ControlId
|
||||||
* \brief Control static metadata
|
* \brief Control static metadata
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue