Skip to content

Improve QImage support #746

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased](https://github.com/KDAB/cxx-qt/compare/v0.6.0...HEAD)

### Added

- Support for further types: `QLine`, `QLineF`, `QImage`

## [0.6.0](https://github.com/KDAB/cxx-qt/compare/v0.5.3...v0.6.0) - 2023-11-17

### Added
Expand Down
3 changes: 3 additions & 0 deletions crates/cxx-qt-lib-headers/include/gui/qimage.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ QImage
qimageInitFromData(const rust::Slice<std::uint8_t const> data,
rust::Str format);

::std::int64_t
qimageCacheKey(const QImage& image);

} // namespace cxxqtlib1
} // namespace rust
#endif
1 change: 1 addition & 0 deletions crates/cxx-qt-lib/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub use qstringlist::QStringList;
mod qt;
pub use qt::{
AspectRatioMode, CaseSensitivity, ConnectionType, DateFormat, SplitBehaviorFlags, TimeSpec,
TransformationMode,
};

mod qtime;
Expand Down
11 changes: 11 additions & 0 deletions crates/cxx-qt-lib/src/core/qt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ mod ffi {
TimeZone,
}

/// This enum type defines whether image transformations (e.g., scaling) should be smooth or not.
#[repr(i32)]
enum TransformationMode {
/// The transformation is performed quickly, with no smoothing.
FastTransformation,
/// The resulting image is transformed using bilinear filtering.
SmoothTransformation,
}

unsafe extern "C++" {
include!("cxx-qt-lib/qt.h");
type AspectRatioMode;
Expand All @@ -75,9 +84,11 @@ mod ffi {
type DateFormat;
type SplitBehaviorFlags;
type TimeSpec;
type TransformationMode;
}
}

pub use ffi::{
AspectRatioMode, CaseSensitivity, ConnectionType, DateFormat, SplitBehaviorFlags, TimeSpec,
TransformationMode,
};
5 changes: 5 additions & 0 deletions crates/cxx-qt-lib/src/gui/qimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ qimageInitFromData(const rust::Slice<std::uint8_t const> data, rust::Str format)
formatString.empty() ? nullptr : formatString.data());
}

::std::int64_t
qimageCacheKey(const QImage& image)
{
return static_cast<::std::int64_t>(image.cacheKey());
}
}
}

Expand Down
235 changes: 233 additions & 2 deletions crates/cxx-qt-lib/src/gui/qimage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,189 @@ use std::mem::MaybeUninit;

#[cxx::bridge]
mod ffi {
#[namespace = "Qt"]
unsafe extern "C++" {
include!("cxx-qt-lib/qt.h");
type TransformationMode = crate::TransformationMode;
type AspectRatioMode = crate::AspectRatioMode;
}

/// The type of image format available in Qt.
#[repr(i32)]
#[namespace = "rust::cxxqtlib1"]
#[derive(Debug)]
enum QImageFormat {
Format_Invalid,
Format_Mono,
Format_MonoLSB,
Format_Indexed8,
Format_RGB32,
Format_ARGB32,
Format_ARGB32_Premultiplied,
Format_RGB16,
Format_ARGB8565_Premultiplied,
Format_RGB666,
Format_ARGB6666_Premultiplied,
Format_RGB555,
Format_ARGB8555_Premultiplied,
Format_RGB888,
Format_RGB444,
Format_ARGB4444_Premultiplied,
Format_RGBX8888,
Format_RGBA8888,
Format_RGBA8888_Premultiplied,
Format_BGR30,
Format_A2BGR30_Premultiplied,
Format_RGB30,
Format_A2RGB30_Premultiplied,
Format_Alpha8,
Format_Grayscale8,
Format_RGBX64,
Format_RGBA64,
Format_RGBA64_Premultiplied,
Format_Grayscale16,
Format_BGR888,
/* Qt 6.2
Format_RGBX16FPx4,
Format_RGBA16FPx4,
Format_RGBA16FPx4_Premultiplied,
Format_RGBX32FPx4,
Format_RGBA32FPx4,
Format_RGBA32FPx4_Premultiplied,
*/
}

unsafe extern "C++" {
include!("cxx-qt-lib/qimage.h");
type QImage = super::QImage;
include!("cxx-qt-lib/qsize.h");
type QSize = crate::QSize;
include!("cxx-qt-lib/qrect.h");
type QRect = crate::QRect;
include!("cxx-qt-lib/qcolor.h");
type QColor = crate::QColor;
include!("cxx-qt-lib/qpoint.h");
type QPoint = crate::QPoint;

/// Returns true if all the colors in the image are shades of gray
#[rust_name = "all_gray"]
fn allGray(self: &QImage) -> bool;

/// Returns a sub-area of the image as a new image.
fn copy(self: &QImage, rect: &QRect) -> QImage;

/// Returns the size of the color table for the image.
#[rust_name = "color_count"]
fn colorCount(self: &QImage) -> i32;

/// Returns the depth of the image.
fn depth(self: &QImage) -> i32;

/// Returns the number of pixels that fit horizontally in a physical meter.
#[rust_name = "dots_per_meter_x"]
fn dotsPerMeterX(self: &QImage) -> i32;

/// Returns the number of pixels that fit vertically in a physical meter.
#[rust_name = "dots_per_meter_y"]
fn dotsPerMeterY(self: &QImage) -> i32;

/// Fills the entire image with the given color.
fn fill(self: &mut QImage, color: &QColor);

/// Returns the format of the image.
fn format(self: &QImage) -> QImageFormat;

/// 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;

/// For 32-bit images, this function is equivalent to allGray().
/// For color indexed images, this function returns true if color(i) is QRgb(i, i, i)
/// for all indexes of the color table; otherwise returns false.
#[rust_name = "is_gray_scale"]
fn isGrayscale(self: &QImage) -> bool;

/// Returns true if the image has a format that respects the alpha channel, otherwise returns false.
#[rust_name = "has_alpha_channel"]
fn hasAlphaChannel(self: &QImage) -> bool;

/// Returns the height of the image.
fn height(self: &QImage) -> i32;

/// Returns the enclosing rectangle (0, 0, width(), height()) of the image.
fn rect(self: &QImage) -> QRect;

/// Returns a copy of the image scaled to a rectangle with the given width and height according to the given aspectRatioMode and transformMode.
fn scaled(
self: &QImage,
width: i32,
height: i32,
aspectRatioMode: AspectRatioMode,
transformMode: TransformationMode,
) -> QImage;

/// Returns a scaled copy of the image. The returned image is scaled to the given height using the specified transformation mode.
#[rust_name = "scaled_to_height"]
fn scaledToHeight(self: &QImage, width: i32, mode: TransformationMode) -> QImage;

/// Returns a scaled copy of the image. The returned image is scaled to the given width using the specified transformation mode.
#[rust_name = "scaled_to_width"]
fn scaledToWidth(self: &QImage, width: i32, mode: TransformationMode) -> QImage;

/// Resizes the color table to contain colorCount entries.
#[rust_name = "set_color_count"]
fn setColorCount(self: &mut QImage, colorCount: i32);

/// Sets the alpha channel of this image to the given alphaChannel.
#[rust_name = "set_alpha_channel"]
fn setAlphaChannel(self: &mut QImage, alphaChannel: &QImage);

/// Sets the number of pixels by which the image is intended to be offset by when positioning relative to other images, to offset.
#[rust_name = "set_offset"]
fn setOffset(self: &mut QImage, point: &QPoint);

/// Sets the pixel color at (x, y) to color.
#[rust_name = "set_pixel_color"]
fn setPixelColor(self: &mut QImage, x: i32, y: i32, color: &QColor);

/// Returns the size of the image.
fn size(self: &QImage) -> QSize;

/// Swaps image other with this image. This operation is very fast and never fails.
fn swap(self: &mut QImage, other: &mut QImage);

/// Returns the number of pixels by which the image is intended to be offset by when positioning relative to other images.
fn offset(self: &QImage) -> QPoint;

/// Returns the color of the pixel at coordinates (x, y) as a QColor.
#[rust_name = "pixel_color"]
fn pixelColor(self: &QImage, x: i32, y: i32) -> QColor;

/// Returns the pixel index at (x, y).
#[rust_name = "pixel_index"]
fn pixelIndex(self: &QImage, x: i32, y: i32) -> i32;

/// Returns true if pos is a valid coordinate pair within the image.
fn valid(self: &QImage, x: i32, y: i32) -> bool;

/// Returns the width of the image.
fn width(self: &QImage) -> i32;
}

#[namespace = "rust::cxxqtlib1"]
unsafe extern "C++" {
include!("cxx-qt-lib/common.h");
type QImageFormat;

#[doc(hidden)]
#[rust_name = "qimage_init_default"]
fn construct() -> QImage;

#[doc(hidden)]
#[rust_name = "qimage_init_from_width_and_height_and_image_format"]
fn construct(width: i32, height: i32, format: QImageFormat) -> QImage;

#[doc(hidden)]
#[rust_name = "qimage_drop"]
Expand All @@ -30,11 +199,13 @@ mod ffi {
#[doc(hidden)]
#[rust_name = "qimage_init_from_data"]
fn qimageInitFromData(data: &[u8], format: &str) -> QImage;

#[doc(hidden)]
#[rust_name = "qimage_cache_key"]
fn qimageCacheKey(image: &QImage) -> i64;
}
}

/// > ⚠ **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.
///
Expand All @@ -49,6 +220,20 @@ pub struct QImage {
_data: MaybeUninit<[usize; 3]>,
}

impl Clone for QImage {
/// Constructs a copy of other.
fn clone(&self) -> Self {
self.copy(&self.rect())
}
}

impl Default for QImage {
/// Constructs a null image.
fn default() -> Self {
ffi::qimage_init_default()
}
}

// Safety:
//
// Static checks on the C++ side to ensure the size & alignment is the same.
Expand Down Expand Up @@ -79,4 +264,50 @@ impl QImage {
None
}
}
/// Returns a number that identifies the contents of this QImage object.
pub fn cache_key(&self) -> i64 {
ffi::qimage_cache_key(self)
}

/// Construct a Rust QImage from a given width, height, and QImage Format
pub fn from_height_width_and_format(
width: i32,
height: i32,
format: ffi::QImageFormat,
) -> Self {
ffi::qimage_init_from_width_and_height_and_image_format(width, height, format)
}
}

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_values() {
let default_image = QImage::default();
assert!(default_image.all_gray());
assert!(default_image.is_null());
assert_eq!(default_image.width(), 0);
assert_eq!(default_image.height(), 0);
assert_eq!(default_image.depth(), 0);
assert!(default_image.size().is_null());
}

#[test]
fn test_create_qimage_from_format() {
let qimage = QImage::from_height_width_and_format(50, 70, ffi::QImageFormat::Format_Mono);
assert_eq!(qimage.width(), 50);
assert_eq!(qimage.height(), 70);
assert!(!qimage.is_null());
assert_eq!(qimage.format(), ffi::QImageFormat::Format_Mono);
}

#[test]
fn test_copy() {
let qimage = QImage::from_height_width_and_format(50, 70, ffi::QImageFormat::Format_Mono);
let qimage2 = qimage.copy(&qimage.rect());
assert_eq!(qimage.width(), qimage2.width());
assert_eq!(qimage.height(), qimage2.height());
assert_eq!(qimage.format(), qimage2.format());
}
}