1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
3
4#pragma once
5#include <string_view>
6#include <span>
7#include "slint_generated_public.h"
8#include "slint_size.h"
9#include "slint_image_internal.h"
10#include "slint_string.h"
11#include "slint_sharedvector.h"
12
13namespace slint {
14
15/// SharedPixelBuffer is a container for storing image data as pixels. It is
16/// internally reference counted and cheap to copy.
17///
18/// You can construct a new empty shared pixel buffer with its default constructor,
19/// or you can copy it from an existing contiguous buffer that you might already have, using the
20/// range constructor.
21///
22/// See the documentation for Image for examples how to use this type to integrate
23/// Slint with external rendering functions.
24template<typename Pixel>
25struct SharedPixelBuffer
26{
27 /// Construct an empty SharedPixelBuffer.
28 SharedPixelBuffer() = default;
29
30 /// Construct a SharedPixelBuffer with the given \a width and \a height. The pixels are default
31 /// initialized.
32 SharedPixelBuffer(uint32_t width, uint32_t height)
33 : m_width(width), m_height(height), m_data(width * height)
34 {
35 }
36
37 /// Construct a SharedPixelBuffer by copying the data from the \a data array.
38 /// The array must be of size \a width * \a height .
39 SharedPixelBuffer(uint32_t width, uint32_t height, const Pixel *data)
40 : m_width(width), m_height(height), m_data(data, data + (width * height))
41 {
42 }
43
44 /// Returns the width of the buffer in pixels.
45 uint32_t width() const { return m_width; }
46 /// Returns the height of the buffer in pixels.
47 uint32_t height() const { return m_height; }
48
49 /// Returns a const pointer to the first pixel of this buffer.
50 const Pixel *begin() const { return m_data.begin(); }
51 /// Returns a const pointer past this buffer.
52 const Pixel *end() const { return m_data.end(); }
53 /// Returns a pointer to the first pixel of this buffer.
54 Pixel *begin() { return m_data.begin(); }
55 /// Returns a pointer past this buffer.
56 Pixel *end() { return m_data.end(); }
57 /// Returns a const pointer to the first pixel of this buffer.
58 const Pixel *cbegin() const { return m_data.begin(); }
59 /// Returns a const pointer past this buffer.
60 const Pixel *cend() const { return m_data.end(); }
61
62 /// Compare two SharedPixelBuffers. They are considered equal if all their pixels are equal.
63 bool operator==(const SharedPixelBuffer &other) const = default;
64
65private:
66 friend struct Image;
67 uint32_t m_width;
68 uint32_t m_height;
69 SharedVector<Pixel> m_data;
70};
71
72/// An image type that can be displayed by the Image element
73///
74/// You can construct Image objects from a path to an image file on disk, using
75/// Image::load_from_path().
76///
77/// Another typical use-case is to render the image content with C++ code.
78/// For this it’s most efficient to create a new SharedPixelBuffer with the known dimensions and
79/// pass the pixel pointer returned by begin() to your rendering function. Afterwards you can create
80/// an Image using the constructor taking a SharedPixelBuffer.
81///
82/// The following example creates a 320x200 RGB pixel buffer and calls a function to draw a shape
83/// into it:
84/// ```cpp
85/// slint::SharedPixelBuffer::<slint::Rgb8Pixel> pixel_buffer(320, 200);
86/// low_level_render(pixel_buffer.width(), pixel_buffer.height(),
87/// static_cast<unsigned char *>(pixel_buffer.begin()));
88/// slint::Image image(pixel_buffer);
89/// ```
90///
91/// Another use-case is to import existing image data into Slint, by
92/// creating a new Image through copying of the buffer:
93///
94/// ```cpp
95/// slint::Image image(slint::SharedPixelBuffer<slint::Rgb8Pixel>(the_width, the_height,
96/// static_cast<slint::Rgb8Pixel*>(the_data));
97/// ```
98///
99/// This only works if the static_cast is valid and the underlying data has the same
100/// memory layout as slint::Rgb8Pixel or slint::Rgba8Pixel. Otherwise, you will have to do a
101/// pixel conversion as you copy the pixels:
102///
103/// ```cpp
104/// slint::SharedPixelBuffer::<slint::Rgb8Pixel> pixel_buffer(the_width, the_height);
105/// slint::Rgb8Pixel *raw_data = pixel_buffer.begin();
106/// for (int i = 0; i < the_width * the_height; i++) {
107/// raw_data[i] = { bgr_data[i * 3 + 2], bgr_data[i * 3 + 1], bgr_data[i * 3] };
108/// }
109/// ```
110struct Image
111{
112public:
113 /// This enum describes the origin to use when rendering a borrowed OpenGL texture.
114 enum class BorrowedOpenGLTextureOrigin {
115 /// The top-left of the texture is the top-left of the texture drawn on the screen.
116 TopLeft,
117 /// The bottom-left of the texture is the top-left of the texture draw on the screen,
118 /// flipping it vertically.
119 BottomLeft,
120 };
121
122 Image() : data(Data::ImageInner_None()) { }
123
124#ifndef SLINT_FEATURE_FREESTANDING
125 /// Load an image from an image file
126 [[nodiscard]] static Image load_from_path(const SharedString &file_path)
127 {
128 Image img;
129 cbindgen_private::types::slint_image_load_from_path(path: &file_path, image: &img.data);
130 return img;
131 }
132#endif
133
134 /// Constructs a new Image from an existing OpenGL texture. The texture remains borrowed by
135 /// Slint for the duration of being used for rendering, such as when assigned as source property
136 /// to an `Image` element. It's the application's responsibility to delete the texture when it
137 /// is not used anymore.
138 ///
139 /// The texture must be bindable against the `GL_TEXTURE_2D` target, have `GL_RGBA` as format
140 /// for the pixel data.
141 ///
142 /// When Slint renders the texture, it assumes that the origin of the texture is at the
143 /// top-left. This is different from the default OpenGL coordinate system. If you want to
144 /// flip the origin, use BorrowedOpenGLTextureOrigin::BottomLeft.
145 ///
146 /// Safety:
147 ///
148 /// This function is unsafe because invalid texture ids may lead to undefined behavior in OpenGL
149 /// drivers. A valid texture id is one that was created by the same OpenGL context that is
150 /// current during any of the invocations of the callback set on
151 /// [`Window::set_rendering_notifier()`]. OpenGL contexts between instances of [`slint::Window`]
152 /// are not sharing resources. Consequently
153 /// [`slint::Image`] objects created from borrowed OpenGL textures cannot be shared between
154 /// different windows.
155 [[nodiscard]] static Image create_from_borrowed_gl_2d_rgba_texture(
156 uint32_t texture_id, Size<uint32_t> size,
157 BorrowedOpenGLTextureOrigin origin = BorrowedOpenGLTextureOrigin::TopLeft)
158 {
159 cbindgen_private::types::BorrowedOpenGLTextureOrigin origin_private =
160 origin == BorrowedOpenGLTextureOrigin::TopLeft
161 ? cbindgen_private::types::BorrowedOpenGLTextureOrigin::TopLeft
162 : cbindgen_private::types::BorrowedOpenGLTextureOrigin::BottomLeft;
163 return Image(Data::ImageInner_BorrowedOpenGLTexture(
164 0: cbindgen_private::types::BorrowedOpenGLTexture {
165 .texture_id: texture_id,
166 .size: size,
167 .origin: origin_private,
168 })
169
170 );
171 }
172
173 /// Construct an image from a SharedPixelBuffer of RGB pixels.
174 Image(SharedPixelBuffer<Rgb8Pixel> buffer)
175 : data(Data::ImageInner_EmbeddedImage(
176 cache_key: cbindgen_private::types::ImageCacheKey::Invalid(),
177 buffer: cbindgen_private::types::SharedImageBuffer::RGB8(
178 0: cbindgen_private::types::SharedPixelBuffer<Rgb8Pixel> {
179 .width = buffer.width(),
180 .height = buffer.height(),
181 .data = buffer.m_data })))
182 {
183 }
184
185 /// Construct an image from a SharedPixelBuffer of RGBA pixels.
186 Image(SharedPixelBuffer<Rgba8Pixel> buffer)
187 : data(Data::ImageInner_EmbeddedImage(
188 cache_key: cbindgen_private::types::ImageCacheKey::Invalid(),
189 buffer: cbindgen_private::types::SharedImageBuffer::RGBA8(
190 0: cbindgen_private::types::SharedPixelBuffer<Rgba8Pixel> {
191 .width = buffer.width(),
192 .height = buffer.height(),
193 .data = buffer.m_data })))
194 {
195 }
196
197 /// Returns the size of the Image in pixels.
198 Size<uint32_t> size() const
199 {
200 return cbindgen_private::types::slint_image_size(image: &data);
201 }
202
203 /// Returns the path of the image on disk, if it was constructed via Image::load_from_path().
204 std::optional<slint::SharedString> path() const
205 {
206 if (auto *str = cbindgen_private::types::slint_image_path(image: &data)) {
207 return *str;
208 } else {
209 return {};
210 }
211 }
212
213 /// Sets the nine-slice edges of the image.
214 ///
215 /// [Nine-slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) is a method for scaling
216 /// images in such a way that the corners are not distorted.
217 /// The arguments define the pixel sizes of the edges that cut the image into 9 slices.
218 void set_nine_slice_edges(unsigned short top, unsigned short right, unsigned short bottom,
219 unsigned short left)
220 {
221 cbindgen_private::types::slint_image_set_nine_slice_edges(image: &data, top, right, bottom, left);
222 }
223
224 /// Returns true if \a a refers to the same image as \a b; false otherwise.
225 friend bool operator==(const Image &a, const Image &b)
226 {
227 return cbindgen_private::types::slint_image_compare_equal(image1: &a.data, image2: &b.data);
228 }
229 /// Returns false if \a a refers to the same image as \a b; true otherwise.
230 friend bool operator!=(const Image &a, const Image &b)
231 {
232 return !(a == b);
233 }
234
235 /// \private
236 explicit Image(cbindgen_private::types::Image inner) : data(inner) { }
237
238private:
239 using Tag = cbindgen_private::types::ImageInner::Tag;
240 using Data = cbindgen_private::types::Image;
241 Data data;
242};
243
244namespace private_api {
245inline Image load_image_from_embedded_data(std::span<const uint8_t> data,
246 std::string_view extension)
247{
248 cbindgen_private::types::Image img(cbindgen_private::types::Image::ImageInner_None());
249 cbindgen_private::types::slint_image_load_from_embedded_data(
250 data: slint::cbindgen_private::Slice<uint8_t> { const_cast<uint8_t *>(data.data()),
251 data.size() },
252 format: slint::cbindgen_private::Slice<uint8_t> {
253 const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(extension.data())),
254 extension.size() },
255 image: &img);
256 return Image(img);
257}
258
259inline Image image_from_embedded_textures(const cbindgen_private::types::StaticTextures *textures)
260{
261 cbindgen_private::types::Image img(cbindgen_private::types::Image::ImageInner_None());
262 cbindgen_private::types::slint_image_from_embedded_textures(textures, image: &img);
263 return Image(img);
264}
265}
266
267}
268

source code of slint/api/cpp/include/slint_image.h