views:

486

answers:

1

I'm trying to center a rotated image into a destination buffer using GDI+. The source buffer and the destination buffer are different sizes.

The source buffer is the size of the image data: (width, height).
The destination buffer is the size of the rectangle required to fit the entire rotated image: (rotatedWidth, rotatedHeight).

This is the code I'm trying:

// Calculate the size needed for the image to be rotated into   
int width = /* some width */;
int height = /* some height */;
System::Windows::Size destSize 
  = IPMathUtilities::CalculateRotatedImageSize(rotateAreaBoundingPoints, 
    rotationDegree, width, height);

//
// Create source bitmap object
Bitmap^ sourceBitmap = gcnew Bitmap(width, height, width * 2, 
  PixelFormat::Format16bppRgb555, ptrSourceBuffer);

//
// Create destination bitmap object
int destBufferSize = destSize.Width * destSize.Height * 2;
BYTE* pDestBuffer = new BYTE[destBufferSize];
memset(pDestBuffer, 0, destBufferSize);
Bitmap^ destBitmap = gcnew Bitmap(destSize.Width, destSize.Height, 
  destSize.Width * 2, PixelFormat::Format16bppRgb555, (IntPtr)pDestBuffer);

//
// Draw rotated source image in destination image
Graphics^ g = Graphics::FromImage(destBitmap);
g->TranslateTransform(-width/2, -height/2);
g->RotateTransform(rotationDegree, MatrixOrder::Append);
g->TranslateTransform(destSize.Width / 2.0, destSize.Height / 2.0, 
  MatrixOrder::Append);
g->DrawImage(sourceBitmap, 0, 0, width, height);

This almost works. It's close - I'm finding if height is greater than width, the left position of the rotated image is incorrect. Similarly, if width is greater than height, the top position of the rotated image is incorrect.

Some notes:

  • IPMathUtilities is a utility class I wrote.
  • I'm 100% positive that IPMathUtilities::CalculateRotatedImageSize() calculates the correct size of the rectangle required to fit the entire rotated image. 100% certain. I've tested it thoroughly and it works.
  • I recently asked a similar question: Why is iplRotate() not giving me correct results?. I ended giving up on IPL/IPP and trying GDI+.

Any ideas?

+2  A: 

After the rotate, first move the image back to the original position, then additionally by half the difference the new canvas is larger. If your calculation in CalculateRotatedImageSize is indeed correct, it should then fit exactly. Just tested this code and it seems to work:

g.TranslateTransform((float)(org.Width / -2), (float)(org.Height / -2));
g.RotateTransform(45, System.Drawing.Drawing2D.MatrixOrder.Append );
g.TranslateTransform((float)(org.Width / 2), (float)(org.Height / 2), System.Drawing.Drawing2D.MatrixOrder.Append);
g.TranslateTransform((float)((rotated.Width - org.Width) / 2), (float)((rotated.Height - org.Height) / 2), System.Drawing.Drawing2D.MatrixOrder.Append);

EDIT: sorry, of course

g.TranslateTransform((float)(org.Width / 2), (float)(org.Height / 2), System.Drawing.Drawing2D.MatrixOrder.Append);
g.TranslateTransform((float)((rotated.Width - org.Width) / 2), (float)((rotated.Height - org.Height) / 2), System.Drawing.Drawing2D.MatrixOrder.Append);

is really the same as

g.TranslateTransform((float)(rotated.Width / 2), (float)(rotated.Height / 2), System.Drawing.Drawing2D.MatrixOrder.Append);

which is just the code you posted. Seems to work fine for me though.

EDIT2: perhaps the error is just

g->DrawImage(sourceBitmap, 0, 0, width, height);

Try

g->DrawImage(sourceBitmap, 0, 0);

instead

Ben Schwehn
Your first code block had something I was missing - the translation back from the origin to source image space - that was the missing key. Thank you so much. I'm so relieved to have centered-rotated images. Thank you!
unforgiven3
but this: g.TranslateTransform((float)(org.Width / -2), (float)(org.Height / -2));g.RotateTransform(45, System.Drawing.Drawing2D.MatrixOrder.Append );g.TranslateTransform((float)(rotated.Width / 2), (float)(rotated.Height / 2), System.Drawing.Drawing2D.MatrixOrder.Append);g.DrawImage(org, 0, 0);works for me. Isn't that just equivalent to the code you posted. Anyway, glad it works now :)
Ben Schwehn
I don't think it's equivalent - in my original code, after the rotate, I was translated directly to destination image space. In your code, you're translating first back to source image space, then to destination image space. You have an extra translation, which is what I was missing. :-)
unforgiven3
Unfortunately, this doesn't work for rectangular source images... they are still off-center :-(
unforgiven3
g->DrawImage(sourceBitmap, 0, 0) doesn't work either
unforgiven3
that's what I thought, the code is equivalent i believe. However it does work in my test, centers correctly in a larger canvas. Haven't implemented a CalculateRotatedImageSize so it's just a larger frame, but quite definitley centered exactly for different angles and differnt canvas sizes. I can post a sample c# project, the diff to your code might help?
Ben Schwehn
That'd be great, I'd love to take a look at the sample project!
unforgiven3
http://bschwehn.de/temp/RotateImageTest.zip
Ben Schwehn
Great, I'll try that out, thanks!
unforgiven3
It worked, thank you! I'm good for arbitrary angles now. Thanks for posting the sample app - I appreciate it!
unforgiven3