1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
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 friend class Window;
68 uint32_t m_width;
69 uint32_t m_height;
70 SharedVector<Pixel> m_data;
71};
72
73/// An image type that can be displayed by the Image element
74///
75/// You can construct Image objects from a path to an image file on disk, using
76/// Image::load_from_path().
77///
78/// Another typical use-case is to render the image content with C++ code.
79/// For this it’s most efficient to create a new SharedPixelBuffer with the known dimensions and
80/// pass the pixel pointer returned by begin() to your rendering function. Afterwards you can create
81/// an Image using the constructor taking a SharedPixelBuffer.
82///
83/// The following example creates a 320x200 RGB pixel buffer and calls a function to draw a shape
84/// into it:
85/// ```cpp
86/// slint::SharedPixelBuffer::<slint::Rgb8Pixel> pixel_buffer(320, 200);
87/// low_level_render(pixel_buffer.width(), pixel_buffer.height(),
88/// static_cast<unsigned char *>(pixel_buffer.begin()));
89/// slint::Image image(pixel_buffer);
90/// ```
91///
92/// Another use-case is to import existing image data into Slint, by
93/// creating a new Image through copying of the buffer:
94///
95/// ```cpp
96/// slint::Image image(slint::SharedPixelBuffer<slint::Rgb8Pixel>(the_width, the_height,
97/// static_cast<slint::Rgb8Pixel*>(the_data));
98/// ```
99///
100/// This only works if the static_cast is valid and the underlying data has the same
101/// memory layout as slint::Rgb8Pixel or slint::Rgba8Pixel. Otherwise, you will have to do a
102/// pixel conversion as you copy the pixels:
103///
104/// ```cpp
105/// slint::SharedPixelBuffer::<slint::Rgb8Pixel> pixel_buffer(the_width, the_height);
106/// slint::Rgb8Pixel *raw_data = pixel_buffer.begin();
107/// for (int i = 0; i < the_width * the_height; i++) {
108/// raw_data[i] = { bgr_data[i * 3 + 2], bgr_data[i * 3 + 1], bgr_data[i * 3] };
109/// }
110/// ```
111struct Image
112{
113public:
114 /// This enum describes the origin to use when rendering a borrowed OpenGL texture.
115 enum class BorrowedOpenGLTextureOrigin {
116 /// The top-left of the texture is the top-left of the texture drawn on the screen.
117 TopLeft,
118 /// The bottom-left of the texture is the top-left of the texture draw on the screen,
119 /// flipping it vertically.
120 BottomLeft,
121 };
122
123 Image() : data(Data::ImageInner_None()) { }
124
125#if !defined(SLINT_FEATURE_FREESTANDING) || defined(DOXYGEN)
126 /// Load an image from an image file
127 [[nodiscard]] static Image load_from_path(const SharedString &file_path)
128 {
129 Image img;
130 cbindgen_private::types::slint_image_load_from_path(path: &file_path, image: &img.data);
131 return img;
132 }
133#endif
134
135 /// Constructs a new Image from an existing OpenGL texture. The texture remains borrowed by
136 /// Slint for the duration of being used for rendering, such as when assigned as source property
137 /// to an `Image` element. It's the application's responsibility to delete the texture when it
138 /// is not used anymore.
139 ///
140 /// The texture must be bindable against the `GL_TEXTURE_2D` target, have `GL_RGBA` as format
141 /// for the pixel data.
142 ///
143 /// When Slint renders the texture, it assumes that the origin of the texture is at the
144 /// top-left. This is different from the default OpenGL coordinate system. If you want to
145 /// flip the origin, use BorrowedOpenGLTextureOrigin::BottomLeft.
146 ///
147 /// Safety:
148 ///
149 /// This function is unsafe because invalid texture ids may lead to undefined behavior in OpenGL
150 /// drivers. A valid texture id is one that was created by the same OpenGL context that is
151 /// current during any of the invocations of the callback set on
152 /// [`Window::set_rendering_notifier()`]. OpenGL contexts between instances of [`slint::Window`]
153 /// are not sharing resources. Consequently
154 /// [`slint::Image`] objects created from borrowed OpenGL textures cannot be shared between
155 /// different windows.
156 [[nodiscard]] static Image create_from_borrowed_gl_2d_rgba_texture(
157 uint32_t texture_id, Size<uint32_t> size,
158 BorrowedOpenGLTextureOrigin origin = BorrowedOpenGLTextureOrigin::TopLeft)
159 {
160 cbindgen_private::types::BorrowedOpenGLTextureOrigin origin_private =
161 origin == BorrowedOpenGLTextureOrigin::TopLeft
162 ? cbindgen_private::types::BorrowedOpenGLTextureOrigin::TopLeft
163 : cbindgen_private::types::BorrowedOpenGLTextureOrigin::BottomLeft;
164 return Image(Data::ImageInner_BorrowedOpenGLTexture(
165 0: cbindgen_private::types::BorrowedOpenGLTexture {
166 .texture_id: texture_id,
167 .size: size,
168 .origin: origin_private,
169 })
170
171 );
172 }
173
174 /// Construct an image from a SharedPixelBuffer of RGB pixels.
175 Image(SharedPixelBuffer<Rgb8Pixel> buffer)
176 : data(Data::ImageInner_EmbeddedImage(
177 cbindgen_private::types::ImageCacheKey::Invalid(),
178 cbindgen_private::types::SharedImageBuffer::RGB8(
179 cbindgen_private::types::SharedPixelBuffer<Rgb8Pixel> {
180 .width = buffer.width(),
181 .height = buffer.height(),
182 .data = buffer.m_data })))
183 {
184 }
185
186 /// Construct an image from a SharedPixelBuffer of RGBA pixels.
187 Image(SharedPixelBuffer<Rgba8Pixel> buffer)
188 : data(Data::ImageInner_EmbeddedImage(
189 cbindgen_private::types::ImageCacheKey::Invalid(),
190 cbindgen_private::types::SharedImageBuffer::RGBA8(
191 cbindgen_private::types::SharedPixelBuffer<Rgba8Pixel> {
192 .width = buffer.width(),
193 .height = buffer.height(),
194 .data = buffer.m_data })))
195 {
196 }
197
198 /// Returns the size of the Image in pixels.
199 Size<uint32_t> size() const { return cbindgen_private::types::slint_image_size(&data); }
200
201 /// Returns the path of the image on disk, if it was constructed via Image::load_from_path().
202 std::optional<slint::SharedString> path() const
203 {
204 if (auto *str = cbindgen_private::types::slint_image_path(&data)) {
205 return *str;
206 } else {
207 return {};
208 }
209 }
210
211 /// Sets the nine-slice edges of the image.
212 ///
213 /// [Nine-slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) is a method for scaling
214 /// images in such a way that the corners are not distorted.
215 /// The arguments define the pixel sizes of the edges that cut the image into 9 slices.
216 void set_nine_slice_edges(unsigned short top, unsigned short right, unsigned short bottom,
217 unsigned short left)
218 {
219 cbindgen_private::types::slint_image_set_nine_slice_edges(&data, top, right, bottom, left);
220 }
221
222 /// Returns the pixel buffer for the Image if available in RGB format without alpha.
223 /// Returns nullopt if the pixels cannot be obtained, for example when the image was created
224 /// from borrowed OpenGL textures.
225 std::optional<SharedPixelBuffer<Rgb8Pixel>> to_rgb8() const
226 {
227 SharedPixelBuffer<Rgb8Pixel> result;
228 if (cbindgen_private ::types::slint_image_to_rgb8(&data, &result.m_data, &result.m_width,
229 &result.m_height)) {
230 return result;
231 } else {
232 return {};
233 }
234 }
235
236 /// Returns the pixel buffer for the Image if available in RGBA format.
237 /// Returns nullopt if the pixels cannot be obtained, for example when the image was created
238 /// from borrowed OpenGL textures.
239 std::optional<SharedPixelBuffer<Rgba8Pixel>> to_rgba8() const
240 {
241 SharedPixelBuffer<Rgba8Pixel> result;
242 if (cbindgen_private ::types::slint_image_to_rgba8(&data, &result.m_data, &result.m_width,
243 &result.m_height)) {
244 return result;
245 } else {
246 return {};
247 }
248 }
249
250 /// Returns the pixel buffer for the Image if available in RGBA format, with the alpha channel
251 /// pre-multiplied to the red, green, and blue channels. Returns nullopt if the pixels cannot be
252 /// obtained, for example when the image was created from borrowed OpenGL textures.
253 std::optional<SharedPixelBuffer<Rgba8Pixel>> to_rgba8_premultiplied() const
254 {
255 SharedPixelBuffer<Rgba8Pixel> result;
256 if (cbindgen_private ::types::slint_image_to_rgba8_premultiplied(
257 &data, &result.m_data, &result.m_width, &result.m_height)) {
258 return result;
259 } else {
260 return {};
261 }
262 }
263
264 /// Returns true if \a a refers to the same image as \a b; false otherwise.
265 friend bool operator==(const Image &a, const Image &b)
266 {
267 return cbindgen_private::types::slint_image_compare_equal(image1: &a.data, image2: &b.data);
268 }
269 /// Returns false if \a a refers to the same image as \a b; true otherwise.
270 friend bool operator!=(const Image &a, const Image &b) { return !(a == b); }
271
272 /// \private
273 explicit Image(cbindgen_private::types::Image inner) : data(inner) { }
274
275private:
276 using Tag = cbindgen_private::types::ImageInner::Tag;
277 using Data = cbindgen_private::types::Image;
278 Data data;
279};
280
281namespace private_api {
282inline Image load_image_from_embedded_data(std::span<const uint8_t> data,
283 std::string_view extension)
284{
285 cbindgen_private::types::Image img(cbindgen_private::types::Image::ImageInner_None());
286 cbindgen_private::types::slint_image_load_from_embedded_data(
287 data: slint::cbindgen_private::Slice<uint8_t> { const_cast<uint8_t *>(data.data()),
288 data.size() },
289 format: slint::cbindgen_private::Slice<uint8_t> {
290 const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(extension.data())),
291 extension.size() },
292 image: &img);
293 return Image(img);
294}
295
296inline Image image_from_embedded_textures(const cbindgen_private::types::StaticTextures *textures)
297{
298 cbindgen_private::types::Image img(cbindgen_private::types::Image::ImageInner_None());
299 cbindgen_private::types::slint_image_from_embedded_textures(textures, image: &img);
300 return Image(img);
301}
302}
303
304}
305

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