Image involuntarily resizes when drawn - c#

I have an Image Which is 100 X 100,
But when i want to draw it, it gets resized for some odd resone,
here is the code:
w = 100;
h = image1.height + image2.height + image3.height;//every image height is 100
SpaceShip = (Image)new Bitmap(SpaceShip, new Size(w, h));
Graphics g = Graphics.FromImage(SpaceShip);
g.Clear(Color.Transparent);
g.DrawImage(image1, new Point(0, 0));
g.DrawImage(image2, new Point(0,image1.Height));
g.DrawImage(image3, new Point(0, image1.Height +image2.Height));
g.Dispose();
this is the output:
http://i.stack.imgur.com/YJt42.png
the triangle is 100x100 with transperant backround.

Use the overload of DrawImage that takes source/dest rectangles and GraphicsUnit.Pixel. https://msdn.microsoft.com/en-us/library/ms142040(v=vs.110).aspx
This is happening because of DPI scaling.

Related

DrawEllipse: Ellipse goes outside of the Bitmap size

I want to draw a circle with DrawEllipse on a specified Bitmap, with the same size of the Bitmap, but the result is that the circle appears clipped at the edges.
Why this problem?
Bitmap layer = new Bitmap(80, 80);
using (Graphics g = Graphics.FromImage(layer))
{
using (Pen p = new Pen(Color.Black, 4))
{
g.DrawEllipse(p, new Rectangle(0, 0, layer.Width, layer.Height));
}
}
pictureBox3.Size = new Size(100, 100);
pictureBox3.Image = layer;
By default a Pen has a PenAlignment.Center.
This means that half of its widh will draw outside the bounding rectangle.
You can simply avoid the issue by changing it to PenAlignment.Inset:
using (Pen p = new Pen(Color.Black, 4) { Alignment = PenAlignment.Inset})
{
g.DrawEllipse(p, new Rectangle(0, 0, layer.Width, layer.Height));
}
Update: If you want to turn on smoothing for the Graphics object you will need 1 or 2 extra pixels on both sides of the pen stroke for the anti-aliasing pixels. Using a smaller bounding rectanlge can't be avoided now. But..:
Rectangle rect = new Rectangle(Point.Empty, layer.Size);
rect.Inflate(-1, -1); // or -2
..should do..

Adapt Rectangle's region to a different size

Under Windows Forms, I take a full size screenshot of a specific window that has a specific window size, I save it into a Bitmap object, then, I declared a Rectangle structure to crop a region of that Bitmap, because later I need to manipulate only a very specific part/region of the screenshot...
To make things simpler for this question, lets say the window and bitmap size is 640x480, the Rectangle's X,Y is: 436,150 and the Width,Height is: 146,170, and what I crop from the screenshot (the bitmap) is a balloon image. The window is a videogame.
The problem is that when the window size increase, the balloon image increase too, as obvious, so the x,y and width/height of my rectangle for a window size of 640x480 will not properly capture/crop the entire balloon image when the window of the game has a bigger size...
I need to know how can I calculate the x,y width/height that my rectangle should have to properly crop the balloon image when the window size changes. I need to adapt the rectangle.
So, if this is the predefined size and rectangle I have:
{ new Size(640, 480), new Rectangle(436, 150, 146, 170) }
From that, the approximated adapted values that the rectangle should have to properly crop the same equivalent area in a window size of 800x600 and 1280x768 it would be more or less these:
{ new Size(800, 600), new Rectangle(546, 186, 186, 212) }
{ new Size(1280, 768), new Rectangle(830, 232, 240, 274) }
...are just approximated values, but not perfect, because I did it manually since Im not sure which is the way to calculate and automate this math operation.
I hope my question and problem was understood. Thankyou in advance.
Maybe you're over-thinking it, but all you need to do is capture the percentage change between the original size and the new size (for both X and Y), and then apply that percentage to the properties of the original rectangle to get the new rectangle.
For example:
public static Rectangle GetNewRectangle(Size oldSize, Rectangle oldRectangle,
Size newSize)
{
var percentChangeX = (double)newSize.Width / oldSize.Width;
var percentChangeY = (double)newSize.Height / oldSize.Height;
return new Rectangle
{
X = (int)(oldRectangle.X * percentChangeX),
Y = (int)(oldRectangle.Y * percentChangeY),
Width = (int)(oldRectangle.Width * percentChangeX),
Height = (int)(oldRectangle.Height * percentChangeY)
};
}
Example usage:
// Helper method to display size and rectangle properties
private static string GetDisplayValues(Size size, Rectangle rect)
{
return $" - size: {size.Width} x {size.Height}\n" +
$" - rect: {rect.X}, {rect.Y} : {rect.Width} x {rect.Height}\n";
}
private static void Main()
{
var size = new Size(640, 480);
var rect = new Rectangle(436, 150, 146, 170);
Console.WriteLine($"Original:\n{GetDisplayValues(size, rect)}");
var newSize = new Size(800, 600);
var newRect = GetNewRectangle(size, rect, newSize);
Console.WriteLine($"Resized:\n{GetDisplayValues(newSize, newRect)}");
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
Try this:
if width 640:
X = 436 / 640 = 0.68125 (68.125%)
W = 146 / 640 = 0.22125 (22.125%)
if heigth 480:
Y = 150 / 480 = 0.3125 (31.25%)
H = 170 / 480 = 0.3541666666666666666666666667 (35.41666666666666666666666667%)
Considering the size of the form as this.Width, and the height as this.Height:
decimal pX = 0.68125;
decimal pW = 0.22125;
decimal pY = 0.3125;
decimal pH = 0.3541666666666666666666666667;
Rectangle rect = new Rectangle(this.Width * pX, this.Height * pY, this.Width * pW, this.Height * pH);
Given a source Bitmap and a selection Rectangle inside its boundaries:
RectangleF SourceRect = new Rectangle(Point.Empty, SourceBitmap.Size);
Rectangle SelectionRect = new Rectangle([Point], [Size]);
When the SourceBitmap changes its size, the new size of the selection rectangle is calculated using the scale factor given by the relation between the old size and the new size of the SourceBitmap:
RectangleF DestinationRect = new RectangleF(Point.Empty, InflatedBitmap.Size);
SizeF ScaleFactor = new SizeF(DestinationRect.Width / SourceRect.Width,
DestinationRect.Height / SourceRect.Height);
PointF NewPosition = new PointF(SelectionRect.X * ScaleFactor.Width, SelectionRect.Y * ScaleFactor.Height);
SizeF NewSize = new SizeF(SelectionRect.Width * ScaleFactor.Width, SelectionRect.Height * ScaleFactor.Height);
RectangleF InflatedSelection = new RectangleF(NewPosition, NewSize);
With a SourceBitmap and a selection rectangle sized as:
RectangleF SourceRect = new RectangleF(0, 0, 640, 480);
RectangleF SelectionRect = new RectangleF(436, 150, 146, 170);
If the inflated bitmaps are sized as:
RectangleF DestinationRect1 = new RectangleF(0, 0, 800, 600);
RectangleF DestinationRect2 = new RectangleF(0, 0, 1280, 768);
The Inflated selection with a scale factor of (1.25, 1.25) and (2, 1.6) will be (rounded down):
RectangleF InflatedSelection1 = new RectangleF(545, 187, 182, 212);
RectangleF InflatedSelection2 = new RectangleF(872, 240, 292, 272);

rotating Graphics/ image in c#

I have the following code which i wrote to try and rotate a bitmap(this is a test) the idea is to take a bitmap and rotate it by some amount of degrees and then draw it on the screen using win forms
protected override void OnDoubleClick(EventArgs e)
{
base.OnDoubleClick(e);
Graphics g = this.CreateGraphics();
Bitmap b = new Bitmap(path);
g.Clear(Color.White);
imagePosition = Cursor.Position;
b = RotateImage(b, 45);
g.DrawImage(b, new Point(100, 100));
}
public Bitmap RotateImage(Bitmap b, float angle)
{
Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
returnBitmap.SetResolution(b.HorizontalResolution, b.VerticalResolution);
Graphics g = Graphics.FromImage(returnBitmap);
g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
g.RotateTransform(angle);
g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
g.DrawImage(b, new Point(0, 0));
return returnBitmap;
}
this is the image before rotation
and this is the image after rotating 45 degrees like was shown in the code
The reason it's getting clipped is that there isn't enough space to display it rotated. The diagonal is longer than the sides (Pythagoras).
You need to make more space for the image, then it should display OK. How you do that will depend on what the image is contained in.

Drawline with same pen width, but the line widths are different in result

I draw lines with the same pen, but the line widths are different in result. Why?
Bitmap b = new Bitmap(400, 400);
Graphics g = Graphics.FromImage(b);
g.PageUnit = GraphicsUnit.Point;
g.Clear(Color.White);
Pen pen = new Pen(Color.Red, 1.2f);
for (int i = 20; i < 200; i = i + 20)
{
g.DrawLine(pen, 10, i, 190, i);
}
g.Dispose();
b.Save("d:/temp/test.png", ImageFormat.Png);
b.Dispose()
Here is the result:
MSDN for GraphicsUnit
It's because you're working with Points and not Pixels and the variation in the width of the lines is the result of a rounding errors in the placement of the line and the width of the line in relation to how it gets rendered in pixels in the final product.
If you don't care about printing the image, it might be best to stick with Pixels.
Edit: If you want to continue using points, space things relative to your pen width:
Bitmap b = new Bitmap(400, 400);
Graphics g = Graphics.FromImage(b);
g.PageUnit = GraphicsUnit.Point;
g.Clear(Color.White);
Pen pen = new Pen(Color.Red, 1.2f);
for (float i = 20f * pen.Width; i < 200f * pen.Width; i = i + 20f * pen.Width)
{
g.DrawLine(pen, 10f, i, 190f, i);
}
g.Dispose();
b.Save("c:/temp/test.png", ImageFormat.Png);
b.Dispose();

Alignment property of Pens tool in WinForms graphics

When I want to draw a rectangle in c# using pen tools If the rectangle width and height is less then the pens width then program draw nothing in from if pens alignment property Inset.But when I set alignment center then It print a rectangle. which is not size of my rectangle. Actually what happened at that time?
example:
Pen p = new Pen(Color.Black, 300);
p.Alignment = PenAlignment.Center;
g.DrawRectangle(p, 100, 100,10, 10);
p.Dispose();
The output figure is:
But how it is possible to draw a rectangle of 1o pixel width,hight with a pen of 300 pixel width?
So, basically, when the rectangle width or height smaller then 300px, you want to draw a black filled rectangle?
void DrawRectangle(Graphics g, Color pencolor, int penwidth, Rectangle x)
{
if(x.Width < penwidth || x.Height < penHeight)
{
Pen p = new Pen(pencolor);
p.Alignment = PenAlignment.Inset;
g.FillRectangle(p, x);
p.Dispose();
}
else
{
Pen p = new Pen(pencolor, penwidth);
p.Alignment = PenAlignment.Inset;
g.DrawRectangle(p, x);
p.Dispose();
}
}
Hope that helps.

Categories

Resources