Graphics.DrawPolygon not drawing at correct location - c#

On the left is the polygon I drew using my paint-like program. On the right is the polygon drawn by System.Drawing.Bitmap/Graphics:
The code to draw it is as follows:
protected static Bitmap CropImage(Bitmap src, Vector2[] rect)
{
var result = new Bitmap(src.Width, src.Height);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
var pen = new Pen(Color.White);
g.DrawImage(src, new Point(0,0));
var poly = rect.Select(p => p.ToPointF()).ToArray();
g.DrawPolygon(pen, poly);
}
return result;
}
poly is:
{System.Drawing.PointF[4]}
[0]: {X = 57.4230042 Y = 57.4229736}
[1]: {X = 147.058868 Y = 56.0224}
[2]: {X = 148.43277 Y = 143.951767}
[3]: {X = 58.7969131 Y = 145.352341}
Each of the black squares in the image is 50x50. If you look at poly, all the coordinates are exactly as you'd expect: poly[0] is a little above 50,50 which corresponds to inside the black square labelled "6" (as shown in left image).
So how is Graphics getting confused and putting it in the wrong spot? It looks like it's scaling the whole rect down.

You are confused. The rectangle is correct, but the background image is scaled up. Note that the 6 and the black square are a lot bigger, but the rectangle is the exact same size.
Note that the Graphics.DrawImage method scales the source image to match the destination resolution.
This method draws an image using its physical size, so the image will have its correct size in inches regardless of the resolution (dots per inch) of the display device. For example, suppose an image has a pixel width of 216 and a horizontal resolution of 72 dots per inch. If you call this method to draw that image on a device that has a resolution of 96 dots per inch, the pixel width of the rendered image will be (216/72)*96 = 288.
You should use the overload that accepts a Rectangle instead:
g.DrawImage(src, new Rectangle(0, 0, src.Width, src.Height));

Related

Change image size (width, height) to have defined dpi

i try to explain what i need.
1 -My goal condition is to transform black pixels of image in Cartesian points without resize image. (ok, Done)
2 -Second goal condition is to resize image and redo step 1. (ok, i have normalized points with original image size and the job is done when changed width or height!)
3 - Now i need to reduced number of pixels in image to have defined number of DPI in my image to redo step 1. How? I have found the method setResolution(..,..) but how i must change the width and height of my image to obtain the correct resolution in terms of DPI? (see [?????] in code )
var image2 = new Bitmap(canvasWidth, canvasHeight);
image2.SetResolution(200.0f,200.0f); // I need this for example!
using (System.Drawing.Graphics gr = System.Drawing.Graphics.FromImage(image2)) {
gr.SmoothingMode = SmoothingMode.HighSpeed;
gr.InterpolationMode = InterpolationMode.Low;
gr.PixelOffsetMode = PixelOffsetMode.None;
gr.Clear(Color.White);
gr.DrawImage(this.LoadedImage, new System.Drawing.RectangleF(new PointF((float)this.Centre.x, (float)this.Centre.y), new Size(canvasWidth [?????], canvasHeight[??????])));
return image2;
}
If i run over everyone pixel in my new image2 i have the same result like run over OriginalImage. Well,in the end i need to reduced the number of pixels of my image to obtain a defined Dots Per Inch result.
I hope I was clear.
Thanks.

ColorBlend issue - black stripe at the end

I want to create a linear gradient with 7 step colors and custom size - from black, blue, cyan, green, yellow, red to white. My problem is that the final bitmap has a black stripe on the right side. Anyone have an idea what's the matter?
public static List<Color> interpolateColorScheme(int size)
{
// create result list with for interpolated colors
List<Color> colorList = new List<Color>();
// use Bitmap and Graphics from bitmap
using (Bitmap bmp = new Bitmap(size, 200))
using (Graphics G = Graphics.FromImage(bmp))
{
// create empty rectangle canvas
Rectangle rect = new Rectangle(Point.Empty, bmp.Size);
// use LinearGradientBrush class for gradient computation
LinearGradientBrush brush = new LinearGradientBrush
(rect, Color.Empty, Color.Empty, 0, false);
// setup ColorBlend object
ColorBlend colorBlend = new ColorBlend();
colorBlend.Positions = new float[7];
colorBlend.Positions[0] = 0;
colorBlend.Positions[1] = 1 / 6f;
colorBlend.Positions[2] = 2 / 6f;
colorBlend.Positions[3] = 3 / 6f;
colorBlend.Positions[4] = 4 / 6f;
colorBlend.Positions[5] = 5 / 6f;
colorBlend.Positions[6] = 1;
// blend colors and copy them to result color list
colorBlend.Colors = new Color[7];
colorBlend.Colors[0] = Color.Black;
colorBlend.Colors[1] = Color.Blue;
colorBlend.Colors[2] = Color.Cyan;
colorBlend.Colors[3] = Color.Green;
colorBlend.Colors[4] = Color.Yellow;
colorBlend.Colors[5] = Color.Red;
colorBlend.Colors[6] = Color.White;
brush.InterpolationColors = colorBlend;
G.FillRectangle(brush, rect);
bmp.Save("gradient_debug_image_sarcus.png", ImageFormat.Png);
for (int i = 0; i < size; i++) colorList.Add(bmp.GetPixel(i, 0));
brush.Dispose();
}
// return interpolated colors
return colorList;
}
Here is my gradient:
I took your code and tried every size from 2 to ushort.MaxValue, generating the gradient and scanning from the right edge to determine how many black pixels there were.
For many sizes, there are no black pixels. However, for certain consecutive runs of sizes, as the size increases, the number of black pixels also increases. There are approximately 2140 such runs in the tested range. This implies that there is a rounding error in the gradient drawing.
This bug has been encountered before (http://www.pcreview.co.uk/threads/error-on-lineargradientbrush.2165794/). The two solutions that link recommends are to
draw the gradient larger than you need it or
use WrapMode.TileFlipX.
What that link gets wrong is that the rounding error is not just 1 pixel at all times; at large image sizes it can be as large as 127 pixels (in the range I tested). Drawing the gradient larger than you need it requires you to know (or estimate) how much bigger you need to make the gradient. You could try scaling by (size + Math.Ceiling(size / 512.0)) / size, which is an upper bound on the error for the range of image sizes I have tested.
If you're looking for a simpler solution, specifying brush.WrapMode = WrapMode.TileFlipX will cause the brush to draw normally up to the (incorrect) edge of the gradient, then repeat the gradient in reverse until the actual edge of the specified rectangle. Since the rounding error is small compared to the size of the rectangle, this will look like the final color of the gradient has been extended to the edge of the rectangle. Visually, it looks good, but it may be unsuitable if you require very precise results.

Simpler way of drawing an image with pixels as the unit

I am trying to get the handle on some C# functions. Is there any simpler way to draw an image to the screen in Windows Forms using pixels as the measurement ? I just want that accuracy, as I am going to eventually run through an array of a World Level and put grass and walls etc on the screen based on the array.
Image grass = Bitmap.FromFile("grass.png");
// Create parallelogram for drawing image.
Point ulCorner = new Point(0, 0);
Point urCorner = new Point(100, 0);
Point llCorner = new Point(0, 100);
Point[] destPara = { ulCorner, urCorner, llCorner };
// Create rectangle for source image.
Rectangle srcRect = new Rectangle(0, 0, 100, 100);
GraphicsUnit units = GraphicsUnit.Pixel;
// Draw image to screen.
GFX.DrawImage(grass, destPara, srcRect, units);
I know I can do the following:
GFX.DrawImage(grass, new point(50,50) );
But the problem is this is now not using pixels as the measurement so I cannot position it correctly on the screen.
Call DrawImage but specifying the width and height you want, otherwise GDI+ will take into account the DPI of the device and scale it.
graphics.DrawImage(grass, top, left, grass.Width, grass.Height);
Beware DrawImageUnscaled(), contrary to what it suggests, will actually continue to scale.
Well Graphics.DrawImageUnscaled Method (Image, Int32, Int32) doesn't do anything different than DrawImage (Image, Int32, Int32) - Tell me more...

How to Draw a given Character in exact height?

I am drawing the text using Graphics.DrawString() method, But the text height drawn is not same as which i gave.
For Eg:
Font F=new Font("Arial", 1f,GraphicUnit.Inch);
g.DrawString("M", F,Brushes.red,new Point(0,0));
By using the above code, i'm drawing the text with height 1 inch, but the text drawn is not exactly in 1 inch.
I need to Draw the text in Exact height which i'm giving. Thanks in advance..
The simplest solution will be to use a GraphicsPath. Here are the steps necessary:
Calculate the height you want in pixels: To get 1.0f inches at, say 150 dpi you need 150 pixels.
Then create a GraphicsPath and add the character or string in the font and font style you want to use, using the calculated height
Now measure the resulting height, using GetBounds.
Then scale the height up to the necessary number of pixels
Finally clear the path and add the string again with the new height
Now you can use FillPath to output the pixels..
Here is a code example. It writes the test string to a file. If you want to write it to a printer or a control using their Graphics objects, you can do it the same way; just get/set the dpi before you calculate the first estimate of the height..
The code below creates this file; the Consolas 'x' is 150 pixels tall as is the 2nd character (ox95) from the Wingdings font. (Note that I did not center the output):
// we are using these test data:
int Dpi = 150;
float targetHeight = 1.00f;
FontFamily ff = new FontFamily("Consolas");
int fs = (int) FontStyle.Regular;
string targetString = "X";
// this would be the height without the white space
int targetPixels = (int) targetHeight * Dpi;
// we write to a Btimpap. I make it large enough..
// Instead you can write to a printer or a Control surface..
using (Bitmap bmp = new Bitmap(targetPixels * 2, targetPixels * 2))
{
// either set the resolution here
// or get and use it above from the Graphics!
bmp.SetResolution(Dpi, Dpi);
using (Graphics G = Graphics.FromImage(bmp))
{
// good quality, please!
G.SmoothingMode = SmoothingMode.AntiAlias;
G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
// target position (in pixels)
PointF p0 = new PointF(0, 0);
GraphicsPath gp = new GraphicsPath();
// first try:
gp.AddString(targetString, ff, fs, targetPixels, p0,
StringFormat.GenericDefault);
// this is the 1st result
RectangleF gbBounds = gp.GetBounds();
// now we correct the height:
float tSize = targetPixels * targetPixels / gbBounds.Height;
// and if needed the location:
p0 = new PointF(p0.X - gbBounds.X, p0.X - gbBounds.Y);
// and retry
gp.Reset();
gp.AddString(targetString, ff, fs, tSize, p0, StringFormat.GenericDefault);
// this should be good
G.Clear(Color.White);
G.FillPath(Brushes.Black, gp);
}
//now we save the image
bmp.Save("D:\\testString.png", ImageFormat.Png);
}
You may want to try using the correction factor to scale up a Font size and use DrawString after all.
There is also a way to calculate the numbers ahead using FontMetrics, but I understand the link to mean that such an approach could be font-dependent..

C# drawing rectangle in a specific color channel

I am trying to draw 2 rectangles 1 in green channel 2nd in red channel of a bitmap.
This is the code:
Bitmap bitmap_guess = new Bitmap(C_PALETTE_X_PIXEL_MAX, C_PALETTE_Y_PIXEL_MAX, PixelFormat.Format24bppRgb);
Graphics graphics = Graphics.FromImage(bitmap_guess);
Rectangle box_rect = new Rectangle(0, 0, C_BOX_PIXELS_WIDTH, C_BOX_PIXELS_HEIGHT);
matrix = new Matrix();
matrix.RotateAt(thc, new System.Drawing.PointF(xc, yc), MatrixOrder.Prepend);
graphics.Transform = matrix;
graphics.FillRectangle(new SolidBrush(Color.Green), xc, yc, box_rect.Width, box_rect.Height);
matrix = new Matrix();
matrix.RotateAt(th, new System.Drawing.PointF(x, y), MatrixOrder.Prepend);
graphics.Transform = matrix;
graphics.FillRectangle(new SolidBrush(Color.Red), x, y, box_rect.Width, box_rect.Height);
The problem is that when I draw the 2nd rectangle in red color it overwrites the 1st rectangles overlapping pixels to 0's. I'd want the rectangles to to change color when they overlap instead of plainly overwriting the previous pixel values. IE - to draw in a single channel instead of all 3.
I'm specifically looking for a Graphics operation for this.
Kind regards,
Laov
This might help you Merging two images in C#/.NET
You then could draw each rectangle into its own image and merge the results...

Categories