mirror of
https://github.com/slint-ui/slint.git
synced 2025-07-13 08:05:19 +00:00
304 lines
13 KiB
C++
304 lines
13 KiB
C++
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
||
|
||
#pragma once
|
||
#include <string_view>
|
||
#include <span>
|
||
#include "slint_generated_public.h"
|
||
#include "slint_size.h"
|
||
#include "slint_image_internal.h"
|
||
#include "slint_string.h"
|
||
#include "slint_sharedvector.h"
|
||
|
||
namespace slint {
|
||
|
||
/// SharedPixelBuffer is a container for storing image data as pixels. It is
|
||
/// internally reference counted and cheap to copy.
|
||
///
|
||
/// 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 the
|
||
/// range 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 \a width and \a height. The pixels are default
|
||
/// initialized.
|
||
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))
|
||
{
|
||
}
|
||
|
||
/// Returns the width of the buffer in pixels.
|
||
uint32_t width() const { return m_width; }
|
||
/// Returns 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 SharedPixelBuffers. They are considered equal if all their pixels are equal.
|
||
bool operator==(const SharedPixelBuffer &other) const = default;
|
||
|
||
private:
|
||
friend struct Image;
|
||
friend class Window;
|
||
uint32_t m_width;
|
||
uint32_t m_height;
|
||
SharedVector<Pixel> m_data;
|
||
};
|
||
|
||
/// An image type that can be displayed by the Image element
|
||
///
|
||
/// You can construct Image objects from a path to an image file on disk, using
|
||
/// Image::load_from_path().
|
||
///
|
||
/// Another typical use-case is to render the image content with C++ code.
|
||
/// For this it’s most efficient to create a new SharedPixelBuffer with the known dimensions and
|
||
/// pass the pixel pointer returned by begin() to your rendering function. Afterwards you can create
|
||
/// an Image using the constructor taking a SharedPixelBuffer.
|
||
///
|
||
/// The following example creates a 320x200 RGB pixel buffer and calls a function to draw a shape
|
||
/// into it:
|
||
/// ```cpp
|
||
/// slint::SharedPixelBuffer::<slint::Rgb8Pixel> pixel_buffer(320, 200);
|
||
/// low_level_render(pixel_buffer.width(), pixel_buffer.height(),
|
||
/// static_cast<unsigned char *>(pixel_buffer.begin()));
|
||
/// slint::Image image(pixel_buffer);
|
||
/// ```
|
||
///
|
||
/// Another use-case is to import existing image data into Slint, by
|
||
/// creating a new Image through copying of the buffer:
|
||
///
|
||
/// ```cpp
|
||
/// slint::Image image(slint::SharedPixelBuffer<slint::Rgb8Pixel>(the_width, the_height,
|
||
/// static_cast<slint::Rgb8Pixel*>(the_data));
|
||
/// ```
|
||
///
|
||
/// This only works if the static_cast is valid and the underlying data has the same
|
||
/// memory layout as slint::Rgb8Pixel or slint::Rgba8Pixel. Otherwise, you will have to do a
|
||
/// pixel conversion as you copy the pixels:
|
||
///
|
||
/// ```cpp
|
||
/// slint::SharedPixelBuffer::<slint::Rgb8Pixel> pixel_buffer(the_width, the_height);
|
||
/// slint::Rgb8Pixel *raw_data = pixel_buffer.begin();
|
||
/// for (int i = 0; i < the_width * the_height; i++) {
|
||
/// raw_data[i] = { bgr_data[i * 3 + 2], bgr_data[i * 3 + 1], bgr_data[i * 3] };
|
||
/// }
|
||
/// ```
|
||
struct Image
|
||
{
|
||
public:
|
||
/// This enum describes the origin to use when rendering a borrowed OpenGL texture.
|
||
enum class BorrowedOpenGLTextureOrigin {
|
||
/// The top-left of the texture is the top-left of the texture drawn on the screen.
|
||
TopLeft,
|
||
/// The bottom-left of the texture is the top-left of the texture draw on the screen,
|
||
/// flipping it vertically.
|
||
BottomLeft,
|
||
};
|
||
|
||
Image() : data(Data::ImageInner_None()) { }
|
||
|
||
#if !defined(SLINT_FEATURE_FREESTANDING) || defined(DOXYGEN)
|
||
/// Load an image from an image file
|
||
[[nodiscard]] static Image load_from_path(const SharedString &file_path)
|
||
{
|
||
Image img;
|
||
cbindgen_private::types::slint_image_load_from_path(&file_path, &img.data);
|
||
return img;
|
||
}
|
||
#endif
|
||
|
||
/// Constructs a new Image from an existing OpenGL texture. The texture remains borrowed by
|
||
/// Slint for the duration of being used for rendering, such as when assigned as source property
|
||
/// to an `Image` element. It's the application's responsibility to delete the texture when it
|
||
/// is not used anymore.
|
||
///
|
||
/// The texture must be bindable against the `GL_TEXTURE_2D` target, have `GL_RGBA` as format
|
||
/// for the pixel data.
|
||
///
|
||
/// When Slint renders the texture, it assumes that the origin of the texture is at the
|
||
/// top-left. This is different from the default OpenGL coordinate system. If you want to
|
||
/// flip the origin, use BorrowedOpenGLTextureOrigin::BottomLeft.
|
||
///
|
||
/// Safety:
|
||
///
|
||
/// This function is unsafe because invalid texture ids may lead to undefined behavior in OpenGL
|
||
/// drivers. A valid texture id is one that was created by the same OpenGL context that is
|
||
/// current during any of the invocations of the callback set on
|
||
/// [`Window::set_rendering_notifier()`]. OpenGL contexts between instances of [`slint::Window`]
|
||
/// are not sharing resources. Consequently
|
||
/// [`slint::Image`] objects created from borrowed OpenGL textures cannot be shared between
|
||
/// different windows.
|
||
[[nodiscard]] static Image create_from_borrowed_gl_2d_rgba_texture(
|
||
uint32_t texture_id, Size<uint32_t> size,
|
||
BorrowedOpenGLTextureOrigin origin = BorrowedOpenGLTextureOrigin::TopLeft)
|
||
{
|
||
cbindgen_private::types::BorrowedOpenGLTextureOrigin origin_private =
|
||
origin == BorrowedOpenGLTextureOrigin::TopLeft
|
||
? cbindgen_private::types::BorrowedOpenGLTextureOrigin::TopLeft
|
||
: cbindgen_private::types::BorrowedOpenGLTextureOrigin::BottomLeft;
|
||
return Image(Data::ImageInner_BorrowedOpenGLTexture(
|
||
cbindgen_private::types::BorrowedOpenGLTexture {
|
||
texture_id,
|
||
size,
|
||
origin_private,
|
||
})
|
||
|
||
);
|
||
}
|
||
|
||
/// Construct an image from a SharedPixelBuffer of RGB pixels.
|
||
Image(SharedPixelBuffer<Rgb8Pixel> buffer)
|
||
: data(Data::ImageInner_EmbeddedImage(
|
||
cbindgen_private::types::ImageCacheKey::Invalid(),
|
||
cbindgen_private::types::SharedImageBuffer::RGB8(
|
||
cbindgen_private::types::SharedPixelBuffer<Rgb8Pixel> {
|
||
.width = buffer.width(),
|
||
.height = buffer.height(),
|
||
.data = buffer.m_data })))
|
||
{
|
||
}
|
||
|
||
/// Construct an image from a SharedPixelBuffer of RGBA pixels.
|
||
Image(SharedPixelBuffer<Rgba8Pixel> buffer)
|
||
: data(Data::ImageInner_EmbeddedImage(
|
||
cbindgen_private::types::ImageCacheKey::Invalid(),
|
||
cbindgen_private::types::SharedImageBuffer::RGBA8(
|
||
cbindgen_private::types::SharedPixelBuffer<Rgba8Pixel> {
|
||
.width = buffer.width(),
|
||
.height = buffer.height(),
|
||
.data = buffer.m_data })))
|
||
{
|
||
}
|
||
|
||
/// Returns the size of the Image in pixels.
|
||
Size<uint32_t> size() const { return cbindgen_private::types::slint_image_size(&data); }
|
||
|
||
/// Returns the path of the image on disk, if it was constructed via Image::load_from_path().
|
||
std::optional<slint::SharedString> path() const
|
||
{
|
||
if (auto *str = cbindgen_private::types::slint_image_path(&data)) {
|
||
return *str;
|
||
} else {
|
||
return {};
|
||
}
|
||
}
|
||
|
||
/// Sets the nine-slice edges of the image.
|
||
///
|
||
/// [Nine-slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) is a method for scaling
|
||
/// images in such a way that the corners are not distorted.
|
||
/// The arguments define the pixel sizes of the edges that cut the image into 9 slices.
|
||
void set_nine_slice_edges(unsigned short top, unsigned short right, unsigned short bottom,
|
||
unsigned short left)
|
||
{
|
||
cbindgen_private::types::slint_image_set_nine_slice_edges(&data, top, right, bottom, left);
|
||
}
|
||
|
||
/// Returns the pixel buffer for the Image if available in RGB format without alpha.
|
||
/// Returns nullopt if the pixels cannot be obtained, for example when the image was created
|
||
/// from borrowed OpenGL textures.
|
||
std::optional<SharedPixelBuffer<Rgb8Pixel>> to_rgb8() const
|
||
{
|
||
SharedPixelBuffer<Rgb8Pixel> result;
|
||
if (cbindgen_private ::types::slint_image_to_rgb8(&data, &result.m_data, &result.m_width,
|
||
&result.m_height)) {
|
||
return result;
|
||
} else {
|
||
return {};
|
||
}
|
||
}
|
||
|
||
/// Returns the pixel buffer for the Image if available in RGBA format.
|
||
/// Returns nullopt if the pixels cannot be obtained, for example when the image was created
|
||
/// from borrowed OpenGL textures.
|
||
std::optional<SharedPixelBuffer<Rgba8Pixel>> to_rgba8() const
|
||
{
|
||
SharedPixelBuffer<Rgba8Pixel> result;
|
||
if (cbindgen_private ::types::slint_image_to_rgba8(&data, &result.m_data, &result.m_width,
|
||
&result.m_height)) {
|
||
return result;
|
||
} else {
|
||
return {};
|
||
}
|
||
}
|
||
|
||
/// Returns the pixel buffer for the Image if available in RGBA format, with the alpha channel
|
||
/// pre-multiplied to the red, green, and blue channels. Returns nullopt if the pixels cannot be
|
||
/// obtained, for example when the image was created from borrowed OpenGL textures.
|
||
std::optional<SharedPixelBuffer<Rgba8Pixel>> to_rgba8_premultiplied() const
|
||
{
|
||
SharedPixelBuffer<Rgba8Pixel> result;
|
||
if (cbindgen_private ::types::slint_image_to_rgba8_premultiplied(
|
||
&data, &result.m_data, &result.m_width, &result.m_height)) {
|
||
return result;
|
||
} else {
|
||
return {};
|
||
}
|
||
}
|
||
|
||
/// Returns true if \a a refers to the same image as \a b; false otherwise.
|
||
friend bool operator==(const Image &a, const Image &b)
|
||
{
|
||
return cbindgen_private::types::slint_image_compare_equal(&a.data, &b.data);
|
||
}
|
||
/// Returns false if \a a refers to the same image as \a b; true otherwise.
|
||
friend bool operator!=(const Image &a, const Image &b) { return !(a == b); }
|
||
|
||
/// \private
|
||
explicit Image(cbindgen_private::types::Image inner) : data(inner) { }
|
||
|
||
private:
|
||
using Tag = cbindgen_private::types::ImageInner::Tag;
|
||
using Data = cbindgen_private::types::Image;
|
||
Data data;
|
||
};
|
||
|
||
namespace private_api {
|
||
inline Image load_image_from_embedded_data(std::span<const uint8_t> data,
|
||
std::string_view extension)
|
||
{
|
||
cbindgen_private::types::Image img(cbindgen_private::types::Image::ImageInner_None());
|
||
cbindgen_private::types::slint_image_load_from_embedded_data(
|
||
slint::cbindgen_private::Slice<uint8_t> { const_cast<uint8_t *>(data.data()),
|
||
data.size() },
|
||
slint::cbindgen_private::Slice<uint8_t> {
|
||
const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(extension.data())),
|
||
extension.size() },
|
||
&img);
|
||
return Image(img);
|
||
}
|
||
|
||
inline Image image_from_embedded_textures(const cbindgen_private::types::StaticTextures *textures)
|
||
{
|
||
cbindgen_private::types::Image img(cbindgen_private::types::Image::ImageInner_None());
|
||
cbindgen_private::types::slint_image_from_embedded_textures(textures, &img);
|
||
return Image(img);
|
||
}
|
||
}
|
||
|
||
}
|