LockBits метод вращения изображения не работает? - PullRequest
0 голосов
/ 05 октября 2010

Привет всем. Я прибег к использованию LockBits для 2-мерного поворота растрового изображения после того, как устал от низкой производительности и дурацкого поведения как Get / Set Pixel, так и RotateTransfom. Итак, вот код, который я придумал, и, по моим расчетам, он должен работать отлично. Это не так.

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

У кого-нибудь есть идеи? Я свежая. Заранее спасибо!

EDIT: Вот что я использовал (большое спасибо Гансу Пассанту):

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

Это то же самое, что и его, только для каждого изображения, в отличие от формы.

1 Ответ

2 голосов
/ 05 октября 2010

Вы копаете себе более глубокую яму. Это идет не так, как надо, размер повернутого растрового изображения не равен ширине х высоте. Это также очень неэффективно. Вам нужно запустить RotateTransform, важно также использовать TranslateTransform и выбрать правильное место для рисования изображения.

Вот пример приложения Windows Forms, которое вращает растровое изображение вокруг своей центральной точки, смещение, достаточное для касания внутреннего края формы при его вращении. Перетащите таймер на форму и добавьте ресурс изображения с помощью Project + Properties, вкладка Resource. Назовите его 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, я пропустил этот шаг.

...