在Silverlight中旋转图像而不进行裁剪

时间:2010-03-24 10:07:49

标签: graphics silverlight-3.0 rotation

我目前正在制作一个简单的Silverlight应用程序,允许用户上传图像,裁剪,调整大小和旋转图像,然后通过网络服务将其加载到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设置为原始图像的高度和宽度。

4 个答案:

答案 0 :(得分:7)

您的目标图片看起来与源图片的尺寸相同。如果要旋转超过90度,则应更换宽度和高度:

WriteableBitmap wb = new WriteableBitmap(destHeight, destWidth);

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

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

尝试使用一张长方形纸,看看为什么这是有意义的。

答案 1 :(得分:2)

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

        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();

答案 2 :(得分:0)

如果图像不是正方形,则会进行裁剪。

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

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

答案 3 :(得分:0)

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

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

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)

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

  • 将旋转角度转换为Radians
  • 计算从矩形中心到角落的“半径”
  • 将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 released a Test-bed of the code on my website so you can try it for real - click to try it

P.S。是的,这是我对另一个问题的回答,完全重复,但问题确实需要与完整的问题相同的答案。