LockBits 图像旋转方法不起作用?

发布于 2024-09-25 22:52:00 字数 2979 浏览 3 评论 0原文

嘿大家。在厌倦了 Get/Set Pixel 和 RotateTransfom 的缓慢性能和古怪行为后,我求助于使用 LockBits 进行 2d 位图图像旋转。这是我想出的代码,根据我的计算,它应该可以完美工作。事实并非如此。

private static void InternalRotateImage(Bitmap originalBitmap, Bitmap rotatedBitmap, PointF centerPoint, float theta)
    {
        BitmapData originalData = originalBitmap.LockBits(
            new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height),
            ImageLockMode.ReadOnly,
            originalBitmap.PixelFormat);

        BitmapData rotatedData = rotatedBitmap.LockBits(
            new Rectangle(0, 0, rotatedBitmap.Width, rotatedBitmap.Height),
            ImageLockMode.WriteOnly,
            rotatedBitmap.PixelFormat);

        unsafe
        {
            byte[,] A = new byte[originalData.Height * 2, originalBitmap.Width * 2];
            byte[,] R = new byte[originalData.Height * 2, originalBitmap.Width * 2];
            byte[,] G = new byte[originalData.Height * 2, originalBitmap.Width * 2];
            byte[,] B = new byte[originalData.Height * 2, originalBitmap.Width * 2];

            for (int y = 0; y < originalData.Height; y++)
            {
                byte* row = (byte*)originalData.Scan0 + (y * originalData.Stride);
                for (int x = 0; x < originalData.Width; x++)
                {
                    B[y, x] = row[x * 4];
                    G[y, x] = row[x * 4 + 1];
                    R[y, x] = row[x * 4 + 2];
                    A[y, x] = row[x * 4 + 3];
                }
            }

            for (int y = 0; y < rotatedData.Height; y++)
            {
                byte* row = (byte*)rotatedData.Scan0 + (y * rotatedData.Stride);
                for (int x = 0; x < rotatedData.Width; x++)
                {
                    int newy = (int)Math.Abs((Math.Cos(theta) * (x - centerPoint.X) - Math.Sin(theta) * (y - centerPoint.Y) + centerPoint.X));
                    int newx = (int)Math.Abs((Math.Sin(theta) * (x - centerPoint.X) + Math.Cos(theta) * (y - centerPoint.Y) + centerPoint.Y));

                    row[x * 4] = B[newy, newx];
                    row[x * 4 + 1] = G[newy, newx];
                    row[x * 4 + 2] = R[newy, newx];
                    row[x * 4 + 3] = A[newy, newx];
                }
            }

        }
            originalBitmap.UnlockBits(originalData);
            rotatedBitmap.UnlockBits(rotatedData);
        }

有人有什么想法吗?我刚出来。提前致谢!

编辑: 这就是我最终使用的(非常感谢 Hans Passant):

private Image RotateImage(Image img, float rotationAngle)
    {
        Image image = new Bitmap(img.Width * 2, img.Height * 2);
        Graphics gfx = Graphics.FromImage(image);

        int center = (int)Math.Sqrt(img.Width * img.Width + img.Height * img.Height) / 2;
        gfx.TranslateTransform(center, center);
        gfx.RotateTransform(rotationAngle);
        gfx.DrawImage(img, -img.Width / 2, -img.Height / 2);

        return image;
    }

它与他的相同,只是基于每个图像,而不是表单。

Hey all. I resorted to using LockBits for 2d bitmap image rotation after getting fed up with the slow performance and wacky behavior of both Get/Set Pixel, and RotateTransfom. So here is the code I've come up with, and by my reckoning, it should work perfectly. It doesn't.

private static void InternalRotateImage(Bitmap originalBitmap, Bitmap rotatedBitmap, PointF centerPoint, float theta)
    {
        BitmapData originalData = originalBitmap.LockBits(
            new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height),
            ImageLockMode.ReadOnly,
            originalBitmap.PixelFormat);

        BitmapData rotatedData = rotatedBitmap.LockBits(
            new Rectangle(0, 0, rotatedBitmap.Width, rotatedBitmap.Height),
            ImageLockMode.WriteOnly,
            rotatedBitmap.PixelFormat);

        unsafe
        {
            byte[,] A = new byte[originalData.Height * 2, originalBitmap.Width * 2];
            byte[,] R = new byte[originalData.Height * 2, originalBitmap.Width * 2];
            byte[,] G = new byte[originalData.Height * 2, originalBitmap.Width * 2];
            byte[,] B = new byte[originalData.Height * 2, originalBitmap.Width * 2];

            for (int y = 0; y < originalData.Height; y++)
            {
                byte* row = (byte*)originalData.Scan0 + (y * originalData.Stride);
                for (int x = 0; x < originalData.Width; x++)
                {
                    B[y, x] = row[x * 4];
                    G[y, x] = row[x * 4 + 1];
                    R[y, x] = row[x * 4 + 2];
                    A[y, x] = row[x * 4 + 3];
                }
            }

            for (int y = 0; y < rotatedData.Height; y++)
            {
                byte* row = (byte*)rotatedData.Scan0 + (y * rotatedData.Stride);
                for (int x = 0; x < rotatedData.Width; x++)
                {
                    int newy = (int)Math.Abs((Math.Cos(theta) * (x - centerPoint.X) - Math.Sin(theta) * (y - centerPoint.Y) + centerPoint.X));
                    int newx = (int)Math.Abs((Math.Sin(theta) * (x - centerPoint.X) + Math.Cos(theta) * (y - centerPoint.Y) + centerPoint.Y));

                    row[x * 4] = B[newy, newx];
                    row[x * 4 + 1] = G[newy, newx];
                    row[x * 4 + 2] = R[newy, newx];
                    row[x * 4 + 3] = A[newy, newx];
                }
            }

        }
            originalBitmap.UnlockBits(originalData);
            rotatedBitmap.UnlockBits(rotatedData);
        }

Anybody got any ideas? I'm fresh out. Thanks in advance!

EDIT:
This is what I ended up using (many thanks to Hans Passant):

private Image RotateImage(Image img, float rotationAngle)
    {
        Image image = new Bitmap(img.Width * 2, img.Height * 2);
        Graphics gfx = Graphics.FromImage(image);

        int center = (int)Math.Sqrt(img.Width * img.Width + img.Height * img.Height) / 2;
        gfx.TranslateTransform(center, center);
        gfx.RotateTransform(rotationAngle);
        gfx.DrawImage(img, -img.Width / 2, -img.Height / 2);

        return image;
    }

It's the same thing as his, just on a per image basis, as opposed to a form.

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

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

发布评论

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

评论(1

长安忆 2024-10-02 22:52:00

你正在给自己挖一个更深的坑。这很早就出错了,旋转位图的大小不是宽度 x 高度。这也是非常低效的。您需要启动 RotateTransform,重要的是还要使用 TranslateTransform 并选择正确的图像绘制位置。

下面是一个示例 Windows 窗体应用程序,它围绕其中心点旋转位图,偏移量刚好足以在旋转时接触窗体的内边缘。将计时器放在表单上,​​然后使用“项目 + 属性”、“资源”选项卡添加图像资源。将其命名为 SampleImage,它不必是方形的。使代码如下所示:

public partial class Form1 : Form {
    private float mDegrees;
    private Image mBmp;
    public Form1() {
        InitializeComponent();
        mBmp = Properties.Resources.SampleImage;
        timer1.Enabled = true;
        timer1.Interval = 50;
        timer1.Tick += new System.EventHandler(this.timer1_Tick);
        this.DoubleBuffered = true;
    }
    private void timer1_Tick(object sender, EventArgs e) {
        mDegrees += 3.0F;
        this.Invalidate();
    }
    protected override void OnPaint(PaintEventArgs e) {
        int center = (int)Math.Sqrt(mBmp.Width * mBmp.Width + mBmp.Height * mBmp.Height) / 2;
        e.Graphics.TranslateTransform(center, center);
        e.Graphics.RotateTransform(mDegrees);
        e.Graphics.DrawImage(mBmp, -mBmp.Width/2, -mBmp.Height/2);
    }
}

您可以通过创建 32bppPArgb 格式的位图来使绘制速度更快,我跳过了该步骤。

You are digging yourself a deeper hole. This goes wrong early, the size of the rotated bitmap is not Width x Height. It is also very inefficient. You need to get RotateTransform going, it is important to also use TranslateTransform and pick the correct image drawing location.

Here's a sample Windows Forms app that rotates a bitmap around its center point, offset just enough to touch the inner edge of the form when it rotates. Drop a Timer on the form and add an image resource with Project + Properties, Resource tab. Name it SampleImage, it doesn't have to be square. Make the code look like this:

public partial class Form1 : Form {
    private float mDegrees;
    private Image mBmp;
    public Form1() {
        InitializeComponent();
        mBmp = Properties.Resources.SampleImage;
        timer1.Enabled = true;
        timer1.Interval = 50;
        timer1.Tick += new System.EventHandler(this.timer1_Tick);
        this.DoubleBuffered = true;
    }
    private void timer1_Tick(object sender, EventArgs e) {
        mDegrees += 3.0F;
        this.Invalidate();
    }
    protected override void OnPaint(PaintEventArgs e) {
        int center = (int)Math.Sqrt(mBmp.Width * mBmp.Width + mBmp.Height * mBmp.Height) / 2;
        e.Graphics.TranslateTransform(center, center);
        e.Graphics.RotateTransform(mDegrees);
        e.Graphics.DrawImage(mBmp, -mBmp.Width/2, -mBmp.Height/2);
    }
}

You can make draw a lot faster by creating a bitmap in the 32bppPArgb format, I skipped that step.

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