use super::scalar_;
use crate::{prelude::*, scalar, Point, Point3, RSXform, Rect, Scalar, Size, Vector};
use skia_bindings::{self as sb, SkMatrix};
use std::{
ops::{Index, IndexMut, Mul},
slice,
};
pub use skia_bindings::SkApplyPerspectiveClip as ApplyPerspectiveClip;
variant_name!(ApplyPerspectiveClip::Yes);
bitflags! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TypeMask: u32 {
const IDENTITY = sb::SkMatrix_TypeMask_kIdentity_Mask as _;
const TRANSLATE = sb::SkMatrix_TypeMask_kTranslate_Mask as _;
const SCALE = sb::SkMatrix_TypeMask_kScale_Mask as _;
const AFFINE = sb::SkMatrix_TypeMask_kAffine_Mask as _;
const PERSPECTIVE = sb::SkMatrix_TypeMask_kPerspective_Mask as _;
}
}
impl TypeMask {
const UNKNOWN: u32 = sb::SkMatrix_kUnknown_Mask as _;
}
pub use skia_bindings::SkMatrix_ScaleToFit as ScaleToFit;
variant_name!(ScaleToFit::Fill);
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Matrix {
mat: [scalar; 9usize],
type_mask: u32,
}
native_transmutable!(SkMatrix, Matrix, matrix_layout);
impl PartialEq for Matrix {
fn eq(&self, rhs: &Self) -> bool {
unsafe { sb::C_SkMatrix_Equals(self.native(), rhs.native()) }
}
}
impl Mul for Matrix {
type Output = Self;
fn mul(self, rhs: Matrix) -> Self::Output {
Matrix::concat(&self, &rhs)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Member {
ScaleX = 0,
SkewX = 1,
TransX = 2,
SkewY = 3,
ScaleY = 4,
TransY = 5,
Persp0 = 6,
Persp1 = 7,
Persp2 = 8,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum AffineMember {
ScaleX = 0,
SkewY = 1,
SkewX = 2,
ScaleY = 3,
TransX = 4,
TransY = 5,
}
impl Index<Member> for Matrix {
type Output = scalar;
fn index(&self, index: Member) -> &Self::Output {
&self[index as usize]
}
}
impl Index<AffineMember> for Matrix {
type Output = scalar;
fn index(&self, index: AffineMember) -> &Self::Output {
&self[index as usize]
}
}
impl Index<usize> for Matrix {
type Output = scalar;
fn index(&self, index: usize) -> &Self::Output {
&self.native().fMat[index]
}
}
impl IndexMut<Member> for Matrix {
fn index_mut(&mut self, index: Member) -> &mut Self::Output {
self.index_mut(index as usize)
}
}
impl IndexMut<AffineMember> for Matrix {
fn index_mut(&mut self, index: AffineMember) -> &mut Self::Output {
self.index_mut(index as usize)
}
}
impl IndexMut<usize> for Matrix {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
unsafe { &mut *sb::C_SkMatrix_SubscriptMut(self.native_mut(), index) }
}
}
impl Default for Matrix {
fn default() -> Self {
Matrix::new()
}
}
impl Matrix {
const fn new() -> Self {
Self {
mat: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
type_mask: TypeMask::IDENTITY.bits() | 0x10,
}
}
#[deprecated(since = "0.33.0", note = "use Matrix::scale()")]
pub fn new_scale(scale: (scalar, scalar)) -> Self {
Self::scale(scale)
}
#[must_use]
pub fn scale((sx, sy): (scalar, scalar)) -> Self {
let mut m = Self::new();
m.set_scale((sx, sy), None);
m
}
#[deprecated(since = "0.33.0", note = "use Matrix::translate()")]
pub fn new_trans(d: impl Into<Vector>) -> Self {
Self::translate(d)
}
#[must_use]
pub fn translate(d: impl Into<Vector>) -> Self {
let mut m = Self::new();
m.set_translate(d);
m
}
#[must_use]
pub fn rotate_deg(deg: scalar) -> Self {
let mut m = Self::new();
m.set_rotate(deg, None);
m
}
#[must_use]
pub fn rotate_deg_pivot(deg: scalar, pivot: impl Into<Point>) -> Self {
let mut m = Self::new();
m.set_rotate(deg, pivot.into());
m
}
#[must_use]
pub fn rotate_rad(rad: scalar) -> Self {
Self::rotate_deg(scalar_::radians_to_degrees(rad))
}
#[must_use]
pub fn skew((kx, ky): (scalar, scalar)) -> Self {
let mut m = Self::new();
m.set_skew((kx, ky), None);
m
}
#[must_use]
pub fn rect_to_rect(
src: impl AsRef<Rect>,
dst: impl AsRef<Rect>,
scale_to_fit: impl Into<Option<ScaleToFit>>,
) -> Option<Self> {
Self::from_rect_to_rect(src, dst, scale_to_fit.into().unwrap_or(ScaleToFit::Fill))
}
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn new_all(
scale_x: scalar,
skew_x: scalar,
trans_x: scalar,
skew_y: scalar,
scale_y: scalar,
trans_y: scalar,
pers_0: scalar,
pers_1: scalar,
pers_2: scalar,
) -> Self {
let mut m = Self::new();
m.set_all(
scale_x, skew_x, trans_x, skew_y, scale_y, trans_y, pers_0, pers_1, pers_2,
);
m
}
pub fn get_type(&self) -> TypeMask {
TypeMask::from_bits_truncate(unsafe { sb::C_SkMatrix_getType(self.native()) } as _)
}
pub fn is_identity(&self) -> bool {
self.get_type() == TypeMask::IDENTITY
}
pub fn is_scale_translate(&self) -> bool {
(self.get_type() & !(TypeMask::SCALE | TypeMask::TRANSLATE)).is_empty()
}
pub fn is_translate(&self) -> bool {
(self.get_type() & !TypeMask::TRANSLATE).is_empty()
}
pub fn rect_stays_rect(&self) -> bool {
unsafe { sb::C_SkMatrix_rectStaysRect(self.native()) }
}
pub fn preserves_axis_alignment(&self) -> bool {
self.rect_stays_rect()
}
pub fn has_perspective(&self) -> bool {
unsafe { sb::C_SkMatrix_hasPerspective(self.native()) }
}
pub fn is_similarity(&self) -> bool {
unsafe { self.native().isSimilarity(scalar::NEARLY_ZERO) }
}
pub fn preserves_right_angles(&self) -> bool {
unsafe { self.native().preservesRightAngles(scalar::NEARLY_ZERO) }
}
pub fn rc(&self, r: usize, c: usize) -> scalar {
assert!(r <= 2);
assert!(c <= 2);
self[r * 3 + c]
}
pub fn scale_x(&self) -> scalar {
self[Member::ScaleX]
}
pub fn scale_y(&self) -> scalar {
self[Member::ScaleY]
}
pub fn skew_y(&self) -> scalar {
self[Member::SkewY]
}
pub fn skew_x(&self) -> scalar {
self[Member::SkewX]
}
pub fn translate_x(&self) -> scalar {
self[Member::TransX]
}
pub fn translate_y(&self) -> scalar {
self[Member::TransY]
}
pub fn persp_x(&self) -> scalar {
self[Member::Persp0]
}
pub fn persp_y(&self) -> scalar {
self[Member::Persp1]
}
pub fn set_scale_x(&mut self, v: scalar) -> &mut Self {
self.set(Member::ScaleX, v)
}
pub fn set_scale_y(&mut self, v: scalar) -> &mut Self {
self.set(Member::ScaleY, v)
}
pub fn set_skew_y(&mut self, v: scalar) -> &mut Self {
self.set(Member::SkewY, v)
}
pub fn set_skew_x(&mut self, v: scalar) -> &mut Self {
self.set(Member::SkewX, v)
}
pub fn set_translate_x(&mut self, v: scalar) -> &mut Self {
self.set(Member::TransX, v)
}
pub fn set_translate_y(&mut self, v: scalar) -> &mut Self {
self.set(Member::TransY, v)
}
pub fn set_persp_x(&mut self, v: scalar) -> &mut Self {
self.set(Member::Persp0, v)
}
pub fn set_persp_y(&mut self, v: scalar) -> &mut Self {
self.set(Member::Persp1, v)
}
#[allow(clippy::too_many_arguments)]
pub fn set_all(
&mut self,
scale_x: scalar,
skew_x: scalar,
trans_x: scalar,
skew_y: scalar,
scale_y: scalar,
trans_y: scalar,
persp_0: scalar,
persp_1: scalar,
persp_2: scalar,
) -> &mut Self {
self[Member::ScaleX] = scale_x;
self[Member::SkewX] = skew_x;
self[Member::TransX] = trans_x;
self[Member::SkewY] = skew_y;
self[Member::ScaleY] = scale_y;
self[Member::TransY] = trans_y;
self[Member::Persp0] = persp_0;
self[Member::Persp1] = persp_1;
self[Member::Persp2] = persp_2;
self.type_mask = TypeMask::UNKNOWN;
self
}
pub fn get_9(&self, buffer: &mut [scalar; 9]) {
buffer.copy_from_slice(&self.mat)
}
pub fn set_9(&mut self, buffer: &[scalar; 9]) -> &mut Self {
unsafe {
self.native_mut().set9(buffer.as_ptr());
}
self
}
pub fn reset(&mut self) -> &mut Self {
unsafe {
self.native_mut().reset();
}
self
}
pub fn set_identity(&mut self) -> &mut Self {
self.reset();
self
}
pub fn set_translate(&mut self, v: impl Into<Vector>) -> &mut Self {
let v = v.into();
unsafe {
self.native_mut().setTranslate(v.x, v.y);
}
self
}
pub fn set_scale(
&mut self,
(sx, sy): (scalar, scalar),
pivot: impl Into<Option<Point>>,
) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut().setScale(sx, sy, pivot.x, pivot.y);
}
self
}
pub fn set_rotate(&mut self, degrees: scalar, pivot: impl Into<Option<Point>>) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut().setRotate(degrees, pivot.x, pivot.y);
}
self
}
pub fn set_sin_cos(
&mut self,
(sin_value, cos_value): (scalar, scalar),
pivot: impl Into<Option<Point>>,
) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut()
.setSinCos(sin_value, cos_value, pivot.x, pivot.y);
}
self
}
pub fn set_rsxform(&mut self, rsxform: &RSXform) -> &mut Self {
unsafe {
self.native_mut().setRSXform(rsxform.native());
}
self
}
pub fn set_skew(
&mut self,
(kx, ky): (scalar, scalar),
pivot: impl Into<Option<Point>>,
) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut().setSkew(kx, ky, pivot.x, pivot.y);
}
self
}
pub fn set_concat(&mut self, a: &Self, b: &Self) -> &mut Self {
unsafe {
self.native_mut().setConcat(a.native(), b.native());
}
self
}
pub fn pre_translate(&mut self, delta: impl Into<Vector>) -> &mut Self {
let delta = delta.into();
unsafe {
self.native_mut().preTranslate(delta.x, delta.y);
}
self
}
pub fn pre_scale(
&mut self,
(sx, sy): (scalar, scalar),
pivot: impl Into<Option<Point>>,
) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut().preScale(sx, sy, pivot.x, pivot.y);
}
self
}
pub fn pre_rotate(&mut self, degrees: scalar, pivot: impl Into<Option<Point>>) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut().preRotate(degrees, pivot.x, pivot.y);
}
self
}
pub fn pre_skew(
&mut self,
(kx, ky): (scalar, scalar),
pivot: impl Into<Option<Point>>,
) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut().preSkew(kx, ky, pivot.x, pivot.y);
}
self
}
pub fn pre_concat(&mut self, other: &Self) -> &mut Self {
unsafe {
self.native_mut().preConcat(other.native());
}
self
}
pub fn post_translate(&mut self, delta: impl Into<Vector>) -> &mut Self {
let delta = delta.into();
unsafe {
self.native_mut().postTranslate(delta.x, delta.y);
}
self
}
pub fn post_scale(
&mut self,
(sx, sy): (scalar, scalar),
pivot: impl Into<Option<Point>>,
) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut().postScale(sx, sy, pivot.x, pivot.y);
}
self
}
#[deprecated(
since = "0.27.0",
note = "use post_scale((1.0 / x as scalar, 1.0 / y as scalar), None)"
)]
pub fn post_idiv(&mut self, (div_x, div_y): (i32, i32)) -> bool {
if div_x == 0 || div_y == 0 {
return false;
}
self.post_scale((1.0 / div_x as scalar, 1.0 / div_y as scalar), None);
true
}
pub fn post_rotate(&mut self, degrees: scalar, pivot: impl Into<Option<Point>>) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut().postRotate(degrees, pivot.x, pivot.y);
}
self
}
pub fn post_skew(
&mut self,
(kx, ky): (scalar, scalar),
pivot: impl Into<Option<Point>>,
) -> &mut Self {
let pivot = pivot.into().unwrap_or_default();
unsafe {
self.native_mut().postSkew(kx, ky, pivot.x, pivot.y);
}
self
}
pub fn post_concat(&mut self, other: &Matrix) -> &mut Self {
unsafe {
self.native_mut().postConcat(other.native());
}
self
}
pub fn set_rect_to_rect(
&mut self,
src: impl AsRef<Rect>,
dst: impl AsRef<Rect>,
stf: ScaleToFit,
) -> bool {
unsafe {
self.native_mut()
.setRectToRect(src.as_ref().native(), dst.as_ref().native(), stf)
}
}
pub fn from_rect_to_rect(
src: impl AsRef<Rect>,
dst: impl AsRef<Rect>,
stf: ScaleToFit,
) -> Option<Self> {
let mut m = Self::new_identity();
m.set_rect_to_rect(src, dst, stf).if_true_some(m)
}
pub fn set_poly_to_poly(&mut self, src: &[Point], dst: &[Point]) -> bool {
if src.len() != dst.len() {
return false;
}
unsafe {
self.native_mut().setPolyToPoly(
src.native().as_ptr(),
dst.native().as_ptr(),
src.len().try_into().unwrap(),
)
}
}
pub fn from_poly_to_poly(src: &[Point], dst: &[Point]) -> Option<Matrix> {
let mut m = Matrix::new_identity();
m.set_poly_to_poly(src, dst).if_true_some(m)
}
#[must_use]
pub fn invert(&self) -> Option<Matrix> {
let mut m = Matrix::new_identity();
unsafe { sb::C_SkMatrix_invert(self.native(), m.native_mut()) }.if_true_some(m)
}
pub fn set_affine_identity(affine: &mut [scalar; 6]) {
unsafe { SkMatrix::SetAffineIdentity(affine.as_mut_ptr()) }
}
#[must_use]
pub fn to_affine(self) -> Option<[scalar; 6]> {
let mut affine = [scalar::default(); 6];
unsafe { self.native().asAffine(affine.as_mut_ptr()) }.if_true_some(affine)
}
pub fn set_affine(&mut self, affine: &[scalar; 6]) -> &mut Self {
unsafe { self.native_mut().setAffine(affine.as_ptr()) };
self
}
pub fn from_affine(affine: &[scalar; 6]) -> Matrix {
let mut m = Matrix::new_identity();
unsafe {
m.native_mut().setAffine(affine.as_ptr());
}
m
}
pub fn normalize_perspective(&mut self) {
unsafe { sb::C_SkMatrix_normalizePerspective(self.native_mut()) }
}
pub fn map_points(&self, dst: &mut [Point], src: &[Point]) {
assert!(dst.len() >= src.len());
unsafe {
self.native().mapPoints(
dst.native_mut().as_mut_ptr(),
src.native().as_ptr(),
src.len().try_into().unwrap(),
)
};
}
pub fn map_points_inplace(&self, pts: &mut [Point]) {
let ptr = pts.native_mut().as_mut_ptr();
unsafe {
self.native()
.mapPoints(ptr, ptr, pts.len().try_into().unwrap())
};
}
pub fn map_homogeneous_points(&self, dst: &mut [Point3], src: &[Point3]) {
assert!(dst.len() >= src.len());
unsafe {
self.native().mapHomogeneousPoints(
dst.native_mut().as_mut_ptr(),
src.native().as_ptr(),
src.len().try_into().unwrap(),
)
};
}
pub fn map_homogeneous_points_2d(&self, dst: &mut [Point3], src: &[Point]) {
assert!(dst.len() >= src.len());
unsafe {
self.native().mapHomogeneousPoints1(
dst.native_mut().as_mut_ptr(),
src.native().as_ptr(),
src.len().try_into().unwrap(),
)
};
}
pub fn map_point(&self, point: impl Into<Point>) -> Point {
let point = point.into();
let mut p = Point::default();
unsafe { self.native().mapXY(point.x, point.y, p.native_mut()) };
p
}
pub fn map_xy(&self, x: scalar, y: scalar) -> Point {
self.map_point((x, y))
}
pub fn map_origin(&self) -> Point {
let mut x = self.translate_x();
let mut y = self.translate_y();
if self.has_perspective() {
let mut w = self[Member::Persp2];
if w != 0.0 {
w = 1.0 / w;
}
x *= w;
y *= w;
}
Point::new(x, y)
}
pub fn map_vectors(&self, dst: &mut [Vector], src: &[Vector]) {
assert!(dst.len() >= src.len());
unsafe {
self.native().mapVectors(
dst.native_mut().as_mut_ptr(),
src.native().as_ptr(),
src.len().try_into().unwrap(),
)
}
}
pub fn map_vectors_inplace(&self, vecs: &mut [Vector]) {
let ptr = vecs.native_mut().as_mut_ptr();
unsafe {
self.native()
.mapVectors(ptr, ptr, vecs.len().try_into().unwrap())
}
}
pub fn map_vector(&self, vec: impl Into<Vector>) -> Vector {
let mut vec = vec.into();
self.map_vectors_inplace(slice::from_mut(&mut vec));
vec
}
pub fn map_rect(&self, rect: impl AsRef<Rect>) -> (Rect, bool) {
self.map_rect_with_perspective_clip(rect, ApplyPerspectiveClip::Yes)
}
pub fn map_rect_with_perspective_clip(
&self,
rect: impl AsRef<Rect>,
perspective_clip: ApplyPerspectiveClip,
) -> (Rect, bool) {
let mut rect = *rect.as_ref();
let ptr = rect.native_mut();
let rect_stays_rect = unsafe { self.native().mapRect(ptr, ptr, perspective_clip) };
(rect, rect_stays_rect)
}
pub fn map_rect_to_quad(&self, rect: impl AsRef<Rect>) -> [Point; 4] {
let mut quad = rect.as_ref().to_quad();
self.map_points_inplace(quad.as_mut());
quad
}
pub fn map_rect_scale_translate(&self, src: impl AsRef<Rect>) -> Option<Rect> {
if self.is_scale_translate() {
let mut rect = Rect::default();
unsafe {
self.native()
.mapRectScaleTranslate(rect.native_mut(), src.as_ref().native())
};
Some(rect)
} else {
None
}
}
pub fn map_radius(&self, radius: scalar) -> Option<scalar> {
if !self.has_perspective() {
Some(unsafe { self.native().mapRadius(radius) })
} else {
None
}
}
#[deprecated(since = "0.27.0", note = "removed without replacement")]
pub fn is_fixed_step_in_x(&self) -> ! {
unimplemented!("removed without replacement")
}
#[deprecated(since = "0.27.0", note = "removed without replacement")]
pub fn fixed_step_in_x(&self, _y: scalar) -> ! {
unimplemented!("removed without replacement")
}
#[deprecated(since = "0.27.0", note = "removed without replacement")]
pub fn cheap_equal_to(&self, _other: &Matrix) -> ! {
unimplemented!("removed without replacement")
}
pub fn dump(&self) {
unsafe { self.native().dump() }
}
pub fn min_scale(&self) -> scalar {
unsafe { self.native().getMinScale() }
}
pub fn max_scale(&self) -> scalar {
unsafe { self.native().getMaxScale() }
}
#[must_use]
pub fn min_max_scales(&self) -> (scalar, scalar) {
let mut r: [scalar; 2] = Default::default();
unsafe { self.native().getMinMaxScales(r.as_mut_ptr()) };
#[allow(clippy::tuple_array_conversions)]
(r[0], r[1])
}
pub fn decompose_scale(&self, mut remaining: Option<&mut Matrix>) -> Option<Size> {
let mut size = Size::default();
unsafe {
self.native()
.decomposeScale(size.native_mut(), remaining.native_ptr_or_null_mut())
}
.if_true_some(size)
}
pub fn i() -> &'static Matrix {
&IDENTITY
}
pub fn invalid_matrix() -> &'static Matrix {
Self::from_native_ref(unsafe { &*sb::C_SkMatrix_InvalidMatrix() })
}
pub fn concat(a: &Matrix, b: &Matrix) -> Matrix {
let mut m = Matrix::new_identity();
unsafe { m.native_mut().setConcat(a.native(), b.native()) };
m
}
pub fn dirty_matrix_type_cache(&mut self) {
self.native_mut().fTypeMask = 0x80;
}
pub fn set_scale_translate(
&mut self,
(sx, sy): (scalar, scalar),
t: impl Into<Vector>,
) -> &mut Self {
let t = t.into();
unsafe { sb::C_SkMatrix_setScaleTranslate(self.native_mut(), sx, sy, t.x, t.y) }
self
}
pub fn is_finite(&self) -> bool {
unsafe { sb::C_SkMatrix_isFinite(self.native()) }
}
pub const fn new_identity() -> Self {
Self::new()
}
}
impl IndexGet for Matrix {}
impl IndexSet for Matrix {}
pub const IDENTITY: Matrix = Matrix::new_identity();
#[cfg(test)]
mod tests {
use super::{AffineMember, Matrix, TypeMask};
use crate::prelude::*;
#[test]
fn test_get_set_trait_compilation() {
let mut m = Matrix::new_identity();
let _x = m.get(AffineMember::ScaleX);
m.set(AffineMember::ScaleX, 1.0);
}
#[test]
#[allow(clippy::float_cmp)]
fn test_tuple_to_vector() {
let mut m = Matrix::new_identity();
m.set_translate((10.0, 11.0));
assert_eq!(10.0, m.translate_x());
assert_eq!(11.0, m.translate_y());
}
#[test]
fn setting_a_matrix_component_recomputes_typemask() {
let mut m = Matrix::default();
assert_eq!(TypeMask::IDENTITY, m.get_type());
m.set_persp_x(0.1);
assert_eq!(
TypeMask::TRANSLATE | TypeMask::SCALE | TypeMask::AFFINE | TypeMask::PERSPECTIVE,
m.get_type()
);
}
}