Ответ 1
Вам нужно рассчитать масштаб, основанный на повороте углов относительно центра.
Если изображение представляет собой квадрат, нужен только один угол, но для прямоугольника вам нужно проверить 2 угла, чтобы увидеть, перекрывается ли вертикальный или горизонтальный край. Эта проверка представляет собой линейное сравнение того, насколько превышены высота и ширина прямоугольника.
Нажмите здесь для рабочего тестового приложения, созданного для этого ответа (изображение ниже):
double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
Псевдокод выглядит следующим образом (фактический код С# в конце):
- Преобразование угла поворота в радианы
- Рассчитать "радиус" от центра прямоугольника до угла
- Преобразование позиции углов BR в полярные координаты
- Преобразование позиции углов BL в полярные координаты
- Примените поворот к обеим полярным координатам
- Преобразуйте новые позиции обратно в декартовы координаты (значение ABS)
- Найти наибольшее из двух горизонтальных положений
- Найдите наибольшую из двух вертикальных позиций
- Рассчитать изменение треугольника для горизонтального размера
- Рассчитать изменение дельты для вертикального размера
- Ширина возврата /2/x, если горизонтальное изменение больше
- Высота возврата /2/y, если вертикальное изменение больше
Результат - это множитель, который будет масштабировать изображение до подходящего исходного прямоугольника независимо от его вращения.
** Примечание. Хотя многие математические операции можно выполнять с использованием матричных операций, для этого недостаточно расчетов. Я также подумал, что это станет лучшим примером из первых принципов. *
Код С#:
/// <summary>
/// Calculate the scaling required to fit a rectangle into a rotation of that same rectangle
/// </summary>
/// <param name="rotation">Rotation in degrees</param>
/// <param name="pixelWidth">Width in pixels</param>
/// <param name="pixelHeight">Height in pixels</param>
/// <returns>A scaling value between 1 and 0</returns>
/// <remarks>Released to the public domain 2011 - David Johnston (HiTech Magic Ltd)</remarks>
private double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
{
// Convert angle to radians for the math lib
double rotationRadians = rotation * PiDiv180;
// Centre is half the width and height
double width = pixelWidth / 2.0;
double height = pixelHeight / 2.0;
double radius = Math.Sqrt(width * width + height * height);
// Convert BR corner into polar coordinates
double angle = Math.Atan(height / width);
// Now create the matching BL corner in polar coordinates
double angle2 = Math.Atan(height / -width);
// Apply the rotation to the points
angle += rotationRadians;
angle2 += rotationRadians;
// Convert back to rectangular coordinate
double x = Math.Abs(radius * Math.Cos(angle));
double y = Math.Abs(radius * Math.Sin(angle));
double x2 = Math.Abs(radius * Math.Cos(angle2));
double y2 = Math.Abs(radius * Math.Sin(angle2));
// Find the largest extents in X & Y
x = Math.Max(x, x2);
y = Math.Max(y, y2);
// Find the largest change (pixel, not ratio)
double deltaX = x - width;
double deltaY = y - height;
// Return the ratio that will bring the largest change into the region
return (deltaX > deltaY) ? width / x : height / y;
}
Пример использования:
private WriteableBitmap GenerateConstrainedBitmap(BitmapImage sourceImage, int pixelWidth, int pixelHeight, double rotation)
{
double scale = CalculateConstraintScale(rotation, pixelWidth, pixelHeight);
// Create a transform to render the image rotated and scaled
var transform = new TransformGroup();
var rt = new RotateTransform()
{
Angle = rotation,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(rt);
var st = new ScaleTransform()
{
ScaleX = scale,
ScaleY = scale,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(st);
// Resize to specified target size
var tempImage = new Image()
{
Stretch = Stretch.Fill,
Width = pixelWidth,
Height = pixelHeight,
Source = sourceImage,
};
tempImage.UpdateLayout();
// Render to a writeable bitmap
var writeableBitmap = new WriteableBitmap(pixelWidth, pixelHeight);
writeableBitmap.Render(tempImage, transform);
writeableBitmap.Invalidate();
return writeableBitmap;
}