https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
| 144 | |
| 145 | // https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute |
| 146 | Matrix SVGNode::ComputeViewboxMatrix(const Rect& viewBox, const Rect& viewPort, |
| 147 | SVGPreserveAspectRatio PreAspectRatio) { |
| 148 | if (viewBox.isEmpty() || viewPort.isEmpty()) { |
| 149 | return Matrix::MakeScale(0, 0); |
| 150 | } |
| 151 | |
| 152 | auto computeScale = [&]() -> Point { |
| 153 | const auto scaleX = viewPort.width() / viewBox.width(); |
| 154 | const auto scaleY = viewPort.height() / viewBox.height(); |
| 155 | |
| 156 | if (PreAspectRatio.align == SVGPreserveAspectRatio::Align::None) { |
| 157 | // none -> anisotropic scaling, regardless of fScale |
| 158 | return {scaleX, scaleY}; |
| 159 | } |
| 160 | |
| 161 | // isotropic scaling |
| 162 | const auto s = PreAspectRatio.scale == SVGPreserveAspectRatio::Scale::Meet |
| 163 | ? std::min(scaleX, scaleY) |
| 164 | : std::max(scaleX, scaleY); |
| 165 | return {s, s}; |
| 166 | }; |
| 167 | |
| 168 | auto computeTrans = [&](const Point& scale) -> Point { |
| 169 | static constexpr float alignCoeffs[] = { |
| 170 | 0.0f, // Min |
| 171 | 0.5f, // Mid |
| 172 | 1.0f // Max |
| 173 | }; |
| 174 | |
| 175 | const size_t x_coeff = static_cast<int>(PreAspectRatio.align) >> 0 & 0x03; |
| 176 | const size_t y_coeff = static_cast<int>(PreAspectRatio.align) >> 2 & 0x03; |
| 177 | |
| 178 | const auto tx = -viewBox.x() * scale.x; |
| 179 | const auto ty = -viewBox.y() * scale.y; |
| 180 | const auto dx = viewPort.width() - viewBox.width() * scale.x; |
| 181 | const auto dy = viewPort.height() - viewBox.height() * scale.y; |
| 182 | |
| 183 | return {tx + dx * alignCoeffs[x_coeff], ty + dy * alignCoeffs[y_coeff]}; |
| 184 | }; |
| 185 | |
| 186 | auto scale = computeScale(); |
| 187 | auto transform = computeTrans(scale); |
| 188 | |
| 189 | return Matrix::MakeTrans(transform.x, transform.y) * Matrix::MakeScale(scale.x, scale.y); |
| 190 | } |
| 191 | } // namespace tgfx |