在 Silverlight 中旋转图像而不裁剪

发布于 2024-08-26 07:57:42 字数 846 浏览 4 评论 0原文

我目前正在开发一个简单的 Silverlight 应用程序,它允许人们上传图像、裁剪、调整大小和旋转图像,然后通过 Web 服务将其加载到 CMS。

裁剪和调整大小已经完成,但是旋转会引起一些问题。旋转后图像被裁剪并偏离中心。

WriteableBitmap wb = new WriteableBitmap(destWidth, destHeight);

RotateTransform rt = new RotateTransform();
rt.Angle = 90;
rt.CenterX = width/2;
rt.CenterY = height/2;

//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = width;
tempImage2.Height = height;
tempImage2.Source = rawImage;

wb.Render(tempImage2,rt);
wb.Invalidate();
rawImage = wb;

message.Text = "h:" + rawImage.PixelHeight.ToString();
message.Text += ":w:" + rawImage.PixelWidth.ToString();

//Finally set the Image back
MyImage.Source = wb;
MyImage.Width = destWidth;
MyImage.Height = destHeight;

上面的代码此时只需要旋转 90°,因此我只需将 destWidthdestHeight 设置为原始图像的高度和宽度。

I am currently working on a simple Silverlight app that will allow people to upload an image, crop, resize and rotate it and then load it via a webservice to a CMS.

Cropping and resizing is done, however rotation is causing some problems. The image gets cropped and is off centre after the rotation.

WriteableBitmap wb = new WriteableBitmap(destWidth, destHeight);

RotateTransform rt = new RotateTransform();
rt.Angle = 90;
rt.CenterX = width/2;
rt.CenterY = height/2;

//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = width;
tempImage2.Height = height;
tempImage2.Source = rawImage;

wb.Render(tempImage2,rt);
wb.Invalidate();
rawImage = wb;

message.Text = "h:" + rawImage.PixelHeight.ToString();
message.Text += ":w:" + rawImage.PixelWidth.ToString();

//Finally set the Image back
MyImage.Source = wb;
MyImage.Width = destWidth;
MyImage.Height = destHeight;

The code above only needs to rotate by 90° at this time so I'm just setting destWidth and destHeight to the height and width of the original image.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧城烟雨 2024-09-02 07:57:42

看起来您的目标图像与源图像的大小相同。如果你想旋转超过 90 度,你的宽度和高度应该交换:

WriteableBitmap wb = new WriteableBitmap(destHeight, destWidth);

此外,如果你绕原始图像的中心旋转,它的一部分最终会超出边界。您可以包含一些平移变换,或者只是围绕不同的点旋转图像:

rt.CenterX = rt.CenterY = Math.Min(width / 2, height / 2);

用一张矩形纸尝试一下,看看为什么这样做是有意义的。

It looks like your target image is the same size as your source image. If you want to rotate over 90 degrees, your width and height should be exchanged:

WriteableBitmap wb = new WriteableBitmap(destHeight, destWidth);

Also, if you rotate about the centre of the original image, part of it will end up outside the boundaries. You could either include some translation transforms, or simply rotate the image about a different point:

rt.CenterX = rt.CenterY = Math.Min(width / 2, height / 2);

Try it with a piece of rectangular paper to see why that makes sense.

木格 2024-09-02 07:57:42

非常感谢上面的人..他们帮助了很多。我在这里提供了一个简单的示例,其中包括将旋转图像移回到结果的左上角所需的附加变换。

        int width = currentImage.PixelWidth;
        int height = currentImage.PixelHeight;
        int full = Math.Max(width, height);

        Image tempImage2 = new Image();
        tempImage2.Width = full;
        tempImage2.Height = full;
        tempImage2.Source = currentImage;

        // New bitmap has swapped width/height
        WriteableBitmap wb1 = new WriteableBitmap(height,width);


        TransformGroup transformGroup = new TransformGroup();

        // Rotate around centre
        RotateTransform rotate = new RotateTransform();
        rotate.Angle = 90;
        rotate.CenterX = full/2;
        rotate.CenterY = full/2;
        transformGroup.Children.Add(rotate);

        // and transform back to top left corner of new image
        TranslateTransform translate = new TranslateTransform();
        translate.X = -(full - height) / 2;
        translate.Y = -(full - width) / 2;
        transformGroup.Children.Add(translate);



        wb1.Render(tempImage2, transformGroup);
        wb1.Invalidate();

Many thanks to those above.. they helped a lot. I include here a simple example which includes the additional transform necessary to move the rotated image back to the top left corner of the result.

        int width = currentImage.PixelWidth;
        int height = currentImage.PixelHeight;
        int full = Math.Max(width, height);

        Image tempImage2 = new Image();
        tempImage2.Width = full;
        tempImage2.Height = full;
        tempImage2.Source = currentImage;

        // New bitmap has swapped width/height
        WriteableBitmap wb1 = new WriteableBitmap(height,width);


        TransformGroup transformGroup = new TransformGroup();

        // Rotate around centre
        RotateTransform rotate = new RotateTransform();
        rotate.Angle = 90;
        rotate.CenterX = full/2;
        rotate.CenterY = full/2;
        transformGroup.Children.Add(rotate);

        // and transform back to top left corner of new image
        TranslateTransform translate = new TranslateTransform();
        translate.X = -(full - height) / 2;
        translate.Y = -(full - width) / 2;
        transformGroup.Children.Add(translate);



        wb1.Render(tempImage2, transformGroup);
        wb1.Invalidate();
生死何惧 2024-09-02 07:57:42

如果图像不是方形的,您将被裁剪。

我知道这不会给你完全正确的结果,你需要稍后裁剪它,但它会在每个方向创建一个足够大的位图来拍摄旋转的图像。

    //Draw to the Writeable Bitmap
    Image tempImage2 = new Image();
    tempImage2.Width = Math.Max(width, height);
    tempImage2.Height = Math.Max(width, height);
    tempImage2.Source = rawImage;

If the image isn't square you will get cropping.

I know this won't give you exactly the right result, you'll need to crop it afterwards, but it will create a bitmap big enough in each direction to take the rotated image.

    //Draw to the Writeable Bitmap
    Image tempImage2 = new Image();
    tempImage2.Width = Math.Max(width, height);
    tempImage2.Height = Math.Max(width, height);
    tempImage2.Source = rawImage;
纵性 2024-09-02 07:57:42

您需要根据角相对于中心的旋转来计算缩放比例。

如果图像是正方形,则只需要一个角,但对于矩形,您需要检查 2 个角,以便查看垂直或水平边缘是否重叠。此检查是对超出矩形高度和宽度的程度进行线性比较。

单击此处查看为此答案创建的工作测试台应用程序(下图):

在此输入图像描述

double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)

伪代码如下(实际C#代码在最后):

  • 将旋转角度转换为弧度
  • 计算“半径” " 从矩形中心到一个角
  • 将 BR 角位置转换为极坐标
  • 将 BL 角位置转换为极坐标
  • 将旋转应用到两个极坐标
  • 将新位置转换回笛卡尔坐标(ABS 值)
  • 查找 2 个水平位置中最大的位置
  • 查找2 个垂直位置中最大的
  • 一个 计算水平尺寸的增量变化
  • 计算垂直尺寸的增量变化
  • 如果水平变化较大,则返回宽度/2 / x 如果
  • 垂直变化较大,则返回高度/2 / y

结果是一个乘数,将无论旋转如何,缩小图像以适合原始矩形。

**注意:虽然可以使用矩阵运算完成大部分数学运算,但没有足够的计算来保证这一点。我还认为它会根据第一原理成为一个更好的示例。*

C# 代码:

    /// <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;
    }

I在我的网站上发布了代码的测试床,以便您可以真正尝试它 - 点击尝试

PS是的,这是我在另一个问题中的答案,完全重复,但该问题确实需要与该问题相同的答案一个是完整的。

You need to calculate the scaling based on the rotation of the corners relative to the centre.

If the image is a square only one corner is needed, but for a rectangle you need to check 2 corners in order to see if a vertical or horizontal edge is overlapped. This check is a linear comparison of how much the rectangle's height and width are exceeded.

Click here for the working testbed app created for this answer (image below):

enter image description here

double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)

The pseudo-code is as follows (actual C# code at the end):

  • Convert rotation angle into Radians
  • Calculate the "radius" from the rectangle centre to a corner
  • Convert BR corner position to polar coordinates
  • Convert BL corner position to polar coordinates
  • Apply the rotation to both polar coordinates
  • Convert the new positions back to Cartesian coordinates (ABS value)
  • Find the largest of the 2 horizontal positions
  • Find the largest of the 2 vertical positions
  • Calculate the delta change for horizontal size
  • Calculate the delta change for vertical size
  • Return width/2 / x if horizontal change is greater
  • Return height/2 / y if vertical change is greater

The result is a multiplier that will scale the image down to fit the original rectangle regardless of rotation.

**Note: While it is possible to do much of the maths using matrix operations, there are not enough calculations to warrant that. I also thought it would make a better example from first-principles.*

C# Code:

    /// <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;
    }

Example of use:

    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;
    }

I released a Test-bed of the code on my website so you can try it for real - click to try it

P.S. Yes this is my answer from another question, duplicated exactly, but the question does require the same answer as that one to be complete.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文