mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 10:50:00 +00:00
C++ Image API: introduce the SharedPixelBuffer
This commit is contained in:
parent
be47c8464c
commit
ef7fb6422a
5 changed files with 136 additions and 41 deletions
|
@ -171,6 +171,8 @@ fn gen_corelib(
|
|||
"GraphicsAPI",
|
||||
"CloseRequestResponse",
|
||||
"StandardListViewItem",
|
||||
"Rgb8Pixel",
|
||||
"Rgba8Pixel",
|
||||
];
|
||||
|
||||
config.export.exclude = [
|
||||
|
@ -344,6 +346,7 @@ fn gen_corelib(
|
|||
.iter()
|
||||
.filter(|exclusion| !rust_types.iter().any(|inclusion| inclusion == *exclusion))
|
||||
.chain(extra_excluded_types.iter())
|
||||
.chain(public_exported_types.iter())
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
|
@ -392,11 +395,8 @@ fn gen_corelib(
|
|||
public_config.export.exclude = private_exported_types.into_iter().collect();
|
||||
public_config.export.exclude.push("Point".into());
|
||||
public_config.export.include = public_exported_types.into_iter().map(str::to_string).collect();
|
||||
|
||||
public_config.export.body.insert(
|
||||
"StandardListViewItem".to_owned(),
|
||||
"/// \\private\nfriend bool operator==(const StandardListViewItem&, const StandardListViewItem&) = default;".into(),
|
||||
);
|
||||
public_config.structure.derive_eq = true;
|
||||
public_config.structure.derive_neq = true;
|
||||
|
||||
cbindgen::Builder::new()
|
||||
.with_config(public_config)
|
||||
|
@ -405,6 +405,8 @@ fn gen_corelib(
|
|||
.with_src(crate_dir.join("window.rs"))
|
||||
.with_src(crate_dir.join("api.rs"))
|
||||
.with_src(crate_dir.join("model.rs"))
|
||||
.with_src(crate_dir.join("graphics/image.rs"))
|
||||
.with_include("slint_string.h")
|
||||
.with_after_include(format!(
|
||||
r"
|
||||
/// This macro expands to the to the numeric value of the major version of Slint you're
|
||||
|
@ -564,9 +566,6 @@ fn gen_backend(
|
|||
.with_crate(crate_dir)
|
||||
.with_include("slint_image_internal.h")
|
||||
.with_include("slint_internal.h")
|
||||
.with_after_include(
|
||||
"namespace slint::cbindgen_private { using slint::cbindgen_private::types::Rgb8Pixel; }",
|
||||
)
|
||||
.generate()
|
||||
.context("Unable to generate bindings for slint_backend_internal.h")?
|
||||
.write_to_file(include_dir.join("slint_backend_internal.h"));
|
||||
|
|
|
@ -11,8 +11,61 @@
|
|||
|
||||
namespace slint {
|
||||
|
||||
using cbindgen_private::types::Rgb8Pixel;
|
||||
using cbindgen_private::types::Rgba8Pixel;
|
||||
/// SharedPixelBuffer is a container for storing image data as pixels. It is
|
||||
/// internally reference counted and cheap to clone.
|
||||
///
|
||||
/// You can construct a new empty shared pixel buffer with its default constructor,
|
||||
/// or you can copy it from an existing contiguous buffer that you might already have, using that
|
||||
/// constructor
|
||||
///
|
||||
/// See the documentation for Image for examples how to use this type to integrate
|
||||
/// Slint with external rendering functions.
|
||||
template<typename Pixel>
|
||||
struct SharedPixelBuffer
|
||||
{
|
||||
/// construct an empty SharedPixelBuffer
|
||||
SharedPixelBuffer() = default;
|
||||
|
||||
/// construct a SharedPixelBuffer with the given size
|
||||
SharedPixelBuffer(uint32_t width, uint32_t height)
|
||||
: m_width(width), m_height(height), m_data(width * height)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a SharedPixelBuffer by copying the data from the \a data array.
|
||||
/// The array must be of size \a width * \a height
|
||||
SharedPixelBuffer(uint32_t width, uint32_t height, const Pixel *data)
|
||||
: m_width(width), m_height(height), m_data(data, data + (width * height))
|
||||
{
|
||||
}
|
||||
|
||||
/// Return the width of the buffer in pixels
|
||||
uint32_t width() const { return m_width; }
|
||||
/// Return the height of the buffer in pixels
|
||||
uint32_t height() const { return m_height; }
|
||||
|
||||
/// Returns a const pointer to the first pixel of this buffer.
|
||||
const Pixel *begin() const { return m_data.begin(); }
|
||||
/// Returns a const pointer past this buffer.
|
||||
const Pixel *end() const { return m_data.end(); }
|
||||
/// Returns a pointer to the first pixel of this buffer.
|
||||
Pixel *begin() { return m_data.begin(); }
|
||||
/// Returns a pointer past this buffer.
|
||||
Pixel *end() { return m_data.end(); }
|
||||
/// Returns a const pointer to the first pixel of this buffer.
|
||||
const Pixel *cbegin() const { return m_data.begin(); }
|
||||
/// Returns a const pointer past this buffer.
|
||||
const Pixel *cend() const { return m_data.end(); }
|
||||
|
||||
/// Compare two SharedPixelBuffer. They are considered equal if all their pixels are equal
|
||||
bool operator==(const SharedPixelBuffer &other) const = default;
|
||||
|
||||
private:
|
||||
friend struct Image;
|
||||
uint32_t m_width;
|
||||
uint32_t m_height;
|
||||
SharedVector<Pixel> m_data;
|
||||
};
|
||||
|
||||
/// An image type that can be displayed by the Image element
|
||||
struct Image
|
||||
|
@ -28,32 +81,28 @@ public:
|
|||
return img;
|
||||
}
|
||||
|
||||
/// Construct an image from a vector of RGB pixel.
|
||||
/// The size of the vector \a data should be \a width * \a height
|
||||
static Image from_raw_data(unsigned int width, unsigned int height,
|
||||
const SharedVector<Rgb8Pixel> &data)
|
||||
{
|
||||
Image img;
|
||||
img.data = Data::ImageInner_EmbeddedImage(
|
||||
/// Construct an image from a SharedPixelBuffer of RGB pixel.
|
||||
Image(SharedPixelBuffer<Rgb8Pixel> buffer)
|
||||
: data(Data::ImageInner_EmbeddedImage(
|
||||
cbindgen_private::types::ImageCacheKey::Invalid(),
|
||||
cbindgen_private::types::SharedImageBuffer::RGB8(
|
||||
cbindgen_private::types::SharedPixelBuffer<Rgb8Pixel> {
|
||||
.width = width, .height = height, .data = data }));
|
||||
return img;
|
||||
.width = buffer.width(),
|
||||
.height = buffer.height(),
|
||||
.data = buffer.m_data })))
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct an image from a vector of RGBA pixels.
|
||||
/// The size of the vector \a data should be \a width * \a height
|
||||
static Image from_raw_data(unsigned int width, unsigned int height,
|
||||
const SharedVector<Rgba8Pixel> &data)
|
||||
{
|
||||
Image img;
|
||||
img.data = Data::ImageInner_EmbeddedImage(
|
||||
/// Construct an image from a SharedPixelBuffer of RGB pixel.
|
||||
Image(SharedPixelBuffer<Rgba8Pixel> buffer)
|
||||
: data(Data::ImageInner_EmbeddedImage(
|
||||
cbindgen_private::types::ImageCacheKey::Invalid(),
|
||||
cbindgen_private::types::SharedImageBuffer::RGBA8(
|
||||
cbindgen_private::types::SharedPixelBuffer<Rgba8Pixel> {
|
||||
.width = width, .height = height, .data = data }));
|
||||
return img;
|
||||
.width = buffer.width(),
|
||||
.height = buffer.height(),
|
||||
.data = buffer.m_data })))
|
||||
{
|
||||
}
|
||||
|
||||
/// Returns the size of the Image in pixels.
|
||||
|
|
|
@ -230,8 +230,7 @@ public:
|
|||
///
|
||||
/// The stride is the amount of pixels between two lines in the buffer.
|
||||
/// It is must be at least as large as the width of the window.
|
||||
void render(std::span<slint::cbindgen_private::Rgb8Pixel> buffer,
|
||||
std::size_t pixel_stride) const
|
||||
void render(std::span<slint::Rgb8Pixel> buffer, std::size_t pixel_stride) const
|
||||
{
|
||||
cbindgen_private::slint_software_renderer_render_rgb8(inner, buffer.data(), buffer.size(),
|
||||
pixel_stride);
|
||||
|
|
|
@ -21,17 +21,48 @@ struct SharedVector
|
|||
cbindgen_private::slint_shared_vector_empty())))
|
||||
{
|
||||
}
|
||||
|
||||
/// Creates a new vector that holds all the elements of the given std::initializer_list \a args.
|
||||
SharedVector(std::initializer_list<T> args) : SharedVector()
|
||||
SharedVector(std::initializer_list<T> args)
|
||||
: SharedVector(SharedVector::with_capacity(args.size()))
|
||||
{
|
||||
auto new_array = SharedVector::with_capacity(args.size());
|
||||
auto new_data = reinterpret_cast<T *>(new_array.inner + 1);
|
||||
auto new_data = reinterpret_cast<T *>(inner + 1);
|
||||
auto input_it = args.begin();
|
||||
for (std::size_t i = 0; i < args.size(); ++i, ++input_it) {
|
||||
new (new_data + i) T(*input_it);
|
||||
new_array.inner->size++;
|
||||
inner->size++;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a vector of a given size, with default-constructed data.
|
||||
explicit SharedVector(size_t size) : SharedVector(SharedVector::with_capacity(size))
|
||||
{
|
||||
auto new_data = reinterpret_cast<T *>(inner + 1);
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
new (new_data + i) T();
|
||||
inner->size++;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a vector of a given size, with with copies of the value.
|
||||
explicit SharedVector(size_t size, const T &value)
|
||||
: SharedVector(SharedVector::with_capacity(size))
|
||||
{
|
||||
auto new_data = reinterpret_cast<T *>(inner + 1);
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
new (new_data + i) T(value);
|
||||
inner->size++;
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs the container with the contents of the range `[first, last)`.
|
||||
template<class InputIt>
|
||||
SharedVector(InputIt first, InputIt last)
|
||||
: SharedVector(SharedVector::with_capacity(std::distance(first, last)))
|
||||
{
|
||||
for (auto it = first; it != last; ++it) {
|
||||
push_back(*it);
|
||||
}
|
||||
*this = std::move(new_array);
|
||||
}
|
||||
|
||||
/// Creates a new vector that is a copy of \a other.
|
||||
|
@ -41,6 +72,7 @@ struct SharedVector
|
|||
++inner->refcount;
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroys this vector. The underlying data is destroyed if no other
|
||||
/// vector references it.
|
||||
~SharedVector() { drop(); }
|
||||
|
@ -139,13 +171,10 @@ struct SharedVector
|
|||
/// and all the elements also compare equal; false otherwise.
|
||||
friend bool operator==(const SharedVector &a, const SharedVector &b)
|
||||
{
|
||||
if (a.size() != a.size())
|
||||
if (a.size() != b.size())
|
||||
return false;
|
||||
return std::equal(a.cbegin(), a.cend(), b.cbegin());
|
||||
}
|
||||
/// Returns false if the vector \a a has the same number of elements as \a b
|
||||
/// and all the elements also compare equal; true otherwise.
|
||||
friend bool operator!=(const SharedVector &a, const SharedVector &b) { return !(a == b); }
|
||||
|
||||
/// \private
|
||||
std::size_t capacity() const { return inner->capacity; }
|
||||
|
|
|
@ -173,7 +173,7 @@ TEST_CASE("Image")
|
|||
REQUIRE(*actual_path == SOURCE_DIR "/../../../logo/slint-logo-square-light-128x128.png");
|
||||
}
|
||||
|
||||
img = Image::from_raw_data(0, 0, SharedVector<Rgba8Pixel> {});
|
||||
img = Image(SharedPixelBuffer<Rgba8Pixel> {});
|
||||
{
|
||||
auto size = img.size();
|
||||
REQUIRE(size.width == 0);
|
||||
|
@ -182,7 +182,8 @@ TEST_CASE("Image")
|
|||
}
|
||||
auto red = Rgb8Pixel { 0xff, 0, 0 };
|
||||
auto blu = Rgb8Pixel { 0, 0, 0xff };
|
||||
img = Image::from_raw_data(3, 2, SharedVector<Rgb8Pixel> { red, red, blu, red, blu, blu });
|
||||
Rgb8Pixel some_data[] = { red, red, blu, red, blu, blu };
|
||||
img = Image(SharedPixelBuffer<Rgb8Pixel>(3, 2, some_data));
|
||||
{
|
||||
auto size = img.size();
|
||||
REQUIRE(size.width == 3);
|
||||
|
@ -207,6 +208,7 @@ TEST_CASE("SharedVector")
|
|||
REQUIRE(vec.size() == 4);
|
||||
auto orig_cap = vec.capacity();
|
||||
REQUIRE(orig_cap >= vec.size());
|
||||
|
||||
vec.clear();
|
||||
REQUIRE(vec.size() == 0);
|
||||
REQUIRE(vec.capacity() == 0); // vec was shared, so start with new empty vector.
|
||||
|
@ -216,6 +218,23 @@ TEST_CASE("SharedVector")
|
|||
|
||||
REQUIRE(copy.size() == 4);
|
||||
REQUIRE(copy.capacity() == orig_cap);
|
||||
|
||||
SharedVector<SharedString> vec2 { "Hello", "World", "of", "Vectors" };
|
||||
REQUIRE(copy == vec2);
|
||||
REQUIRE(copy != vec);
|
||||
|
||||
copy.clear(); // copy is not shared (anymore), retain capacity.
|
||||
REQUIRE(copy.capacity() == orig_cap);
|
||||
|
||||
SharedVector<SharedString> vec3(2, "Welcome back");
|
||||
REQUIRE(vec3.size() == 2);
|
||||
REQUIRE(vec3[1] == "Welcome back");
|
||||
REQUIRE(vec3 != vec);
|
||||
|
||||
vec.push_back("Welcome back");
|
||||
REQUIRE(vec3 == vec);
|
||||
|
||||
SharedVector<int> vec4(5);
|
||||
REQUIRE(vec4.size() == 5);
|
||||
REQUIRE(vec4[3] == 0);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue