use crate::{PdfiumRotation, pdfium_types::FS_MATRIX};
#[derive(Debug, Copy, Clone)]
pub struct PdfiumMatrix {
a: f32,
b: f32,
c: f32,
d: f32,
e: f32,
f: f32,
}
impl PdfiumMatrix {
pub const fn new(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> Self {
Self { a, b, c, d, e, f }
}
pub const fn new_scale_pan(scale: f32, pan_x: f32, pan_y: f32) -> Self {
Self {
a: scale,
b: 0.0,
c: 0.0,
d: scale,
e: pan_x,
f: pan_y,
}
}
pub const fn new_scale(scale: f32) -> Self {
Self {
a: scale,
b: 0.0,
c: 0.0,
d: scale,
e: 0.0,
f: 0.0,
}
}
pub const fn new_pan(pan_x: f32, pan_y: f32) -> Self {
Self {
a: 1.0,
b: 0.0,
c: 0.0,
d: 1.0,
e: pan_x,
f: pan_y,
}
}
pub const fn new_scale_opt_pan(scale: f32, pan: Option<(f32, f32)>) -> Self {
if let Some((pan_x, pan_y)) = pan {
Self::new_scale_pan(scale, pan_x, pan_y)
} else {
Self::new_scale(scale)
}
}
pub const fn zero() -> Self {
Self {
a: 0.0,
b: 0.0,
c: 0.0,
d: 0.0,
e: 0.0,
f: 0.0,
}
}
pub const fn identity() -> Self {
Self {
a: 1.0,
b: 0.0,
c: 0.0,
d: 1.0,
e: 0.0,
f: 0.0,
}
}
pub const fn rotation(rotation: PdfiumRotation) -> Self {
match rotation {
PdfiumRotation::None => Self {
a: 1.0,
b: 0.0,
c: 0.0,
d: 1.0,
e: 0.0,
f: 0.0,
},
PdfiumRotation::Cw90 => Self {
a: 0.0,
b: -1.0,
c: 1.0,
d: 0.0,
e: 0.0,
f: 0.0,
},
PdfiumRotation::Cw180 => Self {
a: -1.0,
b: 0.0,
c: 0.0,
d: -1.0,
e: 0.0,
f: 0.0,
},
PdfiumRotation::Cw270 => Self {
a: 0.0,
b: 1.0,
c: -1.0,
d: 0.0,
e: 0.0,
f: 0.0,
},
}
}
pub fn rotation_degrees(degrees: f32) -> Self {
let theta = -degrees.to_radians();
let (cos_t, sin_t) = (theta.cos(), theta.sin());
Self {
a: cos_t,
b: sin_t,
c: -sin_t,
d: cos_t,
e: 0.0,
f: 0.0,
}
}
pub fn apply(&self, x: f32, y: f32) -> (f32, f32) {
(
self.a * x + self.c * y + self.e,
self.b * x + self.d * y + self.f,
)
}
}
impl From<&PdfiumMatrix> for FS_MATRIX {
fn from(matrix: &PdfiumMatrix) -> Self {
FS_MATRIX {
a: matrix.a,
b: matrix.b,
c: matrix.c,
d: matrix.d,
e: matrix.e,
f: matrix.f,
}
}
}
impl From<FS_MATRIX> for PdfiumMatrix {
#[inline]
fn from(matrix: FS_MATRIX) -> Self {
Self::new(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f)
}
}
impl std::ops::Mul for PdfiumMatrix {
type Output = PdfiumMatrix;
fn mul(self, rhs: Self) -> Self::Output {
Self {
a: self.a * rhs.a + self.c * rhs.b,
b: self.b * rhs.a + self.d * rhs.b,
c: self.a * rhs.c + self.c * rhs.d,
d: self.b * rhs.c + self.d * rhs.d,
e: self.a * rhs.e + self.c * rhs.f + self.e,
f: self.b * rhs.e + self.d * rhs.f + self.f,
}
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn test_matrix_scale_pan() {
let matrix = PdfiumMatrix::new_scale(2.0);
assert_eq!(matrix.apply(3.0, 4.0), (6.0, 8.0));
let matrix = PdfiumMatrix::new_scale_pan(3.0, 2.5, 2.8);
assert_eq!(matrix.apply(3.0, 4.0), (11.5, 14.8));
}
#[test]
fn test_matrix_rotate() {
let matrix = PdfiumMatrix::rotation(PdfiumRotation::None);
assert_eq!(matrix.apply(3.0, 4.0), (3.0, 4.0));
let matrix = PdfiumMatrix::rotation(PdfiumRotation::Cw90);
assert_eq!(matrix.apply(3.0, 4.0), (4.0, -3.0));
let matrix = PdfiumMatrix::rotation(PdfiumRotation::Cw180);
assert_eq!(matrix.apply(3.0, 4.0), (-3.0, -4.0));
let matrix = PdfiumMatrix::rotation(PdfiumRotation::Cw270);
assert_eq!(matrix.apply(3.0, 4.0), (-4.0, 3.0));
}
#[test]
fn test_matrix_mul() {
let matrix = PdfiumMatrix::new_scale(2.0) * PdfiumMatrix::rotation(PdfiumRotation::Cw90);
assert_eq!(matrix.apply(3.0, 4.0), (8.0, -6.0));
let matrix = PdfiumMatrix::rotation(PdfiumRotation::Cw90) * PdfiumMatrix::new_scale(2.0);
assert_eq!(matrix.apply(3.0, 4.0), (8.0, -6.0));
let matrix = PdfiumMatrix::rotation(PdfiumRotation::Cw180)
* PdfiumMatrix::new_scale_pan(3.0, 2.5, 2.8);
assert_eq!(matrix.apply(3.0, 4.0), (-11.5, -14.8));
let matrix = PdfiumMatrix::new_scale_pan(3.0, 2.5, 2.8)
* PdfiumMatrix::rotation(PdfiumRotation::Cw180);
assert_eq!(matrix.apply(3.0, 4.0), (-6.5, -9.2));
}
}