diff --git a/crates/cxx-qt-lib-headers/include/gui/qimage.h b/crates/cxx-qt-lib-headers/include/gui/qimage.h new file mode 100644 index 000000000..c4fecab89 --- /dev/null +++ b/crates/cxx-qt-lib-headers/include/gui/qimage.h @@ -0,0 +1,34 @@ +// clang-format off +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Leon Matthes +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#pragma once + +#ifdef CXX_QT_GUI_FEATURE + +#include + +#include "rust/cxx.h" + +#include + +namespace rust { + +// QImage has a move constructor, however it is basically trivial. +template<> +struct IsRelocatable : ::std::true_type +{ +}; + +namespace cxxqtlib1 { +using QImageFormat = QImage::Format; + +QImage +qimageInitFromData(const rust::Slice data, + rust::Str format); + +} // namespace cxxqtlib1 +} // namespace rust +#endif diff --git a/crates/cxx-qt-lib-headers/src/lib.rs b/crates/cxx-qt-lib-headers/src/lib.rs index e514f2afd..147dc9597 100644 --- a/crates/cxx-qt-lib-headers/src/lib.rs +++ b/crates/cxx-qt-lib-headers/src/lib.rs @@ -71,6 +71,8 @@ pub fn write_headers(directory: impl AsRef) { "qguiapplication.h", ), #[cfg(feature = "qt_gui")] + (include_str!("../include/gui/qimage.h"), "qimage.h"), + #[cfg(feature = "qt_gui")] (include_str!("../include/gui/qvector2d.h"), "qvector2d.h"), #[cfg(feature = "qt_gui")] (include_str!("../include/gui/qvector3d.h"), "qvector3d.h"), diff --git a/crates/cxx-qt-lib/build.rs b/crates/cxx-qt-lib/build.rs index 7bc9ec32b..cfba8f710 100644 --- a/crates/cxx-qt-lib/build.rs +++ b/crates/cxx-qt-lib/build.rs @@ -158,6 +158,7 @@ fn main() { "core/qvector/qvector_qcolor", "gui/qcolor", "gui/qguiapplication", + "gui/qimage", "gui/qvector2d", "gui/qvector3d", "gui/qvector4d", @@ -225,6 +226,7 @@ fn main() { cpp_files.extend([ "gui/qcolor", "gui/qguiapplication", + "gui/qimage", "gui/qvector2d", "gui/qvector3d", "gui/qvector4d", diff --git a/crates/cxx-qt-lib/src/gui/mod.rs b/crates/cxx-qt-lib/src/gui/mod.rs index 1204dc446..49052357c 100644 --- a/crates/cxx-qt-lib/src/gui/mod.rs +++ b/crates/cxx-qt-lib/src/gui/mod.rs @@ -17,3 +17,6 @@ pub use qvector3d::QVector3D; mod qvector4d; pub use qvector4d::QVector4D; + +mod qimage; +pub use qimage::QImage; diff --git a/crates/cxx-qt-lib/src/gui/qimage.cpp b/crates/cxx-qt-lib/src/gui/qimage.cpp new file mode 100644 index 000000000..489823e42 --- /dev/null +++ b/crates/cxx-qt-lib/src/gui/qimage.cpp @@ -0,0 +1,51 @@ +// clang-format off +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Leon Matthes +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#ifdef CXX_QT_GUI_FEATURE + +#include "cxx-qt-lib/qimage.h" +#include "../assertion_utils.h" +#include + +// A QImage inherits from QPaintDevice. + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +// QPaintDevice in Qt5 contains two things: +// 1. ushort painters; (due to the following field this has padding to make it +// 64-bit long) +// 2. QPaintDevicePrivate *reserved; +// Then QImage adds an additional field: +// 3. QImageData *d; +// For a total of 3 pointers in length. +// Because of the added v-table, it's a total of 4 pointers in size. +assert_alignment_and_size(QImage, + alignof(::std::size_t), + sizeof(::std::size_t) * 4); +#else +// In Qt6 the QPaintDevice doesn't contain the `reserved` pointer, making it 1 +// pointer smaller +assert_alignment_and_size(QImage, + alignof(::std::size_t), + sizeof(::std::size_t) * 3); +#endif + +namespace rust { +namespace cxxqtlib1 { + +QImage +qimageInitFromData(const rust::Slice data, rust::Str format) +{ + std::string formatString(format); + return QImage::fromData(static_cast(data.data()), + static_cast(data.size()), + formatString.empty() ? nullptr : formatString.data()); +} + +} +} + +#endif diff --git a/crates/cxx-qt-lib/src/gui/qimage.rs b/crates/cxx-qt-lib/src/gui/qimage.rs new file mode 100644 index 000000000..dc1360389 --- /dev/null +++ b/crates/cxx-qt-lib/src/gui/qimage.rs @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Leon Matthes +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use cxx::{type_id, ExternType}; +use std::mem::MaybeUninit; + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("cxx-qt-lib/qimage.h"); + type QImage = super::QImage; + + /// Whether the QImage is null. + /// + /// This means that the QImage has all parameters set to zero and no allocated data. + #[rust_name = "is_null"] + fn isNull(self: &QImage) -> bool; + } + + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + include!("cxx-qt-lib/common.h"); + + #[doc(hidden)] + #[rust_name = "qimage_drop"] + fn drop(image: &mut QImage); + + #[doc(hidden)] + #[rust_name = "qimage_init_from_data"] + fn qimageInitFromData(data: &[u8], format: &str) -> QImage; + } +} + +/// > ⚠ **Warning**: The QImage API in CXX-Qt-lib is not yet complete and subject to change. +/// +/// This struct is the Rust representation of the [`QImage`](https://doc.qt.io/qt-6/qimage.html) +/// class. +/// +/// It provides a way to store and manipulate images in a hardware-independent manner. +#[repr(C)] +pub struct QImage { + // Static checks on the C++ side ensure this is true. + // See qcolor.cpp + #[cfg(qt_version_major = "5")] + _data: MaybeUninit<[usize; 4]>, + #[cfg(qt_version_major = "6")] + _data: MaybeUninit<[usize; 3]>, +} + +// Safety: +// +// Static checks on the C++ side to ensure the size & alignment is the same. +unsafe impl ExternType for QImage { + type Id = type_id!("QImage"); + type Kind = cxx::kind::Trivial; +} + +impl Drop for QImage { + fn drop(&mut self) { + ffi::qimage_drop(self); + } +} + +impl QImage { + /// Convert raw image data to a [`QImage`]. + /// + /// The data must be in the given `format`. + /// See [`QImageReader::supportedImageFormats()`](https://doc.qt.io/qt-6/qimagereader.html#supportedImageFormats) for the list of supported formats. + /// + /// If no `format` is provided, the format will be quessed from the image header. + pub fn from_data(data: &[u8], format: Option<&str>) -> Option { + let image = ffi::qimage_init_from_data(data, format.unwrap_or("")); + + if !image.is_null() { + Some(image) + } else { + None + } + } +}