How to Preserve aspect ratio of a button image .net - c#

I'm trying to re-size an image of the Button control while preserving the aspect ratio from the original image so the new image doesn't look squashed/stretched.
And when i again get it back to the original position (Normal Window Maximized state) it should retain its original resolution.
Basically i want the images in all of monitor resolutions to look the same.
For ex: In my application,
An image of a Globe looks fine with the monitor resolution 1280 * 1024 (5:4)
but looks stretched like an ellipse with the monitor resolution 1920×1080 (16:9)
I know i have to do some form of transformation programatically, but am not getting how to do it. I tried calling the below method on OnResize event, though it works for the first iteration, the images gets smaller on subsequent resizing and finally i lose my image finally.
public Rectangle MaintainAspectRatio(Image imgPhoto, Rectangle thumbRect)
{
int sourceWidth = imgPhoto.Width;
int sourceHeight = imgPhoto.Height;
int sourceX = 0;
int sourceY = 0;
int destX = 0;
int destY = 0;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)thumbRect.Width / (float)sourceWidth);
nPercentH = ((float)thumbRect.Height / (float)sourceHeight);
//if we have to pad the height pad both the top and the bottom
//with the difference between the scaled height and the desired height
if (nPercentH < nPercentW)
{
nPercent = nPercentH;
destX = (int)((thumbRect.Width - (sourceWidth * nPercent)) / 2);
}
else
{
nPercent = nPercentW;
destY = (int)((thumbRect.Height - (sourceHeight * nPercent)) / 2);
}
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Rectangle retRect = new Rectangle(thumbRect.X, thumbRect.Y, destWidth, destHeight);
return retRect;
}
I use this code in my overriden OnPaint eventhandler method as :
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
int iHeight;
int iWidth;
if (Image != null)
{
Rectangle ResizedRectangle = MaintainAspectRatio(Image,ClientRectangle);
iHeight = ResizedRectangle.Height;
iWidth = ResizedRectangle.Width;
}
else
{
iWidth = ClientRectangle.Width;
iHeight = ClientRectangle.Height;
}
// Draw border
Color oLeftTopColor = SystemColors.ControlLightLight;
Color oRightBottomColor = SystemColors.ActiveCaption;
Pen oLeftTopPen = new Pen(oLeftTopColor);
Pen oRightBottomPen = new Pen(oRightBottomColor);
// top line
g.DrawLine(oLeftTopPen, 0, 0, iWidth - 1, 0);
//g.DrawLine(new Pen(SystemColors.ControlLight), 1, 1, iWidth-2, 1);
// bottom line
g.DrawLine(oRightBottomPen, 0, iHeight, iWidth - 1, iHeight);
//g.DrawLine(new Pen(SystemColors.ControlDark), 1, iHeight-2, iWidth-2, iHeight-2);
// right line
g.DrawLine(oRightBottomPen, iWidth, 0, iWidth, iHeight - 1);
//g.DrawLine(new Pen(SystemColors.ControlDark), iWidth-2, 1, iWidth-2, iHeight-2);
// left line
g.DrawLine(oLeftTopPen, 0, 0, 0, iHeight - 1);
//g.DrawLine(new Pen(SystemColors.ControlLightLight), 1, 1, 1, iHeight-2);
// Draw image
if (Image != null)
{
Rectangle oDrawRectagle = new Rectangle(
0, 0, iWidth, iHeight);
if (Enabled == false)
{
e.Graphics.DrawImage(Image, oDrawRectagle,
0, 0, Image.Width, Image.Height,
GraphicsUnit.Pixel);
}
else
{
e.Graphics.DrawImage(Image, oDrawRectagle);
}
}
// Draw text
if (Text != null)
{
int iTextTop = 5;
int iTextLeft = 5;
int iMaxTextWidth = iWidth - 10;
int iMaxTextHeight = iHeight - 10;
}
}
Can anyone guide me how should i proceed with this ?

I tried calling the below method on
OnResize event, though it works for
the first iteration, the images gets
smaller on subsequent resizing and
finally i lose my image finally.
I think you should use the DisplaySettingsChanged instead of OnResize event, it will trigger only when resolution is changed.

It's not very clear how you are using the image - maybe you should post the code where you actually display the image.
An image might appear deformed on different resolutions if the pixel aspect ratio (PAR) differs in the two resolutions - is this your case? Do you want to display an image independent of the PAR?
In this case you need to keep the image internally at a known DPI value (96, usually) and then stretch it with your method by the values of the current resolution's DPI, which can be obtain with something like this:
Single xDpi, yDpi;
IntPtr dc = GetDC(IntPtr.Zero);
using(Graphics g = Graphics.FromHdc(dc))
{
xDpi = g.DpiX;
yDpi = g.DpiY;
}
if (ReleaseDC(IntPtr.Zero) != 0)
{
// GetLastError and handle...
}
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("user32.dll")]
private static extern Int32 ReleaseDC(IntPtr hwnd);
Then you need to scale your image so that final width = original width * 96 / xDpi, and similar for height.

Related

Issue with horizontally resizing BMP Image (C#)

Link to Project: https://github.com/FFladenmuller/resize-bmp
Code works for a resize with a factor of 1. However, if trying a larger factor and I open the image, Photos says: "It looks like we don't support this file format".
I have not added in padding yet but I have only worked with images whose width is divisible by 4.
For loop to add BGR bytes to new Image:
for (int i = 54; i < oldBMP.Info.Count - 2; i += 3)
{
for(int j = 0; j < sizeMultiplier; j++)
{
newBMP.Info.Add(oldBMP.Info[i]);
newBMP.Info.Add(oldBMP.Info[i + 1]);
newBMP.Info.Add(oldBMP.Info[i + 2]);
}
}
First for loop to increment through BGR triples, second for loop to add each pixel sizeMultiplier amount of times.
Define the following method:
public static Bitmap ResizeImage(Bitmap image, Size size)
{
try
{
Bitmap result = new Bitmap(size.Width, size.Height);
using (Graphics g = Graphics.FromImage((Image)result))
{
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawImage(image, 0, 0, size.Width, size.Height);
}
return result;
}
catch
{
return image;
}
}
Then, within your code, whenever a resize is needed:
Bitmap image = new Bitmap(#"C:\Path\MyImage.bmp");
Single scaleWidth = 1.2f;
Int32 targetWidth = (Int32)((Single)image.Width * scaleWidth);
Single scaleHeight = 1.0f;
Int32 targetHeight = (Int32)((Single)image.Height * scaleHeight);
Size size = new Size(targetWidth, targetHeight);
Bitmap imageResized = ResizeImage(image, size);
An alternative (that has the drawback of reducing the output quality) is the following:
Bitmap image = new Bitmap(#"C:\Path\MyImage.bmp");
Single scaleWidth = 1.2f;
Int32 targetWidth = (Int32)((Single)image.Width * scaleWidth);
Single scaleHeight = 1.0f;
Int32 targetHeight = (Int32)((Single)image.Height * scaleHeight);
Bitmap imageResized = new Bitmap(image, targetWidth, targetHeight);

Resize width on upload and keep the height ratio proportional

I'm using the following system drawing code to resize images on Upload. The problem is that landscape or portrait images get distorted cause the sytem drawing is making them square. Is it possible to resize the width only and keep the height proportional? and How? Thanks
HttpPostedFile imageFile = UploadImages.PostedFile;
System.Drawing.Image ri = System.Drawing.Image.FromStream(imageFile.InputStream);
ri = ResizeBitmap((Bitmap) ri, 200, 200);
private Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
{
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
g.DrawImage(b, 0, 0, nWidth, nHeight);
return result;
}
If what you want to do is create a new bitmap 200 pixels wide and with height scaled proportionately, you can do this:
private static int CalculateProportionalHeight(int oldWidth, int oldHeight, int newWidth)
{
if (oldWidth <= 0 || oldHeight <= 0 || newWidth <= 0)
// For safety.
return oldHeight;
double widthFactor = (double)newWidth / (double)oldWidth;
int newHeight = (int)Math.Round(widthFactor * (double)oldHeight);
if (newHeight < 1)
newHeight = 1; // just in case.
return newHeight;
}
private static Bitmap ResizeBitmap(Bitmap b, int nWidth)
{
int nHeight = CalculateProportionalHeight(b.Width, b.Height, nWidth);
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
g.DrawImage(b, 0, 0, nWidth, nHeight);
return result;
}
Or are you looking to create a 200x200 bitmap with the old image scaled to fit inside, and letterboxed if necessary?
Update
If you are looking to create an image of a fixed 200x200 size, with the image scaled down proportionately to fit and letterboxed, this should do it:
static RectangleF PlaceInside(int oldWidth, int oldHeight, int newWidth, int newHeight)
{
if (oldWidth <= 0 || oldHeight <= 0 || newWidth <= 0 || newHeight <= 0)
return new RectangleF(oldWidth, oldHeight, newWidth, newHeight);
float widthFactor = (float)newWidth / (float)oldWidth;
float heightFactor = (float)newHeight / (float)oldHeight;
if (widthFactor < heightFactor)
{
// prefer width
float scaledHeight = widthFactor * oldHeight;
// new new RectangleF(x, y, width, height)
return new RectangleF(0, (newHeight - scaledHeight) / 2.0f, newWidth, scaledHeight);
}
else
{
// prefer height
float scaledWidth = heightFactor * oldWidth;
// new new RectangleF(x, y, width, height)
return new RectangleF((newWidth - scaledWidth) / 2.0f, 0, scaledWidth, newHeight);
}
}
private static Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
{
int oldWidth = b.Width;
int oldHeight = b.Height;
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
{
var box = PlaceInside(oldWidth, oldHeight, nWidth, nHeight);
g.DrawImage(b, box);
}
return result;
}
Update 2
And here's a version that creates an image of width 200 and proportional height if landscape and height 200 and proportional width if portrait:
private static Bitmap ResizeBitmapUpto(Bitmap b, int nWidth, int nHeight, System.Drawing.Drawing2D.InterpolationMode interpolationMode)
{
int oldWidth = b.Width;
int oldHeight = b.Height;
var box = PlaceInside(oldWidth, oldHeight, nWidth, nHeight);
int actualNewWidth = (int)Math.Max(Math.Round(box.Width), 1);
int actualNewHeight = (int)Math.Max(Math.Round(box.Height), 1);
Bitmap result = new Bitmap(actualNewWidth, actualNewHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
{
g.InterpolationMode = interpolationMode;
g.DrawImage(b, 0, 0, actualNewWidth, actualNewHeight);
}
return result;
}
I added an interpolationMode so you can experiment with different qualities as per Ksv3n's answer.
(hopefully) Last Update
Here's the test setup I used to validate the code. I was able to open, resize and save a variety of images successfully on my computer.
public static void TestResizeBitmapUpto(string file, string newFile)
{
try
{
using (var image = Bitmap.FromFile(file))
{
if (image == null)
return;
using (Bitmap b = new Bitmap(image))
{
using (var newBitmap = ResizeBitmapUpto(b, 200, 200, System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor))
{
newBitmap.Save(newFile);
}
}
}
}
catch (System.IO.FileNotFoundException e)
{
Debug.WriteLine(e.ToString());
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
What's missing in your code is :
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
Here is the method you can use to resize your image with keeeping it proportional :
private Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
{
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.DrawImage(b, 0, 0, nWidth, nHeight);
}
return result;
}
g.DrawImage will stretch the image to what you defined (in your case 200/200 which is a square).
You need to calculate the real values for nWidth and nHeight:
// original image (b.Width, b.Height)
double originalWidth = 200;
double originalHeight = 100;
// user defined wanted width
double wantedWidth = 200; // nWidth parameter to your method
double wantedHeight = 300; // nHeight parameter to your method
double ratioW = originalWidth / wantedWidth;
double ratioH = originalHeight / wantedHeight;
double ratio = Math.Max(ratioW, ratioH);
// rectangle proportional to the original that fits into the wanted
double destinationWidth = originalWidth / ratio; // what you pass to DrawImage as nWidth
double destinationHeight = originalHeight / ratio; // what you pass to DrawImage as nWidth
What it does is calculates the width and height ratio of the original and wanted image and takes the max of it. Uses that to divide the original values, which will make them fit perfectly inside the wanted rectangle.
This will draw the scaled image aligned top or left depending what is the orientation, since the resulting image will be equal or bigger then the wanted or original. To make it centered in the resulting image you would need to adjust left and top coordinates for DrawImage() by taking the difference in width/height and dividing by 2.
If the resulting image can be of different size then what the user specified (nWidth/nHeight) then you can simply initialize it with the destinationWidth/Height, and return that instead, without bothering for centering.

how to improve quality of print when I print Win Form in C#

I am trying to print my form using GDI ,but when I print it ,the quality of the print is not that good(donknow whether Image getting aliased?) ,form size is 700x700 ,also there is one parameter which dint understood -raster op code-,here is code am using...
private void printDocument1_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g1 = this.CreateGraphics();
System.Drawing.Image MyImage = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height, g1);
Graphics g2 = Graphics.FromImage(MyImage);
IntPtr dc1 = g1.GetHdc();
IntPtr dc2 = g2.GetHdc();
BitBlt(dc2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, dc1, 0, 0, 13369376);
g1.ReleaseHdc(dc1);
g2.ReleaseHdc(dc2);
Bitmap bmp = new Bitmap(MyImage);
int x = e.MarginBounds.X;
int y = e.MarginBounds.Y;
int width = bmp.Width;
int height = bmp.Height;
if ((width / e.MarginBounds.Width) > (height / e.MarginBounds.Height))
{
width = e.MarginBounds.Width;
height = bmp.Height * e.MarginBounds.Width / bmp.Width;
}
else
{
height = e.MarginBounds.Height;
width = bmp.Width * e.MarginBounds.Height / bmp.Height;
}
System.Drawing.Rectangle destRect = new System.Drawing.Rectangle(x, y, width, height);
e.Graphics.DrawImage(bmp, destRect, 0, 0, bmp.Width, bmp.Height, System.Drawing.GraphicsUnit.Pixel);
}
Maybe you have a problem with the original image. Give me a link to an image. Check the image size.
Try insert line
g2.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
Good luck!
It is normal that the result will be scaled and aliased. The source has too few pixels compared to the resolution of a modern printer.
Consider using WPF, that uses a vector based rendering thus there's no loss/distortion when scaling.
Cheers

ASP.Net MVC Image Upload Resizing by downscaling or padding

A user will be able to upload an image. If the image is greater than a set size I want to downsize it to that size. Obviously it doesn't have to match exactly due to ratios, the width would be the key size so the height would be variable.
If the image is smaller than the set size I would like to create a new image to the set size with a background of a defined colour and then centre the uploaded image into it therefore the result is the original with paddded colour.
Any code examples or links greatly appreciated
Here's a snippet of code I quickly knocked up for resizing it based on the width. I'm sure you could figure out how to add a background color to the Bitmap. It's not complete code but just an idea of how to do things.
public static void ResizeLogo(string originalFilename, string resizeFilename)
{
Image imgOriginal = Image.FromFile(originalFilename);
//pass in whatever value you want for the width (180)
Image imgActual = ScaleBySize(imgOriginal, 180);
imgActual.Save(resizeFilename);
imgActual.Dispose();
}
public static Image ScaleBySize(Image imgPhoto, int size)
{
int logoSize = size;
float sourceWidth = imgPhoto.Width;
float sourceHeight = imgPhoto.Height;
float destHeight = 0;
float destWidth = 0;
int sourceX = 0;
int sourceY = 0;
int destX = 0;
int destY = 0;
// Resize Image to have the height = logoSize/2 or width = logoSize.
// Height is greater than width, set Height = logoSize and resize width accordingly
if (sourceWidth > (2 * sourceHeight))
{
destWidth = logoSize;
destHeight = (float)(sourceHeight * logoSize / sourceWidth);
}
else
{
int h = logoSize / 2;
destHeight = h;
destWidth = (float)(sourceWidth * h / sourceHeight);
}
// Width is greater than height, set Width = logoSize and resize height accordingly
Bitmap bmPhoto = new Bitmap((int)destWidth, (int)destHeight,
PixelFormat.Format32bppPArgb);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(imgPhoto,
new Rectangle(destX, destY, (int)destWidth, (int)destHeight),
new Rectangle(sourceX, sourceY, (int)sourceWidth, (int)sourceHeight),
GraphicsUnit.Pixel);
grPhoto.Dispose();
return bmPhoto;
}
You can just load the file into a bitmap object:
http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.aspx
Then just check the width on the object. For the second part of your problem, I would recommend using a tool like ImageMagick
http://www.imagemagick.org/script/index.php
to accurately resize the first image or to create the background image and merge the two images together.

Per-pixel collision problem in C#

I am writing a small 2d game engine in C# for my own purposes, and it works fine except for the sprite collision detection. I've decided to make it a per-pixel detection (easiest for me to implement), but it is not working the way it's supposed to. The code detects a collision long before it happens. I've examined every component of the detection, but I can't find the problem.
The collision detection method:
public static bool CheckForCollision(Sprite s1, Sprite s2, bool perpixel) {
if(!perpixel) {
return s1.CollisionBox.IntersectsWith(s2.CollisionBox);
}
else {
Rectangle rect;
Image img1 = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(s1.Image, s1.Position, s1.Origin, s1.Rotation, out rect), s1.Scale);
int posx1 = rect.X;
int posy1 = rect.Y;
Image img2 = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(s2.Image, s2.Position, s2.Origin, s2.Rotation, out rect), s2.Scale);
int posx2 = rect.X;
int posy2 = rect.Y;
Rectangle abounds = new Rectangle(posx1, posy1, (int)img1.Width, (int)img1.Height);
Rectangle bbounds = new Rectangle(posx2, posy2, (int)img2.Width, (int)img2.Height);
if(Utilities.RectangleIntersects(abounds, bbounds)) {
uint[] bitsA = s1.GetPixelData(false);
uint[] bitsB = s2.GetPixelData(false);
int x1 = Math.Max(abounds.X, bbounds.X);
int x2 = Math.Min(abounds.X + abounds.Width, bbounds.X + bbounds.Width);
int y1 = Math.Max(abounds.Y, bbounds.Y);
int y2 = Math.Min(abounds.Y + abounds.Height, bbounds.Y + bbounds.Height);
for(int y = y1; y < y2; ++y) {
for(int x = x1; x < x2; ++x) {
if(((bitsA[(x - abounds.X) + (y - abounds.Y) * abounds.Width] & 0xFF000000) >> 24) > 20 &&
((bitsB[(x - bbounds.X) + (y - bbounds.Y) * bbounds.Width] & 0xFF000000) >> 24) > 20)
return true;
}
}
}
return false;
}
}
The image rotation method:
internal static Image RotateImagePoint(Image img, Vector pos, Vector orig, double rotation, out Rectangle sz) {
if(!(new Rectangle(new Point(0), img.Size).Contains(new Point((int)orig.X, (int)orig.Y))))
Console.WriteLine("Origin point is not present in image bound; unwanted cropping might occur");
rotation = (double)ra_de((double)rotation);
sz = GetRotateDimensions((int)pos.X, (int)pos.Y, img.Width, img.Height, rotation, false);
Bitmap bmp = new Bitmap(sz.Width, sz.Height);
Graphics g = Graphics.FromImage(bmp);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.RotateTransform((float)rotation);
g.TranslateTransform(sz.Width / 2, sz.Height / 2, MatrixOrder.Append);
g.DrawImage(img, (float)-orig.X, (float)-orig.Y);
g.Dispose();
return bmp;
}
internal static Rectangle GetRotateDimensions(int imgx, int imgy, int imgwidth, int imgheight, double rotation, bool Crop) {
Rectangle sz = new Rectangle();
if (Crop == true) {
// absolute trig values goes for all angles
double dera = de_ra(rotation);
double sin = Math.Abs(Math.Sin(dera));
double cos = Math.Abs(Math.Cos(dera));
// general trig rules:
// length(adjacent) = cos(theta) * length(hypotenuse)
// length(opposite) = sin(theta) * length(hypotenuse)
// applied width = lo(img height) + la(img width)
sz.Width = (int)(sin * imgheight + cos * imgwidth);
// applied height = lo(img width) + la(img height)
sz.Height = (int)(sin * imgwidth + cos * imgheight);
}
else {
// get image diagonal to fit any rotation (w & h =diagonal)
sz.X = imgx - (int)Math.Sqrt(Math.Pow(imgwidth, 2.0) + Math.Pow(imgheight, 2.0));
sz.Y = imgy - (int)Math.Sqrt(Math.Pow(imgwidth, 2.0) + Math.Pow(imgheight, 2.0));
sz.Width = (int)Math.Sqrt(Math.Pow(imgwidth, 2.0) + Math.Pow(imgheight, 2.0)) * 2;
sz.Height = sz.Width;
}
return sz;
}
Pixel getting method:
public uint[] GetPixelData(bool useBaseImage) {
Rectangle rect;
Image image;
if (useBaseImage)
image = Image;
else
image = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(Image, Position, Origin, Rotation, out rect), Scale);
BitmapData data;
try {
data = ((Bitmap)image).LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
}
catch (ArgumentException) {
data = ((Bitmap)image).LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat);
}
byte[] rawdata = new byte[data.Stride * image.Height];
Marshal.Copy(data.Scan0, rawdata, 0, data.Stride * image.Height);
((Bitmap)image).UnlockBits(data);
int pixelsize = 4;
if (data.PixelFormat == PixelFormat.Format24bppRgb)
pixelsize = 3;
else if (data.PixelFormat == PixelFormat.Format32bppArgb || data.PixelFormat == PixelFormat.Format32bppRgb)
pixelsize = 4;
double intdatasize = Math.Ceiling((double)rawdata.Length / pixelsize);
uint[] intdata = new uint[(int)intdatasize];
Buffer.BlockCopy(rawdata, 0, intdata, 0, rawdata.Length);
return intdata;
}
The pixel retrieval method works, and the rotation method works as well, so the only place that the code might be wrong is the collision detection code, but I really have no idea where the problem might be.
I don't think many people here will bother to scrutinize your code to figure out what exactly is wrong. But I can come with some hints to how you can find the problem.
If collision happens long before it is supposed to I suggest your bounding box check isn't working properly.
I would change the code to dump out all the data about rectangles at collision. So you can create some code that will display the situation at collision. That might be easier than looking over the numbers.
Apart from that I doubt that per pixel collision detection easier for you to implement. When you allow for rotation and scaling that quickly becomes difficult to get right. I would do polygon based collision detection instead.
I have made my own 2D engine like you but I used polygon based collision detection and that worked fine.
I think I've found your problem.
internal static Rectangle GetRotateDimensions(int imgx, int imgy, int imgwidth, int imgheight, double rotation, bool Crop) {
Rectangle sz = new Rectangle(); // <-- Default constructed rect here.
if (Crop == true) {
// absolute trig values goes for all angles
double dera = de_ra(rotation);
double sin = Math.Abs(Math.Sin(dera));
double cos = Math.Abs(Math.Cos(dera));
// general trig rules:
// length(adjacent) = cos(theta) * length(hypotenuse)
// length(opposite) = sin(theta) * length(hypotenuse)
// applied width = lo(img height) + la(img width)
sz.Width = (int)(sin * imgheight + cos * imgwidth);
// applied height = lo(img width) + la(img height)
sz.Height = (int)(sin * imgwidth + cos * imgheight);
// <-- Never gets the X & Y assigned !!!
}
Since you never assigned imgx and imgy to the X and Y coordinates of the Rectangle, every call of GetRotateDimensions will produce a Rectangle with the same location. They may be of differing sizes, but they will always be in the default X,Y position. This would cause the really early collisions that you are seeing because any time you tried to detect collisions on two sprites, GetRotateDimensions would put their bounds in the same position regardless of where they actually are.
Once you have corrected that problem, you may run into another error:
Rectangle rect;
Image img1 = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(s1.Image, s1.Position, s1.Origin, s1.Rotation, out rect), s1.Scale);
// <-- Should resize rect here.
int posx1 = rect.X;
int posy1 = rect.Y;
You get your boundary rect from the RotateImagePoint function, but you then resize the image. The X and Y from the rect are probably not exactly the same as that of the resized boundaries of the image. I'm guessing that you mean for the center of the image to remain in place while all points contract toward or expand from the center in the resize. If this is the case, then you need to resize rect as well as the image in order to get the correct position.
I doubt this is the actual problem, but LockBits doesn't guarantee that the bits data is aligned to the image's Width.
I.e., there may be some padding. You need to access the image using data[x + y * stride] and not data[x + y * width]. The Stride is also part of the BitmapData.

Categories

Resources