I am trying to add a text scale to a color image.
The agcScale.jpg image (below) is 2 winform labels on the top and bottom and 2 winform pictureboxes on the left and right.
The exact same code was used to produce the strings in the right and left pictureboxes, the only difference is that pictureBoxAgcVscale contains only the strings.
Why does DrawString in pictureBoxAgc look fine but DrawString in pictureBoxAgcVscale look so bad? I can probably fix pictureBoxAgcVscale by doing a bmp.SetPixel for each pixel but that seems like the wrong way to fix this.
private void DisplayAgcVscale(double min, double max)
{
var bmp = new Bitmap(pictureBoxAgcVscale.Width, pictureBoxAgcVscale.Height);
var c = (max - min) / bmp.Height;
using (var g = Graphics.FromImage(bmp))
{
var font = new Font("Microsoft Sans Serif", 8.25F);
var y1 = bmp.Height / 10;
for (var y = y1; y < bmp.Height; y += y1)
{
var agc = y * c + min;
var text = agc.ToString("#0.000V");
var h = bmp.Height - y - font.Height / 2;
g.DrawString(text, font, Brushes.Black, 0, h);
}
}
pictureBoxAgcVscale.Image = bmp;
}
You are drawing black text on a transparent background. The anti-aliasing pixels are fading from black to black, no choice, turning the letters into blobs. It works for the text on the left because you draw the pixels first.
You forgot g.Clear().
I had a similar issue, but in a listbox, and it wasn't resolved by clearing the rectangle. I had to apply a "TextRenderingHint":
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
e.Graphics.DrawString(listText, myFont, myBrush, e.Bounds, StringFormat.GenericDefault);
Related
My code. Output:
Bitmap bitmap = new Bitmap(600, 300, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bitmap);
GraphicsPath path = new GraphicsPath();
StringFormat format = new StringFormat();
Rectangle rect = new Rectangle(0, 0, 600, 300);
SolidBrush Brush = new SolidBrush(Color.White);
g.FillRectangle(Brush, rect);
g.SmoothingMode = SmoothingMode.AntiAlias;
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
path.AddString(textBox1.Text, FontFamily.GenericSansSerif, (int)FontStyle.Bold, 128, rect, format);
Brush = new SolidBrush(Color.Blue);
g.FillPath(Brush, path);
float x = path.GetBounds().X;
float y = path.GetBounds().Y;
float w = path.GetBounds().Width / textBox1.Text.Length;
float h = path.GetBounds().Height;
Pen redPen = new Pen(Color.Red, 2);
for (int i = 0; i < textBox1.Text.Length+1; i++)
{
rect = new Rectangle((int)x, (int)y, (int)w*i, (int)h);
g.DrawRectangle(redPen, rect);
}
pictureBox1.Image = bitmap;
I am getting the wrong result in the output because I cannot get the corners of the letters and am using the wrong way.
But i need get correct pixels of letter corners, draw rectangle and fill it.
Like this:
This line made me find one error in your code:
for (int i = 0; i < textBox1.Text.Length+1; i++)
It should be:
for (int i = 0; i < textBox1.Text.Length; i++)
But then indeed only 3 red boxes seem to appear.
The reason for that is the first rectangle (with your code) is very small, because the width is (int)w*i. That should be (int)w*(i+1).
Now back to the place where you are drawing rectangles.
If you take the text 'WWWW', you will see that your solution seems pretty OK.
But if you test with 'IIII', ten you should note that the left-most 'I' is left aligned in the red box, and the right-most 'I' is right aligned in the red box.
You are drawing 4 equal boxes round 4 letters with different with.
A solution could be to draw the letters with a monospaced font.
or, if you do not want a monospaced font, look at How to measure width of character precisely?
I'm trying to add multiple watermarks to an image with some gap in between. I have been able to achieve this with two small caveats though.
What I want is:
Watermark should be vertically center aligned.
The program need to stop adding watermark when there is no sufficient width and/or height left in the image (so that there is no cutting of text).
My code is:
static void WatermarkedImage(string path, string fileName, string message, string destFileName)
{
using (Image image = Image.FromFile(path + fileName))
{
Graphics graphics = Graphics.FromImage(image);
Font font = new Font("Arial", 20, FontStyle.Bold);
SolidBrush brush = new SolidBrush(Color.FromArgb(150, 255, 0, 0));
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
int index = 0;
float offsetX = image.Width / 3;
float offsetY = image.Height / 3;
int increment = Convert.ToInt32(image.Height * 0.15);
while (offsetY * 1.25 < image.Height)
{
Matrix matrix = new Matrix();
matrix.Translate(offsetX, offsetY);
matrix.Rotate(-45.0f);
graphics.Transform = matrix;
graphics.DrawString(message, font, brush, 0, 50, format);
offsetX += increment;
offsetY += increment;
index++;
}
image.Save(path + destFileName);
}
}
This is what I'm getting with this:
Desired Output.
Any help is much appreciated.
Thank You!
Update-1
To check if all of the text is inside the image, use MeasureString to see how large the string will be. From this you can construct a rectangle, transform each corner of said rectangle using the matrix, and check if they are all inside the image.
Your desired output does not have any offset on the x-axis, so you shouldn't increment x offset at all.
offsetY += increment;
index++;
}
while (offsetY * 1.25 < image.Height);
image.Save(path + destFileName);
I am working on a project using c# and visual studio(.NET Framework) in which the user can interact with some square boxes arranged in a grid using a PictureBox. The default size will be 20 x 20
The user can change the number of squares in the PictureBox but the size of the PictureBox should remain the same. I use WinForms.
The code that I have tried by using my logic and some of the internet's, I get it working as a square grid with squares in them, but as I increase the xNum or yNum, the grid starts becoming smaller and smaller. Here is my function which performs the drawing of rectangles:
public Graphics CreateRectangles(int xNum, int yNum, Graphics g)
{
rectList.Clear();
int width = (pictureBox1.Image.Height / xNum) - 1;
int height = (pictureBox1.Image.Height / yNum) - 1;
int both = (width + height) / 2;
Rectangle rect = new Rectangle();
rect.Size = new Size(both, both);
for(int x = 0; x < xNum; x++)
{
rect.X = x * rect.Width;
for(int y = 0; y < yNum; y++)
{
rect.Y = y * rect.Height;
rectList.Add(rect);
}
}
pictureBox1.Refresh();
foreach (Rectangle rec in rectList)
{
Pen p = new Pen(Color.Blue);
g.DrawRectangle(p, rec);
}
g.DrawLine(new Pen(Color.Blue), 0, yNum * height, xNum * width, yNum * height);
return g;
}
I clear the screen whenever I want to draw by
pictureBox1.Image = new Bitmap((Screen.PrimaryScreen.Bounds.Width / 8) * 7, (Screen.PrimaryScreen.Bounds.Height / 8) * 7);
Please help me fix the issue so that the size of the square area in the pictureBox1 remains the largest it can be without overlapping other GUI Elements.
Thanks In Advance. 👍
EDIT:
I know that if I have relative sizes then the cell size will go down, but that'
s expected behaviour, But I don't want the whole frame of the table to go down in size, As when I increase the xNum, the size of the outer frame/box should remain same
When I draw an image using Graphics.DrawImage and draw it at a bigger size than the original image, it ends up being a bit too small. You can see this in the following picture:
The green lines shouldn't be visible and are not part of the image. Rather they get drawn behind the image and the image should cover them.
How can I draw an image with the exact right size?
EDIT: I draw the green part with the same rectangle I pass into the DrawImage call, with the exact dimensions of how big the image should be. So no flaw in my values (I think).
EDIT 2: I draw the green rectangle using FillRectangle, so no pen calculations need to be done. Also, I logged the values that I pass into the rectangle for both the image and the green fill, and the values are correct. It's just the image that's off. I will post code later, as I'm not at my computer at the moment.
EDIT 3: This is the code I use to render the images:
// This is for zooming
public readonly float[] SCALES = { 0.05f, 0.1f, 0.125f, 0.25f, 0.333f, 0.5f, 0.667f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f, 10.0f, 12.0f, 15.0f, 20.0f, 30.0f, 36.0f };
private int scaleIndex = 8;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
float ScaleFactor = SCALES[scaleIndex];
e.Graphics.InterpolationMode = ScaleFactor < 1 ? InterpolationMode.Bicubic : InterpolationMode.NearestNeighbor;
Image im = Properties.Resources.TSprite0;
for (int y = 0; y < TilesVertical; y++)
{
for (int x = 0; x < TilesHorizontal; x++)
{
float sx = im.Width * ScaleFactor;
float sy = im.Height * ScaleFactor;
Point p = new Point((int)(-scrollPosition.X + sx * x), (int)(-scrollPosition.Y + sy * y));
Size s = new Size((int)Math.Floor(sx), (int)Math.Floor(sy));
// The green rectangle in the background should be the same size as the image
e.Graphics.FillRectangle(Brushes.Lime, new Rectangle(p, s));
e.Graphics.DrawImage(im, new Rectangle(p, s), 0, 0, 16, 16, GraphicsUnit.Pixel);
}
}
im.Dispose();
}
EDIT 4: Also note that the image seems to be cropped on the left and top instead of resized. Take a look at this comparison of the original image upscaled in Photoshop and then how GDI+ renders it:
The issue happens when scaling to 2x or larger.
Looks like the whole problem is caused by the wrong default PixelOffsetMode.
By offsetting pixels during rendering, you can improve render quality
at the cost of render speed.
Setting it to
g.PixelOffsetMode = PixelOffsetMode.Half;
makes it go away for me.
Setting it to
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
also works fine.
Default, None and HighSpeed cause the image to be rendered a little to the left and up.
Often you will also want to set InterpolationMode.NearestNeighbor.
What brush should i use to draw rectangles with white interior of the line and lines for the perimeter of the rectangle like the elevations below.
The form1 winform is what i am working on and the image behind the winform is how i need to the rectangles to look in my winform.
To make the question easier, how can i fill the interior portion of the rectangles with white?
How do i fill the LINES of the rectangle with white? I do not need to fill the inside of the rectangle, I need to fill a portion of the 4 lines that make up the rectangle with white.
void BuildShopDrawing(ElevationResponse elevation)
{
float penWidth = (float)((2f / 12f) * PIXELS_PER_FOOT);
Pen blackPen = new Pen(Color.FromArgb(40, 84, 149), penWidth);
Bitmap canvas = new Bitmap((((int)elevation.TotalWidthFeet) * PIXELS_PER_FOOT) + 55, (((int)elevation.TotalHeightFeet) * PIXELS_PER_FOOT) + 25);
Graphics dc = Graphics.FromImage(canvas);
RectangleF[] bays = new RectangleF[elevation.Bays.Count];
float x = 10F;
float width = 0F;
float height = 0F;
for (int i = 0; i < elevation.Bays.Count; i++)
{
if (i > 0)
{
x += (float)((elevation.Bays[i - 1].WidthInches / 12) * PIXELS_PER_FOOT);
}
width = (float)(elevation.Bays[i].WidthInches / 12) * PIXELS_PER_FOOT;
height = (float)(elevation.Bays[i].HeightInches / 12) * PIXELS_PER_FOOT;
bays[i] =
new RectangleF(new PointF(x, 10),
new SizeF(width, height));
}
dc.DrawRectangles(blackPen, bays);
this.picBx.Image = canvas;
this.Size = new System.Drawing.Size(canvas.Width + 10, canvas.Height + 50);
}
You need to look a bit more thoroughly at the Pen Class more specifically the CompoundArray Property, it will give you something like you are wanting, You will need to play around some other of the Pen Class properties to get your transitions right. And as a side note when you post example code that depends on external custom classes you make it harder for someone to help, it is always best to make sure that the code can run by itself.
Try adding this after you declare your pen.
float[] cmpArray = new float[4]{0.0F, 0.2F, 0.7F, 1.0F};
blackPen.CompoundArray = cmpArray;
It looks something like this: