1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Some useful utilities.

use crate::{tree, geom::*};

// TODO: https://github.com/rust-lang/rust/issues/44095
/// Bounds `f64` number.
#[inline]
pub fn f64_bound(min: f64, val: f64, max: f64) -> f64 {
    debug_assert!(min.is_finite());
    debug_assert!(val.is_finite());
    debug_assert!(max.is_finite());

    if val > max {
        max
    } else if val < min {
        min
    } else {
        val
    }
}

/// Converts `viewBox` to `Transform`.
pub fn view_box_to_transform(
    view_box: Rect,
    aspect: tree::AspectRatio,
    img_size: Size,
) -> tree::Transform {
    let vr = view_box;

    let sx = img_size.width() / vr.width();
    let sy = img_size.height() / vr.height();

    let (sx, sy) = if aspect.align == tree::Align::None {
        (sx, sy)
    } else {
        let s = if aspect.slice {
            if sx < sy { sy } else { sx }
        } else {
            if sx > sy { sy } else { sx }
        };

        (s, s)
    };

    let x = -vr.x() * sx;
    let y = -vr.y() * sy;
    let w = img_size.width() - vr.width() * sx;
    let h = img_size.height() - vr.height() * sy;

    let (tx, ty) = aligned_pos(aspect.align, x, y, w, h);
    tree::Transform::new(sx, 0.0, 0.0, sy, tx, ty)
}

/// Converts `viewBox` to `Transform` with an optional clip rectangle.
///
/// Unlike `view_box_to_transform`, returns an optional clip rectangle
/// that should be applied before rendering the image.
pub fn view_box_to_transform_with_clip(
    view_box: &tree::ViewBox,
    img_size: ScreenSize,
) -> (tree::Transform, Option<Rect>) {
    let r = view_box.rect;

    let new_size = img_size.fit_view_box(&view_box);

    let (tx, ty, clip) = if view_box.aspect.slice {
        let (dx, dy) = aligned_pos(
            view_box.aspect.align,
            0.0, 0.0, new_size.width() as f64 - r.width(), new_size.height() as f64 - r.height(),
        );

        (r.x() - dx, r.y() - dy, Some(r))
    } else {
        let (dx, dy) = aligned_pos(
            view_box.aspect.align,
            r.x(), r.y(), r.width() - new_size.width() as f64, r.height() - new_size.height() as f64,
        );

        (dx, dy, None)
    };

    let sx = new_size.width() as f64 / img_size.width() as f64;
    let sy = new_size.height() as f64 / img_size.height() as f64;
    let ts = tree::Transform::new(sx, 0.0, 0.0, sy, tx, ty);

    (ts, clip)
}

/// Returns object aligned position.
pub fn aligned_pos(
    align: tree::Align,
    x: f64, y: f64, w: f64, h: f64,
) -> (f64, f64) {
    match align {
        tree::Align::None     => (x,           y          ),
        tree::Align::XMinYMin => (x,           y          ),
        tree::Align::XMidYMin => (x + w / 2.0, y          ),
        tree::Align::XMaxYMin => (x + w,       y          ),
        tree::Align::XMinYMid => (x,           y + h / 2.0),
        tree::Align::XMidYMid => (x + w / 2.0, y + h / 2.0),
        tree::Align::XMaxYMid => (x + w,       y + h / 2.0),
        tree::Align::XMinYMax => (x,           y + h      ),
        tree::Align::XMidYMax => (x + w / 2.0, y + h      ),
        tree::Align::XMaxYMax => (x + w,       y + h      ),
    }
}

pub(crate) fn file_extension(path: &std::path::Path) -> Option<&str> {
    if let Some(ext) = path.extension() {
        ext.to_str()
    } else {
        None
    }
}