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 | use std::vec; |
5 | |
6 | use i_slint_core::{ |
7 | graphics::{Image, SharedImageBuffer, SharedPixelBuffer}, |
8 | ImageInner, |
9 | }; |
10 | use napi::{ |
11 | bindgen_prelude::{Buffer, External}, |
12 | Env, JsUnknown, |
13 | }; |
14 | |
15 | // This is needed for typedoc check JsImageData::image |
16 | pub type ImageData = Image; |
17 | |
18 | /// SlintPoint implements {@link ImageData}. |
19 | #[napi ] |
20 | pub struct SlintImageData { |
21 | inner: Image, |
22 | } |
23 | |
24 | impl From<Image> for SlintImageData { |
25 | fn from(image: Image) -> Self { |
26 | Self { inner: image } |
27 | } |
28 | } |
29 | |
30 | #[napi ] |
31 | impl SlintImageData { |
32 | /// Constructs a new image with the given width and height. |
33 | /// Each pixel will set to red = 0, green = 0, blue = 0 and alpha = 0. |
34 | #[napi (constructor)] |
35 | pub fn new(width: u32, height: u32) -> Self { |
36 | Self { inner: Image::from_rgba8(SharedPixelBuffer::new(width, height)) } |
37 | } |
38 | |
39 | /// Returns the width of the image in pixels. |
40 | #[napi (getter)] |
41 | pub fn width(&self) -> u32 { |
42 | self.inner.size().width |
43 | } |
44 | |
45 | /// Returns the height of the image in pixels. |
46 | #[napi (getter)] |
47 | pub fn height(&self) -> u32 { |
48 | self.inner.size().height |
49 | } |
50 | |
51 | /// Returns the image as buffer. |
52 | /// A Buffer is a subclass of Uint8Array. |
53 | #[napi (getter)] |
54 | pub fn data(&self) -> Buffer { |
55 | let image_inner: &ImageInner = (&self.inner).into(); |
56 | if let Some(buffer) = image_inner.render_to_buffer(None) { |
57 | match buffer { |
58 | SharedImageBuffer::RGB8(buffer) => { |
59 | return Buffer::from(rgb_to_rgba( |
60 | buffer.as_bytes(), |
61 | (self.width() * self.height()) as usize, |
62 | )) |
63 | } |
64 | SharedImageBuffer::RGBA8(buffer) => return Buffer::from(buffer.as_bytes()), |
65 | SharedImageBuffer::RGBA8Premultiplied(buffer) => { |
66 | return Buffer::from(rgb_to_rgba( |
67 | buffer.as_bytes(), |
68 | (self.width() * self.height()) as usize, |
69 | )) |
70 | } |
71 | } |
72 | } |
73 | |
74 | Buffer::from(vec![0; (self.width() * self.height() * 4) as usize]) |
75 | } |
76 | |
77 | #[napi (getter)] |
78 | pub fn path(&self, env: Env) -> napi::Result<JsUnknown> { |
79 | self.inner.path().map_or_else( |
80 | || env.get_undefined().map(|v| v.into_unknown()), |
81 | |p| env.create_string(p.to_string_lossy().as_ref()).map(|v| v.into_unknown()), |
82 | ) |
83 | } |
84 | |
85 | /// @hidden |
86 | #[napi (getter)] |
87 | pub fn image(&self) -> External<ImageData> { |
88 | External::new(self.inner.clone()) |
89 | } |
90 | } |
91 | |
92 | fn rgb_to_rgba(bytes: &[u8], size: usize) -> Vec<u8> { |
93 | let mut rgba_bytes: Vec = vec![]; |
94 | |
95 | for i: usize in 0..size { |
96 | if (i * 3) + 2 >= bytes.len() { |
97 | continue; |
98 | } |
99 | |
100 | rgba_bytes.push(bytes[i * 3]); |
101 | rgba_bytes.push(bytes[(i * 3) + 1]); |
102 | rgba_bytes.push(bytes[(i * 3) + 2]); |
103 | rgba_bytes.push(255); |
104 | } |
105 | |
106 | rgba_bytes |
107 | } |
108 | |