slint/api/cpp/include/slint_image.h
Simon Hausmann a7a3b3f9ee Changelog: C++: Add slint::Window::take_snapshot()
Corresponds to the Rust API.
2024-11-13 12:38:36 +01:00

304 lines
13 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 its 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);
}
}
}